Skip to content

Commit d2a4cc5

Browse files
committed
fix: derive spectrum display state from official spectrum frames
1 parent 66c8c48 commit d2a4cc5

4 files changed

Lines changed: 74 additions & 19 deletions

File tree

index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ declare class HamLib extends EventEmitter {
622622
* @param levelType Level type ('AF', 'RF', 'SQL', 'RFPOWER', etc.)
623623
* @param value Level value (0.0-1.0 typically)
624624
*/
625-
setLevel(levelType: LevelType, value: number): Promise<number>;
625+
setLevel(levelType: LevelType, value: number, vfo?: string): Promise<number>;
626626

627627
/**
628628
* Get radio level

lib/index.js

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,29 @@ class HamLib extends EventEmitter {
3333
super();
3434
this._nativeInstance = new nativeModule.HamLib(model, port);
3535
this._managedSpectrumRunning = false;
36+
this._lastSpectrumLine = null;
37+
}
38+
39+
_recordSpectrumLine(line) {
40+
if (!line || typeof line !== 'object') {
41+
return line;
42+
}
43+
this._lastSpectrumLine = line;
44+
return line;
45+
}
46+
47+
_getLastSpectrumDisplayStateFromLine() {
48+
const line = this._lastSpectrumLine;
49+
if (!line || typeof line !== 'object') {
50+
return null;
51+
}
52+
53+
return {
54+
modeId: Number.isFinite(line.mode) ? line.mode : null,
55+
spanHz: Number.isFinite(line.spanHz) ? line.spanHz : null,
56+
edgeLowHz: Number.isFinite(line.lowEdgeFreq) ? line.lowEdgeFreq : null,
57+
edgeHighHz: Number.isFinite(line.highEdgeFreq) ? line.highEdgeFreq : null,
58+
};
3659
}
3760

3861
/**
@@ -322,7 +345,10 @@ class HamLib extends EventEmitter {
322345
* @param {string} levelType - Level type ('AF', 'RF', 'SQL', 'RFPOWER', etc.)
323346
* @param {number} value - Level value (0.0-1.0 typically)
324347
*/
325-
async setLevel(levelType, value) {
348+
async setLevel(levelType, value, vfo) {
349+
if (vfo) {
350+
return this._nativeInstance.setLevel(levelType, value, vfo);
351+
}
326352
return this._nativeInstance.setLevel(levelType, value);
327353
}
328354

@@ -1254,8 +1280,12 @@ class HamLib extends EventEmitter {
12541280

12551281
await applyLevel('SPECTRUM_MODE', await resolveModeId(config.mode));
12561282
await applyLevel('SPECTRUM_SPAN', config.spanHz);
1257-
await applyLevel('SPECTRUM_EDGE_LOW', config.edgeLowHz);
1258-
await applyLevel('SPECTRUM_EDGE_HIGH', config.edgeHighHz);
1283+
if (config.edgeLowHz !== undefined || config.edgeHighHz !== undefined) {
1284+
await this.setSpectrumFixedEdges({
1285+
lowHz: config.edgeLowHz,
1286+
highHz: config.edgeHighHz,
1287+
});
1288+
}
12591289
await applyLevel('SPECTRUM_SPEED', config.speed);
12601290
await applyLevel('SPECTRUM_REF', config.referenceLevel);
12611291
await applyLevel('SPECTRUM_AVG', config.averageMode);
@@ -1287,9 +1317,15 @@ class HamLib extends EventEmitter {
12871317
}
12881318

12891319
async getSpectrumFixedEdges() {
1320+
const lineState = this._getLastSpectrumDisplayStateFromLine();
1321+
if (lineState?.edgeLowHz !== null && lineState?.edgeHighHz !== null) {
1322+
return { lowHz: lineState.edgeLowHz, highHz: lineState.edgeHighHz };
1323+
}
1324+
1325+
const targetVfo = 'VFO-A';
12901326
const [lowHz, highHz] = await Promise.all([
1291-
this.getLevel('SPECTRUM_EDGE_LOW'),
1292-
this.getLevel('SPECTRUM_EDGE_HIGH'),
1327+
this.getLevel('SPECTRUM_EDGE_LOW', targetVfo),
1328+
this.getLevel('SPECTRUM_EDGE_HIGH', targetVfo),
12931329
]);
12941330
return { lowHz, highHz };
12951331
}
@@ -1298,23 +1334,28 @@ class HamLib extends EventEmitter {
12981334
if (!Number.isFinite(lowHz) || !Number.isFinite(highHz) || lowHz >= highHz) {
12991335
throw new Error('Spectrum fixed edge range must satisfy lowHz < highHz');
13001336
}
1301-
await this.setLevel('SPECTRUM_EDGE_LOW', lowHz);
1302-
await this.setLevel('SPECTRUM_EDGE_HIGH', highHz);
1337+
const targetVfo = 'VFO-A';
1338+
await this.setLevel('SPECTRUM_EDGE_LOW', lowHz, targetVfo);
1339+
await this.setLevel('SPECTRUM_EDGE_HIGH', highHz, targetVfo);
13031340
return { lowHz, highHz };
13041341
}
13051342

13061343
async getSpectrumDisplayState() {
13071344
const summary = await this.getSpectrumSupportSummary();
1308-
const [modeId, spanHz, fixedEdges, edgeSlot] = await Promise.all([
1345+
const lineState = this._getLastSpectrumDisplayStateFromLine();
1346+
const [queriedModeId, queriedSpanHz, fixedEdges, edgeSlot] = await Promise.all([
13091347
summary.configurableLevels.includes('SPECTRUM_MODE') ? this.getLevel('SPECTRUM_MODE') : Promise.resolve(null),
13101348
summary.configurableLevels.includes('SPECTRUM_SPAN') ? this.getLevel('SPECTRUM_SPAN') : Promise.resolve(null),
1311-
summary.supportsFixedEdges ? this.getSpectrumFixedEdges() : Promise.resolve(null),
1349+
summary.supportsFixedEdges ? this.getSpectrumFixedEdges().catch(() => null) : Promise.resolve(null),
13121350
summary.supportsEdgeSlotSelection ? this.getSpectrumEdgeSlot().catch(() => null) : Promise.resolve(null),
13131351
]);
1352+
1353+
const modeId = queriedModeId ?? lineState?.modeId ?? null;
1354+
const spanHz = queriedSpanHz ?? lineState?.spanHz ?? null;
13141355
const modeInfo = (summary.modes ?? []).find((entry) => entry.id === modeId) ?? null;
13151356
const mode = normalizeSpectrumModeName(modeInfo?.name);
1316-
const edgeLowHz = fixedEdges?.lowHz ?? null;
1317-
const edgeHighHz = fixedEdges?.highHz ?? null;
1357+
const edgeLowHz = fixedEdges?.lowHz ?? lineState?.edgeLowHz ?? null;
1358+
const edgeHighHz = fixedEdges?.highHz ?? lineState?.edgeHighHz ?? null;
13181359
const derivedSpanHz = (edgeLowHz !== null && edgeHighHz !== null) ? (edgeHighHz - edgeLowHz) : null;
13191360

13201361
return {
@@ -1352,9 +1393,14 @@ class HamLib extends EventEmitter {
13521393
* @returns {Promise<boolean>}
13531394
*/
13541395
async startSpectrumStream(callback) {
1355-
const listener = typeof callback === 'function'
1356-
? callback
1357-
: (line) => this.emit('spectrumLine', line);
1396+
const listener = (line) => {
1397+
const recorded = this._recordSpectrumLine(line);
1398+
if (typeof callback === 'function') {
1399+
callback(recorded);
1400+
return;
1401+
}
1402+
this.emit('spectrumLine', recorded);
1403+
};
13581404
return this._nativeInstance.startSpectrumStream(listener);
13591405
}
13601406

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hamlib",
3-
"version": "0.3.2",
3+
"version": "0.3.3",
44
"description": "Node.js bindings for Hamlib rig control with official spectrum streaming support",
55
"main": "index.js",
66
"module": "lib/index.mjs",

src/hamlib.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2938,12 +2938,21 @@ Napi::Value NodeHamLib::SetLevel(const Napi::CallbackInfo & info) {
29382938
}
29392939

29402940
if (info.Length() < 2 || !info[0].IsString() || !info[1].IsNumber()) {
2941-
Napi::TypeError::New(env, "Expected (levelType: string, value: number)").ThrowAsJavaScriptException();
2941+
Napi::TypeError::New(env, "Expected (levelType: string, value: number, vfo?: string)").ThrowAsJavaScriptException();
29422942
return env.Null();
29432943
}
29442944

29452945
std::string levelTypeStr = info[0].As<Napi::String>().Utf8Value();
29462946
double levelValue = info[1].As<Napi::Number>().DoubleValue();
2947+
int vfo = SHIM_RIG_VFO_CURR;
2948+
if (info.Length() >= 3 && info[2].IsString()) {
2949+
std::string vfoStr = info[2].As<Napi::String>().Utf8Value();
2950+
if (vfoStr == "VFO-A") {
2951+
vfo = SHIM_RIG_VFO_A;
2952+
} else if (vfoStr == "VFO-B") {
2953+
vfo = SHIM_RIG_VFO_B;
2954+
}
2955+
}
29472956

29482957
// Map level type strings to hamlib constants
29492958
uint64_t levelType;
@@ -3021,8 +3030,8 @@ Napi::Value NodeHamLib::SetLevel(const Napi::CallbackInfo & info) {
30213030
if (isSpectrumLevel) {
30223031
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
30233032
int result = shim_rig_level_is_float(levelType)
3024-
? shim_rig_set_level_f(my_rig, SHIM_RIG_VFO_CURR, levelType, static_cast<float>(levelValue))
3025-
: shim_rig_set_level_i(my_rig, SHIM_RIG_VFO_CURR, levelType, static_cast<int>(levelValue));
3033+
? shim_rig_set_level_f(my_rig, vfo, levelType, static_cast<float>(levelValue))
3034+
: shim_rig_set_level_i(my_rig, vfo, levelType, static_cast<int>(levelValue));
30263035

30273036
if (result != SHIM_RIG_OK) {
30283037
deferred.Reject(Napi::Error::New(env, shim_rigerror(result)).Value());

0 commit comments

Comments
 (0)