icom-wlan-node 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +234 -23
- package/dist/core/Session.d.ts +9 -0
- package/dist/core/Session.js +34 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -2
- package/dist/rig/IcomCiv.js +6 -1
- package/dist/rig/IcomConstants.d.ts +72 -0
- package/dist/rig/IcomConstants.js +82 -1
- package/dist/rig/IcomControl.d.ts +185 -7
- package/dist/rig/IcomControl.js +819 -67
- package/dist/rig/IcomRigCommands.d.ts +7 -0
- package/dist/rig/IcomRigCommands.js +28 -0
- package/dist/transport/UdpClient.js +18 -4
- package/dist/types.d.ts +254 -0
- package/dist/types.js +46 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,37 +106,190 @@ await rig.setPtt(false);
|
|
|
106
106
|
- `civFrame(Buffer)` — one complete CI‑V frame (FE FE ... FD)
|
|
107
107
|
- `audio({ pcm16: Buffer })` — audio frames
|
|
108
108
|
- `error(Error)` — UDP errors
|
|
109
|
+
- `connectionLost(ConnectionLostInfo)` — session timeout detected
|
|
110
|
+
- `connectionRestored(ConnectionRestoredInfo)` — reconnected successfully
|
|
111
|
+
- `reconnectAttempting(ReconnectAttemptInfo)` — reconnect attempt started
|
|
112
|
+
- `reconnectFailed(ReconnectFailedInfo)` — reconnect attempt failed
|
|
109
113
|
- Methods
|
|
110
|
-
- `connect()` / `disconnect()` — connects control + CIV + audio sub‑sessions; resolves when all ready
|
|
111
|
-
- `sendCiv(buf: Buffer)` — send a raw CI‑V frame
|
|
112
|
-
- `setPtt(on: boolean)`
|
|
113
|
-
- `
|
|
114
|
+
- **Connection**: `connect()` / `disconnect()` — connects control + CIV + audio sub‑sessions; resolves when all ready
|
|
115
|
+
- **Raw CI‑V**: `sendCiv(buf: Buffer)` — send a raw CI‑V frame
|
|
116
|
+
- **Audio TX**: `setPtt(on: boolean)`, `sendAudioFloat32()`, `sendAudioPcm16()`
|
|
117
|
+
- **Rig Control**: `setFrequency()`, `setMode()`, `setConnectorDataMode()`, `setConnectorWLanLevel()`
|
|
118
|
+
- **Rig Query**: `readOperatingFrequency()`, `readOperatingMode()`, `readTransmitFrequency()`, `readTransceiverState()`, `readBandEdges()`
|
|
119
|
+
- **Meters (RX)**: `readSquelchStatus()`, `readAudioSquelch()`, `readOvfStatus()`, `getLevelMeter()`
|
|
120
|
+
- **Meters (TX)**: `readSWR()`, `readALC()`, `readPowerLevel()`, `readCompLevel()`
|
|
121
|
+
- **Power Supply**: `readVoltage()`, `readCurrent()`
|
|
122
|
+
- **Audio Config**: `getConnectorWLanLevel()`
|
|
123
|
+
- **Connection Monitoring**: `getConnectionPhase()`, `getConnectionMetrics()`, `getConnectionState()`, `isAnySessionDisconnected()`, `configureMonitoring()`
|
|
124
|
+
|
|
125
|
+
### Connection Management & Auto-Reconnect
|
|
126
|
+
|
|
127
|
+
The library features a robust state machine for connection lifecycle management with automatic reconnection support.
|
|
128
|
+
|
|
129
|
+
#### Connection State Machine
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
ConnectionPhase: IDLE → CONNECTING → CONNECTED → DISCONNECTING
|
|
133
|
+
↓ ↓
|
|
134
|
+
RECONNECTING ←────────────┘
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### Basic Usage
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
// Connect (idempotent - safe to call multiple times)
|
|
141
|
+
await rig.connect();
|
|
142
|
+
|
|
143
|
+
// Query connection phase
|
|
144
|
+
const phase = rig.getConnectionPhase(); // 'IDLE' | 'CONNECTING' | 'CONNECTED' | ...
|
|
145
|
+
|
|
146
|
+
// Get detailed metrics
|
|
147
|
+
const metrics = rig.getConnectionMetrics();
|
|
148
|
+
console.log(metrics.phase); // Current phase
|
|
149
|
+
console.log(metrics.uptime); // Milliseconds since connected
|
|
150
|
+
console.log(metrics.sessions); // Per-session states {control, civ, audio}
|
|
151
|
+
|
|
152
|
+
// Disconnect (also idempotent)
|
|
153
|
+
await rig.disconnect();
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### Connection Monitoring Events
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
// Connection lost (any session timeout)
|
|
160
|
+
rig.events.on('connectionLost', (info) => {
|
|
161
|
+
console.error(`Lost: ${info.sessionType}, idle: ${info.timeSinceLastData}ms`);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Connection restored after reconnect
|
|
165
|
+
rig.events.on('connectionRestored', (info) => {
|
|
166
|
+
console.log(`Restored after ${info.downtime}ms downtime`);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Reconnect attempt started
|
|
170
|
+
rig.events.on('reconnectAttempting', (info) => {
|
|
171
|
+
console.log(`Reconnect attempt #${info.attemptNumber}, delay: ${info.delay}ms`);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Reconnect attempt failed
|
|
175
|
+
rig.events.on('reconnectFailed', (info) => {
|
|
176
|
+
console.error(`Attempt #${info.attemptNumber} failed: ${info.error}`);
|
|
177
|
+
if (!info.willRetry) console.error('Giving up - max retries reached');
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Auto-Reconnect Configuration
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
rig.configureMonitoring({
|
|
185
|
+
timeout: 8000, // Session timeout: 8s (default: 5s)
|
|
186
|
+
checkInterval: 1000, // Check every 1s (default: 1s)
|
|
187
|
+
autoReconnect: true, // Enable auto-reconnect (default: false)
|
|
188
|
+
maxReconnectAttempts: 10, // Max retries (default: undefined = infinite)
|
|
189
|
+
reconnectBaseDelay: 2000, // Base delay: 2s (default: 2s)
|
|
190
|
+
reconnectMaxDelay: 30000 // Max delay: 30s (default: 30s, uses exponential backoff)
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Exponential Backoff**: Delays are `baseDelay × 2^(attempt-1)`, capped at `maxDelay`.
|
|
195
|
+
Example: 2s → 4s → 8s → 16s → 30s (capped) → 30s ...
|
|
196
|
+
|
|
197
|
+
#### Error Handling
|
|
198
|
+
|
|
199
|
+
**Common Errors**:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
try {
|
|
203
|
+
await rig.connect();
|
|
204
|
+
} catch (err) {
|
|
205
|
+
if (err.message.includes('timeout')) {
|
|
206
|
+
// Connection timeout (no response from radio)
|
|
207
|
+
} else if (err.message.includes('Login failed')) {
|
|
208
|
+
// Authentication error (check userName/password)
|
|
209
|
+
} else if (err.message.includes('Radio reported connected=false')) {
|
|
210
|
+
// Radio rejected connection (may be busy with another client)
|
|
211
|
+
} else if (err.message.includes('Cannot connect while disconnecting')) {
|
|
212
|
+
// Invalid state transition (wait for disconnect to complete)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Listen for UDP errors
|
|
217
|
+
rig.events.on('error', (err) => {
|
|
218
|
+
console.error('UDP error:', err.message);
|
|
219
|
+
// Network issues, invalid packets, etc.
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Connection States to Handle**:
|
|
224
|
+
- **CONNECTING**: Wait or show "connecting..." UI
|
|
225
|
+
- **CONNECTED**: Normal operation
|
|
226
|
+
- **RECONNECTING**: Show "reconnecting..." UI, disable TX
|
|
227
|
+
- **DISCONNECTING**: Cleanup in progress
|
|
228
|
+
- **IDLE**: Not connected
|
|
114
229
|
|
|
115
230
|
### High‑Level API
|
|
116
231
|
|
|
117
232
|
The library exposes common CI‑V operations as friendly methods. Addresses are handled internally (`ctrAddr=0xe0`, `rigAddr` discovered via capabilities).
|
|
118
233
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
- `
|
|
234
|
+
#### Rig Control
|
|
235
|
+
|
|
236
|
+
- `setFrequency(hz: number)` — Set operating frequency in Hz
|
|
237
|
+
- `setMode(mode: IcomMode | number, options?: { dataMode?: boolean })` — Set mode (supports string or numeric code)
|
|
238
|
+
- `setPtt(on: boolean)` — Key/unkey transmitter
|
|
239
|
+
|
|
240
|
+
**Supported Modes** (IcomMode string constants):
|
|
241
|
+
- `'LSB'`, `'USB'`, `'AM'`, `'CW'`, `'RTTY'`, `'FM'`, `'WFM'`, `'CW_R'`, `'RTTY_R'`, `'DV'`
|
|
242
|
+
- Or use numeric codes: `0x00` (LSB), `0x01` (USB), `0x02` (AM), etc.
|
|
243
|
+
|
|
244
|
+
#### Rig Query
|
|
245
|
+
|
|
122
246
|
- `readOperatingFrequency(options?: QueryOptions) => Promise<number|null>`
|
|
123
247
|
- `readOperatingMode(options?: QueryOptions) => Promise<{ mode: number; filter?: number; modeName?: string; filterName?: string } | null>`
|
|
124
248
|
- `readTransmitFrequency(options?: QueryOptions) => Promise<number|null>`
|
|
125
249
|
- `readTransceiverState(options?: QueryOptions) => Promise<'TX' | 'RX' | 'UNKNOWN' | null>`
|
|
126
250
|
- `readBandEdges(options?: QueryOptions) => Promise<Buffer|null>`
|
|
127
|
-
- `readSWR(options?: QueryOptions) => Promise<{ raw: number; swr: number; alert: boolean } | null>`
|
|
128
|
-
- `readALC(options?: QueryOptions) => Promise<{ raw: number; percent: number; alert: boolean } | null>`
|
|
129
|
-
- `getConnectorWLanLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>`
|
|
130
|
-
- `getLevelMeter(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>`
|
|
131
|
-
- `setConnectorWLanLevel(level: number)`
|
|
132
|
-
- `setConnectorDataMode(mode: ConnectorDataMode | number)`
|
|
133
251
|
|
|
134
|
-
|
|
252
|
+
#### Meters & Levels
|
|
253
|
+
|
|
254
|
+
**Reception Meters** (available anytime):
|
|
255
|
+
- `readSquelchStatus(options?: QueryOptions) => Promise<{ raw: number; isOpen: boolean } | null>` — Squelch gate state (CI-V 0x15/0x01)
|
|
256
|
+
- `readAudioSquelch(options?: QueryOptions) => Promise<{ raw: number; isOpen: boolean } | null>` — Audio squelch state (CI-V 0x15/0x05)
|
|
257
|
+
- `readOvfStatus(options?: QueryOptions) => Promise<{ raw: number; isOverload: boolean } | null>` — ADC overload detection (CI-V 0x15/0x07)
|
|
258
|
+
- `getLevelMeter(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` — S-meter level (CI-V 0x15/0x02)
|
|
259
|
+
|
|
260
|
+
**Transmission Meters** (require PTT on):
|
|
261
|
+
- `readSWR(options?: QueryOptions) => Promise<{ raw: number; swr: number; alert: boolean } | null>` — SWR meter (CI-V 0x15/0x12)
|
|
262
|
+
- `readALC(options?: QueryOptions) => Promise<{ raw: number; percent: number; alert: boolean } | null>` — ALC meter (CI-V 0x15/0x13)
|
|
263
|
+
- `readPowerLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` — Output power level (CI-V 0x15/0x11)
|
|
264
|
+
- `readCompLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` — Voice compression level (CI-V 0x15/0x14)
|
|
265
|
+
|
|
266
|
+
**Power Supply Monitoring**:
|
|
267
|
+
- `readVoltage(options?: QueryOptions) => Promise<{ raw: number; volts: number } | null>` — Supply voltage (CI-V 0x15/0x15)
|
|
268
|
+
- `readCurrent(options?: QueryOptions) => Promise<{ raw: number; amps: number } | null>` — Supply current draw (CI-V 0x15/0x16)
|
|
269
|
+
|
|
270
|
+
**Audio Configuration**:
|
|
271
|
+
- `getConnectorWLanLevel(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` — Get WLAN audio level (CI-V 0x1A/0x05/0x01/0x17)
|
|
272
|
+
- `setConnectorWLanLevel(level: number)` — Set WLAN audio level (0-255)
|
|
273
|
+
|
|
274
|
+
#### Connector Settings
|
|
275
|
+
|
|
276
|
+
- `setConnectorDataMode(mode: ConnectorDataMode | number)` — Set data routing mode (supports string or numeric)
|
|
277
|
+
|
|
278
|
+
**Supported Connector Modes** (ConnectorDataMode string constants):
|
|
279
|
+
- `'MIC'` (0x00), `'ACC'` (0x01), `'USB'` (0x02), `'WLAN'` (0x03)
|
|
280
|
+
|
|
281
|
+
#### Examples
|
|
135
282
|
|
|
136
283
|
```ts
|
|
137
|
-
// Set frequency and mode
|
|
284
|
+
// Set frequency and mode using string constants
|
|
138
285
|
await rig.setFrequency(14074000);
|
|
139
|
-
await rig.setMode(
|
|
286
|
+
await rig.setMode('USB', { dataMode: true }); // USB-D for FT8
|
|
287
|
+
|
|
288
|
+
// Or use numeric codes
|
|
289
|
+
await rig.setMode(0x01, { dataMode: true }); // USB=0x01
|
|
290
|
+
|
|
291
|
+
// Set LSB mode
|
|
292
|
+
await rig.setMode('LSB');
|
|
140
293
|
|
|
141
294
|
// Query current frequency (Hz)
|
|
142
295
|
const hz = await rig.readOperatingFrequency({ timeout: 3000 });
|
|
@@ -152,17 +305,75 @@ for (let n = 0; n < 10; n++) {
|
|
|
152
305
|
}
|
|
153
306
|
await rig.setPtt(false);
|
|
154
307
|
|
|
155
|
-
// Read meters
|
|
308
|
+
// Read reception meters (available anytime)
|
|
309
|
+
const squelch = await rig.readSquelchStatus({ timeout: 2000 });
|
|
310
|
+
if (squelch) {
|
|
311
|
+
console.log(`Squelch: ${squelch.isOpen ? 'OPEN' : 'CLOSED'}`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const audioSq = await rig.readAudioSquelch({ timeout: 2000 });
|
|
315
|
+
if (audioSq) {
|
|
316
|
+
console.log(`Audio Squelch: ${audioSq.isOpen ? 'OPEN' : 'CLOSED'}`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const ovf = await rig.readOvfStatus({ timeout: 2000 });
|
|
320
|
+
if (ovf) {
|
|
321
|
+
console.log(`ADC: ${ovf.isOverload ? '⚠️ OVERLOAD' : '✓ OK'}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const sMeter = await rig.getLevelMeter({ timeout: 2000 });
|
|
325
|
+
if (sMeter) {
|
|
326
|
+
console.log(`S-Meter: ${sMeter.percent.toFixed(1)}%`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Read power supply monitoring
|
|
330
|
+
const voltage = await rig.readVoltage({ timeout: 2000 });
|
|
331
|
+
if (voltage) {
|
|
332
|
+
console.log(`Voltage: ${voltage.volts.toFixed(2)}V`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const current = await rig.readCurrent({ timeout: 2000 });
|
|
336
|
+
if (current) {
|
|
337
|
+
console.log(`Current: ${current.amps.toFixed(2)}A`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Read transmission meters (requires PTT on)
|
|
341
|
+
await rig.setPtt(true);
|
|
342
|
+
await new Promise(r => setTimeout(r, 200)); // Wait for meters to stabilize
|
|
343
|
+
|
|
156
344
|
const swr = await rig.readSWR({ timeout: 2000 });
|
|
345
|
+
if (swr) {
|
|
346
|
+
console.log(`SWR: ${swr.swr.toFixed(2)} ${swr.alert ? '⚠️ HIGH' : '✓'}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
157
349
|
const alc = await rig.readALC({ timeout: 2000 });
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
350
|
+
if (alc) {
|
|
351
|
+
console.log(`ALC: ${alc.percent.toFixed(1)}% ${alc.alert ? '⚠️ HIGH' : '✓'}`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const power = await rig.readPowerLevel({ timeout: 2000 });
|
|
355
|
+
if (power) {
|
|
356
|
+
console.log(`Power: ${power.percent.toFixed(1)}%`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const comp = await rig.readCompLevel({ timeout: 2000 });
|
|
360
|
+
if (comp) {
|
|
361
|
+
console.log(`COMP: ${comp.percent.toFixed(1)}%`);
|
|
362
|
+
}
|
|
162
363
|
|
|
163
|
-
|
|
164
|
-
|
|
364
|
+
await rig.setPtt(false);
|
|
365
|
+
|
|
366
|
+
// Configure WLAN connector
|
|
367
|
+
const wlanLevel = await rig.getConnectorWLanLevel({ timeout: 2000 });
|
|
368
|
+
if (wlanLevel) {
|
|
369
|
+
console.log(`WLAN Level: ${wlanLevel.percent.toFixed(1)}%`);
|
|
165
370
|
}
|
|
371
|
+
|
|
372
|
+
// Set connector to WLAN mode using string constant
|
|
373
|
+
await rig.setConnectorDataMode('WLAN');
|
|
374
|
+
// Or numeric: await rig.setConnectorDataMode(0x03);
|
|
375
|
+
|
|
376
|
+
await rig.setConnectorWLanLevel(120); // Set WLAN audio level
|
|
166
377
|
```
|
|
167
378
|
|
|
168
379
|
## Design Notes
|
package/dist/core/Session.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { UdpClient } from '../transport/UdpClient';
|
|
2
|
+
import { SessionType } from '../types';
|
|
2
3
|
export interface SessionOptions {
|
|
3
4
|
ip: string;
|
|
4
5
|
port: number;
|
|
@@ -19,10 +20,12 @@ export declare class Session {
|
|
|
19
20
|
lastSentTime: number;
|
|
20
21
|
lastReceivedTime: number;
|
|
21
22
|
private address;
|
|
23
|
+
private handlers;
|
|
22
24
|
private txHistory;
|
|
23
25
|
private areYouThereTimer?;
|
|
24
26
|
private pingTimer?;
|
|
25
27
|
private destroyed;
|
|
28
|
+
sessionType?: SessionType;
|
|
26
29
|
constructor(address: SessionOptions, handlers: SessionHandlers);
|
|
27
30
|
open(): void;
|
|
28
31
|
close(): void;
|
|
@@ -40,4 +43,10 @@ export declare class Session {
|
|
|
40
43
|
stopPing(): void;
|
|
41
44
|
stopTimers(): void;
|
|
42
45
|
setRemote(ip: string, port: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* Reset session state to initial values
|
|
48
|
+
* Call this before reconnecting to ensure clean state
|
|
49
|
+
* (especially important after radio restart)
|
|
50
|
+
*/
|
|
51
|
+
resetState(): void;
|
|
43
52
|
}
|
package/dist/core/Session.js
CHANGED
|
@@ -19,21 +19,24 @@ class Session {
|
|
|
19
19
|
this.txHistory = new Map();
|
|
20
20
|
this.destroyed = false;
|
|
21
21
|
this.address = address;
|
|
22
|
+
this.handlers = handlers;
|
|
22
23
|
this.udp.on('data', (rinfo, data) => {
|
|
23
24
|
if (this.destroyed)
|
|
24
25
|
return;
|
|
25
26
|
this.lastReceivedTime = Date.now();
|
|
26
27
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
(0, debug_1.dbgV)(`RX port=${this.localPort} from ${rinfo.address}:${rinfo.port} len=${data.length} type=${t >= 0 ? '0x' + t.toString(16) : 'n/a'}`);
|
|
28
|
+
const type = data.length >= 6 ? data.readUInt16LE(4) : -1;
|
|
29
|
+
(0, debug_1.dbgV)(`RX port=${this.localPort} from ${rinfo.address}:${rinfo.port} len=${data.length} type=${type >= 0 ? '0x' + type.toString(16) : 'n/a'}`);
|
|
30
30
|
}
|
|
31
31
|
catch { }
|
|
32
32
|
handlers.onData(data);
|
|
33
33
|
});
|
|
34
34
|
this.udp.on('error', handlers.onSendError);
|
|
35
35
|
}
|
|
36
|
-
open() {
|
|
36
|
+
open() {
|
|
37
|
+
this.destroyed = false; // Reset destroyed flag to allow sending data after reconnection
|
|
38
|
+
this.udp.open();
|
|
39
|
+
}
|
|
37
40
|
close() {
|
|
38
41
|
this.stopTimers();
|
|
39
42
|
this.destroyed = true;
|
|
@@ -78,8 +81,9 @@ class Session {
|
|
|
78
81
|
} }
|
|
79
82
|
startAreYouThere() {
|
|
80
83
|
this.stopAreYouThere();
|
|
84
|
+
(0, debug_1.dbg)(`Starting AreYouThere timer for ${this.address.ip}:${this.address.port}`);
|
|
81
85
|
this.areYouThereTimer = setInterval(() => {
|
|
82
|
-
(0, debug_1.
|
|
86
|
+
(0, debug_1.dbg)(`AYT -> ${this.address.ip}:${this.address.port} localId=${this.localId}`);
|
|
83
87
|
this.sendUntracked(IcomPackets_1.ControlPacket.toBytes(IcomPackets_1.Cmd.ARE_YOU_THERE, 0, this.localId, 0));
|
|
84
88
|
}, 500);
|
|
85
89
|
}
|
|
@@ -101,5 +105,30 @@ class Session {
|
|
|
101
105
|
setRemote(ip, port) {
|
|
102
106
|
this.address = { ip, port };
|
|
103
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Reset session state to initial values
|
|
110
|
+
* Call this before reconnecting to ensure clean state
|
|
111
|
+
* (especially important after radio restart)
|
|
112
|
+
*/
|
|
113
|
+
resetState() {
|
|
114
|
+
// Reset destroyed flag to ensure session is usable after state reset
|
|
115
|
+
this.destroyed = false;
|
|
116
|
+
// Generate new IDs
|
|
117
|
+
this.localId = Date.now() >>> 0;
|
|
118
|
+
this.remoteId = 0;
|
|
119
|
+
// Reset sequence numbers
|
|
120
|
+
this.trackedSeq = 1;
|
|
121
|
+
this.pingSeq = 0;
|
|
122
|
+
this.innerSeq = 0x30;
|
|
123
|
+
// Reset tokens
|
|
124
|
+
this.rigToken = 0;
|
|
125
|
+
this.localToken = (Date.now() & 0xffff) >>> 0;
|
|
126
|
+
// Reset timestamps
|
|
127
|
+
this.lastSentTime = Date.now();
|
|
128
|
+
this.lastReceivedTime = Date.now();
|
|
129
|
+
// Clear history
|
|
130
|
+
this.txHistory.clear();
|
|
131
|
+
(0, debug_1.dbgV)(`Session state reset: localId=${this.localId}, localToken=${this.localToken}`);
|
|
132
|
+
}
|
|
104
133
|
}
|
|
105
134
|
exports.Session = Session;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export { IcomControl } from './rig/IcomControl';
|
|
3
|
-
export { MODE_MAP, CONNECTOR_MODE_MAP, DEFAULT_CONTROLLER_ADDR, METER_THRESHOLDS, getModeCode, getConnectorModeCode, getModeString, getConnectorModeString, getFilterString } from './rig/IcomConstants';
|
|
3
|
+
export { MODE_MAP, CONNECTOR_MODE_MAP, DEFAULT_CONTROLLER_ADDR, METER_THRESHOLDS, METER_CALIBRATION, getModeCode, getConnectorModeCode, getModeString, getConnectorModeString, getFilterString, rawToPowerPercent, rawToVoltage, rawToCurrent } from './rig/IcomConstants';
|
|
4
4
|
export { parseTwoByteBcd, intToTwoByteBcd } from './utils/bcd';
|
|
5
5
|
export { IcomRigCommands } from './rig/IcomRigCommands';
|
|
6
6
|
export { AUDIO_RATE } from './rig/IcomAudio';
|
package/dist/index.js
CHANGED
|
@@ -14,8 +14,8 @@ 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.AUDIO_RATE = exports.IcomRigCommands = exports.intToTwoByteBcd = exports.parseTwoByteBcd = exports.getFilterString = exports.getConnectorModeString = exports.getModeString = exports.getConnectorModeCode = exports.getModeCode = exports.METER_THRESHOLDS = exports.DEFAULT_CONTROLLER_ADDR = exports.CONNECTOR_MODE_MAP = exports.MODE_MAP = exports.IcomControl = void 0;
|
|
18
|
-
// Export types
|
|
17
|
+
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.IcomControl = void 0;
|
|
18
|
+
// Export types (includes ConnectionPhase, ConnectionMetrics, etc.)
|
|
19
19
|
__exportStar(require("./types"), exports);
|
|
20
20
|
// Export main class
|
|
21
21
|
var IcomControl_1 = require("./rig/IcomControl");
|
|
@@ -26,11 +26,15 @@ Object.defineProperty(exports, "MODE_MAP", { enumerable: true, get: function ()
|
|
|
26
26
|
Object.defineProperty(exports, "CONNECTOR_MODE_MAP", { enumerable: true, get: function () { return IcomConstants_1.CONNECTOR_MODE_MAP; } });
|
|
27
27
|
Object.defineProperty(exports, "DEFAULT_CONTROLLER_ADDR", { enumerable: true, get: function () { return IcomConstants_1.DEFAULT_CONTROLLER_ADDR; } });
|
|
28
28
|
Object.defineProperty(exports, "METER_THRESHOLDS", { enumerable: true, get: function () { return IcomConstants_1.METER_THRESHOLDS; } });
|
|
29
|
+
Object.defineProperty(exports, "METER_CALIBRATION", { enumerable: true, get: function () { return IcomConstants_1.METER_CALIBRATION; } });
|
|
29
30
|
Object.defineProperty(exports, "getModeCode", { enumerable: true, get: function () { return IcomConstants_1.getModeCode; } });
|
|
30
31
|
Object.defineProperty(exports, "getConnectorModeCode", { enumerable: true, get: function () { return IcomConstants_1.getConnectorModeCode; } });
|
|
31
32
|
Object.defineProperty(exports, "getModeString", { enumerable: true, get: function () { return IcomConstants_1.getModeString; } });
|
|
32
33
|
Object.defineProperty(exports, "getConnectorModeString", { enumerable: true, get: function () { return IcomConstants_1.getConnectorModeString; } });
|
|
33
34
|
Object.defineProperty(exports, "getFilterString", { enumerable: true, get: function () { return IcomConstants_1.getFilterString; } });
|
|
35
|
+
Object.defineProperty(exports, "rawToPowerPercent", { enumerable: true, get: function () { return IcomConstants_1.rawToPowerPercent; } });
|
|
36
|
+
Object.defineProperty(exports, "rawToVoltage", { enumerable: true, get: function () { return IcomConstants_1.rawToVoltage; } });
|
|
37
|
+
Object.defineProperty(exports, "rawToCurrent", { enumerable: true, get: function () { return IcomConstants_1.rawToCurrent; } });
|
|
34
38
|
// Export BCD utilities
|
|
35
39
|
var bcd_1 = require("./utils/bcd");
|
|
36
40
|
Object.defineProperty(exports, "parseTwoByteBcd", { enumerable: true, get: function () { return bcd_1.parseTwoByteBcd; } });
|
package/dist/rig/IcomCiv.js
CHANGED
|
@@ -12,9 +12,14 @@ class IcomCiv {
|
|
|
12
12
|
}
|
|
13
13
|
start() {
|
|
14
14
|
this.stop();
|
|
15
|
-
//
|
|
15
|
+
// CIV session watchdog: Keep-alive mechanism to maintain CIV connection
|
|
16
|
+
// Matches FT8CN's startCivDataTimer() implementation:
|
|
17
|
+
// - Checks every 500ms if CIV session is receiving data
|
|
18
|
+
// - If no data received for >2000ms, sends OpenClose(true) to re-establish connection
|
|
19
|
+
// - This prevents silent disconnection where radio stops responding without error
|
|
16
20
|
this.openTimer = setInterval(() => {
|
|
17
21
|
if (Date.now() - this.sess.lastReceivedTime > 2000) {
|
|
22
|
+
(0, debug_1.dbg)('CIV watchdog: No data for >2s, sending OpenClose to keep alive');
|
|
18
23
|
this.sendOpenClose(true);
|
|
19
24
|
}
|
|
20
25
|
}, 500);
|
|
@@ -82,3 +82,75 @@ export declare const METER_THRESHOLDS: {
|
|
|
82
82
|
* Matches FT8CN's IComPacketTypes.METER_TIMER_PERIOD_MS
|
|
83
83
|
*/
|
|
84
84
|
export declare const METER_TIMER_PERIOD_MS = 500;
|
|
85
|
+
/**
|
|
86
|
+
* Meter calibration constants for physical unit conversion
|
|
87
|
+
* Based on IC-705 official CI-V reference manual
|
|
88
|
+
*/
|
|
89
|
+
export declare const METER_CALIBRATION: {
|
|
90
|
+
/**
|
|
91
|
+
* Power level (CI-V 0x15/0x11) calibration points
|
|
92
|
+
* Used for converting raw BCD to percentage
|
|
93
|
+
*/
|
|
94
|
+
readonly POWER: {
|
|
95
|
+
/** 50% power reference point */
|
|
96
|
+
readonly HALF: {
|
|
97
|
+
readonly raw: 323;
|
|
98
|
+
readonly percent: 50;
|
|
99
|
+
};
|
|
100
|
+
/** 100% power reference point */
|
|
101
|
+
readonly FULL: {
|
|
102
|
+
readonly raw: 531;
|
|
103
|
+
readonly percent: 100;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Voltage (CI-V 0x15/0x15) calibration points
|
|
108
|
+
* Used for converting raw BCD to volts
|
|
109
|
+
*/
|
|
110
|
+
readonly VOLTAGE: {
|
|
111
|
+
/** Low voltage reference: 5V */
|
|
112
|
+
readonly LOW: {
|
|
113
|
+
readonly raw: 117;
|
|
114
|
+
readonly volts: 5;
|
|
115
|
+
};
|
|
116
|
+
/** High voltage reference: 16V */
|
|
117
|
+
readonly HIGH: {
|
|
118
|
+
readonly raw: 577;
|
|
119
|
+
readonly volts: 16;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Current (CI-V 0x15/0x16) calibration points
|
|
124
|
+
* Used for converting raw BCD to amperes
|
|
125
|
+
*/
|
|
126
|
+
readonly CURRENT: {
|
|
127
|
+
/** Low current reference: 2A */
|
|
128
|
+
readonly LOW: {
|
|
129
|
+
readonly raw: 289;
|
|
130
|
+
readonly amps: 2;
|
|
131
|
+
};
|
|
132
|
+
/** High current reference: 4A */
|
|
133
|
+
readonly HIGH: {
|
|
134
|
+
readonly raw: 577;
|
|
135
|
+
readonly amps: 4;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Convert raw power level to percentage
|
|
141
|
+
* @param raw - Raw BCD value (0-255)
|
|
142
|
+
* @returns Power percentage (0-100%)
|
|
143
|
+
*/
|
|
144
|
+
export declare function rawToPowerPercent(raw: number): number;
|
|
145
|
+
/**
|
|
146
|
+
* Convert raw voltage reading to volts
|
|
147
|
+
* @param raw - Raw BCD value (0-255)
|
|
148
|
+
* @returns Voltage in volts
|
|
149
|
+
*/
|
|
150
|
+
export declare function rawToVoltage(raw: number): number;
|
|
151
|
+
/**
|
|
152
|
+
* Convert raw current reading to amperes
|
|
153
|
+
* @param raw - Raw BCD value (0-255)
|
|
154
|
+
* @returns Current in amperes
|
|
155
|
+
*/
|
|
156
|
+
export declare function rawToCurrent(raw: number): number;
|
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
* Based on FT8CN's IcomRigConstant.java
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.METER_TIMER_PERIOD_MS = exports.METER_THRESHOLDS = exports.DEFAULT_CONTROLLER_ADDR = exports.CONNECTOR_MODE_MAP = exports.MODE_MAP = void 0;
|
|
7
|
+
exports.METER_CALIBRATION = exports.METER_TIMER_PERIOD_MS = exports.METER_THRESHOLDS = exports.DEFAULT_CONTROLLER_ADDR = exports.CONNECTOR_MODE_MAP = exports.MODE_MAP = void 0;
|
|
8
8
|
exports.getModeCode = getModeCode;
|
|
9
9
|
exports.getConnectorModeCode = getConnectorModeCode;
|
|
10
10
|
exports.getModeString = getModeString;
|
|
11
11
|
exports.getConnectorModeString = getConnectorModeString;
|
|
12
12
|
exports.getFilterString = getFilterString;
|
|
13
|
+
exports.rawToPowerPercent = rawToPowerPercent;
|
|
14
|
+
exports.rawToVoltage = rawToVoltage;
|
|
15
|
+
exports.rawToCurrent = rawToCurrent;
|
|
13
16
|
/**
|
|
14
17
|
* Operating modes supported by ICOM radios
|
|
15
18
|
* LSB:0, USB:1, AM:2, CW:3, RTTY:4, FM:5, WFM:6, CW_R:7, RTTY_R:8, DV:17
|
|
@@ -110,3 +113,81 @@ exports.METER_THRESHOLDS = {
|
|
|
110
113
|
* Matches FT8CN's IComPacketTypes.METER_TIMER_PERIOD_MS
|
|
111
114
|
*/
|
|
112
115
|
exports.METER_TIMER_PERIOD_MS = 500;
|
|
116
|
+
/**
|
|
117
|
+
* Meter calibration constants for physical unit conversion
|
|
118
|
+
* Based on IC-705 official CI-V reference manual
|
|
119
|
+
*/
|
|
120
|
+
exports.METER_CALIBRATION = {
|
|
121
|
+
/**
|
|
122
|
+
* Power level (CI-V 0x15/0x11) calibration points
|
|
123
|
+
* Used for converting raw BCD to percentage
|
|
124
|
+
*/
|
|
125
|
+
POWER: {
|
|
126
|
+
/** 50% power reference point */
|
|
127
|
+
HALF: { raw: 0x0143, percent: 50 },
|
|
128
|
+
/** 100% power reference point */
|
|
129
|
+
FULL: { raw: 0x0213, percent: 100 }
|
|
130
|
+
},
|
|
131
|
+
/**
|
|
132
|
+
* Voltage (CI-V 0x15/0x15) calibration points
|
|
133
|
+
* Used for converting raw BCD to volts
|
|
134
|
+
*/
|
|
135
|
+
VOLTAGE: {
|
|
136
|
+
/** Low voltage reference: 5V */
|
|
137
|
+
LOW: { raw: 0x0075, volts: 5.0 },
|
|
138
|
+
/** High voltage reference: 16V */
|
|
139
|
+
HIGH: { raw: 0x0241, volts: 16.0 }
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* Current (CI-V 0x15/0x16) calibration points
|
|
143
|
+
* Used for converting raw BCD to amperes
|
|
144
|
+
*/
|
|
145
|
+
CURRENT: {
|
|
146
|
+
/** Low current reference: 2A */
|
|
147
|
+
LOW: { raw: 0x0121, amps: 2.0 },
|
|
148
|
+
/** High current reference: 4A */
|
|
149
|
+
HIGH: { raw: 0x0241, amps: 4.0 }
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Linear interpolation helper for meter value conversion
|
|
154
|
+
* @param raw - Raw BCD value
|
|
155
|
+
* @param x1 - Lower calibration point (raw value)
|
|
156
|
+
* @param y1 - Lower calibration point (physical value)
|
|
157
|
+
* @param x2 - Upper calibration point (raw value)
|
|
158
|
+
* @param y2 - Upper calibration point (physical value)
|
|
159
|
+
* @returns Interpolated physical value
|
|
160
|
+
*/
|
|
161
|
+
function linearInterpolate(raw, x1, y1, x2, y2) {
|
|
162
|
+
// Clamp to range
|
|
163
|
+
if (raw <= x1)
|
|
164
|
+
return y1;
|
|
165
|
+
if (raw >= x2)
|
|
166
|
+
return y2;
|
|
167
|
+
// Linear interpolation: y = y1 + (raw - x1) * (y2 - y1) / (x2 - x1)
|
|
168
|
+
return y1 + ((raw - x1) * (y2 - y1)) / (x2 - x1);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Convert raw power level to percentage
|
|
172
|
+
* @param raw - Raw BCD value (0-255)
|
|
173
|
+
* @returns Power percentage (0-100%)
|
|
174
|
+
*/
|
|
175
|
+
function rawToPowerPercent(raw) {
|
|
176
|
+
return linearInterpolate(raw, exports.METER_CALIBRATION.POWER.HALF.raw, exports.METER_CALIBRATION.POWER.HALF.percent, exports.METER_CALIBRATION.POWER.FULL.raw, exports.METER_CALIBRATION.POWER.FULL.percent);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Convert raw voltage reading to volts
|
|
180
|
+
* @param raw - Raw BCD value (0-255)
|
|
181
|
+
* @returns Voltage in volts
|
|
182
|
+
*/
|
|
183
|
+
function rawToVoltage(raw) {
|
|
184
|
+
return linearInterpolate(raw, exports.METER_CALIBRATION.VOLTAGE.LOW.raw, exports.METER_CALIBRATION.VOLTAGE.LOW.volts, exports.METER_CALIBRATION.VOLTAGE.HIGH.raw, exports.METER_CALIBRATION.VOLTAGE.HIGH.volts);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Convert raw current reading to amperes
|
|
188
|
+
* @param raw - Raw BCD value (0-255)
|
|
189
|
+
* @returns Current in amperes
|
|
190
|
+
*/
|
|
191
|
+
function rawToCurrent(raw) {
|
|
192
|
+
return linearInterpolate(raw, exports.METER_CALIBRATION.CURRENT.LOW.raw, exports.METER_CALIBRATION.CURRENT.LOW.amps, exports.METER_CALIBRATION.CURRENT.HIGH.raw, exports.METER_CALIBRATION.CURRENT.HIGH.amps);
|
|
193
|
+
}
|