3203 lines
109 KiB
C++
3203 lines
109 KiB
C++
// Copyright (c) Charles J. Cliffe
|
|
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
#include "AppFrame.h"
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/wx.h"
|
|
#endif
|
|
|
|
#include "wx/numdlg.h"
|
|
#include "wx/filedlg.h"
|
|
|
|
#if !wxUSE_GLCANVAS
|
|
#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library"
|
|
#endif
|
|
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include "AudioSinkFileThread.h"
|
|
#include "CubicSDR.h"
|
|
#include "DataTree.h"
|
|
#include "ColorTheme.h"
|
|
#include "DemodulatorMgr.h"
|
|
#include "ImagePanel.h"
|
|
#include "ActionDialog.h"
|
|
|
|
#include <thread>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
|
|
#include <wx/panel.h>
|
|
#include <wx/numformatter.h>
|
|
#include <cstddef>
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__)
|
|
#include "CubicSDR.xpm"
|
|
#endif
|
|
|
|
wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
|
|
//EVT_MENU(wxID_NEW, AppFrame::OnNewWindow)
|
|
EVT_CLOSE(AppFrame::OnClose)
|
|
EVT_MENU(wxID_ANY, AppFrame::OnMenu)
|
|
EVT_IDLE(AppFrame::OnIdle)
|
|
EVT_SPLITTER_DCLICK(wxID_ANY, AppFrame::OnDoubleClickSash)
|
|
EVT_SPLITTER_UNSPLIT(wxID_ANY, AppFrame::OnUnSplit)
|
|
wxEND_EVENT_TABLE()
|
|
|
|
#ifdef USE_HAMLIB
|
|
#include "RigThread.h"
|
|
#include "PortSelectorDialog.h"
|
|
#include "rs232.h"
|
|
#endif
|
|
|
|
|
|
|
|
class ActionDialogBookmarkReset : public ActionDialog {
|
|
public:
|
|
ActionDialogBookmarkReset() : ActionDialog(wxGetApp().getAppFrame(), wxID_ANY, wxT("Reset Bookmarks?")) {
|
|
m_questionText->SetLabelText(wxT("Resetting bookmarks will erase all current bookmarks; are you sure?"));
|
|
}
|
|
|
|
void doClickOK() override {
|
|
wxGetApp().getBookmarkMgr().resetBookmarks();
|
|
wxGetApp().getBookmarkMgr().updateBookmarks();
|
|
wxGetApp().getBookmarkMgr().updateActiveList();
|
|
}
|
|
};
|
|
|
|
|
|
#define APPFRAME_MODEMPROPS_MINSIZE 20
|
|
#define APPFRAME_MODEMPROPS_MAXSIZE 240
|
|
|
|
AppFrame::AppFrame() :
|
|
wxFrame(nullptr, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(nullptr) {
|
|
|
|
initIcon();
|
|
|
|
deviceChanged.store(false);
|
|
modemPropertiesUpdated.store(false);
|
|
|
|
demodTray = new wxBoxSizer(wxHORIZONTAL);
|
|
auto *demodScopeTray = new wxBoxSizer(wxVERTICAL);
|
|
auto *demodTunerTray = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
// OpenGL settings:
|
|
//deprecated format: std::vector<int> attribList = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
|
|
wxGLAttributes attribList;
|
|
attribList.PlatformDefaults().RGBA().MinRGBA(8, 8, 8, 8).DoubleBuffer().EndList();
|
|
|
|
mainSplitter = new wxSplitterWindow( this, wxID_MAIN_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE );
|
|
mainSplitter->SetSashGravity(10.0f / 37.0f);
|
|
mainSplitter->SetMinimumPaneSize(1);
|
|
|
|
auto *demodPanel = new wxPanel(mainSplitter, wxID_ANY);
|
|
|
|
#ifdef CUBICSDR_HEADER_IMAGE
|
|
wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath());
|
|
std::string headerPath = exePath.GetPath().ToStdString();
|
|
headerPath += filePathSeparator + std::string("" CUBICSDR_HEADER_IMAGE);
|
|
wxInitAllImageHandlers();
|
|
|
|
ImagePanel *imgPanel = new ImagePanel(demodPanel, headerPath, wxBITMAP_TYPE_ANY);
|
|
|
|
std::string headerBgColor = "" CUBICSDR_HEADER_BG;
|
|
if (headerBgColor != "") {
|
|
imgPanel->SetBackgroundColour(wxColour(headerBgColor));
|
|
}
|
|
|
|
imgPanel->SetBestFittingSize(wxSize(200, 0));
|
|
|
|
demodTray->Add(imgPanel, 0, wxEXPAND | wxALL, 0);
|
|
demodTray->AddSpacer(1);
|
|
#endif
|
|
|
|
gainCanvas = new GainCanvas(demodPanel, attribList);
|
|
gainCanvas->setHelpTip("Tuner gains, usually in dB. Click / use Mousewheel to change.");
|
|
gainSizerItem = demodTray->Add(gainCanvas, 0, wxEXPAND | wxALL, 0);
|
|
gainSizerItem->Show(false);
|
|
gainSpacerItem = demodTray->AddSpacer(1);
|
|
gainSpacerItem->Show(false);
|
|
|
|
// Demod mode selector
|
|
demodModeSelector = makeModemSelectorPanel(demodPanel, attribList);
|
|
|
|
demodTray->Add(demodModeSelector, 2, wxEXPAND | wxALL, 0);
|
|
|
|
#ifdef ENABLE_DIGITAL_LAB
|
|
// Digital Lab Mode Selector
|
|
demodModeSelectorAdv = makeModemAdvSelectorPanel(demodPanel, attribList);
|
|
demodTray->Add(demodModeSelectorAdv, 3, wxEXPAND | wxALL, 0);
|
|
#endif
|
|
|
|
// Modem properties panel
|
|
modemProps = makeModemProperties(demodPanel);
|
|
|
|
demodTray->Add(modemProps, 15, wxEXPAND | wxALL, 0);
|
|
|
|
#ifndef __APPLE__
|
|
demodTray->AddSpacer(1);
|
|
#endif
|
|
|
|
#if CUBICSDR_ENABLE_VIEW_DEMOD
|
|
// Demodulator View
|
|
auto *demodVisuals = new wxBoxSizer(wxVERTICAL);
|
|
|
|
// Demod Spectrum
|
|
demodSpectrumCanvas = makeDemodSpectrumCanvas(demodPanel, attribList);
|
|
demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0);
|
|
|
|
wxGetApp().getDemodSpectrumProcessor()->setup(DEFAULT_DMOD_FFT_SIZE);
|
|
wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodSpectrumCanvas->getVisualDataQueue());
|
|
|
|
demodVisuals->AddSpacer(1);
|
|
|
|
// Demod Waterfall
|
|
demodWaterfallCanvas = makeWaterfallCanvas(demodPanel, attribList);
|
|
demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas);
|
|
demodVisuals->Add(demodWaterfallCanvas, 6, wxEXPAND | wxALL, 0);
|
|
|
|
demodSpectrumCanvas->attachWaterfallCanvas(demodWaterfallCanvas);
|
|
|
|
wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodWaterfallCanvas->getVisualDataQueue());
|
|
demodVisuals->SetMinSize(wxSize(128,-1));
|
|
|
|
demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0);
|
|
demodTray->AddSpacer(1);
|
|
#else
|
|
demodSpectrumCanvas = nullptr;
|
|
demodWaterfallCanvas = nullptr;
|
|
#endif
|
|
|
|
// Demod Signal/Squelch Meter
|
|
demodSignalMeter = makeSignalMeter(demodPanel, attribList);
|
|
demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0);
|
|
demodTray->AddSpacer(1);
|
|
|
|
#if CUBICSDR_ENABLE_VIEW_SCOPE
|
|
// Scope
|
|
scopeCanvas = makeScopeCanvas(demodPanel, attribList);
|
|
demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0);
|
|
|
|
wxGetApp().getScopeProcessor()->setup(DEFAULT_SCOPE_FFT_SIZE);
|
|
wxGetApp().getScopeProcessor()->attachOutput(scopeCanvas->getInputQueue());
|
|
|
|
demodScopeTray->AddSpacer(1);
|
|
#else
|
|
scopeCanvas = nullptr;
|
|
#endif
|
|
|
|
// Delta Lock Button
|
|
deltaLockButton = makeDeltaLockButton(demodPanel, attribList);
|
|
|
|
demodTunerTray->Add(deltaLockButton, 0, wxEXPAND | wxALL, 0);
|
|
demodTunerTray->AddSpacer(1);
|
|
|
|
// Modem Tuner
|
|
demodTuner = makeModemTuner(demodPanel, attribList);
|
|
demodTunerTray->Add(demodTuner, 1, wxEXPAND | wxALL, 0);
|
|
|
|
demodScopeTray->Add(demodTunerTray, 1, wxEXPAND | wxALL, 0);
|
|
demodTray->Add(demodScopeTray, 30, wxEXPAND | wxALL, 0);
|
|
demodTray->AddSpacer(1);
|
|
|
|
auto *demodGainTray = new wxBoxSizer(wxVERTICAL);
|
|
|
|
// Demod Gain Meter
|
|
demodGainMeter = makeModemGainMeter(demodPanel, attribList);
|
|
demodGainTray->Add(demodGainMeter, 8, wxEXPAND | wxALL, 0);
|
|
demodGainTray->AddSpacer(1);
|
|
|
|
// Solo Button
|
|
soloModeButton = makeSoloModeButton(demodPanel, attribList);
|
|
demodGainTray->Add(soloModeButton, 1, wxEXPAND | wxALL, 0);
|
|
demodGainTray->AddSpacer(1);
|
|
|
|
// Mute Button
|
|
demodMuteButton = makeModemMuteButton(demodPanel, attribList);
|
|
demodGainTray->Add(demodMuteButton, 1, wxEXPAND | wxALL, 0);
|
|
|
|
demodTray->Add(demodGainTray, 1, wxEXPAND | wxALL, 0);
|
|
|
|
demodPanel->SetSizer(demodTray);
|
|
|
|
bookmarkSplitter = new wxSplitterWindow(mainSplitter, wxID_BM_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE );
|
|
bookmarkSplitter->SetMinimumPaneSize(1);
|
|
bookmarkSplitter->SetSashGravity(1.0f / 20.0f);
|
|
|
|
mainVisSplitter = new wxSplitterWindow( bookmarkSplitter, wxID_VIS_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE );
|
|
mainVisSplitter->SetMinimumPaneSize(1);
|
|
mainVisSplitter->SetSashGravity(6.0f / 25.0f);
|
|
|
|
auto *spectrumPanel = new wxPanel(mainVisSplitter, wxID_ANY);
|
|
auto *spectrumSizer = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
// Spectrum Canvas
|
|
spectrumCanvas = makeSpectrumCanvas(spectrumPanel, attribList);
|
|
|
|
wxGetApp().getSpectrumProcessor()->setup(DEFAULT_FFT_SIZE);
|
|
wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue());
|
|
|
|
spectrumSizer->Add(spectrumCanvas, 63, wxEXPAND | wxALL, 0);
|
|
spectrumSizer->AddSpacer(1);
|
|
|
|
auto *spectrumCtlTray = new wxBoxSizer(wxVERTICAL);
|
|
|
|
// Peak Hold
|
|
peakHoldButton = makePeakHoldButton(spectrumPanel, attribList);
|
|
spectrumCtlTray->Add(peakHoldButton, 1, wxEXPAND | wxALL, 0);
|
|
spectrumCtlTray->AddSpacer(1);
|
|
|
|
// Spectrum Average Meter
|
|
spectrumAvgMeter = makeSpectrumAvgMeter(spectrumPanel, attribList);
|
|
spectrumCtlTray->Add(spectrumAvgMeter, 8, wxEXPAND | wxALL, 0);
|
|
spectrumSizer->Add(spectrumCtlTray, 1, wxEXPAND | wxALL, 0);
|
|
spectrumPanel->SetSizer(spectrumSizer);
|
|
|
|
auto *waterfallPanel = new wxPanel(mainVisSplitter, wxID_ANY);
|
|
auto *waterfallSizer = new wxBoxSizer(wxHORIZONTAL);
|
|
|
|
// Waterfall
|
|
waterfallCanvas = makeWaterfall(waterfallPanel, attribList);
|
|
// Create and connect the FFT visual data thread
|
|
waterfallDataThread = new FFTVisualDataThread();
|
|
waterfallDataThread->setInputQueue("IQDataInput", wxGetApp().getWaterfallVisualQueue());
|
|
waterfallDataThread->setOutputQueue("FFTDataOutput", waterfallCanvas->getVisualDataQueue());
|
|
waterfallDataThread->getProcessor()->setHideDC(true);
|
|
|
|
t_FFTData = new std::thread(&FFTVisualDataThread::threadMain, waterfallDataThread);
|
|
|
|
|
|
// Waterfall speed meter
|
|
waterfallSpeedMeter = makeWaterfallSpeedMeter(waterfallPanel, attribList);
|
|
|
|
waterfallSizer->Add(waterfallCanvas, 63, wxEXPAND | wxALL, 0);
|
|
waterfallSizer->AddSpacer(1);
|
|
waterfallSizer->Add(waterfallSpeedMeter, 1, wxEXPAND | wxALL, 0);
|
|
waterfallPanel->SetSizer(waterfallSizer);
|
|
|
|
mainVisSplitter->SplitHorizontally( spectrumPanel, waterfallPanel, 0 );
|
|
|
|
// Bookmark View
|
|
bookmarkView = new BookmarkView(bookmarkSplitter, wxID_ANY, wxDefaultPosition, wxSize(120,-1));
|
|
bookmarkSplitter->SplitVertically( bookmarkView, mainVisSplitter );
|
|
mainSplitter->SplitHorizontally( demodPanel, bookmarkSplitter );
|
|
|
|
|
|
// TODO: refactor these..
|
|
waterfallCanvas->attachSpectrumCanvas(spectrumCanvas);
|
|
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
|
|
|
|
// Primary sizer for the window
|
|
auto *vbox = new wxBoxSizer(wxVERTICAL);
|
|
vbox->Add(mainSplitter, 1, wxEXPAND | wxALL, 0);
|
|
|
|
/* * /
|
|
vbox->AddSpacer(1);
|
|
testCanvas = new UITestCanvas(this, attribList);
|
|
vbox->Add(testCanvas, 20, wxEXPAND | wxALL, 0);
|
|
// */
|
|
|
|
this->SetSizer(vbox);
|
|
|
|
// Load and apply configuration
|
|
initConfigurationSettings();
|
|
|
|
// Initialize menu
|
|
initMenuBar();
|
|
|
|
// Create status bar
|
|
CreateStatusBar();
|
|
|
|
// Show the window
|
|
Show();
|
|
|
|
// Force refresh of all
|
|
Refresh();
|
|
|
|
// Pop up the device selector
|
|
wxGetApp().deviceSelector();
|
|
}
|
|
|
|
void AppFrame::initIcon() {
|
|
#if defined(__linux__) || defined(__FreeBSD__)
|
|
SetIcon(wxICON(cubicsdr));
|
|
#endif
|
|
#ifdef _WIN32
|
|
SetIcon(wxICON(frame_icon));
|
|
#endif
|
|
|
|
}
|
|
|
|
void AppFrame::initMenuBar() {// File Menu
|
|
menuBar = new wxMenuBar;
|
|
fileMenu = makeFileMenu();
|
|
menuBar->Append(fileMenu, wxT("&File"));
|
|
|
|
// Settings Menu
|
|
settingsMenu = new wxMenu;
|
|
menuBar->Append(settingsMenu, wxT("&Settings"));
|
|
|
|
// Sample Rate Menu
|
|
sampleRateMenu = new wxMenu;
|
|
menuBar->Append(sampleRateMenu, wxT("Sample &Rate"));
|
|
|
|
// Audio Sample Rate Menu
|
|
menuBar->Append(makeAudioSampleRateMenu(), wxT("Audio &Sample Rate"));
|
|
|
|
//Add a Recording menu
|
|
recordingMenu = makeRecordingMenu();
|
|
menuBar->Append(recordingMenu, wxT("Recordin&g"));
|
|
updateRecordingMenu();
|
|
|
|
#ifdef USE_HAMLIB
|
|
rigPortDialog = nullptr;
|
|
|
|
// Rig Menu
|
|
rigMenu = makeRigMenu();
|
|
menuBar->Append(rigMenu, wxT("&Rig Control"));
|
|
#endif
|
|
|
|
// Display Menu
|
|
displayMenu = makeDisplayMenu();
|
|
menuBar->Append(displayMenu, wxT("&Display"));
|
|
|
|
SetMenuBar(menuBar);
|
|
|
|
wxAcceleratorEntry entries[3];
|
|
entries[0].Set(wxACCEL_CTRL, (int) 'O', wxID_OPEN);
|
|
entries[1].Set(wxACCEL_CTRL, (int) 'S', wxID_SAVE);
|
|
entries[2].Set(wxACCEL_CTRL, (int) 'A', wxID_SAVEAS);
|
|
|
|
wxAcceleratorTable accel(3, entries);
|
|
SetAcceleratorTable(accel);
|
|
}
|
|
|
|
void AppFrame::initConfigurationSettings() {
|
|
|
|
// Init Theme
|
|
ThemeMgr::mgr.setTheme(wxGetApp().getConfig()->getTheme());
|
|
bookmarkView->updateTheme();
|
|
|
|
// Init Font Scale
|
|
int fontScale = wxGetApp().getConfig()->getFontScale();
|
|
GLFont::setScale((GLFont::GLFontScale)fontScale);
|
|
|
|
// Init window position from configuration
|
|
wxRect *win = wxGetApp().getConfig()->getWindow();
|
|
if (win) {
|
|
SetPosition(win->GetPosition());
|
|
SetClientSize(win->GetSize());
|
|
} else {
|
|
SetClientSize(1280, 600);
|
|
Centre();
|
|
}
|
|
|
|
// Init maximize state
|
|
if (wxGetApp().getConfig()->getWindowMaximized()) {
|
|
Maximize();
|
|
}
|
|
|
|
// Init frequency snap
|
|
wxGetApp().setFrequencySnap(wxGetApp().getConfig()->getSnap());
|
|
|
|
// Init spectrum average speed
|
|
float spectrumAvg = wxGetApp().getConfig()->getSpectrumAvgSpeed();
|
|
spectrumAvgMeter->setLevel(spectrumAvg);
|
|
wxGetApp().getSpectrumProcessor()->setFFTAverageRate(spectrumAvg);
|
|
|
|
// Init waterfall speed
|
|
int wflps =wxGetApp().getConfig()->getWaterfallLinesPerSec();
|
|
waterfallSpeedMeter->setLevel(sqrtf(wflps));
|
|
waterfallDataThread->setLinesPerSecond(wflps);
|
|
waterfallCanvas->setLinesPerSecond(wflps);
|
|
|
|
// Init modem property collapsed state
|
|
int mpc =wxGetApp().getConfig()->getModemPropsCollapsed();
|
|
if (mpc) {
|
|
modemProps->setCollapsed(true);
|
|
}
|
|
|
|
// Init main splitter position (separates top/bottom area horizontally)
|
|
int msPos = wxGetApp().getConfig()->getMainSplit();
|
|
if (msPos != -1) {
|
|
mainSplitter->SetSashPosition(msPos);
|
|
}
|
|
|
|
// Init bookmark splitter position (separates bookmark/main vis vertically)
|
|
int bsPos = wxGetApp().getConfig()->getBookmarkSplit();
|
|
if (bsPos != -1) {
|
|
bookmarkSplitter->SetSashPosition(bsPos);
|
|
}
|
|
|
|
// Init vis splitter position (separates spectrum/waterfall horizontally)
|
|
int vsPos = wxGetApp().getConfig()->getVisSplit();
|
|
if (vsPos != -1) {
|
|
mainVisSplitter->SetSashPosition(vsPos);
|
|
}
|
|
|
|
if (!wxGetApp().getConfig()->getBookmarksVisible()) {
|
|
bookmarkSplitter->Unsplit(bookmarkView);
|
|
bookmarkSplitter->Layout();
|
|
}
|
|
|
|
#ifdef USE_HAMLIB
|
|
rigModel = wxGetApp().getConfig()->getRigModel();
|
|
rigSerialRate = wxGetApp().getConfig()->getRigRate();
|
|
rigPort = wxGetApp().getConfig()->getRigPort();
|
|
#endif
|
|
}
|
|
|
|
ModemProperties *AppFrame::makeModemProperties(wxPanel *parent) {
|
|
auto *pProperties = new ModemProperties(parent, wxID_ANY);
|
|
pProperties->SetMinSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE, -1));
|
|
pProperties->SetMaxSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE, -1));
|
|
|
|
ModemArgInfoList dummyInfo;
|
|
pProperties->initProperties(dummyInfo, nullptr);
|
|
pProperties->updateTheme();
|
|
return pProperties;
|
|
}
|
|
|
|
ModeSelectorCanvas *AppFrame::makeModemAdvSelectorPanel(wxPanel *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new ModeSelectorCanvas(parent, attribList);
|
|
pCanvas->addChoice("ASK");
|
|
pCanvas->addChoice("APSK");
|
|
pCanvas->addChoice("BPSK");
|
|
pCanvas->addChoice("DPSK");
|
|
pCanvas->addChoice("PSK");
|
|
pCanvas->addChoice("FSK");
|
|
pCanvas->addChoice("GMSK");
|
|
pCanvas->addChoice("OOK");
|
|
pCanvas->addChoice("ST");
|
|
pCanvas->addChoice("SQAM");
|
|
pCanvas->addChoice("QAM");
|
|
pCanvas->addChoice("QPSK");
|
|
pCanvas->setHelpTip("Choose advanced modulation types.");
|
|
pCanvas->SetMinSize(wxSize(50, -1));
|
|
pCanvas->SetMaxSize(wxSize(50, -1));
|
|
return pCanvas;
|
|
}
|
|
|
|
#ifdef USE_HAMLIB
|
|
wxMenu *AppFrame::makeRigMenu() {
|
|
auto *pMenu = new wxMenu;
|
|
|
|
rigEnableMenuItem = pMenu->AppendCheckItem(wxID_RIG_TOGGLE, wxT("Enable Rig"));
|
|
|
|
pMenu->Append(wxID_RIG_SDR_IF, wxT("SDR-IF"));
|
|
|
|
rigControlMenuItem = pMenu->AppendCheckItem(wxID_RIG_CONTROL, wxT("Control Rig"));
|
|
rigControlMenuItem->Check(wxGetApp().getConfig()->getRigControlMode());
|
|
|
|
rigFollowMenuItem = pMenu->AppendCheckItem(wxID_RIG_FOLLOW, wxT("Follow Rig"));
|
|
rigFollowMenuItem->Check(wxGetApp().getConfig()->getRigFollowMode());
|
|
|
|
rigCenterLockMenuItem = pMenu->AppendCheckItem(wxID_RIG_CENTERLOCK, wxT("Floating Center"));
|
|
rigCenterLockMenuItem->Check(wxGetApp().getConfig()->getRigCenterLock());
|
|
|
|
rigFollowModemMenuItem = pMenu->AppendCheckItem(wxID_RIG_FOLLOW_MODEM, wxT("Track Modem"));
|
|
rigFollowModemMenuItem->Check(wxGetApp().getConfig()->getRigFollowModem());
|
|
|
|
rigModelMenu = new wxMenu;
|
|
RigList &rl = RigThread::enumerate();
|
|
numRigs = rl.size();
|
|
|
|
std::map<string, int> mfgCount;
|
|
std::map<string, wxMenu *> mfgMenu;
|
|
for (auto ri : rl) {
|
|
mfgCount[ri->mfg_name]++;
|
|
}
|
|
|
|
int modelMenuId = wxID_RIG_MODEL_BASE;
|
|
|
|
for (auto ri : rl) {
|
|
string modelString(ri->mfg_name);
|
|
modelString.append(" ");
|
|
modelString.append(ri->model_name);
|
|
|
|
wxMenu *parentMenu = nullptr;
|
|
|
|
if (mfgCount[ri->mfg_name] > 1) {
|
|
if (mfgMenu.find(ri->mfg_name) == mfgMenu.end()) {
|
|
rigModelMenu->AppendSubMenu(mfgMenu[ri->mfg_name] = new wxMenu(), ri->mfg_name);
|
|
}
|
|
parentMenu = mfgMenu[ri->mfg_name];
|
|
} else {
|
|
parentMenu = rigModelMenu;
|
|
}
|
|
|
|
rigModelMenuItems[ri->rig_model] = parentMenu->AppendCheckItem(modelMenuId, modelString, ri->copyright);
|
|
|
|
if (rigModel == ri->rig_model) {
|
|
rigModelMenuItems[ri->rig_model]->Check(true);
|
|
}
|
|
|
|
modelMenuId++;
|
|
}
|
|
|
|
pMenu->AppendSubMenu(rigModelMenu, wxT("Model"));
|
|
|
|
auto *rigSerialMenu = new wxMenu;
|
|
|
|
rigSerialRates.push_back(1200);
|
|
rigSerialRates.push_back(2400);
|
|
rigSerialRates.push_back(4800);
|
|
rigSerialRates.push_back(9600);
|
|
rigSerialRates.push_back(19200);
|
|
rigSerialRates.push_back(38400);
|
|
rigSerialRates.push_back(57600);
|
|
rigSerialRates.push_back(115200);
|
|
rigSerialRates.push_back(128000);
|
|
rigSerialRates.push_back(256000);
|
|
|
|
int rateMenuId = wxID_RIG_SERIAL_BASE;
|
|
for (auto rate_i : rigSerialRates) {
|
|
string rateString;
|
|
rateString.append(std::to_string(rate_i));
|
|
rateString.append(" baud");
|
|
|
|
rigSerialMenuItems[rate_i] = rigSerialMenu->AppendRadioItem(rateMenuId, rateString, wxT("Description?"));
|
|
|
|
if (rigSerialRate == rate_i) {
|
|
rigSerialMenuItems[rate_i]->Check(true);
|
|
}
|
|
|
|
rateMenuId++;
|
|
}
|
|
|
|
pMenu->AppendSubMenu(rigSerialMenu, wxT("Serial Rate"));
|
|
|
|
rigPortMenuItem = pMenu->Append(wxID_RIG_PORT, wxT("Control Port"));
|
|
|
|
return pMenu;
|
|
}
|
|
#endif
|
|
|
|
|
|
ScopeCanvas *AppFrame::makeScopeCanvas(wxPanel *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new ScopeCanvas(parent, attribList);
|
|
pCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum, 'B' to toggle decibels display.");
|
|
pCanvas->SetMinSize(wxSize(128, -1));
|
|
return pCanvas;
|
|
}
|
|
|
|
wxMenu *AppFrame::makeDisplayMenu() {
|
|
|
|
//Add Display menu
|
|
auto *dispMenu = new wxMenu;
|
|
auto *fontMenu = new wxMenu;
|
|
|
|
int fontScale = wxGetApp().getConfig()->getFontScale();
|
|
|
|
fontMenu->AppendRadioItem(wxID_DISPLAY_BASE, "Default")->Check(GLFont::GLFONT_SCALE_NORMAL == fontScale);
|
|
fontMenu->AppendRadioItem(wxID_DISPLAY_BASE + 1, "1.5x")->Check(GLFont::GLFONT_SCALE_MEDIUM == fontScale);
|
|
fontMenu->AppendRadioItem(wxID_DISPLAY_BASE + 2, "2.0x")->Check(GLFont::GLFONT_SCALE_LARGE == fontScale);
|
|
|
|
dispMenu->AppendSubMenu(fontMenu, "&Text Size");
|
|
|
|
auto *themeMenu = new wxMenu;
|
|
|
|
int themeId = wxGetApp().getConfig()->getTheme();
|
|
|
|
themeMenu->AppendRadioItem(wxID_THEME_DEFAULT, "Default")->Check(themeId==COLOR_THEME_DEFAULT);
|
|
themeMenu->AppendRadioItem(wxID_THEME_DEFAULT_JET, "Default (Jet Waterfall)")->Check(themeId == COLOR_THEME_DEFAULT_JET);
|
|
themeMenu->AppendRadioItem(wxID_THEME_RADAR, "RADAR")->Check(themeId==COLOR_THEME_RADAR);
|
|
themeMenu->AppendRadioItem(wxID_THEME_BW, "Black & White")->Check(themeId==COLOR_THEME_BW);
|
|
themeMenu->AppendRadioItem(wxID_THEME_SHARP, "Sharp")->Check(themeId==COLOR_THEME_SHARP);
|
|
themeMenu->AppendRadioItem(wxID_THEME_RAD, "Rad")->Check(themeId==COLOR_THEME_RAD);
|
|
themeMenu->AppendRadioItem(wxID_THEME_TOUCH, "Touch")->Check(themeId==COLOR_THEME_TOUCH);
|
|
themeMenu->AppendRadioItem(wxID_THEME_HD, "HD")->Check(themeId==COLOR_THEME_HD);
|
|
|
|
dispMenu->AppendSubMenu(themeMenu, wxT("&Color Scheme"));
|
|
|
|
hideBookmarksItem = dispMenu->AppendCheckItem(wxID_DISPLAY_BOOKMARKS, wxT("Hide Bookmarks"));
|
|
hideBookmarksItem->Check(!wxGetApp().getConfig()->getBookmarksVisible());
|
|
|
|
return dispMenu;
|
|
}
|
|
|
|
wxMenu *AppFrame::makeAudioSampleRateMenu() {
|
|
// Audio Sample Rates
|
|
auto *pMenu = new wxMenu;
|
|
|
|
auto outputDevices = wxGetApp().getDemodMgr().getOutputDevices();
|
|
|
|
#define NUM_RATES_DEFAULT 4
|
|
unsigned int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 };
|
|
|
|
for (auto & outputDevice : outputDevices) {
|
|
unsigned int desired_rate = 0;
|
|
unsigned int desired_rank = NUM_RATES_DEFAULT + 1;
|
|
|
|
for (unsigned int & sampleRate : outputDevice.second.sampleRates) {
|
|
for (unsigned int i = 0; i < NUM_RATES_DEFAULT; i++) {
|
|
if (desired_rates[i] == sampleRate) {
|
|
if (desired_rank > i) {
|
|
desired_rank = i;
|
|
desired_rate = sampleRate;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (desired_rank > NUM_RATES_DEFAULT) {
|
|
desired_rate = outputDevice.second.sampleRates.back();
|
|
}
|
|
AudioThread::deviceSampleRate[outputDevice.first] = desired_rate;
|
|
}
|
|
|
|
for (auto & outputDevice : outputDevices) {
|
|
int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * outputDevice.first;
|
|
auto *subMenu = new wxMenu;
|
|
pMenu->AppendSubMenu(subMenu, outputDevice.second.name, wxT("Description?"));
|
|
|
|
int j = 0;
|
|
for (auto srate = outputDevice.second.sampleRates.begin(); srate != outputDevice.second.sampleRates.end();
|
|
srate++) {
|
|
stringstream srateName;
|
|
srateName << ((float) (*srate) / 1000.0f) << "kHz";
|
|
wxMenuItem *itm = subMenu->AppendRadioItem(menu_id + j, srateName.str(), wxT("Description?"));
|
|
|
|
if ((int)(*srate) == AudioThread::deviceSampleRate[outputDevice.first]) {
|
|
itm->Check(true);
|
|
}
|
|
audioSampleRateMenuItems[menu_id + j] = itm;
|
|
|
|
j++;
|
|
}
|
|
}
|
|
return pMenu;
|
|
}
|
|
|
|
MeterCanvas *AppFrame::makeWaterfallSpeedMeter(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new MeterCanvas(parent, attribList);
|
|
pCanvas->setHelpTip("Waterfall speed, click or drag to adjust (max 1024 lines per second)");
|
|
pCanvas->setMax(sqrt(1024));
|
|
pCanvas->setLevel(sqrt(DEFAULT_WATERFALL_LPS));
|
|
pCanvas->setShowUserInput(false);
|
|
pCanvas->SetMinSize(wxSize(12, 24));
|
|
return pCanvas;
|
|
}
|
|
|
|
WaterfallCanvas *AppFrame::makeWaterfall(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new WaterfallCanvas(parent, attribList);
|
|
pCanvas->setup(DEFAULT_FFT_SIZE, DEFAULT_MAIN_WATERFALL_LINES_NB);
|
|
return pCanvas;
|
|
}
|
|
|
|
MeterCanvas * AppFrame::makeSpectrumAvgMeter(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new MeterCanvas(parent, attribList);
|
|
pCanvas->setHelpTip("Spectrum averaging speed, click or drag to adjust.");
|
|
pCanvas->setMax(1.0);
|
|
pCanvas->setLevel(0.65f);
|
|
pCanvas->setShowUserInput(false);
|
|
pCanvas->SetMinSize(wxSize(12, 24));
|
|
return pCanvas;
|
|
}
|
|
|
|
SpectrumCanvas *AppFrame::makeSpectrumCanvas(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new SpectrumCanvas(parent, attribList);
|
|
pCanvas->setShowDb(true);
|
|
pCanvas->setUseDBOfs(true);
|
|
pCanvas->setScaleFactorEnabled(true);
|
|
return pCanvas;
|
|
}
|
|
|
|
ModeSelectorCanvas *AppFrame::makePeakHoldButton(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new ModeSelectorCanvas(parent, attribList);
|
|
pCanvas->addChoice(1, "P");
|
|
pCanvas->setPadding(-1, -1);
|
|
pCanvas->setHighlightColor(RGBA4f(0.2f, 0.8f, 0.2f));
|
|
pCanvas->setHelpTip("Peak Hold Toggle");
|
|
pCanvas->setToggleMode(true);
|
|
pCanvas->setSelection(-1);
|
|
pCanvas->SetMinSize(wxSize(12, 24));
|
|
return pCanvas;
|
|
}
|
|
|
|
ModeSelectorCanvas *AppFrame::makeModemMuteButton(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new ModeSelectorCanvas(parent, attribList);
|
|
pCanvas->addChoice(1, "M");
|
|
pCanvas->setPadding(-1, -1);
|
|
pCanvas->setHighlightColor(RGBA4f(0.8f, 0.2f, 0.2f));
|
|
pCanvas->setHelpTip("Demodulator Mute Toggle");
|
|
pCanvas->setToggleMode(true);
|
|
pCanvas->setSelection(-1);
|
|
pCanvas->SetMinSize(wxSize(12, 28));
|
|
return pCanvas;
|
|
}
|
|
|
|
ModeSelectorCanvas *AppFrame::makeSoloModeButton(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new ModeSelectorCanvas(parent, attribList);
|
|
pCanvas->addChoice(1, "S");
|
|
pCanvas->setPadding(-1, -1);
|
|
pCanvas->setHighlightColor(RGBA4f(0.8f, 0.8f, 0.2f));
|
|
pCanvas->setHelpTip("Solo Mode Toggle");
|
|
pCanvas->setToggleMode(true);
|
|
pCanvas->setSelection(-1);
|
|
pCanvas->SetMinSize(wxSize(12, 28));
|
|
return pCanvas;
|
|
}
|
|
|
|
MeterCanvas *AppFrame::makeModemGainMeter(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new MeterCanvas(parent, attribList);
|
|
pCanvas->setMax(2.0);
|
|
pCanvas->setHelpTip("Current Demodulator Gain Level. Click / Drag to set Gain level.");
|
|
pCanvas->setShowUserInput(false);
|
|
pCanvas->SetMinSize(wxSize(13, 24));
|
|
return pCanvas;
|
|
}
|
|
|
|
TuningCanvas *AppFrame::makeModemTuner(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new TuningCanvas(parent, attribList);
|
|
pCanvas->SetMinClientSize(wxSize(200, 28));
|
|
return pCanvas;
|
|
}
|
|
|
|
ModeSelectorCanvas * AppFrame::makeDeltaLockButton(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new ModeSelectorCanvas(parent, attribList);
|
|
pCanvas->addChoice(1, "V");
|
|
pCanvas->setPadding(-1, -1);
|
|
pCanvas->setHighlightColor(RGBA4f(0.8f, 0.8f, 0.2f));
|
|
pCanvas->setHelpTip("Delta Lock Toggle (V) - Enable to lock modem relative to center frequency.");
|
|
pCanvas->setToggleMode(true);
|
|
pCanvas->setSelection(-1);
|
|
pCanvas->SetMinSize(wxSize(20, 28));
|
|
return pCanvas;
|
|
}
|
|
|
|
MeterCanvas *AppFrame::makeSignalMeter(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new MeterCanvas(parent, attribList);
|
|
pCanvas->setMax(DEMOD_SIGNAL_MAX);
|
|
pCanvas->setMin(DEMOD_SIGNAL_MIN);
|
|
pCanvas->setLevel(DEMOD_SIGNAL_MIN);
|
|
pCanvas->setInputValue(DEMOD_SIGNAL_MIN);
|
|
pCanvas->setHelpTip("Current Signal Level. Click / Drag to set Squelch level. Right-Click to Auto-Zero Squelch");
|
|
pCanvas->SetMinSize(wxSize(12, 24));
|
|
return pCanvas;
|
|
}
|
|
|
|
SpectrumCanvas *AppFrame::makeDemodSpectrumCanvas(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new SpectrumCanvas(parent, attribList);
|
|
pCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
|
|
return pCanvas;
|
|
}
|
|
|
|
WaterfallCanvas *AppFrame::makeWaterfallCanvas(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
auto *pCanvas = new WaterfallCanvas(parent, attribList);
|
|
pCanvas->setup(DEFAULT_DMOD_FFT_SIZE, DEFAULT_DEMOD_WATERFALL_LINES_NB);
|
|
pCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
|
|
pCanvas->setMinBandwidth(8000);
|
|
pCanvas->getVisualDataQueue()->set_max_num_items(3);
|
|
pCanvas->setLinesPerSecond((int)(DEFAULT_DEMOD_WATERFALL_LINES_NB / DEMOD_WATERFALL_DURATION_IN_SECONDS));
|
|
return pCanvas;
|
|
}
|
|
|
|
ModeSelectorCanvas *AppFrame::makeModemSelectorPanel(wxWindow *parent, const wxGLAttributes &attribList) {
|
|
vector<string> modemList = {"FM", "FMS", "NBFM", "AM", "CW", "LSB", "USB", "DSB", "I/Q" };
|
|
|
|
#ifdef CUBICSDR_MODEM_EXCLUDE
|
|
std::string excludeListStr = "" CUBICSDR_MODEM_EXCLUDE;
|
|
std::vector<std::string> excludeList = str_explode(",",excludeListStr);
|
|
for (auto ex_i : excludeList) {
|
|
std::vector<std::string>::iterator found_i = std::find(modemList.begin(),modemList.end(),ex_i);
|
|
if (found_i != modemList.end()) {
|
|
modemList.erase(found_i);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
auto *pCanvas = new ModeSelectorCanvas(parent, attribList);
|
|
|
|
for (const auto& mt_i : modemList) {
|
|
pCanvas->addChoice(mt_i);
|
|
}
|
|
|
|
#ifdef CUBICSDR_MODEM_EXCLUDE
|
|
demodModeSelector->setHelpTip("Use buttons to choose modulation type.");
|
|
#else
|
|
pCanvas->setHelpTip("Choose modulation type: Frequency Modulation (Hotkey F), Amplitude Modulation (A) and Lower (L), Upper (U), Double Side-Band and more.");
|
|
#endif
|
|
|
|
pCanvas->SetMinSize(wxSize(50, -1));
|
|
pCanvas->SetMaxSize(wxSize(50, -1));
|
|
|
|
return pCanvas;
|
|
}
|
|
|
|
AppFrame::~AppFrame() {
|
|
|
|
waterfallDataThread->terminate();
|
|
t_FFTData->join();
|
|
}
|
|
|
|
wxMenu *AppFrame::makeFileMenu() {
|
|
|
|
auto *menu = new wxMenu;
|
|
#ifndef __APPLE__
|
|
#ifdef CUBICSDR_ENABLE_ABOUT_DIALOG
|
|
menu->Append(wxID_ABOUT_CUBICSDR, "About " CUBICSDR_INSTALL_NAME);
|
|
#endif
|
|
#endif
|
|
menu->Append(wxID_SDR_DEVICES, "SDR Devices");
|
|
menu->AppendSeparator();
|
|
menu->Append(wxID_SDR_START_STOP, "Stop / Start Device");
|
|
menu->AppendSeparator();
|
|
|
|
auto *sessionMenu = new wxMenu;
|
|
|
|
sessionMenu->Append(wxID_OPEN, "&Open Session");
|
|
sessionMenu->Append(wxID_SAVE, "&Save Session");
|
|
sessionMenu->Append(wxID_SAVEAS, "Save Session &As..");
|
|
sessionMenu->AppendSeparator();
|
|
sessionMenu->Append(wxID_RESET, "&Reset Session");
|
|
|
|
menu->AppendSubMenu(sessionMenu, "Session");
|
|
|
|
menu->AppendSeparator();
|
|
|
|
auto *bookmarkMenu = new wxMenu;
|
|
|
|
bookmarkMenu->Append(wxID_OPEN_BOOKMARKS, "Open Bookmarks");
|
|
bookmarkMenu->Append(wxID_SAVE_BOOKMARKS, "Save Bookmarks");
|
|
bookmarkMenu->Append(wxID_SAVEAS_BOOKMARKS, "Save Bookmarks As..");
|
|
bookmarkMenu->AppendSeparator();
|
|
bookmarkMenu->Append(wxID_RESET_BOOKMARKS, "Reset Bookmarks");
|
|
|
|
menu->AppendSubMenu(bookmarkMenu, "Bookmarks");
|
|
|
|
#ifndef __APPLE__
|
|
menu->AppendSeparator();
|
|
menu->Append(wxID_CLOSE);
|
|
#else
|
|
#ifdef CUBICSDR_ENABLE_ABOUT_DIALOG
|
|
if (wxApp::s_macAboutMenuItemId != wxID_NONE) {
|
|
wxString aboutLabel;
|
|
aboutLabel.Printf(_("About %s"), CUBICSDR_INSTALL_NAME);
|
|
menu->Append(wxApp::s_macAboutMenuItemId, aboutLabel);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
return menu;
|
|
}
|
|
|
|
wxMenu *AppFrame::makeRecordingMenu() {
|
|
|
|
recordingMenuItems.clear();
|
|
|
|
auto *menu = new wxMenu;
|
|
|
|
recordingMenuItems[wxID_RECORDING_PATH] = menu->Append(wxID_RECORDING_PATH, getSettingsLabel("Set Recording Path", "<Not Set>"));
|
|
|
|
menu->AppendSeparator();
|
|
|
|
//Squelch options as sub-menu:
|
|
auto *subMenu = new wxMenu;
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE] = menu->AppendSubMenu(subMenu, "Squelch");
|
|
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_SILENCE, "Record Silence",
|
|
"Record below squelch-break audio as silence, i.e records as the user may hear.");
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_SKIP] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_SKIP, "Skip Silence",
|
|
"Do not record below squelch-break audio, i.e squelch-break audio parts are packed together.");
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_ALWAYS] = subMenu->AppendRadioItem(wxID_RECORDING_SQUELCH_ALWAYS, "Record Always",
|
|
"Record everything irrespective of the squelch level.");
|
|
|
|
recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT] = menu->Append(wxID_RECORDING_FILE_TIME_LIMIT, getSettingsLabel("File time limit", "<Not Set>"),
|
|
"Creates a new file automatically, each time the recording lasts longer than the limit, named according to the current time.");
|
|
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true);
|
|
|
|
return menu;
|
|
}
|
|
|
|
void AppFrame::updateRecordingMenu() {
|
|
|
|
// Recording path:
|
|
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
|
|
if (recPath.length() > 32) {
|
|
recPath = "..." + recPath.substr(recPath.length() - 32, 32);
|
|
}
|
|
|
|
recordingMenuItems[wxID_RECORDING_PATH]->SetItemLabel(getSettingsLabel("Set Recording Path", recPath.empty() ? "<Not Set>" : recPath));
|
|
|
|
//Squelch options:
|
|
int squelchEnumValue = wxGetApp().getConfig()->getRecordingSquelchOption();
|
|
|
|
if (squelchEnumValue == AudioSinkFileThread::SQUELCH_RECORD_SILENCE) {
|
|
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true);
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Silence"));
|
|
|
|
} else if (squelchEnumValue == AudioSinkFileThread::SQUELCH_SKIP_SILENCE) {
|
|
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_SKIP]->Check(true);
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Skip Silence"));
|
|
|
|
} else if (squelchEnumValue == AudioSinkFileThread::SQUELCH_RECORD_ALWAYS) {
|
|
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_ALWAYS]->Check(true);
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Always"));
|
|
}
|
|
else {
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_SILENCE]->Check(true);
|
|
recordingMenuItems[wxID_RECORDING_SQUELCH_BASE]->SetItemLabel(getSettingsLabel("Squelch", "Record Silence"));
|
|
|
|
}
|
|
|
|
//File time limit:
|
|
int fileTimeLimitSeconds = wxGetApp().getConfig()->getRecordingFileTimeLimit();
|
|
|
|
if (fileTimeLimitSeconds <= 0) {
|
|
|
|
recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT]->SetItemLabel(getSettingsLabel("File time limit","<Not Set>"));
|
|
}
|
|
else {
|
|
recordingMenuItems[wxID_RECORDING_FILE_TIME_LIMIT]->SetItemLabel(getSettingsLabel("File time limit",
|
|
std::to_string(fileTimeLimitSeconds), "s"));
|
|
}
|
|
}
|
|
|
|
void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo_in) {
|
|
devInfo = devInfo_in;
|
|
deviceChanged.store(true);
|
|
}
|
|
|
|
void AppFrame::notifyDeviceChanged() {
|
|
deviceChanged.store(true);
|
|
}
|
|
|
|
void AppFrame::handleUpdateDeviceParams() {
|
|
|
|
if (!deviceChanged.load() || devInfo == nullptr) {
|
|
return;
|
|
}
|
|
|
|
int i = 0;
|
|
SoapySDR::Device *soapyDev = devInfo->getSoapyDevice();
|
|
|
|
// Build settings menu
|
|
auto *newSettingsMenu = new wxMenu;
|
|
showTipMenuItem = newSettingsMenu->AppendCheckItem(wxID_SET_TIPS, "Show Hover Tips");
|
|
showTipMenuItem->Check(wxGetApp().getConfig()->getShowTips());
|
|
|
|
// CPU usage menu:
|
|
performanceMenuItems.clear();
|
|
|
|
auto *subMenu = new wxMenu;
|
|
|
|
performanceMenuItems[wxID_PERF_BASE + (int)AppConfig::PERF_HIGH] = subMenu->AppendRadioItem(wxID_PERF_BASE + (int)AppConfig::PERF_HIGH, "High (+enhanced DSP)");
|
|
performanceMenuItems[wxID_PERF_BASE + (int)AppConfig::PERF_NORMAL] = subMenu->AppendRadioItem(wxID_PERF_BASE + (int)AppConfig::PERF_NORMAL, "Normal");
|
|
performanceMenuItems[wxID_PERF_BASE + (int)AppConfig::PERF_LOW] = subMenu->AppendRadioItem(wxID_PERF_BASE + (int)AppConfig::PERF_LOW, "Low (-slow UI)");
|
|
|
|
AppConfig::PerfModeEnum perfMode = wxGetApp().getConfig()->getPerfMode();
|
|
|
|
if (perfMode == AppConfig::PERF_HIGH) {
|
|
wxGetApp().setChannelizerType(SDRPostThreadChannelizerType::SDRPostPFBCH2);
|
|
} else {
|
|
wxGetApp().setChannelizerType(SDRPostThreadChannelizerType::SDRPostPFBCH);
|
|
}
|
|
|
|
performanceMenuItems[wxID_PERF_BASE + (int)perfMode]->Check(true);
|
|
|
|
wxMenuItem* selectedPerfModeItem = performanceMenuItems[wxID_PERF_BASE + (int)perfMode];
|
|
|
|
performanceMenuItems[wxID_PERF_CURRENT] = newSettingsMenu->AppendSubMenu(subMenu, "CPU usage");
|
|
performanceMenuItems[wxID_PERF_CURRENT]->SetItemLabel(getSettingsLabel("CPU usage", selectedPerfModeItem->GetItemLabel().ToStdString()));
|
|
|
|
newSettingsMenu->AppendSeparator();
|
|
|
|
settingsMenuItems.clear();
|
|
|
|
settingsMenuItems[wxID_SET_DB_OFFSET] = newSettingsMenu->Append(wxID_SET_DB_OFFSET, getSettingsLabel("Power Level Offset", std::to_string(wxGetApp().getConfig()->getDBOffset()), "dB"));
|
|
settingsMenuItems[wxID_SET_FREQ_OFFSET] = newSettingsMenu->Append(wxID_SET_FREQ_OFFSET, getSettingsLabel("Frequency Offset", std::to_string(wxGetApp().getOffset() / 1000 ) , "KHz"));
|
|
|
|
if (devInfo->hasCORR(SOAPY_SDR_RX, 0)) {
|
|
settingsMenuItems[wxID_SET_PPM] = newSettingsMenu->Append(wxID_SET_PPM, getSettingsLabel("Device PPM", std::to_string(wxGetApp().getPPM()) , "ppm"));
|
|
}
|
|
|
|
if (devInfo->getDriver() != "rtlsdr") {
|
|
iqSwapMenuItem = newSettingsMenu->AppendCheckItem(wxID_SET_IQSWAP, "I/Q Swap");
|
|
iqSwapMenuItem->Check(wxGetApp().getSDRThread()->getIQSwap());
|
|
}
|
|
|
|
agcMenuItem = nullptr;
|
|
if (!soapyDev->listGains(SOAPY_SDR_RX, 0).empty()) {
|
|
agcMenuItem = newSettingsMenu->AppendCheckItem(wxID_AGC_CONTROL, "Automatic Gain");
|
|
agcMenuItem->Check(wxGetApp().getAGCMode());
|
|
} else if (!wxGetApp().getAGCMode()) {
|
|
wxGetApp().setAGCMode(true);
|
|
}
|
|
|
|
//Add an Antenna menu if more than one (RX) antenna, to keep the UI free of useless entries
|
|
antennaNames.clear();
|
|
antennaMenuItems.clear();
|
|
std::vector<std::string> availableAntennas = devInfo->getAntennaNames(SOAPY_SDR_RX, 0);
|
|
|
|
if (availableAntennas.size() > 1) {
|
|
|
|
newSettingsMenu->AppendSeparator();
|
|
|
|
antennaNames = availableAntennas;
|
|
|
|
auto *subMenu = new wxMenu;
|
|
|
|
int i = 0;
|
|
std::string antennaChecked;
|
|
for (const std::string& currentAntenna : availableAntennas) {
|
|
|
|
antennaMenuItems[wxID_ANTENNAS_BASE + i] = subMenu->AppendRadioItem(wxID_ANTENNAS_BASE + i, currentAntenna);
|
|
|
|
if (wxGetApp().getAntennaName() == currentAntenna) {
|
|
antennaMenuItems[wxID_ANTENNAS_BASE + i]->Check(true);
|
|
antennaChecked = currentAntenna;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
antennaMenuItems[wxID_ANTENNA_CURRENT] = newSettingsMenu->AppendSubMenu(subMenu, "Antenna");
|
|
|
|
//Change the Antenna label to indicate the current antenna.
|
|
if (!antennaChecked.empty()) {
|
|
|
|
antennaMenuItems[wxID_ANTENNA_CURRENT]->SetItemLabel(getSettingsLabel("Antenna", antennaChecked));
|
|
}
|
|
}
|
|
|
|
//Add an informative, read-only menu entry to display the current TX selected antenna, if any.
|
|
if (devInfo->getAntennaNames(SOAPY_SDR_TX, 0).size() > 1) {
|
|
|
|
currentTXantennaName = devInfo->getAntennaName(SOAPY_SDR_TX, 0);
|
|
|
|
newSettingsMenu->AppendSeparator();
|
|
|
|
antennaMenuItems[wxID_ANTENNA_CURRENT_TX] = newSettingsMenu->Append(wxID_ANTENNA_CURRENT_TX, getSettingsLabel("TX Antenna", currentTXantennaName));
|
|
antennaMenuItems[wxID_ANTENNA_CURRENT_TX]->Enable(false);
|
|
}
|
|
|
|
//Runtime settings part
|
|
SoapySDR::ArgInfoList::const_iterator args_i;
|
|
settingArgs = soapyDev->getSettingInfo();
|
|
|
|
if (!settingArgs.empty()) {
|
|
newSettingsMenu->AppendSeparator();
|
|
}
|
|
//for each Runtime option of index i:
|
|
for (args_i = settingArgs.begin(); args_i != settingArgs.end(); args_i++) {
|
|
|
|
SoapySDR::ArgInfo arg = (*args_i);
|
|
|
|
std::string currentVal = soapyDev->readSetting(arg.key);
|
|
|
|
if (arg.type == SoapySDR::ArgInfo::BOOL) {
|
|
wxMenuItem *item = newSettingsMenu->AppendCheckItem(wxID_SETTINGS_BASE+i, arg.name, arg.description);
|
|
item->Check(currentVal=="true");
|
|
i++;
|
|
} else if (arg.type == SoapySDR::ArgInfo::INT) {
|
|
|
|
settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->Append(wxID_SETTINGS_BASE + i, getSettingsLabel(arg.name, currentVal, arg.units), arg.description);
|
|
i++;
|
|
} else if (arg.type == SoapySDR::ArgInfo::FLOAT) {
|
|
settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->Append(wxID_SETTINGS_BASE + i, getSettingsLabel(arg.name, currentVal, arg.units), arg.description);
|
|
i++;
|
|
} else if (arg.type == SoapySDR::ArgInfo::STRING) {
|
|
if (!arg.options.empty()) {
|
|
auto *subMenu = new wxMenu;
|
|
int j = 0;
|
|
std::vector<int> subItemsIds;
|
|
//for each of this options
|
|
for (const std::string& optName : arg.options) {
|
|
//by default the option name is the same as the displayed name.
|
|
std::string displayName = optName;
|
|
|
|
if (!arg.optionNames.empty()) {
|
|
displayName = arg.optionNames[j];
|
|
}
|
|
wxMenuItem *item = subMenu->AppendRadioItem(wxID_SETTINGS_BASE+i, displayName);
|
|
subItemsIds.push_back(wxID_SETTINGS_BASE + i);
|
|
|
|
if (currentVal == optName) {
|
|
item->Check(true);
|
|
}
|
|
j++;
|
|
i++;
|
|
}
|
|
settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->AppendSubMenu(subMenu, getSettingsLabel(arg.name, currentVal, arg.units), arg.description);
|
|
//map subitems ids to their parent item !
|
|
for (int currentSubId : subItemsIds) {
|
|
settingsMenuItems[currentSubId] = settingsMenuItems[wxID_SETTINGS_BASE + i];
|
|
}
|
|
} else {
|
|
settingsMenuItems[wxID_SETTINGS_BASE + i] = newSettingsMenu->Append(wxID_SETTINGS_BASE + i, getSettingsLabel(arg.name, currentVal, arg.units), arg.description);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
settingsIdMax = wxID_SETTINGS_BASE+i;
|
|
|
|
menuBar->Replace(1, newSettingsMenu, wxT("&Settings"));
|
|
settingsMenu = newSettingsMenu;
|
|
|
|
// Build/Rebuild the sample rate menu :
|
|
sampleRates = devInfo->getSampleRates(SOAPY_SDR_RX, 0);
|
|
sampleRateMenuItems.clear();
|
|
|
|
auto *newSampleRateMenu = new wxMenu;
|
|
int ofs = 0;
|
|
|
|
//Current sample rate, try to keep it as is.
|
|
long sampleRate = wxGetApp().getSampleRate();
|
|
|
|
long minRate = sampleRates.front();
|
|
long maxRate = sampleRates.back();
|
|
|
|
//If it is beyond limits, make device choose a reasonable value
|
|
if (sampleRate < minRate || sampleRate > maxRate) {
|
|
sampleRate = devInfo->getSampleRateNear(SOAPY_SDR_RX, 0, sampleRate);
|
|
}
|
|
|
|
//Check if a manual entry was previously set: if so, check its value is still within the limits of the device. If not so, reset it.
|
|
if (manualSampleRate > 0 &&
|
|
(manualSampleRate < minRate || manualSampleRate > maxRate)) {
|
|
manualSampleRate = -1;
|
|
}
|
|
|
|
bool checked = false;
|
|
for (long & i : sampleRates) {
|
|
|
|
sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_BASE+ofs, frequencyToStr(i));
|
|
|
|
if (sampleRate == i) {
|
|
sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs]->Check(true);
|
|
checked = true;
|
|
}
|
|
ofs++;
|
|
}
|
|
|
|
//Add a manual sample value radio button, but disabled by default in case the user
|
|
//never ever uses manual entry.
|
|
if (manualSampleRate <= 0) {
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual : N/A"));
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(false);
|
|
}
|
|
else {
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual : ") + frequencyToStr(manualSampleRate));
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true);
|
|
}
|
|
|
|
//We apply the current sample rate after all
|
|
if (!checked) {
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true);
|
|
}
|
|
|
|
//Append a normal button (NOT a radio-button) for manual entry dialog at the end
|
|
newSampleRateMenu->AppendSeparator();
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL_DIALOG] = newSampleRateMenu->Append(wxID_BANDWIDTH_MANUAL_DIALOG, wxT("Manual Entry..."));
|
|
|
|
menuBar->Replace(2, newSampleRateMenu, wxT("Sample &Rate"));
|
|
sampleRateMenu = newSampleRateMenu;
|
|
|
|
if (!wxGetApp().getAGCMode()) {
|
|
gainSpacerItem->Show(true);
|
|
gainSizerItem->Show(true);
|
|
gainSizerItem->SetMinSize(devInfo->getSoapyDevice()->listGains(SOAPY_SDR_RX,0).size()*50,0);
|
|
demodTray->Layout();
|
|
} else {
|
|
gainSpacerItem->Show(false);
|
|
gainSizerItem->Show(false);
|
|
demodTray->Layout();
|
|
}
|
|
|
|
|
|
#if USE_HAMLIB
|
|
if (wxGetApp().getConfig()->getRigEnabled() && !wxGetApp().rigIsActive()) {
|
|
enableRig();
|
|
rigEnableMenuItem->Check(true);
|
|
}
|
|
|
|
std::string deviceId = devInfo->getDeviceId();
|
|
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId);
|
|
|
|
if (wxGetApp().rigIsActive()) {
|
|
rigSDRIF = devConfig->getRigIF(rigModel);
|
|
if (rigSDRIF) {
|
|
wxGetApp().lockFrequency(rigSDRIF);
|
|
} else {
|
|
wxGetApp().unlockFrequency();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
deviceChanged.store(false);
|
|
}
|
|
|
|
#ifdef USE_HAMLIB
|
|
void AppFrame::enableRig() {
|
|
wxGetApp().stopRig();
|
|
wxGetApp().initRig(rigModel, rigPort, rigSerialRate);
|
|
|
|
if (devInfo != nullptr) {
|
|
std::string deviceId = devInfo->getDeviceId();
|
|
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId);
|
|
rigSDRIF = devConfig->getRigIF(rigModel);
|
|
if (rigSDRIF) {
|
|
wxGetApp().lockFrequency(rigSDRIF);
|
|
} else {
|
|
wxGetApp().unlockFrequency();
|
|
}
|
|
} else {
|
|
wxGetApp().unlockFrequency();
|
|
}
|
|
|
|
wxGetApp().getConfig()->setRigEnabled(true);
|
|
}
|
|
|
|
void AppFrame::disableRig() {
|
|
wxGetApp().stopRig();
|
|
wxGetApp().unlockFrequency();
|
|
wxGetApp().getConfig()->setRigEnabled(false);
|
|
}
|
|
|
|
void AppFrame::setRigControlPort(const std::string& portName) {
|
|
if (rigPortDialog == nullptr) {
|
|
return;
|
|
}
|
|
if (!portName.empty()) {
|
|
rigPort = portName;
|
|
|
|
wxGetApp().stopRig();
|
|
wxGetApp().initRig(rigModel, rigPort, rigSerialRate);
|
|
|
|
Refresh();
|
|
}
|
|
rigPortDialog->EndModal(0);
|
|
delete rigPortDialog;
|
|
rigPortDialog = nullptr;
|
|
}
|
|
|
|
|
|
void AppFrame::dismissRigControlPortDialog() {
|
|
rigPortDialog->EndModal(0);
|
|
delete rigPortDialog;
|
|
rigPortDialog = nullptr;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void AppFrame::OnMenu(wxCommandEvent &event) {
|
|
actionOnMenuAbout(event)
|
|
|| actionOnMenuSDRStartStop(event)
|
|
|| actionOnMenuPerformance(event)
|
|
|| actionOnMenuTips(event)
|
|
|| actionOnMenuIQSwap(event)
|
|
|| actionOnMenuFreqOffset(event)
|
|
|| actionOnMenuDBOffset(event)
|
|
|| actionOnMenuAGC(event)
|
|
|| actionOnMenuSDRDevices(event)
|
|
|| actionOnMenuSetPPM(event)
|
|
|| actionOnMenuLoadSave(event)
|
|
|| actionOnMenuReset(event)
|
|
|| actionOnMenuClose(event)
|
|
|| actionOnMenuSettings(event)
|
|
|| actionOnMenuSampleRate(event)
|
|
|| actionOnMenuAudioSampleRate(event)
|
|
|| actionOnMenuRecording(event)
|
|
|| actionOnMenuDisplay(event)
|
|
//Optional : Rig
|
|
|| actionOnMenuRig(event);
|
|
}
|
|
|
|
|
|
bool AppFrame::actionOnMenuDisplay(wxCommandEvent& event) {
|
|
|
|
//by default, is managed.
|
|
bool bManaged = true;
|
|
|
|
if (event.GetId() == wxID_THEME_DEFAULT) {
|
|
ThemeMgr::mgr.setTheme(COLOR_THEME_DEFAULT);
|
|
}
|
|
else if (event.GetId() == wxID_THEME_DEFAULT_JET) {
|
|
ThemeMgr::mgr.setTheme(COLOR_THEME_DEFAULT_JET);
|
|
}
|
|
else if (event.GetId() == wxID_THEME_SHARP) {
|
|
ThemeMgr::mgr.setTheme(COLOR_THEME_SHARP);
|
|
}
|
|
else if (event.GetId() == wxID_THEME_BW) {
|
|
ThemeMgr::mgr.setTheme(COLOR_THEME_BW);
|
|
}
|
|
else if (event.GetId() == wxID_THEME_RAD) {
|
|
ThemeMgr::mgr.setTheme(COLOR_THEME_RAD);
|
|
}
|
|
else if (event.GetId() == wxID_THEME_TOUCH) {
|
|
ThemeMgr::mgr.setTheme(COLOR_THEME_TOUCH);
|
|
}
|
|
else if (event.GetId() == wxID_THEME_HD) {
|
|
ThemeMgr::mgr.setTheme(COLOR_THEME_HD);
|
|
}
|
|
else if (event.GetId() == wxID_THEME_RADAR) {
|
|
ThemeMgr::mgr.setTheme(COLOR_THEME_RADAR);
|
|
}
|
|
//Display : font sizes
|
|
else if (event.GetId() == wxID_DISPLAY_BASE) {
|
|
GLFont::setScale(GLFont::GLFONT_SCALE_NORMAL);
|
|
}
|
|
else if (event.GetId() == wxID_DISPLAY_BASE + 1) {
|
|
GLFont::setScale(GLFont::GLFONT_SCALE_MEDIUM);
|
|
}
|
|
else if (event.GetId() == wxID_DISPLAY_BASE + 2) {
|
|
GLFont::setScale(GLFont::GLFONT_SCALE_LARGE);
|
|
}
|
|
else if (event.GetId() == wxID_DISPLAY_BOOKMARKS) {
|
|
if (hideBookmarksItem->IsChecked()) {
|
|
bookmarkSplitter->Unsplit(bookmarkView);
|
|
bookmarkSplitter->Layout();
|
|
}
|
|
else {
|
|
bookmarkSplitter->SplitVertically(bookmarkView, mainVisSplitter, wxGetApp().getConfig()->getBookmarkSplit());
|
|
bookmarkSplitter->Layout();
|
|
}
|
|
}
|
|
else {
|
|
bManaged = false;
|
|
}
|
|
|
|
//update theme choice in children elements:
|
|
if (event.GetId() >= wxID_THEME_DEFAULT && event.GetId() <= wxID_THEME_RADAR) {
|
|
|
|
gainCanvas->setThemeColors();
|
|
modemProps->updateTheme();
|
|
bookmarkView->updateTheme();
|
|
}
|
|
|
|
//force all windows refresh
|
|
if (bManaged) {
|
|
Refresh();
|
|
}
|
|
|
|
return bManaged;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuReset(wxCommandEvent& event) {
|
|
|
|
if (event.GetId() == wxID_RESET) {
|
|
|
|
wxGetApp().getDemodMgr().terminateAll();
|
|
wxGetApp().setFrequency(100000000);
|
|
wxGetApp().getDemodMgr().setLastDemodulatorType("FM");
|
|
demodModeSelector->setSelection(1);
|
|
wxGetApp().getDemodMgr().setLastMuted(false);
|
|
wxGetApp().getDemodMgr().setLastBandwidth(DEFAULT_DEMOD_BW);
|
|
wxGetApp().getDemodMgr().setLastGain(1.0);
|
|
wxGetApp().getDemodMgr().setLastSquelchLevel(-100);
|
|
waterfallCanvas->setBandwidth(wxGetApp().getSampleRate());
|
|
waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
|
spectrumCanvas->setBandwidth(wxGetApp().getSampleRate());
|
|
spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
|
waterfallDataThread->setLinesPerSecond(DEFAULT_WATERFALL_LPS);
|
|
waterfallCanvas->setLinesPerSecond(DEFAULT_WATERFALL_LPS);
|
|
waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS));
|
|
wxGetApp().getSpectrumProcessor()->setFFTAverageRate(0.65f);
|
|
spectrumAvgMeter->setLevel(0.65f);
|
|
|
|
wxString titleBar = CUBICSDR_TITLE;
|
|
//append the name of the current Device, if any.
|
|
if (wxGetApp().getDevice()) {
|
|
titleBar += " - " + wxGetApp().getDevice()->getName();
|
|
}
|
|
|
|
SetTitle(titleBar);
|
|
currentSessionFile = "";
|
|
currentBookmarkFile = "";
|
|
bookmarkSplitter->Unsplit(bookmarkView);
|
|
bookmarkSplitter->SplitVertically(bookmarkView, mainVisSplitter, wxGetApp().getConfig()->getBookmarkSplit());
|
|
hideBookmarksItem->Check(false);
|
|
//force all windows refresh
|
|
Refresh();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuAbout(wxCommandEvent& event) {
|
|
|
|
#ifdef __APPLE__
|
|
if (event.GetId() == wxApp::s_macAboutMenuItemId) {
|
|
#else
|
|
if (event.GetId() == wxID_ABOUT_CUBICSDR) {
|
|
#endif
|
|
if (aboutDlg != nullptr) {
|
|
aboutDlg->Raise();
|
|
aboutDlg->SetFocus();
|
|
}
|
|
else {
|
|
aboutDlg = new AboutDialog(nullptr);
|
|
aboutDlg->Connect(wxEVT_CLOSE_WINDOW, wxCommandEventHandler(AppFrame::OnAboutDialogClose), nullptr, this);
|
|
|
|
aboutDlg->Show();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) {
|
|
|
|
int antennaIdMax = wxID_ANTENNAS_BASE + antennaNames.size();
|
|
|
|
if (event.GetId() >= wxID_ANTENNAS_BASE && event.GetId() < antennaIdMax) {
|
|
|
|
wxGetApp().setAntennaName(antennaNames[event.GetId() - wxID_ANTENNAS_BASE]);
|
|
|
|
antennaMenuItems[wxID_ANTENNA_CURRENT]->SetItemLabel(getSettingsLabel("Antenna", wxGetApp().getAntennaName()));
|
|
return true;
|
|
}
|
|
else if (event.GetId() >= wxID_SETTINGS_BASE && event.GetId() < settingsIdMax) {
|
|
|
|
int setIdx = event.GetId() - wxID_SETTINGS_BASE;
|
|
int menuIdx = 0;
|
|
|
|
for (auto & arg : settingArgs) {
|
|
if (arg.type == SoapySDR::ArgInfo::STRING && !arg.options.empty() && setIdx >= menuIdx && setIdx < menuIdx + (int)arg.options.size()) {
|
|
int optIdx = setIdx - menuIdx;
|
|
wxGetApp().getSDRThread()->writeSetting(arg.key, arg.options[optIdx]);
|
|
|
|
//update parent menu item label to display the current value
|
|
settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, arg.options[optIdx], arg.units));
|
|
break;
|
|
}
|
|
else if (arg.type == SoapySDR::ArgInfo::STRING && !arg.options.empty()) {
|
|
menuIdx += arg.options.size();
|
|
}
|
|
else if (menuIdx == setIdx) {
|
|
if (arg.type == SoapySDR::ArgInfo::BOOL) {
|
|
wxGetApp().getSDRThread()->writeSetting(arg.key, (wxGetApp().getSDRThread()->readSetting(arg.key) == "true") ? "false" : "true");
|
|
break;
|
|
}
|
|
else if (arg.type == SoapySDR::ArgInfo::STRING) {
|
|
wxString stringVal = wxGetTextFromUser(arg.description, arg.name, wxGetApp().getSDRThread()->readSetting(arg.key));
|
|
|
|
settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, stringVal.ToStdString(), arg.units));
|
|
|
|
if (!stringVal.ToStdString().empty()) {
|
|
wxGetApp().getSDRThread()->writeSetting(arg.key, stringVal.ToStdString());
|
|
}
|
|
break;
|
|
}
|
|
else if (arg.type == SoapySDR::ArgInfo::INT) {
|
|
int currentVal;
|
|
try {
|
|
currentVal = std::stoi(wxGetApp().getSDRThread()->readSetting(arg.key));
|
|
}
|
|
catch (const std::invalid_argument &) {
|
|
currentVal = 0;
|
|
}
|
|
int intVal = wxGetNumberFromUser(arg.description, arg.units, arg.name, currentVal, arg.range.minimum(), arg.range.maximum(), this);
|
|
|
|
settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, std::to_string(intVal), arg.units));
|
|
|
|
if (intVal != -1) {
|
|
wxGetApp().getSDRThread()->writeSetting(arg.key, std::to_string(intVal));
|
|
}
|
|
break;
|
|
}
|
|
else if (arg.type == SoapySDR::ArgInfo::FLOAT) {
|
|
wxString floatVal = wxGetTextFromUser(arg.description, arg.name, wxGetApp().getSDRThread()->readSetting(arg.key));
|
|
try {
|
|
wxGetApp().getSDRThread()->writeSetting(arg.key, floatVal.ToStdString());
|
|
}
|
|
catch (const std::invalid_argument &) {
|
|
// ...
|
|
}
|
|
settingsMenuItems[menuIdx + wxID_SETTINGS_BASE]->SetItemLabel(getSettingsLabel(arg.name, floatVal.ToStdString(), arg.units));
|
|
break;
|
|
}
|
|
else {
|
|
menuIdx++;
|
|
}
|
|
}
|
|
else {
|
|
menuIdx++;
|
|
}
|
|
} //end for
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuAGC(wxCommandEvent& event) {
|
|
|
|
if (event.GetId() == wxID_AGC_CONTROL) {
|
|
|
|
if (wxGetApp().getDevice() == nullptr) {
|
|
agcMenuItem->Check(true);
|
|
return true;
|
|
}
|
|
if (!wxGetApp().getAGCMode()) {
|
|
wxGetApp().setAGCMode(true);
|
|
gainSpacerItem->Show(false);
|
|
gainSizerItem->Show(false);
|
|
demodTray->Layout();
|
|
}
|
|
else {
|
|
wxGetApp().setAGCMode(false);
|
|
gainSpacerItem->Show(true);
|
|
gainSizerItem->Show(true);
|
|
gainSizerItem->SetMinSize(wxGetApp().getDevice()->getSoapyDevice()->listGains(SOAPY_SDR_RX, 0).size() * 40, 0);
|
|
demodTray->Layout();
|
|
gainCanvas->updateGainUI();
|
|
}
|
|
|
|
//full Refresh, some graphical elements has changed
|
|
Refresh();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuSampleRate(wxCommandEvent& event) {
|
|
|
|
if (event.GetId() == wxID_BANDWIDTH_MANUAL) {
|
|
wxGetApp().setSampleRate(manualSampleRate);
|
|
return true;
|
|
}
|
|
else if (event.GetId() == wxID_BANDWIDTH_MANUAL_DIALOG) {
|
|
|
|
int rateHigh = 0, rateLow = 0;
|
|
|
|
SDRDeviceInfo *dev = wxGetApp().getDevice();
|
|
if (dev != nullptr) {
|
|
|
|
std::vector<long> sampleRateList = dev->getSampleRates(SOAPY_SDR_RX, 0);
|
|
|
|
//default
|
|
rateLow = MANUAL_SAMPLE_RATE_MIN;
|
|
rateHigh = MANUAL_SAMPLE_RATE_MAX;
|
|
|
|
if (!sampleRateList.empty()) {
|
|
rateLow = sampleRateList.front();
|
|
rateHigh = sampleRateList.back();
|
|
}
|
|
|
|
long bw = wxGetNumberFromUser("\n" + dev->getName() + "\n\n "
|
|
+ "min: " + std::to_string(rateLow) + " Hz"
|
|
+ ", max: " + std::to_string(rateHigh) + " Hz\n",
|
|
"Sample Rate in Hz",
|
|
"Manual Sample Rate Entry",
|
|
//If a manual sample rate has already been input, recall this one.
|
|
manualSampleRate > 0 ? manualSampleRate : wxGetApp().getSampleRate(),
|
|
rateLow,
|
|
rateHigh,
|
|
this);
|
|
|
|
if (bw != -1) {
|
|
|
|
manualSampleRate = bw;
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true);
|
|
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->SetItemLabel(wxT("Manual : ") + frequencyToStr(manualSampleRate));
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true);
|
|
wxGetApp().setSampleRate(manualSampleRate);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE + (int)sampleRates.size()) {
|
|
|
|
wxGetApp().setSampleRate(sampleRates[event.GetId() - wxID_BANDWIDTH_BASE]);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuAudioSampleRate(wxCommandEvent& event) {
|
|
|
|
if (event.GetId() >= wxID_AUDIO_BANDWIDTH_BASE) {
|
|
|
|
int evId = event.GetId();
|
|
|
|
auto outputDevices = wxGetApp().getDemodMgr().getOutputDevices();
|
|
|
|
int i = 0;
|
|
for (auto & outputDevice : outputDevices) {
|
|
int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * outputDevice.first;
|
|
|
|
int j = 0;
|
|
for (auto srate = outputDevice.second.sampleRates.begin(); srate != outputDevice.second.sampleRates.end();
|
|
srate++) {
|
|
|
|
if (evId == menu_id + j) {
|
|
//audioSampleRateMenuItems[menu_id+j];
|
|
//std::cout << "Would set audio sample rate on device " << mdevices_i->second.name << " (" << mdevices_i->first << ") to " << (*srate) << "Hz" << std::endl;
|
|
AudioThread::setDeviceSampleRate(outputDevice.first, *srate);
|
|
|
|
return true;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuLoadSave(wxCommandEvent& event) {
|
|
|
|
if (event.GetId() == wxID_SAVE) {
|
|
|
|
if (!currentSessionFile.empty()) {
|
|
saveSession(currentSessionFile);
|
|
}
|
|
else {
|
|
wxFileDialog saveFileDialog(this, _("Save XML Session file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
|
if (saveFileDialog.ShowModal() == wxID_CANCEL) {
|
|
return true;
|
|
}
|
|
saveSession(saveFileDialog.GetPath().ToStdString());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (event.GetId() == wxID_OPEN) {
|
|
wxFileDialog openFileDialog(this, _("Open XML Session file"), "", "", "XML files (*.xml)|*.xml", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
|
if (openFileDialog.ShowModal() == wxID_CANCEL) {
|
|
return true;
|
|
}
|
|
loadSession(openFileDialog.GetPath().ToStdString());
|
|
|
|
return true;
|
|
}
|
|
else if (event.GetId() == wxID_SAVEAS) {
|
|
wxFileDialog saveFileDialog(this, _("Save XML Session file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
|
if (saveFileDialog.ShowModal() == wxID_CANCEL) {
|
|
return true;
|
|
}
|
|
saveSession(saveFileDialog.GetPath().ToStdString());
|
|
|
|
return true;
|
|
}
|
|
|
|
//save mecanic for bookmark files
|
|
else if (event.GetId() == wxID_SAVE_BOOKMARKS) {
|
|
|
|
if (!currentBookmarkFile.empty()) {
|
|
wxGetApp().getBookmarkMgr().saveToFile(currentBookmarkFile, false, true);
|
|
}
|
|
else {
|
|
wxFileDialog saveFileDialog(this, _("Save XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
|
if (saveFileDialog.ShowModal() == wxID_CANCEL) {
|
|
return true;
|
|
}
|
|
|
|
// Make sure the file name actually ends in .xml
|
|
std::string fileName = saveFileDialog.GetPath().ToStdString();
|
|
std::string lcFileName = fileName;
|
|
|
|
std::transform(lcFileName.begin(), lcFileName.end(), lcFileName.begin(), ::tolower);
|
|
|
|
if (lcFileName.find_last_of(".xml") != lcFileName.length() - 1) {
|
|
fileName.append(".xml");
|
|
}
|
|
|
|
wxGetApp().getBookmarkMgr().saveToFile(fileName, false, true);
|
|
currentBookmarkFile = fileName;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (event.GetId() == wxID_OPEN_BOOKMARKS) {
|
|
|
|
wxFileDialog openFileDialog(this, _("Open XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
|
if (openFileDialog.ShowModal() == wxID_CANCEL) {
|
|
return true;
|
|
}
|
|
if (wxGetApp().getBookmarkMgr().loadFromFile(openFileDialog.GetPath().ToStdString(), false, true)) {
|
|
|
|
wxGetApp().getBookmarkMgr().updateBookmarks();
|
|
wxGetApp().getBookmarkMgr().updateActiveList();
|
|
|
|
currentBookmarkFile = openFileDialog.GetPath().ToStdString();
|
|
}
|
|
else {
|
|
//failure at loading.
|
|
currentBookmarkFile = "";
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (event.GetId() == wxID_SAVEAS_BOOKMARKS) {
|
|
|
|
wxFileDialog saveFileDialog(this, _("Save XML Bookmark file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
|
if (saveFileDialog.ShowModal() == wxID_CANCEL) {
|
|
return true;
|
|
}
|
|
|
|
// Make sure the file name actually ends in .xml
|
|
std::string fileName = saveFileDialog.GetPath().ToStdString();
|
|
std::string lcFileName = fileName;
|
|
|
|
std::transform(lcFileName.begin(), lcFileName.end(), lcFileName.begin(), ::tolower);
|
|
|
|
if (lcFileName.find_last_of(".xml") != lcFileName.length() - 1) {
|
|
fileName.append(".xml");
|
|
}
|
|
|
|
wxGetApp().getBookmarkMgr().saveToFile(fileName, false, true);
|
|
currentBookmarkFile = fileName;
|
|
|
|
return true;
|
|
}
|
|
else if (event.GetId() == wxID_RESET_BOOKMARKS) {
|
|
|
|
ActionDialog::showDialog(new ActionDialogBookmarkReset());
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuRecording(wxCommandEvent& event) {
|
|
|
|
if (event.GetId() == wxID_RECORDING_PATH) {
|
|
|
|
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
|
|
|
|
wxDirDialog recPathDialog(this, _("File Path for Recordings"), recPath, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
|
|
if (recPathDialog.ShowModal() == wxID_CANCEL) {
|
|
return true;
|
|
}
|
|
|
|
wxGetApp().getConfig()->setRecordingPath(recPathDialog.GetPath().ToStdString());
|
|
|
|
updateRecordingMenu();
|
|
return true;
|
|
|
|
}
|
|
else if (event.GetId() == wxID_RECORDING_SQUELCH_SILENCE) {
|
|
|
|
wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_RECORD_SILENCE);
|
|
|
|
updateRecordingMenu();
|
|
return true;
|
|
}
|
|
else if (event.GetId() == wxID_RECORDING_SQUELCH_SKIP) {
|
|
|
|
wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_SKIP_SILENCE);
|
|
|
|
updateRecordingMenu();
|
|
return true;
|
|
}
|
|
else if (event.GetId() == wxID_RECORDING_SQUELCH_ALWAYS) {
|
|
|
|
wxGetApp().getConfig()->setRecordingSquelchOption(AudioSinkFileThread::SQUELCH_RECORD_ALWAYS);
|
|
|
|
updateRecordingMenu();
|
|
return true;
|
|
}
|
|
else if (event.GetId() == wxID_RECORDING_FILE_TIME_LIMIT) {
|
|
|
|
int currentFileLimitSeconds = wxGetApp().getConfig()->getRecordingFileTimeLimit();
|
|
|
|
long newFileLimit = wxGetNumberFromUser(wxString("\nFile time limit:\n") +
|
|
"\nCreates a new file automatically, each time the recording lasts longer than the limit, named according to the current time.\n\n " +
|
|
+ "min: 0 s (no limit)"
|
|
+ ", max: 36000 s (10 hours)\n",
|
|
"Time in seconds",
|
|
"File Time Limit",
|
|
//If a manual sample rate has already been input, recall this one.
|
|
currentFileLimitSeconds > 0 ? currentFileLimitSeconds : 0,
|
|
0,
|
|
36000,
|
|
this);
|
|
|
|
if (newFileLimit != -1) {
|
|
|
|
wxGetApp().getConfig()->setRecordingFileTimeLimit((int)newFileLimit);
|
|
|
|
updateRecordingMenu();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuRig(wxCommandEvent &event) {
|
|
|
|
bool bManaged = false;
|
|
|
|
#ifdef USE_HAMLIB
|
|
|
|
bool resetRig = false;
|
|
if (event.GetId() >= wxID_RIG_MODEL_BASE && event.GetId() < wxID_RIG_MODEL_BASE + numRigs) {
|
|
|
|
int rigIdx = event.GetId() - wxID_RIG_MODEL_BASE;
|
|
|
|
RigList &rl = RigThread::enumerate();
|
|
rigModel = rl[rigIdx]->rig_model;
|
|
|
|
if (devInfo != nullptr) {
|
|
std::string deviceId = devInfo->getDeviceId();
|
|
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId);
|
|
|
|
rigSDRIF = devConfig->getRigIF(rigModel);
|
|
if (rigSDRIF) {
|
|
wxGetApp().lockFrequency(rigSDRIF);
|
|
} else {
|
|
wxGetApp().unlockFrequency();
|
|
}
|
|
} else {
|
|
wxGetApp().unlockFrequency();
|
|
}
|
|
|
|
resetRig = true;
|
|
bManaged = true;
|
|
|
|
for (auto ri : rigModelMenuItems) {
|
|
if (ri.second->IsChecked()) {
|
|
ri.second->Check(false);
|
|
}
|
|
}
|
|
|
|
rigModelMenuItems[rigModel]->Check(true);
|
|
}
|
|
|
|
int rigSerialIdMax = wxID_RIG_SERIAL_BASE + rigSerialRates.size();
|
|
|
|
if (event.GetId() >= wxID_RIG_SERIAL_BASE && event.GetId() < rigSerialIdMax) {
|
|
int serialIdx = event.GetId() - wxID_RIG_SERIAL_BASE;
|
|
rigSerialRate = rigSerialRates[serialIdx];
|
|
resetRig = true;
|
|
bManaged = true;
|
|
}
|
|
|
|
if (event.GetId() == wxID_RIG_PORT) {
|
|
if (rigPortDialog == nullptr) {
|
|
rigPortDialog = new PortSelectorDialog(this, wxID_ANY, rigPort);
|
|
rigPortDialog->ShowModal();
|
|
}
|
|
}
|
|
|
|
if (event.GetId() == wxID_RIG_TOGGLE) {
|
|
resetRig = false;
|
|
if (!wxGetApp().rigIsActive()) {
|
|
enableRig();
|
|
} else {
|
|
disableRig();
|
|
}
|
|
|
|
bManaged = true;
|
|
}
|
|
|
|
if (event.GetId() == wxID_RIG_SDR_IF) {
|
|
if (devInfo != nullptr) {
|
|
std::string deviceId = devInfo->getDeviceId();
|
|
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId);
|
|
long long freqRigIF = wxGetNumberFromUser("Rig SDR-IF Frequency", "Frequency (Hz)", "Frequency",
|
|
devConfig->getRigIF(rigModel), 0, 2000000000);
|
|
if (freqRigIF != -1) {
|
|
rigSDRIF = freqRigIF;
|
|
devConfig->setRigIF(rigModel, rigSDRIF);
|
|
}
|
|
if (rigSDRIF && wxGetApp().rigIsActive()) {
|
|
wxGetApp().lockFrequency(rigSDRIF);
|
|
} else {
|
|
wxGetApp().unlockFrequency();
|
|
}
|
|
}
|
|
bManaged = true;
|
|
}
|
|
|
|
if (event.GetId() == wxID_RIG_CONTROL) {
|
|
if (wxGetApp().rigIsActive()) {
|
|
RigThread *rt = wxGetApp().getRigThread();
|
|
rt->setControlMode(!rt->getControlMode());
|
|
rigControlMenuItem->Check(rt->getControlMode());
|
|
wxGetApp().getConfig()->setRigControlMode(rt->getControlMode());
|
|
} else {
|
|
wxGetApp().getConfig()->setRigControlMode(rigControlMenuItem->IsChecked());
|
|
}
|
|
bManaged = true;
|
|
}
|
|
|
|
if (event.GetId() == wxID_RIG_FOLLOW) {
|
|
if (wxGetApp().rigIsActive()) {
|
|
RigThread *rt = wxGetApp().getRigThread();
|
|
rt->setFollowMode(!rt->getFollowMode());
|
|
rigFollowMenuItem->Check(rt->getFollowMode());
|
|
wxGetApp().getConfig()->setRigFollowMode(rt->getFollowMode());
|
|
} else {
|
|
wxGetApp().getConfig()->setRigFollowMode(rigFollowMenuItem->IsChecked());
|
|
}
|
|
|
|
bManaged = true;
|
|
}
|
|
|
|
if (event.GetId() == wxID_RIG_CENTERLOCK) {
|
|
if (wxGetApp().rigIsActive()) {
|
|
RigThread *rt = wxGetApp().getRigThread();
|
|
rt->setCenterLock(!rt->getCenterLock());
|
|
rigCenterLockMenuItem->Check(rt->getCenterLock());
|
|
wxGetApp().getConfig()->setRigCenterLock(rt->getCenterLock());
|
|
} else {
|
|
wxGetApp().getConfig()->setRigCenterLock(rigCenterLockMenuItem->IsChecked());
|
|
}
|
|
|
|
bManaged = true;
|
|
}
|
|
|
|
if (event.GetId() == wxID_RIG_FOLLOW_MODEM) {
|
|
if (wxGetApp().rigIsActive()) {
|
|
RigThread *rt = wxGetApp().getRigThread();
|
|
rt->setFollowModem(!rt->getFollowModem());
|
|
rigFollowModemMenuItem->Check(rt->getFollowModem());
|
|
wxGetApp().getConfig()->setRigFollowModem(rt->getFollowModem());
|
|
} else {
|
|
wxGetApp().getConfig()->setRigFollowModem(rigFollowModemMenuItem->IsChecked());
|
|
}
|
|
|
|
bManaged = true;
|
|
}
|
|
|
|
if (wxGetApp().rigIsActive() && resetRig) {
|
|
wxGetApp().stopRig();
|
|
wxGetApp().initRig(rigModel, rigPort, rigSerialRate);
|
|
}
|
|
#endif
|
|
|
|
return bManaged;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuClose(wxCommandEvent &event) {
|
|
if (event.GetId() == wxID_CLOSE || event.GetId() == wxID_EXIT) {
|
|
Close(false);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuSetPPM(wxCommandEvent &event) {
|
|
if (event.GetId() == wxID_SET_PPM) {
|
|
long ofs = wxGetNumberFromUser(
|
|
"Frequency correction for device in PPM.\ni.e. -51 for -51 PPM\n\nNote: you can adjust PPM interactively\nby holding ALT over the frequency tuning bar.\n",
|
|
"Parts per million (PPM)",
|
|
"Frequency Correction", wxGetApp().getPPM(), -1000, 1000, this);
|
|
wxGetApp().setPPM(ofs);
|
|
|
|
settingsMenuItems[wxID_SET_PPM]->SetItemLabel(
|
|
getSettingsLabel("Device PPM", to_string(wxGetApp().getPPM()), "ppm"));
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuSDRDevices(wxCommandEvent &event) {
|
|
if (event.GetId() == wxID_SDR_DEVICES) {
|
|
wxGetApp().deviceSelector();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuDBOffset(wxCommandEvent &event) {
|
|
if (event.GetId() == wxID_SET_DB_OFFSET) {
|
|
long ofs = wxGetNumberFromUser("Shift the displayed RF power level by this amount.\ni.e. -30 for -30 dB",
|
|
"Decibels (dB)",
|
|
"Power Level Offset", wxGetApp().getConfig()->getDBOffset(), -1000, 1000, this);
|
|
if (ofs != -1) {
|
|
wxGetApp().getConfig()->setDBOffset(ofs);
|
|
settingsMenuItems[wxID_SET_DB_OFFSET]->SetItemLabel(
|
|
getSettingsLabel("Power Level Offset", to_string(wxGetApp().getConfig()->getDBOffset()), "dB"));
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuFreqOffset(wxCommandEvent &event) {
|
|
if (event.GetId() == wxID_SET_FREQ_OFFSET) {
|
|
//enter in KHz to accommodate > 2GHz shifts for down/upconverters on 32 bit platforms.
|
|
long ofs = wxGetNumberFromUser(
|
|
"Shift the displayed frequency by this amount of KHz.\ni.e. -125000 for -125 MHz", "Frequency (KHz)",
|
|
"Frequency Offset", (long long) (wxGetApp().getOffset() / 1000.0), -2000000000, 2000000000, this);
|
|
if (ofs != -1) {
|
|
wxGetApp().setOffset((long long) ofs * 1000);
|
|
|
|
settingsMenuItems[wxID_SET_FREQ_OFFSET]->SetItemLabel(
|
|
getSettingsLabel("Frequency Offset", to_string(wxGetApp().getOffset() / 1000), "KHz"));
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuIQSwap(wxCommandEvent &event) {
|
|
if (event.GetId() == wxID_SET_IQSWAP) {
|
|
wxGetApp().getSDRThread()->setIQSwap(!wxGetApp().getSDRThread()->getIQSwap());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuTips(wxCommandEvent &event) {
|
|
if (event.GetId() == wxID_SET_TIPS) {
|
|
wxGetApp().getConfig()->setShowTips(!wxGetApp().getConfig()->getShowTips());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuPerformance(wxCommandEvent &event) {
|
|
if (event.GetId() >= wxID_PERF_BASE && event.GetId() <= wxID_PERF_BASE + (int) AppConfig::PERF_HIGH) {
|
|
|
|
int perfEnumAsInt = event.GetId() - wxID_PERF_BASE;
|
|
AppConfig::PerfModeEnum perfEnumSet = AppConfig::PERF_NORMAL;
|
|
|
|
if (perfEnumAsInt == (int) AppConfig::PERF_HIGH) {
|
|
perfEnumSet = AppConfig::PERF_HIGH;
|
|
|
|
} else if (perfEnumAsInt == (int) AppConfig::PERF_LOW) {
|
|
perfEnumSet = AppConfig::PERF_LOW;
|
|
}
|
|
|
|
wxGetApp().getConfig()->setPerfMode(perfEnumSet);
|
|
|
|
//update Channelizer mode:
|
|
if (perfEnumSet == AppConfig::PERF_HIGH) {
|
|
wxGetApp().setChannelizerType(SDRPostPFBCH2);
|
|
} else {
|
|
wxGetApp().setChannelizerType(SDRPostPFBCH);
|
|
}
|
|
|
|
//update UI
|
|
wxMenuItem *selectedPerfModeItem = performanceMenuItems[event.GetId()];
|
|
performanceMenuItems[wxID_PERF_CURRENT]->SetItemLabel(
|
|
getSettingsLabel("CPU usage", selectedPerfModeItem->GetItemLabel().ToStdString()));
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AppFrame::actionOnMenuSDRStartStop(wxCommandEvent &event) {
|
|
if (event.GetId() == wxID_SDR_START_STOP) {
|
|
if (!wxGetApp().getSDRThread()->isTerminated()) {
|
|
wxGetApp().stopDevice(true, 2000);
|
|
} else {
|
|
SDRDeviceInfo *dev = wxGetApp().getDevice();
|
|
if (dev != nullptr) {
|
|
wxGetApp().setDevice(dev, 0);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void AppFrame::OnClose(wxCloseEvent& event) {
|
|
wxGetApp().closeDeviceSelector();
|
|
if (aboutDlg) {
|
|
aboutDlg->Destroy();
|
|
}
|
|
|
|
if (wxGetApp().getDemodSpectrumProcessor()) {
|
|
wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodSpectrumCanvas->getVisualDataQueue());
|
|
wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodWaterfallCanvas->getVisualDataQueue());
|
|
}
|
|
wxGetApp().getSpectrumProcessor()->removeOutput(spectrumCanvas->getVisualDataQueue());
|
|
|
|
if (saveDisabled) {
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
if (this->GetPosition().y > 0) {
|
|
wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize());
|
|
wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized());
|
|
}
|
|
#else
|
|
wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize());
|
|
wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized());
|
|
#endif
|
|
wxGetApp().getConfig()->setTheme(ThemeMgr::mgr.getTheme());
|
|
wxGetApp().getConfig()->setFontScale(GLFont::getScale());
|
|
wxGetApp().getConfig()->setSnap(wxGetApp().getFrequencySnap());
|
|
wxGetApp().getConfig()->setCenterFreq(wxGetApp().getFrequency());
|
|
wxGetApp().getConfig()->setSpectrumAvgSpeed(wxGetApp().getSpectrumProcessor()->getFFTAverageRate());
|
|
wxGetApp().getConfig()->setWaterfallLinesPerSec(waterfallDataThread->getLinesPerSecond());
|
|
wxGetApp().getConfig()->setManualDevices(SDREnumerator::getManuals());
|
|
wxGetApp().getConfig()->setModemPropsCollapsed(modemProps->isCollapsed());
|
|
wxGetApp().getConfig()->setMainSplit(mainSplitter->GetSashPosition());
|
|
wxGetApp().getConfig()->setVisSplit(mainVisSplitter->GetSashPosition());
|
|
if (!hideBookmarksItem->IsChecked()) wxGetApp().getConfig()->setBookmarkSplit(bookmarkSplitter->GetSashPosition());
|
|
wxGetApp().getConfig()->setBookmarksVisible(!hideBookmarksItem->IsChecked());
|
|
#ifdef USE_HAMLIB
|
|
wxGetApp().getConfig()->setRigEnabled(rigEnableMenuItem->IsChecked());
|
|
wxGetApp().getConfig()->setRigModel(rigModel);
|
|
wxGetApp().getConfig()->setRigRate(rigSerialRate);
|
|
wxGetApp().getConfig()->setRigPort(rigPort);
|
|
wxGetApp().getConfig()->setRigFollowMode(rigFollowMenuItem->IsChecked());
|
|
wxGetApp().getConfig()->setRigControlMode(rigControlMenuItem->IsChecked());
|
|
wxGetApp().getConfig()->setRigCenterLock(rigCenterLockMenuItem->IsChecked());
|
|
wxGetApp().getConfig()->setRigFollowModem(rigFollowModemMenuItem->IsChecked());
|
|
#endif
|
|
wxGetApp().getConfig()->save();
|
|
wxGetApp().getBookmarkMgr().saveToFile("bookmarks.xml");
|
|
event.Skip();
|
|
}
|
|
|
|
void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) {
|
|
new AppFrame();
|
|
}
|
|
|
|
void AppFrame::OnIdle(wxIdleEvent &event) {
|
|
|
|
handleUpdateDeviceParams();
|
|
|
|
handleTXAntennaChange();
|
|
|
|
DemodulatorInstancePtr demod = wxGetApp().getDemodMgr().getCurrentModem();
|
|
|
|
if (demod && demod->isModemInitialized()) {
|
|
handleCurrentModem();
|
|
} else if (demod) {
|
|
// Wait state for current demodulator modem to activate..
|
|
} else {
|
|
handleModeSelector();
|
|
handleGainMeter();
|
|
handleDemodWaterfallSpectrum();
|
|
handleSpectrumWaterfall();
|
|
handleMuteButton();
|
|
}
|
|
|
|
handleScopeProcessor();
|
|
handleScopeSpectrumProcessors();
|
|
handleModemProperties();
|
|
handlePeakHold();
|
|
|
|
#if USE_HAMLIB
|
|
handleRigMenu();
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
if (scopeCanvas && scopeCanvas->HasFocus()) {
|
|
waterfallCanvas->SetFocus();
|
|
}
|
|
#endif
|
|
|
|
if (!this->IsActive()) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
|
} else {
|
|
if (wxGetApp().getConfig()->getPerfMode() == AppConfig::PERF_LOW) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
|
} else {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
}
|
|
}
|
|
|
|
event.RequestMore();
|
|
}
|
|
|
|
void AppFrame::handleTXAntennaChange() {//Refresh the current TX antenna on, if any:
|
|
if ((antennaMenuItems.find(wxID_ANTENNA_CURRENT_TX) != antennaMenuItems.end()) && devInfo) {
|
|
string actualTxAntenna = devInfo->getAntennaName(SOAPY_SDR_TX, 0);
|
|
|
|
if (currentTXantennaName != actualTxAntenna) {
|
|
currentTXantennaName = actualTxAntenna;
|
|
antennaMenuItems[wxID_ANTENNA_CURRENT_TX]->SetItemLabel(
|
|
getSettingsLabel("TX Antenna", currentTXantennaName));
|
|
}
|
|
}
|
|
}
|
|
|
|
#if USE_HAMLIB
|
|
void AppFrame::handleRigMenu() {
|
|
if (rigEnableMenuItem->IsChecked()) {
|
|
if (!wxGetApp().rigIsActive()) {
|
|
rigEnableMenuItem->Check(false);
|
|
wxGetApp().stopRig();
|
|
wxGetApp().getConfig()->setRigEnabled(false);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void AppFrame::handlePeakHold() {
|
|
int peakHoldMode = peakHoldButton->getSelection();
|
|
if (peakHoldButton->modeChanged()) {
|
|
wxGetApp().getSpectrumProcessor()->setPeakHold(peakHoldMode == 1);
|
|
|
|
//make the peak hold act on the current dmod also, like a zoomed-in version.
|
|
if (wxGetApp().getDemodSpectrumProcessor()) {
|
|
wxGetApp().getDemodSpectrumProcessor()->setPeakHold(peakHoldMode == 1);
|
|
}
|
|
peakHoldButton->clearModeChanged();
|
|
}
|
|
}
|
|
|
|
void AppFrame::handleModemProperties() {
|
|
DemodulatorInstancePtr demod = wxGetApp().getDemodMgr().getCurrentModem();
|
|
|
|
if (modemPropertiesUpdated.load() && demod && demod->isModemInitialized()) {
|
|
|
|
//reset notification flag
|
|
modemPropertiesUpdated.store(false);
|
|
|
|
modemProps->initProperties(demod->getModemArgs(), demod);
|
|
modemProps->updateTheme();
|
|
demodTray->Layout();
|
|
modemProps->fitColumns();
|
|
#if ENABLE_DIGITAL_LAB
|
|
if (demod->getModemType() == "digital") {
|
|
auto *outp = (ModemDigitalOutputConsole *) demod->getOutput();
|
|
if (!outp->getDialog()) {
|
|
outp->setTitle(demod->getDemodulatorType() + ": " + frequencyToStr(demod->getFrequency()));
|
|
outp->setDialog(new DigitalConsole(this, outp));
|
|
}
|
|
demod->showOutput();
|
|
}
|
|
#endif
|
|
} else if (!demod && modemPropertiesUpdated.load()) {
|
|
ModemArgInfoList dummyInfo;
|
|
modemProps->initProperties(dummyInfo, nullptr);
|
|
modemProps->updateTheme();
|
|
demodTray->Layout();
|
|
}
|
|
|
|
if (modemProps->IsShown() && modemProps->isCollapsed() && modemProps->GetMinWidth() > 22) {
|
|
modemProps->SetMinSize(wxSize(APPFRAME_MODEMPROPS_MINSIZE, -1));
|
|
modemProps->SetMaxSize(wxSize(APPFRAME_MODEMPROPS_MINSIZE, -1));
|
|
demodTray->Layout();
|
|
modemProps->fitColumns();
|
|
} else if (modemProps->IsShown() && !modemProps->isCollapsed() && modemProps->GetMinWidth() < 200) {
|
|
modemProps->SetMinSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE, -1));
|
|
modemProps->SetMaxSize(wxSize(APPFRAME_MODEMPROPS_MAXSIZE, -1));
|
|
demodTray->Layout();
|
|
modemProps->fitColumns();
|
|
}
|
|
}
|
|
|
|
void AppFrame::handleScopeSpectrumProcessors() {
|
|
SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor();
|
|
|
|
if (spectrumAvgMeter->inputChanged()) {
|
|
float val = spectrumAvgMeter->getInputValue();
|
|
if (val < 0.01) {
|
|
val = 0.01f;
|
|
}
|
|
if (val > 0.99) {
|
|
val = 0.99f;
|
|
}
|
|
spectrumAvgMeter->setLevel(val);
|
|
proc->setFFTAverageRate(val);
|
|
|
|
GetStatusBar()->SetStatusText(
|
|
wxString::Format(wxT("Spectrum averaging speed changed to %0.2f%%."), val * 100.0));
|
|
}
|
|
|
|
SpectrumVisualProcessor *dproc = wxGetApp().getDemodSpectrumProcessor();
|
|
|
|
if (dproc) {
|
|
dproc->setView(demodWaterfallCanvas->getViewState(), demodWaterfallCanvas->getCenterFrequency(),
|
|
demodWaterfallCanvas->getBandwidth());
|
|
}
|
|
|
|
SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor();
|
|
|
|
if (waterfallSpeedMeter->inputChanged()) {
|
|
float val = waterfallSpeedMeter->getInputValue();
|
|
waterfallSpeedMeter->setLevel(val);
|
|
waterfallDataThread->setLinesPerSecond((int) ceil(val * val));
|
|
waterfallCanvas->setLinesPerSecond((int) ceil(val * val));
|
|
GetStatusBar()->SetStatusText(
|
|
wxString::Format(wxT("Waterfall max speed changed to %d lines per second."), (int) ceil(val * val)));
|
|
}
|
|
|
|
wproc->setView(waterfallCanvas->getViewState(), waterfallCanvas->getCenterFrequency(),
|
|
waterfallCanvas->getBandwidth());
|
|
|
|
proc->setView(wproc->isView(), wproc->getCenterFrequency(), wproc->getBandwidth());
|
|
}
|
|
|
|
void AppFrame::handleScopeProcessor() {
|
|
if (scopeCanvas) {
|
|
scopeCanvas->setPPMMode(demodTuner->isAltDown());
|
|
|
|
wxGetApp().getScopeProcessor()->setScopeEnabled(scopeCanvas->scopeVisible());
|
|
wxGetApp().getScopeProcessor()->setSpectrumEnabled(scopeCanvas->spectrumVisible());
|
|
wxGetApp().getAudioVisualQueue()->set_max_num_items(
|
|
(scopeCanvas->scopeVisible() ? 1 : 0) + (scopeCanvas->spectrumVisible() ? 1 : 0));
|
|
|
|
wxGetApp().getScopeProcessor()->run();
|
|
}
|
|
}
|
|
|
|
void AppFrame::handleMuteButton() {
|
|
if (demodMuteButton->modeChanged()) {
|
|
int muteMode = demodMuteButton->getSelection();
|
|
if (muteMode == -1) {
|
|
wxGetApp().getDemodMgr().setLastMuted(false);
|
|
} else if (muteMode == 1) {
|
|
wxGetApp().getDemodMgr().setLastMuted(true);
|
|
}
|
|
demodMuteButton->clearModeChanged();
|
|
}
|
|
}
|
|
|
|
void AppFrame::handleSpectrumWaterfall() {
|
|
if (spectrumCanvas->getViewState() &&
|
|
abs(wxGetApp().getFrequency() - spectrumCanvas->getCenterFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
|
spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
|
waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
|
}
|
|
}
|
|
|
|
void AppFrame::handleDemodWaterfallSpectrum() {
|
|
if (demodWaterfallCanvas && wxGetApp().getFrequency() != demodWaterfallCanvas->getCenterFrequency()) {
|
|
demodWaterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
|
if (demodSpectrumCanvas) {
|
|
demodSpectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
|
}
|
|
}
|
|
}
|
|
|
|
void AppFrame::handleGainMeter() {
|
|
DemodulatorMgr *mgr = &wxGetApp().getDemodMgr();
|
|
|
|
demodGainMeter->setLevel(mgr->getLastGain());
|
|
if (demodSignalMeter->inputChanged()) {
|
|
mgr->setLastSquelchLevel(demodSignalMeter->getInputValue());
|
|
}
|
|
if (demodGainMeter->inputChanged()) {
|
|
mgr->setLastGain(demodGainMeter->getInputValue());
|
|
demodGainMeter->setLevel(demodGainMeter->getInputValue());
|
|
}
|
|
}
|
|
|
|
void AppFrame::handleModeSelector() {
|
|
DemodulatorMgr *mgr = &wxGetApp().getDemodMgr();
|
|
|
|
string dSelection = demodModeSelector->getSelectionLabel();
|
|
#ifdef ENABLE_DIGITAL_LAB
|
|
string dSelectionadv = demodModeSelectorAdv->getSelectionLabel();
|
|
|
|
// basic demodulators
|
|
if (!dSelection.empty() && dSelection != mgr->getLastDemodulatorType()) {
|
|
mgr->setLastDemodulatorType(dSelection);
|
|
mgr->setLastBandwidth(Modem::getModemDefaultSampleRate(dSelection));
|
|
demodTuner->setHalfBand(dSelection == "USB" || dSelection == "LSB");
|
|
demodModeSelectorAdv->setSelection(-1);
|
|
}
|
|
// advanced demodulators
|
|
else if (!dSelectionadv.empty() && dSelectionadv != mgr->getLastDemodulatorType()) {
|
|
mgr->setLastDemodulatorType(dSelectionadv);
|
|
mgr->setLastBandwidth(Modem::getModemDefaultSampleRate(dSelectionadv));
|
|
demodTuner->setHalfBand(false);
|
|
demodModeSelector->setSelection(-1);
|
|
}
|
|
#else
|
|
// basic demodulators
|
|
if (dSelection != "" && dSelection != mgr->getLastDemodulatorType()) {
|
|
mgr->setLastDemodulatorType(dSelection);
|
|
mgr->setLastBandwidth(Modem::getModemDefaultSampleRate(dSelection));
|
|
demodTuner->setHalfBand(dSelection=="USB" || dSelection=="LSB");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void AppFrame::handleCurrentModem() {
|
|
|
|
DemodulatorInstancePtr demod = wxGetApp().getDemodMgr().getCurrentModem();
|
|
|
|
if (demod->isTracking()) {
|
|
if (spectrumCanvas->getViewState()) {
|
|
long long diff =
|
|
abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()) + (demod->getBandwidth() / 2) +
|
|
(demod->getBandwidth() / 4);
|
|
|
|
if (diff > spectrumCanvas->getBandwidth() / 2) {
|
|
if (demod->getBandwidth() > (int) spectrumCanvas->getBandwidth()) {
|
|
diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency());
|
|
} else {
|
|
diff = diff - spectrumCanvas->getBandwidth() / 2;
|
|
}
|
|
spectrumCanvas->moveCenterFrequency(
|
|
(demod->getFrequency() < spectrumCanvas->getCenterFrequency()) ? diff : -diff);
|
|
demod->setTracking(false);
|
|
}
|
|
} else {
|
|
demod->setTracking(false);
|
|
}
|
|
}
|
|
|
|
if (demod->getBandwidth() != wxGetApp().getDemodMgr().getLastBandwidth()) {
|
|
wxGetApp().getDemodMgr().setLastBandwidth(demod->getBandwidth());
|
|
}
|
|
|
|
if (demod.get() != activeDemodulator) {
|
|
demodSignalMeter->setInputValue(demod->getSquelchLevel());
|
|
demodGainMeter->setInputValue(demod->getGain());
|
|
wxGetApp().getDemodMgr().setLastGain(demod->getGain());
|
|
int outputDevice = demod->getOutputDevice();
|
|
if (scopeCanvas) {
|
|
auto outputDevices = wxGetApp().getDemodMgr().getOutputDevices();
|
|
scopeCanvas->setDeviceName(outputDevices[outputDevice].name);
|
|
}
|
|
// outputDeviceMenuItems[outputDevice]->Check(true);
|
|
string dType = demod->getDemodulatorType();
|
|
demodModeSelector->setSelection(dType);
|
|
#ifdef ENABLE_DIGITAL_LAB
|
|
demodModeSelectorAdv->setSelection(dType);
|
|
#endif
|
|
deltaLockButton->setSelection(demod->isDeltaLock() ? 1 : -1);
|
|
demodMuteButton->setSelection(demod->isMuted() ? 1 : -1);
|
|
modemPropertiesUpdated.store(true);
|
|
demodTuner->setHalfBand(dType == "USB" || dType == "LSB");
|
|
}
|
|
if (!demodWaterfallCanvas || demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) {
|
|
long long centerFreq = demod->getFrequency();
|
|
unsigned int demodBw = (unsigned int) ceil((float) demod->getBandwidth() * 2.25);
|
|
|
|
if (demod->getDemodulatorType() == "USB") {
|
|
demodBw /= 2;
|
|
centerFreq += demod->getBandwidth() / 4;
|
|
}
|
|
|
|
if (demod->getDemodulatorType() == "LSB") {
|
|
demodBw /= 2;
|
|
centerFreq -= demod->getBandwidth() / 4;
|
|
}
|
|
|
|
if (demodBw > wxGetApp().getSampleRate() / 2) {
|
|
demodBw = wxGetApp().getSampleRate() / 2;
|
|
}
|
|
if (demodBw < 20000) {
|
|
demodBw = 20000;
|
|
}
|
|
|
|
if (demodWaterfallCanvas && centerFreq != demodWaterfallCanvas->getCenterFrequency()) {
|
|
demodWaterfallCanvas->setCenterFrequency(centerFreq);
|
|
demodSpectrumCanvas->setCenterFrequency(centerFreq);
|
|
}
|
|
|
|
string dSelection = demodModeSelector->getSelectionLabel();
|
|
#ifdef ENABLE_DIGITAL_LAB
|
|
string dSelectionadv = demodModeSelectorAdv->getSelectionLabel();
|
|
|
|
// basic demodulators
|
|
if (!dSelection.empty() && dSelection != demod->getDemodulatorType()) {
|
|
demod->setDemodulatorType(dSelection);
|
|
demodTuner->setHalfBand(dSelection == "USB" || dSelection == "LSB");
|
|
demodModeSelectorAdv->setSelection(-1);
|
|
}
|
|
// advanced demodulators
|
|
else if (!dSelectionadv.empty() && dSelectionadv != demod->getDemodulatorType()) {
|
|
demod->setDemodulatorType(dSelectionadv);
|
|
demodTuner->setHalfBand(false);
|
|
demodModeSelector->setSelection(-1);
|
|
}
|
|
#else
|
|
// basic demodulators
|
|
if (dSelection != "" && dSelection != demod->getDemodulatorType()) {
|
|
demod->setDemodulatorType(dSelection);
|
|
demodTuner->setHalfBand(dSelection=="USB" || dSelection=="LSB");
|
|
}
|
|
#endif
|
|
|
|
int muteMode = demodMuteButton->getSelection();
|
|
if (demodMuteButton->modeChanged()) {
|
|
if (demod->isMuted() && muteMode == -1) {
|
|
demod->setMuted(false);
|
|
} else if (!demod->isMuted() && muteMode == 1) {
|
|
demod->setMuted(true);
|
|
}
|
|
wxGetApp().getDemodMgr().setLastMuted(demod->isMuted());
|
|
demodMuteButton->clearModeChanged();
|
|
} else {
|
|
if (demod->isMuted() && muteMode == -1) {
|
|
demodMuteButton->setSelection(1);
|
|
wxGetApp().getDemodMgr().setLastMuted(demod->isMuted());
|
|
demodMuteButton->Refresh();
|
|
} else if (!demod->isMuted() && muteMode == 1) {
|
|
demodMuteButton->setSelection(-1);
|
|
wxGetApp().getDemodMgr().setLastMuted(demod->isMuted());
|
|
demodMuteButton->Refresh();
|
|
}
|
|
}
|
|
|
|
int deltaMode = deltaLockButton->getSelection();
|
|
if (deltaLockButton->modeChanged()) {
|
|
if (demod->isDeltaLock() && deltaMode == -1) {
|
|
demod->setDeltaLock(false);
|
|
} else if (!demod->isDeltaLock() && deltaMode == 1) {
|
|
demod->setDeltaLockOfs(demod->getFrequency() - wxGetApp().getFrequency());
|
|
demod->setDeltaLock(true);
|
|
}
|
|
wxGetApp().getDemodMgr().setLastDeltaLock(demod->isDeltaLock());
|
|
deltaLockButton->clearModeChanged();
|
|
} else {
|
|
if (demod->isDeltaLock() && deltaMode == -1) {
|
|
deltaLockButton->setSelection(1);
|
|
wxGetApp().getDemodMgr().setLastDeltaLock(true);
|
|
deltaLockButton->Refresh();
|
|
} else if (!demod->isDeltaLock() && deltaMode == 1) {
|
|
deltaLockButton->setSelection(-1);
|
|
wxGetApp().getDemodMgr().setLastDeltaLock(false);
|
|
deltaLockButton->Refresh();
|
|
}
|
|
}
|
|
|
|
int soloMode = soloModeButton->getSelection();
|
|
if (soloModeButton->modeChanged()) {
|
|
if (soloMode == 1) {
|
|
wxGetApp().setSoloMode(true);
|
|
} else {
|
|
wxGetApp().setSoloMode(false);
|
|
}
|
|
soloModeButton->clearModeChanged();
|
|
} else {
|
|
if (wxGetApp().getSoloMode() != (soloMode == 1)) {
|
|
soloModeButton->setSelection(wxGetApp().getSoloMode() ? 1 : -1);
|
|
soloModeButton->Refresh();
|
|
}
|
|
}
|
|
|
|
if (demodWaterfallCanvas) {
|
|
demodWaterfallCanvas->setBandwidth(demodBw);
|
|
demodSpectrumCanvas->setBandwidth(demodBw);
|
|
}
|
|
}
|
|
|
|
demodSignalMeter->setLevel(demod->getSignalLevel());
|
|
demodSignalMeter->setMin(demod->getSignalFloor());
|
|
demodSignalMeter->setMax(demod->getSignalCeil());
|
|
|
|
demodGainMeter->setLevel(demod->getGain());
|
|
if (demodSignalMeter->inputChanged()) {
|
|
demod->setSquelchLevel(demodSignalMeter->getInputValue());
|
|
}
|
|
if (demodGainMeter->inputChanged()) {
|
|
demod->setGain(demodGainMeter->getInputValue());
|
|
demodGainMeter->setLevel(demodGainMeter->getInputValue());
|
|
}
|
|
activeDemodulator = demod.get();
|
|
}
|
|
|
|
|
|
void AppFrame::OnDoubleClickSash(wxSplitterEvent& event)
|
|
{
|
|
wxWindow *a, *b;
|
|
wxSplitterWindow *w = nullptr;
|
|
float g = 0.5;
|
|
|
|
if (event.GetId() == wxID_MAIN_SPLITTER) {
|
|
w = mainSplitter;
|
|
g = 10.0f/37.0f;
|
|
} else if (event.GetId() == wxID_VIS_SPLITTER) {
|
|
w = mainVisSplitter;
|
|
g = 6.0f/25.0f;
|
|
}
|
|
|
|
if (w != nullptr) {
|
|
a = w->GetWindow1();
|
|
b = w->GetWindow2();
|
|
w->Unsplit();
|
|
w->SetSashGravity(g);
|
|
wxSize s = w->GetSize();
|
|
|
|
w->SplitHorizontally(a, b, int(float(s.GetHeight()) * g));
|
|
}
|
|
|
|
event.Veto();
|
|
}
|
|
|
|
void AppFrame::OnUnSplit(wxSplitterEvent& event)
|
|
{
|
|
event.Veto();
|
|
}
|
|
|
|
void AppFrame::OnAboutDialogClose(wxCommandEvent& /* event */) {
|
|
aboutDlg->Destroy();
|
|
aboutDlg = nullptr;
|
|
}
|
|
|
|
void AppFrame::saveSession(const std::string& fileName) {
|
|
wxGetApp().getSessionMgr().saveSession(fileName);
|
|
|
|
currentSessionFile = fileName;
|
|
std::string filePart = fileName.substr(fileName.find_last_of(filePathSeparator) + 1);
|
|
GetStatusBar()->SetStatusText(wxString::Format(wxT("Saved session: %s"), currentSessionFile.c_str()));
|
|
|
|
wxString titleBar = CUBICSDR_TITLE;
|
|
//append the name of the current Device, if any.
|
|
if (wxGetApp().getDevice()) {
|
|
titleBar += " - " + wxGetApp().getDevice()->getName();
|
|
}
|
|
titleBar += ": " + filePart;
|
|
SetTitle(titleBar);
|
|
}
|
|
|
|
bool AppFrame::loadSession(const std::string& fileName) {
|
|
bool result = wxGetApp().getSessionMgr().loadSession(fileName);
|
|
|
|
int sample_rate = wxGetApp().getSampleRate();
|
|
|
|
//scan the available sample rates and see if it matches a predefined one
|
|
int menuIndex = -1;
|
|
for (auto discreteRate : sampleRates) {
|
|
if (discreteRate == sample_rate) {
|
|
menuIndex++;
|
|
//activate Bandwidth Menu entry matching this predefined sample_rate.
|
|
sampleRateMenuItems[wxID_BANDWIDTH_BASE + menuIndex]->Check(true);
|
|
break;
|
|
}
|
|
} //end for
|
|
//this is a manual entry
|
|
if (menuIndex == -1) {
|
|
manualSampleRate = sample_rate;
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true);
|
|
// Apply the manual value, activate the menu entry
|
|
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->SetItemLabel(wxString("Manual Entry : ") + frequencyToStr(sample_rate));
|
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true);
|
|
}
|
|
|
|
deviceChanged.store(true);
|
|
|
|
currentSessionFile = fileName;
|
|
|
|
std::string filePart = fileName.substr(fileName.find_last_of(filePathSeparator) + 1);
|
|
|
|
GetStatusBar()->SetStatusText(wxString::Format(wxT("Loaded session file: %s"), currentSessionFile.c_str()));
|
|
|
|
wxString titleBar = CUBICSDR_TITLE;
|
|
//append the name of the current Device, if any.
|
|
if (wxGetApp().getDevice()) {
|
|
titleBar += " - " + wxGetApp().getDevice()->getName();
|
|
}
|
|
titleBar += ": " + filePart;
|
|
SetTitle(titleBar);
|
|
|
|
wxGetApp().getBookmarkMgr().updateActiveList();
|
|
|
|
return result;
|
|
}
|
|
|
|
FFTVisualDataThread *AppFrame::getWaterfallDataThread() {
|
|
return waterfallDataThread;
|
|
}
|
|
|
|
WaterfallCanvas *AppFrame::getWaterfallCanvas() {
|
|
return waterfallCanvas;
|
|
}
|
|
|
|
SpectrumCanvas *AppFrame::getSpectrumCanvas() {
|
|
return spectrumCanvas;
|
|
}
|
|
void AppFrame::notifyUpdateModemProperties() {
|
|
|
|
modemPropertiesUpdated.store(true);
|
|
}
|
|
|
|
void AppFrame::setMainWaterfallFFTSize(int fftSize) {
|
|
wxGetApp().getSpectrumProcessor()->setFFTSize(fftSize);
|
|
spectrumCanvas->setFFTSize(fftSize);
|
|
waterfallDataThread->getProcessor()->setFFTSize(fftSize);
|
|
waterfallCanvas->setFFTSize(fftSize);
|
|
}
|
|
|
|
void AppFrame::setScopeDeviceName(std::string deviceName) {
|
|
if (scopeCanvas) {
|
|
scopeCanvas->setDeviceName(deviceName);
|
|
}
|
|
}
|
|
|
|
|
|
void AppFrame::refreshGainUI() {
|
|
gainCanvas->updateGainUI();
|
|
gainCanvas->Refresh();
|
|
}
|
|
|
|
bool AppFrame::isUserDemodBusy() {
|
|
return (modemProps && modemProps->isMouseInView())
|
|
|| (waterfallCanvas->isMouseInView() && waterfallCanvas->isMouseDown())
|
|
|| (demodWaterfallCanvas && demodWaterfallCanvas->isMouseInView() && demodWaterfallCanvas->isMouseDown())
|
|
|| (wxGetApp().getDemodMgr().getCurrentModem() &&
|
|
wxGetApp().getDemodMgr().getActiveContextModem() &&
|
|
wxGetApp().getDemodMgr().getCurrentModem() != wxGetApp().getDemodMgr().getActiveContextModem());
|
|
}
|
|
|
|
BookmarkView *AppFrame::getBookmarkView() {
|
|
return bookmarkView;
|
|
}
|
|
|
|
void AppFrame::disableSave(bool state) {
|
|
saveDisabled = state;
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
bool AppFrame::canFocus() {
|
|
return (!wxGetApp().isDeviceSelectorOpen() && (!modemProps || !modemProps->isMouseInView()));
|
|
}
|
|
#endif
|
|
|
|
FrequencyDialog::FrequencyDialogTarget AppFrame::getFrequencyDialogTarget() {
|
|
FrequencyDialog::FrequencyDialogTarget target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_DEFAULT;
|
|
|
|
if (waterfallSpeedMeter->getMouseTracker()->mouseInView()) {
|
|
target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_WATERFALL_LPS;
|
|
}
|
|
else if (spectrumAvgMeter->getMouseTracker()->mouseInView()) {
|
|
target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_SPECTRUM_AVG;
|
|
}
|
|
else if (demodTuner->getMouseTracker()->mouseInView()) {
|
|
switch (demodTuner->getHoverState()) {
|
|
case TuningCanvas::ActiveState::TUNING_HOVER_BW:
|
|
target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_BANDWIDTH;
|
|
break;
|
|
case TuningCanvas::ActiveState::TUNING_HOVER_FREQ:
|
|
target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_FREQ;
|
|
break;
|
|
case TuningCanvas::ActiveState::TUNING_HOVER_CENTER:
|
|
default:
|
|
target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_DEFAULT;
|
|
break;
|
|
|
|
}
|
|
}
|
|
else if (gainCanvas->getMouseTracker()->mouseInView()) {
|
|
target = FrequencyDialog::FrequencyDialogTarget::FDIALOG_TARGET_GAIN;
|
|
}
|
|
return target;
|
|
}
|
|
|
|
void AppFrame::gkNudge(const DemodulatorInstancePtr& demod, int snap) {
|
|
if (demod) {
|
|
auto demodFreq = demod->getFrequency()+snap;
|
|
auto demodBw = demod->getBandwidth();
|
|
|
|
auto ctr = wxGetApp().getFrequency();
|
|
auto bw = wxGetApp().getSampleRate();
|
|
|
|
// Don't let it get nudged out of view.
|
|
if (ctr - (bw / 2) > (demodFreq - demodBw / 2) || ctr + (bw / 2) < (demodFreq + demodBw / 2)) {
|
|
wxGetApp().setFrequency(ctr+(snap*2));
|
|
}
|
|
|
|
demod->setFrequency(demod->getFrequency()+snap);
|
|
demod->updateLabel(demod->getFrequency());
|
|
}
|
|
}
|
|
|
|
int AppFrame::OnGlobalKeyDown(wxKeyEvent &event) {
|
|
if (!this->IsActive()) {
|
|
return -1;
|
|
}
|
|
|
|
#ifdef USE_HAMLIB
|
|
if (rigPortDialog != nullptr) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
if (modemProps && (modemProps->HasFocus() || modemProps->isMouseInView())) {
|
|
return -1;
|
|
}
|
|
|
|
if (bookmarkView && bookmarkView->isMouseInView()) {
|
|
return -1;
|
|
}
|
|
|
|
DemodulatorInstancePtr demod = nullptr;
|
|
|
|
DemodulatorInstancePtr lastDemod = wxGetApp().getDemodMgr().getCurrentModem();
|
|
|
|
int snap = wxGetApp().getFrequencySnap();
|
|
|
|
if (event.ControlDown()) {
|
|
return 1;
|
|
}
|
|
|
|
if (event.ShiftDown()) {
|
|
if (snap != 1) {
|
|
snap /= 2;
|
|
}
|
|
}
|
|
|
|
#ifdef wxHAS_RAW_KEY_CODES
|
|
switch (event.GetRawKeyCode()) {
|
|
case 30:
|
|
gkNudge(lastDemod, snap);
|
|
return 1;
|
|
case 33:
|
|
gkNudge(lastDemod, -snap);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
|
|
switch (event.GetKeyCode()) {
|
|
case WXK_UP:
|
|
case WXK_NUMPAD_UP:
|
|
case WXK_DOWN:
|
|
case WXK_NUMPAD_DOWN:
|
|
case WXK_LEFT:
|
|
case WXK_NUMPAD_LEFT:
|
|
case WXK_RIGHT:
|
|
case WXK_NUMPAD_RIGHT:
|
|
waterfallCanvas->OnKeyDown(event); // TODO: Move the stuff from there to here
|
|
return 1;
|
|
case 'V':
|
|
return 1;
|
|
case ']':
|
|
gkNudge(lastDemod, snap);
|
|
return 1;
|
|
case '[':
|
|
gkNudge(lastDemod, -snap);
|
|
return 1;
|
|
case 'A':
|
|
case 'F':
|
|
case 'L':
|
|
case 'U':
|
|
case 'S':
|
|
case 'P':
|
|
case 'M':
|
|
case 'R':
|
|
return 1;
|
|
case WXK_NUMPAD0:
|
|
case WXK_NUMPAD1:
|
|
case WXK_NUMPAD2:
|
|
case WXK_NUMPAD3:
|
|
case WXK_NUMPAD4:
|
|
case WXK_NUMPAD5:
|
|
case WXK_NUMPAD6:
|
|
case WXK_NUMPAD7:
|
|
case WXK_NUMPAD8:
|
|
case WXK_NUMPAD9:
|
|
wxGetApp().showFrequencyInput(getFrequencyDialogTarget(), std::to_string(event.GetKeyCode() - WXK_NUMPAD0));
|
|
return 1;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
wxGetApp().showFrequencyInput(getFrequencyDialogTarget(), std::to_string(event.GetKeyCode() - '0'));
|
|
return 1;
|
|
break;
|
|
case WXK_TAB:
|
|
lastDemod = wxGetApp().getDemodMgr().getCurrentModem();
|
|
if (!lastDemod) {
|
|
break;
|
|
}
|
|
if (event.ShiftDown()) {
|
|
demod = wxGetApp().getDemodMgr().getPreviousDemodulator(lastDemod);
|
|
} else {
|
|
demod = wxGetApp().getDemodMgr().getNextDemodulator(lastDemod);
|
|
}
|
|
if (demod) {
|
|
wxGetApp().getDemodMgr().setActiveDemodulator(nullptr);
|
|
wxGetApp().getDemodMgr().setActiveDemodulator(demod, false);
|
|
}
|
|
return 1;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//Re-dispatch the key events if the mouse cursor is within a given
|
|
//widget region, effectively activating its specific key shortcuts,
|
|
//which else are overridden by this global key handler.
|
|
if (demodTuner->getMouseTracker()->mouseInView()) {
|
|
demodTuner->OnKeyDown(event);
|
|
} else if (waterfallCanvas->getMouseTracker()->mouseInView()) {
|
|
waterfallCanvas->OnKeyDown(event);
|
|
}
|
|
else if (spectrumCanvas->getMouseTracker()->mouseInView()) {
|
|
spectrumCanvas->OnKeyDown(event);
|
|
}
|
|
else if (scopeCanvas->getMouseTracker()->mouseInView()) {
|
|
scopeCanvas->OnKeyDown(event);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) {
|
|
if (!this->IsActive()) {
|
|
return -1;
|
|
}
|
|
|
|
#ifdef USE_HAMLIB
|
|
if (rigPortDialog != nullptr) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
if (modemProps && (modemProps->HasFocus() || modemProps->isMouseInView())) {
|
|
return -1;
|
|
}
|
|
|
|
if (bookmarkView && bookmarkView->isMouseInView()) {
|
|
return -1;
|
|
}
|
|
|
|
if (event.ControlDown()) {
|
|
return 1;
|
|
}
|
|
|
|
DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveContextModem();
|
|
DemodulatorInstancePtr lastDemod = wxGetApp().getDemodMgr().getCurrentModem();
|
|
|
|
#ifdef wxHAS_RAW_KEY_CODES
|
|
switch (event.GetRawKeyCode()) {
|
|
case 30:
|
|
return 1;
|
|
case 33:
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
switch (event.GetKeyCode()) {
|
|
case WXK_SPACE:
|
|
if (!demodTuner->getMouseTracker()->mouseInView()) {
|
|
wxGetApp().showFrequencyInput(getFrequencyDialogTarget());
|
|
return 1;
|
|
}
|
|
break;
|
|
case WXK_UP:
|
|
case WXK_NUMPAD_UP:
|
|
case WXK_DOWN:
|
|
case WXK_NUMPAD_DOWN:
|
|
case WXK_LEFT:
|
|
case WXK_NUMPAD_LEFT:
|
|
case WXK_RIGHT:
|
|
case WXK_NUMPAD_RIGHT:
|
|
waterfallCanvas->OnKeyUp(event);
|
|
return 1;
|
|
case 'V':
|
|
if (activeDemod) {
|
|
lastDemod = activeDemod;
|
|
}
|
|
if (lastDemod && lastDemod->isDeltaLock()) {
|
|
lastDemod->setDeltaLock(false);
|
|
} else if (lastDemod) {
|
|
lastDemod->setDeltaLockOfs(lastDemod->getFrequency() - wxGetApp().getFrequency());
|
|
lastDemod->setDeltaLock(true);
|
|
}
|
|
break;
|
|
case 'A':
|
|
demodModeSelector->setSelection("AM");
|
|
return 1;
|
|
break;
|
|
case 'F':
|
|
if (demodModeSelector->getSelectionLabel() == "FM") {
|
|
demodModeSelector->setSelection("FMS");
|
|
} else if (demodModeSelector->getSelectionLabel() == "FMS") {
|
|
demodModeSelector->setSelection("NBFM");
|
|
} else if (demodModeSelector->getSelectionLabel() == "NBFM") {
|
|
demodModeSelector->setSelection("FM");
|
|
}
|
|
return 1;
|
|
break;
|
|
case 'L':
|
|
demodModeSelector->setSelection("LSB");
|
|
return 1;
|
|
break;
|
|
case 'U':
|
|
demodModeSelector->setSelection("USB");
|
|
return 1;
|
|
break;
|
|
case 'S':
|
|
wxGetApp().setSoloMode(!wxGetApp().getSoloMode());
|
|
return 1;
|
|
break;
|
|
case 'R':
|
|
if (event.ShiftDown()) {
|
|
toggleAllActiveDemodRecording();
|
|
} else {
|
|
toggleActiveDemodRecording();
|
|
}
|
|
break;
|
|
case 'P':
|
|
wxGetApp().getSpectrumProcessor()->setPeakHold(!wxGetApp().getSpectrumProcessor()->getPeakHold());
|
|
if (wxGetApp().getDemodSpectrumProcessor()) {
|
|
wxGetApp().getDemodSpectrumProcessor()->setPeakHold(wxGetApp().getSpectrumProcessor()->getPeakHold());
|
|
}
|
|
peakHoldButton->setSelection(wxGetApp().getSpectrumProcessor()->getPeakHold()?1:0);
|
|
peakHoldButton->clearModeChanged();
|
|
break;
|
|
case ']':
|
|
case '[':
|
|
return 1;
|
|
case 'M':
|
|
if (activeDemod) {
|
|
lastDemod = activeDemod;
|
|
}
|
|
if (lastDemod) {
|
|
lastDemod->setMuted(!lastDemod->isMuted());
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//Re-dispatch the key events if the mouse cursor is within a given
|
|
//widget region, effectively activating its specific key shortcuts,
|
|
//which else are overridden by this global key handler.
|
|
if (demodTuner->getMouseTracker()->mouseInView()) {
|
|
demodTuner->OnKeyUp(event);
|
|
}
|
|
else if (waterfallCanvas->getMouseTracker()->mouseInView()) {
|
|
waterfallCanvas->OnKeyUp(event);
|
|
}
|
|
else if (spectrumCanvas->getMouseTracker()->mouseInView()) {
|
|
spectrumCanvas->OnKeyUp(event);
|
|
}
|
|
else if (scopeCanvas->getMouseTracker()->mouseInView()) {
|
|
scopeCanvas->OnKeyUp(event);
|
|
}
|
|
|
|
// TODO: Catch key-ups outside of original target
|
|
|
|
return 1;
|
|
}
|
|
|
|
void AppFrame::toggleActiveDemodRecording() {
|
|
if (!wxGetApp().getConfig()->verifyRecordingPath()) {
|
|
return;
|
|
}
|
|
|
|
DemodulatorInstancePtr activeDemod = wxGetApp().getDemodMgr().getActiveContextModem();
|
|
|
|
if (activeDemod) {
|
|
activeDemod->setRecording(!activeDemod->isRecording());
|
|
wxGetApp().getBookmarkMgr().updateActiveList();
|
|
}
|
|
}
|
|
|
|
void AppFrame::toggleAllActiveDemodRecording() {
|
|
if (!wxGetApp().getConfig()->verifyRecordingPath()) {
|
|
return;
|
|
}
|
|
|
|
// All demods, irrespective of their active state:
|
|
// recording will start eventually when a demod come in range.
|
|
auto allDemods = wxGetApp().getDemodMgr().getDemodulators();
|
|
|
|
//by default, do a false => true for all:
|
|
bool stateToSet = true;
|
|
|
|
for (const auto& i : allDemods) {
|
|
if (i->isRecording()) {
|
|
stateToSet = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const auto& i : allDemods) {
|
|
|
|
i->setRecording(stateToSet);
|
|
}
|
|
//this effectively refresh the BookmarkView buttons, including Recording buttons.
|
|
wxGetApp().getBookmarkMgr().updateActiveList();
|
|
}
|
|
|
|
void AppFrame::setWaterfallLinesPerSecond(int lps) {
|
|
waterfallSpeedMeter->setUserInputValue(sqrt(lps));
|
|
}
|
|
|
|
void AppFrame::setSpectrumAvgSpeed(double avg) {
|
|
spectrumAvgMeter->setUserInputValue(avg);
|
|
}
|
|
|
|
void AppFrame::setViewState(long long center_freq, int bandwidth) {
|
|
spectrumCanvas->setView(center_freq, bandwidth);
|
|
waterfallCanvas->setView(center_freq, bandwidth);
|
|
}
|
|
|
|
void AppFrame::setViewState() {
|
|
spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
|
waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
|
spectrumCanvas->disableView();
|
|
waterfallCanvas->disableView();
|
|
}
|
|
|
|
|
|
long long AppFrame::getViewCenterFreq() {
|
|
return waterfallCanvas->getCenterFrequency();
|
|
|
|
}
|
|
|
|
|
|
int AppFrame::getViewBandwidth() {
|
|
return waterfallCanvas->getBandwidth();
|
|
}
|
|
|
|
|
|
void AppFrame::setStatusText(wxWindow* window, const std::string& statusText) {
|
|
GetStatusBar()->SetStatusText(statusText);
|
|
if (wxGetApp().getConfig()->getShowTips()) {
|
|
if (statusText != lastToolTip) {
|
|
wxToolTip::Enable(false);
|
|
window->SetToolTip(statusText);
|
|
lastToolTip = statusText;
|
|
wxToolTip::SetDelay(1000);
|
|
wxToolTip::Enable(true);
|
|
}
|
|
}
|
|
else {
|
|
window->SetToolTip("");
|
|
lastToolTip = "";
|
|
}
|
|
}
|
|
|
|
void AppFrame::setStatusText(const std::string& statusText, int value) {
|
|
GetStatusBar()->SetStatusText(
|
|
wxString::Format(statusText.c_str(), wxNumberFormatter::ToString((long)value, wxNumberFormatter::Style_WithThousandsSep)));
|
|
}
|
|
|
|
wxString AppFrame::getSettingsLabel(const std::string& settingsName,
|
|
const std::string& settingsValue,
|
|
const std::string& settingsSuffix) {
|
|
|
|
size_t itemStringSize = 40;
|
|
int justifValueSize = itemStringSize - settingsName.length() - 1;
|
|
|
|
std::stringstream full_label;
|
|
|
|
full_label << settingsName + " : ";
|
|
full_label << std::right << std::setw(justifValueSize);
|
|
|
|
if (settingsSuffix.empty()) {
|
|
full_label << settingsValue;
|
|
} else {
|
|
full_label << settingsValue + " " + settingsSuffix;
|
|
}
|
|
|
|
return wxString(full_label.str());
|
|
}
|