1 // Copyright (c) Charles J. Cliffe
2 // SPDX-License-Identifier: GPL-2.0+
3 
4 #define OPENGL
5 
6 #include "CubicSDRDefs.h"
7 #include "wx/wxprec.h"
8 
9 #ifndef WX_PRECOMP
10 #include "wx/wx.h"
11 #endif
12 
13 #if !wxUSE_GLCANVAS
14 #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library"
15 #endif
16 
17 #include "CubicSDR.h"
18 #include <iomanip>
19 
20 #ifdef _OSX_APP_
21 #include "CoreFoundation/CoreFoundation.h"
22 #endif
23 
24 #ifdef USE_HAMLIB
25 #include "RigThread.h"
26 #endif
27 
28 IMPLEMENT_APP(CubicSDR)
29 
30 #include <fstream>
31 #include <clocale>
32 
33 #include "ActionDialog.h"
34 
35 #include <memory>
36 
37 
38 //#ifdef ENABLE_DIGITAL_LAB
39 //// console output buffer for windows
40 //#ifdef _WINDOWS
41 //class outbuf : public std::streambuf {
42 //	public:
43 //	outbuf() {
44 //		setp(0, 0);
45 //	}
46 //	virtual int_type overflow(int_type c = traits_type::eof()) {
47 //		return fputc(c, stdout) == EOF ? traits_type::eof() : c;
48 //	}
49 //};
50 //#endif
51 //#endif
52 
53 #ifdef MINGW_PATCH
54 	FILE _iob[] = { *stdin, *stdout, *stderr };
55 
__iob_func(void)56 	extern "C" FILE * __cdecl __iob_func(void)
57 	{
58 		return _iob;
59 	}
60 
__isnan(double x)61 	extern "C" int __cdecl __isnan(double x)
62 	{
63 		return _finite(x)?0:1;
64 	}
65 
__isnanf(float x)66 	extern "C" int __cdecl __isnanf(float x)
67 	{
68 		return _finitef(x)?0:1;
69 	}
70 #endif
71 
72 
filterChars(std::string & s,const std::string & allowed)73 std::string& filterChars(std::string& s, const std::string& allowed) {
74     s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) {
75         return allowed.find(c) == std::string::npos;
76     }), s.end());
77     return s;
78 }
79 
frequencyToStr(long long freq)80 std::string frequencyToStr(long long freq) {
81     long double freqTemp;
82 
83     freqTemp = freq;
84     std::string suffix;
85     std::stringstream freqStr;
86 
87     if (freqTemp >= 1.0e9) {
88         freqTemp /= 1.0e9;
89         freqStr << std::setprecision(10);
90         suffix = std::string("GHz");
91     } else if (freqTemp >= 1.0e6) {
92         freqTemp /= 1.0e6;
93         freqStr << std::setprecision(7);
94         suffix = std::string("MHz");
95     } else if (freqTemp >= 1.0e3) {
96         freqTemp /= 1.0e3;
97         freqStr << std::setprecision(4);
98         suffix = std::string("KHz");
99     }
100 
101     freqStr << freqTemp;
102     freqStr << suffix;
103 
104     return freqStr.str();
105 }
106 
strToFrequency(std::string freqStr)107 long long strToFrequency(std::string freqStr) {
108     std::string filterStr = filterChars(freqStr, std::string("0123456789.MKGHmkgh"));
109 
110     size_t numLen = filterStr.find_first_not_of("0123456789.");
111 
112     if (numLen == std::string::npos) {
113         numLen = freqStr.length();
114     }
115 
116     std::string numPartStr = freqStr.substr(0, numLen);
117     std::string suffixStr = freqStr.substr(numLen);
118 
119     std::stringstream numPartStream;
120     numPartStream.str(numPartStr);
121 
122     long double freqTemp = 0;
123 
124     numPartStream >> freqTemp;
125 
126     if (suffixStr.length()) {
127         if (suffixStr.find_first_of("Gg") != std::string::npos) {
128             freqTemp *= 1.0e9;
129         } else if (suffixStr.find_first_of("Mm") != std::string::npos) {
130             freqTemp *= 1.0e6;
131         } else if (suffixStr.find_first_of("Kk") != std::string::npos) {
132             freqTemp *= 1.0e3;
133         } else if (suffixStr.find_first_of("Hh") != std::string::npos) {
134             // ...
135         }
136     } else if (numPartStr.find_first_of('.') != std::string::npos || freqTemp <= 3000) {
137         freqTemp *= 1.0e6;
138     }
139 
140     return (long long) freqTemp;
141 }
142 
143 
144 
145 class ActionDialogBookmarkCatastophe : public ActionDialog {
146 public:
ActionDialogBookmarkCatastophe()147     ActionDialogBookmarkCatastophe() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Last-Loaded Backup Failure :( :( :(")) {
148         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."));
149     }
150 
doClickOK()151     void doClickOK() override {
152         wxGetApp().getAppFrame()->disableSave(true);
153         wxGetApp().getAppFrame()->Close(false);
154     }
155 };
156 
157 
158 
159 class ActionDialogBookmarkBackupLoadFailed : public ActionDialog {
160 public:
ActionDialogBookmarkBackupLoadFailed()161     ActionDialogBookmarkBackupLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Backup Load Failure :( :(")) {
162         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?"));
163     }
164 
doClickOK()165     void doClickOK() override {
166         if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) {
167             if (wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.lastloaded",false)) {
168                 wxGetApp().getBookmarkMgr().updateBookmarks();
169                 wxGetApp().getBookmarkMgr().updateActiveList();
170             } else {
171                 ActionDialog::showDialog(new ActionDialogBookmarkCatastophe());
172             }
173         }
174     }
175 };
176 
177 
178 class ActionDialogBookmarkLoadFailed : public ActionDialog {
179 public:
ActionDialogBookmarkLoadFailed()180     ActionDialogBookmarkLoadFailed() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Bookmark Load Failure :(")) {
181         m_questionText->SetLabelText(wxT("Sorry; unable to load your bookmarks file. \nWould you like to attempt to load the backup file?"));
182     }
183 
doClickOK()184     void doClickOK() override {
185         bool loadOk = false;
186         if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) {
187             loadOk = wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml.backup",false);
188         }
189         if (loadOk) {
190             wxGetApp().getBookmarkMgr().updateBookmarks();
191             wxGetApp().getBookmarkMgr().updateActiveList();
192         } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) {
193             ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed());
194         } else {
195             ActionDialog::showDialog(new ActionDialogBookmarkCatastophe());
196         }
197     }
198 };
199 
200 
201 
202 class ActionDialogRigError : public ActionDialog {
203 public:
ActionDialogRigError(const std::string & message)204     explicit ActionDialogRigError(const std::string& message) : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Rig Control Error")) {
205         m_questionText->SetLabelText(message);
206     }
207 
doClickOK()208     void doClickOK() override {
209     }
210 };
211 
212 
CubicSDR()213 CubicSDR::CubicSDR() : frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), agcMode(false)
214 {
215         config.load();
216 
217         sampleRateInitialized.store(false);
218         agcMode.store(true);
219         soloMode.store(false);
220         shuttingDown.store(false);
221         fdlgTarget = FrequencyDialog::FDIALOG_TARGET_DEFAULT;
222         stoppedDev = nullptr;
223 
224         //set OpenGL configuration:
225         m_glContextAttributes = new wxGLContextAttrs();
226 
227         wxGLContextAttrs glSettings;
228         glSettings.PlatformDefaults().EndList();
229 
230         *m_glContextAttributes = glSettings;
231 }
232 
initAudioDevices() const233 void CubicSDR::initAudioDevices() const {
234     std::vector<RtAudio::DeviceInfo> devices;
235     std::map<int, RtAudio::DeviceInfo> inputDevices, outputDevices;
236 
237     AudioThread::enumerateDevices(devices);
238 
239     int i = 0;
240 
241     for (auto & device : devices) {
242         if (device.inputChannels) {
243             inputDevices[i] = device;
244         }
245         if (device.outputChannels) {
246             outputDevices[i] = device;
247         }
248         i++;
249     }
250 
251     wxGetApp().getDemodMgr().setOutputDevices(outputDevices);
252 }
253 
OnInit()254 bool CubicSDR::OnInit() {
255 
256     //use the current locale most appropriate to this system,
257     //so that character-related functions are likely to handle Unicode
258     //better (by default, was "C" locale).
259     std::setlocale(LC_ALL, "");
260 
261 //#ifdef _OSX_APP_
262 //    CFBundleRef mainBundle = CFBundleGetMainBundle();
263 //    CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
264 //    char path[PATH_MAX];
265 //    if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
266 //    {
267 //        // error!
268 //    }
269 //    CFRelease(resourcesURL);
270 //    chdir(path);
271 //#endif
272 
273     if (!wxApp::OnInit()) {
274         return false;
275     }
276 
277     //Deactivated code to allocate an explicit Console on Windows.
278     //This tends to hang the apllication on heavy demod (re)creation.
279     //To continue to debug with std::cout traces, simply run CubicSDR in a MINSYS2 compatble shell on Windows:
280     //ex: Cygwin shell, Git For Windows Bash shell....
281 #if (0)
282     	if (AllocConsole()) {
283     		freopen("CONOUT$", "w", stdout);
284     		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
285             SetConsoleTitle(L"CubicSDR: stdout");
286 
287     	}
288 
289         //refresh
290         ofstream ob;
291         std::streambuf *sb = std::cout.rdbuf();
292         std::cout.rdbuf(sb);
293 #endif
294 
295 
296     wxApp::SetAppName(CUBICSDR_INSTALL_NAME);
297 
298 #ifdef USE_HAMLIB
299     t_Rig = nullptr;
300     rigThread = nullptr;
301 
302     RigThread::enumerate();
303 #endif
304 
305     Modem::addModemFactory(ModemFM::factory, "FM", 200000);
306     Modem::addModemFactory(ModemNBFM::factory, "NBFM", 12500);
307     Modem::addModemFactory(ModemFMStereo::factory, "FMS", 200000);
308     Modem::addModemFactory(ModemAM::factory, "AM", 6000);
309     Modem::addModemFactory(ModemCW::factory, "CW", 500);
310     Modem::addModemFactory(ModemLSB::factory, "LSB", 5400);
311     Modem::addModemFactory(ModemUSB::factory, "USB", 5400);
312     Modem::addModemFactory(ModemDSB::factory, "DSB", 5400);
313     Modem::addModemFactory(ModemIQ::factory, "I/Q", 48000);
314 
315 #ifdef ENABLE_DIGITAL_LAB
316     Modem::addModemFactory(ModemAPSK::factory, "APSK", 200000);
317     Modem::addModemFactory(ModemASK::factory, "ASK", 200000);
318     Modem::addModemFactory(ModemBPSK::factory, "BPSK", 200000);
319     Modem::addModemFactory(ModemDPSK::factory, "DPSK", 200000);
320     Modem::addModemFactory(ModemFSK::factory, "FSK", 19200);
321     Modem::addModemFactory(ModemGMSK::factory, "GMSK", 19200);
322     Modem::addModemFactory(ModemOOK::factory, "OOK", 200000);
323     Modem::addModemFactory(ModemPSK::factory, "PSK", 200000);
324     Modem::addModemFactory(ModemQAM::factory, "QAM", 200000);
325     Modem::addModemFactory(ModemQPSK::factory, "QPSK", 200000);
326     Modem::addModemFactory(ModemSQAM::factory, "SQAM", 200000);
327     Modem::addModemFactory(ModemST::factory, "ST", 200000);
328 #endif
329 
330     frequency = wxGetApp().getConfig()->getCenterFreq();
331     offset = 0;
332     ppm = 0;
333     devicesReady.store(false);
334     devicesFailed.store(false);
335     deviceSelectorOpen.store(false);
336 
337     initAudioDevices();
338 
339     // Visual Data
340     spectrumVisualThread = new SpectrumVisualDataThread();
341 
342     pipeIQVisualData = std::make_shared<DemodulatorThreadInputQueue>();
343     pipeIQVisualData->set_max_num_items(1);
344 
345     pipeWaterfallIQVisualData = std::make_shared<DemodulatorThreadInputQueue>();
346     pipeWaterfallIQVisualData->set_max_num_items(128);
347 
348     getSpectrumProcessor()->setInput(pipeIQVisualData);
349     getSpectrumProcessor()->setHideDC(true);
350 
351     // I/Q Data
352     pipeSDRIQData = std::make_shared<SDRThreadIQDataQueue>();
353     pipeSDRIQData->set_max_num_items(100);
354 
355     sdrThread = new SDRThread();
356     sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData);
357 
358     sdrPostThread = new SDRPostThread();
359     sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData);
360 
361     sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
362     sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
363 
364 #if CUBICSDR_ENABLE_VIEW_SCOPE
365     pipeAudioVisualData = std::make_shared<DemodulatorThreadOutputQueue>();
366     pipeAudioVisualData->set_max_num_items(1);
367 
368     scopeProcessor.setInput(pipeAudioVisualData);
369 #else
370     pipeAudioVisualData = nullptr;
371 #endif
372 
373 #if CUBICSDR_ENABLE_VIEW_DEMOD
374     demodVisualThread = new SpectrumVisualDataThread();
375     pipeDemodIQVisualData = std::make_shared<DemodulatorThreadInputQueue>();
376     pipeDemodIQVisualData->set_max_num_items(1);
377 
378     if (getDemodSpectrumProcessor()) {
379         getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData);
380     }
381     sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData);
382 #else
383     demodVisualThread = nullptr;
384     pipeDemodIQVisualData = nullptr;
385     t_DemodVisual = nullptr;
386 #endif
387 
388     // Now that input/output queue plumbing is completely done, we can
389     //safely starts all the threads:
390     t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread);
391 
392     if (demodVisualThread != nullptr) {
393         t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread);
394     }
395 
396     //Start SDRPostThread last.
397     t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread);
398 
399 
400     sdrEnum = new SDREnumerator();
401 
402     SDREnumerator::setManuals(config.getManualDevices());
403 
404     appframe = new AppFrame();
405 	t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum);
406 
407 //#ifdef __APPLE__
408 //    int main_policy;
409 //    struct sched_param main_param;
410 //
411 //    main_policy = SCHED_RR;
412 //    main_param.sched_priority = sched_get_priority_min(SCHED_RR)+2;
413 //
414 //    pthread_setschedparam(pthread_self(), main_policy, &main_param);
415 //#endif
416 
417     if (!wxGetApp().getBookmarkMgr().loadFromFile("bookmarks.xml")) {
418         if (wxGetApp().getBookmarkMgr().hasBackup("bookmarks.xml")) {
419             ActionDialog::showDialog(new ActionDialogBookmarkLoadFailed());
420         } else if (wxGetApp().getBookmarkMgr().hasLastLoad("bookmarks.xml")) {
421             ActionDialog::showDialog(new ActionDialogBookmarkBackupLoadFailed());
422         } else {
423             ActionDialog::showDialog(new ActionDialogBookmarkCatastophe());
424         }
425     } else {
426         getBookmarkMgr().updateActiveList();
427         getBookmarkMgr().updateBookmarks();
428     }
429 
430     return true;
431 }
432 
OnExit()433 int CubicSDR::OnExit() {
434     shuttingDown.store(true);
435 
436 #if USE_HAMLIB
437     if (rigIsActive()) {
438         std::cout << "Terminating Rig thread.."  << std::endl << std::flush;
439         stopRig();
440     }
441 #endif
442 
443     bool terminationSequenceOK = true;
444 
445     //The thread feeding them all should be terminated first, so:
446     std::cout << "Terminating SDR thread.." << std::endl << std::flush ;
447     sdrThread->terminate();
448     terminationSequenceOK = terminationSequenceOK && sdrThread->isTerminated(3000);
449 
450     //in case termination sequence goes wrong, kill App brutally now because it can get stuck.
451     if (!terminationSequenceOK) {
452         //no trace here because it could occur if the device is not started.
453         ::exit(11);
454     }
455 
456     std::cout << "Terminating SDR post-processing thread.." << std::endl << std::flush;
457     sdrPostThread->terminate();
458 
459     //Wait for termination for sdrPostThread second:: since it is doing
460     //mostly blocking push() to the other threads, they must stay alive
461     //so that sdrPostThread can complete a processing loop and die.
462     terminationSequenceOK = terminationSequenceOK && sdrPostThread->isTerminated(3000);
463 
464     //in case termination sequence goes wrong, kill App brutally now because it can get stuck.
465     if (!terminationSequenceOK) {
466         std::cout << "Cannot terminate application properly, calling exit() now." << std::endl << std::flush;
467         ::exit(12);
468     }
469 
470     std::cout << "Terminating All Demodulators.." << std::endl << std::flush;
471     demodMgr.terminateAll();
472 
473     std::cout << "Terminating Visual Processor threads.." << std::endl << std::flush;
474     spectrumVisualThread->terminate();
475     if (demodVisualThread) {
476         demodVisualThread->terminate();
477     }
478 
479     //Wait nicely
480     terminationSequenceOK = terminationSequenceOK &&  spectrumVisualThread->isTerminated(1000);
481 
482     if (demodVisualThread) {
483         terminationSequenceOK = terminationSequenceOK && demodVisualThread->isTerminated(1000);
484     }
485 
486     //in case termination sequence goes wrong, kill App brutally because it can get stuck.
487     if (!terminationSequenceOK) {
488         std::cout << "Cannot terminate application properly, calling exit() now." << std::endl << std::flush;
489         ::exit(13);
490     }
491 
492     //Then join the thread themselves:
493     if (t_SDR) {
494         t_SDR->join();
495     }
496 
497     t_PostSDR->join();
498 
499     if (t_DemodVisual) {
500         t_DemodVisual->join();
501     }
502 
503     t_SpectrumVisual->join();
504 
505     //Now only we can delete:
506     delete t_SDR;
507     t_SDR = nullptr;
508 
509     delete sdrThread;
510     sdrThread = nullptr;
511 
512     delete sdrPostThread;
513     sdrPostThread = nullptr;
514 
515     delete t_PostSDR;
516     t_PostSDR = nullptr;
517 
518     delete t_SpectrumVisual;
519     t_SpectrumVisual = nullptr;
520 
521     delete spectrumVisualThread;
522     spectrumVisualThread = nullptr;
523 
524     delete t_DemodVisual;
525     t_DemodVisual = nullptr;
526 
527     delete demodVisualThread;
528     demodVisualThread = nullptr;
529 
530     delete m_glContext;
531     m_glContext = nullptr;
532 
533     //
534     AudioThread::deviceCleanup();
535 
536     std::cout << "Application termination complete." << std::endl << std::flush;
537 
538     return wxApp::OnExit();
539 }
540 
GetContext(wxGLCanvas * canvas)541 PrimaryGLContext& CubicSDR::GetContext(wxGLCanvas *canvas) {
542     PrimaryGLContext *glContext;
543     if (!m_glContext) {
544         m_glContext = new PrimaryGLContext(canvas, nullptr, GetContextAttributes());
545     }
546     glContext = m_glContext;
547 
548     return *glContext;
549 }
550 
GetContextAttributes()551 wxGLContextAttrs* CubicSDR::GetContextAttributes() {
552 
553     return m_glContextAttributes;
554 }
555 
OnInitCmdLine(wxCmdLineParser & parser)556 void CubicSDR::OnInitCmdLine(wxCmdLineParser& parser) {
557     parser.SetDesc (commandLineInfo);
558     parser.SetSwitchChars (wxT("-"));
559 }
560 
OnCmdLineParsed(wxCmdLineParser & parser)561 bool CubicSDR::OnCmdLineParsed(wxCmdLineParser& parser) {
562     auto *confName = new wxString;
563     if (parser.Found("c",confName)) {
564         if (!confName->empty()) {
565             config.setConfigName(confName->ToStdString());
566         }
567     }
568 
569 #ifdef BUNDLE_SOAPY_MODS
570     if (parser.Found("b")) {
571         useLocalMod.store(false);
572     } else {
573         useLocalMod.store(true);
574     }
575 #else
576     useLocalMod.store(true);
577 #endif
578 
579     auto *modPath = new wxString;
580 
581     if (parser.Found("m",modPath)) {
582         if (!modPath->empty()) {
583             modulePath = modPath->ToStdString();
584         } else {
585             modulePath = "";
586         }
587     }
588 
589     return true;
590 }
591 
closeDeviceSelector()592 void CubicSDR::closeDeviceSelector() {
593     if (deviceSelectorOpen) {
594         deviceSelectorDialog->Close();
595     }
596 }
597 
deviceSelector()598 void CubicSDR::deviceSelector() {
599     if (deviceSelectorOpen) {
600         deviceSelectorDialog->Raise();
601         deviceSelectorDialog->SetFocus();
602         return;
603     }
604     deviceSelectorOpen.store(true);
605     wxRect *winRect = getConfig()->getWindow();
606     wxPoint pos(wxDefaultPosition);
607     if (winRect != nullptr) {
608         pos = wxPoint(winRect->x, winRect->y);
609     }
610     deviceSelectorDialog = new SDRDevicesDialog(appframe, pos);
611     deviceSelectorDialog->Show();
612 }
613 
addRemote(const std::string & remoteAddr)614 void CubicSDR::addRemote(const std::string& remoteAddr) {
615     SDREnumerator::addRemote(remoteAddr);
616     devicesReady.store(false);
617     t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum);
618 }
619 
removeRemote(const std::string & remoteAddr)620 void CubicSDR::removeRemote(const std::string& remoteAddr) {
621     SDREnumerator::removeRemote(remoteAddr);
622 }
623 
sdrThreadNotify(SDRThread::SDRThreadState state,const std::string & message)624 void CubicSDR::sdrThreadNotify(SDRThread::SDRThreadState state, const std::string& message) {
625 
626     std::lock_guard < std::mutex > lock(notify_busy);
627 
628 
629     if (state == SDRThread::SDR_THREAD_INITIALIZED) {
630         appframe->initDeviceParams(getDevice());
631     }
632     if (state == SDRThread::SDR_THREAD_MESSAGE) {
633         notifyMessage = message;
634     }
635     if (state == SDRThread::SDR_THREAD_FAILED) {
636         notifyMessage = message;
637 //        wxMessageDialog *info;
638 //        info = new wxMessageDialog(NULL, message, wxT("Error initializing device"), wxOK | wxICON_ERROR);
639 //        info->ShowModal();
640     }
641     //if (appframe) { appframe->SetStatusText(message); }
642 
643 }
644 
645 
sdrEnumThreadNotify(SDREnumerator::SDREnumState state,std::string message)646 void CubicSDR::sdrEnumThreadNotify(SDREnumerator::SDREnumState state, std::string message) {
647     std::lock_guard < std::mutex > lock(notify_busy);
648 
649     if (state == SDREnumerator::SDR_ENUM_MESSAGE) {
650         notifyMessage = message;
651     }
652     if (state == SDREnumerator::SDR_ENUM_DEVICES_READY) {
653         devs = SDREnumerator::enumerate_devices("", true);
654         devicesReady.store(true);
655     }
656     if (state == SDREnumerator::SDR_ENUM_FAILED) {
657         devicesFailed.store(true);
658     }
659     //if (appframe) { appframe->SetStatusText(message); }
660 
661 
662 }
663 
664 
setFrequency(long long freq)665 void CubicSDR::setFrequency(long long freq) {
666     if (freq < sampleRate / 2) {
667         freq = sampleRate / 2;
668     }
669     frequency = freq;
670     sdrThread->setFrequency(freq);
671     getSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold());
672 
673     //make the peak hold act on the current dmod also, like a zoomed-in version.
674     if (getDemodSpectrumProcessor()) {
675         getDemodSpectrumProcessor()->setPeakHold(getSpectrumProcessor()->getPeakHold());
676     }
677 }
678 
getOffset()679 long long CubicSDR::getOffset() {
680     return offset;
681 }
682 
setOffset(long long ofs)683 void CubicSDR::setOffset(long long ofs) {
684     offset = ofs;
685 
686     if (sdrThread && !sdrThread->isTerminated()) {
687         sdrThread->setOffset(offset);
688     }
689 }
690 
setAntennaName(const std::string & name)691 void CubicSDR::setAntennaName(const std::string& name) {
692     antennaName = name;
693 
694     if (sdrThread && !sdrThread->isTerminated()) {
695         sdrThread->setAntenna(antennaName);
696     }
697 }
698 
getAntennaName()699 const std::string& CubicSDR::getAntennaName() {
700     return antennaName;
701 }
702 
setChannelizerType(SDRPostThreadChannelizerType chType)703 void CubicSDR::setChannelizerType(SDRPostThreadChannelizerType chType) {
704     if (sdrPostThread && !sdrPostThread->isTerminated()) {
705         sdrPostThread->setChannelizerType(chType);
706     }
707 }
708 
getChannelizerType()709 SDRPostThreadChannelizerType CubicSDR::getChannelizerType() {
710 
711     if (sdrPostThread && !sdrPostThread->isTerminated()) {
712         return sdrPostThread->getChannelizerType();
713     }
714 
715     return SDRPostThreadChannelizerType::SDRPostPFBCH;
716 }
717 
getFrequency()718 long long CubicSDR::getFrequency() {
719     return frequency;
720 }
721 
722 
lockFrequency(long long freq)723 void CubicSDR::lockFrequency(long long freq) {
724     frequency_locked.store(true);
725     lock_freq.store(freq);
726 
727     if (sdrThread && !sdrThread->isTerminated()) {
728         sdrThread->lockFrequency(freq);
729     }
730 }
731 
isFrequencyLocked()732 bool CubicSDR::isFrequencyLocked() {
733     return frequency_locked.load();
734 }
735 
unlockFrequency()736 void CubicSDR::unlockFrequency() {
737     frequency_locked.store(false);
738     if (sdrThread && !sdrThread->isTerminated()) {
739         sdrThread->unlockFrequency();
740     }
741 }
742 
setSampleRate(long long rate_in)743 void CubicSDR::setSampleRate(long long rate_in) {
744     sampleRate = rate_in;
745 
746     if (sdrThread && !sdrThread->isTerminated()) {
747         sdrThread->setSampleRate(sampleRate);
748     }
749 
750     setFrequency(frequency);
751 
752     if (rate_in <= CHANNELIZER_RATE_MAX / 8) {
753         appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE / 4);
754         appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false);
755         spectrumVisualThread->getProcessor()->setHideDC(false);
756     } else if (rate_in <= CHANNELIZER_RATE_MAX) {
757         appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE / 2);
758         appframe->getWaterfallDataThread()->getProcessor()->setHideDC(false);
759         spectrumVisualThread->getProcessor()->setHideDC(false);
760     } else if (rate_in > CHANNELIZER_RATE_MAX) {
761         appframe->setMainWaterfallFFTSize(DEFAULT_FFT_SIZE);
762         appframe->getWaterfallDataThread()->getProcessor()->setHideDC(true);
763         spectrumVisualThread->getProcessor()->setHideDC(true);
764     }
765 }
766 
stopDevice(bool store,int waitMsForTermination)767 void CubicSDR::stopDevice(bool store, int waitMsForTermination) {
768 
769     //Firt we must stop the threads
770     sdrThread->terminate();
771     sdrThread->isTerminated(waitMsForTermination);
772 
773     if (t_SDR) {
774         t_SDR->join();
775         delete t_SDR;
776         t_SDR = nullptr;
777     }
778 
779     //Only now we can nullify devices
780     if (store) {
781         stoppedDev = sdrThread->getDevice();
782     }
783     else {
784         stoppedDev = nullptr;
785     }
786 
787     sdrThread->setDevice(nullptr);
788 }
789 
reEnumerateDevices()790 void CubicSDR::reEnumerateDevices() {
791     devicesReady.store(false);
792     devs = nullptr;
793     SDREnumerator::reset();
794     t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum);
795 }
796 
setDevice(SDRDeviceInfo * dev,int waitMsForTermination)797 void CubicSDR::setDevice(SDRDeviceInfo *dev, int waitMsForTermination) {
798 
799     sdrThread->terminate();
800     sdrThread->isTerminated(waitMsForTermination);
801 
802     if (t_SDR) {
803        t_SDR->join();
804        delete t_SDR;
805        t_SDR = nullptr;
806     }
807 
808     for (SoapySDR::Kwargs::const_iterator i = settingArgs.begin(); i != settingArgs.end(); i++) {
809         sdrThread->writeSetting(i->first, i->second);
810     }
811     sdrThread->setStreamArgs(streamArgs);
812     sdrThread->setDevice(dev);
813 
814     DeviceConfig *devConfig = config.getDevice(dev->getDeviceId());
815 
816     SoapySDR::Device *soapyDev = dev->getSoapyDevice();
817 
818     if (soapyDev) {
819         if (long devSampleRate = devConfig->getSampleRate()) {
820             sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, devSampleRate);
821             sampleRateInitialized.store(true);
822         }
823 
824         if (!sampleRateInitialized.load()) {
825             sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, DEFAULT_SAMPLE_RATE);
826             sampleRateInitialized.store(true);
827         } else {
828             sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, sampleRate);
829         }
830 
831         if (frequency < sampleRate/2) {
832             frequency = sampleRate/2;
833         }
834 
835         setFrequency(frequency);
836         setSampleRate(sampleRate);
837 
838         setPPM(devConfig->getPPM());
839         setOffset(devConfig->getOffset());
840         setAGCMode(devConfig->getAGCMode());
841         setAntennaName(devConfig->getAntennaName());
842 
843         t_SDR = new std::thread(&SDRThread::threadMain, sdrThread);
844 }
845 
846     stoppedDev = nullptr;
847 }
848 
getDevice()849 SDRDeviceInfo *CubicSDR::getDevice() {
850     if (!sdrThread->getDevice() && stoppedDev) {
851         return stoppedDev;
852     }
853 
854     return sdrThread->getDevice();
855 }
856 
getScopeProcessor()857 ScopeVisualProcessor *CubicSDR::getScopeProcessor() {
858     return &scopeProcessor;
859 }
860 
getSpectrumProcessor()861 SpectrumVisualProcessor *CubicSDR::getSpectrumProcessor() {
862     return spectrumVisualThread->getProcessor();
863 }
864 
getDemodSpectrumProcessor()865 SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() {
866     if (demodVisualThread) {
867         return demodVisualThread->getProcessor();
868     } else {
869         return nullptr;
870     }
871 }
872 
getAudioVisualQueue()873 DemodulatorThreadOutputQueuePtr CubicSDR::getAudioVisualQueue() {
874     return pipeAudioVisualData;
875 }
876 
getIQVisualQueue()877 DemodulatorThreadInputQueuePtr CubicSDR::getIQVisualQueue() {
878     return pipeIQVisualData;
879 }
880 
getWaterfallVisualQueue()881 DemodulatorThreadInputQueuePtr CubicSDR::getWaterfallVisualQueue() {
882     return pipeWaterfallIQVisualData;
883 }
884 
getBookmarkMgr()885 BookmarkMgr &CubicSDR::getBookmarkMgr() {
886     return bookmarkMgr;
887 }
888 
getDemodMgr()889 DemodulatorMgr &CubicSDR::getDemodMgr() {
890     return demodMgr;
891 }
892 
getSessionMgr()893 SessionMgr &CubicSDR::getSessionMgr() {
894     return sessionMgr;
895 }
896 
getSDRPostThread()897 SDRPostThread *CubicSDR::getSDRPostThread() {
898     return sdrPostThread;
899 }
900 
getSDRThread()901 SDRThread *CubicSDR::getSDRThread() {
902     return sdrThread;
903 }
904 
905 
notifyDemodulatorsChanged()906 void CubicSDR::notifyDemodulatorsChanged() {
907 
908     sdrPostThread->notifyDemodulatorsChanged();
909 }
910 
getSampleRate()911 long long CubicSDR::getSampleRate() {
912     return sampleRate;
913 }
914 
removeDemodulator(const DemodulatorInstancePtr & demod)915 void CubicSDR::removeDemodulator(const DemodulatorInstancePtr& demod) {
916     if (!demod) {
917         return;
918     }
919     demod->setActive(false);
920     sdrPostThread->notifyDemodulatorsChanged();
921     wxGetApp().getAppFrame()->notifyUpdateModemProperties();
922 }
923 
getDevices()924 std::vector<SDRDeviceInfo*>* CubicSDR::getDevices() {
925     return devs;
926 }
927 
928 
getConfig()929 AppConfig *CubicSDR::getConfig() {
930     return &config;
931 }
932 
saveConfig()933 void CubicSDR::saveConfig() {
934     config.save();
935 }
936 
setPPM(int ppm_in)937 void CubicSDR::setPPM(int ppm_in) {
938     ppm = ppm_in;
939     if (sdrThread && !sdrThread->isTerminated()) {
940         sdrThread->setPPM(ppm);
941     }
942 }
943 
getPPM()944 int CubicSDR::getPPM() {
945     SDRDeviceInfo *dev = sdrThread->getDevice();
946     if (dev) {
947         ppm = config.getDevice(dev->getDeviceId())->getPPM();
948     }
949     return ppm;
950 }
951 
showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode,const wxString & initString)952 void CubicSDR::showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode, const wxString& initString) {
953     const wxString demodTitle("Set Demodulator Frequency");
954     const wxString freqTitle("Set Center Frequency");
955     const wxString bwTitle("Modem Bandwidth (150Hz - 500KHz)");
956     const wxString lpsTitle("Lines-Per-Second (1-1024)");
957     const wxString avgTitle("Average Rate (0.1 - 0.99)");
958     const wxString gainTitle("Gain Entry: "+wxGetApp().getActiveGainEntry());
959 
960     wxString title;
961     auto activeModem = demodMgr.getActiveContextModem();
962 
963     switch (targetMode) {
964         case FrequencyDialog::FDIALOG_TARGET_DEFAULT:
965         case FrequencyDialog::FDIALOG_TARGET_FREQ:
966             title = activeModem ?demodTitle:freqTitle;
967             break;
968         case FrequencyDialog::FDIALOG_TARGET_BANDWIDTH:
969             title = bwTitle;
970             break;
971         case FrequencyDialog::FDIALOG_TARGET_WATERFALL_LPS:
972             title = lpsTitle;
973             break;
974         case FrequencyDialog::FDIALOG_TARGET_SPECTRUM_AVG:
975             title = avgTitle;
976             break;
977         case FrequencyDialog::FDIALOG_TARGET_GAIN:
978             title = gainTitle;
979             if (wxGetApp().getActiveGainEntry().empty()) {
980                 return;
981             }
982             break;
983         default:
984             break;
985     }
986 
987     FrequencyDialog fdialog(appframe, -1, title, activeModem, wxPoint(-100,-100), wxSize(350, 75), wxDEFAULT_DIALOG_STYLE, targetMode, initString);
988     fdialog.ShowModal();
989 }
990 
showLabelInput()991 void CubicSDR::showLabelInput() {
992 
993     DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveContextModem();
994 
995     if (activeDemod != nullptr) {
996 
997         const wxString demodTitle("Edit Demodulator label");
998 
999         DemodLabelDialog labelDialog(appframe, -1, demodTitle, activeDemod, wxPoint(-100, -100), wxSize(500, 75), wxDEFAULT_DIALOG_STYLE);
1000         labelDialog.ShowModal();
1001     }
1002 }
1003 
getAppFrame()1004 AppFrame *CubicSDR::getAppFrame() {
1005     return appframe;
1006 }
1007 
setFrequencySnap(int snap_in)1008 void CubicSDR::setFrequencySnap(int snap_in) {
1009     if (snap_in > 1000000) {
1010         snap_in = 1000000;
1011     }
1012     this->snap = snap_in;
1013 }
1014 
getFrequencySnap()1015 int CubicSDR::getFrequencySnap() {
1016     return snap;
1017 }
1018 
areDevicesReady()1019 bool CubicSDR::areDevicesReady() {
1020     return devicesReady.load();
1021 }
1022 
notifyMainUIOfDeviceChange(bool forceRefreshOfGains)1023 void CubicSDR::notifyMainUIOfDeviceChange(bool forceRefreshOfGains) {
1024     appframe->notifyDeviceChanged();
1025 
1026 	if (forceRefreshOfGains) {
1027 		appframe->refreshGainUI();
1028 	}
1029 }
1030 
areDevicesEnumerating()1031 bool CubicSDR::areDevicesEnumerating() {
1032     return !sdrEnum->isTerminated();
1033 }
1034 
areModulesMissing()1035 bool CubicSDR::areModulesMissing() {
1036     return devicesFailed.load();
1037 }
1038 
getNotification()1039 std::string CubicSDR::getNotification() {
1040     std::string msg;
1041     std::lock_guard < std::mutex > lock(notify_busy);
1042     msg = notifyMessage;
1043 
1044     return msg;
1045 }
1046 
setDeviceSelectorClosed()1047 void CubicSDR::setDeviceSelectorClosed() {
1048     deviceSelectorOpen.store(false);
1049 }
1050 
isDeviceSelectorOpen()1051 bool CubicSDR::isDeviceSelectorOpen() {
1052 	return deviceSelectorOpen.load();
1053 }
1054 
setAGCMode(bool mode)1055 void CubicSDR::setAGCMode(bool mode) {
1056     agcMode.store(mode);
1057 
1058     if (sdrThread && !sdrThread->isTerminated()) {
1059         sdrThread->setAGCMode(mode);
1060     }
1061 }
1062 
getAGCMode()1063 bool CubicSDR::getAGCMode() {
1064     return agcMode.load();
1065 }
1066 
1067 
setGain(const std::string & name,float gain_in)1068 void CubicSDR::setGain(const std::string& name, float gain_in) {
1069     sdrThread->setGain(name,gain_in);
1070 }
1071 
getGain(const std::string & name)1072 float CubicSDR::getGain(const std::string& name) {
1073     return sdrThread->getGain(name);
1074 }
1075 
setStreamArgs(SoapySDR::Kwargs streamArgs_in)1076 void CubicSDR::setStreamArgs(SoapySDR::Kwargs streamArgs_in) {
1077     streamArgs = streamArgs_in;
1078 }
1079 
setDeviceArgs(SoapySDR::Kwargs settingArgs_in)1080 void CubicSDR::setDeviceArgs(SoapySDR::Kwargs settingArgs_in) {
1081     settingArgs = settingArgs_in;
1082 }
1083 
getUseLocalMod()1084 bool CubicSDR::getUseLocalMod() {
1085     return useLocalMod.load();
1086 }
1087 
getModulePath()1088 std::string CubicSDR::getModulePath() {
1089     return modulePath;
1090 }
1091 
setActiveGainEntry(std::string gainName)1092 void CubicSDR::setActiveGainEntry(std::string gainName) {
1093     activeGain = gainName;
1094 }
1095 
getActiveGainEntry()1096 std::string CubicSDR::getActiveGainEntry() {
1097     return activeGain;
1098 }
1099 
setSoloMode(bool solo)1100 void CubicSDR::setSoloMode(bool solo) {
1101     soloMode.store(solo);
1102 }
1103 
getSoloMode()1104 bool CubicSDR::getSoloMode() {
1105     return soloMode.load();
1106 }
1107 
isShuttingDown()1108 bool CubicSDR::isShuttingDown()
1109 {
1110     return shuttingDown.load();
1111 }
1112 
FilterEvent(wxEvent & event)1113 int CubicSDR::FilterEvent(wxEvent& event) {
1114     if (!appframe) {
1115         return -1;
1116     }
1117 
1118     if (event.GetEventType() == wxEVT_KEY_DOWN || event.GetEventType() == wxEVT_CHAR_HOOK) {
1119 		return appframe->OnGlobalKeyDown((wxKeyEvent&)event);
1120     }
1121 
1122     if (event.GetEventType() == wxEVT_KEY_UP || event.GetEventType() == wxEVT_CHAR_HOOK) {
1123         return appframe->OnGlobalKeyUp((wxKeyEvent&)event);
1124     }
1125 
1126     return -1;  // process normally
1127 }
1128 
1129 #ifdef USE_HAMLIB
getRigThread()1130 RigThread *CubicSDR::getRigThread() {
1131     return rigThread;
1132 }
1133 
initRig(int rigModel,std::string rigPort,int rigSerialRate)1134 void CubicSDR::initRig(int rigModel, std::string rigPort, int rigSerialRate) {
1135     if (rigThread) {
1136 
1137         rigThread->terminate();
1138         rigThread->isTerminated(1000);
1139     }
1140 
1141     if (t_Rig && t_Rig->joinable()) {
1142         t_Rig->join();
1143     }
1144 
1145     //now we can delete
1146     if (rigThread) {
1147 
1148         delete rigThread;
1149         rigThread = nullptr;
1150     }
1151     if (t_Rig) {
1152 
1153         delete t_Rig;
1154         t_Rig = nullptr;
1155     }
1156 
1157     rigThread = new RigThread();
1158     rigThread->initRig(rigModel, rigPort, rigSerialRate);
1159     rigThread->setControlMode(wxGetApp().getConfig()->getRigControlMode());
1160     rigThread->setFollowMode(wxGetApp().getConfig()->getRigFollowMode());
1161     rigThread->setCenterLock(wxGetApp().getConfig()->getRigCenterLock());
1162     rigThread->setFollowModem(wxGetApp().getConfig()->getRigFollowModem());
1163 
1164     t_Rig = new std::thread(&RigThread::threadMain, rigThread);
1165 }
1166 
stopRig()1167 void CubicSDR::stopRig() {
1168     if (!rigThread) {
1169         return;
1170     }
1171 
1172     if (rigThread) {
1173         rigThread->terminate();
1174         rigThread->isTerminated(1000);
1175 
1176         if (rigThread->getErrorState()) {
1177             ActionDialog::showDialog(new ActionDialogRigError(rigThread->getErrorMessage()));
1178         }
1179     }
1180 
1181     if (t_Rig && t_Rig->joinable()) {
1182         t_Rig->join();
1183     }
1184 
1185     //now we can delete
1186     if (rigThread) {
1187 
1188         delete rigThread;
1189         rigThread = nullptr;
1190     }
1191 
1192     if (t_Rig) {
1193 
1194         delete t_Rig;
1195         t_Rig = nullptr;
1196     }
1197 }
1198 
rigIsActive()1199 bool CubicSDR::rigIsActive() {
1200     return (rigThread && !rigThread->isTerminated());
1201 }
1202 
1203 #endif
1204