From 11452f8a94175895d45235c9770580ee070a4e12 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Fri, 23 Nov 2012 10:41:31 +0000 Subject: [PATCH] wired up squelch, added slow SNR option, works OK on Linux, need to test on Win32 git-svn-id: https://svn.code.sf.net/p/freetel/code@1048 01035d8c-6547-0410-b346-abe4f91aad63 --- fdmdv2/src/fdmdv2_defines.h | 5 +- fdmdv2/src/fdmdv2_main.cpp | 85 ++++++++++++++++------ fdmdv2/src/fdmdv2_main.h | 9 ++- fdmdv2/src/fdmdv2_plot_spectrum.cpp | 3 +- fdmdv2/src/fdmdv2_plot_spectrum.h | 2 - fdmdv2/src/fdmdv2_plot_waterfall_linux.cpp | 7 +- fdmdv2/src/fdmdv2_plot_waterfall_linux.h | 2 - fdmdv2/src/topFrame.cpp | 20 +++-- fdmdv2/src/topFrame.h | 2 + 9 files changed, 95 insertions(+), 40 deletions(-) diff --git a/fdmdv2/src/fdmdv2_defines.h b/fdmdv2/src/fdmdv2_defines.h index b74966d0..6224d78c 100644 --- a/fdmdv2/src/fdmdv2_defines.h +++ b/fdmdv2/src/fdmdv2_defines.h @@ -31,11 +31,12 @@ #define MIN_MAG_DB -40.0 // min of spectrogram/waterfall magnitude axis #define MAX_MAG_DB 0.0 // max of spectrogram/waterfall magnitude axis #define STEP_MAG_DB 5.0 // magnitude axis step -#define BETA 0.1 // constant for time averaging spectrum data +#define BETA 0.9 // constant for time averaging spectrum data #define MIN_F_HZ 0 // min freq on Waterfall and Spectrum #define MAX_F_HZ 4000 // max freq on Waterfall and Spectrum #define STEP_F_HZ 500 // freq step on Waterfall and Spectrum graticule #define WATERFALL_SECS_Y 5 // number of seconds respresented by y axis of waterfall +#define WATERFALL_SECS_STEP 5 // graticule y axis steps of waterfall #define DT 0.1 // time between real time graphing updates #define FS 8000 // FDMDV modem sample rate @@ -70,6 +71,8 @@ // Squelch #define SQ_DEFAULT_SNR 4.0 +#define SNRSLOW_BETA 0.5 // time constant for slow SNR for display + enum { ID_ROTATE_LEFT = wxID_HIGHEST + 1, diff --git a/fdmdv2/src/fdmdv2_main.cpp b/fdmdv2/src/fdmdv2_main.cpp index db379e59..6fe7e604 100644 --- a/fdmdv2/src/fdmdv2_main.cpp +++ b/fdmdv2/src/fdmdv2_main.cpp @@ -47,6 +47,7 @@ float g_SquelchLevel; int g_analog; int g_split; int g_tx; +float g_snr; // tx/rx processing states int g_nRxIn = FDMDV_NOM_SAMPLES_PER_FRAME; @@ -236,8 +237,8 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent) // Add Demod Input window m_panelDemodIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelDemodIn, _("Demod In"), true, wxNullBitmap); - g_plotDemodInFifo = fifo_create(2*WAVEFORM_PLOT_BUF); + m_auiNbookCtrl->AddPage(m_panelDemodIn, _("Frm Radio"), true, wxNullBitmap); + g_plotDemodInFifo = fifo_create(2*WAVEFORM_PLOT_BUF); } if(wxGetApp().m_show_speech_in) @@ -245,8 +246,8 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent) // Add Speech Input window m_panelSpeechIn = new PlotScalar((wxFrame*) m_auiNbookCtrl, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechIn, _("Speech In"), true, wxNullBitmap); - g_plotSpeechInFifo = fifo_create(2*WAVEFORM_PLOT_BUF); + m_auiNbookCtrl->AddPage(m_panelSpeechIn, _("Frm Mic"), true, wxNullBitmap); + g_plotSpeechInFifo = fifo_create(2*WAVEFORM_PLOT_BUF); } if(wxGetApp().m_show_speech_out) @@ -254,7 +255,7 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent) // Add Speech Output window m_panelSpeechOut = new PlotScalar((wxFrame*) m_auiNbookCtrl, WAVEFORM_PLOT_TIME, 1.0/WAVEFORM_PLOT_FS, -1, 1, 1, 0.2, "%2.1f", 0); - m_auiNbookCtrl->AddPage(m_panelSpeechOut, _("Speech Out"), true, wxNullBitmap); + m_auiNbookCtrl->AddPage(m_panelSpeechOut, _("To Spkr/Hdphns"), true, wxNullBitmap); g_plotSpeechOutFifo = fifo_create(2*WAVEFORM_PLOT_BUF); } @@ -298,6 +299,10 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent) wxGetApp().m_recFileFromRadioPath = pConfig->Read("/File/recFileFromRadioPath", wxT("")); wxGetApp().m_recFileFromRadioSecs = pConfig->Read("/File/recFileFromRadioSecs", 30); + bool slow = false; + wxGetApp().m_snrSlow = pConfig->Read("/Audio/snrSlow", slow); + //printf("wxGetApp().m_snrSlow %d\n", (int)wxGetApp().m_snrSlow); + pConfig->SetPath(wxT("/")); // this->Connect(m_menuItemHelpUpdates->GetId(), wxEVT_UPDATE_UI, wxUpdateUIEventHandler(TopFrame::OnHelpCheckUpdatesUI)); @@ -327,6 +332,11 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent) m_textSQ->SetLabel(sqsnr_string); m_ckboxSQ->SetValue(g_SquelchActive); + // SNR settings + + m_ckboxSNR->SetValue(wxGetApp().m_snrSlow); + setsnrBeta(wxGetApp().m_snrSlow); + #ifdef _USE_TIMER Bind(wxEVT_TIMER, &MainFrame::OnTimer, this); // ID_MY_WINDOW); m_plotTimer.SetOwner(this, ID_TIMER_WATERFALL); @@ -414,6 +424,8 @@ MainFrame::~MainFrame() pConfig->Write(wxT("/File/playFileToMicInPath"), wxGetApp().m_playFileToMicInPath); pConfig->Write(wxT("/File/recFileFromRadioPath"), wxGetApp().m_recFileFromRadioPath); pConfig->Write(wxT("/File/recFileFromRadioSecs"), wxGetApp().m_recFileFromRadioSecs); + + pConfig->Write(wxT("/Audio/snrSlow"), wxGetApp().m_snrSlow); } m_togRxID->Disconnect(wxEVT_UPDATE_UI, wxUpdateUIEventHandler(MainFrame::OnTogBtnRxIDUI), NULL, this); @@ -502,11 +514,22 @@ void MainFrame::OnTimer(wxTimerEvent &evt) // SNR text box and guage ------------------------------------------------------------ - float snr_limited = g_stats.snr_est; + // LP filter g_stats.snr_est some more to stabilise the + // display. g_stats.snr_est already has some low pass filtering + // but we need it fairly fast to activate squelch. So we + // optionally perform some further filtering for the display + // version of SNR. The "Slow" checkbox controls the amount of + // filtering. The filtered snr also controls the squelch + + g_snr = m_snrBeta*g_snr + (1.0 - m_snrBeta)*g_stats.snr_est; + float snr_limited = g_snr; if (snr_limited < -9.0) snr_limited = -9.0; // stop text box overflow char snr[15]; - sprintf(snr, "%4.1f", snr_limited); + if (wxGetApp().m_snrSlow) + sprintf(snr, "%4.1f", snr_limited); + else + sprintf(snr, "%d", (int)(snr_limited+0.5)); // round to nearest dB wxString snr_string(snr); m_textSNR->SetLabel(snr_string); @@ -641,6 +664,28 @@ void MainFrame::OnCheckSQClick(wxCommandEvent& event) } } +void MainFrame::setsnrBeta(bool snrSlow) +{ + if(snrSlow) + { + m_snrBeta = 0.9; // make this closer to 1.0 to smooth SNR est further + } + else + { + m_snrBeta = 0.0; // no smoothing of SNR estimate from demodulator + } +} + +//------------------------------------------------------------------------- +// OnCheckSQClick() +//------------------------------------------------------------------------- +void MainFrame::OnCheckSNRClick(wxCommandEvent& event) +{ + wxGetApp().m_snrSlow = m_ckboxSNR->GetValue(); + setsnrBeta(wxGetApp().m_snrSlow); + //printf("m_snrSlow: %d\n", (int)wxGetApp().m_snrSlow); +} + //------------------------------------------------------------------------- // OnTogBtnTXClick() //------------------------------------------------------------------------- @@ -1219,7 +1264,9 @@ void MainFrame::OnTogBtnOnOff(wxCommandEvent& event) g_pFDMDV = fdmdv_create(); g_pCodec2 = codec2_create(CODEC2_MODE_1400); g_State = 0; - printf("g_stats.snr: %f\n", g_stats.snr_est); + g_snr = 0.0; + + //printf("g_stats.snr: %f\n", g_stats.snr_est); // attempt to start sound cards and tx/rx processing @@ -1586,19 +1633,6 @@ void MainFrame::startRxStream() } -//---------------------------------------------------------------- -// update average of each spectrum point -//---------------------------------------------------------------- -void MainFrame::averageData(float mag_dB[]) -{ - int i; - - for(i = 0; i < FDMDV_NSPEC; i++) - { - g_avmag[i] = (1.0 - BETA) * g_avmag[i] + BETA * mag_dB[i]; - } -} - // returns number of output samples generated by resampling int resample(SRC_STATE *src, @@ -1735,6 +1769,11 @@ void txRxProcessing() memset(out8k_short, 0, sizeof(short)*N8); fifo_read(cbData->rxoutfifo, out8k_short, N8); } + + if (g_SquelchActive && (g_SquelchLevel > g_snr)) { + //printf("g_SquelchLevel: %f g_snr: %f\n", g_SquelchLevel, g_snr); + memset(out8k_short, 0, sizeof(short)*N8); + } resample_for_plot(g_plotSpeechOutFifo, out8k_short, N8); @@ -1960,7 +1999,7 @@ void per_frame_rx_processing( // Average rx spectrum data using a simple IIR low pass filter for(i = 0; i < FDMDV_NSPEC; i++) { - g_avmag[i] = (1.0 - BETA) * g_avmag[i] + BETA * rx_spec[i]; + g_avmag[i] = BETA * g_avmag[i] + (1.0 - BETA) * rx_spec[i]; } // @@ -1984,7 +2023,7 @@ void per_frame_rx_processing( output_buf[i] = 0; fifo_write(output_fifo, output_buf, N8); - if((g_stats.fest_coarse_fine == 1) && (g_stats.snr_est > 3.0)) + if(g_stats.fest_coarse_fine == 1) { next_state = 1; } diff --git a/fdmdv2/src/fdmdv2_main.h b/fdmdv2/src/fdmdv2_main.h index 3b0dfb63..2e326e5c 100644 --- a/fdmdv2/src/fdmdv2_main.h +++ b/fdmdv2/src/fdmdv2_main.h @@ -126,6 +126,8 @@ class MainApp : public wxApp wxString m_recFileFromRadioPath; unsigned int m_recFileFromRadioSecs; + bool m_snrSlow; + int m_show_wf; int m_show_spect; int m_show_scatter; @@ -269,12 +271,14 @@ class MainFrame : public TopFrame int soundCard, int sampleRate, int inputChannels); protected: + + void setsnrBeta(bool snrSlow); + // protected event handlers virtual void OnCloseFrame(wxCloseEvent& event); virtual void OnExitClick(wxCommandEvent& event); //void OnQuit(wxCommandEvent& event); - void averageData(float mag_dB[]); void startTxStream(); void startRxStream(); void stopTxStream(); @@ -315,6 +319,7 @@ class MainFrame : public TopFrame void OnCmdSliderScrollChanged( wxScrollEvent& event ); void OnSliderScrollTop( wxScrollEvent& event ); void OnCheckSQClick( wxCommandEvent& event ); + void OnCheckSNRClick( wxCommandEvent& event ); // Toggle Buttons void OnTogBtnSplitClick(wxCommandEvent& event); @@ -353,6 +358,8 @@ class MainFrame : public TopFrame bool m_useMemory; wxTextCtrl* m_tc; int m_zoom; + + float m_snrBeta; }; void resample_for_plot(struct FIFO *plotFifo, short buf[], int length); diff --git a/fdmdv2/src/fdmdv2_plot_spectrum.cpp b/fdmdv2/src/fdmdv2_plot_spectrum.cpp index 61ac9f77..8191ea4d 100644 --- a/fdmdv2/src/fdmdv2_plot_spectrum.cpp +++ b/fdmdv2/src/fdmdv2_plot_spectrum.cpp @@ -23,7 +23,8 @@ #include "fdmdv2_main.h" -extern float g_avmag[]; +extern float g_avmag[]; // average mag data passed to draw() +void fdmdv2_clickTune(float frequency); // callback to pass new click freq BEGIN_EVENT_TABLE(PlotSpectrum, PlotPanel) EVT_MOTION (PlotSpectrum::OnMouseMove) diff --git a/fdmdv2/src/fdmdv2_plot_spectrum.h b/fdmdv2/src/fdmdv2_plot_spectrum.h index 49fce6bf..eb6e6d43 100644 --- a/fdmdv2/src/fdmdv2_plot_spectrum.h +++ b/fdmdv2/src/fdmdv2_plot_spectrum.h @@ -46,6 +46,4 @@ class PlotSpectrum : public PlotPanel DECLARE_EVENT_TABLE() }; -void fdmdv2_clickTune(float frequency); - #endif //__FDMDV2_PLOT_SPECTRUM__ diff --git a/fdmdv2/src/fdmdv2_plot_waterfall_linux.cpp b/fdmdv2/src/fdmdv2_plot_waterfall_linux.cpp index eb0fe94c..08ecc4c4 100644 --- a/fdmdv2/src/fdmdv2_plot_waterfall_linux.cpp +++ b/fdmdv2/src/fdmdv2_plot_waterfall_linux.cpp @@ -22,7 +22,8 @@ #include "wx/wx.h" #include "fdmdv2_main.h" -extern float g_avmag[]; // magnitude spectrum passed in to draw() +extern float g_avmag[]; // av mag spec passed in to draw() +void fdmdv2_clickTune(float frequency); // callback to pass new click freq BEGIN_EVENT_TABLE(PlotWaterfall, PlotPanel) EVT_PAINT (PlotWaterfall::OnPaint) @@ -236,7 +237,7 @@ void PlotWaterfall::drawGraticule(wxAutoBufferedPaintDC& dc) // Horizontal gridlines dc.SetPen(m_penDotDash); - for(time=0; time<=WATERFALL_SECS_Y; time++) { + for(time=0; time<=WATERFALL_SECS_Y; time+=WATERFALL_SECS_STEP) { y = m_rGrid.GetHeight() - time*time_s_to_py; y += PLOT_BORDER; dc.DrawLine(PLOT_BORDER + XLEFT_OFFSET, y, @@ -255,7 +256,7 @@ void PlotWaterfall::plotPixelData() { float spec_index_per_px; float intensity_per_dB; - int px_per_sec; + float px_per_sec; int index; int dy; int dy_blocks; diff --git a/fdmdv2/src/fdmdv2_plot_waterfall_linux.h b/fdmdv2/src/fdmdv2_plot_waterfall_linux.h index 55534560..f7769f4e 100644 --- a/fdmdv2/src/fdmdv2_plot_waterfall_linux.h +++ b/fdmdv2/src/fdmdv2_plot_waterfall_linux.h @@ -58,6 +58,4 @@ class PlotWaterfall : public PlotPanel DECLARE_EVENT_TABLE() }; -void fdmdv2_clickTune(float frequency); - #endif //__FDMDV2_PLOT_WATERFALL__ diff --git a/fdmdv2/src/topFrame.cpp b/fdmdv2/src/topFrame.cpp index 79df13a4..2cf19588 100644 --- a/fdmdv2/src/topFrame.cpp +++ b/fdmdv2/src/topFrame.cpp @@ -186,15 +186,18 @@ TopFrame::TopFrame(wxWindow* parent, wxWindowID id, const wxString& title, const //------------------------------ // Box for S/N ratio (Numeric) //------------------------------ - //wxBoxSizer* bSizer29; - //bSizer29 = new wxBoxSizer(wxVERTICAL); + m_textSNR = new wxStaticText(this, wxID_ANY, wxT(" 0.0"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); - //m_textSNR = new wxStaticText(this, wxID_ANY, wxEmptyString, wxPoint(-1,-1), wxSize(35,25), wxTE_READONLY); - //m_textSNR->SetToolTip(_("Show SNR ratio numerically.")); - //m_textSNR->SetMinSize(wxSize(35,25)); - //bSizer29->Add(m_textSNR, 0, wxALIGN_CENTER|wxALL, 1); - //snrSizer->Add(bSizer29, 0, wxEXPAND, 1); snrSizer->Add(m_textSNR, 0, wxALIGN_CENTER_HORIZONTAL, 1); + + //------------------------------ + // S/N ratio slow Checkbox + //------------------------------ + + m_ckboxSNR = new wxCheckBox(this, wxID_ANY, _("Slow"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE); + m_ckboxSNR->SetToolTip(_("Smooth but slow SNR estimation")); + snrSizer->Add(m_ckboxSNR, 0, wxALIGN_CENTER_HORIZONTAL, 5); + leftSizer->Add(snrSizer, 2, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxALL, 1); //------------------------------ @@ -206,6 +209,7 @@ TopFrame::TopFrame(wxWindow* parent, wxWindowID id, const wxString& title, const m_gaugeLevel->SetToolTip(_("Display signal level.")); levelSizer->Add(m_gaugeLevel, 1, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); leftSizer->Add(levelSizer, 2, wxALIGN_CENTER|wxALL|wxEXPAND, 1); + bSizer1->Add(leftSizer, 0, wxALL|wxEXPAND, 5); //===================================================== @@ -466,6 +470,8 @@ TopFrame::TopFrame(wxWindow* parent, wxWindowID id, const wxString& title, const m_sliderSQ->Connect(wxEVT_SCROLL_TOP, wxScrollEventHandler(TopFrame::OnSliderScrollTop), NULL, this); m_ckboxSQ->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSQClick), NULL, this); + m_ckboxSNR->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(TopFrame::OnCheckSNRClick), NULL, this); + m_togBtnOnOff->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnOnOff), NULL, this); #ifdef UNIMPLEMENTED m_togBtnLoopRx->Connect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(TopFrame::OnTogBtnLoopRx), NULL, this); diff --git a/fdmdv2/src/topFrame.h b/fdmdv2/src/topFrame.h index b78070e3..5f6eb5ec 100644 --- a/fdmdv2/src/topFrame.h +++ b/fdmdv2/src/topFrame.h @@ -83,6 +83,7 @@ class TopFrame : public wxFrame wxMenu* help; wxGauge* m_gaugeSNR; wxStaticText* m_textSNR; + wxCheckBox* m_ckboxSNR; wxGauge* m_gaugeLevel; wxTextCtrl* m_txtCtrl; wxSlider* m_sliderSQ; @@ -137,6 +138,7 @@ class TopFrame : public wxFrame virtual void OnCmdSliderScrollChanged( wxScrollEvent& event ) { event.Skip(); } virtual void OnSliderScrollTop( wxScrollEvent& event ) { event.Skip(); } virtual void OnCheckSQClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCheckSNRClick( wxCommandEvent& event ) { event.Skip(); } virtual void OnTogBtnLoopRx( wxCommandEvent& event ) { event.Skip(); } virtual void OnTogBtnLoopTx( wxCommandEvent& event ) { event.Skip(); } -- 2.25.1