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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Icom WLAN (UDP) protocol implementation in Node.js + TypeScript, featuring:
4
4
 
5
+ CI-V profile support is Hamlib-aligned for modern ICOM radios: WLAN UDP packets carry standard CI-V frames, with model-specific profiles for IC-705, IC-905, IC-7300, IC-9700, IC-7610, and IC-7760.
6
+
5
7
  - Control channel handshake (AreYouThere/AreYouReady), login (0x80/0x60), token confirm/renew (0x40)
6
8
  - CI‑V over UDP encapsulation (open/close keep‑alive + CIV frame transport)
7
9
  - Scope/spectrum data capture over CI‑V `0x27`, with automatic segment assembly into friendly frame events
@@ -35,7 +37,8 @@ import { IcomControl, AUDIO_RATE, DisconnectReason } from 'icom-wlan-node';
35
37
  const rig = new IcomControl({
36
38
  control: { ip: '192.168.1.50', port: 50001 },
37
39
  userName: 'user',
38
- password: 'pass'
40
+ password: 'pass',
41
+ model: 'auto' // or force a profile, e.g. 'IC-705'
39
42
  });
40
43
 
41
44
  rig.events.on('login', (res) => {
@@ -48,7 +51,7 @@ rig.events.on('status', (s) => {
48
51
  });
49
52
 
50
53
  rig.events.on('capabilities', (c) => {
51
- console.log('CIV address:', c.civAddress, 'audio:', c.audioName);
54
+ console.log('CIV address:', c.civAddress, 'audio:', c.audioName, 'profile:', c.profileName);
52
55
  });
53
56
 
54
57
  rig.events.on('civ', (bytes) => {
@@ -87,6 +90,42 @@ rig.events.on('error', (err) => console.error('UDP error', err));
87
90
  rig.sendCiv(Buffer.from([0xfe,0xfe,0xa4,0xe0,0x03,0xfd]));
88
91
  ```
89
92
 
93
+ ### Main Rig Control Usage
94
+
95
+ Modern ICOM LAN/WLAN radios still transport standard serial CI‑V frames inside the UDP CIV payload. `icom-wlan-node` selects a model profile automatically from the radio name or CI‑V address, or you can force one with `model`.
96
+
97
+ ```ts
98
+ const rig = new IcomControl({
99
+ control: { ip: '192.168.1.50', port: 50001 },
100
+ userName: 'icom',
101
+ password: 'icomicom',
102
+ model: 'auto' // 'IC-705', 'IC-905', 'IC-7300', 'IC-9700', 'IC-7610', 'IC-7760'
103
+ });
104
+
105
+ await rig.connect();
106
+
107
+ // Frequency and mode are profile-aware:
108
+ // modern profiles use CI-V 0x25/0x26, legacy fallback uses 0x05/0x06.
109
+ await rig.setFrequency(14074000);
110
+ await rig.setMode('USB', { dataMode: true, filter: 1 }); // USB-D, filter 1
111
+
112
+ const freqHz = await rig.readOperatingFrequency();
113
+ const mode = await rig.readOperatingMode();
114
+ const tx = await rig.readPtt();
115
+ console.log({ freqHz, mode, state: tx ? 'TX' : 'RX' });
116
+
117
+ // Tuner uses Hamlib-aligned CI-V 0x1C/0x01.
118
+ const tuner = await rig.readTunerStatus();
119
+ await rig.setTunerEnabled(true);
120
+
121
+ // Meters use active-profile calibration tables.
122
+ const swr = await rig.readSWR();
123
+ const power = await rig.readPowerLevel();
124
+ console.log({ tuner, swr, watts: power?.watts, powerPercent: power?.percent });
125
+ ```
126
+
127
+ Profile-specific behavior includes IC-905 6-byte frequency BCD above 5.85 GHz, model-specific scope fixed-edge ranges, and calibrated SWR/ALC/RF power/COMP/voltage/current meters. Private connector commands such as WLAN level or connector data mode are only enabled when the active profile declares the vendor extension; unsupported writes throw `UnsupportedCommandError`.
128
+
90
129
  ### PTT and Audio TX
91
130
 
92
131
  ```ts
@@ -136,7 +175,7 @@ await rig.disableScope();
136
175
 
137
176
  ## API Overview
138
177
 
139
- - `new IcomControl(options)`
178
+ - `new IcomControl(options)` — `options.model` may be `'auto'` or a supported model profile such as `'IC-705'`, `'IC-905'`, `'IC-7300'`, `'IC-9700'`, `'IC-7610'`, or `'IC-7760'`
140
179
  - `options.control`: `{ ip, port }` radio control UDP endpoint
141
180
  - `options.userName`, `options.password`
142
181
  - Events (`rig.events.on(...)`)
@@ -160,12 +199,12 @@ await rig.disableScope();
160
199
  - **Scope / Spectrum**: `scope`, `enableScope()`, `disableScope()`, `waitForScopeFrame()`
161
200
  - **Audio TX**: `setPtt(on: boolean)`, `sendAudioFloat32()`, `sendAudioPcm16()`
162
201
  - **Rig Control**: `setFrequency()`, `setMode()`, `setConnectorDataMode()`, `setConnectorWLanLevel()`
163
- - **Rig Query**: `readOperatingFrequency()`, `readOperatingMode()`, `readTransmitFrequency()`, `readTransceiverState()`, `readBandEdges()`
202
+ - **Rig Query**: `readOperatingFrequency()`, `readOperatingMode()`, `readTransmitFrequency()`, `readPtt()`, `readTransceiverState()`, `readBandEdges()`
164
203
  - **Antenna Tuner**: `readTunerStatus()`, `setTunerEnabled()`, `startManualTune()`
165
204
  - **Meters (RX)**: `readSquelchStatus()`, `readAudioSquelch()`, `readOvfStatus()`, `getLevelMeter()`
166
205
  - **Meters (TX)**: `readSWR()`, `readALC()`, `readPowerLevel()`, `readCompLevel()`
167
206
  - **Power Supply**: `readVoltage()`, `readCurrent()`
168
- - **Audio Config**: `getConnectorWLanLevel()`
207
+ - **Audio Config**: `getUsbAfLevel()`, `setUsbAfLevel()`, `getConnectorWLanLevel()`
169
208
  - **Connection Monitoring**: `getConnectionPhase()`, `getConnectionMetrics()`, `getConnectionState()`, `isAnySessionDisconnected()`, `configureMonitoring()`
170
209
 
171
210
  ### Connection Management & Auto-Reconnect
@@ -285,8 +324,8 @@ The library exposes common CI‑V operations as friendly methods. Addresses are
285
324
 
286
325
  #### Rig Control
287
326
 
288
- - `setFrequency(hz: number)` — Set operating frequency in Hz
289
- - `setMode(mode: IcomMode | number, options?: { dataMode?: boolean })` — Set mode (supports string or numeric code)
327
+ - `setFrequency(hz: number)` — Set operating frequency in Hz; modern profiles use targetable CI-V `0x25`, and IC-905 uses 6-byte BCD above 5.85 GHz
328
+ - `setMode(mode: IcomMode | number, options?: { dataMode?: boolean; filter?: 1|2|3 })` — Set mode; modern profiles use CI-V `0x26` with VFO, data-mode and filter
290
329
  - `setPtt(on: boolean)` — Key/unkey transmitter
291
330
 
292
331
  **Supported Modes** (IcomMode string constants):
@@ -296,8 +335,9 @@ The library exposes common CI‑V operations as friendly methods. Addresses are
296
335
  #### Rig Query
297
336
 
298
337
  - `readOperatingFrequency(options?: QueryOptions) => Promise<number|null>`
299
- - `readOperatingMode(options?: QueryOptions) => Promise<{ mode: number; filter?: number; modeName?: string; filterName?: string } | null>`
338
+ - `readOperatingMode(options?: QueryOptions) => Promise<{ mode: number; filter?: number; modeName?: string; filterName?: string; dataMode?: boolean } | null>`
300
339
  - `readTransmitFrequency(options?: QueryOptions) => Promise<number|null>`
340
+ - `readPtt(options?: QueryOptions) => Promise<boolean|null>`
301
341
  - `readTransceiverState(options?: QueryOptions) => Promise<'TX' | 'RX' | 'UNKNOWN' | null>`
302
342
  - `readBandEdges(options?: QueryOptions) => Promise<Buffer|null>`
303
343
 
@@ -306,8 +346,18 @@ The library exposes common CI‑V operations as friendly methods. Addresses are
306
346
  - `scope: IcomScopeService` — Standalone scope service object that can be reused with other CI‑V transport paths in the future
307
347
  - `enableScope() => Promise<void>` — Send the minimal command sequence to enable basic scope output
308
348
  - `disableScope() => Promise<void>` — Send the minimal command sequence to disable scope output
349
+ - `readScopeMode(options?: QueryOptions & { receiver?: 0 | 1 }) => Promise<IcomScopeModeInfo | null>` — Read current scope mode using CI‑V `0x27 0x14`
350
+ - `setScopeMode(mode: IcomScopeMode | 0 | 1 | 2 | 3, options?: { receiver?: 0 | 1 }) => Promise<void>` — Set current scope mode
309
351
  - `readScopeSpan(options?: QueryOptions & { receiver?: 0 | 1 }) => Promise<{ receiver: 0 | 1; spanHz: number } | null>` — Read current scope span
310
- - `setScopeSpan(spanHz: number, options?: { receiver?: 0 | 1 }) => Promise<void>` — Set scope span using CI‑V `0x27 0x15`
352
+ - `setScopeSpan(spanHz: number, options?: { receiver?: 0 | 1 }) => Promise<void>` — Set public scope span using CI‑V `0x27 0x15`; the wire value is `spanHz / 2` per Hamlib
353
+ - `readScopeEdge(options?: QueryOptions & { receiver?: 0 | 1 }) => Promise<IcomScopeEdgeInfo | null>` — Read active fixed-edge slot using CI‑V `0x27 0x16`
354
+ - `setScopeEdge(edgeSlot: number, options?: { receiver?: 0 | 1 }) => Promise<void>` — Select active fixed-edge slot
355
+ - `readScopeFixedEdge(rangeId: number, edgeSlot: number, options?: QueryOptions) => Promise<IcomScopeFixedEdgeInfo | null>` — Read fixed-edge frequencies using CI‑V `0x27 0x1E`
356
+ - `setScopeFixedEdge({ rangeId?, edgeSlot?, lowHz, highHz }) => Promise<IcomScopeFixedEdgeInfo>` — Set fixed-edge frequencies, auto-resolving `rangeId` from the current rig frequency when omitted
357
+ - `resolveScopeFrequencyRangeId(frequencyHz?: number) => Promise<number>` — Resolve ICOM fixed-edge range ID from a target or current operating frequency
358
+ - `getSpectrumMode()/setSpectrumMode()` / `getSpectrumSpan()/setSpectrumSpan()` / `getSpectrumEdgeSlot()/setSpectrumEdgeSlot()` / `getSpectrumFixedEdges()/setSpectrumFixedEdges()` — Hamlib-like convenience aliases over the scope-specific methods
359
+ - `getSpectrumDisplayState(options?: QueryOptions & { receiver?: 0 | 1 }) => Promise<IcomSpectrumDisplayState>` — Read a Hamlib-like normalized display state
360
+ - `configureSpectrumDisplay(config?: IcomSpectrumDisplayConfig) => Promise<IcomSpectrumDisplayState>` — Apply a normalized display config covering center/fixed modes
311
361
  - `waitForScopeFrame(options?: QueryOptions) => Promise<IcomScopeFrame | null>` — Wait for the next complete scope frame
312
362
 
313
363
  `IcomScopeFrame` shape:
@@ -330,17 +380,17 @@ interface IcomScopeFrame {
330
380
 
331
381
  Current implementation notes:
332
382
 
333
- - Currently implements basic on/off controls, `0x27 0x15` span read/write, and `0x27 00 00` scope data capture
383
+ - Implements basic on/off controls, `0x27 0x15` span read/write, fixed-edge selection/ranges, and `0x27 00 00` scope data capture
334
384
  - The parsing layer is decoupled from the UDP session layer and only depends on complete CI‑V frames
335
- - Frequency fields are currently parsed with `freqLen=5` by default
385
+ - Frequency and fixed-edge ranges are profile-aware; unsupported model-specific variants should be added in `src/rig/IcomProfiles.ts`
336
386
  - LAN aggregate waterfall payload splitting is not implemented yet; standard segment input is supported
337
387
  - The `scope` logic is designed to be reusable for future serial CI‑V or Hamlib CI‑V integration
338
388
 
339
389
  #### Antenna Tuner (ATU)
340
390
 
341
- - `readTunerStatus(options?: QueryOptions) => Promise<{ raw: number; state: 'OFF'|'ON'|'TUNING' } | null>` — Read tuner status (CIV 0x1A/0x00)
342
- - `setTunerEnabled(enabled: boolean) => Promise<void>` — Enable/disable internal tuner (CI‑V 0x1A/0x01)
343
- - `startManualTune() => Promise<void>` — Trigger one manual tune cycle (CI‑V 0x1A/0x02/0x00)
391
+ - `readTunerStatus(options?: QueryOptions) => Promise<{ raw: number; state: 'OFF'|'ON'|'TUNING' } | null>` — Read tuner status (Hamlib-aligned CI-V `0x1C 0x01`)
392
+ - `setTunerEnabled(enabled: boolean) => Promise<void>` — Enable/disable internal tuner (Hamlib-aligned CI‑V `0x1C 0x01 0x00/0x01`)
393
+ - `startManualTune() => Promise<void>` — Trigger one manual tune cycle (Hamlib-aligned CI‑V `0x1C 0x01 0x02`)
344
394
 
345
395
  #### Meters & Levels
346
396
 
@@ -353,20 +403,20 @@ Current implementation notes:
353
403
  **Transmission Meters** (require PTT on):
354
404
  - `readSWR(options?: QueryOptions) => Promise<{ raw: number; swr: number; alert: boolean } | null>` — SWR meter (CI-V 0x15/0x12)
355
405
  - `readALC(options?: QueryOptions) => Promise<{ raw: number; percent: number; alert: boolean } | null>` — ALC meter (CI-V 0x15/0x13)
356
- - `readPowerLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` — Output power level (CI-V 0x15/0x11)
357
- - `readCompLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` — Voice compression level (CI-V 0x15/0x14)
406
+ - `readPowerLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number; watts?: number } | null>` — Output power level (CI-V 0x15/0x11)
407
+ - `readCompLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number; db?: number } | null>` — Voice compression level (CI-V 0x15/0x14)
358
408
 
359
409
  **Power Supply Monitoring**:
360
410
  - `readVoltage(options?: QueryOptions) => Promise<{ raw: number; volts: number } | null>` — Supply voltage (CI-V 0x15/0x15)
361
411
  - `readCurrent(options?: QueryOptions) => Promise<{ raw: number; amps: number } | null>` — Supply current draw (CI-V 0x15/0x16)
362
412
 
363
413
  **Audio Configuration**:
364
- - `getConnectorWLanLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` — Get WLAN audio level (CI-V 0x1A/0x05/0x01/0x17)
365
- - `setConnectorWLanLevel(level: number)` — Set WLAN audio level (0-255)
414
+ - `getUsbAfLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` / `setUsbAfLevel(level: number)` Hamlib-aligned USB AF level when the active profile declares it
415
+ - `getConnectorWLanLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` / `setConnectorWLanLevel(level: number)` — Private `icom-wlan-node` WLAN level extension; returns `null` or throws `UnsupportedCommandError` when the active profile does not declare it
366
416
 
367
417
  #### Connector Settings
368
418
 
369
- - `setConnectorDataMode(mode: ConnectorDataMode | number)` — Set data routing mode (supports string or numeric)
419
+ - `setConnectorDataMode(mode: ConnectorDataMode | number)` — Private connector routing extension; supported only on profiles that declare the vendor command
370
420
 
371
421
  **Supported Connector Modes** (ConnectorDataMode string constants):
372
422
  - `'MIC'` (0x00), `'ACC'` (0x01), `'USB'` (0x02), `'WLAN'` (0x03)
@@ -447,17 +497,17 @@ if (alc) {
447
497
 
448
498
  const power = await rig.readPowerLevel({ timeout: 2000 });
449
499
  if (power) {
450
- console.log(`Power: ${power.percent.toFixed(1)}%`);
500
+ console.log(`Power: ${power.percent.toFixed(1)}%${power.watts != null ? ` (${power.watts.toFixed(1)} W)` : ''}`);
451
501
  }
452
502
 
453
503
  const comp = await rig.readCompLevel({ timeout: 2000 });
454
504
  if (comp) {
455
- console.log(`COMP: ${comp.percent.toFixed(1)}%`);
505
+ console.log(`COMP: ${comp.percent.toFixed(1)}%${comp.db != null ? ` (${comp.db.toFixed(1)} dB)` : ''}`);
456
506
  }
457
507
 
458
508
  await rig.setPtt(false);
459
509
 
460
- // Configure WLAN connector
510
+ // Configure WLAN connector (private extension; profile-gated)
461
511
  const wlanLevel = await rig.getConnectorWLanLevel({ timeout: 2000 });
462
512
  if (wlanLevel) {
463
513
  console.log(`WLAN Level: ${wlanLevel.percent.toFixed(1)}%`);
@@ -521,9 +571,8 @@ ICOM_IP=192.168.31.253 ICOM_PORT=50001 ICOM_USER=icom ICOM_PASS=icomicom npm tes
521
571
  - Full token renewal loop and advanced status flag parsing simplified.
522
572
  - Audio receive/playback is library‑only; playback is up to the integrator.
523
573
  - Robust retransmit/multi‑retransmit handling can be extended.
524
- - Scope support is currently limited to basic on/off commands plus standard `0x27 00 00` segment parsing.
574
+ - Scope support includes basic enable/disable, mode/span/edge/fixed-edge control, and standard `0x27 00 00` segment parsing.
525
575
  - LAN aggregate waterfall payload splitting is not implemented yet.
526
- - Scope control subcommands beyond basic enable/disable are not implemented yet.
527
576
 
528
577
  ## License
529
578
 
package/dist/index.d.ts CHANGED
@@ -5,6 +5,9 @@ export { IcomScopeCommands } from './scope/IcomScopeCommands';
5
5
  export { MODE_MAP, CONNECTOR_MODE_MAP, DEFAULT_CONTROLLER_ADDR, METER_THRESHOLDS, METER_CALIBRATION, getModeCode, getConnectorModeCode, getModeString, getConnectorModeString, getFilterString, rawToPowerPercent, rawToVoltage, rawToCurrent } from './rig/IcomConstants';
6
6
  export { parseTwoByteBcd, intToTwoByteBcd } from './utils/bcd';
7
7
  export { IcomRigCommands } from './rig/IcomRigCommands';
8
+ export { CIV } from './rig/IcomCivSpec';
9
+ export { buildCivFrame, encodeFrequencyBcdLE, decodeFrequencyBcdLE, encodeBcdBE, decodeBcdBE } from './rig/IcomCivFrame';
10
+ export { ICOM_PROFILES, resolveIcomProfile, getProfileByModel, interpolateCalibration } from './rig/IcomProfiles';
8
11
  export { AUDIO_RATE } from './rig/IcomAudio';
9
12
  export { setupGlobalErrorHandlers, setupBasicErrorProtection, GlobalErrorHandlerOptions } from './utils/errorHandling';
10
- export { ConnectionAbortedError, getDisconnectMessage } from './utils/errors';
13
+ export { ConnectionAbortedError, UnsupportedCommandError, getDisconnectMessage } from './utils/errors';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.getDisconnectMessage = exports.ConnectionAbortedError = exports.setupBasicErrorProtection = exports.setupGlobalErrorHandlers = exports.AUDIO_RATE = exports.IcomRigCommands = exports.intToTwoByteBcd = exports.parseTwoByteBcd = exports.rawToCurrent = exports.rawToVoltage = exports.rawToPowerPercent = exports.getFilterString = exports.getConnectorModeString = exports.getModeString = exports.getConnectorModeCode = exports.getModeCode = exports.METER_CALIBRATION = exports.METER_THRESHOLDS = exports.DEFAULT_CONTROLLER_ADDR = exports.CONNECTOR_MODE_MAP = exports.MODE_MAP = exports.IcomScopeCommands = exports.IcomScopeService = exports.IcomControl = void 0;
17
+ exports.getDisconnectMessage = exports.UnsupportedCommandError = exports.ConnectionAbortedError = exports.setupBasicErrorProtection = exports.setupGlobalErrorHandlers = exports.AUDIO_RATE = exports.interpolateCalibration = exports.getProfileByModel = exports.resolveIcomProfile = exports.ICOM_PROFILES = exports.decodeBcdBE = exports.encodeBcdBE = exports.decodeFrequencyBcdLE = exports.encodeFrequencyBcdLE = exports.buildCivFrame = exports.CIV = exports.IcomRigCommands = exports.intToTwoByteBcd = exports.parseTwoByteBcd = exports.rawToCurrent = exports.rawToVoltage = exports.rawToPowerPercent = exports.getFilterString = exports.getConnectorModeString = exports.getModeString = exports.getConnectorModeCode = exports.getModeCode = exports.METER_CALIBRATION = exports.METER_THRESHOLDS = exports.DEFAULT_CONTROLLER_ADDR = exports.CONNECTOR_MODE_MAP = exports.MODE_MAP = exports.IcomScopeCommands = exports.IcomScopeService = exports.IcomControl = void 0;
18
18
  // Export types (includes ConnectionPhase, ConnectionMetrics, etc.)
19
19
  __exportStar(require("./types"), exports);
20
20
  // Export main class
@@ -46,6 +46,19 @@ Object.defineProperty(exports, "intToTwoByteBcd", { enumerable: true, get: funct
46
46
  // Export low-level utilities (for advanced users)
47
47
  var IcomRigCommands_1 = require("./rig/IcomRigCommands");
48
48
  Object.defineProperty(exports, "IcomRigCommands", { enumerable: true, get: function () { return IcomRigCommands_1.IcomRigCommands; } });
49
+ var IcomCivSpec_1 = require("./rig/IcomCivSpec");
50
+ Object.defineProperty(exports, "CIV", { enumerable: true, get: function () { return IcomCivSpec_1.CIV; } });
51
+ var IcomCivFrame_1 = require("./rig/IcomCivFrame");
52
+ Object.defineProperty(exports, "buildCivFrame", { enumerable: true, get: function () { return IcomCivFrame_1.buildCivFrame; } });
53
+ Object.defineProperty(exports, "encodeFrequencyBcdLE", { enumerable: true, get: function () { return IcomCivFrame_1.encodeFrequencyBcdLE; } });
54
+ Object.defineProperty(exports, "decodeFrequencyBcdLE", { enumerable: true, get: function () { return IcomCivFrame_1.decodeFrequencyBcdLE; } });
55
+ Object.defineProperty(exports, "encodeBcdBE", { enumerable: true, get: function () { return IcomCivFrame_1.encodeBcdBE; } });
56
+ Object.defineProperty(exports, "decodeBcdBE", { enumerable: true, get: function () { return IcomCivFrame_1.decodeBcdBE; } });
57
+ var IcomProfiles_1 = require("./rig/IcomProfiles");
58
+ Object.defineProperty(exports, "ICOM_PROFILES", { enumerable: true, get: function () { return IcomProfiles_1.ICOM_PROFILES; } });
59
+ Object.defineProperty(exports, "resolveIcomProfile", { enumerable: true, get: function () { return IcomProfiles_1.resolveIcomProfile; } });
60
+ Object.defineProperty(exports, "getProfileByModel", { enumerable: true, get: function () { return IcomProfiles_1.getProfileByModel; } });
61
+ Object.defineProperty(exports, "interpolateCalibration", { enumerable: true, get: function () { return IcomProfiles_1.interpolateCalibration; } });
49
62
  var IcomAudio_1 = require("./rig/IcomAudio");
50
63
  Object.defineProperty(exports, "AUDIO_RATE", { enumerable: true, get: function () { return IcomAudio_1.AUDIO_RATE; } });
51
64
  // Export error handling utilities (optional, for robustness)
@@ -55,4 +68,5 @@ Object.defineProperty(exports, "setupBasicErrorProtection", { enumerable: true,
55
68
  // Export disconnect error utilities
56
69
  var errors_1 = require("./utils/errors");
57
70
  Object.defineProperty(exports, "ConnectionAbortedError", { enumerable: true, get: function () { return errors_1.ConnectionAbortedError; } });
71
+ Object.defineProperty(exports, "UnsupportedCommandError", { enumerable: true, get: function () { return errors_1.UnsupportedCommandError; } });
58
72
  Object.defineProperty(exports, "getDisconnectMessage", { enumerable: true, get: function () { return errors_1.getDisconnectMessage; } });
@@ -0,0 +1,13 @@
1
+ export interface CivFrameBuildOptions {
2
+ rigAddr: number;
3
+ ctrlAddr: number;
4
+ cmd: number;
5
+ subcmd?: number | number[];
6
+ payload?: Buffer | number[];
7
+ }
8
+ export declare function buildCivFrame(options: CivFrameBuildOptions): Buffer;
9
+ export declare function encodeFrequencyBcdLE(freqHz: number, byteLength?: number): Buffer;
10
+ export declare function decodeFrequencyBcdLE(bytes: Buffer | Uint8Array): number;
11
+ export declare function encodeBcdBE(value: number, byteLength: number): Buffer;
12
+ export declare function decodeBcdBE(bytes: Buffer | Uint8Array): number;
13
+ export declare function getCivPayload(frame: Buffer): Buffer | null;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCivFrame = buildCivFrame;
4
+ exports.encodeFrequencyBcdLE = encodeFrequencyBcdLE;
5
+ exports.decodeFrequencyBcdLE = decodeFrequencyBcdLE;
6
+ exports.encodeBcdBE = encodeBcdBE;
7
+ exports.decodeBcdBE = decodeBcdBE;
8
+ exports.getCivPayload = getCivPayload;
9
+ const IcomCivSpec_1 = require("./IcomCivSpec");
10
+ function buildCivFrame(options) {
11
+ const subcmd = options.subcmd === undefined
12
+ ? []
13
+ : Array.isArray(options.subcmd)
14
+ ? options.subcmd
15
+ : [options.subcmd];
16
+ const payload = options.payload ? Array.from(options.payload) : [];
17
+ return Buffer.from([
18
+ IcomCivSpec_1.CIV.PR,
19
+ IcomCivSpec_1.CIV.PR,
20
+ options.rigAddr & 0xff,
21
+ options.ctrlAddr & 0xff,
22
+ options.cmd & 0xff,
23
+ ...subcmd.map((v) => v & 0xff),
24
+ ...payload.map((v) => v & 0xff),
25
+ IcomCivSpec_1.CIV.FI,
26
+ ]);
27
+ }
28
+ function encodeFrequencyBcdLE(freqHz, byteLength = 5) {
29
+ const out = Buffer.alloc(byteLength);
30
+ let remaining = Math.max(0, Math.round(freqHz));
31
+ for (let i = 0; i < byteLength; i++) {
32
+ const twoDigits = remaining % 100;
33
+ out[i] = ((((twoDigits / 10) | 0) & 0x0f) << 4) | (twoDigits % 10);
34
+ remaining = Math.floor(remaining / 100);
35
+ }
36
+ return out;
37
+ }
38
+ function decodeFrequencyBcdLE(bytes) {
39
+ let hz = 0;
40
+ let multiplier = 1;
41
+ for (const byte of bytes) {
42
+ hz += (byte & 0x0f) * multiplier;
43
+ multiplier *= 10;
44
+ hz += ((byte >> 4) & 0x0f) * multiplier;
45
+ multiplier *= 10;
46
+ }
47
+ return hz;
48
+ }
49
+ function encodeBcdBE(value, byteLength) {
50
+ const out = Buffer.alloc(byteLength);
51
+ let remaining = Math.max(0, Math.floor(value));
52
+ for (let i = byteLength - 1; i >= 0; i--) {
53
+ const twoDigits = remaining % 100;
54
+ out[i] = ((((twoDigits / 10) | 0) & 0x0f) << 4) | (twoDigits % 10);
55
+ remaining = Math.floor(remaining / 100);
56
+ }
57
+ return out;
58
+ }
59
+ function decodeBcdBE(bytes) {
60
+ let value = 0;
61
+ for (const byte of bytes) {
62
+ value = value * 100 + (((byte >> 4) & 0x0f) * 10) + (byte & 0x0f);
63
+ }
64
+ return value;
65
+ }
66
+ function getCivPayload(frame) {
67
+ if (frame.length < 6)
68
+ return null;
69
+ if (frame[0] !== IcomCivSpec_1.CIV.PR || frame[1] !== IcomCivSpec_1.CIV.PR || frame[frame.length - 1] !== IcomCivSpec_1.CIV.FI)
70
+ return null;
71
+ return frame.subarray(4, frame.length - 1);
72
+ }
@@ -0,0 +1,54 @@
1
+ /** Hamlib-aligned CI-V command and subcommand constants. */
2
+ export declare const CIV: {
3
+ readonly PR: 254;
4
+ readonly FI: 253;
5
+ readonly ACK: 251;
6
+ readonly NAK: 250;
7
+ readonly CTRLID: 224;
8
+ readonly C_RD_BAND: 2;
9
+ readonly C_RD_FREQ: 3;
10
+ readonly C_RD_MODE: 4;
11
+ readonly C_SET_FREQ: 5;
12
+ readonly C_SET_MODE: 6;
13
+ readonly C_CTL_LVL: 20;
14
+ readonly C_RD_SQSM: 21;
15
+ readonly C_CTL_FUNC: 22;
16
+ readonly C_CTL_MEM: 26;
17
+ readonly C_CTL_PTT: 28;
18
+ readonly C_SEND_SEL_FREQ: 37;
19
+ readonly C_SEND_SEL_MODE: 38;
20
+ readonly C_CTL_SCP: 39;
21
+ readonly S_PTT: 0;
22
+ readonly S_ANT_TUN: 1;
23
+ readonly S_RD_TX_FREQ: 3;
24
+ readonly S_MEM_PARM: 5;
25
+ readonly S_MEM_DATA_MODE: 6;
26
+ readonly S_LVL_AF: 1;
27
+ readonly S_LVL_RF: 2;
28
+ readonly S_LVL_SQL: 3;
29
+ readonly S_LVL_NR: 6;
30
+ readonly S_LVL_RFPOWER: 10;
31
+ readonly S_LVL_MICGAIN: 11;
32
+ readonly S_LVL_COMP: 14;
33
+ readonly S_LVL_BKINDL: 15;
34
+ readonly S_LVL_NB: 18;
35
+ readonly S_LVL_DIGI: 19;
36
+ readonly S_SQL: 1;
37
+ readonly S_SML: 2;
38
+ readonly S_CSQL: 5;
39
+ readonly S_OVF: 7;
40
+ readonly S_RFML: 17;
41
+ readonly S_SWR: 18;
42
+ readonly S_ALC: 19;
43
+ readonly S_CMP: 20;
44
+ readonly S_VD: 21;
45
+ readonly S_ID: 22;
46
+ readonly S_SCP_DAT: 0;
47
+ readonly S_SCP_STS: 16;
48
+ readonly S_SCP_DOP: 17;
49
+ readonly S_SCP_MOD: 20;
50
+ readonly S_SCP_SPN: 21;
51
+ readonly S_SCP_EDG: 22;
52
+ readonly S_SCP_FEF: 30;
53
+ };
54
+ export declare const ICOM_MODE_FILTER_DEFAULT: 1;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ICOM_MODE_FILTER_DEFAULT = exports.CIV = void 0;
4
+ /** Hamlib-aligned CI-V command and subcommand constants. */
5
+ exports.CIV = {
6
+ PR: 0xfe,
7
+ FI: 0xfd,
8
+ ACK: 0xfb,
9
+ NAK: 0xfa,
10
+ CTRLID: 0xe0,
11
+ C_RD_BAND: 0x02,
12
+ C_RD_FREQ: 0x03,
13
+ C_RD_MODE: 0x04,
14
+ C_SET_FREQ: 0x05,
15
+ C_SET_MODE: 0x06,
16
+ C_CTL_LVL: 0x14,
17
+ C_RD_SQSM: 0x15,
18
+ C_CTL_FUNC: 0x16,
19
+ C_CTL_MEM: 0x1a,
20
+ C_CTL_PTT: 0x1c,
21
+ C_SEND_SEL_FREQ: 0x25,
22
+ C_SEND_SEL_MODE: 0x26,
23
+ C_CTL_SCP: 0x27,
24
+ S_PTT: 0x00,
25
+ S_ANT_TUN: 0x01,
26
+ S_RD_TX_FREQ: 0x03,
27
+ S_MEM_PARM: 0x05,
28
+ S_MEM_DATA_MODE: 0x06,
29
+ S_LVL_AF: 0x01,
30
+ S_LVL_RF: 0x02,
31
+ S_LVL_SQL: 0x03,
32
+ S_LVL_NR: 0x06,
33
+ S_LVL_RFPOWER: 0x0a,
34
+ S_LVL_MICGAIN: 0x0b,
35
+ S_LVL_COMP: 0x0e,
36
+ S_LVL_BKINDL: 0x0f,
37
+ S_LVL_NB: 0x12,
38
+ S_LVL_DIGI: 0x13,
39
+ S_SQL: 0x01,
40
+ S_SML: 0x02,
41
+ S_CSQL: 0x05,
42
+ S_OVF: 0x07,
43
+ S_RFML: 0x11,
44
+ S_SWR: 0x12,
45
+ S_ALC: 0x13,
46
+ S_CMP: 0x14,
47
+ S_VD: 0x15,
48
+ S_ID: 0x16,
49
+ S_SCP_DAT: 0x00,
50
+ S_SCP_STS: 0x10,
51
+ S_SCP_DOP: 0x11,
52
+ S_SCP_MOD: 0x14,
53
+ S_SCP_SPN: 0x15,
54
+ S_SCP_EDG: 0x16,
55
+ S_SCP_FEF: 0x1e,
56
+ };
57
+ exports.ICOM_MODE_FILTER_DEFAULT = 1;
@@ -1,7 +1,8 @@
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 } 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';
5
+ import { IcomProfile } from './IcomProfiles';
5
6
  export declare class IcomControl {
6
7
  private ev;
7
8
  private sess;
@@ -16,6 +17,8 @@ export declare class IcomControl {
16
17
  private tokenTimer?;
17
18
  private civAssembleBuf;
18
19
  private meterTimer?;
20
+ private activeProfile;
21
+ private lastFilter;
19
22
  private connectionSession;
20
23
  private nextSessionId;
21
24
  private abortHandlers;
@@ -23,6 +26,9 @@ export declare class IcomControl {
23
26
  private monitorConfig;
24
27
  constructor(options: IcomRigOptions);
25
28
  get events(): RigEventEmitter;
29
+ get profile(): IcomProfile;
30
+ private resolveActiveProfile;
31
+ private getProfileModelId;
26
32
  /**
27
33
  * Transition to a new connection phase with logging
28
34
  * @private
@@ -83,6 +89,70 @@ export declare class IcomControl {
83
89
  setScopeSpan(spanHz: number, options?: {
84
90
  receiver?: 0 | 1;
85
91
  }): Promise<void>;
92
+ readScopeMode(options?: QueryOptions & {
93
+ receiver?: 0 | 1;
94
+ }): Promise<IcomScopeModeInfo | null>;
95
+ setScopeMode(mode: IcomScopeMode | 0 | 1 | 2 | 3, options?: {
96
+ receiver?: 0 | 1;
97
+ }): Promise<void>;
98
+ readScopeEdge(options?: QueryOptions & {
99
+ receiver?: 0 | 1;
100
+ }): Promise<IcomScopeEdgeInfo | null>;
101
+ setScopeEdge(edgeSlot: number, options?: {
102
+ receiver?: 0 | 1;
103
+ }): Promise<void>;
104
+ readScopeFixedEdge(rangeId: number, edgeSlot: number, options?: QueryOptions): Promise<IcomScopeFixedEdgeInfo | null>;
105
+ setScopeFixedEdge(options: {
106
+ rangeId?: number;
107
+ edgeSlot?: number;
108
+ lowHz: number;
109
+ highHz: number;
110
+ }): Promise<IcomScopeFixedEdgeInfo>;
111
+ resolveScopeFrequencyRangeId(frequencyHz?: number): Promise<number>;
112
+ getScopeSupportedEdgeSlots(): number[];
113
+ getSpectrumDisplayState(options?: QueryOptions & {
114
+ receiver?: 0 | 1;
115
+ }): Promise<IcomSpectrumDisplayState>;
116
+ configureSpectrumDisplay(config?: IcomSpectrumDisplayConfig): Promise<IcomSpectrumDisplayState>;
117
+ getSpectrumMode(options?: QueryOptions & {
118
+ receiver?: 0 | 1;
119
+ }): Promise<IcomScopeMode | null>;
120
+ setSpectrumMode(mode: IcomScopeMode, options?: {
121
+ receiver?: 0 | 1;
122
+ }): Promise<void>;
123
+ getSpectrumSpan(options?: QueryOptions & {
124
+ receiver?: 0 | 1;
125
+ }): Promise<number | null>;
126
+ setSpectrumSpan(spanHz: number, options?: {
127
+ receiver?: 0 | 1;
128
+ }): Promise<void>;
129
+ getSpectrumEdgeSlot(options?: QueryOptions & {
130
+ receiver?: 0 | 1;
131
+ }): Promise<number | null>;
132
+ setSpectrumEdgeSlot(edgeSlot: number, options?: {
133
+ receiver?: 0 | 1;
134
+ }): Promise<void>;
135
+ getSpectrumFixedEdges(options?: QueryOptions & {
136
+ receiver?: 0 | 1;
137
+ rangeId?: number;
138
+ edgeSlot?: number;
139
+ }): Promise<{
140
+ lowHz: number;
141
+ highHz: number;
142
+ rangeId: number;
143
+ edgeSlot: number;
144
+ } | null>;
145
+ setSpectrumFixedEdges(options: {
146
+ rangeId?: number;
147
+ edgeSlot?: number;
148
+ lowHz: number;
149
+ highHz: number;
150
+ }): Promise<{
151
+ lowHz: number;
152
+ highHz: number;
153
+ rangeId: number;
154
+ edgeSlot: number;
155
+ }>;
86
156
  waitForScopeFrame(options?: QueryOptions): Promise<import("../types").IcomScopeFrame | null>;
87
157
  /**
88
158
  * Set PTT (Push-To-Talk) state
@@ -123,15 +193,14 @@ export declare class IcomControl {
123
193
  filter?: number;
124
194
  modeName?: string;
125
195
  filterName?: string;
196
+ dataMode?: boolean;
126
197
  } | null>;
127
198
  /**
128
199
  * Read current transmit frequency (when TX)
129
200
  */
130
201
  readTransmitFrequency(options?: QueryOptions): Promise<number | null>;
131
- /**
132
- * Read transceiver state (TX/RX) via 0x1A 0x00 0x48
133
- * Note: Java comments mark this as not recommended; use with caution.
134
- */
202
+ readPtt(options?: QueryOptions): Promise<boolean | null>;
203
+ /** Read transceiver state (TX/RX) using standard Hamlib-aligned PTT status. */
135
204
  readTransceiverState(options?: QueryOptions): Promise<'TX' | 'RX' | 'UNKNOWN' | null>;
136
205
  /**
137
206
  * Read band edge data (0x02). Format may vary by rig; returns raw data bytes after command.
@@ -193,6 +262,8 @@ export declare class IcomControl {
193
262
  * @param level - Audio level (0-255)
194
263
  */
195
264
  setConnectorWLanLevel(level: number): Promise<void>;
265
+ getUsbAfLevel(options?: QueryOptions): Promise<WlanLevelReading | null>;
266
+ setUsbAfLevel(level: number): Promise<void>;
196
267
  /**
197
268
  * Set connector data routing mode
198
269
  * @param mode - Data routing mode (MIC, ACC, USB, WLAN)
@@ -207,17 +278,17 @@ export declare class IcomControl {
207
278
  * ==============================
208
279
  */
209
280
  /**
210
- * Read antenna tuner status (CI-V 0x1A/0x00)
281
+ * Read antenna tuner status (CI-V 0x1C/0x01)
211
282
  * 00=OFF, 01=ON, 02=TUNING
212
283
  */
213
284
  readTunerStatus(options?: QueryOptions): Promise<TunerStatusReading | null>;
214
285
  /**
215
- * Enable or disable internal antenna tuner (CI-V 0x1A/0x01)
286
+ * Enable or disable internal antenna tuner (CI-V 0x1C/0x01)
216
287
  * @param enabled true to enable, false to disable
217
288
  */
218
289
  setTunerEnabled(enabled: boolean): Promise<void>;
219
290
  /**
220
- * Start a manual tuning cycle (same as [TUNE] key) (CI-V 0x1A/0x02/0x00)
291
+ * Start a manual tuning cycle (same as [TUNE] key) (CI-V 0x1C/0x01/0x02)
221
292
  */
222
293
  startManualTune(): Promise<void>;
223
294
  /** Get AF (audio output) gain. Returns 0.0–1.0, or null on timeout. */
@@ -236,6 +307,10 @@ export declare class IcomControl {
236
307
  getMicGain(options?: QueryOptions): Promise<LevelReading | null>;
237
308
  /** Set microphone gain. Value 0.0–1.0. */
238
309
  setMicGain(value: number): void;
310
+ /** Get break-in delay. Returns 0.0–1.0, or null on timeout. */
311
+ getBreakInDelay(options?: QueryOptions): Promise<LevelReading | null>;
312
+ /** Set break-in delay. Value 0.0–1.0. */
313
+ setBreakInDelay(value: number): void;
239
314
  /** Get noise blanker level. 0.0 = off, >0.0 = on with strength. */
240
315
  getNBLevel(options?: QueryOptions): Promise<LevelReading | null>;
241
316
  /** Set noise blanker level. Value 0.0 (off) – 1.0. */
@@ -329,9 +404,11 @@ export declare class IcomControl {
329
404
  * @returns Parsed BCD integer value, or null if invalid
330
405
  */
331
406
  private static extractMeterData;
407
+ private static extractTrailingBcd;
332
408
  private static matchCommand;
333
409
  private static matchCommandFrame;
334
410
  private waitForCiv;
411
+ static parseFrequencyReply(frame: Buffer, payloadOffsetAfterCommand: number, byteLength?: number): number | null;
335
412
  static parseIcomFreqFromReply(frame: Buffer): number | null;
336
413
  sendAudioFloat32(samples: Float32Array, addLeadingBuffer?: boolean): void;
337
414
  sendAudioPcm16(samples: Int16Array): void;