UNPKG

rf-touchstone

Version:

A Javascript/TypeScript library for reading, manipulating, and writing Touchstone files (.snp files) used in radio frequency (RF) and microwave engineering.

804 lines (778 loc) 28 kB
import { abs } from 'mathjs'; import { add } from 'mathjs'; import { arg } from 'mathjs'; import { Complex } from 'mathjs'; import { complex } from 'mathjs'; import { index } from 'mathjs'; import { log10 } from 'mathjs'; import { multiply } from 'mathjs'; import { pi } from 'mathjs'; import { pow } from 'mathjs'; import { range } from 'mathjs'; import { round } from 'mathjs'; import { subset } from 'mathjs'; export { abs } export { add } export { arg } export { Complex } export { complex } /** * Class representing frequency data in RF and microwave engineering * * @remarks * The Frequency class manages frequency-related data, including: * - Frequency unit management * - Storage of frequency points * - Unit conversion capabilities * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scale = [1.0, 2.0, 3.0]; * ``` */ export declare class Frequency { /** * Internal storage for frequency unit * Defaults to 'Hz' as the base SI unit for frequency */ private _unit; /** * Sets the frequency unit for the instance * * @param newUnit - The frequency unit to set * @throws {Error} If the provided unit is not one of the supported frequency units * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'MHz'; // Sets unit to Megahertz * ``` */ set unit(newUnit: FrequencyUnit); /** * Gets the current frequency unit * * @returns The current frequency unit * * @example * ```typescript * const freq = new Frequency(); * console.log(freq.unit); // Outputs: 'Hz' * ``` */ get unit(): FrequencyUnit; /** * Internal storage for frequency points array * Each element represents a frequency point in the specified unit * @private */ private _f_scaled; /** * Array of frequency points in the current frequency unit * Each element represents a discrete frequency point for measurement or analysis * * @param value - Array of frequency points * @throws {Error} If the input is not an array * @throws {Error} If any element in the array is not a number * @throws {Error} If the array contains negative frequencies * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scaled = [1.0, 1.5, 2.0]; // Sets frequency points in GHz * ``` */ set f_scaled(value: number[]); /** * Gets the array of frequency points * @returns Array of frequency points in the current unit * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'MHz'; * freq.f_scaled = [100, 200, 300]; * console.log(freq.f_scaled); // Outputs: [100, 200, 300] * ``` */ get f_scaled(): number[]; /** * Private helper method to get frequency values in a target unit. * @param targetUnit - The key of the target frequency unit in FREQUENCY_MULTIPLIERS. * @returns Array of frequency points in the target unit. */ private _getFrequencyInTargetUnit; /** * Private helper method to set frequency values from a source unit. * @param values - Array of frequency points in the source unit. * @param sourceUnit - The key of the source frequency unit in FREQUENCY_MULTIPLIERS. */ private _setFrequencyFromTargetUnit; /** * Gets frequency points in Hz * @returns Array of frequency points in Hz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scaled = [1, 2, 3]; // 1 GHz, 2 GHz, 3 GHz * console.log(freq.f_Hz); // Outputs: [1e9, 2e9, 3e9] * ``` */ get f_Hz(): number[]; /** * Sets frequency points assuming input is in Hz * @param values - Array of frequency points in Hz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'MHz'; * freq.f_Hz = [1e9, 2e9, 3e9]; // Sets frequencies as 1 GHz, 2 GHz, 3 GHz * console.log(freq.f_scaled); // Outputs: [1000, 2000, 3000] (in MHz) * ``` */ set f_Hz(values: number[]); /** * Gets frequency points in kHz * @returns Array of frequency points in kHz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'Hz'; * freq.f_scaled = [1000, 2000, 3000]; // 1 kHz, 2 kHz, 3 kHz * console.log(freq.f_kHz); // Outputs: [1, 2, 3] * ``` */ get f_kHz(): number[]; /** * Sets frequency points assuming input is in kHz * @param values - Array of frequency points in kHz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'Hz'; * freq.f_kHz = [1, 2, 3]; // Sets frequencies as 1 kHz, 2 kHz, 3 kHz * console.log(freq.f_scaled); // Outputs: [1000, 2000, 3000] (in Hz) * ``` */ set f_kHz(values: number[]); /** * Gets frequency points in MHz * @returns Array of frequency points in MHz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scaled = [0.1, 0.2, 0.3]; // 0.1 GHz, 0.2 GHz, 0.3 GHz * console.log(freq.f_MHz); // Outputs: [100, 200, 300] * ``` */ get f_MHz(): number[]; /** * Sets frequency points assuming input is in MHz * @param values - Array of frequency points in MHz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_MHz = [100, 200, 300]; // Sets frequencies as 100 MHz, 200 MHz, 300 MHz * console.log(freq.f_scaled); // Outputs: [0.1, 0.2, 0.3] (in GHz) * ``` */ set f_MHz(values: number[]); /** * Gets frequency points in GHz * @returns Array of frequency points in GHz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'MHz'; * freq.f_scaled = [1000, 2000, 3000]; // 1000 MHz, 2000 MHz, 3000 MHz * console.log(freq.f_GHz); // Outputs: [1, 2, 3] * ``` */ get f_GHz(): number[]; /** * Sets frequency points assuming input is in GHz * @param values - Array of frequency points in GHz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'MHz'; * freq.f_GHz = [1, 2, 3]; // Sets frequencies as 1 GHz, 2 GHz, 3 GHz * console.log(freq.f_scaled); // Outputs: [1000, 2000, 3000] (in MHz) * ``` */ set f_GHz(values: number[]); /** * Gets frequency points in THz * @returns Array of frequency points in THz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scaled = [1000, 2000, 3000]; // 1000 GHz, 2000 GHz, 3000 GHz * console.log(freq.f_THz); // Outputs: [1, 2, 3] * ``` */ get f_THz(): number[]; /** * Sets frequency points assuming input is in THz * @param values - Array of frequency points in THz * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_THz = [1, 2, 3]; // Sets frequencies as 1 THz, 2 THz, 3 THz * console.log(freq.f_scaled); // Outputs: [1000, 2000, 3000] (in GHz) * ``` */ set f_THz(values: number[]); /** * Private helper method to get wavelength values in a target unit. * @param targetWavelengthUnit - The key of the target wavelength unit in WAVELENGTH_MULTIPLIERS_TO_M. * @returns Array of wavelength points in the target unit. */ private _getWavelengthInTargetUnit; /** * Private helper method to set frequency values from wavelength values in a source unit. * @param values - Array of wavelength points in the source unit. * @param sourceWavelengthUnit - The key of the source wavelength unit in WAVELENGTH_MULTIPLIERS_TO_M. */ private _setWavelengthFromTargetUnit; /** * Gets wavelength in meters * @returns Array of wavelength values in meters * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scaled = [1, 2, 3]; // Frequencies in GHz * console.log(freq.wavelength_m); // Outputs: Wavelengths in meters * ``` */ get wavelength_m(): number[]; /** * Sets wavelength in meters. Converts to frequency and stores. * @param values - Array of wavelength values in meters * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.wavelength_m = [0.3, 0.15, 0.1]; // Wavelengths in meters * console.log(freq.f_scaled); // Outputs: Frequencies in GHz * ``` */ set wavelength_m(values: number[]); /** * Gets wavelength in centimeters * @returns Array of wavelength values in centimeters * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scaled = [10, 20, 30]; // Frequencies in GHz * console.log(freq.wavelength_cm); // Outputs: Wavelengths in centimeters * ``` */ get wavelength_cm(): number[]; /** * Sets wavelength in centimeters. Converts to frequency and stores. * @param values - Array of wavelength values in centimeters * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.wavelength_cm = [30, 15, 10]; // Wavelengths in centimeters * console.log(freq.f_scaled); // Outputs: Frequencies in GHz * ``` */ set wavelength_cm(values: number[]); /** * Gets wavelength in millimeters * @returns Array of wavelength values in millimeters * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scaled = [100, 200, 300]; // Frequencies in GHz * console.log(freq.wavelength_mm); // Outputs: Wavelengths in millimeters * ``` */ get wavelength_mm(): number[]; /** * Sets wavelength in millimeters. Converts to frequency and stores. * @param values - Array of wavelength values in millimeters * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.wavelength_mm = [300, 150, 100]; // Wavelengths in millimeters * console.log(freq.f_scaled); // Outputs: Frequencies in GHz * ``` */ set wavelength_mm(values: number[]); /** * Gets wavelength in micrometers * @returns Array of wavelength values in micrometers * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scaled = [1000, 2000, 3000]; // Frequencies in GHz * console.log(freq.wavelength_um); // Outputs: Wavelengths in micrometers * ``` */ get wavelength_um(): number[]; /** * Sets wavelength in micrometers. Converts to frequency and stores. * @param values - Array of wavelength values in micrometers * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.wavelength_um = [300000, 150000, 100000]; // Wavelengths in micrometers * console.log(freq.f_scaled); // Outputs: Frequencies in GHz * ``` */ set wavelength_um(values: number[]); /** * Gets wavelength in nanometers * @returns Array of wavelength values in nanometers * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'THz'; * freq.f_scaled = [100, 200, 300]; // Frequencies in THz * console.log(freq.wavelength_nm); // Outputs: Wavelengths in nanometers * ``` */ get wavelength_nm(): number[]; /** * Sets wavelength in nanometers. Converts to frequency and stores. * @param values - Array of wavelength values in nanometers * * @example * ```typescript * const freq = new Frequency(); * freq.unit = 'THz'; * freq.wavelength_nm = [300, 150, 100]; // Wavelengths in nanometers * console.log(freq.f_scaled); // Outputs: Frequencies in THz * ``` */ set wavelength_nm(values: number[]); } /** * Type definition for frequency units * - Hz: Hertz (cycles per second) * - kHz: Kilohertz (10³ Hz) * - MHz: Megahertz (10⁶ Hz) * - GHz: Gigahertz (10⁹ Hz) * Touchstone standard doesn't support THz as the frequency unit in Touchstone file */ export declare type FrequencyUnit = (typeof FrequencyUnits)[number]; /** * Frequency units: 'Hz', 'kHz', 'MHz', 'GHz' * Touchstone standard doesn't support THz as the frequency unit in Touchstone file */ export declare const FrequencyUnits: readonly ["Hz", "kHz", "MHz", "GHz"]; export { index } export { log10 } export { multiply } export { pi } export { pow } export { range } export { round } /** * Speed of light in m/s */ export declare const SPEED_OF_LIGHT = 299792458; export { subset } /** * Touchstone class for reading and writing network parameter data in Touchstone format. * Supports both version 1.0 and 1.1 of the Touchstone specification. * * @remarks * #### Overview * * The **Touchstone file format** (also known as `.snp` files) is an industry-standard ASCII text format used to represent the n-port network parameters of electrical circuits. These files are commonly used in RF and microwave engineering to describe the behavior of devices such as filters, amplifiers, and interconnects. * * A Touchstone file contains data about network parameters (e.g., S-parameters, Y-parameters, Z-parameters) at specific frequencies. * * ##### Key Features: * - **File Extensions**: Traditionally, Touchstone files use extensions like `.s1p`, `.s2p`, `.s3p`, etc., where the number indicates the number of ports. For example, `.s2p` represents a 2-port network. * - **Case Insensitivity**: Touchstone files are case-insensitive, meaning keywords and values can be written in uppercase or lowercase. * - **Versioning**: **Only version 1.0 and 1.1 are supported in this class** * * --- * * #### File Structure * * A Touchstone file consists of several sections, each serving a specific purpose. Below is a breakdown of the structure: * * ##### 1. Header Section * * - **Comment Lines**: Lines starting with `!` are treated as comments and ignored during parsing. * - **Option Line**: Line starting with `#` defines global settings for the file, such as frequency units, parameter type, and data format. Example: * ```plaintext * # GHz S MA R 50 * ``` * - `GHz`: Frequency unit (can be `Hz`, `kHz`, `MHz`, or `GHz`). * - `S`: Parameter type (`S`, `Y`, `Z`, `H`, or `G`). * - `MA`: Data format (`MA` for magnitude-angle, `DB` for decibel-angle, or `RI` for real-imaginary). * - `R 50`: Reference resistance in ohms (default is 50 ohms if omitted). * * ##### 2. Network Data * * The core of the file contains the network parameter data, organized by frequency. Each frequency point is followed by its corresponding parameter values. * * - **Single-Ended Networks**: Data is arranged in a matrix format. For example, a 2-port network might look like this: * ```plaintext * <frequency> <N11> <N21> <N12> <N22> * ``` * * --- * * #### References: * - {@link https://ibis.org/touchstone_ver2.1/touchstone_ver2_1.pdf Touchstone(R) File Format Specification (Version 2.1)} * - {@link https://books.google.com/books/about/S_Parameters_for_Signal_Integrity.html?id=_dLKDwAAQBAJ S-Parameters for Signal Integrity} * - {@link https://github.com/scikit-rf/scikit-rf/blob/master/skrf/io/touchstone.py scikit-rf: Open Source RF Engineering} * - {@link https://github.com/Nubis-Communications/SignalIntegrity/blob/master/SignalIntegrity/Lib/SParameters/SParameters.py SignalIntegrity: Signal and Power Integrity Tools} * * @example * * #### Example 1: Simple 1-Port S-Parameter File * ```plaintext * ! 1-port S-parameter file * # MHz S MA R 50 * 100 0.99 -4 * 200 0.80 -22 * 300 0.707 -45 * ``` * * #### Example 2: Simple 2-Port S-Parameter File * ```plaintext * ! Sample S2P File * # HZ S RI R 50 * ! Freq S11(real) S11(imag) S21(real) S21(imag) S12(real) S12(imag) S22(real) S22(imag) * 1000000000 0.9 -0.1 0.01 0.02 0.01 0.02 0.8 -0.15 * 2000000000 0.8 -0.2 0.02 0.03 0.02 0.03 0.7 -0.25 * ``` */ export declare class Touchstone { /** * Comments in the file header with "!" symbol at the beginning of each row */ comments: string[]; /** * Touchstone format: MA, DB, and RI */ private _format; /** * Set the Touchstone format: MA, DB, RI, or undefined * - RI: real and imaginary, i.e. $A + j \cdot B$ * - MA: magnitude and angle (in degrees), i.e. $A \cdot e^{j \cdot {\pi \over 180} \cdot B }$ * - DB: decibels and angle (in degrees), i.e. $10^{A \over 20} \cdot e^{j \cdot {\pi \over 180} \cdot B}$ * @param format * @returns * @throws Will throw an error if the format is not valid */ set format(format: TouchstoneFormat | undefined | null); /** * Get the Touchstone format * @returns */ get format(): TouchstoneFormat | undefined; /** * Type of network parameter: 'S' | 'Y' | 'Z' | 'G' | 'H' */ private _parameter; /** * Set the type of network parameter * - S: Scattering parameters * - Y: Admittance parameters * - Z: Impedance parameters * - H: Hybrid-h parameters * - G: Hybrid-g parameters * @param parameter * @returns * @throws Will throw an error if the parameter is not valid */ set parameter(parameter: TouchstoneParameter | undefined | null); /** * Get the type of network parameter */ get parameter(): TouchstoneParameter | undefined | null; /** * Reference impedance(s) for the network parameters * Default: 50Ω */ private _impedance; /** * Set the Touchstone impedance. * Default: 50Ω * @param impedance * @returns * @throws Will throw an error if the impedance is not valid */ set impedance(impedance: TouchstoneImpedance); /** * Get the Touchstone impedance. * Default: 50Ω * @returns */ get impedance(): TouchstoneImpedance; /** * The number of ports in the network */ private _nports; /** * Set the ports number * @param nports * @returns * @throws Will throw an error if the number of ports is not valid */ set nports(nports: number | undefined | null); /** * Get the ports number */ get nports(): number | undefined | null; /** * Frequency points */ frequency: Frequency | undefined; /** * 3D array to store the network parameter data * - The first dimension is the exits (output) port number * - The second dimension is the enters (input) port number * - The third dimension is the frequency index * For example, data[i][j][k] would be the parameter from j+1 port to i+1 port at frequency index k */ private _matrix; /** * Sets the network parameter matrix. * * @param matrix - The 3D complex matrix to store, or undefined/null to clear * @remarks * This setter provides a way to directly assign the network parameter matrix. * Setting to undefined or null will clear the existing matrix data. */ set matrix(matrix: TouchstoneMatrix | undefined | null); /** * Gets the current network parameter matrix (3D array). * Represents the S/Y/Z/G/H-parameters of the network. * * @remarks * Matrix Structure: * - First dimension [i]: Output (exit) port number (0 to nports-1) * - Second dimension [j]: Input (enter) port number (0 to nports-1) * - Third dimension [k]: Frequency point index * * Example: * - matrix[i][j][k] represents the parameter from port j+1 to port i+1 at frequency k * - For S-parameters: matrix[1][0][5] is S₂₁ at the 6th frequency point * * Special case for 2-port networks: * - Indices are swapped to match traditional Touchstone format * - matrix[0][1][k] represents S₁₂ (not S₂₁) * * @returns The current network parameter matrix, or undefined if not set */ get matrix(): TouchstoneMatrix | undefined | null; /** * Reads and parses a Touchstone format string into the internal data structure. * * @param string - The Touchstone format string to parse * @param nports - Number of ports in the network * * @throws {Error} If the option line is missing or invalid * @throws {Error} If multiple option lines are found * @throws {Error} If the impedance specification is invalid * @throws {Error} If the data format is invalid or incomplete * * @remarks * The method performs the following steps: * 1. Parses comments and option line * 2. Extracts frequency points * 3. Converts raw data into complex numbers based on format * 4. Stores the results in the matrix property * * @example * ```typescript * import { Touchstone, Frequency } from 'rf-touchstone'; * * const s1pString = ` * ! This is a 1-port S-parameter file * # MHz S MA R 50 * 100 0.99 -4 * 200 0.80 -22 * 300 0.707 -45 * `; * * const touchstone = new Touchstone(); * touchstone.readContent(s1pString, 1); * * console.log(touchstone.comments); // Outputs: [ 'This is a 1-port S-parameter file' ] * console.log(touchstone.format); // Outputs: 'MA' * console.log(touchstone.parameter); // Outputs: 'S' * console.log(touchstone.impedance); // Outputs: 50 * console.log(touchstone.nports); // Outputs: 1 * console.log(touchstone.frequency?.f_scaled); // Outputs: [ 100, 200, 300 ] * console.log(touchstone.matrix); // Outputs: the parsed matrix data * ``` */ readContent(string: string, nports: number): void; /** * Validates the internal state of the Touchstone instance. * Performs comprehensive checks on all required data and matrix dimensions. * * @throws {Error} If any of the following conditions are met: * - Number of ports is undefined * - Frequency object is not initialized * - Frequency points array is empty * - Network parameter type is undefined * - Data format is undefined * - Network parameter matrix is undefined * - Matrix dimensions don't match with nports or frequency points * * @remarks * This method performs two main validation steps: * 1. Essential Data Validation: * - Checks existence of all required properties * - Ensures frequency points are available * * 2. Matrix Dimension Validation: * - Verifies matrix row count matches port number * - Ensures each row has correct number of columns * - Validates frequency points count in each matrix element */ validate(): void; /** * Generates a Touchstone format string from the internal data structure. * * @returns The generated Touchstone format string * * @throws {Error} If any required data is missing * @throws {Error} If the matrix dimensions are invalid * * @remarks * The generated string includes: * 1. Comments (if any) * 2. Option line with format, parameter type, and impedance * 3. Network parameter data in the specified format * * @example * ```typescript * import { Touchstone, Frequency } from 'rf-touchstone'; * import { complex } from 'mathjs'; * * const touchstone = new Touchstone(); * * // Set properties and matrix data (using simplified complex number representation for example) * touchstone.comments = ['Generated by rf-touchstone']; * touchstone.nports = 1; * touchstone.frequency = new Frequency(); * touchstone.frequency.unit = 'GHz'; * touchstone.frequency.f_scaled = [1.0, 2.0]; * touchstone.parameter = 'S'; * touchstone.format = 'MA'; * touchstone.impedance = 50; * touchstone.matrix = [ * [ // Output port 1 * [complex(0.5, 0.1), complex(0.4, 0.2)] // S11 at each frequency * ] * ]; * * const s1pString = touchstone.writeContent(); * console.log(s1pString); * // Expected output (approximately, due to floating point precision): * // ! Generated by rf-touchstone * // # GHz S MA R 50 * // 1 0.5099 11.3099 * // 2 0.4472 26.5651 * ``` */ writeContent(): string; } /** * S-parameter format: MA, DB, and RI * - RI: real and imaginary, i.e. $A + j \cdot B$ * - MA: magnitude and angle (in degrees), i.e. $A \cdot e^{j \cdot {\pi \over 180} \cdot B }$ * - DB: decibels and angle (in degrees), i.e. $10^{A \over 20} \cdot e^{j \cdot {\pi \over 180} \cdot B}$ */ export declare type TouchstoneFormat = (typeof TouchstoneFormats)[number]; /** * S-parameter format: MA, DB, and RI * - RI: real and imaginary, i.e. $A + j \cdot B$ * - MA: magnitude and angle (in degrees), i.e. $A \cdot e^{j \cdot {\pi \over 180} \cdot B }$ * - DB: decibels and angle (in degrees), i.e. $10^{A \over 20} \cdot e^{j \cdot {\pi \over 180} \cdot B}$ */ export declare const TouchstoneFormats: readonly ["RI", "MA", "DB"]; /** * The reference resistance(s) for the network parameters. * The token "R" (case-insensitive) followed by one or more reference resistance values. * Default: 50Ω * * For Touchstone 1.0, this is a single value for all ports. * For Touchstone 1.1, this can be an array of values (one per port) */ export declare type TouchstoneImpedance = number | number[]; /** * Network parameter matrix stored as complex numbers. * * @remarks * The matrix is a 3D array with the following dimensions: * - First dimension: output port index (0 to nports-1) * - Second dimension: input port index (0 to nports-1) * - Third dimension: frequency point index * * For example: * - matrix[i][j][k] represents the parameter from port j+1 to port i+1 at frequency k * - For S-parameters: matrix[1][0][5] is S₂₁ at the 6th frequency point * * Special case for 2-port networks: * - Indices are swapped to match traditional Touchstone format * - matrix[0][1][k] represents S₁₂ (not S₂₁) */ export declare type TouchstoneMatrix = Complex[][][]; /** * Type of network parameters: 'S' | 'Y' | 'Z' | 'G' | 'H' * - S: Scattering parameters * - Y: Admittance parameters * - Z: Impedance parameters * - H: Hybrid-h parameters * - G: Hybrid-g parameters */ export declare type TouchstoneParameter = (typeof TouchstoneParameters)[number]; /** * Type of network parameters * - S: Scattering parameters * - Y: Admittance parameters * - Z: Impedance parameters * - H: Hybrid-h parameters * - G: Hybrid-g parameters */ export declare const TouchstoneParameters: string[]; export { }