smithkit 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +481 -2
- package/dist/lib/Smith.d.ts +103 -0
- package/dist/lib/Smith.js +551 -0
- package/dist/lib/SmithArcsDefs.d.ts +10 -0
- package/dist/lib/SmithArcsDefs.js +10 -0
- package/dist/lib/SmithConstantCircle.d.ts +62 -0
- package/dist/lib/SmithConstantCircle.js +222 -0
- package/dist/lib/arcs/Tick.d.ts +33 -0
- package/dist/lib/arcs/Tick.js +40 -0
- package/dist/lib/complex/Complex.d.ts +130 -0
- package/dist/lib/complex/Complex.js +636 -0
- package/dist/lib/components.d.ts +14 -0
- package/dist/lib/components.js +26 -0
- package/dist/lib/draw/ConstCircles.d.ts +61 -0
- package/dist/lib/draw/ConstCircles.js +151 -0
- package/dist/lib/draw/ConstConductance.d.ts +4 -0
- package/dist/lib/draw/ConstConductance.js +6 -0
- package/dist/lib/draw/ConstQCircles.d.ts +29 -0
- package/dist/lib/draw/ConstQCircles.js +86 -0
- package/dist/lib/draw/ConstReactance.d.ts +4 -0
- package/dist/lib/draw/ConstReactance.js +6 -0
- package/dist/lib/draw/ConstResistance.d.ts +4 -0
- package/dist/lib/draw/ConstResistance.js +6 -0
- package/dist/lib/draw/ConstSusceptance.d.ts +4 -0
- package/dist/lib/draw/ConstSusceptance.js +6 -0
- package/dist/lib/draw/ConstSwrCircles.d.ts +29 -0
- package/dist/lib/draw/ConstSwrCircles.js +85 -0
- package/dist/lib/draw/MouseGesture.d.ts +6 -0
- package/dist/lib/draw/MouseGesture.js +26 -0
- package/dist/lib/draw/SmithArc.d.ts +10 -0
- package/dist/lib/draw/SmithArc.js +26 -0
- package/dist/lib/draw/SmithCircle.d.ts +10 -0
- package/dist/lib/draw/SmithCircle.js +24 -0
- package/dist/lib/draw/SmithCursor.d.ts +49 -0
- package/dist/lib/draw/SmithCursor.js +186 -0
- package/dist/lib/draw/SmithData.d.ts +66 -0
- package/dist/lib/draw/SmithData.js +366 -0
- package/dist/lib/draw/SmithDrawOptions.d.ts +5 -0
- package/dist/lib/draw/SmithDrawOptions.js +1 -0
- package/dist/lib/draw/SmithGridLayer.d.ts +19 -0
- package/dist/lib/draw/SmithGridLayer.js +48 -0
- package/dist/lib/draw/SmithGroup.d.ts +11 -0
- package/dist/lib/draw/SmithGroup.js +31 -0
- package/dist/lib/draw/SmithLine.d.ts +11 -0
- package/dist/lib/draw/SmithLine.js +27 -0
- package/dist/lib/draw/SmithMarker.d.ts +25 -0
- package/dist/lib/draw/SmithMarker.js +122 -0
- package/dist/lib/draw/SmithPath.d.ts +8 -0
- package/dist/lib/draw/SmithPath.js +20 -0
- package/dist/lib/draw/SmithScaler.d.ts +19 -0
- package/dist/lib/draw/SmithScaler.js +48 -0
- package/dist/lib/draw/SmithShape.d.ts +15 -0
- package/dist/lib/draw/SmithShape.js +42 -0
- package/dist/lib/draw/SmithSvg.d.ts +9 -0
- package/dist/lib/draw/SmithSvg.js +27 -0
- package/dist/lib/draw/SmithText.d.ts +22 -0
- package/dist/lib/draw/SmithText.js +56 -0
- package/dist/lib/formatting.d.ts +7 -0
- package/dist/lib/formatting.js +16 -0
- package/dist/lib/grid/definitions.d.ts +3 -0
- package/dist/lib/grid/definitions.js +123 -0
- package/dist/lib/grid/geometry.d.ts +20 -0
- package/dist/lib/grid/geometry.js +55 -0
- package/dist/lib/grid/labels.d.ts +3 -0
- package/dist/lib/grid/labels.js +41 -0
- package/dist/lib/grid/types.d.ts +30 -0
- package/dist/lib/grid/types.js +6 -0
- package/dist/lib/index.d.ts +16 -0
- package/dist/lib/index.js +9 -0
- package/dist/lib/io/touchstone.d.ts +7 -0
- package/dist/lib/io/touchstone.js +72 -0
- package/dist/lib/layers.d.ts +48 -0
- package/dist/lib/layers.js +73 -0
- package/dist/lib/math/complexRatio.d.ts +3 -0
- package/dist/lib/math/complexRatio.js +30 -0
- package/dist/lib/measurements.d.ts +53 -0
- package/dist/lib/measurements.js +19 -0
- package/dist/lib/renormalization.d.ts +10 -0
- package/dist/lib/renormalization.js +62 -0
- package/dist/lib/rf.d.ts +35 -0
- package/dist/lib/rf.js +74 -0
- package/dist/lib/samples.d.ts +6 -0
- package/dist/lib/samples.js +1 -0
- package/dist/lib/scales/SmithPeripheralScales.d.ts +10 -0
- package/dist/lib/scales/SmithPeripheralScales.js +124 -0
- package/dist/lib/scales/SmithScales.d.ts +16 -0
- package/dist/lib/scales/SmithScales.js +134 -0
- package/dist/lib/scales/peripheralScales.d.ts +12 -0
- package/dist/lib/scales/peripheralScales.js +49 -0
- package/dist/lib/scales/radialScales.d.ts +12 -0
- package/dist/lib/scales/radialScales.js +109 -0
- package/dist/lib/shapes/Arc.d.ts +6 -0
- package/dist/lib/shapes/Arc.js +1 -0
- package/dist/lib/shapes/Circle.d.ts +5 -0
- package/dist/lib/shapes/Circle.js +1 -0
- package/dist/lib/shapes/Line.d.ts +5 -0
- package/dist/lib/shapes/Line.js +1 -0
- package/dist/lib/shapes/Point.d.ts +1 -0
- package/dist/lib/shapes/Point.js +1 -0
- package/package.json +76 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Krzysztof Rosiński
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,482 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Smithkit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**`smithkit`** is a framework-independent JavaScript and TypeScript library for
|
|
4
|
+
interactive SVG Smith charts, RF calculations, and S11 measurement exploration.
|
|
5
|
+
|
|
6
|
+
[Try the interactive demo](https://rosek86.github.io/smithkit/).
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Labeled resistance, reactance, conductance, and susceptance grids.
|
|
11
|
+
- Configurable constant-Q and constant-VSWR circles.
|
|
12
|
+
- Zoom, pan, view reset, and cursor readings.
|
|
13
|
+
- Named traces with colors, visibility, and draggable markers that snap to samples.
|
|
14
|
+
- Marker comparisons: frequency, complex impedance, and wrapped phase differences.
|
|
15
|
+
- Physical impedance/admittance conversions and DOM-independent RF readings.
|
|
16
|
+
- Twelve parameter scales mounted independently of chart zoom.
|
|
17
|
+
- Four peripheral rulers for phase and electrical length.
|
|
18
|
+
- One-port Touchstone 1.x import with RI, MA, and DB representations.
|
|
19
|
+
- ESM modules and TypeScript declarations.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
After publication, install the first release, **0.1.0**, with:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm install smithkit@0.1.0
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Until publication, build and install an archive from source:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
git clone https://github.com/rosek86/smithkit.git
|
|
33
|
+
cd smithkit
|
|
34
|
+
npm ci
|
|
35
|
+
npm pack
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Install the resulting archive in your application:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
npm install /path/to/smithkit/smithkit-0.1.0.tgz
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Building requires Node.js 24 or later. Browser applications need SVG, ES2022,
|
|
45
|
+
and an ESM-compatible build tool. No library stylesheet is required.
|
|
46
|
+
|
|
47
|
+
For a complete application using the installed package, see the
|
|
48
|
+
[standalone integration example](https://github.com/rosek86/smithkit/tree/main/examples/basic).
|
|
49
|
+
It demonstrates mounting, replacing samples, event subscriptions, resizing, and cleanup.
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
|
|
53
|
+
Give the container an explicit size and run the code after it is mounted:
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<div id="smith" style="width: 100%; max-width: 700px; aspect-ratio: 1;"></div>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { Smith } from 'smithkit';
|
|
61
|
+
|
|
62
|
+
const chart = new Smith(50); // Reference impedance in ohms; defaults to 50.
|
|
63
|
+
chart.draw('#smith');
|
|
64
|
+
const traceId = chart.addTrace(
|
|
65
|
+
[
|
|
66
|
+
{ frequencyHz: 1e9, reflectionCoefficient: [0.5, -0.5] },
|
|
67
|
+
{ frequencyHz: 1.5e9, reflectionCoefficient: [0, 0] },
|
|
68
|
+
{ frequencyHz: 2e9, reflectionCoefficient: [0.5, 0.5] },
|
|
69
|
+
],
|
|
70
|
+
{ name: 'Antenna', color: '#2563eb' },
|
|
71
|
+
);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Each `TraceSample` contains `frequencyHz` and the dimensionless voltage reflection
|
|
75
|
+
coefficient Γ as `[real, imaginary]`. Samples are copied and validated; readonly tuples and arrays (`as const`) are accepted. Frequencies
|
|
76
|
+
must be finite and non-negative; both coordinates must be finite. A trace must
|
|
77
|
+
contain at least one sample.
|
|
78
|
+
|
|
79
|
+
Each new trace receives one marker on its first sample. Drag markers to select
|
|
80
|
+
samples, scroll to zoom, and drag the chart to pan. Markers stay above all sample
|
|
81
|
+
points. `chart.resetView()` restores the initial view, including peripheral rulers.
|
|
82
|
+
|
|
83
|
+
Construct `Smith` only in a browser, such as your framework's mount hook. Importing
|
|
84
|
+
the package and using its calculation or parsing functions does not require a DOM.
|
|
85
|
+
|
|
86
|
+
Zoom is enabled by default. For a fixed view, call `chart.setZoomEnabled(false)` before or after mounting.
|
|
87
|
+
It disables wheel/double-click zoom and mouse/touch panning and pinch zoom while
|
|
88
|
+
preserving the current view. Set it back to `true` to restore gestures. This is a
|
|
89
|
+
view control: cursor readings and marker dragging stay available. `resetView()`
|
|
90
|
+
also remains available when zoom is disabled. New wheel gestures do not consume
|
|
91
|
+
page scrolling. Disabling during an active gesture freezes the view while that
|
|
92
|
+
gesture finishes normally.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { Smith } from 'smithkit';
|
|
96
|
+
|
|
97
|
+
const chart = new Smith(50);
|
|
98
|
+
chart.setZoomEnabled(false);
|
|
99
|
+
chart.draw('#smith');
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`draw(selector | HTMLElement)` moves the existing SVG when called again. A missing
|
|
103
|
+
container throws. Call `chart.destroy()` on unmount; it removes the SVG, releases
|
|
104
|
+
event handlers, and cancels queued notifications. Repeated destruction is safe.
|
|
105
|
+
|
|
106
|
+
## Traces and markers
|
|
107
|
+
|
|
108
|
+
Trace and marker IDs are opaque strings, unique within a chart and never reused
|
|
109
|
+
there. They survive renaming, hiding, updating samples, and removing other items.
|
|
110
|
+
They are not persistent across chart instances or page reloads.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
const markerA = chart.getTraces().find((trace) => trace.id === traceId)!.markers[0].id;
|
|
114
|
+
const markerB = chart.addMarker(traceId, 2)!; // Zero-based sample index.
|
|
115
|
+
|
|
116
|
+
chart.setTraceOptions(traceId, { name: 'Tuned antenna', visible: true });
|
|
117
|
+
chart.setMarkerSample(markerB, 1);
|
|
118
|
+
console.log(chart.getMarker(markerB)?.frequencyHz); // 1.5e9
|
|
119
|
+
|
|
120
|
+
const difference = chart.compareMarkers(markerA, markerB);
|
|
121
|
+
console.log(difference?.frequencyDeltaHz); // B − A, in Hz.
|
|
122
|
+
console.log(difference?.impedanceDeltaOhms?.toVector());
|
|
123
|
+
console.log(difference?.phaseDeltaDegrees);
|
|
124
|
+
|
|
125
|
+
chart.removeMarker(markerB);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
| Method | Result / behavior |
|
|
129
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
130
|
+
| `addTrace(samples, options?)` | Returns a trace ID. Options: nonblank `name`, solid CSS `color`, and `visible`. |
|
|
131
|
+
| `getTraces()` | Detached `TraceInfo` snapshots: ID, name, color, visibility, sample count, and marker metadata. |
|
|
132
|
+
| `setTraceOptions(id, options)` | Updates the supplied options only. Invalid options throw before any change. |
|
|
133
|
+
| `updateTrace(id, samples, options?)` | Replaces samples and preserves identity, appearance, and markers. Empty/invalid input throws without changing the trace. |
|
|
134
|
+
| `removeTrace(id)` | Removes the trace and its markers. |
|
|
135
|
+
| `clearTraces()` | Removes all traces and their markers. |
|
|
136
|
+
| `addMarker(traceId, sampleIndex = 0)` | Returns a marker ID, or `undefined` if the trace is missing. |
|
|
137
|
+
| `getMarker(id)` | Returns a `MarkerSnapshot`, or `undefined` if missing. |
|
|
138
|
+
| `setMarkerSample(id, sampleIndex)` | Selects an existing sample and queues a marker notification. |
|
|
139
|
+
| `removeMarker(id)` | Removes one marker without renumbering the remaining markers. |
|
|
140
|
+
| `compareMarkers(a, b)` | Returns B − A, or `undefined` if either marker is missing. |
|
|
141
|
+
| `referenceImpedanceOhms` | Read-only reference impedance. Must be positive and finite. |
|
|
142
|
+
|
|
143
|
+
Update/remove methods return a boolean: `false` means the ID was not found.
|
|
144
|
+
Sample indices must be zero-based integers within the trace; invalid indices throw
|
|
145
|
+
`RangeError`. Sample replacement follows the nearest measured frequency by default.
|
|
146
|
+
Pass `{ markerSelection: 'sample-index' }` to retain indices (clamped to the new last
|
|
147
|
+
sample if the trace shrinks), or `{ markerSelection: 'reflection' }` to select the
|
|
148
|
+
nearest Γ. Frequency and Γ ties select the earlier sample in input order, including
|
|
149
|
+
unsorted sweeps and duplicate frequencies. Selection uses the actual selected sample,
|
|
150
|
+
not a previous requested frequency. No interpolation is performed.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
chart.updateTrace(
|
|
154
|
+
traceId,
|
|
155
|
+
[
|
|
156
|
+
{ frequencyHz: 1e9, reflectionCoefficient: [0.2, 0] },
|
|
157
|
+
{ frequencyHz: 2e9, reflectionCoefficient: [0.3, 0] },
|
|
158
|
+
],
|
|
159
|
+
{ markerSelection: 'frequency' },
|
|
160
|
+
);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Invalid strategies throw `TypeError`; empty or invalid samples throw `RangeError`
|
|
164
|
+
before mutation. Unknown trace IDs return `false` without validating input.
|
|
165
|
+
Renormalization always preserves sample indices because it transforms the existing
|
|
166
|
+
sweep rather than replacing it.
|
|
167
|
+
|
|
168
|
+
Trace snapshots contain `markers: { id, number, sampleIndex }[]`. `number` is the
|
|
169
|
+
stable display number within that trace. A `MarkerSnapshot` includes the common
|
|
170
|
+
RF reading fields below plus `traceId`, `markerId`, `markerNumber`, `sampleIndex`,
|
|
171
|
+
and `frequencyHz`.
|
|
172
|
+
|
|
173
|
+
Hiding a trace hides its points and markers while retaining measurements for
|
|
174
|
+
comparisons. Hiding or deleting a dragged marker/trace ends the drag. Mutations
|
|
175
|
+
after destruction throw; trace/marker lookups return empty or missing results.
|
|
176
|
+
|
|
177
|
+
## Trace appearance
|
|
178
|
+
|
|
179
|
+
Pass `mode: 'points' | 'line' | 'both'`, `lineWidth`, and `pointRadius` to
|
|
180
|
+
`addTrace` or `setTraceOptions`. Defaults are points, 2 px line width, and 2 px point radius. Both sizes are
|
|
181
|
+
CSS pixels and remain constant under chart zoom and container resize. Widths and radii must be positive and finite.
|
|
182
|
+
`getTraces()` includes the current style.
|
|
183
|
+
|
|
184
|
+
Lines connect all samples in input order, without smoothing or frequency sorting.
|
|
185
|
+
A line-only trace creates one SVG path and no sample circles, suitable for dense
|
|
186
|
+
sweeps. Points/both render every sample for traces up to 5,000 samples. Larger traces
|
|
187
|
+
render one representative per point-radius-sized display cell and skip points
|
|
188
|
+
outside the viewport; representatives are recalculated during zoom/pan.
|
|
189
|
+
This only simplifies the image: all original samples remain available to markers,
|
|
190
|
+
snapping, readouts, and comparisons. Lines still connect every input sample.
|
|
191
|
+
Styles do not change marker identity or layer ordering. Hidden traces retain their style after sample replacement.
|
|
192
|
+
The demo exposes the same controls per trace.
|
|
193
|
+
|
|
194
|
+
## Events and readings
|
|
195
|
+
|
|
196
|
+
`onEvent(listener)` supports multiple independent subscriptions and returns an
|
|
197
|
+
unsubscribe function. `SmithEvent` is a discriminated union: checking `type`
|
|
198
|
+
narrows `data` without a cast or a property-presence test.
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { SmithEventType } from 'smithkit';
|
|
202
|
+
|
|
203
|
+
const unsubscribe = chart.onEvent((event) => {
|
|
204
|
+
if (event.type === SmithEventType.Cursor) {
|
|
205
|
+
// undefined when the pointer leaves or a marker drag starts.
|
|
206
|
+
console.log(event.data?.impedanceOhms?.toVector());
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
// Marker, MarkerDragStart, and MarkerDragEnd always carry a MarkerSnapshot.
|
|
210
|
+
console.log(event.data.markerId, event.data.frequencyHz);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// On component unmount:
|
|
214
|
+
unsubscribe();
|
|
215
|
+
chart.destroy();
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The event types are `Cursor` (`'cursor'`), `Marker` (`'marker'`),
|
|
219
|
+
`MarkerDragStart` (`'marker-drag-start'`), and `MarkerDragEnd` (`'marker-drag-end'`).
|
|
220
|
+
Cursor movement is suspended during marker dragging. Drag start/end events fire
|
|
221
|
+
synchronously; marker position notifications are queued and report the latest
|
|
222
|
+
snapshot at delivery. Before delivery, repeated updates to the same marker coalesce
|
|
223
|
+
into one notification; different markers have independent notifications. Creation
|
|
224
|
+
queues an initial marker notification too. Removed markers cannot deliver queued
|
|
225
|
+
position events.
|
|
226
|
+
Trace metadata changes do not emit marker events; refresh metadata after mutations.
|
|
227
|
+
Destruction suppresses application callbacks.
|
|
228
|
+
|
|
229
|
+
`chart.cursorReading` returns the last cursor position. Cursor and marker readings
|
|
230
|
+
share `SmithReading`; `readReflection(gamma, referenceImpedanceOhms = 50)` produces
|
|
231
|
+
the same reading without constructing a chart.
|
|
232
|
+
|
|
233
|
+
| Fields | Units / meaning |
|
|
234
|
+
| ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
|
|
235
|
+
| `reflectionCoefficient` | Complex, dimensionless voltage Γ. |
|
|
236
|
+
| `impedanceOhms`, `admittanceSiemens` | Physical complex Z in Ω and Y in S. Multiply Y by 1000 for mS display. |
|
|
237
|
+
| `phaseDegrees` | Phase of Γ in degrees; undefined at Γ = 0. |
|
|
238
|
+
| `vswr`, `q` | Dimensionless voltage standing-wave ratio and \|X/R\|. |
|
|
239
|
+
| `returnLossDb`, `reflectionLossDb` | dB. Reflection loss is also called mismatch loss. |
|
|
240
|
+
| `standingWaveRatioDb` | 20 log₁₀(VSWR), in dB. |
|
|
241
|
+
| `attenuationDb` | Half the return loss; interpretation described below. |
|
|
242
|
+
| `powerReflectionCoefficient`, `powerTransmissionCoefficient` | \|Γ\|² and 1 − \|Γ\|². |
|
|
243
|
+
| `voltageReflectionCoefficientMagnitude` | \|Γ\|. |
|
|
244
|
+
| `voltageTransmissionCoefficientMagnitude`, `currentTransmissionCoefficientMagnitude` | \|1 + Γ\| and \|1 − Γ\|, respectively. |
|
|
245
|
+
| `standingWavePeak`, `standingWaveLossCoefficient` | √VSWR and (1 + \|Γ\|²)/(1 − \|Γ\|²). |
|
|
246
|
+
|
|
247
|
+
Singular Z at Γ = +1 and Y at Γ = −1 are `undefined`. Q is undefined at an open
|
|
248
|
+
or short, and infinite for a nonzero purely reactive impedance. Finite points near
|
|
249
|
+
these limits retain finite readings within the supported numeric range. An exact
|
|
250
|
+
infinite scalar limit is preserved: perfect match gives infinite return loss;
|
|
251
|
+
\|Γ\| = 1 gives infinite VSWR and reflection loss.
|
|
252
|
+
|
|
253
|
+
For active loads (\|Γ\| > 1), VSWR, standing-wave quantities, reflection loss,
|
|
254
|
+
attenuation, and power transmission are `undefined`. Other quantities remain
|
|
255
|
+
available. Non-finite complex inputs or invalid reference impedances throw
|
|
256
|
+
`RangeError` in the public RF functions. Unrepresentable complex conversion
|
|
257
|
+
results are `undefined`.
|
|
258
|
+
|
|
259
|
+
## Selecting a marker by frequency
|
|
260
|
+
|
|
261
|
+
`chart.setMarkerFrequency(markerId, frequencyHz)` selects the nearest measured
|
|
262
|
+
frequency and returns `true`, or `false` if the marker does not exist. It accepts
|
|
263
|
+
finite, non-negative Hz values; other values throw `RangeError`. It works with
|
|
264
|
+
unsorted sweeps and duplicate frequencies. Equal-distance ties choose the earliest
|
|
265
|
+
sample in input order. Requests outside the sweep select the nearest endpoint.
|
|
266
|
+
No interpolation or sample reordering is performed.
|
|
267
|
+
|
|
268
|
+
Read `chart.getMarker(markerId).frequencyHz` for the actual selected frequency.
|
|
269
|
+
The same queued `Marker` event is emitted as for sample-index selection, and
|
|
270
|
+
comparisons update accordingly. The demo accepts MHz and shows the selected
|
|
271
|
+
measurement frequency; dragging and sample selection keep that field synchronized.
|
|
272
|
+
|
|
273
|
+
## Calculations without a chart
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
import { Complex, readReflection, impedanceToReflection } from 'smithkit';
|
|
277
|
+
|
|
278
|
+
const gamma = impedanceToReflection(Complex.from(75, 25), 50)!;
|
|
279
|
+
const reading = readReflection(gamma, 50);
|
|
280
|
+
console.log(reading.impedanceOhms?.toVector()); // Approximately [75, 25].
|
|
281
|
+
console.log(reading.admittanceSiemens?.toVector());
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
| Function | Input and output |
|
|
285
|
+
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
|
|
286
|
+
| `reflectionToImpedance(gamma, referenceImpedanceOhms = 50)` | Γ → physical Z in Ω. |
|
|
287
|
+
| `impedanceToReflection(impedanceOhms, referenceImpedanceOhms = 50)` | Physical Z in Ω → Γ. |
|
|
288
|
+
| `reflectionToAdmittance(gamma, referenceImpedanceOhms = 50)` | Γ → physical Y in S. |
|
|
289
|
+
| `admittanceToReflection(admittanceSiemens, referenceImpedanceOhms = 50)` | Physical Y in S → Γ. |
|
|
290
|
+
| `readReflection(gamma, referenceImpedanceOhms = 50)` | Complete `SmithReading`. |
|
|
291
|
+
| `compareMarkerReadings(a, b)` | Compare readings without a chart. Each input needs `frequencyHz`, `reflectionCoefficient`, and `impedanceOhms`. |
|
|
292
|
+
|
|
293
|
+
Comparison results contain `frequencyDeltaHz`, `impedanceDeltaOhms`, and
|
|
294
|
+
`phaseDeltaDegrees`. Phase differences use B − A wrapped to **[−180°, 180°)**:
|
|
295
|
+
179° → −179° gives +2°; an exact half turn gives −180°. Phase is undefined if
|
|
296
|
+
either Γ is zero or non-finite. Impedance differences are undefined if either Z
|
|
297
|
+
is singular or non-finite. Frequencies must be finite and non-negative. This is
|
|
298
|
+
a point comparison, not sweep phase unwrapping or group delay.
|
|
299
|
+
|
|
300
|
+
`Complex.from(re, im)` constructs complex numbers. Read components through `re` and `im`. Arithmetic (`add`, `sub`,
|
|
301
|
+
`mul`, `div`, `pow`) returns new values. The class also provides `abs`, `arg`
|
|
302
|
+
(radians), and static functions such as `Complex.conj`, `Complex.sqrt`,
|
|
303
|
+
`Complex.exp`, `Complex.log`, and trigonometric/hyperbolic functions and inverses. `toVector()` returns `[real, imaginary]`; `toString(dp)` formats
|
|
304
|
+
a Cartesian value. See the exported declarations for all operations.
|
|
305
|
+
|
|
306
|
+
Formatting helpers are independent exports: `formatNumber(value)` (three significant
|
|
307
|
+
digits and SI prefixes), `formatComplex(value, unit?, decimalPlaces?)`, and
|
|
308
|
+
`formatComplexPolar(value, unit?, decimalPlaces?)` (phase in degrees). They work
|
|
309
|
+
without a chart or DOM. Complex formatters default to three fractional digits.
|
|
310
|
+
`formatNumber` includes spacing so you can append a unit, e.g. `formatNumber(1e9) + 'Hz'`.
|
|
311
|
+
|
|
312
|
+
`reactanceToComponent(reactanceOhms, frequencyHz)` calculates an ideal equivalent
|
|
313
|
+
series component without formatting or constructing a chart:
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
import { reactanceToComponent, formatNumber } from 'smithkit';
|
|
317
|
+
|
|
318
|
+
const component = reactanceToComponent(-50, 1e9);
|
|
319
|
+
if (component?.kind === 'capacitor') {
|
|
320
|
+
console.log(formatNumber(component.capacitanceFarads) + 'F'); // 3.18 pF
|
|
321
|
+
} else if (component?.kind === 'inductor') {
|
|
322
|
+
console.log(formatNumber(component.inductanceHenries) + 'H');
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Use `reading.impedanceOhms.im` for the reactance when impedance is defined.
|
|
327
|
+
Zero frequency, zero reactance, and unrepresentable results return `undefined`.
|
|
328
|
+
Non-finite reactance or negative/non-finite frequency throws `RangeError`.
|
|
329
|
+
This is a single-frequency equivalence, not a fitted circuit model.
|
|
330
|
+
|
|
331
|
+
## Grid layers
|
|
332
|
+
|
|
333
|
+
Resistance and reactance are initially visible. Conductance, susceptance, Q,
|
|
334
|
+
and VSWR layers start hidden. All controls are under `chart.layers`:
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
chart.layers.resistance.setVisible(false);
|
|
338
|
+
chart.layers.reactance.setVisible(false);
|
|
339
|
+
chart.layers.conductance.setVisible(true);
|
|
340
|
+
chart.layers.susceptance.setVisible(true);
|
|
341
|
+
chart.layers.conductance.setMinorVisible(false);
|
|
342
|
+
chart.layers.susceptance.setStyle({ stroke: '#64748b', textColor: '#2a7f62' });
|
|
343
|
+
chart.layers.vswr.addValue(4);
|
|
344
|
+
chart.layers.vswr.setVisible(true);
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
The four grid layers support `setVisible`, `setMinorVisible`, and
|
|
348
|
+
`setStyle(Partial<GridStyle>)`. Style fields: `stroke`, `majorWidth`, `minorWidth`,
|
|
349
|
+
`textColor`, `textFontFamily`, `textFontSize`. Widths are positive finite numbers
|
|
350
|
+
in CSS pixels. Font size is a positive finite number in chart units because labels
|
|
351
|
+
scale with the grid geometry. Partial updates retain other settings. Invalid numeric
|
|
352
|
+
styles throw before any property is changed.
|
|
353
|
+
|
|
354
|
+
`layers.q` and `layers.vswr` support `setVisible`,
|
|
355
|
+
`setStyle({ stroke?, strokeWidth? })`, `addValue`, and `removeValue`.
|
|
356
|
+
Circle stroke widths are positive finite numbers in CSS pixels.
|
|
357
|
+
Q values must be positive and finite; VSWR values must be finite and at least 1.
|
|
358
|
+
Duplicate additions and removal of absent values have no effect.
|
|
359
|
+
|
|
360
|
+
Grid labels use normalized values; readings use physical units. Strokes default
|
|
361
|
+
to slate gray with 1 px major and 0.6 px minor lines. Stroke widths stay constant
|
|
362
|
+
under zoom; label sizes use SVG user units. Layer controls do not expose drawing
|
|
363
|
+
objects or SVG nodes.
|
|
364
|
+
|
|
365
|
+
## Parameter scales
|
|
366
|
+
|
|
367
|
+
Mount `SmithScales` independently so its twelve axes stay outside chart zoom:
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
import { SmithScales, SmithEventType } from 'smithkit';
|
|
371
|
+
|
|
372
|
+
const scales = new SmithScales();
|
|
373
|
+
scales.draw('#smith-scales'); // Provide a separate mounted container.
|
|
374
|
+
const stopScales = chart.onEvent((event) => {
|
|
375
|
+
if (event.type === SmithEventType.Cursor || event.type === SmithEventType.Marker) {
|
|
376
|
+
const gamma = event.data?.reflectionCoefficient ?? null;
|
|
377
|
+
scales.update(gamma);
|
|
378
|
+
chart.peripheralScales.update(gamma);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// On unmount: stopScales(); scales.destroy(); chart.destroy();
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
`update(Complex | null)` synchronizes indicators, values, tooltips, and accessible
|
|
386
|
+
labels. Null, non-finite, or active-load inputs hide indicators. `draw` accepts a
|
|
387
|
+
selector or `HTMLElement`; `destroy` is idempotent. Scales adapt to container width.
|
|
388
|
+
|
|
389
|
+
Labels follow the complete Smith chart terminology: **Reflection loss**,
|
|
390
|
+
**Power reflection coefficient**, **Power transmission coefficient**, and separate
|
|
391
|
+
**Voltage transmission coefficient** and **Current transmission coefficient**.
|
|
392
|
+
Ratios are dimensionless; loss/standing-wave dB axes are in dB. Transmission
|
|
393
|
+
indicators depend on Γ phase as well as magnitude.
|
|
394
|
+
|
|
395
|
+
Attenuation is −10 log₁₀\|Γ\|, half the return loss. Its one-way loss interpretation
|
|
396
|
+
assumes a matched line or attenuator terminated in an open or short: the reflected
|
|
397
|
+
wave traverses it twice. It does not measure insertion loss of an arbitrary load
|
|
398
|
+
from S11 alone. At \|Γ\| = 0.1 it reads 10 dB, while return loss reads 20 dB.
|
|
399
|
+
|
|
400
|
+
`chart.peripheralScales` supports `setVisible(boolean)` and `update(Complex | null)`.
|
|
401
|
+
Its four rulers zoom with the chart: reflection phase, voltage transmission phase,
|
|
402
|
+
wavelengths toward generator, and wavelengths toward load. Wavelength rulers start
|
|
403
|
+
at the negative real axis, with 0.5 λ per revolution (generator clockwise, load
|
|
404
|
+
counterclockwise). These are electrical-length coordinates modulo 0.5 λ.
|
|
405
|
+
Reflection phase and wavelength coordinates are undefined at perfect match;
|
|
406
|
+
transmission phase is undefined at Γ = −1.
|
|
407
|
+
|
|
408
|
+
Scale conventions follow the [complete Smith chart reference](https://www.uiyinc.com/assets/The-Complete-Smith-Chart-Black-Magic-Design.jpg)
|
|
409
|
+
and [CERN's Smith chart introduction](https://arxiv.org/abs/1201.4068).
|
|
410
|
+
|
|
411
|
+
## Reference impedance and renormalization
|
|
412
|
+
|
|
413
|
+
`chart.renormalize(referenceImpedanceOhms)` changes the positive real reference
|
|
414
|
+
impedance and transforms every loaded Γ to preserve physical Z. Trace/marker IDs,
|
|
415
|
+
names, colors, visibility, frequencies, and selected sample indices are retained.
|
|
416
|
+
Results for all traces are validated before any change; a singular/unrepresentable
|
|
417
|
+
result rejects the operation without changing Z₀ or data. Active marker drags end,
|
|
418
|
+
cursor indicators are cleared, and marker readings are queued with the new values.
|
|
419
|
+
Changing to the current Z₀ has no effect. Mutations after destruction throw.
|
|
420
|
+
|
|
421
|
+
Two DOM-independent helpers are also exported:
|
|
422
|
+
|
|
423
|
+
- `renormalizeReflection(gamma, fromOhms, toOhms)` returns the new complex Γ or
|
|
424
|
+
`undefined` for a singular/unrepresentable result. Exact open and short limits
|
|
425
|
+
remain Γ = +1 and −1. References must be positive, finite, real ohm values.
|
|
426
|
+
- `renormalizeSamples(samples, fromOhms, toOhms)` returns a new sample array in
|
|
427
|
+
the same order, preserving frequencies. Invalid samples or a singular result
|
|
428
|
+
throw `RangeError`; the input is never modified.
|
|
429
|
+
|
|
430
|
+
For example, a matched 75 Ω load has Γ = 0 at 75 Ω and Γ = 0.2 at 50 Ω.
|
|
431
|
+
Renormalization changes Γ, VSWR, and loss readings; physical impedance stays 75 Ω.
|
|
432
|
+
Simply relabeling the same Γ with a new Z₀ would describe a different load.
|
|
433
|
+
|
|
434
|
+
The demo exposes chart Z₀ and explicitly renormalizes existing traces when applied.
|
|
435
|
+
Import renormalization is enabled by a labeled checkbox. Disable it to reject
|
|
436
|
+
files whose reference differs from the chart. Complex reference impedances and
|
|
437
|
+
multiport renormalization are outside the supported scope.
|
|
438
|
+
|
|
439
|
+
## Touchstone import
|
|
440
|
+
|
|
441
|
+
`parseTouchstone(text)` returns `{ samples, referenceImpedanceOhms }`. It accepts
|
|
442
|
+
one-port Touchstone 1.x, converts RI/MA/DB into Cartesian Γ and all frequencies
|
|
443
|
+
into Hz, and throws for unsupported or invalid input.
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
import { Smith, parseTouchstone } from 'smithkit';
|
|
447
|
+
|
|
448
|
+
async function showMeasurement(file: File): Promise<Smith> {
|
|
449
|
+
const { samples, referenceImpedanceOhms } = parseTouchstone(await file.text());
|
|
450
|
+
const chart = new Smith(referenceImpedanceOhms);
|
|
451
|
+
chart.draw('#smith');
|
|
452
|
+
chart.addTrace(samples, { name: file.name });
|
|
453
|
+
return chart;
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Omitted options use Touchstone defaults: GHz, S, MA, 50 Ω. For an existing chart,
|
|
458
|
+
use `renormalizeSamples` when the file reference differs from
|
|
459
|
+
`chart.referenceImpedanceOhms`. The parser itself does not renormalize data.
|
|
460
|
+
Multiport data and Touchstone 2.x are not supported.
|
|
461
|
+
|
|
462
|
+
## Development and license
|
|
463
|
+
|
|
464
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development, testing, release, and demo
|
|
465
|
+
hosting instructions. Licensed under [MIT](LICENSE).
|
|
466
|
+
|
|
467
|
+
## Numerical limits
|
|
468
|
+
|
|
469
|
+
`Complex` uses JavaScript binary64 numbers. Multiplication and division scale
|
|
470
|
+
intermediate products to avoid premature overflow and underflow; logarithms,
|
|
471
|
+
square roots, and reciprocals also handle extreme finite components. Results still
|
|
472
|
+
round to binary64: unrepresentable magnitudes become infinity and tiny results
|
|
473
|
+
can underflow to signed zero. Division by zero and reciprocals at zero return
|
|
474
|
+
`[NaN, NaN]`. These operations reject non-finite operands with NaN components.
|
|
475
|
+
The principal logarithm of zero retains its `-Infinity` real limit, and signed
|
|
476
|
+
zeros select the side of square-root and logarithm branch cuts. `pow(z, 0)` is one,
|
|
477
|
+
including zero to the zeroth power; negative powers of zero are undefined.
|
|
478
|
+
|
|
479
|
+
RF helpers validate finite inputs and use `undefined` for unrepresentable complex
|
|
480
|
+
readings, including an overflowing impedance difference between markers. These
|
|
481
|
+
conventions do not imply arbitrary precision or numerical certification of every
|
|
482
|
+
transcendental operation.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { TraceSamples } from './samples.js';
|
|
2
|
+
import type { ChartLayers, PeripheralScales } from './layers.js';
|
|
3
|
+
import type { SmithReading } from './rf.js';
|
|
4
|
+
import type { TraceOptions, TraceUpdateOptions, TraceInfo, MarkerSnapshot, MarkerComparison } from './measurements.js';
|
|
5
|
+
export declare enum SmithEventType {
|
|
6
|
+
Cursor = "cursor",
|
|
7
|
+
Marker = "marker",
|
|
8
|
+
MarkerDragStart = "marker-drag-start",
|
|
9
|
+
MarkerDragEnd = "marker-drag-end"
|
|
10
|
+
}
|
|
11
|
+
export type SmithEvent = {
|
|
12
|
+
type: SmithEventType.Cursor;
|
|
13
|
+
data: SmithReading | undefined;
|
|
14
|
+
} | {
|
|
15
|
+
type: SmithEventType.Marker | SmithEventType.MarkerDragStart | SmithEventType.MarkerDragEnd;
|
|
16
|
+
data: MarkerSnapshot;
|
|
17
|
+
};
|
|
18
|
+
export declare class Smith {
|
|
19
|
+
private referenceOhms;
|
|
20
|
+
private calcs;
|
|
21
|
+
private scalers;
|
|
22
|
+
private transform;
|
|
23
|
+
private zoomEnabled;
|
|
24
|
+
private applyingView;
|
|
25
|
+
private zoomBehavior;
|
|
26
|
+
private svg;
|
|
27
|
+
private resizeObserver;
|
|
28
|
+
private container;
|
|
29
|
+
private dataContainer;
|
|
30
|
+
private markerContainer;
|
|
31
|
+
private reactanceAxis;
|
|
32
|
+
private constResistance;
|
|
33
|
+
private constReactance;
|
|
34
|
+
private constConductance;
|
|
35
|
+
private constSusceptance;
|
|
36
|
+
private constSwrCircles;
|
|
37
|
+
private constQCircles;
|
|
38
|
+
private cursor;
|
|
39
|
+
private peripheralScaleRenderer;
|
|
40
|
+
private data;
|
|
41
|
+
private destroyed;
|
|
42
|
+
private mouseGesture;
|
|
43
|
+
private nextDatasetColor;
|
|
44
|
+
private nextTraceId;
|
|
45
|
+
private nextMarkerId;
|
|
46
|
+
private traceMetadata;
|
|
47
|
+
private markerIds;
|
|
48
|
+
private draggedMarkers;
|
|
49
|
+
private cursorBeforeMarkerDrag;
|
|
50
|
+
private listeners;
|
|
51
|
+
readonly layers: ChartLayers;
|
|
52
|
+
readonly peripheralScales: PeripheralScales;
|
|
53
|
+
constructor(referenceOhms?: number);
|
|
54
|
+
draw(target: string | HTMLElement): void;
|
|
55
|
+
/** Remove this chart and release its event handlers. Safe to call more than once. */
|
|
56
|
+
destroy(): void;
|
|
57
|
+
private updateViewportScale;
|
|
58
|
+
private assertAlive;
|
|
59
|
+
private createScalers;
|
|
60
|
+
private cursorMove;
|
|
61
|
+
private markerDragChanged;
|
|
62
|
+
private initCursor;
|
|
63
|
+
/** Last cursor position. Use cursor events to detect pointer leave. */
|
|
64
|
+
get cursorReading(): SmithReading;
|
|
65
|
+
private initializeZoom;
|
|
66
|
+
/** Enable or disable wheel, double-click, and mouse/touch zoom/pan. Keeps the current view. */
|
|
67
|
+
setZoomEnabled(enabled: boolean): void;
|
|
68
|
+
private applyView;
|
|
69
|
+
resetView(): void;
|
|
70
|
+
private onZoom;
|
|
71
|
+
private cursorContainer;
|
|
72
|
+
private drawReactanceAxis;
|
|
73
|
+
/** Add a named trace with one initial marker. Returns a chart-local, stable ID. */
|
|
74
|
+
addTrace(values: TraceSamples, options?: TraceOptions): string;
|
|
75
|
+
/** Detached metadata snapshots; IDs and marker display numbers survive removals. */
|
|
76
|
+
getTraces(): TraceInfo[];
|
|
77
|
+
private traceIndex;
|
|
78
|
+
private markerId;
|
|
79
|
+
private findMarker;
|
|
80
|
+
private validateTraceOptions;
|
|
81
|
+
setTraceOptions(id: string, options: TraceOptions): boolean;
|
|
82
|
+
/** Replace samples; by default each marker follows its nearest measured frequency. */
|
|
83
|
+
updateTrace(id: string, values: TraceSamples, options?: TraceUpdateOptions): boolean;
|
|
84
|
+
removeTrace(id: string): boolean;
|
|
85
|
+
/** Place a marker on an existing sample; no interpolation is performed. */
|
|
86
|
+
addMarker(traceId: string, sampleIndex?: number): string | undefined;
|
|
87
|
+
removeMarker(id: string): boolean;
|
|
88
|
+
setMarkerSample(id: string, sampleIndex: number): boolean;
|
|
89
|
+
/** Select the nearest measured frequency; ties choose the earliest input sample. */
|
|
90
|
+
setMarkerFrequency(id: string, frequencyHz: number): boolean;
|
|
91
|
+
getMarker(id: string): MarkerSnapshot | undefined;
|
|
92
|
+
/** Return B − A, or undefined if either marker no longer exists. */
|
|
93
|
+
compareMarkers(a: string, b: string): MarkerComparison | undefined;
|
|
94
|
+
/** Current positive real reference impedance, in ohms. */
|
|
95
|
+
get referenceImpedanceOhms(): number;
|
|
96
|
+
/** Renormalize all traces while retaining physical impedance, IDs, and selected sample indices. */
|
|
97
|
+
renormalize(referenceImpedanceOhms: number): void;
|
|
98
|
+
clearTraces(): void;
|
|
99
|
+
private createSmithData;
|
|
100
|
+
/** Subscribe to chart events. The returned function removes this subscription. */
|
|
101
|
+
onEvent(listener: (event: SmithEvent) => void): () => void;
|
|
102
|
+
private emit;
|
|
103
|
+
}
|