touchstone 0.14.0

Touchstone (s2p, etc.) file parser, plotter, and more
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# touchstone

Touchstone (SNP) parser for RF Engineering — Full N-Port Support

Parse, analyze, and manipulate Touchstone files with any number of ports (1-port, 2-port, 3-port, 4-port, and beyond).

## Installation

```bash
cargo install touchstone
```

This installs an executable in your `.cargo/bin` directory (`.cargo/bin/touchstone`).

---

## 1. What Are Touchstone Files?

Touchstone files (also called **SNP** files) are the industry-standard format for storing
**S-parameter** data measured or simulated for RF and microwave networks.

Each file describes how electromagnetic signals scatter through an N-port network — reflections,
transmissions, and coupling — across a range of frequencies.

The file extension encodes the port count: `.s1p` for a 1-port, `.s2p` for a 2-port, `.s3p`
for a 3-port, and so on up to `.s32p` and beyond.

A typical `.s2p` file looks like this:

```text
! Two-port network measurement
# GHz S RI R 50
1.0  0.5 -0.3  0.1 0.2  0.1 0.2  0.5 -0.3
2.0  0.4 -0.2  0.2 0.1  0.2 0.1  0.4 -0.2
```

The `#` line is the **option line**: it declares the frequency unit (`GHz`), parameter type (`S`),
data format (`RI` = Real-Imaginary), and reference impedance (`R 50` = 50 Ω).

---

## 2. Loading a Network

Use `Network::new` to parse any Touchstone file:

```rust
use touchstone::Network;

fn main() -> Result<(), touchstone::TouchstoneError> {
    let ntwk = Network::new("files/ntwk1.s2p")?;

    println!("Ports: {}", ntwk.rank);
    println!("Frequency unit: {}", ntwk.frequency_unit);
    println!("Format: {}", ntwk.format);
    println!("Reference impedance: {} Ω", ntwk.z0);
    println!("Data points: {}", ntwk.f.len());
    Ok(())
}
```

`Network::new` auto-detects the port count, data format, and frequency unit from the file, and
returns I/O or parse errors instead of panicking.

For uploaded data or API endpoints, parse Touchstone content directly from memory. The
`source_name` argument is used as the network name and for `.sNp` extension inference:

```rust
use touchstone::Network;

fn main() -> Result<(), touchstone::TouchstoneError> {
    let body = b"# GHz S RI R 50\n1.0 0.1 0.0 4.0 0.0 0.01 0.0 0.2 0.0\n";
    let ntwk = Network::from_bytes("uploaded.s2p", body)?;

    assert_eq!(ntwk.rank, 2);
    Ok(())
}
```

Non-fatal parser diagnostics are stored in `network.warnings`:

```rust
use touchstone::{Network, TouchstoneWarning};

fn main() -> Result<(), touchstone::TouchstoneError> {
    let ntwk = Network::from_str("uploaded.s1p", "1.0 0.5 0.0\n")?;

    assert!(matches!(
        ntwk.warnings.as_slice(),
        [TouchstoneWarning::MissingOptionLine { .. }]
    ));
    Ok(())
}
```

Touchstone v2 reference impedance metadata is available through
`network.reference_impedance()`. Networks with one scalar reference impedance return
`ReferenceImpedance::Common(z0)`, while files with per-port `[Reference]` values return
`ReferenceImpedance::PerPort(values)`.

---

## 3. Accessing S-Parameters

S-parameters are accessed with **1-indexed** port numbers, matching the conventional
S₁₁, S₂₁, etc. notation used in RF engineering.

Three accessor methods return a `Vec` over all frequencies:

| Method  | Returns                   | Struct fields                          |
|---------|---------------------------|----------------------------------------|
| `s_db`  | dB magnitude + angle (°)  | `FrequencyDB { frequency, s_db }`      |
| `s_ri`  | Real + imaginary parts    | `FrequencyRI { frequency, s_ri }`      |
| `s_ma`  | Linear magnitude + angle  | `FrequencyMA { frequency, s_ma }`      |

