CubicSDR/src/AppFrame.cpp

334 lines
12 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>
#include "SDRThread.h"
#include "DemodulatorMgr.h"
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"
2014-11-21 21:50:14 -05:00
#include <thread>
#include <wx/panel.h>
wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
//EVT_MENU(wxID_NEW, AppFrame::OnNewWindow)
2014-12-31 22:38:59 -05:00
EVT_MENU(wxID_CLOSE, AppFrame::OnClose)
EVT_MENU(wxID_ANY, AppFrame::OnMenu)
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread)
EVT_IDLE(AppFrame::OnIdle)
wxEND_EVENT_TABLE()
AppFrame::AppFrame() :
2015-01-09 17:19:05 -05:00
wxFrame(NULL, wxID_ANY, wxT("CubicSDR " CUBICSDR_VERSION " by Charles J. Cliffe (@ccliffe)")), activeDemodulator(NULL) {
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *demodOpts = new wxBoxSizer(wxVERTICAL);
2014-12-28 20:55:05 -05:00
wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL);
2015-01-05 19:12:16 -05:00
demodModeSelector = new ModeSelectorCanvas(this, NULL);
demodModeSelector->addChoice(DEMOD_TYPE_FM,"FM");
demodModeSelector->addChoice(DEMOD_TYPE_AM,"AM");
demodModeSelector->addChoice(DEMOD_TYPE_LSB,"LSB");
demodModeSelector->addChoice(DEMOD_TYPE_USB,"USB");
demodModeSelector->addChoice(DEMOD_TYPE_DSB,"DSB");
2015-01-05 19:12:16 -05:00
demodTray->Add(demodModeSelector, 2, wxEXPAND | wxALL, 0);
// demodTray->AddSpacer(2);
demodSpectrumCanvas = new SpectrumCanvas(this, NULL);
demodSpectrumCanvas->setup(1024);
demodSpectrumCanvas->setView(DEFAULT_FREQ, 300000);
2015-01-06 19:15:27 -05:00
demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0);
demodVisuals->AddSpacer(1);
2014-12-28 20:55:05 -05:00
demodWaterfallCanvas = new WaterfallCanvas(this, NULL);
2015-01-06 19:15:27 -05:00
demodWaterfallCanvas->setup(1024, 128);
demodWaterfallCanvas->setView(DEFAULT_FREQ, 300000);
demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas);
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);
2014-12-28 20:55:05 -05:00
2014-12-31 19:45:01 -05:00
demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0);
demodTray->AddSpacer(2);
demodSignalMeter = new MeterCanvas(this, NULL);
demodSignalMeter->setMax(0.5);
demodSignalMeter->setHelpTip("Current Signal Level. Click / Drag to set Squelch level.");
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(2);
scopeCanvas = new ScopeCanvas(this, NULL);
demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0);
demodScopeTray->AddSpacer(2);
demodTuner = new TuningCanvas(this, NULL);
demodTuner->setHelpTip("Testing tuner");
demodScopeTray->Add(demodTuner, 1, wxEXPAND | wxALL, 0);
demodTray->Add(demodScopeTray, 30, wxEXPAND | wxALL, 0);
2015-01-06 19:15:27 -05:00
vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0);
vbox->AddSpacer(2);
spectrumCanvas = new SpectrumCanvas(this, NULL);
spectrumCanvas->setup(2048);
2015-01-06 19:15:27 -05:00
vbox->Add(spectrumCanvas, 5, wxEXPAND | wxALL, 0);
vbox->AddSpacer(2);
waterfallCanvas = new WaterfallCanvas(this, NULL);
waterfallCanvas->setup(2048, 512);
waterfallCanvas->attachSpectrumCanvas(spectrumCanvas);
2015-01-01 21:10:54 -05:00
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
2015-01-06 19:15:27 -05:00
vbox->Add(waterfallCanvas, 20, wxEXPAND | wxALL, 0);
this->SetSizer(vbox);
// waterfallCanvas->SetFocusFromKbd();
2014-12-10 21:22:13 -05:00
waterfallCanvas->SetFocus();
2014-10-27 23:52:25 -04:00
// SetIcon(wxICON(sample));
// Make a menubar
wxMenuBar *menuBar = new wxMenuBar;
wxMenu *menu = new wxMenu;
// menu->Append(wxID_NEW);
menu->Append(wxID_SET_FREQ_OFFSET, "Set Frequency Offset");
menu->Append(wxID_OPEN, "&Open Session");
menu->Append(wxID_SAVE, "&Save Session");
menu->Append(wxID_SAVEAS, "Save Session &As..");
menu->AppendSeparator();
menu->Append(wxID_CLOSE);
2014-12-31 19:45:01 -05:00
menuBar->Append(menu, wxT("&File"));
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++;
}
i = 0;
2014-12-31 19:45:01 -05:00
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-01-02 22:44:09 -05:00
menuBar->Append(menu, wxT("Active Demodulator &Output"));
2015-01-01 03:48:32 -05:00
SetMenuBar(menuBar);
CreateStatusBar();
SetClientSize(1280, 600);
Centre();
Show();
2014-12-16 21:30:03 -05:00
GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), DEFAULT_FREQ));
// 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() {
}
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);
}
} else if (event.GetId() == wxID_SAVE) {
wxFileDialog saveFileDialog(this, _("Save XML Session file"), "", "", "XML files (*.xml)|*.xml", wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
if (saveFileDialog.ShowModal() == wxID_CANCEL) {
return;
}
// saveFileDialog.GetPath();
} 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;
}
// openFileDialog.GetPath();
} else if (event.GetId() == wxID_SAVEAS) {
} else if (event.GetId() == wxID_EXIT) {
Close(false);
}
}
2014-12-31 22:38:59 -05:00
void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) {
Close(false);
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) {
bool work_done = false;
//#ifdef __APPLE__
// std::this_thread::sleep_for(std::chrono::milliseconds(4));
// std::this_thread::yield();
//#endif
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
if (demod) {
2014-12-31 19:45:01 -05:00
if (demod != activeDemodulator) {
demodSignalMeter->setInputValue(demod->getSquelchLevel());
int outputDevice = demod->getOutputDevice();
scopeCanvas->setDeviceName(outputDevices[outputDevice].name);
outputDeviceMenuItems[outputDevice]->Check(true);
2015-01-01 18:08:54 -05:00
int dType = demod->getDemodulatorType();
demodModeSelector->setSelection(dType);
2014-12-31 19:45:01 -05:00
}
2014-12-28 20:55:05 -05:00
if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) {
if (demod->getParams().frequency != demodWaterfallCanvas->getCenterFrequency()) {
demodWaterfallCanvas->setCenterFrequency(demod->getFrequency());
demodSpectrumCanvas->setCenterFrequency(demod->getFrequency());
2014-12-28 20:55:05 -05:00
}
int dSelection = demodModeSelector->getSelection();
if (dSelection != -1 && dSelection != demod->getDemodulatorType()) {
demod->setDemodulatorType(dSelection);
}
2014-12-28 20:55:05 -05:00
unsigned int demodBw = (unsigned int) ceil((float) demod->getParams().bandwidth * 2.5);
if (demodBw > SRATE / 2) {
demodBw = SRATE / 2;
2014-12-28 20:55:05 -05:00
}
2015-01-01 18:08:54 -05:00
if (demodBw < 80000) {
demodBw = 80000;
2014-12-28 20:55:05 -05:00
}
demodWaterfallCanvas->setBandwidth(demodBw);
demodSpectrumCanvas->setBandwidth(demodBw);
}
2014-12-31 19:45:01 -05:00
demodSignalMeter->setLevel(demod->getSignalLevel());
if (demodSignalMeter->inputChanged()) {
demod->setSquelchLevel(demodSignalMeter->getInputValue());
}
activeDemodulator = demod;
}
if (!wxGetApp().getIQVisualQueue()->empty()) {
2014-12-26 16:15:35 -05:00
DemodulatorThreadIQData *iqData;
wxGetApp().getIQVisualQueue()->pop(iqData);
2014-12-23 01:59:03 -05:00
if (iqData && iqData->data.size()) {
// spectrumCanvas->setData(iqData);
waterfallCanvas->setData(iqData);
demodWaterfallCanvas->setData(iqData);
2014-12-23 01:59:03 -05:00
delete iqData;
} else {
std::cout << "Incoming IQ data empty?" << std::endl;
}
work_done = true;
}
if (!wxGetApp().getAudioVisualQueue()->empty()) {
2014-12-23 01:59:03 -05:00
AudioThreadInput *demodAudioData;
wxGetApp().getAudioVisualQueue()->pop(demodAudioData);
2014-12-23 01:59:03 -05:00
if (demodAudioData && demodAudioData->data.size()) {
if (scopeCanvas->waveform_points.size() != demodAudioData->data.size() * 2) {
scopeCanvas->waveform_points.resize(demodAudioData->data.size() * 2);
}
2014-12-23 01:59:03 -05:00
for (int i = 0, iMax = demodAudioData->data.size(); i < iMax; i++) {
scopeCanvas->waveform_points[i * 2 + 1] = demodAudioData->data[i] * 0.5f;
scopeCanvas->waveform_points[i * 2] = ((double) i / (double) iMax);
}
scopeCanvas->setStereo(demodAudioData->channels == 2);
2014-12-26 23:28:18 -05:00
2014-12-23 01:59:03 -05:00
delete demodAudioData;
} else {
std::cout << "Incoming Demodulator data empty?" << std::endl;
}
work_done = true;
}
if (!waterfallCanvas->HasFocus()) {
waterfallCanvas->SetFocus();
}
if (!work_done) {
2014-12-16 21:30:03 -05:00
event.Skip();
}
}
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();
header.newChild("offset") = wxGetApp().getOffset();
DataNode &demods = s.rootNode().newChild("demodulators");
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") = (long)(*instance_i)->getBandwidth();
demod.newChild("frequency") = (long long)(*instance_i)->getFrequency();
demod.newChild("type") = (int)(*instance_i)->getDemodulatorType();
if ((*instance_i)->isSquelchEnabled()) {
demod.newChild("squelch") = (*instance_i)->getSquelchLevel();
demod.newChild("squelch_enabled") = (char)1;
}
demod.newChild("stereo") = (char)(*instance_i)->isStereo();
demod.newChild("output_device") = outputDevices[(*instance_i)->getOutputDevice()].name;
}
s.SaveToFileXML(fileName);
}
bool AppFrame::loadSession(std::string fileName) {
}