icom-wlan-node 0.6.1 → 0.6.3

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.
@@ -70,6 +70,78 @@ function modeNameToCode(mode) {
70
70
  case 'scroll-fixed': return 3;
71
71
  }
72
72
  }
73
+ const FUNCTION_SPECS = {
74
+ NB: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_NB },
75
+ NR: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_NR },
76
+ COMP: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_COMP },
77
+ VOX: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_VOX },
78
+ TONE: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_TONE },
79
+ TSQL: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_TSQL },
80
+ SBKIN: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_BKIN },
81
+ FBKIN: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_BKIN },
82
+ MON: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_MON },
83
+ ANF: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_ANF },
84
+ MN: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_MN },
85
+ LOCK: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_DIAL_LK },
86
+ RIT: { command: IcomCivSpec_1.CIV.C_CTL_RIT, subcmd: IcomCivSpec_1.CIV.S_RIT },
87
+ XIT: { command: IcomCivSpec_1.CIV.C_CTL_RIT, subcmd: IcomCivSpec_1.CIV.S_XIT },
88
+ TUNER: { command: IcomCivSpec_1.CIV.C_CTL_PTT, subcmd: IcomCivSpec_1.CIV.S_ANT_TUN },
89
+ APF: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_APF },
90
+ AFC: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_AFC },
91
+ VSC: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_VSC },
92
+ DUAL_WATCH: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_MEM_DUALMODE },
93
+ SATMODE: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_MEM_SATMODE },
94
+ SCOPE: { command: IcomCivSpec_1.CIV.C_CTL_SCP, subcmd: IcomCivSpec_1.CIV.S_SCP_STS },
95
+ SPECTRUM: { command: IcomCivSpec_1.CIV.C_CTL_SCP, subcmd: IcomCivSpec_1.CIV.S_SCP_DOP },
96
+ SPECTRUM_HOLD: { command: IcomCivSpec_1.CIV.C_CTL_SCP, subcmd: IcomCivSpec_1.CIV.S_SCP_HLD, payloadPrefix: [0], readPrefix: [0] },
97
+ OVF_STATUS: { command: IcomCivSpec_1.CIV.C_RD_SQSM, subcmd: IcomCivSpec_1.CIV.S_OVF },
98
+ DIGI_SEL: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_DIGISEL },
99
+ IPP: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_IPP },
100
+ TX_INHIBIT: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_TX_INHIBIT },
101
+ DPP: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_DPP },
102
+ };
103
+ function keySpeedWpmToRaw(wpm) {
104
+ const target = Math.max(6, Math.min(48, Math.round(wpm)));
105
+ return CW_LOOKUP.find(([, speed]) => speed === target)?.[0] ?? CW_LOOKUP[0][0];
106
+ }
107
+ function keySpeedRawToWpm(raw) {
108
+ return CW_LOOKUP.find(([rigValue]) => rigValue >= raw)?.[1] ?? 48;
109
+ }
110
+ const LEVEL_SPECS = {
111
+ AF: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_AF, dataBytes: 2, dataType: 'level' },
112
+ RF: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_RF, dataBytes: 2, dataType: 'level' },
113
+ SQL: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_SQL, dataBytes: 2, dataType: 'level' },
114
+ IF: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_IF, dataBytes: 2, dataType: 'level' },
115
+ APF: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_APF, dataBytes: 2, dataType: 'level' },
116
+ NR: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_NR, dataBytes: 2, dataType: 'level' },
117
+ NB: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_NB, dataBytes: 2, dataType: 'level' },
118
+ PBT_IN: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_PBTIN, dataBytes: 2, dataType: 'level' },
119
+ PBT_OUT: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_PBTOUT, dataBytes: 2, dataType: 'level' },
120
+ CWPITCH: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_CWPITCH, dataBytes: 2, dataType: 'int', publicToRaw: (hz) => Math.round((Math.max(300, Math.min(900, hz)) - 300) * 255 / 600), rawToPublic: (raw) => Math.round(300 + raw * 600 / 255) },
121
+ RFPOWER: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_RFPOWER, dataBytes: 2, dataType: 'level' },
122
+ MICGAIN: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_MICGAIN, dataBytes: 2, dataType: 'level' },
123
+ KEYSPD: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_KEYSPD, dataBytes: 2, dataType: 'int', publicToRaw: keySpeedWpmToRaw, rawToPublic: keySpeedRawToWpm },
124
+ NOTCHF_RAW: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_NOTCHF, dataBytes: 2, dataType: 'level' },
125
+ COMP: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_COMP, dataBytes: 2, dataType: 'level' },
126
+ BKINDL: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_BKINDL, dataBytes: 2, dataType: 'level' },
127
+ BALANCE: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_BALANCE, dataBytes: 2, dataType: 'level' },
128
+ VOXGAIN: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_VOXGAIN, dataBytes: 2, dataType: 'level' },
129
+ ANTIVOX: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_ANTIVOX, dataBytes: 2, dataType: 'level' },
130
+ MONITOR_GAIN: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_MON, dataBytes: 2, dataType: 'level' },
131
+ DRIVE_GAIN: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_DRIVE, dataBytes: 2, dataType: 'level' },
132
+ DIGI_SEL_LEVEL: { command: IcomCivSpec_1.CIV.C_CTL_LVL, subcmd: IcomCivSpec_1.CIV.S_LVL_DIGI, dataBytes: 2, dataType: 'level' },
133
+ AGC: { command: IcomCivSpec_1.CIV.C_CTL_FUNC, subcmd: IcomCivSpec_1.CIV.S_FUNC_AGC, dataBytes: 1, dataType: 'int' },
134
+ AGC_TIME: { command: IcomCivSpec_1.CIV.C_CTL_MEM, subcmd: 0x04, dataBytes: 1, dataType: 'int' },
135
+ };
136
+ const CW_LOOKUP = [
137
+ [0, 6], [7, 7], [12, 8], [19, 9], [25, 10], [31, 11], [37, 12], [43, 13],
138
+ [49, 14], [55, 15], [61, 16], [67, 17], [73, 18], [79, 19], [84, 20],
139
+ [91, 21], [97, 22], [103, 23], [108, 24], [114, 25], [121, 26], [128, 27],
140
+ [134, 28], [140, 29], [144, 30], [151, 31], [156, 32], [164, 33],
141
+ [169, 34], [175, 35], [182, 36], [188, 37], [192, 38], [199, 39],
142
+ [203, 40], [211, 41], [215, 42], [224, 43], [229, 44], [234, 45],
143
+ [239, 46], [244, 47], [250, 48],
144
+ ];
73
145
  class IcomControl {
74
146
  constructor(options) {
75
147
  this.ev = new events_1.EventEmitter();
@@ -78,6 +150,8 @@ class IcomControl {
78
150
  this.civAssembleBuf = Buffer.alloc(0); // CIV stream reassembler
79
151
  this.activeProfile = (0, IcomProfiles_1.getProfileByModel)('generic-modern-icom');
80
152
  this.lastFilter = 1;
153
+ this.cwQueue = Promise.resolve();
154
+ this.cwGeneration = 0;
81
155
  // Connection state machine (replaces old fragmented state flags)
82
156
  this.connectionSession = {
83
157
  phase: types_1.ConnectionPhase.IDLE,
@@ -1393,6 +1467,666 @@ class IcomControl {
1393
1467
  amps: (0, IcomProfiles_1.interpolateCalibration)(raw, this.activeProfile.calibrations.current)
1394
1468
  };
1395
1469
  }
1470
+ async getFunction(name, options) {
1471
+ if (!this.activeProfile.functions.includes(name))
1472
+ return null;
1473
+ const ext = this.activeProfile.extParamSpecs[name];
1474
+ if (ext) {
1475
+ const value = await this.readExtParamValue(ext, `func-ext:${name}`, options);
1476
+ return value === null ? null : value !== 0;
1477
+ }
1478
+ const spec = FUNCTION_SPECS[name];
1479
+ if (!spec)
1480
+ return null;
1481
+ const raw = await this.readFunctionRaw(name, spec, options);
1482
+ if (raw === null)
1483
+ return null;
1484
+ if (name === 'SBKIN')
1485
+ return raw === 1;
1486
+ if (name === 'FBKIN')
1487
+ return raw === 2;
1488
+ return raw !== 0;
1489
+ }
1490
+ setFunction(name, enabled) {
1491
+ if (!this.activeProfile.functions.includes(name)) {
1492
+ throw this.unsupported(name, 'setFunction', 'Function is not enabled for active profile');
1493
+ }
1494
+ const ext = this.activeProfile.extParamSpecs[name];
1495
+ if (ext) {
1496
+ this.writeExtParamValue(ext, enabled ? 1 : 0);
1497
+ return;
1498
+ }
1499
+ const spec = FUNCTION_SPECS[name];
1500
+ if (!spec || name === 'OVF_STATUS') {
1501
+ throw this.unsupported(name, 'setFunction', 'Function is read-only or has no CI-V writer');
1502
+ }
1503
+ let value = enabled ? 1 : 0;
1504
+ if (name === 'SBKIN')
1505
+ value = enabled ? 1 : 0;
1506
+ if (name === 'FBKIN')
1507
+ value = enabled ? 2 : 0;
1508
+ this.writeFunctionRaw(spec, value);
1509
+ }
1510
+ async getLevel(name, options) {
1511
+ if (!this.activeProfile.levels.includes(name))
1512
+ return null;
1513
+ const ext = this.activeProfile.extParamSpecs[name];
1514
+ if (ext) {
1515
+ return this.readExtParamValue(ext, `level-ext:${name}`, options);
1516
+ }
1517
+ const spec = LEVEL_SPECS[name];
1518
+ if (!spec)
1519
+ return null;
1520
+ const raw = await this.readLevelRaw(name, spec, options);
1521
+ if (raw === null)
1522
+ return null;
1523
+ return spec.rawToPublic ? spec.rawToPublic(raw) : spec.dataType === 'level' ? raw / 255 : raw;
1524
+ }
1525
+ setLevel(name, value) {
1526
+ if (!this.activeProfile.levels.includes(name)) {
1527
+ throw this.unsupported(name, 'setLevel', 'Level is not enabled for active profile');
1528
+ }
1529
+ const ext = this.activeProfile.extParamSpecs[name];
1530
+ if (ext) {
1531
+ this.writeExtParamValue(ext, value);
1532
+ return;
1533
+ }
1534
+ const spec = LEVEL_SPECS[name];
1535
+ if (!spec) {
1536
+ throw this.unsupported(name, 'setLevel', 'No level command mapping');
1537
+ }
1538
+ const raw = spec.publicToRaw
1539
+ ? spec.publicToRaw(value)
1540
+ : spec.dataType === 'level'
1541
+ ? Math.round(Math.max(0, Math.min(1, value)) * 255)
1542
+ : Math.round(value);
1543
+ this.writeLevelRaw(spec, raw);
1544
+ }
1545
+ async getParameter(name, options) {
1546
+ if (!this.activeProfile.parameters.includes(name))
1547
+ return null;
1548
+ const ext = this.activeProfile.extParamSpecs[name];
1549
+ if (!ext)
1550
+ return null;
1551
+ const value = await this.readExtParamValue(ext, `parm:${name}`, options);
1552
+ if (value === null)
1553
+ return null;
1554
+ return ext.dataType === 'bool' ? value !== 0 : value;
1555
+ }
1556
+ setParameter(name, value) {
1557
+ if (!this.activeProfile.parameters.includes(name)) {
1558
+ throw this.unsupported(name, 'setParameter', 'Parameter is not enabled for active profile');
1559
+ }
1560
+ const ext = this.activeProfile.extParamSpecs[name];
1561
+ if (!ext) {
1562
+ throw this.unsupported(name, 'setParameter', 'No parameter command mapping');
1563
+ }
1564
+ this.writeExtParamValue(ext, typeof value === 'boolean' ? (value ? 1 : 0) : value);
1565
+ }
1566
+ async getNoiseBlankerEnabled(options) { return this.getFunction('NB', options); }
1567
+ setNoiseBlankerEnabled(enabled) { this.setFunction('NB', enabled); }
1568
+ async getNoiseReductionEnabled(options) { return this.getFunction('NR', options); }
1569
+ setNoiseReductionEnabled(enabled) { this.setFunction('NR', enabled); }
1570
+ async getCompressorEnabled(options) { return this.getFunction('COMP', options); }
1571
+ setCompressorEnabled(enabled) { this.setFunction('COMP', enabled); }
1572
+ async getVoxEnabled(options) { return this.getFunction('VOX', options); }
1573
+ setVoxEnabled(enabled) { this.setFunction('VOX', enabled); }
1574
+ async getMonitorEnabled(options) { return this.getFunction('MON', options); }
1575
+ setMonitorEnabled(enabled) { this.setFunction('MON', enabled); }
1576
+ async getAutoNotchEnabled(options) { return this.getFunction('ANF', options); }
1577
+ setAutoNotchEnabled(enabled) { this.setFunction('ANF', enabled); }
1578
+ async getManualNotchEnabled(options) { return this.getFunction('MN', options); }
1579
+ setManualNotchEnabled(enabled) { this.setFunction('MN', enabled); }
1580
+ async getDialLockEnabled(options) { return this.getFunction('LOCK', options); }
1581
+ setDialLockEnabled(enabled) { this.setFunction('LOCK', enabled); }
1582
+ async getBreakInMode(options) {
1583
+ const spec = FUNCTION_SPECS.SBKIN;
1584
+ if (!spec || !this.activeProfile.functions.includes('SBKIN'))
1585
+ return null;
1586
+ const raw = await this.readFunctionRaw('SBKIN', spec, options);
1587
+ if (raw === null)
1588
+ return null;
1589
+ return raw === 1 ? 'semi' : raw === 2 ? 'full' : 'off';
1590
+ }
1591
+ setBreakInMode(mode) {
1592
+ const spec = FUNCTION_SPECS.SBKIN;
1593
+ if (!spec || !this.activeProfile.functions.includes('SBKIN')) {
1594
+ throw this.unsupported('SBKIN', 'setBreakInMode', 'Break-in function is not enabled for active profile');
1595
+ }
1596
+ this.writeFunctionRaw(spec, mode === 'semi' ? 1 : mode === 'full' ? 2 : 0);
1597
+ }
1598
+ async getRFGain(options) { return this.getLevel('RF', options); }
1599
+ setRFGain(value) { this.setLevel('RF', value); }
1600
+ async getIFShift(options) { return this.getLevel('IF', options); }
1601
+ setIFShift(value) { this.setLevel('IF', value); }
1602
+ async getPbtIn(options) { return this.getLevel('PBT_IN', options); }
1603
+ setPbtIn(value) { this.setLevel('PBT_IN', value); }
1604
+ async getPbtOut(options) { return this.getLevel('PBT_OUT', options); }
1605
+ setPbtOut(value) { this.setLevel('PBT_OUT', value); }
1606
+ async getCwPitch(options) { return this.getLevel('CWPITCH', options); }
1607
+ setCwPitch(hz) { this.setLevel('CWPITCH', hz); }
1608
+ async getKeySpeed(options) { return this.getLevel('KEYSPD', options); }
1609
+ setKeySpeed(wpm) { this.setLevel('KEYSPD', wpm); }
1610
+ async sendMorse(text, options = {}) {
1611
+ const normalized = IcomControl.normalizeMorseText(text);
1612
+ if (normalized.length === 0)
1613
+ return;
1614
+ const generation = this.cwGeneration;
1615
+ return this.enqueueCw(async () => {
1616
+ if (generation !== this.cwGeneration)
1617
+ return;
1618
+ if (!this.activeProfile.cw.sendMorse) {
1619
+ throw this.unsupported('SEND_MORSE', 'sendMorse', 'CW text sending is not enabled for active profile');
1620
+ }
1621
+ const timeoutMs = options.timeout ?? 3000;
1622
+ if (options.checkMode !== false) {
1623
+ const mode = await this.readOperatingMode({ timeout: timeoutMs });
1624
+ if (!mode || (mode.mode !== IcomConstants_1.MODE_MAP.CW && mode.mode !== IcomConstants_1.MODE_MAP.CW_R)) {
1625
+ throw new Error(`CW 0x17 sendMorse requires CW/CW_R mode; current mode is ${mode?.modeName ?? mode?.mode ?? 'unknown'}`);
1626
+ }
1627
+ }
1628
+ const profileMax = Math.max(1, Math.min(30, this.activeProfile.cw.maxChunkLength || 30));
1629
+ const requested = Number.isFinite(options.chunkLength) ? Math.floor(options.chunkLength) : profileMax;
1630
+ const chunkLength = Math.max(1, Math.min(30, profileMax, requested));
1631
+ const interChunkDelayMs = Number.isFinite(options.interChunkDelayMs)
1632
+ ? Math.max(0, Math.floor(options.interChunkDelayMs))
1633
+ : 0;
1634
+ const bytes = Buffer.from(normalized, 'ascii');
1635
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1636
+ const rigAddr = this.civ.civAddress & 0xff;
1637
+ for (let offset = 0, chunkIndex = 1; offset < bytes.length; offset += chunkLength, chunkIndex++) {
1638
+ if (generation !== this.cwGeneration)
1639
+ return;
1640
+ const chunk = bytes.subarray(offset, Math.min(offset + chunkLength, bytes.length));
1641
+ const frame = IcomRigCommands_1.IcomRigCommands.sendMorseText(ctrAddr, rigAddr, chunk);
1642
+ await this.sendCwFrameAndWaitForAck(frame, timeoutMs, `chunk ${chunkIndex}`);
1643
+ if (generation !== this.cwGeneration)
1644
+ return;
1645
+ if (interChunkDelayMs > 0 && offset + chunkLength < bytes.length) {
1646
+ await new Promise((resolve) => setTimeout(resolve, interChunkDelayMs));
1647
+ }
1648
+ }
1649
+ });
1650
+ }
1651
+ sendCwText(text, options) {
1652
+ return this.sendMorse(text, options);
1653
+ }
1654
+ async stopMorse(options = {}) {
1655
+ this.cwGeneration += 1;
1656
+ return this.enqueueCw(async () => {
1657
+ if (!this.activeProfile.cw.sendMorse) {
1658
+ throw this.unsupported('SEND_MORSE', 'stopMorse', 'CW text sending is not enabled for active profile');
1659
+ }
1660
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1661
+ const rigAddr = this.civ.civAddress & 0xff;
1662
+ await this.sendCwFrameAndWaitForAck(IcomRigCommands_1.IcomRigCommands.stopMorse(ctrAddr, rigAddr), options.timeout ?? 3000, 'stop');
1663
+ });
1664
+ }
1665
+ async getNotchRaw(options) { return this.getLevel('NOTCHF_RAW', options); }
1666
+ setNotchRaw(value) { this.setLevel('NOTCHF_RAW', value); }
1667
+ async getCompressionLevel(options) { return this.getLevel('COMP', options); }
1668
+ setCompressionLevel(value) { this.setLevel('COMP', value); }
1669
+ async getMonitorGain(options) { return this.getLevel('MONITOR_GAIN', options); }
1670
+ setMonitorGain(value) { this.setLevel('MONITOR_GAIN', value); }
1671
+ async getVoxGain(options) { return this.getLevel('VOXGAIN', options); }
1672
+ setVoxGain(value) { this.setLevel('VOXGAIN', value); }
1673
+ async getAntiVox(options) { return this.getLevel('ANTIVOX', options); }
1674
+ setAntiVox(value) { this.setLevel('ANTIVOX', value); }
1675
+ async getRitOffset(options) { return this.readRitOffset(options); }
1676
+ setRitOffset(offsetHz) { this.writeRitOffset(offsetHz); }
1677
+ async getXitOffset(options) { return this.readRitOffset(options); }
1678
+ setXitOffset(offsetHz) { this.writeRitOffset(offsetHz); }
1679
+ async getRitEnabled(options) { return this.getFunction('RIT', options); }
1680
+ setRitEnabled(enabled) { this.setFunction('RIT', enabled); }
1681
+ async getXitEnabled(options) { return this.getFunction('XIT', options); }
1682
+ setXitEnabled(enabled) { this.setFunction('XIT', enabled); }
1683
+ async getVfo(options) {
1684
+ const timeoutMs = options?.timeout ?? 3000;
1685
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1686
+ const rigAddr = this.civ.civAddress & 0xff;
1687
+ const req = IcomRigCommands_1.IcomRigCommands.readCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_SET_VFO, IcomCivSpec_1.CIV.S_BAND_SEL);
1688
+ const resp = await this.waitForCivFrame('vfo:0x07:0xd2', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_SET_VFO, [IcomCivSpec_1.CIV.S_BAND_SEL], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1689
+ if (!resp || resp.length < 8)
1690
+ return null;
1691
+ return resp[6] === 0 ? 'MAIN' : 'SUB';
1692
+ }
1693
+ setVfo(vfo) {
1694
+ const subcmd = this.vfoToSubcmd(vfo);
1695
+ if (subcmd === null) {
1696
+ throw this.unsupported(vfo, 'setVfo', 'VFO cannot be directly selected');
1697
+ }
1698
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1699
+ const rigAddr = this.civ.civAddress & 0xff;
1700
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_SET_VFO, subcmd));
1701
+ }
1702
+ vfoOperation(op) {
1703
+ if (!this.activeProfile.vfoOps.includes(op)) {
1704
+ throw this.unsupported(op, 'vfoOperation', 'VFO operation is not enabled for active profile');
1705
+ }
1706
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1707
+ const rigAddr = this.civ.civAddress & 0xff;
1708
+ switch (op) {
1709
+ case 'copy':
1710
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_SET_VFO, IcomCivSpec_1.CIV.S_BTOA));
1711
+ return;
1712
+ case 'exchange':
1713
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_SET_VFO, IcomCivSpec_1.CIV.S_XCHNG));
1714
+ return;
1715
+ case 'from-vfo':
1716
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_WR_MEM, undefined));
1717
+ return;
1718
+ case 'to-vfo':
1719
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_MEM2VFO, undefined));
1720
+ return;
1721
+ case 'memory-clear':
1722
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_CLR_MEM, undefined));
1723
+ return;
1724
+ case 'tune':
1725
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.startManualTune(ctrAddr, rigAddr));
1726
+ return;
1727
+ }
1728
+ }
1729
+ async getSplitEnabled(options) {
1730
+ const raw = await this.readSplitRaw(options);
1731
+ if (raw === null)
1732
+ return null;
1733
+ return raw === IcomCivSpec_1.CIV.S_SPLT_ON;
1734
+ }
1735
+ setSplitEnabled(enabled) {
1736
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1737
+ const rigAddr = this.civ.civAddress & 0xff;
1738
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.setSplit(ctrAddr, rigAddr, enabled));
1739
+ }
1740
+ async getSplitFrequency(options) {
1741
+ const timeoutMs = options?.timeout ?? 3000;
1742
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1743
+ const rigAddr = this.civ.civAddress & 0xff;
1744
+ const req = IcomRigCommands_1.IcomRigCommands.readSelectedFrequency(ctrAddr, rigAddr, 1);
1745
+ const resp = await this.waitForCivFrame('freq:0x25:1', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_SEND_SEL_FREQ, [0x01], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1746
+ return resp ? IcomControl.parseFrequencyReply(resp, 1) : null;
1747
+ }
1748
+ setSplitFrequency(hz) {
1749
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1750
+ const rigAddr = this.civ.civAddress & 0xff;
1751
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.setSelectedFrequency(ctrAddr, rigAddr, hz, this.activeProfile.frequencyBcdBytes(hz), 1));
1752
+ }
1753
+ async getSplitMode(options) {
1754
+ const timeoutMs = options?.timeout ?? 3000;
1755
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1756
+ const rigAddr = this.civ.civAddress & 0xff;
1757
+ const req = IcomRigCommands_1.IcomRigCommands.readSelectedMode(ctrAddr, rigAddr, 1);
1758
+ const resp = await this.waitForCivFrame('mode:0x26:1', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_SEND_SEL_MODE, [0x01], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1759
+ if (!resp || resp.length < 10)
1760
+ return null;
1761
+ const { getModeString, getFilterString } = await Promise.resolve().then(() => __importStar(require('./IcomConstants')));
1762
+ const mode = resp[6];
1763
+ const dataMode = resp[7] !== 0x00;
1764
+ const filter = resp[8];
1765
+ return { mode, filter, dataMode, modeName: getModeString(mode), filterName: getFilterString(filter) };
1766
+ }
1767
+ setSplitMode(mode, options) {
1768
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1769
+ const rigAddr = this.civ.civAddress & 0xff;
1770
+ const modeCode = typeof mode === 'string' ? (0, IcomConstants_1.getModeCode)(mode) : mode;
1771
+ const filter = options?.filter ?? this.lastFilter ?? this.activeProfile.defaultFilter;
1772
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.setSelectedMode(ctrAddr, rigAddr, modeCode, !!options?.dataMode, filter, 1));
1773
+ }
1774
+ async getTuningStep(options) {
1775
+ const timeoutMs = options?.timeout ?? 3000;
1776
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1777
+ const rigAddr = this.civ.civAddress & 0xff;
1778
+ const req = IcomRigCommands_1.IcomRigCommands.readTuningStep(ctrAddr, rigAddr);
1779
+ const resp = await this.waitForCivFrame('ts:0x10', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_SET_TS, [], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1780
+ if (!resp || resp.length < 7)
1781
+ return null;
1782
+ return this.activeProfile.tuningSteps.find((step) => step.code === resp[5])?.hz ?? null;
1783
+ }
1784
+ setTuningStep(hz) {
1785
+ const matched = this.activeProfile.tuningSteps.find((step) => step.hz === hz);
1786
+ if (!matched) {
1787
+ throw this.unsupported(String(hz), 'setTuningStep', 'Tuning step is not enabled for active profile');
1788
+ }
1789
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1790
+ const rigAddr = this.civ.civAddress & 0xff;
1791
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.setTuningStep(ctrAddr, rigAddr, matched.code));
1792
+ }
1793
+ async getRepeaterShift(options) {
1794
+ const raw = await this.readSplitRaw(options);
1795
+ if (raw === null)
1796
+ return null;
1797
+ if (raw === IcomCivSpec_1.CIV.S_DUP_M)
1798
+ return 'minus';
1799
+ if (raw === IcomCivSpec_1.CIV.S_DUP_P)
1800
+ return 'plus';
1801
+ return 'none';
1802
+ }
1803
+ setRepeaterShift(shift) {
1804
+ if (!this.activeProfile.repeater) {
1805
+ throw this.unsupported(shift, 'setRepeaterShift', 'Repeater controls are not enabled for active profile');
1806
+ }
1807
+ const code = shift === 'minus' ? IcomCivSpec_1.CIV.S_DUP_M : shift === 'plus' ? IcomCivSpec_1.CIV.S_DUP_P : IcomCivSpec_1.CIV.S_DUP_OFF;
1808
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1809
+ const rigAddr = this.civ.civAddress & 0xff;
1810
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.setRepeaterShift(ctrAddr, rigAddr, code));
1811
+ }
1812
+ async getRepeaterOffset(options) {
1813
+ const timeoutMs = options?.timeout ?? 3000;
1814
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1815
+ const rigAddr = this.civ.civAddress & 0xff;
1816
+ const req = IcomRigCommands_1.IcomRigCommands.readCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_RD_OFFS);
1817
+ const resp = await this.waitForCivFrame('rptr:offset', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_RD_OFFS, [], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1818
+ if (!resp || resp.length < 9)
1819
+ return null;
1820
+ return (0, IcomCivFrame_1.decodeFrequencyBcdLE)(resp.subarray(5, 8)) * 100;
1821
+ }
1822
+ setRepeaterOffset(offsetHz) {
1823
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1824
+ const rigAddr = this.civ.civAddress & 0xff;
1825
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_SET_OFFS, undefined, (0, IcomCivFrame_1.encodeFrequencyBcdLE)(Math.round(offsetHz / 100), 3)));
1826
+ }
1827
+ async getToneFrequency(options) { return this.readTone(IcomCivSpec_1.CIV.S_TONE_RPTR, options); }
1828
+ setToneFrequency(hz) { this.writeTone(IcomCivSpec_1.CIV.S_TONE_RPTR, hz); }
1829
+ async getToneSquelchFrequency(options) { return this.readTone(IcomCivSpec_1.CIV.S_TONE_SQL, options); }
1830
+ setToneSquelchFrequency(hz) { this.writeTone(IcomCivSpec_1.CIV.S_TONE_SQL, hz); }
1831
+ async getBeepEnabled(options) { return this.getParameter('BEEP', options); }
1832
+ setBeepEnabled(enabled) { this.setParameter('BEEP', enabled); }
1833
+ async getBacklight(options) { return this.getParameter('BACKLIGHT', options); }
1834
+ setBacklight(value) { this.setParameter('BACKLIGHT', value); }
1835
+ async getScreenSaver(options) { return this.getParameter('SCREENSAVER', options); }
1836
+ setScreenSaver(value) { this.setParameter('SCREENSAVER', value); }
1837
+ async getKeyerType(options) { return this.getParameter('KEYERTYPE', options); }
1838
+ setKeyerType(value) { this.setParameter('KEYERTYPE', value); }
1839
+ async getAudioIfMode(options) {
1840
+ for (const source of ['wlan', 'lan', 'acc']) {
1841
+ const parm = this.audioIfSourceToParameter(source);
1842
+ if (parm && this.activeProfile.parameters.includes(parm)) {
1843
+ const enabled = await this.getParameter(parm, options);
1844
+ if (enabled === true)
1845
+ return source;
1846
+ }
1847
+ }
1848
+ return this.activeProfile.audioIfSources.includes('default') ? 'default' : null;
1849
+ }
1850
+ setAudioIfMode(source) {
1851
+ if (!this.activeProfile.audioIfSources.includes(source)) {
1852
+ throw this.unsupported(source, 'setAudioIfMode', 'Audio IF source is not enabled for active profile');
1853
+ }
1854
+ for (const candidate of ['wlan', 'lan', 'acc']) {
1855
+ const parm = this.audioIfSourceToParameter(candidate);
1856
+ if (parm && this.activeProfile.parameters.includes(parm)) {
1857
+ this.setParameter(parm, source === candidate);
1858
+ }
1859
+ }
1860
+ if (this.activeProfile.parameters.includes('AFIF')) {
1861
+ this.setParameter('AFIF', source !== 'default');
1862
+ }
1863
+ }
1864
+ async getSpectrumDataOutput(options) {
1865
+ return this.getScopeBoolean(IcomCivSpec_1.CIV.S_SCP_DOP, [], 'spectrum:data-output', options);
1866
+ }
1867
+ setSpectrumDataOutput(enabled) { this.writeScopeSimple(IcomCivSpec_1.CIV.S_SCP_DOP, [enabled ? 1 : 0]); }
1868
+ async getSpectrumHold(options) {
1869
+ const receiver = options?.receiver ?? 0;
1870
+ return this.getScopeBoolean(IcomCivSpec_1.CIV.S_SCP_HLD, [receiver], `spectrum:hold:${receiver}`, options);
1871
+ }
1872
+ setSpectrumHold(enabled, options) {
1873
+ const receiver = options?.receiver ?? 0;
1874
+ this.writeScopeSimple(IcomCivSpec_1.CIV.S_SCP_HLD, [receiver, enabled ? 1 : 0]);
1875
+ }
1876
+ async getSpectrumSpeed(options) {
1877
+ const receiver = options?.receiver ?? 0;
1878
+ const raw = await this.getScopeByte(IcomCivSpec_1.CIV.S_SCP_SWP, [receiver], `spectrum:speed:${receiver}`, options);
1879
+ if (raw === null)
1880
+ return null;
1881
+ return raw === 0 ? 'fast' : raw === 1 ? 'mid' : 'slow';
1882
+ }
1883
+ setSpectrumSpeed(speed, options) {
1884
+ const receiver = options?.receiver ?? 0;
1885
+ const raw = speed === 'fast' ? 0 : speed === 'mid' ? 1 : 2;
1886
+ this.writeScopeSimple(IcomCivSpec_1.CIV.S_SCP_SWP, [receiver, raw]);
1887
+ }
1888
+ async getSpectrumRef(options) {
1889
+ const receiver = options?.receiver ?? 0;
1890
+ const timeoutMs = options?.timeout ?? 3000;
1891
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1892
+ const rigAddr = this.civ.civAddress & 0xff;
1893
+ const req = IcomRigCommands_1.IcomRigCommands.readCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_CTL_SCP, IcomCivSpec_1.CIV.S_SCP_REF, [receiver]);
1894
+ const resp = await this.waitForCivFrame(`spectrum:ref:${receiver}`, (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_SCP, [IcomCivSpec_1.CIV.S_SCP_REF, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1895
+ if (!resp || resp.length < 10)
1896
+ return null;
1897
+ const value = (0, IcomCivFrame_1.decodeBcdBE)(resp.subarray(7, 9)) / 100;
1898
+ return resp[9] ? -value : value;
1899
+ }
1900
+ setSpectrumRef(db, options) {
1901
+ const receiver = options?.receiver ?? 0;
1902
+ const rounded = Math.round(db * 2) / 2;
1903
+ this.writeScopeSimple(IcomCivSpec_1.CIV.S_SCP_REF, [receiver, ...(0, IcomCivFrame_1.encodeBcdBE)(Math.abs(Math.round(rounded * 100)), 2), rounded < 0 ? 1 : 0]);
1904
+ }
1905
+ async getSpectrumAverage(options) { return this.getLevel('SPECTRUM_AVG', options); }
1906
+ setSpectrumAverage(value) { this.setLevel('SPECTRUM_AVG', value); }
1907
+ async getSpectrumVbw(options) {
1908
+ const receiver = options?.receiver ?? 0;
1909
+ return this.getScopeByte(IcomCivSpec_1.CIV.S_SCP_VBW, [receiver], `spectrum:vbw:${receiver}`, options);
1910
+ }
1911
+ setSpectrumVbw(value, options) {
1912
+ const receiver = options?.receiver ?? 0;
1913
+ this.writeScopeSimple(IcomCivSpec_1.CIV.S_SCP_VBW, [receiver, Math.max(0, Math.min(1, Math.trunc(value)))]);
1914
+ }
1915
+ async getSpectrumRbw(options) {
1916
+ const receiver = options?.receiver ?? 0;
1917
+ return this.getScopeByte(IcomCivSpec_1.CIV.S_SCP_RBW, [receiver], `spectrum:rbw:${receiver}`, options);
1918
+ }
1919
+ setSpectrumRbw(value, options) {
1920
+ const receiver = options?.receiver ?? 0;
1921
+ this.writeScopeSimple(IcomCivSpec_1.CIV.S_SCP_RBW, [receiver, Math.max(0, Math.min(2, Math.trunc(value)))]);
1922
+ }
1923
+ async getSpectrumDuringTx(options) {
1924
+ return this.getScopeBoolean(IcomCivSpec_1.CIV.S_SCP_STX, [], 'spectrum:during-tx', options);
1925
+ }
1926
+ setSpectrumDuringTx(enabled) { this.writeScopeSimple(IcomCivSpec_1.CIV.S_SCP_STX, [enabled ? 1 : 0]); }
1927
+ async getSpectrumCenterType(options) {
1928
+ const raw = await this.getScopeByte(IcomCivSpec_1.CIV.S_SCP_CFQ, [], 'spectrum:center-type', options);
1929
+ if (raw === null)
1930
+ return null;
1931
+ return raw === 1 ? 'carrier-point-center' : raw === 2 ? 'carrier-point-center-abs' : 'filter-center';
1932
+ }
1933
+ setSpectrumCenterType(type) {
1934
+ const raw = type === 'carrier-point-center' ? 1 : type === 'carrier-point-center-abs' ? 2 : 0;
1935
+ this.writeScopeSimple(IcomCivSpec_1.CIV.S_SCP_CFQ, [raw]);
1936
+ }
1937
+ unsupported(commandName, api, reason) {
1938
+ return new errors_1.UnsupportedCommandError({
1939
+ modelId: this.getProfileModelId(),
1940
+ commandName: api,
1941
+ civCommand: commandName,
1942
+ reason,
1943
+ });
1944
+ }
1945
+ enqueueCw(operation) {
1946
+ const run = this.cwQueue.then(operation, operation);
1947
+ this.cwQueue = run.then(() => undefined, () => undefined);
1948
+ return run;
1949
+ }
1950
+ async sendCwFrameAndWaitForAck(frame, timeoutMs, label) {
1951
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1952
+ const rigAddr = this.civ.civAddress & 0xff;
1953
+ const resp = await this.waitForCivFrame('cw:ack:0x17', (candidate) => IcomControl.matchAckNakFrame(candidate, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(frame));
1954
+ if (!resp) {
1955
+ throw new Error(`CW 0x17 ${label} ACK timeout`);
1956
+ }
1957
+ if (resp[4] === IcomCivSpec_1.CIV.NAK) {
1958
+ throw new Error(`CW 0x17 ${label} NAK received`);
1959
+ }
1960
+ }
1961
+ async readFunctionRaw(name, spec, options) {
1962
+ const timeoutMs = options?.timeout ?? 3000;
1963
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1964
+ const rigAddr = this.civ.civAddress & 0xff;
1965
+ const prefix = spec.readPrefix ?? [];
1966
+ const req = IcomRigCommands_1.IcomRigCommands.readCommand(ctrAddr, rigAddr, spec.command, spec.subcmd, prefix);
1967
+ const tail = [spec.subcmd, ...prefix];
1968
+ const resp = await this.waitForCivFrame(`func:0x${spec.command.toString(16)}:0x${spec.subcmd.toString(16)}:${prefix.join('.')}:${name}`, (frame) => IcomControl.matchCommandFrame(frame, spec.command, tail, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1969
+ if (!resp)
1970
+ return null;
1971
+ const index = 5 + tail.length;
1972
+ return index < resp.length - 1 ? resp[index] : null;
1973
+ }
1974
+ writeFunctionRaw(spec, value) {
1975
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1976
+ const rigAddr = this.civ.civAddress & 0xff;
1977
+ const payload = [...(spec.payloadPrefix ?? []), value & 0xff];
1978
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, spec.command, spec.subcmd, payload));
1979
+ }
1980
+ async readLevelRaw(name, spec, options) {
1981
+ const timeoutMs = options?.timeout ?? 3000;
1982
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1983
+ const rigAddr = this.civ.civAddress & 0xff;
1984
+ const req = IcomRigCommands_1.IcomRigCommands.readCommand(ctrAddr, rigAddr, spec.command, spec.subcmd);
1985
+ const resp = await this.waitForCivFrame(`level:0x${spec.command.toString(16)}:0x${spec.subcmd.toString(16)}:${name}`, (frame) => IcomControl.matchCommandFrame(frame, spec.command, [spec.subcmd], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1986
+ if (!resp)
1987
+ return null;
1988
+ const data = resp.subarray(6, resp.length - 1);
1989
+ if (data.length < spec.dataBytes)
1990
+ return null;
1991
+ return (0, IcomCivFrame_1.decodeBcdBE)(data.subarray(0, spec.dataBytes));
1992
+ }
1993
+ writeLevelRaw(spec, raw) {
1994
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1995
+ const rigAddr = this.civ.civAddress & 0xff;
1996
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, spec.command, spec.subcmd, IcomControl.encodeDataValue(raw, spec.dataType, spec.dataBytes)));
1997
+ }
1998
+ async readExtParamValue(ext, keyPrefix, options) {
1999
+ const timeoutMs = options?.timeout ?? 3000;
2000
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
2001
+ const rigAddr = this.civ.civAddress & 0xff;
2002
+ const req = IcomRigCommands_1.IcomRigCommands.readExtParam(ctrAddr, rigAddr, ext.command, ext.subcmd, ext.subext);
2003
+ const tail = [ext.subcmd, ...ext.subext];
2004
+ const resp = await this.waitForCivFrame(`${keyPrefix}:0x${ext.command.toString(16)}:0x${ext.subcmd.toString(16)}:${ext.subext.join('.')}`, (frame) => IcomControl.matchCommandFrame(frame, ext.command, tail, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
2005
+ if (!resp)
2006
+ return null;
2007
+ const start = 5 + tail.length;
2008
+ return IcomControl.decodeDataValue(resp.subarray(start, resp.length - 1), ext.dataType, ext.dataBytes);
2009
+ }
2010
+ writeExtParamValue(ext, value) {
2011
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
2012
+ const rigAddr = this.civ.civAddress & 0xff;
2013
+ const raw = ext.dataType === 'level' ? Math.round(Math.max(0, Math.min(1, value)) * 255) : Math.round(value);
2014
+ const payload = IcomControl.encodeDataValue(raw, ext.dataType, ext.dataBytes);
2015
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeExtParam(ctrAddr, rigAddr, ext.command, ext.subcmd, ext.subext, payload));
2016
+ }
2017
+ static encodeDataValue(value, dataType, bytes) {
2018
+ if (dataType === 'time') {
2019
+ const seconds = Math.max(0, Math.round(value));
2020
+ const hhmm = Math.floor(seconds / 3600) * 100 + Math.floor(seconds / 60) % 60;
2021
+ return (0, IcomCivFrame_1.encodeBcdBE)(hhmm, bytes);
2022
+ }
2023
+ return (0, IcomCivFrame_1.encodeBcdBE)(value, bytes);
2024
+ }
2025
+ static decodeDataValue(data, dataType, bytes) {
2026
+ if (data.length < bytes)
2027
+ return null;
2028
+ const value = (0, IcomCivFrame_1.decodeBcdBE)(data.subarray(0, bytes));
2029
+ if (dataType === 'level')
2030
+ return value / 255;
2031
+ if (dataType === 'bool')
2032
+ return value === 0 ? 0 : 1;
2033
+ if (dataType === 'time') {
2034
+ const hours = Math.floor(value / 100);
2035
+ const minutes = value % 100;
2036
+ return hours * 3600 + minutes * 60;
2037
+ }
2038
+ return value;
2039
+ }
2040
+ async readRitOffset(options) {
2041
+ const timeoutMs = options?.timeout ?? 3000;
2042
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
2043
+ const rigAddr = this.civ.civAddress & 0xff;
2044
+ const req = IcomRigCommands_1.IcomRigCommands.readRitOffset(ctrAddr, rigAddr);
2045
+ const resp = await this.waitForCivFrame('rit:offset', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_RIT, [IcomCivSpec_1.CIV.S_RIT_FREQ], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
2046
+ if (!resp || resp.length < 9)
2047
+ return null;
2048
+ const value = (0, IcomCivFrame_1.decodeFrequencyBcdLE)(resp.subarray(6, 8));
2049
+ return resp[8] === 0 ? value : -value;
2050
+ }
2051
+ writeRitOffset(offsetHz) {
2052
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
2053
+ const rigAddr = this.civ.civAddress & 0xff;
2054
+ const payload = [...(0, IcomCivFrame_1.encodeFrequencyBcdLE)(Math.abs(Math.round(offsetHz)), 2), offsetHz < 0 ? 1 : 0];
2055
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.setRitOffset(ctrAddr, rigAddr, payload));
2056
+ }
2057
+ vfoToSubcmd(vfo) {
2058
+ switch (vfo) {
2059
+ case 'A': return IcomCivSpec_1.CIV.S_VFOA;
2060
+ case 'B': return IcomCivSpec_1.CIV.S_VFOB;
2061
+ case 'MAIN':
2062
+ case 'MAIN_A':
2063
+ case 'MAIN_B': return IcomCivSpec_1.CIV.S_MAIN;
2064
+ case 'SUB':
2065
+ case 'SUB_A':
2066
+ case 'SUB_B': return IcomCivSpec_1.CIV.S_SUB;
2067
+ case 'CURR':
2068
+ case 'TX': return null;
2069
+ case 'MEM': return null;
2070
+ }
2071
+ }
2072
+ async readSplitRaw(options) {
2073
+ const timeoutMs = options?.timeout ?? 3000;
2074
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
2075
+ const rigAddr = this.civ.civAddress & 0xff;
2076
+ const req = IcomRigCommands_1.IcomRigCommands.readSplit(ctrAddr, rigAddr);
2077
+ const resp = await this.waitForCivFrame('split:0x0f', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_SPLT, [], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
2078
+ if (!resp || resp.length < 7)
2079
+ return null;
2080
+ return resp[5];
2081
+ }
2082
+ async readTone(subcmd, options) {
2083
+ if (!this.activeProfile.tone)
2084
+ return null;
2085
+ const timeoutMs = options?.timeout ?? 3000;
2086
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
2087
+ const rigAddr = this.civ.civAddress & 0xff;
2088
+ const req = IcomRigCommands_1.IcomRigCommands.readTone(ctrAddr, rigAddr, subcmd);
2089
+ const resp = await this.waitForCivFrame(`tone:0x${subcmd.toString(16)}`, (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_SET_TONE, [subcmd], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
2090
+ if (!resp || resp.length < 10)
2091
+ return null;
2092
+ return (0, IcomCivFrame_1.decodeBcdBE)(resp.subarray(6, 9)) / 10;
2093
+ }
2094
+ writeTone(subcmd, hz) {
2095
+ if (!this.activeProfile.tone) {
2096
+ throw this.unsupported(String(subcmd), 'setToneFrequency', 'Tone controls are not enabled for active profile');
2097
+ }
2098
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
2099
+ const rigAddr = this.civ.civAddress & 0xff;
2100
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.setTone(ctrAddr, rigAddr, subcmd, Math.round(hz * 10)));
2101
+ }
2102
+ audioIfSourceToParameter(source) {
2103
+ switch (source) {
2104
+ case 'wlan': return 'AFIF_WLAN';
2105
+ case 'lan': return 'AFIF_LAN';
2106
+ case 'acc': return 'AFIF_ACC';
2107
+ case 'default': return 'AFIF';
2108
+ }
2109
+ }
2110
+ async getScopeBoolean(subcmd, payload, key, options) {
2111
+ const raw = await this.getScopeByte(subcmd, payload, key, options);
2112
+ return raw === null ? null : raw !== 0;
2113
+ }
2114
+ async getScopeByte(subcmd, payload, key, options) {
2115
+ const timeoutMs = options?.timeout ?? 3000;
2116
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
2117
+ const rigAddr = this.civ.civAddress & 0xff;
2118
+ const req = IcomRigCommands_1.IcomRigCommands.readCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_CTL_SCP, subcmd, payload);
2119
+ const resp = await this.waitForCivFrame(key, (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_SCP, [subcmd, ...payload], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
2120
+ if (!resp)
2121
+ return null;
2122
+ const index = 5 + 1 + payload.length;
2123
+ return index < resp.length - 1 ? resp[index] : null;
2124
+ }
2125
+ writeScopeSimple(subcmd, payload) {
2126
+ const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
2127
+ const rigAddr = this.civ.civAddress & 0xff;
2128
+ this.sendCiv(IcomRigCommands_1.IcomRigCommands.writeCommand(ctrAddr, rigAddr, IcomCivSpec_1.CIV.C_CTL_SCP, subcmd, payload));
2129
+ }
1396
2130
  static isReplyOf(frame, cmd, ctrAddr, rigAddr) {
1397
2131
  // typical reply FE FE [ctr] [rig] cmd ... FD
1398
2132
  return frame.length >= 7 && frame[0] === 0xfe && frame[1] === 0xfe && frame[4] === (cmd & 0xff);
@@ -1463,6 +2197,29 @@ class IcomControl {
1463
2197
  return false;
1464
2198
  return true;
1465
2199
  }
2200
+ static matchAckNakFrame(frame, ctrAddr, rigAddr) {
2201
+ if (!(frame.length >= 6 && frame[0] === IcomCivSpec_1.CIV.PR && frame[1] === IcomCivSpec_1.CIV.PR))
2202
+ return false;
2203
+ const addrCtrOk = frame[2] === (ctrAddr & 0xff) || frame[2] === 0x00;
2204
+ if (!addrCtrOk || frame[3] !== (rigAddr & 0xff))
2205
+ return false;
2206
+ if (frame[4] !== IcomCivSpec_1.CIV.ACK && frame[4] !== IcomCivSpec_1.CIV.NAK)
2207
+ return false;
2208
+ return frame[frame.length - 1] === IcomCivSpec_1.CIV.FI;
2209
+ }
2210
+ static normalizeMorseText(text) {
2211
+ if (typeof text !== 'string') {
2212
+ throw new Error('CW 0x17 sendMorse text must be a string');
2213
+ }
2214
+ const normalized = text.toUpperCase().replace(/[\r\n\t]/g, ' ');
2215
+ for (let i = 0; i < normalized.length; i++) {
2216
+ const code = normalized.charCodeAt(i);
2217
+ if (code < 0x20 || code > 0x7e) {
2218
+ throw new Error(`CW 0x17 sendMorse text contains unsupported non-printable or non-ASCII character at index ${i}`);
2219
+ }
2220
+ }
2221
+ return normalized;
2222
+ }
1466
2223
  async waitForCiv(predicate, timeoutMs, onSend) {
1467
2224
  return new Promise((resolve) => {
1468
2225
  let done = false;