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
package/dist/dialect/index.cjs
CHANGED
|
@@ -36,6 +36,166 @@ __export(dialect_exports, {
|
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(dialect_exports);
|
|
38
38
|
|
|
39
|
+
// src/meter/types.ts
|
|
40
|
+
var UNKNOWN_TCI_METER_CAPABILITIES = {
|
|
41
|
+
rxLevel: "unknown",
|
|
42
|
+
rxAverageLevel: "unknown",
|
|
43
|
+
rxPeakBin: "unknown",
|
|
44
|
+
txMicLevel: "unknown",
|
|
45
|
+
txRmsPower: "unknown",
|
|
46
|
+
txPeakPower: "unknown",
|
|
47
|
+
txSwr: "unknown",
|
|
48
|
+
txAlcDbfs: "unknown"
|
|
49
|
+
};
|
|
50
|
+
function cloneMeterCapabilities(capabilities) {
|
|
51
|
+
return { ...capabilities };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/meter/adapters.ts
|
|
55
|
+
var StandardTciMeterAdapter = class {
|
|
56
|
+
declaredCapabilities;
|
|
57
|
+
options;
|
|
58
|
+
constructor(options = {}) {
|
|
59
|
+
this.options = options;
|
|
60
|
+
this.declaredCapabilities = {
|
|
61
|
+
...UNKNOWN_TCI_METER_CAPABILITIES,
|
|
62
|
+
rxLevel: "declared",
|
|
63
|
+
txMicLevel: "declared",
|
|
64
|
+
txRmsPower: "declared",
|
|
65
|
+
txPeakPower: "declared",
|
|
66
|
+
txSwr: "declared",
|
|
67
|
+
rxAverageLevel: options.supportsRxExtended ? "declared" : "unknown",
|
|
68
|
+
rxPeakBin: options.supportsRxExtended ? "declared" : "unknown",
|
|
69
|
+
txAlcDbfs: options.txAlcUnit === "dbfs" ? "declared" : "unknown",
|
|
70
|
+
...options.capabilities
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
normalizeInterval(intervalMs) {
|
|
74
|
+
const requestedMs = Math.round(intervalMs);
|
|
75
|
+
if (this.options.interval?.fixedMs !== void 0) {
|
|
76
|
+
return { requestedMs, appliedMs: this.options.interval.fixedMs };
|
|
77
|
+
}
|
|
78
|
+
const minMs = this.options.interval?.minMs ?? requestedMs;
|
|
79
|
+
const maxMs = this.options.interval?.maxMs ?? requestedMs;
|
|
80
|
+
const normalized = Math.max(minMs, Math.min(maxMs, requestedMs));
|
|
81
|
+
return {
|
|
82
|
+
requestedMs,
|
|
83
|
+
appliedMs: this.options.interval?.reportsApplied === false ? void 0 : normalized
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
buildEnableCommand(kind, enabled, intervalMs) {
|
|
87
|
+
return {
|
|
88
|
+
name: kind === "rx" ? "RX_SENSORS_ENABLE" : "TX_SENSORS_ENABLE",
|
|
89
|
+
args: enabled ? [true, intervalMs] : [false]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
decode(command, receivedAtMs) {
|
|
93
|
+
switch (command.name) {
|
|
94
|
+
case "rx_sensors":
|
|
95
|
+
return decodeRx(command, receivedAtMs, "rx_sensors");
|
|
96
|
+
case "rx_channel_sensors":
|
|
97
|
+
return decodeRx(command, receivedAtMs, "rx_channel_sensors");
|
|
98
|
+
case "rx_channel_sensors_ex":
|
|
99
|
+
return decodeRx(command, receivedAtMs, "rx_channel_sensors_ex");
|
|
100
|
+
case "tx_sensors":
|
|
101
|
+
return decodeTx(command, receivedAtMs, this.options.txAlcUnit);
|
|
102
|
+
default:
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
function createUnknownTciMeterAdapter() {
|
|
108
|
+
return new StandardTciMeterAdapter({
|
|
109
|
+
capabilities: cloneMeterCapabilities(UNKNOWN_TCI_METER_CAPABILITIES),
|
|
110
|
+
interval: { reportsApplied: false }
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function decodeRx(command, receivedAtMs, source) {
|
|
114
|
+
const receiver = integer(command.args[0]);
|
|
115
|
+
const hasChannel = source !== "rx_sensors";
|
|
116
|
+
const channel = hasChannel ? integer(command.args[1]) : 0;
|
|
117
|
+
const levelIndex = hasChannel ? 2 : 1;
|
|
118
|
+
const levelDbm = finite(command.args[levelIndex]);
|
|
119
|
+
if (receiver === void 0 || receiver < 0 || channel === void 0 || channel < 0 || levelDbm === void 0) {
|
|
120
|
+
return { issue: `Invalid ${command.originalName} meter frame: ${command.raw}` };
|
|
121
|
+
}
|
|
122
|
+
const frame = { receiver, channel, levelDbm, source, receivedAtMs };
|
|
123
|
+
if (source === "rx_channel_sensors_ex") {
|
|
124
|
+
const averageLevelDbm = finite(command.args[3]);
|
|
125
|
+
const peakBinDbm = finite(command.args[4]);
|
|
126
|
+
if (averageLevelDbm === void 0 || peakBinDbm === void 0) {
|
|
127
|
+
return { issue: `Invalid ${command.originalName} extended meter frame: ${command.raw}` };
|
|
128
|
+
}
|
|
129
|
+
frame.averageLevelDbm = averageLevelDbm;
|
|
130
|
+
frame.peakBinDbm = peakBinDbm;
|
|
131
|
+
if (command.args.length > 5) frame.extraArgs = command.args.slice(5);
|
|
132
|
+
} else if (command.args.length > levelIndex + 1) {
|
|
133
|
+
frame.extraArgs = command.args.slice(levelIndex + 1);
|
|
134
|
+
}
|
|
135
|
+
return { decoded: { kind: "rx", frame } };
|
|
136
|
+
}
|
|
137
|
+
function decodeTx(command, receivedAtMs, alcUnit) {
|
|
138
|
+
const trx = integer(command.args[0]);
|
|
139
|
+
if (trx === void 0 || trx < 0) {
|
|
140
|
+
return { issue: `Invalid ${command.originalName} transmitter index: ${command.raw}` };
|
|
141
|
+
}
|
|
142
|
+
const micLevelDbm = optionalFinite(command.args[1]);
|
|
143
|
+
const rmsPowerWatts = optionalFinite(command.args[2]);
|
|
144
|
+
const peakPowerWatts = optionalFinite(command.args[3]);
|
|
145
|
+
const swr = optionalFinite(command.args[4]);
|
|
146
|
+
const alcValue = alcUnit ? optionalFinite(command.args[5]) : void 0;
|
|
147
|
+
if (micLevelDbm.invalid || rmsPowerWatts.invalid || peakPowerWatts.invalid || swr.invalid || alcValue?.invalid) {
|
|
148
|
+
return { issue: `Invalid ${command.originalName} numeric meter frame: ${command.raw}` };
|
|
149
|
+
}
|
|
150
|
+
if (rmsPowerWatts.value !== void 0 && rmsPowerWatts.value < 0 || peakPowerWatts.value !== void 0 && peakPowerWatts.value < 0 || swr.value !== void 0 && swr.value < 1) {
|
|
151
|
+
return { issue: `Out-of-range ${command.originalName} meter frame: ${command.raw}` };
|
|
152
|
+
}
|
|
153
|
+
if (micLevelDbm.value === void 0 && rmsPowerWatts.value === void 0 && peakPowerWatts.value === void 0 && swr.value === void 0 && alcValue?.value === void 0) {
|
|
154
|
+
return { issue: `Empty ${command.originalName} meter frame: ${command.raw}` };
|
|
155
|
+
}
|
|
156
|
+
const frame = {
|
|
157
|
+
trx,
|
|
158
|
+
micLevelDbm: micLevelDbm.value,
|
|
159
|
+
rmsPowerWatts: rmsPowerWatts.value,
|
|
160
|
+
peakPowerWatts: peakPowerWatts.value,
|
|
161
|
+
swr: swr.value,
|
|
162
|
+
receivedAtMs
|
|
163
|
+
};
|
|
164
|
+
if (alcUnit && alcValue?.value !== void 0) frame.alc = { value: alcValue.value, unit: alcUnit };
|
|
165
|
+
const knownArgs = alcUnit ? 6 : 5;
|
|
166
|
+
if (command.args.length > knownArgs) frame.extraArgs = command.args.slice(knownArgs);
|
|
167
|
+
return { decoded: { kind: "tx", frame } };
|
|
168
|
+
}
|
|
169
|
+
function finite(value) {
|
|
170
|
+
if (value === void 0 || value === "") return void 0;
|
|
171
|
+
const parsed = Number(value);
|
|
172
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
173
|
+
}
|
|
174
|
+
function integer(value) {
|
|
175
|
+
const parsed = finite(value);
|
|
176
|
+
return parsed !== void 0 && Number.isInteger(parsed) ? parsed : void 0;
|
|
177
|
+
}
|
|
178
|
+
function optionalFinite(value) {
|
|
179
|
+
if (value === void 0 || value === "") return { invalid: false };
|
|
180
|
+
const parsed = Number(value);
|
|
181
|
+
return Number.isFinite(parsed) ? { value: parsed, invalid: false } : { invalid: true };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/meter/TciMeterStreamSession.ts
|
|
185
|
+
var import_eventemitter3 = require("eventemitter3");
|
|
186
|
+
|
|
187
|
+
// src/errors.ts
|
|
188
|
+
var TciError = class extends Error {
|
|
189
|
+
code;
|
|
190
|
+
details;
|
|
191
|
+
constructor(code, message, details) {
|
|
192
|
+
super(message);
|
|
193
|
+
this.name = "TciError";
|
|
194
|
+
this.code = code;
|
|
195
|
+
this.details = details;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
39
199
|
// src/dialect/builtins.ts
|
|
40
200
|
var StandardTciDialect = class {
|
|
41
201
|
id;
|
|
@@ -43,6 +203,9 @@ var StandardTciDialect = class {
|
|
|
43
203
|
streamLengthSemantics;
|
|
44
204
|
supportsStreamChannels;
|
|
45
205
|
supportsTxAudioSource;
|
|
206
|
+
supportsIqStream;
|
|
207
|
+
iqSampleRates;
|
|
208
|
+
meterAdapter;
|
|
46
209
|
driveHasTrx;
|
|
47
210
|
detector;
|
|
48
211
|
resolver;
|
|
@@ -52,6 +215,9 @@ var StandardTciDialect = class {
|
|
|
52
215
|
this.streamLengthSemantics = options.streamLengthSemantics;
|
|
53
216
|
this.supportsStreamChannels = options.supportsStreamChannels;
|
|
54
217
|
this.supportsTxAudioSource = options.supportsTxAudioSource;
|
|
218
|
+
this.supportsIqStream = options.supportsIqStream;
|
|
219
|
+
this.iqSampleRates = [...options.iqSampleRates];
|
|
220
|
+
this.meterAdapter = options.meterAdapter;
|
|
55
221
|
this.driveHasTrx = options.driveHasTrx;
|
|
56
222
|
this.detector = options.detect;
|
|
57
223
|
this.resolver = options.resolve;
|
|
@@ -99,7 +265,10 @@ var expertSdr14Dialect = new StandardTciDialect({
|
|
|
99
265
|
streamLengthSemantics: "per-channel",
|
|
100
266
|
supportsStreamChannels: false,
|
|
101
267
|
supportsTxAudioSource: false,
|
|
268
|
+
supportsIqStream: true,
|
|
269
|
+
iqSampleRates: [48e3, 96e3, 192e3, 384e3],
|
|
102
270
|
driveHasTrx: false,
|
|
271
|
+
meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),
|
|
103
272
|
detect: (context) => {
|
|
104
273
|
const parsed = version(context);
|
|
105
274
|
if (!parsed || compareTciVersion(parsed, [1, 4]) > 0) return { score: 0, evidence: [] };
|
|
@@ -112,7 +281,10 @@ var expertSdrLegacyDialect = new StandardTciDialect({
|
|
|
112
281
|
streamLengthSemantics: "per-channel",
|
|
113
282
|
supportsStreamChannels: false,
|
|
114
283
|
supportsTxAudioSource: false,
|
|
284
|
+
supportsIqStream: true,
|
|
285
|
+
iqSampleRates: [48e3, 96e3, 192e3, 384e3],
|
|
115
286
|
driveHasTrx: true,
|
|
287
|
+
meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),
|
|
116
288
|
detect: (context) => {
|
|
117
289
|
const parsed = version(context);
|
|
118
290
|
if (!parsed || compareTciVersion(parsed, [1, 5]) < 0 || compareTciVersion(parsed, [1, 9]) >= 0) return { score: 0, evidence: [] };
|
|
@@ -125,7 +297,10 @@ var expertSdrModernDialect = new StandardTciDialect({
|
|
|
125
297
|
streamLengthSemantics: "scalar",
|
|
126
298
|
supportsStreamChannels: true,
|
|
127
299
|
supportsTxAudioSource: true,
|
|
300
|
+
supportsIqStream: true,
|
|
301
|
+
iqSampleRates: [48e3, 96e3, 192e3, 384e3],
|
|
128
302
|
driveHasTrx: true,
|
|
303
|
+
meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),
|
|
129
304
|
detect: (context) => {
|
|
130
305
|
const parsed = version(context);
|
|
131
306
|
if (!parsed || compareTciVersion(parsed, [1, 9]) < 0) return { score: 0, evidence: [] };
|
|
@@ -143,7 +318,13 @@ var thetisDialect = new StandardTciDialect({
|
|
|
143
318
|
streamLengthSemantics: "scalar",
|
|
144
319
|
supportsStreamChannels: true,
|
|
145
320
|
supportsTxAudioSource: true,
|
|
321
|
+
supportsIqStream: true,
|
|
322
|
+
iqSampleRates: [48e3, 96e3, 192e3, 384e3],
|
|
146
323
|
driveHasTrx: true,
|
|
324
|
+
meterAdapter: new StandardTciMeterAdapter({
|
|
325
|
+
interval: { minMs: 30, maxMs: 1e3 },
|
|
326
|
+
supportsRxExtended: true
|
|
327
|
+
}),
|
|
147
328
|
detect: (context) => {
|
|
148
329
|
const evidence = [];
|
|
149
330
|
let score = 0;
|
|
@@ -169,7 +350,13 @@ var aetherSdrDialect = new StandardTciDialect({
|
|
|
169
350
|
streamLengthSemantics: "scalar",
|
|
170
351
|
supportsStreamChannels: true,
|
|
171
352
|
supportsTxAudioSource: true,
|
|
353
|
+
supportsIqStream: true,
|
|
354
|
+
iqSampleRates: [24e3, 48e3, 96e3, 192e3],
|
|
172
355
|
driveHasTrx: true,
|
|
356
|
+
meterAdapter: new StandardTciMeterAdapter({
|
|
357
|
+
interval: { fixedMs: 200 },
|
|
358
|
+
txAlcUnit: "dbfs"
|
|
359
|
+
}),
|
|
173
360
|
detect: (context) => {
|
|
174
361
|
if (!/^aethersdr$/i.test(context.identity.device ?? "")) return { score: 0, evidence: [] };
|
|
175
362
|
const evidence = [`AetherSDR device identity: ${context.identity.device}`];
|
|
@@ -188,7 +375,10 @@ var genericObservedDialect = new StandardTciDialect({
|
|
|
188
375
|
streamLengthSemantics: "auto",
|
|
189
376
|
supportsStreamChannels: true,
|
|
190
377
|
supportsTxAudioSource: true,
|
|
378
|
+
supportsIqStream: false,
|
|
379
|
+
iqSampleRates: [],
|
|
191
380
|
driveHasTrx: true,
|
|
381
|
+
meterAdapter: createUnknownTciMeterAdapter(),
|
|
192
382
|
detect: (context) => ({
|
|
193
383
|
score: context.commandNames.has("ready") ? 10 : 0,
|
|
194
384
|
evidence: ["No vendor-specific match; using observed command shapes"],
|
|
@@ -203,7 +393,10 @@ var genericObservedDialect = new StandardTciDialect({
|
|
|
203
393
|
streamLengthSemantics: "auto",
|
|
204
394
|
supportsStreamChannels: context.commandNames.has("audio_stream_channels"),
|
|
205
395
|
supportsTxAudioSource: compareTciVersion(version(context) ?? [0], [2, 0]) >= 0,
|
|
396
|
+
supportsIqStream: context.commandNames.has("iq_samplerate"),
|
|
397
|
+
iqSampleRates: context.commandNames.has("iq_samplerate") ? [48e3] : [],
|
|
206
398
|
driveHasTrx,
|
|
399
|
+
meterAdapter: createUnknownTciMeterAdapter(),
|
|
207
400
|
detect: genericObservedDialect.detect.bind(genericObservedDialect)
|
|
208
401
|
});
|
|
209
402
|
}
|
|
@@ -234,18 +427,6 @@ function formatVersion(value) {
|
|
|
234
427
|
return value.join(".");
|
|
235
428
|
}
|
|
236
429
|
|
|
237
|
-
// src/errors.ts
|
|
238
|
-
var TciError = class extends Error {
|
|
239
|
-
code;
|
|
240
|
-
details;
|
|
241
|
-
constructor(code, message, details) {
|
|
242
|
-
super(message);
|
|
243
|
-
this.name = "TciError";
|
|
244
|
-
this.code = code;
|
|
245
|
-
this.details = details;
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
|
|
249
430
|
// src/dialect/registry.ts
|
|
250
431
|
var TciDialectRegistry = class {
|
|
251
432
|
dialects = /* @__PURE__ */ new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/dialect/index.ts","../../src/dialect/builtins.ts","../../src/errors.ts","../../src/dialect/registry.ts","../../src/dialect/handshake.ts"],"sourcesContent":["export * from './types.js';\nexport * from './builtins.js';\nexport * from './registry.js';\nexport * from './handshake.js';\n","import type {\n TciDialect,\n TciDialectDetectionContext,\n TciDialectScore,\n TciDriveState,\n TciStreamLengthSemantics,\n} from './types.js';\n\ntype VersionTuple = readonly number[];\n\ninterface StandardDialectOptions {\n id: TciDialect['id'];\n label: string;\n streamLengthSemantics: TciStreamLengthSemantics;\n supportsStreamChannels: boolean;\n supportsTxAudioSource: boolean;\n driveHasTrx: boolean;\n detect: (context: TciDialectDetectionContext) => TciDialectScore;\n resolve?: (context: TciDialectDetectionContext) => TciDialect;\n}\n\nclass StandardTciDialect implements TciDialect {\n readonly id: TciDialect['id'];\n readonly label: string;\n readonly streamLengthSemantics: TciStreamLengthSemantics;\n readonly supportsStreamChannels: boolean;\n readonly supportsTxAudioSource: boolean;\n private readonly driveHasTrx: boolean;\n private readonly detector: StandardDialectOptions['detect'];\n private readonly resolver?: StandardDialectOptions['resolve'];\n\n constructor(options: StandardDialectOptions) {\n this.id = options.id;\n this.label = options.label;\n this.streamLengthSemantics = options.streamLengthSemantics;\n this.supportsStreamChannels = options.supportsStreamChannels;\n this.supportsTxAudioSource = options.supportsTxAudioSource;\n this.driveHasTrx = options.driveHasTrx;\n this.detector = options.detect;\n this.resolver = options.resolve;\n }\n\n detect(context: TciDialectDetectionContext): TciDialectScore {\n return this.detector(context);\n }\n\n resolve(context: TciDialectDetectionContext): TciDialect {\n return this.resolver?.(context) ?? this;\n }\n\n buildDriveSetArgs(trx: number, value: number): readonly unknown[] {\n return this.driveHasTrx ? [trx, value] : [value];\n }\n\n buildDriveReadArgs(trx: number): readonly unknown[] {\n return this.driveHasTrx ? [trx] : [];\n }\n\n parseDrive(args: readonly string[], defaultTrx: number): TciDriveState | undefined {\n return parseDriveState(args, defaultTrx, this.driveHasTrx);\n }\n\n buildTuneDriveSetArgs(trx: number, value: number): readonly unknown[] {\n return this.driveHasTrx ? [trx, value] : [value];\n }\n\n buildTuneDriveReadArgs(trx: number): readonly unknown[] {\n return this.driveHasTrx ? [trx] : [];\n }\n\n parseTuneDrive(args: readonly string[], defaultTrx: number): TciDriveState | undefined {\n return parseDriveState(args, defaultTrx, this.driveHasTrx);\n }\n}\n\nfunction parseDriveState(args: readonly string[], defaultTrx: number, hasTrx: boolean): TciDriveState | undefined {\n const trx = hasTrx ? Number(args[0]) : defaultTrx;\n const value = Number(args[hasTrx ? 1 : 0]);\n if (!Number.isInteger(trx) || !Number.isFinite(value)) return undefined;\n return { trx, value };\n}\n\nfunction version(context: TciDialectDetectionContext): VersionTuple | undefined {\n return parseTciVersion(context.identity.protocolVersion);\n}\n\nfunction programIncludes(context: TciDialectDetectionContext, value: string): boolean {\n return context.identity.programName?.toLowerCase().includes(value) ?? false;\n}\n\nexport const expertSdr14Dialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.4', label: 'ExpertSDR / TCI 1.4', streamLengthSemantics: 'per-channel',\n supportsStreamChannels: false, supportsTxAudioSource: false, driveHasTrx: false,\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 4]) > 0) return { score: 0, evidence: [] };\n return { score: 80 + (programIncludes(context, 'expert') ? 10 : 0), evidence: [`protocol ${formatVersion(parsed)} <= 1.4`] };\n },\n});\n\nexport const expertSdrLegacyDialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.5-1.8', label: 'ExpertSDR / TCI 1.5-1.8', streamLengthSemantics: 'per-channel',\n supportsStreamChannels: false, supportsTxAudioSource: false, driveHasTrx: true,\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 5]) < 0 || compareTciVersion(parsed, [1, 9]) >= 0) return { score: 0, evidence: [] };\n return { score: 80 + (programIncludes(context, 'expert') ? 10 : 0), evidence: [`protocol ${formatVersion(parsed)} is in 1.5-1.8`] };\n },\n});\n\nexport const expertSdrModernDialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.9-2.0', label: 'ExpertSDR / TCI 1.9-2.0', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, driveHasTrx: true,\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 9]) < 0) return { score: 0, evidence: [] };\n const future = compareTciVersion(parsed, [2, 0]) > 0;\n return {\n score: 70 + (programIncludes(context, 'expert') ? 15 : 0),\n evidence: [`protocol ${formatVersion(parsed)} uses modern stream negotiation`],\n warnings: future ? [`Unknown future TCI version ${formatVersion(parsed)}; using the modern dialect`] : [],\n };\n },\n});\n\nexport const thetisDialect: TciDialect = new StandardTciDialect({\n id: 'thetis-2.0', label: 'Thetis / TCI 2.0', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, driveHasTrx: true,\n detect: (context) => {\n const evidence: string[] = [];\n let score = 0;\n if (programIncludes(context, 'thetis')) { score += 120; evidence.push('PROTOCOL program is Thetis'); }\n const observed = ['tx_frequency_ex', 'tx_profiles_ex', 'tx_profile_ex', 'calibration_ex']\n .filter((name) => context.commandNames.has(name));\n if (observed.length > 0) { score += 100; evidence.push(`Thetis extension commands: ${observed.join(', ')}`); }\n if (/anan|hermes|orion|saturn/i.test(context.identity.device ?? '')) {\n score += 30;\n evidence.push(`Thetis-family device: ${context.identity.device}`);\n }\n return { score, evidence };\n },\n});\n\nexport const aetherSdrDialect: TciDialect = new StandardTciDialect({\n id: 'aethersdr-1.5', label: 'AetherSDR / TCI 1.5 hybrid', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, driveHasTrx: true,\n detect: (context) => {\n if (!/^aethersdr$/i.test(context.identity.device ?? '')) return { score: 0, evidence: [] };\n const evidence = [`AetherSDR device identity: ${context.identity.device}`];\n const modernAudioCommands = ['audio_stream_sample_type', 'audio_stream_channels', 'audio_stream_samples']\n .filter((name) => context.commandNames.has(name));\n if (modernAudioCommands.length > 0) evidence.push(`Modern audio negotiation: ${modernAudioCommands.join(', ')}`);\n return {\n score: 150,\n evidence,\n warnings: context.identity.protocolVersion === '1.5'\n ? ['AetherSDR reports TCI 1.5 but uses modern scalar audio stream semantics']\n : [],\n };\n },\n});\n\nexport const genericObservedDialect: TciDialect = new StandardTciDialect({\n id: 'generic-observed', label: 'Generic observed TCI', streamLengthSemantics: 'auto',\n supportsStreamChannels: true, supportsTxAudioSource: true, driveHasTrx: true,\n detect: (context) => ({\n score: context.commandNames.has('ready') ? 10 : 0,\n evidence: ['No vendor-specific match; using observed command shapes'],\n warnings: ['Dialect identity is uncertain'],\n }),\n resolve: (context) => {\n const drive = [...context.commands].reverse().find((command) => command.name === 'drive');\n const driveHasTrx = (drive?.args.length ?? 0) >= 2;\n return new StandardTciDialect({\n id: 'generic-observed',\n label: 'Generic observed TCI',\n streamLengthSemantics: 'auto',\n supportsStreamChannels: context.commandNames.has('audio_stream_channels'),\n supportsTxAudioSource: compareTciVersion(version(context) ?? [0], [2, 0]) >= 0,\n driveHasTrx,\n detect: genericObservedDialect.detect.bind(genericObservedDialect),\n });\n },\n});\n\nexport const builtInDialects: readonly TciDialect[] = [\n aetherSdrDialect, thetisDialect, expertSdr14Dialect, expertSdrLegacyDialect, expertSdrModernDialect, genericObservedDialect,\n];\n\nexport function parseTciVersion(value: string | undefined): VersionTuple | undefined {\n if (!value) return undefined;\n const match = value.trim().match(/^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?/);\n if (!match) return undefined;\n return match.slice(1).filter((part): part is string => part !== undefined).map(Number);\n}\n\nexport function compareTciVersion(left: VersionTuple, right: VersionTuple): number {\n const length = Math.max(left.length, right.length);\n for (let index = 0; index < length; index += 1) {\n const difference = (left[index] ?? 0) - (right[index] ?? 0);\n if (difference !== 0) return difference;\n }\n return 0;\n}\n\nfunction formatVersion(value: VersionTuple): string { return value.join('.'); }\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","import { TciError } from '../errors.js';\nimport { builtInDialects } from './builtins.js';\nimport type {\n TciDialect,\n TciDialectDetection,\n TciDialectDetectionContext,\n TciDialectId,\n TciDialectSelection,\n} from './types.js';\n\nexport class TciDialectRegistry {\n private readonly dialects = new Map<TciDialectId, TciDialect>();\n\n constructor(dialects: readonly TciDialect[] = builtInDialects) {\n for (const dialect of dialects) this.register(dialect);\n }\n\n register(dialect: TciDialect): void { this.dialects.set(dialect.id, dialect); }\n get(id: TciDialectId): TciDialect | undefined { return this.dialects.get(id); }\n list(): TciDialect[] { return [...this.dialects.values()]; }\n\n select(context: TciDialectDetectionContext, selection: TciDialectSelection = 'auto'): TciDialectDetection {\n if (typeof selection === 'object') {\n return { dialect: selection, confidence: 'manual', evidence: ['Custom dialect supplied by caller'], warnings: [] };\n }\n if (selection !== 'auto') {\n const dialect = this.get(selection);\n if (!dialect) throw new TciError('unknown-dialect', `Unknown TCI dialect: ${selection}`);\n return { dialect: dialect.resolve?.(context) ?? dialect, confidence: 'manual', evidence: [`Dialect ${selection} selected by caller`], warnings: [] };\n }\n\n const candidates = this.list()\n .map((dialect) => ({ dialect, result: dialect.detect(context) }))\n .sort((left, right) => right.result.score - left.result.score);\n const selected = candidates[0];\n if (!selected || selected.result.score <= 0) {\n throw new TciError('unknown-dialect', 'Unable to identify the TCI server dialect');\n }\n return {\n dialect: selected.dialect.resolve?.(context) ?? selected.dialect,\n confidence: selected.result.score >= 100 ? 'high' : selected.result.score >= 70 ? 'medium' : 'low',\n evidence: selected.result.evidence,\n warnings: selected.result.warnings ?? [],\n };\n }\n}\n\nexport const defaultTciDialectRegistry = new TciDialectRegistry();\n","import { TciError } from '../errors.js';\nimport type { TciCommand } from '../protocol/text.js';\nimport type { TciProtocolIdentity } from './types.js';\n\nconst IDENTITY_COMMANDS = new Set(['protocol', 'device', 'trx_count', 'channels_count', 'channel_count']);\nconst STATE_COMMANDS = new Set(['vfo', 'modulation', 'modulations_list', 'trx', 'drive']);\n\nexport function parseProtocolIdentity(commands: readonly TciCommand[]): TciProtocolIdentity {\n const protocol = [...commands].reverse().find((command) => command.name === 'protocol');\n const device = [...commands].reverse().find((command) => command.name === 'device');\n const rawProtocolArgs = protocol?.args ?? [];\n const firstLooksLikeVersion = /^\\d+(?:\\.\\d+){0,2}/.test(rawProtocolArgs[0] ?? '');\n return {\n programName: firstLooksLikeVersion ? undefined : rawProtocolArgs[0],\n protocolVersion: firstLooksLikeVersion ? rawProtocolArgs[0] : rawProtocolArgs[1],\n rawProtocolArgs: [...rawProtocolArgs],\n device: device?.args.join(','),\n };\n}\n\nexport function assertValidTciHandshake(commands: readonly TciCommand[]): void {\n const names = new Set(commands.map((command) => command.name));\n if (!names.has('ready')) throw new TciError('handshake-timeout', 'TCI READY was not received');\n const categories = [\n [...IDENTITY_COMMANDS].some((name) => names.has(name)),\n [...STATE_COMMANDS].some((name) => names.has(name)),\n ].filter(Boolean).length;\n const identitySignals = [...IDENTITY_COMMANDS].filter((name) => names.has(name)).length;\n if (categories < 2 && identitySignals < 2) {\n throw new TciError('invalid-handshake', 'WebSocket opened but did not provide enough TCI initialization evidence');\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/dialect/index.ts","../../src/meter/types.ts","../../src/meter/adapters.ts","../../src/meter/TciMeterStreamSession.ts","../../src/errors.ts","../../src/dialect/builtins.ts","../../src/dialect/registry.ts","../../src/dialect/handshake.ts"],"sourcesContent":["export * from './types.js';\nexport * from './builtins.js';\nexport * from './registry.js';\nexport * from './handshake.js';\n","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","import type {\n TciDialect,\n TciDialectDetectionContext,\n TciDialectScore,\n TciDriveState,\n TciStreamLengthSemantics,\n} from './types.js';\nimport { StandardTciMeterAdapter, createUnknownTciMeterAdapter } from '../meter/index.js';\n\ntype VersionTuple = readonly number[];\n\ninterface StandardDialectOptions {\n id: TciDialect['id'];\n label: string;\n streamLengthSemantics: TciStreamLengthSemantics;\n supportsStreamChannels: boolean;\n supportsTxAudioSource: boolean;\n supportsIqStream: boolean;\n iqSampleRates: readonly number[];\n driveHasTrx: boolean;\n meterAdapter?: TciDialect['meterAdapter'];\n detect: (context: TciDialectDetectionContext) => TciDialectScore;\n resolve?: (context: TciDialectDetectionContext) => TciDialect;\n}\n\nclass StandardTciDialect implements TciDialect {\n readonly id: TciDialect['id'];\n readonly label: string;\n readonly streamLengthSemantics: TciStreamLengthSemantics;\n readonly supportsStreamChannels: boolean;\n readonly supportsTxAudioSource: boolean;\n readonly supportsIqStream: boolean;\n readonly iqSampleRates: readonly number[];\n readonly meterAdapter?: TciDialect['meterAdapter'];\n private readonly driveHasTrx: boolean;\n private readonly detector: StandardDialectOptions['detect'];\n private readonly resolver?: StandardDialectOptions['resolve'];\n\n constructor(options: StandardDialectOptions) {\n this.id = options.id;\n this.label = options.label;\n this.streamLengthSemantics = options.streamLengthSemantics;\n this.supportsStreamChannels = options.supportsStreamChannels;\n this.supportsTxAudioSource = options.supportsTxAudioSource;\n this.supportsIqStream = options.supportsIqStream;\n this.iqSampleRates = [...options.iqSampleRates];\n this.meterAdapter = options.meterAdapter;\n this.driveHasTrx = options.driveHasTrx;\n this.detector = options.detect;\n this.resolver = options.resolve;\n }\n\n detect(context: TciDialectDetectionContext): TciDialectScore {\n return this.detector(context);\n }\n\n resolve(context: TciDialectDetectionContext): TciDialect {\n return this.resolver?.(context) ?? this;\n }\n\n buildDriveSetArgs(trx: number, value: number): readonly unknown[] {\n return this.driveHasTrx ? [trx, value] : [value];\n }\n\n buildDriveReadArgs(trx: number): readonly unknown[] {\n return this.driveHasTrx ? [trx] : [];\n }\n\n parseDrive(args: readonly string[], defaultTrx: number): TciDriveState | undefined {\n return parseDriveState(args, defaultTrx, this.driveHasTrx);\n }\n\n buildTuneDriveSetArgs(trx: number, value: number): readonly unknown[] {\n return this.driveHasTrx ? [trx, value] : [value];\n }\n\n buildTuneDriveReadArgs(trx: number): readonly unknown[] {\n return this.driveHasTrx ? [trx] : [];\n }\n\n parseTuneDrive(args: readonly string[], defaultTrx: number): TciDriveState | undefined {\n return parseDriveState(args, defaultTrx, this.driveHasTrx);\n }\n}\n\nfunction parseDriveState(args: readonly string[], defaultTrx: number, hasTrx: boolean): TciDriveState | undefined {\n const trx = hasTrx ? Number(args[0]) : defaultTrx;\n const value = Number(args[hasTrx ? 1 : 0]);\n if (!Number.isInteger(trx) || !Number.isFinite(value)) return undefined;\n return { trx, value };\n}\n\nfunction version(context: TciDialectDetectionContext): VersionTuple | undefined {\n return parseTciVersion(context.identity.protocolVersion);\n}\n\nfunction programIncludes(context: TciDialectDetectionContext, value: string): boolean {\n return context.identity.programName?.toLowerCase().includes(value) ?? false;\n}\n\nexport const expertSdr14Dialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.4', label: 'ExpertSDR / TCI 1.4', streamLengthSemantics: 'per-channel',\n supportsStreamChannels: false, supportsTxAudioSource: false, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: false,\n meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 4]) > 0) return { score: 0, evidence: [] };\n return { score: 80 + (programIncludes(context, 'expert') ? 10 : 0), evidence: [`protocol ${formatVersion(parsed)} <= 1.4`] };\n },\n});\n\nexport const expertSdrLegacyDialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.5-1.8', label: 'ExpertSDR / TCI 1.5-1.8', streamLengthSemantics: 'per-channel',\n supportsStreamChannels: false, supportsTxAudioSource: false, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: true,\n meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 5]) < 0 || compareTciVersion(parsed, [1, 9]) >= 0) return { score: 0, evidence: [] };\n return { score: 80 + (programIncludes(context, 'expert') ? 10 : 0), evidence: [`protocol ${formatVersion(parsed)} is in 1.5-1.8`] };\n },\n});\n\nexport const expertSdrModernDialect: TciDialect = new StandardTciDialect({\n id: 'expertsdr-1.9-2.0', label: 'ExpertSDR / TCI 1.9-2.0', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: true,\n meterAdapter: new StandardTciMeterAdapter({ interval: { reportsApplied: false } }),\n detect: (context) => {\n const parsed = version(context);\n if (!parsed || compareTciVersion(parsed, [1, 9]) < 0) return { score: 0, evidence: [] };\n const future = compareTciVersion(parsed, [2, 0]) > 0;\n return {\n score: 70 + (programIncludes(context, 'expert') ? 15 : 0),\n evidence: [`protocol ${formatVersion(parsed)} uses modern stream negotiation`],\n warnings: future ? [`Unknown future TCI version ${formatVersion(parsed)}; using the modern dialect`] : [],\n };\n },\n});\n\nexport const thetisDialect: TciDialect = new StandardTciDialect({\n id: 'thetis-2.0', label: 'Thetis / TCI 2.0', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: true,\n iqSampleRates: [48_000, 96_000, 192_000, 384_000], driveHasTrx: true,\n meterAdapter: new StandardTciMeterAdapter({\n interval: { minMs: 30, maxMs: 1_000 },\n supportsRxExtended: true,\n }),\n detect: (context) => {\n const evidence: string[] = [];\n let score = 0;\n if (programIncludes(context, 'thetis')) { score += 120; evidence.push('PROTOCOL program is Thetis'); }\n const observed = ['tx_frequency_ex', 'tx_profiles_ex', 'tx_profile_ex', 'calibration_ex']\n .filter((name) => context.commandNames.has(name));\n if (observed.length > 0) { score += 100; evidence.push(`Thetis extension commands: ${observed.join(', ')}`); }\n if (/anan|hermes|orion|saturn/i.test(context.identity.device ?? '')) {\n score += 30;\n evidence.push(`Thetis-family device: ${context.identity.device}`);\n }\n return { score, evidence };\n },\n});\n\nexport const aetherSdrDialect: TciDialect = new StandardTciDialect({\n id: 'aethersdr-1.5', label: 'AetherSDR / TCI 1.5 hybrid', streamLengthSemantics: 'scalar',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: true,\n iqSampleRates: [24_000, 48_000, 96_000, 192_000], driveHasTrx: true,\n meterAdapter: new StandardTciMeterAdapter({\n interval: { fixedMs: 200 },\n txAlcUnit: 'dbfs',\n }),\n detect: (context) => {\n if (!/^aethersdr$/i.test(context.identity.device ?? '')) return { score: 0, evidence: [] };\n const evidence = [`AetherSDR device identity: ${context.identity.device}`];\n const modernAudioCommands = ['audio_stream_sample_type', 'audio_stream_channels', 'audio_stream_samples']\n .filter((name) => context.commandNames.has(name));\n if (modernAudioCommands.length > 0) evidence.push(`Modern audio negotiation: ${modernAudioCommands.join(', ')}`);\n return {\n score: 150,\n evidence,\n warnings: context.identity.protocolVersion === '1.5'\n ? ['AetherSDR reports TCI 1.5 but uses modern scalar audio stream semantics']\n : [],\n };\n },\n});\n\nexport const genericObservedDialect: TciDialect = new StandardTciDialect({\n id: 'generic-observed', label: 'Generic observed TCI', streamLengthSemantics: 'auto',\n supportsStreamChannels: true, supportsTxAudioSource: true, supportsIqStream: false,\n iqSampleRates: [], driveHasTrx: true,\n meterAdapter: createUnknownTciMeterAdapter(),\n detect: (context) => ({\n score: context.commandNames.has('ready') ? 10 : 0,\n evidence: ['No vendor-specific match; using observed command shapes'],\n warnings: ['Dialect identity is uncertain'],\n }),\n resolve: (context) => {\n const drive = [...context.commands].reverse().find((command) => command.name === 'drive');\n const driveHasTrx = (drive?.args.length ?? 0) >= 2;\n return new StandardTciDialect({\n id: 'generic-observed',\n label: 'Generic observed TCI',\n streamLengthSemantics: 'auto',\n supportsStreamChannels: context.commandNames.has('audio_stream_channels'),\n supportsTxAudioSource: compareTciVersion(version(context) ?? [0], [2, 0]) >= 0,\n supportsIqStream: context.commandNames.has('iq_samplerate'),\n iqSampleRates: context.commandNames.has('iq_samplerate') ? [48_000] : [],\n driveHasTrx,\n meterAdapter: createUnknownTciMeterAdapter(),\n detect: genericObservedDialect.detect.bind(genericObservedDialect),\n });\n },\n});\n\nexport const builtInDialects: readonly TciDialect[] = [\n aetherSdrDialect, thetisDialect, expertSdr14Dialect, expertSdrLegacyDialect, expertSdrModernDialect, genericObservedDialect,\n];\n\nexport function parseTciVersion(value: string | undefined): VersionTuple | undefined {\n if (!value) return undefined;\n const match = value.trim().match(/^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?/);\n if (!match) return undefined;\n return match.slice(1).filter((part): part is string => part !== undefined).map(Number);\n}\n\nexport function compareTciVersion(left: VersionTuple, right: VersionTuple): number {\n const length = Math.max(left.length, right.length);\n for (let index = 0; index < length; index += 1) {\n const difference = (left[index] ?? 0) - (right[index] ?? 0);\n if (difference !== 0) return difference;\n }\n return 0;\n}\n\nfunction formatVersion(value: VersionTuple): string { return value.join('.'); }\n","import { TciError } from '../errors.js';\nimport { builtInDialects } from './builtins.js';\nimport type {\n TciDialect,\n TciDialectDetection,\n TciDialectDetectionContext,\n TciDialectId,\n TciDialectSelection,\n} from './types.js';\n\nexport class TciDialectRegistry {\n private readonly dialects = new Map<TciDialectId, TciDialect>();\n\n constructor(dialects: readonly TciDialect[] = builtInDialects) {\n for (const dialect of dialects) this.register(dialect);\n }\n\n register(dialect: TciDialect): void { this.dialects.set(dialect.id, dialect); }\n get(id: TciDialectId): TciDialect | undefined { return this.dialects.get(id); }\n list(): TciDialect[] { return [...this.dialects.values()]; }\n\n select(context: TciDialectDetectionContext, selection: TciDialectSelection = 'auto'): TciDialectDetection {\n if (typeof selection === 'object') {\n return { dialect: selection, confidence: 'manual', evidence: ['Custom dialect supplied by caller'], warnings: [] };\n }\n if (selection !== 'auto') {\n const dialect = this.get(selection);\n if (!dialect) throw new TciError('unknown-dialect', `Unknown TCI dialect: ${selection}`);\n return { dialect: dialect.resolve?.(context) ?? dialect, confidence: 'manual', evidence: [`Dialect ${selection} selected by caller`], warnings: [] };\n }\n\n const candidates = this.list()\n .map((dialect) => ({ dialect, result: dialect.detect(context) }))\n .sort((left, right) => right.result.score - left.result.score);\n const selected = candidates[0];\n if (!selected || selected.result.score <= 0) {\n throw new TciError('unknown-dialect', 'Unable to identify the TCI server dialect');\n }\n return {\n dialect: selected.dialect.resolve?.(context) ?? selected.dialect,\n confidence: selected.result.score >= 100 ? 'high' : selected.result.score >= 70 ? 'medium' : 'low',\n evidence: selected.result.evidence,\n warnings: selected.result.warnings ?? [],\n };\n }\n}\n\nexport const defaultTciDialectRegistry = new TciDialectRegistry();\n","import { TciError } from '../errors.js';\nimport type { TciCommand } from '../protocol/text.js';\nimport type { TciProtocolIdentity } from './types.js';\n\nconst IDENTITY_COMMANDS = new Set(['protocol', 'device', 'trx_count', 'channels_count', 'channel_count']);\nconst STATE_COMMANDS = new Set(['vfo', 'modulation', 'modulations_list', 'trx', 'drive']);\n\nexport function parseProtocolIdentity(commands: readonly TciCommand[]): TciProtocolIdentity {\n const protocol = [...commands].reverse().find((command) => command.name === 'protocol');\n const device = [...commands].reverse().find((command) => command.name === 'device');\n const rawProtocolArgs = protocol?.args ?? [];\n const firstLooksLikeVersion = /^\\d+(?:\\.\\d+){0,2}/.test(rawProtocolArgs[0] ?? '');\n return {\n programName: firstLooksLikeVersion ? undefined : rawProtocolArgs[0],\n protocolVersion: firstLooksLikeVersion ? rawProtocolArgs[0] : rawProtocolArgs[1],\n rawProtocolArgs: [...rawProtocolArgs],\n device: device?.args.join(','),\n };\n}\n\nexport function assertValidTciHandshake(commands: readonly TciCommand[]): void {\n const names = new Set(commands.map((command) => command.name));\n if (!names.has('ready')) throw new TciError('handshake-timeout', 'TCI READY was not received');\n const categories = [\n [...IDENTITY_COMMANDS].some((name) => names.has(name)),\n [...STATE_COMMANDS].some((name) => names.has(name)),\n ].filter(Boolean).length;\n const identitySignals = [...IDENTITY_COMMANDS].filter((name) => names.has(name)).length;\n if (categories < 2 && identitySignals < 2) {\n throw new TciError('invalid-handshake', 'WebSocket opened but did not provide enough TCI initialization evidence');\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/dialect/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { d as TciDialect,
|
|
2
|
-
export { B as BuiltInTciDialectId, g as TciDialectScore, h as TciDriveState,
|
|
1
|
+
import { d as TciDialect, a as TciDialectId, f as TciDialectDetectionContext, c as TciDialectSelection, e as TciDialectDetection, i as TciProtocolIdentity } from '../types-CYK_NOTz.cjs';
|
|
2
|
+
export { B as BuiltInTciDialectId, g as TciDialectScore, h as TciDriveState, b as TciHandshakeResult, j as TciStreamLengthSemantics, T as TciWriteResult } from '../types-CYK_NOTz.cjs';
|
|
3
3
|
import { T as TciCommand } from '../text-BwCWY1k1.cjs';
|
|
4
|
+
import '../types-C4MtLKoy.cjs';
|
|
4
5
|
|
|
5
6
|
type VersionTuple = readonly number[];
|
|
6
7
|
declare const expertSdr14Dialect: TciDialect;
|
package/dist/dialect/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { d as TciDialect,
|
|
2
|
-
export { B as BuiltInTciDialectId, g as TciDialectScore, h as TciDriveState,
|
|
1
|
+
import { d as TciDialect, a as TciDialectId, f as TciDialectDetectionContext, c as TciDialectSelection, e as TciDialectDetection, i as TciProtocolIdentity } from '../types-DVIPU-VR.js';
|
|
2
|
+
export { B as BuiltInTciDialectId, g as TciDialectScore, h as TciDriveState, b as TciHandshakeResult, j as TciStreamLengthSemantics, T as TciWriteResult } from '../types-DVIPU-VR.js';
|
|
3
3
|
import { T as TciCommand } from '../text-BwCWY1k1.js';
|
|
4
|
+
import '../types-C0qVJVSO.js';
|
|
4
5
|
|
|
5
6
|
type VersionTuple = readonly number[];
|
|
6
7
|
declare const expertSdr14Dialect: TciDialect;
|