Create rig control interface and port Hamlib implementation to it.
parent
5ddbdf0563
commit
55872ce43d
|
@ -44,3 +44,4 @@ list(APPEND FREEDV_STATIC_DEPS hamlib)
|
|||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector")
|
||||
set(HAMLIB_ADD_DEPENDENCY TRUE)
|
|
@ -55,7 +55,9 @@ add_subdirectory(config)
|
|||
add_subdirectory(gui)
|
||||
add_subdirectory(pipeline)
|
||||
add_subdirectory(reporting)
|
||||
add_subdirectory(rig_control)
|
||||
add_subdirectory(sox)
|
||||
add_subdirectory(util)
|
||||
|
||||
# WIN32 is needed for Windows GUI apps and is ignored for UNIX like systems.
|
||||
# In addition, there are some required OSX-specific code files for platform specific handling.
|
||||
|
@ -80,9 +82,9 @@ endif(APPLE)
|
|||
|
||||
# Link imported or build tree targets.
|
||||
if(APPLE)
|
||||
target_link_libraries(FreeDV fdv_audio fdv_audio_pipeline fdv_config fdv_gui_util fdv_reporting fdv_sox lpcnetfreedv codec2)
|
||||
target_link_libraries(FreeDV fdv_audio fdv_audio_pipeline fdv_config fdv_gui_util fdv_reporting fdv_rig_control fdv_sox fdv_util lpcnetfreedv codec2)
|
||||
else(APPLE)
|
||||
target_link_libraries(freedv fdv_audio fdv_audio_pipeline fdv_config fdv_gui_util fdv_reporting fdv_sox lpcnetfreedv codec2)
|
||||
target_link_libraries(freedv fdv_audio fdv_audio_pipeline fdv_config fdv_gui_util fdv_reporting fdv_rig_control fdv_sox fdv_util lpcnetfreedv codec2)
|
||||
endif(APPLE)
|
||||
|
||||
# Add build dependencies for internally built external libraries.
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
add_library(fdv_rig_control STATIC
|
||||
HamlibRigController.cpp
|
||||
)
|
||||
|
||||
target_include_directories(fdv_rig_control PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/..)
|
||||
|
||||
if(HAMLIB_ADD_DEPENDENCY)
|
||||
add_dependencies(fdv_rig_control hamlib)
|
||||
endif(HAMLIB_ADD_DEPENDENCY)
|
|
@ -0,0 +1,563 @@
|
|||
//=========================================================================
|
||||
// Name: HamlibRigController.cpp
|
||||
// Purpose: Controls radios using Hamlib library.
|
||||
//
|
||||
// Authors: Mooneer Salem
|
||||
// License:
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.1,
|
||||
// as published by the Free Software Foundation. This program is
|
||||
// distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//=========================================================================
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
#include "HamlibRigController.h"
|
||||
|
||||
extern int g_verbose;
|
||||
|
||||
HamlibRigController::RigList HamlibRigController::RigList_;
|
||||
std::mutex HamlibRigController::RigListMutex_;
|
||||
|
||||
int HamlibRigController::BuildRigList_(const struct rig_caps *rig, rig_ptr_t rigList) {
|
||||
((HamlibRigController::RigList *)rigList)->push_back(rig);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool HamlibRigController::RigCompare_(const struct rig_caps *rig1, const struct rig_caps *rig2) {
|
||||
/* Compare manufacturer. */
|
||||
int r = strcasecmp(rig1->mfg_name, rig2->mfg_name);
|
||||
if (r != 0)
|
||||
return r < 0;
|
||||
|
||||
/* Compare model. */
|
||||
r = strcasecmp(rig1->model_name, rig2->model_name);
|
||||
if (r != 0)
|
||||
return r < 0;
|
||||
|
||||
/* Compare rig ID. */
|
||||
return rig1->rig_model < rig2->rig_model;
|
||||
}
|
||||
|
||||
HamlibRigController::HamlibRigController(std::string rigName, std::string serialPort, const int serialRate, const int civHex, const PttType pttType, std::string pttSerialPort)
|
||||
: rigName_(rigName)
|
||||
, serialPort_(serialPort)
|
||||
, serialRate_(serialRate)
|
||||
, civHex_(civHex)
|
||||
, pttType_(pttType)
|
||||
, pttSerialPort_(pttSerialPort)
|
||||
, rig_(nullptr)
|
||||
, multipleVfos_(false)
|
||||
, pttSet_(false)
|
||||
{
|
||||
// Perform initial load of rig list if this is our first time being created.
|
||||
std::unique_lock<std::mutex> lk(RigListMutex_);
|
||||
if (RigList_.size() == 0)
|
||||
{
|
||||
/* Stop hamlib from spewing info to stderr. */
|
||||
rig_set_debug(RIG_DEBUG_NONE);
|
||||
|
||||
/* Create sorted list of rigs. */
|
||||
rig_load_all_backends();
|
||||
rig_list_foreach(&HamlibRigController::BuildRigList_, &RigList_);
|
||||
sort(RigList_.begin(), RigList_.end(), &HamlibRigController::RigCompare_);
|
||||
|
||||
/* Reset debug output. */
|
||||
rig_set_debug(RIG_DEBUG_VERBOSE);
|
||||
}
|
||||
}
|
||||
|
||||
HamlibRigController::~HamlibRigController()
|
||||
{
|
||||
// Disconnect in a synchronous fashion before killing our thread.
|
||||
std::condition_variable cv;
|
||||
std::mutex mtx;
|
||||
std::unique_lock<std::mutex> lk(mtx);
|
||||
|
||||
enqueue_([&]() {
|
||||
std::unique_lock<std::mutex> innerLock(mtx);
|
||||
if (rig_ != nullptr)
|
||||
{
|
||||
disconnectImpl_();
|
||||
}
|
||||
cv.notify_one();
|
||||
});
|
||||
|
||||
cv.wait(lk);
|
||||
}
|
||||
|
||||
void HamlibRigController::connect()
|
||||
{
|
||||
enqueue_(std::bind(&HamlibRigController::connectImpl_, this));
|
||||
}
|
||||
|
||||
void HamlibRigController::disconnect()
|
||||
{
|
||||
enqueue_(std::bind(&HamlibRigController::disconnectImpl_, this));
|
||||
}
|
||||
|
||||
void HamlibRigController::ptt(bool state)
|
||||
{
|
||||
enqueue_(std::bind(&HamlibRigController::pttImpl_, this, state));
|
||||
}
|
||||
|
||||
void HamlibRigController::setFrequency(uint64_t frequency)
|
||||
{
|
||||
enqueue_(std::bind(&HamlibRigController::setFrequencyImpl_, this, frequency));
|
||||
}
|
||||
|
||||
void HamlibRigController::setMode(IRigFrequencyController::Mode mode)
|
||||
{
|
||||
enqueue_(std::bind(&HamlibRigController::setModeImpl_, this, mode));
|
||||
}
|
||||
|
||||
void HamlibRigController::requestCurrentFrequencyMode()
|
||||
{
|
||||
enqueue_(std::bind(&HamlibRigController::requestCurrentFrequencyModeImpl_, this));
|
||||
}
|
||||
|
||||
unsigned int HamlibRigController::rigNameToIndex_(std::string rigName)
|
||||
{
|
||||
unsigned int index = 0;
|
||||
for (auto& entry : RigList_)
|
||||
{
|
||||
char name[128];
|
||||
snprintf(name, 128, "%s %s", entry->mfg_name, entry->model_name);
|
||||
|
||||
if (rigName == std::string(name))
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Note: these execute in the thread created by ThreadedObject on object instantiation.
|
||||
|
||||
void HamlibRigController::connectImpl_()
|
||||
{
|
||||
if (rig_ != nullptr)
|
||||
{
|
||||
std::string errMsg = "Already connected to radio.";
|
||||
onRigError(this, errMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
auto rigIndex = rigNameToIndex_(rigName_);
|
||||
if (rigIndex == -1)
|
||||
{
|
||||
std::string errMsg = "Could not find rig " + rigName_ + " in Hamlib database.";
|
||||
onRigError(this, errMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialise, configure and open. */
|
||||
rig_ = rig_init(RigList_[rigIndex]->rig_model);
|
||||
if (!rig_)
|
||||
{
|
||||
if (g_verbose) fprintf(stderr, "rig_init() failed, returning\n");
|
||||
|
||||
std::string errMsg = "Hamlib could not initialize rig " + rigName_;
|
||||
onRigError(this, errMsg);
|
||||
return;
|
||||
}
|
||||
if (g_verbose) fprintf(stderr, "rig_init() OK ....\n");
|
||||
|
||||
/* Set CI-V address if necessary. */
|
||||
if (!strncmp(RigList_[rigIndex]->mfg_name, "Icom", 4))
|
||||
{
|
||||
char civAddr[5];
|
||||
snprintf(civAddr, 5, "0x%0X", civHex_);
|
||||
if (g_verbose) fprintf(stderr, "hamlib: setting CI-V address to: %s\n", civAddr);
|
||||
rig_set_conf(rig_, rig_token_lookup(rig_, "civaddr"), civAddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_verbose) fprintf(stderr, "hamlib: ignoring CI-V configuration due to non-Icom radio\r\n");
|
||||
}
|
||||
|
||||
rig_set_conf(rig_, rig_token_lookup(rig_, "rig_pathname"), serialPort_.c_str());
|
||||
|
||||
if (pttSerialPort_.size() > 0)
|
||||
{
|
||||
rig_set_conf(rig_, rig_token_lookup(rig_, "ptt_pathname"), pttSerialPort_.c_str());
|
||||
}
|
||||
|
||||
if (serialRate_ > 0)
|
||||
{
|
||||
if (g_verbose) fprintf(stderr, "hamlib: setting serial rate: %d\n", serialRate_);
|
||||
std::stringstream ss;
|
||||
ss << serialRate_;
|
||||
rig_set_conf(rig_, rig_token_lookup(rig_, "serial_speed"), ss.str().c_str());
|
||||
}
|
||||
|
||||
// Set PTT type as needed.
|
||||
switch(pttType_)
|
||||
{
|
||||
case PTT_VIA_RTS:
|
||||
rig_set_conf(rig_, rig_token_lookup(rig_, "ptt_type"), "RTS");
|
||||
break;
|
||||
case PTT_VIA_DTR:
|
||||
rig_set_conf(rig_, rig_token_lookup(rig_, "ptt_type"), "DTR");
|
||||
break;
|
||||
case PTT_VIA_NONE:
|
||||
rig_set_conf(rig_, rig_token_lookup(rig_, "ptt_type"), "NONE");
|
||||
break;
|
||||
case PTT_VIA_CAT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (rig_open(rig_) == RIG_OK)
|
||||
{
|
||||
if (g_verbose) fprintf(stderr, "hamlib: rig_open() OK\n");
|
||||
onRigConnected(this);
|
||||
|
||||
// Determine whether we have multiple VFOs.
|
||||
multipleVfos_ = false;
|
||||
vfo_t vfo;
|
||||
if (rig_get_vfo (rig_, &vfo) == RIG_OK && (rig_->state.vfo_list & RIG_VFO_B))
|
||||
{
|
||||
multipleVfos_ = true;
|
||||
}
|
||||
|
||||
// Get current frequency and mode when we first connect so we can
|
||||
// revert on close.
|
||||
requestCurrentFrequencyMode();
|
||||
return;
|
||||
}
|
||||
if (g_verbose) fprintf(stderr, "hamlib: rig_open() failed ...\n");
|
||||
|
||||
rig_cleanup(rig_);
|
||||
rig_ = nullptr;
|
||||
}
|
||||
|
||||
void HamlibRigController::disconnectImpl_()
|
||||
{
|
||||
if (rig_)
|
||||
{
|
||||
// Stop transmitting.
|
||||
if (pttSet_)
|
||||
{
|
||||
pttImpl_(false);
|
||||
}
|
||||
|
||||
rig_close(rig_);
|
||||
rig_cleanup(rig_);
|
||||
rig_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HamlibRigController::pttImpl_(bool state)
|
||||
{
|
||||
if (g_verbose) fprintf(stderr,"Hamlib::ptt: %d\n", state);
|
||||
|
||||
if (rig_ == nullptr)
|
||||
{
|
||||
onRigError(this, "Cannot set PTT: not connected to radio");
|
||||
return;
|
||||
}
|
||||
|
||||
ptt_t on = state ? RIG_PTT_ON : RIG_PTT_OFF;
|
||||
|
||||
int result = rig_set_ptt(rig_, RIG_VFO_CURR, on);
|
||||
if (result != RIG_OK)
|
||||
{
|
||||
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
|
||||
|
||||
std::string errMsg = "Cannot set PTT: " + std::string(rigerror(result));
|
||||
onRigError(this, errMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pttSet_ != state)
|
||||
{
|
||||
onPttChange(this, state);
|
||||
}
|
||||
|
||||
pttSet_ = state;
|
||||
|
||||
if (!state)
|
||||
{
|
||||
requestCurrentFrequencyMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HamlibRigController::setFrequencyImpl_(uint64_t frequencyHz)
|
||||
{
|
||||
if (rig_ == nullptr)
|
||||
{
|
||||
onRigError(this, "Cannot get current frequency/mode: not connected to radio");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pttSet_)
|
||||
{
|
||||
// If transmitting, temporarily stop transmitting so we can change the mode.
|
||||
int result = rig_set_ptt(rig_, RIG_VFO_CURR, RIG_PTT_OFF);
|
||||
if (result != RIG_OK)
|
||||
{
|
||||
// If we can't stop transmitting, we shouldn't try to change the mode
|
||||
// as it'll fail on some radios.
|
||||
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vfo_t currVfo = getCurrentVfo_();
|
||||
setFrequencyHelper_(currVfo, frequencyHz);
|
||||
|
||||
if (pttSet_)
|
||||
{
|
||||
// If transmitting, temporarily stop transmitting so we can change the mode.
|
||||
int result = rig_set_ptt(rig_, RIG_VFO_CURR, RIG_PTT_ON);
|
||||
if (result != RIG_OK)
|
||||
{
|
||||
// If we can't stop transmitting, we shouldn't try to change the mode
|
||||
// as it'll fail on some radios.
|
||||
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HamlibRigController::setModeImpl_(IRigFrequencyController::Mode mode)
|
||||
{
|
||||
rmode_t hamlibMode;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case USB:
|
||||
hamlibMode = RIG_MODE_USB;
|
||||
break;
|
||||
case LSB:
|
||||
hamlibMode = RIG_MODE_LSB;
|
||||
break;
|
||||
case DIGU:
|
||||
hamlibMode = RIG_MODE_PKTUSB;
|
||||
break;
|
||||
case DIGL:
|
||||
hamlibMode = RIG_MODE_PKTLSB;
|
||||
break;
|
||||
case FM:
|
||||
hamlibMode = RIG_MODE_FM;
|
||||
break;
|
||||
case DIGFM:
|
||||
hamlibMode = RIG_MODE_PKTFM;
|
||||
break;
|
||||
case AM:
|
||||
hamlibMode = RIG_MODE_AM;
|
||||
break;
|
||||
default:
|
||||
onRigError(this, "Cannot set mode: mode not recognized");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pttSet_)
|
||||
{
|
||||
// If transmitting, temporarily stop transmitting so we can change the mode.
|
||||
int result = rig_set_ptt(rig_, RIG_VFO_CURR, RIG_PTT_OFF);
|
||||
if (result != RIG_OK)
|
||||
{
|
||||
// If we can't stop transmitting, we shouldn't try to change the mode
|
||||
// as it'll fail on some radios.
|
||||
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vfo_t currVfo = getCurrentVfo_();
|
||||
setModeHelper_(currVfo, hamlibMode);
|
||||
|
||||
if (pttSet_)
|
||||
{
|
||||
// If transmitting, temporarily stop transmitting so we can change the mode.
|
||||
int result = rig_set_ptt(rig_, RIG_VFO_CURR, RIG_PTT_ON);
|
||||
if (result != RIG_OK)
|
||||
{
|
||||
// If we can't stop transmitting, we shouldn't try to change the mode
|
||||
// as it'll fail on some radios.
|
||||
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HamlibRigController::requestCurrentFrequencyModeImpl_()
|
||||
{
|
||||
if (rig_ == nullptr)
|
||||
{
|
||||
onRigError(this, "Cannot get current frequency/mode: not connected to radio");
|
||||
return;
|
||||
}
|
||||
|
||||
rmode_t mode = RIG_MODE_NONE;
|
||||
pbwidth_t passband = 0;
|
||||
vfo_t currVfo = getCurrentVfo_();
|
||||
|
||||
modeAttempt:
|
||||
int result = rig_get_mode(rig_, currVfo, &mode, &passband);
|
||||
if (result != RIG_OK && currVfo == RIG_VFO_CURR)
|
||||
{
|
||||
if (g_verbose) fprintf(stderr, "rig_get_mode: error = %s \n", rigerror(result));
|
||||
}
|
||||
else if (result != RIG_OK)
|
||||
{
|
||||
// We supposedly have multiple VFOs but ran into problems
|
||||
// trying to get information about the supposed active VFO.
|
||||
// Make a last ditch effort using RIG_VFO_CURR before fully failing.
|
||||
currVfo = RIG_VFO_CURR;
|
||||
goto modeAttempt;
|
||||
}
|
||||
else
|
||||
{
|
||||
freqAttempt:
|
||||
freq_t freq = 0;
|
||||
result = rig_get_freq(rig_, currVfo, &freq);
|
||||
if (result != RIG_OK && currVfo == RIG_VFO_CURR)
|
||||
{
|
||||
if (g_verbose) fprintf(stderr, "rig_get_freq: error = %s \n", rigerror(result));
|
||||
}
|
||||
else if (result != RIG_OK)
|
||||
{
|
||||
// We supposedly have multiple VFOs but ran into problems
|
||||
// trying to get information about the supposed active VFO.
|
||||
// Make a last ditch effort using RIG_VFO_CURR before fully failing.
|
||||
currVfo = RIG_VFO_CURR;
|
||||
goto freqAttempt;
|
||||
}
|
||||
else
|
||||
{
|
||||
IRigFrequencyController::Mode currMode;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case RIG_MODE_USB:
|
||||
currMode = USB;
|
||||
break;
|
||||
case RIG_MODE_LSB:
|
||||
currMode = LSB;
|
||||
break;
|
||||
case RIG_MODE_PKTUSB:
|
||||
currMode = DIGU;
|
||||
break;
|
||||
case RIG_MODE_PKTLSB:
|
||||
currMode = DIGL;
|
||||
break;
|
||||
case RIG_MODE_FM:
|
||||
currMode = FM;
|
||||
break;
|
||||
case RIG_MODE_PKTFM:
|
||||
currMode = DIGFM;
|
||||
break;
|
||||
case RIG_MODE_AM:
|
||||
currMode = AM;
|
||||
break;
|
||||
default:
|
||||
currMode = UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
onFreqModeChange(this, freq, currMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vfo_t HamlibRigController::getCurrentVfo_()
|
||||
{
|
||||
vfo_t vfo = RIG_VFO_CURR;
|
||||
|
||||
if (multipleVfos_)
|
||||
{
|
||||
int result = rig_get_vfo(rig_, &vfo);
|
||||
if (result != RIG_OK && result != -RIG_ENAVAIL)
|
||||
{
|
||||
// Note: we'll still attempt operation using RIG_VFO_CURR just in case.
|
||||
if (g_verbose) fprintf(stderr, "rig_get_vfo: error = %s \n", rigerror(result));
|
||||
vfo = RIG_VFO_CURR;
|
||||
}
|
||||
}
|
||||
|
||||
return vfo;
|
||||
}
|
||||
|
||||
void HamlibRigController::setFrequencyHelper_(vfo_t currVfo, uint64_t frequencyHz)
|
||||
{
|
||||
bool setOkay = false;
|
||||
|
||||
freqAttempt:
|
||||
int result = rig_set_freq(rig_, currVfo, frequencyHz);
|
||||
if (result != RIG_OK && currVfo == RIG_VFO_CURR)
|
||||
{
|
||||
if (g_verbose) fprintf(stderr, "rig_set_freq: error = %s \n", rigerror(result));
|
||||
}
|
||||
else if (result != RIG_OK)
|
||||
{
|
||||
// We supposedly have multiple VFOs but ran into problems
|
||||
// trying to get information about the supposed active VFO.
|
||||
// Make a last ditch effort using RIG_VFO_CURR before fully failing.
|
||||
currVfo = RIG_VFO_CURR;
|
||||
goto freqAttempt;
|
||||
}
|
||||
else
|
||||
{
|
||||
setOkay = true;
|
||||
}
|
||||
|
||||
// If we're able to set either frequency or mode, we should update the current state accordingly.
|
||||
// The actual configuration of the radio (in case of failure) will auto-update within
|
||||
// a few seconds after this update.
|
||||
if (setOkay)
|
||||
{
|
||||
requestCurrentFrequencyMode();
|
||||
}
|
||||
}
|
||||
|
||||
void HamlibRigController::setModeHelper_(vfo_t currVfo, rmode_t mode)
|
||||
{
|
||||
bool setOkay = false;
|
||||
|
||||
modeAttempt:
|
||||
int result = rig_set_mode(rig_, currVfo, mode, RIG_PASSBAND_NOCHANGE);
|
||||
if (result != RIG_OK && currVfo == RIG_VFO_CURR)
|
||||
{
|
||||
if (g_verbose) fprintf(stderr, "rig_set_mode: error = %s \n", rigerror(result));
|
||||
}
|
||||
else if (result != RIG_OK)
|
||||
{
|
||||
// We supposedly have multiple VFOs but ran into problems
|
||||
// trying to get information about the supposed active VFO.
|
||||
// Make a last ditch effort using RIG_VFO_CURR before fully failing.
|
||||
currVfo = RIG_VFO_CURR;
|
||||
goto modeAttempt;
|
||||
}
|
||||
else
|
||||
{
|
||||
setOkay = true;
|
||||
}
|
||||
|
||||
// If we're able to set either frequency or mode, we should update the current state accordingly.
|
||||
// The actual configuration of the radio (in case of failure) will auto-update within
|
||||
// a few seconds after this update.
|
||||
if (setOkay)
|
||||
{
|
||||
requestCurrentFrequencyMode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
//=========================================================================
|
||||
// Name: HamlibRigController.h
|
||||
// Purpose: Controls radios using Hamlib library.
|
||||
//
|
||||
// Authors: Mooneer Salem
|
||||
// License:
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.1,
|
||||
// as published by the Free Software Foundation. This program is
|
||||
// distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//=========================================================================
|
||||
|
||||
#ifndef HAMLIB_RIG_CONTROLLER_H
|
||||
#define HAMLIB_RIG_CONTROLLER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
#include "util/ThreadedObject.h"
|
||||
#include "IRigFrequencyController.h"
|
||||
#include "IRigPttController.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <hamlib/rig.h>
|
||||
}
|
||||
|
||||
class HamlibRigController : public ThreadedObject, public IRigFrequencyController, public IRigPttController
|
||||
{
|
||||
public:
|
||||
enum PttType
|
||||
{
|
||||
PTT_VIA_CAT = 0,
|
||||
PTT_VIA_RTS,
|
||||
PTT_VIA_DTR,
|
||||
PTT_VIA_NONE,
|
||||
};
|
||||
|
||||
HamlibRigController(std::string rigName, std::string serialPort, const int serialRate, const int civHex = 0, const PttType pttType = PTT_VIA_CAT, std::string pttSerialPort = std::string());
|
||||
virtual ~HamlibRigController();
|
||||
|
||||
virtual void connect() override;
|
||||
virtual void disconnect() override;
|
||||
virtual void ptt(bool state) override;
|
||||
virtual void setFrequency(uint64_t frequency) override;
|
||||
virtual void setMode(IRigFrequencyController::Mode mode) override;
|
||||
virtual void requestCurrentFrequencyMode() override;
|
||||
|
||||
private:
|
||||
using RigList = std::vector<const struct rig_caps *>;
|
||||
|
||||
std::string rigName_;
|
||||
std::string serialPort_;
|
||||
const int serialRate_;
|
||||
const int civHex_;
|
||||
const PttType pttType_;
|
||||
std::string pttSerialPort_;
|
||||
|
||||
RIG* rig_;
|
||||
bool multipleVfos_;
|
||||
bool pttSet_;
|
||||
|
||||
unsigned int rigNameToIndex_(std::string rigName);
|
||||
vfo_t getCurrentVfo_();
|
||||
void setFrequencyHelper_(vfo_t currVfo, uint64_t frequencyHz);
|
||||
void setModeHelper_(vfo_t currVfo, rmode_t mode);
|
||||
|
||||
void connectImpl_();
|
||||
void disconnectImpl_();
|
||||
void pttImpl_(bool state);
|
||||
void setFrequencyImpl_(uint64_t frequencyHz);
|
||||
void setModeImpl_(IRigFrequencyController::Mode mode);
|
||||
void requestCurrentFrequencyModeImpl_();
|
||||
|
||||
static RigList RigList_;
|
||||
static std::mutex RigListMutex_;
|
||||
|
||||
static bool RigCompare_(const struct rig_caps *rig1, const struct rig_caps *rig2);
|
||||
static int BuildRigList_(const struct rig_caps *rig, rig_ptr_t);
|
||||
};
|
||||
|
||||
#endif // HAMLIB_RIG_CONTROLLER_H
|
|
@ -0,0 +1,46 @@
|
|||
//=========================================================================
|
||||
// Name: IRigController.h
|
||||
// Purpose: Interface for control of radios.
|
||||
//
|
||||
// Authors: Mooneer Salem
|
||||
// License:
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.1,
|
||||
// as published by the Free Software Foundation. This program is
|
||||
// distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//=========================================================================
|
||||
|
||||
#ifndef I_RIG_CONTROLLER_H
|
||||
#define I_RIG_CONTROLLER_H
|
||||
|
||||
#include <string>
|
||||
#include "util/EventHandler.h"
|
||||
|
||||
class IRigController
|
||||
{
|
||||
public:
|
||||
virtual ~IRigController() = default;
|
||||
|
||||
// Event handlers common across all rig control objects.
|
||||
EventHandler<IRigController*, std::string> onRigError;
|
||||
EventHandler<IRigController*> onRigConnected;
|
||||
EventHandler<IRigController*> onRigDisconnected;
|
||||
|
||||
virtual void connect() = 0;
|
||||
virtual void disconnect() = 0;
|
||||
|
||||
protected:
|
||||
IRigController() = default;
|
||||
};
|
||||
|
||||
#endif // I_RIG_CONTROLLER_H
|
|
@ -0,0 +1,57 @@
|
|||
//=========================================================================
|
||||
// Name: IRigFrequencyController.h
|
||||
// Purpose: Interface for control of frequency/mode on radios.
|
||||
//
|
||||
// Authors: Mooneer Salem
|
||||
// License:
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.1,
|
||||
// as published by the Free Software Foundation. This program is
|
||||
// distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//=========================================================================
|
||||
|
||||
#ifndef I_RIG_FREQUENCY_CONTROLLER_H
|
||||
#define I_RIG_FREQUENCY_CONTROLLER_H
|
||||
|
||||
#include "IRigController.h"
|
||||
|
||||
class IRigFrequencyController : virtual public IRigController
|
||||
{
|
||||
public:
|
||||
enum Mode
|
||||
{
|
||||
UNKNOWN,
|
||||
|
||||
USB,
|
||||
DIGU,
|
||||
LSB,
|
||||
DIGL,
|
||||
FM,
|
||||
DIGFM,
|
||||
AM,
|
||||
};
|
||||
|
||||
virtual ~IRigFrequencyController() = default;
|
||||
|
||||
// Event handlers that are called by implementers of this interface.
|
||||
EventHandler<IRigFrequencyController*, uint64_t, Mode> onFreqModeChange;
|
||||
|
||||
virtual void setFrequency(uint64_t frequency) = 0;
|
||||
virtual void setMode(Mode mode) = 0;
|
||||
virtual void requestCurrentFrequencyMode() = 0;
|
||||
|
||||
protected:
|
||||
IRigFrequencyController() = default;
|
||||
};
|
||||
|
||||
#endif // I_RIG_FREQUENCY_CONTROLLER_H
|
|
@ -0,0 +1,42 @@
|
|||
//=========================================================================
|
||||
// Name: IRigPttController.h
|
||||
// Purpose: Interface for control of PTT on radios.
|
||||
//
|
||||
// Authors: Mooneer Salem
|
||||
// License:
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.1,
|
||||
// as published by the Free Software Foundation. This program is
|
||||
// distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//=========================================================================
|
||||
|
||||
#ifndef I_RIG_PTT_CONTROLLER_H
|
||||
#define I_RIG_PTT_CONTROLLER_H
|
||||
|
||||
#include "IRigController.h"
|
||||
|
||||
class IRigPttController : virtual public IRigController
|
||||
{
|
||||
public:
|
||||
virtual ~IRigPttController() = default;
|
||||
|
||||
// Event handlers that are called by implementers of this interface.
|
||||
EventHandler<IRigPttController*, bool> onPttChange;
|
||||
|
||||
virtual void ptt(bool state) = 0;
|
||||
|
||||
protected:
|
||||
IRigPttController() = default;
|
||||
};
|
||||
|
||||
#endif // I_RIG_PTT_CONTROLLER_H
|
|
@ -0,0 +1,3 @@
|
|||
add_library(fdv_util STATIC
|
||||
ThreadedObject.cpp
|
||||
)
|
|
@ -0,0 +1,68 @@
|
|||
//=========================================================================
|
||||
// Name: EventHandler.h
|
||||
// Purpose: List of functions that handle an event
|
||||
//
|
||||
// Authors: Mooneer Salem
|
||||
// License:
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.1,
|
||||
// as published by the Free Software Foundation. This program is
|
||||
// distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//=========================================================================
|
||||
|
||||
#ifndef EVENT_HANDLER_H
|
||||
#define EVENT_HANDLER_H
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
template<typename... FnArgs>
|
||||
class EventHandler
|
||||
{
|
||||
public:
|
||||
using FnType = std::function<void(FnArgs...)>;
|
||||
|
||||
EventHandler() = default;
|
||||
virtual ~EventHandler() = default;
|
||||
|
||||
void operator() (FnArgs... args);
|
||||
EventHandler<FnArgs...>& operator+=(FnType fn) const;
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::vector<FnType> fnList_;
|
||||
};
|
||||
|
||||
template<typename... FnArgs>
|
||||
void EventHandler<FnArgs...>::operator() (FnArgs... args)
|
||||
{
|
||||
for (auto& fn : fnList_)
|
||||
{
|
||||
fn(args...);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... FnArgs>
|
||||
EventHandler<FnArgs...>& EventHandler<FnArgs...>::operator+=(FnType fn) const
|
||||
{
|
||||
fnList_.push_back(fn);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... FnArgs>
|
||||
void EventHandler<FnArgs...>::clear()
|
||||
{
|
||||
fnList_.clear();
|
||||
}
|
||||
|
||||
#endif // EVENT_HANDLER_H
|
|
@ -0,0 +1,72 @@
|
|||
//=========================================================================
|
||||
// Name: ThreadedObject.cpp
|
||||
// Purpose: Wrapper to allow class to execute in its own thread.
|
||||
//
|
||||
// Authors: Mooneer Salem
|
||||
// License:
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.1,
|
||||
// as published by the Free Software Foundation. This program is
|
||||
// distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//=========================================================================
|
||||
|
||||
#include "ThreadedObject.h"
|
||||
|
||||
ThreadedObject::ThreadedObject()
|
||||
: isDestroying_(false),
|
||||
objectThread_(std::bind(&ThreadedObject::eventLoop_, this))
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
ThreadedObject::~ThreadedObject()
|
||||
{
|
||||
isDestroying_ = true;
|
||||
eventQueueCV_.notify_one();
|
||||
objectThread_.join();
|
||||
}
|
||||
|
||||
void ThreadedObject::enqueue_(std::function<void()> fn)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(eventQueueMutex_);
|
||||
eventQueue_.push_back(fn);
|
||||
eventQueueCV_.notify_one();
|
||||
}
|
||||
|
||||
void ThreadedObject::eventLoop_()
|
||||
{
|
||||
while (!isDestroying_)
|
||||
{
|
||||
std::function<void()> fn;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(eventQueueMutex_);
|
||||
eventQueueCV_.wait(lk, [&]() {
|
||||
return isDestroying_ || eventQueue_.size() > 0;
|
||||
});
|
||||
|
||||
if (isDestroying_)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
fn = eventQueue_[0];
|
||||
eventQueue_.erase(eventQueue_.begin());
|
||||
}
|
||||
|
||||
if (fn)
|
||||
{
|
||||
fn();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
//=========================================================================
|
||||
// Name: ThreadedObject.h
|
||||
// Purpose: Wrapper to allow class to execute in its own thread.
|
||||
//
|
||||
// Authors: Mooneer Salem
|
||||
// License:
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.1,
|
||||
// as published by the Free Software Foundation. This program is
|
||||
// distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//=========================================================================
|
||||
|
||||
#ifndef THREADED_OBJECT_H
|
||||
#define THREADED_OBJECT_H
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
class ThreadedObject
|
||||
{
|
||||
public:
|
||||
virtual ~ThreadedObject();
|
||||
|
||||
protected:
|
||||
ThreadedObject();
|
||||
|
||||
void enqueue_(std::function<void()> fn);
|
||||
|
||||
private:
|
||||
bool isDestroying_;
|
||||
std::thread objectThread_;
|
||||
std::vector<std::function<void()> > eventQueue_;
|
||||
std::mutex eventQueueMutex_;
|
||||
std::condition_variable eventQueueCV_;
|
||||
|
||||
void eventLoop_();
|
||||
};
|
||||
|
||||
#endif // THREADED_OBJECT_H
|
Loading…
Reference in New Issue