icom-wlan-node 0.5.0 → 0.6.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/README.md +73 -24
- package/dist/index.d.ts +4 -1
- package/dist/index.js +15 -1
- package/dist/rig/IcomCivFrame.d.ts +13 -0
- package/dist/rig/IcomCivFrame.js +72 -0
- package/dist/rig/IcomCivSpec.d.ts +54 -0
- package/dist/rig/IcomCivSpec.js +57 -0
- package/dist/rig/IcomControl.d.ts +85 -8
- package/dist/rig/IcomControl.js +374 -114
- package/dist/rig/IcomProfiles.d.ts +61 -0
- package/dist/rig/IcomProfiles.js +255 -0
- package/dist/rig/IcomRigCommands.d.ts +13 -15
- package/dist/rig/IcomRigCommands.js +66 -86
- package/dist/scope/IcomScopeCommands.d.ts +7 -0
- package/dist/scope/IcomScopeCommands.js +41 -22
- package/dist/types.d.ts +57 -8
- package/dist/utils/errors.d.ts +13 -0
- package/dist/utils/errors.js +17 -1
- package/package.json +2 -2
package/dist/rig/IcomControl.js
CHANGED
|
@@ -49,12 +49,34 @@ const smeter_1 = require("../utils/smeter");
|
|
|
49
49
|
const IcomScopeCommands_1 = require("../scope/IcomScopeCommands");
|
|
50
50
|
const IcomScopeParser_1 = require("../scope/IcomScopeParser");
|
|
51
51
|
const IcomScopeService_1 = require("../scope/IcomScopeService");
|
|
52
|
+
const IcomCivFrame_1 = require("./IcomCivFrame");
|
|
53
|
+
const IcomCivSpec_1 = require("./IcomCivSpec");
|
|
54
|
+
const IcomProfiles_1 = require("./IcomProfiles");
|
|
55
|
+
const DEFAULT_SCOPE_SPANS_HZ = [25000000, 10000000, 5000000, 2500000, 1000000, 500000, 250000, 100000, 50000, 25000, 10000, 5000, 2500];
|
|
56
|
+
function modeCodeToName(mode) {
|
|
57
|
+
switch (mode) {
|
|
58
|
+
case 0: return 'center';
|
|
59
|
+
case 1: return 'fixed';
|
|
60
|
+
case 2: return 'scroll-center';
|
|
61
|
+
case 3: return 'scroll-fixed';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function modeNameToCode(mode) {
|
|
65
|
+
switch (mode) {
|
|
66
|
+
case 'center': return 0;
|
|
67
|
+
case 'fixed': return 1;
|
|
68
|
+
case 'scroll-center': return 2;
|
|
69
|
+
case 'scroll-fixed': return 3;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
52
72
|
class IcomControl {
|
|
53
73
|
constructor(options) {
|
|
54
74
|
this.ev = new events_1.EventEmitter();
|
|
55
75
|
this.rigName = '';
|
|
56
76
|
this.macAddress = Buffer.alloc(6);
|
|
57
77
|
this.civAssembleBuf = Buffer.alloc(0); // CIV stream reassembler
|
|
78
|
+
this.activeProfile = (0, IcomProfiles_1.getProfileByModel)('generic-modern-icom');
|
|
79
|
+
this.lastFilter = 1;
|
|
58
80
|
// Connection state machine (replaces old fragmented state flags)
|
|
59
81
|
this.connectionSession = {
|
|
60
82
|
phase: types_1.ConnectionPhase.IDLE,
|
|
@@ -72,7 +94,8 @@ class IcomControl {
|
|
|
72
94
|
reconnectBaseDelay: 2000,
|
|
73
95
|
reconnectMaxDelay: 30000
|
|
74
96
|
};
|
|
75
|
-
this.options = options;
|
|
97
|
+
this.options = { ...options, model: options.model ?? 'auto' };
|
|
98
|
+
this.activeProfile = (0, IcomProfiles_1.resolveIcomProfile)({ requestedModel: this.options.model });
|
|
76
99
|
// Setup control session
|
|
77
100
|
this.sess = new Session_1.Session({ ip: options.control.ip, port: options.control.port }, {
|
|
78
101
|
onData: (data) => this.onData(data),
|
|
@@ -100,6 +123,25 @@ class IcomControl {
|
|
|
100
123
|
this.scope.on('scopeFrame', (frame) => this.ev.emit('scopeFrame', frame));
|
|
101
124
|
}
|
|
102
125
|
get events() { return this.ev; }
|
|
126
|
+
get profile() { return this.activeProfile; }
|
|
127
|
+
resolveActiveProfile(context = {}) {
|
|
128
|
+
const next = (0, IcomProfiles_1.resolveIcomProfile)({
|
|
129
|
+
requestedModel: this.options.model,
|
|
130
|
+
rigName: context.rigName ?? this.rigName,
|
|
131
|
+
civAddress: context.civAddress ?? this.civ.civAddress,
|
|
132
|
+
});
|
|
133
|
+
if (this.options.model && this.options.model !== 'auto' && context.civAddress !== undefined && next.defaultCivAddress !== (context.civAddress & 0xff)) {
|
|
134
|
+
(0, debug_1.dbg)(`Configured profile ${next.modelId} default CI-V 0x${next.defaultCivAddress.toString(16)} differs from radio CI-V 0x${(context.civAddress & 0xff).toString(16)}`);
|
|
135
|
+
}
|
|
136
|
+
if (next.modelId !== this.activeProfile.modelId) {
|
|
137
|
+
(0, debug_1.dbg)(`ICOM profile selected: ${next.profileName}`);
|
|
138
|
+
}
|
|
139
|
+
this.activeProfile = next;
|
|
140
|
+
this.lastFilter = next.defaultFilter;
|
|
141
|
+
}
|
|
142
|
+
getProfileModelId() {
|
|
143
|
+
return this.activeProfile.modelId;
|
|
144
|
+
}
|
|
103
145
|
// ============================================================================
|
|
104
146
|
// State Machine Management
|
|
105
147
|
// ============================================================================
|
|
@@ -538,10 +580,10 @@ class IcomControl {
|
|
|
538
580
|
if (!resp || resp.length < 13) {
|
|
539
581
|
return null;
|
|
540
582
|
}
|
|
541
|
-
const
|
|
583
|
+
const encodedSpanHz = (0, IcomScopeParser_1.parseIcomBcdFreqLE)(resp.subarray(7, 12));
|
|
542
584
|
return {
|
|
543
585
|
receiver,
|
|
544
|
-
spanHz,
|
|
586
|
+
spanHz: encodedSpanHz * 2,
|
|
545
587
|
};
|
|
546
588
|
}
|
|
547
589
|
async setScopeSpan(spanHz, options) {
|
|
@@ -550,6 +592,182 @@ class IcomControl {
|
|
|
550
592
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
551
593
|
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeSpan(ctrAddr, rigAddr, spanHz, receiver));
|
|
552
594
|
}
|
|
595
|
+
async readScopeMode(options) {
|
|
596
|
+
const timeoutMs = options?.timeout ?? 3000;
|
|
597
|
+
const receiver = options?.receiver ?? 0;
|
|
598
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
599
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
600
|
+
const req = IcomScopeCommands_1.IcomScopeCommands.readScopeMode(ctrAddr, rigAddr, receiver);
|
|
601
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x14, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
602
|
+
if (!resp || resp.length < 9) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
const mode = resp[7];
|
|
606
|
+
return {
|
|
607
|
+
receiver,
|
|
608
|
+
mode,
|
|
609
|
+
modeName: modeCodeToName(mode),
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
async setScopeMode(mode, options) {
|
|
613
|
+
const receiver = options?.receiver ?? 0;
|
|
614
|
+
const modeCode = typeof mode === 'string' ? modeNameToCode(mode) : mode;
|
|
615
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
616
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
617
|
+
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeMode(ctrAddr, rigAddr, modeCode, receiver));
|
|
618
|
+
}
|
|
619
|
+
async readScopeEdge(options) {
|
|
620
|
+
const timeoutMs = options?.timeout ?? 3000;
|
|
621
|
+
const receiver = options?.receiver ?? 0;
|
|
622
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
623
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
624
|
+
const req = IcomScopeCommands_1.IcomScopeCommands.readScopeEdge(ctrAddr, rigAddr, receiver);
|
|
625
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x16, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
626
|
+
if (!resp || resp.length < 9) {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
receiver,
|
|
631
|
+
edgeSlot: resp[7],
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
async setScopeEdge(edgeSlot, options) {
|
|
635
|
+
const receiver = options?.receiver ?? 0;
|
|
636
|
+
const slots = this.activeProfile.scopeEdgeSlots;
|
|
637
|
+
const maxSlot = slots.length ? Math.max(...slots) : 4;
|
|
638
|
+
const safeEdgeSlot = Math.max(1, Math.min(maxSlot, Math.trunc(edgeSlot)));
|
|
639
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
640
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
641
|
+
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeEdge(ctrAddr, rigAddr, safeEdgeSlot, receiver));
|
|
642
|
+
}
|
|
643
|
+
async readScopeFixedEdge(rangeId, edgeSlot, options) {
|
|
644
|
+
const timeoutMs = options?.timeout ?? 3000;
|
|
645
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
646
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
647
|
+
const req = IcomScopeCommands_1.IcomScopeCommands.readScopeFixedEdge(ctrAddr, rigAddr, rangeId, edgeSlot);
|
|
648
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x1e, rangeId, edgeSlot], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
649
|
+
if (!resp || resp.length < 18) {
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
return {
|
|
653
|
+
rangeId,
|
|
654
|
+
edgeSlot,
|
|
655
|
+
lowHz: (0, IcomScopeParser_1.parseIcomBcdFreqLE)(resp.subarray(8, 13)),
|
|
656
|
+
highHz: (0, IcomScopeParser_1.parseIcomBcdFreqLE)(resp.subarray(13, 18)),
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
async setScopeFixedEdge(options) {
|
|
660
|
+
const rangeId = options.rangeId ?? await this.resolveScopeFrequencyRangeId();
|
|
661
|
+
const edgeInfo = options.edgeSlot
|
|
662
|
+
? { edgeSlot: options.edgeSlot }
|
|
663
|
+
: await this.readScopeEdge({ receiver: 0, timeout: 3000 });
|
|
664
|
+
const edgeSlot = edgeInfo?.edgeSlot ?? 1;
|
|
665
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
666
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
667
|
+
this.sendCiv(IcomScopeCommands_1.IcomScopeCommands.setScopeFixedEdge(ctrAddr, rigAddr, rangeId, edgeSlot, options.lowHz, options.highHz));
|
|
668
|
+
return {
|
|
669
|
+
rangeId,
|
|
670
|
+
edgeSlot,
|
|
671
|
+
lowHz: options.lowHz,
|
|
672
|
+
highHz: options.highHz,
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
async resolveScopeFrequencyRangeId(frequencyHz) {
|
|
676
|
+
const targetFrequency = frequencyHz ?? await this.readOperatingFrequency({ timeout: 3000 });
|
|
677
|
+
if (!targetFrequency) {
|
|
678
|
+
throw new Error('Unable to resolve scope frequency range without operating frequency');
|
|
679
|
+
}
|
|
680
|
+
const matched = this.activeProfile.scopeRanges.find((range) => targetFrequency >= range.lowHz && targetFrequency < range.highHz);
|
|
681
|
+
if (!matched) {
|
|
682
|
+
throw new Error(`No scope frequency range matches ${targetFrequency} Hz`);
|
|
683
|
+
}
|
|
684
|
+
return matched.rangeId;
|
|
685
|
+
}
|
|
686
|
+
getScopeSupportedEdgeSlots() {
|
|
687
|
+
return [...this.activeProfile.scopeEdgeSlots];
|
|
688
|
+
}
|
|
689
|
+
async getSpectrumDisplayState(options) {
|
|
690
|
+
const receiver = options?.receiver ?? 0;
|
|
691
|
+
const [modeInfo, spanInfo, edgeInfo] = await Promise.all([
|
|
692
|
+
this.readScopeMode({ ...options, receiver }),
|
|
693
|
+
this.readScopeSpan({ ...options, receiver }),
|
|
694
|
+
this.readScopeEdge({ ...options, receiver }),
|
|
695
|
+
]);
|
|
696
|
+
let fixedEdgeInfo = null;
|
|
697
|
+
if (modeInfo?.modeName === 'fixed' || modeInfo?.modeName === 'scroll-fixed') {
|
|
698
|
+
try {
|
|
699
|
+
const rangeId = await this.resolveScopeFrequencyRangeId();
|
|
700
|
+
fixedEdgeInfo = await this.readScopeFixedEdge(rangeId, edgeInfo?.edgeSlot ?? 1, options);
|
|
701
|
+
}
|
|
702
|
+
catch (_) {
|
|
703
|
+
fixedEdgeInfo = null;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
mode: modeInfo?.modeName ?? null,
|
|
708
|
+
modeCode: modeInfo?.mode ?? null,
|
|
709
|
+
spanHz: spanInfo?.spanHz ?? (fixedEdgeInfo ? fixedEdgeInfo.highHz - fixedEdgeInfo.lowHz : null),
|
|
710
|
+
edgeSlot: edgeInfo?.edgeSlot ?? null,
|
|
711
|
+
edgeLowHz: fixedEdgeInfo?.lowHz ?? null,
|
|
712
|
+
edgeHighHz: fixedEdgeInfo?.highHz ?? null,
|
|
713
|
+
supportedModes: ['center', 'fixed', 'scroll-center', 'scroll-fixed'],
|
|
714
|
+
supportedSpans: [...DEFAULT_SCOPE_SPANS_HZ],
|
|
715
|
+
supportedEdgeSlots: this.getScopeSupportedEdgeSlots(),
|
|
716
|
+
supportsFixedEdges: this.activeProfile.scopeRanges.length > 0,
|
|
717
|
+
supportsEdgeSlotSelection: this.activeProfile.scopeEdgeSlots.length > 0,
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
async configureSpectrumDisplay(config = {}) {
|
|
721
|
+
const receiver = config.receiver ?? 0;
|
|
722
|
+
if (config.mode !== undefined) {
|
|
723
|
+
await this.setScopeMode(config.mode, { receiver });
|
|
724
|
+
}
|
|
725
|
+
if (config.edgeSlot !== undefined) {
|
|
726
|
+
await this.setScopeEdge(config.edgeSlot, { receiver });
|
|
727
|
+
}
|
|
728
|
+
if (config.spanHz !== undefined && (!config.mode || config.mode === 'center' || config.mode === 'scroll-center')) {
|
|
729
|
+
await this.setScopeSpan(config.spanHz, { receiver });
|
|
730
|
+
}
|
|
731
|
+
if (config.edgeLowHz !== undefined && config.edgeHighHz !== undefined && (!config.mode || config.mode === 'fixed' || config.mode === 'scroll-fixed')) {
|
|
732
|
+
await this.setScopeFixedEdge({
|
|
733
|
+
rangeId: config.rangeId,
|
|
734
|
+
edgeSlot: config.edgeSlot,
|
|
735
|
+
lowHz: config.edgeLowHz,
|
|
736
|
+
highHz: config.edgeHighHz,
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
return this.getSpectrumDisplayState({ receiver, timeout: 3000 });
|
|
740
|
+
}
|
|
741
|
+
async getSpectrumMode(options) {
|
|
742
|
+
return (await this.readScopeMode(options))?.modeName ?? null;
|
|
743
|
+
}
|
|
744
|
+
async setSpectrumMode(mode, options) {
|
|
745
|
+
await this.setScopeMode(mode, options);
|
|
746
|
+
}
|
|
747
|
+
async getSpectrumSpan(options) {
|
|
748
|
+
return (await this.readScopeSpan(options))?.spanHz ?? null;
|
|
749
|
+
}
|
|
750
|
+
async setSpectrumSpan(spanHz, options) {
|
|
751
|
+
await this.setScopeSpan(spanHz, options);
|
|
752
|
+
}
|
|
753
|
+
async getSpectrumEdgeSlot(options) {
|
|
754
|
+
return (await this.readScopeEdge(options))?.edgeSlot ?? null;
|
|
755
|
+
}
|
|
756
|
+
async setSpectrumEdgeSlot(edgeSlot, options) {
|
|
757
|
+
await this.setScopeEdge(edgeSlot, options);
|
|
758
|
+
}
|
|
759
|
+
async getSpectrumFixedEdges(options) {
|
|
760
|
+
const rangeId = options?.rangeId ?? await this.resolveScopeFrequencyRangeId();
|
|
761
|
+
const edgeSlot = options?.edgeSlot ?? await this.getSpectrumEdgeSlot(options) ?? 1;
|
|
762
|
+
const info = await this.readScopeFixedEdge(rangeId, edgeSlot, options);
|
|
763
|
+
if (!info) {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
return info;
|
|
767
|
+
}
|
|
768
|
+
async setSpectrumFixedEdges(options) {
|
|
769
|
+
return this.setScopeFixedEdge(options);
|
|
770
|
+
}
|
|
553
771
|
async waitForScopeFrame(options) {
|
|
554
772
|
const timeoutMs = options?.timeout ?? 3000;
|
|
555
773
|
return this.scope.waitForScopeFrame(timeoutMs);
|
|
@@ -589,7 +807,11 @@ class IcomControl {
|
|
|
589
807
|
async setFrequency(hz) {
|
|
590
808
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
591
809
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
592
|
-
this.
|
|
810
|
+
const bcdBytes = this.activeProfile.frequencyBcdBytes(hz);
|
|
811
|
+
const frame = this.activeProfile.supportsX25X26
|
|
812
|
+
? IcomRigCommands_1.IcomRigCommands.setSelectedFrequency(ctrAddr, rigAddr, hz, bcdBytes, 0)
|
|
813
|
+
: IcomRigCommands_1.IcomRigCommands.setFrequency(ctrAddr, rigAddr, hz, bcdBytes);
|
|
814
|
+
this.sendCiv(frame);
|
|
593
815
|
}
|
|
594
816
|
/**
|
|
595
817
|
* Set operating mode
|
|
@@ -605,11 +827,16 @@ class IcomControl {
|
|
|
605
827
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
606
828
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
607
829
|
const modeCode = typeof mode === 'string' ? (0, IcomConstants_1.getModeCode)(mode) : mode;
|
|
608
|
-
|
|
609
|
-
|
|
830
|
+
const filter = options?.filter ?? this.lastFilter ?? this.activeProfile.defaultFilter;
|
|
831
|
+
if (this.activeProfile.supportsX25X26 && this.activeProfile.modeWithFilter) {
|
|
832
|
+
this.sendCiv(IcomRigCommands_1.IcomRigCommands.setSelectedMode(ctrAddr, rigAddr, modeCode, !!options?.dataMode, filter, 0));
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
if (options?.dataMode && this.activeProfile.dataModeSupported) {
|
|
836
|
+
this.sendCiv(IcomRigCommands_1.IcomRigCommands.setOperationDataMode(ctrAddr, rigAddr, modeCode, filter));
|
|
610
837
|
}
|
|
611
838
|
else {
|
|
612
|
-
this.sendCiv(IcomRigCommands_1.IcomRigCommands.setMode(ctrAddr, rigAddr, modeCode));
|
|
839
|
+
this.sendCiv(IcomRigCommands_1.IcomRigCommands.setMode(ctrAddr, rigAddr, modeCode, filter));
|
|
613
840
|
}
|
|
614
841
|
}
|
|
615
842
|
/**
|
|
@@ -624,12 +851,16 @@ class IcomControl {
|
|
|
624
851
|
const timeoutMs = options?.timeout ?? 3000;
|
|
625
852
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
626
853
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
627
|
-
const
|
|
628
|
-
const
|
|
854
|
+
const useX25 = this.activeProfile.supportsX25X26;
|
|
855
|
+
const req = useX25
|
|
856
|
+
? IcomRigCommands_1.IcomRigCommands.readSelectedFrequency(ctrAddr, rigAddr, 0)
|
|
857
|
+
: IcomRigCommands_1.IcomRigCommands.readOperatingFrequency(ctrAddr, rigAddr);
|
|
858
|
+
const resp = await this.waitForCivFrame((frame) => useX25
|
|
859
|
+
? IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_SEND_SEL_FREQ, [0x00], ctrAddr, rigAddr)
|
|
860
|
+
: IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_RD_FREQ, [], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
629
861
|
if (!resp)
|
|
630
862
|
return null;
|
|
631
|
-
|
|
632
|
-
return freq;
|
|
863
|
+
return IcomControl.parseFrequencyReply(resp, useX25 ? 1 : 0);
|
|
633
864
|
}
|
|
634
865
|
/**
|
|
635
866
|
* Read current operating mode and filter
|
|
@@ -639,78 +870,56 @@ class IcomControl {
|
|
|
639
870
|
const timeoutMs = options?.timeout ?? 3000;
|
|
640
871
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
641
872
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
642
|
-
const
|
|
643
|
-
const
|
|
873
|
+
const useX26 = this.activeProfile.supportsX25X26 && this.activeProfile.modeWithFilter;
|
|
874
|
+
const req = useX26 ? IcomRigCommands_1.IcomRigCommands.readSelectedMode(ctrAddr, rigAddr, 0) : IcomRigCommands_1.IcomRigCommands.readOperatingMode(ctrAddr, rigAddr);
|
|
875
|
+
const resp = await this.waitForCivFrame((frame) => useX26
|
|
876
|
+
? IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_SEND_SEL_MODE, [0x00], ctrAddr, rigAddr)
|
|
877
|
+
: IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_RD_MODE, [], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
644
878
|
if (!resp)
|
|
645
879
|
return null;
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
const filter = resp.length > 6 ? resp[6] : undefined;
|
|
880
|
+
const mode = useX26 ? resp[6] : resp[5];
|
|
881
|
+
const dataMode = useX26 ? resp[7] !== 0x00 : undefined;
|
|
882
|
+
const filter = useX26 ? resp[8] : (resp.length > 6 ? resp[6] : undefined);
|
|
649
883
|
if (mode === undefined)
|
|
650
884
|
return null;
|
|
651
|
-
|
|
885
|
+
if (filter === 1 || filter === 2 || filter === 3)
|
|
886
|
+
this.lastFilter = filter;
|
|
652
887
|
const { getModeString, getFilterString } = await Promise.resolve().then(() => __importStar(require('./IcomConstants')));
|
|
653
888
|
const modeName = getModeString(mode);
|
|
654
889
|
const filterName = getFilterString(filter);
|
|
655
|
-
return { mode, filter, modeName, filterName };
|
|
890
|
+
return { mode, filter, modeName, filterName, dataMode };
|
|
656
891
|
}
|
|
657
892
|
/**
|
|
658
893
|
* Read current transmit frequency (when TX)
|
|
659
894
|
*/
|
|
660
895
|
async readTransmitFrequency(options) {
|
|
896
|
+
if (!this.activeProfile.supportsX1C03TxFreq)
|
|
897
|
+
return null;
|
|
661
898
|
const timeoutMs = options?.timeout ?? 3000;
|
|
662
899
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
663
900
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
664
901
|
const req = IcomRigCommands_1.IcomRigCommands.readTransmitFrequency(ctrAddr, rigAddr);
|
|
665
|
-
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame,
|
|
902
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_PTT, [IcomCivSpec_1.CIV.S_RD_TX_FREQ], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
666
903
|
if (!resp)
|
|
667
904
|
return null;
|
|
668
|
-
|
|
669
|
-
// Find 0x1c position and read next 2 bytes (0x03 + 5 BCD bytes)
|
|
670
|
-
let idx = resp.indexOf(0x1c, 4);
|
|
671
|
-
if (idx < 0 || idx + 6 >= resp.length)
|
|
672
|
-
idx = 4;
|
|
673
|
-
if (idx + 6 >= resp.length)
|
|
674
|
-
return null;
|
|
675
|
-
// After 0x1c 0x03, we expect 5 BCD bytes
|
|
676
|
-
if (resp[idx + 1] !== 0x03)
|
|
677
|
-
return null;
|
|
678
|
-
const d0 = resp[idx + 2];
|
|
679
|
-
const d1 = resp[idx + 3];
|
|
680
|
-
const d2 = resp[idx + 4];
|
|
681
|
-
const d3 = resp[idx + 5];
|
|
682
|
-
const d4 = resp[idx + 6];
|
|
683
|
-
const bcdToInt = (b) => ((b >> 4) & 0x0f) * 10 + (b & 0x0f);
|
|
684
|
-
const v0 = bcdToInt(d0);
|
|
685
|
-
const v1 = bcdToInt(d1);
|
|
686
|
-
const v2 = bcdToInt(d2);
|
|
687
|
-
const v3 = bcdToInt(d3);
|
|
688
|
-
const v4 = bcdToInt(d4);
|
|
689
|
-
const hz = v0 + v1 * 100 + v2 * 10000 + v3 * 1000000 + v4 * 100000000;
|
|
690
|
-
return hz;
|
|
905
|
+
return IcomControl.parseFrequencyReply(resp, 1);
|
|
691
906
|
}
|
|
692
|
-
|
|
693
|
-
* Read transceiver state (TX/RX) via 0x1A 0x00 0x48
|
|
694
|
-
* Note: Java comments mark this as not recommended; use with caution.
|
|
695
|
-
*/
|
|
696
|
-
async readTransceiverState(options) {
|
|
907
|
+
async readPtt(options) {
|
|
697
908
|
const timeoutMs = options?.timeout ?? 3000;
|
|
698
909
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
699
910
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
700
|
-
const req = IcomRigCommands_1.IcomRigCommands.
|
|
701
|
-
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame,
|
|
702
|
-
if (!resp)
|
|
911
|
+
const req = IcomRigCommands_1.IcomRigCommands.readPTT(ctrAddr, rigAddr);
|
|
912
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_PTT, [IcomCivSpec_1.CIV.S_PTT], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
913
|
+
if (!resp || resp.length < 7)
|
|
914
|
+
return null;
|
|
915
|
+
return resp[6] !== 0x00;
|
|
916
|
+
}
|
|
917
|
+
/** Read transceiver state (TX/RX) using standard Hamlib-aligned PTT status. */
|
|
918
|
+
async readTransceiverState(options) {
|
|
919
|
+
const ptt = await this.readPtt(options);
|
|
920
|
+
if (ptt === null)
|
|
703
921
|
return null;
|
|
704
|
-
|
|
705
|
-
const pos = 5 + 2; // after 0x1a [0x00,0x48]
|
|
706
|
-
const state = resp.length > pos ? resp[pos] : undefined;
|
|
707
|
-
if (state === undefined)
|
|
708
|
-
return 'UNKNOWN';
|
|
709
|
-
if (state === 0x01)
|
|
710
|
-
return 'TX';
|
|
711
|
-
if (state === 0x00)
|
|
712
|
-
return 'RX';
|
|
713
|
-
return 'UNKNOWN';
|
|
922
|
+
return ptt ? 'TX' : 'RX';
|
|
714
923
|
}
|
|
715
924
|
/**
|
|
716
925
|
* Read band edge data (0x02). Format may vary by rig; returns raw data bytes after command.
|
|
@@ -747,8 +956,8 @@ class IcomControl {
|
|
|
747
956
|
return null;
|
|
748
957
|
return {
|
|
749
958
|
raw,
|
|
750
|
-
swr: raw
|
|
751
|
-
alert: raw >=
|
|
959
|
+
swr: (0, IcomProfiles_1.interpolateCalibration)(raw, this.activeProfile.calibrations.swr),
|
|
960
|
+
alert: (0, IcomProfiles_1.interpolateCalibration)(raw, this.activeProfile.calibrations.swr) >= 3.0
|
|
752
961
|
};
|
|
753
962
|
}
|
|
754
963
|
/**
|
|
@@ -772,8 +981,8 @@ class IcomControl {
|
|
|
772
981
|
return null;
|
|
773
982
|
return {
|
|
774
983
|
raw,
|
|
775
|
-
percent: (raw
|
|
776
|
-
alert: raw >
|
|
984
|
+
percent: (0, IcomProfiles_1.interpolateCalibration)(raw, this.activeProfile.calibrations.alc),
|
|
985
|
+
alert: (0, IcomProfiles_1.interpolateCalibration)(raw, this.activeProfile.calibrations.alc) > 100
|
|
777
986
|
};
|
|
778
987
|
}
|
|
779
988
|
/**
|
|
@@ -787,12 +996,15 @@ class IcomControl {
|
|
|
787
996
|
* }
|
|
788
997
|
*/
|
|
789
998
|
async getConnectorWLanLevel(options) {
|
|
999
|
+
const ext = this.activeProfile.vendorExtensions.connectorWlanLevel;
|
|
1000
|
+
if (!ext)
|
|
1001
|
+
return null;
|
|
790
1002
|
const timeoutMs = options?.timeout ?? 3000;
|
|
791
1003
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
792
1004
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
793
|
-
const req = IcomRigCommands_1.IcomRigCommands.getConnectorWLanLevel(ctrAddr, rigAddr);
|
|
794
|
-
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame,
|
|
795
|
-
const raw = IcomControl.
|
|
1005
|
+
const req = IcomRigCommands_1.IcomRigCommands.getConnectorWLanLevel(ctrAddr, rigAddr, ext.subext);
|
|
1006
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, ext.command, [ext.subcmd, ...ext.subext], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
1007
|
+
const raw = IcomControl.extractTrailingBcd(resp, ext.dataBytes);
|
|
796
1008
|
if (raw === null)
|
|
797
1009
|
return null;
|
|
798
1010
|
return {
|
|
@@ -829,18 +1041,44 @@ class IcomControl {
|
|
|
829
1041
|
if (data.length === 0)
|
|
830
1042
|
return null;
|
|
831
1043
|
const raw = data[data.length - 1] & 0xff; // use low byte as 0-255 level
|
|
832
|
-
|
|
833
|
-
// Uses IC-705 calibration by default (can be extended to support other models)
|
|
834
|
-
return (0, smeter_1.rawToSMeter)(raw, 'IC-705');
|
|
1044
|
+
return (0, smeter_1.rawToSMeter)(raw, this.activeProfile.calibrations.sMeterModel);
|
|
835
1045
|
}
|
|
836
1046
|
/**
|
|
837
1047
|
* Set WLAN connector audio level
|
|
838
1048
|
* @param level - Audio level (0-255)
|
|
839
1049
|
*/
|
|
840
1050
|
async setConnectorWLanLevel(level) {
|
|
1051
|
+
const ext = this.activeProfile.vendorExtensions.connectorWlanLevel;
|
|
1052
|
+
if (!ext) {
|
|
1053
|
+
throw new errors_1.UnsupportedCommandError({ modelId: this.getProfileModelId(), commandName: 'setConnectorWLanLevel', civCommand: '0x1a/0x05', reason: 'No vendor WLAN level extension for active profile' });
|
|
1054
|
+
}
|
|
1055
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
1056
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
1057
|
+
this.sendCiv(IcomRigCommands_1.IcomRigCommands.setConnectorWLanLevel(ctrAddr, rigAddr, level, ext.subext));
|
|
1058
|
+
}
|
|
1059
|
+
async getUsbAfLevel(options) {
|
|
1060
|
+
const ext = this.activeProfile.extParams.usbAfLevel;
|
|
1061
|
+
if (!ext)
|
|
1062
|
+
return null;
|
|
1063
|
+
const timeoutMs = options?.timeout ?? 3000;
|
|
1064
|
+
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
1065
|
+
const rigAddr = this.civ.civAddress & 0xff;
|
|
1066
|
+
const req = IcomRigCommands_1.IcomRigCommands.getUsbAfLevel(ctrAddr, rigAddr, ext.subext);
|
|
1067
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, ext.command, [ext.subcmd, ...ext.subext], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
1068
|
+
const raw = IcomControl.extractTrailingBcd(resp, ext.dataBytes);
|
|
1069
|
+
if (raw === null)
|
|
1070
|
+
return null;
|
|
1071
|
+
return { raw, percent: (raw / 255) * 100 };
|
|
1072
|
+
}
|
|
1073
|
+
async setUsbAfLevel(level) {
|
|
1074
|
+
const ext = this.activeProfile.extParams.usbAfLevel;
|
|
1075
|
+
if (!ext) {
|
|
1076
|
+
throw new errors_1.UnsupportedCommandError({ modelId: this.getProfileModelId(), commandName: 'setUsbAfLevel', civCommand: '0x1a/0x05', reason: 'No USB AF level extension for active profile' });
|
|
1077
|
+
}
|
|
841
1078
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
842
1079
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
843
|
-
|
|
1080
|
+
const raw = Math.max(0, Math.min(255, Math.round(level)));
|
|
1081
|
+
this.sendCiv(IcomRigCommands_1.IcomRigCommands.setUsbAfLevel(ctrAddr, rigAddr, raw, ext.subext));
|
|
844
1082
|
}
|
|
845
1083
|
/**
|
|
846
1084
|
* Set connector data routing mode
|
|
@@ -850,10 +1088,14 @@ class IcomControl {
|
|
|
850
1088
|
* await rig.setConnectorDataMode('WLAN');
|
|
851
1089
|
*/
|
|
852
1090
|
async setConnectorDataMode(mode) {
|
|
1091
|
+
const ext = this.activeProfile.vendorExtensions.connectorDataMode;
|
|
1092
|
+
if (!ext) {
|
|
1093
|
+
throw new errors_1.UnsupportedCommandError({ modelId: this.getProfileModelId(), commandName: 'setConnectorDataMode', civCommand: '0x1a/0x05', reason: 'No vendor connector data-mode extension for active profile' });
|
|
1094
|
+
}
|
|
853
1095
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
854
1096
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
855
1097
|
const modeCode = typeof mode === 'string' ? (0, IcomConstants_1.getConnectorModeCode)(mode) : mode;
|
|
856
|
-
this.sendCiv(IcomRigCommands_1.IcomRigCommands.setConnectorDataMode(ctrAddr, rigAddr, modeCode));
|
|
1098
|
+
this.sendCiv(IcomRigCommands_1.IcomRigCommands.setConnectorDataMode(ctrAddr, rigAddr, modeCode, ext.subext));
|
|
857
1099
|
}
|
|
858
1100
|
/**
|
|
859
1101
|
* ==============================
|
|
@@ -861,7 +1103,7 @@ class IcomControl {
|
|
|
861
1103
|
* ==============================
|
|
862
1104
|
*/
|
|
863
1105
|
/**
|
|
864
|
-
* Read antenna tuner status (CI-V
|
|
1106
|
+
* Read antenna tuner status (CI-V 0x1C/0x01)
|
|
865
1107
|
* 00=OFF, 01=ON, 02=TUNING
|
|
866
1108
|
*/
|
|
867
1109
|
async readTunerStatus(options) {
|
|
@@ -869,10 +1111,10 @@ class IcomControl {
|
|
|
869
1111
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
870
1112
|
const rigAddr = this.civ.civAddress & 0xff;
|
|
871
1113
|
const req = IcomRigCommands_1.IcomRigCommands.getTunerStatus(ctrAddr, rigAddr);
|
|
872
|
-
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame,
|
|
1114
|
+
const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_PTT, [IcomCivSpec_1.CIV.S_ANT_TUN], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
|
|
873
1115
|
if (!resp)
|
|
874
1116
|
return null;
|
|
875
|
-
// Expect FE FE [ctr] [rig]
|
|
1117
|
+
// Expect FE FE [ctr] [rig] 0x1C 0x01 [status] FD
|
|
876
1118
|
const raw = resp.length > 6 ? (resp[6] & 0xff) : undefined;
|
|
877
1119
|
if (raw === undefined)
|
|
878
1120
|
return null;
|
|
@@ -880,7 +1122,7 @@ class IcomControl {
|
|
|
880
1122
|
return { raw, state };
|
|
881
1123
|
}
|
|
882
1124
|
/**
|
|
883
|
-
* Enable or disable internal antenna tuner (CI-V
|
|
1125
|
+
* Enable or disable internal antenna tuner (CI-V 0x1C/0x01)
|
|
884
1126
|
* @param enabled true to enable, false to disable
|
|
885
1127
|
*/
|
|
886
1128
|
async setTunerEnabled(enabled) {
|
|
@@ -889,7 +1131,7 @@ class IcomControl {
|
|
|
889
1131
|
this.sendCiv(IcomRigCommands_1.IcomRigCommands.setTunerEnabled(ctrAddr, rigAddr, enabled));
|
|
890
1132
|
}
|
|
891
1133
|
/**
|
|
892
|
-
* Start a manual tuning cycle (same as [TUNE] key) (CI-V
|
|
1134
|
+
* Start a manual tuning cycle (same as [TUNE] key) (CI-V 0x1C/0x01/0x02)
|
|
893
1135
|
*/
|
|
894
1136
|
async startManualTune() {
|
|
895
1137
|
const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
|
|
@@ -934,14 +1176,25 @@ class IcomControl {
|
|
|
934
1176
|
}
|
|
935
1177
|
/** Get microphone gain. Returns 0.0–1.0, or null on timeout. */
|
|
936
1178
|
async getMicGain(options) {
|
|
937
|
-
const v = await this.read0x14Level(
|
|
1179
|
+
const v = await this.read0x14Level(IcomCivSpec_1.CIV.S_LVL_MICGAIN, options);
|
|
938
1180
|
if (v === null)
|
|
939
1181
|
return null;
|
|
940
1182
|
return { raw: Math.round(v * 255), normalized: v };
|
|
941
1183
|
}
|
|
942
1184
|
/** Set microphone gain. Value 0.0–1.0. */
|
|
943
1185
|
setMicGain(value) {
|
|
944
|
-
this.write0x14Level(
|
|
1186
|
+
this.write0x14Level(IcomCivSpec_1.CIV.S_LVL_MICGAIN, value);
|
|
1187
|
+
}
|
|
1188
|
+
/** Get break-in delay. Returns 0.0–1.0, or null on timeout. */
|
|
1189
|
+
async getBreakInDelay(options) {
|
|
1190
|
+
const v = await this.read0x14Level(IcomCivSpec_1.CIV.S_LVL_BKINDL, options);
|
|
1191
|
+
if (v === null)
|
|
1192
|
+
return null;
|
|
1193
|
+
return { raw: Math.round(v * 255), normalized: v };
|
|
1194
|
+
}
|
|
1195
|
+
/** Set break-in delay. Value 0.0–1.0. */
|
|
1196
|
+
setBreakInDelay(value) {
|
|
1197
|
+
this.write0x14Level(IcomCivSpec_1.CIV.S_LVL_BKINDL, value);
|
|
945
1198
|
}
|
|
946
1199
|
/** Get noise blanker level. 0.0 = off, >0.0 = on with strength. */
|
|
947
1200
|
async getNBLevel(options) {
|
|
@@ -956,14 +1209,14 @@ class IcomControl {
|
|
|
956
1209
|
}
|
|
957
1210
|
/** Get noise reduction level. 0.0 = off, >0.0 = on with strength. */
|
|
958
1211
|
async getNRLevel(options) {
|
|
959
|
-
const v = await this.read0x14Level(
|
|
1212
|
+
const v = await this.read0x14Level(IcomCivSpec_1.CIV.S_LVL_NR, options);
|
|
960
1213
|
if (v === null)
|
|
961
1214
|
return null;
|
|
962
1215
|
return { raw: Math.round(v * 255), normalized: v };
|
|
963
1216
|
}
|
|
964
1217
|
/** Set noise reduction level. Value 0.0 (off) – 1.0. */
|
|
965
1218
|
setNRLevel(value) {
|
|
966
|
-
this.write0x14Level(
|
|
1219
|
+
this.write0x14Level(IcomCivSpec_1.CIV.S_LVL_NR, value);
|
|
967
1220
|
}
|
|
968
1221
|
/**
|
|
969
1222
|
* Read squelch status (noise/signal gate state)
|
|
@@ -1056,9 +1309,12 @@ class IcomControl {
|
|
|
1056
1309
|
const raw = IcomControl.extractMeterData(resp);
|
|
1057
1310
|
if (raw === null)
|
|
1058
1311
|
return null;
|
|
1312
|
+
const watts = (0, IcomProfiles_1.interpolateCalibration)(raw, this.activeProfile.calibrations.rfPowerWatts);
|
|
1313
|
+
const maxWatts = this.activeProfile.calibrations.rfPowerWatts[this.activeProfile.calibrations.rfPowerWatts.length - 1]?.value ?? 100;
|
|
1059
1314
|
return {
|
|
1060
1315
|
raw,
|
|
1061
|
-
percent:
|
|
1316
|
+
percent: maxWatts > 0 ? Math.min(100, (watts / maxWatts) * 100) : 0,
|
|
1317
|
+
watts
|
|
1062
1318
|
};
|
|
1063
1319
|
}
|
|
1064
1320
|
/**
|
|
@@ -1080,9 +1336,11 @@ class IcomControl {
|
|
|
1080
1336
|
const raw = IcomControl.extractMeterData(resp);
|
|
1081
1337
|
if (raw === null)
|
|
1082
1338
|
return null;
|
|
1339
|
+
const db = (0, IcomProfiles_1.interpolateCalibration)(raw, this.activeProfile.calibrations.compDb);
|
|
1083
1340
|
return {
|
|
1084
1341
|
raw,
|
|
1085
|
-
percent: (
|
|
1342
|
+
percent: (db / 30) * 100,
|
|
1343
|
+
db
|
|
1086
1344
|
};
|
|
1087
1345
|
}
|
|
1088
1346
|
/**
|
|
@@ -1106,7 +1364,7 @@ class IcomControl {
|
|
|
1106
1364
|
return null;
|
|
1107
1365
|
return {
|
|
1108
1366
|
raw,
|
|
1109
|
-
volts: (0,
|
|
1367
|
+
volts: (0, IcomProfiles_1.interpolateCalibration)(raw, this.activeProfile.calibrations.voltage)
|
|
1110
1368
|
};
|
|
1111
1369
|
}
|
|
1112
1370
|
/**
|
|
@@ -1130,7 +1388,7 @@ class IcomControl {
|
|
|
1130
1388
|
return null;
|
|
1131
1389
|
return {
|
|
1132
1390
|
raw,
|
|
1133
|
-
amps: (0,
|
|
1391
|
+
amps: (0, IcomProfiles_1.interpolateCalibration)(raw, this.activeProfile.calibrations.current)
|
|
1134
1392
|
};
|
|
1135
1393
|
}
|
|
1136
1394
|
static isReplyOf(frame, cmd, ctrAddr, rigAddr) {
|
|
@@ -1146,9 +1404,17 @@ class IcomControl {
|
|
|
1146
1404
|
static extractMeterData(frame) {
|
|
1147
1405
|
if (!frame || frame.length < 9)
|
|
1148
1406
|
return null;
|
|
1149
|
-
//
|
|
1150
|
-
|
|
1151
|
-
|
|
1407
|
+
// FE FE [ctr] [rig] 0x15 [sub] [bcd_hi] [bcd_lo] FD
|
|
1408
|
+
return (0, IcomCivFrame_1.decodeBcdBE)(frame.subarray(6, 8));
|
|
1409
|
+
}
|
|
1410
|
+
static extractTrailingBcd(frame, byteLength) {
|
|
1411
|
+
if (!frame || frame.length < 6 + byteLength)
|
|
1412
|
+
return null;
|
|
1413
|
+
const end = frame.length - 1;
|
|
1414
|
+
const start = end - byteLength;
|
|
1415
|
+
if (start < 5)
|
|
1416
|
+
return null;
|
|
1417
|
+
return (0, IcomCivFrame_1.decodeBcdBE)(frame.subarray(start, end));
|
|
1152
1418
|
}
|
|
1153
1419
|
static matchCommand(frame, cmd, tail) {
|
|
1154
1420
|
// FE FE ?? ?? cmd ... tail... FD
|
|
@@ -1218,34 +1484,23 @@ class IcomControl {
|
|
|
1218
1484
|
}, timeoutMs);
|
|
1219
1485
|
});
|
|
1220
1486
|
}
|
|
1221
|
-
|
|
1222
|
-
static parseIcomFreqFromReply(frame) {
|
|
1223
|
-
// Expect: FE FE [ctr] [rig] 0x03 [bcd0..bcd4] FD
|
|
1487
|
+
static parseFrequencyReply(frame, payloadOffsetAfterCommand, byteLength) {
|
|
1224
1488
|
if (!(frame && frame.length >= 7))
|
|
1225
1489
|
return null;
|
|
1226
|
-
if (frame[0] !== 0xfe || frame[1] !== 0xfe)
|
|
1490
|
+
if (frame[0] !== 0xfe || frame[1] !== 0xfe || frame[frame.length - 1] !== 0xfd)
|
|
1227
1491
|
return null;
|
|
1228
|
-
|
|
1492
|
+
const start = 5 + payloadOffsetAfterCommand;
|
|
1493
|
+
const maxBytes = frame.length - 1 - start;
|
|
1494
|
+
const len = byteLength ?? (maxBytes >= 6 ? 6 : 5);
|
|
1495
|
+
if (maxBytes < len || len <= 0)
|
|
1229
1496
|
return null;
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
if (
|
|
1497
|
+
return (0, IcomCivFrame_1.decodeFrequencyBcdLE)(frame.subarray(start, start + len));
|
|
1498
|
+
}
|
|
1499
|
+
// Parse standard CI-V 0x03 read-frequency replies.
|
|
1500
|
+
static parseIcomFreqFromReply(frame) {
|
|
1501
|
+
if (!IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_RD_FREQ, []))
|
|
1235
1502
|
return null;
|
|
1236
|
-
|
|
1237
|
-
const d1 = frame[idx + 2];
|
|
1238
|
-
const d2 = frame[idx + 3];
|
|
1239
|
-
const d3 = frame[idx + 4];
|
|
1240
|
-
const d4 = frame[idx + 5];
|
|
1241
|
-
const bcdToInt = (b) => ((b >> 4) & 0x0f) * 10 + (b & 0x0f);
|
|
1242
|
-
const v0 = bcdToInt(d0);
|
|
1243
|
-
const v1 = bcdToInt(d1);
|
|
1244
|
-
const v2 = bcdToInt(d2);
|
|
1245
|
-
const v3 = bcdToInt(d3);
|
|
1246
|
-
const v4 = bcdToInt(d4);
|
|
1247
|
-
const hz = v0 + v1 * 100 + v2 * 10000 + v3 * 1000000 + v4 * 100000000;
|
|
1248
|
-
return hz;
|
|
1503
|
+
return IcomControl.parseFrequencyReply(frame, 0);
|
|
1249
1504
|
}
|
|
1250
1505
|
sendAudioFloat32(samples, addLeadingBuffer = false) {
|
|
1251
1506
|
this.audio.enqueueFloat32(samples, addLeadingBuffer);
|
|
@@ -1380,11 +1635,15 @@ class IcomControl {
|
|
|
1380
1635
|
audioName: IcomPackets_1.RadioCapPacket.getAudioName(cap),
|
|
1381
1636
|
supportTX: IcomPackets_1.RadioCapPacket.getSupportTX(cap)
|
|
1382
1637
|
};
|
|
1383
|
-
if (info.civAddress != null)
|
|
1638
|
+
if (info.civAddress != null) {
|
|
1384
1639
|
this.civ.civAddress = info.civAddress;
|
|
1640
|
+
this.resolveActiveProfile({ civAddress: info.civAddress });
|
|
1641
|
+
info.modelId = this.activeProfile.modelId;
|
|
1642
|
+
info.profileName = this.activeProfile.profileName;
|
|
1643
|
+
}
|
|
1385
1644
|
if (info.supportTX != null)
|
|
1386
1645
|
this.civ.supportTX = info.supportTX;
|
|
1387
|
-
(0, debug_1.dbgV)('CAP <= civAddr=', info.civAddress, 'audioName=', info.audioName, 'supportTX=', info.supportTX);
|
|
1646
|
+
(0, debug_1.dbgV)('CAP <= civAddr=', info.civAddress, 'audioName=', info.audioName, 'supportTX=', info.supportTX, 'profile=', info.modelId);
|
|
1388
1647
|
this.ev.emit('capabilities', info);
|
|
1389
1648
|
}
|
|
1390
1649
|
break;
|
|
@@ -1396,7 +1655,8 @@ class IcomControl {
|
|
|
1396
1655
|
const busy = IcomPackets_1.ConnInfoPacket.getBusy(buf);
|
|
1397
1656
|
this.macAddress = IcomPackets_1.ConnInfoPacket.getMacAddress(buf);
|
|
1398
1657
|
this.rigName = IcomPackets_1.ConnInfoPacket.getRigName(buf);
|
|
1399
|
-
|
|
1658
|
+
this.resolveActiveProfile({ rigName: this.rigName });
|
|
1659
|
+
(0, debug_1.dbg)('CONNINFO <= busy=', busy, 'rigName=', this.rigName, 'profile=', this.activeProfile.modelId);
|
|
1400
1660
|
if (busy) {
|
|
1401
1661
|
(0, debug_1.dbg)('CONNINFO busy=true detected - likely reconnecting while rig still has old session');
|
|
1402
1662
|
(0, debug_1.dbg)('Sending ConnInfo reply anyway to allow STATUS packet delivery');
|