rf-touchstone 0.0.4 → 0.0.5
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.
- package/.github/AI_USAGE_GUIDE.md +585 -0
- package/dist/Touchstone.es.js +17 -12
- package/dist/touchstone.d.ts +31 -20
- package/package.json +7 -2
- package/readme.md +20 -0
- package/src/touchstone.ts +31 -20
|
@@ -0,0 +1,585 @@
|
|
|
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
|
+
## Core Concepts
|
|
35
|
+
|
|
36
|
+
### 1. **Touchstone Class** - Main API Entry Point
|
|
37
|
+
|
|
38
|
+
The `Touchstone` class is the primary interface for working with .snp files.
|
|
39
|
+
|
|
40
|
+
#### Creating Touchstone Objects
|
|
41
|
+
|
|
42
|
+
**From URL (Browser or Node.js with fetch):**
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// Auto-detect nports from filename (.s2p → 2-port)
|
|
46
|
+
const ts = await Touchstone.fromUrl('https://example.com/device.s2p')
|
|
47
|
+
|
|
48
|
+
// Explicit nports if filename doesn't indicate
|
|
49
|
+
const ts = await Touchstone.fromUrl('https://example.com/data.txt', 2)
|
|
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
|
+
// Use touchstone object
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**From Text Content (Universal - works in any environment):**
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { Touchstone } from 'rf-touchstone'
|
|
66
|
+
|
|
67
|
+
// Example S2P file content (could come from any source: upload, API, database, etc.)
|
|
68
|
+
const s2pContent = `! 2-port S-parameter data
|
|
69
|
+
# GHz S MA R 50
|
|
70
|
+
1.0 0.50 -45 0.95 10 0.05 85 0.60 -30
|
|
71
|
+
2.0 0.45 -60 0.90 15 0.10 80 0.55 -45
|
|
72
|
+
3.0 0.40 -75 0.85 20 0.15 75 0.50 -60`
|
|
73
|
+
|
|
74
|
+
// Parse the text content
|
|
75
|
+
const touchstone = Touchstone.fromText(s2pContent, 2) // 2 = 2-port network
|
|
76
|
+
|
|
77
|
+
// Now you can use the parsed data
|
|
78
|
+
console.log(touchstone.frequency.f_scaled) // [1.0, 2.0, 3.0]
|
|
79
|
+
console.log(touchstone.format) // 'MA'
|
|
80
|
+
console.log(touchstone.parameter) // 'S'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. **Accessing Touchstone Data**
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const ts = await Touchstone.fromUrl('device.s2p')
|
|
87
|
+
|
|
88
|
+
// Frequency data
|
|
89
|
+
const frequencies = ts.frequency.f_scaled // Array of numbers in specified unit
|
|
90
|
+
const unit = ts.frequency.unit // 'Hz' | 'kHz' | 'MHz' | 'GHz'
|
|
91
|
+
|
|
92
|
+
// Options line data
|
|
93
|
+
const format = ts.format // 'RI' | 'MA' | 'DB'
|
|
94
|
+
const parameter = ts.parameter // 'S' | 'Y' | 'Z' | 'G' | 'H'
|
|
95
|
+
const impedance = ts.impedance // typically 50
|
|
96
|
+
const nports = ts.nports // Number of ports (1, 2, 3, 4, etc.)
|
|
97
|
+
|
|
98
|
+
// Network parameter matrix
|
|
99
|
+
// IMPORTANT: matrix is 3D array [outPort][inPort][frequencyIndex]
|
|
100
|
+
const matrix = ts.matrix // Complex number matrix
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 3. **Understanding Matrix Indexing** ⚠️ CRITICAL
|
|
104
|
+
|
|
105
|
+
**Matrix Structure:**
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// matrix[i][j][k]
|
|
109
|
+
// - i: Output port index (0 to nports-1) - where the signal exits
|
|
110
|
+
// - j: Input port index (0 to nports-1) - where the signal enters
|
|
111
|
+
// - k: Frequency point index (0 to numFrequencies-1)
|
|
112
|
+
//
|
|
113
|
+
// Sij = matrix[i-1][j-1][k] means: signal enters at port j, exits at port i
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Accessing S-parameters (consistent for ALL port counts):**
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// For 2-port network:
|
|
120
|
+
const s11 = ts.matrix[0][0][freqIdx] // S11
|
|
121
|
+
const s12 = ts.matrix[0][1][freqIdx] // S12
|
|
122
|
+
const s21 = ts.matrix[1][0][freqIdx] // S21
|
|
123
|
+
const s22 = ts.matrix[1][1][freqIdx] // S22
|
|
124
|
+
|
|
125
|
+
// For 4-port network (same pattern):
|
|
126
|
+
const s11 = ts.matrix[0][0][freqIdx] // S11
|
|
127
|
+
const s21 = ts.matrix[1][0][freqIdx] // S21
|
|
128
|
+
const s31 = ts.matrix[2][0][freqIdx] // S31
|
|
129
|
+
const s41 = ts.matrix[3][0][freqIdx] // S41
|
|
130
|
+
const s12 = ts.matrix[0][1][freqIdx] // S12
|
|
131
|
+
// ... and so on
|
|
132
|
+
|
|
133
|
+
// General pattern: Sij = ts.matrix[i-1][j-1][freqIdx]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 4. **Format Conversion**
|
|
137
|
+
|
|
138
|
+
To convert between formats (RI, MA, DB):
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const ts = await Touchstone.fromUrl('device.s2p')
|
|
142
|
+
|
|
143
|
+
// Method 1: Change format and regenerate content
|
|
144
|
+
ts.format = 'DB' // Change to Decibel/Angle
|
|
145
|
+
const dbContent = ts.writeContent() // Generate new file content
|
|
146
|
+
// Now you can save dbContent or parse it as a new Touchstone object
|
|
147
|
+
|
|
148
|
+
// Method 2: Create new instance with different format
|
|
149
|
+
const dbTs = Touchstone.fromText(dbContent, ts.nports)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 5. **Writing Touchstone Content**
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const ts = await Touchstone.fromUrl('device.s2p')
|
|
156
|
+
|
|
157
|
+
// Generate Touchstone file content in current format
|
|
158
|
+
const content = ts.writeContent()
|
|
159
|
+
|
|
160
|
+
// In Browser - Download as file
|
|
161
|
+
const blob = new Blob([content], { type: 'text/plain' })
|
|
162
|
+
const url = URL.createObjectURL(blob)
|
|
163
|
+
const a = document.createElement('a')
|
|
164
|
+
a.href = url
|
|
165
|
+
a.download = 'modified-device.s2p'
|
|
166
|
+
a.click()
|
|
167
|
+
|
|
168
|
+
// In Node.js - Save to file
|
|
169
|
+
import fs from 'fs/promises'
|
|
170
|
+
await fs.writeFile('modified-device.s2p', content)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 6. **Working with Complex Numbers**
|
|
174
|
+
|
|
175
|
+
The library uses mathjs Complex type `{ re: number, im: number }`:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { abs, arg, pi } from 'rf-touchstone'
|
|
179
|
+
|
|
180
|
+
// Get S11 at first frequency for 2-port
|
|
181
|
+
const s11 = ts.matrix[0][0][0] // Complex { re: ..., im: ... }
|
|
182
|
+
|
|
183
|
+
// Calculate magnitude
|
|
184
|
+
const magnitude = abs(s11) // |S11|
|
|
185
|
+
|
|
186
|
+
// Calculate phase (in radians)
|
|
187
|
+
const phase = arg(s11) // ∠S11
|
|
188
|
+
|
|
189
|
+
// Convert to dB
|
|
190
|
+
const magnitudeDB = 20 * Math.log10(magnitude)
|
|
191
|
+
|
|
192
|
+
// Convert phase to degrees
|
|
193
|
+
const phaseDegrees = (phase * 180) / pi
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Common Use Cases
|
|
197
|
+
|
|
198
|
+
### Example 1: File Upload in Browser (Vanilla JavaScript)
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { Touchstone, abs, arg, pi } from 'rf-touchstone'
|
|
202
|
+
|
|
203
|
+
// HTML: <input type="file" id="fileInput" accept=".s*p">
|
|
204
|
+
const fileInput = document.getElementById('fileInput') as HTMLInputElement
|
|
205
|
+
|
|
206
|
+
fileInput.addEventListener('change', async (event) => {
|
|
207
|
+
const file = (event.target as HTMLInputElement).files?![0]
|
|
208
|
+
if (!file) return
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const ts = await Touchstone.fromFile(file)
|
|
212
|
+
|
|
213
|
+
console.log(`${ts.nports}-Port Network`)
|
|
214
|
+
console.log(`Frequencies: ${ts.frequency.f_scaled.length}`)
|
|
215
|
+
console.log(`Unit: ${ts.frequency.unit}`)
|
|
216
|
+
console.log(`Format: ${ts.format}`)
|
|
217
|
+
|
|
218
|
+
// Access S11 at first frequency (works for any port count)
|
|
219
|
+
const s11 = ts.matrix[0][0][0]
|
|
220
|
+
console.log(`S11 Magnitude: ${abs(s11)}`)
|
|
221
|
+
console.log(`S11 Phase (deg): ${(arg(s11) * 180 / pi).toFixed(2)}`)
|
|
222
|
+
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error('Error loading Touchstone file:', error)
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Example 2: Format Converter (Node.js)
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { Touchstone } from 'rf-touchstone'
|
|
233
|
+
import fs from 'fs/promises'
|
|
234
|
+
|
|
235
|
+
async function convertTouchstone(
|
|
236
|
+
inputPath: string,
|
|
237
|
+
outputPath: string,
|
|
238
|
+
targetFormat: 'RI' | 'MA' | 'DB'
|
|
239
|
+
) {
|
|
240
|
+
// Read input file
|
|
241
|
+
const content = await fs.readFile(inputPath, 'utf-8')
|
|
242
|
+
|
|
243
|
+
// Determine nports from filename
|
|
244
|
+
const nports = Touchstone.parsePorts(inputPath)
|
|
245
|
+
if (nports === null) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
`Cannot determine number of ports from filename: ${inputPath}`
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Parse the file
|
|
252
|
+
const ts = Touchstone.fromText(content, nports)
|
|
253
|
+
|
|
254
|
+
console.log(`Original format: ${ts.format}`)
|
|
255
|
+
|
|
256
|
+
// Convert format
|
|
257
|
+
ts.format = targetFormat
|
|
258
|
+
const convertedContent = ts.writeContent()
|
|
259
|
+
|
|
260
|
+
// Write output
|
|
261
|
+
await fs.writeFile(outputPath, convertedContent)
|
|
262
|
+
|
|
263
|
+
console.log(`Converted to ${targetFormat}`)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Usage
|
|
267
|
+
await convertTouchstone('input.s2p', 'output.s2p', 'DB')
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Example 3: S-Parameter Plotting (Any Framework)
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import { Touchstone, abs } from 'rf-touchstone'
|
|
274
|
+
|
|
275
|
+
async function getPlotData(url: string) {
|
|
276
|
+
const ts = await Touchstone.fromUrl(url)
|
|
277
|
+
|
|
278
|
+
const numFreqs = ts.frequency.f_scaled.length
|
|
279
|
+
const frequencies = ts.frequency.f_scaled
|
|
280
|
+
|
|
281
|
+
// Extract S21 magnitude in dB for all frequencies (2-port network)
|
|
282
|
+
const s21_dB: number[] = []
|
|
283
|
+
for (let freqIdx = 0; freqIdx < numFreqs; freqIdx++) {
|
|
284
|
+
const s21 = ts.matrix[1][0][freqIdx] // S21 for 2-port
|
|
285
|
+
const magnitude = abs(s21)
|
|
286
|
+
s21_dB.push(20 * Math.log10(magnitude))
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
frequencies,
|
|
291
|
+
s21_dB,
|
|
292
|
+
unit: ts.frequency.unit,
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Use with any chart library (Chart.js, D3, Plotly, etc.)
|
|
297
|
+
const data = await getPlotData('https://example.com/device.s2p')
|
|
298
|
+
console.log(data)
|
|
299
|
+
// { frequencies: [...], s21_dB: [...], unit: 'GHZ' }
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Example 4: Data Analysis
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { Touchstone, abs } from 'rf-touchstone'
|
|
306
|
+
|
|
307
|
+
async function analyzeTouchstone(file: File) {
|
|
308
|
+
const ts = await Touchstone.fromFile(file)
|
|
309
|
+
|
|
310
|
+
if (ts.nports !== 2) {
|
|
311
|
+
throw new Error('This example is for 2-port networks only')
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const numFreqs = ts.frequency.f_scaled.length
|
|
315
|
+
|
|
316
|
+
// Calculate insertion loss (S21)
|
|
317
|
+
let maxS21 = 0
|
|
318
|
+
for (let i = 0; i < numFreqs; i++) {
|
|
319
|
+
const mag = abs(ts.matrix[1][0][i]) // S21
|
|
320
|
+
if (mag > maxS21) maxS21 = mag
|
|
321
|
+
}
|
|
322
|
+
const insertionLoss_dB = -20 * Math.log10(maxS21)
|
|
323
|
+
|
|
324
|
+
// Calculate return loss (S11)
|
|
325
|
+
let maxS11 = 0
|
|
326
|
+
for (let i = 0; i < numFreqs; i++) {
|
|
327
|
+
const mag = abs(ts.matrix[0][0][i]) // S11
|
|
328
|
+
if (mag > maxS11) maxS11 = mag
|
|
329
|
+
}
|
|
330
|
+
const returnLoss_dB = -20 * Math.log10(maxS11)
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
nports: ts.nports,
|
|
334
|
+
numFrequencies: numFreqs,
|
|
335
|
+
frequencyRange: {
|
|
336
|
+
min: ts.frequency.f_scaled[0],
|
|
337
|
+
max: ts.frequency.f_scaled[numFreqs - 1],
|
|
338
|
+
unit: ts.frequency.unit,
|
|
339
|
+
},
|
|
340
|
+
insertionLoss_dB: insertionLoss_dB.toFixed(2),
|
|
341
|
+
returnLoss_dB: returnLoss_dB.toFixed(2),
|
|
342
|
+
format: ts.format,
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Example 5: Vue Component
|
|
348
|
+
|
|
349
|
+
```vue
|
|
350
|
+
<template>
|
|
351
|
+
<div>
|
|
352
|
+
<input type="file" @change="handleFileUpload" accept=".s*p" />
|
|
353
|
+
<div v-if="touchstone">
|
|
354
|
+
<h2>{{ touchstone.nports }}-Port Network</h2>
|
|
355
|
+
<p>Frequencies: {{ touchstone.frequency.f_scaled.length }}</p>
|
|
356
|
+
<p>Unit: {{ touchstone.frequency.unit }}</p>
|
|
357
|
+
<p>Format: {{ touchstone.format }}</p>
|
|
358
|
+
<button @click="convertToDb">Convert to dB</button>
|
|
359
|
+
<button @click="downloadFile">Download</button>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
</template>
|
|
363
|
+
|
|
364
|
+
<script setup lang="ts">
|
|
365
|
+
import { ref } from 'vue'
|
|
366
|
+
import { Touchstone } from 'rf-touchstone'
|
|
367
|
+
|
|
368
|
+
const touchstone = ref<Touchstone | null>(null)
|
|
369
|
+
|
|
370
|
+
async function handleFileUpload(event: Event) {
|
|
371
|
+
const file = (event.target as HTMLInputElement).files?.[0]
|
|
372
|
+
if (file) {
|
|
373
|
+
touchstone.value = await Touchstone.fromFile(file)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function convertToDb() {
|
|
378
|
+
if (touchstone.value) {
|
|
379
|
+
touchstone.value.format = 'DB'
|
|
380
|
+
// Re-parse to get converted matrix
|
|
381
|
+
const content = touchstone.value.writeContent()
|
|
382
|
+
touchstone.value = Touchstone.fromText(content, touchstone.value.nports!)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function downloadFile() {
|
|
387
|
+
if (touchstone.value) {
|
|
388
|
+
const content = touchstone.value.writeContent()
|
|
389
|
+
const blob = new Blob([content], { type: 'text/plain' })
|
|
390
|
+
const url = URL.createObjectURL(blob)
|
|
391
|
+
const a = document.createElement('a')
|
|
392
|
+
a.href = url
|
|
393
|
+
a.download = `converted.s${touchstone.value.nports}p`
|
|
394
|
+
a.click()
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
</script>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## TypeScript Types
|
|
401
|
+
|
|
402
|
+
### Key Interfaces and Types
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// Touchstone class
|
|
406
|
+
class Touchstone {
|
|
407
|
+
// Properties
|
|
408
|
+
nports?: number
|
|
409
|
+
frequency?: Frequency
|
|
410
|
+
format?: 'RI' | 'MA' | 'DB'
|
|
411
|
+
parameter?: 'S' | 'Y' | 'Z' | 'G' | 'H'
|
|
412
|
+
impedance: number | number[] // default: 50
|
|
413
|
+
matrix?: Complex[][][] // [outPort][inPort][freqIdx]
|
|
414
|
+
comments: string[]
|
|
415
|
+
|
|
416
|
+
// Static methods
|
|
417
|
+
static fromText(content: string, nports: number): Touchstone
|
|
418
|
+
static fromUrl(url: string, nports?: number): Promise<Touchstone>
|
|
419
|
+
static fromFile(file: File, nports?: number): Promise<Touchstone>
|
|
420
|
+
static getFilename(pathOrUrl: string): string
|
|
421
|
+
static parsePorts(filename: string): number | null
|
|
422
|
+
|
|
423
|
+
// Instance methods
|
|
424
|
+
readContent(content: string, nports: number): void
|
|
425
|
+
writeContent(): string
|
|
426
|
+
validate(): void
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Complex number (from mathjs)
|
|
430
|
+
interface Complex {
|
|
431
|
+
re: number // Real part
|
|
432
|
+
im: number // Imaginary part
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Frequency class
|
|
436
|
+
class Frequency {
|
|
437
|
+
unit: 'Hz' | 'kHz' | 'MHz' | 'GHz'
|
|
438
|
+
f_scaled: number[] // Frequency values in current unit
|
|
439
|
+
|
|
440
|
+
// Getters/setters for different units
|
|
441
|
+
f_Hz: number[]
|
|
442
|
+
f_kHz: number[]
|
|
443
|
+
f_MHz: number[]
|
|
444
|
+
f_GHz: number[]
|
|
445
|
+
f_THz: number[]
|
|
446
|
+
|
|
447
|
+
// Wavelength getters/setters
|
|
448
|
+
wavelength_m: number[]
|
|
449
|
+
wavelength_cm: number[]
|
|
450
|
+
wavelength_mm: number[]
|
|
451
|
+
wavelength_um: number[]
|
|
452
|
+
wavelength_nm: number[]
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Helper Functions (from mathjs)
|
|
456
|
+
// These can be imported: import { abs, arg, pi } from 'rf-touchstone'
|
|
457
|
+
function abs(x: Complex | number): number
|
|
458
|
+
function arg(x: Complex): number // Returns phase in radians
|
|
459
|
+
function complex(re: number, im: number): Complex
|
|
460
|
+
function complex(obj: { r: number; phi: number }): Complex // Polar form
|
|
461
|
+
const pi: number
|
|
462
|
+
|
|
463
|
+
// Other available helpers:
|
|
464
|
+
// add, multiply, pow, log10, round, range, index, subset
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Important Notes
|
|
468
|
+
|
|
469
|
+
### Module Formats Support
|
|
470
|
+
|
|
471
|
+
This library supports **multiple module formats** for maximum compatibility:
|
|
472
|
+
|
|
473
|
+
- **UMD (Universal Module Definition)**: `dist/Touchstone.umd.js` - For direct browser `<script>` tags
|
|
474
|
+
- **ESM (ES Modules)**: `dist/Touchstone.es.js` - For modern bundlers and `import` statements
|
|
475
|
+
- **CJS (CommonJS)**: `dist/Touchstone.cjs.js` - For older Node.js projects using `require()`
|
|
476
|
+
|
|
477
|
+
**Using in Browser (UMD):**
|
|
478
|
+
|
|
479
|
+
Via CDN:
|
|
480
|
+
|
|
481
|
+
```html
|
|
482
|
+
<!-- Using unpkg -->
|
|
483
|
+
<script src="https://unpkg.com/rf-touchstone"></script>
|
|
484
|
+
|
|
485
|
+
<!-- Or using jsDelivr -->
|
|
486
|
+
<script src="https://cdn.jsdelivr.net/npm/rf-touchstone"></script>
|
|
487
|
+
|
|
488
|
+
<script>
|
|
489
|
+
const { Touchstone, abs, arg } = window.Touchstone
|
|
490
|
+
</script>
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Using in Modern Projects (ESM):**
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
import { Touchstone } from 'rf-touchstone'
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Using in Legacy Node.js (CommonJS):**
|
|
500
|
+
|
|
501
|
+
```javascript
|
|
502
|
+
const { Touchstone } = require('rf-touchstone')
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Browser vs Node.js
|
|
506
|
+
|
|
507
|
+
- `Touchstone.fromUrl()` works in both environments (uses `fetch`)
|
|
508
|
+
- `Touchstone.fromFile()` is browser-only (uses `File` API)
|
|
509
|
+
- For Node.js file reading, use `fs` and `Touchstone.fromText()`
|
|
510
|
+
|
|
511
|
+
### Port Indexing
|
|
512
|
+
|
|
513
|
+
**Matrix indexing is consistent for ALL port counts:**
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
// General pattern: Sij = matrix[i-1][j-1][freqIdx]
|
|
517
|
+
|
|
518
|
+
// For any N-port network:
|
|
519
|
+
const s11 = matrix[0][0][freqIdx] // S11
|
|
520
|
+
const s21 = matrix[1][0][freqIdx] // S21
|
|
521
|
+
const s12 = matrix[0][1][freqIdx] // S12
|
|
522
|
+
const s22 = matrix[1][1][freqIdx] // S22
|
|
523
|
+
// etc.
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Memory Considerations
|
|
527
|
+
|
|
528
|
+
Large .snp files (many ports/frequencies) create large matrices:
|
|
529
|
+
|
|
530
|
+
- Processing in chunks
|
|
531
|
+
- Not storing multiple copies simultaneously
|
|
532
|
+
|
|
533
|
+
## Error Handling
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
try {
|
|
537
|
+
const ts = await Touchstone.fromUrl('device.s2p')
|
|
538
|
+
} catch (error) {
|
|
539
|
+
if (error instanceof Error) {
|
|
540
|
+
// Common errors:
|
|
541
|
+
// - Network errors (URL fetch failed)
|
|
542
|
+
// - Parse errors (invalid Touchstone format)
|
|
543
|
+
// - File not found
|
|
544
|
+
// - Could not determine nports
|
|
545
|
+
console.error('Failed to load Touchstone:', error.message)
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## Resources
|
|
551
|
+
|
|
552
|
+
- **API Documentation**: https://panz2018.github.io/RF-Touchstone/api/modules
|
|
553
|
+
- **GitHub**: https://github.com/panz2018/RF-Touchstone
|
|
554
|
+
- **NPM**: https://www.npmjs.com/package/rf-touchstone
|
|
555
|
+
|
|
556
|
+
## Common Patterns Summary
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
// ✅ DO: Load from URL
|
|
560
|
+
const ts = await Touchstone.fromUrl('file.s2p')
|
|
561
|
+
|
|
562
|
+
// ✅ DO: Access matrix correctly (same for all port counts)
|
|
563
|
+
const s21 = ts.matrix[1][0][freqIdx] // S21
|
|
564
|
+
|
|
565
|
+
// ✅ DO: Convert format properly
|
|
566
|
+
ts.format = 'DB'
|
|
567
|
+
const dbContent = ts.writeContent()
|
|
568
|
+
|
|
569
|
+
// ✅ DO: Iterate frequencies correctly
|
|
570
|
+
for (let freqIdx = 0; freqIdx < ts.frequency.f_scaled.length; freqIdx++) {
|
|
571
|
+
const s11 = ts.matrix[0][0][freqIdx]
|
|
572
|
+
// process s11...
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ❌ DON'T: Use non-existent convertFormat method
|
|
576
|
+
// const tsDB = ts.convertFormat('DB') // This doesn't exist!
|
|
577
|
+
|
|
578
|
+
// ❌ DON'T: Use wrong matrix indexing
|
|
579
|
+
// const s11 = ts.matrix[freqIdx][0][0] // WRONG!
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
**Last Updated**: 2026-01-25
|
|
585
|
+
**Library Version**: 0.0.5+
|
package/dist/Touchstone.es.js
CHANGED
|
@@ -8834,10 +8834,11 @@ class nu {
|
|
|
8834
8834
|
frequency;
|
|
8835
8835
|
/**
|
|
8836
8836
|
* 3D array to store the network parameter data
|
|
8837
|
-
* -
|
|
8838
|
-
* -
|
|
8839
|
-
* -
|
|
8840
|
-
*
|
|
8837
|
+
* - First dimension [i]: Output port index (0 to nports-1) - where signal exits
|
|
8838
|
+
* - Second dimension [j]: Input port index (0 to nports-1) - where signal enters
|
|
8839
|
+
* - Third dimension [k]: Frequency index
|
|
8840
|
+
*
|
|
8841
|
+
* For example: matrix[i][j][k] is the parameter from port j+1 to port i+1 at frequency k
|
|
8841
8842
|
*/
|
|
8842
8843
|
_matrix;
|
|
8843
8844
|
/**
|
|
@@ -8858,17 +8859,21 @@ class nu {
|
|
|
8858
8859
|
*
|
|
8859
8860
|
* @remarks
|
|
8860
8861
|
* Matrix Structure:
|
|
8861
|
-
* - First dimension [i]: Output
|
|
8862
|
-
* - Second dimension [j]: Input
|
|
8862
|
+
* - First dimension [i]: Output port index (0 to nports-1) - where the signal exits
|
|
8863
|
+
* - Second dimension [j]: Input port index (0 to nports-1) - where the signal enters
|
|
8863
8864
|
* - Third dimension [k]: Frequency point index
|
|
8864
8865
|
*
|
|
8865
|
-
*
|
|
8866
|
-
*
|
|
8867
|
-
*
|
|
8866
|
+
* @example
|
|
8867
|
+
* ```typescript
|
|
8868
|
+
* // For any N-port network (2-port, 4-port, etc.):
|
|
8869
|
+
* const s11 = touchstone.matrix[0][0][freqIdx] // S11: port 1 → port 1
|
|
8870
|
+
* const s21 = touchstone.matrix[1][0][freqIdx] // S21: port 1 → port 2
|
|
8871
|
+
* const s12 = touchstone.matrix[0][1][freqIdx] // S12: port 2 → port 1
|
|
8872
|
+
* const s22 = touchstone.matrix[1][1][freqIdx] // S22: port 2 → port 2
|
|
8868
8873
|
*
|
|
8869
|
-
*
|
|
8870
|
-
*
|
|
8871
|
-
*
|
|
8874
|
+
* // General pattern: Sij = matrix[i-1][j-1][freqIdx]
|
|
8875
|
+
* // where i is the output port number, j is the input port number
|
|
8876
|
+
* ```
|
|
8872
8877
|
*
|
|
8873
8878
|
* @returns The current network parameter matrix, or undefined if not set
|
|
8874
8879
|
*/
|
package/dist/touchstone.d.ts
CHANGED
|
@@ -46,17 +46,23 @@ export type TouchstoneImpedance = number | number[];
|
|
|
46
46
|
*
|
|
47
47
|
* @remarks
|
|
48
48
|
* The matrix is a 3D array with the following dimensions:
|
|
49
|
-
* - First dimension:
|
|
50
|
-
* - Second dimension:
|
|
51
|
-
* - Third dimension:
|
|
49
|
+
* - First dimension [i]: Output port index (0 to nports-1) - where the signal exits
|
|
50
|
+
* - Second dimension [j]: Input port index (0 to nports-1) - where the signal enters
|
|
51
|
+
* - Third dimension [k]: Frequency point index
|
|
52
52
|
*
|
|
53
53
|
* For example:
|
|
54
|
-
* - matrix[i][j][k] represents the parameter from port j+1 to port i+1 at frequency k
|
|
55
|
-
* - For S-parameters: matrix[1][0][5] is S₂₁ at the 6th frequency point
|
|
54
|
+
* - `matrix[i][j][k]` represents the parameter from port j+1 to port i+1 at frequency k
|
|
55
|
+
* - For S-parameters: `matrix[1][0][5]` is S₂₁ at the 6th frequency point
|
|
56
|
+
* (signal enters at port 1, exits at port 2)
|
|
56
57
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* // Access S21 (port 1 → port 2) at first frequency
|
|
61
|
+
* const s21 = touchstone.matrix[1][0][0]
|
|
62
|
+
*
|
|
63
|
+
* // Access S11 (port 1 → port 1, reflection) at first frequency
|
|
64
|
+
* const s11 = touchstone.matrix[0][0][0]
|
|
65
|
+
* ```
|
|
60
66
|
*/
|
|
61
67
|
export type TouchstoneMatrix = Complex[][][];
|
|
62
68
|
/**
|
|
@@ -249,10 +255,11 @@ export declare class Touchstone {
|
|
|
249
255
|
frequency: Frequency | undefined;
|
|
250
256
|
/**
|
|
251
257
|
* 3D array to store the network parameter data
|
|
252
|
-
* -
|
|
253
|
-
* -
|
|
254
|
-
* -
|
|
255
|
-
*
|
|
258
|
+
* - First dimension [i]: Output port index (0 to nports-1) - where signal exits
|
|
259
|
+
* - Second dimension [j]: Input port index (0 to nports-1) - where signal enters
|
|
260
|
+
* - Third dimension [k]: Frequency index
|
|
261
|
+
*
|
|
262
|
+
* For example: matrix[i][j][k] is the parameter from port j+1 to port i+1 at frequency k
|
|
256
263
|
*/
|
|
257
264
|
private _matrix;
|
|
258
265
|
/**
|
|
@@ -267,17 +274,21 @@ export declare class Touchstone {
|
|
|
267
274
|
*
|
|
268
275
|
* @remarks
|
|
269
276
|
* Matrix Structure:
|
|
270
|
-
* - First dimension [i]: Output
|
|
271
|
-
* - Second dimension [j]: Input
|
|
277
|
+
* - First dimension [i]: Output port index (0 to nports-1) - where the signal exits
|
|
278
|
+
* - Second dimension [j]: Input port index (0 to nports-1) - where the signal enters
|
|
272
279
|
* - Third dimension [k]: Frequency point index
|
|
273
280
|
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* // For any N-port network (2-port, 4-port, etc.):
|
|
284
|
+
* const s11 = touchstone.matrix[0][0][freqIdx] // S11: port 1 → port 1
|
|
285
|
+
* const s21 = touchstone.matrix[1][0][freqIdx] // S21: port 1 → port 2
|
|
286
|
+
* const s12 = touchstone.matrix[0][1][freqIdx] // S12: port 2 → port 1
|
|
287
|
+
* const s22 = touchstone.matrix[1][1][freqIdx] // S22: port 2 → port 2
|
|
277
288
|
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
289
|
+
* // General pattern: Sij = matrix[i-1][j-1][freqIdx]
|
|
290
|
+
* // where i is the output port number, j is the input port number
|
|
291
|
+
* ```
|
|
281
292
|
*
|
|
282
293
|
* @returns The current network parameter matrix, or undefined if not set
|
|
283
294
|
*/
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rf-touchstone",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"description": "A Javascript/TypeScript library for reading, manipulating, and writing Touchstone files (.snp files) used in radio frequency (RF) and microwave engineering.",
|
|
6
6
|
"main": "dist/Touchstone.cjs.js",
|
|
7
7
|
"module": "dist/Touchstone.es.js",
|
|
8
|
+
"browser": "dist/Touchstone.umd.js",
|
|
9
|
+
"unpkg": "dist/Touchstone.umd.js",
|
|
10
|
+
"jsdelivr": "dist/Touchstone.umd.js",
|
|
8
11
|
"types": "dist/index.d.ts",
|
|
9
12
|
"exports": {
|
|
10
13
|
".": {
|
|
11
14
|
"types": "./dist/index.d.ts",
|
|
15
|
+
"browser": "./dist/Touchstone.umd.js",
|
|
12
16
|
"import": "./dist/Touchstone.es.js",
|
|
13
17
|
"require": "./dist/Touchstone.cjs.js"
|
|
14
18
|
}
|
|
@@ -18,7 +22,8 @@
|
|
|
18
22
|
"./src",
|
|
19
23
|
"./readme.md",
|
|
20
24
|
"./LICENSE.md",
|
|
21
|
-
"./coverage/coverage-badge.svg"
|
|
25
|
+
"./coverage/coverage-badge.svg",
|
|
26
|
+
"./.github/AI_USAGE_GUIDE.md"
|
|
22
27
|
],
|
|
23
28
|
"license": "MIT",
|
|
24
29
|
"author": "https://github.com/panz2018",
|
package/readme.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A Javascript/TypeScript library for reading, manipulating, and writing Touchstone files (.snp files) used in radio frequency (RF) and microwave engineering.
|
|
4
4
|
|
|
5
|
+
<a href="https://deepwiki.com/panz2018/RF-Touchstone" target="_blank"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki" align="right"></a>
|
|
6
|
+
|
|
5
7
|
[](https://github.com/panz2018/RF-Touchstone/actions/workflows/test.yml)
|
|
6
8
|
[](coverage/coverage-badge.svg)
|
|
7
9
|
|
|
@@ -35,12 +37,29 @@ RF-Touchstone currently supports versions 1.0 and 1.1 of the Touchstone specific
|
|
|
35
37
|
|
|
36
38
|
## Installation
|
|
37
39
|
|
|
40
|
+
### Via NPM/Yarn
|
|
41
|
+
|
|
38
42
|
```bash
|
|
39
43
|
npm install rf-touchstone
|
|
40
44
|
# or
|
|
41
45
|
yarn add rf-touchstone
|
|
42
46
|
```
|
|
43
47
|
|
|
48
|
+
### Via CDN (Browser)
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<!-- Using unpkg -->
|
|
52
|
+
<script src="https://unpkg.com/rf-touchstone"></script>
|
|
53
|
+
|
|
54
|
+
<!-- Or using jsDelivr -->
|
|
55
|
+
<script src="https://cdn.jsdelivr.net/npm/rf-touchstone"></script>
|
|
56
|
+
|
|
57
|
+
<script>
|
|
58
|
+
// Access via global variable
|
|
59
|
+
const { Touchstone } = window.Touchstone
|
|
60
|
+
</script>
|
|
61
|
+
```
|
|
62
|
+
|
|
44
63
|
## Quick Start
|
|
45
64
|
|
|
46
65
|
```typescript
|
|
@@ -83,6 +102,7 @@ This repository includes examples to demonstrate the usage of `rf-touchstone` in
|
|
|
83
102
|
For detailed documentation, please visit:
|
|
84
103
|
|
|
85
104
|
- [Project Website](https://panz2018.github.io/RF-Touchstone/)
|
|
105
|
+
- [AI Usage Guide](https://github.com/panz2018/RF-Touchstone/blob/main/.github/AI_USAGE_GUIDE.md) - Comprehensive guide for AI coding assistants and developers integrating this library
|
|
86
106
|
- [DeepWiki Documentation](https://deepwiki.com/panz2018/RF-Touchstone) - Interactive AI documentation that allows you to converse and ask questions about this repository
|
|
87
107
|
- [Changelog](https://github.com/panz2018/RF-Touchstone/releases)
|
|
88
108
|
|
package/src/touchstone.ts
CHANGED
|
@@ -67,17 +67,23 @@ export type TouchstoneImpedance = number | number[]
|
|
|
67
67
|
*
|
|
68
68
|
* @remarks
|
|
69
69
|
* The matrix is a 3D array with the following dimensions:
|
|
70
|
-
* - First dimension:
|
|
71
|
-
* - Second dimension:
|
|
72
|
-
* - Third dimension:
|
|
70
|
+
* - First dimension [i]: Output port index (0 to nports-1) - where the signal exits
|
|
71
|
+
* - Second dimension [j]: Input port index (0 to nports-1) - where the signal enters
|
|
72
|
+
* - Third dimension [k]: Frequency point index
|
|
73
73
|
*
|
|
74
74
|
* For example:
|
|
75
|
-
* - matrix[i][j][k] represents the parameter from port j+1 to port i+1 at frequency k
|
|
76
|
-
* - For S-parameters: matrix[1][0][5] is S₂₁ at the 6th frequency point
|
|
75
|
+
* - `matrix[i][j][k]` represents the parameter from port j+1 to port i+1 at frequency k
|
|
76
|
+
* - For S-parameters: `matrix[1][0][5]` is S₂₁ at the 6th frequency point
|
|
77
|
+
* (signal enters at port 1, exits at port 2)
|
|
77
78
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // Access S21 (port 1 → port 2) at first frequency
|
|
82
|
+
* const s21 = touchstone.matrix[1][0][0]
|
|
83
|
+
*
|
|
84
|
+
* // Access S11 (port 1 → port 1, reflection) at first frequency
|
|
85
|
+
* const s11 = touchstone.matrix[0][0][0]
|
|
86
|
+
* ```
|
|
81
87
|
*/
|
|
82
88
|
export type TouchstoneMatrix = Complex[][][]
|
|
83
89
|
|
|
@@ -457,10 +463,11 @@ export class Touchstone {
|
|
|
457
463
|
|
|
458
464
|
/**
|
|
459
465
|
* 3D array to store the network parameter data
|
|
460
|
-
* -
|
|
461
|
-
* -
|
|
462
|
-
* -
|
|
463
|
-
*
|
|
466
|
+
* - First dimension [i]: Output port index (0 to nports-1) - where signal exits
|
|
467
|
+
* - Second dimension [j]: Input port index (0 to nports-1) - where signal enters
|
|
468
|
+
* - Third dimension [k]: Frequency index
|
|
469
|
+
*
|
|
470
|
+
* For example: matrix[i][j][k] is the parameter from port j+1 to port i+1 at frequency k
|
|
464
471
|
*/
|
|
465
472
|
private _matrix: TouchstoneMatrix | undefined
|
|
466
473
|
|
|
@@ -483,17 +490,21 @@ export class Touchstone {
|
|
|
483
490
|
*
|
|
484
491
|
* @remarks
|
|
485
492
|
* Matrix Structure:
|
|
486
|
-
* - First dimension [i]: Output
|
|
487
|
-
* - Second dimension [j]: Input
|
|
493
|
+
* - First dimension [i]: Output port index (0 to nports-1) - where the signal exits
|
|
494
|
+
* - Second dimension [j]: Input port index (0 to nports-1) - where the signal enters
|
|
488
495
|
* - Third dimension [k]: Frequency point index
|
|
489
496
|
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
497
|
+
* @example
|
|
498
|
+
* ```typescript
|
|
499
|
+
* // For any N-port network (2-port, 4-port, etc.):
|
|
500
|
+
* const s11 = touchstone.matrix[0][0][freqIdx] // S11: port 1 → port 1
|
|
501
|
+
* const s21 = touchstone.matrix[1][0][freqIdx] // S21: port 1 → port 2
|
|
502
|
+
* const s12 = touchstone.matrix[0][1][freqIdx] // S12: port 2 → port 1
|
|
503
|
+
* const s22 = touchstone.matrix[1][1][freqIdx] // S22: port 2 → port 2
|
|
493
504
|
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
496
|
-
*
|
|
505
|
+
* // General pattern: Sij = matrix[i-1][j-1][freqIdx]
|
|
506
|
+
* // where i is the output port number, j is the input port number
|
|
507
|
+
* ```
|
|
497
508
|
*
|
|
498
509
|
* @returns The current network parameter matrix, or undefined if not set
|
|
499
510
|
*/
|