tci-client-node 0.2.0 → 0.4.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/CHANGELOG.md +17 -0
- package/README.md +50 -6
- package/dist/audio/index.cjs +11 -0
- package/dist/audio/index.cjs.map +1 -1
- package/dist/audio/index.d.cts +4 -2
- package/dist/audio/index.d.ts +4 -2
- package/dist/audio/index.js +10 -0
- package/dist/audio/index.js.map +1 -1
- package/dist/dialect/index.cjs +193 -12
- package/dist/dialect/index.cjs.map +1 -1
- package/dist/dialect/index.d.cts +3 -2
- package/dist/dialect/index.d.ts +3 -2
- package/dist/dialect/index.js +193 -12
- package/dist/dialect/index.js.map +1 -1
- package/dist/errors-CT3b8LLw.d.cts +9 -0
- package/dist/errors-CT3b8LLw.d.ts +9 -0
- package/dist/index.cjs +606 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -6
- package/dist/index.d.ts +68 -6
- package/dist/index.js +599 -6
- package/dist/index.js.map +1 -1
- package/dist/meter/index.cjs +348 -0
- package/dist/meter/index.cjs.map +1 -0
- package/dist/meter/index.d.cts +78 -0
- package/dist/meter/index.d.ts +78 -0
- package/dist/meter/index.js +317 -0
- package/dist/meter/index.js.map +1 -0
- package/dist/protocol/index.d.cts +36 -2
- package/dist/protocol/index.d.ts +36 -2
- package/dist/testing/index.cjs +58 -0
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +23 -1
- package/dist/testing/index.d.ts +23 -1
- package/dist/testing/index.js +58 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/types-C0qVJVSO.d.ts +74 -0
- package/dist/types-C4MtLKoy.d.cts +74 -0
- package/dist/{types-xRotf9gW.d.cts → types-CYK_NOTz.d.cts} +5 -1
- package/dist/{types-9seY9Th-.d.ts → types-DVIPU-VR.d.ts} +5 -1
- package/package.json +11 -2
- package/dist/index-lbK6NGY4.d.cts +0 -42
- package/dist/index-paj1AOJY.d.ts +0 -42
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
// src/meter/types.ts
|
|
2
|
+
var UNKNOWN_TCI_METER_CAPABILITIES = {
|
|
3
|
+
rxLevel: "unknown",
|
|
4
|
+
rxAverageLevel: "unknown",
|
|
5
|
+
rxPeakBin: "unknown",
|
|
6
|
+
txMicLevel: "unknown",
|
|
7
|
+
txRmsPower: "unknown",
|
|
8
|
+
txPeakPower: "unknown",
|
|
9
|
+
txSwr: "unknown",
|
|
10
|
+
txAlcDbfs: "unknown"
|
|
11
|
+
};
|
|
12
|
+
function cloneMeterCapabilities(capabilities) {
|
|
13
|
+
return { ...capabilities };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/meter/adapters.ts
|
|
17
|
+
var StandardTciMeterAdapter = class {
|
|
18
|
+
declaredCapabilities;
|
|
19
|
+
options;
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
this.declaredCapabilities = {
|
|
23
|
+
...UNKNOWN_TCI_METER_CAPABILITIES,
|
|
24
|
+
rxLevel: "declared",
|
|
25
|
+
txMicLevel: "declared",
|
|
26
|
+
txRmsPower: "declared",
|
|
27
|
+
txPeakPower: "declared",
|
|
28
|
+
txSwr: "declared",
|
|
29
|
+
rxAverageLevel: options.supportsRxExtended ? "declared" : "unknown",
|
|
30
|
+
rxPeakBin: options.supportsRxExtended ? "declared" : "unknown",
|
|
31
|
+
txAlcDbfs: options.txAlcUnit === "dbfs" ? "declared" : "unknown",
|
|
32
|
+
...options.capabilities
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
normalizeInterval(intervalMs) {
|
|
36
|
+
const requestedMs = Math.round(intervalMs);
|
|
37
|
+
if (this.options.interval?.fixedMs !== void 0) {
|
|
38
|
+
return { requestedMs, appliedMs: this.options.interval.fixedMs };
|
|
39
|
+
}
|
|
40
|
+
const minMs = this.options.interval?.minMs ?? requestedMs;
|
|
41
|
+
const maxMs = this.options.interval?.maxMs ?? requestedMs;
|
|
42
|
+
const normalized = Math.max(minMs, Math.min(maxMs, requestedMs));
|
|
43
|
+
return {
|
|
44
|
+
requestedMs,
|
|
45
|
+
appliedMs: this.options.interval?.reportsApplied === false ? void 0 : normalized
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
buildEnableCommand(kind, enabled, intervalMs) {
|
|
49
|
+
return {
|
|
50
|
+
name: kind === "rx" ? "RX_SENSORS_ENABLE" : "TX_SENSORS_ENABLE",
|
|
51
|
+
args: enabled ? [true, intervalMs] : [false]
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
decode(command, receivedAtMs) {
|
|
55
|
+
switch (command.name) {
|
|
56
|
+
case "rx_sensors":
|
|
57
|
+
return decodeRx(command, receivedAtMs, "rx_sensors");
|
|
58
|
+
case "rx_channel_sensors":
|
|
59
|
+
return decodeRx(command, receivedAtMs, "rx_channel_sensors");
|
|
60
|
+
case "rx_channel_sensors_ex":
|
|
61
|
+
return decodeRx(command, receivedAtMs, "rx_channel_sensors_ex");
|
|
62
|
+
case "tx_sensors":
|
|
63
|
+
return decodeTx(command, receivedAtMs, this.options.txAlcUnit);
|
|
64
|
+
default:
|
|
65
|
+
return void 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
function createUnknownTciMeterAdapter() {
|
|
70
|
+
return new StandardTciMeterAdapter({
|
|
71
|
+
capabilities: cloneMeterCapabilities(UNKNOWN_TCI_METER_CAPABILITIES),
|
|
72
|
+
interval: { reportsApplied: false }
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function decodeRx(command, receivedAtMs, source) {
|
|
76
|
+
const receiver = integer(command.args[0]);
|
|
77
|
+
const hasChannel = source !== "rx_sensors";
|
|
78
|
+
const channel = hasChannel ? integer(command.args[1]) : 0;
|
|
79
|
+
const levelIndex = hasChannel ? 2 : 1;
|
|
80
|
+
const levelDbm = finite(command.args[levelIndex]);
|
|
81
|
+
if (receiver === void 0 || receiver < 0 || channel === void 0 || channel < 0 || levelDbm === void 0) {
|
|
82
|
+
return { issue: `Invalid ${command.originalName} meter frame: ${command.raw}` };
|
|
83
|
+
}
|
|
84
|
+
const frame = { receiver, channel, levelDbm, source, receivedAtMs };
|
|
85
|
+
if (source === "rx_channel_sensors_ex") {
|
|
86
|
+
const averageLevelDbm = finite(command.args[3]);
|
|
87
|
+
const peakBinDbm = finite(command.args[4]);
|
|
88
|
+
if (averageLevelDbm === void 0 || peakBinDbm === void 0) {
|
|
89
|
+
return { issue: `Invalid ${command.originalName} extended meter frame: ${command.raw}` };
|
|
90
|
+
}
|
|
91
|
+
frame.averageLevelDbm = averageLevelDbm;
|
|
92
|
+
frame.peakBinDbm = peakBinDbm;
|
|
93
|
+
if (command.args.length > 5) frame.extraArgs = command.args.slice(5);
|
|
94
|
+
} else if (command.args.length > levelIndex + 1) {
|
|
95
|
+
frame.extraArgs = command.args.slice(levelIndex + 1);
|
|
96
|
+
}
|
|
97
|
+
return { decoded: { kind: "rx", frame } };
|
|
98
|
+
}
|
|
99
|
+
function decodeTx(command, receivedAtMs, alcUnit) {
|
|
100
|
+
const trx = integer(command.args[0]);
|
|
101
|
+
if (trx === void 0 || trx < 0) {
|
|
102
|
+
return { issue: `Invalid ${command.originalName} transmitter index: ${command.raw}` };
|
|
103
|
+
}
|
|
104
|
+
const micLevelDbm = optionalFinite(command.args[1]);
|
|
105
|
+
const rmsPowerWatts = optionalFinite(command.args[2]);
|
|
106
|
+
const peakPowerWatts = optionalFinite(command.args[3]);
|
|
107
|
+
const swr = optionalFinite(command.args[4]);
|
|
108
|
+
const alcValue = alcUnit ? optionalFinite(command.args[5]) : void 0;
|
|
109
|
+
if (micLevelDbm.invalid || rmsPowerWatts.invalid || peakPowerWatts.invalid || swr.invalid || alcValue?.invalid) {
|
|
110
|
+
return { issue: `Invalid ${command.originalName} numeric meter frame: ${command.raw}` };
|
|
111
|
+
}
|
|
112
|
+
if (rmsPowerWatts.value !== void 0 && rmsPowerWatts.value < 0 || peakPowerWatts.value !== void 0 && peakPowerWatts.value < 0 || swr.value !== void 0 && swr.value < 1) {
|
|
113
|
+
return { issue: `Out-of-range ${command.originalName} meter frame: ${command.raw}` };
|
|
114
|
+
}
|
|
115
|
+
if (micLevelDbm.value === void 0 && rmsPowerWatts.value === void 0 && peakPowerWatts.value === void 0 && swr.value === void 0 && alcValue?.value === void 0) {
|
|
116
|
+
return { issue: `Empty ${command.originalName} meter frame: ${command.raw}` };
|
|
117
|
+
}
|
|
118
|
+
const frame = {
|
|
119
|
+
trx,
|
|
120
|
+
micLevelDbm: micLevelDbm.value,
|
|
121
|
+
rmsPowerWatts: rmsPowerWatts.value,
|
|
122
|
+
peakPowerWatts: peakPowerWatts.value,
|
|
123
|
+
swr: swr.value,
|
|
124
|
+
receivedAtMs
|
|
125
|
+
};
|
|
126
|
+
if (alcUnit && alcValue?.value !== void 0) frame.alc = { value: alcValue.value, unit: alcUnit };
|
|
127
|
+
const knownArgs = alcUnit ? 6 : 5;
|
|
128
|
+
if (command.args.length > knownArgs) frame.extraArgs = command.args.slice(knownArgs);
|
|
129
|
+
return { decoded: { kind: "tx", frame } };
|
|
130
|
+
}
|
|
131
|
+
function finite(value) {
|
|
132
|
+
if (value === void 0 || value === "") return void 0;
|
|
133
|
+
const parsed = Number(value);
|
|
134
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
135
|
+
}
|
|
136
|
+
function integer(value) {
|
|
137
|
+
const parsed = finite(value);
|
|
138
|
+
return parsed !== void 0 && Number.isInteger(parsed) ? parsed : void 0;
|
|
139
|
+
}
|
|
140
|
+
function optionalFinite(value) {
|
|
141
|
+
if (value === void 0 || value === "") return { invalid: false };
|
|
142
|
+
const parsed = Number(value);
|
|
143
|
+
return Number.isFinite(parsed) ? { value: parsed, invalid: false } : { invalid: true };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/meter/TciMeterStreamSession.ts
|
|
147
|
+
import { EventEmitter } from "eventemitter3";
|
|
148
|
+
|
|
149
|
+
// src/errors.ts
|
|
150
|
+
var TciError = class extends Error {
|
|
151
|
+
code;
|
|
152
|
+
details;
|
|
153
|
+
constructor(code, message, details) {
|
|
154
|
+
super(message);
|
|
155
|
+
this.name = "TciError";
|
|
156
|
+
this.code = code;
|
|
157
|
+
this.details = details;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// src/meter/TciMeterStreamSession.ts
|
|
162
|
+
var RX_COALESCE_MS = 20;
|
|
163
|
+
var TciMeterStreamSession = class extends EventEmitter {
|
|
164
|
+
receiver;
|
|
165
|
+
channel;
|
|
166
|
+
trx;
|
|
167
|
+
requestedIntervalMs;
|
|
168
|
+
appliedIntervalMs;
|
|
169
|
+
rxEnabled;
|
|
170
|
+
txEnabled;
|
|
171
|
+
adapter;
|
|
172
|
+
callbacks;
|
|
173
|
+
capabilities;
|
|
174
|
+
pendingRx = /* @__PURE__ */ new Map();
|
|
175
|
+
closed = false;
|
|
176
|
+
constructor(options) {
|
|
177
|
+
super();
|
|
178
|
+
this.receiver = options.receiver;
|
|
179
|
+
this.channel = options.channel;
|
|
180
|
+
this.trx = options.trx;
|
|
181
|
+
this.requestedIntervalMs = options.requestedIntervalMs;
|
|
182
|
+
this.appliedIntervalMs = options.appliedIntervalMs;
|
|
183
|
+
this.rxEnabled = options.rxEnabled;
|
|
184
|
+
this.txEnabled = options.txEnabled;
|
|
185
|
+
this.adapter = options.adapter;
|
|
186
|
+
this.callbacks = options.callbacks;
|
|
187
|
+
this.capabilities = cloneMeterCapabilities(options.adapter.declaredCapabilities);
|
|
188
|
+
}
|
|
189
|
+
getCapabilities() {
|
|
190
|
+
return cloneMeterCapabilities(this.capabilities);
|
|
191
|
+
}
|
|
192
|
+
async close() {
|
|
193
|
+
if (this.closed) return;
|
|
194
|
+
this.closed = true;
|
|
195
|
+
this.clearPendingRx();
|
|
196
|
+
try {
|
|
197
|
+
await this.callbacks.close();
|
|
198
|
+
} finally {
|
|
199
|
+
this.emit("closed");
|
|
200
|
+
this.removeAllListeners();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
_acceptCommand(command, receivedAtMs) {
|
|
204
|
+
if (this.closed) return;
|
|
205
|
+
this.acceptEnableAcknowledgement(command);
|
|
206
|
+
const result = this.adapter.decode(command, receivedAtMs);
|
|
207
|
+
if (!result) return;
|
|
208
|
+
if (result.issue) {
|
|
209
|
+
this.emit("error", new TciError("protocol-error", result.issue));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (result.decoded?.kind === "rx") this.acceptRxFrame(result.decoded.frame);
|
|
213
|
+
if (result.decoded?.kind === "tx") this.acceptTxFrame(result.decoded.frame);
|
|
214
|
+
}
|
|
215
|
+
_fail(error) {
|
|
216
|
+
if (this.closed) return;
|
|
217
|
+
this.closed = true;
|
|
218
|
+
this.clearPendingRx();
|
|
219
|
+
this.emit("error", error);
|
|
220
|
+
this.emit("closed");
|
|
221
|
+
this.removeAllListeners();
|
|
222
|
+
}
|
|
223
|
+
acceptRxFrame(frame) {
|
|
224
|
+
if (!this.rxEnabled || frame.receiver !== this.receiver || frame.channel !== this.channel) return;
|
|
225
|
+
this.observe("rxLevel");
|
|
226
|
+
if (frame.averageLevelDbm !== void 0) this.observe("rxAverageLevel");
|
|
227
|
+
if (frame.peakBinDbm !== void 0) this.observe("rxPeakBin");
|
|
228
|
+
const key = `${frame.receiver}:${frame.channel}`;
|
|
229
|
+
const pending = this.pendingRx.get(key);
|
|
230
|
+
if (pending) {
|
|
231
|
+
const sameReading = Math.abs(pending.frame.levelDbm - frame.levelDbm) < 0.05;
|
|
232
|
+
if (sameReading && rxPriority(frame) >= rxPriority(pending.frame)) {
|
|
233
|
+
clearTimeout(pending.timer);
|
|
234
|
+
this.pendingRx.delete(key);
|
|
235
|
+
} else if (sameReading) {
|
|
236
|
+
return;
|
|
237
|
+
} else {
|
|
238
|
+
this.flushRx(key, pending);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const timer = setTimeout(() => {
|
|
242
|
+
const current = this.pendingRx.get(key);
|
|
243
|
+
if (current?.frame === frame) this.flushRx(key, current);
|
|
244
|
+
}, RX_COALESCE_MS);
|
|
245
|
+
this.pendingRx.set(key, { frame, timer });
|
|
246
|
+
}
|
|
247
|
+
acceptTxFrame(frame) {
|
|
248
|
+
if (!this.txEnabled || frame.trx !== this.trx) return;
|
|
249
|
+
if (frame.micLevelDbm !== void 0) this.observe("txMicLevel");
|
|
250
|
+
if (frame.rmsPowerWatts !== void 0) this.observe("txRmsPower");
|
|
251
|
+
if (frame.peakPowerWatts !== void 0) this.observe("txPeakPower");
|
|
252
|
+
if (frame.swr !== void 0) this.observe("txSwr");
|
|
253
|
+
if (frame.alc?.unit === "dbfs") this.observe("txAlcDbfs");
|
|
254
|
+
this.emit("txFrame", frame);
|
|
255
|
+
}
|
|
256
|
+
acceptEnableAcknowledgement(command) {
|
|
257
|
+
const enabled = command.args[0]?.toLowerCase();
|
|
258
|
+
if (enabled !== "true" && enabled !== "1" && enabled !== "on") return;
|
|
259
|
+
if (command.name === "rx_sensors_enable" && this.rxEnabled) {
|
|
260
|
+
this.acknowledge(["rxLevel"]);
|
|
261
|
+
}
|
|
262
|
+
if (command.name === "tx_sensors_enable" && this.txEnabled) {
|
|
263
|
+
this.acknowledge(["txMicLevel", "txRmsPower", "txPeakPower", "txSwr"]);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
acknowledge(keys) {
|
|
267
|
+
let changed = false;
|
|
268
|
+
for (const key of keys) {
|
|
269
|
+
if (supportRank(this.capabilities[key]) < supportRank("acknowledged")) {
|
|
270
|
+
this.capabilities[key] = "acknowledged";
|
|
271
|
+
changed = true;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (changed) this.emit("capabilitiesChanged", this.getCapabilities());
|
|
275
|
+
}
|
|
276
|
+
observe(key) {
|
|
277
|
+
if (this.capabilities[key] === "observed") return;
|
|
278
|
+
this.capabilities[key] = "observed";
|
|
279
|
+
this.emit("capabilitiesChanged", this.getCapabilities());
|
|
280
|
+
}
|
|
281
|
+
flushRx(key, pending) {
|
|
282
|
+
clearTimeout(pending.timer);
|
|
283
|
+
if (this.pendingRx.get(key) === pending) this.pendingRx.delete(key);
|
|
284
|
+
this.emit("rxFrame", pending.frame);
|
|
285
|
+
}
|
|
286
|
+
clearPendingRx() {
|
|
287
|
+
for (const pending of this.pendingRx.values()) clearTimeout(pending.timer);
|
|
288
|
+
this.pendingRx.clear();
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
function rxPriority(frame) {
|
|
292
|
+
if (frame.source === "rx_channel_sensors_ex") return 3;
|
|
293
|
+
if (frame.source === "rx_channel_sensors") return 2;
|
|
294
|
+
return 1;
|
|
295
|
+
}
|
|
296
|
+
function supportRank(value) {
|
|
297
|
+
switch (value) {
|
|
298
|
+
case "unsupported":
|
|
299
|
+
return -1;
|
|
300
|
+
case "unknown":
|
|
301
|
+
return 0;
|
|
302
|
+
case "declared":
|
|
303
|
+
return 1;
|
|
304
|
+
case "acknowledged":
|
|
305
|
+
return 2;
|
|
306
|
+
case "observed":
|
|
307
|
+
return 3;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
export {
|
|
311
|
+
StandardTciMeterAdapter,
|
|
312
|
+
TciMeterStreamSession,
|
|
313
|
+
UNKNOWN_TCI_METER_CAPABILITIES,
|
|
314
|
+
cloneMeterCapabilities,
|
|
315
|
+
createUnknownTciMeterAdapter
|
|
316
|
+
};
|
|
317
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/meter/types.ts","../../src/meter/adapters.ts","../../src/meter/TciMeterStreamSession.ts","../../src/errors.ts"],"sourcesContent":["import type { TciCommand } from '../protocol/index.js';\n\nexport type TciMeterSupport =\n | 'unknown'\n | 'declared'\n | 'acknowledged'\n | 'observed'\n | 'unsupported';\n\nexport interface TciMeterCapabilities {\n rxLevel: TciMeterSupport;\n rxAverageLevel: TciMeterSupport;\n rxPeakBin: TciMeterSupport;\n txMicLevel: TciMeterSupport;\n txRmsPower: TciMeterSupport;\n txPeakPower: TciMeterSupport;\n txSwr: TciMeterSupport;\n txAlcDbfs: TciMeterSupport;\n}\n\nexport type TciRxMeterSource = 'rx_sensors' | 'rx_channel_sensors' | 'rx_channel_sensors_ex';\n\nexport interface TciRxMeterFrame {\n receiver: number;\n channel: number;\n levelDbm: number;\n averageLevelDbm?: number;\n peakBinDbm?: number;\n source: TciRxMeterSource;\n receivedAtMs: number;\n extraArgs?: readonly string[];\n}\n\nexport interface TciTxMeterFrame {\n trx: number;\n micLevelDbm?: number;\n rmsPowerWatts?: number;\n peakPowerWatts?: number;\n swr?: number;\n alc?: { value: number; unit: 'dbfs' | 'percent' };\n receivedAtMs: number;\n extraArgs?: readonly string[];\n}\n\nexport interface TciMeterStreamOptions {\n receiver?: number;\n channel?: number;\n trx?: number;\n rx?: boolean;\n tx?: boolean;\n intervalMs?: number;\n}\n\nexport interface TciMeterInterval {\n requestedMs: number;\n appliedMs?: number;\n}\n\nexport interface TciMeterCommand {\n name: string;\n args: readonly unknown[];\n}\n\nexport type TciMeterDecodedFrame =\n | { kind: 'rx'; frame: TciRxMeterFrame }\n | { kind: 'tx'; frame: TciTxMeterFrame };\n\nexport interface TciMeterDecodeResult {\n decoded?: TciMeterDecodedFrame;\n issue?: string;\n}\n\nexport interface TciMeterAdapter {\n readonly declaredCapabilities: TciMeterCapabilities;\n normalizeInterval(intervalMs: number): TciMeterInterval;\n buildEnableCommand(kind: 'rx' | 'tx', enabled: boolean, intervalMs: number): TciMeterCommand;\n decode(command: TciCommand, receivedAtMs: number): TciMeterDecodeResult | undefined;\n}\n\nexport const UNKNOWN_TCI_METER_CAPABILITIES: TciMeterCapabilities = {\n rxLevel: 'unknown',\n rxAverageLevel: 'unknown',\n rxPeakBin: 'unknown',\n txMicLevel: 'unknown',\n txRmsPower: 'unknown',\n txPeakPower: 'unknown',\n txSwr: 'unknown',\n txAlcDbfs: 'unknown',\n};\n\nexport function cloneMeterCapabilities(capabilities: TciMeterCapabilities): TciMeterCapabilities {\n return { ...capabilities };\n}\n","import type { TciCommand } from '../protocol/index.js';\nimport {\n cloneMeterCapabilities,\n UNKNOWN_TCI_METER_CAPABILITIES,\n type TciMeterAdapter,\n type TciMeterCapabilities,\n type TciMeterDecodeResult,\n type TciMeterInterval,\n type TciRxMeterFrame,\n type TciTxMeterFrame,\n} from './types.js';\n\ninterface StandardTciMeterAdapterOptions {\n capabilities?: Partial<TciMeterCapabilities>;\n interval?: { minMs?: number; maxMs?: number; fixedMs?: number; reportsApplied?: boolean };\n supportsRxExtended?: boolean;\n txAlcUnit?: 'dbfs' | 'percent';\n}\n\nexport class StandardTciMeterAdapter implements TciMeterAdapter {\n readonly declaredCapabilities: TciMeterCapabilities;\n private readonly options: StandardTciMeterAdapterOptions;\n\n constructor(options: StandardTciMeterAdapterOptions = {}) {\n this.options = options;\n this.declaredCapabilities = {\n ...UNKNOWN_TCI_METER_CAPABILITIES,\n rxLevel: 'declared',\n txMicLevel: 'declared',\n txRmsPower: 'declared',\n txPeakPower: 'declared',\n txSwr: 'declared',\n rxAverageLevel: options.supportsRxExtended ? 'declared' : 'unknown',\n rxPeakBin: options.supportsRxExtended ? 'declared' : 'unknown',\n txAlcDbfs: options.txAlcUnit === 'dbfs' ? 'declared' : 'unknown',\n ...options.capabilities,\n };\n }\n\n normalizeInterval(intervalMs: number): TciMeterInterval {\n const requestedMs = Math.round(intervalMs);\n if (this.options.interval?.fixedMs !== undefined) {\n return { requestedMs, appliedMs: this.options.interval.fixedMs };\n }\n const minMs = this.options.interval?.minMs ?? requestedMs;\n const maxMs = this.options.interval?.maxMs ?? requestedMs;\n const normalized = Math.max(minMs, Math.min(maxMs, requestedMs));\n return {\n requestedMs,\n appliedMs: this.options.interval?.reportsApplied === false ? undefined : normalized,\n };\n }\n\n buildEnableCommand(kind: 'rx' | 'tx', enabled: boolean, intervalMs: number) {\n return {\n name: kind === 'rx' ? 'RX_SENSORS_ENABLE' : 'TX_SENSORS_ENABLE',\n args: enabled ? [true, intervalMs] : [false],\n };\n }\n\n decode(command: TciCommand, receivedAtMs: number): TciMeterDecodeResult | undefined {\n switch (command.name) {\n case 'rx_sensors':\n return decodeRx(command, receivedAtMs, 'rx_sensors');\n case 'rx_channel_sensors':\n return decodeRx(command, receivedAtMs, 'rx_channel_sensors');\n case 'rx_channel_sensors_ex':\n return decodeRx(command, receivedAtMs, 'rx_channel_sensors_ex');\n case 'tx_sensors':\n return decodeTx(command, receivedAtMs, this.options.txAlcUnit);\n default:\n return undefined;\n }\n }\n}\n\nexport function createUnknownTciMeterAdapter(): TciMeterAdapter {\n return new StandardTciMeterAdapter({\n capabilities: cloneMeterCapabilities(UNKNOWN_TCI_METER_CAPABILITIES),\n interval: { reportsApplied: false },\n });\n}\n\nfunction decodeRx(\n command: TciCommand,\n receivedAtMs: number,\n source: TciRxMeterFrame['source'],\n): TciMeterDecodeResult {\n const receiver = integer(command.args[0]);\n const hasChannel = source !== 'rx_sensors';\n const channel = hasChannel ? integer(command.args[1]) : 0;\n const levelIndex = hasChannel ? 2 : 1;\n const levelDbm = finite(command.args[levelIndex]);\n if (receiver === undefined || receiver < 0 || channel === undefined || channel < 0 || levelDbm === undefined) {\n return { issue: `Invalid ${command.originalName} meter frame: ${command.raw}` };\n }\n\n const frame: TciRxMeterFrame = { receiver, channel, levelDbm, source, receivedAtMs };\n if (source === 'rx_channel_sensors_ex') {\n const averageLevelDbm = finite(command.args[3]);\n const peakBinDbm = finite(command.args[4]);\n if (averageLevelDbm === undefined || peakBinDbm === undefined) {\n return { issue: `Invalid ${command.originalName} extended meter frame: ${command.raw}` };\n }\n frame.averageLevelDbm = averageLevelDbm;\n frame.peakBinDbm = peakBinDbm;\n if (command.args.length > 5) frame.extraArgs = command.args.slice(5);\n } else if (command.args.length > levelIndex + 1) {\n frame.extraArgs = command.args.slice(levelIndex + 1);\n }\n return { decoded: { kind: 'rx', frame } };\n}\n\nfunction decodeTx(\n command: TciCommand,\n receivedAtMs: number,\n alcUnit?: 'dbfs' | 'percent',\n): TciMeterDecodeResult {\n const trx = integer(command.args[0]);\n if (trx === undefined || trx < 0) {\n return { issue: `Invalid ${command.originalName} transmitter index: ${command.raw}` };\n }\n\n const micLevelDbm = optionalFinite(command.args[1]);\n const rmsPowerWatts = optionalFinite(command.args[2]);\n const peakPowerWatts = optionalFinite(command.args[3]);\n const swr = optionalFinite(command.args[4]);\n const alcValue = alcUnit ? optionalFinite(command.args[5]) : undefined;\n if (micLevelDbm.invalid || rmsPowerWatts.invalid || peakPowerWatts.invalid || swr.invalid || alcValue?.invalid) {\n return { issue: `Invalid ${command.originalName} numeric meter frame: ${command.raw}` };\n }\n if ((rmsPowerWatts.value !== undefined && rmsPowerWatts.value < 0)\n || (peakPowerWatts.value !== undefined && peakPowerWatts.value < 0)\n || (swr.value !== undefined && swr.value < 1)) {\n return { issue: `Out-of-range ${command.originalName} meter frame: ${command.raw}` };\n }\n if (micLevelDbm.value === undefined && rmsPowerWatts.value === undefined\n && peakPowerWatts.value === undefined && swr.value === undefined && alcValue?.value === undefined) {\n return { issue: `Empty ${command.originalName} meter frame: ${command.raw}` };\n }\n\n const frame: TciTxMeterFrame = {\n trx,\n micLevelDbm: micLevelDbm.value,\n rmsPowerWatts: rmsPowerWatts.value,\n peakPowerWatts: peakPowerWatts.value,\n swr: swr.value,\n receivedAtMs,\n };\n if (alcUnit && alcValue?.value !== undefined) frame.alc = { value: alcValue.value, unit: alcUnit };\n const knownArgs = alcUnit ? 6 : 5;\n if (command.args.length > knownArgs) frame.extraArgs = command.args.slice(knownArgs);\n return { decoded: { kind: 'tx', frame } };\n}\n\nfunction finite(value: string | undefined): number | undefined {\n if (value === undefined || value === '') return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction integer(value: string | undefined): number | undefined {\n const parsed = finite(value);\n return parsed !== undefined && Number.isInteger(parsed) ? parsed : undefined;\n}\n\nfunction optionalFinite(value: string | undefined): { value?: number; invalid: boolean } {\n if (value === undefined || value === '') return { invalid: false };\n const parsed = Number(value);\n return Number.isFinite(parsed) ? { value: parsed, invalid: false } : { invalid: true };\n}\n","import { EventEmitter } from 'eventemitter3';\nimport { TciError } from '../errors.js';\nimport type { TciCommand } from '../protocol/index.js';\nimport {\n cloneMeterCapabilities,\n type TciMeterAdapter,\n type TciMeterCapabilities,\n type TciMeterSupport,\n type TciRxMeterFrame,\n type TciTxMeterFrame,\n} from './types.js';\n\nconst RX_COALESCE_MS = 20;\n\nexport interface TciMeterStreamEvents {\n rxFrame: (frame: TciRxMeterFrame) => void;\n txFrame: (frame: TciTxMeterFrame) => void;\n capabilitiesChanged: (capabilities: TciMeterCapabilities) => void;\n error: (error: TciError) => void;\n closed: () => void;\n}\n\ninterface TciMeterStreamCallbacks {\n close: () => Promise<void>;\n}\n\ninterface PendingRxFrame {\n frame: TciRxMeterFrame;\n timer: NodeJS.Timeout;\n}\n\nexport class TciMeterStreamSession extends EventEmitter<TciMeterStreamEvents> {\n readonly receiver: number;\n readonly channel: number;\n readonly trx: number;\n readonly requestedIntervalMs: number;\n readonly appliedIntervalMs?: number;\n readonly rxEnabled: boolean;\n readonly txEnabled: boolean;\n\n private readonly adapter: TciMeterAdapter;\n private readonly callbacks: TciMeterStreamCallbacks;\n private capabilities: TciMeterCapabilities;\n private readonly pendingRx = new Map<string, PendingRxFrame>();\n private closed = false;\n\n constructor(options: {\n receiver: number;\n channel: number;\n trx: number;\n requestedIntervalMs: number;\n appliedIntervalMs?: number;\n rxEnabled: boolean;\n txEnabled: boolean;\n adapter: TciMeterAdapter;\n callbacks: TciMeterStreamCallbacks;\n }) {\n super();\n this.receiver = options.receiver;\n this.channel = options.channel;\n this.trx = options.trx;\n this.requestedIntervalMs = options.requestedIntervalMs;\n this.appliedIntervalMs = options.appliedIntervalMs;\n this.rxEnabled = options.rxEnabled;\n this.txEnabled = options.txEnabled;\n this.adapter = options.adapter;\n this.callbacks = options.callbacks;\n this.capabilities = cloneMeterCapabilities(options.adapter.declaredCapabilities);\n }\n\n getCapabilities(): TciMeterCapabilities {\n return cloneMeterCapabilities(this.capabilities);\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.clearPendingRx();\n try {\n await this.callbacks.close();\n } finally {\n this.emit('closed');\n this.removeAllListeners();\n }\n }\n\n _acceptCommand(command: TciCommand, receivedAtMs: number): void {\n if (this.closed) return;\n this.acceptEnableAcknowledgement(command);\n const result = this.adapter.decode(command, receivedAtMs);\n if (!result) return;\n if (result.issue) {\n this.emit('error', new TciError('protocol-error', result.issue));\n return;\n }\n if (result.decoded?.kind === 'rx') this.acceptRxFrame(result.decoded.frame);\n if (result.decoded?.kind === 'tx') this.acceptTxFrame(result.decoded.frame);\n }\n\n _fail(error: TciError): void {\n if (this.closed) return;\n this.closed = true;\n this.clearPendingRx();\n this.emit('error', error);\n this.emit('closed');\n this.removeAllListeners();\n }\n\n private acceptRxFrame(frame: TciRxMeterFrame): void {\n if (!this.rxEnabled || frame.receiver !== this.receiver || frame.channel !== this.channel) return;\n this.observe('rxLevel');\n if (frame.averageLevelDbm !== undefined) this.observe('rxAverageLevel');\n if (frame.peakBinDbm !== undefined) this.observe('rxPeakBin');\n\n const key = `${frame.receiver}:${frame.channel}`;\n const pending = this.pendingRx.get(key);\n if (pending) {\n const sameReading = Math.abs(pending.frame.levelDbm - frame.levelDbm) < 0.05;\n if (sameReading && rxPriority(frame) >= rxPriority(pending.frame)) {\n clearTimeout(pending.timer);\n this.pendingRx.delete(key);\n } else if (sameReading) {\n return;\n } else {\n this.flushRx(key, pending);\n }\n }\n const timer = setTimeout(() => {\n const current = this.pendingRx.get(key);\n if (current?.frame === frame) this.flushRx(key, current);\n }, RX_COALESCE_MS);\n this.pendingRx.set(key, { frame, timer });\n }\n\n private acceptTxFrame(frame: TciTxMeterFrame): void {\n if (!this.txEnabled || frame.trx !== this.trx) return;\n if (frame.micLevelDbm !== undefined) this.observe('txMicLevel');\n if (frame.rmsPowerWatts !== undefined) this.observe('txRmsPower');\n if (frame.peakPowerWatts !== undefined) this.observe('txPeakPower');\n if (frame.swr !== undefined) this.observe('txSwr');\n if (frame.alc?.unit === 'dbfs') this.observe('txAlcDbfs');\n this.emit('txFrame', frame);\n }\n\n private acceptEnableAcknowledgement(command: TciCommand): void {\n const enabled = command.args[0]?.toLowerCase();\n if (enabled !== 'true' && enabled !== '1' && enabled !== 'on') return;\n if (command.name === 'rx_sensors_enable' && this.rxEnabled) {\n this.acknowledge(['rxLevel']);\n }\n if (command.name === 'tx_sensors_enable' && this.txEnabled) {\n this.acknowledge(['txMicLevel', 'txRmsPower', 'txPeakPower', 'txSwr']);\n }\n }\n\n private acknowledge(keys: Array<keyof TciMeterCapabilities>): void {\n let changed = false;\n for (const key of keys) {\n if (supportRank(this.capabilities[key]) < supportRank('acknowledged')) {\n this.capabilities[key] = 'acknowledged';\n changed = true;\n }\n }\n if (changed) this.emit('capabilitiesChanged', this.getCapabilities());\n }\n\n private observe(key: keyof TciMeterCapabilities): void {\n if (this.capabilities[key] === 'observed') return;\n this.capabilities[key] = 'observed';\n this.emit('capabilitiesChanged', this.getCapabilities());\n }\n\n private flushRx(key: string, pending: PendingRxFrame): void {\n clearTimeout(pending.timer);\n if (this.pendingRx.get(key) === pending) this.pendingRx.delete(key);\n this.emit('rxFrame', pending.frame);\n }\n\n private clearPendingRx(): void {\n for (const pending of this.pendingRx.values()) clearTimeout(pending.timer);\n this.pendingRx.clear();\n }\n}\n\nfunction rxPriority(frame: TciRxMeterFrame): number {\n if (frame.source === 'rx_channel_sensors_ex') return 3;\n if (frame.source === 'rx_channel_sensors') return 2;\n return 1;\n}\n\nfunction supportRank(value: TciMeterSupport): number {\n switch (value) {\n case 'unsupported': return -1;\n case 'unknown': return 0;\n case 'declared': return 1;\n case 'acknowledged': return 2;\n case 'observed': return 3;\n }\n}\n","export type TciErrorCode =\n | 'connect-timeout'\n | 'handshake-timeout'\n | 'invalid-handshake'\n | 'unknown-dialect'\n | 'command-timeout'\n | 'not-connected'\n | 'disconnected'\n | 'protocol-error'\n | 'invalid-frame'\n | 'cancelled';\n\nexport class TciError extends Error {\n readonly code: TciErrorCode;\n readonly details?: unknown;\n\n constructor(code: TciErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'TciError';\n this.code = code;\n this.details = details;\n }\n}\n\nexport function toTciError(error: unknown, fallbackCode: TciErrorCode = 'protocol-error'): TciError {\n if (error instanceof TciError) {\n return error;\n }\n if (error instanceof Error) {\n return new TciError(fallbackCode, error.message, error);\n }\n return new TciError(fallbackCode, String(error), error);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,2 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
1
|
+
import { T as TciCommand, a as TciCommandInput } from '../text-BwCWY1k1.cjs';
|
|
2
|
+
export { c as commandKey, e as escapeTciText, f as formatTciCommand, i as isCommandReplyTo, n as normalizeCommandName, p as parseTciCommand, b as parseTciText, u as unescapeTciText } from '../text-BwCWY1k1.cjs';
|
|
3
|
+
import { T as TciError } from '../errors-CT3b8LLw.cjs';
|
|
4
|
+
|
|
5
|
+
type TciCommandMatcher = (reply: TciCommand, request: TciCommand) => boolean;
|
|
6
|
+
interface QueueCommandOptions {
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
matcher?: TciCommandMatcher;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
}
|
|
11
|
+
interface QueuedCommandResult {
|
|
12
|
+
request: TciCommand;
|
|
13
|
+
reply: TciCommand;
|
|
14
|
+
}
|
|
15
|
+
interface TciCommandQueueOptions {
|
|
16
|
+
send: (raw: string) => void | Promise<void>;
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
}
|
|
19
|
+
declare class TciCommandQueue {
|
|
20
|
+
private readonly send;
|
|
21
|
+
private readonly defaultTimeoutMs;
|
|
22
|
+
private queue;
|
|
23
|
+
private active?;
|
|
24
|
+
private connected;
|
|
25
|
+
constructor(options: TciCommandQueueOptions);
|
|
26
|
+
setConnected(connected: boolean): void;
|
|
27
|
+
enqueue(command: TciCommandInput, options?: QueueCommandOptions): Promise<QueuedCommandResult>;
|
|
28
|
+
handleCommand(commandInput: TciCommandInput): boolean;
|
|
29
|
+
cancelAll(error?: TciError): void;
|
|
30
|
+
get size(): number;
|
|
31
|
+
private pump;
|
|
32
|
+
private finishActive;
|
|
33
|
+
private rejectPending;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { type QueueCommandOptions, type QueuedCommandResult, TciCommand, TciCommandInput, type TciCommandMatcher, TciCommandQueue, type TciCommandQueueOptions };
|
package/dist/protocol/index.d.ts
CHANGED
|
@@ -1,2 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
1
|
+
import { T as TciCommand, a as TciCommandInput } from '../text-BwCWY1k1.js';
|
|
2
|
+
export { c as commandKey, e as escapeTciText, f as formatTciCommand, i as isCommandReplyTo, n as normalizeCommandName, p as parseTciCommand, b as parseTciText, u as unescapeTciText } from '../text-BwCWY1k1.js';
|
|
3
|
+
import { T as TciError } from '../errors-CT3b8LLw.js';
|
|
4
|
+
|
|
5
|
+
type TciCommandMatcher = (reply: TciCommand, request: TciCommand) => boolean;
|
|
6
|
+
interface QueueCommandOptions {
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
matcher?: TciCommandMatcher;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
}
|
|
11
|
+
interface QueuedCommandResult {
|
|
12
|
+
request: TciCommand;
|
|
13
|
+
reply: TciCommand;
|
|
14
|
+
}
|
|
15
|
+
interface TciCommandQueueOptions {
|
|
16
|
+
send: (raw: string) => void | Promise<void>;
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
}
|
|
19
|
+
declare class TciCommandQueue {
|
|
20
|
+
private readonly send;
|
|
21
|
+
private readonly defaultTimeoutMs;
|
|
22
|
+
private queue;
|
|
23
|
+
private active?;
|
|
24
|
+
private connected;
|
|
25
|
+
constructor(options: TciCommandQueueOptions);
|
|
26
|
+
setConnected(connected: boolean): void;
|
|
27
|
+
enqueue(command: TciCommandInput, options?: QueueCommandOptions): Promise<QueuedCommandResult>;
|
|
28
|
+
handleCommand(commandInput: TciCommandInput): boolean;
|
|
29
|
+
cancelAll(error?: TciError): void;
|
|
30
|
+
get size(): number;
|
|
31
|
+
private pump;
|
|
32
|
+
private finishActive;
|
|
33
|
+
private rejectPending;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { type QueueCommandOptions, type QueuedCommandResult, TciCommand, TciCommandInput, type TciCommandMatcher, TciCommandQueue, type TciCommandQueueOptions };
|
package/dist/testing/index.cjs
CHANGED
|
@@ -337,6 +337,7 @@ var MockTciServer = class {
|
|
|
337
337
|
mode = "DIGU";
|
|
338
338
|
ptt = false;
|
|
339
339
|
drive = 30;
|
|
340
|
+
iqSampleRate = 48e3;
|
|
340
341
|
constructor(options = {}) {
|
|
341
342
|
this.options = {
|
|
342
343
|
port: options.port ?? 0,
|
|
@@ -408,6 +409,20 @@ var MockTciServer = class {
|
|
|
408
409
|
});
|
|
409
410
|
this.broadcastBinary(frame);
|
|
410
411
|
}
|
|
412
|
+
sendIqFrame(options = {}) {
|
|
413
|
+
const samples = options.samples ?? new Float32Array(4096);
|
|
414
|
+
const frame = buildStreamFrame({
|
|
415
|
+
receiver: options.receiver ?? 0,
|
|
416
|
+
sampleRate: options.sampleRate ?? this.iqSampleRate,
|
|
417
|
+
sampleType: options.sampleType ?? 3 /* FLOAT32 */,
|
|
418
|
+
streamType: 0 /* IQ_STREAM */,
|
|
419
|
+
channels: options.channels ?? 2,
|
|
420
|
+
samples,
|
|
421
|
+
payload: options.payload,
|
|
422
|
+
lengthSemantics: options.lengthSemantics
|
|
423
|
+
});
|
|
424
|
+
this.broadcastBinary(frame);
|
|
425
|
+
}
|
|
411
426
|
sendTxChrono(options = {}) {
|
|
412
427
|
const sampleType = options.sampleType ?? 3 /* FLOAT32 */;
|
|
413
428
|
const channels = options.channels ?? 1;
|
|
@@ -423,6 +438,38 @@ var MockTciServer = class {
|
|
|
423
438
|
});
|
|
424
439
|
this.broadcastBinary(frame);
|
|
425
440
|
}
|
|
441
|
+
sendRxMeterFrame(options) {
|
|
442
|
+
const receiver = options.receiver ?? 0;
|
|
443
|
+
const channel = options.channel ?? 0;
|
|
444
|
+
const source = options.source ?? "channel";
|
|
445
|
+
if (source === "legacy") {
|
|
446
|
+
this.broadcastCommand("RX_SENSORS", [receiver, options.levelDbm]);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (source === "extended") {
|
|
450
|
+
this.broadcastCommand("RX_CHANNEL_SENSORS_EX", [
|
|
451
|
+
receiver,
|
|
452
|
+
channel,
|
|
453
|
+
options.levelDbm,
|
|
454
|
+
options.averageLevelDbm ?? options.levelDbm,
|
|
455
|
+
options.peakBinDbm ?? options.levelDbm
|
|
456
|
+
]);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
this.broadcastCommand("RX_CHANNEL_SENSORS", [receiver, channel, options.levelDbm]);
|
|
460
|
+
}
|
|
461
|
+
sendTxMeterFrame(options = {}) {
|
|
462
|
+
const args = [
|
|
463
|
+
options.trx ?? 0,
|
|
464
|
+
options.micLevelDbm ?? -20,
|
|
465
|
+
options.rmsPowerWatts ?? 0,
|
|
466
|
+
options.peakPowerWatts ?? options.rmsPowerWatts ?? 0,
|
|
467
|
+
options.swr ?? 1
|
|
468
|
+
];
|
|
469
|
+
if (options.alc !== void 0) args.push(options.alc);
|
|
470
|
+
if (options.extraArgs) args.push(...options.extraArgs);
|
|
471
|
+
this.broadcastCommand("TX_SENSORS", args);
|
|
472
|
+
}
|
|
426
473
|
closeClients() {
|
|
427
474
|
for (const socket of this.sockets) {
|
|
428
475
|
socket.close();
|
|
@@ -437,6 +484,8 @@ var MockTciServer = class {
|
|
|
437
484
|
"DEVICE:Mock ExpertSDR3;",
|
|
438
485
|
"MODULATIONS_LIST:LSB,USB,CW,AM,NFM,DIGU,DIGL;",
|
|
439
486
|
`VFO:0,0,${this.frequency};`,
|
|
487
|
+
`DDS:0,${this.frequency};`,
|
|
488
|
+
`IQ_SAMPLERATE:${this.iqSampleRate};`,
|
|
440
489
|
`MODULATION:0,${this.mode};`,
|
|
441
490
|
`TRX:0,${this.ptt};`,
|
|
442
491
|
`DRIVE:0,${this.drive};`,
|
|
@@ -517,6 +566,15 @@ var MockTciServer = class {
|
|
|
517
566
|
case "tx_stream_audio_buffering":
|
|
518
567
|
socket.send(formatTciCommand(command.originalName, command.args));
|
|
519
568
|
break;
|
|
569
|
+
case "iq_samplerate": {
|
|
570
|
+
if (command.args[0] !== void 0) this.iqSampleRate = Number(command.args[0]);
|
|
571
|
+
socket.send(formatTciCommand("IQ_SAMPLERATE", [this.iqSampleRate]));
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
case "iq_start":
|
|
575
|
+
case "iq_stop":
|
|
576
|
+
socket.send(formatTciCommand(command.originalName, command.args));
|
|
577
|
+
break;
|
|
520
578
|
default:
|
|
521
579
|
if (this.options.echoUnknown) {
|
|
522
580
|
socket.send(formatTciCommand(command.originalName, command.args));
|