From 6df9661db1129415bfc6ca0ef84b9c00654c5a5b Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Thu, 31 Dec 2015 20:44:39 -0500 Subject: [PATCH] Add peak hold to spectrum visuals --- src/AppFrame.cpp | 25 +++++++- src/AppFrame.h | 2 +- src/panel/SpectrumPanel.cpp | 10 ++++ src/panel/SpectrumPanel.h | 2 + src/process/SpectrumVisualProcessor.cpp | 78 +++++++++++++++++++++++-- src/process/SpectrumVisualProcessor.h | 8 ++- src/visual/SpectrumCanvas.cpp | 2 + 7 files changed, 118 insertions(+), 9 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index b0fecb2..54e3596 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -206,18 +206,33 @@ AppFrame::AppFrame() : spectrumCanvas->setShowDb(true); spectrumCanvas->setScaleFactorEnabled(true); wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue()); + + wxBoxSizer *spectrumCtlTray = new wxBoxSizer(wxVERTICAL); + peakHoldButton = new ModeSelectorCanvas(spectrumPanel, attribList); + peakHoldButton->addChoice(1, "P"); + peakHoldButton->setPadding(-1,-1); + peakHoldButton->setHighlightColor(RGBA4f(0.2,0.8,0.2)); + peakHoldButton->setHelpTip("Peak Hold Toggle"); + peakHoldButton->setToggleMode(true); + peakHoldButton->setSelection(-1); + peakHoldButton->SetMinSize(wxSize(12,24)); + + spectrumCtlTray->Add(peakHoldButton, 1, wxEXPAND | wxALL, 0); + spectrumCtlTray->AddSpacer(1); + spectrumAvgMeter = new MeterCanvas(spectrumPanel, attribList); spectrumAvgMeter->setHelpTip("Spectrum averaging speed, click or drag to adjust."); spectrumAvgMeter->setMax(1.0); spectrumAvgMeter->setLevel(0.65); spectrumAvgMeter->setShowUserInput(false); spectrumAvgMeter->SetMinSize(wxSize(12,24)); - + + spectrumCtlTray->Add(spectrumAvgMeter, 8, wxEXPAND | wxALL, 0); spectrumSizer->Add(spectrumCanvas, 63, wxEXPAND | wxALL, 0); spectrumSizer->AddSpacer(1); - spectrumSizer->Add(spectrumAvgMeter, 1, wxEXPAND | wxALL, 0); + spectrumSizer->Add(spectrumCtlTray, 1, wxEXPAND | wxALL, 0); spectrumPanel->SetSizer(spectrumSizer); // vbox->Add(spectrumSizer, 5, wxEXPAND | wxALL, 0); @@ -1088,6 +1103,12 @@ void AppFrame::OnIdle(wxIdleEvent& event) { #endif } + int peakHoldMode = peakHoldButton->getSelection(); + if (peakHoldButton->modeChanged()) { + wxGetApp().getSpectrumProcessor()->setPeakHold(peakHoldMode == 1); + peakHoldButton->clearModeChanged(); + } + if (!this->IsActive()) { std::this_thread::sleep_for(std::chrono::milliseconds(25)); } diff --git a/src/AppFrame.h b/src/AppFrame.h index fc4b9ed..5d8f4ad 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -94,7 +94,7 @@ private: // UITestCanvas *testCanvas; MeterCanvas *spectrumAvgMeter; MeterCanvas *waterfallSpeedMeter; - ModeSelectorCanvas *demodMuteButton; + ModeSelectorCanvas *demodMuteButton, *peakHoldButton; GainCanvas *gainCanvas; wxSizerItem *gainSizerItem, *gainSpacerItem; wxSplitterWindow *mainVisSplitter, *mainSplitter; diff --git a/src/panel/SpectrumPanel.cpp b/src/panel/SpectrumPanel.cpp index ac2ac3e..1ffd14a 100644 --- a/src/panel/SpectrumPanel.cpp +++ b/src/panel/SpectrumPanel.cpp @@ -88,6 +88,10 @@ void SpectrumPanel::setPoints(std::vector &points) { this->points.assign(points.begin(), points.end()); } +void SpectrumPanel::setPeakPoints(std::vector &points) { + this->peak_points.assign(points.begin(), points.end()); +} + void SpectrumPanel::drawPanelContents() { glDisable(GL_TEXTURE_2D); @@ -135,6 +139,12 @@ void SpectrumPanel::drawPanelContents() { glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &points[0]); glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); + if (peak_points.size()) { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0, 1.0, 0, 0.5); + glVertexPointer(2, GL_FLOAT, 0, &peak_points[0]); + glDrawArrays(GL_LINE_STRIP, 0, peak_points.size() / 2); + } glDisableClientState(GL_VERTEX_ARRAY); } diff --git a/src/panel/SpectrumPanel.h b/src/panel/SpectrumPanel.h index 3ccd747..39aa3fe 100644 --- a/src/panel/SpectrumPanel.h +++ b/src/panel/SpectrumPanel.h @@ -7,6 +7,7 @@ public: SpectrumPanel(); void setPoints(std::vector &points); + void setPeakPoints(std::vector &points); float getFloorValue(); void setFloorValue(float floorValue); @@ -35,6 +36,7 @@ private: long long freq; long long bandwidth; std::vector points; + std::vector peak_points; GLTextPanel dbPanelCeil; GLTextPanel dbPanelFloor; diff --git a/src/process/SpectrumVisualProcessor.cpp b/src/process/SpectrumVisualProcessor.cpp index b288167..af22b29 100644 --- a/src/process/SpectrumVisualProcessor.cpp +++ b/src/process/SpectrumVisualProcessor.cpp @@ -21,6 +21,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last fftSizeChanged.store(false); newFFTSize.store(0); lastView = false; + peakHold.store(false); } SpectrumVisualProcessor::~SpectrumVisualProcessor() { @@ -76,6 +77,20 @@ long SpectrumVisualProcessor::getBandwidth() { return bandwidth.load(); } +void SpectrumVisualProcessor::setPeakHold(bool peakHold_in) { + fft_ceil_peak = fft_floor_maa; + fft_floor_peak = fft_ceil_maa; + + for (int i = 0, iMax = fftSizeInternal; i < iMax; i++) { + fft_result_peak[i] = fft_floor_maa; + } + peakHold.store(peakHold_in); +} + +bool SpectrumVisualProcessor::getPeakHold() { + return peakHold.load(); +} + int SpectrumVisualProcessor::getDesiredInputSize() { return desiredInputSize.load(); } @@ -160,6 +175,7 @@ void SpectrumVisualProcessor::process() { iqData->busy_rw.lock(); busy_run.lock(); + bool doPeak = peakHold.load(); std::vector *data = &iqData->data; @@ -211,9 +227,11 @@ void SpectrumVisualProcessor::process() { if (freqDiff > 0) { memmove(&fft_result_ma[0], &fft_result_ma[numShift], (fftSizeInternal-numShift) * sizeof(double)); memmove(&fft_result_maa[0], &fft_result_maa[numShift], (fftSizeInternal-numShift) * sizeof(double)); + memmove(&fft_result_peak[0], &fft_result_peak[numShift], (fftSizeInternal-numShift) * sizeof(double)); } else { memmove(&fft_result_ma[numShift], &fft_result_ma[0], (fftSizeInternal-numShift) * sizeof(double)); memmove(&fft_result_maa[numShift], &fft_result_maa[0], (fftSizeInternal-numShift) * sizeof(double)); + memmove(&fft_result_peak[numShift], &fft_result_peak[0], (fftSizeInternal-numShift) * sizeof(double)); } } } @@ -330,6 +348,13 @@ void SpectrumVisualProcessor::process() { if (output->spectrum_points.size() != fftSize * 2) { output->spectrum_points.resize(fftSize * 2); } + if (doPeak) { + if (output->spectrum_hold_points.size() != fftSize * 2) { + output->spectrum_hold_points.resize(fftSize * 2); + } + } else { + output->spectrum_hold_points.resize(0); + } fftwf_execute(fftw_plan); @@ -340,11 +365,13 @@ void SpectrumVisualProcessor::process() { fft_result.reserve(fftSizeInternal); fft_result_ma.reserve(fftSizeInternal); fft_result_maa.reserve(fftSizeInternal); + fft_result_peak.reserve(fftSizeInternal); } fft_result.resize(fftSizeInternal); fft_result_ma.resize(fftSizeInternal); fft_result_maa.resize(fftSizeInternal); fft_result_temp.resize(fftSizeInternal); + fft_result_peak.resize(fftSizeInternal); } for (int i = 0, iMax = fftSizeInternal / 2; i < iMax; i++) { @@ -398,6 +425,9 @@ void SpectrumVisualProcessor::process() { fft_result_maa[i] = fft_result_temp[i]; } } + for (int i = 0, iMax = fftSizeInternal; i < iMax; i++) { + fft_result_peak[i] = fft_result_maa[i]; + } } for (int i = 0, iMax = fftSizeInternal; i < iMax; i++) { @@ -412,6 +442,11 @@ void SpectrumVisualProcessor::process() { if (fft_result_maa[i] < fft_floor || fft_floor != fft_floor) { fft_floor = fft_result_maa[i]; } + if (doPeak) { + if (fft_result_maa[i] > fft_result_peak[i]) { + fft_result_peak[i] = fft_result_maa[i]; + } + } } if (fft_ceil_ma != fft_ceil_ma) fft_ceil_ma = fft_ceil; @@ -423,15 +458,26 @@ void SpectrumVisualProcessor::process() { fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; if (fft_floor_maa != fft_floor_maa) fft_floor_maa = fft_floor; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; + + if (doPeak) { + if (fft_ceil_maa > fft_ceil_peak) { + fft_ceil_peak = fft_ceil_maa; + } + if (fft_floor_maa < fft_floor_peak) { + fft_floor_peak = fft_floor_maa; + } + } float sf = scaleFactor.load(); double visualRatio = (double(bandwidth) / double(resampleBw)); double visualStart = (double(fftSizeInternal) / 2.0) - (double(fftSizeInternal) * (visualRatio / 2.0)); double visualAccum = 0; - double acc = 0, accCount = 0, i = 0; + double peak_acc = 0, acc = 0, accCount = 0, i = 0; - + double point_ceil = doPeak?fft_ceil_peak:fft_ceil_maa; + double point_floor = doPeak?fft_floor_peak:fft_floor_maa; + for (int x = 0, xMax = output->spectrum_points.size() / 2; x < xMax; x++) { visualAccum += visualRatio * double(SPECTRUM_VZM); @@ -444,15 +490,25 @@ void SpectrumVisualProcessor::process() { idx = fftSizeInternal-1; } acc += fft_result_maa[idx]; + if (doPeak) { + peak_acc += fft_result_peak[idx]; + } accCount += 1.0; visualAccum -= 1.0; i++; } output->spectrum_points[x * 2] = ((float) x / (float) xMax); + if (doPeak) { + output->spectrum_hold_points[x * 2] = ((float) x / (float) xMax); + } if (accCount) { - output->spectrum_points[x * 2 + 1] = ((log10((acc/accCount)+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))))*sf; + output->spectrum_points[x * 2 + 1] = ((log10((acc/accCount)+0.25 - (point_floor-0.75)) / log10((point_ceil+0.25) - (point_floor-0.75))))*sf; acc = 0.0; + if (doPeak) { + output->spectrum_hold_points[x * 2 + 1] = ((log10((peak_acc/accCount)+0.25 - (point_floor-0.75)) / log10((point_ceil+0.25) - (point_floor-0.75))))*sf; + peak_acc = 0.0; + } accCount = 0.0; } } @@ -489,12 +545,24 @@ void SpectrumVisualProcessor::process() { output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftEnd + n) * 2 + 1]; n++; } + if (doPeak) { + int n = 1; + for (int i = fftStart; i < halfWay; i++) { + output->spectrum_hold_points[i * 2 + 1] = output->spectrum_hold_points[(fftStart - n) * 2 + 1]; + n++; + } + n = 1; + for (int i = halfWay; i < fftEnd; i++) { + output->spectrum_hold_points[i * 2 + 1] = output->spectrum_hold_points[(fftEnd + n) * 2 + 1]; + n++; + } + } } } } - output->fft_ceiling = fft_ceil_maa/sf; - output->fft_floor = fft_floor_maa; + output->fft_ceiling = point_ceil/sf; + output->fft_floor = point_floor; output->centerFreq = centerFreq; output->bandwidth = bandwidth; diff --git a/src/process/SpectrumVisualProcessor.h b/src/process/SpectrumVisualProcessor.h index e900bea..00e92d7 100644 --- a/src/process/SpectrumVisualProcessor.h +++ b/src/process/SpectrumVisualProcessor.h @@ -10,6 +10,7 @@ class SpectrumVisualData : public ReferenceCounter { public: std::vector spectrum_points; + std::vector spectrum_hold_points; double fft_ceiling, fft_floor; long long centerFreq; int bandwidth; @@ -35,6 +36,9 @@ public: void setBandwidth(long bandwidth_in); long getBandwidth(); + void setPeakHold(bool peakHold_in); + bool getPeakHold(); + int getDesiredInputSize(); void setup(int fftSize); @@ -65,11 +69,13 @@ private: double fft_ceil_ma, fft_ceil_maa; double fft_floor_ma, fft_floor_maa; + double fft_ceil_peak, fft_floor_peak; std::atomic fft_average_rate; std::vector fft_result; std::vector fft_result_ma; std::vector fft_result_maa; + std::vector fft_result_peak; std::vector fft_result_temp; msresamp_crcf resampler; @@ -81,7 +87,7 @@ private: std::vector resampleBuffer; std::atomic_int desiredInputSize; std::mutex busy_run; - std::atomic_bool hideDC; + std::atomic_bool hideDC, peakHold; std::atomic scaleFactor; std::atomic_bool fftSizeChanged; }; diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 50c9af9..57dcc01 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -58,6 +58,7 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { if (vData) { spectrumPanel.setPoints(vData->spectrum_points); + spectrumPanel.setPeakPoints(vData->spectrum_hold_points); spectrumPanel.setFloorValue(vData->fft_floor); spectrumPanel.setCeilValue(vData->fft_ceiling); vData->decRefCount(); @@ -140,6 +141,7 @@ void SpectrumCanvas::moveCenterFrequency(long long freqChange) { freq -= freqChange; } wxGetApp().setFrequency(freq); + wxGetApp().getSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold()); } }