icom-wlan-node 0.4.0 → 0.5.1
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/README.md +14 -3
- package/dist/rig/IcomControl.d.ts +71 -1
- package/dist/rig/IcomControl.js +234 -0
- package/dist/scope/IcomScopeCommands.d.ts +10 -0
- package/dist/scope/IcomScopeCommands.js +68 -0
- package/dist/types.d.ts +42 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -306,6 +306,18 @@ The library exposes common CI‑V operations as friendly methods. Addresses are
|
|
|
306
306
|
- `scope: IcomScopeService` — Standalone scope service object that can be reused with other CI‑V transport paths in the future
|
|
307
307
|
- `enableScope() => Promise<void>` — Send the minimal command sequence to enable basic scope output
|
|
308
308
|
- `disableScope() => Promise<void>` — Send the minimal command sequence to disable scope output
|
|
309
|
+
- `readScopeMode(options?: QueryOptions & { receiver?: 0 | 1 }) => Promise<IcomScopeModeInfo | null>` — Read current scope mode using CI‑V `0x27 0x14`
|
|
310
|
+
- `setScopeMode(mode: IcomScopeMode | 0 | 1 | 2 | 3, options?: { receiver?: 0 | 1 }) => Promise<void>` — Set current scope mode
|
|
311
|
+
- `readScopeSpan(options?: QueryOptions & { receiver?: 0 | 1 }) => Promise<{ receiver: 0 | 1; spanHz: number } | null>` — Read current scope span
|
|
312
|
+
- `setScopeSpan(spanHz: number, options?: { receiver?: 0 | 1 }) => Promise<void>` — Set scope span using CI‑V `0x27 0x15`
|
|
313
|
+
- `readScopeEdge(options?: QueryOptions & { receiver?: 0 | 1 }) => Promise<IcomScopeEdgeInfo | null>` — Read active fixed-edge slot using CI‑V `0x27 0x16`
|
|
314
|
+
- `setScopeEdge(edgeSlot: number, options?: { receiver?: 0 | 1 }) => Promise<void>` — Select active fixed-edge slot
|
|
315
|
+
- `readScopeFixedEdge(rangeId: number, edgeSlot: number, options?: QueryOptions) => Promise<IcomScopeFixedEdgeInfo | null>` — Read fixed-edge frequencies using CI‑V `0x27 0x1E`
|
|
316
|
+
- `setScopeFixedEdge({ rangeId?, edgeSlot?, lowHz, highHz }) => Promise<IcomScopeFixedEdgeInfo>` — Set fixed-edge frequencies, auto-resolving `rangeId` from the current rig frequency when omitted
|
|
317
|
+
- `resolveScopeFrequencyRangeId(frequencyHz?: number) => Promise<number>` — Resolve ICOM fixed-edge range ID from a target or current operating frequency
|
|
318
|
+
- `getSpectrumMode()/setSpectrumMode()` / `getSpectrumSpan()/setSpectrumSpan()` / `getSpectrumEdgeSlot()/setSpectrumEdgeSlot()` / `getSpectrumFixedEdges()/setSpectrumFixedEdges()` — Hamlib-like convenience aliases over the scope-specific methods
|
|
319
|
+
- `getSpectrumDisplayState(options?: QueryOptions & { receiver?: 0 | 1 }) => Promise<IcomSpectrumDisplayState>` — Read a Hamlib-like normalized display state
|
|
320
|
+
- `configureSpectrumDisplay(config?: IcomSpectrumDisplayConfig) => Promise<IcomSpectrumDisplayState>` — Apply a normalized display config covering center/fixed modes
|
|
309
321
|
- `waitForScopeFrame(options?: QueryOptions) => Promise<IcomScopeFrame | null>` — Wait for the next complete scope frame
|
|
310
322
|
|
|
311
323
|
`IcomScopeFrame` shape:
|
|
@@ -328,7 +340,7 @@ interface IcomScopeFrame {
|
|
|
328
340
|
|
|
329
341
|
Current implementation notes:
|
|
330
342
|
|
|
331
|
-
- Currently implements
|
|
343
|
+
- Currently implements basic on/off controls, `0x27 0x15` span read/write, and `0x27 00 00` scope data capture
|
|
332
344
|
- The parsing layer is decoupled from the UDP session layer and only depends on complete CI‑V frames
|
|
333
345
|
- Frequency fields are currently parsed with `freqLen=5` by default
|
|
334
346
|
- LAN aggregate waterfall payload splitting is not implemented yet; standard segment input is supported
|
|
@@ -519,9 +531,8 @@ ICOM_IP=192.168.31.253 ICOM_PORT=50001 ICOM_USER=icom ICOM_PASS=icomicom npm tes
|
|
|
519
531
|
- Full token renewal loop and advanced status flag parsing simplified.
|
|
520
532
|
- Audio receive/playback is library‑only; playback is up to the integrator.
|
|
521
533
|
- Robust retransmit/multi‑retransmit handling can be extended.
|
|
522
|
-
- Scope support
|
|
534
|
+
- Scope support includes basic enable/disable, mode/span/edge/fixed-edge control, and standard `0x27 00 00` segment parsing.
|
|
523
535
|
- LAN aggregate waterfall payload splitting is not implemented yet.
|
|
524
|
-
- Scope control subcommands beyond basic enable/disable are not implemented yet.
|
|
525
536
|
|
|
526
537
|
## License
|
|
527
538
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IcomRigOptions, RigEventEmitter, IcomMode, ConnectorDataMode, SetModeOptions, QueryOptions, SwrReading, AlcReading, WlanLevelReading, LevelMeterReading, SquelchStatusReading, AudioSquelchReading, OvfStatusReading, PowerLevelReading, CompLevelReading, VoltageReading, CurrentReading, ConnectionState, ConnectionMonitorConfig, ConnectionPhase, ConnectionMetrics, DisconnectReason, DisconnectOptions, TunerStatusReading, LevelReading } from '../types';
|
|
1
|
+
import { IcomRigOptions, RigEventEmitter, IcomMode, ConnectorDataMode, SetModeOptions, QueryOptions, SwrReading, AlcReading, WlanLevelReading, LevelMeterReading, SquelchStatusReading, AudioSquelchReading, OvfStatusReading, PowerLevelReading, CompLevelReading, VoltageReading, CurrentReading, ConnectionState, ConnectionMonitorConfig, ConnectionPhase, ConnectionMetrics, DisconnectReason, DisconnectOptions, TunerStatusReading, LevelReading, IcomScopeSpanInfo, IcomScopeMode, IcomScopeModeInfo, IcomScopeEdgeInfo, IcomScopeFixedEdgeInfo, IcomSpectrumDisplayState, IcomSpectrumDisplayConfig } from '../types';
|
|
2
2
|
import { IcomCiv } from './IcomCiv';
|
|
3
3
|
import { IcomAudio } from './IcomAudio';
|
|
4
4
|
import { IcomScopeService } from '../scope/IcomScopeService';
|
|
@@ -77,6 +77,76 @@ export declare class IcomControl {
|
|
|
77
77
|
sendCiv(data: Buffer): void;
|
|
78
78
|
enableScope(): Promise<void>;
|
|
79
79
|
disableScope(): Promise<void>;
|
|
80
|
+
readScopeSpan(options?: QueryOptions & {
|
|
81
|
+
receiver?: 0 | 1;
|
|
82
|
+
}): Promise<IcomScopeSpanInfo | null>;
|
|
83
|
+
setScopeSpan(spanHz: number, options?: {
|
|
84
|
+
receiver?: 0 | 1;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
readScopeMode(options?: QueryOptions & {
|
|
87
|
+
receiver?: 0 | 1;
|
|
88
|
+
}): Promise<IcomScopeModeInfo | null>;
|
|
89
|
+
setScopeMode(mode: IcomScopeMode | 0 | 1 | 2 | 3, options?: {
|
|
90
|
+
receiver?: 0 | 1;
|
|
91
|
+
}): Promise<void>;
|
|
92
|
+
readScopeEdge(options?: QueryOptions & {
|
|
93
|
+
receiver?: 0 | 1;
|
|
94
|
+
}): Promise<IcomScopeEdgeInfo | null>;
|
|
95
|
+
setScopeEdge(edgeSlot: number, options?: {
|
|
96
|
+
receiver?: 0 | 1;
|
|
97
|
+
}): Promise<void>;
|
|
98
|
+
readScopeFixedEdge(rangeId: number, edgeSlot: number, options?: QueryOptions): Promise<IcomScopeFixedEdgeInfo | null>;
|
|
99
|
+
setScopeFixedEdge(options: {
|
|
100
|
+
rangeId?: number;
|
|
101
|
+
edgeSlot?: number;
|
|
102
|
+
lowHz: number;
|
|
103
|
+
highHz: number;
|
|
104
|
+
}): Promise<IcomScopeFixedEdgeInfo>;
|
|
105
|
+
resolveScopeFrequencyRangeId(frequencyHz?: number): Promise<number>;
|
|
106
|
+
getScopeSupportedEdgeSlots(): number[];
|
|
107
|
+
getSpectrumDisplayState(options?: QueryOptions & {
|
|
108
|
+
receiver?: 0 | 1;
|
|
109
|
+
}): Promise<IcomSpectrumDisplayState>;
|
|
110
|
+
configureSpectrumDisplay(config?: IcomSpectrumDisplayConfig): Promise<IcomSpectrumDisplayState>;
|
|
111
|
+
getSpectrumMode(options?: QueryOptions & {
|
|
112
|
+
receiver?: 0 | 1;
|
|
113
|
+
}): Promise<IcomScopeMode | null>;
|
|
114
|
+
setSpectrumMode(mode: IcomScopeMode, options?: {
|
|
115
|
+
receiver?: 0 | 1;
|
|
116
|
+
}): Promise<void>;
|
|
117
|
+
getSpectrumSpan(options?: QueryOptions & {
|
|
118
|
+
receiver?: 0 | 1;
|
|
119
|
+
}): Promise<number | null>;
|
|
120
|
+
setSpectrumSpan(spanHz: number, options?: {
|
|
121
|
+
receiver?: 0 | 1;
|
|
122
|
+
}): Promise<void>;
|
|
123
|
+
getSpectrumEdgeSlot(options?: QueryOptions & {
|
|
124
|
+
receiver?: 0 | 1;
|
|
125
|
+
}): Promise<number | null>;
|
|
126
|
+
setSpectrumEdgeSlot(edgeSlot: number, options?: {
|
|
127
|
+
receiver?: 0 | 1;
|
|
128
|
+
}): Promise<void>;
|
|
129
|
+
getSpectrumFixedEdges(options?: QueryOptions & {
|
|
130
|
+
receiver?: 0 | 1;
|
|
131
|
+
rangeId?: number;
|
|
132
|
+
edgeSlot?: number;
|
|
133
|
+
}): Promise<{
|
|
134
|
+
lowHz: number;
|
|
135
|
+
highHz: number;
|
|
136
|
+
rangeId: number;
|
|
137
|
+
edgeSlot: number;
|
|
138
|
+
} | null>;
|
|
139
|
+
setSpectrumFixedEdges(options: {
|
|
140
|
+
rangeId?: number;
|
|
141
|
+
edgeSlot?: number;
|
|
142
|
+
lowHz: number;
|
|
143
|
+
highHz: number;
|
|
144
|
+
}): Promise<{
|
|
145
|
+
lowHz: number;
|
|
146
|
+
highHz: number;
|
|
147
|
+
rangeId: number;
|
|
148
|
+
edgeSlot: number;
|
|
149
|
+
}>;
|
|
80
150
|
waitForScopeFrame(options?: QueryOptions): Promise<import("../types").IcomScopeFrame | null>;
|
|
81
151
|
/**
|
|
82
152
|
* Set PTT (Push-To-Talk) state
|
package/dist/rig/IcomControl.js
CHANGED
|
@@ -47,7 +47,45 @@ const bcd_1 = require("../utils/bcd");
|
|
|
47
47
|
const errors_1 = require("../utils/errors");
|
|
48
48
|
const smeter_1 = require("../utils/smeter");
|
|
49
49
|
const IcomScopeCommands_1 = require("../scope/IcomScopeCommands");
|
|
50
|
+
const IcomScopeParser_1 = require("../scope/IcomScopeParser");
|
|
50
51
|
const IcomScopeService_1 = require("../scope/IcomScopeService");
|
|
52
|
+
const DEFAULT_SCOPE_EDGE_SLOTS = [1, 2, 3, 4];
|
|
53
|
+
const DEFAULT_SCOPE_SPANS_HZ = [25000000, 10000000, 5000000, 2500000, 1000000, 500000, 250000, 100000, 50000, 25000, 10000, 5000, 2500];
|
|
54
|
+
const DEFAULT_SCOPE_FREQUENCY_RANGES = [
|
|
55
|
+
{ rangeId: 1, lowHz: 30000, highHz: 1600000 },
|
|
56
|
+
{ rangeId: 2, lowHz: 1600000, highHz: 2000000 },
|
|
57
|
+
{ rangeId: 3, lowHz: 2000000, highHz: 6000000 },
|
|
58
|
+
{ rangeId: 4, lowHz: 6000000, highHz: 8000000 },
|
|
59
|
+
{ rangeId: 5, lowHz: 8000000, highHz: 11000000 },
|
|
60
|
+
{ rangeId: 6, lowHz: 11000000, highHz: 15000000 },
|
|
61
|
+
{ rangeId: 7, lowHz: 15000000, highHz: 20000000 },
|
|
62
|
+
{ rangeId: 8, lowHz: 20000000, highHz: 22000000 },
|
|
63
|
+
{ rangeId: 9, lowHz: 22000000, highHz: 26000000 },
|
|
64
|
+
{ rangeId: 10, lowHz: 26000000, highHz: 30000000 },
|
|
65
|
+
{ rangeId: 11, lowHz: 30000000, highHz: 45000000 },
|
|
66
|
+
{ rangeId: 12, lowHz: 45000000, highHz: 60000000 },
|
|
67
|
+
{ rangeId: 13, lowHz: 60000000, highHz: 74800000 },
|
|
68
|
+
{ rangeId: 14, lowHz: 74800000, highHz: 108000000 },
|
|
69
|
+
{ rangeId: 15, lowHz: 108000000, highHz: 137000000 },
|
|
70
|
+
{ rangeId: 16, lowHz: 137000000, highHz: 200000000 },
|
|
71
|
+
{ rangeId: 17, lowHz: 400000000, highHz: 470000000 },
|
|
72
|
+
];
|
|
73
|
+
function modeCodeToName(mode) {
|
|
74
|
+
switch (mode) {
|
|
75
|
+
case 0: return 'center';
|
|
76
|
+
case 1: return 'fixed';
|
|
77
|
+
case 2: return 'scroll-center';
|
|
78
|
+
case 3: return 'scroll-fixed';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function modeNameToCode(mode) {
|
|
82
|
+
switch (mode) {
|
|
83
|
+
case 'center': return 0;
|
|
84
|
+
case 'fixed': return 1;
|
|
85
|
+
case 'scroll-center': return 2;
|
|
86
|
+
case 'scroll-fixed': return 3;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
51
89
|
class IcomControl {
|
|
52
90
|
constructor(options) {
|
|
53
91
|
this.ev = new events_1.EventEmitter();
|
|
@@ -527,6 +565,202 @@ class IcomControl {
|
|
|
527
565
|
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeDataOutput(ctrAddr, rigAddr, false));
|
|
528
566
|
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeDisplay(ctrAddr, rigAddr, false));
|
|
529
567
|
}
|
|
568
|
+
async readScopeSpan(options) {
|
|
569
|
+
const timeoutMs = options?.timeout ?? 3000;
|
|
570
|
+
const receiver = options?.receiver ?? 0;
|
|
571
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
572
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
573
|
+
const req = IcomScopeCommands_1.IcomScopeCommands.readScopeSpan(ctrAddr, rigAddr, receiver);
|
|
574
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x15, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
575
|
+
if (!resp || resp.length < 13) {
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
const spanHz = (0, IcomScopeParser_1.parseIcomBcdFreqLE)(resp.subarray(7, 12));
|
|
579
|
+
return {
|
|
580
|
+
receiver,
|
|
581
|
+
spanHz,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
async setScopeSpan(spanHz, options) {
|
|
585
|
+
const receiver = options?.receiver ?? 0;
|
|
586
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
587
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
588
|
+
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeSpan(ctrAddr, rigAddr, spanHz, receiver));
|
|
589
|
+
}
|
|
590
|
+
async readScopeMode(options) {
|
|
591
|
+
const timeoutMs = options?.timeout ?? 3000;
|
|
592
|
+
const receiver = options?.receiver ?? 0;
|
|
593
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
594
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
595
|
+
const req = IcomScopeCommands_1.IcomScopeCommands.readScopeMode(ctrAddr, rigAddr, receiver);
|
|
596
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x14, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
597
|
+
if (!resp || resp.length < 9) {
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
const mode = resp[7];
|
|
601
|
+
return {
|
|
602
|
+
receiver,
|
|
603
|
+
mode,
|
|
604
|
+
modeName: modeCodeToName(mode),
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
async setScopeMode(mode, options) {
|
|
608
|
+
const receiver = options?.receiver ?? 0;
|
|
609
|
+
const modeCode = typeof mode === 'string' ? modeNameToCode(mode) : mode;
|
|
610
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
611
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
612
|
+
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeMode(ctrAddr, rigAddr, modeCode, receiver));
|
|
613
|
+
}
|
|
614
|
+
async readScopeEdge(options) {
|
|
615
|
+
const timeoutMs = options?.timeout ?? 3000;
|
|
616
|
+
const receiver = options?.receiver ?? 0;
|
|
617
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
618
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
619
|
+
const req = IcomScopeCommands_1.IcomScopeCommands.readScopeEdge(ctrAddr, rigAddr, receiver);
|
|
620
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x16, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
621
|
+
if (!resp || resp.length < 9) {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
return {
|
|
625
|
+
receiver,
|
|
626
|
+
edgeSlot: resp[7],
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
async setScopeEdge(edgeSlot, options) {
|
|
630
|
+
const receiver = options?.receiver ?? 0;
|
|
631
|
+
const safeEdgeSlot = Math.max(1, Math.min(4, Math.trunc(edgeSlot)));
|
|
632
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
633
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
634
|
+
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeEdge(ctrAddr, rigAddr, safeEdgeSlot, receiver));
|
|
635
|
+
}
|
|
636
|
+
async readScopeFixedEdge(rangeId, edgeSlot, options) {
|
|
637
|
+
const timeoutMs = options?.timeout ?? 3000;
|
|
638
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
639
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
640
|
+
const req = IcomScopeCommands_1.IcomScopeCommands.readScopeFixedEdge(ctrAddr, rigAddr, rangeId, edgeSlot);
|
|
641
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x1e, rangeId, edgeSlot], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
642
|
+
if (!resp || resp.length < 18) {
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
rangeId,
|
|
647
|
+
edgeSlot,
|
|
648
|
+
lowHz: (0, IcomScopeParser_1.parseIcomBcdFreqLE)(resp.subarray(8, 13)),
|
|
649
|
+
highHz: (0, IcomScopeParser_1.parseIcomBcdFreqLE)(resp.subarray(13, 18)),
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
async setScopeFixedEdge(options) {
|
|
653
|
+
const rangeId = options.rangeId ?? await this.resolveScopeFrequencyRangeId();
|
|
654
|
+
const edgeInfo = options.edgeSlot
|
|
655
|
+
? { edgeSlot: options.edgeSlot }
|
|
656
|
+
: await this.readScopeEdge({ receiver: 0, timeout: 3000 });
|
|
657
|
+
const edgeSlot = edgeInfo?.edgeSlot ?? 1;
|
|
658
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
659
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
660
|
+
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeFixedEdge(ctrAddr, rigAddr, rangeId, edgeSlot, options.lowHz, options.highHz));
|
|
661
|
+
return {
|
|
662
|
+
rangeId,
|
|
663
|
+
edgeSlot,
|
|
664
|
+
lowHz: options.lowHz,
|
|
665
|
+
highHz: options.highHz,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
async resolveScopeFrequencyRangeId(frequencyHz) {
|
|
669
|
+
const targetFrequency = frequencyHz ?? await this.readOperatingFrequency({ timeout: 3000 });
|
|
670
|
+
if (!targetFrequency) {
|
|
671
|
+
throw new Error('Unable to resolve scope frequency range without operating frequency');
|
|
672
|
+
}
|
|
673
|
+
const matched = DEFAULT_SCOPE_FREQUENCY_RANGES.find((range) => targetFrequency >= range.lowHz && targetFrequency < range.highHz);
|
|
674
|
+
if (!matched) {
|
|
675
|
+
throw new Error(`No scope frequency range matches ${targetFrequency} Hz`);
|
|
676
|
+
}
|
|
677
|
+
return matched.rangeId;
|
|
678
|
+
}
|
|
679
|
+
getScopeSupportedEdgeSlots() {
|
|
680
|
+
return [...DEFAULT_SCOPE_EDGE_SLOTS];
|
|
681
|
+
}
|
|
682
|
+
async getSpectrumDisplayState(options) {
|
|
683
|
+
const receiver = options?.receiver ?? 0;
|
|
684
|
+
const [modeInfo, spanInfo, edgeInfo] = await Promise.all([
|
|
685
|
+
this.readScopeMode({ ...options, receiver }),
|
|
686
|
+
this.readScopeSpan({ ...options, receiver }),
|
|
687
|
+
this.readScopeEdge({ ...options, receiver }),
|
|
688
|
+
]);
|
|
689
|
+
let fixedEdgeInfo = null;
|
|
690
|
+
if (modeInfo?.modeName === 'fixed' || modeInfo?.modeName === 'scroll-fixed') {
|
|
691
|
+
try {
|
|
692
|
+
const rangeId = await this.resolveScopeFrequencyRangeId();
|
|
693
|
+
fixedEdgeInfo = await this.readScopeFixedEdge(rangeId, edgeInfo?.edgeSlot ?? 1, options);
|
|
694
|
+
}
|
|
695
|
+
catch (_) {
|
|
696
|
+
fixedEdgeInfo = null;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return {
|
|
700
|
+
mode: modeInfo?.modeName ?? null,
|
|
701
|
+
modeCode: modeInfo?.mode ?? null,
|
|
702
|
+
spanHz: spanInfo?.spanHz ?? (fixedEdgeInfo ? fixedEdgeInfo.highHz - fixedEdgeInfo.lowHz : null),
|
|
703
|
+
edgeSlot: edgeInfo?.edgeSlot ?? null,
|
|
704
|
+
edgeLowHz: fixedEdgeInfo?.lowHz ?? null,
|
|
705
|
+
edgeHighHz: fixedEdgeInfo?.highHz ?? null,
|
|
706
|
+
supportedModes: ['center', 'fixed', 'scroll-center', 'scroll-fixed'],
|
|
707
|
+
supportedSpans: [...DEFAULT_SCOPE_SPANS_HZ],
|
|
708
|
+
supportedEdgeSlots: this.getScopeSupportedEdgeSlots(),
|
|
709
|
+
supportsFixedEdges: true,
|
|
710
|
+
supportsEdgeSlotSelection: true,
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
async configureSpectrumDisplay(config = {}) {
|
|
714
|
+
const receiver = config.receiver ?? 0;
|
|
715
|
+
if (config.mode !== undefined) {
|
|
716
|
+
await this.setScopeMode(config.mode, { receiver });
|
|
717
|
+
}
|
|
718
|
+
if (config.edgeSlot !== undefined) {
|
|
719
|
+
await this.setScopeEdge(config.edgeSlot, { receiver });
|
|
720
|
+
}
|
|
721
|
+
if (config.spanHz !== undefined && (!config.mode || config.mode === 'center' || config.mode === 'scroll-center')) {
|
|
722
|
+
await this.setScopeSpan(config.spanHz, { receiver });
|
|
723
|
+
}
|
|
724
|
+
if (config.edgeLowHz !== undefined && config.edgeHighHz !== undefined && (!config.mode || config.mode === 'fixed' || config.mode === 'scroll-fixed')) {
|
|
725
|
+
await this.setScopeFixedEdge({
|
|
726
|
+
rangeId: config.rangeId,
|
|
727
|
+
edgeSlot: config.edgeSlot,
|
|
728
|
+
lowHz: config.edgeLowHz,
|
|
729
|
+
highHz: config.edgeHighHz,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
return this.getSpectrumDisplayState({ receiver, timeout: 3000 });
|
|
733
|
+
}
|
|
734
|
+
async getSpectrumMode(options) {
|
|
735
|
+
return (await this.readScopeMode(options))?.modeName ?? null;
|
|
736
|
+
}
|
|
737
|
+
async setSpectrumMode(mode, options) {
|
|
738
|
+
await this.setScopeMode(mode, options);
|
|
739
|
+
}
|
|
740
|
+
async getSpectrumSpan(options) {
|
|
741
|
+
return (await this.readScopeSpan(options))?.spanHz ?? null;
|
|
742
|
+
}
|
|
743
|
+
async setSpectrumSpan(spanHz, options) {
|
|
744
|
+
await this.setScopeSpan(spanHz, options);
|
|
745
|
+
}
|
|
746
|
+
async getSpectrumEdgeSlot(options) {
|
|
747
|
+
return (await this.readScopeEdge(options))?.edgeSlot ?? null;
|
|
748
|
+
}
|
|
749
|
+
async setSpectrumEdgeSlot(edgeSlot, options) {
|
|
750
|
+
await this.setScopeEdge(edgeSlot, options);
|
|
751
|
+
}
|
|
752
|
+
async getSpectrumFixedEdges(options) {
|
|
753
|
+
const rangeId = options?.rangeId ?? await this.resolveScopeFrequencyRangeId();
|
|
754
|
+
const edgeSlot = options?.edgeSlot ?? await this.getSpectrumEdgeSlot(options) ?? 1;
|
|
755
|
+
const info = await this.readScopeFixedEdge(rangeId, edgeSlot, options);
|
|
756
|
+
if (!info) {
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
return info;
|
|
760
|
+
}
|
|
761
|
+
async setSpectrumFixedEdges(options) {
|
|
762
|
+
return this.setScopeFixedEdge(options);
|
|
763
|
+
}
|
|
530
764
|
async waitForScopeFrame(options) {
|
|
531
765
|
const timeoutMs = options?.timeout ?? 3000;
|
|
532
766
|
return this.scope.waitForScopeFrame(timeoutMs);
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
export declare const IcomScopeCommands: {
|
|
2
2
|
setScopeDataOutput(ctrAddr: number, rigAddr: number, enabled: boolean): Buffer;
|
|
3
3
|
setScopeDisplay(ctrAddr: number, rigAddr: number, enabled: boolean): Buffer;
|
|
4
|
+
readScopeSpan(ctrAddr: number, rigAddr: number, receiver?: 0 | 1): Buffer;
|
|
5
|
+
setScopeSpan(ctrAddr: number, rigAddr: number, spanHz: number, receiver?: 0 | 1): Buffer;
|
|
6
|
+
readScopeMode(ctrAddr: number, rigAddr: number, receiver?: 0 | 1): Buffer;
|
|
7
|
+
setScopeMode(ctrAddr: number, rigAddr: number, mode: 0 | 1 | 2 | 3, receiver?: 0 | 1): Buffer;
|
|
8
|
+
readScopeEdge(ctrAddr: number, rigAddr: number, receiver?: 0 | 1): Buffer;
|
|
9
|
+
setScopeEdge(ctrAddr: number, rigAddr: number, edgeSlot: number, receiver?: 0 | 1): Buffer;
|
|
10
|
+
readScopeFixedEdge(ctrAddr: number, rigAddr: number, rangeId: number, edgeSlot: number): Buffer;
|
|
11
|
+
setScopeFixedEdge(ctrAddr: number, rigAddr: number, rangeId: number, edgeSlot: number, lowHz: number, highHz: number): Buffer;
|
|
12
|
+
encodeScopeSpanHz(spanHz: number): Buffer;
|
|
13
|
+
encodeScopeFreqHz(freqHz: number): Buffer;
|
|
4
14
|
};
|
|
@@ -11,5 +11,73 @@ exports.IcomScopeCommands = {
|
|
|
11
11
|
return Buffer.from([
|
|
12
12
|
0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x27, 0x10, enabled ? 0x01 : 0x00, 0xfd
|
|
13
13
|
]);
|
|
14
|
+
},
|
|
15
|
+
readScopeSpan(ctrAddr, rigAddr, receiver = 0) {
|
|
16
|
+
return Buffer.from([
|
|
17
|
+
0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x27, 0x15, receiver & 0xff, 0xfd
|
|
18
|
+
]);
|
|
19
|
+
},
|
|
20
|
+
setScopeSpan(ctrAddr, rigAddr, spanHz, receiver = 0) {
|
|
21
|
+
const bytes = exports.IcomScopeCommands.encodeScopeSpanHz(spanHz);
|
|
22
|
+
return Buffer.from([
|
|
23
|
+
0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x27, 0x15, receiver & 0xff, ...bytes, 0xfd
|
|
24
|
+
]);
|
|
25
|
+
},
|
|
26
|
+
readScopeMode(ctrAddr, rigAddr, receiver = 0) {
|
|
27
|
+
return Buffer.from([
|
|
28
|
+
0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x27, 0x14, receiver & 0xff, 0xfd
|
|
29
|
+
]);
|
|
30
|
+
},
|
|
31
|
+
setScopeMode(ctrAddr, rigAddr, mode, receiver = 0) {
|
|
32
|
+
return Buffer.from([
|
|
33
|
+
0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x27, 0x14, receiver & 0xff, mode & 0xff, 0xfd
|
|
34
|
+
]);
|
|
35
|
+
},
|
|
36
|
+
readScopeEdge(ctrAddr, rigAddr, receiver = 0) {
|
|
37
|
+
return Buffer.from([
|
|
38
|
+
0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x27, 0x16, receiver & 0xff, 0xfd
|
|
39
|
+
]);
|
|
40
|
+
},
|
|
41
|
+
setScopeEdge(ctrAddr, rigAddr, edgeSlot, receiver = 0) {
|
|
42
|
+
return Buffer.from([
|
|
43
|
+
0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x27, 0x16, receiver & 0xff, edgeSlot & 0xff, 0xfd
|
|
44
|
+
]);
|
|
45
|
+
},
|
|
46
|
+
readScopeFixedEdge(ctrAddr, rigAddr, rangeId, edgeSlot) {
|
|
47
|
+
return Buffer.from([
|
|
48
|
+
0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x27, 0x1e, rangeId & 0xff, edgeSlot & 0xff, 0xfd
|
|
49
|
+
]);
|
|
50
|
+
},
|
|
51
|
+
setScopeFixedEdge(ctrAddr, rigAddr, rangeId, edgeSlot, lowHz, highHz) {
|
|
52
|
+
return Buffer.from([
|
|
53
|
+
0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x27, 0x1e,
|
|
54
|
+
rangeId & 0xff,
|
|
55
|
+
edgeSlot & 0xff,
|
|
56
|
+
...exports.IcomScopeCommands.encodeScopeFreqHz(lowHz),
|
|
57
|
+
...exports.IcomScopeCommands.encodeScopeFreqHz(highHz),
|
|
58
|
+
0xfd
|
|
59
|
+
]);
|
|
60
|
+
},
|
|
61
|
+
encodeScopeSpanHz(spanHz) {
|
|
62
|
+
const safeSpanHz = Math.max(0, Math.round(spanHz));
|
|
63
|
+
const out = Buffer.alloc(5);
|
|
64
|
+
let remaining = safeSpanHz;
|
|
65
|
+
for (let i = 0; i < out.length; i++) {
|
|
66
|
+
const twoDigits = remaining % 100;
|
|
67
|
+
out[i] = ((((twoDigits / 10) | 0) & 0x0f) << 4) | (twoDigits % 10);
|
|
68
|
+
remaining = Math.floor(remaining / 100);
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
},
|
|
72
|
+
encodeScopeFreqHz(freqHz) {
|
|
73
|
+
const safeFreqHz = Math.max(0, Math.round(freqHz));
|
|
74
|
+
const out = Buffer.alloc(5);
|
|
75
|
+
let remaining = safeFreqHz;
|
|
76
|
+
for (let i = 0; i < out.length; i++) {
|
|
77
|
+
const twoDigits = remaining % 100;
|
|
78
|
+
out[i] = ((((twoDigits / 10) | 0) & 0x0f) << 4) | (twoDigits % 10);
|
|
79
|
+
remaining = Math.floor(remaining / 100);
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
14
82
|
}
|
|
15
83
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -110,6 +110,48 @@ export interface IcomScopeFrame {
|
|
|
110
110
|
rawCivPayloads: Buffer[];
|
|
111
111
|
transport: IcomScopeTransport;
|
|
112
112
|
}
|
|
113
|
+
export interface IcomScopeSpanInfo {
|
|
114
|
+
receiver: 0 | 1;
|
|
115
|
+
spanHz: number;
|
|
116
|
+
}
|
|
117
|
+
export type IcomScopeMode = 'center' | 'fixed' | 'scroll-center' | 'scroll-fixed';
|
|
118
|
+
export interface IcomScopeModeInfo {
|
|
119
|
+
receiver: 0 | 1;
|
|
120
|
+
mode: 0 | 1 | 2 | 3;
|
|
121
|
+
modeName: IcomScopeMode;
|
|
122
|
+
}
|
|
123
|
+
export interface IcomScopeEdgeInfo {
|
|
124
|
+
receiver: 0 | 1;
|
|
125
|
+
edgeSlot: number;
|
|
126
|
+
}
|
|
127
|
+
export interface IcomScopeFixedEdgeInfo {
|
|
128
|
+
rangeId: number;
|
|
129
|
+
edgeSlot: number;
|
|
130
|
+
lowHz: number;
|
|
131
|
+
highHz: number;
|
|
132
|
+
}
|
|
133
|
+
export interface IcomSpectrumDisplayState {
|
|
134
|
+
mode: IcomScopeMode | null;
|
|
135
|
+
modeCode: 0 | 1 | 2 | 3 | null;
|
|
136
|
+
spanHz: number | null;
|
|
137
|
+
edgeSlot: number | null;
|
|
138
|
+
edgeLowHz: number | null;
|
|
139
|
+
edgeHighHz: number | null;
|
|
140
|
+
supportedModes: IcomScopeMode[];
|
|
141
|
+
supportedSpans: number[];
|
|
142
|
+
supportedEdgeSlots: number[];
|
|
143
|
+
supportsFixedEdges: boolean;
|
|
144
|
+
supportsEdgeSlotSelection: boolean;
|
|
145
|
+
}
|
|
146
|
+
export interface IcomSpectrumDisplayConfig {
|
|
147
|
+
receiver?: 0 | 1;
|
|
148
|
+
mode?: IcomScopeMode;
|
|
149
|
+
spanHz?: number;
|
|
150
|
+
edgeSlot?: number;
|
|
151
|
+
rangeId?: number;
|
|
152
|
+
edgeLowHz?: number;
|
|
153
|
+
edgeHighHz?: number;
|
|
154
|
+
}
|
|
113
155
|
/**
|
|
114
156
|
* Result of a meter reading operation (SWR, ALC, etc.)
|
|
115
157
|
* @deprecated Use specific types like SwrReading, AlcReading instead
|