rf-touchstone 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,701 @@
1
+ # RF-Touchstone AI Usage Guide
2
+
3
+ > **For AI Coding Assistants**: This guide helps you understand how to integrate and use the `rf-touchstone` library in user projects.
4
+
5
+ ## Quick Start
6
+
7
+ ### Installation
8
+
9
+ **Via NPM/Yarn:**
10
+
11
+ ```bash
12
+ npm install rf-touchstone
13
+ # or
14
+ yarn add rf-touchstone
15
+ ```
16
+
17
+ **Via CDN:**
18
+
19
+ ```html
20
+ <script src="https://unpkg.com/rf-touchstone"></script>
21
+ <script>
22
+ const { Touchstone, abs, arg, pi } = window.Touchstone
23
+ </script>
24
+ ```
25
+
26
+ ### Basic Import Pattern
27
+
28
+ ```typescript
29
+ import { Touchstone } from 'rf-touchstone'
30
+ // Optional math helpers from mathjs
31
+ import { abs, arg, pi, complex } from 'rf-touchstone'
32
+ ```
33
+
34
+ ## Touchstone Class
35
+
36
+ The `Touchstone` class is the primary interface for working with .snp files.
37
+
38
+ ### Creating Touchstone Objects
39
+
40
+ **From URL (Browser or Node.js with fetch):**
41
+
42
+ ```typescript
43
+ // Auto-detect nports from filename (.s2p → 2-port)
44
+ const ts = await Touchstone.fromUrl('https://example.com/device.s2p')
45
+ console.log(ts.name) // 'device' - automatically extracted from filename
46
+
47
+ // Explicit nports if filename doesn't indicate
48
+ const ts = await Touchstone.fromUrl('https://example.com/data.txt', 2)
49
+ console.log(ts.name) // 'data' - basename without extension
50
+ ```
51
+
52
+ **From File Object (Browser file input):**
53
+
54
+ ```typescript
55
+ // In any framework with file input
56
+ const handleFileUpload = async (file: File) => {
57
+ const touchstone = await Touchstone.fromFile(file)
58
+ console.log(touchstone.name) // Filename without extension
59
+ // Use touchstone object
60
+ }
61
+ ```
62
+
63
+ **From Text Content (Universal - works in any environment):**
64
+
65
+ ```typescript
66
+ import { Touchstone } from 'rf-touchstone'
67
+
68
+ // Example S2P file content (could come from any source: upload, API, database, etc.)
69
+ const s2pContent = `! 2-port S-parameter data
70
+ # GHz S MA R 50
71
+ 1.0 0.50 -45 0.95 10 0.05 85 0.60 -30
72
+ 2.0 0.45 -60 0.90 15 0.10 80 0.55 -45
73
+ 3.0 0.40 -75 0.85 20 0.15 75 0.50 -60`
74
+
75
+ // Parse the text content with optional name
76
+ const touchstone = Touchstone.fromText(s2pContent, 2, 'my_measurement') // 2 = 2-port network
77
+
78
+ // Now you can use the parsed data
79
+ console.log(touchstone.name) // 'my_measurement'
80
+ console.log(touchstone.frequency.f_scaled) // [1.0, 2.0, 3.0]
81
+ console.log(touchstone.format) // 'MA'
82
+ console.log(touchstone.parameter) // 'S'
83
+ ```
84
+
85
+ ### Accessing Touchstone Data
86
+
87
+ ```typescript
88
+ const ts = await Touchstone.fromUrl('device.s2p')
89
+
90
+ // Frequency data
91
+ const frequencies = ts.frequency.f_scaled // Array of numbers in specified unit
92
+ const unit = ts.frequency.unit // 'Hz' | 'kHz' | 'MHz' | 'GHz'
93
+
94
+ // Name property (automatically extracted from filename or manually set)
95
+ const name = ts.name // e.g., 'device' (without extension)
96
+ ts.name = 'modified_device' // Can be modified for plot legends, filenames, etc.
97
+
98
+ // Options line data
99
+ const format = ts.format // 'RI' | 'MA' | 'DB'
100
+ const parameter = ts.parameter // 'S' | 'Y' | 'Z' | 'G' | 'H'
101
+ const impedance = ts.impedance // typically 50
102
+ const nports = ts.nports // Number of ports (1, 2, 3, 4, etc.)
103
+
104
+ // Network parameter matrix
105
+ // IMPORTANT: matrix is 3D array [outPort][inPort][frequencyIndex]
106
+ const matrix = ts.matrix // Complex number matrix
107
+ ```
108
+
109
+ ### Understanding Matrix Indexing ⚠️ CRITICAL
110
+
111
+ **Matrix Structure:**
112
+
113
+ ```typescript
114
+ // matrix[i][j][k]
115
+ // - i: Output port index (0 to nports-1) - where the signal exits
116
+ // - j: Input port index (0 to nports-1) - where the signal enters
117
+ // - k: Frequency point index (0 to numFrequencies-1)
118
+ //
119
+ // Sij = matrix[i-1][j-1][k] means: signal enters at port j, exits at port i
120
+ ```
121
+
122
+ **Accessing S-parameters (consistent for ALL port counts):**
123
+
124
+ ```typescript
125
+ // For 2-port network:
126
+ const s11 = ts.matrix[0][0][freqIdx] // S11
127
+ const s12 = ts.matrix[0][1][freqIdx] // S12
128
+ const s21 = ts.matrix[1][0][freqIdx] // S21
129
+ const s22 = ts.matrix[1][1][freqIdx] // S22
130
+
131
+ // For 4-port network (same pattern):
132
+ const s11 = ts.matrix[0][0][freqIdx] // S11
133
+ const s21 = ts.matrix[1][0][freqIdx] // S21
134
+ const s31 = ts.matrix[2][0][freqIdx] // S31
135
+ const s41 = ts.matrix[3][0][freqIdx] // S41
136
+ const s12 = ts.matrix[0][1][freqIdx] // S12
137
+ // ... and so on
138
+
139
+ // General pattern: Sij = ts.matrix[i-1][j-1][freqIdx]
140
+ ```
141
+
142
+ ### Format Conversion
143
+
144
+ To convert between formats (RI, MA, DB):
145
+
146
+ ```typescript
147
+ const ts = await Touchstone.fromUrl('device.s2p')
148
+
149
+ // Method 1: Change format and regenerate content
150
+ ts.format = 'DB' // Change to Decibel/Angle
151
+ const dbContent = ts.writeContent() // Generate new file content
152
+ // Now you can save dbContent or parse it as a new Touchstone object
153
+
154
+ // Method 2: Create new instance with different format
155
+ const dbTs = Touchstone.fromText(dbContent, ts.nports)
156
+ ```
157
+
158
+ ### Writing Touchstone Content
159
+
160
+ ```typescript
161
+ const ts = await Touchstone.fromUrl('device.s2p')
162
+
163
+ // Generate Touchstone file content in current format
164
+ const content = ts.writeContent()
165
+
166
+ // In Browser - Download as file
167
+ const blob = new Blob([content], { type: 'text/plain' })
168
+ const url = URL.createObjectURL(blob)
169
+ const a = document.createElement('a')
170
+ a.href = url
171
+ a.download = 'modified-device.s2p'
172
+ a.click()
173
+
174
+ // In Node.js - Save to file
175
+ import fs from 'fs/promises'
176
+ await fs.writeFile('modified-device.s2p', content)
177
+ ```
178
+
179
+ ### Working with Complex Numbers
180
+
181
+ The library uses mathjs Complex type `{ re: number, im: number }`:
182
+
183
+ ```typescript
184
+ import { abs, arg, pi } from 'rf-touchstone'
185
+
186
+ // Get S11 at first frequency for 2-port
187
+ const s11 = ts.matrix[0][0][0] // Complex { re: ..., im: ... }
188
+
189
+ // Calculate magnitude
190
+ const magnitude = abs(s11) // |S11|
191
+
192
+ // Calculate phase (in radians)
193
+ const phase = arg(s11) // ∠S11
194
+
195
+ // Convert to dB
196
+ const magnitudeDB = 20 * Math.log10(magnitude)
197
+
198
+ // Convert phase to degrees
199
+ const phaseDegrees = (phase * 180) / pi
200
+ ```
201
+
202
+ ## Frequency Class
203
+
204
+ The `Frequency` class provides powerful utilities for unit conversion and wavelength calculations. While it's typically accessed through `touchstone.frequency`, you can also work with it directly.
205
+
206
+ ### Accessing Frequency Data
207
+
208
+ ```typescript
209
+ const ts = await Touchstone.fromUrl('device.s2p')
210
+
211
+ // Access frequency points in the file's original unit
212
+ const frequencies = ts.frequency.f_scaled // e.g., [1.0, 2.0, 3.0]
213
+ const unit = ts.frequency.unit // e.g., 'GHz'
214
+
215
+ // Get frequency points in different units
216
+ const freqsInHz = ts.frequency.f_Hz // [1e9, 2e9, 3e9]
217
+ const freqsInMHz = ts.frequency.f_MHz // [1000, 2000, 3000]
218
+ const freqsInGHz = ts.frequency.f_GHz // [1.0, 2.0, 3.0]
219
+ ```
220
+
221
+ ### Converting Frequency Units
222
+
223
+ ```typescript
224
+ const ts = await Touchstone.fromUrl('device.s2p')
225
+
226
+ // Get current unit
227
+ console.log(ts.frequency.unit) // 'GHz'
228
+ console.log(ts.frequency.f_scaled) // [1.0, 2.0, 3.0]
229
+
230
+ // Change unit - automatically rescales f_scaled
231
+ ts.frequency.unit = 'MHz'
232
+ console.log(ts.frequency.f_scaled) // [1000, 2000, 3000]
233
+
234
+ // Access in any unit without changing internal representation
235
+ const inHz = ts.frequency.f_Hz // [1e9, 2e9, 3e9]
236
+ const inTHz = ts.frequency.f_THz // [0.001, 0.002, 0.003]
237
+ ```
238
+
239
+ > [!IMPORTANT]
240
+ > **THz Support**: While THz can be accessed programmatically via `f_THz` getter/setter, it is **not** an official unit in Touchstone v1.x/v2.x file formats. Do not set `frequency.unit = 'THz'` as it will throw an error. THz is only available through the `f_THz` property for conversion purposes.
241
+
242
+ ### Working with Wavelength
243
+
244
+ The `Frequency` class can convert between frequency and wavelength using the relationship $\lambda = c/f$ where $c$ is the speed of light (299,792,458 m/s).
245
+
246
+ ```typescript
247
+ const ts = await Touchstone.fromUrl('device.s2p')
248
+
249
+ // Get wavelengths in different units
250
+ const wavelengthsInM = ts.frequency.wavelength_m // meters
251
+ const wavelengthsInCm = ts.frequency.wavelength_cm // centimeters
252
+ const wavelengthsInMm = ts.frequency.wavelength_mm // millimeters
253
+ const wavelengthsInUm = ts.frequency.wavelength_um // micrometers
254
+ const wavelengthsInNm = ts.frequency.wavelength_nm // nanometers
255
+
256
+ // Example: 1 GHz → λ ≈ 0.3 m = 30 cm = 300 mm
257
+ ```
258
+
259
+ ### Setting Frequency from Wavelength
260
+
261
+ You can also set frequency points by specifying wavelengths. This is bidirectional - setting wavelength updates the underlying frequency data:
262
+
263
+ ```typescript
264
+ import { Frequency } from 'rf-touchstone'
265
+
266
+ const freq = new Frequency()
267
+ freq.unit = 'GHz'
268
+
269
+ // Set frequencies using wavelength in millimeters
270
+ // For λ = 300 mm → f ≈ 1 GHz
271
+ // For λ = 150 mm → f ≈ 2 GHz
272
+ freq.wavelength_mm = [300, 150, 100]
273
+
274
+ console.log(freq.f_GHz) // [~1.0, ~2.0, ~3.0]
275
+ console.log(freq.f_scaled) // [~1.0, ~2.0, ~3.0]
276
+ ```
277
+
278
+ ### Practical Example: Frequency Range Analysis
279
+
280
+ ```typescript
281
+ async function analyzeFrequencyRange(file: File) {
282
+ const ts = await Touchstone.fromFile(file)
283
+
284
+ const freqs = ts.frequency.f_GHz
285
+ const wavelengths = ts.frequency.wavelength_mm
286
+
287
+ return {
288
+ numPoints: freqs.length,
289
+ range: {
290
+ frequency: {
291
+ min: freqs[0],
292
+ max: freqs[freqs.length - 1],
293
+ unit: 'GHz',
294
+ },
295
+ wavelength: {
296
+ min: wavelengths[wavelengths.length - 1], // shortest
297
+ max: wavelengths[0], // longest
298
+ unit: 'mm',
299
+ },
300
+ },
301
+ // Calculate frequency spacing
302
+ spacing:
303
+ freqs.length > 1
304
+ ? ((freqs[freqs.length - 1] - freqs[0]) / (freqs.length - 1)).toFixed(3)
305
+ : 'N/A',
306
+ }
307
+ }
308
+ ```
309
+
310
+ ## Common Use Cases
311
+
312
+ ### Example 1: File Upload in Browser (Vanilla JavaScript)
313
+
314
+ ```typescript
315
+ import { Touchstone, abs, arg, pi } from 'rf-touchstone'
316
+
317
+ // HTML: <input type="file" id="fileInput" accept=".s*p">
318
+ const fileInput = document.getElementById('fileInput') as HTMLInputElement
319
+
320
+ fileInput.addEventListener('change', async (event) => {
321
+ const file = (event.target as HTMLInputElement).files?![0]
322
+ if (!file) return
323
+
324
+ try {
325
+ const ts = await Touchstone.fromFile(file)
326
+
327
+ console.log(`${ts.nports}-Port Network`)
328
+ console.log(`Frequencies: ${ts.frequency.f_scaled.length}`)
329
+ console.log(`Unit: ${ts.frequency.unit}`)
330
+ console.log(`Format: ${ts.format}`)
331
+
332
+ // Access S11 at first frequency (works for any port count)
333
+ const s11 = ts.matrix[0][0][0]
334
+ console.log(`S11 Magnitude: ${abs(s11)}`)
335
+ console.log(`S11 Phase (deg): ${(arg(s11) * 180 / pi).toFixed(2)}`)
336
+
337
+ } catch (error) {
338
+ console.error('Error loading Touchstone file:', error)
339
+ }
340
+ })
341
+ ```
342
+
343
+ ### Example 2: Format Converter (Node.js)
344
+
345
+ ```typescript
346
+ import { Touchstone } from 'rf-touchstone'
347
+ import fs from 'fs/promises'
348
+
349
+ async function convertTouchstone(
350
+ inputPath: string,
351
+ outputPath: string,
352
+ targetFormat: 'RI' | 'MA' | 'DB'
353
+ ) {
354
+ // Read input file
355
+ const content = await fs.readFile(inputPath, 'utf-8')
356
+
357
+ // Determine nports from filename
358
+ const nports = Touchstone.parsePorts(inputPath)
359
+ if (nports === null) {
360
+ throw new Error(
361
+ `Cannot determine number of ports from filename: ${inputPath}`
362
+ )
363
+ }
364
+
365
+ // Parse the file
366
+ const ts = Touchstone.fromText(content, nports)
367
+
368
+ console.log(`Original format: ${ts.format}`)
369
+
370
+ // Convert format
371
+ ts.format = targetFormat
372
+ const convertedContent = ts.writeContent()
373
+
374
+ // Write output
375
+ await fs.writeFile(outputPath, convertedContent)
376
+
377
+ console.log(`Converted to ${targetFormat}`)
378
+ }
379
+
380
+ // Usage
381
+ await convertTouchstone('input.s2p', 'output.s2p', 'DB')
382
+ ```
383
+
384
+ ### Example 3: S-Parameter Plotting (Any Framework)
385
+
386
+ ```typescript
387
+ import { Touchstone, abs } from 'rf-touchstone'
388
+
389
+ async function getPlotData(url: string) {
390
+ const ts = await Touchstone.fromUrl(url)
391
+
392
+ const numFreqs = ts.frequency.f_scaled.length
393
+ const frequencies = ts.frequency.f_scaled
394
+
395
+ // Extract S21 magnitude in dB for all frequencies (2-port network)
396
+ const s21_dB: number[] = []
397
+ for (let freqIdx = 0; freqIdx < numFreqs; freqIdx++) {
398
+ const s21 = ts.matrix[1][0][freqIdx] // S21 for 2-port
399
+ const magnitude = abs(s21)
400
+ s21_dB.push(20 * Math.log10(magnitude))
401
+ }
402
+
403
+ return {
404
+ frequencies,
405
+ s21_dB,
406
+ unit: ts.frequency.unit,
407
+ }
408
+ }
409
+
410
+ // Use with any chart library (Chart.js, D3, Plotly, etc.)
411
+ const data = await getPlotData('https://example.com/device.s2p')
412
+ console.log(data)
413
+ // { frequencies: [...], s21_dB: [...], unit: 'GHZ' }
414
+ ```
415
+
416
+ ### Example 4: Data Analysis
417
+
418
+ ```typescript
419
+ import { Touchstone, abs } from 'rf-touchstone'
420
+
421
+ async function analyzeTouchstone(file: File) {
422
+ const ts = await Touchstone.fromFile(file)
423
+
424
+ if (ts.nports !== 2) {
425
+ throw new Error('This example is for 2-port networks only')
426
+ }
427
+
428
+ const numFreqs = ts.frequency.f_scaled.length
429
+
430
+ // Calculate insertion loss (S21)
431
+ let maxS21 = 0
432
+ for (let i = 0; i < numFreqs; i++) {
433
+ const mag = abs(ts.matrix[1][0][i]) // S21
434
+ if (mag > maxS21) maxS21 = mag
435
+ }
436
+ const insertionLoss_dB = -20 * Math.log10(maxS21)
437
+
438
+ // Calculate return loss (S11)
439
+ let maxS11 = 0
440
+ for (let i = 0; i < numFreqs; i++) {
441
+ const mag = abs(ts.matrix[0][0][i]) // S11
442
+ if (mag > maxS11) maxS11 = mag
443
+ }
444
+ const returnLoss_dB = -20 * Math.log10(maxS11)
445
+
446
+ return {
447
+ nports: ts.nports,
448
+ numFrequencies: numFreqs,
449
+ frequencyRange: {
450
+ min: ts.frequency.f_scaled[0],
451
+ max: ts.frequency.f_scaled[numFreqs - 1],
452
+ unit: ts.frequency.unit,
453
+ },
454
+ insertionLoss_dB: insertionLoss_dB.toFixed(2),
455
+ returnLoss_dB: returnLoss_dB.toFixed(2),
456
+ format: ts.format,
457
+ }
458
+ }
459
+ ```
460
+
461
+ ### Example 5: Vue Component
462
+
463
+ ```vue
464
+ <template>
465
+ <div>
466
+ <input type="file" @change="handleFileUpload" accept=".s*p" />
467
+ <div v-if="touchstone">
468
+ <h2>{{ touchstone.nports }}-Port Network</h2>
469
+ <p>Frequencies: {{ touchstone.frequency.f_scaled.length }}</p>
470
+ <p>Unit: {{ touchstone.frequency.unit }}</p>
471
+ <p>Format: {{ touchstone.format }}</p>
472
+ <button @click="convertToDb">Convert to dB</button>
473
+ <button @click="downloadFile">Download</button>
474
+ </div>
475
+ </div>
476
+ </template>
477
+
478
+ <script setup lang="ts">
479
+ import { ref } from 'vue'
480
+ import { Touchstone } from 'rf-touchstone'
481
+
482
+ const touchstone = ref<Touchstone | null>(null)
483
+
484
+ async function handleFileUpload(event: Event) {
485
+ const file = (event.target as HTMLInputElement).files?.[0]
486
+ if (file) {
487
+ touchstone.value = await Touchstone.fromFile(file)
488
+ }
489
+ }
490
+
491
+ function convertToDb() {
492
+ if (touchstone.value) {
493
+ touchstone.value.format = 'DB'
494
+ // Re-parse to get converted matrix
495
+ const content = touchstone.value.writeContent()
496
+ touchstone.value = Touchstone.fromText(content, touchstone.value.nports!)
497
+ }
498
+ }
499
+
500
+ function downloadFile() {
501
+ if (touchstone.value) {
502
+ const content = touchstone.value.writeContent()
503
+ const blob = new Blob([content], { type: 'text/plain' })
504
+ const url = URL.createObjectURL(blob)
505
+ const a = document.createElement('a')
506
+ a.href = url
507
+ a.download = `converted.s${touchstone.value.nports}p`
508
+ a.click()
509
+ }
510
+ }
511
+ </script>
512
+ ```
513
+
514
+ ## TypeScript Types
515
+
516
+ ### Key Interfaces and Types
517
+
518
+ ```typescript
519
+ // Touchstone class
520
+ class Touchstone {
521
+ // Properties
522
+ name?: string // Filename without extension, used for legends and default filenames
523
+ nports?: number
524
+ frequency?: Frequency
525
+ format?: 'RI' | 'MA' | 'DB'
526
+ parameter?: 'S' | 'Y' | 'Z' | 'G' | 'H'
527
+ impedance: number | number[] // default: 50
528
+ matrix?: Complex[][][] // [outPort][inPort][freqIdx]
529
+ comments: string[]
530
+
531
+ // Static methods
532
+ static fromText(content: string, nports: number, name?: string): Touchstone
533
+ static fromUrl(url: string, nports?: number): Promise<Touchstone>
534
+ static fromFile(file: File, nports?: number): Promise<Touchstone>
535
+ static getFilename(pathOrUrl: string): string
536
+ static getBasename(filenameOrPath: string): string
537
+ static parsePorts(filename: string): number | null
538
+
539
+ // Instance methods
540
+ readContent(content: string, nports: number): void
541
+ writeContent(): string
542
+ validate(): void
543
+ }
544
+
545
+ // Complex number (from mathjs)
546
+ interface Complex {
547
+ re: number // Real part
548
+ im: number // Imaginary part
549
+ }
550
+
551
+ // Frequency class
552
+ class Frequency {
553
+ unit: 'Hz' | 'kHz' | 'MHz' | 'GHz'
554
+ f_scaled: number[] // Frequency values in current unit
555
+
556
+ // Getters/setters for different units
557
+ f_Hz: number[]
558
+ f_kHz: number[]
559
+ f_MHz: number[]
560
+ f_GHz: number[]
561
+ f_THz: number[]
562
+
563
+ // Wavelength getters/setters
564
+ wavelength_m: number[]
565
+ wavelength_cm: number[]
566
+ wavelength_mm: number[]
567
+ wavelength_um: number[]
568
+ wavelength_nm: number[]
569
+ }
570
+
571
+ // Helper Functions (from mathjs)
572
+ // These can be imported: import { abs, arg, pi } from 'rf-touchstone'
573
+ function abs(x: Complex | number): number
574
+ function arg(x: Complex): number // Returns phase in radians
575
+ function complex(re: number, im: number): Complex
576
+ function complex(obj: { r: number; phi: number }): Complex // Polar form
577
+ const pi: number
578
+
579
+ // Other available helpers:
580
+ // add, multiply, pow, log10, round, range, index, subset
581
+ ```
582
+
583
+ ## Important Notes
584
+
585
+ ### Module Formats Support
586
+
587
+ This library supports **multiple module formats** for maximum compatibility:
588
+
589
+ - **UMD (Universal Module Definition)**: `dist/Touchstone.umd.js` - For direct browser `<script>` tags
590
+ - **ESM (ES Modules)**: `dist/Touchstone.es.js` - For modern bundlers and `import` statements
591
+ - **CJS (CommonJS)**: `dist/Touchstone.cjs.js` - For older Node.js projects using `require()`
592
+
593
+ **Using in Browser (UMD):**
594
+
595
+ Via CDN:
596
+
597
+ ```html
598
+ <!-- Using unpkg -->
599
+ <script src="https://unpkg.com/rf-touchstone"></script>
600
+
601
+ <!-- Or using jsDelivr -->
602
+ <script src="https://cdn.jsdelivr.net/npm/rf-touchstone"></script>
603
+
604
+ <script>
605
+ const { Touchstone, abs, arg } = window.Touchstone
606
+ </script>
607
+ ```
608
+
609
+ **Using in Modern Projects (ESM):**
610
+
611
+ ```typescript
612
+ import { Touchstone } from 'rf-touchstone'
613
+ ```
614
+
615
+ **Using in Legacy Node.js (CommonJS):**
616
+
617
+ ```javascript
618
+ const { Touchstone } = require('rf-touchstone')
619
+ ```
620
+
621
+ ### Browser vs Node.js
622
+
623
+ - `Touchstone.fromUrl()` works in both environments (uses `fetch`)
624
+ - `Touchstone.fromFile()` is browser-only (uses `File` API)
625
+ - For Node.js file reading, use `fs` and `Touchstone.fromText()`
626
+
627
+ ### Port Indexing
628
+
629
+ **Matrix indexing is consistent for ALL port counts:**
630
+
631
+ ```typescript
632
+ // General pattern: Sij = matrix[i-1][j-1][freqIdx]
633
+
634
+ // For any N-port network:
635
+ const s11 = matrix[0][0][freqIdx] // S11
636
+ const s21 = matrix[1][0][freqIdx] // S21
637
+ const s12 = matrix[0][1][freqIdx] // S12
638
+ const s22 = matrix[1][1][freqIdx] // S22
639
+ // etc.
640
+ ```
641
+
642
+ ### Memory Considerations
643
+
644
+ Large .snp files (many ports/frequencies) create large matrices:
645
+
646
+ - Processing in chunks
647
+ - Not storing multiple copies simultaneously
648
+
649
+ ## Error Handling
650
+
651
+ ```typescript
652
+ try {
653
+ const ts = await Touchstone.fromUrl('device.s2p')
654
+ } catch (error) {
655
+ if (error instanceof Error) {
656
+ // Common errors:
657
+ // - Network errors (URL fetch failed)
658
+ // - Parse errors (invalid Touchstone format)
659
+ // - File not found
660
+ // - Could not determine nports
661
+ console.error('Failed to load Touchstone:', error.message)
662
+ }
663
+ }
664
+ ```
665
+
666
+ ## Resources
667
+
668
+ - **API Documentation**: https://panz2018.github.io/RF-Touchstone/api/modules
669
+ - **GitHub**: https://github.com/panz2018/RF-Touchstone
670
+ - **NPM**: https://www.npmjs.com/package/rf-touchstone
671
+
672
+ ## Common Patterns Summary
673
+
674
+ ```typescript
675
+ // ✅ DO: Load from URL
676
+ const ts = await Touchstone.fromUrl('file.s2p')
677
+
678
+ // ✅ DO: Access matrix correctly (same for all port counts)
679
+ const s21 = ts.matrix[1][0][freqIdx] // S21
680
+
681
+ // ✅ DO: Convert format properly
682
+ ts.format = 'DB'
683
+ const dbContent = ts.writeContent()
684
+
685
+ // ✅ DO: Iterate frequencies correctly
686
+ for (let freqIdx = 0; freqIdx < ts.frequency.f_scaled.length; freqIdx++) {
687
+ const s11 = ts.matrix[0][0][freqIdx]
688
+ // process s11...
689
+ }
690
+
691
+ // ❌ DON'T: Use non-existent convertFormat method
692
+ // const tsDB = ts.convertFormat('DB') // This doesn't exist!
693
+
694
+ // ❌ DON'T: Use wrong matrix indexing
695
+ // const s11 = ts.matrix[freqIdx][0][0] // WRONG!
696
+ ```
697
+
698
+ ---
699
+
700
+ **Last Updated**: 2026-01-25
701
+ **Library Version**: 0.0.5+