hamlib 0.4.2 → 0.4.3
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 +37 -1
- package/binding.gyp +1 -0
- package/index.d.ts +112 -4
- package/lib/index.js +145 -6
- package/lib/index.mjs +3 -3
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/libhamlib.4.dylib +0 -0
- package/prebuilds/darwin-arm64/node.napi.node +0 -0
- package/prebuilds/darwin-x64/libhamlib.4.dylib +0 -0
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-arm64/libhamlib.so +0 -0
- package/prebuilds/linux-arm64/libhamlib.so.4 +0 -0
- package/prebuilds/linux-arm64/libhamlib.so.4.0.7 +0 -0
- package/prebuilds/linux-arm64/node.napi.node +0 -0
- package/prebuilds/linux-x64/libhamlib.so +0 -0
- package/prebuilds/linux-x64/libhamlib.so.4 +0 -0
- package/prebuilds/linux-x64/libhamlib.so.4.0.7 +0 -0
- package/prebuilds/linux-x64/node.napi.node +0 -0
- package/prebuilds/win32-x64/hamlib_shim.dll +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/src/addon.cpp +5 -1
- package/src/hamlib.cpp +54 -12
- package/src/node_rotator.cpp +1309 -0
- package/src/node_rotator.h +62 -0
- package/src/shim/hamlib_shim.c +403 -0
- package/src/shim/hamlib_shim.h +117 -2
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 } = require('hamlib');
|
|
42
44
|
|
|
43
45
|
async function main() {
|
|
44
46
|
// Create rig instance (model 1035 = FT-991A)
|
|
@@ -55,6 +57,13 @@ async function main() {
|
|
|
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
|
|
@@ -136,6 +166,12 @@ const audioLevel = await rig.getLevel('AF');
|
|
|
136
166
|
await rig.setFunction('NB', true); // Noise blanker on
|
|
137
167
|
const voxEnabled = await rig.getFunction('VOX');
|
|
138
168
|
|
|
169
|
+
// Antenna switching (1-based antenna number)
|
|
170
|
+
await rig.setAntenna(2); // Select antenna 2
|
|
171
|
+
await rig.setAntenna(2, 7); // Select antenna 2 with backend-specific option
|
|
172
|
+
const antenna = await rig.getAntenna();
|
|
173
|
+
console.log(antenna.currentAntenna, antenna.option);
|
|
174
|
+
|
|
139
175
|
// Split operation
|
|
140
176
|
await rig.setSplit(true); // Enable split
|
|
141
177
|
await rig.setSplitFreq(144340000); // TX frequency
|
package/binding.gyp
CHANGED
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
|
-
|
|
12
|
+
isOpen: boolean;
|
|
13
13
|
/** Original model number */
|
|
14
14
|
originalModel: number;
|
|
15
15
|
/** Actual model number used */
|
|
16
|
-
|
|
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 / 电台型号。
|
|
@@ -1299,6 +1360,7 @@ declare class HamLib extends EventEmitter {
|
|
|
1299
1360
|
* await rig.setAntenna(2); // Select antenna 2
|
|
1300
1361
|
*/
|
|
1301
1362
|
setAntenna(antenna: number, vfo?: VFO): Promise<number>;
|
|
1363
|
+
setAntenna(antenna: number, option: number, vfo?: VFO): Promise<number>;
|
|
1302
1364
|
|
|
1303
1365
|
/**
|
|
1304
1366
|
* Get comprehensive antenna information
|
|
@@ -1574,6 +1636,50 @@ declare class HamLib extends EventEmitter {
|
|
|
1574
1636
|
getVfoInfo(vfo?: VFO): Promise<VfoInfo>;
|
|
1575
1637
|
}
|
|
1576
1638
|
|
|
1639
|
+
declare class Rotator extends EventEmitter {
|
|
1640
|
+
constructor(model: number, port?: string);
|
|
1641
|
+
|
|
1642
|
+
static getSupportedRotators(): SupportedRotatorInfo[];
|
|
1643
|
+
static getHamlibVersion(): string;
|
|
1644
|
+
static setDebugLevel(level: RigDebugLevel): void;
|
|
1645
|
+
static getDebugLevel(): never;
|
|
1646
|
+
static getCopyright(): string;
|
|
1647
|
+
static getLicense(): string;
|
|
1648
|
+
|
|
1649
|
+
open(): Promise<number>;
|
|
1650
|
+
close(): Promise<number>;
|
|
1651
|
+
destroy(): Promise<number>;
|
|
1652
|
+
getConnectionInfo(): RotatorConnectionInfo;
|
|
1653
|
+
|
|
1654
|
+
setPosition(azimuth: number, elevation: number): Promise<number>;
|
|
1655
|
+
getPosition(): Promise<RotatorPosition>;
|
|
1656
|
+
move(direction: RotatorDirection, speed: number): Promise<number>;
|
|
1657
|
+
stop(): Promise<number>;
|
|
1658
|
+
park(): Promise<number>;
|
|
1659
|
+
reset(resetType: RotatorResetType): Promise<number>;
|
|
1660
|
+
|
|
1661
|
+
getInfo(): Promise<string>;
|
|
1662
|
+
getStatus(): Promise<RotatorStatus>;
|
|
1663
|
+
|
|
1664
|
+
setConf(name: string, value: string): Promise<number>;
|
|
1665
|
+
getConf(name: string): Promise<string>;
|
|
1666
|
+
getConfigSchema(): HamlibConfigFieldDescriptor[];
|
|
1667
|
+
getPortCaps(): HamlibPortCaps;
|
|
1668
|
+
getRotatorCaps(): RotatorCaps;
|
|
1669
|
+
|
|
1670
|
+
setLevel(level: string, value: number): Promise<number>;
|
|
1671
|
+
getLevel(level: string): Promise<number>;
|
|
1672
|
+
getSupportedLevels(): string[];
|
|
1673
|
+
|
|
1674
|
+
setFunction(func: string, enable: boolean): Promise<number>;
|
|
1675
|
+
getFunction(func: string): Promise<boolean>;
|
|
1676
|
+
getSupportedFunctions(): string[];
|
|
1677
|
+
|
|
1678
|
+
setParm(parm: string, value: number): Promise<number>;
|
|
1679
|
+
getParm(parm: string): Promise<number>;
|
|
1680
|
+
getSupportedParms(): string[];
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1577
1683
|
/**
|
|
1578
1684
|
* Clock information for rig's internal clock
|
|
1579
1685
|
*/
|
|
@@ -1604,17 +1710,19 @@ interface VfoInfo {
|
|
|
1604
1710
|
*/
|
|
1605
1711
|
declare const nodeHamlib: {
|
|
1606
1712
|
HamLib: typeof HamLib;
|
|
1713
|
+
Rotator: typeof Rotator;
|
|
1607
1714
|
};
|
|
1608
1715
|
|
|
1609
1716
|
// Export types for use elsewhere
|
|
1610
|
-
export { ConnectionInfo, ModeInfo, SupportedRigInfo,
|
|
1717
|
+
export { ConnectionInfo, ModeInfo, SupportedRigInfo, SupportedRotatorInfo, AntennaInfo, RotatorConnectionInfo,
|
|
1718
|
+
RotatorPosition, RotatorStatus, RotatorDirection, RotatorResetType, RotatorCaps, VFO, RadioMode, MemoryChannelData,
|
|
1611
1719
|
MemoryChannelInfo, SplitModeInfo, SplitStatusInfo, LevelType, FunctionType,
|
|
1612
1720
|
ScanType, VfoOperationType, SerialConfigParam, SerialBaudRate, SerialParity,
|
|
1613
1721
|
SerialHandshake, SerialControlState, PttType, DcdType, SerialConfigOptions,
|
|
1614
1722
|
HamlibConfigFieldType, HamlibConfigFieldDescriptor, HamlibPortType, HamlibPortCaps,
|
|
1615
1723
|
SpectrumScopeInfo, SpectrumModeInfo, SpectrumAverageModeInfo, SpectrumLine,
|
|
1616
1724
|
SpectrumCapabilities, SpectrumSupportSummary, SpectrumConfig, SpectrumDisplayState,
|
|
1617
|
-
ClockInfo, VfoInfo, HamLib };
|
|
1725
|
+
ClockInfo, VfoInfo, HamLib, Rotator };
|
|
1618
1726
|
|
|
1619
1727
|
// Support both CommonJS and ES module exports
|
|
1620
1728
|
// @ts-ignore
|
package/lib/index.js
CHANGED
|
@@ -1073,18 +1073,21 @@ class HamLib extends EventEmitter {
|
|
|
1073
1073
|
/**
|
|
1074
1074
|
* Set antenna selection
|
|
1075
1075
|
* @param {number} antenna - Antenna number to select (1-based)
|
|
1076
|
+
* @param {number|string} [optionOrVfo] - Optional antenna option or VFO token
|
|
1076
1077
|
* @param {string} [vfo] - Optional VFO ('VFOA' or 'VFOB')
|
|
1077
1078
|
* @returns {Promise<number>} Success status
|
|
1078
1079
|
* @example
|
|
1079
1080
|
* await rig.setAntenna(1); // Select antenna 1
|
|
1080
1081
|
* await rig.setAntenna(2); // Select antenna 2
|
|
1081
1082
|
*/
|
|
1082
|
-
async setAntenna(antenna, vfo) {
|
|
1083
|
+
async setAntenna(antenna, optionOrVfo, vfo) {
|
|
1083
1084
|
if (vfo !== undefined) {
|
|
1084
|
-
return this._nativeInstance.setAntenna(antenna, vfo);
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1085
|
+
return this._nativeInstance.setAntenna(antenna, optionOrVfo, vfo);
|
|
1086
|
+
}
|
|
1087
|
+
if (optionOrVfo !== undefined) {
|
|
1088
|
+
return this._nativeInstance.setAntenna(antenna, optionOrVfo);
|
|
1087
1089
|
}
|
|
1090
|
+
return this._nativeInstance.setAntenna(antenna);
|
|
1088
1091
|
}
|
|
1089
1092
|
|
|
1090
1093
|
/**
|
|
@@ -1410,7 +1413,143 @@ class HamLib extends EventEmitter {
|
|
|
1410
1413
|
}
|
|
1411
1414
|
}
|
|
1412
1415
|
|
|
1416
|
+
class Rotator extends EventEmitter {
|
|
1417
|
+
constructor(model, port) {
|
|
1418
|
+
super();
|
|
1419
|
+
this._nativeInstance = new nativeModule.Rotator(model, port);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
static getSupportedRotators() {
|
|
1423
|
+
return nativeModule.Rotator.getSupportedRotators();
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
static getHamlibVersion() {
|
|
1427
|
+
return nativeModule.Rotator.getHamlibVersion();
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
static setDebugLevel(level) {
|
|
1431
|
+
return nativeModule.Rotator.setDebugLevel(level);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
static getDebugLevel() {
|
|
1435
|
+
return nativeModule.Rotator.getDebugLevel();
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
static getCopyright() {
|
|
1439
|
+
return nativeModule.Rotator.getCopyright();
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
static getLicense() {
|
|
1443
|
+
return nativeModule.Rotator.getLicense();
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
async open() {
|
|
1447
|
+
return this._nativeInstance.open();
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
async close() {
|
|
1451
|
+
return this._nativeInstance.close();
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
async destroy() {
|
|
1455
|
+
return this._nativeInstance.destroy();
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
getConnectionInfo() {
|
|
1459
|
+
return this._nativeInstance.getConnectionInfo();
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
async setPosition(azimuth, elevation) {
|
|
1463
|
+
return this._nativeInstance.setPosition(azimuth, elevation);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
async getPosition() {
|
|
1467
|
+
return this._nativeInstance.getPosition();
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
async move(direction, speed) {
|
|
1471
|
+
return this._nativeInstance.move(direction, speed);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
async stop() {
|
|
1475
|
+
return this._nativeInstance.stop();
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
async park() {
|
|
1479
|
+
return this._nativeInstance.park();
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
async reset(resetType) {
|
|
1483
|
+
return this._nativeInstance.reset(resetType);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
async getInfo() {
|
|
1487
|
+
return this._nativeInstance.getInfo();
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
async getStatus() {
|
|
1491
|
+
return this._nativeInstance.getStatus();
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
async setConf(name, value) {
|
|
1495
|
+
return this._nativeInstance.setConf(name, value);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
async getConf(name) {
|
|
1499
|
+
return this._nativeInstance.getConf(name);
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
getConfigSchema() {
|
|
1503
|
+
return this._nativeInstance.getConfigSchema();
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
getPortCaps() {
|
|
1507
|
+
return this._nativeInstance.getPortCaps();
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
getRotatorCaps() {
|
|
1511
|
+
return this._nativeInstance.getRotatorCaps();
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
async setLevel(level, value) {
|
|
1515
|
+
return this._nativeInstance.setLevel(level, value);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
async getLevel(level) {
|
|
1519
|
+
return this._nativeInstance.getLevel(level);
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
getSupportedLevels() {
|
|
1523
|
+
return this._nativeInstance.getSupportedLevels();
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
async setFunction(func, enable) {
|
|
1527
|
+
return this._nativeInstance.setFunction(func, enable);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
async getFunction(func) {
|
|
1531
|
+
return this._nativeInstance.getFunction(func);
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
getSupportedFunctions() {
|
|
1535
|
+
return this._nativeInstance.getSupportedFunctions();
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
async setParm(parm, value) {
|
|
1539
|
+
return this._nativeInstance.setParm(parm, value);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
async getParm(parm) {
|
|
1543
|
+
return this._nativeInstance.getParm(parm);
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
getSupportedParms() {
|
|
1547
|
+
return this._nativeInstance.getSupportedParms();
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1413
1551
|
// Export for CommonJS
|
|
1414
|
-
module.exports = { HamLib };
|
|
1552
|
+
module.exports = { HamLib, Rotator };
|
|
1415
1553
|
module.exports.HamLib = HamLib;
|
|
1416
|
-
module.exports.
|
|
1554
|
+
module.exports.Rotator = Rotator;
|
|
1555
|
+
module.exports.default = { HamLib, Rotator };
|
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 } = require('./index.js');
|
|
11
11
|
|
|
12
12
|
// Export for ES modules
|
|
13
|
-
export { HamLib };
|
|
14
|
-
export default { HamLib };
|
|
13
|
+
export { HamLib, Rotator };
|
|
14
|
+
export default { HamLib, Rotator };
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
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)
|
package/src/hamlib.cpp
CHANGED
|
@@ -106,6 +106,27 @@ static int parseVfoString(Napi::Env env, const std::string& vfoToken) {
|
|
|
106
106
|
return vfo;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
static int antennaOrdinalToMask(Napi::Env env, int antennaOrdinal) {
|
|
110
|
+
if (antennaOrdinal < 1 || antennaOrdinal > 30) {
|
|
111
|
+
Napi::RangeError::New(env, "Antenna number must be between 1 and 30").ThrowAsJavaScriptException();
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
return static_cast<int>(1u << (antennaOrdinal - 1));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static int antennaMaskToOrdinal(int antennaMask) {
|
|
118
|
+
const uint32_t mask = static_cast<uint32_t>(antennaMask);
|
|
119
|
+
if (mask == 0 || (mask & (mask - 1)) != 0) {
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
for (int bit = 0; bit < 30; ++bit) {
|
|
123
|
+
if (mask == (1u << bit)) {
|
|
124
|
+
return bit + 1;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
109
130
|
// Base AsyncWorker implementation with Promise support
|
|
110
131
|
HamLibAsyncWorker::HamLibAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance)
|
|
111
132
|
: AsyncWorker(env), hamlib_instance_(hamlib_instance), result_code_(0), error_message_(""), deferred_(Napi::Promise::Deferred::New(env)) {}
|
|
@@ -1230,9 +1251,9 @@ public:
|
|
|
1230
1251
|
} else {
|
|
1231
1252
|
Napi::Object result = Napi::Object::New(env);
|
|
1232
1253
|
|
|
1233
|
-
result.Set("currentAntenna", Napi::Number::New(env, antenna_curr_));
|
|
1234
|
-
result.Set("txAntenna", Napi::Number::New(env, antenna_tx_));
|
|
1235
|
-
result.Set("rxAntenna", Napi::Number::New(env, antenna_rx_));
|
|
1254
|
+
result.Set("currentAntenna", Napi::Number::New(env, antennaMaskToOrdinal(antenna_curr_)));
|
|
1255
|
+
result.Set("txAntenna", Napi::Number::New(env, antennaMaskToOrdinal(antenna_tx_)));
|
|
1256
|
+
result.Set("rxAntenna", Napi::Number::New(env, antennaMaskToOrdinal(antenna_rx_)));
|
|
1236
1257
|
result.Set("option", Napi::Number::New(env, option_));
|
|
1237
1258
|
|
|
1238
1259
|
deferred_.Resolve(result);
|
|
@@ -3768,18 +3789,39 @@ Napi::Value NodeHamLib::SetAntenna(const Napi::CallbackInfo & info) {
|
|
|
3768
3789
|
return env.Null();
|
|
3769
3790
|
}
|
|
3770
3791
|
|
|
3771
|
-
int
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
if (info.Length() >= 2 && info[1].IsString()) {
|
|
3776
|
-
vfo = parseVfoParameter(info, 1, SHIM_RIG_VFO_CURR);
|
|
3777
|
-
RETURN_NULL_IF_INVALID_VFO(vfo);
|
|
3792
|
+
const int antennaOrdinal = info[0].As<Napi::Number>().Int32Value();
|
|
3793
|
+
const int antenna = antennaOrdinalToMask(env, antennaOrdinal);
|
|
3794
|
+
if (antenna == 0) {
|
|
3795
|
+
return env.Null();
|
|
3778
3796
|
}
|
|
3779
|
-
|
|
3780
|
-
|
|
3797
|
+
|
|
3798
|
+
int vfo = SHIM_RIG_VFO_CURR;
|
|
3781
3799
|
float option = 0.0f;
|
|
3782
3800
|
|
|
3801
|
+
if (info.Length() >= 2) {
|
|
3802
|
+
if (info[1].IsNumber()) {
|
|
3803
|
+
option = info[1].As<Napi::Number>().FloatValue();
|
|
3804
|
+
if (info.Length() >= 3 && info[2].IsString()) {
|
|
3805
|
+
vfo = parseVfoParameter(info, 2, SHIM_RIG_VFO_CURR);
|
|
3806
|
+
RETURN_NULL_IF_INVALID_VFO(vfo);
|
|
3807
|
+
} else if (info.Length() >= 3 && !info[2].IsUndefined()) {
|
|
3808
|
+
Napi::TypeError::New(env, "Expected VFO token as third argument").ThrowAsJavaScriptException();
|
|
3809
|
+
return env.Null();
|
|
3810
|
+
}
|
|
3811
|
+
} else if (info[1].IsString()) {
|
|
3812
|
+
vfo = parseVfoParameter(info, 1, SHIM_RIG_VFO_CURR);
|
|
3813
|
+
RETURN_NULL_IF_INVALID_VFO(vfo);
|
|
3814
|
+
} else if (!info[1].IsUndefined()) {
|
|
3815
|
+
Napi::TypeError::New(env, "Expected antenna option as number or VFO token as string").ThrowAsJavaScriptException();
|
|
3816
|
+
return env.Null();
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
if (info.Length() >= 3 && info[1].IsString()) {
|
|
3821
|
+
Napi::TypeError::New(env, "Antenna option must come before VFO").ThrowAsJavaScriptException();
|
|
3822
|
+
return env.Null();
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3783
3825
|
SetAntennaAsyncWorker* asyncWorker = new SetAntennaAsyncWorker(env, this, antenna, vfo, option);
|
|
3784
3826
|
asyncWorker->Queue();
|
|
3785
3827
|
return asyncWorker->GetPromise();
|