diff --git a/CMakeLists.txt b/CMakeLists.txt index 3697dfd..9ddee92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -263,6 +263,8 @@ SET (cubicsdr_sources src/process/VisualProcessor.cpp src/process/ScopeVisualProcessor.cpp src/process/SpectrumVisualProcessor.cpp + src/process/FFTVisualDataThread.cpp + src/process/SpectrumVisualDataThread.cpp src/ui/GLPanel.cpp external/rtaudio/RtAudio.cpp external/lodepng/lodepng.cpp @@ -316,6 +318,8 @@ SET (cubicsdr_headers src/process/VisualProcessor.h src/process/ScopeVisualProcessor.h src/process/SpectrumVisualProcessor.h + src/process/FFTVisualDataThread.h + src/process/SpectrumVisualDataThread.h src/ui/GLPanel.h src/ui/UITestCanvas.cpp src/ui/UITestCanvas.h diff --git a/README.md b/README.md index 79a923b..cba51ff 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Features and Status: - [ ] Undo / Redo action - I/Q Recording and playback - [ ] Recording + - [ ] Record waterfall output to PNG file chunks of desired height - [ ] Record I/Q input data - [ ] Simultaneously record demod decimated I/Q and audio - [ ] Playback diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 0af69e7..12cd972 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -89,11 +89,11 @@ AppFrame::AppFrame() : demodModeSelectorCons->setHelpTip("Choose number of constallations types."); demodTray->Add(demodModeSelectorCons, 2, wxEXPAND | wxALL, 0); - wxGetApp().getDemodSpectrumProcesor()->setup(1024); + wxGetApp().getDemodSpectrumProcessor()->setup(1024); demodSpectrumCanvas = new SpectrumCanvas(this, attribList); demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000); demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0); - wxGetApp().getDemodSpectrumProcesor()->attachOutput(demodSpectrumCanvas->getVisualDataQueue()); + wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodSpectrumCanvas->getVisualDataQueue()); demodVisuals->AddSpacer(1); @@ -103,7 +103,7 @@ AppFrame::AppFrame() : demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas); demodSpectrumCanvas->attachWaterfallCanvas(demodWaterfallCanvas); demodVisuals->Add(demodWaterfallCanvas, 6, wxEXPAND | wxALL, 0); - wxGetApp().getDemodSpectrumProcesor()->attachOutput(demodWaterfallCanvas->getVisualDataQueue()); + wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodWaterfallCanvas->getVisualDataQueue()); demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0); @@ -140,9 +140,9 @@ AppFrame::AppFrame() : vbox->AddSpacer(1); wxBoxSizer *spectrumSizer = new wxBoxSizer(wxHORIZONTAL); - wxGetApp().getSpectrumProcesor()->setup(2048); + wxGetApp().getSpectrumProcessor()->setup(2048); spectrumCanvas = new SpectrumCanvas(this, attribList); - wxGetApp().getSpectrumProcesor()->attachOutput(spectrumCanvas->getVisualDataQueue()); + wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue()); spectrumAvgMeter = new MeterCanvas(this, attribList); spectrumAvgMeter->setHelpTip("Spectrum averaging speed, click or drag to adjust."); @@ -160,15 +160,14 @@ AppFrame::AppFrame() : wxBoxSizer *wfSizer = new wxBoxSizer(wxHORIZONTAL); - wxGetApp().getWaterfallProcesor()->setup(2048); waterfallCanvas = new WaterfallCanvas(this, attribList); waterfallCanvas->setup(2048, 512); - - fftDistrib.setInput(wxGetApp().getWaterfallVisualQueue()); - fftDistrib.attachOutput(&fftQueue); - - wxGetApp().getWaterfallProcesor()->setInput(&fftQueue); - wxGetApp().getWaterfallProcesor()->attachOutput(waterfallCanvas->getVisualDataQueue()); + + waterfallDataThread = new FFTVisualDataThread(); + t_FFTData = new std::thread(&FFTVisualDataThread::threadMain, waterfallDataThread); + + waterfallDataThread->setInputQueue("IQDataInput", wxGetApp().getWaterfallVisualQueue()); + waterfallDataThread->setOutputQueue("FFTDataOutput", waterfallCanvas->getVisualDataQueue()); waterfallSpeedMeter = new MeterCanvas(this, attribList); waterfallSpeedMeter->setHelpTip("Waterfall speed, click or drag to adjust (max 1024 lines per second)"); @@ -398,12 +397,12 @@ AppFrame::AppFrame() : float spectrumAvg = wxGetApp().getConfig()->getSpectrumAvgSpeed(); spectrumAvgMeter->setLevel(spectrumAvg); - wxGetApp().getSpectrumProcesor()->setFFTAverageRate(spectrumAvg); + wxGetApp().getSpectrumProcessor()->setFFTAverageRate(spectrumAvg); int wflps =wxGetApp().getConfig()->getWaterfallLinesPerSec(); waterfallSpeedMeter->setLevel(sqrt(wflps)); - fftDistrib.setLinesPerSecond(wflps); + waterfallDataThread->setLinesPerSecond(wflps); ThemeMgr::mgr.setTheme(wxGetApp().getConfig()->getTheme()); @@ -427,6 +426,8 @@ AppFrame::AppFrame() : } AppFrame::~AppFrame() { + waterfallDataThread->terminate(); + t_FFTData->join(); } @@ -512,9 +513,9 @@ void AppFrame::OnMenu(wxCommandEvent& event) { waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); spectrumCanvas->setBandwidth(wxGetApp().getSampleRate()); spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); - fftDistrib.setLinesPerSecond(DEFAULT_WATERFALL_LPS); + waterfallDataThread->setLinesPerSecond(DEFAULT_WATERFALL_LPS); waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS)); - wxGetApp().getSpectrumProcesor()->setFFTAverageRate(0.65); + wxGetApp().getSpectrumProcessor()->setFFTAverageRate(0.65); spectrumAvgMeter->setLevel(0.65); demodModeSelector->Refresh(); demodTuner->Refresh(); @@ -633,13 +634,17 @@ void AppFrame::OnMenu(wxCommandEvent& event) { } void AppFrame::OnClose(wxCloseEvent& event) { + wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodSpectrumCanvas->getVisualDataQueue()); + wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodWaterfallCanvas->getVisualDataQueue()); + wxGetApp().getSpectrumProcessor()->removeOutput(spectrumCanvas->getVisualDataQueue()); + wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize()); wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized()); wxGetApp().getConfig()->setTheme(ThemeMgr::mgr.getTheme()); wxGetApp().getConfig()->setSnap(wxGetApp().getFrequencySnap()); wxGetApp().getConfig()->setCenterFreq(wxGetApp().getFrequency()); - wxGetApp().getConfig()->setSpectrumAvgSpeed(wxGetApp().getSpectrumProcesor()->getFFTAverageRate()); - wxGetApp().getConfig()->setWaterfallLinesPerSec(fftDistrib.getLinesPerSecond()); + wxGetApp().getConfig()->setSpectrumAvgSpeed(wxGetApp().getSpectrumProcessor()->getFFTAverageRate()); + wxGetApp().getConfig()->setWaterfallLinesPerSec(waterfallDataThread->getLinesPerSecond()); wxGetApp().getConfig()->save(); event.Skip(); } @@ -802,75 +807,47 @@ void AppFrame::OnIdle(wxIdleEvent& event) { wxGetApp().getScopeProcessor()->run(); wxGetApp().getSpectrumDistributor()->run(); - SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcesor(); + SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor(); if (spectrumAvgMeter->inputChanged()) { float val = spectrumAvgMeter->getInputValue(); + if (val < 0.01) { + val = 0.01; + } spectrumAvgMeter->setLevel(val); proc->setFFTAverageRate(val); GetStatusBar()->SetStatusText(wxString::Format(wxT("Spectrum averaging speed changed to %0.2f%%."),val*100.0)); } - proc->setView(spectrumCanvas->getViewState()); - proc->setBandwidth(spectrumCanvas->getBandwidth()); - proc->setCenterFrequency(spectrumCanvas->getCenterFrequency()); + proc->setView(waterfallCanvas->getViewState()); + proc->setBandwidth(waterfallCanvas->getBandwidth()); + proc->setCenterFrequency(waterfallCanvas->getCenterFrequency()); - proc->run(); - - SpectrumVisualProcessor *dproc = wxGetApp().getDemodSpectrumProcesor(); + SpectrumVisualProcessor *dproc = wxGetApp().getDemodSpectrumProcessor(); dproc->setView(demodWaterfallCanvas->getViewState()); dproc->setBandwidth(demodWaterfallCanvas->getBandwidth()); dproc->setCenterFrequency(demodWaterfallCanvas->getCenterFrequency()); - - dproc->run(); - SpectrumVisualProcessor *wproc = wxGetApp().getWaterfallProcesor(); + SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor(); - int fftSize = wproc->getDesiredInputSize(); - - if (fftSize) { - fftDistrib.setFFTSize(fftSize); - } else { - fftDistrib.setFFTSize(DEFAULT_FFT_SIZE); - } - if (waterfallSpeedMeter->inputChanged()) { float val = waterfallSpeedMeter->getInputValue(); waterfallSpeedMeter->setLevel(val); - fftDistrib.setLinesPerSecond((int)ceil(val*val)); - wxGetApp().getWaterfallVisualQueue()->set_max_num_items((int)ceil(val*val)); - + waterfallDataThread->setLinesPerSecond((int)ceil(val*val)); GetStatusBar()->SetStatusText(wxString::Format(wxT("Waterfall max speed changed to %d lines per second."),(int)ceil(val*val))); } - fftDistrib.run(); - wproc->setView(waterfallCanvas->getViewState()); wproc->setBandwidth(waterfallCanvas->getBandwidth()); wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency()); - while (!wproc->isInputEmpty()) { - wproc->run(); - } - waterfallCanvas->processInputQueue(); demodWaterfallCanvas->processInputQueue(); - - if (this->IsVisible()) { - waterfallCanvas->DoPaint(); - demodWaterfallCanvas->DoPaint(); -#ifdef __APPLE__ - usleep(5000); -#endif - } else { -#ifndef _WIN32 - usleep(15000); -#else - Sleep(15); -#endif + if (!this->IsActive()) { + std::this_thread::sleep_for(std::chrono::milliseconds(25)); } event.RequestMore(); diff --git a/src/AppFrame.h b/src/AppFrame.h index 1d5ef25..9d68071 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -9,7 +9,8 @@ #include "MeterCanvas.h" #include "TuningCanvas.h" #include "ModeSelectorCanvas.h" -#include "UITestCanvas.h" +#include "FFTVisualDataThread.h" +//#include "UITestCanvas.h" #include @@ -97,8 +98,9 @@ private: std::string currentSessionFile; - FFTDataDistributor fftDistrib; - DemodulatorThreadInputQueue fftQueue; + FFTVisualDataThread *waterfallDataThread; + std::thread *t_FFTData; + wxDECLARE_EVENT_TABLE(); }; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 5f81911..dfbdd73 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -12,7 +12,6 @@ #endif #include "CubicSDR.h" -#include "FrequencyDialog.h" #ifdef _OSX_APP_ #include "CoreFoundation/CoreFoundation.h" @@ -35,7 +34,7 @@ class outbuf : public std::streambuf { CubicSDR::CubicSDR() : appframe(NULL), m_glContext(NULL), frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), directSamplingMode(0), - sdrThread(NULL), sdrPostThread(NULL), pipeSDRCommand(NULL), pipeSDRIQData(NULL), pipeIQVisualData(NULL), pipeAudioVisualData(NULL), t_SDR(NULL), t_PostSDR(NULL) { + sdrThread(NULL), sdrPostThread(NULL), spectrumVisualThread(NULL), demodVisualThread(NULL), pipeSDRCommand(NULL), pipeSDRIQData(NULL), pipeIQVisualData(NULL), pipeAudioVisualData(NULL), t_SDR(NULL), t_PostSDR(NULL) { } @@ -75,6 +74,9 @@ bool CubicSDR::OnInit() { directSamplingMode = 0; // Visual Data + spectrumVisualThread = new SpectrumVisualDataThread(); + demodVisualThread = new SpectrumVisualDataThread(); + pipeIQVisualData = new DemodulatorThreadInputQueue(); pipeIQVisualData->set_max_num_items(1); @@ -92,8 +94,8 @@ bool CubicSDR::OnInit() { spectrumDistributor.attachOutput(pipeDemodIQVisualData); spectrumDistributor.attachOutput(pipeSpectrumIQVisualData); - demodSpectrumProcessor.setInput(pipeDemodIQVisualData); - spectrumProcessor.setInput(pipeSpectrumIQVisualData); + getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData); + getSpectrumProcessor()->setInput(pipeSpectrumIQVisualData); pipeAudioVisualData = new DemodulatorThreadOutputQueue(); pipeAudioVisualData->set_max_num_items(1); @@ -104,7 +106,7 @@ bool CubicSDR::OnInit() { pipeSDRIQData = new SDRThreadIQDataQueue(); pipeSDRCommand = new SDRThreadCommandQueue(); - pipeSDRIQData->set_max_num_items(1); + pipeSDRIQData->set_max_num_items(100); sdrThread = new SDRThread(); sdrThread->setInputQueue("SDRCommandQueue",pipeSDRCommand); @@ -158,6 +160,8 @@ bool CubicSDR::OnInit() { t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread); t_SDR = new std::thread(&SDRThread::threadMain, sdrThread); + t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread); + t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread); appframe = new AppFrame(); if (dev != NULL) { @@ -191,6 +195,13 @@ int CubicSDR::OnExit() { std::cout << "Terminating SDR post-processing thread.." << std::endl; sdrPostThread->terminate(); t_PostSDR->join(); + + std::cout << "Terminating Visual Processor threads.." << std::endl; + spectrumVisualThread->terminate(); + t_SpectrumVisual->join(); + + demodVisualThread->terminate(); + t_DemodVisual->join(); delete sdrThread; delete t_SDR; @@ -198,6 +209,11 @@ int CubicSDR::OnExit() { delete sdrPostThread; delete t_PostSDR; + delete t_SpectrumVisual; + delete spectrumVisualThread; + delete t_DemodVisual; + delete demodVisualThread; + delete pipeSDRCommand; delete pipeIQVisualData; @@ -297,16 +313,12 @@ ScopeVisualProcessor *CubicSDR::getScopeProcessor() { return &scopeProcessor; } -SpectrumVisualProcessor *CubicSDR::getSpectrumProcesor() { - return &spectrumProcessor; +SpectrumVisualProcessor *CubicSDR::getSpectrumProcessor() { + return spectrumVisualThread->getProcessor(); } -SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcesor() { - return &demodSpectrumProcessor; -} - -SpectrumVisualProcessor *CubicSDR::getWaterfallProcesor() { - return &waterfallProcessor; +SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() { + return demodVisualThread->getProcessor(); } VisualDataDistributor *CubicSDR::getSpectrumDistributor() { @@ -416,8 +428,25 @@ int CubicSDR::getPPM() { } -void CubicSDR::showFrequencyInput() { - FrequencyDialog fdialog(appframe, -1, demodMgr.getActiveDemodulator()?_("Set Demodulator Frequency"):_("Set Center Frequency"), demodMgr.getActiveDemodulator(), wxPoint(-100,-100), wxSize(320, 75 )); +void CubicSDR::showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode) { + const wxString demodTitle("Set Demodulator Frequency"); + const wxString freqTitle("Set Center Frequency"); + const wxString bwTitle("Set Demodulator Bandwidth"); + + wxString title; + + switch (targetMode) { + case FrequencyDialog::FDIALOG_TARGET_DEFAULT: + title = demodMgr.getActiveDemodulator()?demodTitle:freqTitle; + break; + case FrequencyDialog::FDIALOG_TARGET_BANDWIDTH: + title = bwTitle; + break; + default: + break; + } + + FrequencyDialog fdialog(appframe, -1, title, demodMgr.getActiveDemodulator(), wxPoint(-100,-100), wxSize(320, 75 ), wxDEFAULT_DIALOG_STYLE, targetMode); fdialog.ShowModal(); } diff --git a/src/CubicSDR.h b/src/CubicSDR.h index 77dba79..a1d4aec 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -16,9 +16,11 @@ #include "DemodulatorMgr.h" #include "AppConfig.h" #include "AppFrame.h" +#include "FrequencyDialog.h" #include "ScopeVisualProcessor.h" #include "SpectrumVisualProcessor.h" +#include "SpectrumVisualDataThread.h" #include @@ -56,9 +58,8 @@ public: int getDevice(); ScopeVisualProcessor *getScopeProcessor(); - SpectrumVisualProcessor *getSpectrumProcesor(); - SpectrumVisualProcessor *getDemodSpectrumProcesor(); - SpectrumVisualProcessor *getWaterfallProcesor(); + SpectrumVisualProcessor *getSpectrumProcessor(); + SpectrumVisualProcessor *getDemodSpectrumProcessor(); VisualDataDistributor *getSpectrumDistributor(); DemodulatorThreadOutputQueue* getAudioVisualQueue(); @@ -78,7 +79,7 @@ public: void setPPM(int ppm_in); int getPPM(); - void showFrequencyInput(); + void showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode = FrequencyDialog::FDIALOG_TARGET_DEFAULT); private: AppFrame *appframe; @@ -96,6 +97,8 @@ private: SDRThread *sdrThread; SDRPostThread *sdrPostThread; + SpectrumVisualDataThread *spectrumVisualThread; + SpectrumVisualDataThread *demodVisualThread; SDRThreadCommandQueue* pipeSDRCommand; SDRThreadIQDataQueue* pipeSDRIQData; @@ -106,14 +109,13 @@ private: DemodulatorThreadInputQueue* pipeWaterfallIQVisualData; ScopeVisualProcessor scopeProcessor; - SpectrumVisualProcessor spectrumProcessor; - SpectrumVisualProcessor waterfallProcessor; - SpectrumVisualProcessor demodSpectrumProcessor; VisualDataDistributor spectrumDistributor; - + std::thread *t_SDR; std::thread *t_PostSDR; + std::thread *t_SpectrumVisual; + std::thread *t_DemodVisual; }; static const wxCmdLineEntryDesc commandLineInfo [] = diff --git a/src/FrequencyDialog.cpp b/src/FrequencyDialog.cpp index 712739e..bed49cd 100644 --- a/src/FrequencyDialog.cpp +++ b/src/FrequencyDialog.cpp @@ -10,15 +10,22 @@ EVT_CHAR_HOOK(FrequencyDialog::OnChar) wxEND_EVENT_TABLE() FrequencyDialog::FrequencyDialog(wxWindow * parent, wxWindowID id, const wxString & title, DemodulatorInstance *demod, const wxPoint & position, - const wxSize & size, long style) : + const wxSize & size, long style, FrequencyDialogTarget targetMode) : wxDialog(parent, id, title, position, size, style) { wxString freqStr; activeDemod = demod; + this->targetMode = targetMode; - if (activeDemod) { - freqStr = frequencyToStr(activeDemod->getFrequency()); - } else { - freqStr = frequencyToStr(wxGetApp().getFrequency()); + if (targetMode == FDIALOG_TARGET_DEFAULT) { + if (activeDemod) { + freqStr = frequencyToStr(activeDemod->getFrequency()); + } else { + freqStr = frequencyToStr(wxGetApp().getFrequency()); + } + } + + if (targetMode == FDIALOG_TARGET_BANDWIDTH) { + freqStr = frequencyToStr(wxGetApp().getDemodMgr().getLastBandwidth()); } dialogText = new wxTextCtrl(this, wxID_FREQ_INPUT, freqStr, wxPoint(6, 1), wxSize(size.GetWidth() - 20, size.GetHeight() - 70), @@ -109,14 +116,23 @@ void FrequencyDialog::OnChar(wxKeyEvent& event) { case WXK_NUMPAD_ENTER: // Do Stuff freq = strToFrequency(dialogText->GetValue().ToStdString()); - if (activeDemod) { - activeDemod->setTracking(true); - activeDemod->setFollow(true); - activeDemod->setFrequency(freq); - activeDemod->updateLabel(freq); - } else { - wxGetApp().setFrequency(freq); - } + if (targetMode == FDIALOG_TARGET_DEFAULT) { + if (activeDemod) { + activeDemod->setTracking(true); + activeDemod->setFollow(true); + activeDemod->setFrequency(freq); + activeDemod->updateLabel(freq); + } else { + wxGetApp().setFrequency(freq); + } + } + if (targetMode == FDIALOG_TARGET_BANDWIDTH) { + if (activeDemod) { + activeDemod->setBandwidth(freq); + } else { + wxGetApp().getDemodMgr().setLastBandwidth(freq); + } + } Close(); break; case WXK_ESCAPE: diff --git a/src/FrequencyDialog.h b/src/FrequencyDialog.h index 42ad7b0..4f2b931 100644 --- a/src/FrequencyDialog.h +++ b/src/FrequencyDialog.h @@ -11,12 +11,13 @@ class FrequencyDialog: public wxDialog { public: - + typedef enum FrequencyDialogTarget { FDIALOG_TARGET_DEFAULT, FDIALOG_TARGET_CENTERFREQ, FDIALOG_TARGET_FREQ, FDIALOG_TARGET_BANDWIDTH } FrequencyDialogTarget; FrequencyDialog ( wxWindow * parent, wxWindowID id, const wxString & title, DemodulatorInstance *demod = NULL, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, - long style = wxDEFAULT_DIALOG_STYLE ); + long style = wxDEFAULT_DIALOG_STYLE, + FrequencyDialogTarget targetMode = FDIALOG_TARGET_DEFAULT); wxTextCtrl * dialogText; @@ -28,5 +29,6 @@ private: void OnEnter ( wxCommandEvent &event ); void OnChar ( wxKeyEvent &event ); std::string& filterChars(std::string& s, const std::string& allowed); + FrequencyDialogTarget targetMode; DECLARE_EVENT_TABLE() }; diff --git a/src/process/FFTVisualDataThread.cpp b/src/process/FFTVisualDataThread.cpp new file mode 100644 index 0000000..0e92dbb --- /dev/null +++ b/src/process/FFTVisualDataThread.cpp @@ -0,0 +1,64 @@ +#include "FFTVisualDataThread.h" +#include "CubicSDR.h" + +FFTVisualDataThread::FFTVisualDataThread() : linesPerSecond(DEFAULT_WATERFALL_LPS) { + lpsChanged.store(true); +} + +FFTVisualDataThread::~FFTVisualDataThread() { + +} + +void FFTVisualDataThread::setLinesPerSecond(int lps) { + linesPerSecond.store(lps); + lpsChanged.store(true); +} + +int FFTVisualDataThread::getLinesPerSecond() { + return linesPerSecond.load(); +} + +SpectrumVisualProcessor *FFTVisualDataThread::getProcessor() { + return &wproc; +} + +void FFTVisualDataThread::run() { + DemodulatorThreadInputQueue *pipeIQDataIn = (DemodulatorThreadInputQueue *)getInputQueue("IQDataInput"); + SpectrumVisualDataQueue *pipeFFTDataOut = (SpectrumVisualDataQueue *)getOutputQueue("FFTDataOutput"); + + fftDistrib.setInput(pipeIQDataIn); + fftDistrib.attachOutput(&fftQueue); + wproc.setInput(&fftQueue); + wproc.attachOutput(pipeFFTDataOut); + wproc.setup(2048); + + std::cout << "FFT visual data thread started." << std::endl; + + while(!terminated) { + + std::this_thread::sleep_for(std::chrono::milliseconds(12)); + + int fftSize = wproc.getDesiredInputSize(); + + if (fftSize) { + fftDistrib.setFFTSize(fftSize); + } else { + fftDistrib.setFFTSize(DEFAULT_FFT_SIZE); + } + + if (lpsChanged.load()) { + fftDistrib.setLinesPerSecond(linesPerSecond.load()); + pipeIQDataIn->set_max_num_items(linesPerSecond.load()); + lpsChanged.store(false); + } + + fftDistrib.run(); + + while (!wproc.isInputEmpty()) { + wproc.run(); + } + } + + std::cout << "FFT visual data thread done." << std::endl; +} + diff --git a/src/process/FFTVisualDataThread.h b/src/process/FFTVisualDataThread.h new file mode 100644 index 0000000..a08cd4d --- /dev/null +++ b/src/process/FFTVisualDataThread.h @@ -0,0 +1,24 @@ +#pragma once + +#include "IOThread.h" +#include "SpectrumVisualProcessor.h" + +class FFTVisualDataThread : public IOThread { +public: + FFTVisualDataThread(); + ~FFTVisualDataThread(); + + void setLinesPerSecond(int lps); + int getLinesPerSecond(); + SpectrumVisualProcessor *getProcessor(); + + void run(); + +protected: + FFTDataDistributor fftDistrib; + DemodulatorThreadInputQueue fftQueue; + SpectrumVisualProcessor wproc; + + std::atomic_int linesPerSecond; + std::atomic_bool lpsChanged; +}; diff --git a/src/process/SpectrumVisualDataThread.cpp b/src/process/SpectrumVisualDataThread.cpp new file mode 100644 index 0000000..cd737f4 --- /dev/null +++ b/src/process/SpectrumVisualDataThread.cpp @@ -0,0 +1,25 @@ +#include "SpectrumVisualDataThread.h" +#include "CubicSDR.h" + +SpectrumVisualDataThread::SpectrumVisualDataThread() { +} + +SpectrumVisualDataThread::~SpectrumVisualDataThread() { + +} + +SpectrumVisualProcessor *SpectrumVisualDataThread::getProcessor() { + return &sproc; +} + +void SpectrumVisualDataThread::run() { + std::cout << "Spectrum visual data thread started." << std::endl; + + while(!terminated) { + std::this_thread::sleep_for(std::chrono::milliseconds(12)); + sproc.run(); + } + + std::cout << "Spectrum visual data thread done." << std::endl; +} + diff --git a/src/process/SpectrumVisualDataThread.h b/src/process/SpectrumVisualDataThread.h new file mode 100644 index 0000000..017b519 --- /dev/null +++ b/src/process/SpectrumVisualDataThread.h @@ -0,0 +1,16 @@ +#pragma once + +#include "IOThread.h" +#include "SpectrumVisualProcessor.h" + +class SpectrumVisualDataThread : public IOThread { +public: + SpectrumVisualDataThread(); + ~SpectrumVisualDataThread(); + SpectrumVisualProcessor *getProcessor(); + + void run(); + +protected: + SpectrumVisualProcessor sproc; +}; diff --git a/src/process/SpectrumVisualProcessor.h b/src/process/SpectrumVisualProcessor.h index fd7f013..8e1e694 100644 --- a/src/process/SpectrumVisualProcessor.h +++ b/src/process/SpectrumVisualProcessor.h @@ -111,7 +111,10 @@ protected: if (inp) { if (inp->data.size() >= fftSize) { - for (int i = 0, iMax = inp->data.size()-fftSize; i <= iMax; i += fftSize) { + if (lineRateAccum + (lineRateStep * floor((double)inp->data.size()/(double)fftSize)) < 1.0) { + // move along, nothing to see here.. + lineRateAccum += (lineRateStep * inp->data.size()/fftSize); + } else for (int i = 0, iMax = inp->data.size()-fftSize; i <= iMax; i += fftSize) { lineRateAccum += lineRateStep; if (lineRateAccum >= 1.0) { diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index e4a9d2b..e19cc51 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -221,14 +221,14 @@ void SDRPostThread::run() { data_in->decRefCount(); } - buffers.purge(); +// buffers.purge(); if (iqVisualQueue && !iqVisualQueue->empty()) { DemodulatorThreadIQData *visualDataDummy; iqVisualQueue->pop(visualDataDummy); } - visualDataBuffers.purge(); +// visualDataBuffers.purge(); std::cout << "SDR post-processing thread done." << std::endl; } diff --git a/src/sdr/SDRThread.cpp b/src/sdr/SDRThread.cpp index a9a0d43..81f4cc0 100644 --- a/src/sdr/SDRThread.cpp +++ b/src/sdr/SDRThread.cpp @@ -295,7 +295,7 @@ void SDRThread::run() { } } - buffers.purge(); +// buffers.purge(); std::cout << "SDR thread done." << std::endl; } diff --git a/src/util/GLExt.cpp b/src/util/GLExt.cpp index eb1e7d6..88785a9 100644 --- a/src/util/GLExt.cpp +++ b/src/util/GLExt.cpp @@ -32,9 +32,8 @@ void initGLExtensions() { return; } - const GLubyte *extensions = glGetString(GL_EXTENSIONS); - - std::cout << std::endl << "Supported GL Extensions: " << std::endl << extensions << std::endl << std::endl; +// const GLubyte *extensions = glGetString(GL_EXTENSIONS); +// std::cout << std::endl << "Supported GL Extensions: " << std::endl << extensions << std::endl << std::endl; #ifdef __linux__ const GLint interval = 2; diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index f21a6a8..a35aca5 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -22,6 +22,7 @@ EVT_IDLE(SpectrumCanvas::OnIdle) EVT_MOTION(SpectrumCanvas::OnMouseMoved) EVT_LEFT_DOWN(SpectrumCanvas::OnMouseDown) EVT_LEFT_UP(SpectrumCanvas::OnMouseReleased) +EVT_ENTER_WINDOW(SpectrumCanvas::OnMouseEnterWindow) EVT_LEAVE_WINDOW(SpectrumCanvas::OnMouseLeftWindow) EVT_MOUSEWHEEL(SpectrumCanvas::OnMouseWheelMoved) wxEND_EVENT_TABLE() @@ -152,6 +153,11 @@ void SpectrumCanvas::OnMouseReleased(wxMouseEvent& event) { SetCursor(wxCURSOR_SIZEWE); } +void SpectrumCanvas::OnMouseEnterWindow(wxMouseEvent& event) { + InteractiveCanvas::OnMouseEnterWindow(event); + SetCursor(wxCURSOR_SIZEWE); +} + void SpectrumCanvas::OnMouseLeftWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseLeftWindow(event); SetCursor(wxCURSOR_SIZEWE); @@ -163,4 +169,4 @@ void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) { SpectrumVisualDataQueue *SpectrumCanvas::getVisualDataQueue() { return &visualDataQueue; -} \ No newline at end of file +} diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index 6c891ae..af07f6c 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -30,6 +30,7 @@ private: void OnMouseDown(wxMouseEvent& event); void OnMouseWheelMoved(wxMouseEvent& event); void OnMouseReleased(wxMouseEvent& event); + void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); PrimaryGLContext *glContext; diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp index 8a85ac4..b6fa267 100644 --- a/src/visual/TuningCanvas.cpp +++ b/src/visual/TuningCanvas.cpp @@ -297,7 +297,7 @@ void TuningCanvas::OnMouseMoved(wxMouseEvent& event) { setStatusText("Click, wheel or drag a digit to change frequency; SPACE for direct input. Right click to set/clear snap. Hold ALT to change PPM. Hold SHIFT to disable carry."); break; case TUNING_HOVER_BW: - setStatusText("Click, wheel or drag a digit to change bandwidth. Hold SHIFT to disable carry."); + setStatusText("Click, wheel or drag a digit to change bandwidth; SPACE for direct input. Hold SHIFT to disable carry."); break; case TUNING_HOVER_CENTER: setStatusText("Click, wheel or drag a digit to change center frequency; SPACE for direct input. Hold SHIFT to disable carry."); @@ -407,8 +407,12 @@ void TuningCanvas::setHelpTip(std::string tip) { void TuningCanvas::OnKeyDown(wxKeyEvent& event) { InteractiveCanvas::OnKeyDown(event); - if (event.GetKeyCode() == WXK_SPACE && (hoverState == TUNING_HOVER_CENTER || hoverState == TUNING_HOVER_FREQ)) { - wxGetApp().showFrequencyInput(); + if (event.GetKeyCode() == WXK_SPACE) { + if (hoverState == TUNING_HOVER_CENTER || hoverState == TUNING_HOVER_FREQ) { + wxGetApp().showFrequencyInput(FrequencyDialog::FDIALOG_TARGET_DEFAULT); + } else if (hoverState == TUNING_HOVER_BW) { + wxGetApp().showFrequencyInput(FrequencyDialog::FDIALOG_TARGET_BANDWIDTH); + } } } diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 00bc773..f2d2578 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -88,12 +88,8 @@ void WaterfallCanvas::processInputQueue() { } void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { -// event.Skip(); -} - -void WaterfallCanvas::DoPaint() { - wxClientDC dc(this); - // wxPaintDC dc(this); +// wxClientDC dc(this); + wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); long double currentZoom = zoom; @@ -109,45 +105,49 @@ void WaterfallCanvas::DoPaint() { long long bw; if (currentZoom != 1) { long long freq = wxGetApp().getFrequency(); - + bw = getBandwidth(); + + double mpos = 0; + float mouseInView = false; + + if (mouseTracker.mouseInView()) { + mpos = mouseTracker.getMouseX(); + mouseInView = true; + } else if (spectrumCanvas && spectrumCanvas->getMouseTracker()->mouseInView()) { + mpos = spectrumCanvas->getMouseTracker()->getMouseX(); + mouseInView = true; + } + if (currentZoom < 1) { - centerFreq = getCenterFrequency(); - bw = getBandwidth(); bw = (long long) ceil((long double) bw * currentZoom); if (bw < 30000) { bw = 30000; } - if (mouseTracker.mouseInView()) { - long long mfreqA = getFrequencyAt(mouseTracker.getMouseX()); + if (mouseInView) { + long long mfreqA = getFrequencyAt(mpos); setBandwidth(bw); - long long mfreqB = getFrequencyAt(mouseTracker.getMouseX()); + long long mfreqB = getFrequencyAt(mpos); centerFreq += mfreqA - mfreqB; } setView(centerFreq, bw); - if (spectrumCanvas) { - spectrumCanvas->setView(centerFreq, bw); - } } else { if (isView) { - bw = getBandwidth(); bw = (long long) ceil((long double) bw * currentZoom); + if (bw >= wxGetApp().getSampleRate()) { disableView(); if (spectrumCanvas) { spectrumCanvas->disableView(); } } else { - if (mouseTracker.mouseInView()) { - long long mfreqA = getFrequencyAt(mouseTracker.getMouseX()); + if (mouseInView) { + long long mfreqA = getFrequencyAt(mpos); setBandwidth(bw); - long long mfreqB = getFrequencyAt(mouseTracker.getMouseX()); + long long mfreqB = getFrequencyAt(mpos); centerFreq += mfreqA - mfreqB; - } - - setView(getCenterFrequency(), bw); - if (spectrumCanvas) { - spectrumCanvas->setView(centerFreq, bw); + } else { + setBandwidth(bw); } } } @@ -158,8 +158,15 @@ void WaterfallCanvas::DoPaint() { if (centerFreq > freq && (centerFreq + bandwidth / 2) > (freq + wxGetApp().getSampleRate() / 2)) { centerFreq = (freq + wxGetApp().getSampleRate() / 2) - bandwidth / 2; } + + if (spectrumCanvas) { + if ((spectrumCanvas->getCenterFrequency() != centerFreq) || (spectrumCanvas->getBandwidth() != bw)) { + spectrumCanvas->setView(centerFreq,bw); + } + } } + glContext->SetCurrent(*this); initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); @@ -256,10 +263,16 @@ void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) { ctrlDown = event.ControlDown(); switch (event.GetKeyCode()) { case 'A': - zoom = 1.0; + case WXK_UP: + case WXK_NUMPAD_UP: + zoom = 1.0; + mouseZoom = 0.95; break; case 'Z': - zoom = 1.0; + case WXK_DOWN: + case WXK_NUMPAD_DOWN: + zoom = 1.0; + mouseZoom = 1.05; break; } } @@ -269,68 +282,37 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); - long long freq; - long long originalFreq; + long long originalFreq = getCenterFrequency(); + long long freq = originalFreq; + switch (event.GetKeyCode()) { case 'A': - zoom = 0.95; + case WXK_UP: + case WXK_NUMPAD_UP: + mouseZoom = 1.0; + zoom = 0.95; break; case 'Z': - zoom = 1.05; + case WXK_DOWN: + case WXK_NUMPAD_DOWN: + mouseZoom = 1.0; + zoom = 1.05; break; case WXK_RIGHT: - freq = wxGetApp().getFrequency(); - originalFreq = freq; + case WXK_NUMPAD_RIGHT: if (shiftDown) { - freq += wxGetApp().getSampleRate() * 10; - if (isView) { - setView(centerFreq + (freq - originalFreq), getBandwidth()); - if (spectrumCanvas) { - spectrumCanvas->setView(getCenterFrequency(), getBandwidth()); - } - } + freq += getBandwidth() * 10; } else { - freq += wxGetApp().getSampleRate() / 2; - if (isView) { - setView(centerFreq + (freq - originalFreq), getBandwidth()); - if (spectrumCanvas) { - spectrumCanvas->setView(getCenterFrequency(), getBandwidth()); - } - } + freq += getBandwidth() / 2; } - wxGetApp().setFrequency(freq); - setStatusText("Set center frequency: %s", freq); break; case WXK_LEFT: - freq = wxGetApp().getFrequency(); - originalFreq = freq; + case WXK_NUMPAD_LEFT: if (shiftDown) { - if ((freq - wxGetApp().getSampleRate() * 10) < wxGetApp().getSampleRate() / 2) { - freq = wxGetApp().getSampleRate() / 2; - } else { - freq -= wxGetApp().getSampleRate() * 10; - } - if (isView) { - setView(centerFreq + (freq - originalFreq), getBandwidth()); - if (spectrumCanvas) { - spectrumCanvas->setView(getCenterFrequency(), getBandwidth()); - } - } + freq -= getBandwidth() * 10; } else { - if ((freq - wxGetApp().getSampleRate() / 2) < wxGetApp().getSampleRate() / 2) { - freq = wxGetApp().getSampleRate() / 2; - } else { - freq -= wxGetApp().getSampleRate() / 2; - } - if (isView) { - setView(centerFreq + (freq - originalFreq), getBandwidth()); - if (spectrumCanvas) { - spectrumCanvas->setView(getCenterFrequency(), getBandwidth()); - } - } + freq -= getBandwidth() / 2; } - wxGetApp().setFrequency(freq); - setStatusText("Set center frequency: %s", freq); break; case 'D': case WXK_DELETE: @@ -357,11 +339,44 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { event.Skip(); return; } + + long long minFreq = wxGetApp().getSampleRate()/2; + if (freq < minFreq) { + freq = minFreq; + } + + if (freq != originalFreq) { + if (isView) { + setView(freq, getBandwidth()); + if (spectrumCanvas) { + spectrumCanvas->setView(freq, getBandwidth()); + } + + long long minFreq = wxGetApp().getFrequency()-(wxGetApp().getSampleRate()/2); + long long maxFreq = wxGetApp().getFrequency()+(wxGetApp().getSampleRate()/2); + + if (freq < minFreq) { + wxGetApp().setFrequency(freq+(wxGetApp().getSampleRate()/2)); + setStatusText("Set center frequency: %s", freq); + } + if (freq > maxFreq) { + wxGetApp().setFrequency(freq-(wxGetApp().getSampleRate()/2)); + setStatusText("Set center frequency: %s", freq); + } + } else { + if (spectrumCanvas) { + spectrumCanvas->setCenterFrequency(freq); + } + wxGetApp().setFrequency(freq); + setStatusText("Set center frequency: %s", freq); + } + } + } void WaterfallCanvas::OnIdle(wxIdleEvent &event) { -// Refresh(); -// event.RequestMore(); - event.Skip(); + Refresh(); + event.RequestMore(); +// event.Skip(); } void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 1b59e35..2f51ec3 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -28,7 +28,6 @@ public: void attachSpectrumCanvas(SpectrumCanvas *canvas_in); void processInputQueue(); SpectrumVisualDataQueue *getVisualDataQueue(); - void DoPaint(); private: void OnPaint(wxPaintEvent& event);