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.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +481 -2
  3. package/dist/lib/Smith.d.ts +103 -0
  4. package/dist/lib/Smith.js +551 -0
  5. package/dist/lib/SmithArcsDefs.d.ts +10 -0
  6. package/dist/lib/SmithArcsDefs.js +10 -0
  7. package/dist/lib/SmithConstantCircle.d.ts +62 -0
  8. package/dist/lib/SmithConstantCircle.js +222 -0
  9. package/dist/lib/arcs/Tick.d.ts +33 -0
  10. package/dist/lib/arcs/Tick.js +40 -0
  11. package/dist/lib/complex/Complex.d.ts +130 -0
  12. package/dist/lib/complex/Complex.js +636 -0
  13. package/dist/lib/components.d.ts +14 -0
  14. package/dist/lib/components.js +26 -0
  15. package/dist/lib/draw/ConstCircles.d.ts +61 -0
  16. package/dist/lib/draw/ConstCircles.js +151 -0
  17. package/dist/lib/draw/ConstConductance.d.ts +4 -0
  18. package/dist/lib/draw/ConstConductance.js +6 -0
  19. package/dist/lib/draw/ConstQCircles.d.ts +29 -0
  20. package/dist/lib/draw/ConstQCircles.js +86 -0
  21. package/dist/lib/draw/ConstReactance.d.ts +4 -0
  22. package/dist/lib/draw/ConstReactance.js +6 -0
  23. package/dist/lib/draw/ConstResistance.d.ts +4 -0
  24. package/dist/lib/draw/ConstResistance.js +6 -0
  25. package/dist/lib/draw/ConstSusceptance.d.ts +4 -0
  26. package/dist/lib/draw/ConstSusceptance.js +6 -0
  27. package/dist/lib/draw/ConstSwrCircles.d.ts +29 -0
  28. package/dist/lib/draw/ConstSwrCircles.js +85 -0
  29. package/dist/lib/draw/MouseGesture.d.ts +6 -0
  30. package/dist/lib/draw/MouseGesture.js +26 -0
  31. package/dist/lib/draw/SmithArc.d.ts +10 -0
  32. package/dist/lib/draw/SmithArc.js +26 -0
  33. package/dist/lib/draw/SmithCircle.d.ts +10 -0
  34. package/dist/lib/draw/SmithCircle.js +24 -0
  35. package/dist/lib/draw/SmithCursor.d.ts +49 -0
  36. package/dist/lib/draw/SmithCursor.js +186 -0
  37. package/dist/lib/draw/SmithData.d.ts +66 -0
  38. package/dist/lib/draw/SmithData.js +366 -0
  39. package/dist/lib/draw/SmithDrawOptions.d.ts +5 -0
  40. package/dist/lib/draw/SmithDrawOptions.js +1 -0
  41. package/dist/lib/draw/SmithGridLayer.d.ts +19 -0
  42. package/dist/lib/draw/SmithGridLayer.js +48 -0
  43. package/dist/lib/draw/SmithGroup.d.ts +11 -0
  44. package/dist/lib/draw/SmithGroup.js +31 -0
  45. package/dist/lib/draw/SmithLine.d.ts +11 -0
  46. package/dist/lib/draw/SmithLine.js +27 -0
  47. package/dist/lib/draw/SmithMarker.d.ts +25 -0
  48. package/dist/lib/draw/SmithMarker.js +122 -0
  49. package/dist/lib/draw/SmithPath.d.ts +8 -0
  50. package/dist/lib/draw/SmithPath.js +20 -0
  51. package/dist/lib/draw/SmithScaler.d.ts +19 -0
  52. package/dist/lib/draw/SmithScaler.js +48 -0
  53. package/dist/lib/draw/SmithShape.d.ts +15 -0
  54. package/dist/lib/draw/SmithShape.js +42 -0
  55. package/dist/lib/draw/SmithSvg.d.ts +9 -0
  56. package/dist/lib/draw/SmithSvg.js +27 -0
  57. package/dist/lib/draw/SmithText.d.ts +22 -0
  58. package/dist/lib/draw/SmithText.js +56 -0
  59. package/dist/lib/formatting.d.ts +7 -0
  60. package/dist/lib/formatting.js +16 -0
  61. package/dist/lib/grid/definitions.d.ts +3 -0
  62. package/dist/lib/grid/definitions.js +123 -0
  63. package/dist/lib/grid/geometry.d.ts +20 -0
  64. package/dist/lib/grid/geometry.js +55 -0
  65. package/dist/lib/grid/labels.d.ts +3 -0
  66. package/dist/lib/grid/labels.js +41 -0
  67. package/dist/lib/grid/types.d.ts +30 -0
  68. package/dist/lib/grid/types.js +6 -0
  69. package/dist/lib/index.d.ts +16 -0
  70. package/dist/lib/index.js +9 -0
  71. package/dist/lib/io/touchstone.d.ts +7 -0
  72. package/dist/lib/io/touchstone.js +72 -0
  73. package/dist/lib/layers.d.ts +48 -0
  74. package/dist/lib/layers.js +73 -0
  75. package/dist/lib/math/complexRatio.d.ts +3 -0
  76. package/dist/lib/math/complexRatio.js +30 -0
  77. package/dist/lib/measurements.d.ts +53 -0
  78. package/dist/lib/measurements.js +19 -0
  79. package/dist/lib/renormalization.d.ts +10 -0
  80. package/dist/lib/renormalization.js +62 -0
  81. package/dist/lib/rf.d.ts +35 -0
  82. package/dist/lib/rf.js +74 -0
  83. package/dist/lib/samples.d.ts +6 -0
  84. package/dist/lib/samples.js +1 -0
  85. package/dist/lib/scales/SmithPeripheralScales.d.ts +10 -0
  86. package/dist/lib/scales/SmithPeripheralScales.js +124 -0
  87. package/dist/lib/scales/SmithScales.d.ts +16 -0
  88. package/dist/lib/scales/SmithScales.js +134 -0
  89. package/dist/lib/scales/peripheralScales.d.ts +12 -0
  90. package/dist/lib/scales/peripheralScales.js +49 -0
  91. package/dist/lib/scales/radialScales.d.ts +12 -0
  92. package/dist/lib/scales/radialScales.js +109 -0
  93. package/dist/lib/shapes/Arc.d.ts +6 -0
  94. package/dist/lib/shapes/Arc.js +1 -0
  95. package/dist/lib/shapes/Circle.d.ts +5 -0
  96. package/dist/lib/shapes/Circle.js +1 -0
  97. package/dist/lib/shapes/Line.d.ts +5 -0
  98. package/dist/lib/shapes/Line.js +1 -0
  99. package/dist/lib/shapes/Point.d.ts +1 -0
  100. package/dist/lib/shapes/Point.js +1 -0
  101. package/package.json +76 -4
