diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ab7f52..7bde9eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,13 @@ cmake_minimum_required (VERSION 2.8) +project (CubicSDR) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") + SET(CUBICSDR_VERSION_MAJOR "0") SET(CUBICSDR_VERSION_MINOR "1") -SET(CUBICSDR_VERSION_PATCH "20") -SET(CUBICSDR_VERSION_REL "alpha") +SET(CUBICSDR_VERSION_PATCH "21") +SET(CUBICSDR_VERSION_REL "alpha-rig_control") SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}-${CUBICSDR_VERSION_REL}") SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}") @@ -34,7 +38,21 @@ ADD_DEFINITIONS( ENDIF() ENDIF() -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") + +set(USE_HAMLIB ON CACHE BOOL "Support hamlib for radio control functions.") + +if (USE_HAMLIB) + find_package(hamlib REQUIRED) + + if (NOT HAMLIB_FOUND) + message(FATAL_ERROR "hamlib development files not found...") + endif () + + include_directories(${HAMLIB_INCLUDE_DIR}) + link_libraries(${HAMLIB_LIBRARY}) + + ADD_DEFINITIONS(-DUSE_HAMLIB) +endif () macro(configure_files srcDir destDir globStr) message(STATUS "Copying ${srcDir}/${globStr} to directory ${destDir}") @@ -70,8 +88,6 @@ macro(configure_files_recurse srcDir destDir) endforeach(templateFile) endmacro(configure_files_recurse) -project (CubicSDR) - if( CMAKE_SIZEOF_VOID_P EQUAL 8 ) MESSAGE( "64 bit compiler detected" ) SET( EX_PLATFORM 64 ) @@ -433,6 +449,19 @@ IF(ENABLE_LIQUID_EXPERIMENTAL) ENDIF() ENDIF() + +IF (USE_HAMLIB) + SET (cubicsdr_headers + ${cubicsdr_headers} + src/rig/RigThread.h + ) + SET (cubicsdr_sources + ${cubicsdr_sources} + src/rig/RigThread.cpp + ) +ENDIF() + + SET (CUBICSDR_RESOURCES ${PROJECT_SOURCE_DIR}/font/vera_sans_mono12.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono16.fnt @@ -453,6 +482,9 @@ set(REG_EXT "[^/]*([.]cpp|[.]c|[.]h|[.]hpp)$") SOURCE_GROUP("Base" REGULAR_EXPRESSION "src/${REG_EXT}") SOURCE_GROUP("Forms\\SDRDevices" REGULAR_EXPRESSION "src/forms/SDRDevices/${REG_EXT}") SOURCE_GROUP("SDR" REGULAR_EXPRESSION "src/sdr/${REG_EXT}") +IF(USE_HAMLIB) + SOURCE_GROUP("Rig" REGULAR_EXPRESSION "src/rig/${REG_EXT}") +ENDIF() SOURCE_GROUP("Demodulator" REGULAR_EXPRESSION "src/demod/${REG_EXT}") SOURCE_GROUP("Modem" REGULAR_EXPRESSION "src/modules/modem/${REG_EXT}") SOURCE_GROUP("Modem\\Analog" REGULAR_EXPRESSION "src/modules/modem/analog/${REG_EXT}") @@ -486,6 +518,7 @@ include_directories ( ${PROJECT_SOURCE_DIR}/src/visual ${PROJECT_SOURCE_DIR}/src/process ${PROJECT_SOURCE_DIR}/src/ui + ${PROJECT_SOURCE_DIR}/src/rig ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/external/rtaudio ${PROJECT_SOURCE_DIR}/external/lodepng diff --git a/cmake/Modules/Findhamlib.cmake b/cmake/Modules/Findhamlib.cmake new file mode 100644 index 0000000..543643f --- /dev/null +++ b/cmake/Modules/Findhamlib.cmake @@ -0,0 +1,56 @@ +# - Try to find Hamlib +# Author: George L. Emigh - AB4BD +# +# Change Log: Charles J. Cliffe +# Updates: +# Jan 2015 - Add /opt/ paths for OSX MacPorts +# - Fix HAMLIB_INCLUDE_DIR absolute search +# TODO: +# Windows support +# Static support +# +# HAMLIB_FOUND - system has Hamlib +# HAMLIB_LIBRARY - location of the library for hamlib +# HAMLIB_INCLUDE_DIR - location of the include files for hamlib + +set(HAMLIB_FOUND FALSE) + +find_path(HAMLIB_INCLUDE_DIR + NAMES hamlib/rig.h + PATHS + /usr/include + /usr/local/include + /opt/local/include +) + +find_library(HAMLIB_LIBRARY + NAMES hamlib + PATHS + /usr/lib64/hamlib + /usr/lib/hamlib + /usr/lib64 + /usr/lib + /usr/local/lib64/hamlib + /usr/local/lib/hamlib + /usr/local/lib64 + /usr/local/lib + /opt/local/lib + /opt/local/lib/hamlib +) + +if(HAMLIB_INCLUDE_DIR AND HAMLIB_LIBRARY) + set(HAMLIB_FOUND TRUE) + # message(STATUS "Hamlib version: ${VERSION}") + message(STATUS "Found hamlib library directory at: ${HAMLIB_LIBRARY}") + message(STATUS "Found hamlib include directory at: ${HAMLIB_INCLUDE_DIR}") +endif(HAMLIB_INCLUDE_DIR AND HAMLIB_LIBRARY) + +IF(NOT HAMLIB_FOUND) + IF(NOT HAMLIB_FIND_QUIETLY) + MESSAGE(STATUS "HAMLIB was not found.") + ELSE(NOT HAMLIB_FIND_QUIETLY) + IF(HAMLIB_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "HAMLIB was not found.") + ENDIF(HAMLIB_FIND_REQUIRED) + ENDIF(NOT HAMLIB_FIND_QUIETLY) +ENDIF(NOT HAMLIB_FOUND) \ No newline at end of file diff --git a/src/AppConfig.cpp b/src/AppConfig.cpp index b16cb34..ae110d1 100644 --- a/src/AppConfig.cpp +++ b/src/AppConfig.cpp @@ -55,6 +55,12 @@ void DeviceConfig::save(DataNode *node) { for (ConfigSettings::const_iterator set_i = settings.begin(); set_i != settings.end(); set_i++) { *settingsNode->newChild(set_i->first.c_str()) = set_i->second; } + DataNode *rigIFs = node->newChild("rig_ifs"); + for (std::map::const_iterator rigIF_i = rigIF.begin(); rigIF_i != rigIF.end(); rigIF_i++) { + DataNode *ifNode = rigIFs->newChild("rig_if"); + *ifNode->newChild("model") = rigIF_i->first; + *ifNode->newChild("sdr_if") = rigIF_i->second; + } busy_lock.unlock(); } @@ -98,6 +104,21 @@ void DeviceConfig::load(DataNode *node) { } } } + if (node->hasAnother("rig_ifs")) { + DataNode *rigIFNodes = node->getNext("rig_ifs"); + while (rigIFNodes->hasAnother("rig_if")) { + DataNode *rigIFNode = rigIFNodes->getNext("rig_if"); + if (rigIFNode->hasAnother("model") && rigIFNode->hasAnother("sdr_if")) { + int load_model; + long long load_freq; + + rigIFNode->getNext("model")->element()->get(load_model); + rigIFNode->getNext("sdr_if")->element()->get(load_freq); + + rigIF[load_model] = load_freq; + } + } + } busy_lock.unlock(); } @@ -140,6 +161,16 @@ ConfigSettings DeviceConfig::getSettings() { return settings; } +void DeviceConfig::setRigIF(int rigType, long long freq) { + rigIF[rigType] = freq; +} + +long long DeviceConfig::getRigIF(int rigType) { + if (rigIF.find(rigType) != rigIF.end()) { + return rigIF[rigType]; + } + return 0; +} AppConfig::AppConfig() : configName("") { winX.store(0); @@ -152,6 +183,11 @@ AppConfig::AppConfig() : configName("") { centerFreq.store(100000000); waterfallLinesPerSec.store(DEFAULT_WATERFALL_LPS); spectrumAvgSpeed.store(0.65f); +#ifdef USE_HAMLIB + rigModel.store(1); + rigRate.store(57600); + rigPort = "/dev/ttyUSB0"; +#endif } DeviceConfig *AppConfig::getDevice(std::string deviceId) { @@ -297,6 +333,13 @@ bool AppConfig::save() { DataNode *device_node = devices_node->newChild("device"); device_config_i->second->save(device_node); } + +#ifdef USE_HAMLIB + DataNode *rig_node = cfg.rootNode()->newChild("rig"); + *rig_node->newChild("model") = rigModel.load(); + *rig_node->newChild("rate") = rigRate.load(); + *rig_node->newChild("port") = rigPort; +#endif std::string cfgFileName = getConfigFileName(); @@ -411,6 +454,27 @@ bool AppConfig::load() { } } } + +#ifdef USE_HAMLIB + if (cfg.rootNode()->hasAnother("rig")) { + DataNode *rig_node = cfg.rootNode()->getNext("rig"); + + if (rig_node->hasAnother("model")) { + int loadModel; + rig_node->getNext("model")->element()->get(loadModel); + rigModel.store(loadModel?loadModel:1); + } + if (rig_node->hasAnother("rate")) { + int loadRate; + rig_node->getNext("rate")->element()->get(loadRate); + rigRate.store(loadRate?loadRate:57600); + } + if (rig_node->hasAnother("port")) { + rigPort = rig_node->getNext("port")->element()->toString(); + } + } +#endif + return true; } @@ -419,3 +483,32 @@ bool AppConfig::reset() { return true; } + + +#if USE_HAMLIB + +int AppConfig::getRigModel() { + return rigModel.load(); +} + +void AppConfig::setRigModel(int rigModel) { + this->rigModel.store(rigModel); +} + +int AppConfig::getRigRate() { + return rigRate.load(); +} + +void AppConfig::setRigRate(int rigRate) { + this->rigRate.store(rigRate); +} + +std::string AppConfig::getRigPort() { + return rigPort; +} + +void AppConfig::setRigPort(std::string rigPort) { + this->rigPort = rigPort; +} + +#endif diff --git a/src/AppConfig.h b/src/AppConfig.h index 4d69489..b7e7f7c 100644 --- a/src/AppConfig.h +++ b/src/AppConfig.h @@ -35,6 +35,9 @@ public: void setSetting(std::string key, std::string value); std::string getSetting(std::string key, std::string defaultValue); + void setRigIF(int rigType, long long freq); + long long getRigIF(int rigType); + void save(DataNode *node); void load(DataNode *node); @@ -46,6 +49,7 @@ private: std::atomic_llong offset; ConfigSettings streamOpts; std::map settings; + std::map rigIF; }; class AppConfig { @@ -75,6 +79,17 @@ public: void setSpectrumAvgSpeed(float avgSpeed); float getSpectrumAvgSpeed(); +#if USE_HAMLIB + int getRigModel(); + void setRigModel(int rigModel); + + int getRigRate(); + void setRigRate(int rigRate); + + std::string getRigPort(); + void setRigPort(std::string rigPort); +#endif + void setConfigName(std::string configName); std::string getConfigFileName(bool ignoreName=false); bool save(); @@ -91,4 +106,8 @@ private: std::atomic_llong centerFreq; std::atomic_int waterfallLinesPerSec; std::atomic spectrumAvgSpeed; +#if USE_HAMLIB + std::atomic_int rigModel, rigRate; + std::string rigPort; +#endif }; diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 8366421..e2e78ae 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -38,6 +38,9 @@ EVT_SPLITTER_DCLICK(wxID_ANY, AppFrame::OnDoubleClickSash) EVT_SPLITTER_UNSPLIT(wxID_ANY, AppFrame::OnUnSplit) wxEND_EVENT_TABLE() +#ifdef USE_HAMLIB +#include "RigThread.h" +#endif AppFrame::AppFrame() : wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { @@ -409,6 +412,74 @@ AppFrame::AppFrame() : menuBar->Append(menu, wxT("Audio &Sample Rate")); +#ifdef USE_HAMLIB + + rigModel = wxGetApp().getConfig()->getRigModel(); + rigSerialRate = wxGetApp().getConfig()->getRigRate(); + rigPort = wxGetApp().getConfig()->getRigPort(); + + rigMenu = new wxMenu; + + rigEnableMenuItem = rigMenu->AppendCheckItem(wxID_RIG_TOGGLE, wxT("Enable Rig")); + + rigMenu->Append(wxID_RIG_SDR_IF, wxT("SDR-IF")); + + wxMenu *rigModelMenu = new wxMenu; + RigList &rl = RigThread::enumerate(); + numRigs = rl.size(); + + int modelMenuId = wxID_RIG_MODEL_BASE; + for (RigList::const_iterator ri = rl.begin(); ri != rl.end(); ri++) { + std::string modelString((*ri)->mfg_name); + modelString.append(" "); + modelString.append((*ri)->model_name); + + rigModelMenuItems[(*ri)->rig_model] = rigModelMenu->AppendRadioItem(modelMenuId, modelString, wxT("Description?")); + + if (rigModel == (*ri)->rig_model) { + rigModelMenuItems[(*ri)->rig_model]->Check(true); + } + + modelMenuId++; + } + + rigMenu->AppendSubMenu(rigModelMenu, wxT("Model")); + + wxMenu *rigSerialMenu = new wxMenu; + + rigSerialRates.push_back(1200); + rigSerialRates.push_back(2400); + rigSerialRates.push_back(4800); + rigSerialRates.push_back(9600); + rigSerialRates.push_back(19200); + rigSerialRates.push_back(38400); + rigSerialRates.push_back(57600); + rigSerialRates.push_back(115200); + rigSerialRates.push_back(128000); + rigSerialRates.push_back(256000); + + int rateMenuId = wxID_RIG_SERIAL_BASE; + for (std::vector::const_iterator rate_i = rigSerialRates.begin(); rate_i != rigSerialRates.end(); rate_i++) { + std::string rateString; + rateString.append(std::to_string((*rate_i))); + rateString.append(" baud"); + + rigSerialMenuItems[(*rate_i)] = rigSerialMenu->AppendRadioItem(rateMenuId, rateString, wxT("Description?")); + + if (rigSerialRate == (*rate_i)) { + rigSerialMenuItems[(*rate_i)]->Check(true); + } + + rateMenuId++; + } + + rigMenu->AppendSubMenu(rigSerialMenu, wxT("Serial Rate")); + + rigPortMenuItem = rigMenu->Append(wxID_RIG_PORT, wxT("Control Port")); + + menuBar->Append(rigMenu, wxT("&Rig Control")); +#endif + SetMenuBar(menuBar); CreateStatusBar(); @@ -521,7 +592,7 @@ void AppFrame::updateDeviceParams() { } wxMenuItem *item = subMenu->AppendRadioItem(wxID_SETTINGS_BASE+i, displayName); if (currentVal == (*str_i)) { - item->Check(); + item->Check(true); } j++; i++; @@ -579,6 +650,21 @@ void AppFrame::updateDeviceParams() { agcMenuItem->Check(wxGetApp().getAGCMode()); + +#if USE_HAMLIB + std::string deviceId = devInfo->getDeviceId(); + DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId); + + if (wxGetApp().rigIsActive()) { + rigSDRIF = devConfig->getRigIF(rigModel); + if (rigSDRIF) { + wxGetApp().lockFrequency(rigSDRIF); + } else { + wxGetApp().unlockFrequency(); + } + } +#endif + deviceChanged.store(false); } @@ -612,7 +698,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) { // iqSwapMenuItem->Check(swap_state); } else if (event.GetId() == wxID_AGC_CONTROL) { if (wxGetApp().getDevice() == NULL) { - agcMenuItem->Check(); + agcMenuItem->Check(true); return; } if (!wxGetApp().getAGCMode()) { @@ -820,6 +906,90 @@ void AppFrame::OnMenu(wxCommandEvent& event) { i++; } } + +#ifdef USE_HAMLIB + + bool resetRig = false; + if (event.GetId() >= wxID_RIG_MODEL_BASE && event.GetId() < wxID_RIG_MODEL_BASE+numRigs) { + int rigIdx = event.GetId()-wxID_RIG_MODEL_BASE; + RigList &rl = RigThread::enumerate(); + rigModel = rl[rigIdx]->rig_model; + if (devInfo != nullptr) { + std::string deviceId = devInfo->getDeviceId(); + DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId); + rigSDRIF = devConfig->getRigIF(rigModel); + if (rigSDRIF) { + wxGetApp().lockFrequency(rigSDRIF); + } else { + wxGetApp().unlockFrequency(); + } + } else { + wxGetApp().unlockFrequency(); + } + resetRig = true; + } + + if (event.GetId() >= wxID_RIG_SERIAL_BASE && event.GetId() < wxID_RIG_SERIAL_BASE+rigSerialRates.size()) { + int serialIdx = event.GetId()-wxID_RIG_SERIAL_BASE; + rigSerialRate = rigSerialRates[serialIdx]; + resetRig = true; + } + + if (event.GetId() == wxID_RIG_PORT) { + wxString stringVal = wxGetTextFromUser("Rig Serial / COM / Address", "Rig Control Port", rigPort); + std::string rigPortStr = stringVal.ToStdString(); + if (rigPortStr != "") { + rigPort = rigPortStr; + resetRig = true; + } + } + + if (event.GetId() == wxID_RIG_TOGGLE) { + resetRig = false; + if (!wxGetApp().rigIsActive()) { + wxGetApp().stopRig(); + wxGetApp().initRig(rigModel, rigPort, rigSerialRate); + + if (devInfo != nullptr) { + std::string deviceId = devInfo->getDeviceId(); + DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId); + rigSDRIF = devConfig->getRigIF(rigModel); + if (rigSDRIF) { + wxGetApp().lockFrequency(rigSDRIF); + } else { + wxGetApp().unlockFrequency(); + } + } else { + wxGetApp().unlockFrequency(); + } + } else { + wxGetApp().stopRig(); + wxGetApp().unlockFrequency(); + } + } + + if (event.GetId() == wxID_RIG_SDR_IF) { + if (devInfo != nullptr) { + std::string deviceId = devInfo->getDeviceId(); + DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId); + long long freqRigIF = wxGetNumberFromUser("Rig SDR-IF Frequency", "Frequency (Hz)", "Frequency", devConfig->getRigIF(rigModel), 0, 2000000000); + if (freqRigIF != -1) { + rigSDRIF = freqRigIF; + devConfig->setRigIF(rigModel, rigSDRIF); + } + if (rigSDRIF && wxGetApp().rigIsActive()) { + wxGetApp().lockFrequency(rigSDRIF); + } else { + wxGetApp().unlockFrequency(); + } + } + } + + if (wxGetApp().rigIsActive() && resetRig) { + wxGetApp().stopRig(); + wxGetApp().initRig(rigModel, rigPort, rigSerialRate); + } +#endif } @@ -837,6 +1007,11 @@ void AppFrame::OnClose(wxCloseEvent& event) { wxGetApp().getConfig()->setCenterFreq(wxGetApp().getFrequency()); wxGetApp().getConfig()->setSpectrumAvgSpeed(wxGetApp().getSpectrumProcessor()->getFFTAverageRate()); wxGetApp().getConfig()->setWaterfallLinesPerSec(waterfallDataThread->getLinesPerSecond()); +#ifdef USE_HAMLIB + wxGetApp().getConfig()->setRigModel(rigModel); + wxGetApp().getConfig()->setRigRate(rigSerialRate); + wxGetApp().getConfig()->setRigPort(rigPort); +#endif wxGetApp().getConfig()->save(); event.Skip(); } @@ -1116,7 +1291,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { if (!this->IsActive()) { std::this_thread::sleep_for(std::chrono::milliseconds(25)); } - + event.RequestMore(); } diff --git a/src/AppFrame.h b/src/AppFrame.h index 5d8f4ad..3ebe09a 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -53,6 +53,14 @@ #define wxID_AUDIO_BANDWIDTH_BASE 9000 #define wxID_AUDIO_DEVICE_MULTIPLIER 50 +#ifdef USE_HAMLIB +#define wxID_RIG_TOGGLE 11900 +#define wxID_RIG_PORT 11901 +#define wxID_RIG_SDR_IF 11902 +#define wxID_RIG_SERIAL_BASE 11950 +#define wxID_RIG_MODEL_BASE 12000 +#endif + // Define a new frame type class AppFrame: public wxFrame { public: @@ -129,5 +137,20 @@ private: std::atomic_bool modemPropertiesUpdated; ModemArgInfoList newModemArgs; +#ifdef USE_HAMLIB + wxMenu *rigMenu; + wxMenuItem *rigEnableMenuItem; + wxMenuItem *rigPortMenuItem; + wxMenuItem *sdrIFMenuItem; + std::map rigSerialMenuItems; + std::map rigModelMenuItems; + int rigModel; + int rigSerialRate; + long long rigSDRIF; + std::vector rigSerialRates; + std::string rigPort; + int numRigs; +#endif + wxDECLARE_EVENT_TABLE(); }; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index ba56b2f..c97638b 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -18,6 +18,10 @@ #include "CoreFoundation/CoreFoundation.h" #endif +#ifdef USE_HAMLIB +#include "RigThread.h" +#endif + IMPLEMENT_APP(CubicSDR) //#ifdef ENABLE_DIGITAL_LAB @@ -164,6 +168,13 @@ bool CubicSDR::OnInit() { wxApp::SetAppName("CubicSDR"); +#ifdef USE_HAMLIB + t_Rig = nullptr; + rigThread = nullptr; + + RigThread::enumerate(); +#endif + Modem::addModemFactory(new ModemFM); Modem::addModemFactory(new ModemFMStereo); Modem::addModemFactory(new ModemAM); @@ -438,6 +449,25 @@ long long CubicSDR::getFrequency() { return frequency; } + +void CubicSDR::lockFrequency(long long freq) { + frequency_locked.store(true); + lock_freq.store(freq); + + if (sdrThread && !sdrThread->isTerminated()) { + sdrThread->lockFrequency(freq); + } +} + +bool CubicSDR::isFrequencyLocked() { + return frequency_locked.load(); +} + +void CubicSDR::unlockFrequency() { + frequency_locked.store(false); + sdrThread->unlockFrequency(); +} + void CubicSDR::setSampleRate(long long rate_in) { sampleRate = rate_in; sdrThread->setSampleRate(sampleRate); @@ -712,3 +742,51 @@ bool CubicSDR::getUseLocalMod() { std::string CubicSDR::getModulePath() { return modulePath; } + +#ifdef USE_HAMLIB +RigThread *CubicSDR::getRigThread() { + return rigThread; +} + +void CubicSDR::initRig(int rigModel, std::string rigPort, int rigSerialRate) { + if (rigThread) { + if (!rigThread->isTerminated()) { + rigThread->terminate(); + } + delete rigThread; + rigThread = nullptr; + } + if (t_Rig && t_Rig->joinable()) { + t_Rig->join(); + delete t_Rig; + t_Rig = nullptr; + } + rigThread = new RigThread(); + rigThread->initRig(rigModel, rigPort, rigSerialRate); + t_Rig = new std::thread(&RigThread::threadMain, rigThread); +} + +void CubicSDR::stopRig() { + if (!rigThread) { + return; + } + + if (rigThread) { + if (!rigThread->isTerminated()) { + rigThread->terminate(); + } + delete rigThread; + rigThread = nullptr; + } + if (t_Rig && t_Rig->joinable()) { + t_Rig->join(); + delete t_Rig; + t_Rig = nullptr; + } +} + +bool CubicSDR::rigIsActive() { + return (rigThread && !rigThread->isTerminated()); +} + +#endif \ No newline at end of file diff --git a/src/CubicSDR.h b/src/CubicSDR.h index 5895f6b..abace68 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -54,6 +54,10 @@ #include "ModemST.h" #endif +#ifdef USE_HAMLIB +class RigThread; +#endif + #include #define NUM_DEMODULATORS 1 @@ -80,6 +84,10 @@ public: void setFrequency(long long freq); long long getFrequency(); + + void lockFrequency(long long freq); + bool isFrequencyLocked(); + void unlockFrequency(); void setOffset(long long ofs); long long getOffset(); @@ -142,6 +150,13 @@ public: bool getUseLocalMod(); std::string getModulePath(); +#ifdef USE_HAMLIB + RigThread *getRigThread(); + void initRig(int rigModel, std::string rigPort, int rigSerialRate); + void stopRig(); + bool rigIsActive(); +#endif + private: AppFrame *appframe; AppConfig config; @@ -184,6 +199,12 @@ private: std::string notifyMessage; std::string modulePath; std::mutex notify_busy; + std::atomic_bool frequency_locked; + std::atomic_llong lock_freq; +#ifdef USE_HAMLIB + RigThread *rigThread; + std::thread *t_Rig; +#endif }; #ifdef BUNDLE_SOAPY_MODS diff --git a/src/rig/RigThread.cpp b/src/rig/RigThread.cpp new file mode 100644 index 0000000..aa0bd7c --- /dev/null +++ b/src/rig/RigThread.cpp @@ -0,0 +1,103 @@ +#include "RigThread.h" + +std::vector RigThread::rigCaps; + +RigThread::RigThread() { + terminated.store(true); + freq = wxGetApp().getFrequency(); +} + +RigThread::~RigThread() { + +} + +RigList &RigThread::enumerate() { + if (RigThread::rigCaps.empty()) { + rig_set_debug(RIG_DEBUG_ERR); + rig_load_all_backends(); + + rig_list_foreach(RigThread::add_hamlib_rig, 0); + std::sort(RigThread::rigCaps.begin(), RigThread::rigCaps.end(), rigGreater()); + std::cout << "Loaded " << RigThread::rigCaps.size() << " rig models via hamlib." << std::endl; + } + return RigThread::rigCaps; +} + +int RigThread::add_hamlib_rig(const struct rig_caps *rc, void* f) +{ + rigCaps.push_back(rc); + return 1; +} + +void RigThread::initRig(rig_model_t rig_model, std::string rig_file, int serial_rate) { + rigModel = rig_model; + rigFile = rig_file; + serialRate = serial_rate; +}; + +void RigThread::run() { + int retcode, status; + + std::cout << "Rig thread starting." << std::endl; + + rig = rig_init(rigModel); + strncpy(rig->state.rigport.pathname, rigFile.c_str(), FILPATHLEN - 1); + rig->state.rigport.parm.serial.rate = serialRate; + retcode = rig_open(rig); + + if (retcode != 0) { + std::cout << "Rig failed to init. " << std::endl; + terminated.store(true); + return; + } + + char *info_buf = (char *)rig_get_info(rig); + std::cout << "Rig info: " << info_buf << std::endl; + + while (!terminated.load()) { + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + if (freqChanged.load()) { + status = rig_get_freq(rig, RIG_VFO_CURR, &freq); + if (freq != newFreq) { + freq = newFreq; + rig_set_freq(rig, RIG_VFO_CURR, freq); +// std::cout << "Set Rig Freq: %f" << newFreq << std::endl; + } + + freqChanged.store(false); + } else { + freq_t checkFreq; + + status = rig_get_freq(rig, RIG_VFO_CURR, &checkFreq); + + if (checkFreq != freq) { + freq = checkFreq; + wxGetApp().setFrequency((long long)checkFreq); + } else if (wxGetApp().getFrequency() != freq) { + freq = wxGetApp().getFrequency(); + rig_set_freq(rig, RIG_VFO_CURR, freq); + } + } + +// std::cout << "Rig Freq: " << freq << std::endl; + } + + rig_close(rig); + rig_cleanup(rig); + + std::cout << "Rig thread exiting." << std::endl; +}; + +freq_t RigThread::getFrequency() { + if (freqChanged.load()) { + return newFreq; + } else { + return freq; + } +} + +void RigThread::setFrequency(freq_t new_freq) { + newFreq = new_freq; + freqChanged.store(true); +} + diff --git a/src/rig/RigThread.h b/src/rig/RigThread.h new file mode 100644 index 0000000..9d6d80d --- /dev/null +++ b/src/rig/RigThread.h @@ -0,0 +1,43 @@ +#pragma once + +#include "IOThread.h" +#include "CubicSDR.h" +#include +#include + +struct rigGreater +{ + bool operator()( const struct rig_caps *lx, const struct rig_caps *rx ) const { + std::string ln(std::string(std::string(lx->mfg_name) + " " + std::string(lx->model_name))); + std::string rn(std::string(std::string(rx->mfg_name) + " " + std::string(rx->model_name))); + return ln.compare(rn)<0; + } +}; + +typedef std::vector RigList; + +class RigThread : public IOThread { +public: + RigThread(); + ~RigThread(); + + void initRig(rig_model_t rig_model, std::string rig_file, int serial_rate); + void run(); + + freq_t getFrequency(); + void setFrequency(freq_t new_freq); + + static RigList &enumerate(); + static int add_hamlib_rig(const struct rig_caps *rc, void* f); + +private: + RIG *rig; + rig_model_t rigModel; + std::string rigFile; + int serialRate; + + freq_t freq; + freq_t newFreq; + std::atomic_bool freqChanged; + static RigList rigCaps; +}; \ No newline at end of file diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index 08410ca..3e8ad32 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -33,6 +33,9 @@ SDRThread::SDRThread() : IOThread(), buffers("SDRThreadBuffers") { agc_mode_changed.store(false); gain_value_changed.store(false); setting_value_changed.store(false); + frequency_lock_init.store(false); + frequency_locked.store(false); + lock_freq.store(0); } SDRThread::~SDRThread() { @@ -241,7 +244,12 @@ void SDRThread::updateSettings() { } if (freq_changed.load()) { - device->setFrequency(SOAPY_SDR_RX,0,"RF",frequency.load() - offset.load()); + if (frequency_locked.load() && !frequency_lock_init.load()) { + device->setFrequency(SOAPY_SDR_RX,0,"RF",lock_freq.load()); + frequency_lock_init.store(true); + } else if (!frequency_locked.load()) { + device->setFrequency(SOAPY_SDR_RX,0,"RF",frequency.load() - offset.load()); + } freq_changed.store(false); } @@ -372,6 +380,23 @@ long long SDRThread::getFrequency() { return frequency.load(); } +void SDRThread::lockFrequency(long long freq) { + lock_freq.store(freq); + frequency_locked.store(true); + frequency_lock_init.store(false); + setFrequency(freq); +} + +bool SDRThread::isFrequencyLocked() { + return frequency_locked.load(); +} + +void SDRThread::unlockFrequency() { + frequency_locked.store(false); + frequency_lock_init.store(false); + freq_changed.store(true); +} + void SDRThread::setOffset(long long ofs) { offset.store(ofs); offset_changed.store(true); diff --git a/src/sdr/SoapySDRThread.h b/src/sdr/SoapySDRThread.h index 4c21103..978920e 100644 --- a/src/sdr/SoapySDRThread.h +++ b/src/sdr/SoapySDRThread.h @@ -60,6 +60,10 @@ public: void setFrequency(long long freq); long long getFrequency(); + void lockFrequency(long long freq); + bool isFrequencyLocked(); + void unlockFrequency(); + void setOffset(long long ofs); long long getOffset(); @@ -98,11 +102,11 @@ protected: std::map settingChanged; std::atomic sampleRate; - std::atomic_llong frequency, offset; + std::atomic_llong frequency, offset, lock_freq; std::atomic_int ppm, numElems, numChannels; std::atomic_bool hasPPM, hasHardwareDC; std::atomic_bool agc_mode, rate_changed, freq_changed, offset_changed, - ppm_changed, device_changed, agc_mode_changed, gain_value_changed, setting_value_changed; + ppm_changed, device_changed, agc_mode_changed, gain_value_changed, setting_value_changed, frequency_locked, frequency_lock_init; std::mutex gain_busy; std::map gainValues;