Skip to content

Commit e48a87d

Browse files
boybookclaude
andcommitted
fix: add fallback for getLevel on rigs with VFO switching issues
rig_get_level() fails on ICOM serial rigs (e.g. IC-705) because Hamlib's VFO resolution triggers icom_set_vfo which returns "unsupported VFO". The shim layer now tries the standard rig_get_level() first, and if it fails with RIG_EINVAL (-1), falls back to calling the backend's get_level() directly, bypassing VFO switching. Also adds optional VFO parameter to getLevel() JS API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cf7026a commit e48a87d

4 files changed

Lines changed: 40 additions & 12 deletions

File tree

index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,9 +542,10 @@ declare class HamLib {
542542
/**
543543
* Get radio level
544544
* @param levelType Level type ('AF', 'RF', 'SQL', 'STRENGTH', etc.)
545+
* @param vfo Optional VFO ('VFO-A', 'VFO-B', 'currVFO'). Defaults to currVFO.
545546
* @returns Level value
546547
*/
547-
getLevel(levelType: LevelType): Promise<number>;
548+
getLevel(levelType: LevelType, vfo?: string): Promise<number>;
548549

549550
/**
550551
* Get list of supported level types

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.2.3",
3+
"version": "0.2.4",
44
"description": "Node.js wrapper for hamlib radio control library",
55
"main": "index.js",
66
"module": "lib/index.mjs",

src/hamlib.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ class SetLevelAsyncWorker : public HamLibAsyncWorker {
280280
void Execute() override {
281281
CHECK_RIG_VALID();
282282

283-
result_code_ = shim_rig_set_level_f(hamlib_instance_->my_rig, SHIM_RIG_VFO_NONE, level_type_, value_);
283+
result_code_ = shim_rig_set_level_f(hamlib_instance_->my_rig, SHIM_RIG_VFO_CURR, level_type_, value_);
284284
if (result_code_ != SHIM_RIG_OK) {
285285
error_message_ = shim_rigerror(result_code_);
286286
}
@@ -307,15 +307,13 @@ class SetLevelAsyncWorker : public HamLibAsyncWorker {
307307

308308
class GetLevelAsyncWorker : public HamLibAsyncWorker {
309309
public:
310-
GetLevelAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance, uint64_t level_type)
311-
: HamLibAsyncWorker(env, hamlib_instance), level_type_(level_type), value_(0.0f) {}
310+
GetLevelAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance, uint64_t level_type, int vfo = SHIM_RIG_VFO_CURR)
311+
: HamLibAsyncWorker(env, hamlib_instance), level_type_(level_type), vfo_(vfo), value_(0.0f) {}
312312

313313
void Execute() override {
314314
CHECK_RIG_VALID();
315315

316-
// Use RIG_VFO_NONE to avoid unnecessary VFO switching that fails on
317-
// ICOM rigs (e.g. IC-705) where icom_set_vfo returns "unsupported VFO"
318-
result_code_ = shim_rig_get_level_f(hamlib_instance_->my_rig, SHIM_RIG_VFO_NONE, level_type_, &value_);
316+
result_code_ = shim_rig_get_level_f(hamlib_instance_->my_rig, vfo_, level_type_, &value_);
319317
if (result_code_ != SHIM_RIG_OK) {
320318
error_message_ = shim_rigerror(result_code_);
321319
}
@@ -337,6 +335,7 @@ class GetLevelAsyncWorker : public HamLibAsyncWorker {
337335

338336
private:
339337
uint64_t level_type_;
338+
int vfo_;
340339
float value_;
341340
};
342341

@@ -2974,9 +2973,20 @@ Napi::Value NodeHamLib::GetLevel(const Napi::CallbackInfo & info) {
29742973
return env.Null();
29752974
}
29762975

2977-
GetLevelAsyncWorker* worker = new GetLevelAsyncWorker(env, this, levelType);
2976+
// Optional second parameter: VFO string ('VFO-A', 'VFO-B', 'currVFO')
2977+
int vfo = SHIM_RIG_VFO_CURR;
2978+
if (info.Length() >= 2 && info[1].IsString()) {
2979+
std::string vfoStr = info[1].As<Napi::String>().Utf8Value();
2980+
if (vfoStr == "VFO-A") {
2981+
vfo = SHIM_RIG_VFO_A;
2982+
} else if (vfoStr == "VFO-B") {
2983+
vfo = SHIM_RIG_VFO_B;
2984+
}
2985+
}
2986+
2987+
GetLevelAsyncWorker* worker = new GetLevelAsyncWorker(env, this, levelType, vfo);
29782988
worker->Queue();
2979-
2989+
29802990
return worker->GetPromise();
29812991
}
29822992

src/shim/hamlib_shim.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,18 +345,35 @@ SHIM_API int shim_rig_set_level_i(hamlib_shim_handle_t h, int vfo, uint64_t leve
345345
return rig_set_level((RIG*)h, (vfo_t)vfo, (setting_t)level, val);
346346
}
347347

348+
/*
349+
* get_level with automatic fallback:
350+
* 1. Try standard rig_get_level() (with VFO handling, locking, caching)
351+
* 2. If it fails with RIG_EINVAL (-1), fall back to direct backend call
352+
* to bypass VFO switching issues (e.g. ICOM serial rigs where
353+
* icom_set_vfo fails with "unsupported VFO")
354+
*/
355+
static int shim_get_level_with_fallback(hamlib_shim_handle_t h, int vfo, uint64_t level, value_t* val) {
356+
RIG* rig = (RIG*)h;
357+
int ret = rig_get_level(rig, (vfo_t)vfo, (setting_t)level, val);
358+
if (ret == -1 && rig->caps->get_level) {
359+
/* RIG_EINVAL: VFO switching failed, try direct backend call */
360+
ret = rig->caps->get_level(rig, (vfo_t)vfo, (setting_t)level, val);
361+
}
362+
return ret;
363+
}
364+
348365
SHIM_API int shim_rig_get_level_f(hamlib_shim_handle_t h, int vfo, uint64_t level, float* value) {
349366
value_t val;
350367
val.f = 0.0f;
351-
int ret = rig_get_level((RIG*)h, (vfo_t)vfo, (setting_t)level, &val);
368+
int ret = shim_get_level_with_fallback(h, vfo, level, &val);
352369
if (value) *value = val.f;
353370
return ret;
354371
}
355372

356373
SHIM_API int shim_rig_get_level_i(hamlib_shim_handle_t h, int vfo, uint64_t level, int* value) {
357374
value_t val;
358375
val.i = 0;
359-
int ret = rig_get_level((RIG*)h, (vfo_t)vfo, (setting_t)level, &val);
376+
int ret = shim_get_level_with_fallback(h, vfo, level, &val);
360377
if (value) *value = val.i;
361378
return ret;
362379
}

0 commit comments

Comments
 (0)