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