```rust
use touchstone::Network;

let ntwk = Network::new("files/ntwk1.s2p")?;

// S11 in dB (return loss)
let s11_db = ntwk.s_db(1, 1);
for point in &s11_db {
    println!("f={} : dB={}, angle={}", point.frequency, point.s_db.decibel(), point.s_db.angle());
}

// S21 in Real-Imaginary
let s21_ri = ntwk.s_ri(2, 1);
for point in &s21_ri {
    println!("f={} : re={}, im={}", point.frequency, point.s_ri.real(), point.s_ri.imaginary());
}

// S21 in Magnitude-Angle
let s21_ma = ntwk.s_ma(2, 1);
for point in &s21_ma {
    println!("f={} : mag={}, angle={}", point.frequency, point.s_ma.magnitude(), point.s_ma.angle());
}
```

For matrix-oriented workflows, use `s_matrix_at(point_index)` to get a stable full S-parameter
matrix for one frequency point. `NetworkPoint` is returned by `sample_at`, and also exposes the
full `SMatrix` at the requested frequency.

### Interpolation and Resampling

`sample_at(frequency_hz, interpolation, extrapolation)` samples a network at one frequency.
`resample(frequencies_hz, interpolation, extrapolation)` returns a new `Network` on a requested
frequency grid. Linear interpolation is performed in real/imaginary space, with magnitude/angle and
dB/angle values rebuilt from the interpolated complex values.

| Item | Description |
|------|-------------|
| `Interpolation::Linear` | Linear interpolation of each real and imaginary component |
| `Interpolation::Nearest` | Select nearest parsed frequency point; ties choose the lower point |
| `Extrapolation::Error` | Error outside the parsed frequency range |
| `Extrapolation::Clamp` | Hold nearest boundary S-parameters at the requested frequency |

### Network Parameter Conversions

For scalar reference impedance networks, stable matrix conversion APIs are available for common RF
and circuit-simulation workflows:

| Item | Description |
|------|-------------|
| `SMatrix::to_y_matrix(z0)` | Convert S-parameters to admittance parameters |
| `SMatrix::to_z_matrix(z0)` | Convert S-parameters to impedance parameters |
| `SMatrix::to_abcd(z0)` | Convert a two-port S matrix to ABCD parameters |
| `SMatrix::try_from_y_matrix(matrix, z0)` | Convert Y parameters back to S-parameters |
| `SMatrix::try_from_z_matrix(matrix, z0)` | Convert Z parameters back to S-parameters |
| `SMatrix::try_from_abcd(matrix, z0)` | Convert ABCD parameters back to a two-port S matrix |
| `network.y_matrix_at(point_index)` | Y matrix for one parsed frequency point |
| `network.z_matrix_at(point_index)` | Z matrix for one parsed frequency point |
| `network.abcd_at(point_index)` | ABCD matrix for one two-port frequency point |

### Field Aliases

Each S-parameter data pair struct offers multiple accessors for the same underlying data:

| Struct            | Field aliases                                      |
|-------------------|----------------------------------------------------|
| `RealImaginary`   | `.real()`, `.imaginary()`, `.magnitude()`, `.decibel()`, `.angle()` |
| `DecibelAngle`    | `.decibel()`, `.angle()`, `.magnitude()`, `.real()`, `.imaginary()` |
| `MagnitudeAngle`  | `.magnitude()`, `.angle()`, `.decibel()`, `.real()`, `.imaginary()` |

You can also convert between representations:

| From → To         | Method                                 |
|-------------------|----------------------------------------|
| `RealImaginary`   | `.magnitude_angle()`, `.decibel_angle()` |
| `MagnitudeAngle`  | `.real_imaginary()`, `.decible_angle()` (sic) |
| `DecibelAngle`    | (convert via `RealImaginary::from_decibel_angle`) |

---

## 4. Saving Networks

Save a `Network` back to disk. The writer auto-selects single-line format (1–2 ports) or
multi-line format (3+ ports):

```rust
use touchstone::Network;

let ntwk = Network::new("files/ntwk1.s2p")?;
ntwk.save("output.s2p").unwrap();
```

For generated data, build a network from in-memory matrices and serialize without writing a file:

