Add green frequency indicator for RADE. (#1283)

* Add green frequency indicator for RADE.

* Fix divide by zero issue for RX frequency on the spectrum plot.

* Always show tuning line on waterfall plot even if offset == 0.

* Move X axis of Spectrum plot to top.

* Add PR #1283 to changelog.

* Update old screenshot from user manual.

* Remove unused screenshot.
ms-claude-audio-dropouts
Mooneer Salem 2026-04-08 00:46:14 -07:00 committed by GitHub
parent 219dca67f5
commit 951a6239af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 39 additions and 27 deletions

View File

@ -907,7 +907,7 @@ LDPC | Low Density Parity Check Codes - a family of powerful FEC codes
* Add ability to hide self from FreeDV Reporter list. (PR #1260, #1278)
* Add Tune button to allow tuning antennas/rigs. (PR #1259, #1265)
* Add SNR plot to main window. (PR #1250, #1261)
* Bring back RX frequency indicator for RADE. (PR #1265)
* Bring back RX frequency indicator for RADE. (PR #1265, #1283)
* Improve usability of attenuation control in the main window. (PR #1268) - thanks @barjac!
* Use RNNoise for improved noise canceling during TX. (PR #1276)
* FreeDV Reporter: Add ability to filter based on individual columns. (PR #1285)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -731,6 +731,19 @@ float FreeDVInterface::getSNREstimate()
}
}
float FreeDVInterface::getCurrentRxModemOffset()
{
if (txMode_ >= FREEDV_MODE_RADE)
{
// Special handling for RADE
return rade_freq_offset(rade_);
}
else
{
return getCurrentRxModemStats()->foff;
}
}
IPipelineStep* FreeDVInterface::createTransmitPipeline(
int inputSampleRate,
int outputSampleRate,

View File

@ -122,6 +122,8 @@ public:
void setSquelch(bool enable, float level) FREEDV_NONBLOCKING;
void setCarrierAmplitude(int c, float amp);
float getCurrentRxModemOffset();
struct MODEM_STATS* getCurrentRxModemStats() { return &modemStatsList_[modemStatsIndex_]; }

View File

@ -148,7 +148,7 @@ void PlotSpectrum::draw(wxGraphicsContext* ctx, bool repaintDataOnly)
wxBrush ltGraphBkgBrush = wxBrush(BLACK_COLOR);
ctx->SetBrush(ltGraphBkgBrush);
ctx->SetPen(wxPen(BLACK_COLOR, 0));
ctx->DrawRectangle(PLOT_BORDER + leftOffset_, PLOT_BORDER, m_rGrid.GetWidth(), m_rGrid.GetHeight());
ctx->DrawRectangle(PLOT_BORDER + leftOffset_, PLOT_BORDER + bottomOffset_, m_rGrid.GetWidth(), m_rGrid.GetHeight());
// draw spectrum
@ -200,7 +200,7 @@ void PlotSpectrum::draw(wxGraphicsContext* ctx, bool repaintDataOnly)
y = -(mag - m_max_mag_db) * mag_dB_to_py;
x += PLOT_BORDER + leftOffset_;
y += PLOT_BORDER;
y += PLOT_BORDER + bottomOffset_;
if (index && (int)abs(x - prev_x) >= (int)(HZ_GRANULARITY*freq_hz_to_px))
{
@ -265,14 +265,14 @@ void PlotSpectrum::drawGraticuleFast(wxGraphicsContext* ctx, bool repaintDataOnl
x += PLOT_BORDER + leftOffset_;
ctx->SetPen(m_penShortDash);
ctx->StrokeLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, PLOT_BORDER);
ctx->StrokeLine(x, m_rGrid.GetHeight() + PLOT_BORDER + bottomOffset_, x, PLOT_BORDER + bottomOffset_);
if (!repaintDataOnly)
{
snprintf(buf, STR_LENGTH, "%4.0fHz", f);
GetTextExtent(buf, &text_w, &text_h);
if (!overlappedText)
ctx->DrawText(buf, x - text_w/2, m_rGrid.GetHeight() + PLOT_BORDER);
ctx->DrawText(buf, x - text_w/2, PLOT_BORDER);
}
}
@ -281,7 +281,7 @@ void PlotSpectrum::drawGraticuleFast(wxGraphicsContext* ctx, bool repaintDataOnl
{
x = f*freq_hz_to_px;
x += PLOT_BORDER + leftOffset_;
ctx->StrokeLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, m_rGrid.GetHeight() + PLOT_BORDER + YBOTTOM_TEXT_OFFSET-5);
ctx->StrokeLine(x, PLOT_BORDER + bottomOffset_, x, PLOT_BORDER + bottomOffset_ - YBOTTOM_TEXT_OFFSET);
}
// Horizontal gridlines
@ -289,7 +289,7 @@ void PlotSpectrum::drawGraticuleFast(wxGraphicsContext* ctx, bool repaintDataOnl
ctx->SetPen(m_penDotDash);
for(mag=m_min_mag_db; mag<=m_max_mag_db; mag+=STEP_MAG_DB) {
y = -(mag - m_max_mag_db) * mag_dB_to_py;
y += PLOT_BORDER;
y += PLOT_BORDER + bottomOffset_;
ctx->StrokeLine(PLOT_BORDER + leftOffset_, y,
(m_rGrid.GetWidth() + PLOT_BORDER + leftOffset_), y);
if (!repaintDataOnly)
@ -314,25 +314,26 @@ void PlotSpectrum::drawGraticuleFast(wxGraphicsContext* ctx, bool repaintDataOnl
if (!repaintDataOnly)
{
if (m_rxFreq != 0.0) {
float verticalBarLength = m_rCtrl.GetHeight() - (m_rGrid.GetHeight()+ PLOT_BORDER);
float verticalBarLength = PLOT_BORDER + YBOTTOM_TEXT_OFFSET + 5;
float sum = 0.0;
for (auto& f : rxOffsets_)
{
sum += f;
}
float averageOffset = sum / rxOffsets_.size();
float averageOffset = rxOffsets_.size() == 0 ? 0 : sum / rxOffsets_.size();
// get average offset and draw sync tuning line
ctx->SetPen(wxPen(sync_ ? GREEN_COLOR : ORANGE_COLOR, 3));
x = (m_rxFreq + averageOffset) * freq_hz_to_px;
x += PLOT_BORDER + leftOffset_;
ctx->StrokeLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, m_rCtrl.GetHeight());
ctx->StrokeLine(x, 0, x, PLOT_BORDER + bottomOffset_);
// red rx tuning line
ctx->SetPen(wxPen(RED_COLOR, 3));
x = m_rxFreq*freq_hz_to_px;
x += PLOT_BORDER + leftOffset_;
ctx->StrokeLine(x, m_rGrid.GetHeight() + PLOT_BORDER, x, m_rCtrl.GetHeight() - verticalBarLength / 3);
ctx->StrokeLine(x, 0, x, 2 * verticalBarLength / 3);
}
}
}

View File

@ -364,17 +364,14 @@ void PlotWaterfall::drawGraticule(wxGraphicsContext* ctx)
{
sum += f;
}
float averageOffset = rxOffsets_.size() == 0 ? -1 : sum / rxOffsets_.size();
float averageOffset = rxOffsets_.size() == 0 ? 0 : sum / rxOffsets_.size();
if (m_rxFreq != 0.0) {
// get average offset and draw sync tuning line
if (averageOffset > 0)
{
ctx->SetPen(wxPen(sync_ ? GREEN_COLOR : ORANGE_COLOR, 3));
x = (m_rxFreq + averageOffset) * freq_hz_to_px;
x += PLOT_BORDER + leftOffset_;
ctx->StrokeLine(x, 0, x, verticalBarLength);
}
ctx->SetPen(wxPen(sync_ ? GREEN_COLOR : ORANGE_COLOR, 3));
x = (m_rxFreq + averageOffset) * freq_hz_to_px;
x += PLOT_BORDER + leftOffset_;
ctx->StrokeLine(x, 0, x, verticalBarLength);
// red rx tuning line
ctx->SetPen(wxPen(RED_COLOR, 3));

View File

@ -1517,9 +1517,9 @@ void MainFrame::OnTimer(wxTimerEvent &evt)
m_panelWaterfall->setRxFreq(FDMDV_FCENTRE - g_RxFreqOffsetHz);
m_panelWaterfall->m_newdata = true;
m_panelWaterfall->setColor(wxGetApp().appConfiguration.waterfallColor);
m_panelWaterfall->addOffset(freedvInterface.getCurrentRxModemStats()->foff);
m_panelWaterfall->addOffset(freedvInterface.getCurrentRxModemOffset());
m_panelWaterfall->setSync(syncState ? true : false);
m_panelWaterfall->refreshData();
m_panelWaterfall->Refresh();
}
}
else if (timerId == ID_TIMER_SPECTRUM)
@ -1529,10 +1529,10 @@ void MainFrame::OnTimer(wxTimerEvent &evt)
// 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(wxGetApp().appConfiguration.currentSpectrumAveraging + 1);
m_panelSpectrum->addOffset(freedvInterface.getCurrentRxModemStats()->foff);
m_panelSpectrum->addOffset(freedvInterface.getCurrentRxModemOffset());
m_panelSpectrum->setSync(syncState ? true : false);
m_panelSpectrum->m_newdata = true;
m_panelSpectrum->refreshData();
m_panelSpectrum->Refresh();
}
else if (timerId == ID_TIMER_SPEECH_IN)
{
@ -1963,12 +1963,14 @@ void MainFrame::OnTimer(wxTimerEvent &evt)
realigned_ = true;
}
wxString freqOffset = wxString::Format(FRQ_OFF_FMT, freedvInterface.getCurrentRxModemOffset());
m_textFreqOffset->SetLabel(freqOffset);
if (g_mode == FREEDV_MODE_RADE)
{
m_textBits->SetLabel(BITS_UNK_LABEL);
m_textErrors->SetLabel(ERRS_UNK_LABEL);
m_textBER->SetLabel(BER_UNK_LABEL);
m_textFreqOffset->SetLabel(FRQ_OFF_UNK_LABEL);
m_textSyncMetric->SetLabel(SYNC_UNK_LABEL);
m_textCodec2Var->SetLabel(VAR_UNK_LABEL);
}
@ -1987,9 +1989,6 @@ void MainFrame::OnTimer(wxTimerEvent &evt)
wxString resyncs = wxString::Format(RESYNC_FMT, g_resyncs);
m_textResyncs->SetLabel(resyncs);
wxString freqOffset = wxString::Format(FRQ_OFF_FMT, freedvInterface.getCurrentRxModemStats()->foff);
m_textFreqOffset->SetLabel(freqOffset);
wxString syncMetric = wxString::Format(SYNC_FMT, freedvInterface.getCurrentRxModemStats()->sync_metric);
m_textSyncMetric->SetLabel(syncMetric);