package/dist/lib/rf.js ADDED
@@ -0,0 +1,74 @@
1
+ import { Complex } from './complex/Complex.js';
2
+ import { SmithConstantCircle } from './SmithConstantCircle.js';
3
+ const calcs = new SmithConstantCircle();
4
+ function validateReference(referenceImpedanceOhms) {
5
+ if (!Number.isFinite(referenceImpedanceOhms) || referenceImpedanceOhms <= 0) {
6
+ throw new RangeError('Reference impedance must be positive and finite.');
7
+ }
8
+ }
9
+ function validateComplex(value) {
10
+ if (!Number.isFinite(value.re) || !Number.isFinite(value.im)) {
11
+ throw new RangeError('Complex components must be finite.');
12
+ }
13
+ }
14
+ function finite(value) {
15
+ return value && Number.isFinite(value.re) && Number.isFinite(value.im) ? value : undefined;
16
+ }
17
+ /** Convert Γ to physical impedance in ohms; undefined at the open circuit Γ = 1. */
18
+ export function reflectionToImpedance(gamma, referenceImpedanceOhms = 50) {
19
+ validateReference(referenceImpedanceOhms);
20
+ validateComplex(gamma);
21
+ return finite(calcs.rflCoeffToImpedance(gamma)?.mul(referenceImpedanceOhms));
22
+ }
23
+ /** Convert physical impedance in ohms to Γ. Undefined for a singular result. */
24
+ export function impedanceToReflection(impedanceOhms, referenceImpedanceOhms = 50) {
25
+ validateReference(referenceImpedanceOhms);
26
+ validateComplex(impedanceOhms);
27
+ return finite(calcs.impedanceToRflCoeff(impedanceOhms.div(referenceImpedanceOhms)));
28
+ }
29
+ /** Convert Γ to physical admittance in siemens; undefined at the short circuit Γ = −1. */
30
+ export function reflectionToAdmittance(gamma, referenceImpedanceOhms = 50) {
31
+ validateReference(referenceImpedanceOhms);
32
+ validateComplex(gamma);
33
+ return finite(calcs.rflCoeffToAdmittance(gamma)?.div(referenceImpedanceOhms));
34
+ }
35
+ /** Convert physical admittance in siemens to Γ. Undefined for a singular result. */
36
+ export function admittanceToReflection(admittanceSiemens, referenceImpedanceOhms = 50) {
37
+ validateReference(referenceImpedanceOhms);
38
+ validateComplex(admittanceSiemens);
39
+ return finite(calcs.admittanceToRflCoeff(admittanceSiemens.mul(referenceImpedanceOhms)));
40
+ }
41
+ /**
42
+ * Read Γ without a chart or DOM. Undefined means a singular or inapplicable quantity.
43
+ * Passive-load quantities are undefined outside |Γ| ≤ 1; exact infinite limits are retained.
44
+ * Attenuation assumes a fully reflecting termination and is half the return loss.
45
+ */
46
+ export function readReflection(gamma, referenceImpedanceOhms = 50) {
47
+ const impedanceOhms = reflectionToImpedance(gamma, referenceImpedanceOhms);
48
+ const admittanceSiemens = reflectionToAdmittance(gamma, referenceImpedanceOhms);
49
+ const magnitude = gamma.abs();
50
+ const passive = magnitude <= 1;
51
+ const returnLossDb = -20 * Math.log10(magnitude);
52
+ const vswr = passive ? (1 + magnitude) / (1 - magnitude) : undefined;
53
+ return {
54
+ reflectionCoefficient: gamma,
55
+ impedanceOhms,
56
+ admittanceSiemens,
57
+ phaseDegrees: magnitude === 0 ? undefined : (gamma.arg() * 180) / Math.PI,
58
+ vswr,
59
+ standingWaveRatioDb: vswr === undefined ? undefined : 20 * Math.log10(vswr),
60
+ returnLossDb,
61
+ reflectionLossDb: passive ? (-10 / Math.LN10) * Math.log1p(-(magnitude ** 2)) : undefined,
62
+ attenuationDb: passive ? returnLossDb / 2 : undefined,
63
+ q: impedanceOhms && (impedanceOhms.re !== 0 || impedanceOhms.im !== 0)
64
+ ? Math.abs(impedanceOhms.im / impedanceOhms.re)
65
+ : undefined,
66
+ powerReflectionCoefficient: magnitude ** 2,
67
+ voltageReflectionCoefficientMagnitude: magnitude,
68
+ powerTransmissionCoefficient: passive ? 1 - magnitude ** 2 : undefined,
69
+ voltageTransmissionCoefficientMagnitude: gamma.add(1).abs(),
70
+ currentTransmissionCoefficientMagnitude: Complex.one().sub(gamma).abs(),
71
+ standingWavePeak: vswr === undefined ? undefined : Math.sqrt(vswr),
72
+ standingWaveLossCoefficient: passive ? (1 + magnitude ** 2) / (1 - magnitude ** 2) : undefined,
73
+ };
74
+ }
@@ -0,0 +1,6 @@
1
+ /** One S11 sample. The reflection coefficient is [real, imaginary]. */
2
+ export interface TraceSample {
3
+ readonly frequencyHz: number;
4
+ readonly reflectionCoefficient: readonly [number, number];
5
+ }
6
+ export type TraceSamples = readonly TraceSample[];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { Complex } from '../complex/Complex.js';
2
+ import { SmithGroup } from '../draw/SmithGroup.js';
3
+ /** Peripheral rulers in the chart's 500-unit coordinate system. */
4
+ export declare class SmithPeripheralScales extends SmithGroup {
5
+ private readonly scales;
6
+ private readonly axes;
7
+ constructor();
8
+ /** Connect to the same cursor or marker data as the independent parameter scales. */
9
+ update(gamma: Complex | null): void;
10
+ }
@@ -0,0 +1,124 @@
1
+ import { select, format } from 'd3';
2
+ import { SmithGroup } from '../draw/SmithGroup.js';
3
+ import { peripheralScales } from './peripheralScales.js';
4
+ let nextScaleId = 0;
5
+ const firstRadius = 250;
6
+ const ringSpacing = 20;
7
+ function point(radius, degrees) {
8
+ const angle = (degrees * Math.PI) / 180;
9
+ return [250 + radius * Math.cos(angle), 250 - radius * Math.sin(angle)];
10
+ }
11
+ /** Peripheral rulers in the chart's 500-unit coordinate system. */
12
+ export class SmithPeripheralScales extends SmithGroup {
13
+ scales = peripheralScales();
14
+ axes = this.Element.attr('data-layer', 'peripheral-scales')
15
+ .attr('pointer-events', 'none')
16
+ .selectAll('g')
17
+ .data(this.scales)
18
+ .join('g')
19
+ .attr('data-scale', (scale) => scale.id)
20
+ .attr('role', 'img')
21
+ .attr('aria-label', (scale) => scale.title);
22
+ constructor() {
23
+ super();
24
+ this.axes.each(function (scale, index) {
25
+ const axis = select(this);
26
+ const radius = firstRadius + index * ringSpacing;
27
+ const captionAngle = scale.unit === 'λ' ? 180 : 0;
28
+ axis.append('title').text(scale.title);
29
+ axis
30
+ .append('circle')
31
+ .attr('cx', 250)
32
+ .attr('cy', 250)
33
+ .attr('r', radius)
34
+ .attr('fill', 'none')
35
+ .attr('stroke', '#475569')
36
+ .attr('stroke-width', 0.8)
37
+ .attr('vector-effect', 'non-scaling-stroke');
38
+ for (const value of scale.ticks) {
39
+ const angle = scale.angle(value);
40
+ const [x1, y1] = point(radius, angle);
41
+ const major = scale.major(value);
42
+ const [x2, y2] = point(radius - (major ? 4 : 2), angle);
43
+ axis
44
+ .append('line')
45
+ .attr('x1', x1)
46
+ .attr('y1', y1)
47
+ .attr('x2', x2)
48
+ .attr('y2', y2)
49
+ .attr('stroke', '#475569')
50
+ .attr('stroke-width', major ? 0.8 : 0.5)
51
+ .attr('vector-effect', 'non-scaling-stroke');
52
+ const captionDistance = Math.abs(((angle - captionAngle + 540) % 360) - 180);
53
+ if (major && captionDistance > 25) {
54
+ const [x, y] = point(radius + 8, angle);
55
+ const rotation = angle >= 0 ? 90 - angle : -90 - angle;
56
+ axis
57
+ .append('text')
58
+ .attr('x', x)
59
+ .attr('y', y)
60
+ .attr('transform', `rotate(${rotation},${x},${y})`)
61
+ .attr('text-anchor', 'middle')
62
+ .attr('dominant-baseline', 'central')
63
+ .attr('font-family', 'system-ui, sans-serif')
64
+ .attr('font-size', 10.5)
65
+ .attr('fill', '#334155')
66
+ .text(scale.unit === 'λ' ? value.toFixed(2) : String(value));
67
+ }
68
+ }
69
+ // Reserve a label sector for the curved caption without masking the ruler or its ticks.
70
+ const captionRadius = radius + 5;
71
+ const start = point(captionRadius, captionAngle + 45);
72
+ const end = point(captionRadius, captionAngle - 45);
73
+ const pathId = `smithkit-peripheral-caption-${nextScaleId++}`;
74
+ axis
75
+ .append('defs')
76
+ .append('path')
77
+ .attr('id', pathId)
78
+ .attr('d', `M${start} A${captionRadius},${captionRadius} 0 0 1 ${end}`);
79
+ const caption = scale.unit === 'λ'
80
+ ? scale.id === 'wavelengths-generator'
81
+ ? 'WAVELENGTHS TOWARD GENERATOR →'
82
+ : '← WAVELENGTHS TOWARD LOAD'
83
+ : scale.title.toUpperCase();
84
+ axis
85
+ .append('text')
86
+ .attr('class', 'peripheral-caption')
87
+ .attr('text-anchor', 'middle')
88
+ .attr('font-family', 'system-ui, sans-serif')
89
+ .attr('font-size', 10.5)
90
+ .attr('font-weight', 500)
91
+ .attr('letter-spacing', 0.3)
92
+ .attr('fill', '#334155')
93
+ .append('textPath')
94
+ .attr('href', `#${pathId}`)
95
+ .attr('startOffset', '50%')
96
+ .text(caption);
97
+ axis
98
+ .append('circle')
99
+ .attr('class', 'peripheral-indicator')
100
+ .attr('r', 3)
101
+ .attr('visibility', 'hidden')
102
+ .attr('fill', '#dc2626')
103
+ .attr('stroke', 'white')
104
+ .attr('stroke-width', 1);
105
+ });
106
+ }
107
+ /** Connect to the same cursor or marker data as the independent parameter scales. */
108
+ update(gamma) {
109
+ const valid = gamma !== null && Number.isFinite(gamma.abs()) && gamma.abs() <= 1;
110
+ this.axes.each(function (scale, index) {
111
+ const value = valid ? scale.read(gamma) : null;
112
+ const axis = select(this);
113
+ const label = value === null ? scale.title : `${scale.title}: ${format('.4~g')(value)} ${scale.unit}`;
114
+ axis.attr('aria-label', label).select('title').text(label);
115
+ const dot = axis.select('.peripheral-indicator');
116
+ if (value === null) {
117
+ dot.attr('visibility', 'hidden').attr('data-value', null);
118
+ return;
119
+ }
120
+ const [x, y] = point(firstRadius + index * ringSpacing, scale.angle(value));
121
+ dot.attr('cx', x).attr('cy', y).attr('data-value', value).attr('visibility', null);
122
+ });
123
+ }
124
+ }
@@ -0,0 +1,16 @@
1
+ import type { Complex } from '../complex/Complex.js';
2
+ /** Independent parameter scales. Connect update() to a chart's cursor events. */
3
+ export declare class SmithScales {
4
+ private readonly scales;
5
+ private readonly container;
6
+ private destroyed;
7
+ private readonly start;
8
+ private readonly length;
9
+ private readonly axes;
10
+ constructor();
11
+ draw(target: string | HTMLElement): void;
12
+ /** Show a passive-load reflection coefficient, or hide indicators with null. */
13
+ update(gamma: Complex | null): void;
14
+ destroy(): void;
15
+ private assertAlive;
16
+ }
@@ -0,0 +1,134 @@
1
+ import { select, format } from 'd3';
2
+ import { radialScales } from './radialScales.js';
3
+ /** Independent parameter scales. Connect update() to a chart's cursor events. */
4
+ export class SmithScales {
5
+ scales = radialScales();
6
+ container = select(document.createElement('div'))
7
+ .attr('class', 'radial-scales')
8
+ .style('display', 'grid')
9
+ .style('grid-template-columns', 'repeat(auto-fit, minmax(min(100%, 300px), 1fr))')
10
+ .style('gap', '8px 20px');
11
+ destroyed = false;
12
+ start = 8;
13
+ length = 304;
14
+ axes = this.container
15
+ .selectAll('svg')
16
+ .data(this.scales)
17
+ .join('svg')
18
+ .attr('xmlns', 'http://www.w3.org/2000/svg')
19
+ .attr('viewBox', '0 0 320 60')
20
+ .attr('role', 'img')
21
+ .attr('aria-label', (scale) => scale.title)
22
+ .attr('data-scale', (scale) => scale.id)
23
+ .style('display', 'block')
24
+ .style('width', '100%')
25
+ .style('height', 'auto');
26
+ constructor() {
27
+ this.axes
28
+ .append('text')
29
+ .attr('x', this.start)
30
+ .attr('y', 12)
31
+ .attr('font-family', 'system-ui, sans-serif')
32
+ .attr('font-size', 12)
33
+ .attr('fill', '#334155')
34
+ .text((scale) => scale.title);
35
+ this.axes
36
+ .append('text')
37
+ .attr('class', 'scale-value')
38
+ .attr('x', this.start)
39
+ .attr('y', 25)
40
+ .attr('font-family', 'system-ui, sans-serif')
41
+ .attr('font-size', 11)
42
+ .attr('font-weight', 600)
43
+ .attr('fill', '#b42318')
44
+ .text('—');
45
+ this.axes
46
+ .append('line')
47
+ .attr('class', 'scale-axis')
48
+ .attr('x1', this.start)
49
+ .attr('x2', this.start + this.length)
50
+ .attr('y1', 36)
51
+ .attr('y2', 36)
52
+ .attr('stroke', '#64748b')
53
+ .attr('stroke-width', 1);
54
+ const start = this.start;
55
+ const length = this.length;
56
+ this.axes.each(function (scale) {
57
+ const ticks = select(this)
58
+ .selectAll('g')
59
+ .data(scale.values)
60
+ .join('g')
61
+ .attr('transform', (value) => `translate(${start + length * scale.position(value)},36)`);
62
+ ticks.append('line').attr('y2', 5).attr('stroke', '#64748b').attr('stroke-width', 1);
63
+ ticks
64
+ .append('text')
65
+ .attr('y', 17)
66
+ .attr('font-size', 10)
67
+ .attr('font-family', 'system-ui, sans-serif')
68
+ .attr('fill', '#334155')
69
+ .attr('text-anchor', (_, i) => i === 0 ? 'start' : i === scale.values.length - 1 ? 'end' : 'middle')
70
+ .text((value) => (value === Infinity ? '∞' : String(value)));
71
+ });
72
+ this.axes
73
+ .append('circle')
74
+ .attr('class', 'scale-indicator')
75
+ .attr('visibility', 'hidden')
76
+ .attr('cx', this.start)
77
+ .attr('cy', 36)
78
+ .attr('r', 4)
79
+ .attr('fill', '#dc2626')
80
+ .attr('stroke', 'white')
81
+ .attr('stroke-width', 1);
82
+ this.axes.append('title').text((scale) => scale.description ?? scale.title);
83
+ }
84
+ draw(target) {
85
+ this.assertAlive();
86
+ const host = typeof target === 'string' ? document.querySelector(target) : target;
87
+ if (!host) {
88
+ throw new Error('Scale container was not found.');
89
+ }
90
+ host.appendChild(this.container.node());
91
+ }
92
+ /** Show a passive-load reflection coefficient, or hide indicators with null. */
93
+ update(gamma) {
94
+ this.assertAlive();
95
+ const magnitude = gamma === null ? NaN : Math.hypot(gamma.re, gamma.im);
96
+ if (!gamma || !Number.isFinite(magnitude) || magnitude > 1) {
97
+ this.axes.select('circle').attr('visibility', 'hidden').attr('data-position', null);
98
+ this.axes.select('.scale-value').text('—');
99
+ this.axes.attr('aria-label', (scale) => `${scale.title}. ${scale.description ?? ''}`.trim());
100
+ this.axes.select('title').text((scale) => scale.description ?? scale.title);
101
+ return;
102
+ }
103
+ const start = this.start;
104
+ const length = this.length;
105
+ this.axes.each(function (scale) {
106
+ const value = scale.read(gamma);
107
+ const position = Math.max(0, Math.min(1, scale.position(value)));
108
+ const axis = select(this);
109
+ const number = value === Infinity ? '∞' : format('.4~g')(value);
110
+ const suffix = scale.unit === 'dB' ? ' dB' : scale.id === 'vswr' ? ' : 1' : '';
111
+ const readout = number + suffix;
112
+ const label = `${scale.title}: ${readout}${scale.description ? `. ${scale.description}` : ''}`;
113
+ axis.select('.scale-value').text(readout);
114
+ axis.attr('aria-label', label).select('title').text(label);
115
+ axis
116
+ .select('circle')
117
+ .attr('cx', start + position * length)
118
+ .attr('data-position', position)
119
+ .attr('visibility', null);
120
+ });
121
+ }
122
+ destroy() {
123
+ if (this.destroyed) {
124
+ return;
125
+ }
126
+ this.destroyed = true;
127
+ this.container.remove();
128
+ }
129
+ assertAlive() {
130
+ if (this.destroyed) {
131
+ throw new Error('These Smith scales have been destroyed. Create a new instance.');
132
+ }
133
+ }
134
+ }
@@ -0,0 +1,12 @@
1
+ import type { Complex } from '../complex/Complex.js';
2
+ export interface PeripheralScale {
3
+ id: string;
4
+ title: string;
5
+ unit: '°' | 'λ';
6
+ ticks: number[];
7
+ major: (value: number) => boolean;
8
+ angle: (value: number) => number;
9
+ read: (gamma: Complex) => number | null;
10
+ }
11
+ /** A full reflection-coefficient rotation represents half a wavelength along the line. */
12
+ export declare function peripheralScales(): PeripheralScale[];
@@ -0,0 +1,49 @@
1
+ function phase(gamma) {
2
+ return gamma.re === 0 && gamma.im === 0 ? null : (Math.atan2(gamma.im, gamma.re) * 180) / Math.PI;
3
+ }
4
+ /** A full reflection-coefficient rotation represents half a wavelength along the line. */
5
+ export function peripheralScales() {
6
+ const wavelengths = (gamma, direction) => {
7
+ const degrees = phase(gamma);
8
+ return degrees === null ? null : (0.25 + (direction * degrees) / 720 + 0.5) % 0.5;
9
+ };
10
+ return [
11
+ {
12
+ id: 'transmission-phase',
13
+ title: 'Transmission phase · °',
14
+ unit: '°',
15
+ ticks: Array.from({ length: 179 }, (_, i) => -89 + i),
16
+ major: (v) => v % 5 === 0,
17
+ // Project from Γ = −1 to the perimeter, not from the chart center.
18
+ angle: (v) => 2 * v,
19
+ read: (gamma) => phase({ re: 1 + gamma.re, im: gamma.im }),
20
+ },
21
+ {
22
+ id: 'reflection-phase',
23
+ title: 'Reflection phase · °',
24
+ unit: '°',
25
+ ticks: Array.from({ length: 180 }, (_, i) => -178 + i * 2),
26
+ major: (v) => v % 10 === 0,
27
+ angle: (v) => v,
28
+ read: phase,
29
+ },
30
+ {
31
+ id: 'wavelengths-load',
32
+ title: 'Wavelengths → load',
33
+ unit: 'λ',
34
+ ticks: Array.from({ length: 250 }, (_, i) => i / 500),
35
+ major: (v) => Math.round(v * 500) % 5 === 0,
36
+ angle: (v) => 720 * v - 180,
37
+ read: (gamma) => wavelengths(gamma, 1),
38
+ },
39
+ {
40
+ id: 'wavelengths-generator',
41
+ title: 'Wavelengths → generator',
42
+ unit: 'λ',
43
+ ticks: Array.from({ length: 250 }, (_, i) => i / 500),
44
+ major: (v) => Math.round(v * 500) % 5 === 0,
45
+ angle: (v) => 180 - 720 * v,
46
+ read: (gamma) => wavelengths(gamma, -1),
47
+ },
48
+ ];
49
+ }
@@ -0,0 +1,12 @@
1
+ import type { Complex } from '../complex/Complex.js';
2
+ export interface RadialScale {
3
+ id: string;
4
+ title: string;
5
+ description?: string;
6
+ unit: 'dB' | 'ratio';
7
+ read: (gamma: Complex) => number;
8
+ values: number[];
9
+ position: (value: number) => number;
10
+ }
11
+ /** Normalized positions along each scale, from zero to one. */
12
+ export declare function radialScales(): RadialScale[];
@@ -0,0 +1,109 @@
1
+ import { SmithConstantCircle } from '../SmithConstantCircle.js';
2
+ /** Normalized positions along each scale, from zero to one. */
3
+ export function radialScales() {
4
+ const calcs = new SmithConstantCircle();
5
+ return [
6
+ {
7
+ id: 'vswr',
8
+ unit: 'ratio',
9
+ read: (gamma) => calcs.rflCoeffToSwr(gamma),
10
+ title: 'VSWR',
11
+ values: [1, 1.5, 2, 3, 5, 10, Infinity],
12
+ position: (v) => (v === Infinity ? 1 : calcs.swrToRflCoeffEOrI(v)),
13
+ },
14
+ {
15
+ id: 'vswr-db',
16
+ unit: 'dB',
17
+ read: (gamma) => calcs.rflCoeffToDBS(gamma),
18
+ title: 'Standing-wave ratio · dB',
19
+ values: [0, 3, 6, 10, 15, 20, Infinity],
20
+ position: (v) => (v === Infinity ? 1 : calcs.dBSToAbsRflCoeff(v)),
21
+ },
22
+ {
23
+ id: 'return-loss',
24
+ unit: 'dB',
25
+ read: (gamma) => calcs.rflCoeffToReturnLoss(gamma),
26
+ title: 'Return loss · dB',
27
+ values: [Infinity, 20, 14, 10, 6, 3, 0],
28
+ position: (v) => calcs.returnLossToRflCoeffEOrI(v),
29
+ },
30
+ {
31
+ id: 'mismatch-loss',
32
+ unit: 'dB',
33
+ read: (gamma) => calcs.rflCoeffToMismatchLoss(gamma),
34
+ title: 'Reflection loss · dB',
35
+ description: 'Reflection loss (mismatch loss): −10 log10(1 − |Γ|²), in dB.',
36
+ values: [0, 0.1, 0.5, 1, 2, 4, Infinity],
37
+ position: (v) => calcs.mismatchLossToRflCoeffEOrI(v),
38
+ },
39
+ {
40
+ id: 'reflected-power',
41
+ unit: 'ratio',
42
+ read: (gamma) => calcs.rflCoeffP(gamma),
43
+ title: 'Power reflection coefficient',
44
+ description: 'Reflected power divided by incident power: |Γ|² (dimensionless).',
45
+ values: [0, 0.01, 0.1, 0.25, 0.5, 0.75, 1],
46
+ position: (v) => calcs.rflCoeffPToEOrI(v),
47
+ },
48
+ {
49
+ id: 'transmitted-power',
50
+ unit: 'ratio',
51
+ read: (gamma) => calcs.rflCoeffToTransmCoeffP(gamma),
52
+ title: 'Power transmission coefficient',
53
+ description: 'Net power delivered to the load divided by incident power: 1 − |Γ|² (dimensionless).',
54
+ values: [1, 0.99, 0.9, 0.75, 0.5, 0.25, 0],
55
+ position: (v) => calcs.transmCoeffPToRflCoeffEOrI(v),
56
+ },
57
+ {
58
+ id: 'standing-wave-loss',
59
+ unit: 'ratio',
60
+ read: (gamma) => calcs.rflCoeffToSwLossCoeff(gamma),
61
+ title: 'Standing-wave loss coefficient',
62
+ values: [1, 1.1, 1.5, 2, 3, 5, Infinity],
63
+ position: (v) => (v === Infinity ? 1 : calcs.swLossCoeffToRflCoeffEOrI(v)),
64
+ },
65
+ {
66
+ id: 'standing-wave-peak',
67
+ unit: 'ratio',
68
+ read: (gamma) => calcs.rflCoeffToSwPeakConstP(gamma),
69
+ title: 'Standing-wave peak · constant power',
70
+ values: [1, 1.2, 1.5, 2, 3, Infinity],
71
+ position: (v) => (v === Infinity ? 1 : calcs.swPeakConstPToRflCoeffEOrI(v)),
72
+ },
73
+ {
74
+ id: 'reflection-coefficient',
75
+ unit: 'ratio',
76
+ read: (gamma) => calcs.rflCoeffEOrI(gamma),
77
+ title: 'Reflection coefficient · |Γ|',
78
+ values: [0, 0.2, 0.4, 0.6, 0.8, 1],
79
+ position: (v) => v,
80
+ },
81
+ {
82
+ id: 'attenuation',
83
+ unit: 'dB',
84
+ read: (gamma) => calcs.rflCoeffToAttenuation(gamma),
85
+ title: 'Attenuation · dB',
86
+ description: 'One-way attenuation of a matched line or attenuator terminated in an open or short circuit. Not insertion loss of an arbitrary load.',
87
+ values: [Infinity, 10, 7, 5, 3, 1, 0],
88
+ position: (v) => calcs.attenuationToRflCoeff(v),
89
+ },
90
+ {
91
+ id: 'voltage-transmission',
92
+ unit: 'ratio',
93
+ read: (gamma) => calcs.rflCoeffToTransmCoeffEOrI(gamma),
94
+ title: 'Voltage transmission coefficient',
95
+ description: 'Voltage transmission magnitude: |1 + Γ| (dimensionless).',
96
+ values: [0, 0.4, 0.8, 1.2, 1.6, 2],
97
+ position: (v) => v / 2,
98
+ },
99
+ {
100
+ id: 'current-transmission',
101
+ unit: 'ratio',
102
+ read: (gamma) => calcs.rflCoeffToCurrentTransmission(gamma),
103
+ title: 'Current transmission coefficient',
104
+ description: 'Current transmission magnitude: |1 − Γ|, using the voltage reflection coefficient Γ (dimensionless).',
105
+ values: [0, 0.4, 0.8, 1.2, 1.6, 2],
106
+ position: (v) => v / 2,
107
+ },
108
+ ];
109
+ }
@@ -0,0 +1,6 @@
1
+ import { Point } from './Point.js';
2
+ export interface Arc {
3
+ p1: Point;
4
+ p2: Point;
5
+ r: number;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { Point } from './Point.js';
2
+ export interface Circle {
3
+ p: Point;
4
+ r: number;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { Point } from './Point.js';
2
+ export interface Line {
3
+ p1: Point;
4
+ p2: Point;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export type Point = [number, number];
@@ -0,0 +1 @@
1
+ export {};