CubicSDR/src/AppFrame.cpp

1356 lines
54 KiB
C++
Raw Normal View History

#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>
2014-11-16 16:51:45 -05:00
#include "AudioThread.h"
#include "CubicSDR.h"
2015-01-09 17:17:56 -05:00
#include "DataTree.h"
#include "ColorTheme.h"
#include "DemodulatorMgr.h"
2014-11-21 21:50:14 -05:00
#include <thread>
#include <wx/panel.h>
2015-03-08 17:31:43 -04:00
#ifdef __linux__
#include "CubicSDR.xpm"
#endif
wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
//EVT_MENU(wxID_NEW, AppFrame::OnNewWindow)
2015-07-15 00:32:36 -04:00
EVT_CLOSE(AppFrame::OnClose)
EVT_MENU(wxID_ANY, AppFrame::OnMenu)
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread)
EVT_IDLE(AppFrame::OnIdle)
EVT_SPLITTER_DCLICK(wxID_ANY, AppFrame::OnDoubleClickSash)
EVT_SPLITTER_UNSPLIT(wxID_ANY, AppFrame::OnUnSplit)
wxEND_EVENT_TABLE()
AppFrame::AppFrame() :
wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) {
2015-03-08 20:17:19 -04:00
#ifdef __linux__
SetIcon(wxICON(cubicsdr));
#endif
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
2014-12-28 20:55:05 -05:00
wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL);
demodTray = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL);
int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
mainSplitter = new wxSplitterWindow( this, wxID_MAIN_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE );
mainSplitter->SetSashGravity(12.0/37.0);
mainSplitter->SetMinimumPaneSize(1);
wxPanel *demodPanel = new wxPanel(mainSplitter, wxID_ANY);
gainCanvas = new GainCanvas(demodPanel, attribList);
gainSizerItem = demodTray->Add(gainCanvas, 0, wxEXPAND | wxALL, 0);
gainSizerItem->Show(false);
gainSpacerItem = demodTray->AddSpacer(1);
gainSpacerItem->Show(false);
demodModeSelector = new ModeSelectorCanvas(demodPanel, attribList);
demodModeSelector->addChoice("FM");
demodModeSelector->addChoice("FMS");
demodModeSelector->addChoice("AM");
demodModeSelector->addChoice("LSB");
demodModeSelector->addChoice("USB");
demodModeSelector->addChoice("DSB");
demodModeSelector->addChoice("I/Q");
demodModeSelector->setSelection("FM");
2015-04-02 21:16:03 -04:00
demodModeSelector->setHelpTip("Choose modulation type: Frequency Modulation, Amplitude Modulation and Lower, Upper or Double Side-Band.");
demodModeSelector->SetMinSize(wxSize(40,-1));
demodModeSelector->SetMaxSize(wxSize(40,-1));
2015-01-05 19:12:16 -05:00
demodTray->Add(demodModeSelector, 2, wxEXPAND | wxALL, 0);
2015-11-15 13:52:54 -05:00
#ifdef ENABLE_DIGITAL_LAB
demodModeSelectorAdv = new ModeSelectorCanvas(demodPanel, attribList);
demodModeSelectorAdv->addChoice("ASK");
demodModeSelectorAdv->addChoice("APSK");
demodModeSelectorAdv->addChoice("BPSK");
demodModeSelectorAdv->addChoice("DPSK");
demodModeSelectorAdv->addChoice("PSK");
demodModeSelectorAdv->addChoice("FSK");
demodModeSelectorAdv->addChoice("GMSK");
demodModeSelectorAdv->addChoice("OOK");
demodModeSelectorAdv->addChoice("ST");
demodModeSelectorAdv->addChoice("SQAM");
demodModeSelectorAdv->addChoice("QAM");
demodModeSelectorAdv->addChoice("QPSK");
demodModeSelectorAdv->setHelpTip("Choose advanced modulation types.");
demodModeSelectorAdv->SetMinSize(wxSize(40,-1));
demodModeSelectorAdv->SetMaxSize(wxSize(40,-1));
demodTray->Add(demodModeSelectorAdv, 3, wxEXPAND | wxALL, 0);
2015-11-24 23:32:40 -05:00
#endif
modemPropertiesUpdated.store(false);
modemProps = new ModemProperties(demodPanel, wxID_ANY);
modemProps->SetMinSize(wxSize(200,-1));
modemProps->SetMaxSize(wxSize(200,-1));
modemProps->Hide();
demodTray->Add(modemProps, 15, wxEXPAND | wxALL, 0);
2015-11-15 13:52:54 -05:00
wxGetApp().getDemodSpectrumProcessor()->setup(1024);
demodSpectrumCanvas = new SpectrumCanvas(demodPanel, attribList);
2015-07-21 00:04:04 -04:00
demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
2015-01-06 19:15:27 -05:00
demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0);
wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodSpectrumCanvas->getVisualDataQueue());
demodVisuals->AddSpacer(1);
2014-12-28 20:55:05 -05:00
demodWaterfallCanvas = new WaterfallCanvas(demodPanel, attribList);
2015-01-06 19:15:27 -05:00
demodWaterfallCanvas->setup(1024, 128);
2015-07-21 00:04:04 -04:00
demodWaterfallCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas);
2015-12-01 00:59:11 -05:00
demodWaterfallCanvas->setMinBandwidth(8000);
2015-01-01 21:10:54 -05:00
demodSpectrumCanvas->attachWaterfallCanvas(demodWaterfallCanvas);
2015-01-06 19:15:27 -05:00
demodVisuals->Add(demodWaterfallCanvas, 6, wxEXPAND | wxALL, 0);
wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodWaterfallCanvas->getVisualDataQueue());
2015-09-11 00:42:09 -04:00
demodWaterfallCanvas->getVisualDataQueue()->set_max_num_items(3);
2014-12-28 20:55:05 -05:00
demodVisuals->SetMinSize(wxSize(128,-1));
2014-12-31 19:45:01 -05:00
demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0);
demodTray->AddSpacer(1);
2014-12-31 19:45:01 -05:00
demodSignalMeter = new MeterCanvas(demodPanel, attribList);
2015-11-21 15:12:20 -05:00
demodSignalMeter->setMax(DEMOD_SIGNAL_MAX);
demodSignalMeter->setMin(DEMOD_SIGNAL_MIN);
demodSignalMeter->setLevel(DEMOD_SIGNAL_MIN);
demodSignalMeter->setInputValue(DEMOD_SIGNAL_MIN);
demodSignalMeter->setHelpTip("Current Signal Level. Click / Drag to set Squelch level.");
demodSignalMeter->SetMinSize(wxSize(12,24));
2014-12-31 19:45:01 -05:00
demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0);
2014-12-28 20:55:05 -05:00
demodTray->AddSpacer(1);
scopeCanvas = new ScopeCanvas(demodPanel, attribList);
scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum.");
scopeCanvas->SetMinSize(wxSize(128,-1));
demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0);
wxGetApp().getScopeProcessor()->setup(1024);
2015-08-01 11:03:00 -04:00
wxGetApp().getScopeProcessor()->attachOutput(scopeCanvas->getInputQueue());
demodScopeTray->AddSpacer(1);
demodTuner = new TuningCanvas(demodPanel, attribList);
demodTuner->setHelpTip("Testing tuner");
demodTuner->SetMinClientSize(wxSize(200,24));
demodScopeTray->Add(demodTuner, 1, wxEXPAND | wxALL, 0);
demodTray->Add(demodScopeTray, 30, wxEXPAND | wxALL, 0);
demodTray->AddSpacer(1);
2015-01-10 20:33:30 -05:00
wxBoxSizer *demodGainTray = new wxBoxSizer(wxVERTICAL);
demodGainMeter = new MeterCanvas(demodPanel, attribList);
2015-01-10 20:33:30 -05:00
demodGainMeter->setMax(2.0);
demodGainMeter->setHelpTip("Current Demodulator Gain Level. Click / Drag to set Gain level.");
2015-08-12 20:05:57 -04:00
demodGainMeter->setShowUserInput(false);
demodGainMeter->SetMinSize(wxSize(12,24));
demodGainTray->Add(demodGainMeter, 8, wxEXPAND | wxALL, 0);
demodGainTray->AddSpacer(1);
demodMuteButton = new ModeSelectorCanvas(demodPanel, attribList);
demodMuteButton->addChoice(1, "M");
demodMuteButton->setPadding(-1,-1);
demodMuteButton->setHighlightColor(RGBA4f(0.8,0.2,0.2));
demodMuteButton->setHelpTip("Demodulator Mute Toggle");
demodMuteButton->setToggleMode(true);
2015-08-31 20:26:17 -04:00
demodMuteButton->setSelection(-1);
demodMuteButton->SetMinSize(wxSize(12,24));
demodGainTray->Add(demodMuteButton, 1, wxEXPAND | wxALL, 0);
2015-01-10 20:33:30 -05:00
demodTray->Add(demodGainTray, 1, wxEXPAND | wxALL, 0);
demodPanel->SetSizer(demodTray);
// vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0);
// vbox->AddSpacer(1);
mainVisSplitter = new wxSplitterWindow( mainSplitter, wxID_VIS_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE );
mainVisSplitter->SetSashGravity(5.0/25.0);
mainVisSplitter->SetMinimumPaneSize(1);
// mainVisSplitter->Connect( wxEVT_IDLE, wxIdleEventHandler( AppFrame::mainVisSplitterIdle ), NULL, this );
wxPanel *spectrumPanel = new wxPanel(mainVisSplitter, wxID_ANY);
wxBoxSizer *spectrumSizer = new wxBoxSizer(wxHORIZONTAL);
wxGetApp().getSpectrumProcessor()->setup(2048);
spectrumCanvas = new SpectrumCanvas(spectrumPanel, attribList);
spectrumCanvas->setShowDb(true);
spectrumCanvas->setScaleFactorEnabled(true);
wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue());
2015-12-31 20:44:39 -05:00
wxBoxSizer *spectrumCtlTray = new wxBoxSizer(wxVERTICAL);
2015-12-31 20:44:39 -05:00
peakHoldButton = new ModeSelectorCanvas(spectrumPanel, attribList);
peakHoldButton->addChoice(1, "P");
peakHoldButton->setPadding(-1,-1);
peakHoldButton->setHighlightColor(RGBA4f(0.2,0.8,0.2));
peakHoldButton->setHelpTip("Peak Hold Toggle");
peakHoldButton->setToggleMode(true);
peakHoldButton->setSelection(-1);
peakHoldButton->SetMinSize(wxSize(12,24));
spectrumCtlTray->Add(peakHoldButton, 1, wxEXPAND | wxALL, 0);
spectrumCtlTray->AddSpacer(1);
spectrumAvgMeter = new MeterCanvas(spectrumPanel, attribList);
2015-08-13 20:39:31 -04:00
spectrumAvgMeter->setHelpTip("Spectrum averaging speed, click or drag to adjust.");
spectrumAvgMeter->setMax(1.0);
spectrumAvgMeter->setLevel(0.65);
2015-08-12 20:05:57 -04:00
spectrumAvgMeter->setShowUserInput(false);
spectrumAvgMeter->SetMinSize(wxSize(12,24));
2015-12-31 20:44:39 -05:00
spectrumCtlTray->Add(spectrumAvgMeter, 8, wxEXPAND | wxALL, 0);
2015-08-12 20:05:57 -04:00
spectrumSizer->Add(spectrumCanvas, 63, wxEXPAND | wxALL, 0);
spectrumSizer->AddSpacer(1);
2015-12-31 20:44:39 -05:00
spectrumSizer->Add(spectrumCtlTray, 1, wxEXPAND | wxALL, 0);
spectrumPanel->SetSizer(spectrumSizer);
// vbox->Add(spectrumSizer, 5, wxEXPAND | wxALL, 0);
// vbox->AddSpacer(1);
wxPanel *waterfallPanel = new wxPanel(mainVisSplitter, wxID_ANY);
wxBoxSizer *wfSizer = new wxBoxSizer(wxHORIZONTAL);
waterfallCanvas = new WaterfallCanvas(waterfallPanel, attribList);
waterfallCanvas->setup(2048, 512);
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);
waterfallSpeedMeter = new MeterCanvas(waterfallPanel, attribList);
2015-08-13 20:39:31 -04:00
waterfallSpeedMeter->setHelpTip("Waterfall speed, click or drag to adjust (max 1024 lines per second)");
waterfallSpeedMeter->setMax(sqrt(1024));
2015-08-12 20:05:57 -04:00
waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS));
waterfallSpeedMeter->setShowUserInput(false);
waterfallSpeedMeter->SetMinSize(wxSize(12,24));
wfSizer->Add(waterfallCanvas, 63, wxEXPAND | wxALL, 0);
wfSizer->AddSpacer(1);
wfSizer->Add(waterfallSpeedMeter, 1, wxEXPAND | wxALL, 0);
waterfallPanel->SetSizer(wfSizer);
// vbox->Add(wfSizer, 20, wxEXPAND | wxALL, 0);
mainVisSplitter->SplitHorizontally( spectrumPanel, waterfallPanel, 0 );
mainSplitter->SplitHorizontally( demodPanel, mainVisSplitter );
vbox->Add(mainSplitter, 1, wxEXPAND | wxALL, 0);
// TODO: refactor these..
waterfallCanvas->attachSpectrumCanvas(spectrumCanvas);
2015-01-01 21:10:54 -05:00
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
2015-07-29 18:34:58 -04:00
/*
vbox->AddSpacer(1);
testCanvas = new UITestCanvas(this, attribList);
vbox->Add(testCanvas, 20, wxEXPAND | wxALL, 0);
2015-07-29 18:34:58 -04:00
// */
this->SetSizer(vbox);
// waterfallCanvas->SetFocusFromKbd();
2014-12-10 21:22:13 -05:00
waterfallCanvas->SetFocus();
// SetIcon(wxICON(sample));
// Make a menubar
2015-11-02 21:04:02 -05:00
menuBar = new wxMenuBar;
wxMenu *menu = new wxMenu;
menu->Append(wxID_SDR_DEVICES, "SDR Devices");
menu->AppendSeparator();
2015-05-31 22:13:14 -04:00
menu->Append(wxID_OPEN, "&Open Session");
menu->Append(wxID_SAVE, "&Save Session");
menu->Append(wxID_SAVEAS, "Save Session &As..");
menu->AppendSeparator();
menu->Append(wxID_RESET, "&Reset Session");
#ifndef __APPLE__
menu->AppendSeparator();
menu->Append(wxID_CLOSE);
#endif
menuBar->Append(menu, wxT("&File"));
settingsMenu = new wxMenu;
menuBar->Append(settingsMenu, wxT("&Settings"));
2015-05-31 22:13:14 -04:00
menu = new wxMenu;
2014-12-31 19:45:01 -05:00
std::vector<RtAudio::DeviceInfo>::iterator devices_i;
2015-01-01 18:08:54 -05:00
std::map<int, RtAudio::DeviceInfo>::iterator mdevices_i;
2014-12-31 19:45:01 -05:00
AudioThread::enumerateDevices(devices);
int i = 0;
for (devices_i = devices.begin(); devices_i != devices.end(); devices_i++) {
if (devices_i->inputChannels) {
inputDevices[i] = *devices_i;
2014-12-31 19:45:01 -05:00
}
if (devices_i->outputChannels) {
outputDevices[i] = *devices_i;
2014-12-31 19:45:01 -05:00
}
i++;
}
for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) {
2015-01-01 18:08:54 -05:00
wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE + mdevices_i->first, mdevices_i->second.name, wxT("Description?"));
itm->SetId(wxID_RT_AUDIO_DEVICE + mdevices_i->first);
2014-12-31 19:45:01 -05:00
if (mdevices_i->second.isDefaultOutput) {
itm->Check(true);
}
outputDeviceMenuItems[mdevices_i->first] = itm;
2014-12-31 19:45:01 -05:00
}
2015-05-31 22:13:14 -04:00
menuBar->Append(menu, wxT("Audio &Output"));
2015-01-01 03:48:32 -05:00
2015-01-11 03:09:59 -05:00
menu = new wxMenu;
2015-07-15 00:32:36 -04:00
int themeId = wxGetApp().getConfig()->getTheme();
menu->AppendRadioItem(wxID_THEME_DEFAULT, "Default")->Check(themeId==COLOR_THEME_DEFAULT);
menu->AppendRadioItem(wxID_THEME_RADAR, "RADAR")->Check(themeId==COLOR_THEME_RADAR);
menu->AppendRadioItem(wxID_THEME_BW, "Black & White")->Check(themeId==COLOR_THEME_BW);
menu->AppendRadioItem(wxID_THEME_SHARP, "Sharp")->Check(themeId==COLOR_THEME_SHARP);
menu->AppendRadioItem(wxID_THEME_RAD, "Rad")->Check(themeId==COLOR_THEME_RAD);
menu->AppendRadioItem(wxID_THEME_TOUCH, "Touch")->Check(themeId==COLOR_THEME_TOUCH);
menu->AppendRadioItem(wxID_THEME_HD, "HD")->Check(themeId==COLOR_THEME_HD);
2015-01-11 03:09:59 -05:00
menuBar->Append(menu, wxT("&Color Scheme"));
2015-01-11 17:08:16 -05:00
menu = new wxMenu;
2015-03-12 19:41:50 -04:00
#define NUM_RATES_DEFAULT 4
int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 };
for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) {
int desired_rate = 0;
int desired_rank = NUM_RATES_DEFAULT + 1;
for (std::vector<unsigned int>::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end();
srate++) {
for (i = 0; i < NUM_RATES_DEFAULT; i++) {
if (desired_rates[i] == (*srate)) {
if (desired_rank > i) {
desired_rank = i;
desired_rate = (*srate);
}
}
}
}
if (desired_rank > NUM_RATES_DEFAULT) {
desired_rate = mdevices_i->second.sampleRates.back();
}
AudioThread::deviceSampleRate[mdevices_i->first] = desired_rate;
}
2015-03-12 19:41:50 -04:00
for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) {
int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first;
wxMenu *subMenu = new wxMenu;
menu->AppendSubMenu(subMenu, mdevices_i->second.name, wxT("Description?"));
2015-03-12 19:41:50 -04:00
int j = 0;
for (std::vector<unsigned int>::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end();
srate++) {
2015-03-12 19:41:50 -04:00
std::stringstream srateName;
srateName << ((float) (*srate) / 1000.0f) << "kHz";
wxMenuItem *itm = subMenu->AppendRadioItem(menu_id + j, srateName.str(), wxT("Description?"));
2015-03-12 19:41:50 -04:00
if ((*srate) == AudioThread::deviceSampleRate[mdevices_i->first]) {
2015-03-12 19:41:50 -04:00
itm->Check(true);
}
audioSampleRateMenuItems[menu_id + j] = itm;
2015-03-12 19:41:50 -04:00
j++;
}
}
2015-11-02 21:04:02 -05:00
sampleRateMenu = new wxMenu;
menuBar->Append(sampleRateMenu, wxT("Sample &Rate"));
2015-11-02 21:04:02 -05:00
menuBar->Append(menu, wxT("Audio &Sample Rate"));
2015-03-12 19:41:50 -04:00
SetMenuBar(menuBar);
CreateStatusBar();
2015-07-15 00:32:36 -04:00
wxRect *win = wxGetApp().getConfig()->getWindow();
if (win) {
this->SetPosition(win->GetPosition());
this->SetClientSize(win->GetSize());
} else {
SetClientSize(1280, 600);
Centre();
}
2015-07-16 18:57:03 -04:00
bool max = wxGetApp().getConfig()->getWindowMaximized();
if (max) {
this->Maximize();
}
2015-07-18 21:49:53 -04:00
long long freqSnap = wxGetApp().getConfig()->getSnap();
wxGetApp().setFrequencySnap(freqSnap);
float spectrumAvg = wxGetApp().getConfig()->getSpectrumAvgSpeed();
spectrumAvgMeter->setLevel(spectrumAvg);
wxGetApp().getSpectrumProcessor()->setFFTAverageRate(spectrumAvg);
int wflps =wxGetApp().getConfig()->getWaterfallLinesPerSec();
waterfallSpeedMeter->setLevel(sqrt(wflps));
waterfallDataThread->setLinesPerSecond(wflps);
waterfallCanvas->setLinesPerSecond(wflps);
2015-07-15 00:32:36 -04:00
ThemeMgr::mgr.setTheme(wxGetApp().getConfig()->getTheme());
Show();
2015-03-08 17:31:43 -04:00
#ifdef _WIN32
2015-03-07 20:29:25 -05:00
SetIcon(wxICON(frame_icon));
2015-03-08 17:31:43 -04:00
#endif
2015-01-10 12:27:03 -05:00
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);
deviceChanged.store(false);
devInfo = NULL;
wxGetApp().deviceSelector();
// static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
// wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not");
// ShowFullScreen(true);
}
AppFrame::~AppFrame() {
waterfallDataThread->terminate();
t_FFTData->join();
}
void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) {
this->devInfo = devInfo;
deviceChanged.store(true);
}
void AppFrame::updateDeviceParams() {
if (!deviceChanged.load()) {
return;
}
// Build settings menu
wxMenu *newSettingsMenu = new wxMenu;
newSettingsMenu->Append(wxID_SET_FREQ_OFFSET, "Frequency Offset");
if (devInfo->getRxChannel()->hasCORR()) {
newSettingsMenu->Append(wxID_SET_PPM, "Device PPM");
}
agcMenuItem = newSettingsMenu->AppendCheckItem(wxID_AGC_CONTROL, "Automatic Gain");
agcMenuItem->Check(wxGetApp().getAGCMode());
SoapySDR::ArgInfoList::const_iterator args_i;
int i = 0;
2015-11-03 23:44:35 -05:00
settingArgs = devInfo->getSettingsArgInfo();
for (args_i = settingArgs.begin(); args_i != settingArgs.end(); args_i++) {
SoapySDR::ArgInfo arg = (*args_i);
2015-11-03 23:44:35 -05:00
std::string currentVal = wxGetApp().getSDRThread()->readSetting(arg.key);
if (arg.type == SoapySDR::ArgInfo::BOOL) {
wxMenuItem *item = newSettingsMenu->AppendCheckItem(wxID_SETTINGS_BASE+i, arg.name, arg.description);
2015-11-03 23:44:35 -05:00
item->Check(currentVal=="true");
i++;
} else if (arg.type == SoapySDR::ArgInfo::INT) {
2015-11-03 23:44:35 -05:00
newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description);
i++;
} else if (arg.type == SoapySDR::ArgInfo::FLOAT) {
2015-11-03 23:44:35 -05:00
newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description);
i++;
} else if (arg.type == SoapySDR::ArgInfo::STRING) {
if (arg.options.size()) {
wxMenu *subMenu = new wxMenu;
int j = 0;
for (std::vector<std::string>::iterator str_i = arg.options.begin(); str_i != arg.options.end(); str_i++) {
std::string optName = (*str_i);
std::string displayName = optName;
if (arg.optionNames.size()) {
displayName = arg.optionNames[j];
}
wxMenuItem *item = subMenu->AppendRadioItem(wxID_SETTINGS_BASE+i, displayName);
2015-11-03 23:44:35 -05:00
if (currentVal == (*str_i)) {
item->Check();
}
j++;
2015-11-03 23:44:35 -05:00
i++;
}
newSettingsMenu->AppendSubMenu(subMenu, arg.name, arg.description);
} else {
2015-11-03 23:44:35 -05:00
newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description);
i++;
}
}
}
2015-11-03 23:44:35 -05:00
settingsIdMax = wxID_SETTINGS_BASE+i;
menuBar->Replace(1, newSettingsMenu, wxT("&Settings"));
settingsMenu = newSettingsMenu;
// Build sample rate menu
sampleRates = devInfo->getRxChannel()->getSampleRates();
sampleRateMenuItems.erase(sampleRateMenuItems.begin(),sampleRateMenuItems.end());
2015-11-02 21:04:02 -05:00
wxMenu *newSampleRateMenu = new wxMenu;
int ofs = 0;
long sampleRate = wxGetApp().getSampleRate();
bool checked = false;
for (vector<long>::iterator i = sampleRates.begin(); i != sampleRates.end(); i++) {
2015-11-02 21:04:02 -05:00
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++;
}
2015-11-02 21:04:02 -05:00
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual Entry"));
if (!checked) {
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true);
}
2015-11-02 21:04:02 -05:00
menuBar->Replace(4, newSampleRateMenu, wxT("Sample &Rate"));
2015-11-02 21:04:02 -05:00
sampleRateMenu = newSampleRateMenu;
if (!wxGetApp().getAGCMode()) {
gainSpacerItem->Show(true);
gainSizerItem->Show(true);
gainSizerItem->SetMinSize(devInfo->getRxChannel()->getGains().size()*50,0);
demodTray->Layout();
gainCanvas->updateGainUI();
gainCanvas->Refresh();
gainCanvas->Refresh();
} else {
gainSpacerItem->Show(false);
gainSizerItem->Show(false);
demodTray->Layout();
}
agcMenuItem->Check(wxGetApp().getAGCMode());
deviceChanged.store(false);
}
void AppFrame::OnMenu(wxCommandEvent& event) {
2015-01-01 18:08:54 -05:00
if (event.GetId() >= wxID_RT_AUDIO_DEVICE && event.GetId() < wxID_RT_AUDIO_DEVICE + devices.size()) {
if (activeDemodulator) {
2015-01-01 18:08:54 -05:00
activeDemodulator->setOutputDevice(event.GetId() - wxID_RT_AUDIO_DEVICE);
activeDemodulator = NULL;
}
} else if (event.GetId() == wxID_SET_FREQ_OFFSET) {
long ofs = wxGetNumberFromUser("Shift the displayed frequency by this amount.\ni.e. -125000000 for -125 MHz", "Frequency (Hz)",
"Frequency Offset", wxGetApp().getOffset(), -2000000000, 2000000000, this);
2015-01-06 19:15:27 -05:00
if (ofs != -1) {
wxGetApp().setOffset(ofs);
2015-07-19 12:56:54 -04:00
wxGetApp().saveConfig();
2015-01-06 19:15:27 -05:00
}
} else if (event.GetId() == wxID_SET_DS_OFF) {
// wxGetApp().setDirectSampling(0);
// wxGetApp().saveConfig();
} else if (event.GetId() == wxID_SET_DS_I) {
// wxGetApp().setDirectSampling(1);
// wxGetApp().saveConfig();
} else if (event.GetId() == wxID_SET_DS_Q) {
// wxGetApp().setDirectSampling(2);
// wxGetApp().saveConfig();
2015-05-31 22:13:14 -04:00
} else if (event.GetId() == wxID_SET_SWAP_IQ) {
// bool swap_state = !wxGetApp().getSwapIQ();
// wxGetApp().setSwapIQ(swap_state);
// wxGetApp().saveConfig();
// iqSwapMenuItem->Check(swap_state);
} else if (event.GetId() == wxID_AGC_CONTROL) {
if (wxGetApp().getDevice() == NULL) {
agcMenuItem->Check();
return;
}
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()->getRxChannel()->getGains().size()*40,0);
demodTray->Layout();
gainCanvas->updateGainUI();
gainCanvas->Refresh();
gainCanvas->Refresh();
}
} else if (event.GetId() == wxID_SDR_DEVICES) {
wxGetApp().deviceSelector();
} else if (event.GetId() == wxID_SET_PPM) {
2015-04-22 23:08:43 -04:00
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);
2015-04-22 22:54:48 -04:00
wxGetApp().saveConfig();
} else if (event.GetId() == wxID_SAVE) {
2015-01-10 12:27:03 -05:00
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;
}
saveSession(saveFileDialog.GetPath().ToStdString());
}
} else if (event.GetId() == wxID_OPEN) {
2015-01-10 12:27:03 -05:00
wxFileDialog openFileDialog(this, _("Open XML Session file"), "", "", "XML files (*.xml)|*.xml", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() == wxID_CANCEL) {
return;
}
loadSession(openFileDialog.GetPath().ToStdString());
} else if (event.GetId() == wxID_SAVEAS) {
2015-01-10 12:27:03 -05:00
wxFileDialog saveFileDialog(this, _("Save XML Session file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (saveFileDialog.ShowModal() == wxID_CANCEL) {
return;
}
saveSession(saveFileDialog.GetPath().ToStdString());
} else if (event.GetId() == wxID_RESET) {
wxGetApp().getDemodMgr().terminateAll();
2015-07-21 00:04:04 -04:00
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);
2015-11-21 15:12:20 -05:00
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.65);
spectrumAvgMeter->setLevel(0.65);
demodModeSelector->Refresh();
demodTuner->Refresh();
SetTitle(CUBICSDR_TITLE);
2015-01-10 20:33:30 -05:00
currentSessionFile = "";
2015-11-10 02:13:15 -05:00
} else if (event.GetId() == wxID_CLOSE || event.GetId() == wxID_EXIT) {
Close(false);
2015-01-11 03:09:59 -05:00
} else if (event.GetId() == wxID_THEME_DEFAULT) {
ThemeMgr::mgr.setTheme(COLOR_THEME_DEFAULT);
2015-01-11 03:09:59 -05:00
} else if (event.GetId() == wxID_THEME_SHARP) {
ThemeMgr::mgr.setTheme(COLOR_THEME_SHARP);
2015-01-11 03:09:59 -05:00
} else if (event.GetId() == wxID_THEME_BW) {
ThemeMgr::mgr.setTheme(COLOR_THEME_BW);
2015-01-11 03:09:59 -05:00
} else if (event.GetId() == wxID_THEME_RAD) {
ThemeMgr::mgr.setTheme(COLOR_THEME_RAD);
2015-01-11 03:09:59 -05:00
} else if (event.GetId() == wxID_THEME_TOUCH) {
ThemeMgr::mgr.setTheme(COLOR_THEME_TOUCH);
2015-01-11 03:09:59 -05:00
} else if (event.GetId() == wxID_THEME_HD) {
ThemeMgr::mgr.setTheme(COLOR_THEME_HD);
2015-01-11 03:09:59 -05:00
} else if (event.GetId() == wxID_THEME_RADAR) {
ThemeMgr::mgr.setTheme(COLOR_THEME_RADAR);
}
2015-01-11 17:08:16 -05:00
2015-11-03 23:44:35 -05:00
if (event.GetId() >= wxID_SETTINGS_BASE && event.GetId() < settingsIdMax) {
int setIdx = event.GetId()-wxID_SETTINGS_BASE;
int menuIdx = 0;
for (std::vector<SoapySDR::ArgInfo>::iterator arg_i = settingArgs.begin(); arg_i != settingArgs.end(); arg_i++) {
SoapySDR::ArgInfo &arg = (*arg_i);
if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size() && setIdx >= menuIdx && setIdx < menuIdx+arg.options.size()) {
int optIdx = setIdx-menuIdx;
wxGetApp().getSDRThread()->writeSetting(arg.key, arg.options[optIdx]);
break;
} else if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) {
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));
if (stringVal.ToStdString() != "") {
wxGetApp().getSDRThread()->writeSetting(arg.key, stringVal.ToStdString());
}
break;
2015-11-03 23:44:35 -05:00
} else if (arg.type == SoapySDR::ArgInfo::INT) {
int currentVal;
try {
currentVal = std::stoi(wxGetApp().getSDRThread()->readSetting(arg.key));
} catch (std::invalid_argument e) {
currentVal = 0;
}
int intVal = wxGetNumberFromUser(arg.description, arg.units, arg.name, currentVal, arg.range.minimum(), arg.range.maximum(), this);
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 (std::invalid_argument e) {
// ...
}
break;
} else {
menuIdx++;
}
} else {
menuIdx++;
}
}
}
if (event.GetId() >= wxID_THEME_DEFAULT && event.GetId() <= wxID_THEME_RADAR) {
demodTuner->Refresh();
demodModeSelector->Refresh();
waterfallSpeedMeter->Refresh();
spectrumAvgMeter->Refresh();
gainCanvas->setThemeColors();
}
2015-01-11 17:08:16 -05:00
switch (event.GetId()) {
case wxID_BANDWIDTH_MANUAL:
int rateHigh, rateLow;
SDRDeviceInfo *dev = wxGetApp().getDevice();
if (dev == NULL) {
break;
}
SDRDeviceChannel *chan = dev->getRxChannel();
rateLow = 2000000;
rateHigh = 30000000;
if (chan->getSampleRates().size()) {
rateLow = chan->getSampleRates()[0];
rateHigh = chan->getSampleRates()[chan->getSampleRates().size()-1];
}
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",
wxGetApp().getSampleRate(),
rateLow,
rateHigh,
this);
if (bw != -1) {
wxGetApp().setSampleRate(bw);
}
break;
2015-01-11 17:08:16 -05:00
}
if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE+sampleRates.size()) {
wxGetApp().setSampleRate(sampleRates[event.GetId()-wxID_BANDWIDTH_BASE]);
2015-01-12 00:40:43 -05:00
}
2015-03-12 19:41:50 -04:00
if (event.GetId() >= wxID_AUDIO_BANDWIDTH_BASE) {
int evId = event.GetId();
std::vector<RtAudio::DeviceInfo>::iterator devices_i;
std::map<int, RtAudio::DeviceInfo>::iterator mdevices_i;
int i = 0;
for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) {
int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first;
2015-03-12 19:41:50 -04:00
int j = 0;
for (std::vector<unsigned int>::iterator srate = mdevices_i->second.sampleRates.begin(); srate != mdevices_i->second.sampleRates.end();
srate++) {
2015-03-12 19:41:50 -04:00
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(mdevices_i->first, *srate);
}
2015-03-12 19:41:50 -04:00
j++;
}
i++;
}
2015-03-12 19:41:50 -04:00
}
}
2015-07-15 00:32:36 -04:00
void AppFrame::OnClose(wxCloseEvent& event) {
wxGetApp().closeDeviceSelector();
wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodSpectrumCanvas->getVisualDataQueue());
wxGetApp().getDemodSpectrumProcessor()->removeOutput(demodWaterfallCanvas->getVisualDataQueue());
wxGetApp().getSpectrumProcessor()->removeOutput(spectrumCanvas->getVisualDataQueue());
2015-07-15 00:32:36 -04:00
wxGetApp().getConfig()->setWindow(this->GetPosition(), this->GetClientSize());
2015-07-16 18:57:03 -04:00
wxGetApp().getConfig()->setWindowMaximized(this->IsMaximized());
2015-07-15 00:32:36 -04:00
wxGetApp().getConfig()->setTheme(ThemeMgr::mgr.getTheme());
2015-07-18 21:49:53 -04:00
wxGetApp().getConfig()->setSnap(wxGetApp().getFrequencySnap());
2015-07-21 00:04:04 -04:00
wxGetApp().getConfig()->setCenterFreq(wxGetApp().getFrequency());
wxGetApp().getConfig()->setSpectrumAvgSpeed(wxGetApp().getSpectrumProcessor()->getFFTAverageRate());
wxGetApp().getConfig()->setWaterfallLinesPerSec(waterfallDataThread->getLinesPerSecond());
2015-07-15 00:32:36 -04:00
wxGetApp().getConfig()->save();
event.Skip();
2014-12-31 22:38:59 -05:00
}
void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) {
new AppFrame();
}
void AppFrame::OnThread(wxCommandEvent& event) {
event.Skip();
2014-11-16 16:51:45 -05:00
}
void AppFrame::OnIdle(wxIdleEvent& event) {
if (deviceChanged.load()) {
updateDeviceParams();
}
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
if (demod && demod->isModemInitialized()) {
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() > 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());
}
2014-12-31 19:45:01 -05:00
if (demod != activeDemodulator) {
demodSignalMeter->setInputValue(demod->getSquelchLevel());
2015-01-10 20:33:30 -05:00
demodGainMeter->setInputValue(demod->getGain());
int outputDevice = demod->getOutputDevice();
scopeCanvas->setDeviceName(outputDevices[outputDevice].name);
outputDeviceMenuItems[outputDevice]->Check(true);
std::string dType = demod->getDemodulatorType();
demodModeSelector->setSelection(dType);
2015-11-15 13:52:54 -05:00
#ifdef ENABLE_DIGITAL_LAB
demodModeSelectorAdv->setSelection(dType);
2015-11-15 13:52:54 -05:00
#endif
demodMuteButton->setSelection(demod->isMuted()?1:-1);
2015-11-23 22:16:09 -05:00
modemPropertiesUpdated.store(true);
2015-12-07 20:19:03 -05:00
demodTuner->setHalfBand(dType=="USB" || dType=="LSB");
2014-12-31 19:45:01 -05:00
}
2014-12-28 20:55:05 -05:00
if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) {
long long centerFreq = demod->getFrequency();
2015-02-21 23:25:40 -05:00
unsigned int demodBw = (unsigned int) ceil((float) demod->getBandwidth() * 2.25);
if (demod->getDemodulatorType() == "USB") {
demodBw /= 2;
centerFreq += demod->getBandwidth() / 4;
2014-12-28 20:55:05 -05:00
}
if (demod->getDemodulatorType() == "LSB") {
demodBw /= 2;
centerFreq -= demod->getBandwidth() / 4;
}
2015-01-11 17:08:16 -05:00
if (demodBw > wxGetApp().getSampleRate() / 2) {
demodBw = wxGetApp().getSampleRate() / 2;
2014-12-28 20:55:05 -05:00
}
2015-02-21 23:25:40 -05:00
if (demodBw < 20000) {
demodBw = 20000;
2014-12-28 20:55:05 -05:00
}
if (centerFreq != demodWaterfallCanvas->getCenterFrequency()) {
demodWaterfallCanvas->setCenterFrequency(centerFreq);
demodSpectrumCanvas->setCenterFrequency(centerFreq);
}
std::string dSelection = demodModeSelector->getSelectionLabel();
2015-11-15 13:52:54 -05:00
#ifdef ENABLE_DIGITAL_LAB
std::string dSelectionadv = demodModeSelectorAdv->getSelectionLabel();
// basic demodulators
if (dSelection != "" && dSelection != demod->getDemodulatorType()) {
demod->setDemodulatorType(dSelection);
2015-12-07 20:19:03 -05:00
demodTuner->setHalfBand(dSelection=="USB" || dSelection=="LSB");
demodModeSelectorAdv->setSelection(-1);
2015-11-15 13:52:54 -05:00
}
// advanced demodulators
else if (dSelectionadv != "" && dSelectionadv != demod->getDemodulatorType()) {
demod->setDemodulatorType(dSelectionadv);
2015-12-07 20:19:03 -05:00
demodTuner->setHalfBand(false);
demodModeSelector->setSelection(-1);
}
2015-11-15 13:52:54 -05:00
#else
// basic demodulators
2015-11-19 00:39:14 -05:00
if (dSelection != "" && dSelection != demod->getDemodulatorType()) {
2015-11-15 13:52:54 -05:00
demod->setDemodulatorType(dSelection);
2015-12-07 20:19:03 -05:00
demodTuner->setHalfBand(dSelection=="USB" || dSelection=="LSB");
2015-11-15 13:52:54 -05:00
}
#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());
} else if (!demod->isMuted() && muteMode == 1) {
demodMuteButton->setSelection(-1);
wxGetApp().getDemodMgr().setLastMuted(demod->isMuted());
}
}
demodWaterfallCanvas->setBandwidth(demodBw);
demodSpectrumCanvas->setBandwidth(demodBw);
}
2014-12-31 19:45:01 -05:00
demodSignalMeter->setLevel(demod->getSignalLevel());
2015-01-10 20:33:30 -05:00
demodGainMeter->setLevel(demod->getGain());
2014-12-31 19:45:01 -05:00
if (demodSignalMeter->inputChanged()) {
demod->setSquelchLevel(demodSignalMeter->getInputValue());
}
2015-01-10 20:33:30 -05:00
if (demodGainMeter->inputChanged()) {
demod->setGain(demodGainMeter->getInputValue());
demodGainMeter->setLevel(demodGainMeter->getInputValue());
}
2014-12-31 19:45:01 -05:00
activeDemodulator = demod;
} else if (demod) {
// Wait state for current demodulator modem to activate..
} else {
DemodulatorMgr *mgr = &wxGetApp().getDemodMgr();
std::string dSelection = demodModeSelector->getSelectionLabel();
2015-11-15 13:52:54 -05:00
#ifdef ENABLE_DIGITAL_LAB
std::string dSelectionadv = demodModeSelectorAdv->getSelectionLabel();
// basic demodulators
if (dSelection != "" && 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 != "" && dSelectionadv != mgr->getLastDemodulatorType()) {
mgr->setLastDemodulatorType(dSelectionadv);
mgr->setLastBandwidth(Modem::getModemDefaultSampleRate(dSelectionadv));
demodTuner->setHalfBand(false);
demodModeSelector->setSelection(-1);
}
2015-11-15 13:52:54 -05:00
#else
// basic demodulators
2015-11-19 00:39:14 -05:00
if (dSelection != "" && dSelection != mgr->getLastDemodulatorType()) {
2015-11-15 13:52:54 -05:00
mgr->setLastDemodulatorType(dSelection);
mgr->setLastBandwidth(Modem::getModemDefaultSampleRate(dSelection));
demodTuner->setHalfBand(dSelection=="USB" || dSelection=="LSB");
2015-11-15 13:52:54 -05:00
}
#endif
demodGainMeter->setLevel(mgr->getLastGain());
if (demodSignalMeter->inputChanged()) {
mgr->setLastSquelchLevel(demodSignalMeter->getInputValue());
}
if (demodGainMeter->inputChanged()) {
mgr->setLastGain(demodGainMeter->getInputValue());
demodGainMeter->setLevel(demodGainMeter->getInputValue());
}
if (wxGetApp().getFrequency() != demodWaterfallCanvas->getCenterFrequency()) {
demodWaterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
demodSpectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
}
2015-03-31 17:51:56 -04:00
if (spectrumCanvas->getViewState() && abs(wxGetApp().getFrequency()-spectrumCanvas->getCenterFrequency()) > (wxGetApp().getSampleRate()/2)) {
spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
}
if (demodMuteButton->modeChanged()) {
int muteMode = demodMuteButton->getSelection();
if (muteMode == -1) {
wxGetApp().getDemodMgr().setLastMuted(false);
} else if (muteMode == 1) {
wxGetApp().getDemodMgr().setLastMuted(true);
}
demodMuteButton->clearModeChanged();
}
}
if (demodTuner->getMouseTracker()->mouseInView()) {
if (!demodTuner->HasFocus()) {
demodTuner->SetFocus();
}
} else if (!wxGetApp().isDeviceSelectorOpen() && (!modemProps || !modemProps->isMouseInView())) {
2015-10-10 01:13:48 -04:00
if (!waterfallCanvas->HasFocus()) {
waterfallCanvas->SetFocus();
}
}
2015-04-22 22:54:48 -04:00
scopeCanvas->setPPMMode(demodTuner->isAltDown());
scopeCanvas->setShowDb(spectrumCanvas->getShowDb());
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();
SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor();
if (spectrumAvgMeter->inputChanged()) {
float val = spectrumAvgMeter->getInputValue();
if (val < 0.01) {
val = 0.01;
}
2015-08-18 00:08:22 -04:00
if (val > 0.99) {
val = 0.99;
}
spectrumAvgMeter->setLevel(val);
proc->setFFTAverageRate(val);
2015-08-13 20:39:31 -04:00
GetStatusBar()->SetStatusText(wxString::Format(wxT("Spectrum averaging speed changed to %0.2f%%."),val*100.0));
}
SpectrumVisualProcessor *dproc = wxGetApp().getDemodSpectrumProcessor();
dproc->setView(demodWaterfallCanvas->getViewState(), demodWaterfallCanvas->getCenterFrequency(),demodWaterfallCanvas->getBandwidth());
SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor();
if (waterfallSpeedMeter->inputChanged()) {
2015-08-12 20:05:57 -04:00
float val = waterfallSpeedMeter->getInputValue();
waterfallSpeedMeter->setLevel(val);
waterfallDataThread->setLinesPerSecond((int)ceil(val*val));
waterfallCanvas->setLinesPerSecond((int)ceil(val*val));
2015-08-13 20:39:31 -04:00
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());
wxGetApp().getSDRPostThread()->setIQVisualRange(waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth());
proc->setView(wproc->isView(), wproc->getCenterFrequency(), wproc->getBandwidth());
demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
if (modemPropertiesUpdated.load() && demod && demod->isModemInitialized()) {
modemProps->initProperties(demod->getModemArgs());
modemPropertiesUpdated.store(false);
demodTray->Layout();
#if ENABLE_DIGITAL_LAB
if (demod->getModemType() == "digital") {
ModemDigitalOutputConsole *outp = (ModemDigitalOutputConsole *)demod->getOutput();
if (!outp->getDialog()) {
outp->setTitle(demod->getDemodulatorType() + ": " + frequencyToStr(demod->getFrequency()));
outp->setDialog(new DigitalConsole(this, outp));
}
demod->showOutput();
}
#endif
}
2015-12-31 20:44:39 -05:00
int peakHoldMode = peakHoldButton->getSelection();
if (peakHoldButton->modeChanged()) {
wxGetApp().getSpectrumProcessor()->setPeakHold(peakHoldMode == 1);
peakHoldButton->clearModeChanged();
}
if (!this->IsActive()) {
2015-08-14 21:20:07 -04:00
std::this_thread::sleep_for(std::chrono::milliseconds(25));
2015-08-13 20:39:31 -04:00
}
2015-08-13 18:03:06 -04:00
event.RequestMore();
}
2015-01-09 17:17:56 -05:00
void AppFrame::OnDoubleClickSash(wxSplitterEvent& event)
{
wxWindow *a, *b;
wxSplitterWindow *w = NULL;
float g = 0.5;
if (event.GetId() == wxID_MAIN_SPLITTER) {
w = mainSplitter;
g = 12.0/37.0;
} else if (event.GetId() == wxID_VIS_SPLITTER) {
w = mainVisSplitter;
g = 7.4/37.0;
}
if (w != NULL) {
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();
}
2015-01-09 17:17:56 -05:00
void AppFrame::saveSession(std::string fileName) {
DataTree s("cubicsdr_session");
DataNode *header = s.rootNode()->newChild("header");
*header->newChild("version") = std::string(CUBICSDR_VERSION);
*header->newChild("center_freq") = wxGetApp().getFrequency();
2015-01-09 17:17:56 -05:00
DataNode *demods = s.rootNode()->newChild("demodulators");
2015-01-09 17:17:56 -05:00
std::vector<DemodulatorInstance *> &instances = wxGetApp().getDemodMgr().getDemodulators();
std::vector<DemodulatorInstance *>::iterator instance_i;
for (instance_i = instances.begin(); instance_i != instances.end(); instance_i++) {
DataNode *demod = demods->newChild("demodulator");
*demod->newChild("bandwidth") = (*instance_i)->getBandwidth();
*demod->newChild("frequency") = (*instance_i)->getFrequency();
*demod->newChild("type") = (*instance_i)->getDemodulatorType();
*demod->newChild("squelch_level") = (*instance_i)->getSquelchLevel();
2015-01-10 12:27:03 -05:00
*demod->newChild("squelch_enabled") = (*instance_i)->isSquelchEnabled() ? 1 : 0;
*demod->newChild("output_device") = outputDevices[(*instance_i)->getOutputDevice()].name;
2015-01-10 20:33:30 -05:00
*demod->newChild("gain") = (*instance_i)->getGain();
*demod->newChild("muted") = (*instance_i)->isMuted() ? 1 : 0;
ModemSettings saveSettings = (*instance_i)->readModemSettings();
if (saveSettings.size()) {
DataNode *settingsNode = demod->newChild("settings");
for (ModemSettings::const_iterator msi = saveSettings.begin(); msi != saveSettings.end(); msi++) {
*settingsNode->newChild(msi->first.c_str()) = msi->second;
}
}
2015-01-09 17:17:56 -05:00
}
s.SaveToFileXML(fileName);
2015-01-10 20:33:30 -05:00
currentSessionFile = fileName;
2015-01-11 17:08:16 -05:00
std::string filePart = fileName.substr(fileName.find_last_of(filePathSeparator) + 1);
2015-01-10 20:33:30 -05:00
GetStatusBar()->SetStatusText(wxString::Format(wxT("Saved session: %s"), currentSessionFile.c_str()));
SetTitle(wxString::Format(wxT("%s: %s"), CUBICSDR_TITLE, filePart.c_str()));
2015-01-09 17:17:56 -05:00
}
bool AppFrame::loadSession(std::string fileName) {
DataTree l;
if (!l.LoadFromFileXML(fileName)) {
return false;
}
2015-01-10 12:27:03 -05:00
wxGetApp().getDemodMgr().terminateAll();
try {
DataNode *header = l.rootNode()->getNext("header");
2015-01-10 11:00:03 -05:00
std::string version(*header->getNext("version"));
long long center_freq = *header->getNext("center_freq");
std::cout << "Loading " << version << " session file" << std::endl;
std::cout << "\tCenter Frequency: " << center_freq << std::endl;
2015-01-10 12:27:03 -05:00
wxGetApp().setFrequency(center_freq);
DataNode *demodulators = l.rootNode()->getNext("demodulators");
int numDemodulators = 0;
DemodulatorInstance *loadedDemod = NULL;
while (demodulators->hasAnother("demodulator")) {
DataNode *demod = demodulators->getNext("demodulator");
if (!demod->hasAnother("bandwidth") || !demod->hasAnother("frequency")) {
continue;
}
long bandwidth = *demod->getNext("bandwidth");
long long freq = *demod->getNext("frequency");
2015-01-10 12:27:03 -05:00
float squelch_level = demod->hasAnother("squelch_level") ? (float) *demod->getNext("squelch_level") : 0;
int squelch_enabled = demod->hasAnother("squelch_enabled") ? (int) *demod->getNext("squelch_enabled") : 0;
int muted = demod->hasAnother("muted") ? (int) *demod->getNext("muted") : 0;
2015-01-10 12:27:03 -05:00
std::string output_device = demod->hasAnother("output_device") ? string(*(demod->getNext("output_device"))) : "";
2015-01-10 20:33:30 -05:00
float gain = demod->hasAnother("gain") ? (float) *demod->getNext("gain") : 1.0;
2015-01-10 12:27:03 -05:00
std::string type = "FM";
DataNode *demodTypeNode = demod->hasAnother("type")?demod->getNext("type"):nullptr;
if (demodTypeNode->element()->getDataType() == DATA_INT) {
int legacyType = *demodTypeNode;
int legacyStereo = demod->hasAnother("stereo") ? (int) *demod->getNext("stereo") : 0;
switch (legacyType) { // legacy demod ID
case 1: type = legacyStereo?"FMS":"FM"; break;
case 2: type = "AM"; break;
case 3: type = "LSB"; break;
case 4: type = "USB"; break;
case 5: type = "DSB"; break;
case 6: type = "ASK"; break;
case 7: type = "APSK"; break;
case 8: type = "BPSK"; break;
case 9: type = "DPSK"; break;
case 10: type = "PSK"; break;
case 11: type = "OOK"; break;
case 12: type = "ST"; break;
case 13: type = "SQAM"; break;
case 14: type = "QAM"; break;
case 15: type = "QPSK"; break;
case 16: type = "I/Q"; break;
default: type = "FM"; break;
}
} else if (demodTypeNode->element()->getDataType() == DATA_STRING) {
demodTypeNode->element()->get(type);
}
ModemSettings mSettings;
if (demod->hasAnother("settings")) {
DataNode *modemSettings = demod->getNext("settings");
for (int msi = 0, numSettings = modemSettings->numChildren(); msi < numSettings; msi++) {
DataNode *settingNode = modemSettings->child(msi);
std::string keyName = settingNode->getName();
std::string strSettingValue = settingNode->element()->toString();
if (keyName != "" && strSettingValue != "") {
mSettings[keyName] = strSettingValue;
}
}
}
2015-01-10 12:27:03 -05:00
DemodulatorInstance *newDemod = wxGetApp().getDemodMgr().newThread();
loadedDemod = newDemod;
numDemodulators++;
newDemod->writeModemSettings(mSettings);
2015-01-10 12:27:03 -05:00
newDemod->setDemodulatorType(type);
newDemod->setBandwidth(bandwidth);
newDemod->setFrequency(freq);
2015-01-10 20:33:30 -05:00
newDemod->setGain(gain);
2015-01-10 12:27:03 -05:00
newDemod->updateLabel(freq);
newDemod->setMuted(muted?true:false);
2015-01-10 12:27:03 -05:00
if (squelch_enabled) {
newDemod->setSquelchEnabled(true);
newDemod->setSquelchLevel(squelch_level);
}
2015-11-20 21:55:37 -05:00
2015-01-10 12:27:03 -05:00
bool found_device = false;
std::map<int, RtAudio::DeviceInfo>::iterator i;
for (i = outputDevices.begin(); i != outputDevices.end(); i++) {
if (i->second.name == output_device) {
newDemod->setOutputDevice(i->first);
found_device = true;
}
}
if (!found_device) {
std::cout << "\tWarning: named output device '" << output_device << "' was not found. Using default output.";
}
newDemod->run();
newDemod->setActive(false);
2015-01-10 12:27:03 -05:00
wxGetApp().bindDemodulator(newDemod);
2015-01-10 12:27:03 -05:00
std::cout << "\tAdded demodulator at frequency " << freq << " type " << type << std::endl;
std::cout << "\t\tBandwidth: " << bandwidth << std::endl;
std::cout << "\t\tSquelch Level: " << squelch_level << std::endl;
2015-01-10 12:27:03 -05:00
std::cout << "\t\tSquelch Enabled: " << (squelch_enabled ? "true" : "false") << std::endl;
std::cout << "\t\tOutput Device: " << output_device << std::endl;
}
if ((numDemodulators == 1) && loadedDemod) {
loadedDemod->setActive(true);
loadedDemod->setFollow(true);
loadedDemod->setTracking(true);
wxGetApp().getDemodMgr().setActiveDemodulator(loadedDemod);
}
} catch (DataInvalidChildException &e) {
std::cout << e.what() << std::endl;
return false;
} catch (DataTypeMismatchException &e) {
std::cout << e.what() << std::endl;
return false;
}
2015-01-10 12:27:03 -05:00
currentSessionFile = fileName;
2015-01-10 20:33:30 -05:00
2015-01-11 17:08:16 -05:00
std::string filePart = fileName.substr(fileName.find_last_of(filePathSeparator) + 1);
2015-01-10 20:33:30 -05:00
GetStatusBar()->SetStatusText(wxString::Format(wxT("Loaded session file: %s"), currentSessionFile.c_str()));
SetTitle(wxString::Format(wxT("%s: %s"), CUBICSDR_TITLE, filePart.c_str()));
return true;
2015-01-09 17:17:56 -05:00
}
FFTVisualDataThread *AppFrame::getWaterfallDataThread() {
return waterfallDataThread;
}
void AppFrame::updateModemProperties(ModemArgInfoList args) {
newModemArgs = args;
modemPropertiesUpdated.store(true);
}
void AppFrame::setMainWaterfallFFTSize(int fftSize) {
wxGetApp().getSpectrumProcessor()->setFFTSize(fftSize);
spectrumCanvas->setFFTSize(fftSize);
waterfallDataThread->getProcessor()->setFFTSize(fftSize);
waterfallCanvas->setFFTSize(fftSize);
}