From a3f8bc08e70e1e4700ce7549a5a760322d189a0c Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sat, 27 Dec 2014 23:47:56 -0500 Subject: [PATCH 01/20] Setting up layout for active demodulator UI --- src/AppFrame.cpp | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index ce3ad1f..74454e8 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -18,6 +18,8 @@ #include +#include + wxBEGIN_EVENT_TABLE(AppFrame, wxFrame) //EVT_MENU(wxID_NEW, AppFrame::OnNewWindow) EVT_MENU(wxID_CLOSE, AppFrame::OnClose) @@ -29,9 +31,43 @@ AppFrame::AppFrame() : wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *demodOpts = new wxBoxSizer(wxVERTICAL); + + demodTray->AddSpacer(5); + demodOpts->AddSpacer(5); + + wxStaticText *audioDeviceLabel = new wxStaticText(this, wxID_ANY, wxString("Audio Device:")); + demodOpts->Add(audioDeviceLabel, 1, wxFIXED_MINSIZE | wxALL, 0); + + wxArrayString str; + str.Add("Primary Device"); + wxChoice *wxCh = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, str); + demodOpts->Add(wxCh, 1, wxFIXED_MINSIZE | wxALL, 0); + + demodOpts->AddSpacer(2); + + wxStaticText *demodTypeLabel = new wxStaticText(this, wxID_ANY, wxString("Demodulation:")); + demodOpts->Add(demodTypeLabel, 1, wxFIXED_MINSIZE | wxALL, 0); + + str.Clear(); + str.Add("FM"); + str.Add("FM Stereo"); + str.Add("AM"); + str.Add("LSB"); + str.Add("USB"); + wxChoice *wxDemodChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, str); + demodOpts->Add(wxDemodChoice, 1, wxFIXED_MINSIZE | wxALL, 0); + + demodOpts->AddSpacer(5); + demodTray->AddSpacer(5); + + demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0); scopeCanvas = new ScopeCanvas(this, NULL); - vbox->Add(scopeCanvas, 1, wxEXPAND | wxALL, 0); + demodTray->Add(scopeCanvas, 7, wxEXPAND | wxALL, 0); + + vbox->Add(demodTray, 1, wxEXPAND | wxALL, 0); vbox->AddSpacer(2); spectrumCanvas = new SpectrumCanvas(this, NULL); vbox->Add(spectrumCanvas, 1, wxEXPAND | wxALL, 0); @@ -70,8 +106,6 @@ AppFrame::AppFrame() : AppFrame::~AppFrame() { -// delete t_SDR; - } void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { From 1e970f4373856b98cb9158a1b4a7c3688834f6e4 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 28 Dec 2014 05:13:46 -0500 Subject: [PATCH 02/20] Zoomed-in demodulator waterfall somewhat working - update demod WF from demodulator is jittery - adjustments with immediate update is awkward --- src/AppFrame.cpp | 32 ++- src/AppFrame.h | 3 +- src/CubicSDR.cpp | 1 + src/CubicSDRDefs.h | 2 +- src/audio/AudioThread.cpp | 4 +- src/demod/DemodulatorPreThread.cpp | 13 +- src/sdr/SDRPostThread.cpp | 14 +- src/sdr/SDRPostThread.h | 4 + src/visual/PrimaryGLContext.cpp | 24 +- src/visual/PrimaryGLContext.h | 6 +- src/visual/SpectrumCanvas.cpp | 86 ++++--- src/visual/SpectrumCanvas.h | 4 +- src/visual/SpectrumContext.h | 1 + src/visual/WaterfallCanvas.cpp | 387 +++++++++++++++++++---------- src/visual/WaterfallCanvas.h | 35 ++- src/visual/WaterfallContext.cpp | 41 ++- src/visual/WaterfallContext.h | 7 +- 17 files changed, 461 insertions(+), 203 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 74454e8..0a24ce2 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -28,7 +28,7 @@ EVT_IDLE(AppFrame::OnIdle) wxEND_EVENT_TABLE() AppFrame::AppFrame() : - wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { +wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); @@ -64,15 +64,22 @@ AppFrame::AppFrame() : demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0); + demodWaterfallCanvas = new WaterfallCanvas(this, NULL); + demodWaterfallCanvas->Setup(1024,128); + demodWaterfallCanvas->SetView(DEFAULT_FREQ,300000); + demodTray->Add(demodWaterfallCanvas, 7, wxEXPAND | wxALL, 0); + scopeCanvas = new ScopeCanvas(this, NULL); demodTray->Add(scopeCanvas, 7, wxEXPAND | wxALL, 0); vbox->Add(demodTray, 1, wxEXPAND | wxALL, 0); vbox->AddSpacer(2); spectrumCanvas = new SpectrumCanvas(this, NULL); + spectrumCanvas->Setup(2048); vbox->Add(spectrumCanvas, 1, wxEXPAND | wxALL, 0); vbox->AddSpacer(2); waterfallCanvas = new WaterfallCanvas(this, NULL); + waterfallCanvas->Setup(2048,512); vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0); this->SetSizer(vbox); @@ -129,14 +136,27 @@ void AppFrame::OnIdle(wxIdleEvent& event) { // std::this_thread::sleep_for(std::chrono::milliseconds(4)); // std::this_thread::yield(); //#endif + + DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); + + if (demod) { + if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) { + demodWaterfallCanvas->SetCenterFrequency(demod->getParams().frequency); + } + unsigned int demodBw = (unsigned int) ceil((float) demod->getParams().bandwidth * 1.5); + if (demodBw != demodWaterfallCanvas->GetBandwidth()) { + demodWaterfallCanvas->SetBandwidth(demodBw); + } + } + if (!wxGetApp().getIQVisualQueue()->empty()) { DemodulatorThreadIQData *iqData; wxGetApp().getIQVisualQueue()->pop(iqData); if (iqData && iqData->data.size()) { - spectrumCanvas->setData(&iqData->data); - waterfallCanvas->setData(&iqData->data); - + spectrumCanvas->setData(iqData); + waterfallCanvas->setData(iqData); + demodWaterfallCanvas->setData(iqData); delete iqData; } else { std::cout << "Incoming IQ data empty?" << std::endl; @@ -148,8 +168,8 @@ void AppFrame::OnIdle(wxIdleEvent& event) { AudioThreadInput *demodAudioData; wxGetApp().getAudioVisualQueue()->pop(demodAudioData); if (demodAudioData && demodAudioData->data.size()) { - if (scopeCanvas->waveform_points.size() != demodAudioData->data.size()*2) { - scopeCanvas->waveform_points.resize(demodAudioData->data.size()*2); + if (scopeCanvas->waveform_points.size() != demodAudioData->data.size() * 2) { + scopeCanvas->waveform_points.resize(demodAudioData->data.size() * 2); } for (int i = 0, iMax = demodAudioData->data.size(); i < iMax; i++) { diff --git a/src/AppFrame.h b/src/AppFrame.h index 15c20a3..3c87d95 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -23,7 +23,8 @@ private: ScopeCanvas *scopeCanvas; SpectrumCanvas *spectrumCanvas; WaterfallCanvas *waterfallCanvas; + WaterfallCanvas *demodWaterfallCanvas; // event table -wxDECLARE_EVENT_TABLE(); + wxDECLARE_EVENT_TABLE(); }; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index f2e6e8a..142073d 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -28,6 +28,7 @@ bool CubicSDR::OnInit() { sdrThread = new SDRThread(threadCmdQueueSDR); sdrPostThread = new SDRPostThread(); + sdrPostThread->setNumVisSamples(2048); iqPostDataQueue = new SDRThreadIQDataQueue; iqVisualQueue = new DemodulatorThreadInputQueue; diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 7a8bb8e..3d08ed3 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -7,7 +7,7 @@ #define BUF_SIZE (16384*4) #define SRATE 2500000 #endif -#define FFT_SIZE 2048 +#define DEFAULT_FFT_SIZE 2048 #define DEFAULT_FREQ 98900000 #define AUDIO_FREQUENCY 44100 diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index 346268f..ec0c242 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -367,7 +367,7 @@ void AudioThread::setActive(bool state) { while (!inputQueue->empty()) { // flush queue inputQueue->pop(dummy); if (dummy) { - delete dummy; + dummy->decRefCount(); } } deviceController[parameters.deviceId]->bindThread(this); @@ -376,7 +376,7 @@ void AudioThread::setActive(bool state) { while (!inputQueue->empty()) { // flush queue inputQueue->pop(dummy); if (dummy) { - delete dummy; + dummy->decRefCount(); } } } diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp index 5acf025..9223e84 100644 --- a/src/demod/DemodulatorPreThread.cpp +++ b/src/demod/DemodulatorPreThread.cpp @@ -9,8 +9,8 @@ DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn, DemodulatorThreadPostInputQueue* pQueueOut, DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) : - inputQueue(pQueueIn), postInputQueue(pQueueOut), terminated(false), initialized(false), audio_resampler(NULL), stereo_resampler(NULL), resample_ratio(1), audio_resample_ratio( - 1), resampler(NULL), commandQueue(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl( + inputQueue(pQueueIn), postInputQueue(pQueueOut), terminated(false), initialized(false), audio_resampler(NULL), stereo_resampler(NULL), resample_ratio( + 1), audio_resample_ratio(1), resampler(NULL), commandQueue(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl( threadQueueControl) { float kf = 0.5; // modulation factor @@ -53,7 +53,6 @@ void DemodulatorPreThread::initialize() { } stereo_resampler = msresamp_rrrf_create(audio_resample_ratio, As); - initialized = true; // std::cout << "inputResampleRate " << params.bandwidth << std::endl; @@ -69,12 +68,12 @@ DemodulatorPreThread::~DemodulatorPreThread() { #ifdef __APPLE__ void *DemodulatorPreThread::threadMain() { #else -void DemodulatorPreThread::threadMain() { + void DemodulatorPreThread::threadMain() { #endif #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread - int priority = sched_get_priority_max( SCHED_FIFO )-1; - sched_param prio = {priority}; // scheduling priority of thread + int priority = sched_get_priority_max( SCHED_FIFO) - 1; + sched_param prio = { priority }; // scheduling priority of thread pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif @@ -162,7 +161,7 @@ void DemodulatorPreThread::threadMain() { out_buf_data.resize(bufSize); } - in_buf_data.assign(inp->data.begin(),inp->data.end()); + in_buf_data.assign(inp->data.begin(), inp->data.end()); liquid_float_complex *in_buf = &in_buf_data[0]; liquid_float_complex *out_buf = &out_buf_data[0]; diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index bb93b6d..9322410 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -6,7 +6,7 @@ #include SDRPostThread::SDRPostThread() : - sample_rate(SRATE), iqDataOutQueue(NULL), iqDataInQueue(NULL), iqVisualQueue(NULL), terminated(false), dcFilter(NULL) { + sample_rate(SRATE), iqDataOutQueue(NULL), iqDataInQueue(NULL), iqVisualQueue(NULL), terminated(false), dcFilter(NULL), num_vis_samples(2048) { } SDRPostThread::~SDRPostThread() { @@ -34,6 +34,14 @@ void SDRPostThread::setIQVisualQueue(DemodulatorThreadInputQueue *iqVisQueue) { iqVisualQueue = iqVisQueue; } +void SDRPostThread::setNumVisSamples(int num_vis_samples_in) { + num_vis_samples = num_vis_samples_in; +} + +int SDRPostThread::getNumVisSamples() { + return num_vis_samples; +} + void SDRPostThread::threadMain() { int n_read; double seconds = 0.0; @@ -89,7 +97,9 @@ void SDRPostThread::threadMain() { if (iqVisualQueue != NULL && iqVisualQueue.load()->empty()) { DemodulatorThreadIQData *visualDataOut = new DemodulatorThreadIQData; - visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + FFT_SIZE); + visualDataOut->frequency = data_in->frequency; + visualDataOut->bandwidth = data_in->bandwidth; + visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + num_vis_samples); iqVisualQueue.load()->push(visualDataOut); } diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h index e70f082..7b222a7 100644 --- a/src/sdr/SDRPostThread.h +++ b/src/sdr/SDRPostThread.h @@ -15,6 +15,9 @@ public: void setIQDataOutQueue(DemodulatorThreadInputQueue* iqDataQueue); void setIQVisualQueue(DemodulatorThreadInputQueue* iqVisQueue); + void setNumVisSamples(int num_vis_samples_in); + int getNumVisSamples(); + void threadMain(); void terminate(); @@ -30,4 +33,5 @@ protected: std::vector demodulators_remove; std::atomic terminated; iirfilt_crcf dcFilter; + int num_vis_samples; }; diff --git a/src/visual/PrimaryGLContext.cpp b/src/visual/PrimaryGLContext.cpp index 88fd3b8..90404cc 100644 --- a/src/visual/PrimaryGLContext.cpp +++ b/src/visual/PrimaryGLContext.cpp @@ -92,7 +92,7 @@ GLFont &PrimaryGLContext::getFont(GLFontSize esize) { return fonts[esize]; } -void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float g, float b) { +void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float g, float b, int center_freq, int srate) { if (!demod) { return; } @@ -103,7 +103,11 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float float viewHeight = (float) vp[3]; float viewWidth = (float) vp[2]; - float uxPos = (float) (demod->getParams().frequency - (wxGetApp().getFrequency() - SRATE / 2)) / (float) SRATE; + if (center_freq == -1) { + center_freq = wxGetApp().getFrequency(); + } + + float uxPos = (float) (demod->getParams().frequency - (center_freq - srate / 2)) / (float) srate; uxPos = (uxPos - 0.5) * 2.0; glDisable(GL_TEXTURE_2D); @@ -112,7 +116,7 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR); glColor4f(r, g, b, 0.6); - float ofs = ((float) demod->getParams().bandwidth) / (float) SRATE; + float ofs = ((float) demod->getParams().bandwidth) / (float) srate; glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR); glColor4f(r, g, b, 0.2); @@ -149,12 +153,16 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float } -void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, float b) { +void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, float b, int center_freq, int srate) { if (!demod) { return; } - float uxPos = (float) (demod->getParams().frequency - (wxGetApp().getFrequency() - SRATE / 2)) / (float) SRATE; + if (center_freq == -1) { + center_freq = wxGetApp().getFrequency(); + } + + float uxPos = (float) (demod->getParams().frequency - (center_freq - srate / 2)) / (float) srate; glDisable(GL_DEPTH_TEST); glDisable(GL_TEXTURE_2D); @@ -167,7 +175,7 @@ void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, f glVertex3f((uxPos - 0.5) * 2.0, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0, -1.0, 0.0); - float ofs = ((float) demod->getParams().bandwidth) / (float) SRATE; + float ofs = ((float) demod->getParams().bandwidth) / (float) srate; glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 - ofs, -1.0, 0.0); @@ -191,7 +199,7 @@ void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, f glEnable(GL_DEPTH_TEST); } -void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b, float w) { +void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b, float w, int center_freq, int srate) { DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); int bw = 0; @@ -218,7 +226,7 @@ void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b, if (w) { ofs = w; } else { - ofs = ((float) bw) / (float) SRATE; + ofs = ((float) bw) / (float) srate; } glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0); diff --git a/src/visual/PrimaryGLContext.h b/src/visual/PrimaryGLContext.h index 43a4167..9635548 100644 --- a/src/visual/PrimaryGLContext.h +++ b/src/visual/PrimaryGLContext.h @@ -23,9 +23,9 @@ public: void BeginDraw(); void EndDraw(); - void DrawFreqSelector(float uxPos, float r = 1, float g = 1, float b = 1, float w = 0); - void DrawDemod(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1); - void DrawDemodInfo(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1); + void DrawFreqSelector(float uxPos, float r = 1, float g = 1, float b = 1, float w = 0, int center_freq = -1, int srate = SRATE); + void DrawDemod(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1, int center_freq = -1, int srate = SRATE); + void DrawDemodInfo(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1, int center_freq = -1, int srate = SRATE); static GLFont &getFont(GLFontSize esize); diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index fab9c59..ca164dd 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -27,20 +27,9 @@ wxEND_EVENT_TABLE() SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0) { - - int in_block_size = FFT_SIZE; - int out_block_size = FFT_SIZE; - - in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * in_block_size); - out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size); - plan = fftw_plan_dft_1d(out_block_size, in, out, FFTW_FORWARD, FFTW_MEASURE); - - fft_ceil_ma = fft_ceil_maa = 100.0; - fft_floor_ma = fft_floor_maa = 0.0; + wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), fft_size(0), in(NULL), out(NULL), plan(NULL) { glContext = new SpectrumContext(this, &wxGetApp().GetContext(this)); - timer.start(); mTracker.setTarget(this); mTracker.setVertDragLock(true); @@ -48,6 +37,33 @@ SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : SetCursor(wxCURSOR_SIZEWE); } +void SpectrumCanvas::Setup(int fft_size_in) { + if (fft_size == fft_size_in) { + return; + } + + fft_size = fft_size_in; + + if (in) { + free(in); + } + in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size); + if (out) { + free(out); + } + out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size); + if (plan) { + fftw_destroy_plan(plan); + } + plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE); + + + fft_ceil_ma = fft_ceil_maa = 100.0; + fft_floor_ma = fft_floor_maa = 0.0; + + timer.start(); +} + SpectrumCanvas::~SpectrumCanvas() { } @@ -73,14 +89,23 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { SwapBuffers(); } -void SpectrumCanvas::setData(std::vector *data) { - +void SpectrumCanvas::setData(DemodulatorThreadIQData *input) { + if (!input) { + return; + } + std::vector *data = &input->data; if (data && data->size()) { - if (spectrum_points.size() < FFT_SIZE * 2) { - spectrum_points.resize(FFT_SIZE * 2); + if (fft_size != data->size()) { + Setup(data->size()); + } + if (spectrum_points.size() < fft_size * 2) { + if (spectrum_points.capacity() < fft_size * 2) { + spectrum_points.reserve(fft_size * 2); + } + spectrum_points.resize(fft_size * 2); } - for (int i = 0; i < FFT_SIZE; i++) { + for (int i = 0; i < fft_size; i++) { in[i][0] = (*data)[i].real; in[i][1] = (*data)[i].imag; } @@ -89,31 +114,34 @@ void SpectrumCanvas::setData(std::vector *data) { double fft_ceil = 0, fft_floor = 1; - if (fft_result.size() < FFT_SIZE) { - fft_result.resize(FFT_SIZE); - fft_result_ma.resize(FFT_SIZE); - fft_result_maa.resize(FFT_SIZE); + if (fft_result.size() != fft_size) { + if (fft_result.capacity() < fft_size) { + fft_result.reserve(fft_size); + fft_result_ma.reserve(fft_size); + fft_result_maa.reserve(fft_size); + } + fft_result.resize(fft_size); + fft_result_ma.resize(fft_size); + fft_result_maa.resize(fft_size); } int n; - for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) { + for (int i = 0, iMax = fft_size / 2; i < iMax; i++) { n = (i == 0) ? 1 : i; double a = out[n][0]; double b = out[n][1]; double c = sqrt(a * a + b * b); // n = (i == FFT_SIZE / 2) ? (FFT_SIZE / 2 + 1) : i; - double x = out[FFT_SIZE / 2 + n][0]; - double y = out[FFT_SIZE / 2 + n][1]; + double x = out[fft_size / 2 + n][0]; + double y = out[fft_size / 2 + n][1]; double z = sqrt(x * x + y * y); fft_result[i] = (z); - fft_result[FFT_SIZE / 2 + i] = (c); + fft_result[fft_size / 2 + i] = (c); } - float time_slice = (float) SRATE / (float) (BUF_SIZE / 2); - - for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { + for (int i = 0, iMax = fft_size; i < iMax; i++) { fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65; fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65; @@ -136,7 +164,7 @@ void SpectrumCanvas::setData(std::vector *data) { // fftw_execute(plan[1]); - for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { + for (int i = 0, iMax = fft_size; i < iMax; i++) { float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa)); spectrum_points[i * 2] = ((float) i / (float) iMax); spectrum_points[i * 2 + 1] = v; diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index d02f4fe..cd06923 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -15,9 +15,10 @@ class SpectrumCanvas: public wxGLCanvas { public: SpectrumCanvas(wxWindow *parent, int *attribList = NULL); + void Setup(int fft_size_in); ~SpectrumCanvas(); - void setData(std::vector *data); + void setData(DemodulatorThreadIQData *input); private: void OnPaint(wxPaintEvent& event); @@ -47,6 +48,7 @@ private: SpectrumContext *glContext; Timer timer; float frameTimer; + int fft_size; MouseTracker mTracker; // event table diff --git a/src/visual/SpectrumContext.h b/src/visual/SpectrumContext.h index be5a62f..3167e65 100644 --- a/src/visual/SpectrumContext.h +++ b/src/visual/SpectrumContext.h @@ -14,4 +14,5 @@ public: void Draw(std::vector &points); private: + int fft_size; }; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 597ecee..af7ce4d 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -29,39 +29,93 @@ EVT_ENTER_WINDOW(WaterfallCanvas::mouseEnterWindow) wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : - wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, +wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( - WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false) { - - int in_block_size = FFT_SIZE; - int out_block_size = FFT_SIZE; - - in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * in_block_size); - out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size); - plan = fftw_plan_dft_1d(out_block_size, in, out, FFTW_FORWARD, FFTW_MEASURE); - - fft_ceil_ma = fft_ceil_maa = 100.0; - fft_floor_ma = fft_floor_maa = 0.0; + WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan(NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth(0) { glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); - timer.start(); + + nco_shift = nco_crcf_create(LIQUID_NCO); + shift_freq = 0; mTracker.setTarget(this); SetCursor(wxCURSOR_CROSS); } WaterfallCanvas::~WaterfallCanvas() { + nco_crcf_destroy(nco_shift); +} +void WaterfallCanvas::SetView(int center_freq_in, int bandwidth_in) { + isView = true; + center_freq = center_freq_in; + bandwidth = bandwidth_in; + last_bandwidth = 0; +} + +void WaterfallCanvas::DisableView() { + isView = false; +} + +void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) { + if (fft_size == fft_size_in && waterfall_lines_in == waterfall_lines) { + return; + } + fft_size = fft_size_in; + waterfall_lines = waterfall_lines_in; + + if (in) { + free(in); + } + in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size); + if (out) { + free(out); + } + out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size); + if (plan) { + fftw_destroy_plan(plan); + } + plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE); + + fft_ceil_ma = fft_ceil_maa = 100.0; + fft_floor_ma = fft_floor_maa = 0.0; + + glContext->Setup(fft_size, waterfall_lines); + timer.start(); } int WaterfallCanvas::GetFrequencyAt(float x) { - - int center_freq = wxGetApp().getFrequency(); - int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) x * (float) SRATE); + int iqCenterFreq = GetCenterFrequency(); + int iqBandwidth = GetBandwidth(); + int freq = iqCenterFreq - (int) (0.5 * (float) iqBandwidth) + (int) ((float) x * (float) iqBandwidth); return freq; } +void WaterfallCanvas::SetCenterFrequency(unsigned int center_freq_in) { + center_freq = center_freq_in; +} + +unsigned int WaterfallCanvas::GetCenterFrequency() { + if (isView) { + return center_freq; + } else { + return (unsigned int)wxGetApp().getFrequency(); + } +} + +void WaterfallCanvas::SetBandwidth(unsigned int bandwidth_in) { + bandwidth = bandwidth_in; +} + +unsigned int WaterfallCanvas::GetBandwidth() { + if (isView) { + return bandwidth; + } else { + return SRATE; + } +} + void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); @@ -80,6 +134,9 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { bool isNew = shiftDown || (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive()); + int currentBandwidth = GetBandwidth(); + int currentCenterFreq = GetCenterFrequency(); + if (mTracker.mouseInView()) { if (nextDragState == WF_DRAG_RANGE) { if (mTracker.mouseDown()) { @@ -87,47 +144,49 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { float centerPos = mTracker.getOriginMouseX() + width / 2.0; if (isNew) { - glContext->DrawDemod(lastActiveDemodulator); - glContext->DrawFreqSelector(centerPos, 0, 1, 0, width ? width : (1.0 / (float) ClientSize.x)); + glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth); + glContext->DrawFreqSelector(centerPos, 0, 1, 0, width ? width : (1.0 / (float) ClientSize.x), currentCenterFreq, + currentBandwidth); } else { - glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0); - glContext->DrawFreqSelector(centerPos, 1, 1, 0, width ? width : (1.0 / (float) ClientSize.x)); + glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth); + glContext->DrawFreqSelector(centerPos, 1, 1, 0, width ? width : (1.0 / (float) ClientSize.x), currentCenterFreq, + currentBandwidth); } } else { if (isNew) { - glContext->DrawDemod(lastActiveDemodulator); - glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 1.0 / (float) ClientSize.x); + glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth); + glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 1.0 / (float) ClientSize.x, currentCenterFreq, currentBandwidth); } else { - glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0); - glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 1.0 / (float) ClientSize.x); + glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth); + glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 1.0 / (float) ClientSize.x, currentCenterFreq, currentBandwidth); } } } else { if (activeDemodulator == NULL) { if (lastActiveDemodulator) { if (isNew) { - glContext->DrawDemod(lastActiveDemodulator); - glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0); + glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth); + glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 0, currentCenterFreq, currentBandwidth); } else { - glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0); - glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0); + glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth); + glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 0, currentCenterFreq, currentBandwidth); } } else { - glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0); + glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 0, currentCenterFreq, currentBandwidth); } } else { if (lastActiveDemodulator) { - glContext->DrawDemod(lastActiveDemodulator); + glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth); } - glContext->DrawDemod(activeDemodulator, 1, 1, 0); + glContext->DrawDemod(activeDemodulator, 1, 1, 0, currentCenterFreq, currentBandwidth); } } } else { if (activeDemodulator) { - glContext->DrawDemod(activeDemodulator); + glContext->DrawDemod(activeDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth); } if (lastActiveDemodulator) { - glContext->DrawDemod(lastActiveDemodulator); + glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth); } } @@ -135,7 +194,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { if (activeDemodulator == demods[i] || lastActiveDemodulator == demods[i]) { continue; } - glContext->DrawDemod(demods[i]); + glContext->DrawDemod(demods[i], 1, 1, 1, currentCenterFreq, currentBandwidth); } glContext->EndDraw(); @@ -161,102 +220,178 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); unsigned int freq; - switch (event.GetKeyCode()) { - case WXK_RIGHT: - freq = wxGetApp().getFrequency(); - if (shiftDown) { - freq += SRATE * 10; - } else { - freq += SRATE / 2; - } - wxGetApp().setFrequency(freq); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); - break; - case WXK_LEFT: - freq = wxGetApp().getFrequency(); - if (shiftDown) { - freq -= SRATE * 10; - } else { - freq -= SRATE / 2; - } - wxGetApp().setFrequency(freq); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); - break; - case 'D': - case WXK_DELETE: - if (!activeDemod) { + if (!isView) { + switch (event.GetKeyCode()) { + case WXK_RIGHT: + freq = wxGetApp().getFrequency(); + if (shiftDown) { + freq += SRATE * 10; + } else { + freq += SRATE / 2; + } + wxGetApp().setFrequency(freq); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); break; - } - wxGetApp().removeDemodulator(activeDemod); - wxGetApp().getDemodMgr().deleteThread(activeDemod); - break; - case 'S': - if (!activeDemod) { + case WXK_LEFT: + freq = wxGetApp().getFrequency(); + if (shiftDown) { + freq -= SRATE * 10; + } else { + freq -= SRATE / 2; + } + wxGetApp().setFrequency(freq); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); break; + case 'D': + case WXK_DELETE: + if (!activeDemod) { + break; + } + wxGetApp().removeDemodulator(activeDemod); + wxGetApp().getDemodMgr().deleteThread(activeDemod); + break; + case 'S': + if (!activeDemod) { + break; + } + if (activeDemod->isSquelchEnabled()) { + activeDemod->setSquelchEnabled(false); + } else { + activeDemod->squelchAuto(); + } + break; + case WXK_SPACE: + if (!activeDemod) { + break; + } + if (activeDemod->isStereo()) { + activeDemod->setStereo(false); + } else { + activeDemod->setStereo(true); + } + break; + default: + event.Skip(); + return; } - if (activeDemod->isSquelchEnabled()) { - activeDemod->setSquelchEnabled(false); - } else { - activeDemod->squelchAuto(); - } - break; - case WXK_SPACE: - if (!activeDemod) { - break; - } - if (activeDemod->isStereo()) { - activeDemod->setStereo(false); - } else { - activeDemod->setStereo(true); - } - break; - default: - event.Skip(); - return; } } -void WaterfallCanvas::setData(std::vector *data) { +void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { + if (!input) { + return; + } + + std::vector *data = &input->data; if (data && data->size()) { - if (spectrum_points.size() < FFT_SIZE * 2) { - spectrum_points.resize(FFT_SIZE * 2); + if (fft_size != data->size() && !isView) { + Setup(data->size(), waterfall_lines); } - for (int i = 0; i < FFT_SIZE; i++) { - in[i][0] = (*data)[i].real; - in[i][1] = (*data)[i].imag; + if (last_bandwidth != bandwidth && !isView) { + Setup(bandwidth, waterfall_lines); + } + + if (spectrum_points.size() < fft_size * 2) { + spectrum_points.resize(fft_size * 2); + } + + if (isView) { + if (!input->frequency || !input->bandwidth) { + return; + } + + if (center_freq != input->frequency) { + if (((int)center_freq - (int)input->frequency) != shift_freq || last_input_bandwidth != input->bandwidth) { + if ((int)input->frequency - abs((int)center_freq) < (int) ((float) ((float) SRATE / 2.0))) { + shift_freq = (int)center_freq - (int)input->frequency; + nco_crcf_reset(nco_shift); + nco_crcf_set_frequency(nco_shift, (2.0 * M_PI) * (((float) abs(shift_freq)) / ((float) input->bandwidth))); + } + } + + if (shift_buffer.size() != input->data.size()) { + if (shift_buffer.capacity() < input->data.size()) { + shift_buffer.reserve(input->data.size()); + } + shift_buffer.resize(input->data.size()); + } + + if (shift_freq < 0) { + nco_crcf_mix_block_up(nco_shift, &input->data[0], &shift_buffer[0], input->data.size()); + } else { + nco_crcf_mix_block_down(nco_shift, &input->data[0], &shift_buffer[0], input->data.size()); + } + } else { + shift_buffer.assign(input->data.begin(), input->data.end()); + } + + if (!resampler || bandwidth != last_bandwidth || last_input_bandwidth != input->bandwidth) { + resample_ratio = (float) (bandwidth) / (float) input->bandwidth; + + float As = 60.0f; + + if (resampler) { + msresamp_crcf_destroy(resampler); + } + resampler = msresamp_crcf_create(resample_ratio, As); + + last_bandwidth = bandwidth; + last_input_bandwidth = input->bandwidth; + } + + int out_size = ceil((float) (input->data.size()) * resample_ratio); + + if (resampler_buffer.size() != out_size) { + if (resampler_buffer.capacity() < out_size) { + resampler_buffer.reserve(out_size); + } + resampler_buffer.resize(out_size); + } + + unsigned int num_written; + msresamp_crcf_execute(resampler, &shift_buffer[0], input->data.size(), &resampler_buffer[0], &num_written); + + resampler_buffer.resize(fft_size); + + for (int i = 0; i < fft_size; i++) { + in[i][0] = resampler_buffer[i].real; + in[i][1] = resampler_buffer[i].imag; + } + } else { + for (int i = 0; i < fft_size; i++) { + in[i][0] = (*data)[i].real; + in[i][1] = (*data)[i].imag; + } } fftw_execute(plan); double fft_ceil = 0, fft_floor = 1; - if (fft_result.size() < FFT_SIZE) { - fft_result.resize(FFT_SIZE); - fft_result_ma.resize(FFT_SIZE); - fft_result_maa.resize(FFT_SIZE); + if (fft_result.size() < fft_size) { + fft_result.resize(fft_size); + fft_result_ma.resize(fft_size); + fft_result_maa.resize(fft_size); } int n; - for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) { + for (int i = 0, iMax = fft_size / 2; i < iMax; i++) { n = (i == 0) ? 1 : i; double a = out[n][0]; double b = out[n][1]; double c = sqrt(a * a + b * b); -// n = (i == FFT_SIZE / 2) ? (FFT_SIZE / 2 + 1) : i; - double x = out[FFT_SIZE / 2 + n][0]; - double y = out[FFT_SIZE / 2 + n][1]; + double x = out[fft_size / 2 + n][0]; + double y = out[fft_size / 2 + n][1]; double z = sqrt(x * x + y * y); fft_result[i] = (z); - fft_result[FFT_SIZE / 2 + i] = (c); + fft_result[fft_size / 2 + i] = (c); } - float time_slice = (float) SRATE / (float) (BUF_SIZE / 2); - - for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { + for (int i = 0, iMax = fft_size; i < iMax; i++) { fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65; fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65; @@ -277,12 +412,11 @@ void WaterfallCanvas::setData(std::vector *data) { fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.01; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.01; - for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { + for (int i = 0, iMax = fft_size; i < iMax; i++) { float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa)); spectrum_points[i * 2] = ((float) i / (float) iMax); spectrum_points[i * 2 + 1] = v; } - } } @@ -310,7 +444,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { } if (dragState == WF_DRAG_BANDWIDTH_LEFT || dragState == WF_DRAG_BANDWIDTH_RIGHT) { - int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) SRATE) * 2; + int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) GetBandwidth()) * 2; if (dragState == WF_DRAG_BANDWIDTH_LEFT) { bwDiff = -bwDiff; @@ -326,8 +460,8 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { if (activeDemodulatorBandwidth < 2000) { activeDemodulatorBandwidth = 2000; } - if (activeDemodulatorBandwidth > SRATE) { - activeDemodulatorBandwidth = SRATE; + if (activeDemodulatorBandwidth > GetBandwidth()) { + activeDemodulatorBandwidth = GetBandwidth(); } command.int_value = activeDemodulatorBandwidth; @@ -335,7 +469,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { } if (dragState == WF_DRAG_FREQUENCY) { - int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) SRATE); + int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) GetBandwidth()); if (!activeDemodulatorFrequency) { activeDemodulatorFrequency = demod->getParams().frequency; @@ -363,7 +497,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { mTracker.setHorizDragLock(false); } else if (demodsHover->size()) { int hovered = -1; - int near_dist = SRATE; + int near_dist = GetBandwidth(); DemodulatorInstance *activeDemodulator = NULL; @@ -397,7 +531,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { int freqDiff = ((int) activeDemodulator->getParams().frequency - freqPos); if (abs(freqDiff) > (activeDemodulator->getParams().bandwidth / 3)) { - SetCursor(wxCURSOR_SIZEWE); + SetCursor (wxCURSOR_SIZEWE); if (freqDiff > 0) { nextDragState = WF_DRAG_BANDWIDTH_LEFT; @@ -408,14 +542,14 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); } else { - SetCursor(wxCURSOR_SIZING); + SetCursor (wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); } } else { - SetCursor(wxCURSOR_CROSS); + SetCursor (wxCURSOR_CROSS); nextDragState = WF_DRAG_NONE; } @@ -460,8 +594,8 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { if (mTracker.getOriginDeltaMouseX() == 0 && mTracker.getOriginDeltaMouseY() == 0) { float pos = mTracker.getMouseX(); - int center_freq = wxGetApp().getFrequency(); - int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE); + int input_center_freq = GetCenterFrequency(); + int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth()); if (dragState == WF_DRAG_NONE) { if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) { @@ -497,14 +631,14 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false); - SetCursor(wxCURSOR_SIZING); + SetCursor (wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); } else { float pos = mTracker.getMouseX(); - int center_freq = wxGetApp().getFrequency(); - int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE); + int input_center_freq = GetCenterFrequency(); + int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth()); wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false); nextDragState = WF_DRAG_FREQUENCY; @@ -513,15 +647,15 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { float width = mTracker.getOriginDeltaMouseX(); float pos = mTracker.getOriginMouseX() + width / 2.0; - int center_freq = wxGetApp().getFrequency(); - int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE); - int bandwidth = (int) (fabs(width) * (float) SRATE); + int input_center_freq = GetCenterFrequency(); + unsigned int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth()); + unsigned int bw = (unsigned int) (fabs(width) * (float) GetBandwidth()); - if (bandwidth < 2000) { - bandwidth = 2000; + if (bw < 2000) { + bw = 2000; } - if (!bandwidth) { + if (!bw) { dragState = WF_DRAG_NONE; return; } @@ -531,7 +665,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { } else { demod = wxGetApp().getDemodMgr().newThread(); demod->getParams().frequency = freq; - demod->getParams().bandwidth = bandwidth; + demod->getParams().bandwidth = bw; demod->run(); @@ -556,7 +690,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { command.int_value = freq; demod->getCommandQueue()->push(command); command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH; - command.int_value = bandwidth; + command.int_value = bw; demod->getCommandQueue()->push(command); } @@ -565,11 +699,12 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { void WaterfallCanvas::mouseLeftWindow(wxMouseEvent& event) { mTracker.OnMouseLeftWindow(event); - SetCursor(wxCURSOR_CROSS); + SetCursor (wxCURSOR_CROSS); wxGetApp().getDemodMgr().setActiveDemodulator(NULL); } void WaterfallCanvas::mouseEnterWindow(wxMouseEvent& event) { mTracker.OnMouseEnterWindow(event); - SetCursor(wxCURSOR_CROSS); + SetCursor (wxCURSOR_CROSS); } + diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index e320a73..bd55bc5 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -19,11 +19,21 @@ public: }; WaterfallCanvas(wxWindow *parent, int *attribList = NULL); + void Setup(int fft_size_in, int waterfall_lines_in); ~WaterfallCanvas(); - void setData(std::vector *data); + void setData(DemodulatorThreadIQData *input); int GetFrequencyAt(float x); + void SetView(int center_freq_in, int bandwidth_in); + void DisableView(); + + void SetCenterFrequency(unsigned int center_freq_in); + unsigned int GetCenterFrequency(); + + void SetBandwidth(unsigned int bandwidth_in); + unsigned int GetBandwidth(); + private: void OnPaint(wxPaintEvent& event); void OnKeyDown(wxKeyEvent& event); @@ -62,7 +72,28 @@ private: DragState dragState; DragState nextDragState; - bool shiftDown;bool altDown;bool ctrlDown; + bool shiftDown; + bool altDown; + bool ctrlDown; + + int fft_size; + int waterfall_lines; + + unsigned int center_freq; + unsigned int bandwidth; + + bool isView; + msresamp_crcf resampler; + float resample_ratio; + nco_crcf nco_shift; + int shift_freq; + + int last_input_bandwidth; + int last_bandwidth; + + std::vector shift_buffer; + std::vector resampler_buffer; + // event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/WaterfallContext.cpp b/src/visual/WaterfallContext.cpp index ac90129..91ebb09 100644 --- a/src/visual/WaterfallContext.cpp +++ b/src/visual/WaterfallContext.cpp @@ -3,7 +3,31 @@ #include "CubicSDR.h" WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext) : - PrimaryGLContext(canvas, sharedContext) { + PrimaryGLContext(canvas, sharedContext), waterfall(0), waterfall_tex(NULL) { + grad.addColor(GradientColor(0, 0, 0)); + grad.addColor(GradientColor(0, 0, 1.0)); + grad.addColor(GradientColor(0, 1.0, 0)); + grad.addColor(GradientColor(1.0, 1.0, 0)); + grad.addColor(GradientColor(1.0, 0.2, 0.0)); + + grad.generate(256); +} + +void WaterfallContext::Setup(int fft_size_in, int num_waterfall_lines_in) { + if (waterfall) { + glDeleteTextures(1, &waterfall); + waterfall = 0; + } + if (waterfall_tex) { + delete waterfall_tex; + } + + waterfall_lines = num_waterfall_lines_in; + fft_size = fft_size_in; + + waterfall_tex = new unsigned char[fft_size * waterfall_lines]; + memset(waterfall_tex,0,fft_size * waterfall_lines); + glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); @@ -23,26 +47,19 @@ WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedC glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); - grad.addColor(GradientColor(0, 0, 0)); - grad.addColor(GradientColor(0, 0, 1.0)); - grad.addColor(GradientColor(0, 1.0, 0)); - grad.addColor(GradientColor(1.0, 1.0, 0)); - grad.addColor(GradientColor(1.0, 0.2, 0.0)); - - grad.generate(256); - glPixelTransferi(GL_MAP_COLOR, GL_TRUE); glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, &(grad.getRed())[0]); glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 256, &(grad.getGreen())[0]); glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 256, &(grad.getBlue())[0]); + } void WaterfallContext::Draw(std::vector &points) { if (points.size()) { - memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); + memmove(waterfall_tex + fft_size, waterfall_tex, (waterfall_lines - 1) * fft_size); - for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { + for (int i = 0, iMax = fft_size; i < iMax; i++) { float v = points[i * 2 + 1]; float wv = v; @@ -57,7 +74,7 @@ void WaterfallContext::Draw(std::vector &points) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, waterfall); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); glColor3f(1.0, 1.0, 1.0); diff --git a/src/visual/WaterfallContext.h b/src/visual/WaterfallContext.h index a488f6d..535b254 100644 --- a/src/visual/WaterfallContext.h +++ b/src/visual/WaterfallContext.h @@ -3,8 +3,6 @@ #include "PrimaryGLContext.h" #include "Gradient.h" -#define NUM_WATERFALL_LINES 512 - class WaterfallCanvas; class WaterfallContext: public PrimaryGLContext { @@ -12,9 +10,12 @@ public: WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext); void Draw(std::vector &points); + void Setup(int fft_size_in, int num_waterfall_lines_in); private: Gradient grad; GLuint waterfall; - unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; + unsigned char *waterfall_tex; + int fft_size; + int waterfall_lines; }; From b9ead19981bbf1f7a3cff6caa2e5be4b94931225 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 28 Dec 2014 14:19:43 -0500 Subject: [PATCH 03/20] Unused timer caused random startup crash on windows? --- src/visual/SpectrumCanvas.cpp | 4 +--- src/visual/SpectrumCanvas.h | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index ca164dd..4dc669c 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -27,7 +27,7 @@ wxEND_EVENT_TABLE() SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), fft_size(0), in(NULL), out(NULL), plan(NULL) { + wxFULL_REPAINT_ON_RESIZE), parent(parent), fft_size(0), in(NULL), out(NULL), plan(NULL) { glContext = new SpectrumContext(this, &wxGetApp().GetContext(this)); @@ -60,8 +60,6 @@ void SpectrumCanvas::Setup(int fft_size_in) { fft_ceil_ma = fft_ceil_maa = 100.0; fft_floor_ma = fft_floor_maa = 0.0; - - timer.start(); } SpectrumCanvas::~SpectrumCanvas() { diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index cd06923..d5ddc2b 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -46,8 +46,6 @@ private: std::vector fft_result_maa; SpectrumContext *glContext; - Timer timer; - float frameTimer; int fft_size; MouseTracker mTracker; From 0eb8ef60b249f00d1b0a939daa20f613b0681c2d Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 28 Dec 2014 20:55:05 -0500 Subject: [PATCH 04/20] Layout, interaction improvements --- src/AppFrame.cpp | 29 ++++++++++++++++++++--------- src/visual/WaterfallCanvas.cpp | 34 ++++++++++++++++++++++------------ src/visual/WaterfallCanvas.h | 3 +++ 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 0a24ce2..62f19cd 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -31,8 +31,9 @@ AppFrame::AppFrame() : wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); - wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *demodOpts = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); demodTray->AddSpacer(5); demodOpts->AddSpacer(5); @@ -64,15 +65,20 @@ wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0); + demodWaterfallCanvas = new WaterfallCanvas(this, NULL); demodWaterfallCanvas->Setup(1024,128); demodWaterfallCanvas->SetView(DEFAULT_FREQ,300000); - demodTray->Add(demodWaterfallCanvas, 7, wxEXPAND | wxALL, 0); + demodVisuals->Add(demodWaterfallCanvas, 3, wxEXPAND | wxALL, 0); + + demodTray->Add(demodVisuals, 7, wxEXPAND | wxALL, 0); + + demodTray->AddSpacer(2); scopeCanvas = new ScopeCanvas(this, NULL); demodTray->Add(scopeCanvas, 7, wxEXPAND | wxALL, 0); - vbox->Add(demodTray, 1, wxEXPAND | wxALL, 0); + vbox->Add(demodTray, 2, wxEXPAND | wxALL, 0); vbox->AddSpacer(2); spectrumCanvas = new SpectrumCanvas(this, NULL); spectrumCanvas->Setup(2048); @@ -140,12 +146,17 @@ void AppFrame::OnIdle(wxIdleEvent& event) { DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); if (demod) { - if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) { - demodWaterfallCanvas->SetCenterFrequency(demod->getParams().frequency); - } - unsigned int demodBw = (unsigned int) ceil((float) demod->getParams().bandwidth * 1.5); - if (demodBw != demodWaterfallCanvas->GetBandwidth()) { - demodWaterfallCanvas->SetBandwidth(demodBw); + if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { + if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) { + demodWaterfallCanvas->SetCenterFrequency(demod->getParams().frequency); + } + unsigned int demodBw = (unsigned int) ceil((float) demod->getParams().bandwidth * 2.5); + if (demodBw > SRATE/2) { + demodBw = SRATE/2; + } + if (demodBw != demodWaterfallCanvas->GetBandwidth()) { + demodWaterfallCanvas->SetBandwidth(demodBw); + } } } diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index af7ce4d..49a3481 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -29,9 +29,11 @@ EVT_ENTER_WINDOW(WaterfallCanvas::mouseEnterWindow) wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : -wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, + wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( - WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan(NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth(0) { + WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan( + NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth( + 0) { glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); @@ -100,7 +102,7 @@ unsigned int WaterfallCanvas::GetCenterFrequency() { if (isView) { return center_freq; } else { - return (unsigned int)wxGetApp().getFrequency(); + return (unsigned int) wxGetApp().getFrequency(); } } @@ -116,6 +118,14 @@ unsigned int WaterfallCanvas::GetBandwidth() { } } +WaterfallCanvas::DragState WaterfallCanvas::getDragState() { + return dragState; +} + +WaterfallCanvas::DragState WaterfallCanvas::getNextDragState() { + return nextDragState; +} + void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); @@ -303,9 +313,9 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { } if (center_freq != input->frequency) { - if (((int)center_freq - (int)input->frequency) != shift_freq || last_input_bandwidth != input->bandwidth) { - if ((int)input->frequency - abs((int)center_freq) < (int) ((float) ((float) SRATE / 2.0))) { - shift_freq = (int)center_freq - (int)input->frequency; + if (((int) center_freq - (int) input->frequency) != shift_freq || last_input_bandwidth != input->bandwidth) { + if ((int) input->frequency - abs((int) center_freq) < (int) ((float) ((float) SRATE / 2.0))) { + shift_freq = (int) center_freq - (int) input->frequency; nco_crcf_reset(nco_shift); nco_crcf_set_frequency(nco_shift, (2.0 * M_PI) * (((float) abs(shift_freq)) / ((float) input->bandwidth))); } @@ -531,7 +541,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { int freqDiff = ((int) activeDemodulator->getParams().frequency - freqPos); if (abs(freqDiff) > (activeDemodulator->getParams().bandwidth / 3)) { - SetCursor (wxCURSOR_SIZEWE); + SetCursor(wxCURSOR_SIZEWE); if (freqDiff > 0) { nextDragState = WF_DRAG_BANDWIDTH_LEFT; @@ -542,14 +552,14 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); } else { - SetCursor (wxCURSOR_SIZING); + SetCursor(wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); } } else { - SetCursor (wxCURSOR_CROSS); + SetCursor(wxCURSOR_CROSS); nextDragState = WF_DRAG_NONE; } @@ -631,7 +641,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false); - SetCursor (wxCURSOR_SIZING); + SetCursor(wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); @@ -699,12 +709,12 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { void WaterfallCanvas::mouseLeftWindow(wxMouseEvent& event) { mTracker.OnMouseLeftWindow(event); - SetCursor (wxCURSOR_CROSS); + SetCursor(wxCURSOR_CROSS); wxGetApp().getDemodMgr().setActiveDemodulator(NULL); } void WaterfallCanvas::mouseEnterWindow(wxMouseEvent& event) { mTracker.OnMouseEnterWindow(event); - SetCursor (wxCURSOR_CROSS); + SetCursor(wxCURSOR_CROSS); } diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index bd55bc5..a04aca2 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -34,6 +34,9 @@ public: void SetBandwidth(unsigned int bandwidth_in); unsigned int GetBandwidth(); + DragState getDragState(); + DragState getNextDragState(); + private: void OnPaint(wxPaintEvent& event); void OnKeyDown(wxKeyEvent& event); From d642cc63fdc089d0404f344d04341a5c24406c27 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 28 Dec 2014 21:14:08 -0500 Subject: [PATCH 05/20] scope resolution adjust --- src/demod/DemodulatorThread.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index a546e55..444c037 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -8,7 +8,7 @@ typedef ThreadQueue DemodulatorThreadOutputQueue; -#define DEMOD_VIS_SIZE 2048 +#define DEMOD_VIS_SIZE 1024 class DemodulatorThread { public: From 4403824e3b8ce60d535f941f2e980d4615ce2b4c Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Mon, 29 Dec 2014 00:24:10 -0500 Subject: [PATCH 06/20] Demodulator now has it's own spectrum view --- src/AppFrame.cpp | 29 ++++++++++----- src/AppFrame.h | 1 + src/demod/DemodDefs.h | 4 +-- src/demod/DemodulatorPreThread.cpp | 29 ++++----------- src/demod/DemodulatorPreThread.h | 4 +-- src/demod/DemodulatorThread.cpp | 12 +++---- src/demod/DemodulatorThread.h | 3 +- src/demod/DemodulatorWorkerThread.cpp | 6 ++-- src/demod/DemodulatorWorkerThread.h | 4 +-- src/sdr/SDRPostThread.cpp | 4 +-- src/visual/ScopeCanvas.cpp | 11 +++--- src/visual/ScopeCanvas.h | 4 +-- src/visual/ScopeContext.cpp | 51 +++++++++++++++++++++++---- src/visual/ScopeContext.h | 2 +- src/visual/SpectrumCanvas.cpp | 43 +++++++++++++++++++--- src/visual/SpectrumCanvas.h | 18 +++++++++- src/visual/SpectrumContext.cpp | 9 ++--- src/visual/SpectrumContext.h | 2 +- src/visual/WaterfallCanvas.cpp | 25 +++++++++---- src/visual/WaterfallCanvas.h | 4 +++ 20 files changed, 183 insertions(+), 82 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 62f19cd..f419420 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -28,13 +28,14 @@ EVT_IDLE(AppFrame::OnIdle) wxEND_EVENT_TABLE() AppFrame::AppFrame() : -wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { + wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodOpts = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); + /* demodTray->AddSpacer(5); demodOpts->AddSpacer(5); @@ -63,12 +64,19 @@ wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { demodOpts->AddSpacer(5); demodTray->AddSpacer(5); - demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0); + demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0); */ + demodSpectrumCanvas = new SpectrumCanvas(this, NULL); + demodSpectrumCanvas->Setup(1024); + demodSpectrumCanvas->SetView(DEFAULT_FREQ, 300000); + demodVisuals->Add(demodSpectrumCanvas, 1, wxEXPAND | wxALL, 0); + + demodVisuals->AddSpacer(1); demodWaterfallCanvas = new WaterfallCanvas(this, NULL); - demodWaterfallCanvas->Setup(1024,128); - demodWaterfallCanvas->SetView(DEFAULT_FREQ,300000); + demodWaterfallCanvas->Setup(1024, 256); + demodWaterfallCanvas->SetView(DEFAULT_FREQ, 300000); + demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas); demodVisuals->Add(demodWaterfallCanvas, 3, wxEXPAND | wxALL, 0); demodTray->Add(demodVisuals, 7, wxEXPAND | wxALL, 0); @@ -85,7 +93,8 @@ wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { vbox->Add(spectrumCanvas, 1, wxEXPAND | wxALL, 0); vbox->AddSpacer(2); waterfallCanvas = new WaterfallCanvas(this, NULL); - waterfallCanvas->Setup(2048,512); + waterfallCanvas->Setup(2048, 512); + waterfallCanvas->attachSpectrumCanvas(spectrumCanvas); vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0); this->SetSizer(vbox); @@ -149,13 +158,15 @@ void AppFrame::OnIdle(wxIdleEvent& event) { if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) { demodWaterfallCanvas->SetCenterFrequency(demod->getParams().frequency); + demodSpectrumCanvas->SetCenterFrequency(demod->getParams().frequency); } unsigned int demodBw = (unsigned int) ceil((float) demod->getParams().bandwidth * 2.5); - if (demodBw > SRATE/2) { - demodBw = SRATE/2; + if (demodBw > SRATE / 2) { + demodBw = SRATE / 2; } if (demodBw != demodWaterfallCanvas->GetBandwidth()) { demodWaterfallCanvas->SetBandwidth(demodBw); + demodSpectrumCanvas->SetBandwidth(demodBw); } } } @@ -165,7 +176,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { wxGetApp().getIQVisualQueue()->pop(iqData); if (iqData && iqData->data.size()) { - spectrumCanvas->setData(iqData); +// spectrumCanvas->setData(iqData); waterfallCanvas->setData(iqData); demodWaterfallCanvas->setData(iqData); delete iqData; @@ -188,7 +199,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { scopeCanvas->waveform_points[i * 2] = ((double) i / (double) iMax); } - scopeCanvas->setDivider(demodAudioData->channels == 2); + scopeCanvas->setStereo(demodAudioData->channels == 2); delete demodAudioData; } else { diff --git a/src/AppFrame.h b/src/AppFrame.h index 3c87d95..b8589fa 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -23,6 +23,7 @@ private: ScopeCanvas *scopeCanvas; SpectrumCanvas *spectrumCanvas; WaterfallCanvas *waterfallCanvas; + SpectrumCanvas *demodSpectrumCanvas; WaterfallCanvas *demodWaterfallCanvas; // event table diff --git a/src/demod/DemodDefs.h b/src/demod/DemodDefs.h index ac899ba..d938bac 100644 --- a/src/demod/DemodDefs.h +++ b/src/demod/DemodDefs.h @@ -71,10 +71,10 @@ class DemodulatorThreadPostIQData: public ReferenceCounter { public: std::vector data; int bandwidth; - float audio_resample_ratio; + double audio_resample_ratio; msresamp_rrrf audio_resampler; msresamp_rrrf stereo_resampler; - float resample_ratio; + double resample_ratio; msresamp_crcf resampler; DemodulatorThreadPostIQData() : diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp index 9223e84..f9c26a3 100644 --- a/src/demod/DemodulatorPreThread.cpp +++ b/src/demod/DemodulatorPreThread.cpp @@ -30,32 +30,17 @@ DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn void DemodulatorPreThread::initialize() { initialized = false; - resample_ratio = (float) (params.bandwidth) / (float) params.inputRate; - audio_resample_ratio = (float) (params.audioSampleRate) / (float) params.bandwidth; + resample_ratio = (double) (params.bandwidth) / (double) params.inputRate; + audio_resample_ratio = (double) (params.audioSampleRate) / (double) params.bandwidth; float As = 60.0f; // stop-band attenuation [dB] - // create multi-stage arbitrary resampler object - if (resampler) { - msresamp_crcf_destroy(resampler); - } resampler = msresamp_crcf_create(resample_ratio, As); -// msresamp_crcf_print(resampler); - - if (audio_resampler) { - msresamp_rrrf_destroy(audio_resampler); - } audio_resampler = msresamp_rrrf_create(audio_resample_ratio, As); -// msresamp_crcf_print(audio_resampler); - - if (stereo_resampler) { - msresamp_rrrf_destroy(stereo_resampler); - } stereo_resampler = msresamp_rrrf_create(audio_resample_ratio, As); initialized = true; // std::cout << "inputResampleRate " << params.bandwidth << std::endl; - last_params = params; } @@ -68,12 +53,12 @@ DemodulatorPreThread::~DemodulatorPreThread() { #ifdef __APPLE__ void *DemodulatorPreThread::threadMain() { #else - void DemodulatorPreThread::threadMain() { +void DemodulatorPreThread::threadMain() { #endif #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread int priority = sched_get_priority_max( SCHED_FIFO) - 1; - sched_param prio = { priority }; // scheduling priority of thread + sched_param prio = {priority}; // scheduling priority of thread pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif @@ -137,13 +122,13 @@ void *DemodulatorPreThread::threadMain() { if (inp->frequency != params.frequency) { if ((params.frequency - inp->frequency) != shift_freq) { shift_freq = params.frequency - inp->frequency; - if (abs(shift_freq) <= (int) ((float) (SRATE / 2) * 1.5)) { - nco_crcf_set_frequency(nco_shift, (2.0 * M_PI) * (((float) abs(shift_freq)) / ((float) SRATE))); + if (abs(shift_freq) <= (int) ((double) (SRATE / 2) * 1.5)) { + nco_crcf_set_frequency(nco_shift, (2.0 * M_PI) * (((double) abs(shift_freq)) / ((double) SRATE))); } } } - if (abs(shift_freq) > (int) ((float) (SRATE / 2) * 1.5)) { + if (abs(shift_freq) > (int) ((double) (SRATE / 2) * 1.5)) { continue; } diff --git a/src/demod/DemodulatorPreThread.h b/src/demod/DemodulatorPreThread.h index 7bd0b23..962b3f9 100644 --- a/src/demod/DemodulatorPreThread.h +++ b/src/demod/DemodulatorPreThread.h @@ -53,11 +53,11 @@ protected: AudioThreadInputQueue *audioInputQueue; msresamp_crcf resampler; - float resample_ratio; + double resample_ratio; msresamp_rrrf audio_resampler; msresamp_rrrf stereo_resampler; - float audio_resample_ratio; + double audio_resample_ratio; DemodulatorThreadParameters params; DemodulatorThreadParameters last_params; diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index 3f10217..b3e9640 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -36,7 +36,7 @@ void DemodulatorThread::threadMain() { firfilt_rrrf fir_filter2 = NULL; msresamp_crcf resampler = NULL; - float fc = 0.5 * ((double) 36000 / (double) AUDIO_FREQUENCY); // filter cutoff frequency + double fc = 0.5 * ((double) 36000 / (double) AUDIO_FREQUENCY); // filter cutoff frequency if (fc <= 0) { fc = 0; } @@ -61,7 +61,7 @@ void DemodulatorThread::threadMain() { firhilbf firC2R = firhilbf_create(m, slsl); nco_crcf nco_shift = nco_crcf_create(LIQUID_NCO); - float shift_freq = 0; + double shift_freq = 0; agc = agc_crcf_create(); agc_crcf_set_bandwidth(agc, 1e-3f); @@ -105,7 +105,7 @@ void DemodulatorThread::threadMain() { stereo_resampler = inp->stereo_resampler; } - int out_size = ceil((float) (bufSize) * inp->resample_ratio); + int out_size = ceil((double) (bufSize) * inp->resample_ratio); if (agc_data.size() != out_size) { if (agc_data.capacity() < out_size) { @@ -121,7 +121,7 @@ void DemodulatorThread::threadMain() { agc_crcf_execute_block(agc, &resampled_data[0], num_written, &agc_data[0]); - float audio_resample_ratio = inp->audio_resample_ratio; + double audio_resample_ratio = inp->audio_resample_ratio; if (demod_output.size() != num_written) { if (demod_output.capacity() < num_written) { @@ -130,7 +130,7 @@ void DemodulatorThread::threadMain() { demod_output.resize(num_written); } - int audio_out_size = ceil((float) (num_written) * audio_resample_ratio); + int audio_out_size = ceil((double) (num_written) * audio_resample_ratio); freqdem_demodulate_block(fdem, &agc_data[0], num_written, &demod_output[0]); @@ -152,7 +152,7 @@ void DemodulatorThread::threadMain() { demod_output_stereo.resize(num_written); } - double freq = (2.0 * M_PI) * (((float) abs(38000)) / ((float) inp->bandwidth)); + double freq = (2.0 * M_PI) * (((double) abs(38000)) / ((double) inp->bandwidth)); if (shift_freq != freq) { nco_crcf_set_frequency(nco_shift, freq); diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index 444c037..0501b59 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -58,5 +58,6 @@ protected: DemodulatorThreadCommandQueue* threadQueueNotify; DemodulatorThreadControlCommandQueue *threadQueueControl; float squelch_level; - float squelch_tolerance;bool squelch_enabled; + float squelch_tolerance; + bool squelch_enabled; }; diff --git a/src/demod/DemodulatorWorkerThread.cpp b/src/demod/DemodulatorWorkerThread.cpp index 1d79c9f..44c584a 100644 --- a/src/demod/DemodulatorWorkerThread.cpp +++ b/src/demod/DemodulatorWorkerThread.cpp @@ -34,8 +34,8 @@ void DemodulatorWorkerThread::threadMain() { if (filterChanged) { DemodulatorWorkerThreadResult result(DemodulatorWorkerThreadResult::DEMOD_WORKER_THREAD_RESULT_FILTERS); - result.resample_ratio = (float) (filterCommand.bandwidth) / (float) filterCommand.inputRate; - result.audio_resample_ratio = (float) (filterCommand.audioSampleRate) / (float) filterCommand.bandwidth; + result.resample_ratio = (double) (filterCommand.bandwidth) / (double) filterCommand.inputRate; + result.audio_resample_ratio = (double) (filterCommand.audioSampleRate) / (double) filterCommand.bandwidth; float As = 60.0f; // stop-band attenuation [dB] @@ -46,8 +46,8 @@ void DemodulatorWorkerThread::threadMain() { result.audioSampleRate = filterCommand.audioSampleRate; result.bandwidth = filterCommand.bandwidth; result.inputRate = filterCommand.inputRate; - resultQueue->push(result); + std::this_thread::sleep_for(std::chrono::milliseconds(30)); } } diff --git a/src/demod/DemodulatorWorkerThread.h b/src/demod/DemodulatorWorkerThread.h index a51e88d..a45336c 100644 --- a/src/demod/DemodulatorWorkerThread.h +++ b/src/demod/DemodulatorWorkerThread.h @@ -36,10 +36,10 @@ public: DemodulatorThreadResultEnum cmd; msresamp_crcf resampler; - float resample_ratio; + double resample_ratio; msresamp_rrrf audio_resampler; msresamp_rrrf stereo_resampler; - float audio_resample_ratio; + double audio_resample_ratio; unsigned int inputRate; unsigned int bandwidth; diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 9322410..b269d42 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -132,7 +132,7 @@ void SDRPostThread::threadMain() { DemodulatorInstance *demod = *i; if (demod->getParams().frequency != data_in->frequency - && abs(data_in->frequency - demod->getParams().frequency) > (int) ((float) ((float) SRATE / 2.0))) { + && abs(data_in->frequency - demod->getParams().frequency) > (int) ((double) ((double) SRATE / 2.0))) { continue; } activeDemods++; @@ -166,7 +166,7 @@ void SDRPostThread::threadMain() { DemodulatorThreadInputQueue *demodQueue = demod->threadQueueDemod; if (demod->getParams().frequency != data_in->frequency - && abs(data_in->frequency - demod->getParams().frequency) > (int) ((float) ((float) SRATE / 2.0))) { + && abs(data_in->frequency - demod->getParams().frequency) > (int) ((double) ((double) SRATE / 2.0))) { if (demod->isActive()) { demod->setActive(false); DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData; diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp index 76071ed..80ad241 100644 --- a/src/visual/ScopeCanvas.cpp +++ b/src/visual/ScopeCanvas.cpp @@ -21,7 +21,7 @@ wxEND_EVENT_TABLE() ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), divider(false) { + wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), stereo(false) { glContext = new ScopeContext(this, &wxGetApp().GetContext(this)); timer.start(); @@ -35,8 +35,8 @@ void ScopeCanvas::setWaveformPoints(std::vector &waveform_points_in) { waveform_points = waveform_points_in; } -void ScopeCanvas::setDivider(bool state) { - divider = state; +void ScopeCanvas::setStereo(bool state) { + stereo = state; } void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { @@ -47,10 +47,7 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glViewport(0, 0, ClientSize.x, ClientSize.y); glContext->DrawBegin(); - glContext->Plot(waveform_points); - if (divider) { - glContext->DrawDivider(); - } + glContext->Plot(waveform_points, stereo); glContext->DrawEnd(); SwapBuffers(); diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h index 26ace11..29d1cd4 100644 --- a/src/visual/ScopeCanvas.h +++ b/src/visual/ScopeCanvas.h @@ -19,7 +19,7 @@ public: ~ScopeCanvas(); void setWaveformPoints(std::vector &waveform_points_in); - void setDivider(bool state); + void setStereo(bool state); private: void OnPaint(wxPaintEvent& event); @@ -30,7 +30,7 @@ private: ScopeContext *glContext; Timer timer; float frameTimer; - bool divider; + bool stereo; // event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/ScopeContext.cpp b/src/visual/ScopeContext.cpp index 5c1badd..1a12298 100644 --- a/src/visual/ScopeContext.cpp +++ b/src/visual/ScopeContext.cpp @@ -20,18 +20,57 @@ void ScopeContext::DrawBegin() { glDisable (GL_TEXTURE_2D); } -void ScopeContext::Plot(std::vector &points) { +void ScopeContext::Plot(std::vector &points, bool stereo) { glColor3f(1.0, 1.0, 1.0); + if (stereo) { + glColor3f(0.7, 0.7, 0.7); + glBegin(GL_LINES); + glVertex2f(-1.0,0.0); + glVertex2f(1.0,0.0); + glEnd(); + glColor3f(0.3, 0.3, 0.3); + glBegin(GL_LINES); + glVertex2f(-1.0,0.5); + glVertex2f(1.0,0.5); + glVertex2f(-1.0,-0.5); + glVertex2f(1.0,-0.5); + glEnd(); + } else { + glColor3f(0.3, 0.3, 0.3); + glBegin(GL_LINES); + glVertex2f(-1.0,0.0); + glVertex2f(1.0,0.0); + glEnd(); + } + + glColor3f(0.9, 0.9, 0.9); if (points.size()) { - glPushMatrix(); - glTranslatef(-1.0f, 0.0f, 0.0f); - glScalef(2.0f, 2.0f, 1.0f); glEnableClientState (GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &points[0]); - glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); + if (stereo) { + glPushMatrix(); + glTranslatef(-1.0f, 0.5f, 0.0f); + glScalef(4.0f, 0.92f, 1.0f); + glDrawArrays(GL_LINE_STRIP, 0, points.size() / 4); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(-3.0f, -0.5f, 0.0f); + glPushMatrix(); + glScalef(4.0f, 0.92f, 1.0f); + glDrawArrays(GL_LINE_STRIP, points.size() / 4, points.size() / 4); + glPopMatrix(); + glPopMatrix(); + } else { + glPushMatrix(); + glTranslatef(-1.0f, 0.0f, 0.0f); + glScalef(2.0f, 2.0f, 1.0f); + glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); + glPopMatrix(); + } glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); + } } diff --git a/src/visual/ScopeContext.h b/src/visual/ScopeContext.h index 6f001b0..321f14e 100644 --- a/src/visual/ScopeContext.h +++ b/src/visual/ScopeContext.h @@ -12,7 +12,7 @@ public: ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext); void DrawBegin(); - void Plot(std::vector &points); + void Plot(std::vector &points, bool stereo=false); void DrawDivider(); void DrawEnd(); diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 4dc669c..1c73431 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -27,7 +27,7 @@ wxEND_EVENT_TABLE() SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), fft_size(0), in(NULL), out(NULL), plan(NULL) { + wxFULL_REPAINT_ON_RESIZE), parent(parent), fft_size(0), in(NULL), out(NULL), plan(NULL), center_freq(0), bandwidth(0), isView(0) { glContext = new SpectrumContext(this, &wxGetApp().GetContext(this)); @@ -74,12 +74,12 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glViewport(0, 0, ClientSize.x, ClientSize.y); glContext->BeginDraw(); - glContext->Draw(spectrum_points); + glContext->Draw(spectrum_points, GetCenterFrequency(), GetBandwidth()); std::vector &demods = wxGetApp().getDemodMgr().getDemodulators(); for (int i = 0, iMax = demods.size(); i < iMax; i++) { - glContext->DrawDemodInfo(demods[i]); + glContext->DrawDemodInfo(demods[i], 1, 1, 1, GetCenterFrequency(), GetBandwidth()); } glContext->EndDraw(); @@ -171,6 +171,41 @@ void SpectrumCanvas::setData(DemodulatorThreadIQData *input) { } } + + +void SpectrumCanvas::SetView(int center_freq_in, int bandwidth_in) { + isView = true; + center_freq = center_freq_in; + bandwidth = bandwidth_in; +} + +void SpectrumCanvas::DisableView() { + isView = false; +} +void SpectrumCanvas::SetCenterFrequency(unsigned int center_freq_in) { + center_freq = center_freq_in; +} + +unsigned int SpectrumCanvas::GetCenterFrequency() { + if (isView) { + return center_freq; + } else { + return (unsigned int) wxGetApp().getFrequency(); + } +} + +void SpectrumCanvas::SetBandwidth(unsigned int bandwidth_in) { + bandwidth = bandwidth_in; +} + +unsigned int SpectrumCanvas::GetBandwidth() { + if (isView) { + return bandwidth; + } else { + return SRATE; + } +} + void SpectrumCanvas::OnIdle(wxIdleEvent &event) { // timer.update(); // frameTimer += timer.lastUpdateSeconds(); @@ -183,7 +218,7 @@ void SpectrumCanvas::OnIdle(wxIdleEvent &event) { void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { mTracker.OnMouseMoved(event); if (mTracker.mouseDown()) { - int freqChange = mTracker.getDeltaMouseX() * SRATE; + int freqChange = mTracker.getDeltaMouseX() * GetBandwidth(); if (freqChange != 0) { int freq = wxGetApp().getFrequency(); diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index d5ddc2b..dea6b74 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -14,11 +14,23 @@ class SpectrumCanvas: public wxGLCanvas { public: + std::vector spectrum_points; + SpectrumCanvas(wxWindow *parent, int *attribList = NULL); void Setup(int fft_size_in); ~SpectrumCanvas(); void setData(DemodulatorThreadIQData *input); + + void SetView(int center_freq_in, int bandwidth_in); + void DisableView(); + + void SetCenterFrequency(unsigned int center_freq_in); + unsigned int GetCenterFrequency(); + + void SetBandwidth(unsigned int bandwidth_in); + unsigned int GetBandwidth(); + private: void OnPaint(wxPaintEvent& event); @@ -33,7 +45,6 @@ private: void mouseLeftWindow(wxMouseEvent& event); wxWindow *parent; - std::vector spectrum_points; fftw_complex *in, *out; fftw_plan plan; @@ -48,6 +59,11 @@ private: SpectrumContext *glContext; int fft_size; + unsigned int center_freq; + unsigned int bandwidth; + + bool isView; + MouseTracker mTracker; // event table wxDECLARE_EVENT_TABLE(); diff --git a/src/visual/SpectrumContext.cpp b/src/visual/SpectrumContext.cpp index 8d4993e..2a5207a 100644 --- a/src/visual/SpectrumContext.cpp +++ b/src/visual/SpectrumContext.cpp @@ -6,7 +6,7 @@ #include SpectrumContext::SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedContext) : - PrimaryGLContext(canvas, sharedContext) { + PrimaryGLContext(canvas, sharedContext), fft_size(0) { glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); @@ -15,7 +15,7 @@ SpectrumContext::SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedCont } -void SpectrumContext::Draw(std::vector &points) { +void SpectrumContext::Draw(std::vector &points, int freq, int bandwidth) { glDisable(GL_TEXTURE_2D); glColor3f(1.0, 1.0, 1.0); @@ -35,9 +35,10 @@ void SpectrumContext::Draw(std::vector &points) { glGetIntegerv( GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; + float viewWidth = (float) vp[2]; - float leftFreq = (float) wxGetApp().getFrequency() - ((float) SRATE / 2.0); - float rightFreq = leftFreq + (float) SRATE; + float leftFreq = (float) freq - ((float) bandwidth / 2.0); + float rightFreq = leftFreq + (float) bandwidth; float firstMhz = floor(leftFreq / 1000000.0) * 1000000.0; float mhzStart = ((firstMhz - leftFreq) / (rightFreq - leftFreq)) * 2.0; diff --git a/src/visual/SpectrumContext.h b/src/visual/SpectrumContext.h index 3167e65..91c900c 100644 --- a/src/visual/SpectrumContext.h +++ b/src/visual/SpectrumContext.h @@ -11,7 +11,7 @@ class SpectrumContext: public PrimaryGLContext { public: SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedContext); - void Draw(std::vector &points); + void Draw(std::vector &points, int freq, int bandwidth); private: int fft_size; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 49a3481..589b966 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -17,6 +17,8 @@ #include +#define MIN_FM_BANDWIDTH 10000 + wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) EVT_PAINT(WaterfallCanvas::OnPaint) EVT_KEY_DOWN(WaterfallCanvas::OnKeyDown) EVT_KEY_UP(WaterfallCanvas::OnKeyUp) @@ -30,7 +32,7 @@ wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( + wxFULL_REPAINT_ON_RESIZE), parent(parent), spectrumCanvas(NULL), frameTimer(0), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan( NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth( 0) { @@ -126,6 +128,11 @@ WaterfallCanvas::DragState WaterfallCanvas::getNextDragState() { return nextDragState; } +void WaterfallCanvas::attachSpectrumCanvas(SpectrumCanvas *canvas_in) { + spectrumCanvas = canvas_in; +} + + void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); @@ -427,6 +434,10 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { spectrum_points[i * 2] = ((float) i / (float) iMax); spectrum_points[i * 2 + 1] = v; } + + if (spectrumCanvas) { + spectrumCanvas->spectrum_points.assign(spectrum_points.begin(),spectrum_points.end()); + } } } @@ -467,11 +478,11 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { DemodulatorThreadCommand command; command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH; activeDemodulatorBandwidth = activeDemodulatorBandwidth + bwDiff; - if (activeDemodulatorBandwidth < 2000) { - activeDemodulatorBandwidth = 2000; + if (activeDemodulatorBandwidth > SRATE) { + activeDemodulatorBandwidth = SRATE; } - if (activeDemodulatorBandwidth > GetBandwidth()) { - activeDemodulatorBandwidth = GetBandwidth(); + if (activeDemodulatorBandwidth < MIN_FM_BANDWIDTH) { + activeDemodulatorBandwidth = MIN_FM_BANDWIDTH; } command.int_value = activeDemodulatorBandwidth; @@ -661,8 +672,8 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { unsigned int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth()); unsigned int bw = (unsigned int) (fabs(width) * (float) GetBandwidth()); - if (bw < 2000) { - bw = 2000; + if (bw < MIN_FM_BANDWIDTH) { + bw = MIN_FM_BANDWIDTH; } if (!bw) { diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index a04aca2..fb8196c 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -8,6 +8,7 @@ #include "WaterfallContext.h" #include "MouseTracker.h" +#include "SpectrumCanvas.h" #include "fftw3.h" #include "Timer.h" @@ -37,6 +38,8 @@ public: DragState getDragState(); DragState getNextDragState(); + void attachSpectrumCanvas(SpectrumCanvas *canvas_in); + private: void OnPaint(wxPaintEvent& event); void OnKeyDown(wxKeyEvent& event); @@ -52,6 +55,7 @@ private: void mouseLeftWindow(wxMouseEvent& event); wxWindow *parent; + SpectrumCanvas *spectrumCanvas; std::vector spectrum_points; fftw_complex *in, *out; From b7793ef9056a1d400995e763db053c6e75ba635e Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 31 Dec 2014 19:45:01 -0500 Subject: [PATCH 07/20] Squelch slider/signal indicator --- CMakeLists.txt | 4 +++ src/AppFrame.cpp | 54 ++++++++++++++++++++++++++++--- src/AppFrame.h | 13 +++++++- src/CubicSDRDefs.h | 2 +- src/audio/AudioThread.cpp | 10 ++++-- src/audio/AudioThread.h | 2 +- src/demod/DemodulatorInstance.cpp | 13 ++++++++ src/demod/DemodulatorInstance.h | 13 ++++++-- src/demod/DemodulatorThread.cpp | 34 +++++++++++++++---- src/demod/DemodulatorThread.h | 9 ++++-- src/util/MouseTracker.cpp | 8 ++--- src/visual/WaterfallCanvas.cpp | 17 ++++++---- 12 files changed, 148 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3db72f8..a222e4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,8 @@ SET (cubicsdr_sources src/util/MouseTracker.cpp src/util/GLFont.cpp src/visual/PrimaryGLContext.cpp + src/visual/MeterCanvas.cpp + src/visual/MeterContext.cpp src/visual/ScopeCanvas.cpp src/visual/ScopeContext.cpp src/visual/SpectrumCanvas.cpp @@ -165,6 +167,8 @@ SET (cubicsdr_headers src/util/MouseTracker.h src/util/GLFont.h src/visual/PrimaryGLContext.h + src/visual/MeterCanvas.h + src/visual/MeterContext.h src/visual/ScopeCanvas.h src/visual/ScopeContext.h src/visual/SpectrumCanvas.h diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index f419420..1334782 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -79,12 +79,18 @@ AppFrame::AppFrame() : demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas); demodVisuals->Add(demodWaterfallCanvas, 3, wxEXPAND | wxALL, 0); - demodTray->Add(demodVisuals, 7, wxEXPAND | wxALL, 0); + demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0); + + demodTray->AddSpacer(2); + + demodSignalMeter = new MeterCanvas(this, NULL); + demodSignalMeter->setMax(0.5); + demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0); demodTray->AddSpacer(2); scopeCanvas = new ScopeCanvas(this, NULL); - demodTray->Add(scopeCanvas, 7, wxEXPAND | wxALL, 0); + demodTray->Add(scopeCanvas, 30, wxEXPAND | wxALL, 0); vbox->Add(demodTray, 2, wxEXPAND | wxALL, 0); vbox->AddSpacer(2); @@ -105,12 +111,42 @@ AppFrame::AppFrame() : // SetIcon(wxICON(sample)); // Make a menubar - wxMenu *menu = new wxMenu; +// wxMenu *menu = new wxMenu; // menu->Append(wxID_NEW); // menu->AppendSeparator(); - menu->Append(wxID_CLOSE); +// menu->Append(wxID_CLOSE); +// wxMenuBar *menuBar = new wxMenuBar; +// menuBar->Append(menu, wxT("&File")); + + wxMenu *menu = new wxMenu; + + std::vector devices; + std::vector::iterator devices_i; + std::map::iterator mdevices_i; + AudioThread::enumerateDevices(devices); + + int i = 0; + + for (devices_i = devices.begin(); devices_i != devices.end(); devices_i++) { + if (devices_i->inputChannels) { + input_devices[i] = *devices_i; + } + if (devices_i->outputChannels) { + output_devices[i] = *devices_i; + } + i++; + } + + + for (mdevices_i = output_devices.begin(); mdevices_i != output_devices.end(); mdevices_i++) { + wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE+i,mdevices_i->second.name,wxT("Description?")); + if (mdevices_i->second.isDefaultOutput) { + itm->Check(true); + } + } + wxMenuBar *menuBar = new wxMenuBar; - menuBar->Append(menu, wxT("&File")); + menuBar->Append(menu, wxT("&Device")); SetMenuBar(menuBar); @@ -155,6 +191,9 @@ void AppFrame::OnIdle(wxIdleEvent& event) { DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); if (demod) { + if (demod != activeDemodulator) { + demodSignalMeter->setInputValue(demod->getSquelchLevel()); + } if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) { demodWaterfallCanvas->SetCenterFrequency(demod->getParams().frequency); @@ -169,6 +208,11 @@ void AppFrame::OnIdle(wxIdleEvent& event) { demodSpectrumCanvas->SetBandwidth(demodBw); } } + demodSignalMeter->setLevel(demod->getSignalLevel()); + if (demodSignalMeter->inputChanged()) { + demod->setSquelchLevel(demodSignalMeter->getInputValue()); + } + activeDemodulator = demod; } if (!wxGetApp().getIQVisualQueue()->empty()) { diff --git a/src/AppFrame.h b/src/AppFrame.h index b8589fa..912b871 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -6,6 +6,11 @@ #include "ScopeCanvas.h" #include "SpectrumCanvas.h" #include "WaterfallCanvas.h" +#include "MeterCanvas.h" + +#include + +#define wxID_RT_AUDIO_DEVICE 10000 // Define a new frame type class AppFrame: public wxFrame { @@ -25,7 +30,13 @@ private: WaterfallCanvas *waterfallCanvas; SpectrumCanvas *demodSpectrumCanvas; WaterfallCanvas *demodWaterfallCanvas; - + MeterCanvas *demodSignalMeter; // event table + + DemodulatorInstance *activeDemodulator; + + std::map input_devices; + std::map output_devices; + wxDECLARE_EVENT_TABLE(); }; diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 3d08ed3..ca0583e 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -4,7 +4,7 @@ #define BUF_SIZE (16384*2) #define SRATE 2000000 #else -#define BUF_SIZE (16384*4) +#define BUF_SIZE (16384*5) #define SRATE 2500000 #endif #define DEFAULT_FFT_SIZE 2048 diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index ec0c242..4bb46a7 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -211,11 +211,15 @@ static int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBu } #endif -void AudioThread::enumerateDevices() { - int numDevices = dac.getDeviceCount(); +void AudioThread::enumerateDevices(std::vector &devs) { + RtAudio endac; + + int numDevices = endac.getDeviceCount(); for (int i = 0; i < numDevices; i++) { - RtAudio::DeviceInfo info = dac.getDeviceInfo(i); + RtAudio::DeviceInfo info = endac.getDeviceInfo(i); + + devs.push_back(info); std::cout << std::endl; diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h index 941fdaf..de0c806 100644 --- a/src/audio/AudioThread.h +++ b/src/audio/AudioThread.h @@ -66,7 +66,7 @@ public: AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify); ~AudioThread(); - void enumerateDevices(); + static void enumerateDevices(std::vector &devs); void threadMain(); void terminate(); diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index 9650f01..6618a1a 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -181,3 +181,16 @@ void DemodulatorInstance::setSquelchEnabled(bool state) { squelch = state; } +float DemodulatorInstance::getSignalLevel() { + return demodulatorThread->getSignalLevel(); +} + +void DemodulatorInstance::setSquelchLevel(float signal_level_in) { + demodulatorThread->setSquelchLevel(signal_level_in); +} + + +float DemodulatorInstance::getSquelchLevel() { + return demodulatorThread->getSquelchLevel(); +} + diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index 8d0ee39..7432e0b 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -52,11 +52,20 @@ public: bool isStereo(); void setStereo(bool state); - void squelchAuto();bool isSquelchEnabled(); + void squelchAuto(); + bool isSquelchEnabled(); void setSquelchEnabled(bool state); + float getSignalLevel(); + void setSquelchLevel(float signal_level_in); + float getSquelchLevel(); + private: - std::atomic label;bool terminated;bool demodTerminated;bool audioTerminated;bool preDemodTerminated; + std::atomic label; // + bool terminated; // + bool demodTerminated; // + bool audioTerminated; // + bool preDemodTerminated; std::atomic active; std::atomic squelch; std::atomic stereo; diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index b3e9640..50aa8dd 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -9,11 +9,9 @@ DemodulatorThread::DemodulatorThread(DemodulatorThreadPostInputQueue* pQueue, DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) : postInputQueue(pQueue), visOutQueue(NULL), audioInputQueue(NULL), agc(NULL), stereo(false), terminated(false), threadQueueNotify( - threadQueueNotify), threadQueueControl(threadQueueControl), squelch_level(0), squelch_tolerance(0), squelch_enabled(false) { + threadQueueNotify), threadQueueControl(threadQueueControl), squelch_level(0), squelch_tolerance(0), signal_level(0), squelch_enabled(false) { - float kf = 0.5; // modulation factor - fdem = freqdem_create(kf); -// freqdem_print(fdem); + fdem = freqdem_create(0.5); } DemodulatorThread::~DemodulatorThread() { } @@ -64,7 +62,7 @@ void DemodulatorThread::threadMain() { double shift_freq = 0; agc = agc_crcf_create(); - agc_crcf_set_bandwidth(agc, 1e-3f); + agc_crcf_set_bandwidth(agc, 0.9); std::cout << "Demodulator thread started.." << std::endl; @@ -176,10 +174,19 @@ void DemodulatorThread::threadMain() { msresamp_rrrf_execute(stereo_resampler, &demod_output_stereo[0], num_written, &resampled_audio_output_stereo[0], &num_audio_written); } + float current_level = ((60.0/fabs(agc_crcf_get_rssi(agc)))/15.0 - signal_level); //agc_crcf_get_signal_level(agc); + + if (current_level > signal_level) { + signal_level = signal_level + (current_level-signal_level) * 0.5; + } else { + signal_level = signal_level + (current_level-signal_level) * 0.05; + } + + AudioThreadInput *ati = NULL; if (audioInputQueue != NULL) { - if (!squelch_enabled || ((agc_crcf_get_signal_level(agc)) >= 0.1)) { + if (!squelch_enabled || (signal_level >= squelch_level)) { for (buffers_i = buffers.begin(); buffers_i != buffers.end(); buffers_i++) { if ((*buffers_i)->getRefCount() <= 0) { @@ -330,3 +337,18 @@ void DemodulatorThread::setStereo(bool state) { bool DemodulatorThread::isStereo() { return stereo; } + +float DemodulatorThread::getSignalLevel() { + return signal_level; +} + +void DemodulatorThread::setSquelchLevel(float signal_level_in) { + if (!squelch_enabled) { + squelch_enabled = true; + } + squelch_level = signal_level_in; +} + +float DemodulatorThread::getSquelchLevel() { + return squelch_level; +} diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index 0501b59..7843a69 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -32,12 +32,16 @@ public: } void initialize(); - void terminate(); void setStereo(bool state); bool isStereo(); + float getSignalLevel(); + void setSquelchLevel(float signal_level_in); + float getSquelchLevel(); + + #ifdef __APPLE__ static void *pthread_helper(void *context) { return ((DemodulatorThread *) context)->threadMain(); @@ -57,7 +61,8 @@ protected: DemodulatorThreadCommandQueue* threadQueueNotify; DemodulatorThreadControlCommandQueue *threadQueueControl; - float squelch_level; + std::atomic squelch_level; float squelch_tolerance; + std::atomic signal_level; bool squelch_enabled; }; diff --git a/src/util/MouseTracker.cpp b/src/util/MouseTracker.cpp index 59819ed..905ca0d 100644 --- a/src/util/MouseTracker.cpp +++ b/src/util/MouseTracker.cpp @@ -9,7 +9,7 @@ void MouseTracker::OnMouseMoved(wxMouseEvent& event) { const wxSize ClientSize = target->GetClientSize(); mouseX = (float) event.m_x / (float) ClientSize.x; - mouseY = (float) event.m_y / (float) ClientSize.y; + mouseY = 1.0 - (float) event.m_y / (float) ClientSize.y; deltaMouseX = mouseX - lastMouseX; deltaMouseY = mouseY - lastMouseY; @@ -17,11 +17,11 @@ void MouseTracker::OnMouseMoved(wxMouseEvent& event) { if (isMouseDown) { #ifndef __APPLE__ if (horizDragLock && vertDragLock) { - target->WarpPointer(originMouseX * ClientSize.x, originMouseY * ClientSize.y); + target->WarpPointer(originMouseX * ClientSize.x, (1.0-originMouseY) * ClientSize.y); mouseX = originMouseX; mouseY = originMouseY; } else if (vertDragLock && mouseY != lastMouseY) { - target->WarpPointer(event.m_x, originMouseY * ClientSize.y); + target->WarpPointer(event.m_x, (1.0-originMouseY) * ClientSize.y); mouseY = originMouseY; } else if (horizDragLock && mouseX != lastMouseX) { target->WarpPointer(originMouseX * ClientSize.x, event.m_y); @@ -42,7 +42,7 @@ void MouseTracker::OnMouseDown(wxMouseEvent& event) { const wxSize ClientSize = target->GetClientSize(); mouseX = lastMouseX = (float) event.m_x / (float) ClientSize.x; - mouseY = lastMouseY = (float) event.m_y / (float) ClientSize.y; + mouseY = lastMouseY = 1.0 - (float) event.m_y / (float) ClientSize.y; originMouseX = mouseX; originMouseY = mouseY; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 589b966..0c9a4a1 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -42,6 +42,9 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : nco_shift = nco_crcf_create(LIQUID_NCO); shift_freq = 0; + fft_ceil_ma = fft_ceil_maa = 100.0; + fft_floor_ma = fft_floor_maa = 0.0; + mTracker.setTarget(this); SetCursor(wxCURSOR_CROSS); } @@ -81,9 +84,6 @@ void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) { } plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE); - fft_ceil_ma = fft_ceil_maa = 100.0; - fft_floor_ma = fft_floor_maa = 0.0; - glContext->Setup(fft_size, waterfall_lines); timer.start(); } @@ -409,8 +409,13 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { } for (int i = 0, iMax = fft_size; i < iMax; i++) { - fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65; - fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65; + if (isView) { + fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.85; + fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.55; + } else { + fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65; + fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65; + } if (fft_result_maa[i] > fft_ceil) { fft_ceil = fft_result_maa[i]; @@ -423,7 +428,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { fft_ceil += 1; fft_floor -= 1; - fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.01; + fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.01; fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.01; From 6679b20fbbbf3bc0475daf577faf0fd56794b33d Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 31 Dec 2014 21:31:37 -0500 Subject: [PATCH 08/20] Can now choose demodulator output devices --- src/AppFrame.cpp | 24 ++++++++--- src/AppFrame.h | 3 +- src/audio/AudioThread.cpp | 71 +++++++++++++++++++++++-------- src/audio/AudioThread.h | 5 +++ src/demod/DemodulatorInstance.cpp | 13 ++++++ src/demod/DemodulatorInstance.h | 3 ++ src/util/GLFont.cpp | 5 ++- src/visual/ScopeCanvas.cpp | 17 +++++--- src/visual/ScopeCanvas.h | 6 +-- src/visual/ScopeContext.cpp | 42 +++++++++++------- src/visual/ScopeContext.h | 1 + src/visual/SpectrumCanvas.cpp | 5 --- src/visual/SpectrumCanvas.h | 1 - src/visual/WaterfallCanvas.cpp | 8 +--- src/visual/WaterfallCanvas.h | 3 -- 15 files changed, 136 insertions(+), 71 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 1334782..9308687 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -22,13 +22,15 @@ wxBEGIN_EVENT_TABLE(AppFrame, wxFrame) //EVT_MENU(wxID_NEW, AppFrame::OnNewWindow) -EVT_MENU(wxID_CLOSE, AppFrame::OnClose) +//EVT_MENU(wxID_CLOSE, AppFrame::OnClose) +EVT_MENU(wxID_ANY, AppFrame::OnMenu) + EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread) EVT_IDLE(AppFrame::OnIdle) wxEND_EVENT_TABLE() AppFrame::AppFrame() : - wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { + wxFrame(NULL, wxID_ANY, wxT("CubicSDR")), activeDemodulator(NULL) { wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodOpts = new wxBoxSizer(wxVERTICAL); @@ -137,12 +139,14 @@ AppFrame::AppFrame() : i++; } + i = 0; for (mdevices_i = output_devices.begin(); mdevices_i != output_devices.end(); mdevices_i++) { - wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE+i,mdevices_i->second.name,wxT("Description?")); + wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE+mdevices_i->first,mdevices_i->second.name,wxT("Description?")); if (mdevices_i->second.isDefaultOutput) { itm->Check(true); } + output_device_menuitems[mdevices_i->first] = itm; } wxMenuBar *menuBar = new wxMenuBar; @@ -166,10 +170,13 @@ AppFrame::~AppFrame() { } -void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { - - // true is to force the frame to close - Close(true); +void AppFrame::OnMenu(wxCommandEvent& event) { + if (event.GetId() >= wxID_RT_AUDIO_DEVICE && event.GetId() < wxID_RT_AUDIO_DEVICE+output_devices.size()) { + if (activeDemodulator) { + activeDemodulator->setOutputDevice(event.GetId()-wxID_RT_AUDIO_DEVICE); + activeDemodulator = NULL; + } + } } void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) { @@ -193,6 +200,9 @@ void AppFrame::OnIdle(wxIdleEvent& event) { if (demod) { if (demod != activeDemodulator) { demodSignalMeter->setInputValue(demod->getSquelchLevel()); + int outputDevice = demod->getOutputDevice(); + scopeCanvas->setDeviceName(output_devices[outputDevice].name); + output_device_menuitems[outputDevice]->Check(true); } if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) { diff --git a/src/AppFrame.h b/src/AppFrame.h index 912b871..be1b2ea 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -21,7 +21,7 @@ public: void OnEventInput(wxThreadEvent& event); private: - void OnClose(wxCommandEvent& event); + void OnMenu(wxCommandEvent& event); void OnNewWindow(wxCommandEvent& event); void OnIdle(wxIdleEvent& event); @@ -37,6 +37,7 @@ private: std::map input_devices; std::map output_devices; + std::map output_device_menuitems; wxDECLARE_EVENT_TABLE(); }; diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index 4bb46a7..5a2d012 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -10,7 +10,7 @@ std::map AudioThread::deviceThread; #endif AudioThread::AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify) : - currentInput(NULL), inputQueue(inputQueue), audio_queue_ptr(0), underflow_count(0), terminated(false), active(false), gain(1.0), threadQueueNotify( + currentInput(NULL), inputQueue(inputQueue), audio_queue_ptr(0), underflow_count(0), terminated(false), active(false), output_device(-1), gain(1.0), threadQueueNotify( threadQueueNotify) { #ifdef __APPLE__ boundThreads = new std::vector; @@ -25,7 +25,9 @@ AudioThread::~AudioThread() { #ifdef __APPLE__ void AudioThread::bindThread(AudioThread *other) { - boundThreads.load()->push_back(other); + if (boundThreads.find(other) == boundThreads.end()) { + boundThreads.load()->push_back(other); + } } void AudioThread::removeThread(AudioThread *other) { @@ -263,22 +265,8 @@ void AudioThread::enumerateDevices(std::vector &devs) { } } -void AudioThread::threadMain() { -#ifdef __APPLE__ - pthread_t tID = pthread_self(); // ID of this thread - int priority = sched_get_priority_max( SCHED_RR) - 1; - sched_param prio = {priority}; // scheduling priority of thread - pthread_setschedparam(tID, SCHED_RR, &prio); -#endif - - std::cout << "Audio thread initializing.." << std::endl; - - if (dac.getDeviceCount() < 1) { - std::cout << "No audio devices found!" << std::endl; - return; - } - - parameters.deviceId = dac.getDefaultOutputDevice(); +void AudioThread::setupDevice(int deviceId) { + parameters.deviceId = deviceId; parameters.nChannels = 2; parameters.firstChannel = 0; unsigned int sampleRate = AUDIO_FREQUENCY; @@ -287,9 +275,15 @@ void AudioThread::threadMain() { RtAudio::StreamOptions opts; opts.streamName = "CubicSDR Audio Output"; + output_device = deviceId; + try { #ifdef __APPLE__ + if (active && deviceController.find(parameters.deviceId) != deviceController.end()) { + deviceController[parameters.deviceId]->removeThread(this); + } + opts.priority = sched_get_priority_max(SCHED_FIFO); // opts.flags = RTAUDIO_MINIMIZE_LATENCY; opts.flags = RTAUDIO_SCHEDULE_REALTIME; @@ -306,6 +300,13 @@ void AudioThread::threadMain() { } active = true; #else + if (dac.isStreamOpen()) { + if (dac.isStreamRunning()) { + dac.stopStream(); + } + dac.closeStream(); + } + dac.openStream(¶meters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts); dac.startStream(); @@ -314,10 +315,39 @@ void AudioThread::threadMain() { e.printMessage(); return; } +} + +int AudioThread::getOutputDevice() { + if (output_device == -1) { + return dac.getDefaultOutputDevice(); + } + return output_device; +} + +void AudioThread::threadMain() { +#ifdef __APPLE__ + pthread_t tID = pthread_self(); // ID of this thread + int priority = sched_get_priority_max( SCHED_RR) - 1; + sched_param prio = {priority}; // scheduling priority of thread + pthread_setschedparam(tID, SCHED_RR, &prio); +#endif + + std::cout << "Audio thread initializing.." << std::endl; + + if (dac.getDeviceCount() < 1) { + std::cout << "No audio devices found!" << std::endl; + return; + } + + setupDevice(dac.getDefaultOutputDevice()); while (!terminated) { AudioThreadCommand command; cmdQueue.pop(command); + + if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE) { + setupDevice(command.int_value); + } } #ifdef __APPLE__ @@ -387,3 +417,8 @@ void AudioThread::setActive(bool state) { #endif active = state; } + + +AudioThreadCommandQueue *AudioThread::getCommandQueue() { + return &cmdQueue; +} diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h index de0c806..1e9ed8e 100644 --- a/src/audio/AudioThread.h +++ b/src/audio/AudioThread.h @@ -61,6 +61,7 @@ public: std::atomic underflow_count; std::atomic terminated; std::atomic active; + std::atomic output_device; float gain; AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify); @@ -68,12 +69,16 @@ public: static void enumerateDevices(std::vector &devs); + void setupDevice(int deviceId); + int getOutputDevice(); void threadMain(); void terminate(); bool isActive(); void setActive(bool state); + AudioThreadCommandQueue *getCommandQueue(); + private: RtAudio dac; RtAudio::StreamParameters parameters; diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index 6618a1a..fefe5c4 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -194,3 +194,16 @@ float DemodulatorInstance::getSquelchLevel() { return demodulatorThread->getSquelchLevel(); } + +void DemodulatorInstance::setOutputDevice(int device_id) { + if (audioThread) { + AudioThreadCommand command; + command.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE; + command.int_value = device_id; + audioThread->getCommandQueue()->push(command); + } +} + +int DemodulatorInstance::getOutputDevice() { + return audioThread->getOutputDevice(); +} diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index 7432e0b..a88b8a6 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -60,6 +60,9 @@ public: void setSquelchLevel(float signal_level_in); float getSquelchLevel(); + void setOutputDevice(int device_id); + int getOutputDevice(); + private: std::atomic label; // bool terminated; // diff --git a/src/util/GLFont.cpp b/src/util/GLFont.cpp index 4fa2bed..88c7b64 100644 --- a/src/util/GLFont.cpp +++ b/src/util/GLFont.cpp @@ -382,7 +382,7 @@ void GLFont::drawString(std::string str, float xpos, float ypos, int pxHeight, A glPushMatrix(); glTranslatef(xpos, ypos, 0.0f); - switch (hAlign) { + switch (vAlign) { case GLFONT_ALIGN_TOP: glTranslatef(0.0, -size, 0.0); break; @@ -393,7 +393,7 @@ void GLFont::drawString(std::string str, float xpos, float ypos, int pxHeight, A break; } - switch (vAlign) { + switch (hAlign) { case GLFONT_ALIGN_RIGHT: glTranslatef(-msgWidth, 0.0, 0.0); break; @@ -445,5 +445,6 @@ void GLFont::drawString(std::string str, float xpos, float ypos, int pxHeight, A glPopMatrix(); glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); } diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp index 80ad241..ef04240 100644 --- a/src/visual/ScopeCanvas.cpp +++ b/src/visual/ScopeCanvas.cpp @@ -21,10 +21,9 @@ wxEND_EVENT_TABLE() ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), stereo(false) { + wxFULL_REPAINT_ON_RESIZE), parent(parent), stereo(false) { glContext = new ScopeContext(this, &wxGetApp().GetContext(this)); - timer.start(); } ScopeCanvas::~ScopeCanvas() { @@ -39,6 +38,11 @@ void ScopeCanvas::setStereo(bool state) { stereo = state; } +void ScopeCanvas::setDeviceName(std::string device_name) { + deviceName = device_name; + deviceName.append(" "); +} + void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); @@ -47,17 +51,16 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glViewport(0, 0, ClientSize.x, ClientSize.y); glContext->DrawBegin(); + if (!deviceName.empty()) { + glContext->DrawDeviceName(deviceName); + } glContext->Plot(waveform_points, stereo); glContext->DrawEnd(); + SwapBuffers(); } void ScopeCanvas::OnIdle(wxIdleEvent &event) { -// timer.update(); -// frameTimer += timer.lastUpdateSeconds(); -// if (frameTimer > 1.0/30.0) { Refresh(false); -// frameTimer = 0; -// } } diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h index 29d1cd4..94f3328 100644 --- a/src/visual/ScopeCanvas.h +++ b/src/visual/ScopeCanvas.h @@ -7,9 +7,7 @@ #include #include "ScopeContext.h" - #include "fftw3.h" -#include "Timer.h" class ScopeCanvas: public wxGLCanvas { public: @@ -20,6 +18,7 @@ public: void setWaveformPoints(std::vector &waveform_points_in); void setStereo(bool state); + void setDeviceName(std::string device_name); private: void OnPaint(wxPaintEvent& event); @@ -28,8 +27,7 @@ private: wxWindow *parent; ScopeContext *glContext; - Timer timer; - float frameTimer; + std::string deviceName; bool stereo; // event table wxDECLARE_EVENT_TABLE(); diff --git a/src/visual/ScopeContext.cpp b/src/visual/ScopeContext.cpp index 1a12298..15d43ef 100644 --- a/src/visual/ScopeContext.cpp +++ b/src/visual/ScopeContext.cpp @@ -4,20 +4,20 @@ ScopeContext::ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext) : PrimaryGLContext(canvas, sharedContext) { - glDisable (GL_CULL_FACE); - glDisable (GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); - glMatrixMode (GL_PROJECTION); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); } void ScopeContext::DrawBegin() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glMatrixMode (GL_MODELVIEW); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glDisable (GL_TEXTURE_2D); + glDisable(GL_TEXTURE_2D); } void ScopeContext::Plot(std::vector &points, bool stereo) { @@ -26,27 +26,27 @@ void ScopeContext::Plot(std::vector &points, bool stereo) { if (stereo) { glColor3f(0.7, 0.7, 0.7); glBegin(GL_LINES); - glVertex2f(-1.0,0.0); - glVertex2f(1.0,0.0); + glVertex2f(-1.0, 0.0); + glVertex2f(1.0, 0.0); glEnd(); glColor3f(0.3, 0.3, 0.3); glBegin(GL_LINES); - glVertex2f(-1.0,0.5); - glVertex2f(1.0,0.5); - glVertex2f(-1.0,-0.5); - glVertex2f(1.0,-0.5); + glVertex2f(-1.0, 0.5); + glVertex2f(1.0, 0.5); + glVertex2f(-1.0, -0.5); + glVertex2f(1.0, -0.5); glEnd(); } else { glColor3f(0.3, 0.3, 0.3); glBegin(GL_LINES); - glVertex2f(-1.0,0.0); - glVertex2f(1.0,0.0); + glVertex2f(-1.0, 0.0); + glVertex2f(1.0, 0.0); glEnd(); } glColor3f(0.9, 0.9, 0.9); if (points.size()) { - glEnableClientState (GL_VERTEX_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &points[0]); if (stereo) { glPushMatrix(); @@ -74,6 +74,16 @@ void ScopeContext::Plot(std::vector &points, bool stereo) { } } +void ScopeContext::DrawDeviceName(std::string deviceName) { + GLint vp[4]; + glGetIntegerv( GL_VIEWPORT, vp); + float viewHeight = (float) vp[3]; + float hPos = (float) (viewHeight - 20) / viewHeight; + + glColor3f(0.65,0.65,0.65); + getFont(PrimaryGLContext::GLFONT_SIZE12).drawString(deviceName.c_str(), 1.0, hPos, 12, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER); +} + void ScopeContext::DrawEnd() { glFlush(); @@ -83,7 +93,7 @@ void ScopeContext::DrawEnd() { void ScopeContext::DrawDivider() { glColor3f(1.0, 1.0, 1.0); glBegin(GL_LINES); - glVertex2f(0.0,-1.0); - glVertex2f(0.0,1.0); + glVertex2f(0.0, -1.0); + glVertex2f(0.0, 1.0); glEnd(); } diff --git a/src/visual/ScopeContext.h b/src/visual/ScopeContext.h index 321f14e..d4fa1a0 100644 --- a/src/visual/ScopeContext.h +++ b/src/visual/ScopeContext.h @@ -13,6 +13,7 @@ public: void DrawBegin(); void Plot(std::vector &points, bool stereo=false); + void DrawDeviceName(std::string deviceName); void DrawDivider(); void DrawEnd(); diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 1c73431..6b9708e 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -207,12 +207,7 @@ unsigned int SpectrumCanvas::GetBandwidth() { } void SpectrumCanvas::OnIdle(wxIdleEvent &event) { -// timer.update(); -// frameTimer += timer.lastUpdateSeconds(); -// if (frameTimer > 1.0/30.0) { Refresh(false); -// frameTimer = 0; -// } } void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index dea6b74..1d68c6c 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -9,7 +9,6 @@ #include "SpectrumContext.h" #include "fftw3.h" -#include "Timer.h" #include "MouseTracker.h" class SpectrumCanvas: public wxGLCanvas { diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 0c9a4a1..4f69b82 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -32,7 +32,7 @@ wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), spectrumCanvas(NULL), frameTimer(0), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( + wxFULL_REPAINT_ON_RESIZE), parent(parent), spectrumCanvas(NULL), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan( NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth( 0) { @@ -85,7 +85,6 @@ void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) { plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE); glContext->Setup(fft_size, waterfall_lines); - timer.start(); } int WaterfallCanvas::GetFrequencyAt(float x) { @@ -447,12 +446,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { } void WaterfallCanvas::OnIdle(wxIdleEvent &event) { -// timer.update(); -// frameTimer += timer.lastUpdateSeconds(); -// if (frameTimer > 1.0/30.0) { Refresh(false); -// frameTimer = 0; -// } } void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index fb8196c..9656e32 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -11,7 +11,6 @@ #include "SpectrumCanvas.h" #include "fftw3.h" -#include "Timer.h" class WaterfallCanvas: public wxGLCanvas { public: @@ -69,8 +68,6 @@ private: std::vector fft_result_maa; WaterfallContext *glContext; - Timer timer; - float frameTimer; MouseTracker mTracker; int activeDemodulatorBandwidth; From cda41e27edbc6f128c32a02851ba96552592c4d0 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 31 Dec 2014 21:34:05 -0500 Subject: [PATCH 09/20] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 707c176..6cc5775 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Basic Goals and Status: - [ ] 3D visuals - Demodulation: - [x] Multiple demodulators per IQ stream - - [ ] Audio device selection + - [x] Audio device selection - [ ] Modes - [x] FM - [x] WFM @@ -49,9 +49,9 @@ Basic Goals and Status: - [ ] Controls - [ ] Display Frequency and allow manual adjustments - [ ] Allow selection of demodulation type - - [ ] Display separate zoomed-in view of current waterfall and spectrum, allow adjustments - - [ ] Display signal level and allow squelch control - - [ ] Display audio output selection + - [x] Display separate zoomed-in view of current waterfall and spectrum, allow adjustments + - [x] Display signal level and allow squelch control + - [x] Display audio output selection - [ ] Volume control - [ ] Basic noise reduction / filter controls? - Basic Input Controls From 3da7e18b8881534abc266274c864e605497b2d98 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 31 Dec 2014 21:43:05 -0500 Subject: [PATCH 10/20] New meter widget --- src/visual/MeterCanvas.cpp | 131 ++++++++++++++++++++++++++++++++++++ src/visual/MeterCanvas.h | 57 ++++++++++++++++ src/visual/MeterContext.cpp | 40 +++++++++++ src/visual/MeterContext.h | 19 ++++++ 4 files changed, 247 insertions(+) create mode 100644 src/visual/MeterCanvas.cpp create mode 100644 src/visual/MeterCanvas.h create mode 100644 src/visual/MeterContext.cpp create mode 100644 src/visual/MeterContext.h diff --git a/src/visual/MeterCanvas.cpp b/src/visual/MeterCanvas.cpp new file mode 100644 index 0000000..61989b9 --- /dev/null +++ b/src/visual/MeterCanvas.cpp @@ -0,0 +1,131 @@ +#include "MeterCanvas.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#if !wxUSE_GLCANVAS +#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" +#endif + +#include "CubicSDR.h" +#include "CubicSDRDefs.h" +#include "AppFrame.h" +#include + +wxBEGIN_EVENT_TABLE(MeterCanvas, wxGLCanvas) EVT_PAINT(MeterCanvas::OnPaint) +EVT_IDLE(MeterCanvas::OnIdle) +EVT_MOTION(MeterCanvas::mouseMoved) +EVT_LEFT_DOWN(MeterCanvas::mouseDown) +EVT_LEFT_UP(MeterCanvas::mouseReleased) +EVT_LEAVE_WINDOW(MeterCanvas::mouseLeftWindow) +EVT_ENTER_WINDOW(MeterCanvas::mouseEnterWindow) +wxEND_EVENT_TABLE() + +MeterCanvas::MeterCanvas(wxWindow *parent, int *attribList) : + wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, + wxFULL_REPAINT_ON_RESIZE), parent(parent), level(0), level_max(1), inputValue(0), userInputValue(0), shiftDown(false), altDown(false), ctrlDown(false) { + + glContext = new MeterContext(this, &wxGetApp().GetContext(this)); + mTracker.setTarget(this); +} + +MeterCanvas::~MeterCanvas() { + +} + +void MeterCanvas::setLevel(float level_in) { + level = level_in; +} +float MeterCanvas::getLevel() { + return level; +} + +void MeterCanvas::setMax(float max_in) { + level_max = max_in; +} + +bool MeterCanvas::setInputValue(float slider_in) { + userInputValue = inputValue = slider_in; +} +bool MeterCanvas::inputChanged() { + return (inputValue != userInputValue); +} +float MeterCanvas::getInputValue() { + inputValue = userInputValue; + return userInputValue; +} + +void MeterCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { + wxPaintDC dc(this); + const wxSize ClientSize = GetClientSize(); + + glContext->SetCurrent(*this); + glViewport(0, 0, ClientSize.x, ClientSize.y); + + glContext->DrawBegin(); + if (mTracker.mouseInView()) { + glContext->Draw(0.4, 0.4, 0.4, 0.5, mTracker.getMouseY()); + } + + glContext->Draw(0.1, 0.75, 0.1, 0.5, level/level_max); + + glContext->Draw(0.75, 0.1, 0.1, 0.5, userInputValue/level_max); + + glContext->DrawEnd(); + + SwapBuffers(); +} + +void MeterCanvas::OnIdle(wxIdleEvent &event) { + Refresh(false); +} + +void MeterCanvas::mouseMoved(wxMouseEvent& event) { + mTracker.OnMouseMoved(event); + + shiftDown = event.ShiftDown(); + altDown = event.AltDown(); + ctrlDown = event.ControlDown(); + + if (mTracker.mouseDown()) { + userInputValue = mTracker.getMouseY()*level_max; + } +} + +void MeterCanvas::mouseDown(wxMouseEvent& event) { + mTracker.OnMouseDown(event); + + shiftDown = event.ShiftDown(); + altDown = event.AltDown(); + ctrlDown = event.ControlDown(); + + userInputValue = mTracker.getMouseY()*level_max; + mTracker.setHorizDragLock(true); +} + +void MeterCanvas::mouseWheelMoved(wxMouseEvent& event) { + mTracker.OnMouseWheelMoved(event); +} + +void MeterCanvas::mouseReleased(wxMouseEvent& event) { + mTracker.OnMouseReleased(event); + + shiftDown = event.ShiftDown(); + altDown = event.AltDown(); + ctrlDown = event.ControlDown(); + + userInputValue = mTracker.getMouseY()*level_max; +} + +void MeterCanvas::mouseLeftWindow(wxMouseEvent& event) { + mTracker.OnMouseLeftWindow(event); + SetCursor(wxCURSOR_CROSS); +} + +void MeterCanvas::mouseEnterWindow(wxMouseEvent& event) { + mTracker.OnMouseEnterWindow(event); + SetCursor(wxCURSOR_CROSS); +} diff --git a/src/visual/MeterCanvas.h b/src/visual/MeterCanvas.h new file mode 100644 index 0000000..67da7e5 --- /dev/null +++ b/src/visual/MeterCanvas.h @@ -0,0 +1,57 @@ +#pragma once + +#include "wx/glcanvas.h" +#include "wx/timer.h" + +#include +#include + +#include "MeterContext.h" +#include "MouseTracker.h" + +#include "fftw3.h" +#include "Timer.h" + +class MeterCanvas: public wxGLCanvas { +public: + std::vector waveform_points; + + MeterCanvas(wxWindow *parent, int *attribList = NULL); + ~MeterCanvas(); + + void setLevel(float level_in); + float getLevel(); + + void setMax(float max_in); + + bool setInputValue(float slider_in); + bool inputChanged(); + float getInputValue(); + +private: + void OnPaint(wxPaintEvent& event); + void OnIdle(wxIdleEvent &event); + + void mouseMoved(wxMouseEvent& event); + void mouseDown(wxMouseEvent& event); + void mouseWheelMoved(wxMouseEvent& event); + void mouseReleased(wxMouseEvent& event); + void mouseEnterWindow(wxMouseEvent& event); + void mouseLeftWindow(wxMouseEvent& event); + + MouseTracker mTracker; + wxWindow *parent; + MeterContext *glContext; + + float level; + float level_max; + + float inputValue; + float userInputValue; + + bool shiftDown; + bool altDown; + bool ctrlDown; +wxDECLARE_EVENT_TABLE(); +}; + diff --git a/src/visual/MeterContext.cpp b/src/visual/MeterContext.cpp new file mode 100644 index 0000000..40fe4fd --- /dev/null +++ b/src/visual/MeterContext.cpp @@ -0,0 +1,40 @@ +#include "MeterContext.h" +#include "MeterCanvas.h" + +MeterContext::MeterContext(MeterCanvas *canvas, wxGLContext *sharedContext) : + PrimaryGLContext(canvas, sharedContext) { + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); +} + +void MeterContext::DrawBegin() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDisable(GL_TEXTURE_2D); +} + +void MeterContext::Draw(float r, float g, float b, float a, float level) { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glColor4f(r, g, b, a); + glBegin(GL_QUADS); + glVertex2f(1.0, -1.0 + 2.0 * level); + glVertex2f(-1.0, -1.0 + 2.0 * level); + glVertex2f(-1.0, -1.0); + glVertex2f(1.0, -1.0); + glEnd(); + glDisable(GL_BLEND); +} + +void MeterContext::DrawEnd() { + glFlush(); + + CheckGLError(); +} + diff --git a/src/visual/MeterContext.h b/src/visual/MeterContext.h new file mode 100644 index 0000000..3fac2f2 --- /dev/null +++ b/src/visual/MeterContext.h @@ -0,0 +1,19 @@ +#pragma once + +#include "PrimaryGLContext.h" +#include "Gradient.h" + +#define NUM_WATERFALL_LINES 512 + +class MeterCanvas; + +class MeterContext: public PrimaryGLContext { +public: + MeterContext(MeterCanvas *canvas, wxGLContext *sharedContext); + + void DrawBegin(); + void Draw(float r, float g, float b, float a, float level); + void DrawEnd(); + +private: +}; From 955ec54b44362abce932097893401c95fb79cf14 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 31 Dec 2014 22:38:59 -0500 Subject: [PATCH 11/20] Audio device selection fixes for OSX --- src/AppFrame.cpp | 10 +++++++--- src/AppFrame.h | 4 +++- src/audio/AudioThread.cpp | 19 +++++++++++++------ src/audio/AudioThread.h | 1 + 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 9308687..794817a 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -22,7 +22,7 @@ wxBEGIN_EVENT_TABLE(AppFrame, wxFrame) //EVT_MENU(wxID_NEW, AppFrame::OnNewWindow) -//EVT_MENU(wxID_CLOSE, AppFrame::OnClose) +EVT_MENU(wxID_CLOSE, AppFrame::OnClose) EVT_MENU(wxID_ANY, AppFrame::OnMenu) EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread) @@ -122,7 +122,6 @@ AppFrame::AppFrame() : wxMenu *menu = new wxMenu; - std::vector devices; std::vector::iterator devices_i; std::map::iterator mdevices_i; AudioThread::enumerateDevices(devices); @@ -143,6 +142,7 @@ AppFrame::AppFrame() : for (mdevices_i = output_devices.begin(); mdevices_i != output_devices.end(); mdevices_i++) { wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE+mdevices_i->first,mdevices_i->second.name,wxT("Description?")); + itm->SetId(wxID_RT_AUDIO_DEVICE+mdevices_i->first); if (mdevices_i->second.isDefaultOutput) { itm->Check(true); } @@ -171,7 +171,7 @@ AppFrame::~AppFrame() { } void AppFrame::OnMenu(wxCommandEvent& event) { - if (event.GetId() >= wxID_RT_AUDIO_DEVICE && event.GetId() < wxID_RT_AUDIO_DEVICE+output_devices.size()) { + if (event.GetId() >= wxID_RT_AUDIO_DEVICE && event.GetId() < wxID_RT_AUDIO_DEVICE+devices.size()) { if (activeDemodulator) { activeDemodulator->setOutputDevice(event.GetId()-wxID_RT_AUDIO_DEVICE); activeDemodulator = NULL; @@ -179,6 +179,10 @@ void AppFrame::OnMenu(wxCommandEvent& event) { } } +void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { + Close(true); +} + void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) { new AppFrame(); } diff --git a/src/AppFrame.h b/src/AppFrame.h index be1b2ea..7dc83a9 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -10,7 +10,7 @@ #include -#define wxID_RT_AUDIO_DEVICE 10000 +#define wxID_RT_AUDIO_DEVICE 1000 // Define a new frame type class AppFrame: public wxFrame { @@ -22,6 +22,7 @@ public: private: void OnMenu(wxCommandEvent& event); + void OnClose(wxCommandEvent& event); void OnNewWindow(wxCommandEvent& event); void OnIdle(wxIdleEvent& event); @@ -35,6 +36,7 @@ private: DemodulatorInstance *activeDemodulator; + std::vector devices; std::map input_devices; std::map output_devices; std::map output_device_menuitems; diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index 5a2d012..f1b75b6 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -25,7 +25,7 @@ AudioThread::~AudioThread() { #ifdef __APPLE__ void AudioThread::bindThread(AudioThread *other) { - if (boundThreads.find(other) == boundThreads.end()) { + if (std::find(boundThreads.load()->begin(), boundThreads.load()->end(), other) == boundThreads.load()->end()) { boundThreads.load()->push_back(other); } } @@ -275,13 +275,11 @@ void AudioThread::setupDevice(int deviceId) { RtAudio::StreamOptions opts; opts.streamName = "CubicSDR Audio Output"; - output_device = deviceId; - try { #ifdef __APPLE__ - if (active && deviceController.find(parameters.deviceId) != deviceController.end()) { - deviceController[parameters.deviceId]->removeThread(this); + if (deviceController.find(output_device.load()) != deviceController.end()) { + deviceController[output_device.load()]->removeThread(this); } opts.priority = sched_get_priority_max(SCHED_FIFO); @@ -290,6 +288,7 @@ void AudioThread::setupDevice(int deviceId) { if (deviceController.find(parameters.deviceId) == deviceController.end()) { deviceController[parameters.deviceId] = new AudioThread(NULL, NULL); + deviceController[parameters.deviceId]->setInitOutputDevice(parameters.deviceId); deviceController[parameters.deviceId]->bindThread(this); deviceThread[parameters.deviceId] = new std::thread(&AudioThread::threadMain, deviceController[parameters.deviceId]); } else if (deviceController[parameters.deviceId] == this) { @@ -315,6 +314,8 @@ void AudioThread::setupDevice(int deviceId) { e.printMessage(); return; } + + output_device = deviceId; } int AudioThread::getOutputDevice() { @@ -324,6 +325,10 @@ int AudioThread::getOutputDevice() { return output_device; } +void AudioThread::setInitOutputDevice(int deviceId) { + output_device = deviceId; +} + void AudioThread::threadMain() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread @@ -339,7 +344,9 @@ void AudioThread::threadMain() { return; } - setupDevice(dac.getDefaultOutputDevice()); + setupDevice((output_device.load() == -1)?(dac.getDefaultOutputDevice()):output_device.load()); + + std::cout << "Audio thread started." << std::endl; while (!terminated) { AudioThreadCommand command; diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h index 1e9ed8e..36f53f2 100644 --- a/src/audio/AudioThread.h +++ b/src/audio/AudioThread.h @@ -70,6 +70,7 @@ public: static void enumerateDevices(std::vector &devs); void setupDevice(int deviceId); + void setInitOutputDevice(int deviceId); int getOutputDevice(); void threadMain(); void terminate(); From 8b89b27b40435adfd4ebd493410918512f1b6de9 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Thu, 1 Jan 2015 03:48:32 -0500 Subject: [PATCH 12/20] Experimental AM/LSB/USB demodulation --- src/AppFrame.cpp | 29 +++++++++- src/AppFrame.h | 3 + src/demod/DemodulatorInstance.cpp | 9 +++ src/demod/DemodulatorInstance.h | 3 + src/demod/DemodulatorThread.cpp | 96 ++++++++++++++++++++++++------- src/demod/DemodulatorThread.h | 28 +++++++-- src/visual/WaterfallCanvas.cpp | 4 +- 7 files changed, 144 insertions(+), 28 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 794817a..7281b1f 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -150,7 +150,16 @@ AppFrame::AppFrame() : } wxMenuBar *menuBar = new wxMenuBar; - menuBar->Append(menu, wxT("&Device")); + menuBar->Append(menu, wxT("Output &Device")); + + wxMenu *demodMenu = new wxMenu; + demod_menuitems[DemodulatorType::DEMOD_TYPE_FM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_FM,wxT("FM"),wxT("Description?")); + demod_menuitems[DemodulatorType::DEMOD_TYPE_AM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_AM,wxT("AM"),wxT("Description?")); + demod_menuitems[DemodulatorType::DEMOD_TYPE_LSB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_LSB,wxT("LSB"),wxT("Description?")); + demod_menuitems[DemodulatorType::DEMOD_TYPE_USB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_USB,wxT("USB"),wxT("Description?")); + + menuBar->Append(demodMenu, wxT("Demodulaton &Type")); + SetMenuBar(menuBar); @@ -177,6 +186,22 @@ void AppFrame::OnMenu(wxCommandEvent& event) { activeDemodulator = NULL; } } + + if (activeDemodulator) { + if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_FM) { + activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_FM); + activeDemodulator = NULL; + } else if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_AM) { + activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_AM); + activeDemodulator = NULL; + } else if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_LSB) { + activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_LSB); + activeDemodulator = NULL; + } else if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_USB) { + activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_USB); + activeDemodulator = NULL; + } + } } void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { @@ -207,6 +232,8 @@ void AppFrame::OnIdle(wxIdleEvent& event) { int outputDevice = demod->getOutputDevice(); scopeCanvas->setDeviceName(output_devices[outputDevice].name); output_device_menuitems[outputDevice]->Check(true); + DemodulatorType dType = demod->getDemodulatorType(); + demod_menuitems[dType]->Check(true); } if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) { diff --git a/src/AppFrame.h b/src/AppFrame.h index 7dc83a9..c8144da 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -11,6 +11,7 @@ #include #define wxID_RT_AUDIO_DEVICE 1000 +#define wxID_DEMOD_TYPE 1500 // Define a new frame type class AppFrame: public wxFrame { @@ -41,5 +42,7 @@ private: std::map output_devices; std::map output_device_menuitems; + std::map demod_menuitems; + wxDECLARE_EVENT_TABLE(); }; diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index fefe5c4..3e67f56 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -207,3 +207,12 @@ void DemodulatorInstance::setOutputDevice(int device_id) { int DemodulatorInstance::getOutputDevice() { return audioThread->getOutputDevice(); } + +void DemodulatorInstance::setDemodulatorType(DemodulatorType demod_type_in) { + demodulatorThread->setDemodulatorType(demod_type_in); +} + +DemodulatorType DemodulatorInstance::getDemodulatorType() { + return demodulatorThread->getDemodulatorType(); +} + diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index a88b8a6..cc943ba 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -63,6 +63,9 @@ public: void setOutputDevice(int device_id); int getOutputDevice(); + void setDemodulatorType(DemodulatorType demod_type_in); + DemodulatorType getDemodulatorType(); + private: std::atomic label; // bool terminated; // diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index 50aa8dd..f12ae2a 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -8,10 +8,18 @@ DemodulatorThread::DemodulatorThread(DemodulatorThreadPostInputQueue* pQueue, DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) : - postInputQueue(pQueue), visOutQueue(NULL), audioInputQueue(NULL), agc(NULL), stereo(false), terminated(false), threadQueueNotify( - threadQueueNotify), threadQueueControl(threadQueueControl), squelch_level(0), squelch_tolerance(0), signal_level(0), squelch_enabled(false) { + postInputQueue(pQueue), visOutQueue(NULL), audioInputQueue(NULL), agc(NULL), am_max(1), am_max_ma(1), am_max_maa(1), stereo(false), terminated( + false), demodulatorType(DemodulatorType::DEMOD_TYPE_FM), threadQueueNotify(threadQueueNotify), threadQueueControl(threadQueueControl), squelch_level( + 0), squelch_tolerance(0), signal_level(0), squelch_enabled(false) { fdem = freqdem_create(0.5); + liquid_ampmodem_type type_lsb = LIQUID_AMPMODEM_LSB; + ampdem_lsb = ampmodem_create(1.0, 0.5, type_lsb, 1); + liquid_ampmodem_type type_usb = LIQUID_AMPMODEM_USB; + ampdem_usb = ampmodem_create(1.0, -0.5, type_usb, 1); + liquid_ampmodem_type type_dsb = LIQUID_AMPMODEM_DSB; + ampdem = ampmodem_create(0.5, 0.0, type_dsb, 0); + } DemodulatorThread::~DemodulatorThread() { } @@ -66,16 +74,6 @@ void DemodulatorThread::threadMain() { std::cout << "Demodulator thread started.." << std::endl; - std::deque buffers; - std::deque::iterator buffers_i; - - std::vector resampled_data; - std::vector agc_data; - std::vector demod_output; - std::vector demod_output_stereo; - std::vector resampled_audio_output; - std::vector resampled_audio_output_stereo; - double freq_index = 0; while (!terminated) { @@ -101,6 +99,11 @@ void DemodulatorThread::threadMain() { resampler = inp->resampler; audio_resampler = inp->audio_resampler; stereo_resampler = inp->stereo_resampler; + + ampmodem_reset(ampdem_lsb); + ampmodem_reset(ampdem_usb); + ampmodem_reset(ampdem); + freqdem_reset(fdem); } int out_size = ceil((double) (bufSize) * inp->resample_ratio); @@ -108,17 +111,17 @@ void DemodulatorThread::threadMain() { if (agc_data.size() != out_size) { if (agc_data.capacity() < out_size) { agc_data.reserve(out_size); + agc_am_data.reserve(out_size); resampled_data.reserve(out_size); } agc_data.resize(out_size); resampled_data.resize(out_size); + agc_am_data.resize(out_size); } unsigned int num_written; msresamp_crcf_execute(resampler, &(inp->data[0]), bufSize, &resampled_data[0], &num_written); - agc_crcf_execute_block(agc, &resampled_data[0], num_written, &agc_data[0]); - double audio_resample_ratio = inp->audio_resample_ratio; if (demod_output.size() != num_written) { @@ -130,7 +133,51 @@ void DemodulatorThread::threadMain() { int audio_out_size = ceil((double) (num_written) * audio_resample_ratio); - freqdem_demodulate_block(fdem, &agc_data[0], num_written, &demod_output[0]); + agc_crcf_execute_block(agc, &resampled_data[0], num_written, &agc_data[0]); + + float current_level = 0; + + current_level = ((60.0 / fabs(agc_crcf_get_rssi(agc))) / 15.0 - signal_level); + + if (agc_crcf_get_signal_level(agc) > current_level) { + current_level = agc_crcf_get_signal_level(agc); + } + + switch (demodulatorType) { + case DemodulatorType::DEMOD_TYPE_FM: + freqdem_demodulate_block(fdem, &agc_data[0], num_written, &demod_output[0]); + break; + case DemodulatorType::DEMOD_TYPE_LSB: + for (int i = 0; i < num_written; i++) { + ampmodem_demodulate(ampdem_lsb, resampled_data[i], &demod_output[i]); + } + + break; + case DemodulatorType::DEMOD_TYPE_USB: + for (int i = 0; i < num_written; i++) { + ampmodem_demodulate(ampdem_usb, resampled_data[i], &demod_output[i]); + } + + break; + case DemodulatorType::DEMOD_TYPE_AM: + am_max = 0; + for (int i = 0; i < num_written; i++) { + ampmodem_demodulate(ampdem, resampled_data[i], &demod_output[i]); + if (demod_output[i] > am_max) { + am_max = demod_output[i]; + } + } + am_max_ma = am_max_ma + (am_max - am_max_ma) * 0.03; + am_max_maa = am_max_maa + (am_max_ma - am_max_maa) * 0.03; + + float gain = 0.95/am_max_maa; + + for (int i = 0; i < num_written; i++) { + demod_output[i] *= gain; + } + + break; + } if (audio_out_size != resampled_audio_output.size()) { if (resampled_audio_output.capacity() < audio_out_size) { @@ -174,15 +221,12 @@ void DemodulatorThread::threadMain() { msresamp_rrrf_execute(stereo_resampler, &demod_output_stereo[0], num_written, &resampled_audio_output_stereo[0], &num_audio_written); } - float current_level = ((60.0/fabs(agc_crcf_get_rssi(agc)))/15.0 - signal_level); //agc_crcf_get_signal_level(agc); - if (current_level > signal_level) { - signal_level = signal_level + (current_level-signal_level) * 0.5; + signal_level = signal_level + (current_level - signal_level) * 0.5; } else { - signal_level = signal_level + (current_level-signal_level) * 0.05; + signal_level = signal_level + (current_level - signal_level) * 0.05; } - AudioThreadInput *ati = NULL; if (audioInputQueue != NULL) { @@ -242,8 +286,8 @@ void DemodulatorThread::threadMain() { ati_vis->data.resize(stereoSize); for (int i = 0; i < stereoSize / 2; i++) { - ati_vis->data[i] = ati->data[i*2]; - ati_vis->data[i + stereoSize / 2] = ati->data[i*2+1]; + ati_vis->data[i] = ati->data[i * 2]; + ati_vis->data[i + stereoSize / 2] = ati->data[i * 2 + 1]; } } else { ati_vis->channels = 1; @@ -352,3 +396,11 @@ void DemodulatorThread::setSquelchLevel(float signal_level_in) { float DemodulatorThread::getSquelchLevel() { return squelch_level; } + +void DemodulatorThread::setDemodulatorType(DemodulatorType demod_type_in) { + demodulatorType = demod_type_in; +} + +DemodulatorType DemodulatorThread::getDemodulatorType() { + return demodulatorType; +} diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index 7843a69..8d7f5f1 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -34,13 +34,14 @@ public: void initialize(); void terminate(); - void setStereo(bool state); - bool isStereo(); + void setStereo(bool state);bool isStereo(); float getSignalLevel(); void setSquelchLevel(float signal_level_in); float getSquelchLevel(); + void setDemodulatorType(DemodulatorType demod_type_in); + DemodulatorType getDemodulatorType(); #ifdef __APPLE__ static void *pthread_helper(void *context) { @@ -49,20 +50,39 @@ public: #endif protected: + std::deque buffers; + std::deque::iterator buffers_i; + + std::vector resampled_data; + std::vector agc_data; + std::vector agc_am_data; + std::vector demod_output; + std::vector demod_output_stereo; + std::vector resampled_audio_output; + std::vector resampled_audio_output_stereo; + DemodulatorThreadPostInputQueue* postInputQueue; DemodulatorThreadOutputQueue* visOutQueue; AudioThreadInputQueue *audioInputQueue; freqdem fdem; + ampmodem ampdem; + ampmodem ampdem_usb; + ampmodem ampdem_lsb; + agc_crcf agc; + float am_max; + float am_max_ma; + float am_max_maa; + std::atomic stereo; std::atomic terminated; + std::atomic demodulatorType; DemodulatorThreadCommandQueue* threadQueueNotify; DemodulatorThreadControlCommandQueue *threadQueueControl; std::atomic squelch_level; float squelch_tolerance; - std::atomic signal_level; - bool squelch_enabled; + std::atomic signal_level;bool squelch_enabled; }; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 4f69b82..4c56c59 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -17,7 +17,9 @@ #include -#define MIN_FM_BANDWIDTH 10000 +#define MIN_FM_BANDWIDTH 2000 +#define MIN_AM_BANDWIDTH 2000 + wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) EVT_PAINT(WaterfallCanvas::OnPaint) EVT_KEY_DOWN(WaterfallCanvas::OnKeyDown) From 4f43f65065ef0dee3c5cbb62cf45556132560b5f Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Thu, 1 Jan 2015 18:08:54 -0500 Subject: [PATCH 13/20] AM, USB and LSB somewhat working --- src/AppFrame.cpp | 88 ++++++++--------- src/AppFrame.h | 7 +- src/CubicSDRDefs.h | 3 +- src/demod/DemodDefs.h | 16 ++-- src/demod/DemodulatorInstance.cpp | 16 +++- src/demod/DemodulatorInstance.h | 6 +- src/demod/DemodulatorPreThread.cpp | 4 +- src/demod/DemodulatorThread.cpp | 131 ++++++++++++++++++-------- src/demod/DemodulatorThread.h | 13 ++- src/demod/DemodulatorWorkerThread.cpp | 1 - src/visual/SpectrumCanvas.h | 10 +- src/visual/WaterfallCanvas.cpp | 66 ++++++++----- src/visual/WaterfallCanvas.h | 12 +-- 13 files changed, 232 insertions(+), 141 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 7281b1f..9dae70d 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -38,35 +38,35 @@ AppFrame::AppFrame() : wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); /* - demodTray->AddSpacer(5); - demodOpts->AddSpacer(5); + demodTray->AddSpacer(5); + demodOpts->AddSpacer(5); - wxStaticText *audioDeviceLabel = new wxStaticText(this, wxID_ANY, wxString("Audio Device:")); - demodOpts->Add(audioDeviceLabel, 1, wxFIXED_MINSIZE | wxALL, 0); + wxStaticText *audioDeviceLabel = new wxStaticText(this, wxID_ANY, wxString("Audio Device:")); + demodOpts->Add(audioDeviceLabel, 1, wxFIXED_MINSIZE | wxALL, 0); - wxArrayString str; - str.Add("Primary Device"); - wxChoice *wxCh = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, str); - demodOpts->Add(wxCh, 1, wxFIXED_MINSIZE | wxALL, 0); + wxArrayString str; + str.Add("Primary Device"); + wxChoice *wxCh = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, str); + demodOpts->Add(wxCh, 1, wxFIXED_MINSIZE | wxALL, 0); - demodOpts->AddSpacer(2); + demodOpts->AddSpacer(2); - wxStaticText *demodTypeLabel = new wxStaticText(this, wxID_ANY, wxString("Demodulation:")); - demodOpts->Add(demodTypeLabel, 1, wxFIXED_MINSIZE | wxALL, 0); + wxStaticText *demodTypeLabel = new wxStaticText(this, wxID_ANY, wxString("Demodulation:")); + demodOpts->Add(demodTypeLabel, 1, wxFIXED_MINSIZE | wxALL, 0); - str.Clear(); - str.Add("FM"); - str.Add("FM Stereo"); - str.Add("AM"); - str.Add("LSB"); - str.Add("USB"); - wxChoice *wxDemodChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, str); - demodOpts->Add(wxDemodChoice, 1, wxFIXED_MINSIZE | wxALL, 0); + str.Clear(); + str.Add("FM"); + str.Add("FM Stereo"); + str.Add("AM"); + str.Add("LSB"); + str.Add("USB"); + wxChoice *wxDemodChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, str); + demodOpts->Add(wxDemodChoice, 1, wxFIXED_MINSIZE | wxALL, 0); - demodOpts->AddSpacer(5); - demodTray->AddSpacer(5); + demodOpts->AddSpacer(5); + demodTray->AddSpacer(5); - demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0); */ + demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0); */ demodSpectrumCanvas = new SpectrumCanvas(this, NULL); demodSpectrumCanvas->Setup(1024); @@ -123,7 +123,7 @@ AppFrame::AppFrame() : wxMenu *menu = new wxMenu; std::vector::iterator devices_i; - std::map::iterator mdevices_i; + std::map::iterator mdevices_i; AudioThread::enumerateDevices(devices); int i = 0; @@ -141,8 +141,8 @@ AppFrame::AppFrame() : i = 0; for (mdevices_i = output_devices.begin(); mdevices_i != output_devices.end(); mdevices_i++) { - wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE+mdevices_i->first,mdevices_i->second.name,wxT("Description?")); - itm->SetId(wxID_RT_AUDIO_DEVICE+mdevices_i->first); + wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE + mdevices_i->first, mdevices_i->second.name, wxT("Description?")); + itm->SetId(wxID_RT_AUDIO_DEVICE + mdevices_i->first); if (mdevices_i->second.isDefaultOutput) { itm->Check(true); } @@ -153,14 +153,13 @@ AppFrame::AppFrame() : menuBar->Append(menu, wxT("Output &Device")); wxMenu *demodMenu = new wxMenu; - demod_menuitems[DemodulatorType::DEMOD_TYPE_FM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_FM,wxT("FM"),wxT("Description?")); - demod_menuitems[DemodulatorType::DEMOD_TYPE_AM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_AM,wxT("AM"),wxT("Description?")); - demod_menuitems[DemodulatorType::DEMOD_TYPE_LSB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_LSB,wxT("LSB"),wxT("Description?")); - demod_menuitems[DemodulatorType::DEMOD_TYPE_USB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_USB,wxT("USB"),wxT("Description?")); + demod_menuitems[DEMOD_TYPE_FM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_FM, wxT("FM"), wxT("Description?")); + demod_menuitems[DEMOD_TYPE_AM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_AM, wxT("AM"), wxT("Description?")); + demod_menuitems[DEMOD_TYPE_LSB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_LSB, wxT("LSB"), wxT("Description?")); + demod_menuitems[DEMOD_TYPE_USB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_USB, wxT("USB"), wxT("Description?")); menuBar->Append(demodMenu, wxT("Demodulaton &Type")); - SetMenuBar(menuBar); CreateStatusBar(); @@ -180,25 +179,25 @@ AppFrame::~AppFrame() { } void AppFrame::OnMenu(wxCommandEvent& event) { - if (event.GetId() >= wxID_RT_AUDIO_DEVICE && event.GetId() < wxID_RT_AUDIO_DEVICE+devices.size()) { + if (event.GetId() >= wxID_RT_AUDIO_DEVICE && event.GetId() < wxID_RT_AUDIO_DEVICE + devices.size()) { if (activeDemodulator) { - activeDemodulator->setOutputDevice(event.GetId()-wxID_RT_AUDIO_DEVICE); + activeDemodulator->setOutputDevice(event.GetId() - wxID_RT_AUDIO_DEVICE); activeDemodulator = NULL; } } if (activeDemodulator) { - if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_FM) { - activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_FM); + if (event.GetId() == wxID_DEMOD_TYPE_FM) { + activeDemodulator->setDemodulatorType(DEMOD_TYPE_FM); activeDemodulator = NULL; - } else if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_AM) { - activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_AM); + } else if (event.GetId() == wxID_DEMOD_TYPE_AM) { + activeDemodulator->setDemodulatorType(DEMOD_TYPE_AM); activeDemodulator = NULL; - } else if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_LSB) { - activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_LSB); + } else if (event.GetId() == wxID_DEMOD_TYPE_LSB) { + activeDemodulator->setDemodulatorType(DEMOD_TYPE_LSB); activeDemodulator = NULL; - } else if (event.GetId() == wxID_DEMOD_TYPE+DemodulatorType::DEMOD_TYPE_USB) { - activeDemodulator->setDemodulatorType(DemodulatorType::DEMOD_TYPE_USB); + } else if (event.GetId() == wxID_DEMOD_TYPE_USB) { + activeDemodulator->setDemodulatorType(DEMOD_TYPE_USB); activeDemodulator = NULL; } } @@ -232,7 +231,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { int outputDevice = demod->getOutputDevice(); scopeCanvas->setDeviceName(output_devices[outputDevice].name); output_device_menuitems[outputDevice]->Check(true); - DemodulatorType dType = demod->getDemodulatorType(); + int dType = demod->getDemodulatorType(); demod_menuitems[dType]->Check(true); } if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { @@ -244,10 +243,11 @@ void AppFrame::OnIdle(wxIdleEvent& event) { if (demodBw > SRATE / 2) { demodBw = SRATE / 2; } - if (demodBw != demodWaterfallCanvas->GetBandwidth()) { - demodWaterfallCanvas->SetBandwidth(demodBw); - demodSpectrumCanvas->SetBandwidth(demodBw); + if (demodBw < 80000) { + demodBw = 80000; } + demodWaterfallCanvas->SetBandwidth(demodBw); + demodSpectrumCanvas->SetBandwidth(demodBw); } demodSignalMeter->setLevel(demod->getSignalLevel()); if (demodSignalMeter->inputChanged()) { diff --git a/src/AppFrame.h b/src/AppFrame.h index c8144da..d3ff416 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -11,7 +11,10 @@ #include #define wxID_RT_AUDIO_DEVICE 1000 -#define wxID_DEMOD_TYPE 1500 +#define wxID_DEMOD_TYPE_FM 2000 +#define wxID_DEMOD_TYPE_AM 2001 +#define wxID_DEMOD_TYPE_LSB 2002 +#define wxID_DEMOD_TYPE_USB 2003 // Define a new frame type class AppFrame: public wxFrame { @@ -42,7 +45,7 @@ private: std::map output_devices; std::map output_device_menuitems; - std::map demod_menuitems; + std::map demod_menuitems; wxDECLARE_EVENT_TABLE(); }; diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index ca0583e..1a8fa98 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -9,7 +9,8 @@ #endif #define DEFAULT_FFT_SIZE 2048 -#define DEFAULT_FREQ 98900000 +//#define DEFAULT_FREQ 98900000 +#define DEFAULT_FREQ 132000000 #define AUDIO_FREQUENCY 44100 #include diff --git a/src/demod/DemodDefs.h b/src/demod/DemodDefs.h index d938bac..af0625d 100644 --- a/src/demod/DemodDefs.h +++ b/src/demod/DemodDefs.h @@ -7,9 +7,12 @@ #include #include -enum DemodulatorType { - DEMOD_TYPE_NULL, DEMOD_TYPE_AM, DEMOD_TYPE_FM, DEMOD_TYPE_LSB, DEMOD_TYPE_USB -}; +#define DEMOD_TYPE_NULL 0 +#define DEMOD_TYPE_FM 1 +#define DEMOD_TYPE_AM 2 +#define DEMOD_TYPE_LSB 3 +#define DEMOD_TYPE_USB 4 + class DemodulatorThread; class DemodulatorThreadCommand { @@ -41,14 +44,15 @@ public: class DemodulatorThreadControlCommand { public: enum DemodulatorThreadControlCommandEnum { - DEMOD_THREAD_CMD_CTL_NULL, DEMOD_THREAD_CMD_CTL_SQUELCH_AUTO, DEMOD_THREAD_CMD_CTL_SQUELCH_OFF + DEMOD_THREAD_CMD_CTL_NULL, DEMOD_THREAD_CMD_CTL_SQUELCH_AUTO, DEMOD_THREAD_CMD_CTL_SQUELCH_OFF, DEMOD_THREAD_CMD_CTL_TYPE }; DemodulatorThreadControlCommand() : - cmd(DEMOD_THREAD_CMD_CTL_NULL) { + cmd(DEMOD_THREAD_CMD_CTL_NULL), demodType(DEMOD_TYPE_NULL) { } DemodulatorThreadControlCommandEnum cmd; + int demodType; }; class DemodulatorThreadIQData: public ReferenceCounter { @@ -122,7 +126,7 @@ public: unsigned int bandwidth; // set equal to disable second stage re-sampling? unsigned int audioSampleRate; - DemodulatorType demodType; + int demodType; DemodulatorThreadParameters() : frequency(0), inputRate(SRATE), bandwidth(200000), audioSampleRate( diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index 3e67f56..12c2692 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -19,6 +19,8 @@ DemodulatorInstance::DemodulatorInstance() : audioThread = new AudioThread(audioInputQueue, threadQueueNotify); demodulatorThread->setAudioInputQueue(audioInputQueue); + + currentDemodType = demodulatorThread->getDemodulatorType(); } DemodulatorInstance::~DemodulatorInstance() { @@ -208,11 +210,17 @@ int DemodulatorInstance::getOutputDevice() { return audioThread->getOutputDevice(); } -void DemodulatorInstance::setDemodulatorType(DemodulatorType demod_type_in) { - demodulatorThread->setDemodulatorType(demod_type_in); +void DemodulatorInstance::setDemodulatorType(int demod_type_in) { + if (demodulatorThread && threadQueueControl) { + DemodulatorThreadControlCommand command; + command.cmd = DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_TYPE; + currentDemodType = demod_type_in; + command.demodType = demod_type_in; + threadQueueControl->push(command); + } } -DemodulatorType DemodulatorInstance::getDemodulatorType() { - return demodulatorThread->getDemodulatorType(); +int DemodulatorInstance::getDemodulatorType() { + return currentDemodType; } diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index cc943ba..5a440b3 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -63,8 +63,8 @@ public: void setOutputDevice(int device_id); int getOutputDevice(); - void setDemodulatorType(DemodulatorType demod_type_in); - DemodulatorType getDemodulatorType(); + void setDemodulatorType(int demod_type_in); + int getDemodulatorType(); private: std::atomic label; // @@ -75,5 +75,7 @@ private: std::atomic active; std::atomic squelch; std::atomic stereo; + + int currentDemodType; }; diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp index f9c26a3..8ee7a9f 100644 --- a/src/demod/DemodulatorPreThread.cpp +++ b/src/demod/DemodulatorPreThread.cpp @@ -88,8 +88,8 @@ void DemodulatorPreThread::threadMain() { commandQueue->pop(command); switch (command.cmd) { case DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH: - if (command.int_value < 3000) { - command.int_value = 3000; + if (command.int_value < 1500) { + command.int_value = 1500; } if (command.int_value > SRATE) { command.int_value = SRATE; diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index f12ae2a..9a79c3e 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -9,16 +9,14 @@ DemodulatorThread::DemodulatorThread(DemodulatorThreadPostInputQueue* pQueue, DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) : postInputQueue(pQueue), visOutQueue(NULL), audioInputQueue(NULL), agc(NULL), am_max(1), am_max_ma(1), am_max_maa(1), stereo(false), terminated( - false), demodulatorType(DemodulatorType::DEMOD_TYPE_FM), threadQueueNotify(threadQueueNotify), threadQueueControl(threadQueueControl), squelch_level( - 0), squelch_tolerance(0), signal_level(0), squelch_enabled(false) { + false), demodulatorType(DEMOD_TYPE_FM), threadQueueNotify(threadQueueNotify), threadQueueControl(threadQueueControl), squelch_level(0), squelch_tolerance( + 0), signal_level(0), squelch_enabled(false) { fdem = freqdem_create(0.5); - liquid_ampmodem_type type_lsb = LIQUID_AMPMODEM_LSB; - ampdem_lsb = ampmodem_create(1.0, 0.5, type_lsb, 1); - liquid_ampmodem_type type_usb = LIQUID_AMPMODEM_USB; - ampdem_usb = ampmodem_create(1.0, -0.5, type_usb, 1); - liquid_ampmodem_type type_dsb = LIQUID_AMPMODEM_DSB; - ampdem = ampmodem_create(0.5, 0.0, type_dsb, 0); + ampdem_lsb = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_LSB, 1); + ampdem_usb = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_USB, 1); + ampdem = ampmodem_create(0.5, 0.0, LIQUID_AMPMODEM_DSB, 0); + ampdem_active = ampdem; } DemodulatorThread::~DemodulatorThread() { @@ -66,8 +64,23 @@ void DemodulatorThread::threadMain() { firhilbf firR2C = firhilbf_create(m, slsl); firhilbf firC2R = firhilbf_create(m, slsl); - nco_crcf nco_shift = nco_crcf_create(LIQUID_NCO); - double shift_freq = 0; + nco_crcf nco_stereo_shift = nco_crcf_create(LIQUID_NCO); + double nco_stereo_shift_freq = 0; + + nco_crcf nco_ssb_shift_up = nco_crcf_create(LIQUID_NCO); + nco_crcf_set_frequency(nco_ssb_shift_up, (2.0 * M_PI) * 0.25); + + + nco_crcf nco_ssb_shift_down = nco_crcf_create(LIQUID_NCO); + nco_crcf_set_frequency(nco_ssb_shift_down, (2.0 * M_PI) * 0.25); + + // estimate required filter length and generate filter + h_len = estimate_req_filter_len(ft,100.0); + float h2[h_len]; + liquid_firdes_kaiser(h_len,0.25,As,0.0,h2); + + firfilt_crcf ssb_fir_filter = firfilt_crcf_create(h2, h_len); + agc = agc_crcf_create(); agc_crcf_set_bandwidth(agc, 0.9); @@ -106,7 +119,7 @@ void DemodulatorThread::threadMain() { freqdem_reset(fdem); } - int out_size = ceil((double) (bufSize) * inp->resample_ratio); + int out_size = ceil((double) (bufSize) * inp->resample_ratio) + 32; if (agc_data.size() != out_size) { if (agc_data.capacity() < out_size) { @@ -131,7 +144,7 @@ void DemodulatorThread::threadMain() { demod_output.resize(num_written); } - int audio_out_size = ceil((double) (num_written) * audio_resample_ratio); + int audio_out_size = ceil((double) (num_written) * audio_resample_ratio) + 32; agc_crcf_execute_block(agc, &resampled_data[0], num_written, &agc_data[0]); @@ -143,40 +156,51 @@ void DemodulatorThread::threadMain() { current_level = agc_crcf_get_signal_level(agc); } - switch (demodulatorType) { - case DemodulatorType::DEMOD_TYPE_FM: + if (demodulatorType == DEMOD_TYPE_FM) { freqdem_demodulate_block(fdem, &agc_data[0], num_written, &demod_output[0]); - break; - case DemodulatorType::DEMOD_TYPE_LSB: - for (int i = 0; i < num_written; i++) { - ampmodem_demodulate(ampdem_lsb, resampled_data[i], &demod_output[i]); + } else { + float p; + switch (demodulatorType) { + case DEMOD_TYPE_LSB: + for (int i = 0; i < num_written; i++) { // Reject upper band + nco_crcf_mix_up(nco_ssb_shift_up, resampled_data[i], &x); + nco_crcf_step(nco_ssb_shift_up); + firfilt_crcf_push(ssb_fir_filter, x); + firfilt_crcf_execute(ssb_fir_filter, &x); + nco_crcf_mix_down(nco_ssb_shift_down, x, &resampled_data[i]); + nco_crcf_step(nco_ssb_shift_down); + } + break; + case DEMOD_TYPE_USB: + for (int i = 0; i < num_written; i++) { // Reject lower band + nco_crcf_mix_down(nco_ssb_shift_down, resampled_data[i], &x); + nco_crcf_step(nco_ssb_shift_down); + firfilt_crcf_push(ssb_fir_filter, x); + firfilt_crcf_execute(ssb_fir_filter, &x); + nco_crcf_mix_up(nco_ssb_shift_up, x, &resampled_data[i]); + nco_crcf_step(nco_ssb_shift_up); + } + break; + case DEMOD_TYPE_AM: + break; } - break; - case DemodulatorType::DEMOD_TYPE_USB: - for (int i = 0; i < num_written; i++) { - ampmodem_demodulate(ampdem_usb, resampled_data[i], &demod_output[i]); - } - - break; - case DemodulatorType::DEMOD_TYPE_AM: am_max = 0; + for (int i = 0; i < num_written; i++) { - ampmodem_demodulate(ampdem, resampled_data[i], &demod_output[i]); + ampmodem_demodulate(ampdem_active, resampled_data[i], &demod_output[i]); if (demod_output[i] > am_max) { am_max = demod_output[i]; } } - am_max_ma = am_max_ma + (am_max - am_max_ma) * 0.03; - am_max_maa = am_max_maa + (am_max_ma - am_max_maa) * 0.03; + am_max_ma = am_max_ma + (am_max - am_max_ma) * 0.05; + am_max_maa = am_max_maa + (am_max_ma - am_max_maa) * 0.05; - float gain = 0.95/am_max_maa; + float gain = 0.95 / am_max_maa; for (int i = 0; i < num_written; i++) { demod_output[i] *= gain; } - - break; } if (audio_out_size != resampled_audio_output.size()) { @@ -199,15 +223,15 @@ void DemodulatorThread::threadMain() { double freq = (2.0 * M_PI) * (((double) abs(38000)) / ((double) inp->bandwidth)); - if (shift_freq != freq) { - nco_crcf_set_frequency(nco_shift, freq); - shift_freq = freq; + if (nco_stereo_shift_freq != freq) { + nco_crcf_set_frequency(nco_stereo_shift, freq); + nco_stereo_shift_freq = freq; } for (int i = 0; i < num_written; i++) { firhilbf_r2c_execute(firR2C, demod_output[i], &x); - nco_crcf_mix_down(nco_shift, x, &y); - nco_crcf_step(nco_shift); + nco_crcf_mix_down(nco_stereo_shift, x, &y); + nco_crcf_step(nco_stereo_shift); firhilbf_c2r_execute(firC2R, y, &demod_output_stereo[i]); } @@ -310,6 +334,8 @@ void DemodulatorThread::threadMain() { visOutQueue->push(ati_vis); } if (!threadQueueControl->empty()) { + int newDemodType = DEMOD_TYPE_NULL; + while (!threadQueueControl->empty()) { DemodulatorThreadControlCommand command; threadQueueControl->pop(command); @@ -325,10 +351,30 @@ void DemodulatorThread::threadMain() { squelch_tolerance = 1; squelch_enabled = false; break; + case DemodulatorThreadControlCommand::DEMOD_THREAD_CMD_CTL_TYPE: + newDemodType = command.demodType; + break; default: break; } } + + if (newDemodType != DEMOD_TYPE_NULL) { + switch (newDemodType) { + case DEMOD_TYPE_FM: + break; + case DEMOD_TYPE_LSB: + ampdem_active = ampdem_lsb; + break; + case DEMOD_TYPE_USB: + ampdem_active = ampdem_usb; + break; + case DEMOD_TYPE_AM: + ampdem_active = ampdem; + break; + } + demodulatorType = newDemodType; + } } inp->decRefCount(); @@ -353,7 +399,11 @@ void DemodulatorThread::threadMain() { agc_crcf_destroy(agc); firhilbf_destroy(firR2C); firhilbf_destroy(firC2R); - nco_crcf_destroy(nco_shift); +// firhilbf_destroy(firR2Cssb); +// firhilbf_destroy(firC2Rssb); + nco_crcf_destroy(nco_stereo_shift); + nco_crcf_destroy(nco_ssb_shift_up); + nco_crcf_destroy(nco_ssb_shift_down); while (!buffers.empty()) { AudioThreadInput *audioDataDel = buffers.front(); @@ -397,10 +447,11 @@ float DemodulatorThread::getSquelchLevel() { return squelch_level; } -void DemodulatorThread::setDemodulatorType(DemodulatorType demod_type_in) { +void DemodulatorThread::setDemodulatorType(int demod_type_in) { demodulatorType = demod_type_in; } -DemodulatorType DemodulatorThread::getDemodulatorType() { +int DemodulatorThread::getDemodulatorType() { return demodulatorType; } + diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index 8d7f5f1..0e59333 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -34,14 +34,15 @@ public: void initialize(); void terminate(); - void setStereo(bool state);bool isStereo(); + void setStereo(bool state); + bool isStereo(); float getSignalLevel(); void setSquelchLevel(float signal_level_in); float getSquelchLevel(); - void setDemodulatorType(DemodulatorType demod_type_in); - DemodulatorType getDemodulatorType(); + void setDemodulatorType(int demod_type_in); + int getDemodulatorType(); #ifdef __APPLE__ static void *pthread_helper(void *context) { @@ -66,6 +67,7 @@ protected: AudioThreadInputQueue *audioInputQueue; freqdem fdem; + ampmodem ampdem_active; ampmodem ampdem; ampmodem ampdem_usb; ampmodem ampdem_lsb; @@ -78,11 +80,12 @@ protected: std::atomic stereo; std::atomic terminated; - std::atomic demodulatorType; + std::atomic demodulatorType; DemodulatorThreadCommandQueue* threadQueueNotify; DemodulatorThreadControlCommandQueue *threadQueueControl; std::atomic squelch_level; float squelch_tolerance; - std::atomic signal_level;bool squelch_enabled; + std::atomic signal_level; + bool squelch_enabled; }; diff --git a/src/demod/DemodulatorWorkerThread.cpp b/src/demod/DemodulatorWorkerThread.cpp index 44c584a..a57f18f 100644 --- a/src/demod/DemodulatorWorkerThread.cpp +++ b/src/demod/DemodulatorWorkerThread.cpp @@ -47,7 +47,6 @@ void DemodulatorWorkerThread::threadMain() { result.bandwidth = filterCommand.bandwidth; result.inputRate = filterCommand.inputRate; resultQueue->push(result); - std::this_thread::sleep_for(std::chrono::milliseconds(30)); } } diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index 1d68c6c..41b1a48 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -48,12 +48,12 @@ private: fftw_complex *in, *out; fftw_plan plan; - float fft_ceil_ma, fft_ceil_maa; - float fft_floor_ma, fft_floor_maa; + double fft_ceil_ma, fft_ceil_maa; + double fft_floor_ma, fft_floor_maa; - std::vector fft_result; - std::vector fft_result_ma; - std::vector fft_result_maa; + std::vector fft_result; + std::vector fft_result_ma; + std::vector fft_result_maa; SpectrumContext *glContext; int fft_size; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 4c56c59..19078c2 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -17,9 +17,7 @@ #include -#define MIN_FM_BANDWIDTH 2000 -#define MIN_AM_BANDWIDTH 2000 - +#define MIN_BANDWIDTH 1500 wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) EVT_PAINT(WaterfallCanvas::OnPaint) EVT_KEY_DOWN(WaterfallCanvas::OnKeyDown) @@ -36,7 +34,7 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE), parent(parent), spectrumCanvas(NULL), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan( - NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth( + NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth( 0) { glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); @@ -84,7 +82,7 @@ void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) { if (plan) { fftw_destroy_plan(plan); } - plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE); + plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_ESTIMATE); glContext->Setup(fft_size, waterfall_lines); } @@ -133,7 +131,6 @@ void WaterfallCanvas::attachSpectrumCanvas(SpectrumCanvas *canvas_in) { spectrumCanvas = canvas_in; } - void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); @@ -346,7 +343,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { } if (!resampler || bandwidth != last_bandwidth || last_input_bandwidth != input->bandwidth) { - resample_ratio = (float) (bandwidth) / (float) input->bandwidth; + resample_ratio = (double) (bandwidth) / (double) input->bandwidth; float As = 60.0f; @@ -359,7 +356,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { last_input_bandwidth = input->bandwidth; } - int out_size = ceil((float) (input->data.size()) * resample_ratio); + int out_size = ceil((double) (input->data.size()) * resample_ratio) + 32; if (resampler_buffer.size() != out_size) { if (resampler_buffer.capacity() < out_size) { @@ -373,14 +370,37 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { resampler_buffer.resize(fft_size); - for (int i = 0; i < fft_size; i++) { - in[i][0] = resampler_buffer[i].real; - in[i][1] = resampler_buffer[i].imag; + if (num_written < fft_size) { + for (int i = 0; i < num_written; i++) { + in[i][0] = resampler_buffer[i].real; + in[i][1] = resampler_buffer[i].imag; + } + for (int i = num_written; i < fft_size; i++) { + in[i][0] = 0; + in[i][1] = 0; + } + } else { + for (int i = 0; i < fft_size; i++) { + in[i][0] = resampler_buffer[i].real; + in[i][1] = resampler_buffer[i].imag; + } } } else { - for (int i = 0; i < fft_size; i++) { - in[i][0] = (*data)[i].real; - in[i][1] = (*data)[i].imag; + + if (data->size() < fft_size) { + for (int i = 0, iMax = data->size(); i < iMax; i++) { + in[i][0] = (*data)[i].real; + in[i][1] = (*data)[i].imag; + } + for (int i = data->size(); i < fft_size; i++) { + in[i][0] = 0; + in[i][1] = 0; + } + } else { + for (int i = 0; i < fft_size; i++) { + in[i][0] = (*data)[i].real; + in[i][1] = (*data)[i].imag; + } } } @@ -430,19 +450,19 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { fft_floor -= 1; fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; - fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.01; + fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; - fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.01; - fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.01; + fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; + fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; for (int i = 0, iMax = fft_size; i < iMax; i++) { - float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa)); + double v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa)); spectrum_points[i * 2] = ((float) i / (float) iMax); spectrum_points[i * 2 + 1] = v; } if (spectrumCanvas) { - spectrumCanvas->spectrum_points.assign(spectrum_points.begin(),spectrum_points.end()); + spectrumCanvas->spectrum_points.assign(spectrum_points.begin(), spectrum_points.end()); } } } @@ -482,8 +502,8 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { if (activeDemodulatorBandwidth > SRATE) { activeDemodulatorBandwidth = SRATE; } - if (activeDemodulatorBandwidth < MIN_FM_BANDWIDTH) { - activeDemodulatorBandwidth = MIN_FM_BANDWIDTH; + if (activeDemodulatorBandwidth < MIN_BANDWIDTH) { + activeDemodulatorBandwidth = MIN_BANDWIDTH; } command.int_value = activeDemodulatorBandwidth; @@ -673,8 +693,8 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { unsigned int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth()); unsigned int bw = (unsigned int) (fabs(width) * (float) GetBandwidth()); - if (bw < MIN_FM_BANDWIDTH) { - bw = MIN_FM_BANDWIDTH; + if (bw < MIN_BANDWIDTH) { + bw = MIN_BANDWIDTH; } if (!bw) { diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 9656e32..556d50c 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -60,12 +60,12 @@ private: fftw_complex *in, *out; fftw_plan plan; - float fft_ceil_ma, fft_ceil_maa; - float fft_floor_ma, fft_floor_maa; + double fft_ceil_ma, fft_ceil_maa; + double fft_floor_ma, fft_floor_maa; - std::vector fft_result; - std::vector fft_result_ma; - std::vector fft_result_maa; + std::vector fft_result; + std::vector fft_result_ma; + std::vector fft_result_maa; WaterfallContext *glContext; MouseTracker mTracker; @@ -88,7 +88,7 @@ private: bool isView; msresamp_crcf resampler; - float resample_ratio; + double resample_ratio; nco_crcf nco_shift; int shift_freq; From ce75eed995abfc675ec9041bb1e6afeb20ac9591 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Thu, 1 Jan 2015 21:10:54 -0500 Subject: [PATCH 14/20] Experimental waterfall zoom --- src/AppFrame.cpp | 2 + src/visual/SpectrumCanvas.cpp | 43 ++++++-- src/visual/SpectrumCanvas.h | 5 + src/visual/WaterfallCanvas.cpp | 173 +++++++++++++++++++++------------ src/visual/WaterfallCanvas.h | 2 + 5 files changed, 153 insertions(+), 72 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 9dae70d..0f6d918 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -79,6 +79,7 @@ AppFrame::AppFrame() : demodWaterfallCanvas->Setup(1024, 256); demodWaterfallCanvas->SetView(DEFAULT_FREQ, 300000); demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas); + demodSpectrumCanvas->attachWaterfallCanvas(demodWaterfallCanvas); demodVisuals->Add(demodWaterfallCanvas, 3, wxEXPAND | wxALL, 0); demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0); @@ -103,6 +104,7 @@ AppFrame::AppFrame() : waterfallCanvas = new WaterfallCanvas(this, NULL); waterfallCanvas->Setup(2048, 512); waterfallCanvas->attachSpectrumCanvas(spectrumCanvas); + spectrumCanvas->attachWaterfallCanvas(waterfallCanvas); vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0); this->SetSizer(vbox); diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 6b9708e..69a23a6 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -15,6 +15,7 @@ #include "AppFrame.h" #include #include +#include "WaterfallCanvas.h" wxBEGIN_EVENT_TABLE(SpectrumCanvas, wxGLCanvas) EVT_PAINT(SpectrumCanvas::OnPaint) EVT_IDLE(SpectrumCanvas::OnIdle) @@ -27,7 +28,8 @@ wxEND_EVENT_TABLE() SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), fft_size(0), in(NULL), out(NULL), plan(NULL), center_freq(0), bandwidth(0), isView(0) { + wxFULL_REPAINT_ON_RESIZE), parent(parent), fft_size(0), in(NULL), out(NULL), plan(NULL), waterfallCanvas(NULL), center_freq(0), bandwidth(0), isView( + 0) { glContext = new SpectrumContext(this, &wxGetApp().GetContext(this)); @@ -57,7 +59,6 @@ void SpectrumCanvas::Setup(int fft_size_in) { } plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE); - fft_ceil_ma = fft_ceil_maa = 100.0; fft_floor_ma = fft_floor_maa = 0.0; } @@ -171,8 +172,6 @@ void SpectrumCanvas::setData(DemodulatorThreadIQData *input) { } } - - void SpectrumCanvas::SetView(int center_freq_in, int bandwidth_in) { isView = true; center_freq = center_freq_in; @@ -181,7 +180,10 @@ void SpectrumCanvas::SetView(int center_freq_in, int bandwidth_in) { void SpectrumCanvas::DisableView() { isView = false; + center_freq = wxGetApp().getFrequency(); + bandwidth = SRATE; } + void SpectrumCanvas::SetCenterFrequency(unsigned int center_freq_in) { center_freq = center_freq_in; } @@ -217,12 +219,32 @@ void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { if (freqChange != 0) { int freq = wxGetApp().getFrequency(); - freq -= freqChange; - wxGetApp().setFrequency(freq); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText( - wxString::Format(wxT("Set center frequency: %s"), - wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); + if (isView) { + center_freq -= freqChange; + if (waterfallCanvas) { + waterfallCanvas->SetCenterFrequency(center_freq); + } + + if (SRATE/2 < (abs(freq-center_freq)+bandwidth/2)) { +// if (center_freq < freq) { +// freqChange = -((center_freq - (freq - bandwidth / 2)) - (SRATE/2)); +// } else { +// freqChange = ((freq + bandwidth / 2) - center_freq - (SRATE/2)); +// } + } else { + freqChange = 0; + } + } + + if (freqChange) { + freq -= freqChange; + wxGetApp().setFrequency(freq); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText( + wxString::Format(wxT("Set center frequency: %s"), + wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); + } + } } } @@ -246,6 +268,9 @@ void SpectrumCanvas::mouseLeftWindow(wxMouseEvent& event) { SetCursor(wxCURSOR_SIZEWE); } +void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) { + waterfallCanvas = canvas_in; +} //void SpectrumCanvas::rightClick(wxMouseEvent& event) {} //void SpectrumCanvas::keyPressed(wxKeyEvent& event) {} //void SpectrumCanvas::keyReleased(wxKeyEvent& event) {} diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index 41b1a48..45e2d67 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -11,6 +11,8 @@ #include "fftw3.h" #include "MouseTracker.h" +class WaterfallCanvas; + class SpectrumCanvas: public wxGLCanvas { public: std::vector spectrum_points; @@ -30,6 +32,8 @@ public: void SetBandwidth(unsigned int bandwidth_in); unsigned int GetBandwidth(); + void attachWaterfallCanvas(WaterfallCanvas *canvas_in); + private: void OnPaint(wxPaintEvent& event); @@ -56,6 +60,7 @@ private: std::vector fft_result_maa; SpectrumContext *glContext; + WaterfallCanvas *waterfallCanvas; int fft_size; unsigned int center_freq; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 19078c2..62de81f 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -35,7 +35,7 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : wxFULL_REPAINT_ON_RESIZE), parent(parent), spectrumCanvas(NULL), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan( NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth( - 0) { + 0), zoom(0) { glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); @@ -62,6 +62,9 @@ void WaterfallCanvas::SetView(int center_freq_in, int bandwidth_in) { void WaterfallCanvas::DisableView() { isView = false; + center_freq = wxGetApp().getFrequency(); + bandwidth = SRATE; + last_bandwidth = 0; } void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) { @@ -221,8 +224,14 @@ void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) { shiftDown = event.ShiftDown(); altDown = event.AltDown(); ctrlDown = event.ControlDown(); -// switch (event.GetKeyCode()) { -// } + switch (event.GetKeyCode()) { + case 'A': + zoom = 0; + break; + case 'Z': + zoom = 0; + break; + } } void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { @@ -235,60 +244,65 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); unsigned int freq; - if (!isView) { - switch (event.GetKeyCode()) { - case WXK_RIGHT: - freq = wxGetApp().getFrequency(); - if (shiftDown) { - freq += SRATE * 10; - } else { - freq += SRATE / 2; - } - wxGetApp().setFrequency(freq); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); - break; - case WXK_LEFT: - freq = wxGetApp().getFrequency(); - if (shiftDown) { - freq -= SRATE * 10; - } else { - freq -= SRATE / 2; - } - wxGetApp().setFrequency(freq); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); - break; - case 'D': - case WXK_DELETE: - if (!activeDemod) { - break; - } - wxGetApp().removeDemodulator(activeDemod); - wxGetApp().getDemodMgr().deleteThread(activeDemod); - break; - case 'S': - if (!activeDemod) { - break; - } - if (activeDemod->isSquelchEnabled()) { - activeDemod->setSquelchEnabled(false); - } else { - activeDemod->squelchAuto(); - } - break; - case WXK_SPACE: - if (!activeDemod) { - break; - } - if (activeDemod->isStereo()) { - activeDemod->setStereo(false); - } else { - activeDemod->setStereo(true); - } - break; - default: - event.Skip(); - return; + unsigned int bw; + switch (event.GetKeyCode()) { + case 'A': + zoom = 1; + break; + case 'Z': + zoom = -1; + break; + case WXK_RIGHT: + freq = wxGetApp().getFrequency(); + if (shiftDown) { + freq += SRATE * 10; + } else { + freq += SRATE / 2; } + wxGetApp().setFrequency(freq); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); + break; + case WXK_LEFT: + freq = wxGetApp().getFrequency(); + if (shiftDown) { + freq -= SRATE * 10; + } else { + freq -= SRATE / 2; + } + wxGetApp().setFrequency(freq); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); + break; + case 'D': + case WXK_DELETE: + if (!activeDemod) { + break; + } + wxGetApp().removeDemodulator(activeDemod); + wxGetApp().getDemodMgr().deleteThread(activeDemod); + break; + case 'S': + if (!activeDemod) { + break; + } + if (activeDemod->isSquelchEnabled()) { + activeDemod->setSquelchEnabled(false); + } else { + activeDemod->squelchAuto(); + } + break; + case WXK_SPACE: + if (!activeDemod) { + break; + } + if (activeDemod->isStereo()) { + activeDemod->setStereo(false); + } else { + activeDemod->setStereo(true); + } + break; + default: + event.Skip(); + return; } } @@ -297,16 +311,49 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { return; } + unsigned int bw; + if (zoom) { + if (zoom > 0) { + center_freq = GetCenterFrequency(); + bw = GetBandwidth(); + bw = (unsigned int) ceil((float) bw * 0.95); + if (bw < 80000) { + bw = 80000; + } + SetView(center_freq, bw); + if (spectrumCanvas) { + spectrumCanvas->SetView(center_freq, bw); + } + } else { + if (isView) { + bw = GetBandwidth(); + bw = (unsigned int) ceil((float) bw * 1.05); + if ((int) bw >= SRATE) { + bw = (unsigned int) SRATE; + DisableView(); + if (spectrumCanvas) { + spectrumCanvas->DisableView(); + } + } else { + SetView(GetCenterFrequency(), bw); + if (spectrumCanvas) { + spectrumCanvas->SetView(center_freq, bw); + } + } + } + } + } + std::vector *data = &input->data; if (data && data->size()) { - if (fft_size != data->size() && !isView) { - Setup(data->size(), waterfall_lines); - } +// if (fft_size != data->size() && !isView) { +// Setup(data->size(), waterfall_lines); +// } - if (last_bandwidth != bandwidth && !isView) { - Setup(bandwidth, waterfall_lines); - } +// if (last_bandwidth != bandwidth && !isView) { +// Setup(bandwidth, waterfall_lines); +// } if (spectrum_points.size() < fft_size * 2) { spectrum_points.resize(fft_size * 2); @@ -356,7 +403,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { last_input_bandwidth = input->bandwidth; } - int out_size = ceil((double) (input->data.size()) * resample_ratio) + 32; + int out_size = ceil((double) (input->data.size()) * resample_ratio) + 512; if (resampler_buffer.size() != out_size) { if (resampler_buffer.capacity() < out_size) { diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 556d50c..582109c 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -95,6 +95,8 @@ private: int last_input_bandwidth; int last_bandwidth; + int zoom; + std::vector shift_buffer; std::vector resampler_buffer; From 6a03aae3036cdc6108a682a0e37f1f38bb0b223f Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 2 Jan 2015 02:44:50 -0500 Subject: [PATCH 15/20] shift center freq on zoomed edge hit --- src/visual/SpectrumCanvas.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 69a23a6..4d6a7ca 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -221,17 +221,18 @@ void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { int freq = wxGetApp().getFrequency(); if (isView) { - center_freq -= freqChange; + center_freq = center_freq - freqChange; if (waterfallCanvas) { waterfallCanvas->SetCenterFrequency(center_freq); } - if (SRATE/2 < (abs(freq-center_freq)+bandwidth/2)) { -// if (center_freq < freq) { -// freqChange = -((center_freq - (freq - bandwidth / 2)) - (SRATE/2)); -// } else { -// freqChange = ((freq + bandwidth / 2) - center_freq - (SRATE/2)); -// } + int bw = (int)bandwidth; + int bwOfs = ((int)center_freq>freq)?((int)bandwidth/2):(-(int)bandwidth/2); + int freqEdge = ((int)center_freq+bwOfs); + + if (abs(freq-freqEdge) > (SRATE/2)) { + freqChange = -(((int)center_freq>freq)?(freqEdge-freq-(SRATE/2)):(freqEdge-freq+(SRATE/2))); + std::cout << "change: " << freqChange; } else { freqChange = 0; } From ef5462d13c7dac703fa8159af9503c00e6296c92 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 2 Jan 2015 19:35:34 -0500 Subject: [PATCH 16/20] Zoom follows and pins mouse frequency --- src/demod/DemodulatorThread.cpp | 4 ++-- src/visual/SpectrumCanvas.cpp | 1 - src/visual/WaterfallCanvas.cpp | 34 ++++++++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index 9a79c3e..2d81cf2 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -119,7 +119,7 @@ void DemodulatorThread::threadMain() { freqdem_reset(fdem); } - int out_size = ceil((double) (bufSize) * inp->resample_ratio) + 32; + int out_size = ceil((double) (bufSize) * inp->resample_ratio) + 512; if (agc_data.size() != out_size) { if (agc_data.capacity() < out_size) { @@ -144,7 +144,7 @@ void DemodulatorThread::threadMain() { demod_output.resize(num_written); } - int audio_out_size = ceil((double) (num_written) * audio_resample_ratio) + 32; + int audio_out_size = ceil((double) (num_written) * audio_resample_ratio) + 512; agc_crcf_execute_block(agc, &resampled_data[0], num_written, &agc_data[0]); diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 4d6a7ca..404aab7 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -232,7 +232,6 @@ void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { if (abs(freq-freqEdge) > (SRATE/2)) { freqChange = -(((int)center_freq>freq)?(freqEdge-freq-(SRATE/2)):(freqEdge-freq+(SRATE/2))); - std::cout << "change: " << freqChange; } else { freqChange = 0; } diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 62de81f..34cc9dd 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -313,6 +313,8 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { unsigned int bw; if (zoom) { + int freq = wxGetApp().getFrequency(); + if (zoom > 0) { center_freq = GetCenterFrequency(); bw = GetBandwidth(); @@ -320,6 +322,13 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { if (bw < 80000) { bw = 80000; } + if (mTracker.mouseInView()) { + int mfreqA = GetFrequencyAt(mTracker.getMouseX()); + SetBandwidth(bw); + int mfreqB = GetFrequencyAt(mTracker.getMouseX()); + center_freq += mfreqA - mfreqB; + } + SetView(center_freq, bw); if (spectrumCanvas) { spectrumCanvas->SetView(center_freq, bw); @@ -335,6 +344,14 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { spectrumCanvas->DisableView(); } } else { + if (mTracker.mouseInView()) { + int freq = wxGetApp().getFrequency(); + int mfreqA = GetFrequencyAt(mTracker.getMouseX()); + SetBandwidth(bw); + int mfreqB = GetFrequencyAt(mTracker.getMouseX()); + center_freq += mfreqA - mfreqB; + } + SetView(GetCenterFrequency(), bw); if (spectrumCanvas) { spectrumCanvas->SetView(center_freq, bw); @@ -342,6 +359,12 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { } } } + if (center_freq < freq && (center_freq - bandwidth / 2) < (freq - SRATE / 2)) { + center_freq = (freq - SRATE / 2) + bandwidth / 2; + } + if (center_freq > freq && (center_freq + bandwidth / 2) > (freq + SRATE / 2)) { + center_freq = (freq + SRATE / 2) - bandwidth / 2; + } } std::vector *data = &input->data; @@ -695,6 +718,10 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { if (DemodulatorInstance *last = wxGetApp().getDemodMgr().getLastActiveDemodulator()) { demod->getParams().bandwidth = last->getParams().bandwidth; + demod->setDemodulatorType(last->getDemodulatorType()); + demod->setSquelchLevel(last->getSquelchLevel()); + demod->setSquelchEnabled(last->isSquelchEnabled()); + demod->setStereo(last->isStereo()); } demod->run(); @@ -755,7 +782,12 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { demod = wxGetApp().getDemodMgr().newThread(); demod->getParams().frequency = freq; demod->getParams().bandwidth = bw; - + if (DemodulatorInstance *last = wxGetApp().getDemodMgr().getLastActiveDemodulator()) { + demod->setDemodulatorType(last->getDemodulatorType()); + demod->setSquelchLevel(last->getSquelchLevel()); + demod->setSquelchEnabled(last->isSquelchEnabled()); + demod->setStereo(last->isStereo()); + } demod->run(); wxGetApp().bindDemodulator(demod); From 8f8dd01341eb1318f99defde7e4c54043d05bff4 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 2 Jan 2015 21:32:24 -0500 Subject: [PATCH 17/20] Unify common canvas manipulation functions --- CMakeLists.txt | 2 + src/visual/InteractiveCanvas.cpp | 125 +++++++++++++++++++++++++++++++ src/visual/InteractiveCanvas.h | 48 ++++++++++++ src/visual/SpectrumCanvas.cpp | 66 +++------------- src/visual/SpectrumCanvas.h | 22 +----- src/visual/WaterfallCanvas.cpp | 113 +++++++++------------------- src/visual/WaterfallCanvas.h | 25 +------ 7 files changed, 228 insertions(+), 173 deletions(-) create mode 100644 src/visual/InteractiveCanvas.cpp create mode 100644 src/visual/InteractiveCanvas.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a222e4a..d8d9868 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,7 @@ SET (cubicsdr_sources src/util/MouseTracker.cpp src/util/GLFont.cpp src/visual/PrimaryGLContext.cpp + src/visual/InteractiveCanvas.cpp src/visual/MeterCanvas.cpp src/visual/MeterContext.cpp src/visual/ScopeCanvas.cpp @@ -167,6 +168,7 @@ SET (cubicsdr_headers src/util/MouseTracker.h src/util/GLFont.h src/visual/PrimaryGLContext.h + src/visual/InteractiveCanvas.h src/visual/MeterCanvas.h src/visual/MeterContext.h src/visual/ScopeCanvas.h diff --git a/src/visual/InteractiveCanvas.cpp b/src/visual/InteractiveCanvas.cpp new file mode 100644 index 0000000..596943a --- /dev/null +++ b/src/visual/InteractiveCanvas.cpp @@ -0,0 +1,125 @@ +#include "InteractiveCanvas.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#if !wxUSE_GLCANVAS +#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" +#endif + +#include "CubicSDR.h" +#include "CubicSDRDefs.h" +#include "AppFrame.h" +#include + +#include + +InteractiveCanvas::InteractiveCanvas(wxWindow *parent, int *attribList) : + wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, + wxFULL_REPAINT_ON_RESIZE), parent(parent), shiftDown(false), altDown(false), ctrlDown(false), center_freq(0), bandwidth(0), last_bandwidth(0), isView( + false) { + mTracker.setTarget(this); +} + +InteractiveCanvas::~InteractiveCanvas() { +} + +void InteractiveCanvas::SetView(int center_freq_in, int bandwidth_in) { + isView = true; + center_freq = center_freq_in; + bandwidth = bandwidth_in; + last_bandwidth = 0; +} + +void InteractiveCanvas::DisableView() { + isView = false; + center_freq = wxGetApp().getFrequency(); + bandwidth = SRATE; + last_bandwidth = 0; +} + +int InteractiveCanvas::GetFrequencyAt(float x) { + int iqCenterFreq = GetCenterFrequency(); + int iqBandwidth = GetBandwidth(); + int freq = iqCenterFreq - (int) (0.5 * (float) iqBandwidth) + (int) ((float) x * (float) iqBandwidth); + + return freq; +} + +void InteractiveCanvas::SetCenterFrequency(unsigned int center_freq_in) { + center_freq = center_freq_in; +} + +unsigned int InteractiveCanvas::GetCenterFrequency() { + if (isView) { + return center_freq; + } else { + return (unsigned int) wxGetApp().getFrequency(); + } +} + +void InteractiveCanvas::SetBandwidth(unsigned int bandwidth_in) { + bandwidth = bandwidth_in; +} + +unsigned int InteractiveCanvas::GetBandwidth() { + if (isView) { + return bandwidth; + } else { + return SRATE; + } +} + +void InteractiveCanvas::OnKeyUp(wxKeyEvent& event) { + shiftDown = event.ShiftDown(); + altDown = event.AltDown(); + ctrlDown = event.ControlDown(); +} + +void InteractiveCanvas::OnKeyDown(wxKeyEvent& event) { + float angle = 5.0; + + shiftDown = event.ShiftDown(); + altDown = event.AltDown(); + ctrlDown = event.ControlDown(); +} + +void InteractiveCanvas::mouseMoved(wxMouseEvent& event) { + mTracker.OnMouseMoved(event); + + shiftDown = event.ShiftDown(); + altDown = event.AltDown(); + ctrlDown = event.ControlDown(); +} + +void InteractiveCanvas::mouseDown(wxMouseEvent& event) { + mTracker.OnMouseDown(event); + + shiftDown = event.ShiftDown(); + altDown = event.AltDown(); + ctrlDown = event.ControlDown(); +} + +void InteractiveCanvas::mouseWheelMoved(wxMouseEvent& event) { + mTracker.OnMouseWheelMoved(event); +} + +void InteractiveCanvas::mouseReleased(wxMouseEvent& event) { + mTracker.OnMouseReleased(event); + + shiftDown = event.ShiftDown(); + altDown = event.AltDown(); + ctrlDown = event.ControlDown(); +} + +void InteractiveCanvas::mouseLeftWindow(wxMouseEvent& event) { + mTracker.OnMouseLeftWindow(event); +} + +void InteractiveCanvas::mouseEnterWindow(wxMouseEvent& event) { + mTracker.OnMouseEnterWindow(event); +} + diff --git a/src/visual/InteractiveCanvas.h b/src/visual/InteractiveCanvas.h new file mode 100644 index 0000000..979dadd --- /dev/null +++ b/src/visual/InteractiveCanvas.h @@ -0,0 +1,48 @@ +#pragma once + +#include "wx/glcanvas.h" +#include "wx/timer.h" + +#include "MouseTracker.h" + +class InteractiveCanvas: public wxGLCanvas { +public: + InteractiveCanvas(wxWindow *parent, int *attribList = NULL); + ~InteractiveCanvas(); + + int GetFrequencyAt(float x); + + void SetView(int center_freq_in, int bandwidth_in); + void DisableView(); + + void SetCenterFrequency(unsigned int center_freq_in); + unsigned int GetCenterFrequency(); + + void SetBandwidth(unsigned int bandwidth_in); + unsigned int GetBandwidth(); + +protected: + void OnKeyDown(wxKeyEvent& event); + void OnKeyUp(wxKeyEvent& event); + + void mouseMoved(wxMouseEvent& event); + void mouseDown(wxMouseEvent& event); + void mouseWheelMoved(wxMouseEvent& event); + void mouseReleased(wxMouseEvent& event); + void mouseEnterWindow(wxMouseEvent& event); + void mouseLeftWindow(wxMouseEvent& event); + + wxWindow *parent; + MouseTracker mTracker; + + bool shiftDown; + bool altDown; + bool ctrlDown; + + unsigned int center_freq; + unsigned int bandwidth; + unsigned int last_bandwidth; + + bool isView; +}; + diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 404aab7..63bb8cf 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -27,13 +27,11 @@ EVT_MOUSEWHEEL(SpectrumCanvas::mouseWheelMoved) wxEND_EVENT_TABLE() SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : - wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), fft_size(0), in(NULL), out(NULL), plan(NULL), waterfallCanvas(NULL), center_freq(0), bandwidth(0), isView( - 0) { + InteractiveCanvas(parent, attribList), fft_size(0), in(NULL), out(NULL), plan(NULL), fft_ceil_ma(1), fft_ceil_maa(1), fft_floor_ma(0), fft_floor_maa( + 0), waterfallCanvas(NULL) { glContext = new SpectrumContext(this, &wxGetApp().GetContext(this)); - mTracker.setTarget(this); mTracker.setVertDragLock(true); SetCursor(wxCURSOR_SIZEWE); @@ -131,7 +129,6 @@ void SpectrumCanvas::setData(DemodulatorThreadIQData *input) { double b = out[n][1]; double c = sqrt(a * a + b * b); -// n = (i == FFT_SIZE / 2) ? (FFT_SIZE / 2 + 1) : i; double x = out[fft_size / 2 + n][0]; double y = out[fft_size / 2 + n][1]; double z = sqrt(x * x + y * y); @@ -172,48 +169,12 @@ void SpectrumCanvas::setData(DemodulatorThreadIQData *input) { } } -void SpectrumCanvas::SetView(int center_freq_in, int bandwidth_in) { - isView = true; - center_freq = center_freq_in; - bandwidth = bandwidth_in; -} - -void SpectrumCanvas::DisableView() { - isView = false; - center_freq = wxGetApp().getFrequency(); - bandwidth = SRATE; -} - -void SpectrumCanvas::SetCenterFrequency(unsigned int center_freq_in) { - center_freq = center_freq_in; -} - -unsigned int SpectrumCanvas::GetCenterFrequency() { - if (isView) { - return center_freq; - } else { - return (unsigned int) wxGetApp().getFrequency(); - } -} - -void SpectrumCanvas::SetBandwidth(unsigned int bandwidth_in) { - bandwidth = bandwidth_in; -} - -unsigned int SpectrumCanvas::GetBandwidth() { - if (isView) { - return bandwidth; - } else { - return SRATE; - } -} - void SpectrumCanvas::OnIdle(wxIdleEvent &event) { Refresh(false); } void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { - mTracker.OnMouseMoved(event); + InteractiveCanvas::mouseMoved(event); if (mTracker.mouseDown()) { int freqChange = mTracker.getDeltaMouseX() * GetBandwidth(); @@ -226,12 +187,12 @@ void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { waterfallCanvas->SetCenterFrequency(center_freq); } - int bw = (int)bandwidth; - int bwOfs = ((int)center_freq>freq)?((int)bandwidth/2):(-(int)bandwidth/2); - int freqEdge = ((int)center_freq+bwOfs); + int bw = (int) bandwidth; + int bwOfs = ((int) center_freq > freq) ? ((int) bandwidth / 2) : (-(int) bandwidth / 2); + int freqEdge = ((int) center_freq + bwOfs); - if (abs(freq-freqEdge) > (SRATE/2)) { - freqChange = -(((int)center_freq>freq)?(freqEdge-freq-(SRATE/2)):(freqEdge-freq+(SRATE/2))); + if (abs(freq - freqEdge) > (SRATE / 2)) { + freqChange = -(((int) center_freq > freq) ? (freqEdge - freq - (SRATE / 2)) : (freqEdge - freq + (SRATE / 2))); } else { freqChange = 0; } @@ -250,28 +211,25 @@ void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { } void SpectrumCanvas::mouseDown(wxMouseEvent& event) { - mTracker.OnMouseDown(event); + InteractiveCanvas::mouseDown(event); SetCursor(wxCURSOR_CROSS); } void SpectrumCanvas::mouseWheelMoved(wxMouseEvent& event) { - mTracker.OnMouseWheelMoved(event); + InteractiveCanvas::mouseWheelMoved(event); } void SpectrumCanvas::mouseReleased(wxMouseEvent& event) { - mTracker.OnMouseReleased(event); + InteractiveCanvas::mouseReleased(event); SetCursor(wxCURSOR_SIZEWE); } void SpectrumCanvas::mouseLeftWindow(wxMouseEvent& event) { - mTracker.OnMouseLeftWindow(event); + InteractiveCanvas::mouseLeftWindow(event); SetCursor(wxCURSOR_SIZEWE); } void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) { waterfallCanvas = canvas_in; } -//void SpectrumCanvas::rightClick(wxMouseEvent& event) {} -//void SpectrumCanvas::keyPressed(wxKeyEvent& event) {} -//void SpectrumCanvas::keyReleased(wxKeyEvent& event) {} diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index 45e2d67..6a5cfe8 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -6,6 +6,7 @@ #include #include +#include "InteractiveCanvas.h" #include "SpectrumContext.h" #include "fftw3.h" @@ -13,7 +14,7 @@ class WaterfallCanvas; -class SpectrumCanvas: public wxGLCanvas { +class SpectrumCanvas: public InteractiveCanvas { public: std::vector spectrum_points; @@ -22,16 +23,6 @@ public: ~SpectrumCanvas(); void setData(DemodulatorThreadIQData *input); - - void SetView(int center_freq_in, int bandwidth_in); - void DisableView(); - - void SetCenterFrequency(unsigned int center_freq_in); - unsigned int GetCenterFrequency(); - - void SetBandwidth(unsigned int bandwidth_in); - unsigned int GetBandwidth(); - void attachWaterfallCanvas(WaterfallCanvas *canvas_in); private: @@ -47,8 +38,6 @@ private: // void rightClick(wxMouseEvent& event); void mouseLeftWindow(wxMouseEvent& event); - wxWindow *parent; - fftw_complex *in, *out; fftw_plan plan; @@ -62,13 +51,6 @@ private: SpectrumContext *glContext; WaterfallCanvas *waterfallCanvas; int fft_size; - - unsigned int center_freq; - unsigned int bandwidth; - - bool isView; - - MouseTracker mTracker; // event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 34cc9dd..06c889f 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -31,11 +31,9 @@ EVT_ENTER_WINDOW(WaterfallCanvas::mouseEnterWindow) wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : - wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), spectrumCanvas(NULL), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( - WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan( - NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth( - 0), zoom(0) { + InteractiveCanvas(parent, attribList), spectrumCanvas(NULL), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( + WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), waterfall_lines(0), plan( + NULL), in(NULL), out(NULL), resampler(NULL), resample_ratio(0), last_input_bandwidth(0), zoom(0) { glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); @@ -45,7 +43,6 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : fft_ceil_ma = fft_ceil_maa = 100.0; fft_floor_ma = fft_floor_maa = 0.0; - mTracker.setTarget(this); SetCursor(wxCURSOR_CROSS); } @@ -53,20 +50,6 @@ WaterfallCanvas::~WaterfallCanvas() { nco_crcf_destroy(nco_shift); } -void WaterfallCanvas::SetView(int center_freq_in, int bandwidth_in) { - isView = true; - center_freq = center_freq_in; - bandwidth = bandwidth_in; - last_bandwidth = 0; -} - -void WaterfallCanvas::DisableView() { - isView = false; - center_freq = wxGetApp().getFrequency(); - bandwidth = SRATE; - last_bandwidth = 0; -} - void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) { if (fft_size == fft_size_in && waterfall_lines_in == waterfall_lines) { return; @@ -90,38 +73,6 @@ void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) { glContext->Setup(fft_size, waterfall_lines); } -int WaterfallCanvas::GetFrequencyAt(float x) { - int iqCenterFreq = GetCenterFrequency(); - int iqBandwidth = GetBandwidth(); - int freq = iqCenterFreq - (int) (0.5 * (float) iqBandwidth) + (int) ((float) x * (float) iqBandwidth); - - return freq; -} - -void WaterfallCanvas::SetCenterFrequency(unsigned int center_freq_in) { - center_freq = center_freq_in; -} - -unsigned int WaterfallCanvas::GetCenterFrequency() { - if (isView) { - return center_freq; - } else { - return (unsigned int) wxGetApp().getFrequency(); - } -} - -void WaterfallCanvas::SetBandwidth(unsigned int bandwidth_in) { - bandwidth = bandwidth_in; -} - -unsigned int WaterfallCanvas::GetBandwidth() { - if (isView) { - return bandwidth; - } else { - return SRATE; - } -} - WaterfallCanvas::DragState WaterfallCanvas::getDragState() { return dragState; } @@ -221,6 +172,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { } void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) { + InteractiveCanvas::OnKeyUp(event); shiftDown = event.ShiftDown(); altDown = event.AltDown(); ctrlDown = event.ControlDown(); @@ -235,12 +187,9 @@ void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) { } void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { + InteractiveCanvas::OnKeyDown(event); float angle = 5.0; - shiftDown = event.ShiftDown(); - altDown = event.AltDown(); - ctrlDown = event.ControlDown(); - DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); unsigned int freq; @@ -256,8 +205,20 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { freq = wxGetApp().getFrequency(); if (shiftDown) { freq += SRATE * 10; + if (isView) { + SetView(center_freq + SRATE * 10, GetBandwidth()); + if (spectrumCanvas) { + spectrumCanvas->SetView(GetCenterFrequency(), GetBandwidth()); + } + } } else { freq += SRATE / 2; + if (isView) { + SetView(center_freq + SRATE / 2, GetBandwidth()); + if (spectrumCanvas) { + spectrumCanvas->SetView(GetCenterFrequency(), GetBandwidth()); + } + } } wxGetApp().setFrequency(freq); ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); @@ -266,11 +227,23 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { freq = wxGetApp().getFrequency(); if (shiftDown) { freq -= SRATE * 10; + if (isView) { + SetView(center_freq - SRATE * 10, GetBandwidth()); + if (spectrumCanvas) { + spectrumCanvas->SetView(GetCenterFrequency(), GetBandwidth()); + } + } } else { freq -= SRATE / 2; + if (isView) { + SetView(center_freq - SRATE / 2, GetBandwidth()); + if (spectrumCanvas) { + spectrumCanvas->SetView(GetCenterFrequency(), GetBandwidth()); + } + } } wxGetApp().setFrequency(freq); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequenfcy: %i"), freq)); break; case 'D': case WXK_DELETE: @@ -542,12 +515,7 @@ void WaterfallCanvas::OnIdle(wxIdleEvent &event) { } void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { - mTracker.OnMouseMoved(event); - - shiftDown = event.ShiftDown(); - altDown = event.AltDown(); - ctrlDown = event.ControlDown(); - + InteractiveCanvas::mouseMoved(event); DemodulatorInstance *demod = wxGetApp().getDemodMgr().getActiveDemodulator(); if (mTracker.mouseDown()) { @@ -670,12 +638,9 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { } void WaterfallCanvas::mouseDown(wxMouseEvent& event) { - mTracker.OnMouseDown(event); - dragState = nextDragState; + InteractiveCanvas::mouseDown(event); - shiftDown = event.ShiftDown(); - altDown = event.AltDown(); - ctrlDown = event.ControlDown(); + dragState = nextDragState; if (dragState && dragState != WF_DRAG_RANGE) { wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false); @@ -686,15 +651,11 @@ void WaterfallCanvas::mouseDown(wxMouseEvent& event) { } void WaterfallCanvas::mouseWheelMoved(wxMouseEvent& event) { - mTracker.OnMouseWheelMoved(event); + InteractiveCanvas::mouseWheelMoved(event); } void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { - mTracker.OnMouseReleased(event); - - shiftDown = event.ShiftDown(); - altDown = event.AltDown(); - ctrlDown = event.ControlDown(); + InteractiveCanvas::mouseReleased(event); bool isNew = shiftDown || (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive()); @@ -819,13 +780,13 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { } void WaterfallCanvas::mouseLeftWindow(wxMouseEvent& event) { - mTracker.OnMouseLeftWindow(event); + InteractiveCanvas::mouseLeftWindow(event); SetCursor(wxCURSOR_CROSS); wxGetApp().getDemodMgr().setActiveDemodulator(NULL); } void WaterfallCanvas::mouseEnterWindow(wxMouseEvent& event) { - mTracker.OnMouseEnterWindow(event); + InteractiveCanvas::mouseEnterWindow(event); SetCursor(wxCURSOR_CROSS); } diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 582109c..1264103 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -6,13 +6,14 @@ #include #include +#include "InteractiveCanvas.h" #include "WaterfallContext.h" #include "MouseTracker.h" #include "SpectrumCanvas.h" #include "fftw3.h" -class WaterfallCanvas: public wxGLCanvas { +class WaterfallCanvas: public InteractiveCanvas { public: enum DragState { WF_DRAG_NONE, WF_DRAG_BANDWIDTH_LEFT, WF_DRAG_BANDWIDTH_RIGHT, WF_DRAG_FREQUENCY, WF_DRAG_RANGE @@ -23,16 +24,6 @@ public: ~WaterfallCanvas(); void setData(DemodulatorThreadIQData *input); - int GetFrequencyAt(float x); - - void SetView(int center_freq_in, int bandwidth_in); - void DisableView(); - - void SetCenterFrequency(unsigned int center_freq_in); - unsigned int GetCenterFrequency(); - - void SetBandwidth(unsigned int bandwidth_in); - unsigned int GetBandwidth(); DragState getDragState(); DragState getNextDragState(); @@ -53,7 +44,6 @@ private: void mouseEnterWindow(wxMouseEvent& event); void mouseLeftWindow(wxMouseEvent& event); - wxWindow *parent; SpectrumCanvas *spectrumCanvas; std::vector spectrum_points; @@ -68,7 +58,6 @@ private: std::vector fft_result_maa; WaterfallContext *glContext; - MouseTracker mTracker; int activeDemodulatorBandwidth; int activeDemodulatorFrequency; @@ -76,25 +65,15 @@ private: DragState dragState; DragState nextDragState; - bool shiftDown; - bool altDown; - bool ctrlDown; - int fft_size; int waterfall_lines; - unsigned int center_freq; - unsigned int bandwidth; - - bool isView; msresamp_crcf resampler; double resample_ratio; nco_crcf nco_shift; int shift_freq; int last_input_bandwidth; - int last_bandwidth; - int zoom; std::vector shift_buffer; From 63a4913558700aca0a4c8d19a75362e67d81326f Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 2 Jan 2015 22:44:09 -0500 Subject: [PATCH 18/20] Tweaks, cleanup, helptips --- src/AppFrame.cpp | 35 ++------------------------------ src/visual/InteractiveCanvas.cpp | 10 ++++++++- src/visual/InteractiveCanvas.h | 4 ++++ src/visual/SpectrumCanvas.cpp | 6 +++--- src/visual/WaterfallCanvas.cpp | 31 +++++++++++++++++----------- 5 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 0f6d918..f7dfdf6 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -37,37 +37,6 @@ AppFrame::AppFrame() : wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); - /* - demodTray->AddSpacer(5); - demodOpts->AddSpacer(5); - - wxStaticText *audioDeviceLabel = new wxStaticText(this, wxID_ANY, wxString("Audio Device:")); - demodOpts->Add(audioDeviceLabel, 1, wxFIXED_MINSIZE | wxALL, 0); - - wxArrayString str; - str.Add("Primary Device"); - wxChoice *wxCh = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, str); - demodOpts->Add(wxCh, 1, wxFIXED_MINSIZE | wxALL, 0); - - demodOpts->AddSpacer(2); - - wxStaticText *demodTypeLabel = new wxStaticText(this, wxID_ANY, wxString("Demodulation:")); - demodOpts->Add(demodTypeLabel, 1, wxFIXED_MINSIZE | wxALL, 0); - - str.Clear(); - str.Add("FM"); - str.Add("FM Stereo"); - str.Add("AM"); - str.Add("LSB"); - str.Add("USB"); - wxChoice *wxDemodChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, str); - demodOpts->Add(wxDemodChoice, 1, wxFIXED_MINSIZE | wxALL, 0); - - demodOpts->AddSpacer(5); - demodTray->AddSpacer(5); - - demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0); */ - demodSpectrumCanvas = new SpectrumCanvas(this, NULL); demodSpectrumCanvas->Setup(1024); demodSpectrumCanvas->SetView(DEFAULT_FREQ, 300000); @@ -152,7 +121,7 @@ AppFrame::AppFrame() : } wxMenuBar *menuBar = new wxMenuBar; - menuBar->Append(menu, wxT("Output &Device")); + menuBar->Append(menu, wxT("Active Demodulator &Output")); wxMenu *demodMenu = new wxMenu; demod_menuitems[DEMOD_TYPE_FM] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_FM, wxT("FM"), wxT("Description?")); @@ -160,7 +129,7 @@ AppFrame::AppFrame() : demod_menuitems[DEMOD_TYPE_LSB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_LSB, wxT("LSB"), wxT("Description?")); demod_menuitems[DEMOD_TYPE_USB] = demodMenu->AppendRadioItem(wxID_DEMOD_TYPE_USB, wxT("USB"), wxT("Description?")); - menuBar->Append(demodMenu, wxT("Demodulaton &Type")); + menuBar->Append(demodMenu, wxT("Active Demodulator &Type")); SetMenuBar(menuBar); diff --git a/src/visual/InteractiveCanvas.cpp b/src/visual/InteractiveCanvas.cpp index 596943a..a68709e 100644 --- a/src/visual/InteractiveCanvas.cpp +++ b/src/visual/InteractiveCanvas.cpp @@ -20,7 +20,7 @@ InteractiveCanvas::InteractiveCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE), parent(parent), shiftDown(false), altDown(false), ctrlDown(false), center_freq(0), bandwidth(0), last_bandwidth(0), isView( - false) { + false) { mTracker.setTarget(this); } @@ -123,3 +123,11 @@ void InteractiveCanvas::mouseEnterWindow(wxMouseEvent& event) { mTracker.OnMouseEnterWindow(event); } +void InteractiveCanvas::setStatusText(std::string statusText) { + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(statusText); +} + +void InteractiveCanvas::setStatusText(std::string statusText, int value) { + ((wxFrame*) parent)->GetStatusBar()->SetStatusText( + wxString::Format(statusText.c_str(), wxNumberFormatter::ToString((long) value, wxNumberFormatter::Style_WithThousandsSep))); +} diff --git a/src/visual/InteractiveCanvas.h b/src/visual/InteractiveCanvas.h index 979dadd..ac84802 100644 --- a/src/visual/InteractiveCanvas.h +++ b/src/visual/InteractiveCanvas.h @@ -4,6 +4,7 @@ #include "wx/timer.h" #include "MouseTracker.h" +#include class InteractiveCanvas: public wxGLCanvas { public: @@ -32,6 +33,9 @@ protected: void mouseEnterWindow(wxMouseEvent& event); void mouseLeftWindow(wxMouseEvent& event); + void setStatusText(std::string statusText); + void setStatusText(std::string statusText, int value); + wxWindow *parent; MouseTracker mTracker; diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 63bb8cf..be837c2 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -201,12 +201,12 @@ void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { if (freqChange) { freq -= freqChange; wxGetApp().setFrequency(freq); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText( - wxString::Format(wxT("Set center frequency: %s"), - wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); + setStatusText("Set center frequency: %s", freq); } } + } else { + setStatusText("Click and drag to adjust center frequency."); } } diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 06c889f..36bcd17 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -221,7 +221,7 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { } } wxGetApp().setFrequency(freq); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); + setStatusText("Set center frequency: %s", freq); break; case WXK_LEFT: freq = wxGetApp().getFrequency(); @@ -243,7 +243,7 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { } } wxGetApp().setFrequency(freq); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequenfcy: %i"), freq)); + setStatusText("Set center frequency: %s", freq); break; case 'D': case WXK_DELETE: @@ -546,6 +546,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { command.int_value = activeDemodulatorBandwidth; demod->getCommandQueue()->push(command); + setStatusText("Set demodulator bandwidth: %s", activeDemodulatorBandwidth); } if (dragState == WF_DRAG_FREQUENCY) { @@ -563,6 +564,8 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { demod->getCommandQueue()->push(command); demod->updateLabel(activeDemodulatorFrequency); + + setStatusText("Set demodulator frequency: %s", activeDemodulatorFrequency); } } else { int freqPos = GetFrequencyAt(mTracker.getMouseX()); @@ -575,6 +578,11 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { nextDragState = WF_DRAG_RANGE; mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); + if (shiftDown) { + setStatusText("Click and drag to create a new demodulator by range."); + } else { + setStatusText("Click and drag to set the current demodulator range."); + } } else if (demodsHover->size()) { int hovered = -1; int near_dist = GetBandwidth(); @@ -621,16 +629,23 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); + setStatusText("Click and drag to change demodulator bandwidth. D to delete, SPACE for stereo."); } else { SetCursor(wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); + setStatusText("Click and drag to change demodulator frequency. D to delete, SPACE for stereo."); } } else { SetCursor(wxCURSOR_CROSS); nextDragState = WF_DRAG_NONE; + if (shiftDown) { + setStatusText("Click to create a new demodulator or hold ALT to drag range."); + } else { + setStatusText("Click to move active demodulator frequency or hold ALT to drag range; hold SHIFT to create new. A / Z to Zoom. Arrow keys (+SHIFT) to move center frequency."); + } } delete demodsHover; @@ -703,9 +718,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { command.int_value = freq; demod->getCommandQueue()->push(command); - ((wxFrame*) parent)->GetStatusBar()->SetStatusText( - wxString::Format(wxT("Set demodulator frequency: %s"), - wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); + setStatusText("New demodulator at frequency: %s", freq); wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false); SetCursor(wxCURSOR_SIZING); @@ -713,10 +726,6 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { mTracker.setVertDragLock(true); mTracker.setHorizDragLock(false); } else { - float pos = mTracker.getMouseX(); - int input_center_freq = GetCenterFrequency(); - int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth()); - wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false); nextDragState = WF_DRAG_FREQUENCY; } @@ -760,9 +769,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { return; } - ((wxFrame*) parent)->GetStatusBar()->SetStatusText( - wxString::Format(wxT("Set demodulator frequency: %s"), - wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); + setStatusText("New demodulator at frequency: %s", freq); wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false); demod->updateLabel(freq); From dfeccbaaeb00addf260939f0059f12e03b464cd4 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 2 Jan 2015 22:52:29 -0500 Subject: [PATCH 19/20] Update README.md --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6cc5775..b9e73ca 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,13 @@ Basic Goals and Status: - [x] Audio device selection - [ ] Modes - [x] FM - - [x] WFM - - [x] WBFM stereo - - [ ] AM - - [ ] LSB - - [ ] USB + - [x] FM stereo + - [x] AM + - [x] LSB + - [x] USB - [ ] Controls - [ ] Display Frequency and allow manual adjustments - - [ ] Allow selection of demodulation type + - [x] Allow selection of demodulation type - [x] Display separate zoomed-in view of current waterfall and spectrum, allow adjustments - [x] Display signal level and allow squelch control - [x] Display audio output selection From f9fd2ab8c37230e3a8ca935162c81ddea08da6b2 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 2 Jan 2015 22:53:03 -0500 Subject: [PATCH 20/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9e73ca..3772158 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Basic Goals and Status: - Demodulation: - [x] Multiple demodulators per IQ stream - [x] Audio device selection - - [ ] Modes + - [x] Modes - [x] FM - [x] FM stereo - [x] AM