Skip to content

Commit

Permalink
Merge pull request #487 from drowe67/ms-waterfall-maint
Browse files Browse the repository at this point in the history
Adjust waterfall settings to better visualize 2 Hz fading.
  • Loading branch information
tmiw authored Jul 31, 2023
2 parents 0985054 + 39a0aac commit e61e2e0
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 53 deletions.
9 changes: 7 additions & 2 deletions USER_MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -897,9 +897,14 @@ LDPC | Low Density Parity Check Codes - a family of powerful FEC codes
* Fix various screen reader accessibility issues. (PR #481)
* Use separate maximums for each slider type on the Filter dialog. (PR #485)
* Fix minor UI issues with the Easy Setup dialog. (PR #484)
2. Build system:
* Adjust waterfall settings to better visualize 2 Hz fading. (PR #487)
* Fix issue causing the waterfall to not scroll at the expected rate. (PR #487)
2. Enhancements
* Add ability to average spectrum plot across 1-3 samples. (PR #487)
* Adjust sizing of main page tabs for better readability. (PR #487)
3. Build system:
* Update Codec2 to v1.2.0. (PR #483)
3. Cleanup:
4. Cleanup:
* Remove 2400B mode from the UI. (PR #479)

## V1.8.12 July 2023
Expand Down
4 changes: 2 additions & 2 deletions src/defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@
#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.95 // constant for time averaging spectrum data
#define BETA 0.75 // constant for time averaging spectrum data
#define MIN_F_HZ 0 // min freq on Waterfall and Spectrum
#define MAX_F_HZ 3000 // max freq on Waterfall and Spectrum
#define STEP_F_HZ 500 // major (e.g. text legend) freq step on Waterfall and Spectrum graticule
#define STEP_MINOR_F_HZ 100 // minor (ticks) freq step on Waterfall and Spectrum graticule
#define WATERFALL_SECS_Y 30 // number of seconds represented 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 DT 0.10 // time between real time graphing updates
#define FS 8000 // FDMDV modem sample rate

// Scatter diagram
Expand Down
38 changes: 36 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,10 +587,39 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent, wxID_ANY, _("FreeDV ")
m_auiNbookCtrl->AddPage(m_panelWaterfall, _("Waterfall"), true, wxNullBitmap);

// Add Spectrum Plot window
m_panelSpectrum = new PlotSpectrum((wxFrame*) m_auiNbookCtrl, g_avmag,
wxPanel* spectrumPanel = new wxPanel(m_auiNbookCtrl);

wxFlexGridSizer* spectrumPanelSizer = new wxFlexGridSizer(2, 1, 5, 5);
wxBoxSizer* spectrumPanelControlSizer = new wxBoxSizer(wxHORIZONTAL);
spectrumPanelSizer->AddGrowableRow(0);
spectrumPanelSizer->AddGrowableCol(0);

// Actual Spectrum plot
m_panelSpectrum = new PlotSpectrum(spectrumPanel, g_avmag,
MODEM_STATS_NSPEC*((float)MAX_F_HZ/MODEM_STATS_MAX_F_HZ));
m_panelSpectrum->SetToolTip(_("Double-click to tune"));
m_auiNbookCtrl->AddPage(m_panelSpectrum, _("Spectrum"), true, wxNullBitmap);
spectrumPanelSizer->Add(m_panelSpectrum, 0, wxALL | wxEXPAND, 5);

// Spectrum plot control interface
wxStaticText* labelAveraging = new wxStaticText(spectrumPanel, wxID_ANY, wxT("Average across"), wxDefaultPosition, wxDefaultSize, 0);
spectrumPanelControlSizer->Add(labelAveraging, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);

wxString samplingChoices[] = {
"1",
"2",
"3"
};
m_cbxNumSpectrumAveraging = new wxComboBox(spectrumPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 3, samplingChoices, wxCB_DROPDOWN | wxCB_READONLY);
m_cbxNumSpectrumAveraging->SetSelection(0);
spectrumPanelControlSizer->Add(m_cbxNumSpectrumAveraging, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);

wxStaticText* labelSamples = new wxStaticText(spectrumPanel, wxID_ANY, wxT("sample(s)"), wxDefaultPosition, wxDefaultSize, 0);
spectrumPanelControlSizer->Add(labelSamples, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);

spectrumPanelSizer->Add(spectrumPanelControlSizer, 0, wxALL | wxEXPAND, 5);
spectrumPanel->SetSizerAndFit(spectrumPanelSizer);

m_auiNbookCtrl->AddPage(spectrumPanel, _("Spectrum"), true, wxNullBitmap);

// Add Scatter Plot window
m_panelScatter = new PlotScatter((wxFrame*) m_auiNbookCtrl);
Expand Down Expand Up @@ -941,6 +970,11 @@ void MainFrame::OnTimer(wxTimerEvent &evt)
}

m_panelSpectrum->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz);

// Note: each element in this combo box is a numeric value starting from 1,
// so just incrementing the selected index should get us the correct results.
m_panelSpectrum->setNumAveraging(m_cbxNumSpectrumAveraging->GetSelection() + 1);

m_panelSpectrum->m_newdata = true;
m_panelSpectrum->Refresh();

Expand Down
1 change: 1 addition & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ class MainFrame : public TopFrame
PlotScalar* m_panelDemodIn;
PlotScalar* m_panelTestFrameErrors;
PlotScalar* m_panelTestFrameErrorsHist;
wxComboBox* m_cbxNumSpectrumAveraging;

bool m_RxRunning;

Expand Down
39 changes: 38 additions & 1 deletion src/plot_spectrum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ PlotSpectrum::PlotSpectrum(wxWindow* parent, float *magdB, int n_magdB,
m_newdata = false;
m_firstPass = true;
m_line_color = 0;
m_numSampleAveraging = 1;
SetLabelSize(10.0);

m_magdB = magdB;
Expand All @@ -62,13 +63,30 @@ PlotSpectrum::PlotSpectrum(wxWindow* parent, float *magdB, int n_magdB,
m_min_mag_db = min_mag_db;
m_rxFreq = 0.0;
m_clickTune = clickTune;

m_prevMagDB = new float[n_magdB];
assert(m_prevMagDB != nullptr);

m_nextPrevMagDB = new float[n_magdB];
assert(m_nextPrevMagDB != nullptr);

for (int index = 0; index < n_magdB; index++)
{
m_prevMagDB[index] = 0;
m_nextPrevMagDB[index] = 0;
}
}

//----------------------------------------------------------------
// ~PlotSpectrum()
//----------------------------------------------------------------
PlotSpectrum::~PlotSpectrum()
{
delete[] m_prevMagDB;
m_prevMagDB = nullptr;

delete[] m_nextPrevMagDB;
m_nextPrevMagDB = nullptr;
}

//----------------------------------------------------------------
Expand Down Expand Up @@ -126,7 +144,26 @@ void PlotSpectrum::draw(wxGraphicsContext* ctx)
for(index = 0; index < m_n_magdB; index++)
{
x = index*index_to_px;
mag = m_magdB[index];

switch(m_numSampleAveraging)
{
case 1:
mag = m_magdB[index];
break;
case 2:
mag = (m_magdB[index] + m_prevMagDB[index]) / 2;
break;
case 3:
mag = (m_magdB[index] + m_prevMagDB[index] + m_nextPrevMagDB[index]) / 3;
break;
default:
assert(0);
break;
}

m_nextPrevMagDB[index] = m_prevMagDB[index];
m_prevMagDB[index] = m_magdB[index];

if (mag > m_max_mag_db) mag = m_max_mag_db;
if (mag < m_min_mag_db) mag = m_min_mag_db;
y = -(mag - m_max_mag_db) * mag_dB_to_py;
Expand Down
5 changes: 5 additions & 0 deletions src/plot_spectrum.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class PlotSpectrum : public PlotPanel
void setRxFreq(float rxFreq) { m_rxFreq = rxFreq; }
void setFreqScale(int n_magdB) { m_n_magdB = n_magdB; }

void setNumAveraging(int n) { m_numSampleAveraging = n; }

protected:
void OnSize(wxSizeEvent& event);
void OnShow(wxShowEvent& event);
Expand All @@ -49,8 +51,11 @@ class PlotSpectrum : public PlotPanel
float m_max_mag_db;
float m_min_mag_db;
float *m_magdB;
float *m_prevMagDB;
float *m_nextPrevMagDB;
int m_n_magdB;
bool m_clickTune;
int m_numSampleAveraging;

void OnDoubleClickCommon(wxMouseEvent& event);

Expand Down
87 changes: 42 additions & 45 deletions src/plot_waterfall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include "main.h"
#include "osx_interface.h"

// Tweak accordingly
#define Y_PER_SECOND (30)

extern float g_avmag[]; // av mag spec passed in to draw()
void clickTune(float frequency); // callback to pass new click freq

Expand Down Expand Up @@ -236,7 +239,7 @@ void PlotWaterfall::drawGraticule(wxGraphicsContext* ctx)
int x, y, text_w, text_h;
char buf[STR_LENGTH];
wxString s;
float f, time, freq_hz_to_px, time_s_to_py;
float f, time, freq_hz_to_px;

wxBrush ltGraphBkgBrush;
wxColour foregroundColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
Expand All @@ -249,16 +252,15 @@ void PlotWaterfall::drawGraticule(wxGraphicsContext* ctx)
ctx->SetFont(tmpFont);

freq_hz_to_px = (float)m_imgWidth/(MAX_F_HZ-MIN_F_HZ);
time_s_to_py = (float)m_imgHeight/WATERFALL_SECS_Y;

// upper LH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET, PLOT_BORDER)
// lower RH coords of plot area are (PLOT_BORDER + XLEFT_OFFSET + m_rGrid.GetWidth(),
// PLOT_BORDER + m_rGrid.GetHeight())

// Check if small screen size means text will overlap

int textXStep = STEP_F_HZ*freq_hz_to_px;
int textYStep = WATERFALL_SECS_STEP*time_s_to_py;
int textXStep = STEP_F_HZ * freq_hz_to_px;
int textYStep = WATERFALL_SECS_STEP * Y_PER_SECOND;
snprintf(buf, STR_LENGTH, "%4.0fHz", (float)MAX_F_HZ - STEP_F_HZ);
GetTextExtent(buf, &text_w, &text_h);
int overlappedText = (text_w > textXStep) || (text_h > textYStep);
Expand Down Expand Up @@ -290,10 +292,10 @@ void PlotWaterfall::drawGraticule(wxGraphicsContext* ctx)

// Horizontal gridlines
ctx->SetPen(m_penDotDash);
for(time=0; time<=WATERFALL_SECS_Y; time+=WATERFALL_SECS_STEP) {
y = m_rGrid.GetHeight() - (WATERFALL_SECS_Y - time)*time_s_to_py;
y += PLOT_BORDER + YBOTTOM_TEXT_OFFSET;

for(y = PLOT_BORDER + YBOTTOM_TEXT_OFFSET, time=0;
y < m_rGrid.GetHeight();
time += WATERFALL_SECS_STEP, y += Y_PER_SECOND * WATERFALL_SECS_STEP)
{
if (m_graticule)
ctx->StrokeLine(PLOT_BORDER + XLEFT_OFFSET, y,
(m_rGrid.GetWidth() + PLOT_BORDER + XLEFT_OFFSET), y);
Expand All @@ -315,13 +317,11 @@ void PlotWaterfall::drawGraticule(wxGraphicsContext* ctx)
//-------------------------------------------------------------------------
void PlotWaterfall::plotPixelData()
{
float spec_index_per_px;
float intensity_per_dB;
float px_per_sec;
int index;
int dy;
int px;
int py;
int intensity;

/*
Expand All @@ -336,7 +336,7 @@ void PlotWaterfall::plotPixelData()
*/

// determine dy, the height of one "block"
px_per_sec = (float)m_imgHeight / WATERFALL_SECS_Y;
px_per_sec = Y_PER_SECOND;
dy = m_dT * px_per_sec;

// update min and max amplitude estimates
Expand All @@ -356,44 +356,41 @@ void PlotWaterfall::plotPixelData()
m_max_mag = BETA*m_max_mag + (1 - BETA)*max_mag;
m_min_mag = max_mag - 20.0;
intensity_per_dB = (float)256 /(m_max_mag - m_min_mag);
spec_index_per_px = ((float)(MAX_F_HZ)/(float)m_modem_stats_max_f_hz)*(float)MODEM_STATS_NSPEC / (float)m_imgWidth;

// Draw last line of blocks using latest amplitude data ------------------
unsigned char dyImageData[3 * dy * m_imgWidth];
for(py = dy - 1; py >= 0; py--)
int baseRowWidthPixels = ((float)MODEM_STATS_NSPEC / (float)m_modem_stats_max_f_hz) * MAX_F_HZ;
unsigned char dyImageData[3 * baseRowWidthPixels];
for(px = 0; px < baseRowWidthPixels; px++)
{
for(px = 0; px < m_imgWidth; px++)
{
index = px * spec_index_per_px;
assert(index < MODEM_STATS_NSPEC);
index = px;
assert(index < MODEM_STATS_NSPEC);

intensity = intensity_per_dB * (g_avmag[index] - m_min_mag);
if(intensity > 255) intensity = 255;
if (intensity < 0) intensity = 0;
intensity = intensity_per_dB * (g_avmag[index] - m_min_mag);
if(intensity > 255) intensity = 255;
if (intensity < 0) intensity = 0;

int pixelPos = (py * m_imgWidth * 3) + (px * 3);
int pixelPos = (px * 3);

switch (m_colour) {
case 0:
dyImageData[pixelPos] = m_heatmap_lut[intensity] & 0xff;
dyImageData[pixelPos + 1] = (m_heatmap_lut[intensity] >> 8) & 0xff;
dyImageData[pixelPos + 2] = (m_heatmap_lut[intensity] >> 16) & 0xff;
break;
case 1:
dyImageData[pixelPos] = intensity;
dyImageData[pixelPos + 1] = intensity;
dyImageData[pixelPos + 2] = intensity;
break;
case 2:
dyImageData[pixelPos] = intensity;
dyImageData[pixelPos + 1] = intensity;
if (intensity < 127)
dyImageData[pixelPos + 2] = intensity*2;
else
dyImageData[pixelPos + 2] = 255;

break;
}
switch (m_colour) {
case 0:
dyImageData[pixelPos] = m_heatmap_lut[intensity] & 0xff;
dyImageData[pixelPos + 1] = (m_heatmap_lut[intensity] >> 8) & 0xff;
dyImageData[pixelPos + 2] = (m_heatmap_lut[intensity] >> 16) & 0xff;
break;
case 1:
dyImageData[pixelPos] = intensity;
dyImageData[pixelPos + 1] = intensity;
dyImageData[pixelPos + 2] = intensity;
break;
case 2:
dyImageData[pixelPos] = intensity;
dyImageData[pixelPos + 1] = intensity;
if (intensity < 127)
dyImageData[pixelPos + 2] = intensity*2;
else
dyImageData[pixelPos + 2] = 255;

break;
}
}

Expand All @@ -403,15 +400,15 @@ void PlotWaterfall::plotPixelData()

if (dy > 0)
{
wxImage* tmpImage = new wxImage(m_imgWidth, dy, (unsigned char*)&dyImageData, true);
wxImage* tmpImage = new wxImage(baseRowWidthPixels, 1, (unsigned char*)&dyImageData, true);
wxBitmap* tmpBmp = new wxBitmap(*tmpImage);
{
wxMemoryDC fullBmpSourceDC(*m_fullBmp);
wxMemoryDC fullBmpDestDC(*m_fullBmp);
wxMemoryDC tmpBmpSourceDC(*tmpBmp);

fullBmpDestDC.Blit(0, dy, m_imgWidth, m_imgHeight - dy, &fullBmpSourceDC, 0, 0);
fullBmpDestDC.Blit(0, 0, m_imgWidth, dy, &tmpBmpSourceDC, 0, 0);
fullBmpDestDC.StretchBlit(0, 0, m_imgWidth, dy, &tmpBmpSourceDC, 0, 0, baseRowWidthPixels, 1);
}
delete tmpBmp;
delete tmpImage;
Expand Down
1 change: 0 additions & 1 deletion src/topFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,6 @@ TopFrame::TopFrame(wxWindow* parent, wxWindowID id, const wxString& title, const
long nb_style = wxAUI_NB_BOTTOM | wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS;
m_auiNbookCtrl = new TabFreeAuiNotebook(m_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, nb_style);
// This line sets the fontsize for the tabs on the notebook control
m_auiNbookCtrl->SetFont(wxFont(8, 70, 90, 90, false, wxEmptyString));
m_auiNbookCtrl->SetMinSize(wxSize(375,375));

upperSizer->Add(m_auiNbookCtrl, 1, wxALIGN_TOP|wxEXPAND, 1);
Expand Down

0 comments on commit e61e2e0

Please sign in to comment.