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.

750 lines (722 loc) 27.5 kB
import { abs } from 'mathjs'; import { add } from 'mathjs'; import { arg } from 'mathjs'; import { BigNumber } from 'mathjs'; import { Complex } from 'mathjs'; import { index } from 'mathjs'; import { log10 } from 'mathjs'; import { MathCollection } from 'mathjs'; import { MathNumericType } from 'mathjs'; import { Matrix } from 'mathjs'; import { multiply } from 'mathjs'; import { pi } from 'mathjs'; import { PolarCoordinates } from 'mathjs'; import { pow } from 'mathjs'; import { round } from 'mathjs'; import { subset } from 'mathjs'; import { Unit } from 'mathjs'; export { abs } export { add } export { arg } export { Complex } /** * Creates a complex value or converts a value to a complex value. * * @see {@link https://mathjs.org/docs/reference/functions/complex.html | Math.js documentation for complex} */ export declare const complex: { (arg?: MathNumericType | string | PolarCoordinates): Complex; (arg?: MathCollection): MathCollection; (re: number, im: number): Complex; }; /** * Represents frequency data and provides utility methods for unit conversion and wavelength calculation. * * @remarks * The `Frequency` class is designed to handle frequency points commonly used in RF, microwave, * and high-speed digital engineering. It maintains an internal unit and a set of frequency points, * allowing for seamless conversion between different frequency and wavelength units. * * ##### Key Features: * - **Unit Awareness**: Keeps track of whether frequencies are defined in Hz, MHz, GHz, etc. * - **Automatic Conversion**: Automatically scales frequency points when the `unit` property is changed. * - **Wavelength Utilities**: Provides getters and setters for wavelength ($\lambda = c/f$) in various metric units. * * @example * #### Basic Usage * ```typescript * import { Frequency } from 'rf-touchstone'; * * const freq = new Frequency(); * freq.unit = 'GHz'; * freq.f_scaled = [1.0, 2.0, 5.0]; * * console.log(freq.f_Hz); // [1e9, 2e9, 5e9] * console.log(freq.wavelength_mm); // [299.79, 149.90, 59.96] * ``` */ 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. If frequency points already exist, they will be * automatically rescaled to the new unit. * * @param newUnit - The target frequency unit (Hz, kHz, MHz, or GHz). * @throws Error if the provided unit is invalid. */ set unit(newUnit: FrequencyUnit); /** * Gets the current frequency unit. */ get unit(): FrequencyUnit; /** * Internal storage for frequency points array * Each element represents a frequency point in the specified unit * @private */ private _f_scaled; /** * Sets the array of frequency points in the current frequency unit. * * @param value - Array of numerical frequency points. * @throws Error if the input is not a non-negative number array. */ set f_scaled(value: number[]); /** * Gets the array of frequency points in the current unit. */ 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; /** * Internal helper to set frequency values from a source unit. * @param values - Array of frequency points. * @param sourceUnit - The source frequency unit key. */ private _setFrequencyFromTargetUnit; /** * Gets the frequency points in Hertz (Hz). */ get f_Hz(): number[]; /** * Sets the frequency points in Hertz (Hz). */ set f_Hz(values: number[]); /** * Gets the frequency points in Kilohertz (kHz). */ get f_kHz(): number[]; /** * Sets the frequency points in Kilohertz (kHz). */ set f_kHz(values: number[]); /** * Gets the frequency points in Megahertz (MHz). */ get f_MHz(): number[]; /** * Sets the frequency points in Megahertz (MHz). */ set f_MHz(values: number[]); /** * Gets the frequency points in Gigahertz (GHz). */ get f_GHz(): number[]; /** * Sets the frequency points in Gigahertz (GHz). */ set f_GHz(values: number[]); /** * Gets the frequency points in Terahertz (THz). * * @remarks * THz is available for programmatic unit conversion but is NOT a valid file format unit. * Use this getter to convert existing frequency data to THz for calculations or display. */ get f_THz(): number[]; /** * Sets the frequency points in Terahertz (THz). * * @remarks * THz is available for programmatic unit conversion but is NOT a valid file format unit. * This setter converts THz values to the current internal unit (set via `unit` property). * Do not attempt to set `unit = 'THz'` as it will throw an error. */ 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 the wavelength in meters (m). */ get wavelength_m(): number[]; /** * Sets the wavelength in meters (m). * * @remarks * This is a bidirectional conversion: setting wavelength automatically calculates and updates * the underlying frequency points using the relationship f = c/λ, where c is the speed of light. * The resulting frequencies are stored in the current unit specified by the `unit` property. * * @throws Error if any wavelength value is zero (division by zero). */ set wavelength_m(values: number[]); /** * Gets the wavelength in centimeters (cm). */ get wavelength_cm(): number[]; /** * Sets the wavelength in centimeters (cm). * * @remarks * Bidirectional conversion: Updates frequency points via f = c/λ. */ set wavelength_cm(values: number[]); /** * Gets the wavelength in millimeters (mm). */ get wavelength_mm(): number[]; /** * Sets the wavelength in millimeters (mm). * * @remarks * Bidirectional conversion: Updates frequency points via f = c/λ. */ set wavelength_mm(values: number[]); /** * Gets the wavelength in micrometers (μm). */ get wavelength_um(): number[]; /** * Sets the wavelength in micrometers (μm). * * @remarks * Bidirectional conversion: Updates frequency points via f = c/λ. */ set wavelength_um(values: number[]); /** * Gets the wavelength in nanometers (nm). */ get wavelength_nm(): number[]; /** * Sets the wavelength in nanometers (nm). * * @remarks * Bidirectional conversion: Updates frequency points via f = c/λ. */ set wavelength_nm(values: number[]); } /** * Multipliers for converting from any FrequencyUnit or THz to Hz */ export declare const FREQUENCY_MULTIPLIERS: Record<FrequencyUnit | 'THz', number>; /** * Type representing the frequency unit. * - Hz: Hertz ($10^0$ Hz) * - kHz: Kilohertz ($10^3$ Hz) * - MHz: Megahertz ($10^6$ Hz) * - GHz: Gigahertz ($10^9$ Hz) */ export declare type FrequencyUnit = (typeof FrequencyUnits)[number]; /** * Supported frequency units in the Touchstone specification. * Note: THz is not officially supported as a file header unit in Touchstone v1.x/v2.x. * * @remarks * While THz can be accessed programmatically via the `f_THz` getter/setter for unit conversion, * it cannot be used as the `unit` property value. Attempting to set `frequency.unit = 'THz'` * will throw an error. */ export declare const FrequencyUnits: readonly ["Hz", "kHz", "MHz", "GHz"]; export { index } export { log10 } export { multiply } export { pi } export { pow } /** * Create an array or matrix with a range of numbers. * * @see {@link https://mathjs.org/docs/reference/functions/range.html | Math.js documentation for range} */ export declare const range: { (str: string, includeEnd?: boolean): Matrix; (start: number | BigNumber, end: number | BigNumber, includeEnd?: boolean): Matrix; (start: number | BigNumber | Unit, end: number | BigNumber | Unit, step: number | BigNumber | Unit, includeEnd?: boolean): Matrix; }; export { round } /** * Speed of light in m/s */ export declare const SPEED_OF_LIGHT = 299792458; export { subset } /** * Represents a Touchstone file parser and generator. * Supports reading, manipulating, and writing Touchstone (.snp) files * following version 1.0 and 1.1 specifications. * * @remarks * #### Overview * * The **Touchstone file format** (typically with `.snp` extensions) is an industry-standard ASCII format * for representing n-port network parameters. It is widely used in Electronic Design Automation (EDA) * and by measurement equipment (like VNAs) to describe the performance of RF and microwave components. * * ##### Key Features: * - **File Extensions**: `.s1p`, `.s2p`, ... `.sNp` indicate a network with N ports. * - **Case Insensitivity**: Keywords and identifiers are case-insensitive. * - **Versions**: Full support for version 1.0 and 1.1. Version 2.0 is not currently supported. * * --- * * #### Touchstone File Structure * * A file consists of a header (comments and option line) followed by network data. * * ##### 1. Header Section * * - **Comment Lines**: Lines starting with `!` are stored in the `comments` array. * - **Option Line**: The line starting with `#` sets the global context. * Example: `# GHz S MA R 50` * - `GHz`: Frequency unit (`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). * - RI: $A + j \cdot B$ * - MA: $A \cdot e^{j \cdot {\pi \over 180} \cdot B }$ * - DB: $10^{A \over 20} \cdot e^{j \cdot {\pi \over 180} \cdot B}$ * - `R 50`: Reference resistance in ohms (default is 50 ohms if omitted). * * ##### 2. Network Data Section * * Data is listed frequency by frequency. For a 2-port network, the format is: * `<frequency> <N11> <N21> <N12> <N22>` (where each NXx is a pair of values based on the format). * * --- * * #### References: * - {@link https://github.com/scikit-rf/scikit-rf scikit-rf: Open Source RF Engineering} * - {@link https://github.com/Nubis-Communications/SignalIntegrity SignalIntegrity: Signal and Power Integrity Tools} * - {@link https://books.google.com/books/about/S_Parameters_for_Signal_Integrity.html?id=_dLKDwAAQBAJ S-Parameters for Signal Integrity} * - {@link https://ibis.org/touchstone_ver2.1/touchstone_ver2_1.pdf Touchstone(R) File Format Specification (Version 2.1)} * * @example * #### Parsing a local file string * ```typescript * import { Touchstone } from 'rf-touchstone'; * * const content = ` * ! Simple 1-port S-parameter data * # MHz S RI R 50 * 100 0.9 -0.1 * 200 0.8 -0.2 * `; * const ts = Touchstone.fromText(content, 1); * console.log(ts.parameter); // 'S' * console.log(ts.frequency.unit); // 'MHz' * ``` */ export declare class Touchstone { /** * Utility to extract a filename from a URL or a file path string. * * @param pathOrUrl - The URL or string representation of a path. * @returns The filename part of the string. * @throws Error if the filename cannot be determined. */ static getFilename(pathOrUrl: string): string; /** * Extracts the basename from a filename or path by removing the file extension. * * @remarks * This method intelligently handles both simple filenames and full paths. * If a path separator is detected (/ or \), it first extracts the filename, * then removes any file extension. * * @param filenameOrPath - The filename or full path to process. * @returns The basename without extension. * * @example * ```typescript * // Works with simple filenames * Touchstone.getBasename('myfile.s2p') // 'myfile' * Touchstone.getBasename('data.txt') // 'data' * Touchstone.getBasename('document.pdf') // 'document' * * // Also works with full paths * Touchstone.getBasename('/path/to/network.s2p') // 'network' * Touchstone.getBasename('C:\\data\\test.s3p') // 'test' * Touchstone.getBasename('https://example.com/file.txt') // 'file' * * // Files without extension remain unchanged * Touchstone.getBasename('noextension') // 'noextension' * ``` */ static getBasename(filenameOrPath: string): string; /** * Determines the number of ports based on the file extension (e.g., .s2p -> 2). * * @param filename - The filename or URL to inspect. * @returns The number of ports, or null if it cannot be determined. */ static parsePorts(filename: string): number | null; /** * Creates a Touchstone instance from a raw text string. * * @param content - The raw text content of the Touchstone file * @param nports - The number of ports * @param name - Optional name for this Touchstone object (used for plotting legends and default save filename) * @returns A new Touchstone instance */ static fromText(content: string, nports: number, name?: string): Touchstone; /** * Async helper to fetch, parse, and return a Touchstone instance from a URL. * * @param url - The URL of the Touchstone file. * @param nports - The expected number of ports. If null, it attempts to parse from the URL. * @returns A promise resolving to a Touchstone instance. * @throws Error if the fetch fails, or the number of ports cannot be determined. */ static fromUrl(url: string, nports?: number | null): Promise<Touchstone>; /** * Reads a File object (typical in browser environments), parses it, and returns a Touchstone instance. * * @param file - The HTML5 File object to read. * @param nports - The expected number of ports. If null, it attempts to parse from the filename. * @returns A promise resolving to a Touchstone instance. * @throws Error if reading fails or the number of ports cannot be determined. */ static fromFile(file: File, nports?: number | null): Promise<Touchstone>; /** * Name of the Touchstone file (without extension). * Used as default filename when saving and as legend label in plots. * Automatically set when using `fromUrl()` or `fromFile()`, * can be manually provided in `fromText()` or set directly. * * @example * ```typescript * const ts = await Touchstone.fromUrl('http://example.com/network.s2p') * console.log(ts.name) // 'network' * ``` */ name: string | undefined; /** * Array of comment strings extracted from the Touchstone file header (lines starting with `!`). */ comments: string[]; /** * Touchstone format: MA, DB, and RI */ private _format; /** * Sets the Touchstone data format. * - RI: Real and Imaginary, i.e., $A + j \cdot B$ * - MA: Magnitude and Angle (degrees), i.e., $A \cdot e^{j \cdot {\pi \over 180} \cdot B }$ * - DB: Decibel and Angle (degrees), i.e., $10^{A \over 20} \cdot e^{j \cdot {\pi \over 180} \cdot B}$ * * @param format - The target format (RI, MA, or DB). * @throws Error if the format is invalid. */ 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; /** * Sets the type of network parameters. * - S: Scattering parameters * - Y: Admittance parameters * - Z: Impedance parameters * - H: Hybrid-h parameters * - G: Hybrid-g parameters * * @param parameter - The target parameter type (S, Y, Z, G, or H). * @throws Error if the parameter type is invalid. */ 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; /** * Sets the reference impedance (resistance) in Ohms. * Default: 50Ω * * @param impedance - A single number for all ports, or an array of numbers (one per port). * @throws Error if the impedance value is invalid. */ set impedance(impedance: TouchstoneImpedance); /** * Get the Touchstone impedance. * Default: 50Ω * @returns */ get impedance(): TouchstoneImpedance; /** * The number of ports in the network */ private _nports; /** * Sets the number of ports for the network. * * @param nports - The integer number of ports (must be >= 1). * @throws Error if the value is not a positive integer. */ set nports(nports: number | undefined | null); /** * The number of ports in the network. */ get nports(): number | undefined | null; /** * Frequency metadata and point array. */ frequency: Frequency | undefined; /** * 3D array to store the network parameter data * - First dimension [i]: Output port index (0 to nports-1) - where signal exits * - Second dimension [j]: Input port index (0 to nports-1) - where signal enters * - Third dimension [k]: Frequency index * * For example: matrix[i][j][k] is the parameter from port j+1 to port i+1 at frequency k */ private _matrix; /** * Directly sets the network parameter matrix. * * @param matrix - The 3D complex matrix to assign. */ 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 port index (0 to nports-1) - where the signal exits * - Second dimension [j]: Input port index (0 to nports-1) - where the signal enters * - Third dimension [k]: Frequency point index * * @example * ```typescript * // For any N-port network (2-port, 4-port, etc.): * const s11 = touchstone.matrix[0][0][freqIdx] // S11: port 1 → port 1 * const s21 = touchstone.matrix[1][0][freqIdx] // S21: port 1 → port 2 * const s12 = touchstone.matrix[0][1][freqIdx] // S12: port 2 → port 1 * const s22 = touchstone.matrix[1][1][freqIdx] // S22: port 2 → port 2 * * // General pattern: Sij = matrix[i-1][j-1][freqIdx] * // where i is the output port number, j is the input port number * ``` * * @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 } 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 = Touchstone.fromText(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 * 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; } /** * Type representing the Touchstone data format. * - RI: Real and Imaginary, i.e., $A + j \cdot B$ * - MA: Magnitude and Angle (degrees), i.e., $A \cdot e^{j \cdot {\pi \over 180} \cdot B }$ * - DB: Decibel (20*log10) and Angle (degrees), i.e., $10^{A \over 20} \cdot e^{j \cdot {\pi \over 180} \cdot B}$ */ export declare type TouchstoneFormat = (typeof TouchstoneFormats)[number]; /** * Supported Touchstone data formats. * - RI: Real and Imaginary, i.e., $A + j \cdot B$ * - MA: Magnitude and Angle (degrees), i.e., $A \cdot e^{j \cdot {\pi \over 180} \cdot B }$ * - DB: Decibel (20*log10) and Angle (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 [i]: Output port index (0 to nports-1) - where the signal exits * - Second dimension [j]: Input port index (0 to nports-1) - where the signal enters * - Third dimension [k]: 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 * (signal enters at port 1, exits at port 2) * * @example * ```typescript * // Access S21 (port 1 → port 2) at first frequency * const s21 = touchstone.matrix[1][0][0] * * // Access S11 (port 1 → port 1, reflection) at first frequency * const s11 = touchstone.matrix[0][0][0] * ``` */ export declare type TouchstoneMatrix = Complex[][][]; /** * Type representing the network parameter type. * - S: Scattering * - Y: Admittance * - Z: Impedance * - H: Hybrid-h * - G: Hybrid-g */ export declare type TouchstoneParameter = (typeof TouchstoneParameters)[number]; /** * Supported network parameter types in Touchstone files. * - S: Scattering parameters * - Y: Admittance parameters * - Z: Impedance parameters * - H: Hybrid-h parameters * - G: Hybrid-g parameters */ export declare const TouchstoneParameters: readonly ["S", "Y", "Z", "G", "H"]; /** * Multipliers for converting from other wavelength units to meters */ export declare const WAVELENGTH_MULTIPLIERS_TO_M: Record<string, number>; export { }