// Copyright (c) Charles J. Cliffe // SPDX-License-Identifier: GPL-2.0+ #define OPENGL #include "CubicSDRDefs.h" #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if !wxUSE_GLCANVAS #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" #endif #include "CubicSDR.h" #include #ifdef _OSX_APP_ #include "CoreFoundation/CoreFoundation.h" #endif #ifdef USE_HAMLIB #include "RigThread.h" #endif IMPLEMENT_APP(CubicSDR) #include #include #include "ActionDialog.h" #include //#ifdef ENABLE_DIGITAL_LAB //// console output buffer for windows //#ifdef _WINDOWS //class outbuf : public std::streambuf { // public: // outbuf() { // setp(0, 0); // } // virtual int_type overflow(int_type c = traits_type::eof()) { // return fputc(c, stdout) == EOF ? traits_type::eof() : c; // } //}; //#endif //#endif #ifdef MINGW_PATCH FILE _iob[] = { *stdin, *stdout, *stderr }; extern "C" FILE * __cdecl __iob_func(void) { return _iob; } extern "C" int __cdecl __isnan(double x) { return _finite(x)?0:1; } extern "C" int __cdecl __isnanf(float x) { return _finitef(x)?0:1; } #endif std::string& filterChars(std::string& s, const std::string& allowed) { s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) { return allowed.find(c) == std::string::npos; }), s.end()); return s; } std::string frequencyToStr(long long freq) { long double freqTemp; freqTemp = freq; std::string suffix; std::stringstream freqStr; if (freqTemp >= 1.0e9) { freqTemp /= 1.0e9; freqStr << std::setprecision(10); suffix = std::string("GHz"); } else if (freqTemp >= 1.0e6) { freqTemp /= 1.0e6; freqStr << std::setprecision(7); suffix = std::string("MHz"); } else if (freqTemp >= 1.0e3) { freqTemp /= 1.0e3; freqStr << std::setprecision(4); suffix = std::string("KHz"); } freqStr << freqTemp; freqStr << suffix; return freqStr.str(); } long long strToFrequency(std::string freqStr) { std::string filterStr = filterChars(freqStr, std::string("0123456789.MKGHmkgh")); size_t numLen = filterStr.find_first_not_of("0123456789."); if (numLen == std::string::npos) { numLen = freqStr.length(); } std::string numPartStr = freqStr.substr(0, numLen); std::string suffixStr = freqStr.substr(numLen); std::stringstream numPartStream; numPartStream.str(numPartStr); long double freqTemp = 0; numPartStream >> freqTemp; if (suffixStr.length()) { if (suffixStr.find_first_of("Gg") != std::string::npos) { freqTemp *= 1.0e9; } else if (suffixStr.find_first_of("Mm") != std::string::npos) { freqTemp *= 1.0e6; } else if (suffixStr.find_first_of("Kk") != std::string::npos) { freqTemp *= 1.0e3; } else if (suffixStr.find_first_of("Hh") != std::string::npos) { // ... } } else if (numPartStr.find_first_of('.') != std::string::npos || freqTemp <= 3000) { freqTemp *= 1.0e6; } return (long long) freqTemp; } class ActionDialogBookmarkCatastophe : public ActionDialog { public: ActionDialogBookmarkCatastophe() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Last-Loaded Backup Failure :( :( :(")) { m_questionText->SetLabelText(wxT("All attempts to recover bookmarks have failed. \nWould you like to exit without touching any more save files?\nClick OK to exit without saving; or Cancel to continue anyways.")); } void doClickOK() override { wxGetApp().getAppFrame()->disableSave(true); wxGetApp().getAppFrame()->Close(false); } }; class ActionDialogBookmarkBackupLoadFailed : public ActionDialog { public: ActionDialogBookmarkBackupLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Backup Load Failure :( :(")) { m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks backup file. \nWould you like to attempt to load the last succssfully loaded bookmarks file?")); } void doClickOK() override { if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { if (wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.lastloaded",false)) { wxGetApp().getBookmarkMgr().updateBookmarks(); wxGetApp().getBookmarkMgr().updateActiveList(); } else { ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); } } } }; class ActionDialogBookmarkLoadFailed : public ActionDialog { public: ActionDialogBookmarkLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Load Failure :(")) { m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks file. \nWould you like to attempt to load the backup file?")); } void doClickOK() override { bool loadOk = false; if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) { loadOk = wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.backup",false); } if (loadOk) { wxGetApp().getBookmarkMgr().updateBookmarks(); wxGetApp().getBookmarkMgr().updateActiveList(); } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed()); } else { ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); } } }; class ActionDialogRigError : public ActionDialog { public: explicit ActionDialogRigError(const std::string& message) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Rig Control Error")) { m_questionText->SetLabelText(message); } void doClickOK() override { } }; CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), agcMode(false) { config.load(); sampleRateInitialized.store(false); agcMode.store(true); soloMode.store(false); shuttingDown.store(false); fdlgTarget = FrequencyDialog::FDIALOG_TARGET_DEFAULT; stoppedDev = nullptr; //set OpenGL configuration: m_glContextAttributes = new wxGLContextAttrs(); wxGLContextAttrs glSettings; glSettings.CompatibilityProfile().EndList(); *m_glContextAttributes = glSettings; } void CubicSDR::initAudioDevices() const { std::vector devices; std::map inputDevices, outputDevices; AudioThread::enumerateDevices(devices); int i = 0; for (auto & device : devices) { if (device.inputChannels) { inputDevices[i] = device; } if (device.outputChannels) { outputDevices[i] = device; } i++; } wxGetApp().getDemodMgr().setOutputDevices(outputDevices); } bool CubicSDR::OnInit() { //use the current locale most appropriate to this system, //so that character-related functions are likely to handle Unicode //better (by default, was "C" locale). std::setlocale(LC_ALL, ""); //#ifdef _OSX_APP_ // CFBundleRef mainBundle = CFBundleGetMainBundle(); // CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); // char path[PATH_MAX]; // if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX)) // { // // error! // } // CFRelease(resourcesURL); // chdir(path); //#endif if (!wxApp::OnInit()) { return false; } //Deactivated code to allocate an explicit Console on Windows. //This tends to hang the application on heavy demod (re)creation. //To continue to debug with std::cout traces, simply run CubicSDR in a MINSYS2 compatble shell on Windows: //ex: Cygwin shell, Git For Windows Bash shell.... #if (0) if (AllocConsole()) { freopen("CONOUT$", "w", stdout); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); SetConsoleTitle(L"CubicSDR: stdout"); } //refresh ofstream ob; std::streambuf *sb = std::cout.rdbuf(); std::cout.rdbuf(sb); #endif wxApp::SetAppName(CUBICSDR_INSTALL_NAME); #ifdef USE_HAMLIB t_Rig = nullptr; rigThread = nullptr; RigThread::enumerate(); #endif Modem::addModemFactory(ModemFM::factory, "FM", 200000); Modem::addModemFactory(ModemNBFM::factory, "NBFM", 12500); Modem::addModemFactory(ModemFMStereo::factory, "FMS", 200000); Modem::addModemFactory(ModemAM::factory, "AM", 6000); Modem::addModemFactory(ModemCW::factory, "CW", 500); Modem::addModemFactory(ModemLSB::factory, "LSB", 5400); Modem::addModemFactory(ModemUSB::factory, "USB", 5400); Modem::addModemFactory(ModemDSB::factory, "DSB", 5400); Modem::addModemFactory(ModemIQ::factory, "I/Q", 48000); #ifdef ENABLE_DIGITAL_LAB Modem::addModemFactory(ModemAPSK::factory, "APSK", 200000); Modem::addModemFactory(ModemASK::factory, "ASK", 200000); Modem::addModemFactory(ModemBPSK::factory, "BPSK", 200000); Modem::addModemFactory(ModemDPSK::factory, "DPSK", 200000); Modem::addModemFactory(ModemFSK::factory, "FSK", 19200); Modem::addModemFactory(ModemGMSK::factory, "GMSK", 19200); Modem::addModemFactory(ModemOOK::factory, "OOK", 200000); Modem::addModemFactory(ModemPSK::factory, "PSK", 200000); Modem::addModemFactory(ModemQAM::factory, "QAM", 200000); Modem::addModemFactory(ModemQPSK::factory, "QPSK", 200000); Modem::addModemFactory(ModemSQAM::factory, "SQAM", 200000); Modem::addModemFactory(ModemST::factory, "ST", 200000); #endif frequency = wxGetApp().getConfig()->getCenterFreq(); offset = 0; ppm = 0; devicesReady.store(false); devicesFailed.store(false); deviceSelectorOpen.store(false); initAudioDevices(); // Visual Data spectrumVisualThread = new SpectrumVisualDataThread(); pipeIQVisualData = std::make_shared(); pipeIQVisualData->set_max_num_items(1); pipeWaterfallIQVisualData = std::make_shared(); pipeWaterfallIQVisualData->set_max_num_items(128); getSpectrumProcessor()->setInput(pipeIQVisualData); getSpectrumProcessor()->setHideDC(true); // I/Q Data pipeSDRIQData = std::make_shared(); pipeSDRIQData->set_max_num_items(100); sdrThread = new SDRThread(); sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData); sdrPostThread = new SDRPostThread(); sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData); sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData); sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData); #if CUBICSDR_ENABLE_VIEW_SCOPE pipeAudioVisualData = std::make_shared(); pipeAudioVisualData->set_max_num_items(1); scopeProcessor.setInput(pipeAudioVisualData); #else pipeAudioVisualData = nullptr; #endif #if CUBICSDR_ENABLE_VIEW_DEMOD demodVisualThread = new SpectrumVisualDataThread(); pipeDemodIQVisualData = std::make_shared(); pipeDemodIQVisualData->set_max_num_items(1); if (getDemodSpectrumProcessor()) { getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData); } sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData); #else demodVisualThread = nullptr; pipeDemodIQVisualData = nullptr; t_DemodVisual = nullptr; #endif // Now that input/output queue plumbing is completely done, we can //safely starts all the threads: t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread); if (demodVisualThread != nullptr) { t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread); } //Start SDRPostThread last. t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread); sdrEnum = new SDREnumerator(); SDREnumerator::setManuals(config.getManualDevices()); appframe = new AppFrame(); t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum); //#ifdef __APPLE__ // int main_policy; // struct sched_param main_param; // // main_policy = SCHED_RR; // main_param.sched_priority = sched_get_priority_min(SCHED_RR)+2; // // pthread_setschedparam(pthread_self(), main_policy, &main_param); //#endif if (!wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml")) { if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) { ActionDialog::showDialog(new ActionDialogBookmarkLoadFailed()); } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) { ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed()); } else { ActionDialog::showDialog(new ActionDialogBookmarkCatastophe()); } } else { getBookmarkMgr().updateActiveList(); getBookmarkMgr().updateBookmarks(); } return true; } int CubicSDR::OnExit() { shuttingDown.store(true); #if USE_HAMLIB if (rigIsActive()) { std::cout << "Terminating Rig thread.." << std::endl << std::flush; stopRig(); } #endif bool terminationSequenceOK = true; //The thread feeding them all should be terminated first, so: std::cout << "Terminating SDR thread.." << std::endl << std::flush ; sdrThread->terminate(); terminationSequenceOK = terminationSequenceOK && sdrThread->isTerminated(3000); //in case termination sequence goes wrong, kill App brutally now because it can get stuck. if (!terminationSequenceOK) { //no trace here because it could occur if the device is not started. ::exit(11); } std::cout << "Terminating SDR post-processing thread.." << std::endl << std::flush; sdrPostThread->terminate(); //Wait for termination for sdrPostThread second:: since it is doing //mostly blocking push() to the other threads, they must stay alive //so that sdrPostThread can complete a processing loop and die. terminationSequenceOK = terminationSequenceOK && sdrPostThread->isTerminated(3000); //in case termination sequence goes wrong, kill App brutally now because it can get stuck. if (!terminationSequenceOK) { std::cout << "Cannot terminate application properly, calling exit() now." << std::endl << std::flush; ::exit(12); } std::cout << "Terminating All Demodulators.." << std::endl << std::flush; demodMgr.terminateAll(); std::cout << "Terminating Visual Processor threads.." << std::endl << std::flush; spectrumVisualThread->terminate(); if (demodVisualThread) { demodVisualThread->terminate(); } //Wait nicely terminationSequenceOK = terminationSequenceOK && spectrumVisualThread->isTerminated(1000); if (demodVisualThread) { terminationSequenceOK = terminationSequenceOK && demodVisualThread->isTerminated(1000); } //in case termination sequence goes wrong, kill App brutally because it can get stuck. if (!terminationSequenceOK) { std::cout << "Cannot terminate application properly, calling exit() now." << std::endl << std::flush; ::exit(13); } //Then join the thread themselves: if (t_SDR) { t_SDR->join(); } t_PostSDR->join(); if (t_DemodVisual) { t_DemodVisual->join(); } t_SpectrumVisual->join(); //Now only we can delete: delete t_SDR; t_SDR = nullptr; delete sdrThread; sdrThread = nullptr; delete sdrPostThread; sdrPostThread = nullptr; delete t_PostSDR; t_PostSDR = nullptr; delete t_SpectrumVisual; t_SpectrumVisual = nullptr; delete spectrumVisualThread; spectrumVisualThread = nullptr; delete t_DemodVisual; t_DemodVisual = nullptr; delete demodVisualThread; demodVisualThread = nullptr; delete m_glContext; m_glContext = nullptr; // AudioThread::deviceCleanup(); std::cout << "Application termination complete." << std::endl << std::flush; return wxApp::OnExit(); } PrimaryGLContext& CubicSDR::GetContext(wxGLCanvas *canvas) { PrimaryGLContext *glContext; if (!m_glContext) { m_glContext = new PrimaryGLContext(canvas, nullptr, GetContextAttributes()); } glContext = m_glContext; return *glContext; } wxGLContextAttrs* CubicSDR::GetContextAttributes() { return m_glContextAttributes; } void CubicSDR::OnInitCmdLine(wxCmdLineParser& parser) { parser.SetDesc (commandLineInfo); parser.SetSwitchChars (wxT("-")); } bool CubicSDR::OnCmdLineParsed(wxCmdLineParser& parser) { auto *confName = new wxString; if (parser.Found("c",confName)) { if (!confName->empty()) { config.setConfigName(confName->ToStdString()); } } #ifdef BUNDLE_SOAPY_MODS if (parser.Found("b")) { useLocalMod.store(false); } else { useLocalMod.store(true); } #else useLocalMod.store(true); #endif auto *modPath = new wxString; if (parser.Found("m",modPath)) { if (!modPath->empty()) { modulePath = modPath->ToStdString(); } else { modulePath = ""; } } return true; } void CubicSDR::closeDeviceSelector() { if (deviceSelectorOpen) { deviceSelectorDialog->Close(); } } void CubicSDR::deviceSelector() { if (deviceSelectorOpen) { deviceSelectorDialog->Raise(); deviceSelectorDialog->SetFocus(); return; } deviceSelectorOpen.store(true); wxRect *winRect = getConfig()->getWindow(); wxPoint pos(wxDefaultPosition); if (winRect != nullptr) { pos = wxPoint(winRect->x, winRect->y); } deviceSelectorDialog = new SDRDevicesDialog(appframe, pos); deviceSelectorDialog->Show(); } void CubicSDR::addRemote(const std::string& remoteAddr) { SDREnumerator::addRemote(remoteAddr); devicesReady.store(false); t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum); } void CubicSDR::removeRemote(const std::string& remoteAddr) { SDREnumerator::removeRemote(remoteAddr); } void CubicSDR::sdrThreadNotify(SDRThread::SDRThreadState state, const std::string& message) { std::lock_guard < std::mutex > lock(notify_busy); if (state == SDRThread::SDR_THREAD_INITIALIZED) { appframe->initDeviceParams(getDevice()); } if (state == SDRThread::SDR_THREAD_MESSAGE) { notifyMessage = message; } if (state == SDRThread::SDR_THREAD_FAILED) { notifyMessage = message; // wxMessageDialog *info; // info = new wxMessageDialog(NULL, message, wxT("Error initializing device"), wxOK | wxICON_ERROR); // info->ShowModal(); } //if (appframe) { appframe->SetStatusText(message); } } void CubicSDR::sdrEnumThreadNotify(SDREnumerator::SDREnumState state, std::string message) { std::lock_guard < std::mutex > lock(notify_busy); if (state == SDREnumerator::SDR_ENUM_MESSAGE) { notifyMessage = message; } if (state == SDREnumerator::SDR_ENUM_DEVICES_READY) { devs = SDREnumerator::enumerate_devices("", true); devicesReady.store(true); } if (state == SDREnumerator::SDR_ENUM_FAILED) { devicesFailed.store(true); } //if (appframe) { appframe->SetStatusText(message); } } void CubicSDR::setFrequency(long long freq) { if (freq < sampleRate / 2) { freq = sampleRate / 2; } frequency = freq; sdrThread->setFrequency(freq); getSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold()); //make the peak hold act on the current dmod also, like a zoomed-in version. if (getDemodSpectrumProcessor()) { getDemodSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold()); } } long long CubicSDR::getOffset() { return offset; } void CubicSDR::setOffset(long long ofs) { offset = ofs; if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setOffset(offset); } } void CubicSDR::setAntennaName(const std::string& name) { antennaName = name; if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setAntenna(antennaName); } } const std::string& CubicSDR::getAntennaName() { return antennaName; } void CubicSDR::setChannelizerType(SDRPostThreadChannelizerType chType) { if (sdrPostThread && !sdrPostThread->isTerminated()) { sdrPostThread->setChannelizerType(chType); } } SDRPostThreadChannelizerType CubicSDR::getChannelizerType() { if (sdrPostThread && !sdrPostThread->isTerminated()) { return sdrPostThread->getChannelizerType(); } return SDRPostThreadChannelizerType::SDRPostPFBCH; } 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); if (sdrThread && !sdrThread->isTerminated()) { sdrThread->unlockFrequency(); } } void CubicSDR::setSampleRate(long long rate_in) { sampleRate = rate_in; if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setSampleRate(sampleRate); } setFrequency(frequency); if (rate_in <= CHANNELIZER_RATE_MAX / 8) { appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE / 4); appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false); spectrumVisualThread->getProcessor()->setHideDC(false); } else if (rate_in <= CHANNELIZER_RATE_MAX) { appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE / 2); appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false); spectrumVisualThread->getProcessor()->setHideDC(false); } else if (rate_in > CHANNELIZER_RATE_MAX) { appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE); appframe->getWaterfallDataThread()->getProcessor()->setHideDC(true); spectrumVisualThread->getProcessor()->setHideDC(true); } } void CubicSDR::stopDevice(bool store, int waitMsForTermination) { //First we must stop the threads sdrThread->terminate(); sdrThread->isTerminated(waitMsForTermination); if (t_SDR) { t_SDR->join(); delete t_SDR; t_SDR = nullptr; } //Only now we can nullify devices if (store) { stoppedDev = sdrThread->getDevice(); } else { stoppedDev = nullptr; } sdrThread->setDevice(nullptr); } void CubicSDR::reEnumerateDevices() { devicesReady.store(false); devs = nullptr; SDREnumerator::reset(); t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum); } void CubicSDR::setDevice(SDRDeviceInfo *dev, int waitMsForTermination) { sdrThread->terminate(); sdrThread->isTerminated(waitMsForTermination); if (t_SDR) { t_SDR->join(); delete t_SDR; t_SDR = nullptr; } for (SoapySDR::Kwargs::const_iterator i = settingArgs.begin(); i != settingArgs.end(); i++) { sdrThread->writeSetting(i->first, i->second); } sdrThread->setStreamArgs(streamArgs); sdrThread->setDevice(dev); DeviceConfig *devConfig = config.getDevice(dev->getDeviceId()); SoapySDR::Device *soapyDev = dev->getSoapyDevice(); if (soapyDev) { if (long devSampleRate = devConfig->getSampleRate()) { sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, devSampleRate); sampleRateInitialized.store(true); } if (!sampleRateInitialized.load()) { sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, DEFAULT_SAMPLE_RATE); sampleRateInitialized.store(true); } else { sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, sampleRate); } if (frequency < sampleRate/2) { frequency = sampleRate/2; } setFrequency(frequency); setSampleRate(sampleRate); setPPM(devConfig->getPPM()); setOffset(devConfig->getOffset()); setAGCMode(devConfig->getAGCMode()); setAntennaName(devConfig->getAntennaName()); t_SDR = new std::thread(&SDRThread::threadMain, sdrThread); } stoppedDev = nullptr; } SDRDeviceInfo *CubicSDR::getDevice() { if (!sdrThread->getDevice() && stoppedDev) { return stoppedDev; } return sdrThread->getDevice(); } ScopeVisualProcessor *CubicSDR::getScopeProcessor() { return &scopeProcessor; } SpectrumVisualProcessor *CubicSDR::getSpectrumProcessor() { return spectrumVisualThread->getProcessor(); } SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() { if (demodVisualThread) { return demodVisualThread->getProcessor(); } else { return nullptr; } } DemodulatorThreadOutputQueuePtr CubicSDR::getAudioVisualQueue() { return pipeAudioVisualData; } DemodulatorThreadInputQueuePtr CubicSDR::getIQVisualQueue() { return pipeIQVisualData; } DemodulatorThreadInputQueuePtr CubicSDR::getWaterfallVisualQueue() { return pipeWaterfallIQVisualData; } BookmarkMgr &CubicSDR::getBookmarkMgr() { return bookmarkMgr; } DemodulatorMgr &CubicSDR::getDemodMgr() { return demodMgr; } SessionMgr &CubicSDR::getSessionMgr() { return sessionMgr; } SDRPostThread *CubicSDR::getSDRPostThread() { return sdrPostThread; } SDRThread *CubicSDR::getSDRThread() { return sdrThread; } void CubicSDR::notifyDemodulatorsChanged() { sdrPostThread->notifyDemodulatorsChanged(); } long long CubicSDR::getSampleRate() { return sampleRate; } void CubicSDR::removeDemodulator(const DemodulatorInstancePtr& demod) { if (!demod) { return; } demod->setActive(false); sdrPostThread->notifyDemodulatorsChanged(); wxGetApp().getAppFrame()->notifyUpdateModemProperties(); } std::vector* CubicSDR::getDevices() { return devs; } AppConfig *CubicSDR::getConfig() { return &config; } void CubicSDR::saveConfig() { config.save(); } void CubicSDR::setPPM(int ppm_in) { ppm = ppm_in; if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setPPM(ppm); } } int CubicSDR::getPPM() { SDRDeviceInfo *dev = sdrThread->getDevice(); if (dev) { ppm = config.getDevice(dev->getDeviceId())->getPPM(); } return ppm; } void CubicSDR::showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode, const wxString& initString) { const wxString demodTitle("Set Demodulator Frequency"); const wxString freqTitle("Set Center Frequency"); const wxString bwTitle("Modem Bandwidth (150Hz - 500KHz)"); const wxString lpsTitle("Lines-Per-Second (1-1024)"); const wxString avgTitle("Average Rate (0.1 - 0.99)"); const wxString gainTitle("Gain Entry: "+wxGetApp().getActiveGainEntry()); wxString title; auto activeModem = demodMgr.getActiveContextModem(); switch (targetMode) { case FrequencyDialog::FDIALOG_TARGET_DEFAULT: case FrequencyDialog::FDIALOG_TARGET_FREQ: title = activeModem ?demodTitle:freqTitle; break; case FrequencyDialog::FDIALOG_TARGET_BANDWIDTH: title = bwTitle; break; case FrequencyDialog::FDIALOG_TARGET_WATERFALL_LPS: title = lpsTitle; break; case FrequencyDialog::FDIALOG_TARGET_SPECTRUM_AVG: title = avgTitle; break; case FrequencyDialog::FDIALOG_TARGET_GAIN: title = gainTitle; if (wxGetApp().getActiveGainEntry().empty()) { return; } break; default: break; } FrequencyDialog fdialog(appframe, -1, title, activeModem, wxPoint(-100,-100), wxSize(350, 75), wxDEFAULT_DIALOG_STYLE, targetMode, initString); fdialog.ShowModal(); } void CubicSDR::showLabelInput() { DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveContextModem(); if (activeDemod != nullptr) { const wxString demodTitle("Edit Demodulator label"); DemodLabelDialog labelDialog(appframe, -1, demodTitle, activeDemod, wxPoint(-100, -100), wxSize(500, 75), wxDEFAULT_DIALOG_STYLE); labelDialog.ShowModal(); } } AppFrame *CubicSDR::getAppFrame() { return appframe; } void CubicSDR::setFrequencySnap(int snap_in) { if (snap_in > 1000000) { snap_in = 1000000; } this->snap = snap_in; } int CubicSDR::getFrequencySnap() { return snap; } bool CubicSDR::areDevicesReady() { return devicesReady.load(); } void CubicSDR::notifyMainUIOfDeviceChange(bool forceRefreshOfGains) { appframe->notifyDeviceChanged(); if (forceRefreshOfGains) { appframe->refreshGainUI(); } } bool CubicSDR::areDevicesEnumerating() { return !sdrEnum->isTerminated(); } bool CubicSDR::areModulesMissing() { return devicesFailed.load(); } std::string CubicSDR::getNotification() { std::string msg; std::lock_guard < std::mutex > lock(notify_busy); msg = notifyMessage; return msg; } void CubicSDR::setDeviceSelectorClosed() { deviceSelectorOpen.store(false); } bool CubicSDR::isDeviceSelectorOpen() { return deviceSelectorOpen.load(); } void CubicSDR::setAGCMode(bool mode) { agcMode.store(mode); if (sdrThread && !sdrThread->isTerminated()) { sdrThread->setAGCMode(mode); } } bool CubicSDR::getAGCMode() { return agcMode.load(); } void CubicSDR::setGain(const std::string& name, float gain_in) { sdrThread->setGain(name,gain_in); } float CubicSDR::getGain(const std::string& name) { return sdrThread->getGain(name); } void CubicSDR::setStreamArgs(SoapySDR::Kwargs streamArgs_in) { streamArgs = streamArgs_in; } void CubicSDR::setDeviceArgs(SoapySDR::Kwargs settingArgs_in) { settingArgs = settingArgs_in; } bool CubicSDR::getUseLocalMod() { return useLocalMod.load(); } std::string CubicSDR::getModulePath() { return modulePath; } void CubicSDR::setActiveGainEntry(std::string gainName) { activeGain = gainName; } std::string CubicSDR::getActiveGainEntry() { return activeGain; } void CubicSDR::setSoloMode(bool solo) { soloMode.store(solo); } bool CubicSDR::getSoloMode() { return soloMode.load(); } bool CubicSDR::isShuttingDown() { return shuttingDown.load(); } int CubicSDR::FilterEvent(wxEvent& event) { if (!appframe) { return -1; } if (event.GetEventType() == wxEVT_KEY_DOWN || event.GetEventType() == wxEVT_CHAR_HOOK) { return appframe->OnGlobalKeyDown((wxKeyEvent&)event); } if (event.GetEventType() == wxEVT_KEY_UP || event.GetEventType() == wxEVT_CHAR_HOOK) { return appframe->OnGlobalKeyUp((wxKeyEvent&)event); } return -1; // process normally } #ifdef USE_HAMLIB RigThread *CubicSDR::getRigThread() { return rigThread; } void CubicSDR::initRig(int rigModel, std::string rigPort, int rigSerialRate) { if (rigThread) { rigThread->terminate(); rigThread->isTerminated(1000); } if (t_Rig && t_Rig->joinable()) { t_Rig->join(); } //now we can delete if (rigThread) { delete rigThread; rigThread = nullptr; } if (t_Rig) { delete t_Rig; t_Rig = nullptr; } rigThread = new RigThread(); rigThread->initRig(rigModel, rigPort, rigSerialRate); rigThread->setControlMode(wxGetApp().getConfig()->getRigControlMode()); rigThread->setFollowMode(wxGetApp().getConfig()->getRigFollowMode()); rigThread->setCenterLock(wxGetApp().getConfig()->getRigCenterLock()); rigThread->setFollowModem(wxGetApp().getConfig()->getRigFollowModem()); t_Rig = new std::thread(&RigThread::threadMain, rigThread); } void CubicSDR::stopRig() { if (!rigThread) { return; } if (rigThread) { rigThread->terminate(); rigThread->isTerminated(1000); if (rigThread->getErrorState()) { ActionDialog::showDialog(new ActionDialogRigError(rigThread->getErrorMessage())); } } if (t_Rig && t_Rig->joinable()) { t_Rig->join(); } //now we can delete if (rigThread) { delete rigThread; rigThread = nullptr; } if (t_Rig) { delete t_Rig; t_Rig = nullptr; } } bool CubicSDR::rigIsActive() { return (rigThread && !rigThread->isTerminated()); } #endif