freedv-gui/src/ongui.cpp

1376 lines
48 KiB
C++

/*
ongui.cpp
The simpler GUI event handlers.
*/
#include <sstream>
#include <iomanip>
#include <locale>
#include "main.h"
#if !defined(LPCNET_DISABLED)
#include "lpcnet_freedv.h"
#endif // defined(LPCNET_DISABLED)
#include "gui/dialogs/dlg_easy_setup.h"
#include "gui/dialogs/dlg_filter.h"
#include "gui/dialogs/dlg_audiooptions.h"
#include "gui/dialogs/dlg_options.h"
#include "gui/dialogs/dlg_ptt.h"
#include "gui/dialogs/freedv_reporter.h"
#include "gui/dialogs/monitor_volume_adj.h"
#if defined(WIN32)
#include "rig_control/omnirig/OmniRigController.h"
#endif // defined(WIN32)
#include "codec2_fdmdv.h" // for FDMDV_FCENTRE
extern int g_mode;
extern int g_SquelchActive;
extern float g_SquelchLevel;
extern int g_analog;
extern int g_tx;
extern int g_State, g_prev_State;
extern FreeDVInterface freedvInterface;
extern bool g_queueResync;
extern short *g_error_hist, *g_error_histn;
extern int g_resyncs;
extern int g_Nc;
extern int g_txLevel;
extern wxConfigBase *pConfig;
extern bool endingTx;
extern int g_outfifo1_empty;
extern bool g_voice_keyer_tx;
extern paCallBackData* g_rxUserdata;
extern SNDFILE *g_sfRecFileFromModulator;
extern SNDFILE *g_sfRecFile;
extern bool g_recFileFromModulator;
extern bool g_recFileFromRadio;
extern SNDFILE *g_sfRecMicFile;
extern wxMutex g_mutexProtectingCallbackData;
bool g_eoo_enqueued;
void clickTune(float frequency); // callback to pass new click freq
//-------------------------------------------------------------------------
// Forces redraw of main panels on window resize.
//-------------------------------------------------------------------------
void MainFrame::topFrame_OnSize( wxSizeEvent& event )
{
m_auiNbookCtrl->Refresh();
TopFrame::topFrame_OnSize(event);
}
//-------------------------------------------------------------------------
// OnExitClick()
//-------------------------------------------------------------------------
void MainFrame::OnExitClick(wxCommandEvent& event)
{
OnExit(event);
}
//-------------------------------------------------------------------------
// OnToolsEasySetup()
//-------------------------------------------------------------------------
void MainFrame::OnToolsEasySetup(wxCommandEvent& event)
{
EasySetupDialog* dlg = new EasySetupDialog(this);
if (dlg->ShowModal() == wxOK)
{
// Show/hide frequency box based on PSK Reporter status.
m_freqBox->Show(wxGetApp().appConfiguration.reportingConfiguration.reportingEnabled);
// Show/hide callsign combo box based on PSK Reporter Status
if (wxGetApp().appConfiguration.reportingConfiguration.reportingEnabled)
{
m_cboLastReportedCallsigns->Show();
m_txtCtrlCallSign->Hide();
}
else
{
m_cboLastReportedCallsigns->Hide();
m_txtCtrlCallSign->Show();
}
// Initialize FreeDV Reporter if required.
initializeFreeDVReporter_();
// Relayout window so that the changes can take effect.
m_panel->Layout();
}
}
//-------------------------------------------------------------------------
// OnToolsEasySetupUI()
//-------------------------------------------------------------------------
void MainFrame::OnToolsEasySetupUI(wxUpdateUIEvent& event)
{
event.Enable(!m_RxRunning);
}
//-------------------------------------------------------------------------
// OnToolsFreeDVReporter()
//-------------------------------------------------------------------------
void MainFrame::OnToolsFreeDVReporter(wxCommandEvent& event)
{
if (m_reporterDialog == nullptr)
{
m_reporterDialog = new FreeDVReporterDialog(this);
}
m_reporterDialog->refreshLayout();
m_reporterDialog->Show();
m_reporterDialog->Iconize(false); // undo minimize if required
m_reporterDialog->Raise(); // brings from background to foreground if required
}
//-------------------------------------------------------------------------
// OnToolsFreeDVReporterUI()
//-------------------------------------------------------------------------
void MainFrame::OnToolsFreeDVReporterUI(wxUpdateUIEvent& event)
{
event.Enable(wxGetApp().appConfiguration.reportingConfiguration.freedvReporterHostname->ToStdString() != "");
}
//-------------------------------------------------------------------------
// OnToolsAudio()
//-------------------------------------------------------------------------
void MainFrame::OnToolsAudio(wxCommandEvent& event)
{
bool oldRxOnly = g_nSoundCards <= 1 ? true : false;
wxUnusedVar(event);
int rv = 0;
AudioOptsDialog *dlg = new AudioOptsDialog(NULL);
rv = dlg->ShowModal();
if(rv == wxOK)
{
dlg->ExchangeData(EXCHANGE_DATA_OUT);
bool newRxOnly = g_nSoundCards <= 1 ? true : false;
if (oldRxOnly != newRxOnly &&
wxGetApp().m_sharedReporterObject->isValidForReporting())
{
// Receive Only status has changed, refresh FreeDV Reporter
initializeFreeDVReporter_();
}
}
delete dlg;
}
//-------------------------------------------------------------------------
// OnToolsAudioUI()
//-------------------------------------------------------------------------
void MainFrame::OnToolsAudioUI(wxUpdateUIEvent& event)
{
event.Enable(!m_RxRunning);
}
//-------------------------------------------------------------------------
// OnToolsFilter()
//-------------------------------------------------------------------------
void MainFrame::OnToolsFilter(wxCommandEvent& event)
{
wxUnusedVar(event);
if (m_filterDialog == nullptr)
{
m_filterDialog = new FilterDlg(NULL, m_RxRunning, &m_newMicInFilter, &m_newSpkOutFilter);
}
else
{
m_filterDialog->Iconize(false);
m_filterDialog->SetFocus();
m_filterDialog->Raise();
}
m_filterDialog->Show();
}
//-------------------------------------------------------------------------
// OnToolsOptions()
//-------------------------------------------------------------------------
void MainFrame::OnToolsOptions(wxCommandEvent& event)
{
bool oldFreqAsKHz = wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz;
wxUnusedVar(event);
if (optionsDlg->ShowModal() == wxOK)
{
// Update reporting list.
updateReportingFreqList_();
// Show/hide frequency box based on reporting status.
m_freqBox->Show(wxGetApp().appConfiguration.reportingConfiguration.reportingEnabled);
// Show/hide callsign combo box based on reporting Status
if (wxGetApp().appConfiguration.reportingConfiguration.reportingEnabled)
{
m_cboLastReportedCallsigns->Show();
m_txtCtrlCallSign->Hide();
}
else
{
m_cboLastReportedCallsigns->Hide();
m_txtCtrlCallSign->Show();
}
// Update voice keyer file if different
wxFileName fullVKPath(wxGetApp().appConfiguration.voiceKeyerWaveFilePath, wxGetApp().appConfiguration.voiceKeyerWaveFile);
if (vkFileName_ != fullVKPath.GetFullPath().mb_str())
{
// Clear filename to force reselection next time VK is triggered.
vkFileName_ = "";
wxGetApp().appConfiguration.voiceKeyerWaveFile = "";
setVoiceKeyerButtonLabel_("");
}
// Adjust frequency labels on main window
if (wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz)
{
m_freqBox->SetLabel(_("Report Freq. (kHz)"));
}
else
{
m_freqBox->SetLabel(_("Report Freq. (MHz)"));
}
// If the "Frequency as kHz" option has changed, update the frequencies
// in the main window's callsign list.
if (oldFreqAsKHz != wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz)
{
for (int index = 0; index < m_lastReportedCallsignListView->GetItemCount(); index++)
{
wxString newFreq = "";
wxString freq = m_lastReportedCallsignListView->GetItemText(index, 1);
double freqDouble = 0;
freq.ToDouble(&freqDouble);
if (wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz)
{
freqDouble *= 1000.0;
newFreq = wxString::Format("%.01f", freqDouble);
}
else
{
freqDouble /= 1000.0;
newFreq = wxString::Format("%.04f", freqDouble);
}
m_lastReportedCallsignListView->SetItem(index, 1, newFreq);
}
}
// Initialize FreeDV Reporter if required.
initializeFreeDVReporter_();
// Refresh distance column label in case setting was changed.
if (m_reporterDialog != nullptr)
{
m_reporterDialog->refreshLayout();
}
// Relayout window so that the changes can take effect.
m_panel->Layout();
}
}
//-------------------------------------------------------------------------
// OnToolsOptionsUI()
//-------------------------------------------------------------------------
void MainFrame::OnToolsOptionsUI(wxUpdateUIEvent& event)
{
}
//-------------------------------------------------------------------------
// OnToolsComCfg()
//-------------------------------------------------------------------------
void MainFrame::OnToolsComCfg(wxCommandEvent& event)
{
wxUnusedVar(event);
ComPortsDlg *dlg = new ComPortsDlg(NULL);
if (dlg->ShowModal() == wxID_OK)
{
// Reinitialize FreeDV Reporter again in case we changed PTT method.
initializeFreeDVReporter_();
}
delete dlg;
}
//-------------------------------------------------------------------------
// OnToolsComCfgUI()
//-------------------------------------------------------------------------
void MainFrame::OnToolsComCfgUI(wxUpdateUIEvent& event)
{
event.Enable(!m_RxRunning);
}
//-------------------------------------------------------------------------
// OnHelpCheckUpdates()
//-------------------------------------------------------------------------
void MainFrame::OnHelpCheckUpdates(wxCommandEvent& event)
{
wxLaunchDefaultBrowser("https://github.com/drowe67/freedv-gui/releases");
}
//-------------------------------------------------------------------------
// OnHelpCheckUpdatesUI()
//-------------------------------------------------------------------------
void MainFrame::OnHelpCheckUpdatesUI(wxUpdateUIEvent& event)
{
event.Enable(!m_RxRunning);
}
//-------------------------------------------------------------------------
// OnHelpManual()
//-------------------------------------------------------------------------
void MainFrame::OnHelpManual( wxCommandEvent& event )
{
wxLaunchDefaultBrowser("https://github.com/drowe67/freedv-gui/blob/master/USER_MANUAL.pdf");
}
//-------------------------------------------------------------------------
// OnHelp()
//-------------------------------------------------------------------------
void MainFrame::OnHelp( wxCommandEvent& event )
{
wxLaunchDefaultBrowser("https://freedv.org/#gethelp");
}
//-------------------------------------------------------------------------
//OnHelpAbout()
//-------------------------------------------------------------------------
void MainFrame::OnHelpAbout(wxCommandEvent& event)
{
wxUnusedVar(event);
wxString msg;
msg.Printf( wxT("FreeDV GUI %s\n\n")
wxT("For Help and Support visit: http://freedv.org\n\n")
wxT("GNU Public License V2.1\n\n")
wxT("Created by David Witten KD0EAG and David Rowe VK5DGR (2012). ")
wxT("Currently maintained by Mooneer Salem K6AQ and David Rowe VK5DGR.\n\n")
wxT("freedv-gui version: %s\n")
wxT("freedv-gui git hash: %s\n")
wxT("codec2 git hash: %s\n")
#if !defined(LPCNET_DISABLED)
wxT("lpcnet git hash: %s\n")
#endif // !defined(LPCNET_DISABLED)
, FREEDV_VERSION, FREEDV_VERSION, GIT_HASH, freedv_get_hash()
#if !defined(LPCNET_DISABLED)
, lpcnet_get_hash()
#endif // !defined(LPCNET_DISABLED)
);
wxMessageBox(msg, wxT("About"), wxOK | wxICON_INFORMATION, this);
}
// Attempt to talk to rig using Hamlib
bool MainFrame::OpenHamlibRig() {
if (wxGetApp().appConfiguration.rigControlConfiguration.hamlibUseForPTT != true)
return false;
if (wxGetApp().m_intHamlibRig == 0)
return false;
int rig = wxGetApp().m_intHamlibRig;
wxString port = wxGetApp().appConfiguration.rigControlConfiguration.hamlibSerialPort;
wxString pttPort = wxGetApp().appConfiguration.rigControlConfiguration.hamlibPttSerialPort;
auto pttType = (HamlibRigController::PttType)wxGetApp().appConfiguration.rigControlConfiguration.hamlibPTTType.get();
int serial_rate = wxGetApp().appConfiguration.rigControlConfiguration.hamlibSerialRate;
if (wxGetApp().CanAccessSerialPort((const char*)port.ToUTF8()) && (
pttType == HamlibRigController::PTT_VIA_CAT || pttType == HamlibRigController::PTT_VIA_NONE || wxGetApp().CanAccessSerialPort((const char*)pttPort.ToUTF8())))
{
auto tmp = std::make_shared<HamlibRigController>(
rig, (const char*)port.mb_str(wxConvUTF8), serial_rate, wxGetApp().appConfiguration.rigControlConfiguration.hamlibIcomCIVAddress,
pttType, pttType == HamlibRigController::PTT_VIA_CAT || pttType == HamlibRigController::PTT_VIA_NONE ? (const char*)port.mb_str(wxConvUTF8) : (const char*)pttPort.mb_str(wxConvUTF8),
(wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqModeChanges || wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqChangesOnly),
wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqChangesOnly);
// Hamlib also controls PTT.
firstFreqUpdateOnConnect_ = false;
wxGetApp().rigFrequencyController = tmp;
wxGetApp().rigPttController = tmp;
wxGetApp().rigFrequencyController->onRigError += [this](IRigController*, std::string err)
{
std::string fullErr = "Couldn't connect to Radio with hamlib: " + err;
CallAfter([&, fullErr]() {
wxMessageBox(fullErr, wxT("Error"), wxOK | wxICON_ERROR, this);
});
};
wxGetApp().rigFrequencyController->onRigConnected += [&](IRigController*) {
if (wxGetApp().rigFrequencyController &&
(wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqModeChanges || wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqChangesOnly) &&
wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency > 0)
{
// Suppress the frequency update message that will occur immediately after
// connect; this will prevent overwriting of whatever's in the text box.
firstFreqUpdateOnConnect_ = true;
// Set frequency/mode to the one pre-selected by the user before start.
wxGetApp().rigFrequencyController->setFrequency(wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency);
if (wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqModeChanges)
{
wxGetApp().rigFrequencyController->setMode(getCurrentMode_());
}
}
};
wxGetApp().rigFrequencyController->onRigDisconnected += [&](IRigController*) {
CallAfter([&]() {
m_txtModeStatus->SetLabel(wxT("unk"));
m_txtModeStatus->Enable(false);
});
};
wxGetApp().rigFrequencyController->onFreqModeChange += [&](IRigFrequencyController*, uint64_t freq, IRigFrequencyController::Mode mode)
{
CallAfter([&, mode, freq]() {
if (firstFreqUpdateOnConnect_)
{
firstFreqUpdateOnConnect_ = false;
return;
}
// Update string value.
switch(mode)
{
case IRigFrequencyController::USB:
m_txtModeStatus->SetLabel(wxT("USB"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::DIGU:
m_txtModeStatus->SetLabel(wxT("USB-D"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::LSB:
m_txtModeStatus->SetLabel(wxT("LSB"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::DIGL:
m_txtModeStatus->SetLabel(wxT("LSB-D"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::FM:
m_txtModeStatus->SetLabel(wxT("FM"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::DIGFM:
m_txtModeStatus->SetLabel(wxT("FM-D"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::AM:
m_txtModeStatus->SetLabel(wxT("AM"));
m_txtModeStatus->Enable(true);
break;
default:
m_txtModeStatus->SetLabel(wxT("unk"));
m_txtModeStatus->Enable(false);
break;
}
// Widest 60 meter allocation is 5.250-5.450 MHz per https://en.wikipedia.org/wiki/60-meter_band.
bool is60MeterBand = freq >= 5250000 && freq <= 5450000;
// Update color based on the mode and current frequency.
bool isUsbFreq = freq >= 10000000 || is60MeterBand;
bool isLsbFreq = freq < 10000000 && !is60MeterBand;
bool isMatchingMode =
(isUsbFreq && (mode == IRigFrequencyController::USB || mode == IRigFrequencyController::DIGU)) ||
(isLsbFreq && (mode == IRigFrequencyController::LSB || mode == IRigFrequencyController::DIGL));
if (isMatchingMode)
{
m_txtModeStatus->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
}
else
{
m_txtModeStatus->SetForegroundColour(wxColor(*wxRED));
}
// Update frequency box
if (!suppressFreqModeUpdates_ && (
!wxGetApp().appConfiguration.reportingConfiguration.reportingEnabled ||
!wxGetApp().appConfiguration.reportingConfiguration.manualFrequencyReporting))
{
// wxString::Format() doesn't respect locale but C++ iomanip should. Use the latter instead.
std::stringstream ss;
std::locale loc("");
ss.imbue(loc);
if (wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz)
{
ss << std::fixed << std::setprecision(1) << (freq / 1000.0);
}
else
{
ss << std::fixed << std::setprecision(4) << (freq / 1000.0 / 1000.0);
}
m_cboReportFrequency->SetValue(ss.str());
}
m_txtModeStatus->Refresh();
});
};
wxGetApp().rigFrequencyController->connect();
return true;
}
else
{
return false;
}
}
#if defined(WIN32)
// TBD -- a lot of this can be combined with the Hamlib logic above.
void MainFrame::OpenOmniRig()
{
auto tmp = std::make_shared<OmniRigController>(
wxGetApp().appConfiguration.rigControlConfiguration.omniRigRigId,
(wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqModeChanges || wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqChangesOnly),
wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqChangesOnly);
// OmniRig also controls PTT.
wxGetApp().rigFrequencyController = tmp;
if (!wxGetApp().rigPttController)
{
wxGetApp().rigPttController = tmp;
}
wxGetApp().rigFrequencyController->onRigError += [this](IRigController*, std::string err)
{
std::string fullErr = "Couldn't connect to Radio with OmniRig: " + err;
CallAfter([&, fullErr]() {
wxMessageBox(fullErr, wxT("Error"), wxOK | wxICON_ERROR, this);
});
};
wxGetApp().rigFrequencyController->onRigConnected += [&](IRigController*) {
if (wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqModeChanges || wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqChangesOnly)
{
wxGetApp().rigFrequencyController->setFrequency(wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency);
if (wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqModeChanges)
{
wxGetApp().rigFrequencyController->setMode(getCurrentMode_());
}
}
};
wxGetApp().rigFrequencyController->onRigDisconnected += [&](IRigController*) {
CallAfter([&]() {
m_txtModeStatus->SetLabel(wxT("unk"));
m_txtModeStatus->Enable(false);
});
};
wxGetApp().rigFrequencyController->onFreqModeChange += [&](IRigFrequencyController*, uint64_t freq, IRigFrequencyController::Mode mode)
{
CallAfter([&, mode, freq]() {
// Update string value.
switch(mode)
{
case IRigFrequencyController::USB:
m_txtModeStatus->SetLabel(wxT("USB"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::DIGU:
m_txtModeStatus->SetLabel(wxT("USB-D"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::LSB:
m_txtModeStatus->SetLabel(wxT("LSB"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::DIGL:
m_txtModeStatus->SetLabel(wxT("LSB-D"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::FM:
m_txtModeStatus->SetLabel(wxT("FM"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::DIGFM:
m_txtModeStatus->SetLabel(wxT("FM-D"));
m_txtModeStatus->Enable(true);
break;
case IRigFrequencyController::AM:
m_txtModeStatus->SetLabel(wxT("AM"));
m_txtModeStatus->Enable(true);
break;
default:
m_txtModeStatus->SetLabel(wxT("unk"));
m_txtModeStatus->Enable(false);
break;
}
// Widest 60 meter allocation is 5.250-5.450 MHz per https://en.wikipedia.org/wiki/60-meter_band.
bool is60MeterBand = freq >= 5250000 && freq <= 5450000;
// Update color based on the mode and current frequency.
bool isUsbFreq = freq >= 10000000 || is60MeterBand;
bool isLsbFreq = freq < 10000000 && !is60MeterBand;
bool isMatchingMode =
(isUsbFreq && (mode == IRigFrequencyController::USB || mode == IRigFrequencyController::DIGU)) ||
(isLsbFreq && (mode == IRigFrequencyController::LSB || mode == IRigFrequencyController::DIGL));
if (isMatchingMode)
{
m_txtModeStatus->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
}
else
{
m_txtModeStatus->SetForegroundColour(wxColor(*wxRED));
}
// Update frequency box
if (!wxGetApp().appConfiguration.reportingConfiguration.reportingEnabled ||
!wxGetApp().appConfiguration.reportingConfiguration.manualFrequencyReporting)
{
if (wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz)
{
m_cboReportFrequency->SetValue(wxString::Format("%.1f", freq / 1000.0));
}
else
{
m_cboReportFrequency->SetValue(wxString::Format("%.4f", freq / 1000.0 / 1000.0));
}
}
m_txtModeStatus->Refresh();
// Suppress updates if the Report Frequency box has focus.
suppressFreqModeUpdates_ = m_cboReportFrequency->HasFocus();
});
};
// Temporarily suppress frequency updates until we're fully connected.
suppressFreqModeUpdates_ = true;
wxGetApp().rigFrequencyController->connect();
}
#endif // defined(WIN32)
//-------------------------------------------------------------------------
// OnCloseFrame()
//-------------------------------------------------------------------------
void MainFrame::OnCloseFrame(wxCloseEvent& event)
{
auto engine = AudioEngineFactory::GetAudioEngine();
engine->stop();
engine->setOnEngineError(nullptr, nullptr);
Destroy();
}
//-------------------------------------------------------------------------
// OnTop()
//-------------------------------------------------------------------------
void MainFrame::OnTop(wxCommandEvent& event)
{
auto style = GetWindowStyle();
// Clear wxSTAY_ON_TOP first if it's already set.
style &= ~wxSTAY_ON_TOP;
// (Re)set wxSTAY_ON_TOP depending on whether the menu option is checked.
if (event.IsChecked())
{
style |= wxSTAY_ON_TOP;
}
SetWindowStyle(style);
}
//-------------------------------------------------------------------------
// OnDeleteConfig()
//-------------------------------------------------------------------------
void MainFrame::OnDeleteConfig(wxCommandEvent&)
{
wxMessageDialog messageDialog(
this, "Would you like to restore configuration to defaults?", wxT("Restore Defaults"),
wxYES_NO | wxICON_QUESTION | wxCENTRE);
auto answer = messageDialog.ShowModal();
if (answer == wxID_YES)
{
if(pConfig->DeleteAll())
{
wxLogMessage(wxT("Config file/registry key successfully deleted."));
if (wxGetApp().m_sharedReporterObject)
{
wxGetApp().m_sharedReporterObject = nullptr;
}
if (m_reporterDialog != nullptr)
{
m_reporterDialog->setReporter(nullptr);
m_reporterDialog->Close();
m_reporterDialog->Destroy();
m_reporterDialog = nullptr;
}
// Resets all configuration to defaults.
loadConfiguration_();
}
else
{
wxLogError(wxT("Deleting config file/registry key failed."));
}
}
}
//-------------------------------------------------------------------------
// OnDeleteConfigUI()
//-------------------------------------------------------------------------
void MainFrame::OnDeleteConfigUI( wxUpdateUIEvent& event )
{
event.Enable(!m_RxRunning);
}
//-------------------------------------------------------------------------
// Paint()
//-------------------------------------------------------------------------
void MainFrame::OnPaint(wxPaintEvent& WXUNUSED(event))
{
wxPaintDC dc(this);
if(GetMenuBar()->IsChecked(ID_PAINT_BG))
{
dc.Clear();
}
dc.SetUserScale(m_zoom, m_zoom);
}
//-------------------------------------------------------------------------
// OnCmdSliderScroll()
//-------------------------------------------------------------------------
void MainFrame::OnCmdSliderScroll(wxScrollEvent& event)
{
char sqsnr[15];
g_SquelchLevel = (float)m_sliderSQ->GetValue()/2.0 - 5.0;
snprintf(sqsnr, 15, "%4.1f dB", g_SquelchLevel); // 0.5 dB steps
wxString sqsnr_string(sqsnr);
m_textSQ->SetLabel(sqsnr_string);
event.Skip();
}
//-------------------------------------------------------------------------
// OnChangeTxLevel()
//-------------------------------------------------------------------------
void MainFrame::OnChangeTxLevel( wxScrollEvent& event )
{
char fmt[15];
g_txLevel = m_sliderTxLevel->GetValue();
snprintf(fmt, 15, "%0.1f dB", (double)(g_txLevel)/10.0);
wxString fmtString(fmt);
m_txtTxLevelNum->SetLabel(fmtString);
wxGetApp().appConfiguration.transmitLevel = g_txLevel;
}
//-------------------------------------------------------------------------
// OnCheckSQClick()
//-------------------------------------------------------------------------
void MainFrame::OnCheckSQClick(wxCommandEvent& event)
{
if(!g_SquelchActive)
{
g_SquelchActive = true;
}
else
{
g_SquelchActive = false;
}
}
void MainFrame::setsnrBeta(bool snrSlow)
{
if(snrSlow)
{
m_snrBeta = 0.95; // 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().appConfiguration.snrSlow = m_ckboxSNR->GetValue();
setsnrBeta(wxGetApp().appConfiguration.snrSlow);
}
// check for space bar press (only when running)
int MainApp::FilterEvent(wxEvent& event)
{
if ((event.GetEventType() == wxEVT_KEY_DOWN) &&
(((wxKeyEvent&)event).GetKeyCode() == WXK_SPACE))
{
// only use space to toggle PTT if we are running and no modal dialogs (like options) up
bool mainWindowActive = frame->IsActive();
bool reporterActiveButNotUpdatingTextMessage =
frame->m_reporterDialog != nullptr && frame->m_reporterDialog->IsActive() &&
!frame->m_reporterDialog->isTextMessageFieldInFocus();
if (frame->m_RxRunning && (mainWindowActive || reporterActiveButNotUpdatingTextMessage) &&
wxGetApp().appConfiguration.enableSpaceBarForPTT && !frame->isReceiveOnly()) {
// space bar controls tx/rx if keyer not running
if (frame->vk_state == VK_IDLE) {
if (frame->m_btnTogPTT->GetValue())
frame->m_btnTogPTT->SetValue(false);
else
frame->m_btnTogPTT->SetValue(true);
// Update background color of button here because when toggling PTT via keyboard,
// the background color for some reason doesn't update inside togglePTT().
frame->m_btnTogPTT->SetBackgroundColour(frame->m_btnTogPTT->GetValue() ? *wxRED : wxNullColour);
// Actually toggle PTT.
frame->togglePTT();
}
else // space bar stops keyer
frame->VoiceKeyerProcessEvent(VK_SPACE_BAR);
return true; // absorb space so we don't toggle control with focus (e.g. Start)
}
}
return -1;
}
void MainFrame::OnSetMonitorTxAudio( wxCommandEvent& event )
{
wxGetApp().appConfiguration.monitorTxAudio = event.IsChecked();
adjustMonitorPttVolMenuItem_->Enable(wxGetApp().appConfiguration.monitorTxAudio);
}
void MainFrame::OnSetMonitorTxAudioVol( wxCommandEvent& event )
{
auto popup = new MonitorVolumeAdjPopup(this, wxGetApp().appConfiguration.monitorTxAudioVol);
popup->Popup();
}
//-------------------------------------------------------------------------
// OnTogBtnPTTRightClick(): show right-click menu for PTT button
//-------------------------------------------------------------------------
void MainFrame::OnTogBtnPTTRightClick( wxContextMenuEvent& event )
{
auto sz = m_btnTogPTT->GetSize();
m_btnTogPTT->PopupMenu(pttPopupMenu_, wxPoint(-sz.GetWidth() - 25, 0));
}
//-------------------------------------------------------------------------
// OnTogBtnPTT ()
//-------------------------------------------------------------------------
void MainFrame::OnTogBtnPTT (wxCommandEvent& event)
{
if (vk_state == VK_TX)
{
// Disable TX via VK code to prevent state inconsistencies.
VoiceKeyerProcessEvent(VK_SPACE_BAR);
}
else
{
togglePTT();
}
event.Skip();
}
void MainFrame::togglePTT(void) {
std::chrono::high_resolution_clock highResClock;
// Change tabbed page in centre panel depending on PTT state
if (g_tx)
{
// Sleep for long enough that we get the remaining [blocksize] ms of audio.
int msSleep = (1000 * freedvInterface.getTxNumSpeechSamples()) / freedvInterface.getTxSpeechSampleRate();
log_debug("Sleeping for %d ms prior to ending TX", msSleep);
auto before = highResClock.now();
while(true)
{
auto diff = highResClock.now() - before;
if (diff >= std::chrono::milliseconds(msSleep))
{
break;
}
wxThread::Sleep(1);
wxGetApp().Yield(true);
}
// Trigger end of TX processing. This causes us to wait for the remaining samples
// to flow through the system before toggling PTT. Note that there is a 1000ms
// timeout as backup.
if (freedvInterface.getCurrentMode() == FREEDV_MODE_RADE)
{
log_info("Waiting for EOO to be queued");
endingTx = true;
while(true)
{
if (g_eoo_enqueued)
{
log_info("Detected that EOO has been enqueued");
break;
}
wxThread::Sleep(1);
wxGetApp().Yield(true);
}
}
int sample = g_outfifo1_empty;
before = highResClock.now();
while(true)
{
auto diff = highResClock.now() - before;
if (diff >= std::chrono::milliseconds(1000) || (g_outfifo1_empty != sample))
{
log_info("All TX finished (diff = %d ms, fifo_empty = %d, sample = %d), going out of PTT", (int)std::chrono::duration_cast<std::chrono::milliseconds>(diff).count(), g_outfifo1_empty, sample);
break;
}
wxThread::Sleep(1);
// Yield() used to avoid lack of UI responsiveness during delay.
wxGetApp().Yield(true);
}
// Wait an additional configured timeframe before actually clearing PTT (below)
if (wxGetApp().appConfiguration.txRxDelayMilliseconds > 0)
{
// Delay outbound TX audio if going into TX.
// Yield() used to avoid lack of UI responsiveness during delay.
before = highResClock.now();
while(true)
{
auto diff = highResClock.now() - before;
if (diff >= std::chrono::milliseconds(wxGetApp().appConfiguration.txRxDelayMilliseconds.get()))
{
break;
}
wxThread::Sleep(1);
wxGetApp().Yield(true);
}
}
g_tx = false;
endingTx = false;
// tx-> rx transition, swap to the page we were on for last rx
m_auiNbookCtrl->ChangeSelection(wxGetApp().appConfiguration.currentNotebookTab);
// enable sync text
m_textSync->Enable();
m_textCurrentDecodeMode->Enable();
// Re-enable buttons.
m_togBtnOnOff->Enable(true);
m_togBtnAnalog->Enable(true);
m_togBtnVoiceKeyer->Enable(true);
}
else
{
// rx-> tx transition, swap to Mic In page to monitor speech
wxGetApp().appConfiguration.currentNotebookTab = m_auiNbookCtrl->GetSelection();
// Note: GetPageIndex sometimes returns the incorrect results, so iterating and finding
// the current page ourselves is a better bet.
size_t index = 0;
for (; index < m_auiNbookCtrl->GetPageCount(); index++)
{
auto page = m_auiNbookCtrl->GetPage(index);
if (page == (wxWindow *)m_panelSpeechIn)
{
m_auiNbookCtrl->ChangeSelection(index);
break;
}
}
// disable sync text
m_textSync->Disable();
m_textCurrentDecodeMode->Disable();
// Disable On/Off button.
m_togBtnOnOff->Enable(false);
}
if (wxGetApp().appConfiguration.rigControlConfiguration.hamlibUseForPTT) {
if (wxGetApp().rigFrequencyController != nullptr && wxGetApp().rigFrequencyController->isConnected()) {
// Update mode display on the bottom of the main UI.
wxGetApp().rigFrequencyController->requestCurrentFrequencyMode();
}
}
auto newTx = m_btnTogPTT->GetValue();
if (wxGetApp().rigPttController != nullptr && wxGetApp().rigPttController->isConnected())
{
wxGetApp().rigPttController->ptt(newTx);
}
// reset level gauge
m_maxLevel = 0;
m_textLevel->SetLabel(wxT(""));
m_gaugeLevel->SetValue(0);
// Report TX change to registered reporters
for (auto& obj : wxGetApp().m_reporters)
{
obj->transmit(freedvInterface.getCurrentTxModeStr(), newTx);
}
// Change button color depending on TX status.
m_btnTogPTT->SetBackgroundColour(newTx ? *wxRED : wxNullColour);
// If we're recording, switch to/from modulator and radio.
if (g_sfRecFile != nullptr)
{
if (!newTx)
{
g_recFileFromModulator = false;
g_recFileFromRadio = true;
}
else
{
g_recFileFromRadio = false;
g_recFileFromModulator = true;
}
}
if (newTx)
{
endingTx = false;
if (wxGetApp().appConfiguration.txRxDelayMilliseconds > 0)
{
// Delay outbound TX audio if going into TX.
// Note: Yield() used here to avoid lack of UI responsiveness during delay.
auto before = highResClock.now();
while(true)
{
auto diff = highResClock.now() - before;
if (diff >= std::chrono::milliseconds(wxGetApp().appConfiguration.txRxDelayMilliseconds.get()))
{
break;
}
wxThread::Sleep(1);
wxGetApp().Yield(true);
}
}
// g_tx governs when audio actually goes out during TX, so don't set to true until
// after the delay occurs.
g_tx = true;
}
}
HamlibRigController::Mode MainFrame::getCurrentMode_()
{
// Widest 60 meter allocation is 5.250-5.450 MHz per https://en.wikipedia.org/wiki/60-meter_band.
bool is60MeterBand =
wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency >= 5250000 &&
wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency <= 5450000;
bool useAnalog =
wxGetApp().appConfiguration.rigControlConfiguration.hamlibUseAnalogModes || g_analog;
HamlibRigController::Mode lsbMode = useAnalog ? HamlibRigController::LSB : HamlibRigController::DIGL;
HamlibRigController::Mode usbMode = useAnalog ? HamlibRigController::USB : HamlibRigController::DIGU;
HamlibRigController::Mode newMode;
if (wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency < 10000000 &&
!is60MeterBand)
{
newMode = lsbMode;
}
else
{
newMode = usbMode;
}
return newMode;
}
//-------------------------------------------------------------------------
// OnTogBtnAnalogClick()
//-------------------------------------------------------------------------
void MainFrame::OnTogBtnAnalogClick (wxCommandEvent& event)
{
if (g_analog == 0) {
g_analog = 1;
m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(FS/2)));
m_panelWaterfall->setFs(FS);
m_togBtnAnalog->SetLabel(wxT("Di&gital"));
}
else {
g_analog = 0;
m_panelSpectrum->setFreqScale(MODEM_STATS_NSPEC*((float)MAX_F_HZ/(freedvInterface.getRxModemSampleRate()/2)));
m_panelWaterfall->setFs(freedvInterface.getRxModemSampleRate());
m_togBtnAnalog->SetLabel(wxT("A&nalog"));
}
// Report analog change to registered reporters
for (auto& obj : wxGetApp().m_reporters)
{
obj->inAnalogMode(g_analog);
}
if (wxGetApp().rigFrequencyController != nullptr &&
wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency > 0 &&
wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqModeChanges)
{
// Request mode change on the radio side
wxGetApp().rigFrequencyController->setMode(getCurrentMode_());
}
g_State = g_prev_State = 0;
freedvInterface.getCurrentRxModemStats()->snr_est = 0;
event.Skip();
}
void MainFrame::OnCallSignReset(wxCommandEvent& event)
{
m_pcallsign = m_callsign;
memset(m_callsign, 0, MAX_CALLSIGN);
wxString s;
s.Printf("%s", m_callsign);
m_txtCtrlCallSign->SetValue(s);
m_lastReportedCallsignListView->DeleteAllItems();
m_cboLastReportedCallsigns->SetText(_(""));
}
// Force manual resync, just in case demod gets stuck on false sync
void MainFrame::OnReSync(wxCommandEvent& event)
{
if (m_RxRunning) {
log_debug("OnReSync");
// Resync must be triggered from the TX/RX thread, so pushing the button queues it until
// the next execution of the TX/RX loop.
g_queueResync = true;
}
}
void MainFrame::resetStats_()
{
if (m_RxRunning) {
freedvInterface.resetBitStats();
g_resyncs = 0;
int i;
for(i=0; i<2*g_Nc; i++) {
g_error_hist[i] = 0;
g_error_histn[i] = 0;
}
// resets variance stats every time it is called
freedvInterface.setEq(wxGetApp().appConfiguration.filterConfiguration.enable700CEqualizer);
}
}
void MainFrame::OnBerReset(wxCommandEvent& event)
{
resetStats_();
}
void MainFrame::OnChangeReportFrequencyVerify( wxCommandEvent& event )
{
if (wxGetApp().rigFrequencyController != nullptr && suppressFreqModeUpdates_)
{
return;
}
OnChangeReportFrequency(event);
}
void MainFrame::OnChangeReportFrequency( wxCommandEvent& event )
{
wxString freqStr = m_cboReportFrequency->GetValue();
auto oldFreq = wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency;
if (freqStr.Length() > 0)
{
double tmp = 0;
std::stringstream ss(std::string(freqStr.ToUTF8()));
std::locale loc("");
ss.imbue(loc);
if (wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz)
{
ss >> std::fixed >> std::setprecision(1) >> tmp;
wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency = round(tmp * 1000);
}
else
{
ss >> std::fixed >> std::setprecision(4) >> tmp;
wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency = round(tmp * 1000 * 1000);
}
if (wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency > 0)
{
m_cboReportFrequency->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
// Round to the nearest 100 Hz.
uint64_t wholeFreq = wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency / 100;
uint64_t remainder = wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency % 100;
if (remainder >= 50)
{
wholeFreq++;
}
wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency = wholeFreq * 100;
}
else
{
m_cboReportFrequency->SetForegroundColour(wxColor(*wxRED));
}
}
else
{
wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency = 0;
m_cboReportFrequency->SetForegroundColour(wxColor(*wxRED));
}
// Report current frequency to reporters
for (auto& ptr : wxGetApp().m_reporters)
{
ptr->freqChange(wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency);
}
if (oldFreq != wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency)
{
if (wxGetApp().rigFrequencyController != nullptr &&
wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency > 0 &&
(wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqModeChanges || wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqChangesOnly))
{
// Request frequency/mode change on the radio side
wxGetApp().rigFrequencyController->setFrequency(wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency);
if (wxGetApp().appConfiguration.rigControlConfiguration.hamlibEnableFreqModeChanges)
{
wxGetApp().rigFrequencyController->setMode(getCurrentMode_());
}
}
}
if (m_reporterDialog != nullptr)
{
m_reporterDialog->refreshQSYButtonState();
}
}
void MainFrame::OnReportFrequencySetFocus(wxFocusEvent& event)
{
suppressFreqModeUpdates_ = true;
TopFrame::OnReportFrequencySetFocus(event);
}
void MainFrame::OnReportFrequencyKillFocus(wxFocusEvent& event)
{
suppressFreqModeUpdates_ = false;
// Handle any frequency changes as appropriate.
wxCommandEvent tmpEvent;
OnChangeReportFrequency(tmpEvent);
TopFrame::OnReportFrequencyKillFocus(event);
}
void MainFrame::OnSystemColorChanged(wxSysColourChangedEvent& event)
{
// Works around issues on wxWidgets with certain controls not changing backgrounds
// when the user switches between light and dark mode.
wxColour currentControlBackground = wxTransparentColour;
m_collpane->SetBackgroundColour(currentControlBackground);
m_collpane->GetPane()->SetBackgroundColour(currentControlBackground);
TopFrame::OnSystemColorChanged(event);
}
void MainFrame::OnCenterRx(wxCommandEvent& event)
{
clickTune(FDMDV_FCENTRE);
}
void MainFrame::updateReportingFreqList_()
{
uint64_t prevFreqInt = wxGetApp().appConfiguration.reportingConfiguration.reportingFrequency;
double freq = ((double)prevFreqInt)/1000.0;
if (!wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz)
{
freq /= 1000.0;
}
std::stringstream ss;
std::locale loc("");
ss.imbue(loc);
if (wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz)
{
ss << std::fixed << std::setprecision(1) << freq;
}
else
{
ss << std::fixed << std::setprecision(4) << freq;
}
std::string prevSelected = ss.str();
m_cboReportFrequency->Clear();
for (auto& item : wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyList.get())
{
m_cboReportFrequency->Append(item);
}
auto idx = m_cboReportFrequency->FindString(prevSelected);
if (idx >= 0)
{
m_cboReportFrequency->SetSelection(idx);
}
m_cboReportFrequency->SetValue(prevSelected);
// Update associated label if the units have changed
if (wxGetApp().appConfiguration.reportingConfiguration.reportingFrequencyAsKhz)
{
m_freqBox->SetLabel(_("Report Freq. (kHz)"));
}
else
{
m_freqBox->SetLabel(_("Report Freq. (MHz)"));
}
}