From 1e970f4373856b98cb9158a1b4a7c3688834f6e4 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 28 Dec 2014 05:13:46 -0500 Subject: [PATCH] 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; };