@@ -4,6 +4,17 @@ const nodeGypBuild = require('node-gyp-build');
44// Ensure loader resolves from package root (contains prebuilds/ and build/)
55const nativeModule = nodeGypBuild ( path . join ( __dirname , '..' ) ) ;
66
7+ const DEFAULT_SPECTRUM_EDGE_SLOTS = [ 1 , 2 , 3 , 4 ] ;
8+
9+ function normalizeSpectrumModeName ( name ) {
10+ const normalized = String ( name || '' ) . trim ( ) . toLowerCase ( ) ;
11+ if ( normalized === 'center' ) return 'center' ;
12+ if ( normalized === 'fixed' ) return 'fixed' ;
13+ if ( normalized === 'center scroll' || normalized === 'center-scroll' || normalized === 'scroll-center' ) return 'scroll-center' ;
14+ if ( normalized === 'fixed scroll' || normalized === 'fixed-scroll' || normalized === 'scroll-fixed' ) return 'scroll-fixed' ;
15+ return null ;
16+ }
17+
718/**
819 * HamLib class for controlling amateur radio devices
920 *
@@ -1180,8 +1191,16 @@ class HamLib extends EventEmitter {
11801191 const hasSpectrumHoldFunction = supportedFunctions . includes ( 'SPECTRUM_HOLD' ) ;
11811192 const hasTransceiveFunction = supportedFunctions . includes ( 'TRANSCEIVE' ) ;
11821193 const asyncDataSupported = capabilities . asyncDataSupported ?? hasSpectrumFunction ;
1183- const configurableLevels = [ 'SPECTRUM_MODE' , 'SPECTRUM_SPAN' , 'SPECTRUM_SPEED' , 'SPECTRUM_REF' , 'SPECTRUM_AVG' ]
1194+ const configurableLevels = [ 'SPECTRUM_MODE' , 'SPECTRUM_SPAN' , 'SPECTRUM_EDGE_LOW' , 'SPECTRUM_EDGE_HIGH' , ' SPECTRUM_SPEED', 'SPECTRUM_REF' , 'SPECTRUM_AVG' ]
11841195 . filter ( ( name ) => supportedLevels . includes ( name ) ) ;
1196+ let supportsEdgeSlotSelection = false ;
1197+
1198+ try {
1199+ const edgeSlot = await this . getConf ( 'SPECTRUM_EDGE' ) ;
1200+ supportsEdgeSlotSelection = Number . isFinite ( Number . parseInt ( String ( edgeSlot ) , 10 ) ) ;
1201+ } catch ( _ ) {
1202+ supportsEdgeSlotSelection = false ;
1203+ }
11851204
11861205 return {
11871206 supported : Boolean ( hasSpectrumFunction ) ,
@@ -1190,6 +1209,9 @@ class HamLib extends EventEmitter {
11901209 hasSpectrumHoldFunction,
11911210 hasTransceiveFunction,
11921211 configurableLevels,
1212+ supportsFixedEdges : configurableLevels . includes ( 'SPECTRUM_EDGE_LOW' ) && configurableLevels . includes ( 'SPECTRUM_EDGE_HIGH' ) ,
1213+ supportsEdgeSlotSelection,
1214+ supportedEdgeSlots : supportsEdgeSlotSelection ? [ ...DEFAULT_SPECTRUM_EDGE_SLOTS ] : [ ] ,
11931215 scopes : capabilities . scopes ?? [ ] ,
11941216 modes : capabilities . modes ?? [ ] ,
11951217 spans : capabilities . spans ?? [ ] ,
@@ -1208,20 +1230,122 @@ class HamLib extends EventEmitter {
12081230 if ( value === undefined || ! summary . configurableLevels . includes ( name ) ) return ;
12091231 await this . setLevel ( name , value ) ;
12101232 } ;
1233+ const resolveModeId = async ( mode ) => {
1234+ if ( mode === undefined || mode === null ) return undefined ;
1235+ if ( Number . isFinite ( mode ) ) return mode ;
1236+ const requested = normalizeSpectrumModeName ( mode ) ;
1237+ if ( ! requested ) {
1238+ throw new Error ( `Unsupported spectrum mode: ${ mode } ` ) ;
1239+ }
1240+ const matched = ( summary . modes ?? [ ] ) . find ( ( entry ) => normalizeSpectrumModeName ( entry ?. name ) === requested ) ;
1241+ if ( ! matched ) {
1242+ throw new Error ( `Spectrum mode not supported by this backend: ${ mode } ` ) ;
1243+ }
1244+ return matched . id ;
1245+ } ;
12111246
12121247 if ( summary . hasSpectrumHoldFunction && config . hold !== undefined ) {
12131248 await this . setFunction ( 'SPECTRUM_HOLD' , Boolean ( config . hold ) ) ;
12141249 }
12151250
1216- await applyLevel ( 'SPECTRUM_MODE' , config . mode ) ;
1251+ if ( config . edgeSlot !== undefined && summary . supportsEdgeSlotSelection ) {
1252+ await this . setSpectrumEdgeSlot ( config . edgeSlot ) ;
1253+ }
1254+
1255+ await applyLevel ( 'SPECTRUM_MODE' , await resolveModeId ( config . mode ) ) ;
12171256 await applyLevel ( 'SPECTRUM_SPAN' , config . spanHz ) ;
1257+ await applyLevel ( 'SPECTRUM_EDGE_LOW' , config . edgeLowHz ) ;
1258+ await applyLevel ( 'SPECTRUM_EDGE_HIGH' , config . edgeHighHz ) ;
12181259 await applyLevel ( 'SPECTRUM_SPEED' , config . speed ) ;
12191260 await applyLevel ( 'SPECTRUM_REF' , config . referenceLevel ) ;
12201261 await applyLevel ( 'SPECTRUM_AVG' , config . averageMode ) ;
12211262
12221263 return summary ;
12231264 }
12241265
1266+ async getSpectrumEdgeSlot ( ) {
1267+ const raw = await this . getConf ( 'SPECTRUM_EDGE' ) ;
1268+ const parsed = Number . parseInt ( String ( raw ) , 10 ) ;
1269+ if ( ! Number . isFinite ( parsed ) ) {
1270+ throw new Error ( 'Spectrum edge slot is not available' ) ;
1271+ }
1272+ return parsed ;
1273+ }
1274+
1275+ async setSpectrumEdgeSlot ( slot ) {
1276+ const parsed = Number . parseInt ( String ( slot ) , 10 ) ;
1277+ if ( ! Number . isFinite ( parsed ) || parsed < 1 ) {
1278+ throw new Error ( `Invalid spectrum edge slot: ${ slot } ` ) ;
1279+ }
1280+ await this . setConf ( 'SPECTRUM_EDGE' , String ( parsed ) ) ;
1281+ return parsed ;
1282+ }
1283+
1284+ async getSpectrumSupportedEdgeSlots ( ) {
1285+ const summary = await this . getSpectrumSupportSummary ( ) ;
1286+ return summary . supportedEdgeSlots ?? [ ] ;
1287+ }
1288+
1289+ async getSpectrumFixedEdges ( ) {
1290+ const [ lowHz , highHz ] = await Promise . all ( [
1291+ this . getLevel ( 'SPECTRUM_EDGE_LOW' ) ,
1292+ this . getLevel ( 'SPECTRUM_EDGE_HIGH' ) ,
1293+ ] ) ;
1294+ return { lowHz, highHz } ;
1295+ }
1296+
1297+ async setSpectrumFixedEdges ( { lowHz, highHz } ) {
1298+ if ( ! Number . isFinite ( lowHz ) || ! Number . isFinite ( highHz ) || lowHz >= highHz ) {
1299+ throw new Error ( 'Spectrum fixed edge range must satisfy lowHz < highHz' ) ;
1300+ }
1301+ await this . setLevel ( 'SPECTRUM_EDGE_LOW' , lowHz ) ;
1302+ await this . setLevel ( 'SPECTRUM_EDGE_HIGH' , highHz ) ;
1303+ return { lowHz, highHz } ;
1304+ }
1305+
1306+ async getSpectrumDisplayState ( ) {
1307+ const summary = await this . getSpectrumSupportSummary ( ) ;
1308+ const [ modeId , spanHz , fixedEdges , edgeSlot ] = await Promise . all ( [
1309+ summary . configurableLevels . includes ( 'SPECTRUM_MODE' ) ? this . getLevel ( 'SPECTRUM_MODE' ) : Promise . resolve ( null ) ,
1310+ summary . configurableLevels . includes ( 'SPECTRUM_SPAN' ) ? this . getLevel ( 'SPECTRUM_SPAN' ) : Promise . resolve ( null ) ,
1311+ summary . supportsFixedEdges ? this . getSpectrumFixedEdges ( ) : Promise . resolve ( null ) ,
1312+ summary . supportsEdgeSlotSelection ? this . getSpectrumEdgeSlot ( ) . catch ( ( ) => null ) : Promise . resolve ( null ) ,
1313+ ] ) ;
1314+ const modeInfo = ( summary . modes ?? [ ] ) . find ( ( entry ) => entry . id === modeId ) ?? null ;
1315+ const mode = normalizeSpectrumModeName ( modeInfo ?. name ) ;
1316+ const edgeLowHz = fixedEdges ?. lowHz ?? null ;
1317+ const edgeHighHz = fixedEdges ?. highHz ?? null ;
1318+ const derivedSpanHz = ( edgeLowHz !== null && edgeHighHz !== null ) ? ( edgeHighHz - edgeLowHz ) : null ;
1319+
1320+ return {
1321+ mode,
1322+ modeId,
1323+ modeName : modeInfo ?. name ?? null ,
1324+ spanHz : spanHz ?? derivedSpanHz ,
1325+ edgeSlot,
1326+ edgeLowHz,
1327+ edgeHighHz,
1328+ supportedModes : summary . modes ?? [ ] ,
1329+ supportedSpans : summary . spans ?? [ ] ,
1330+ supportedEdgeSlots : summary . supportedEdgeSlots ?? [ ] ,
1331+ supportsFixedEdges : Boolean ( summary . supportsFixedEdges ) ,
1332+ supportsEdgeSlotSelection : Boolean ( summary . supportsEdgeSlotSelection ) ,
1333+ } ;
1334+ }
1335+
1336+ async configureSpectrumDisplay ( config = { } ) {
1337+ const normalizedConfig = { ...config } ;
1338+ if ( ( normalizedConfig . mode === 'fixed' || normalizedConfig . mode === 'scroll-fixed' )
1339+ && normalizedConfig . edgeLowHz !== undefined
1340+ && normalizedConfig . edgeHighHz !== undefined
1341+ && normalizedConfig . edgeLowHz >= normalizedConfig . edgeHighHz ) {
1342+ throw new Error ( 'Spectrum fixed edge range must satisfy edgeLowHz < edgeHighHz' ) ;
1343+ }
1344+
1345+ await this . configureSpectrum ( normalizedConfig ) ;
1346+ return this . getSpectrumDisplayState ( ) ;
1347+ }
1348+
12251349 /**
12261350 * Start the official Hamlib spectrum callback stream.
12271351 * @param {(line: Object) => void } [callback]
0 commit comments