hamlib 0.4.2 → 0.4.4

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/README.md CHANGED
@@ -8,6 +8,8 @@ Control amateur radio transceivers from Node.js using the [Hamlib](https://hamli
8
8
  - **Full Async/Promise API** - Non-blocking operations with async/await support
9
9
  - **Comprehensive Serial Control** - 13 parameters for complete serial port configuration
10
10
  - **Multiple Connections** - Serial ports, network (rigctld), direct control
11
+ - **Rotator Bridge** - Control antenna rotators through Hamlib's `rot_*` API with the same Promise-first bridge style
12
+ - **Antenna Switching** - Rig antenna APIs now use true 1-based antenna numbers and optional antenna option values
11
13
  - **Official Spectrum Streaming** - Wraps Hamlib's official spectrum callback API with Promise helpers and typed events
12
14
  - **TypeScript Ready** - Complete type definitions included
13
15
  - **Cross-platform** - Windows, Linux, macOS
@@ -38,7 +40,7 @@ For faster installation or offline environments, you can manually install precom
38
40
  ## Quick Start
39
41
 
40
42
  ```javascript
41
- const { HamLib } = require('hamlib');
43
+ const { HamLib, Rotator, PASSBAND } = require('hamlib');
42
44
 
43
45
  async function main() {
44
46
  // Create rig instance (model 1035 = FT-991A)
@@ -48,13 +50,20 @@ async function main() {
48
50
 
49
51
  // Basic operations
50
52
  await rig.setFrequency(144390000); // 144.39 MHz
51
- await rig.setMode('FM');
53
+ await rig.setMode('FM', 'nochange');
52
54
 
53
55
  const freq = await rig.getFrequency();
54
56
  const mode = await rig.getMode();
55
57
  console.log(`${freq/1000000} MHz ${mode.mode}`);
56
58
 
57
59
  await rig.close();
60
+
61
+ // Create rotator instance (model 1 = Dummy rotator)
62
+ const rotator = new Rotator(1);
63
+ await rotator.open();
64
+ await rotator.setPosition(180, 30);
65
+ console.log(await rotator.getPosition());
66
+ await rotator.close();
58
67
  }
59
68
 
60
69
  main().catch(console.error);
@@ -77,6 +86,27 @@ await rig.open();
77
86
  await rig.close();
78
87
  ```
79
88
 
89
+ ### Rotator Control
90
+
91
+ ```javascript
92
+ const rotators = Rotator.getSupportedRotators();
93
+ const dummy = rotators.find(r => r.modelName === 'Dummy');
94
+
95
+ const rotator = new Rotator(dummy.rotModel);
96
+ await rotator.open();
97
+
98
+ await rotator.setPosition(135, 20);
99
+ const position = await rotator.getPosition();
100
+
101
+ await rotator.move('RIGHT', 2);
102
+ await rotator.stop();
103
+
104
+ const status = await rotator.getStatus();
105
+ const caps = rotator.getRotatorCaps();
106
+
107
+ await rotator.close();
108
+ ```
109
+
80
110
  ### Basic Control
81
111
 
82
112
  ```javascript
@@ -86,6 +116,9 @@ const freq = await rig.getFrequency();
86
116
 
87
117
  // Mode
88
118
  await rig.setMode('USB');
119
+ await rig.setMode('USB', 'nochange');
120
+ await rig.setMode('USB', PASSBAND.NOCHANGE);
121
+ await rig.setMode('USB', rig.getPassbandNormal('USB'));
89
122
  const mode = await rig.getMode();
90
123
 
91
124
  // VFO
@@ -136,6 +169,12 @@ const audioLevel = await rig.getLevel('AF');
136
169
  await rig.setFunction('NB', true); // Noise blanker on
137
170
  const voxEnabled = await rig.getFunction('VOX');
138
171
 
172
+ // Antenna switching (1-based antenna number)
173
+ await rig.setAntenna(2); // Select antenna 2
174
+ await rig.setAntenna(2, 7); // Select antenna 2 with backend-specific option
175
+ const antenna = await rig.getAntenna();
176
+ console.log(antenna.currentAntenna, antenna.option);
177
+
139
178
  // Split operation
140
179
  await rig.setSplit(true); // Enable split
141
180
  await rig.setSplitFreq(144340000); // TX frequency
@@ -324,7 +363,7 @@ async function repeaterOperation() {
324
363
 
325
364
  // Set up for 2m repeater
326
365
  await rig.setFrequency(145500000); // 145.500 MHz
327
- await rig.setMode('FM');
366
+ await rig.setMode('FM', 'nochange');
328
367
  await rig.setRepeaterShift('MINUS'); // Negative offset
329
368
  await rig.setRepeaterOffset(600000); // 600 kHz offset
330
369
  await rig.setTuningStep(12500); // 12.5 kHz steps
package/binding.gyp CHANGED
@@ -4,6 +4,7 @@
4
4
  "target_name": "hamlib",
5
5
  "sources": [
6
6
  "src/hamlib.cpp",
7
+ "src/node_rotator.cpp",
7
8
  "src/decoder.cpp",
8
9
  "src/addon.cpp"
9
10
  ],
package/index.d.ts CHANGED
@@ -9,11 +9,15 @@ interface ConnectionInfo {
9
9
  /** Port path or network address */
10
10
  portPath: string;
11
11
  /** Connection status */
12
- connected: boolean;
12
+ isOpen: boolean;
13
13
  /** Original model number */
14
14
  originalModel: number;
15
15
  /** Actual model number used */
16
- actualModel: number;
16
+ currentModel: number;
17
+ /** @deprecated Use isOpen */
18
+ connected?: boolean;
19
+ /** @deprecated Use currentModel */
20
+ actualModel?: number;
17
21
  }
18
22
 
19
23
  /**
@@ -44,6 +48,16 @@ interface SupportedRigInfo {
44
48
  rigType: string;
45
49
  }
46
50
 
51
+ interface SupportedRotatorInfo {
52
+ rotModel: number;
53
+ modelName: string;
54
+ mfgName: string;
55
+ version: string;
56
+ status: string;
57
+ rotType: 'azimuth' | 'elevation' | 'azel' | 'other';
58
+ rotTypeMask: number;
59
+ }
60
+
47
61
  /**
48
62
  * Antenna information interface
49
63
  */
@@ -58,6 +72,53 @@ interface AntennaInfo {
58
72
  option: number;
59
73
  }
60
74
 
75
+ interface RotatorConnectionInfo {
76
+ connectionType: 'serial' | 'network';
77
+ portPath: string;
78
+ isOpen: boolean;
79
+ originalModel: number;
80
+ currentModel: number;
81
+ }
82
+
83
+ interface RotatorPosition {
84
+ azimuth: number;
85
+ elevation: number;
86
+ }
87
+
88
+ interface RotatorStatus {
89
+ mask: number;
90
+ flags: string[];
91
+ }
92
+
93
+ type RotatorDirection =
94
+ | 'UP'
95
+ | 'DOWN'
96
+ | 'LEFT'
97
+ | 'RIGHT'
98
+ | 'CCW'
99
+ | 'CW'
100
+ | 'UP_LEFT'
101
+ | 'UP_RIGHT'
102
+ | 'DOWN_LEFT'
103
+ | 'DOWN_RIGHT'
104
+ | 'UP_CCW'
105
+ | 'UP_CW'
106
+ | 'DOWN_CCW'
107
+ | 'DOWN_CW'
108
+ | number;
109
+
110
+ type RotatorResetType = 'ALL' | number;
111
+
112
+ interface RotatorCaps {
113
+ rotType: 'azimuth' | 'elevation' | 'azel' | 'other';
114
+ rotTypeMask: number;
115
+ minAz: number;
116
+ maxAz: number;
117
+ minEl: number;
118
+ maxEl: number;
119
+ supportedStatuses: string[];
120
+ }
121
+
61
122
  /**
62
123
  * Hamlib VFO token.
63
124
  * 实际支持的 token 取决于具体 backend / 电台型号。
@@ -86,6 +147,13 @@ type VFO =
86
147
  */
87
148
  type RadioMode = 'USB' | 'LSB' | 'FM' | 'PKTFM' | 'AM' | 'CW' | 'RTTY' | 'DIG' | string;
88
149
 
150
+ type PassbandSelector = 'narrow' | 'wide' | 'normal' | 'nochange' | number;
151
+
152
+ interface PassbandConstants {
153
+ NORMAL: 0;
154
+ NOCHANGE: -1;
155
+ }
156
+
89
157
  /**
90
158
  * Frequency range information
91
159
  */
@@ -543,14 +611,16 @@ declare class HamLib extends EventEmitter {
543
611
  /**
544
612
  * Set radio mode
545
613
  * @param mode Radio mode (such as 'USB', 'LSB', 'FM', 'PKTFM')
546
- * @param bandwidth Optional bandwidth setting ('narrow', 'wide', or default)
614
+ * @param bandwidth Optional bandwidth selector ('narrow', 'wide', 'normal', 'nochange') or width in Hz
547
615
  * @param vfo Optional VFO to set mode on ('VFOA' or 'VFOB'). If not specified, uses current VFO
548
616
  * @example
549
617
  * await rig.setMode('USB');
550
618
  * await rig.setMode('FM', 'narrow');
619
+ * await rig.setMode('USB', 'nochange');
620
+ * await rig.setMode('USB', PASSBAND.NOCHANGE);
551
621
  * await rig.setMode('USB', 'wide', 'VFOA');
552
622
  */
553
- setMode(mode: RadioMode, bandwidth?: 'narrow' | 'wide', vfo?: VFO): Promise<number>;
623
+ setMode(mode: RadioMode, bandwidth?: PassbandSelector, vfo?: VFO): Promise<number>;
554
624
 
555
625
  /**
556
626
  * Set PTT (Push-to-Talk) status
@@ -1299,6 +1369,7 @@ declare class HamLib extends EventEmitter {
1299
1369
  * await rig.setAntenna(2); // Select antenna 2
1300
1370
  */
1301
1371
  setAntenna(antenna: number, vfo?: VFO): Promise<number>;
1372
+ setAntenna(antenna: number, option: number, vfo?: VFO): Promise<number>;
1302
1373
 
1303
1374
  /**
1304
1375
  * Get comprehensive antenna information
@@ -1574,6 +1645,50 @@ declare class HamLib extends EventEmitter {
1574
1645
  getVfoInfo(vfo?: VFO): Promise<VfoInfo>;
1575
1646
  }
1576
1647
 
1648
+ declare class Rotator extends EventEmitter {
1649
+ constructor(model: number, port?: string);
1650
+
1651
+ static getSupportedRotators(): SupportedRotatorInfo[];
1652
+ static getHamlibVersion(): string;
1653
+ static setDebugLevel(level: RigDebugLevel): void;
1654
+ static getDebugLevel(): never;
1655
+ static getCopyright(): string;
1656
+ static getLicense(): string;
1657
+
1658
+ open(): Promise<number>;
1659
+ close(): Promise<number>;
1660
+ destroy(): Promise<number>;
1661
+ getConnectionInfo(): RotatorConnectionInfo;
1662
+
1663
+ setPosition(azimuth: number, elevation: number): Promise<number>;
1664
+ getPosition(): Promise<RotatorPosition>;
1665
+ move(direction: RotatorDirection, speed: number): Promise<number>;
1666
+ stop(): Promise<number>;
1667
+ park(): Promise<number>;
1668
+ reset(resetType: RotatorResetType): Promise<number>;
1669
+
1670
+ getInfo(): Promise<string>;
1671
+ getStatus(): Promise<RotatorStatus>;
1672
+
1673
+ setConf(name: string, value: string): Promise<number>;
1674
+ getConf(name: string): Promise<string>;
1675
+ getConfigSchema(): HamlibConfigFieldDescriptor[];
1676
+ getPortCaps(): HamlibPortCaps;
1677
+ getRotatorCaps(): RotatorCaps;
1678
+
1679
+ setLevel(level: string, value: number): Promise<number>;
1680
+ getLevel(level: string): Promise<number>;
1681
+ getSupportedLevels(): string[];
1682
+
1683
+ setFunction(func: string, enable: boolean): Promise<number>;
1684
+ getFunction(func: string): Promise<boolean>;
1685
+ getSupportedFunctions(): string[];
1686
+
1687
+ setParm(parm: string, value: number): Promise<number>;
1688
+ getParm(parm: string): Promise<number>;
1689
+ getSupportedParms(): string[];
1690
+ }
1691
+
1577
1692
  /**
1578
1693
  * Clock information for rig's internal clock
1579
1694
  */
@@ -1604,17 +1719,22 @@ interface VfoInfo {
1604
1719
  */
1605
1720
  declare const nodeHamlib: {
1606
1721
  HamLib: typeof HamLib;
1722
+ Rotator: typeof Rotator;
1723
+ PASSBAND: PassbandConstants;
1607
1724
  };
1608
1725
 
1726
+ declare const PASSBAND: PassbandConstants;
1727
+
1609
1728
  // Export types for use elsewhere
1610
- export { ConnectionInfo, ModeInfo, SupportedRigInfo, AntennaInfo, VFO, RadioMode, MemoryChannelData,
1729
+ export { ConnectionInfo, ModeInfo, SupportedRigInfo, SupportedRotatorInfo, AntennaInfo, RotatorConnectionInfo,
1730
+ RotatorPosition, RotatorStatus, RotatorDirection, RotatorResetType, RotatorCaps, VFO, RadioMode, MemoryChannelData,
1611
1731
  MemoryChannelInfo, SplitModeInfo, SplitStatusInfo, LevelType, FunctionType,
1612
1732
  ScanType, VfoOperationType, SerialConfigParam, SerialBaudRate, SerialParity,
1613
1733
  SerialHandshake, SerialControlState, PttType, DcdType, SerialConfigOptions,
1614
1734
  HamlibConfigFieldType, HamlibConfigFieldDescriptor, HamlibPortType, HamlibPortCaps,
1615
1735
  SpectrumScopeInfo, SpectrumModeInfo, SpectrumAverageModeInfo, SpectrumLine,
1616
1736
  SpectrumCapabilities, SpectrumSupportSummary, SpectrumConfig, SpectrumDisplayState,
1617
- ClockInfo, VfoInfo, HamLib };
1737
+ ClockInfo, VfoInfo, PassbandSelector, PassbandConstants, HamLib, Rotator, PASSBAND };
1618
1738
 
1619
1739
  // Support both CommonJS and ES module exports
1620
1740
  // @ts-ignore
package/lib/index.js CHANGED
@@ -4,6 +4,11 @@ const nodeGypBuild = require('node-gyp-build');
4
4
  // Ensure loader resolves from package root (contains prebuilds/ and build/)
5
5
  const nativeModule = nodeGypBuild(path.join(__dirname, '..'));
6
6
 
7
+ const PASSBAND = Object.freeze({
8
+ NORMAL: 0,
9
+ NOCHANGE: -1,
10
+ });
11
+
7
12
  /**
8
13
  * HamLib class for controlling amateur radio devices
9
14
  *
@@ -110,7 +115,7 @@ class HamLib extends EventEmitter {
110
115
  /**
111
116
  * Set radio mode
112
117
  * @param {string} mode - Radio mode ('USB', 'LSB', 'FM', 'PKTFM', etc.)
113
- * @param {string} [bandwidth] - Optional bandwidth ('narrow', 'wide')
118
+ * @param {string|number} [bandwidth] - Optional bandwidth selector ('narrow', 'wide', 'normal', 'nochange') or width in Hz
114
119
  * @param {string} [vfo] - Optional VFO ('VFOA' or 'VFOB')
115
120
  */
116
121
  async setMode(mode, bandwidth, vfo) {
@@ -1073,18 +1078,21 @@ class HamLib extends EventEmitter {
1073
1078
  /**
1074
1079
  * Set antenna selection
1075
1080
  * @param {number} antenna - Antenna number to select (1-based)
1081
+ * @param {number|string} [optionOrVfo] - Optional antenna option or VFO token
1076
1082
  * @param {string} [vfo] - Optional VFO ('VFOA' or 'VFOB')
1077
1083
  * @returns {Promise<number>} Success status
1078
1084
  * @example
1079
1085
  * await rig.setAntenna(1); // Select antenna 1
1080
1086
  * await rig.setAntenna(2); // Select antenna 2
1081
1087
  */
1082
- async setAntenna(antenna, vfo) {
1088
+ async setAntenna(antenna, optionOrVfo, vfo) {
1083
1089
  if (vfo !== undefined) {
1084
- return this._nativeInstance.setAntenna(antenna, vfo);
1085
- } else {
1086
- return this._nativeInstance.setAntenna(antenna);
1090
+ return this._nativeInstance.setAntenna(antenna, optionOrVfo, vfo);
1087
1091
  }
1092
+ if (optionOrVfo !== undefined) {
1093
+ return this._nativeInstance.setAntenna(antenna, optionOrVfo);
1094
+ }
1095
+ return this._nativeInstance.setAntenna(antenna);
1088
1096
  }
1089
1097
 
1090
1098
  /**
@@ -1410,7 +1418,144 @@ class HamLib extends EventEmitter {
1410
1418
  }
1411
1419
  }
1412
1420
 
1421
+ class Rotator extends EventEmitter {
1422
+ constructor(model, port) {
1423
+ super();
1424
+ this._nativeInstance = new nativeModule.Rotator(model, port);
1425
+ }
1426
+
1427
+ static getSupportedRotators() {
1428
+ return nativeModule.Rotator.getSupportedRotators();
1429
+ }
1430
+
1431
+ static getHamlibVersion() {
1432
+ return nativeModule.Rotator.getHamlibVersion();
1433
+ }
1434
+
1435
+ static setDebugLevel(level) {
1436
+ return nativeModule.Rotator.setDebugLevel(level);
1437
+ }
1438
+
1439
+ static getDebugLevel() {
1440
+ return nativeModule.Rotator.getDebugLevel();
1441
+ }
1442
+
1443
+ static getCopyright() {
1444
+ return nativeModule.Rotator.getCopyright();
1445
+ }
1446
+
1447
+ static getLicense() {
1448
+ return nativeModule.Rotator.getLicense();
1449
+ }
1450
+
1451
+ async open() {
1452
+ return this._nativeInstance.open();
1453
+ }
1454
+
1455
+ async close() {
1456
+ return this._nativeInstance.close();
1457
+ }
1458
+
1459
+ async destroy() {
1460
+ return this._nativeInstance.destroy();
1461
+ }
1462
+
1463
+ getConnectionInfo() {
1464
+ return this._nativeInstance.getConnectionInfo();
1465
+ }
1466
+
1467
+ async setPosition(azimuth, elevation) {
1468
+ return this._nativeInstance.setPosition(azimuth, elevation);
1469
+ }
1470
+
1471
+ async getPosition() {
1472
+ return this._nativeInstance.getPosition();
1473
+ }
1474
+
1475
+ async move(direction, speed) {
1476
+ return this._nativeInstance.move(direction, speed);
1477
+ }
1478
+
1479
+ async stop() {
1480
+ return this._nativeInstance.stop();
1481
+ }
1482
+
1483
+ async park() {
1484
+ return this._nativeInstance.park();
1485
+ }
1486
+
1487
+ async reset(resetType) {
1488
+ return this._nativeInstance.reset(resetType);
1489
+ }
1490
+
1491
+ async getInfo() {
1492
+ return this._nativeInstance.getInfo();
1493
+ }
1494
+
1495
+ async getStatus() {
1496
+ return this._nativeInstance.getStatus();
1497
+ }
1498
+
1499
+ async setConf(name, value) {
1500
+ return this._nativeInstance.setConf(name, value);
1501
+ }
1502
+
1503
+ async getConf(name) {
1504
+ return this._nativeInstance.getConf(name);
1505
+ }
1506
+
1507
+ getConfigSchema() {
1508
+ return this._nativeInstance.getConfigSchema();
1509
+ }
1510
+
1511
+ getPortCaps() {
1512
+ return this._nativeInstance.getPortCaps();
1513
+ }
1514
+
1515
+ getRotatorCaps() {
1516
+ return this._nativeInstance.getRotatorCaps();
1517
+ }
1518
+
1519
+ async setLevel(level, value) {
1520
+ return this._nativeInstance.setLevel(level, value);
1521
+ }
1522
+
1523
+ async getLevel(level) {
1524
+ return this._nativeInstance.getLevel(level);
1525
+ }
1526
+
1527
+ getSupportedLevels() {
1528
+ return this._nativeInstance.getSupportedLevels();
1529
+ }
1530
+
1531
+ async setFunction(func, enable) {
1532
+ return this._nativeInstance.setFunction(func, enable);
1533
+ }
1534
+
1535
+ async getFunction(func) {
1536
+ return this._nativeInstance.getFunction(func);
1537
+ }
1538
+
1539
+ getSupportedFunctions() {
1540
+ return this._nativeInstance.getSupportedFunctions();
1541
+ }
1542
+
1543
+ async setParm(parm, value) {
1544
+ return this._nativeInstance.setParm(parm, value);
1545
+ }
1546
+
1547
+ async getParm(parm) {
1548
+ return this._nativeInstance.getParm(parm);
1549
+ }
1550
+
1551
+ getSupportedParms() {
1552
+ return this._nativeInstance.getSupportedParms();
1553
+ }
1554
+ }
1555
+
1413
1556
  // Export for CommonJS
1414
- module.exports = { HamLib };
1557
+ module.exports = { HamLib, Rotator, PASSBAND };
1415
1558
  module.exports.HamLib = HamLib;
1416
- module.exports.default = { HamLib };
1559
+ module.exports.Rotator = Rotator;
1560
+ module.exports.PASSBAND = PASSBAND;
1561
+ module.exports.default = { HamLib, Rotator, PASSBAND };
package/lib/index.mjs CHANGED
@@ -7,8 +7,8 @@ const __dirname = path.dirname(__filename);
7
7
  const require = createRequire(import.meta.url);
8
8
 
9
9
  // Import the CommonJS module
10
- const { HamLib } = require('./index.js');
10
+ const { HamLib, Rotator, PASSBAND } = require('./index.js');
11
11
 
12
12
  // Export for ES modules
13
- export { HamLib };
14
- export default { HamLib };
13
+ export { HamLib, Rotator, PASSBAND };
14
+ export default { HamLib, Rotator, PASSBAND };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hamlib",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Node.js bindings for Hamlib rig control with official spectrum streaming support",
5
5
  "main": "index.js",
6
6
  "module": "lib/index.mjs",
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/src/addon.cpp CHANGED
@@ -1,5 +1,6 @@
1
1
  #include <napi.h>
2
2
  #include "hamlib.h"
3
+ #include "node_rotator.h"
3
4
  #include "decoder.h"
4
5
  #include "shim/hamlib_shim.h"
5
6
 
@@ -11,6 +12,9 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
11
12
  Napi::String name = Napi::String::New(env, "HamLib");
12
13
  exports.Set(name, NodeHamLib::GetClass(env));
13
14
 
15
+ Napi::String rotatorName = Napi::String::New(env, "Rotator");
16
+ exports.Set(rotatorName, NodeRotator::GetClass(env));
17
+
14
18
 
15
19
  // Napi::String decoder_name = Napi::String::New(env, "Decoder");
16
20
  // exports.Set(decoder_name, Decoder::GetClass(env));
@@ -18,4 +22,4 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
18
22
  return exports;
19
23
  }
20
24
 
21
- NODE_API_MODULE(radio, Init)
25
+ NODE_API_MODULE(radio, Init)