From 724808d9ff9526ac94399af816ec47b835e389e0 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sat, 21 Nov 2015 15:12:20 -0500 Subject: [PATCH] Clean-up/fix squelch --- src/AppFrame.cpp | 7 ++- src/demod/DemodulatorInstance.cpp | 17 +----- src/demod/DemodulatorMgr.cpp | 2 +- src/demod/DemodulatorThread.cpp | 90 +++++++++++++++---------------- src/demod/DemodulatorThread.h | 16 +++--- src/visual/MeterCanvas.cpp | 19 ++++--- src/visual/MeterCanvas.h | 3 +- 7 files changed, 74 insertions(+), 80 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 6b00c74..5b3daa2 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -129,7 +129,10 @@ AppFrame::AppFrame() : demodTray->AddSpacer(1); demodSignalMeter = new MeterCanvas(demodPanel, attribList); - demodSignalMeter->setMax(0.5); + demodSignalMeter->setMax(DEMOD_SIGNAL_MAX); + demodSignalMeter->setMin(DEMOD_SIGNAL_MIN); + demodSignalMeter->setLevel(DEMOD_SIGNAL_MIN); + demodSignalMeter->setInputValue(DEMOD_SIGNAL_MIN); demodSignalMeter->setHelpTip("Current Signal Level. Click / Drag to set Squelch level."); demodSignalMeter->SetMinSize(wxSize(12,24)); demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0); @@ -644,7 +647,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) { wxGetApp().getDemodMgr().setLastMuted(false); wxGetApp().getDemodMgr().setLastBandwidth(DEFAULT_DEMOD_BW); wxGetApp().getDemodMgr().setLastGain(1.0); - wxGetApp().getDemodMgr().setLastSquelchLevel(0); + wxGetApp().getDemodMgr().setLastSquelchLevel(-100); waterfallCanvas->setBandwidth(wxGetApp().getSampleRate()); waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); spectrumCanvas->setBandwidth(wxGetApp().getSampleRate()); diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index ecf2383..88139be 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -269,10 +269,6 @@ void DemodulatorInstance::setDemodulatorType(std::string demod_type_in) { } else { setBandwidth(AudioThread::deviceSampleRate[getOutputDevice()]); } - } else if (currentDemodType == "USB" || currentDemodType == "LSB" || currentDemodType == "DSB" || currentDemodType == "AM") { - demodulatorThread->setAGC(false); - } else { - demodulatorThread->setAGC(true); } setGain(getGain()); @@ -400,18 +396,7 @@ int DemodulatorInstance::getAudioSampleRate() { void DemodulatorInstance::setGain(float gain_in) { currentAudioGain = gain_in; - - if (currentDemodType == "I/Q") { - if (gain_in < 0.25) { - audioThread->setGain(1.0); - demodulatorThread->setAGC(false); - } else { - audioThread->setGain(gain_in); - demodulatorThread->setAGC(true); - } - } else { - audioThread->setGain(gain_in); - } + audioThread->setGain(gain_in); } float DemodulatorInstance::getGain() { diff --git a/src/demod/DemodulatorMgr.cpp b/src/demod/DemodulatorMgr.cpp index 9a0ffec..fc6c4e1 100644 --- a/src/demod/DemodulatorMgr.cpp +++ b/src/demod/DemodulatorMgr.cpp @@ -7,7 +7,7 @@ DemodulatorMgr::DemodulatorMgr() : activeDemodulator(NULL), lastActiveDemodulator(NULL), activeVisualDemodulator(NULL), lastBandwidth(DEFAULT_DEMOD_BW), lastDemodType( - DEFAULT_DEMOD_TYPE), lastSquelchEnabled(false), lastSquelch(0), lastGain(1.0), lastMuted(false) { + DEFAULT_DEMOD_TYPE), lastSquelchEnabled(false), lastSquelch(-100), lastGain(1.0), lastMuted(false) { } diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index 8047e5a..b16dd27 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -12,12 +12,10 @@ #include #endif -DemodulatorThread::DemodulatorThread(DemodulatorInstance *parent) : IOThread(), iqAutoGain(NULL), audioSampleRate(0), squelchLevel(0), signalLevel(0), squelchEnabled(false), cModem(nullptr), cModemKit(nullptr), iqInputQueue(NULL), audioOutputQueue(NULL), audioVisOutputQueue(NULL), threadQueueControl(NULL), threadQueueNotify(NULL) { +DemodulatorThread::DemodulatorThread(DemodulatorInstance *parent) : IOThread(), audioSampleRate(0), squelchLevel(-100), signalLevel(-100), squelchEnabled(false), cModem(nullptr), cModemKit(nullptr), iqInputQueue(NULL), audioOutputQueue(NULL), audioVisOutputQueue(NULL), threadQueueControl(NULL), threadQueueNotify(NULL) { demodInstance = parent; muted.store(false); - agcEnabled.store(false); - } DemodulatorThread::~DemodulatorThread() { @@ -30,6 +28,27 @@ void DemodulatorThread::onBindOutput(std::string name, ThreadQueueBase *threadQu } } +float DemodulatorThread::abMagnitude(double alpha, double beta, float inphase, float quadrature) { + // http://dspguru.com/dsp/tricks/magnitude-estimator + /* magnitude ~= alpha * max(|I|, |Q|) + beta * min(|I|, |Q|) */ + double abs_inphase = fabs(inphase); + double abs_quadrature = fabs(quadrature); + if (abs_inphase > abs_quadrature) { + return alpha * abs_inphase + beta * abs_quadrature; + } else { + return alpha * abs_quadrature + beta * abs_inphase; + } +} + +float DemodulatorThread::linearToDb(float linear) { + // http://dspguru.com/dsp/tricks/magnitude-estimator + #define SMALL 1e-20 + if (linear <= SMALL) { + linear = SMALL; + } + return 20.0 * log10(linear); +} + void DemodulatorThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread @@ -38,10 +57,6 @@ void DemodulatorThread::run() { pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif - // Automatic IQ gain - iqAutoGain = agc_crcf_create(); - agc_crcf_set_bandwidth(iqAutoGain, 0.1); - ReBuffer audioVisBuffers; std::cout << "Demodulator thread started.." << std::endl; @@ -84,30 +99,21 @@ void DemodulatorThread::run() { continue; } - if (agcData.size() != bufSize) { - if (agcData.capacity() < bufSize) { - agcData.reserve(bufSize); - } - agcData.resize(bufSize); + float currentSignalLevel = 0; + float accum = 0; + + for (std::vector::iterator i = inp->data.begin(); i != inp->data.end(); i++) { + accum += abMagnitude(0.948059448969, 0.392699081699, i->real, i->imag); } - agc_crcf_execute_block(iqAutoGain, &(inp->data[0]), bufSize, &agcData[0]); - - float currentSignalLevel = 0; - - currentSignalLevel = ((60.0 / fabs(agc_crcf_get_rssi(iqAutoGain))) / 15.0 - signalLevel); - - if (agc_crcf_get_signal_level(iqAutoGain) > currentSignalLevel) { - currentSignalLevel = agc_crcf_get_signal_level(iqAutoGain); + currentSignalLevel = linearToDb(accum / float(inp->data.size())); + if (currentSignalLevel < DEMOD_SIGNAL_MIN+1) { + currentSignalLevel = DEMOD_SIGNAL_MIN+1; } std::vector *inputData; - if (agcEnabled) { - inputData = &agcData; - } else { - inputData = &inp->data; - } + inputData = &inp->data; modemData.sampleRate = inp->sampleRate; modemData.data.assign(inputData->begin(), inputData->end()); @@ -133,17 +139,20 @@ void DemodulatorThread::run() { signalLevel = signalLevel + (currentSignalLevel - signalLevel) * 0.05; } - if (audioOutputQueue != NULL) { - if (ati && (!squelchEnabled || (signalLevel >= squelchLevel))) { - std::vector::iterator data_i; - ati->peak = 0; - for (data_i = ati->data.begin(); data_i != ati->data.end(); data_i++) { - float p = fabs(*data_i); - if (p > ati->peak) { - ati->peak = p; - } + bool squelched = (squelchEnabled && (signalLevel < squelchLevel)); + + if (audioOutputQueue != NULL && ati && !squelched) { + std::vector::iterator data_i; + ati->peak = 0; + for (data_i = ati->data.begin(); data_i != ati->data.end(); data_i++) { + float p = fabs(*data_i); + if (p > ati->peak) { + ati->peak = p; } } + } else if (ati) { + ati->decRefCount(); + ati = nullptr; } if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) { @@ -164,8 +173,8 @@ void DemodulatorThread::run() { if (inp->modemType == "I/Q") { for (int i = 0; i < stereoSize / 2; i++) { - ati_vis->data[i] = agcData[i].real * 0.75; - ati_vis->data[i + stereoSize / 2] = agcData[i].imag * 0.75; + ati_vis->data[i] = (*inputData)[i].real * 0.75; + ati_vis->data[i + stereoSize / 2] = (*inputData)[i].imag * 0.75; } } else { for (int i = 0; i < stereoSize / 2; i++) { @@ -192,7 +201,6 @@ void DemodulatorThread::run() { ati_vis->data.assign(demodOutData->begin(), demodOutData->begin() + num_vis); } - // std::cout << "Signal: " << agc_crcf_get_signal_level(agc) << " -- " << agc_crcf_get_rssi(agc) << "dB " << std::endl; } audioVisOutputQueue->push(ati_vis); @@ -258,14 +266,6 @@ void DemodulatorThread::setMuted(bool muted) { this->muted.store(muted); } -void DemodulatorThread::setAGC(bool state) { - agcEnabled.store(state); -} - -bool DemodulatorThread::getAGC() { - return agcEnabled.load(); -} - float DemodulatorThread::getSignalLevel() { return signalLevel.load(); } diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index 4cbc2ef..9cc2ecf 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -10,6 +10,9 @@ typedef ThreadQueue DemodulatorThreadOutputQueue; #define DEMOD_VIS_SIZE 2048 +#define DEMOD_SIGNAL_MIN -30 +#define DEMOD_SIGNAL_MAX 30 + class DemodulatorInstance; class DemodulatorThread : public IOThread { @@ -23,9 +26,6 @@ public: void run(); void terminate(); - void setAGC(bool state); - bool getAGC(); - void setMuted(bool state); bool isMuted(); @@ -33,16 +33,16 @@ public: void setSquelchLevel(float signal_level_in); float getSquelchLevel(); + protected: + + float abMagnitude(double alpha, double beta, float inphase, float quadrature); + float linearToDb(float linear); + DemodulatorInstance *demodInstance; ReBuffer outputBuffers; - std::vector agcData; - - agc_crcf iqAutoGain; - std::atomic_bool muted; - std::atomic_bool agcEnabled; int audioSampleRate; std::atomic squelchLevel; diff --git a/src/visual/MeterCanvas.cpp b/src/visual/MeterCanvas.cpp index 4fe5e14..e696421 100644 --- a/src/visual/MeterCanvas.cpp +++ b/src/visual/MeterCanvas.cpp @@ -25,7 +25,7 @@ EVT_ENTER_WINDOW(MeterCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() MeterCanvas::MeterCanvas(wxWindow *parent, int *attribList) : - InteractiveCanvas(parent, attribList), level(0), level_max(1), inputValue(0), userInputValue(0), showUserInput(true) { + InteractiveCanvas(parent, attribList), level(0), level_min(0), level_max(1), inputValue(0), userInputValue(0), showUserInput(true) { glContext = new MeterContext(this, &wxGetApp().GetContext(this)); } @@ -47,6 +47,11 @@ void MeterCanvas::setMax(float max_in) { Refresh(); } +void MeterCanvas::setMin(float min_in) { + level_min = min_in; + Refresh(); +} + void MeterCanvas::setInputValue(float slider_in) { userInputValue = inputValue = slider_in; Refresh(); @@ -80,10 +85,10 @@ void MeterCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { if (mouseTracker.mouseInView()) { glContext->Draw(0.4, 0.4, 0.4, 0.5, mouseTracker.getMouseY()); } - glContext->Draw(ThemeMgr::mgr.currentTheme->meterLevel.r, ThemeMgr::mgr.currentTheme->meterLevel.g, ThemeMgr::mgr.currentTheme->meterLevel.b, 0.5, level / level_max); + glContext->Draw(ThemeMgr::mgr.currentTheme->meterLevel.r, ThemeMgr::mgr.currentTheme->meterLevel.g, ThemeMgr::mgr.currentTheme->meterLevel.b, 0.5, (level-level_min) / (level_max-level_min)); if (showUserInput) { - glContext->Draw(ThemeMgr::mgr.currentTheme->meterValue.r, ThemeMgr::mgr.currentTheme->meterValue.g, ThemeMgr::mgr.currentTheme->meterValue.b, 0.5, userInputValue / level_max); - } + glContext->Draw(ThemeMgr::mgr.currentTheme->meterValue.r, ThemeMgr::mgr.currentTheme->meterValue.g, ThemeMgr::mgr.currentTheme->meterValue.b, 0.5, (userInputValue-level_min) / (level_max-level_min)); + } glContext->DrawEnd(); SwapBuffers(); @@ -101,7 +106,7 @@ void MeterCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); if (mouseTracker.mouseDown()) { - userInputValue = mouseTracker.getMouseY() * level_max; + userInputValue = mouseTracker.getMouseY() * (level_max-level_min) + level_min; } else { if (!helpTip.empty()) { setStatusText(helpTip); @@ -111,7 +116,7 @@ void MeterCanvas::OnMouseMoved(wxMouseEvent& event) { void MeterCanvas::OnMouseDown(wxMouseEvent& event) { InteractiveCanvas::OnMouseDown(event); - userInputValue = mouseTracker.getMouseY() * level_max; + userInputValue = mouseTracker.getMouseY() * (level_max-level_min) + level_min; mouseTracker.setHorizDragLock(true); Refresh(); } @@ -123,7 +128,7 @@ void MeterCanvas::OnMouseWheelMoved(wxMouseEvent& event) { void MeterCanvas::OnMouseReleased(wxMouseEvent& event) { InteractiveCanvas::OnMouseReleased(event); - userInputValue = mouseTracker.getMouseY() * level_max; + userInputValue = mouseTracker.getMouseY() * (level_max-level_min) + level_min; Refresh(); } diff --git a/src/visual/MeterCanvas.h b/src/visual/MeterCanvas.h index 0cc97cd..12577b2 100644 --- a/src/visual/MeterCanvas.h +++ b/src/visual/MeterCanvas.h @@ -22,6 +22,7 @@ public: float getLevel(); void setMax(float max_in); + void setMin(float max_in); void setInputValue(float slider_in); bool inputChanged(); @@ -44,7 +45,7 @@ private: MeterContext *glContext; float level; - float level_max; + float level_min, level_max; float inputValue; float userInputValue;