rf-touchstone 0.0.5 → 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.
- package/.github/AI_USAGE_GUIDE.md +128 -12
- package/coverage/coverage-badge.svg +4 -4
- package/dist/Touchstone.cjs.js +1 -1
- package/dist/Touchstone.es.js +92 -21
- package/dist/Touchstone.umd.js +1 -1
- package/dist/frequency.d.ts +33 -1
- package/dist/touchstone.d.ts +43 -1
- package/package.json +1 -1
- package/readme.md +7 -2
- package/src/frequency.ts +33 -1
- package/src/touchstone.ts +72 -4
|
@@ -31,22 +31,22 @@ import { Touchstone } from 'rf-touchstone'
|
|
|
31
31
|
import { abs, arg, pi, complex } from 'rf-touchstone'
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
### 1. **Touchstone Class** - Main API Entry Point
|
|
34
|
+
## Touchstone Class
|
|
37
35
|
|
|
38
36
|
The `Touchstone` class is the primary interface for working with .snp files.
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
### Creating Touchstone Objects
|
|
41
39
|
|
|
42
40
|
**From URL (Browser or Node.js with fetch):**
|
|
43
41
|
|
|
44
42
|
```typescript
|
|
45
43
|
// Auto-detect nports from filename (.s2p → 2-port)
|
|
46
44
|
const ts = await Touchstone.fromUrl('https://example.com/device.s2p')
|
|
45
|
+
console.log(ts.name) // 'device' - automatically extracted from filename
|
|
47
46
|
|
|
48
47
|
// Explicit nports if filename doesn't indicate
|
|
49
48
|
const ts = await Touchstone.fromUrl('https://example.com/data.txt', 2)
|
|
49
|
+
console.log(ts.name) // 'data' - basename without extension
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
**From File Object (Browser file input):**
|
|
@@ -55,6 +55,7 @@ const ts = await Touchstone.fromUrl('https://example.com/data.txt', 2)
|
|
|
55
55
|
// In any framework with file input
|
|
56
56
|
const handleFileUpload = async (file: File) => {
|
|
57
57
|
const touchstone = await Touchstone.fromFile(file)
|
|
58
|
+
console.log(touchstone.name) // Filename without extension
|
|
58
59
|
// Use touchstone object
|
|
59
60
|
}
|
|
60
61
|
```
|
|
@@ -71,16 +72,17 @@ const s2pContent = `! 2-port S-parameter data
|
|
|
71
72
|
2.0 0.45 -60 0.90 15 0.10 80 0.55 -45
|
|
72
73
|
3.0 0.40 -75 0.85 20 0.15 75 0.50 -60`
|
|
73
74
|
|
|
74
|
-
// Parse the text content
|
|
75
|
-
const touchstone = Touchstone.fromText(s2pContent, 2) // 2 = 2-port network
|
|
75
|
+
// Parse the text content with optional name
|
|
76
|
+
const touchstone = Touchstone.fromText(s2pContent, 2, 'my_measurement') // 2 = 2-port network
|
|
76
77
|
|
|
77
78
|
// Now you can use the parsed data
|
|
79
|
+
console.log(touchstone.name) // 'my_measurement'
|
|
78
80
|
console.log(touchstone.frequency.f_scaled) // [1.0, 2.0, 3.0]
|
|
79
81
|
console.log(touchstone.format) // 'MA'
|
|
80
82
|
console.log(touchstone.parameter) // 'S'
|
|
81
83
|
```
|
|
82
84
|
|
|
83
|
-
###
|
|
85
|
+
### Accessing Touchstone Data
|
|
84
86
|
|
|
85
87
|
```typescript
|
|
86
88
|
const ts = await Touchstone.fromUrl('device.s2p')
|
|
@@ -89,6 +91,10 @@ const ts = await Touchstone.fromUrl('device.s2p')
|
|
|
89
91
|
const frequencies = ts.frequency.f_scaled // Array of numbers in specified unit
|
|
90
92
|
const unit = ts.frequency.unit // 'Hz' | 'kHz' | 'MHz' | 'GHz'
|
|
91
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
|
+
|
|
92
98
|
// Options line data
|
|
93
99
|
const format = ts.format // 'RI' | 'MA' | 'DB'
|
|
94
100
|
const parameter = ts.parameter // 'S' | 'Y' | 'Z' | 'G' | 'H'
|
|
@@ -100,7 +106,7 @@ const nports = ts.nports // Number of ports (1, 2, 3, 4, etc.)
|
|
|
100
106
|
const matrix = ts.matrix // Complex number matrix
|
|
101
107
|
```
|
|
102
108
|
|
|
103
|
-
###
|
|
109
|
+
### Understanding Matrix Indexing ⚠️ CRITICAL
|
|
104
110
|
|
|
105
111
|
**Matrix Structure:**
|
|
106
112
|
|
|
@@ -133,7 +139,7 @@ const s12 = ts.matrix[0][1][freqIdx] // S12
|
|
|
133
139
|
// General pattern: Sij = ts.matrix[i-1][j-1][freqIdx]
|
|
134
140
|
```
|
|
135
141
|
|
|
136
|
-
###
|
|
142
|
+
### Format Conversion
|
|
137
143
|
|
|
138
144
|
To convert between formats (RI, MA, DB):
|
|
139
145
|
|
|
@@ -149,7 +155,7 @@ const dbContent = ts.writeContent() // Generate new file content
|
|
|
149
155
|
const dbTs = Touchstone.fromText(dbContent, ts.nports)
|
|
150
156
|
```
|
|
151
157
|
|
|
152
|
-
###
|
|
158
|
+
### Writing Touchstone Content
|
|
153
159
|
|
|
154
160
|
```typescript
|
|
155
161
|
const ts = await Touchstone.fromUrl('device.s2p')
|
|
@@ -170,7 +176,7 @@ import fs from 'fs/promises'
|
|
|
170
176
|
await fs.writeFile('modified-device.s2p', content)
|
|
171
177
|
```
|
|
172
178
|
|
|
173
|
-
###
|
|
179
|
+
### Working with Complex Numbers
|
|
174
180
|
|
|
175
181
|
The library uses mathjs Complex type `{ re: number, im: number }`:
|
|
176
182
|
|
|
@@ -193,6 +199,114 @@ const magnitudeDB = 20 * Math.log10(magnitude)
|
|
|
193
199
|
const phaseDegrees = (phase * 180) / pi
|
|
194
200
|
```
|
|
195
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
|
+
|
|
196
310
|
## Common Use Cases
|
|
197
311
|
|
|
198
312
|
### Example 1: File Upload in Browser (Vanilla JavaScript)
|
|
@@ -405,6 +519,7 @@ function downloadFile() {
|
|
|
405
519
|
// Touchstone class
|
|
406
520
|
class Touchstone {
|
|
407
521
|
// Properties
|
|
522
|
+
name?: string // Filename without extension, used for legends and default filenames
|
|
408
523
|
nports?: number
|
|
409
524
|
frequency?: Frequency
|
|
410
525
|
format?: 'RI' | 'MA' | 'DB'
|
|
@@ -414,10 +529,11 @@ class Touchstone {
|
|
|
414
529
|
comments: string[]
|
|
415
530
|
|
|
416
531
|
// Static methods
|
|
417
|
-
static fromText(content: string, nports: number): Touchstone
|
|
532
|
+
static fromText(content: string, nports: number, name?: string): Touchstone
|
|
418
533
|
static fromUrl(url: string, nports?: number): Promise<Touchstone>
|
|
419
534
|
static fromFile(file: File, nports?: number): Promise<Touchstone>
|
|
420
535
|
static getFilename(pathOrUrl: string): string
|
|
536
|
+
static getBasename(filenameOrPath: string): string
|
|
421
537
|
static parsePorts(filename: string): number | null
|
|
422
538
|
|
|
423
539
|
// Instance methods
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<svg width="103.3" height="20" viewBox="0 0 1033 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="coverage: 100%">
|
|
2
2
|
<title>coverage: 100%</title>
|
|
3
|
-
<linearGradient id="
|
|
3
|
+
<linearGradient id="cFjNt" x2="0" y2="100%">
|
|
4
4
|
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
|
|
5
5
|
<stop offset="1" stop-opacity=".1"/>
|
|
6
6
|
</linearGradient>
|
|
7
|
-
<mask id="
|
|
8
|
-
<g mask="url(#
|
|
7
|
+
<mask id="ZbOuS"><rect width="1033" height="200" rx="30" fill="#FFF"/></mask>
|
|
8
|
+
<g mask="url(#ZbOuS)">
|
|
9
9
|
<rect width="603" height="200" fill="#555"/>
|
|
10
10
|
<rect width="430" height="200" fill="#49c31a" x="603"/>
|
|
11
|
-
<rect width="1033" height="200" fill="url(#
|
|
11
|
+
<rect width="1033" height="200" fill="url(#cFjNt)"/>
|
|
12
12
|
</g>
|
|
13
13
|
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
|
|
14
14
|
<text x="60" y="148" textLength="503" fill="#000" opacity="0.25">coverage</text>
|