hamlib 0.1.2
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/COPYING +339 -0
- package/Readme.md +253 -0
- package/binding.gyp +152 -0
- package/index.d.ts +165 -0
- package/index.js +5 -0
- package/lib/binary-loader.js +95 -0
- package/lib/index.js +138 -0
- package/lib/index.mjs +14 -0
- package/package.json +68 -0
- package/prebuilds/darwin-arm64/hamlib.node +0 -0
- package/prebuilds/linux-arm64/hamlib.node +0 -0
- package/prebuilds/linux-x64/hamlib.node +0 -0
- package/src/addon.cpp +17 -0
- package/src/decoder.cpp +128 -0
- package/src/decoder.h +21 -0
- package/src/hamlib.cpp +476 -0
- package/src/hamlib.h +44 -0
package/src/decoder.cpp
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// #include "decoder.h"
|
|
2
|
+
// #include "portaudio.h"
|
|
3
|
+
// #include <stdio.h>
|
|
4
|
+
|
|
5
|
+
// //https://github.com/Streampunk/naudiodon -- perhaps should just use this?
|
|
6
|
+
|
|
7
|
+
// #define SAMPLE_RATE (44100)
|
|
8
|
+
// #define FRAMES_PER_BUFFER (512)
|
|
9
|
+
|
|
10
|
+
// #define PA_SAMPLE_TYPE paFloat32
|
|
11
|
+
// typedef float SAMPLE;
|
|
12
|
+
// #define SAMPLE_SILENCE (0.0f)
|
|
13
|
+
// #define PRINTF_S_FORMAT "%.8f"
|
|
14
|
+
|
|
15
|
+
// using namespace Napi;
|
|
16
|
+
|
|
17
|
+
// Decoder::Decoder(const Napi::CallbackInfo & info): ObjectWrap(info) {
|
|
18
|
+
// //Napi::Env env = info.Env();
|
|
19
|
+
// PaError err;
|
|
20
|
+
// //suppress output from pa_initialize
|
|
21
|
+
// fclose(stderr);
|
|
22
|
+
// err = Pa_Initialize();
|
|
23
|
+
// freopen("CON", "w", stderr);
|
|
24
|
+
// if (err != paNoError) {
|
|
25
|
+
// printf("ERROR: Pa_Initialize returned 0x%x\n", err);
|
|
26
|
+
// }
|
|
27
|
+
// }
|
|
28
|
+
|
|
29
|
+
// Napi::Value Decoder::Close(const Napi::CallbackInfo & info) {
|
|
30
|
+
// Napi::Env env = info.Env();
|
|
31
|
+
// int retcode = 0;
|
|
32
|
+
// Pa_Terminate();
|
|
33
|
+
// return Napi::Number::New(env, retcode);
|
|
34
|
+
// }
|
|
35
|
+
|
|
36
|
+
// Napi::Value Decoder::ListDevices(const Napi::CallbackInfo & info) {
|
|
37
|
+
// Napi::Env env = info.Env();
|
|
38
|
+
|
|
39
|
+
// int numDevices;
|
|
40
|
+
// const PaDeviceInfo * deviceInfo;
|
|
41
|
+
// numDevices = Pa_GetDeviceCount();
|
|
42
|
+
// Napi::Array arrDevices = Napi::Array::New(env);
|
|
43
|
+
// for (int i = 0; i < numDevices; i++) {
|
|
44
|
+
// deviceInfo = Pa_GetDeviceInfo(i);
|
|
45
|
+
// printf("Source: %s -- inputs: %i outputs: %i\n", deviceInfo->name, deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels);
|
|
46
|
+
// arrDevices.Set(i, Napi::String::New(env, deviceInfo -> name));
|
|
47
|
+
// }
|
|
48
|
+
// return arrDevices;
|
|
49
|
+
// }
|
|
50
|
+
|
|
51
|
+
// static int recordCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
|
|
52
|
+
// {
|
|
53
|
+
// (void)outputBuffer;
|
|
54
|
+
// const float *finput = static_cast<const float *>(inputBuffer);
|
|
55
|
+
// printf("callback called! float: %.8f : frames: %i\n", finput, framesPerBuffer);
|
|
56
|
+
// Decoder *decoder = static_cast<Decoder *>(userData);
|
|
57
|
+
// return paContinue;
|
|
58
|
+
// }
|
|
59
|
+
|
|
60
|
+
// Napi::Value Decoder::SetInputDevice(const Napi::CallbackInfo &info)
|
|
61
|
+
// {
|
|
62
|
+
// Napi::Env env = info.Env();
|
|
63
|
+
// if (!info[0].IsNumber())
|
|
64
|
+
// {
|
|
65
|
+
// Napi::TypeError::New(env, "Device number must be an integer").ThrowAsJavaScriptException();
|
|
66
|
+
// return env.Null();
|
|
67
|
+
// }
|
|
68
|
+
// // begin test
|
|
69
|
+
// inputParameters.device = info[0].As<Napi::Number>().Int32Value();
|
|
70
|
+
// inputParameters.channelCount = 1;
|
|
71
|
+
// inputParameters.sampleFormat = PA_SAMPLE_TYPE;
|
|
72
|
+
// inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
|
|
73
|
+
// inputParameters.hostApiSpecificStreamInfo = NULL;
|
|
74
|
+
// return Napi::Number::New(env, 0);
|
|
75
|
+
// }
|
|
76
|
+
|
|
77
|
+
// Napi::Value Decoder::SetOutputDevice(const Napi::CallbackInfo &info)
|
|
78
|
+
// {
|
|
79
|
+
// Napi::Env env = info.Env();
|
|
80
|
+
// if (!info[0].IsNumber())
|
|
81
|
+
// {
|
|
82
|
+
// Napi::TypeError::New(env, "Device number must be an integer").ThrowAsJavaScriptException();
|
|
83
|
+
// return env.Null();
|
|
84
|
+
// }
|
|
85
|
+
// outputParameters.device = info[0].As<Napi::Number>().Int32Value();
|
|
86
|
+
// outputParameters.channelCount = 1;
|
|
87
|
+
// outputParameters.sampleFormat = PA_SAMPLE_TYPE;
|
|
88
|
+
// outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowInputLatency;
|
|
89
|
+
// outputParameters.hostApiSpecificStreamInfo = NULL;
|
|
90
|
+
// return Napi::Number::New(env, 0);
|
|
91
|
+
// }
|
|
92
|
+
|
|
93
|
+
// Napi::Value Decoder::Open(const Napi::CallbackInfo &info)
|
|
94
|
+
// {
|
|
95
|
+
// Napi::Env env = info.Env();
|
|
96
|
+
// PaError err = paNoError;
|
|
97
|
+
// err = Pa_OpenStream(
|
|
98
|
+
// &stream,
|
|
99
|
+
// &inputParameters,
|
|
100
|
+
// &outputParameters,
|
|
101
|
+
// SAMPLE_RATE,
|
|
102
|
+
// FRAMES_PER_BUFFER,
|
|
103
|
+
// paClipOff, /* we won't output out of range samples so don't bother clipping them */
|
|
104
|
+
// recordCallback,
|
|
105
|
+
// static_cast<void *>(this));
|
|
106
|
+
// if (err != paNoError)
|
|
107
|
+
// {
|
|
108
|
+
// Napi::TypeError::New(env, "Unable to open input!").ThrowAsJavaScriptException();
|
|
109
|
+
// return env.Null();
|
|
110
|
+
// }
|
|
111
|
+
// else
|
|
112
|
+
// {
|
|
113
|
+
// err = Pa_StartStream(stream);
|
|
114
|
+
// return Napi::Number::New(env, 0);
|
|
115
|
+
// }
|
|
116
|
+
// }
|
|
117
|
+
|
|
118
|
+
// Napi::Function Decoder::GetClass(Napi::Env env) {
|
|
119
|
+
// return DefineClass(
|
|
120
|
+
// env,
|
|
121
|
+
// "Decoder", {
|
|
122
|
+
// Decoder::InstanceMethod("close", & Decoder::Close),
|
|
123
|
+
// Decoder::InstanceMethod("listDevices", & Decoder::ListDevices),
|
|
124
|
+
// Decoder::InstanceMethod("setInputDevice", & Decoder::SetInputDevice),
|
|
125
|
+
// Decoder::InstanceMethod("setOutputDevice", & Decoder::SetOutputDevice),
|
|
126
|
+
// Decoder::InstanceMethod("open", & Decoder::Open)
|
|
127
|
+
// });
|
|
128
|
+
// }
|
package/src/decoder.h
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// #pragma once
|
|
2
|
+
|
|
3
|
+
// #include "portaudio.h"
|
|
4
|
+
// #include <napi.h>
|
|
5
|
+
|
|
6
|
+
// class Decoder : public Napi::ObjectWrap<Decoder> {
|
|
7
|
+
// public:
|
|
8
|
+
// Decoder(const Napi::CallbackInfo&);
|
|
9
|
+
// Napi::Value Close(const Napi::CallbackInfo&);
|
|
10
|
+
// Napi::Value ListDevices(const Napi::CallbackInfo&);
|
|
11
|
+
// Napi::Value SetInputDevice(const Napi::CallbackInfo&);
|
|
12
|
+
// Napi::Value SetOutputDevice(const Napi::CallbackInfo&);
|
|
13
|
+
// Napi::Value Open(const Napi::CallbackInfo&);
|
|
14
|
+
|
|
15
|
+
// static Napi::Function GetClass(Napi::Env);
|
|
16
|
+
|
|
17
|
+
// private:
|
|
18
|
+
// bool rig_is_open = false;
|
|
19
|
+
// PaStreamParameters inputParameters, outputParameters;
|
|
20
|
+
// PaStream *stream;
|
|
21
|
+
// };
|
package/src/hamlib.cpp
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
#include "hamlib.h"
|
|
2
|
+
#include <string>
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
using namespace Napi;
|
|
7
|
+
|
|
8
|
+
Napi::FunctionReference NodeHamLib::constructor;
|
|
9
|
+
Napi::ThreadSafeFunction tsfn;
|
|
10
|
+
|
|
11
|
+
NodeHamLib::NodeHamLib(const Napi::CallbackInfo & info): ObjectWrap(info) {
|
|
12
|
+
Napi::Env env = info.Env();
|
|
13
|
+
this->m_currentInfo = (Napi::CallbackInfo *)&info;
|
|
14
|
+
if (info.Length() < 1) {
|
|
15
|
+
Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!info[0].IsNumber()) {
|
|
20
|
+
Napi::TypeError::New(env, "Invalid Rig number").ThrowAsJavaScriptException();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Set default port path if not provided
|
|
25
|
+
strncpy(port_path, "/dev/ttyUSB0", HAMLIB_FILPATHLEN - 1);
|
|
26
|
+
port_path[HAMLIB_FILPATHLEN - 1] = '\0';
|
|
27
|
+
|
|
28
|
+
// Check if port path is provided as second argument
|
|
29
|
+
if (info.Length() >= 2) {
|
|
30
|
+
if (info[1].IsString()) {
|
|
31
|
+
std::string portStr = info[1].As<Napi::String>().Utf8Value();
|
|
32
|
+
strncpy(port_path, portStr.c_str(), HAMLIB_FILPATHLEN - 1);
|
|
33
|
+
port_path[HAMLIB_FILPATHLEN - 1] = '\0';
|
|
34
|
+
} else {
|
|
35
|
+
// If second argument exists but is not a string, treat it as debug level (backward compatibility)
|
|
36
|
+
rig_set_debug_level(RIG_DEBUG_NONE);
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
rig_set_debug_level(RIG_DEBUG_NONE);
|
|
40
|
+
}
|
|
41
|
+
//rig_model_t myrig_model;
|
|
42
|
+
// hamlib_port_t myport;
|
|
43
|
+
// /* may be overriden by backend probe */
|
|
44
|
+
// myport.type.rig = RIG_PORT_SERIAL;
|
|
45
|
+
// myport.parm.serial.rate = 38400;
|
|
46
|
+
// myport.parm.serial.data_bits = 8;
|
|
47
|
+
// myport.parm.serial.stop_bits = 2;
|
|
48
|
+
// myport.parm.serial.parity = RIG_PARITY_NONE;
|
|
49
|
+
// myport.parm.serial.handshake = RIG_HANDSHAKE_HARDWARE;
|
|
50
|
+
// strncpy(myport.pathname, "/dev/ttyUSB0", HAMLIB_FILPATHLEN - 1);
|
|
51
|
+
|
|
52
|
+
// rig_load_all_backends();
|
|
53
|
+
// myrig_model = rig_probe(&myport);
|
|
54
|
+
// fprintf(stderr, "Got Rig Model %d \n", myrig_model);
|
|
55
|
+
|
|
56
|
+
rig_model_t myrig_model = info[0].As < Napi::Number > ().DoubleValue();
|
|
57
|
+
original_model = myrig_model;
|
|
58
|
+
|
|
59
|
+
// Check if port_path is a network address (contains colon)
|
|
60
|
+
is_network_rig = isNetworkAddress(port_path);
|
|
61
|
+
|
|
62
|
+
if (is_network_rig) {
|
|
63
|
+
// Use NETRIGCTL model for network connections
|
|
64
|
+
myrig_model = 2; // RIG_MODEL_NETRIGCTL
|
|
65
|
+
printf("Using network connection to %s\n", port_path);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
my_rig = rig_init(myrig_model);
|
|
69
|
+
//int retcode = 0;
|
|
70
|
+
if (!my_rig) {
|
|
71
|
+
fprintf(stderr, "Unknown rig num: %d\n", myrig_model);
|
|
72
|
+
fprintf(stderr, "Please check riglist.h\n");
|
|
73
|
+
Napi::TypeError::New(env, "Unable to Init Rig").ThrowAsJavaScriptException();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Set port path and type based on connection type
|
|
77
|
+
strncpy(my_rig -> state.rigport.pathname, port_path, HAMLIB_FILPATHLEN - 1);
|
|
78
|
+
|
|
79
|
+
if (is_network_rig) {
|
|
80
|
+
my_rig -> state.rigport.type.rig = RIG_PORT_NETWORK;
|
|
81
|
+
} else {
|
|
82
|
+
my_rig -> state.rigport.type.rig = RIG_PORT_SERIAL;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// this->freq_emit_cb = [info](freq_t freq) {
|
|
86
|
+
// Napi::Env env = info.Env();
|
|
87
|
+
// Napi::Function emit = info.This().As<Napi::Object>().Get("emit").As<Napi::Function>();
|
|
88
|
+
// emit.Call(
|
|
89
|
+
// info.This(),
|
|
90
|
+
// {Napi::String::New(env, "frequency_change"), Napi::Number::New(env, freq)});
|
|
91
|
+
// }
|
|
92
|
+
|
|
93
|
+
rig_is_open = false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
int NodeHamLib::freq_change_cb(RIG *rig, vfo_t vfo, freq_t freq, void* arg) {
|
|
97
|
+
auto instance = static_cast<NodeHamLib*>(arg);
|
|
98
|
+
printf("Rig changed freq to %0.7f Hz\n", freq);
|
|
99
|
+
Napi::Env env = instance->m_currentInfo->Env();
|
|
100
|
+
//Napi::Function emit = instance->m_currentInfo[0].Get("emit").As<Napi::Function>();
|
|
101
|
+
// Napi::Function emit = instance->m_currentInfo[0]->This().As<Napi::Object>().Get("emit").As<Napi::Function>();
|
|
102
|
+
//emit.Call(instance->m_currentInfo->This(), { Napi::String::New(env, "frequency_change"), Napi::Number::New(env, freq) });
|
|
103
|
+
//this->freq_emit_cb(freq);
|
|
104
|
+
//Napi::Function emit = this.As<Napi::Object>().Get("emit").As<Napi::Function>();
|
|
105
|
+
//auto fn = global.Get("process").As<Napi::Object>().Get("emit").As<Napi::Function>();
|
|
106
|
+
//fn.Call({Napi::Number::New(env, freq)});
|
|
107
|
+
return 0;
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
Napi::Value NodeHamLib::Open(const Napi::CallbackInfo & info) {
|
|
112
|
+
Napi::Env env = info.Env();
|
|
113
|
+
|
|
114
|
+
int retcode = rig_open(my_rig);
|
|
115
|
+
if (retcode != RIG_OK) {
|
|
116
|
+
printf("rig_open: error = %s\n", rigerror(retcode));
|
|
117
|
+
// Napi::TypeError::New(env, "Unable to open rig")
|
|
118
|
+
// .ThrowAsJavaScriptException();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
rig_set_freq_callback(my_rig, NodeHamLib::freq_change_cb, this);
|
|
124
|
+
|
|
125
|
+
auto ppt_cb =+[](RIG *rig, vfo_t vfo, ptt_t ptt, rig_ptr_t arg) {
|
|
126
|
+
printf("PPT pushed!");
|
|
127
|
+
return 0;
|
|
128
|
+
};
|
|
129
|
+
retcode = rig_set_ptt_callback (my_rig, ppt_cb, NULL);
|
|
130
|
+
rig_set_trn(my_rig, RIG_TRN_POLL);
|
|
131
|
+
if (retcode != RIG_OK ) {
|
|
132
|
+
printf("rig_set_trn: error = %s \n", rigerror(retcode));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
printf ("callback: %s", rigerror(retcode));
|
|
136
|
+
|
|
137
|
+
rig_is_open = true;
|
|
138
|
+
return Napi::Number::New(env, retcode);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
Napi::Value NodeHamLib::SetVFO(const Napi::CallbackInfo & info) {
|
|
142
|
+
Napi::Env env = info.Env();
|
|
143
|
+
int retcode;
|
|
144
|
+
|
|
145
|
+
if (!rig_is_open) {
|
|
146
|
+
Napi::TypeError::New(env, "Rig is not open!")
|
|
147
|
+
.ThrowAsJavaScriptException();
|
|
148
|
+
return env.Null();
|
|
149
|
+
}
|
|
150
|
+
if (info.Length() < 1) {
|
|
151
|
+
Napi::TypeError::New(env, "Must Specify VFO-A or VFO-B")
|
|
152
|
+
.ThrowAsJavaScriptException();
|
|
153
|
+
return env.Null();
|
|
154
|
+
}
|
|
155
|
+
if (!info[0].IsString()) {
|
|
156
|
+
Napi::TypeError::New(env, "Must Specify VFO-A or VFO-B as a string")
|
|
157
|
+
.ThrowAsJavaScriptException();
|
|
158
|
+
return env.Null();
|
|
159
|
+
}
|
|
160
|
+
auto name = info[0].As < Napi::String > ().Utf8Value().c_str();
|
|
161
|
+
if (strcmp(name, "VFO-A") == 0) {
|
|
162
|
+
retcode = rig_set_vfo(my_rig, RIG_VFO_A);
|
|
163
|
+
} else if (strcmp(name, "VFO-B") == 0) {
|
|
164
|
+
retcode = rig_set_vfo(my_rig, RIG_VFO_B);
|
|
165
|
+
} else {
|
|
166
|
+
retcode = 1;
|
|
167
|
+
}
|
|
168
|
+
return Napi::Number::New(env, retcode);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
Napi::Value NodeHamLib::SetFrequency(const Napi::CallbackInfo & info) {
|
|
172
|
+
Napi::Env env = info.Env();
|
|
173
|
+
int retcode;
|
|
174
|
+
if (!rig_is_open) {
|
|
175
|
+
Napi::TypeError::New(env, "Rig is not open!")
|
|
176
|
+
.ThrowAsJavaScriptException();
|
|
177
|
+
return env.Null();
|
|
178
|
+
}
|
|
179
|
+
if (info.Length() < 1) {
|
|
180
|
+
Napi::TypeError::New(env, "Must specify frequency")
|
|
181
|
+
.ThrowAsJavaScriptException();
|
|
182
|
+
return env.Null();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!info[0].IsNumber()) {
|
|
186
|
+
Napi::TypeError::New(env, "Frequency must be specified as an integer")
|
|
187
|
+
.ThrowAsJavaScriptException();
|
|
188
|
+
return env.Null();
|
|
189
|
+
}
|
|
190
|
+
auto freq = info[0].As < Napi::Number > ().Int32Value();
|
|
191
|
+
|
|
192
|
+
// Support optional VFO parameter
|
|
193
|
+
vfo_t vfo = RIG_VFO_CURR;
|
|
194
|
+
if (info.Length() >= 2 && info[1].IsString()) {
|
|
195
|
+
auto vfostr = info[1].As < Napi::String > ().Utf8Value().c_str();
|
|
196
|
+
if (strcmp(vfostr, "VFO-A") == 0) {
|
|
197
|
+
vfo = RIG_VFO_A;
|
|
198
|
+
} else if (strcmp(vfostr, "VFO-B") == 0) {
|
|
199
|
+
vfo = RIG_VFO_B;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
retcode = rig_set_freq(my_rig, vfo, freq);
|
|
204
|
+
return Napi::Number::New(env, retcode);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
Napi::Value NodeHamLib::SetMode(const Napi::CallbackInfo & info) {
|
|
208
|
+
Napi::Env env = info.Env();
|
|
209
|
+
int retcode;
|
|
210
|
+
pbwidth_t bandwidth;
|
|
211
|
+
if (!rig_is_open) {
|
|
212
|
+
Napi::TypeError::New(env, "Rig is not open!")
|
|
213
|
+
.ThrowAsJavaScriptException();
|
|
214
|
+
return env.Null();
|
|
215
|
+
}
|
|
216
|
+
if (info.Length() < 1) {
|
|
217
|
+
Napi::TypeError::New(env, "Must Specify Mode")
|
|
218
|
+
.ThrowAsJavaScriptException();
|
|
219
|
+
return env.Null();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!info[0].IsString()) {
|
|
223
|
+
Napi::TypeError::New(env, "Must Specify Mode as string")
|
|
224
|
+
.ThrowAsJavaScriptException();
|
|
225
|
+
return env.Null();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
auto modestr = info[0].As < Napi::String > ().Utf8Value().c_str();
|
|
229
|
+
auto mode = rig_parse_mode(modestr);
|
|
230
|
+
|
|
231
|
+
if (info.Length() > 1) {
|
|
232
|
+
if (!info[1].IsString()) {
|
|
233
|
+
Napi::TypeError::New(env, "Must Specify Mode as string")
|
|
234
|
+
.ThrowAsJavaScriptException();
|
|
235
|
+
return env.Null();
|
|
236
|
+
}
|
|
237
|
+
auto bandstr = info[1].As < Napi::String > ().Utf8Value().c_str();
|
|
238
|
+
if (strcmp(bandstr, "narrow") == 0) {
|
|
239
|
+
bandwidth = rig_passband_narrow(my_rig, mode);
|
|
240
|
+
} else if (strcmp(bandstr, "wide") == 0) {
|
|
241
|
+
bandwidth = rig_passband_wide(my_rig, mode);
|
|
242
|
+
} else {
|
|
243
|
+
bandwidth = RIG_PASSBAND_NORMAL;
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
bandwidth = RIG_PASSBAND_NORMAL;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
retcode = rig_set_mode(my_rig, RIG_VFO_CURR, mode, bandwidth);
|
|
250
|
+
if (retcode != RIG_OK) {
|
|
251
|
+
|
|
252
|
+
Napi::TypeError::New(env, rigerror(retcode))
|
|
253
|
+
.ThrowAsJavaScriptException();
|
|
254
|
+
return env.Null();
|
|
255
|
+
}
|
|
256
|
+
return Napi::Number::New(env, retcode);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
Napi::Value NodeHamLib::SetPtt(const Napi::CallbackInfo & info) {
|
|
260
|
+
Napi::Env env = info.Env();
|
|
261
|
+
int retcode;
|
|
262
|
+
bool ptt_state;
|
|
263
|
+
if (!rig_is_open) {
|
|
264
|
+
Napi::TypeError::New(env, "Rig is not open!")
|
|
265
|
+
.ThrowAsJavaScriptException();
|
|
266
|
+
return env.Null();
|
|
267
|
+
}
|
|
268
|
+
if (info.Length() < 1) {
|
|
269
|
+
Napi::TypeError::New(env, "Specify true or false for ppt state")
|
|
270
|
+
.ThrowAsJavaScriptException();
|
|
271
|
+
return env.Null();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (!info[0].IsBoolean()) {
|
|
275
|
+
Napi::TypeError::New(env, "PTT state is not boolean")
|
|
276
|
+
.ThrowAsJavaScriptException();
|
|
277
|
+
return env.Null();
|
|
278
|
+
}
|
|
279
|
+
ptt_state = info[0].As < Napi::Boolean > ();
|
|
280
|
+
if (ptt_state) {
|
|
281
|
+
retcode = rig_set_ptt(my_rig, RIG_VFO_CURR, RIG_PTT_ON);
|
|
282
|
+
} else {
|
|
283
|
+
retcode = rig_set_ptt(my_rig, RIG_VFO_CURR, RIG_PTT_OFF);
|
|
284
|
+
}
|
|
285
|
+
if (retcode != RIG_OK) {
|
|
286
|
+
|
|
287
|
+
Napi::TypeError::New(env, rigerror(retcode))
|
|
288
|
+
.ThrowAsJavaScriptException();
|
|
289
|
+
return env.Null();
|
|
290
|
+
}
|
|
291
|
+
return Napi::Number::New(env, retcode);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
Napi::Value NodeHamLib::GetVFO(const Napi::CallbackInfo & info) {
|
|
295
|
+
Napi::Env env = info.Env();
|
|
296
|
+
int retcode;
|
|
297
|
+
vfo_t vfo;
|
|
298
|
+
if (!rig_is_open) {
|
|
299
|
+
Napi::TypeError::New(env, "Rig is not open!")
|
|
300
|
+
.ThrowAsJavaScriptException();
|
|
301
|
+
return env.Null();
|
|
302
|
+
}
|
|
303
|
+
retcode = rig_get_vfo(my_rig, & vfo);
|
|
304
|
+
if (retcode == RIG_OK) {
|
|
305
|
+
return Napi::Number::New(env, vfo);
|
|
306
|
+
} else {
|
|
307
|
+
//dont throw an exception here, not every radio reports vfo
|
|
308
|
+
Napi::Error::New(env, rigerror(retcode));
|
|
309
|
+
return env.Null();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
Napi::Value NodeHamLib::GetFrequency(const Napi::CallbackInfo & info) {
|
|
315
|
+
Napi::Env env = info.Env();
|
|
316
|
+
int retcode;
|
|
317
|
+
freq_t freq;
|
|
318
|
+
if (!rig_is_open) {
|
|
319
|
+
Napi::TypeError::New(env, "Rig is not open!")
|
|
320
|
+
.ThrowAsJavaScriptException();
|
|
321
|
+
return env.Null();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Support optional VFO parameter
|
|
325
|
+
vfo_t vfo = RIG_VFO_CURR;
|
|
326
|
+
if (info.Length() >= 1 && info[0].IsString()) {
|
|
327
|
+
auto vfostr = info[0].As < Napi::String > ().Utf8Value().c_str();
|
|
328
|
+
if (strcmp(vfostr, "VFO-A") == 0) {
|
|
329
|
+
vfo = RIG_VFO_A;
|
|
330
|
+
} else if (strcmp(vfostr, "VFO-B") == 0) {
|
|
331
|
+
vfo = RIG_VFO_B;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
retcode = rig_get_freq(my_rig, vfo, & freq);
|
|
336
|
+
if (retcode == RIG_OK) {
|
|
337
|
+
return Napi::Number::New(env, freq);
|
|
338
|
+
} else {
|
|
339
|
+
Napi::Error::New(env, rigerror(retcode));
|
|
340
|
+
return env.Null();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
Napi::Value NodeHamLib::GetMode(const Napi::CallbackInfo & info) {
|
|
345
|
+
Napi::Env env = info.Env();
|
|
346
|
+
int retcode;
|
|
347
|
+
rmode_t rmode;
|
|
348
|
+
pbwidth_t width;
|
|
349
|
+
if (!rig_is_open) {
|
|
350
|
+
Napi::TypeError::New(env, "Rig is not open!")
|
|
351
|
+
.ThrowAsJavaScriptException();
|
|
352
|
+
return env.Null();
|
|
353
|
+
}
|
|
354
|
+
retcode = rig_get_mode(my_rig, RIG_VFO_CURR, & rmode, & width);
|
|
355
|
+
if (retcode == RIG_OK) {
|
|
356
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
357
|
+
obj.Set(Napi::String::New(env, "mode"), (char)rmode);
|
|
358
|
+
obj.Set(Napi::String::New(env, "width"), width);
|
|
359
|
+
return obj;
|
|
360
|
+
} else {
|
|
361
|
+
Napi::Error::New(env, rigerror(retcode));
|
|
362
|
+
return env.Null();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
Napi::Value NodeHamLib::GetStrength(const Napi::CallbackInfo & info) {
|
|
367
|
+
Napi::Env env = info.Env();
|
|
368
|
+
int retcode;
|
|
369
|
+
int strength;
|
|
370
|
+
if (!rig_is_open) {
|
|
371
|
+
Napi::TypeError::New(env, "Rig is not open!")
|
|
372
|
+
.ThrowAsJavaScriptException();
|
|
373
|
+
return env.Null();
|
|
374
|
+
}
|
|
375
|
+
retcode = rig_get_strength(my_rig, RIG_VFO_CURR, & strength);
|
|
376
|
+
if (retcode == RIG_OK) {
|
|
377
|
+
return Napi::Number::New(env, strength);
|
|
378
|
+
} else {
|
|
379
|
+
Napi::Error::New(env, rigerror(retcode));
|
|
380
|
+
return env.Null();
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
Napi::Value NodeHamLib::Close(const Napi::CallbackInfo & info) {
|
|
385
|
+
Napi::Env env = info.Env();
|
|
386
|
+
if (!rig_is_open) {
|
|
387
|
+
Napi::TypeError::New(env, "Rig is not open!")
|
|
388
|
+
.ThrowAsJavaScriptException();
|
|
389
|
+
return env.Null();
|
|
390
|
+
}
|
|
391
|
+
int retcode = rig_close(my_rig);
|
|
392
|
+
if (retcode != RIG_OK) {
|
|
393
|
+
Napi::TypeError::New(env, "Unable to open rig")
|
|
394
|
+
.ThrowAsJavaScriptException();
|
|
395
|
+
}
|
|
396
|
+
rig_is_open = false;
|
|
397
|
+
return Napi::Number::New(env, retcode);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
Napi::Value NodeHamLib::Destroy(const Napi::CallbackInfo & info) {
|
|
401
|
+
Napi::Env env = info.Env();
|
|
402
|
+
if (rig_is_open) {
|
|
403
|
+
rig_close(my_rig);
|
|
404
|
+
}
|
|
405
|
+
int retcode = rig_cleanup(my_rig);
|
|
406
|
+
if (retcode != RIG_OK) {
|
|
407
|
+
|
|
408
|
+
Napi::TypeError::New(env, rigerror(retcode))
|
|
409
|
+
.ThrowAsJavaScriptException();
|
|
410
|
+
}
|
|
411
|
+
rig_is_open = false;
|
|
412
|
+
return Napi::Number::New(env, retcode);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
Napi::Value NodeHamLib::GetConnectionInfo(const Napi::CallbackInfo & info) {
|
|
416
|
+
Napi::Env env = info.Env();
|
|
417
|
+
|
|
418
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
419
|
+
obj.Set(Napi::String::New(env, "connectionType"),
|
|
420
|
+
Napi::String::New(env, is_network_rig ? "network" : "serial"));
|
|
421
|
+
obj.Set(Napi::String::New(env, "portPath"),
|
|
422
|
+
Napi::String::New(env, port_path));
|
|
423
|
+
obj.Set(Napi::String::New(env, "isOpen"),
|
|
424
|
+
Napi::Boolean::New(env, rig_is_open));
|
|
425
|
+
obj.Set(Napi::String::New(env, "originalModel"),
|
|
426
|
+
Napi::Number::New(env, original_model));
|
|
427
|
+
obj.Set(Napi::String::New(env, "currentModel"),
|
|
428
|
+
Napi::Number::New(env, is_network_rig ? 2 : original_model));
|
|
429
|
+
|
|
430
|
+
return obj;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
Napi::Function NodeHamLib::GetClass(Napi::Env env) {
|
|
434
|
+
auto ret = DefineClass(
|
|
435
|
+
env,
|
|
436
|
+
"HamLib", {
|
|
437
|
+
NodeHamLib::InstanceMethod("open", & NodeHamLib::Open),
|
|
438
|
+
NodeHamLib::InstanceMethod("setVfo", & NodeHamLib::SetVFO),
|
|
439
|
+
NodeHamLib::InstanceMethod("setFrequency", & NodeHamLib::SetFrequency),
|
|
440
|
+
NodeHamLib::InstanceMethod("setMode", & NodeHamLib::SetMode),
|
|
441
|
+
NodeHamLib::InstanceMethod("setPtt", & NodeHamLib::SetPtt),
|
|
442
|
+
NodeHamLib::InstanceMethod("getVfo", & NodeHamLib::GetVFO),
|
|
443
|
+
NodeHamLib::InstanceMethod("getFrequency", & NodeHamLib::GetFrequency),
|
|
444
|
+
NodeHamLib::InstanceMethod("getMode", & NodeHamLib::GetMode),
|
|
445
|
+
NodeHamLib::InstanceMethod("getStrength", & NodeHamLib::GetStrength),
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
NodeHamLib::InstanceMethod("close", & NodeHamLib::Close),
|
|
451
|
+
NodeHamLib::InstanceMethod("destroy", & NodeHamLib::Destroy),
|
|
452
|
+
NodeHamLib::InstanceMethod("getConnectionInfo", & NodeHamLib::GetConnectionInfo),
|
|
453
|
+
});
|
|
454
|
+
constructor = Napi::Persistent(ret);
|
|
455
|
+
constructor.SuppressDestruct();
|
|
456
|
+
return ret;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Helper method to detect network address format (contains colon)
|
|
460
|
+
bool NodeHamLib::isNetworkAddress(const char* path) {
|
|
461
|
+
if (path == nullptr) {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Check for IPv4 or hostname with port (e.g., "localhost:4532", "192.168.1.1:4532")
|
|
466
|
+
const char* colon = strchr(path, ':');
|
|
467
|
+
if (colon != nullptr) {
|
|
468
|
+
// Make sure there's something after the colon (port number)
|
|
469
|
+
if (strlen(colon + 1) > 0) {
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
|
package/src/hamlib.h
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <napi.h>
|
|
4
|
+
#include <hamlib/rig.h>
|
|
5
|
+
//forward declarations
|
|
6
|
+
//typedef struct s_rig RIG;
|
|
7
|
+
//typedef unsigned int vfo_t;
|
|
8
|
+
//typedef double freq_t;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NodeHamLib : public Napi::ObjectWrap<NodeHamLib> {
|
|
12
|
+
public:
|
|
13
|
+
NodeHamLib(const Napi::CallbackInfo&);
|
|
14
|
+
Napi::Value Open(const Napi::CallbackInfo&);
|
|
15
|
+
Napi::Value SetVFO(const Napi::CallbackInfo&);
|
|
16
|
+
Napi::Value SetFrequency(const Napi::CallbackInfo&);
|
|
17
|
+
Napi::Value SetMode(const Napi::CallbackInfo&);
|
|
18
|
+
Napi::Value SetPtt(const Napi::CallbackInfo&);
|
|
19
|
+
Napi::Value GetFrequency(const Napi::CallbackInfo&);
|
|
20
|
+
Napi::Value GetVFO(const Napi::CallbackInfo&);
|
|
21
|
+
Napi::Value GetMode(const Napi::CallbackInfo&);
|
|
22
|
+
Napi::Value GetStrength(const Napi::CallbackInfo&);
|
|
23
|
+
|
|
24
|
+
Napi::Value Close(const Napi::CallbackInfo&);
|
|
25
|
+
Napi::Value Destroy(const Napi::CallbackInfo&);
|
|
26
|
+
Napi::Value GetConnectionInfo(const Napi::CallbackInfo&);
|
|
27
|
+
static Napi::Function GetClass(Napi::Env);
|
|
28
|
+
|
|
29
|
+
static int freq_change_cb(RIG*, vfo_t, freq_t, void*);
|
|
30
|
+
|
|
31
|
+
private:
|
|
32
|
+
RIG *my_rig;
|
|
33
|
+
bool rig_is_open = false;
|
|
34
|
+
bool is_network_rig = false; // Flag to indicate if using network connection
|
|
35
|
+
rig_model_t original_model = 0; // Store original model when using network
|
|
36
|
+
int count = 0;
|
|
37
|
+
void* freq_emit_cb;
|
|
38
|
+
char port_path[HAMLIB_FILPATHLEN]; // Store the port path
|
|
39
|
+
static Napi::FunctionReference constructor;
|
|
40
|
+
Napi::CallbackInfo * m_currentInfo;
|
|
41
|
+
|
|
42
|
+
// Helper method to detect network address format
|
|
43
|
+
bool isNetworkAddress(const char* path);
|
|
44
|
+
};
|