```rust
use touchstone::{Complex, NetworkBuilder, SMatrix};

let ntwk = NetworkBuilder::new("generated.s1p", 1)
    .point(
        1.0e9,
        SMatrix {
            rank: 1,
            data: vec![vec![Complex { re: 0.5, im: -0.1 }]],
        },
    )
    .build()?;

let touchstone = ntwk.to_touchstone_string()?;
```

---

## 5. Cascading 2-Port Networks

Combine two 2-port networks in series using the ABCD parameter method.
The standard `cascade` connects port 2 of the first network to port 1 of the second:

```rust
use touchstone::Network;

let net1 = Network::new("files/ntwk1.s2p")?;
let net2 = Network::new("files/ntwk2.s2p")?;

let cascaded = net1.cascade(&net2);
println!("Cascaded network has {} data points", cascaded.f.len());
```

For explicit port specification, use `cascade_ports`:

```rust
use touchstone::Network;

let net1 = Network::new("files/ntwk1.s2p")?;
let net2 = Network::new("files/ntwk2.s2p")?;

let cascaded = net1.cascade_ports(&net2, 2, 1);
```

---

## 6. CLI Usage

### File Path

Plot a single Touchstone file:

```bash
touchstone files/ntwk3.s2p
```

Output:

```text
============================
Single file detected. Plotting.
In file: files/ntwk3.s2p
'files/ntwk3.s2p' is a Relative path with separators (nested).
Plot HTML generated at files/ntwk3.s2p.html
You can open the plot in your browser at:
file:///Users/iancleary/Development/touchstone/files/ntwk3.s2p.html
Attempting to open plot in your default browser...
Success! Opening: file:///Users/iancleary/Development/touchstone/files/ntwk3.s2p.html
```

> Works on Windows, macOS, and Linux file systems!

[![HTML file created for ntwk3.s2p by running `touchstone files/ntwk3.s2p` in the root of this directory](https://github.com/iancleary/touchstone/blob/main/examples/ntwk3.s2p.html.png?raw=true)](https://github.com/iancleary/touchstone/tree/main/examples/ntwk3.s2p.html)

You can view the HTML source file itself here: [examples/ntwk3.s2p.html](https://github.com/iancleary/touchstone/tree/main/examples/ntwk3.s2p.html).

### Directory Path

Plot all Touchstone files in a directory:

```bash
touchstone files/
```

Output:

```text
============================
In directory: files/
Directory detected. Plotting all valid network files in directory.
Found network file: "files/ntwk1.s2p"
Found network file: "files/ntwk2.s2p"
Found network file: "files/ntwk3.s2p"
Plot HTML generated at files/combined_plot.html
```

[![HTML file created for the files directory by running `touchstone files/` in the root of this directory](https://github.com/iancleary/touchstone/blob/main/examples/combined_plot.html.png?raw=true)](https://github.com/iancleary/touchstone/tree/main/examples/combined_plot.html)

You can view the HTML source file itself here: [examples/combined_plot.html](https://github.com/iancleary/touchstone/tree/main/examples/combined_plot.html).

### Cascade Command

Cascade two or more 2-port networks from the command line:

```bash
# Cascade two networks
touchstone cascade ntwk1.s2p ntwk2.s2p

# Cascade with custom output name
touchstone cascade ntwk1.s2p ntwk2.s2p --name result.s2p
```

### Full Help

```bash
touchstone --help
```

### Diagnostics (Tracing)

`touchstone` uses [`tracing`](https://docs.rs/tracing) for structured, runtime-controllable diagnostics. Set the `RUST_LOG` environment variable to see what the CLI is doing:

```bash
# See file detection, plot generation, and cascade output paths
RUST_LOG=touchstone=info touchstone files/ntwk1.s2p

# See all diagnostics including per-file discovery in directories
RUST_LOG=touchstone=debug touchstone files/data_folder/

# Only warnings and errors (quiet mode)
RUST_LOG=touchstone=warn touchstone files/ntwk1.s2p
```

If you use `touchstone` as a library, install any `tracing` subscriber in your application to capture events. Without a subscriber, all tracing calls are zero-cost no-ops.

---

## 7. Supported File Types, Data Formats, and Frequency Units

### File Types

| Extension | Ports | Example use case                       |
|-----------|-------|----------------------------------------|
| `.s1p`    | 1     | Terminations, loads, antennas          |
| `.s2p`    | 2     | Amplifiers, filters, cables            |
| `.s3p`    | 3     | Power dividers, circulators            |
| `.s4p`    | 4     | Differential pairs, couplers           |
| `.sNp`    | N     | Any N-port (tested up to 32-port)      |

### Data Formats

| Code | Name             | Pair values              |
|------|------------------|--------------------------|
| `RI` | Real-Imaginary   | real, imaginary          |
| `MA` | Magnitude-Angle  | linear magnitude, degrees|
| `DB` | Decibel-Angle    | dB magnitude, degrees    |

### Frequency Units

`Hz`, `kHz`, `MHz`, `GHz`, `THz` — all supported with automatic conversion.

---

## 8. API Summary

| Item                          | Description                                  |
|-------------------------------|----------------------------------------------|
| `Network::new(path)`          | Parse a Touchstone file and return errors    |
| `Network::from_bytes(name, bytes)` | Parse in-memory UTF-8 Touchstone bytes  |
| `Network::from_str(name, contents)` | Parse an in-memory Touchstone string    |
| `NetworkBuilder::new(name, rank)` | Build generated S-parameter networks     |
| `ReferenceImpedance::Common(z0)` | One scalar reference impedance             |
| `ReferenceImpedance::PerPort(values)` | Per-port Touchstone v2 reference impedances |
| `Complex { re, im }`         | Stable complex value used by public matrices |
| `SMatrix`                    | Stable full S-parameter matrix for one frequency |
| `ParameterMatrix`            | Stable Y- or Z-parameter matrix              |
| `ABCDMatrix`                 | Stable two-port ABCD transmission matrix     |
| `Interpolation`              | `Linear` or `Nearest` sampling policy        |
| `Extrapolation`              | `Error` or `Clamp` out-of-range policy       |
| `network.rank`                | Number of ports                              |
| `network.frequency_unit`      | Frequency unit string                        |
| `network.format`              | Data format (`RI`, `MA`, or `DB`)            |
| `network.z0`                  | Reference impedance (Ω)                      |
| `network.reference_impedance()` | Common or per-port reference metadata      |
| `network.warnings`            | Non-fatal parser diagnostics                 |
| `network.f`                   | Frequency vector (`Vec<f64>`)                |
| `network.f()`                 | Clone of frequency vector                    |
| `network.s_db(j, k)`         | S_jk in dB+angle — `Vec<FrequencyDB>`       |
| `network.s_ri(j, k)`         | S_jk in real+imag — `Vec<FrequencyRI>`       |
| `network.s_ma(j, k)`         | S_jk in mag+angle — `Vec<FrequencyMA>`       |
| `network.s_matrix_at(point_index)` | Full S matrix for one frequency point |
| `network.sample_at(frequency_hz, interpolation, extrapolation)` | Sample at one frequency |
| `network.resample(frequencies_hz, interpolation, extrapolation)` | Return a new frequency grid |
| `network.y_matrix_at(point_index)` | Full Y matrix for one frequency point |
| `network.z_matrix_at(point_index)` | Full Z matrix for one frequency point |
| `network.abcd_at(point_index)` | Two-port ABCD matrix for one frequency point |
| `network.to_touchstone_string()` | Serialize Touchstone text in memory       |
| `network.write_touchstone(writer)` | Write Touchstone text to any writer      |
| `network.save(path)`         | Write network to file                        |
| `network.cascade(&other)`    | Cascade two 2-port networks                  |
| `network.cascade_ports(&other, from, to)` | Cascade with explicit port mapping |
| `network.print_summary()`    | Print metadata to stdout                     |

---

## References

* [Touchstone Wikipedia entry]https://en.wikipedia.org/wiki/Touchstone_file
* [Touchstone File Format Specification Version 2.1]https://ibis.org/touchstone_ver2.1/touchstone_ver2_1.pdf
  * Local Version of [Touchstone File Format Specification Version 2.1]https://github.com/iancleary/touchstone/blob/main//docs/touchstone_ver2_1.pdf