mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2026-06-01 21:54:39 -04:00
Update digital_lab
This commit is contained in:
@@ -2,9 +2,7 @@
|
||||
#include "CubicSDR.h"
|
||||
|
||||
DeviceConfig::DeviceConfig() : deviceId("") {
|
||||
iqSwap.store(0);
|
||||
ppm.store(0);
|
||||
directSampling.store(false);
|
||||
offset.store(0);
|
||||
}
|
||||
|
||||
@@ -20,14 +18,6 @@ int DeviceConfig::getPPM() {
|
||||
return ppm.load();
|
||||
}
|
||||
|
||||
void DeviceConfig::setDirectSampling(int mode) {
|
||||
directSampling.store(mode);
|
||||
}
|
||||
|
||||
int DeviceConfig::getDirectSampling() {
|
||||
return directSampling.load();
|
||||
}
|
||||
|
||||
void DeviceConfig::setOffset(long long offset) {
|
||||
this->offset.store(offset);
|
||||
}
|
||||
@@ -36,14 +26,6 @@ long long DeviceConfig::getOffset() {
|
||||
return offset.load();
|
||||
}
|
||||
|
||||
void DeviceConfig::setIQSwap(bool iqSwap) {
|
||||
this->iqSwap.store(iqSwap);
|
||||
}
|
||||
|
||||
bool DeviceConfig::getIQSwap() {
|
||||
return iqSwap.load();
|
||||
}
|
||||
|
||||
void DeviceConfig::setDeviceId(std::string deviceId) {
|
||||
busy_lock.lock();
|
||||
this->deviceId = deviceId;
|
||||
@@ -64,8 +46,6 @@ void DeviceConfig::save(DataNode *node) {
|
||||
busy_lock.lock();
|
||||
*node->newChild("id") = deviceId;
|
||||
*node->newChild("ppm") = (int)ppm;
|
||||
*node->newChild("iq_swap") = iqSwap;
|
||||
*node->newChild("direct_sampling") = directSampling;
|
||||
*node->newChild("offset") = offset;
|
||||
busy_lock.unlock();
|
||||
}
|
||||
@@ -79,32 +59,6 @@ void DeviceConfig::load(DataNode *node) {
|
||||
setPPM(ppmValue);
|
||||
std::cout << "Loaded PPM for device '" << deviceId << "' at " << ppmValue << "ppm" << std::endl;
|
||||
}
|
||||
if (node->hasAnother("iq_swap")) {
|
||||
DataNode *iq_swap_node = node->getNext("iq_swap");
|
||||
int iqSwapValue = 0;
|
||||
iq_swap_node->element()->get(iqSwapValue);
|
||||
setIQSwap(iqSwapValue?true:false);
|
||||
std::cout << "Loaded I/Q Swap for device '" << deviceId << "' as " << (iqSwapValue?"swapped":"not swapped") << std::endl;
|
||||
}
|
||||
if (node->hasAnother("direct_sampling")) {
|
||||
DataNode *direct_sampling_node = node->getNext("direct_sampling");
|
||||
int directSamplingValue = 0;
|
||||
direct_sampling_node->element()->get(directSamplingValue);
|
||||
setDirectSampling(directSamplingValue);
|
||||
std::cout << "Loaded Direct Sampling Mode for device '" << deviceId << "': ";
|
||||
switch (directSamplingValue) {
|
||||
case 0:
|
||||
std::cout << "off" << std::endl;
|
||||
break;
|
||||
case 1:
|
||||
std::cout << "I-ADC" << std::endl;
|
||||
break;
|
||||
case 2:
|
||||
std::cout << "Q-ADC" << std::endl;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (node->hasAnother("offset")) {
|
||||
DataNode *offset_node = node->getNext("offset");
|
||||
long long offsetValue = 0;
|
||||
|
||||
+1
-8
@@ -17,15 +17,9 @@ public:
|
||||
void setPPM(int ppm);
|
||||
int getPPM();
|
||||
|
||||
void setDirectSampling(int mode);
|
||||
int getDirectSampling();
|
||||
|
||||
void setOffset(long long offset);
|
||||
long long getOffset();
|
||||
|
||||
void setIQSwap(bool iqSwap);
|
||||
bool getIQSwap();
|
||||
|
||||
void setDeviceId(std::string deviceId);
|
||||
std::string getDeviceId();
|
||||
|
||||
@@ -36,8 +30,7 @@ private:
|
||||
std::string deviceId;
|
||||
std::mutex busy_lock;
|
||||
|
||||
std::atomic_int ppm, directSampling;
|
||||
std::atomic_bool iqSwap;
|
||||
std::atomic_int ppm;
|
||||
std::atomic_llong offset;
|
||||
};
|
||||
|
||||
|
||||
+353
-173
@@ -14,12 +14,11 @@
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include "SDRThread.h"
|
||||
#include "DemodulatorMgr.h"
|
||||
#include "AudioThread.h"
|
||||
#include "CubicSDR.h"
|
||||
#include "DataTree.h"
|
||||
#include "ColorTheme.h"
|
||||
#include "DemodulatorMgr.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
@@ -35,8 +34,11 @@ 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) {
|
||||
|
||||
@@ -46,12 +48,25 @@ AppFrame::AppFrame() :
|
||||
|
||||
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL);
|
||||
demodTray = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
|
||||
|
||||
demodModeSelector = new ModeSelectorCanvas(this, attribList);
|
||||
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(DEMOD_TYPE_FM, "FM");
|
||||
demodModeSelector->addChoice(DEMOD_TYPE_AM, "AM");
|
||||
demodModeSelector->addChoice(DEMOD_TYPE_LSB, "LSB");
|
||||
@@ -90,14 +105,14 @@ AppFrame::AppFrame() :
|
||||
demodTray->Add(demodModeSelectorCons, 2, wxEXPAND | wxALL, 0);
|
||||
|
||||
wxGetApp().getDemodSpectrumProcessor()->setup(1024);
|
||||
demodSpectrumCanvas = new SpectrumCanvas(this, attribList);
|
||||
demodSpectrumCanvas = new SpectrumCanvas(demodPanel, attribList);
|
||||
demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
|
||||
demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0);
|
||||
wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodSpectrumCanvas->getVisualDataQueue());
|
||||
|
||||
demodVisuals->AddSpacer(1);
|
||||
|
||||
demodWaterfallCanvas = new WaterfallCanvas(this, attribList);
|
||||
demodWaterfallCanvas = new WaterfallCanvas(demodPanel, attribList);
|
||||
demodWaterfallCanvas->setup(1024, 128);
|
||||
demodWaterfallCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
|
||||
demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas);
|
||||
@@ -110,14 +125,16 @@ AppFrame::AppFrame() :
|
||||
|
||||
demodTray->AddSpacer(1);
|
||||
|
||||
demodSignalMeter = new MeterCanvas(this, attribList);
|
||||
demodSignalMeter = new MeterCanvas(demodPanel, attribList);
|
||||
demodSignalMeter->setMax(0.5);
|
||||
demodSignalMeter->setHelpTip("Current Signal Level. Click / Drag to set Squelch level.");
|
||||
demodSignalMeter->SetMinSize(wxSize(12,24));
|
||||
demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
|
||||
demodTray->AddSpacer(1);
|
||||
|
||||
scopeCanvas = new ScopeCanvas(this, attribList);
|
||||
scopeCanvas = new ScopeCanvas(demodPanel, attribList);
|
||||
scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum.");
|
||||
demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0);
|
||||
wxGetApp().getScopeProcessor()->setup(2048);
|
||||
@@ -125,8 +142,9 @@ AppFrame::AppFrame() :
|
||||
|
||||
demodScopeTray->AddSpacer(1);
|
||||
|
||||
demodTuner = new TuningCanvas(this, attribList);
|
||||
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);
|
||||
@@ -135,73 +153,99 @@ AppFrame::AppFrame() :
|
||||
|
||||
wxBoxSizer *demodGainTray = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
demodGainMeter = new MeterCanvas(this, attribList);
|
||||
demodGainMeter = new MeterCanvas(demodPanel, attribList);
|
||||
demodGainMeter->setMax(2.0);
|
||||
demodGainMeter->setHelpTip("Current Demodulator Gain Level. Click / Drag to set Gain level.");
|
||||
demodGainMeter->setShowUserInput(false);
|
||||
demodGainMeter->SetMinSize(wxSize(12,24));
|
||||
demodGainTray->Add(demodGainMeter, 8, wxEXPAND | wxALL, 0);
|
||||
|
||||
|
||||
demodGainTray->AddSpacer(1);
|
||||
|
||||
demodMuteButton = new ModeSelectorCanvas(this, attribList);
|
||||
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);
|
||||
demodMuteButton->setSelection(-1);
|
||||
|
||||
demodMuteButton->SetMinSize(wxSize(12,24));
|
||||
|
||||
demodGainTray->Add(demodMuteButton, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
demodTray->Add(demodGainTray, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0);
|
||||
vbox->AddSpacer(1);
|
||||
|
||||
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(this, attribList);
|
||||
spectrumCanvas = new SpectrumCanvas(spectrumPanel, attribList);
|
||||
spectrumCanvas->setShowDb(true);
|
||||
spectrumCanvas->setScaleFactorEnabled(true);
|
||||
wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue());
|
||||
|
||||
spectrumAvgMeter = new MeterCanvas(this, attribList);
|
||||
spectrumAvgMeter = new MeterCanvas(spectrumPanel, attribList);
|
||||
spectrumAvgMeter->setHelpTip("Spectrum averaging speed, click or drag to adjust.");
|
||||
spectrumAvgMeter->setMax(1.0);
|
||||
spectrumAvgMeter->setLevel(0.65);
|
||||
spectrumAvgMeter->setShowUserInput(false);
|
||||
spectrumAvgMeter->SetMinSize(wxSize(12,24));
|
||||
|
||||
|
||||
spectrumSizer->Add(spectrumCanvas, 63, wxEXPAND | wxALL, 0);
|
||||
spectrumSizer->AddSpacer(1);
|
||||
spectrumSizer->Add(spectrumAvgMeter, 1, wxEXPAND | wxALL, 0);
|
||||
spectrumPanel->SetSizer(spectrumSizer);
|
||||
|
||||
vbox->Add(spectrumSizer, 5, wxEXPAND | wxALL, 0);
|
||||
// vbox->Add(spectrumSizer, 5, wxEXPAND | wxALL, 0);
|
||||
|
||||
vbox->AddSpacer(1);
|
||||
// vbox->AddSpacer(1);
|
||||
|
||||
wxPanel *waterfallPanel = new wxPanel(mainVisSplitter, wxID_ANY);
|
||||
wxBoxSizer *wfSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
waterfallCanvas = new WaterfallCanvas(this, attribList);
|
||||
|
||||
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(this, attribList);
|
||||
waterfallSpeedMeter = new MeterCanvas(waterfallPanel, attribList);
|
||||
waterfallSpeedMeter->setHelpTip("Waterfall speed, click or drag to adjust (max 1024 lines per second)");
|
||||
waterfallSpeedMeter->setMax(sqrt(1024));
|
||||
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);
|
||||
// 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);
|
||||
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
|
||||
@@ -220,9 +264,11 @@ AppFrame::AppFrame() :
|
||||
// SetIcon(wxICON(sample));
|
||||
|
||||
// Make a menubar
|
||||
wxMenuBar *menuBar = new wxMenuBar;
|
||||
menuBar = new wxMenuBar;
|
||||
wxMenu *menu = new wxMenu;
|
||||
|
||||
menu->Append(wxID_SDR_DEVICES, "SDR Devices");
|
||||
menu->AppendSeparator();
|
||||
menu->Append(wxID_OPEN, "&Open Session");
|
||||
menu->Append(wxID_SAVE, "&Save Session");
|
||||
menu->Append(wxID_SAVEAS, "Save Session &As..");
|
||||
@@ -236,21 +282,9 @@ AppFrame::AppFrame() :
|
||||
|
||||
menuBar->Append(menu, wxT("&File"));
|
||||
|
||||
menu = new wxMenu;
|
||||
|
||||
menu->Append(wxID_SET_FREQ_OFFSET, "Frequency Offset");
|
||||
menu->Append(wxID_SET_PPM, "Device PPM");
|
||||
iqSwapMenuItem = menu->AppendCheckItem(wxID_SET_SWAP_IQ, "Swap I/Q");
|
||||
|
||||
wxMenu *dsMenu = new wxMenu;
|
||||
|
||||
directSamplingMenuItems[0] = dsMenu->AppendRadioItem(wxID_SET_DS_OFF, "Off");
|
||||
directSamplingMenuItems[1] = dsMenu->AppendRadioItem(wxID_SET_DS_I, "I-ADC");
|
||||
directSamplingMenuItems[2] = dsMenu->AppendRadioItem(wxID_SET_DS_Q, "Q-ADC");
|
||||
|
||||
menu->AppendSubMenu(dsMenu, "Direct Sampling");
|
||||
|
||||
menuBar->Append(menu, wxT("&Settings"));
|
||||
settingsMenu = new wxMenu;
|
||||
|
||||
menuBar->Append(settingsMenu, wxT("&Settings"));
|
||||
|
||||
menu = new wxMenu;
|
||||
|
||||
@@ -296,54 +330,6 @@ AppFrame::AppFrame() :
|
||||
menuBar->Append(menu, wxT("&Color Scheme"));
|
||||
|
||||
menu = new wxMenu;
|
||||
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_250K] = menu->AppendRadioItem(wxID_BANDWIDTH_250K, "250k");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_1000M] = menu->AppendRadioItem(wxID_BANDWIDTH_1000M, "1.0M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_1500M] = menu->AppendRadioItem(wxID_BANDWIDTH_1024M, "1.024M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_1024M] = menu->AppendRadioItem(wxID_BANDWIDTH_1500M, "1.5M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_1800M] = menu->AppendRadioItem(wxID_BANDWIDTH_1800M, "1.8M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_1920M] = menu->AppendRadioItem(wxID_BANDWIDTH_1920M, "1.92M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_2000M] = menu->AppendRadioItem(wxID_BANDWIDTH_2000M, "2.0M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_2048M] = menu->AppendRadioItem(wxID_BANDWIDTH_2048M, "2.048M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_2160M] = menu->AppendRadioItem(wxID_BANDWIDTH_2160M, "2.16M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_2400M] = menu->AppendRadioItem(wxID_BANDWIDTH_2400M, "2.4M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_2560M] = menu->AppendRadioItem(wxID_BANDWIDTH_2560M, "2.56M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_2880M] = menu->AppendRadioItem(wxID_BANDWIDTH_2880M, "2.88M");
|
||||
// sampleRateMenuItems[wxID_BANDWIDTH_3000M] = menu->AppendRadioItem(wxID_BANDWIDTH_3000M, "3.0M");
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_3200M] = menu->AppendRadioItem(wxID_BANDWIDTH_3200M, "3.2M");
|
||||
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_2400M]->Check(true);
|
||||
|
||||
menuBar->Append(menu, wxT("&Input Bandwidth"));
|
||||
|
||||
std::vector<SDRDeviceInfo *> *devs = wxGetApp().getDevices();
|
||||
std::vector<SDRDeviceInfo *>::iterator devs_i;
|
||||
|
||||
if (devs->size() > 1) {
|
||||
|
||||
menu = new wxMenu;
|
||||
|
||||
int p = 0;
|
||||
for (devs_i = devs->begin(); devs_i != devs->end(); devs_i++) {
|
||||
std::string devName = (*devs_i)->getName();
|
||||
if ((*devs_i)->isAvailable()) {
|
||||
devName.append(": ");
|
||||
devName.append((*devs_i)->getProduct());
|
||||
devName.append(" [");
|
||||
devName.append((*devs_i)->getSerial());
|
||||
devName.append("]");
|
||||
} else {
|
||||
devName.append(" (In Use?)");
|
||||
}
|
||||
|
||||
menu->AppendRadioItem(wxID_DEVICE_ID + p, devName)->Check(wxGetApp().getDevice() == p);
|
||||
p++;
|
||||
}
|
||||
|
||||
menuBar->Append(menu, wxT("Input &Device"));
|
||||
}
|
||||
|
||||
menu = new wxMenu;
|
||||
|
||||
#define NUM_RATES_DEFAULT 4
|
||||
int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 };
|
||||
@@ -371,7 +357,6 @@ AppFrame::AppFrame() :
|
||||
}
|
||||
|
||||
for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) {
|
||||
new wxMenu;
|
||||
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?"));
|
||||
@@ -392,6 +377,10 @@ AppFrame::AppFrame() :
|
||||
}
|
||||
}
|
||||
|
||||
sampleRateMenu = new wxMenu;
|
||||
|
||||
menuBar->Append(sampleRateMenu, wxT("&Input Bandwidth"));
|
||||
|
||||
menuBar->Append(menu, wxT("Audio &Bandwidth"));
|
||||
|
||||
SetMenuBar(menuBar);
|
||||
@@ -441,7 +430,10 @@ AppFrame::AppFrame() :
|
||||
|
||||
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);
|
||||
@@ -452,19 +444,116 @@ AppFrame::~AppFrame() {
|
||||
t_FFTData->join();
|
||||
}
|
||||
|
||||
void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) {
|
||||
this->devInfo = devInfo;
|
||||
deviceChanged.store(true);
|
||||
}
|
||||
|
||||
void AppFrame::initDeviceParams(std::string deviceId) {
|
||||
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId);
|
||||
void AppFrame::updateDeviceParams() {
|
||||
|
||||
int dsMode = devConfig->getDirectSampling();
|
||||
|
||||
if (dsMode > 0 && dsMode <= 2) {
|
||||
directSamplingMenuItems[devConfig->getDirectSampling()]->Check();
|
||||
if (!deviceChanged.load()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (devConfig->getIQSwap()) {
|
||||
iqSwapMenuItem->Check();
|
||||
|
||||
// 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;
|
||||
settingArgs = devInfo->getSettingsArgInfo();
|
||||
for (args_i = settingArgs.begin(); args_i != settingArgs.end(); args_i++) {
|
||||
SoapySDR::ArgInfo arg = (*args_i);
|
||||
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);
|
||||
item->Check(currentVal=="true");
|
||||
i++;
|
||||
} else if (arg.type == SoapySDR::ArgInfo::INT) {
|
||||
newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description);
|
||||
i++;
|
||||
} else if (arg.type == SoapySDR::ArgInfo::FLOAT) {
|
||||
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);
|
||||
if (currentVal == (*str_i)) {
|
||||
item->Check();
|
||||
}
|
||||
j++;
|
||||
i++;
|
||||
}
|
||||
newSettingsMenu->AppendSubMenu(subMenu, arg.name, arg.description);
|
||||
} else {
|
||||
newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
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());
|
||||
|
||||
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++) {
|
||||
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++;
|
||||
}
|
||||
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual Entry"));
|
||||
if (!checked) {
|
||||
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true);
|
||||
}
|
||||
|
||||
menuBar->Replace(4, newSampleRateMenu, wxT("&Input Bandwidth"));
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -482,19 +571,41 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
|
||||
wxGetApp().saveConfig();
|
||||
}
|
||||
} else if (event.GetId() == wxID_SET_DS_OFF) {
|
||||
wxGetApp().setDirectSampling(0);
|
||||
wxGetApp().saveConfig();
|
||||
// wxGetApp().setDirectSampling(0);
|
||||
// wxGetApp().saveConfig();
|
||||
} else if (event.GetId() == wxID_SET_DS_I) {
|
||||
wxGetApp().setDirectSampling(1);
|
||||
wxGetApp().saveConfig();
|
||||
// wxGetApp().setDirectSampling(1);
|
||||
// wxGetApp().saveConfig();
|
||||
} else if (event.GetId() == wxID_SET_DS_Q) {
|
||||
wxGetApp().setDirectSampling(2);
|
||||
wxGetApp().saveConfig();
|
||||
// wxGetApp().setDirectSampling(2);
|
||||
// wxGetApp().saveConfig();
|
||||
} else if (event.GetId() == wxID_SET_SWAP_IQ) {
|
||||
bool swap_state = !wxGetApp().getSwapIQ();
|
||||
wxGetApp().setSwapIQ(swap_state);
|
||||
wxGetApp().saveConfig();
|
||||
iqSwapMenuItem->Check(swap_state);
|
||||
// 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) {
|
||||
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);
|
||||
@@ -545,7 +656,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
|
||||
demodTuner->Refresh();
|
||||
SetTitle(CUBICSDR_TITLE);
|
||||
currentSessionFile = "";
|
||||
} else if (event.GetId() == wxID_EXIT) {
|
||||
} else if (event.GetId() == wxID_CLOSE || event.GetId() == wxID_EXIT) {
|
||||
Close(false);
|
||||
} else if (event.GetId() == wxID_THEME_DEFAULT) {
|
||||
ThemeMgr::mgr.setTheme(COLOR_THEME_DEFAULT);
|
||||
@@ -563,73 +674,99 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
|
||||
ThemeMgr::mgr.setTheme(COLOR_THEME_RADAR);
|
||||
}
|
||||
|
||||
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) {
|
||||
menuIdx++;
|
||||
} 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();
|
||||
}
|
||||
|
||||
switch (event.GetId()) {
|
||||
case wxID_BANDWIDTH_250K:
|
||||
wxGetApp().setSampleRate(250000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_1000M:
|
||||
wxGetApp().setSampleRate(1000000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_1024M:
|
||||
wxGetApp().setSampleRate(1024000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_1500M:
|
||||
wxGetApp().setSampleRate(1500000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_1800M:
|
||||
wxGetApp().setSampleRate(1800000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_1920M:
|
||||
wxGetApp().setSampleRate(1920000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_2000M:
|
||||
wxGetApp().setSampleRate(2000000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_2048M:
|
||||
wxGetApp().setSampleRate(2048000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_2160M:
|
||||
wxGetApp().setSampleRate(2160000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_2400M:
|
||||
wxGetApp().setSampleRate(2400000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_2560M:
|
||||
wxGetApp().setSampleRate(2560000);
|
||||
break;
|
||||
case wxID_BANDWIDTH_2880M:
|
||||
wxGetApp().setSampleRate(2880000);
|
||||
break;
|
||||
// case wxID_BANDWIDTH_3000M:
|
||||
// wxGetApp().setSampleRate(3000000);
|
||||
// break;
|
||||
case wxID_BANDWIDTH_3200M:
|
||||
wxGetApp().setSampleRate(3200000);
|
||||
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 Bandwidth Entry",
|
||||
wxGetApp().getSampleRate(),
|
||||
rateLow,
|
||||
rateHigh,
|
||||
this);
|
||||
if (bw != -1) {
|
||||
wxGetApp().setSampleRate(bw);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<SDRDeviceInfo *> *devs = wxGetApp().getDevices();
|
||||
if (event.GetId() >= wxID_DEVICE_ID && event.GetId() <= wxID_DEVICE_ID + devs->size()) {
|
||||
int devId = event.GetId() - wxID_DEVICE_ID;
|
||||
wxGetApp().setDevice(devId);
|
||||
|
||||
SDRDeviceInfo *dev = (*wxGetApp().getDevices())[devId];
|
||||
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId());
|
||||
|
||||
int dsMode = devConfig->getDirectSampling();
|
||||
|
||||
if (dsMode >= 0 && dsMode <= 2) {
|
||||
directSamplingMenuItems[devConfig->getDirectSampling()]->Check();
|
||||
}
|
||||
|
||||
iqSwapMenuItem->Check(devConfig->getIQSwap());
|
||||
|
||||
if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE+sampleRates.size()) {
|
||||
wxGetApp().setSampleRate(sampleRates[event.GetId()-wxID_BANDWIDTH_BASE]);
|
||||
}
|
||||
|
||||
|
||||
if (event.GetId() >= wxID_AUDIO_BANDWIDTH_BASE) {
|
||||
int evId = event.GetId();
|
||||
std::vector<RtAudio::DeviceInfo>::iterator devices_i;
|
||||
@@ -683,11 +820,13 @@ void AppFrame::OnThread(wxCommandEvent& event) {
|
||||
|
||||
void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
|
||||
if (deviceChanged.load()) {
|
||||
updateDeviceParams();
|
||||
}
|
||||
|
||||
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||
|
||||
if (demod) {
|
||||
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||
|
||||
if (demod->isTracking()) {
|
||||
if (spectrumCanvas->getViewState()) {
|
||||
long long diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()) + (demod->getBandwidth()/2) + (demod->getBandwidth()/4);
|
||||
@@ -851,8 +990,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
if (!demodTuner->HasFocus()) {
|
||||
demodTuner->SetFocus();
|
||||
}
|
||||
} else if (!waterfallCanvas->HasFocus()) {
|
||||
waterfallCanvas->SetFocus();
|
||||
} else if (!wxGetApp().isDeviceSelectorOpen()) {
|
||||
if (!waterfallCanvas->HasFocus()) {
|
||||
waterfallCanvas->SetFocus();
|
||||
}
|
||||
}
|
||||
|
||||
scopeCanvas->setPPMMode(demodTuner->isAltDown());
|
||||
@@ -863,10 +1004,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0));
|
||||
|
||||
wxGetApp().getScopeProcessor()->run();
|
||||
wxGetApp().getSpectrumDistributor()->run();
|
||||
// wxGetApp().getSpectrumDistributor()->run();
|
||||
|
||||
SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor();
|
||||
|
||||
|
||||
if (spectrumAvgMeter->inputChanged()) {
|
||||
float val = spectrumAvgMeter->getInputValue();
|
||||
if (val < 0.01) {
|
||||
@@ -892,7 +1033,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
dproc->setCenterFrequency(demodWaterfallCanvas->getCenterFrequency());
|
||||
|
||||
SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor();
|
||||
|
||||
|
||||
if (waterfallSpeedMeter->inputChanged()) {
|
||||
float val = waterfallSpeedMeter->getInputValue();
|
||||
waterfallSpeedMeter->setLevel(val);
|
||||
@@ -904,6 +1045,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
wproc->setView(waterfallCanvas->getViewState());
|
||||
wproc->setBandwidth(waterfallCanvas->getBandwidth());
|
||||
wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
|
||||
wxGetApp().getSDRPostThread()->setIQVisualRange(waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth());
|
||||
|
||||
// waterfallCanvas->processInputQueue();
|
||||
// waterfallCanvas->Refresh();
|
||||
@@ -917,6 +1059,39 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
event.RequestMore();
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void AppFrame::saveSession(std::string fileName) {
|
||||
DataTree s("cubicsdr_session");
|
||||
DataNode *header = s.rootNode()->newChild("header");
|
||||
@@ -1054,3 +1229,8 @@ bool AppFrame::loadSession(std::string fileName) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FFTVisualDataThread *AppFrame::getWaterfallDataThread() {
|
||||
return waterfallDataThread;
|
||||
}
|
||||
|
||||
|
||||
+37
-20
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "wx/frame.h"
|
||||
#include <wx/frame.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/splitter.h>
|
||||
#include <wx/sizer.h>
|
||||
|
||||
#include "PrimaryGLContext.h"
|
||||
|
||||
#include "ScopeCanvas.h"
|
||||
@@ -9,7 +13,9 @@
|
||||
#include "MeterCanvas.h"
|
||||
#include "TuningCanvas.h"
|
||||
#include "ModeSelectorCanvas.h"
|
||||
#include "GainCanvas.h"
|
||||
#include "FFTVisualDataThread.h"
|
||||
#include "SDRDeviceInfo.h"
|
||||
//#include "UITestCanvas.h"
|
||||
|
||||
#include <map>
|
||||
@@ -22,6 +28,11 @@
|
||||
#define wxID_SET_DS_I 2005
|
||||
#define wxID_SET_DS_Q 2006
|
||||
#define wxID_SET_SWAP_IQ 2007
|
||||
#define wxID_SDR_DEVICES 2008
|
||||
#define wxID_AGC_CONTROL 2009
|
||||
|
||||
#define wxID_MAIN_SPLITTER 2050
|
||||
#define wxID_VIS_SPLITTER 2051
|
||||
|
||||
#define wxID_THEME_DEFAULT 2100
|
||||
#define wxID_THEME_SHARP 2101
|
||||
@@ -31,20 +42,10 @@
|
||||
#define wxID_THEME_HD 2105
|
||||
#define wxID_THEME_RADAR 2106
|
||||
|
||||
#define wxID_BANDWIDTH_250K 2150
|
||||
#define wxID_BANDWIDTH_1000M 2151
|
||||
#define wxID_BANDWIDTH_1024M 2152
|
||||
#define wxID_BANDWIDTH_1500M 2153
|
||||
#define wxID_BANDWIDTH_1800M 2154
|
||||
#define wxID_BANDWIDTH_1920M 2155
|
||||
#define wxID_BANDWIDTH_2000M 2156
|
||||
#define wxID_BANDWIDTH_2048M 2157
|
||||
#define wxID_BANDWIDTH_2160M 2158
|
||||
#define wxID_BANDWIDTH_2400M 2159
|
||||
#define wxID_BANDWIDTH_2560M 2160
|
||||
#define wxID_BANDWIDTH_2880M 2161
|
||||
//#define wxID_BANDWIDTH_3000M 2162
|
||||
#define wxID_BANDWIDTH_3200M 2163
|
||||
#define wxID_BANDWIDTH_BASE 2150
|
||||
#define wxID_BANDWIDTH_MANUAL 2200
|
||||
|
||||
#define wxID_SETTINGS_BASE 2300
|
||||
|
||||
#define wxID_DEVICE_ID 3500
|
||||
|
||||
@@ -58,18 +59,22 @@ public:
|
||||
~AppFrame();
|
||||
void OnThread(wxCommandEvent& event);
|
||||
void OnEventInput(wxThreadEvent& event);
|
||||
void initDeviceParams(std::string deviceId);
|
||||
void initDeviceParams(SDRDeviceInfo *devInfo);
|
||||
void updateDeviceParams();
|
||||
|
||||
void saveSession(std::string fileName);
|
||||
bool loadSession(std::string fileName);
|
||||
|
||||
FFTVisualDataThread *getWaterfallDataThread();
|
||||
|
||||
private:
|
||||
void OnMenu(wxCommandEvent& event);
|
||||
void OnClose(wxCloseEvent& event);
|
||||
void OnNewWindow(wxCommandEvent& event);
|
||||
void OnIdle(wxIdleEvent& event);
|
||||
|
||||
|
||||
void OnDoubleClickSash(wxSplitterEvent& event);
|
||||
void OnUnSplit(wxSplitterEvent& event);
|
||||
|
||||
ScopeCanvas *scopeCanvas;
|
||||
SpectrumCanvas *spectrumCanvas;
|
||||
WaterfallCanvas *waterfallCanvas;
|
||||
@@ -85,7 +90,11 @@ private:
|
||||
MeterCanvas *spectrumAvgMeter;
|
||||
MeterCanvas *waterfallSpeedMeter;
|
||||
ModeSelectorCanvas *demodMuteButton;
|
||||
|
||||
GainCanvas *gainCanvas;
|
||||
wxSizerItem *gainSizerItem, *gainSpacerItem;
|
||||
wxSplitterWindow *mainVisSplitter, *mainSplitter;
|
||||
wxBoxSizer *demodTray;
|
||||
|
||||
DemodulatorInstance *activeDemodulator;
|
||||
|
||||
std::vector<RtAudio::DeviceInfo> devices;
|
||||
@@ -95,13 +104,21 @@ private:
|
||||
std::map<int, wxMenuItem *> sampleRateMenuItems;
|
||||
std::map<int, wxMenuItem *> audioSampleRateMenuItems;
|
||||
std::map<int, wxMenuItem *> directSamplingMenuItems;
|
||||
wxMenuItem *iqSwapMenuItem;
|
||||
wxMenuBar *menuBar;
|
||||
wxMenu *sampleRateMenu;
|
||||
wxMenuItem *agcMenuItem;
|
||||
wxMenu *settingsMenu;
|
||||
SoapySDR::ArgInfoList settingArgs;
|
||||
int settingsIdMax;
|
||||
std::vector<long> sampleRates;
|
||||
|
||||
std::string currentSessionFile;
|
||||
|
||||
FFTVisualDataThread *waterfallDataThread;
|
||||
|
||||
std::thread *t_FFTData;
|
||||
SDRDeviceInfo *devInfo;
|
||||
std::atomic_bool deviceChanged;
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
+342
-160
@@ -12,6 +12,7 @@
|
||||
#endif
|
||||
|
||||
#include "CubicSDR.h"
|
||||
#include <iomanip>
|
||||
|
||||
#ifdef _OSX_APP_
|
||||
#include "CoreFoundation/CoreFoundation.h"
|
||||
@@ -32,10 +33,101 @@ class outbuf : public std::streambuf {
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MINGW_PATCH
|
||||
FILE _iob[] = { *stdin, *stdout, *stderr };
|
||||
|
||||
CubicSDR::CubicSDR() : appframe(NULL), m_glContext(NULL), frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), directSamplingMode(0),
|
||||
sdrThread(NULL), sdrPostThread(NULL), spectrumVisualThread(NULL), demodVisualThread(NULL), pipeSDRCommand(NULL), pipeSDRIQData(NULL), pipeIQVisualData(NULL), pipeAudioVisualData(NULL), t_SDR(NULL), t_PostSDR(NULL) {
|
||||
extern "C" FILE * __cdecl __iob_func(void)
|
||||
{
|
||||
return _iob;
|
||||
}
|
||||
|
||||
extern "C" int __cdecl __isnan(double x)
|
||||
{
|
||||
return _finite(x)?0:1;
|
||||
}
|
||||
|
||||
extern "C" int __cdecl __isnanf(float x)
|
||||
{
|
||||
return _finitef(x)?0:1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
std::string& filterChars(std::string& s, const std::string& allowed) {
|
||||
s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) {
|
||||
return allowed.find(c) == std::string::npos;
|
||||
}), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string frequencyToStr(long long freq) {
|
||||
long double freqTemp;
|
||||
|
||||
freqTemp = freq;
|
||||
std::string suffix("");
|
||||
std::stringstream freqStr;
|
||||
|
||||
if (freqTemp >= 1.0e9) {
|
||||
freqTemp /= 1.0e9;
|
||||
freqStr << std::setprecision(10);
|
||||
suffix = std::string("GHz");
|
||||
} else if (freqTemp >= 1.0e6) {
|
||||
freqTemp /= 1.0e6;
|
||||
freqStr << std::setprecision(7);
|
||||
suffix = std::string("MHz");
|
||||
} else if (freqTemp >= 1.0e3) {
|
||||
freqTemp /= 1.0e3;
|
||||
freqStr << std::setprecision(4);
|
||||
suffix = std::string("KHz");
|
||||
}
|
||||
|
||||
freqStr << freqTemp;
|
||||
freqStr << suffix;
|
||||
|
||||
return freqStr.str();
|
||||
}
|
||||
|
||||
long long strToFrequency(std::string freqStr) {
|
||||
std::string filterStr = filterChars(freqStr, std::string("0123456789.MKGHmkgh"));
|
||||
|
||||
int numLen = filterStr.find_first_not_of("0123456789.");
|
||||
|
||||
if (numLen == std::string::npos) {
|
||||
numLen = freqStr.length();
|
||||
}
|
||||
|
||||
std::string numPartStr = freqStr.substr(0, numLen);
|
||||
std::string suffixStr = freqStr.substr(numLen);
|
||||
|
||||
std::stringstream numPartStream;
|
||||
numPartStream.str(numPartStr);
|
||||
|
||||
long double freqTemp = 0;
|
||||
|
||||
numPartStream >> freqTemp;
|
||||
|
||||
if (suffixStr.length()) {
|
||||
if (suffixStr.find_first_of("Gg") != std::string::npos) {
|
||||
freqTemp *= 1.0e9;
|
||||
} else if (suffixStr.find_first_of("Mm") != std::string::npos) {
|
||||
freqTemp *= 1.0e6;
|
||||
} else if (suffixStr.find_first_of("Kk") != std::string::npos) {
|
||||
freqTemp *= 1.0e3;
|
||||
} else if (suffixStr.find_first_of("Hh") != std::string::npos) {
|
||||
// ...
|
||||
}
|
||||
} else if (numPartStr.find_first_of(".") != std::string::npos || freqTemp <= 3000) {
|
||||
freqTemp *= 1.0e6;
|
||||
}
|
||||
|
||||
return (long long) freqTemp;
|
||||
}
|
||||
|
||||
|
||||
CubicSDR::CubicSDR() : appframe(NULL), m_glContext(NULL), frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE),
|
||||
sdrThread(NULL), sdrPostThread(NULL), spectrumVisualThread(NULL), demodVisualThread(NULL), pipeSDRIQData(NULL), pipeIQVisualData(NULL), pipeAudioVisualData(NULL), t_SDR(NULL), t_PostSDR(NULL) {
|
||||
sampleRateInitialized.store(false);
|
||||
agcMode.store(true);
|
||||
}
|
||||
|
||||
bool CubicSDR::OnInit() {
|
||||
@@ -71,7 +163,8 @@ bool CubicSDR::OnInit() {
|
||||
frequency = wxGetApp().getConfig()->getCenterFreq();
|
||||
offset = 0;
|
||||
ppm = 0;
|
||||
directSamplingMode = 0;
|
||||
devicesReady.store(false);
|
||||
deviceSelectorOpen.store(false);
|
||||
|
||||
// Visual Data
|
||||
spectrumVisualThread = new SpectrumVisualDataThread();
|
||||
@@ -80,22 +173,15 @@ bool CubicSDR::OnInit() {
|
||||
pipeIQVisualData = new DemodulatorThreadInputQueue();
|
||||
pipeIQVisualData->set_max_num_items(1);
|
||||
|
||||
spectrumDistributor.setInput(pipeIQVisualData);
|
||||
|
||||
pipeDemodIQVisualData = new DemodulatorThreadInputQueue();
|
||||
pipeDemodIQVisualData->set_max_num_items(1);
|
||||
|
||||
pipeSpectrumIQVisualData = new DemodulatorThreadInputQueue();
|
||||
pipeSpectrumIQVisualData->set_max_num_items(1);
|
||||
|
||||
pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
|
||||
pipeWaterfallIQVisualData->set_max_num_items(128);
|
||||
|
||||
spectrumDistributor.attachOutput(pipeDemodIQVisualData);
|
||||
spectrumDistributor.attachOutput(pipeSpectrumIQVisualData);
|
||||
|
||||
getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData);
|
||||
getSpectrumProcessor()->setInput(pipeSpectrumIQVisualData);
|
||||
getSpectrumProcessor()->setInput(pipeIQVisualData);
|
||||
getSpectrumProcessor()->setHideDC(true);
|
||||
|
||||
pipeAudioVisualData = new DemodulatorThreadOutputQueue();
|
||||
pipeAudioVisualData->set_max_num_items(1);
|
||||
@@ -104,83 +190,35 @@ bool CubicSDR::OnInit() {
|
||||
|
||||
// I/Q Data
|
||||
pipeSDRIQData = new SDRThreadIQDataQueue();
|
||||
pipeSDRCommand = new SDRThreadCommandQueue();
|
||||
|
||||
pipeSDRIQData->set_max_num_items(100);
|
||||
|
||||
sdrThread = new SDRThread();
|
||||
sdrThread->setInputQueue("SDRCommandQueue",pipeSDRCommand);
|
||||
sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData);
|
||||
|
||||
sdrPostThread = new SDRPostThread();
|
||||
// sdrPostThread->setNumVisSamples(BUF_SIZE);
|
||||
sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData);
|
||||
sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
|
||||
sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
|
||||
|
||||
std::vector<SDRDeviceInfo *>::iterator devs_i;
|
||||
|
||||
SDRThread::enumerate_rtl(&devs);
|
||||
SDRDeviceInfo *dev = NULL;
|
||||
|
||||
if (devs.size() > 1) {
|
||||
wxArrayString choices;
|
||||
for (devs_i = devs.begin(); devs_i != devs.end(); devs_i++) {
|
||||
std::string devName = (*devs_i)->getName();
|
||||
if ((*devs_i)->isAvailable()) {
|
||||
devName.append(": ");
|
||||
devName.append((*devs_i)->getProduct());
|
||||
devName.append(" [");
|
||||
devName.append((*devs_i)->getSerial());
|
||||
devName.append("]");
|
||||
} else {
|
||||
devName.append(" (In Use?)");
|
||||
}
|
||||
choices.Add(devName);
|
||||
}
|
||||
|
||||
int devId = wxGetSingleChoiceIndex(wxT("Devices"), wxT("Choose Input Device"), choices);
|
||||
if (devId == -1) { // User chose to cancel
|
||||
return false;
|
||||
}
|
||||
|
||||
dev = devs[devId];
|
||||
|
||||
sdrThread->setDeviceId(devId);
|
||||
} else if (devs.size() == 1) {
|
||||
dev = devs[0];
|
||||
}
|
||||
|
||||
if (!dev) {
|
||||
wxMessageDialog *info;
|
||||
info = new wxMessageDialog(NULL, wxT("\x28\u256F\xB0\u25A1\xB0\uFF09\u256F\uFE35\x20\u253B\u2501\u253B"), wxT("RTL-SDR device not found"), wxOK | wxICON_ERROR);
|
||||
info->ShowModal();
|
||||
return false;
|
||||
}
|
||||
sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData);
|
||||
|
||||
t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread);
|
||||
t_SDR = new std::thread(&SDRThread::threadMain, sdrThread);
|
||||
t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread);
|
||||
t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread);
|
||||
|
||||
sdrEnum = new SDREnumerator();
|
||||
|
||||
appframe = new AppFrame();
|
||||
if (dev != NULL) {
|
||||
appframe->initDeviceParams(dev->getDeviceId());
|
||||
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId());
|
||||
ppm = devConfig->getPPM();
|
||||
offset = devConfig->getOffset();
|
||||
directSamplingMode = devConfig->getDirectSampling();
|
||||
}
|
||||
t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum);
|
||||
|
||||
#ifdef __APPLE__
|
||||
int main_policy;
|
||||
struct sched_param main_param;
|
||||
|
||||
main_policy = SCHED_RR;
|
||||
main_param.sched_priority = sched_get_priority_min(SCHED_RR)+2;
|
||||
|
||||
pthread_setschedparam(pthread_self(), main_policy, &main_param);
|
||||
#endif
|
||||
//#ifdef __APPLE__
|
||||
// int main_policy;
|
||||
// struct sched_param main_param;
|
||||
//
|
||||
// main_policy = SCHED_RR;
|
||||
// main_param.sched_priority = sched_get_priority_min(SCHED_RR)+2;
|
||||
//
|
||||
// pthread_setschedparam(pthread_self(), main_policy, &main_param);
|
||||
//#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -189,9 +227,12 @@ int CubicSDR::OnExit() {
|
||||
demodMgr.terminateAll();
|
||||
|
||||
std::cout << "Terminating SDR thread.." << std::endl;
|
||||
sdrThread->terminate();
|
||||
t_SDR->join();
|
||||
|
||||
if (!sdrThread->isTerminated()) {
|
||||
sdrThread->terminate();
|
||||
if (t_SDR) {
|
||||
t_SDR->join();
|
||||
}
|
||||
}
|
||||
std::cout << "Terminating SDR post-processing thread.." << std::endl;
|
||||
sdrPostThread->terminate();
|
||||
t_PostSDR->join();
|
||||
@@ -204,7 +245,6 @@ int CubicSDR::OnExit() {
|
||||
t_DemodVisual->join();
|
||||
|
||||
delete sdrThread;
|
||||
delete t_SDR;
|
||||
|
||||
delete sdrPostThread;
|
||||
delete t_PostSDR;
|
||||
@@ -214,8 +254,6 @@ int CubicSDR::OnExit() {
|
||||
delete t_DemodVisual;
|
||||
delete demodVisualThread;
|
||||
|
||||
delete pipeSDRCommand;
|
||||
|
||||
delete pipeIQVisualData;
|
||||
delete pipeAudioVisualData;
|
||||
delete pipeSDRIQData;
|
||||
@@ -254,17 +292,87 @@ bool CubicSDR::OnCmdLineParsed(wxCmdLineParser& parser) {
|
||||
|
||||
config.load();
|
||||
|
||||
#ifdef BUNDLE_SOAPY_MODS
|
||||
if (parser.Found("b")) {
|
||||
useLocalMod.store(false);
|
||||
} else {
|
||||
useLocalMod.store(true);
|
||||
}
|
||||
#else
|
||||
useLocalMod.store(true);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CubicSDR::deviceSelector() {
|
||||
if (deviceSelectorOpen) {
|
||||
deviceSelectorDialog->Raise();
|
||||
deviceSelectorDialog->SetFocus();
|
||||
return;
|
||||
}
|
||||
deviceSelectorOpen.store(true);
|
||||
deviceSelectorDialog = new SDRDevicesDialog(appframe);
|
||||
deviceSelectorDialog->Show();
|
||||
}
|
||||
|
||||
void CubicSDR::addRemote(std::string remoteAddr) {
|
||||
SDREnumerator::addRemote(remoteAddr);
|
||||
devicesReady.store(false);
|
||||
t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum);
|
||||
}
|
||||
|
||||
void CubicSDR::removeRemote(std::string remoteAddr) {
|
||||
SDREnumerator::removeRemote(remoteAddr);
|
||||
}
|
||||
|
||||
void CubicSDR::sdrThreadNotify(SDRThread::SDRThreadState state, std::string message) {
|
||||
notify_busy.lock();
|
||||
if (state == SDRThread::SDR_THREAD_INITIALIZED) {
|
||||
appframe->initDeviceParams(getDevice());
|
||||
}
|
||||
if (state == SDRThread::SDR_THREAD_MESSAGE) {
|
||||
notifyMessage = message;
|
||||
}
|
||||
if (state == SDRThread::SDR_THREAD_TERMINATED) {
|
||||
t_SDR->join();
|
||||
delete t_SDR;
|
||||
}
|
||||
if (state == SDRThread::SDR_THREAD_FAILED) {
|
||||
notifyMessage = message;
|
||||
// wxMessageDialog *info;
|
||||
// info = new wxMessageDialog(NULL, message, wxT("Error initializing device"), wxOK | wxICON_ERROR);
|
||||
// info->ShowModal();
|
||||
}
|
||||
//if (appframe) { appframe->SetStatusText(message); }
|
||||
notify_busy.unlock();
|
||||
}
|
||||
|
||||
|
||||
void CubicSDR::sdrEnumThreadNotify(SDREnumerator::SDREnumState state, std::string message) {
|
||||
notify_busy.lock();
|
||||
if (state == SDREnumerator::SDR_ENUM_MESSAGE) {
|
||||
notifyMessage = message;
|
||||
}
|
||||
if (state == SDREnumerator::SDR_ENUM_DEVICES_READY) {
|
||||
devs = SDREnumerator::enumerate_devices("", true);
|
||||
devicesReady.store(true);
|
||||
}
|
||||
if (state == SDREnumerator::SDR_ENUM_FAILED) {
|
||||
notifyMessage = message;
|
||||
sdrEnum->terminate();
|
||||
}
|
||||
//if (appframe) { appframe->SetStatusText(message); }
|
||||
notify_busy.unlock();
|
||||
}
|
||||
|
||||
|
||||
void CubicSDR::setFrequency(long long freq) {
|
||||
if (freq < sampleRate / 2) {
|
||||
freq = sampleRate / 2;
|
||||
}
|
||||
frequency = freq;
|
||||
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_TUNE);
|
||||
command.llong_value = freq;
|
||||
pipeSDRCommand->push(command);
|
||||
sdrThread->setFrequency(freq);
|
||||
}
|
||||
|
||||
long long CubicSDR::getOffset() {
|
||||
@@ -273,42 +381,93 @@ long long CubicSDR::getOffset() {
|
||||
|
||||
void CubicSDR::setOffset(long long ofs) {
|
||||
offset = ofs;
|
||||
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_OFFSET);
|
||||
command.llong_value = ofs;
|
||||
pipeSDRCommand->push(command);
|
||||
|
||||
SDRDeviceInfo *dev = (*getDevices())[getDevice()];
|
||||
sdrThread->setOffset(offset);
|
||||
SDRDeviceInfo *dev = getDevice();
|
||||
config.getDevice(dev->getDeviceId())->setOffset(ofs);
|
||||
}
|
||||
|
||||
void CubicSDR::setDirectSampling(int mode) {
|
||||
directSamplingMode = mode;
|
||||
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_DIRECT_SAMPLING);
|
||||
command.llong_value = mode;
|
||||
pipeSDRCommand->push(command);
|
||||
|
||||
SDRDeviceInfo *dev = (*getDevices())[getDevice()];
|
||||
config.getDevice(dev->getDeviceId())->setDirectSampling(mode);
|
||||
}
|
||||
|
||||
int CubicSDR::getDirectSampling() {
|
||||
return directSamplingMode;
|
||||
}
|
||||
|
||||
void CubicSDR::setSwapIQ(bool swapIQ) {
|
||||
sdrPostThread->setSwapIQ(swapIQ);
|
||||
SDRDeviceInfo *dev = (*getDevices())[getDevice()];
|
||||
config.getDevice(dev->getDeviceId())->setIQSwap(swapIQ);
|
||||
}
|
||||
|
||||
bool CubicSDR::getSwapIQ() {
|
||||
return sdrPostThread->getSwapIQ();
|
||||
}
|
||||
|
||||
long long CubicSDR::getFrequency() {
|
||||
return frequency;
|
||||
}
|
||||
|
||||
void CubicSDR::setSampleRate(long long rate_in) {
|
||||
sampleRate = rate_in;
|
||||
sdrThread->setSampleRate(sampleRate);
|
||||
setFrequency(frequency);
|
||||
}
|
||||
|
||||
void CubicSDR::setDevice(SDRDeviceInfo *dev) {
|
||||
if (!sdrThread->isTerminated()) {
|
||||
sdrThread->terminate();
|
||||
if (t_SDR) {
|
||||
t_SDR->join();
|
||||
delete t_SDR;
|
||||
}
|
||||
}
|
||||
|
||||
for (SoapySDR::Kwargs::const_iterator i = settingArgs.begin(); i != settingArgs.end(); i++) {
|
||||
sdrThread->writeSetting(i->first, i->second);
|
||||
}
|
||||
sdrThread->setStreamArgs(streamArgs);
|
||||
sdrThread->setDevice(dev);
|
||||
|
||||
DeviceConfig *devConfig = config.getDevice(dev->getDeviceId());
|
||||
|
||||
SDRDeviceChannel *chan = dev->getRxChannel();
|
||||
|
||||
if (chan) {
|
||||
long long freqHigh, freqLow;
|
||||
|
||||
freqHigh = chan->getRFRange().getHigh();
|
||||
freqLow = chan->getRFRange().getLow();
|
||||
|
||||
// upconverter settings don't like this, need to handle elsewhere..
|
||||
// if (frequency > freqHigh) {
|
||||
// frequency = freqHigh;
|
||||
// }
|
||||
// else if (frequency < freqLow) {
|
||||
// frequency = freqLow;
|
||||
// }
|
||||
|
||||
// Try for a reasonable default sample rate.
|
||||
if (!sampleRateInitialized.load()) {
|
||||
sampleRate = chan->getSampleRateNear(DEFAULT_SAMPLE_RATE);
|
||||
sampleRateInitialized.store(true);
|
||||
}
|
||||
|
||||
int rateHigh, rateLow;
|
||||
|
||||
rateHigh = rateLow = sampleRate;
|
||||
|
||||
if (chan->getSampleRates().size()) {
|
||||
rateLow = chan->getSampleRates()[0];
|
||||
rateHigh = chan->getSampleRates()[chan->getSampleRates().size()-1];
|
||||
}
|
||||
|
||||
if (sampleRate > rateHigh) {
|
||||
sampleRate = rateHigh;
|
||||
} else if (sampleRate < rateLow) {
|
||||
sampleRate = rateLow;
|
||||
}
|
||||
|
||||
if (frequency < sampleRate/2) {
|
||||
frequency = sampleRate/2;
|
||||
}
|
||||
|
||||
setFrequency(frequency);
|
||||
setSampleRate(sampleRate);
|
||||
|
||||
setPPM(devConfig->getPPM());
|
||||
setOffset(devConfig->getOffset());
|
||||
|
||||
t_SDR = new std::thread(&SDRThread::threadMain, sdrThread);
|
||||
}
|
||||
}
|
||||
|
||||
SDRDeviceInfo *CubicSDR::getDevice() {
|
||||
return sdrThread->getDevice();
|
||||
}
|
||||
|
||||
ScopeVisualProcessor *CubicSDR::getScopeProcessor() {
|
||||
return &scopeProcessor;
|
||||
}
|
||||
@@ -321,11 +480,6 @@ SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() {
|
||||
return demodVisualThread->getProcessor();
|
||||
}
|
||||
|
||||
VisualDataReDistributor<DemodulatorThreadIQData> *CubicSDR::getSpectrumDistributor() {
|
||||
return &spectrumDistributor;
|
||||
}
|
||||
|
||||
|
||||
DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() {
|
||||
return pipeAudioVisualData;
|
||||
}
|
||||
@@ -342,6 +496,15 @@ DemodulatorMgr &CubicSDR::getDemodMgr() {
|
||||
return demodMgr;
|
||||
}
|
||||
|
||||
SDRPostThread *CubicSDR::getSDRPostThread() {
|
||||
return sdrPostThread;
|
||||
}
|
||||
|
||||
SDRThread *CubicSDR::getSDRThread() {
|
||||
return sdrThread;
|
||||
}
|
||||
|
||||
|
||||
void CubicSDR::bindDemodulator(DemodulatorInstance *demod) {
|
||||
if (!demod) {
|
||||
return;
|
||||
@@ -349,14 +512,6 @@ void CubicSDR::bindDemodulator(DemodulatorInstance *demod) {
|
||||
sdrPostThread->bindDemodulator(demod);
|
||||
}
|
||||
|
||||
void CubicSDR::setSampleRate(long long rate_in) {
|
||||
sampleRate = rate_in;
|
||||
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_SAMPLERATE);
|
||||
command.llong_value = rate_in;
|
||||
pipeSDRCommand->push(command);
|
||||
setFrequency(frequency);
|
||||
}
|
||||
|
||||
long long CubicSDR::getSampleRate() {
|
||||
return sampleRate;
|
||||
}
|
||||
@@ -370,27 +525,9 @@ void CubicSDR::removeDemodulator(DemodulatorInstance *demod) {
|
||||
}
|
||||
|
||||
std::vector<SDRDeviceInfo*>* CubicSDR::getDevices() {
|
||||
return &devs;
|
||||
return devs;
|
||||
}
|
||||
|
||||
void CubicSDR::setDevice(int deviceId) {
|
||||
sdrThread->setDeviceId(deviceId);
|
||||
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_DEVICE);
|
||||
command.llong_value = deviceId;
|
||||
pipeSDRCommand->push(command);
|
||||
|
||||
SDRDeviceInfo *dev = (*getDevices())[deviceId];
|
||||
DeviceConfig *devConfig = config.getDevice(dev->getDeviceId());
|
||||
|
||||
setPPM(devConfig->getPPM());
|
||||
setDirectSampling(devConfig->getDirectSampling());
|
||||
setSwapIQ(devConfig->getIQSwap());
|
||||
setOffset(devConfig->getOffset());
|
||||
}
|
||||
|
||||
int CubicSDR::getDevice() {
|
||||
return sdrThread->getDeviceId();
|
||||
}
|
||||
|
||||
AppConfig *CubicSDR::getConfig() {
|
||||
return &config;
|
||||
@@ -401,29 +538,20 @@ void CubicSDR::saveConfig() {
|
||||
}
|
||||
|
||||
void CubicSDR::setPPM(int ppm_in) {
|
||||
if (sdrThread->getDeviceId() < 0) {
|
||||
return;
|
||||
}
|
||||
ppm = ppm_in;
|
||||
sdrThread->setPPM(ppm);
|
||||
|
||||
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_PPM);
|
||||
command.llong_value = ppm;
|
||||
pipeSDRCommand->push(command);
|
||||
|
||||
SDRDeviceInfo *dev = (*getDevices())[getDevice()];
|
||||
|
||||
config.getDevice(dev->getDeviceId())->setPPM(ppm_in);
|
||||
SDRDeviceInfo *dev = getDevice();
|
||||
if (dev) {
|
||||
config.getDevice(dev->getDeviceId())->setPPM(ppm_in);
|
||||
}
|
||||
}
|
||||
|
||||
int CubicSDR::getPPM() {
|
||||
if (sdrThread->getDeviceId() < 0) {
|
||||
return 0;
|
||||
SDRDeviceInfo *dev = sdrThread->getDevice();
|
||||
if (dev) {
|
||||
ppm = config.getDevice(dev->getDeviceId())->getPPM();
|
||||
}
|
||||
SDRDeviceInfo *dev = (*getDevices())[getDevice()];
|
||||
|
||||
SDRThreadCommand command_ppm(SDRThreadCommand::SDR_THREAD_CMD_SET_PPM);
|
||||
ppm = config.getDevice(dev->getDeviceId())->getPPM();
|
||||
|
||||
return ppm;
|
||||
}
|
||||
|
||||
@@ -464,3 +592,57 @@ void CubicSDR::setFrequencySnap(int snap) {
|
||||
int CubicSDR::getFrequencySnap() {
|
||||
return snap;
|
||||
}
|
||||
|
||||
bool CubicSDR::areDevicesReady() {
|
||||
return devicesReady.load();
|
||||
}
|
||||
|
||||
bool CubicSDR::areDevicesEnumerating() {
|
||||
return !sdrEnum->isTerminated();
|
||||
}
|
||||
|
||||
std::string CubicSDR::getNotification() {
|
||||
std::string msg;
|
||||
notify_busy.lock();
|
||||
msg = notifyMessage;
|
||||
notify_busy.unlock();
|
||||
return msg;
|
||||
}
|
||||
|
||||
void CubicSDR::setDeviceSelectorClosed() {
|
||||
deviceSelectorOpen.store(false);
|
||||
}
|
||||
|
||||
bool CubicSDR::isDeviceSelectorOpen() {
|
||||
return deviceSelectorOpen.load();
|
||||
}
|
||||
|
||||
void CubicSDR::setAGCMode(bool mode) {
|
||||
agcMode.store(mode);
|
||||
sdrThread->setAGCMode(mode);
|
||||
}
|
||||
|
||||
bool CubicSDR::getAGCMode() {
|
||||
return agcMode.load();
|
||||
}
|
||||
|
||||
|
||||
void CubicSDR::setGain(std::string name, float gain_in) {
|
||||
sdrThread->setGain(name,gain_in);
|
||||
}
|
||||
|
||||
float CubicSDR::getGain(std::string name) {
|
||||
return sdrThread->getGain(name);
|
||||
}
|
||||
|
||||
void CubicSDR::setStreamArgs(SoapySDR::Kwargs streamArgs_in) {
|
||||
streamArgs = streamArgs_in;
|
||||
}
|
||||
|
||||
void CubicSDR::setDeviceArgs(SoapySDR::Kwargs settingArgs_in) {
|
||||
settingArgs = settingArgs_in;
|
||||
}
|
||||
|
||||
bool CubicSDR::getUseLocalMod() {
|
||||
return useLocalMod.load();
|
||||
}
|
||||
+66
-19
@@ -10,7 +10,12 @@
|
||||
#include "PrimaryGLContext.h"
|
||||
|
||||
#include "ThreadQueue.h"
|
||||
#include "SDRThread.h"
|
||||
#ifdef USE_RTL_SDR
|
||||
#include "SDRThread.h"
|
||||
#else
|
||||
#include "SoapySDRThread.h"
|
||||
#include "SDREnumerator.h"
|
||||
#endif
|
||||
#include "SDRPostThread.h"
|
||||
#include "AudioThread.h"
|
||||
#include "DemodulatorMgr.h"
|
||||
@@ -21,11 +26,16 @@
|
||||
#include "ScopeVisualProcessor.h"
|
||||
#include "SpectrumVisualProcessor.h"
|
||||
#include "SpectrumVisualDataThread.h"
|
||||
#include "SDRDevices.h"
|
||||
|
||||
#include <wx/cmdline.h>
|
||||
|
||||
#define NUM_DEMODULATORS 1
|
||||
|
||||
std::string& filterChars(std::string& s, const std::string& allowed);
|
||||
std::string frequencyToStr(long long freq);
|
||||
long long strToFrequency(std::string freqStr);
|
||||
|
||||
class CubicSDR: public wxApp {
|
||||
public:
|
||||
CubicSDR();
|
||||
@@ -38,35 +48,36 @@ public:
|
||||
virtual void OnInitCmdLine(wxCmdLineParser& parser);
|
||||
virtual bool OnCmdLineParsed(wxCmdLineParser& parser);
|
||||
|
||||
void deviceSelector();
|
||||
void sdrThreadNotify(SDRThread::SDRThreadState state, std::string message);
|
||||
void sdrEnumThreadNotify(SDREnumerator::SDREnumState state, std::string message);
|
||||
|
||||
void setFrequency(long long freq);
|
||||
long long getFrequency();
|
||||
|
||||
void setOffset(long long ofs);
|
||||
long long getOffset();
|
||||
|
||||
void setDirectSampling(int mode);
|
||||
int getDirectSampling();
|
||||
|
||||
void setSwapIQ(bool swapIQ);
|
||||
bool getSwapIQ();
|
||||
|
||||
void setSampleRate(long long rate_in);
|
||||
long long getSampleRate();
|
||||
|
||||
std::vector<SDRDeviceInfo *> *getDevices();
|
||||
void setDevice(int deviceId);
|
||||
int getDevice();
|
||||
void setDevice(SDRDeviceInfo *dev);
|
||||
SDRDeviceInfo * getDevice();
|
||||
|
||||
ScopeVisualProcessor *getScopeProcessor();
|
||||
SpectrumVisualProcessor *getSpectrumProcessor();
|
||||
SpectrumVisualProcessor *getDemodSpectrumProcessor();
|
||||
VisualDataReDistributor<DemodulatorThreadIQData> *getSpectrumDistributor();
|
||||
|
||||
DemodulatorThreadOutputQueue* getAudioVisualQueue();
|
||||
DemodulatorThreadInputQueue* getIQVisualQueue();
|
||||
DemodulatorThreadInputQueue* getWaterfallVisualQueue();
|
||||
DemodulatorThreadInputQueue* getActiveDemodVisualQueue();
|
||||
DemodulatorMgr &getDemodMgr();
|
||||
|
||||
SDRPostThread *getSDRPostThread();
|
||||
SDRThread *getSDRThread();
|
||||
|
||||
void bindDemodulator(DemodulatorInstance *demod);
|
||||
void removeDemodulator(DemodulatorInstance *demod);
|
||||
|
||||
@@ -82,11 +93,31 @@ public:
|
||||
void showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode = FrequencyDialog::FDIALOG_TARGET_DEFAULT);
|
||||
AppFrame *getAppFrame();
|
||||
|
||||
bool areDevicesReady();
|
||||
bool areDevicesEnumerating();
|
||||
std::string getNotification();
|
||||
|
||||
void addRemote(std::string remoteAddr);
|
||||
void removeRemote(std::string remoteAddr);
|
||||
|
||||
void setDeviceSelectorClosed();
|
||||
bool isDeviceSelectorOpen();
|
||||
|
||||
void setAGCMode(bool mode);
|
||||
bool getAGCMode();
|
||||
|
||||
void setGain(std::string name, float gain_in);
|
||||
float getGain(std::string name);
|
||||
|
||||
void setStreamArgs(SoapySDR::Kwargs streamArgs_in);
|
||||
void setDeviceArgs(SoapySDR::Kwargs settingArgs_in);
|
||||
|
||||
bool getUseLocalMod();
|
||||
private:
|
||||
AppFrame *appframe;
|
||||
AppConfig config;
|
||||
PrimaryGLContext *m_glContext;
|
||||
std::vector<SDRDeviceInfo *> devs;
|
||||
std::vector<SDRDeviceInfo *> *devs;
|
||||
|
||||
DemodulatorMgr demodMgr;
|
||||
|
||||
@@ -94,36 +125,52 @@ private:
|
||||
long long offset;
|
||||
int ppm, snap;
|
||||
long long sampleRate;
|
||||
int directSamplingMode;
|
||||
std::atomic_bool agcMode;
|
||||
|
||||
SDRThread *sdrThread;
|
||||
SDREnumerator *sdrEnum;
|
||||
SDRPostThread *sdrPostThread;
|
||||
SpectrumVisualDataThread *spectrumVisualThread;
|
||||
SpectrumVisualDataThread *demodVisualThread;
|
||||
|
||||
SDRThreadCommandQueue* pipeSDRCommand;
|
||||
SDRThreadIQDataQueue* pipeSDRIQData;
|
||||
DemodulatorThreadInputQueue* pipeIQVisualData;
|
||||
DemodulatorThreadOutputQueue* pipeAudioVisualData;
|
||||
DemodulatorThreadInputQueue* pipeDemodIQVisualData;
|
||||
DemodulatorThreadInputQueue* pipeSpectrumIQVisualData;
|
||||
DemodulatorThreadInputQueue* pipeWaterfallIQVisualData;
|
||||
DemodulatorThreadInputQueue* pipeActiveDemodIQVisualData;
|
||||
|
||||
ScopeVisualProcessor scopeProcessor;
|
||||
|
||||
VisualDataReDistributor<DemodulatorThreadIQData> spectrumDistributor;
|
||||
SDRDevicesDialog *deviceSelectorDialog;
|
||||
|
||||
std::thread *t_SDR;
|
||||
std::thread *t_PostSDR;
|
||||
std::thread *t_SpectrumVisual;
|
||||
std::thread *t_DemodVisual;
|
||||
SoapySDR::Kwargs streamArgs;
|
||||
SoapySDR::Kwargs settingArgs;
|
||||
|
||||
std::thread *t_SDR, *t_SDREnum, *t_PostSDR, *t_SpectrumVisual, *t_DemodVisual;
|
||||
std::atomic_bool devicesReady;
|
||||
std::atomic_bool deviceSelectorOpen;
|
||||
std::atomic_bool sampleRateInitialized;
|
||||
std::atomic_bool useLocalMod;
|
||||
std::string notifyMessage;
|
||||
std::mutex notify_busy;
|
||||
};
|
||||
|
||||
#ifdef BUNDLE_SOAPY_MODS
|
||||
static const wxCmdLineEntryDesc commandLineInfo [] =
|
||||
{
|
||||
{ wxCMD_LINE_SWITCH, "h", "help", "Command line parameter help", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
|
||||
{ wxCMD_LINE_OPTION, "c", "config", "Specify a named configuration to use, i.e. '-c ham'" },
|
||||
{ wxCMD_LINE_SWITCH, "b", "bundled", "Use bundled SoapySDR modules first instead of local." },
|
||||
{ wxCMD_LINE_NONE }
|
||||
};
|
||||
#else
|
||||
static const wxCmdLineEntryDesc commandLineInfo [] =
|
||||
{
|
||||
{ wxCMD_LINE_SWITCH, "h", "help", "Command line parameter help", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
|
||||
{ wxCMD_LINE_OPTION, "c", "config", "Specify a named configuration to use, i.e. '-c ham'" },
|
||||
{ wxCMD_LINE_NONE }
|
||||
};
|
||||
#endif
|
||||
|
||||
DECLARE_APP(CubicSDR)
|
||||
|
||||
+5
-1
@@ -27,10 +27,14 @@ const char filePathSeparator =
|
||||
|
||||
#define BUF_SIZE (16384*6)
|
||||
|
||||
#define DEFAULT_SAMPLE_RATE 2400000
|
||||
#define DEFAULT_SAMPLE_RATE 2500000
|
||||
#define DEFAULT_FFT_SIZE 2048
|
||||
|
||||
#define DEFAULT_DEMOD_TYPE 1
|
||||
#define DEFAULT_DEMOD_BW 200000
|
||||
|
||||
#define DEFAULT_WATERFALL_LPS 30
|
||||
|
||||
#define CHANNELIZER_RATE_MAX 400000
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "wx/clipbrd.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "CubicSDR.h"
|
||||
|
||||
wxBEGIN_EVENT_TABLE(FrequencyDialog, wxDialog)
|
||||
@@ -37,75 +36,6 @@ FrequencyDialog::FrequencyDialog(wxWindow * parent, wxWindowID id, const wxStrin
|
||||
dialogText->SetSelection(-1, -1);
|
||||
}
|
||||
|
||||
std::string& FrequencyDialog::filterChars(std::string& s, const std::string& allowed) {
|
||||
s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) {
|
||||
return allowed.find(c) == std::string::npos;
|
||||
}), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string FrequencyDialog::frequencyToStr(long long freq) {
|
||||
long double freqTemp;
|
||||
|
||||
freqTemp = freq;
|
||||
std::string suffix("");
|
||||
std::stringstream freqStr;
|
||||
|
||||
if (freqTemp >= 1.0e9) {
|
||||
freqTemp /= 1.0e9;
|
||||
freqStr << std::setprecision(10);
|
||||
suffix = std::string("GHz");
|
||||
} else if (freqTemp >= 1.0e6) {
|
||||
freqTemp /= 1.0e6;
|
||||
freqStr << std::setprecision(7);
|
||||
suffix = std::string("MHz");
|
||||
} else if (freqTemp >= 1.0e3) {
|
||||
freqTemp /= 1.0e3;
|
||||
freqStr << std::setprecision(4);
|
||||
suffix = std::string("KHz");
|
||||
}
|
||||
|
||||
freqStr << freqTemp;
|
||||
freqStr << suffix;
|
||||
|
||||
return freqStr.str();
|
||||
}
|
||||
|
||||
long long FrequencyDialog::strToFrequency(std::string freqStr) {
|
||||
std::string filterStr = filterChars(freqStr, std::string("0123456789.MKGHmkgh"));
|
||||
|
||||
int numLen = filterStr.find_first_not_of("0123456789.");
|
||||
|
||||
if (numLen == std::string::npos) {
|
||||
numLen = freqStr.length();
|
||||
}
|
||||
|
||||
std::string numPartStr = freqStr.substr(0, numLen);
|
||||
std::string suffixStr = freqStr.substr(numLen);
|
||||
|
||||
std::stringstream numPartStream;
|
||||
numPartStream.str(numPartStr);
|
||||
|
||||
long double freqTemp = 0;
|
||||
|
||||
numPartStream >> freqTemp;
|
||||
|
||||
if (suffixStr.length()) {
|
||||
if (suffixStr.find_first_of("Gg") != std::string::npos) {
|
||||
freqTemp *= 1.0e9;
|
||||
} else if (suffixStr.find_first_of("Mm") != std::string::npos) {
|
||||
freqTemp *= 1.0e6;
|
||||
} else if (suffixStr.find_first_of("Kk") != std::string::npos) {
|
||||
freqTemp *= 1.0e3;
|
||||
} else if (suffixStr.find_first_of("Hh") != std::string::npos) {
|
||||
// ...
|
||||
}
|
||||
} else if (numPartStr.find_first_of(".") != std::string::npos || freqTemp <= 3000) {
|
||||
freqTemp *= 1.0e6;
|
||||
}
|
||||
|
||||
return (long long) freqTemp;
|
||||
}
|
||||
|
||||
void FrequencyDialog::OnChar(wxKeyEvent& event) {
|
||||
int c = event.GetKeyCode();
|
||||
|
||||
@@ -21,14 +21,10 @@ public:
|
||||
|
||||
wxTextCtrl * dialogText;
|
||||
|
||||
long long strToFrequency(std::string freqStr);
|
||||
std::string frequencyToStr(long long freq);
|
||||
|
||||
private:
|
||||
DemodulatorInstance *activeDemod;
|
||||
void OnEnter ( wxCommandEvent &event );
|
||||
void OnChar ( wxKeyEvent &event );
|
||||
std::string& filterChars(std::string& s, const std::string& allowed);
|
||||
FrequencyDialogTarget targetMode;
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ AudioThread::AudioThread() : IOThread(),
|
||||
outputDevice.store(-1);
|
||||
gain.store(1.0);
|
||||
|
||||
boundThreads = new std::vector<AudioThread *>;
|
||||
boundThreads.store(new std::vector<AudioThread *>);
|
||||
}
|
||||
|
||||
AudioThread::~AudioThread() {
|
||||
@@ -428,7 +428,7 @@ bool AudioThread::isActive() {
|
||||
void AudioThread::setActive(bool state) {
|
||||
|
||||
AudioThreadInput *dummy;
|
||||
if (state && !active) {
|
||||
if (state && !active && inputQueue) {
|
||||
while (!inputQueue->empty()) { // flush queue
|
||||
inputQueue->pop(dummy);
|
||||
if (dummy) {
|
||||
@@ -438,10 +438,12 @@ void AudioThread::setActive(bool state) {
|
||||
deviceController[parameters.deviceId]->bindThread(this);
|
||||
} else if (!state && active) {
|
||||
deviceController[parameters.deviceId]->removeThread(this);
|
||||
while (!inputQueue->empty()) { // flush queue
|
||||
inputQueue->pop(dummy);
|
||||
if (dummy) {
|
||||
dummy->decRefCount();
|
||||
if(inputQueue) {
|
||||
while (!inputQueue->empty()) { // flush queue
|
||||
inputQueue->pop(dummy);
|
||||
if (dummy) {
|
||||
dummy->decRefCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,9 @@ void DemodulatorInstance::setOutputDevice(int device_id) {
|
||||
|
||||
int DemodulatorInstance::getOutputDevice() {
|
||||
if (currentOutputDevice == -1) {
|
||||
currentOutputDevice = audioThread->getOutputDevice();
|
||||
if (audioThread) {
|
||||
currentOutputDevice = audioThread->getOutputDevice();
|
||||
}
|
||||
}
|
||||
|
||||
return currentOutputDevice;
|
||||
@@ -326,7 +328,7 @@ void DemodulatorInstance::setBandwidth(int bw) {
|
||||
bw = AudioThread::deviceSampleRate[getOutputDevice()];
|
||||
}
|
||||
}
|
||||
if (!active) {
|
||||
if (!active && demodulatorPreThread != NULL) {
|
||||
currentBandwidth = bw;
|
||||
checkBandwidth();
|
||||
demodulatorPreThread->getParams().bandwidth = currentBandwidth;
|
||||
|
||||
@@ -155,18 +155,20 @@ void DemodulatorPreThread::run() {
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
inp->decRefCount();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Requested frequency is not center, shift it into the center!
|
||||
if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) {
|
||||
shiftFrequency = params.frequency - inp->frequency;
|
||||
if (abs(shiftFrequency) <= (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) {
|
||||
nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) wxGetApp().getSampleRate())));
|
||||
if (abs(shiftFrequency) <= (int) ((double) (inp->sampleRate / 2) * 1.5)) {
|
||||
nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) inp->sampleRate)));
|
||||
}
|
||||
}
|
||||
|
||||
if (abs(shiftFrequency) > (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) {
|
||||
if (abs(shiftFrequency) > (int) ((double) (inp->sampleRate / 2) * 1.5)) {
|
||||
inp->decRefCount();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,14 +126,13 @@ void DemodulatorThread::run() {
|
||||
nco_crcf_pll_set_bandwidth(stereoPilot, 0.25f);
|
||||
|
||||
// half band filter used for side-band elimination
|
||||
resamp2_cccf ssbFilt = resamp2_cccf_create(12,-0.25f,60.0f);
|
||||
resamp2_crcf ssbFilt = resamp2_crcf_create(12,-0.25f,60.0f);
|
||||
|
||||
// Automatic IQ gain
|
||||
iqAutoGain = agc_crcf_create();
|
||||
agc_crcf_set_bandwidth(iqAutoGain, 0.1);
|
||||
|
||||
AudioThreadInput *ati_vis = new AudioThreadInput;
|
||||
ati_vis->data.reserve(DEMOD_VIS_SIZE);
|
||||
ReBuffer<AudioThreadInput> audioVisBuffers;
|
||||
|
||||
std::cout << "Demodulator thread started.." << std::endl;
|
||||
|
||||
@@ -271,14 +270,14 @@ void DemodulatorThread::run() {
|
||||
case DEMOD_TYPE_LSB:
|
||||
currentDemodLock = false;
|
||||
for (int i = 0; i < bufSize; i++) { // Reject upper band
|
||||
resamp2_cccf_filter_execute(ssbFilt,(*inputData)[i],&x,&y);
|
||||
resamp2_crcf_filter_execute(ssbFilt,(*inputData)[i],&x,&y);
|
||||
ampmodem_demodulate(demodAM, x, &demodOutputData[i]);
|
||||
}
|
||||
break;
|
||||
case DEMOD_TYPE_USB:
|
||||
currentDemodLock = false;
|
||||
for (int i = 0; i < bufSize; i++) { // Reject lower band
|
||||
resamp2_cccf_filter_execute(ssbFilt,(*inputData)[i],&x,&y);
|
||||
resamp2_crcf_filter_execute(ssbFilt,(*inputData)[i],&x,&y);
|
||||
ampmodem_demodulate(demodAM, y, &demodOutputData[i]);
|
||||
}
|
||||
break;
|
||||
@@ -734,8 +733,8 @@ void DemodulatorThread::run() {
|
||||
}
|
||||
|
||||
if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) {
|
||||
|
||||
ati_vis->busy_update.lock();
|
||||
AudioThreadInput *ati_vis = audioVisBuffers.getBuffer();
|
||||
ati_vis->setRefCount(1);
|
||||
ati_vis->sampleRate = inp->sampleRate;
|
||||
ati_vis->inputRate = inp->sampleRate;
|
||||
|
||||
@@ -780,7 +779,6 @@ void DemodulatorThread::run() {
|
||||
// std::cout << "Signal: " << agc_crcf_get_signal_level(agc) << " -- " << agc_crcf_get_rssi(agc) << "dB " << std::endl;
|
||||
}
|
||||
|
||||
ati_vis->busy_update.unlock();
|
||||
audioVisOutputQueue->push(ati_vis);
|
||||
}
|
||||
|
||||
@@ -899,7 +897,7 @@ void DemodulatorThread::run() {
|
||||
firhilbf_destroy(firStereoR2C);
|
||||
firhilbf_destroy(firStereoC2R);
|
||||
nco_crcf_destroy(stereoPilot);
|
||||
resamp2_cccf_destroy(ssbFilt);
|
||||
resamp2_crcf_destroy(ssbFilt);
|
||||
|
||||
outputBuffers.purge();
|
||||
|
||||
@@ -907,7 +905,7 @@ void DemodulatorThread::run() {
|
||||
AudioThreadInput *dummy_vis;
|
||||
audioVisOutputQueue->pop(dummy_vis);
|
||||
}
|
||||
delete ati_vis;
|
||||
audioVisBuffers.purge();
|
||||
|
||||
DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_DEMOD_TERMINATED);
|
||||
tCmd.context = this;
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
#include "SDRDevices.h"
|
||||
|
||||
#include <wx/textdlg.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include "CubicSDR.h"
|
||||
|
||||
SDRDevicesDialog::SDRDevicesDialog( wxWindow* parent ): devFrame( parent ) {
|
||||
refresh = true;
|
||||
m_addRemoteButton->Disable();
|
||||
m_useSelectedButton->Disable();
|
||||
m_deviceTimer.Start(250);
|
||||
|
||||
}
|
||||
|
||||
void SDRDevicesDialog::OnClose( wxCloseEvent& event ) {
|
||||
wxGetApp().setDeviceSelectorClosed();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void SDRDevicesDialog::OnDeleteItem( wxTreeEvent& event ) {
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
wxPGProperty *SDRDevicesDialog::addArgInfoProperty(wxPropertyGrid *pg, SoapySDR::ArgInfo arg) {
|
||||
|
||||
wxPGProperty *prop = NULL;
|
||||
|
||||
int intVal;
|
||||
double floatVal;
|
||||
std::vector<std::string>::iterator stringIter;
|
||||
|
||||
switch (arg.type) {
|
||||
case SoapySDR::ArgInfo::INT:
|
||||
try {
|
||||
intVal = std::stoi(arg.value);
|
||||
} catch (std::invalid_argument e) {
|
||||
intVal = 0;
|
||||
}
|
||||
prop = pg->Append( new wxIntProperty(arg.name, wxPG_LABEL, intVal) );
|
||||
if (arg.range.minimum() != arg.range.maximum()) {
|
||||
pg->SetPropertyAttribute( prop, wxPG_ATTR_MIN, arg.range.minimum());
|
||||
pg->SetPropertyAttribute( prop, wxPG_ATTR_MAX, arg.range.maximum());
|
||||
}
|
||||
break;
|
||||
case SoapySDR::ArgInfo::FLOAT:
|
||||
try {
|
||||
floatVal = std::stod(arg.value);
|
||||
} catch (std::invalid_argument e) {
|
||||
floatVal = 0;
|
||||
}
|
||||
prop = pg->Append( new wxFloatProperty(arg.name, wxPG_LABEL, floatVal) );
|
||||
if (arg.range.minimum() != arg.range.maximum()) {
|
||||
pg->SetPropertyAttribute( prop, wxPG_ATTR_MIN, arg.range.minimum());
|
||||
pg->SetPropertyAttribute( prop, wxPG_ATTR_MAX, arg.range.maximum());
|
||||
}
|
||||
break;
|
||||
case SoapySDR::ArgInfo::BOOL:
|
||||
prop = pg->Append( new wxBoolProperty(arg.name, wxPG_LABEL, (arg.value=="true")) );
|
||||
break;
|
||||
case SoapySDR::ArgInfo::STRING:
|
||||
if (arg.options.size()) {
|
||||
intVal = 0;
|
||||
prop = pg->Append( new wxEnumProperty(arg.name, wxPG_LABEL) );
|
||||
for (stringIter = arg.options.begin(); stringIter != arg.options.end(); stringIter++) {
|
||||
std::string optName = (*stringIter);
|
||||
std::string displayName = optName;
|
||||
if (arg.optionNames.size()) {
|
||||
displayName = arg.optionNames[intVal];
|
||||
}
|
||||
|
||||
prop->AddChoice(displayName);
|
||||
if ((*stringIter)==arg.value) {
|
||||
prop->SetChoiceSelection(intVal);
|
||||
}
|
||||
|
||||
intVal++;
|
||||
}
|
||||
} else {
|
||||
prop = pg->Append( new wxStringProperty(arg.name, wxPG_LABEL, arg.value) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (prop != NULL) {
|
||||
prop->SetHelpString(arg.key + ": " + arg.description);
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
void SDRDevicesDialog::OnSelectionChanged( wxTreeEvent& event ) {
|
||||
wxTreeItemId selId = devTree->GetSelection();
|
||||
|
||||
dev = getSelectedDevice(selId);
|
||||
props.erase(props.begin(), props.end());
|
||||
if (dev) {
|
||||
m_propertyGrid->Clear();
|
||||
m_propertyGrid->Append(new wxPropertyCategory("Run-time Settings"));
|
||||
|
||||
SoapySDR::ArgInfoList::const_iterator args_i;
|
||||
|
||||
SoapySDR::ArgInfoList args = dev->getSettingsArgInfo();
|
||||
|
||||
for (args_i = args.begin(); args_i != args.end(); args_i++) {
|
||||
SoapySDR::ArgInfo arg = (*args_i);
|
||||
props.push_back(addArgInfoProperty(m_propertyGrid, arg));
|
||||
}
|
||||
|
||||
if (dev->getRxChannel()) {
|
||||
args = dev->getRxChannel()->getStreamArgsInfo();
|
||||
|
||||
if (args.size()) {
|
||||
m_propertyGrid->Append(new wxPropertyCategory("Stream Settings"));
|
||||
|
||||
for (args_i = args.begin(); args_i != args.end(); args_i++) {
|
||||
SoapySDR::ArgInfo arg = (*args_i);
|
||||
props.push_back(addArgInfoProperty(m_propertyGrid, arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void SDRDevicesDialog::OnAddRemote( wxMouseEvent& event ) {
|
||||
if (!SDREnumerator::hasRemoteModule()) {
|
||||
wxMessageDialog *info;
|
||||
info = new wxMessageDialog(NULL, wxT("Install SoapyRemote module to add remote servers.\n\nhttps://github.com/pothosware/SoapyRemote"), wxT("SoapyRemote not found."), wxOK | wxICON_ERROR);
|
||||
info->ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
wxString remoteAddr =
|
||||
wxGetTextFromUser("Remote Address (address[:port])\n\ni.e. 'raspberrypi.local', '192.168.1.103:1234'\n","SoapySDR Remote Address", "", this);
|
||||
|
||||
if (!remoteAddr.Trim().empty()) {
|
||||
wxGetApp().addRemote(remoteAddr.Trim().ToStdString());
|
||||
}
|
||||
devTree->Disable();
|
||||
m_addRemoteButton->Disable();
|
||||
m_useSelectedButton->Disable();
|
||||
refresh = true;
|
||||
|
||||
}
|
||||
|
||||
SDRDeviceInfo *SDRDevicesDialog::getSelectedDevice(wxTreeItemId selId) {
|
||||
devItems_i = devItems.find(selId);
|
||||
if (devItems_i != devItems.end()) {
|
||||
return devItems[selId];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event ) {
|
||||
wxTreeItemId selId = devTree->GetSelection();
|
||||
|
||||
dev = getSelectedDevice(selId);
|
||||
if (dev != NULL) {
|
||||
|
||||
int i = 0;
|
||||
SoapySDR::ArgInfoList::const_iterator args_i;
|
||||
SoapySDR::ArgInfoList args = dev->getSettingsArgInfo();
|
||||
|
||||
SoapySDR::Kwargs settingArgs;
|
||||
SoapySDR::Kwargs streamArgs;
|
||||
|
||||
for (args_i = args.begin(); args_i != args.end(); args_i++) {
|
||||
SoapySDR::ArgInfo arg = (*args_i);
|
||||
wxPGProperty *prop = props[i];
|
||||
|
||||
if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) {
|
||||
settingArgs[arg.key] = arg.options[prop->GetChoiceSelection()];
|
||||
} else if (arg.type == SoapySDR::ArgInfo::BOOL) {
|
||||
settingArgs[arg.key] = (prop->GetValueAsString()=="True")?"true":"false";
|
||||
} else {
|
||||
settingArgs[arg.key] = prop->GetValueAsString();
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (dev->getRxChannel()) {
|
||||
args = dev->getRxChannel()->getStreamArgsInfo();
|
||||
|
||||
if (args.size()) {
|
||||
for (args_i = args.begin(); args_i != args.end(); args_i++) {
|
||||
SoapySDR::ArgInfo arg = (*args_i);
|
||||
wxPGProperty *prop = props[i];
|
||||
|
||||
if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) {
|
||||
streamArgs[arg.key] = arg.options[prop->GetChoiceSelection()];
|
||||
} else if (arg.type == SoapySDR::ArgInfo::BOOL) {
|
||||
streamArgs[arg.key] = (prop->GetValueAsString()=="True")?"true":"false";
|
||||
} else {
|
||||
streamArgs[arg.key] = prop->GetValueAsString();
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wxGetApp().setDeviceArgs(settingArgs);
|
||||
wxGetApp().setStreamArgs(streamArgs);
|
||||
wxGetApp().setDevice(dev);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
void SDRDevicesDialog::OnTreeDoubleClick( wxMouseEvent& event ) {
|
||||
OnUseSelected(event);
|
||||
}
|
||||
|
||||
void SDRDevicesDialog::OnDeviceTimer( wxTimerEvent& event ) {
|
||||
if (refresh) {
|
||||
if (wxGetApp().areDevicesEnumerating() || !wxGetApp().areDevicesReady()) {
|
||||
std::string msg = wxGetApp().getNotification();
|
||||
devStatusBar->SetStatusText(msg);
|
||||
devTree->DeleteAllItems();
|
||||
devTree->AddRoot(msg);
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
devTree->DeleteAllItems();
|
||||
|
||||
wxTreeItemId devRoot = devTree->AddRoot("Devices");
|
||||
wxTreeItemId localBranch = devTree->AppendItem(devRoot, "Local");
|
||||
wxTreeItemId remoteBranch = devTree->AppendItem(devRoot, "Remote");
|
||||
|
||||
devs[""] = SDREnumerator::enumerate_devices("",true);
|
||||
if (devs[""] != NULL) {
|
||||
for (devs_i = devs[""]->begin(); devs_i != devs[""]->end(); devs_i++) {
|
||||
devItems[devTree->AppendItem(localBranch, (*devs_i)->getName())] = (*devs_i);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> remotes = SDREnumerator::getRemotes();
|
||||
std::vector<std::string>::iterator remotes_i;
|
||||
std::vector<SDRDeviceInfo *>::iterator remoteDevs_i;
|
||||
|
||||
if (remotes.size()) {
|
||||
for (remotes_i = remotes.begin(); remotes_i != remotes.end(); remotes_i++) {
|
||||
devs[*remotes_i] = SDREnumerator::enumerate_devices(*remotes_i, true);
|
||||
wxTreeItemId remoteNode = devTree->AppendItem(remoteBranch, *remotes_i);
|
||||
|
||||
if (devs[*remotes_i] != NULL) {
|
||||
for (remoteDevs_i = devs[*remotes_i]->begin(); remoteDevs_i != devs[*remotes_i]->end(); remoteDevs_i++) {
|
||||
devItems[devTree->AppendItem(remoteNode, (*remoteDevs_i)->getName())] = (*remoteDevs_i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_addRemoteButton->Enable();
|
||||
m_useSelectedButton->Enable();
|
||||
devTree->Enable();
|
||||
devTree->ExpandAll();
|
||||
|
||||
devStatusBar->SetStatusText("Ready.");
|
||||
|
||||
refresh = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,945 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<wxFormBuilder_Project>
|
||||
<FileVersion major="1" minor="13" />
|
||||
<object class="Project" expanded="1">
|
||||
<property name="class_decoration"></property>
|
||||
<property name="code_generation">C++</property>
|
||||
<property name="disconnect_events">1</property>
|
||||
<property name="disconnect_mode">source_name</property>
|
||||
<property name="disconnect_php_events">0</property>
|
||||
<property name="disconnect_python_events">0</property>
|
||||
<property name="embedded_files_path">res</property>
|
||||
<property name="encoding">UTF-8</property>
|
||||
<property name="event_generation">connect</property>
|
||||
<property name="file">SDRDevicesForm</property>
|
||||
<property name="first_id">1000</property>
|
||||
<property name="help_provider">none</property>
|
||||
<property name="internationalize">0</property>
|
||||
<property name="name">SDRDevicesForm</property>
|
||||
<property name="namespace"></property>
|
||||
<property name="path">.</property>
|
||||
<property name="precompiled_header"></property>
|
||||
<property name="relative_path">1</property>
|
||||
<property name="skip_lua_events">1</property>
|
||||
<property name="skip_php_events">1</property>
|
||||
<property name="skip_python_events">1</property>
|
||||
<property name="ui_table">UI</property>
|
||||
<property name="use_enum">0</property>
|
||||
<property name="use_microsoft_bom">0</property>
|
||||
<object class="Frame" expanded="1">
|
||||
<property name="aui_managed">0</property>
|
||||
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
|
||||
<property name="bg"></property>
|
||||
<property name="center">wxBOTH</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="event_handler">impl_virtual</property>
|
||||
<property name="extra_style"></property>
|
||||
<property name="fg"></property>
|
||||
<property name="font"></property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">devFrame</property>
|
||||
<property name="pos"></property>
|
||||
<property name="size">700,467</property>
|
||||
<property name="style">wxDEFAULT_FRAME_STYLE</property>
|
||||
<property name="subclass"></property>
|
||||
<property name="title">CubicSDR :: SDR Devices</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<property name="xrc_skip_sizer">1</property>
|
||||
<event name="OnActivate"></event>
|
||||
<event name="OnActivateApp"></event>
|
||||
<event name="OnAuiFindManager"></event>
|
||||
<event name="OnAuiPaneButton"></event>
|
||||
<event name="OnAuiPaneClose"></event>
|
||||
<event name="OnAuiPaneMaximize"></event>
|
||||
<event name="OnAuiPaneRestore"></event>
|
||||
<event name="OnAuiRender"></event>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnClose">OnClose</event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnHibernate"></event>
|
||||
<event name="OnIconize"></event>
|
||||
<event name="OnIdle"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxStatusBar" expanded="1">
|
||||
<property name="bg"></property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="fields">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">devStatusBar</property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pos"></property>
|
||||
<property name="size"></property>
|
||||
<property name="style">wxST_SIZEGRIP</property>
|
||||
<property name="subclass"></property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">devFrameSizer</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND | wxALL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxPanel" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_panel3</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer4</property>
|
||||
<property name="orient">wxHORIZONTAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND | wxALL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxPanel" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_panel6</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer6</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="0">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND|wxALIGN_RIGHT</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxTreeCtrl" expanded="0">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">0</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">devTree</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style">wxTR_DEFAULT_STYLE</property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick">OnTreeDoubleClick</event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnTreeBeginDrag"></event>
|
||||
<event name="OnTreeBeginLabelEdit"></event>
|
||||
<event name="OnTreeBeginRDrag"></event>
|
||||
<event name="OnTreeDeleteItem">OnDeleteItem</event>
|
||||
<event name="OnTreeEndDrag"></event>
|
||||
<event name="OnTreeEndLabelEdit"></event>
|
||||
<event name="OnTreeGetInfo"></event>
|
||||
<event name="OnTreeItemActivated"></event>
|
||||
<event name="OnTreeItemCollapsed"></event>
|
||||
<event name="OnTreeItemCollapsing"></event>
|
||||
<event name="OnTreeItemExpanded"></event>
|
||||
<event name="OnTreeItemExpanding"></event>
|
||||
<event name="OnTreeItemGetTooltip"></event>
|
||||
<event name="OnTreeItemMenu"></event>
|
||||
<event name="OnTreeItemMiddleClick"></event>
|
||||
<event name="OnTreeItemRightClick"></event>
|
||||
<event name="OnTreeKeyDown"></event>
|
||||
<event name="OnTreeSelChanged">OnSelectionChanged</event>
|
||||
<event name="OnTreeSelChanging"></event>
|
||||
<event name="OnTreeSetInfo"></event>
|
||||
<event name="OnTreeStateImageClick"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxPanel" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_panel4</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer5</property>
|
||||
<property name="orient">wxHORIZONTAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="0">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxButton" expanded="0">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default">0</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">Add Remote</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_addRemoteButton</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnButtonClick"></event>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp">OnAddRemote</event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="0">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxButton" expanded="0">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default">0</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">Use Selected</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_useSelectedButton</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnButtonClick"></event>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp">OnUseSelected</event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND | wxALL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxPanel" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_panel61</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer7</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxStaticText" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">SoapySDR Device Options</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_staticText1</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<property name="wrap">-1</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxPropertyGrid" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="bitmap"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="extra_style"></property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="include_advanced">1</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_propertyGrid</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style">wxPG_DEFAULT_STYLE</property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnPropertyGridChanged"></event>
|
||||
<event name="OnPropertyGridChanging"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="wxTimer" expanded="1">
|
||||
<property name="enabled">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="name">m_deviceTimer</property>
|
||||
<property name="oneshot">0</property>
|
||||
<property name="period">5000</property>
|
||||
<property name="permission">protected</property>
|
||||
<event name="OnTimer">OnDeviceTimer</event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</wxFormBuilder_Project>
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "SDRDevices.h"
|
||||
#include "SDRDevicesForm.h"
|
||||
#include "SoapySDRThread.h"
|
||||
#include "SDREnumerator.h"
|
||||
|
||||
class SDRDevicesDialog: public devFrame {
|
||||
public:
|
||||
SDRDevicesDialog( wxWindow* parent );
|
||||
|
||||
void OnClose( wxCloseEvent& event );
|
||||
void OnDeleteItem( wxTreeEvent& event );
|
||||
void OnSelectionChanged( wxTreeEvent& event );
|
||||
void OnAddRemote( wxMouseEvent& event );
|
||||
void OnUseSelected( wxMouseEvent& event );
|
||||
void OnTreeDoubleClick( wxMouseEvent& event );
|
||||
void OnDeviceTimer( wxTimerEvent& event );
|
||||
|
||||
private:
|
||||
SDRDeviceInfo *getSelectedDevice(wxTreeItemId selId);
|
||||
wxPGProperty *addArgInfoProperty(wxPropertyGrid *pg, SoapySDR::ArgInfo arg);
|
||||
|
||||
bool refresh;
|
||||
std::map<std::string, std::vector<SDRDeviceInfo *>* > devs;
|
||||
std::vector<SDRDeviceInfo *>::iterator devs_i;
|
||||
std::map<wxTreeItemId, SDRDeviceInfo *> devItems;
|
||||
std::map<wxTreeItemId, SDRDeviceInfo *>::iterator devItems_i;
|
||||
SDRDeviceInfo *dev = NULL;
|
||||
std::vector<wxPGProperty *> props;
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version Aug 23 2015)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO "NOT" EDIT THIS FILE!
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "SDRDevicesForm.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
devFrame::devFrame( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
|
||||
{
|
||||
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
|
||||
|
||||
devStatusBar = this->CreateStatusBar( 1, wxST_SIZEGRIP, wxID_ANY );
|
||||
wxBoxSizer* devFrameSizer;
|
||||
devFrameSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_panel3 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
wxBoxSizer* bSizer4;
|
||||
bSizer4 = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
m_panel6 = new wxPanel( m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
wxBoxSizer* bSizer6;
|
||||
bSizer6 = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
devTree = new wxTreeCtrl( m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE );
|
||||
devTree->Enable( false );
|
||||
|
||||
bSizer6->Add( devTree, 1, wxEXPAND|wxALIGN_RIGHT, 5 );
|
||||
|
||||
m_panel4 = new wxPanel( m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
wxBoxSizer* bSizer5;
|
||||
bSizer5 = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
m_addRemoteButton = new wxButton( m_panel4, wxID_ANY, wxT("Add Remote"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bSizer5->Add( m_addRemoteButton, 1, wxALL, 5 );
|
||||
|
||||
m_useSelectedButton = new wxButton( m_panel4, wxID_ANY, wxT("Use Selected"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bSizer5->Add( m_useSelectedButton, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
|
||||
|
||||
|
||||
m_panel4->SetSizer( bSizer5 );
|
||||
m_panel4->Layout();
|
||||
bSizer5->Fit( m_panel4 );
|
||||
bSizer6->Add( m_panel4, 0, wxEXPAND, 5 );
|
||||
|
||||
|
||||
m_panel6->SetSizer( bSizer6 );
|
||||
m_panel6->Layout();
|
||||
bSizer6->Fit( m_panel6 );
|
||||
bSizer4->Add( m_panel6, 1, wxEXPAND | wxALL, 5 );
|
||||
|
||||
m_panel61 = new wxPanel( m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
wxBoxSizer* bSizer7;
|
||||
bSizer7 = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_staticText1 = new wxStaticText( m_panel61, wxID_ANY, wxT("SoapySDR Device Options"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_staticText1->Wrap( -1 );
|
||||
bSizer7->Add( m_staticText1, 0, wxALL, 5 );
|
||||
|
||||
m_propertyGrid = new wxPropertyGrid(m_panel61, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_DEFAULT_STYLE);
|
||||
bSizer7->Add( m_propertyGrid, 1, wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
m_panel61->SetSizer( bSizer7 );
|
||||
m_panel61->Layout();
|
||||
bSizer7->Fit( m_panel61 );
|
||||
bSizer4->Add( m_panel61, 1, wxEXPAND | wxALL, 5 );
|
||||
|
||||
|
||||
m_panel3->SetSizer( bSizer4 );
|
||||
m_panel3->Layout();
|
||||
bSizer4->Fit( m_panel3 );
|
||||
devFrameSizer->Add( m_panel3, 1, wxEXPAND | wxALL, 5 );
|
||||
|
||||
|
||||
this->SetSizer( devFrameSizer );
|
||||
this->Layout();
|
||||
m_deviceTimer.SetOwner( this, wxID_ANY );
|
||||
|
||||
this->Centre( wxBOTH );
|
||||
|
||||
// Connect Events
|
||||
this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( devFrame::OnClose ) );
|
||||
devTree->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( devFrame::OnTreeDoubleClick ), NULL, this );
|
||||
devTree->Connect( wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler( devFrame::OnDeleteItem ), NULL, this );
|
||||
devTree->Connect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( devFrame::OnSelectionChanged ), NULL, this );
|
||||
m_addRemoteButton->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnAddRemote ), NULL, this );
|
||||
m_useSelectedButton->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnUseSelected ), NULL, this );
|
||||
this->Connect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( devFrame::OnDeviceTimer ) );
|
||||
}
|
||||
|
||||
devFrame::~devFrame()
|
||||
{
|
||||
// Disconnect Events
|
||||
this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( devFrame::OnClose ) );
|
||||
devTree->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( devFrame::OnTreeDoubleClick ), NULL, this );
|
||||
devTree->Disconnect( wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler( devFrame::OnDeleteItem ), NULL, this );
|
||||
devTree->Disconnect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( devFrame::OnSelectionChanged ), NULL, this );
|
||||
m_addRemoteButton->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnAddRemote ), NULL, this );
|
||||
m_useSelectedButton->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnUseSelected ), NULL, this );
|
||||
this->Disconnect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( devFrame::OnDeviceTimer ) );
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version Aug 23 2015)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO "NOT" EDIT THIS FILE!
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __SDRDEVICESFORM_H__
|
||||
#define __SDRDEVICESFORM_H__
|
||||
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/statusbr.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/treectrl.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/propgrid/propgrid.h>
|
||||
#include <wx/propgrid/advprops.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/frame.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// Class devFrame
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class devFrame : public wxFrame
|
||||
{
|
||||
private:
|
||||
|
||||
protected:
|
||||
wxStatusBar* devStatusBar;
|
||||
wxPanel* m_panel3;
|
||||
wxPanel* m_panel6;
|
||||
wxTreeCtrl* devTree;
|
||||
wxPanel* m_panel4;
|
||||
wxButton* m_addRemoteButton;
|
||||
wxButton* m_useSelectedButton;
|
||||
wxPanel* m_panel61;
|
||||
wxStaticText* m_staticText1;
|
||||
wxPropertyGrid* m_propertyGrid;
|
||||
wxTimer m_deviceTimer;
|
||||
|
||||
// Virtual event handlers, overide them in your derived class
|
||||
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
|
||||
virtual void OnTreeDoubleClick( wxMouseEvent& event ) { event.Skip(); }
|
||||
virtual void OnDeleteItem( wxTreeEvent& event ) { event.Skip(); }
|
||||
virtual void OnSelectionChanged( wxTreeEvent& event ) { event.Skip(); }
|
||||
virtual void OnAddRemote( wxMouseEvent& event ) { event.Skip(); }
|
||||
virtual void OnUseSelected( wxMouseEvent& event ) { event.Skip(); }
|
||||
virtual void OnDeviceTimer( wxTimerEvent& event ) { event.Skip(); }
|
||||
|
||||
|
||||
public:
|
||||
|
||||
devFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("CubicSDR :: SDR Devices"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 700,467 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
|
||||
|
||||
~devFrame();
|
||||
|
||||
};
|
||||
|
||||
#endif //__SDRDEVICESFORM_H__
|
||||
@@ -10,6 +10,7 @@ WaterfallPanel::WaterfallPanel() : GLPanel(), fft_size(0), waterfall_lines(0), w
|
||||
void WaterfallPanel::setup(int fft_size_in, int num_waterfall_lines_in) {
|
||||
waterfall_lines = num_waterfall_lines_in;
|
||||
fft_size = fft_size_in;
|
||||
lines_buffered.store(0);
|
||||
|
||||
if (points.size() != fft_size) {
|
||||
points.resize(fft_size);
|
||||
@@ -23,6 +24,8 @@ void WaterfallPanel::setup(int fft_size_in, int num_waterfall_lines_in) {
|
||||
|
||||
waterfall_ofs[i] = waterfall_lines - 1;
|
||||
}
|
||||
texInitialized.store(false);
|
||||
bufferInitialized.store(false);
|
||||
}
|
||||
|
||||
void WaterfallPanel::refreshTheme() {
|
||||
@@ -52,7 +55,47 @@ void WaterfallPanel::setPoints(std::vector<float> &points) {
|
||||
void WaterfallPanel::step() {
|
||||
int half_fft_size = fft_size / 2;
|
||||
|
||||
if (!waterfall[0]) {
|
||||
if (!bufferInitialized.load()) {
|
||||
if (waterfall_slice != NULL) {
|
||||
delete waterfall_slice;
|
||||
}
|
||||
waterfall_slice = new unsigned char[half_fft_size];
|
||||
bufferInitialized.store(true);
|
||||
}
|
||||
|
||||
if (!texInitialized.load()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (points.size()) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
for (int i = 0, iMax = half_fft_size; i < iMax; i++) {
|
||||
float v = points[j * half_fft_size + i];
|
||||
|
||||
float wv = v < 0 ? 0 : (v > 0.99 ? 0.99 : v);
|
||||
|
||||
waterfall_slice[i] = (unsigned char) floor(wv * 255.0);
|
||||
}
|
||||
|
||||
int newBufSize = (half_fft_size*lines_buffered.load()+half_fft_size);
|
||||
if (lineBuffer[j].size() < newBufSize) {
|
||||
lineBuffer[j].resize(newBufSize);
|
||||
rLineBuffer[j].resize(newBufSize);
|
||||
}
|
||||
memcpy(&(lineBuffer[j][half_fft_size*lines_buffered.load()]), waterfall_slice, sizeof(unsigned char) * half_fft_size);
|
||||
}
|
||||
lines_buffered++;
|
||||
}
|
||||
}
|
||||
|
||||
void WaterfallPanel::update() {
|
||||
int half_fft_size = fft_size / 2;
|
||||
|
||||
if (!bufferInitialized.load()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!texInitialized.load()) {
|
||||
glGenTextures(2, waterfall);
|
||||
|
||||
unsigned char *waterfall_tex;
|
||||
@@ -73,38 +116,42 @@ void WaterfallPanel::step() {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, half_fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex);
|
||||
}
|
||||
|
||||
if (waterfall_slice != NULL) {
|
||||
delete waterfall_slice;
|
||||
}
|
||||
waterfall_slice = new unsigned char[half_fft_size];
|
||||
|
||||
delete[] waterfall_tex;
|
||||
|
||||
texInitialized.store(true);
|
||||
}
|
||||
|
||||
if (points.size()) {
|
||||
for (int i = 0, iMax = lines_buffered.load(); i < iMax; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
memcpy(&(rLineBuffer[j][i*half_fft_size]),
|
||||
&(lineBuffer[j][((iMax-1)*half_fft_size)-(i*half_fft_size)]), sizeof(unsigned char) * half_fft_size);
|
||||
}
|
||||
}
|
||||
|
||||
int run_ofs = 0;
|
||||
while (lines_buffered.load()) {
|
||||
int run_lines = lines_buffered.load();
|
||||
if (run_lines > waterfall_ofs[0]) {
|
||||
run_lines = waterfall_ofs[0];
|
||||
}
|
||||
for (int j = 0; j < 2; j++) {
|
||||
for (int i = 0, iMax = half_fft_size; i < iMax; i++) {
|
||||
float v = points[j * half_fft_size + i];
|
||||
|
||||
float wv = v < 0 ? 0 : (v > 0.99 ? 0.99 : v);
|
||||
|
||||
waterfall_slice[i] = (unsigned char) floor(wv * 255.0);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, waterfall[j]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, waterfall_ofs[j], half_fft_size, 1, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_slice);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, waterfall_ofs[j]-run_lines, half_fft_size, run_lines,
|
||||
GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) &(rLineBuffer[j][run_ofs]));
|
||||
|
||||
waterfall_ofs[j]-=run_lines;
|
||||
|
||||
if (waterfall_ofs[j] == 0) {
|
||||
waterfall_ofs[j] = waterfall_lines;
|
||||
}
|
||||
|
||||
waterfall_ofs[j]--;
|
||||
}
|
||||
run_ofs += run_lines*half_fft_size;
|
||||
lines_buffered.store(lines_buffered.load()-run_lines);
|
||||
}
|
||||
}
|
||||
|
||||
void WaterfallPanel::drawPanelContents() {
|
||||
if (!waterfall[0]) {
|
||||
if (!texInitialized.load()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -130,7 +177,7 @@ void WaterfallPanel::drawPanelContents() {
|
||||
float half_pixel = 1.0 / viewWidth;
|
||||
float half_texel = 1.0 / (float) half_fft_size;
|
||||
float vtexel = 1.0 / (float) waterfall_lines;
|
||||
float vofs = (float) (waterfall_ofs[0] + 1) * vtexel;
|
||||
float vofs = (float) (waterfall_ofs[0]) * vtexel;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, waterfall[0]);
|
||||
glBegin (GL_QUADS);
|
||||
@@ -144,7 +191,7 @@ void WaterfallPanel::drawPanelContents() {
|
||||
glVertex3f(-1.0, 1.0, 0.0);
|
||||
glEnd();
|
||||
|
||||
vofs = (float) (waterfall_ofs[1] + 1) * vtexel;
|
||||
vofs = (float) (waterfall_ofs[1]) * vtexel;
|
||||
glBindTexture(GL_TEXTURE_2D, waterfall[1]);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0.0 + half_texel, 1.0 + vofs);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "GLPanel.h"
|
||||
#include <atomic>
|
||||
|
||||
class WaterfallPanel : public GLPanel {
|
||||
public:
|
||||
@@ -9,7 +10,8 @@ public:
|
||||
void refreshTheme();
|
||||
void setPoints(std::vector<float> &points);
|
||||
void step();
|
||||
|
||||
void update();
|
||||
|
||||
protected:
|
||||
void drawPanelContents();
|
||||
|
||||
@@ -21,6 +23,10 @@ private:
|
||||
int fft_size;
|
||||
int waterfall_lines;
|
||||
unsigned char *waterfall_slice;
|
||||
std::vector<unsigned char> lineBuffer[2];
|
||||
std::vector<unsigned char> rLineBuffer[2];
|
||||
std::atomic_int lines_buffered;
|
||||
std::atomic_bool texInitialized, bufferInitialized;
|
||||
|
||||
ColorTheme *activeTheme;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "FFTDataDistributor.h"
|
||||
|
||||
FFTDataDistributor::FFTDataDistributor() : linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0), fftSize(DEFAULT_FFT_SIZE) {
|
||||
bufferedItems = 0;
|
||||
}
|
||||
|
||||
void FFTDataDistributor::setFFTSize(int fftSize) {
|
||||
@@ -25,34 +26,44 @@ void FFTDataDistributor::process() {
|
||||
|
||||
if (inp) {
|
||||
if (inputBuffer.sampleRate != inp->sampleRate || inputBuffer.frequency != inp->frequency) {
|
||||
|
||||
bufferMax = inp->sampleRate / 4;
|
||||
// std::cout << "Buffer Max: " << bufferMax << std::endl;
|
||||
bufferOffset = 0;
|
||||
|
||||
inputBuffer.sampleRate = inp->sampleRate;
|
||||
inputBuffer.frequency = inp->frequency;
|
||||
inputBuffer.data.assign(inp->data.begin(), inp->data.end());
|
||||
} else {
|
||||
inputBuffer.data.insert(inputBuffer.data.end(), inp->data.begin(), inp->data.end());
|
||||
inputBuffer.data.resize(bufferMax);
|
||||
}
|
||||
if ((bufferOffset + bufferedItems + inp->data.size()) > bufferMax) {
|
||||
memmove(&inputBuffer.data[0], &inputBuffer.data[bufferOffset], bufferedItems*sizeof(liquid_float_complex));
|
||||
bufferOffset = 0;
|
||||
} else {
|
||||
memcpy(&inputBuffer.data[bufferOffset+bufferedItems],&inp->data[0],inp->data.size()*sizeof(liquid_float_complex));
|
||||
bufferedItems += inp->data.size();
|
||||
}
|
||||
inp->decRefCount();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// number of seconds contained in input
|
||||
double inputTime = (double)inputBuffer.data.size() / (double)inputBuffer.sampleRate;
|
||||
double inputTime = (double)bufferedItems / (double)inputBuffer.sampleRate;
|
||||
// number of lines in input
|
||||
double inputLines = (double)inputBuffer.data.size()/(double)fftSize;
|
||||
double inputLines = (double)bufferedItems / (double)fftSize;
|
||||
|
||||
// ratio required to achieve the desired rate
|
||||
double lineRateStep = ((double)linesPerSecond * inputTime)/(double)inputLines;
|
||||
|
||||
if (inputBuffer.data.size() >= fftSize) {
|
||||
if (bufferedItems >= fftSize) {
|
||||
int numProcessed = 0;
|
||||
|
||||
if (lineRateAccum + (lineRateStep * ((double)inputBuffer.data.size()/(double)fftSize)) < 1.0) {
|
||||
if (lineRateAccum + (lineRateStep * ((double)bufferedItems/(double)fftSize)) < 1.0) {
|
||||
// move along, nothing to see here..
|
||||
lineRateAccum += (lineRateStep * ((double)inputBuffer.data.size()/(double)fftSize));
|
||||
numProcessed = inputBuffer.data.size();
|
||||
lineRateAccum += (lineRateStep * ((double)bufferedItems/(double)fftSize));
|
||||
numProcessed = bufferedItems;
|
||||
} else {
|
||||
for (int i = 0, iMax = inputBuffer.data.size(); i < iMax; i += fftSize) {
|
||||
for (int i = 0, iMax = bufferedItems; i < iMax; i += fftSize) {
|
||||
if ((i + fftSize) > iMax) {
|
||||
break;
|
||||
}
|
||||
@@ -62,7 +73,7 @@ void FFTDataDistributor::process() {
|
||||
DemodulatorThreadIQData *outp = outputBuffers.getBuffer();
|
||||
outp->frequency = inputBuffer.frequency;
|
||||
outp->sampleRate = inputBuffer.sampleRate;
|
||||
outp->data.assign(inputBuffer.data.begin()+i,inputBuffer.data.begin()+i+fftSize);
|
||||
outp->data.assign(inputBuffer.data.begin()+bufferOffset+i,inputBuffer.data.begin()+bufferOffset+i+fftSize);
|
||||
distribute(outp);
|
||||
|
||||
while (lineRateAccum >= 1.0) {
|
||||
@@ -74,8 +85,13 @@ void FFTDataDistributor::process() {
|
||||
}
|
||||
}
|
||||
if (numProcessed) {
|
||||
inputBuffer.data.erase(inputBuffer.data.begin(), inputBuffer.data.begin() + numProcessed);
|
||||
}
|
||||
bufferedItems -= numProcessed;
|
||||
bufferOffset += numProcessed;
|
||||
}
|
||||
if (bufferedItems <= 0) {
|
||||
bufferedItems = 0;
|
||||
bufferOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "VisualProcessor.h"
|
||||
#include "DemodDefs.h"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
class FFTDataDistributor : public VisualProcessor<DemodulatorThreadIQData, DemodulatorThreadIQData> {
|
||||
public:
|
||||
@@ -19,4 +20,5 @@ protected:
|
||||
int fftSize;
|
||||
int linesPerSecond;
|
||||
double lineRateAccum;
|
||||
int bufferMax, bufferOffset, bufferedItems;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,8 @@ ScopeVisualProcessor::ScopeVisualProcessor(): fftInData(NULL), fftwOutput(NULL),
|
||||
scopeEnabled.store(true);
|
||||
spectrumEnabled.store(true);
|
||||
fft_average_rate = 0.65;
|
||||
fft_ceil_ma = fft_ceil_maa = 0;
|
||||
fft_floor_ma = fft_floor_maa = 0;
|
||||
}
|
||||
|
||||
ScopeVisualProcessor::~ScopeVisualProcessor() {
|
||||
@@ -66,9 +68,7 @@ void ScopeVisualProcessor::process() {
|
||||
audioInputData->decRefCount();
|
||||
return;
|
||||
}
|
||||
|
||||
audioInputData->busy_update.lock();
|
||||
|
||||
|
||||
ScopeRenderData *renderData = NULL;
|
||||
|
||||
if (scopeEnabled) {
|
||||
@@ -81,7 +81,7 @@ void ScopeVisualProcessor::process() {
|
||||
renderData->channels = audioInputData->channels;
|
||||
renderData->inputRate = audioInputData->inputRate;
|
||||
renderData->sampleRate = audioInputData->sampleRate;
|
||||
|
||||
|
||||
if (renderData->waveform_points.size() != iMax * 2) {
|
||||
renderData->waveform_points.resize(iMax * 2);
|
||||
}
|
||||
@@ -112,12 +112,10 @@ void ScopeVisualProcessor::process() {
|
||||
}
|
||||
|
||||
renderData->spectrum = false;
|
||||
|
||||
distribute(renderData);
|
||||
}
|
||||
|
||||
if (spectrumEnabled) {
|
||||
renderData = outputBuffers.getBuffer();
|
||||
iMax = audioInputData->data.size();
|
||||
|
||||
if (audioInputData->channels==1) {
|
||||
@@ -138,7 +136,14 @@ void ScopeVisualProcessor::process() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderData = outputBuffers.getBuffer();
|
||||
|
||||
renderData->channels = audioInputData->channels;
|
||||
renderData->inputRate = audioInputData->inputRate;
|
||||
renderData->sampleRate = audioInputData->sampleRate;
|
||||
|
||||
audioInputData->decRefCount();
|
||||
|
||||
fftwf_execute(fftw_plan);
|
||||
|
||||
@@ -157,9 +162,9 @@ void ScopeVisualProcessor::process() {
|
||||
}
|
||||
|
||||
for (i = 0; i < (fftSize/2); i++) {
|
||||
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate;
|
||||
fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate;
|
||||
|
||||
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate;
|
||||
|
||||
if (fft_result_maa[i] > fft_ceil) {
|
||||
fft_ceil = fft_result_maa[i];
|
||||
}
|
||||
@@ -167,7 +172,7 @@ void ScopeVisualProcessor::process() {
|
||||
fft_floor = fft_result_maa[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05;
|
||||
fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05;
|
||||
|
||||
@@ -176,8 +181,8 @@ void ScopeVisualProcessor::process() {
|
||||
|
||||
int outSize = fftSize/2;
|
||||
|
||||
if (audioInputData->sampleRate != audioInputData->inputRate) {
|
||||
outSize = (int)floor((float)outSize * ((float)audioInputData->sampleRate/(float)audioInputData->inputRate));
|
||||
if (renderData->sampleRate != renderData->inputRate) {
|
||||
outSize = (int)floor((float)outSize * ((float)renderData->sampleRate/(float)renderData->inputRate));
|
||||
}
|
||||
|
||||
if (renderData->waveform_points.size() != outSize*2) {
|
||||
@@ -193,12 +198,10 @@ void ScopeVisualProcessor::process() {
|
||||
renderData->fft_floor = fft_floor_maa;
|
||||
renderData->fft_ceil = fft_ceil_maa;
|
||||
renderData->fft_size = fftSize/2;
|
||||
renderData->inputRate = audioInputData->inputRate;
|
||||
renderData->sampleRate = audioInputData->sampleRate;
|
||||
renderData->spectrum = true;
|
||||
distribute(renderData);
|
||||
} else {
|
||||
audioInputData->decRefCount();
|
||||
}
|
||||
|
||||
audioInputData->busy_update.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last
|
||||
fftSize.store(0);
|
||||
centerFreq.store(0);
|
||||
bandwidth.store(0);
|
||||
hideDC.store(false);
|
||||
|
||||
freqShifter = nco_crcf_create(LIQUID_NCO);
|
||||
shiftFrequency = 0;
|
||||
@@ -16,6 +17,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last
|
||||
fft_floor_ma = fft_floor_maa = 0.0;
|
||||
desiredInputSize.store(0);
|
||||
fft_average_rate = 0.65;
|
||||
scaleFactor.store(1.0);
|
||||
}
|
||||
|
||||
SpectrumVisualProcessor::~SpectrumVisualProcessor() {
|
||||
@@ -95,6 +97,11 @@ void SpectrumVisualProcessor::setup(int fftSize_in) {
|
||||
busy_run.unlock();
|
||||
}
|
||||
|
||||
void SpectrumVisualProcessor::setHideDC(bool hideDC) {
|
||||
this->hideDC.store(hideDC);
|
||||
}
|
||||
|
||||
|
||||
void SpectrumVisualProcessor::process() {
|
||||
if (!isOutputEmpty()) {
|
||||
return;
|
||||
@@ -301,13 +308,51 @@ void SpectrumVisualProcessor::process() {
|
||||
fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05;
|
||||
fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05;
|
||||
|
||||
float sf = scaleFactor.load();
|
||||
|
||||
for (int i = 0, iMax = fftSize; i < iMax; i++) {
|
||||
float v = (log10(fft_result_maa[i]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75)));
|
||||
output->spectrum_points[i * 2] = ((float) i / (float) iMax);
|
||||
output->spectrum_points[i * 2 + 1] = v;
|
||||
output->spectrum_points[i * 2 + 1] = v*sf;
|
||||
}
|
||||
|
||||
if (hideDC.load()) { // DC-spike removal
|
||||
long long freqMin = centerFreq-(bandwidth/2);
|
||||
long long freqMax = centerFreq+(bandwidth/2);
|
||||
long long zeroPt = (iqData->frequency-freqMin);
|
||||
|
||||
if (freqMin < iqData->frequency && freqMax > iqData->frequency) {
|
||||
int freqRange = int(freqMax-freqMin);
|
||||
int freqStep = freqRange/fftSize;
|
||||
int fftStart = (zeroPt/freqStep)-(2000/freqStep);
|
||||
int fftEnd = (zeroPt/freqStep)+(2000/freqStep);
|
||||
|
||||
// std::cout << "range:" << freqRange << ", step: " << freqStep << ", start: " << fftStart << ", end: " << fftEnd << std::endl;
|
||||
|
||||
if (fftEnd-fftStart < 2) {
|
||||
fftEnd++;
|
||||
fftStart--;
|
||||
}
|
||||
|
||||
int numSteps = (fftEnd-fftStart);
|
||||
int halfWay = fftStart+(numSteps/2);
|
||||
|
||||
if ((fftEnd+numSteps/2+1 < fftSize) && (fftStart-numSteps/2-1 >= 0) && (fftEnd > fftStart)) {
|
||||
int n = 1;
|
||||
for (int i = fftStart; i < halfWay; i++) {
|
||||
output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftStart - n) * 2 + 1];
|
||||
n++;
|
||||
}
|
||||
n = 1;
|
||||
for (int i = halfWay; i < fftEnd; i++) {
|
||||
output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftEnd + n) * 2 + 1];
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output->fft_ceiling = fft_ceil_maa;
|
||||
output->fft_ceiling = fft_ceil_maa/sf;
|
||||
output->fft_floor = fft_floor_maa;
|
||||
}
|
||||
|
||||
@@ -319,3 +364,13 @@ void SpectrumVisualProcessor::process() {
|
||||
busy_run.unlock();
|
||||
}
|
||||
|
||||
|
||||
void SpectrumVisualProcessor::setScaleFactor(float sf) {
|
||||
scaleFactor.store(sf);
|
||||
}
|
||||
|
||||
|
||||
float SpectrumVisualProcessor::getScaleFactor() {
|
||||
return scaleFactor.load();
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ public:
|
||||
int getDesiredInputSize();
|
||||
|
||||
void setup(int fftSize);
|
||||
void setHideDC(bool hideDC);
|
||||
|
||||
void setScaleFactor(float sf);
|
||||
float getScaleFactor();
|
||||
|
||||
protected:
|
||||
void process();
|
||||
@@ -68,4 +72,6 @@ private:
|
||||
std::vector<liquid_float_complex> resampleBuffer;
|
||||
std::atomic_int desiredInputSize;
|
||||
std::mutex busy_run;
|
||||
std::atomic_bool hideDC;
|
||||
std::atomic<float> scaleFactor;
|
||||
};
|
||||
|
||||
+254
-2
@@ -1,4 +1,163 @@
|
||||
#include "SDRDeviceInfo.h"
|
||||
#include <cstdlib>
|
||||
|
||||
SDRDeviceRange::SDRDeviceRange() {
|
||||
low = 0;
|
||||
high = 0;
|
||||
}
|
||||
|
||||
SDRDeviceRange::SDRDeviceRange(double low, double high) {
|
||||
this->low = low;
|
||||
this->high = high;
|
||||
}
|
||||
|
||||
SDRDeviceRange::SDRDeviceRange(std::string name, double low, double high) : SDRDeviceRange(low, high) {
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
double SDRDeviceRange::getLow() {
|
||||
return low;
|
||||
}
|
||||
void SDRDeviceRange::setLow(double low) {
|
||||
this->low = low;
|
||||
}
|
||||
double SDRDeviceRange::getHigh() {
|
||||
return high;
|
||||
}
|
||||
void SDRDeviceRange::setHigh(double high) {
|
||||
this->high = high;
|
||||
}
|
||||
|
||||
std::string SDRDeviceRange::getName() {
|
||||
return this->name;
|
||||
}
|
||||
|
||||
void SDRDeviceRange::setName(std::string name) {
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
SDRDeviceChannel::SDRDeviceChannel() {
|
||||
hardwareDC = false;
|
||||
hasCorr = false;
|
||||
}
|
||||
|
||||
SDRDeviceChannel::~SDRDeviceChannel() {
|
||||
|
||||
}
|
||||
|
||||
int SDRDeviceChannel::getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::setChannel(int channel) {
|
||||
this->channel = channel;
|
||||
}
|
||||
|
||||
bool SDRDeviceChannel::isFullDuplex() {
|
||||
return fullDuplex;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::setFullDuplex(bool fullDuplex) {
|
||||
this->fullDuplex = fullDuplex;
|
||||
}
|
||||
|
||||
bool SDRDeviceChannel::isTx() {
|
||||
return tx;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::setTx(bool tx) {
|
||||
this->tx = tx;
|
||||
}
|
||||
|
||||
bool SDRDeviceChannel::isRx() {
|
||||
return rx;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::setRx(bool rx) {
|
||||
this->rx = rx;
|
||||
}
|
||||
|
||||
SDRDeviceRange &SDRDeviceChannel::getGain() {
|
||||
return rangeGain;
|
||||
}
|
||||
|
||||
SDRDeviceRange &SDRDeviceChannel::getLNAGain() {
|
||||
return rangeLNA;
|
||||
}
|
||||
|
||||
SDRDeviceRange &SDRDeviceChannel::getFreqRange() {
|
||||
return rangeFull;
|
||||
}
|
||||
|
||||
SDRDeviceRange &SDRDeviceChannel::getRFRange() {
|
||||
return rangeRF;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::addGain(SDRDeviceRange range) {
|
||||
gainInfo.push_back(range);
|
||||
}
|
||||
|
||||
std::vector<SDRDeviceRange> &SDRDeviceChannel::getGains() {
|
||||
return gainInfo;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::addGain(std::string name, SoapySDR::Range range) {
|
||||
gainInfo.push_back(SDRDeviceRange(name,range.minimum(),range.maximum()));
|
||||
}
|
||||
|
||||
std::vector<long> &SDRDeviceChannel::getSampleRates() {
|
||||
return sampleRates;
|
||||
}
|
||||
|
||||
long SDRDeviceChannel::getSampleRateNear(long sampleRate_in) {
|
||||
long returnRate = sampleRates[0];
|
||||
long sDelta = (long)sampleRate_in-sampleRates[0];
|
||||
long minDelta = std::abs(sDelta);
|
||||
for (std::vector<long>::iterator i = sampleRates.begin(); i != sampleRates.end(); i++) {
|
||||
long thisDelta = std::abs(sampleRate_in - (*i));
|
||||
if (thisDelta < minDelta) {
|
||||
minDelta = thisDelta;
|
||||
returnRate = (*i);
|
||||
}
|
||||
}
|
||||
return returnRate;
|
||||
}
|
||||
|
||||
std::vector<long long> &SDRDeviceChannel::getFilterBandwidths() {
|
||||
return filterBandwidths;
|
||||
}
|
||||
|
||||
const bool& SDRDeviceChannel::hasHardwareDC() const {
|
||||
return hardwareDC;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::setHardwareDC(const bool& hardware) {
|
||||
hardwareDC = hardware;
|
||||
}
|
||||
|
||||
const bool& SDRDeviceChannel::hasCORR() const {
|
||||
return hasCorr;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::setCORR(const bool& hasCorr) {
|
||||
this->hasCorr = hasCorr;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::setStreamArgsInfo(SoapySDR::ArgInfoList streamArgs) {
|
||||
streamArgInfo = streamArgs;
|
||||
}
|
||||
|
||||
SoapySDR::ArgInfoList SDRDeviceChannel::getStreamArgsInfo() {
|
||||
return streamArgInfo;
|
||||
}
|
||||
|
||||
std::vector<std::string> SDRDeviceChannel::getStreamArgNames() {
|
||||
std::vector<std::string> names;
|
||||
for (SoapySDR::ArgInfoList::const_iterator i = streamArgInfo.begin(); i != streamArgInfo.end(); i++) {
|
||||
names.push_back((*i).key);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
SDRDeviceInfo::SDRDeviceInfo() : name(""), serial(""), available(false) {
|
||||
@@ -9,12 +168,20 @@ std::string SDRDeviceInfo::getDeviceId() {
|
||||
std::string deviceId;
|
||||
|
||||
deviceId.append(getName());
|
||||
deviceId.append(" :: ");
|
||||
deviceId.append(getSerial());
|
||||
// deviceId.append(" :: ");
|
||||
// deviceId.append(getSerial());
|
||||
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
const int SDRDeviceInfo::getIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
void SDRDeviceInfo::setIndex(const int index) {
|
||||
this->index = index;
|
||||
}
|
||||
|
||||
bool SDRDeviceInfo::isAvailable() const {
|
||||
return available;
|
||||
}
|
||||
@@ -63,3 +230,88 @@ void SDRDeviceInfo::setProduct(const std::string& product) {
|
||||
this->product = product;
|
||||
}
|
||||
|
||||
const std::string& SDRDeviceInfo::getDriver() const {
|
||||
return driver;
|
||||
}
|
||||
|
||||
void SDRDeviceInfo::setDriver(const std::string& driver) {
|
||||
this->driver = driver;
|
||||
}
|
||||
|
||||
const std::string& SDRDeviceInfo::getHardware() const {
|
||||
return hardware;
|
||||
}
|
||||
|
||||
void SDRDeviceInfo::setHardware(const std::string& hardware) {
|
||||
this->hardware = hardware;
|
||||
}
|
||||
|
||||
bool SDRDeviceInfo::hasTimestamps() const {
|
||||
return timestamps;
|
||||
}
|
||||
|
||||
void SDRDeviceInfo::setTimestamps(bool timestamps) {
|
||||
this->timestamps = timestamps;
|
||||
}
|
||||
|
||||
void SDRDeviceInfo::setDeviceArgs(SoapySDR::Kwargs deviceArgs) {
|
||||
this->deviceArgs = deviceArgs;
|
||||
}
|
||||
|
||||
SoapySDR::Kwargs SDRDeviceInfo::getDeviceArgs() {
|
||||
return deviceArgs;
|
||||
}
|
||||
|
||||
void SDRDeviceInfo::setStreamArgs(SoapySDR::Kwargs streamArgs) {
|
||||
this->streamArgs = streamArgs;
|
||||
}
|
||||
|
||||
SoapySDR::Kwargs SDRDeviceInfo::getStreamArgs() {
|
||||
return streamArgs;
|
||||
}
|
||||
|
||||
void SDRDeviceInfo::setSettingsInfo(SoapySDR::ArgInfoList settingsArgs) {
|
||||
settingInfo = settingsArgs;
|
||||
}
|
||||
|
||||
SoapySDR::ArgInfoList SDRDeviceInfo::getSettingsArgInfo() {
|
||||
return settingInfo;
|
||||
}
|
||||
|
||||
std::vector<std::string> SDRDeviceInfo::getSettingNames() {
|
||||
std::vector<std::string> names;
|
||||
for (SoapySDR::ArgInfoList::const_iterator i = settingInfo.begin(); i != settingInfo.end(); i++) {
|
||||
names.push_back((*i).key);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
void SDRDeviceInfo::addChannel(SDRDeviceChannel *chan) {
|
||||
channels.push_back(chan);
|
||||
}
|
||||
|
||||
std::vector<SDRDeviceChannel *> &SDRDeviceInfo::getChannels() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
SDRDeviceChannel * SDRDeviceInfo::getRxChannel() {
|
||||
std::vector<SDRDeviceChannel *>::iterator channel_i;
|
||||
for (channel_i = channels.begin(); channel_i != channels.end(); channel_i++) {
|
||||
if ((*channel_i)->isRx()) {
|
||||
return (*channel_i);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDRDeviceChannel * SDRDeviceInfo::getTxChannel() {
|
||||
std::vector<SDRDeviceChannel *>::iterator channel_i;
|
||||
for (channel_i = channels.begin(); channel_i != channels.end(); channel_i++) {
|
||||
if ((*channel_i)->isTx()) {
|
||||
return (*channel_i);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
+135
-6
@@ -1,6 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <SoapySDR/Types.hpp>
|
||||
|
||||
/*
|
||||
----------------------------------------------------
|
||||
-- Device identification
|
||||
----------------------------------------------------
|
||||
driver=rtl
|
||||
hardware=rtl
|
||||
|
||||
----------------------------------------------------
|
||||
-- Peripheral summary
|
||||
----------------------------------------------------
|
||||
Channels: 1 Rx, 0 Tx
|
||||
Timestamps: NO
|
||||
|
||||
----------------------------------------------------
|
||||
-- RX Channel 0
|
||||
----------------------------------------------------
|
||||
Full-duplex: YES
|
||||
Antennas: RX
|
||||
Full gain range: [0, 49.6] dB
|
||||
LNA gain range: [0, 49.6] dB
|
||||
Full freq range: [24, 1766] MHz
|
||||
RF freq range: [24, 1766] MHz
|
||||
CORR freq range: MHz
|
||||
Sample rates: [0.25, 2.56] MHz
|
||||
Filter bandwidths: [] MHz
|
||||
*/
|
||||
|
||||
class SDRDeviceRange {
|
||||
public:
|
||||
SDRDeviceRange();
|
||||
SDRDeviceRange(double low, double high);
|
||||
SDRDeviceRange(std::string name, double low, double high);
|
||||
|
||||
double getLow();
|
||||
void setLow(double low);
|
||||
double getHigh();
|
||||
void setHigh(double high);
|
||||
std::string getName();
|
||||
void setName(std::string name);
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
double low, high;
|
||||
};
|
||||
|
||||
class SDRDeviceChannel {
|
||||
public:
|
||||
SDRDeviceChannel();
|
||||
~SDRDeviceChannel();
|
||||
|
||||
int getChannel();
|
||||
void setChannel(int channel);
|
||||
|
||||
bool isFullDuplex();
|
||||
void setFullDuplex(bool fullDuplex);
|
||||
|
||||
bool isTx();
|
||||
void setTx(bool tx);
|
||||
|
||||
bool isRx();
|
||||
void setRx(bool rx);
|
||||
|
||||
void addGain(SDRDeviceRange range);
|
||||
void addGain(std::string name, SoapySDR::Range range);
|
||||
std::vector<SDRDeviceRange> &getGains();
|
||||
|
||||
SDRDeviceRange &getGain();
|
||||
SDRDeviceRange &getLNAGain();
|
||||
SDRDeviceRange &getFreqRange();
|
||||
SDRDeviceRange &getRFRange();
|
||||
|
||||
std::vector<long> &getSampleRates();
|
||||
long getSampleRateNear(long sampleRate_in);
|
||||
std::vector<long long> &getFilterBandwidths();
|
||||
|
||||
const bool& hasHardwareDC() const;
|
||||
void setHardwareDC(const bool& hardware);
|
||||
|
||||
const bool& hasCORR() const;
|
||||
void setCORR(const bool& corr);
|
||||
|
||||
void setStreamArgsInfo(SoapySDR::ArgInfoList streamArgs);
|
||||
SoapySDR::ArgInfoList getStreamArgsInfo();
|
||||
std::vector<std::string> getStreamArgNames();
|
||||
|
||||
private:
|
||||
int channel;
|
||||
bool fullDuplex, tx, rx, hardwareDC, hasCorr;
|
||||
SDRDeviceRange rangeGain, rangeLNA, rangeFull, rangeRF;
|
||||
std::vector<long> sampleRates;
|
||||
std::vector<long long> filterBandwidths;
|
||||
SoapySDR::ArgInfoList streamArgInfo;
|
||||
std::vector<SDRDeviceRange> gainInfo;
|
||||
};
|
||||
|
||||
|
||||
class SDRDeviceInfo {
|
||||
public:
|
||||
@@ -8,6 +107,9 @@ public:
|
||||
|
||||
std::string getDeviceId();
|
||||
|
||||
const int getIndex() const;
|
||||
void setIndex(const int index);
|
||||
|
||||
bool isAvailable() const;
|
||||
void setAvailable(bool available);
|
||||
|
||||
@@ -25,12 +127,39 @@ public:
|
||||
|
||||
const std::string& getProduct() const;
|
||||
void setProduct(const std::string& product);
|
||||
|
||||
const std::string& getDriver() const;
|
||||
void setDriver(const std::string& driver);
|
||||
|
||||
const std::string& getHardware() const;
|
||||
void setHardware(const std::string& hardware);
|
||||
|
||||
bool hasTimestamps() const;
|
||||
void setTimestamps(bool timestamps);
|
||||
|
||||
void addChannel(SDRDeviceChannel *chan);
|
||||
std::vector<SDRDeviceChannel *> &getChannels();
|
||||
SDRDeviceChannel * getRxChannel();
|
||||
SDRDeviceChannel * getTxChannel();
|
||||
|
||||
void setDeviceArgs(SoapySDR::Kwargs deviceArgs);
|
||||
SoapySDR::Kwargs getDeviceArgs();
|
||||
|
||||
void setStreamArgs(SoapySDR::Kwargs deviceArgs);
|
||||
SoapySDR::Kwargs getStreamArgs();
|
||||
|
||||
void setSettingsInfo(SoapySDR::ArgInfoList settingsArgs);
|
||||
SoapySDR::ArgInfoList getSettingsArgInfo();
|
||||
|
||||
std::vector<std::string> getSettingNames();
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string serial;
|
||||
std::string product;
|
||||
std::string manufacturer;
|
||||
std::string tuner;
|
||||
bool available;
|
||||
int index;
|
||||
std::string name, serial, product, manufacturer, tuner;
|
||||
std::string driver, hardware;
|
||||
bool timestamps, available;
|
||||
|
||||
SoapySDR::Kwargs deviceArgs, streamArgs;
|
||||
SoapySDR::ArgInfoList settingInfo;
|
||||
std::vector<SDRDeviceChannel *> channels;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
#include "SDREnumerator.h"
|
||||
#include "CubicSDRDefs.h"
|
||||
#include <vector>
|
||||
#include "CubicSDR.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
std::vector<std::string> SDREnumerator::factories;
|
||||
std::vector<std::string> SDREnumerator::modules;
|
||||
std::vector<std::string> SDREnumerator::remotes;
|
||||
std::map< std::string, std::vector<SDRDeviceInfo *> > SDREnumerator::devs;
|
||||
bool SDREnumerator::soapy_initialized = false;
|
||||
bool SDREnumerator::has_remote = false;
|
||||
|
||||
SDREnumerator::SDREnumerator() : IOThread() {
|
||||
|
||||
}
|
||||
|
||||
SDREnumerator::~SDREnumerator() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::vector<SDRDeviceInfo *> *SDREnumerator::enumerate_devices(std::string remoteAddr, bool noInit) {
|
||||
|
||||
if (SDREnumerator::devs[remoteAddr].size()) {
|
||||
return &SDREnumerator::devs[remoteAddr];
|
||||
}
|
||||
|
||||
if (noInit) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!soapy_initialized) {
|
||||
std::cout << "SoapySDR init.." << std::endl;
|
||||
std::cout << "\tAPI Version: v" << SoapySDR::getAPIVersion() << std::endl;
|
||||
std::cout << "\tABI Version: v" << SoapySDR::getABIVersion() << std::endl;
|
||||
std::cout << "\tInstall root: " << SoapySDR::getRootPath() << std::endl;
|
||||
|
||||
std::cout << "\tLoading modules... " << std::endl;
|
||||
#ifdef BUNDLE_SOAPY_MODS
|
||||
bool localModPref = wxGetApp().getUseLocalMod();
|
||||
if (localModPref) {
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules..");
|
||||
std::cout << "Checking local system SoapySDR modules.." << std::flush;
|
||||
SoapySDR::loadModules();
|
||||
}
|
||||
|
||||
wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath());
|
||||
std::vector<std::string> localMods = SoapySDR::listModules(exePath.GetPath().ToStdString() + "/modules/");
|
||||
for (std::vector<std::string>::iterator mods_i = localMods.begin(); mods_i != localMods.end(); mods_i++) {
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing bundled SoapySDR module " + (*mods_i) + "..");
|
||||
std::cout << "Loading bundled SoapySDR module " << (*mods_i) << ".." << std::endl;
|
||||
SoapySDR::loadModule(*mods_i);
|
||||
}
|
||||
|
||||
if (!localModPref) {
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules..");
|
||||
std::cout << "Checking system SoapySDR modules.." << std::flush;
|
||||
SoapySDR::loadModules();
|
||||
}
|
||||
#else
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules..");
|
||||
SoapySDR::loadModules();
|
||||
#endif
|
||||
|
||||
// modules = SoapySDR::listModules();
|
||||
// for (size_t i = 0; i < modules.size(); i++) {
|
||||
// std::cout << "\tModule found: " << modules[i] << std::endl;
|
||||
// }
|
||||
// if (modules.empty()) {
|
||||
// std::cout << "No modules found!" << std::endl;
|
||||
// }
|
||||
|
||||
if (SDREnumerator::factories.size()) {
|
||||
SDREnumerator::factories.erase(SDREnumerator::factories.begin(), SDREnumerator::factories.end());
|
||||
}
|
||||
|
||||
std::cout << "\tAvailable factories...";
|
||||
SoapySDR::FindFunctions factories = SoapySDR::Registry::listFindFunctions();
|
||||
for (SoapySDR::FindFunctions::const_iterator it = factories.begin(); it != factories.end(); ++it) {
|
||||
if (it != factories.begin()) {
|
||||
std::cout << ", ";
|
||||
}
|
||||
std::cout << it->first;
|
||||
|
||||
if (it->first == "remote") {
|
||||
has_remote = true;
|
||||
}
|
||||
SDREnumerator::factories.push_back(it->first);
|
||||
}
|
||||
if (factories.empty()) {
|
||||
std::cout << "No factories found!" << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
soapy_initialized = true;
|
||||
}
|
||||
|
||||
std::vector<SoapySDR::Kwargs> results;
|
||||
SoapySDR::Kwargs enumArgs;
|
||||
bool isRemote = false;
|
||||
|
||||
if (remoteAddr.length()) {
|
||||
std::cout << "Enumerating remote address: " << remoteAddr << std::endl;
|
||||
enumArgs["driver"] = "remote";
|
||||
enumArgs["remote"] = remoteAddr;
|
||||
isRemote = true;
|
||||
|
||||
results = SoapySDR::Device::enumerate(enumArgs);
|
||||
} else {
|
||||
results = SoapySDR::Device::enumerate();
|
||||
}
|
||||
|
||||
if (isRemote) {
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Opening remote server ") + remoteAddr + "..");
|
||||
}
|
||||
for (size_t i = 0; i < results.size(); i++) {
|
||||
// std::cout << "Found device " << i << std::endl;
|
||||
SDRDeviceInfo *dev = new SDRDeviceInfo();
|
||||
|
||||
SoapySDR::Kwargs deviceArgs = results[i];
|
||||
|
||||
for (SoapySDR::Kwargs::const_iterator it = deviceArgs.begin(); it != deviceArgs.end(); ++it) {
|
||||
std::cout << " " << it->first << " = " << it->second << std::endl;
|
||||
if (it->first == "driver") {
|
||||
dev->setDriver(it->second);
|
||||
} else if (it->first == "label" || it->first == "device") {
|
||||
dev->setName(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
dev->setDeviceArgs(deviceArgs);
|
||||
|
||||
std::cout << "Make device " << i << std::endl;
|
||||
try {
|
||||
SoapySDR::Device *device = SoapySDR::Device::make(dev->getDeviceArgs());
|
||||
SoapySDR::Kwargs info = device->getHardwareInfo();
|
||||
for (SoapySDR::Kwargs::const_iterator it = info.begin(); it != info.end(); ++it) {
|
||||
std::cout << " " << it->first << "=" << it->second << std::endl;
|
||||
if (it->first == "hardware") {
|
||||
dev->setHardware(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (isRemote) {
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Querying remote " + remoteAddr + " device #" + std::to_string(i) + ": " + dev-> getName());
|
||||
} else {
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Querying device #") + std::to_string(i) + ": " + dev->getName());
|
||||
}
|
||||
|
||||
int numChan = device->getNumChannels(SOAPY_SDR_RX);
|
||||
for (int i = 0; i < numChan; i++) {
|
||||
SDRDeviceChannel *chan = new SDRDeviceChannel();
|
||||
|
||||
SoapySDR::RangeList rfRange = device->getFrequencyRange(SOAPY_SDR_RX, i);
|
||||
double rfMin = rfRange[0].minimum();
|
||||
double rfMax = rfRange[rfRange.size()-1].maximum();
|
||||
chan->setChannel(i);
|
||||
chan->setFullDuplex(device->getFullDuplex(SOAPY_SDR_RX, i));
|
||||
chan->setRx(true);
|
||||
chan->setTx(false);
|
||||
chan->getRFRange().setLow(rfMin);
|
||||
chan->getRFRange().setHigh(rfMax);
|
||||
|
||||
std::vector<std::string> freqs = device->listFrequencies(SOAPY_SDR_RX,i);
|
||||
if (std::find(freqs.begin(), freqs.end(), "CORR") != freqs.end()) {
|
||||
chan->setCORR(true);
|
||||
} else {
|
||||
chan->setCORR(false);
|
||||
}
|
||||
|
||||
if (device->hasDCOffsetMode(SOAPY_SDR_RX, i)) {
|
||||
chan->setHardwareDC(true);
|
||||
} else {
|
||||
chan->setHardwareDC(false);
|
||||
}
|
||||
|
||||
std::vector<double> rates = device->listSampleRates(SOAPY_SDR_RX, i);
|
||||
for (std::vector<double>::iterator i = rates.begin(); i != rates.end(); i++) {
|
||||
chan->getSampleRates().push_back((long)(*i));
|
||||
}
|
||||
|
||||
chan->setStreamArgsInfo(device->getStreamArgsInfo(SOAPY_SDR_RX, i));
|
||||
|
||||
std::vector<std::string> gainNames = device->listGains(SOAPY_SDR_RX, i);
|
||||
|
||||
for (std::vector<std::string>::iterator gname = gainNames.begin(); gname!= gainNames.end(); gname++) {
|
||||
chan->addGain((*gname),device->getGainRange(SOAPY_SDR_RX, i, (*gname)));
|
||||
}
|
||||
|
||||
dev->addChannel(chan);
|
||||
}
|
||||
|
||||
|
||||
SoapySDR::Kwargs streamArgs;
|
||||
|
||||
if (isRemote) {
|
||||
// if (deviceArgs.count("rtl") != 0) {
|
||||
// streamArgs["remote:mtu"] = "8192";
|
||||
// streamArgs["remote:window"] = "16384000";
|
||||
// }
|
||||
double fullScale = 0;
|
||||
std::string nativeFormat = device->getNativeStreamFormat(SOAPY_SDR_RX, dev->getRxChannel()->getChannel(), fullScale);
|
||||
|
||||
if (nativeFormat.length()) {
|
||||
streamArgs["remote:format"] = nativeFormat;
|
||||
}
|
||||
}
|
||||
|
||||
dev->setStreamArgs(streamArgs);
|
||||
|
||||
|
||||
dev->setSettingsInfo(device->getSettingInfo());
|
||||
|
||||
SoapySDR::Device::unmake(device);
|
||||
|
||||
dev->setAvailable(true);
|
||||
} catch (const std::exception &ex) {
|
||||
std::cerr << "Error making device: " << ex.what() << std::endl;
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Error querying device #") + std::to_string(i));
|
||||
dev->setAvailable(false);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
SDREnumerator::devs[remoteAddr].push_back(dev);
|
||||
}
|
||||
if (SDREnumerator::devs[remoteAddr].empty()) {
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("No devices found!"));
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
return &SDREnumerator::devs[remoteAddr];
|
||||
}
|
||||
|
||||
|
||||
void SDREnumerator::run() {
|
||||
|
||||
std::cout << "SDR enumerator starting." << std::endl;
|
||||
terminated.store(false);
|
||||
|
||||
// if (!remotes.size()) {
|
||||
// remotes.push_back("raspberrypi.local");
|
||||
// }
|
||||
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Scanning local devices, please wait..");
|
||||
SDREnumerator::enumerate_devices("");
|
||||
|
||||
if (remotes.size()) {
|
||||
std::vector<std::string>::iterator remote_i;
|
||||
for (remote_i = remotes.begin(); remote_i != remotes.end(); remote_i++) {
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Scanning devices at " + (*remote_i) + ", please wait..");
|
||||
SDREnumerator::enumerate_devices(*remote_i);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Reporting enumeration complete." << std::endl;
|
||||
terminated.store(true);
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_DEVICES_READY, "Finished scanning devices.");
|
||||
std::cout << "SDR enumerator done." << std::endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SDREnumerator::addRemote(std::string remoteAddr) {
|
||||
std::vector<std::string>::iterator remote_i = std::find(remotes.begin(), remotes.end(), remoteAddr);
|
||||
|
||||
if (remote_i != remotes.end()) {
|
||||
return;
|
||||
} else {
|
||||
remotes.push_back(remoteAddr);
|
||||
}
|
||||
}
|
||||
|
||||
void SDREnumerator::removeRemote(std::string remoteAddr) {
|
||||
std::vector<std::string>::iterator remote_i = std::find(remotes.begin(), remotes.end(), remoteAddr);
|
||||
|
||||
if (remote_i != remotes.end()) {
|
||||
if (devs.find(*remote_i) != devs.end()) {
|
||||
while (devs[*remote_i].size()) {
|
||||
SDRDeviceInfo *devRemove = devs[*remote_i].back();
|
||||
devs[*remote_i].pop_back();
|
||||
delete devRemove;
|
||||
}
|
||||
}
|
||||
remotes.erase(remote_i);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> &SDREnumerator::getRemotes() {
|
||||
return remotes;
|
||||
}
|
||||
|
||||
bool SDREnumerator::hasRemoteModule() {
|
||||
return SDREnumerator::has_remote;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "IOThread.h"
|
||||
#include "SDRDeviceInfo.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
#include <SoapySDR/Version.hpp>
|
||||
#include <SoapySDR/Modules.hpp>
|
||||
#include <SoapySDR/Registry.hpp>
|
||||
#include <SoapySDR/Device.hpp>
|
||||
|
||||
|
||||
class SDREnumerator: public IOThread {
|
||||
private:
|
||||
|
||||
public:
|
||||
SDREnumerator();
|
||||
~SDREnumerator();
|
||||
enum SDREnumState { SDR_ENUM_DEVICES_READY, SDR_ENUM_MESSAGE, SDR_ENUM_TERMINATED, SDR_ENUM_FAILED };
|
||||
|
||||
static std::vector<SDRDeviceInfo *> *enumerate_devices(std::string remoteAddr = "", bool noInit=false);
|
||||
|
||||
void run();
|
||||
|
||||
static void addRemote(std::string remoteAddr);
|
||||
static void removeRemote(std::string remoteAddr);
|
||||
static std::vector<std::string> &getRemotes();
|
||||
static bool hasRemoteModule();
|
||||
|
||||
protected:
|
||||
static bool soapy_initialized, has_remote;
|
||||
static std::vector<std::string> factories;
|
||||
static std::vector<std::string> modules;
|
||||
static std::vector<std::string> remotes;
|
||||
static std::map< std::string, std::vector<SDRDeviceInfo *> > devs;
|
||||
};
|
||||
+269
-130
@@ -5,26 +5,22 @@
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
SDRPostThread::SDRPostThread() : IOThread(),
|
||||
iqDataInQueue(NULL), iqDataOutQueue(NULL), iqVisualQueue(NULL), dcFilter(NULL){
|
||||
|
||||
swapIQ.store(false);
|
||||
SDRPostThread::SDRPostThread() : IOThread() {
|
||||
iqDataInQueue = NULL;
|
||||
iqDataOutQueue = NULL;
|
||||
iqVisualQueue = NULL;
|
||||
|
||||
// create a lookup table
|
||||
for (unsigned int i = 0; i <= 0xffff; i++) {
|
||||
liquid_float_complex tmp,tmp_swap;
|
||||
# if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
tmp_swap.imag = tmp.real = (float(i & 0xff) - 127.4f) * (1.0f/128.0f);
|
||||
tmp_swap.real = tmp.imag = (float(i >> 8) - 127.4f) * (1.0f/128.0f);
|
||||
_lut.push_back(tmp);
|
||||
_lut_swap.push_back(tmp_swap);
|
||||
#else // BIG_ENDIAN
|
||||
tmp_swap.imag = tmp.real = (float(i >> 8) - 127.4f) * (1.0f/128.0f);
|
||||
tmp_swap.real = tmp.imag = (float(i & 0xff) - 127.4f) * (1.0f/128.0f);
|
||||
_lut.push_back(tmp);
|
||||
_lut_swap.push_back(tmp_swap);
|
||||
#endif
|
||||
}
|
||||
numChannels = 0;
|
||||
channelizer = NULL;
|
||||
|
||||
sampleRate = 0;
|
||||
nRunDemods = 0;
|
||||
|
||||
visFrequency.store(0);
|
||||
visBandwidth.store(0);
|
||||
|
||||
doRefresh.store(false);
|
||||
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005);
|
||||
}
|
||||
|
||||
SDRPostThread::~SDRPostThread() {
|
||||
@@ -33,6 +29,7 @@ SDRPostThread::~SDRPostThread() {
|
||||
void SDRPostThread::bindDemodulator(DemodulatorInstance *demod) {
|
||||
busy_demod.lock();
|
||||
demodulators.push_back(demod);
|
||||
doRefresh.store(true);
|
||||
busy_demod.unlock();
|
||||
}
|
||||
|
||||
@@ -46,39 +43,121 @@ void SDRPostThread::removeDemodulator(DemodulatorInstance *demod) {
|
||||
|
||||
if (i != demodulators.end()) {
|
||||
demodulators.erase(i);
|
||||
doRefresh.store(true);
|
||||
}
|
||||
busy_demod.unlock();
|
||||
}
|
||||
|
||||
void SDRPostThread::setSwapIQ(bool swapIQ) {
|
||||
this->swapIQ.store(swapIQ);
|
||||
void SDRPostThread::initPFBChannelizer() {
|
||||
// std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl;
|
||||
if (channelizer) {
|
||||
firpfbch_crcf_destroy(channelizer);
|
||||
}
|
||||
channelizer = firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 4, 60);
|
||||
|
||||
chanBw = (sampleRate / numChannels);
|
||||
|
||||
chanCenters.resize(numChannels+1);
|
||||
demodChannelActive.resize(numChannels+1);
|
||||
|
||||
// std::cout << "Channel bandwidth spacing: " << (chanBw) << std::endl;
|
||||
}
|
||||
|
||||
bool SDRPostThread::getSwapIQ() {
|
||||
return this->swapIQ.load();
|
||||
void SDRPostThread::updateActiveDemodulators() {
|
||||
// In range?
|
||||
std::vector<DemodulatorInstance *>::iterator demod_i;
|
||||
|
||||
nRunDemods = 0;
|
||||
|
||||
for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) {
|
||||
DemodulatorInstance *demod = *demod_i;
|
||||
DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe();
|
||||
|
||||
// not in range?
|
||||
if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) {
|
||||
// deactivate if active
|
||||
if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) {
|
||||
demod->setActive(false);
|
||||
DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData;
|
||||
dummyDataOut->frequency = frequency;
|
||||
dummyDataOut->sampleRate = sampleRate;
|
||||
demodQueue->push(dummyDataOut);
|
||||
}
|
||||
|
||||
// follow if follow mode
|
||||
if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) {
|
||||
wxGetApp().setFrequency(demod->getFrequency());
|
||||
demod->setFollow(false);
|
||||
}
|
||||
} else if (!demod->isActive()) { // in range, activate if not activated
|
||||
demod->setActive(true);
|
||||
if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) {
|
||||
wxGetApp().getDemodMgr().setActiveDemodulator(demod);
|
||||
}
|
||||
}
|
||||
|
||||
if (!demod->isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add to the current run
|
||||
if (nRunDemods == runDemods.size()) {
|
||||
runDemods.push_back(demod);
|
||||
demodChannel.push_back(-1);
|
||||
} else {
|
||||
runDemods[nRunDemods] = demod;
|
||||
demodChannel[nRunDemods] = -1;
|
||||
}
|
||||
nRunDemods++;
|
||||
}
|
||||
}
|
||||
|
||||
void SDRPostThread::updateChannels() {
|
||||
// calculate channel center frequencies, todo: cache
|
||||
for (int i = 0; i < numChannels/2; i++) {
|
||||
int ofs = ((chanBw) * i);
|
||||
chanCenters[i] = frequency + ofs;
|
||||
chanCenters[i+(numChannels/2)] = frequency - (sampleRate/2) + ofs;
|
||||
}
|
||||
chanCenters[numChannels] = frequency + (sampleRate/2);
|
||||
}
|
||||
|
||||
int SDRPostThread::getChannelAt(long long frequency) {
|
||||
int chan = -1;
|
||||
long long minDelta = sampleRate;
|
||||
for (int i = 0; i < numChannels+1; i++) {
|
||||
long long fdelta = abs(frequency - chanCenters[i]);
|
||||
if (fdelta < minDelta) {
|
||||
minDelta = fdelta;
|
||||
chan = i;
|
||||
}
|
||||
}
|
||||
return chan;
|
||||
}
|
||||
|
||||
void SDRPostThread::setIQVisualRange(long long frequency, int bandwidth) {
|
||||
visFrequency.store(frequency);
|
||||
visBandwidth.store(bandwidth);
|
||||
}
|
||||
|
||||
void SDRPostThread::run() {
|
||||
#ifdef __APPLE__
|
||||
pthread_t tID = pthread_self(); // ID of this thread
|
||||
int priority = sched_get_priority_max( SCHED_FIFO) - 1;
|
||||
int priority = sched_get_priority_max( SCHED_FIFO);
|
||||
sched_param prio = {priority}; // scheduling priority of thread
|
||||
pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
||||
#endif
|
||||
|
||||
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005);
|
||||
|
||||
std::cout << "SDR post-processing thread started.." << std::endl;
|
||||
|
||||
iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput");
|
||||
iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput");
|
||||
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput");
|
||||
|
||||
ReBuffer<DemodulatorThreadIQData> buffers;
|
||||
std::vector<liquid_float_complex> fpData;
|
||||
std::vector<liquid_float_complex> dataOut;
|
||||
|
||||
iqActiveDemodVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQActiveDemodVisualDataOutput");
|
||||
|
||||
iqDataInQueue->set_max_num_items(0);
|
||||
|
||||
std::vector<liquid_float_complex> dcBuf;
|
||||
|
||||
while (!terminated) {
|
||||
SDRThreadIQData *data_in;
|
||||
@@ -86,125 +165,185 @@ void SDRPostThread::run() {
|
||||
iqDataInQueue->pop(data_in);
|
||||
// std::lock_guard < std::mutex > lock(data_in->m_mutex);
|
||||
|
||||
if (data_in && data_in->data.size()) {
|
||||
int dataSize = data_in->data.size()/2;
|
||||
if (dataSize > fpData.capacity()) {
|
||||
fpData.reserve(dataSize);
|
||||
dataOut.reserve(dataSize);
|
||||
}
|
||||
if (dataSize != fpData.size()) {
|
||||
fpData.resize(dataSize);
|
||||
dataOut.resize(dataSize);
|
||||
if (data_in && data_in->data.size() && data_in->numChannels) {
|
||||
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) {
|
||||
numChannels = data_in->numChannels;
|
||||
sampleRate = data_in->sampleRate;
|
||||
initPFBChannelizer();
|
||||
doRefresh.store(true);
|
||||
}
|
||||
|
||||
if (swapIQ) {
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
fpData[i] = _lut_swap[*((uint16_t*)&data_in->data[2*i])];
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
fpData[i] = _lut[*((uint16_t*)&data_in->data[2*i])];
|
||||
}
|
||||
}
|
||||
|
||||
iirfilt_crcf_execute_block(dcFilter, &fpData[0], dataSize, &dataOut[0]);
|
||||
|
||||
if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
|
||||
DemodulatorThreadIQData *visualDataOut = visualDataBuffers.getBuffer();
|
||||
visualDataOut->setRefCount(1);
|
||||
|
||||
int num_vis_samples = dataOut.size();
|
||||
int dataSize = data_in->data.size();
|
||||
int outSize = data_in->data.size();
|
||||
|
||||
// if (visualDataOut->data.size() < num_vis_samples) {
|
||||
// if (visualDataOut->data.capacity() < num_vis_samples) {
|
||||
// visualDataOut->data.reserve(num_vis_samples);
|
||||
// }
|
||||
// visualDataOut->data.resize(num_vis_samples);
|
||||
// }
|
||||
//
|
||||
visualDataOut->frequency = data_in->frequency;
|
||||
visualDataOut->sampleRate = data_in->sampleRate;
|
||||
visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + num_vis_samples);
|
||||
|
||||
iqVisualQueue->push(visualDataOut);
|
||||
if (outSize > dataOut.capacity()) {
|
||||
dataOut.reserve(outSize);
|
||||
}
|
||||
if (outSize != dataOut.size()) {
|
||||
dataOut.resize(outSize);
|
||||
}
|
||||
int activeVisChannel = -1;
|
||||
|
||||
// if (visBandwidth.load() && visBandwidth.load() < (chanBw/2)) {
|
||||
// activeVisChannel = getChannelAt(visFrequency);
|
||||
// }
|
||||
|
||||
if (iqDataOutQueue != NULL && !iqDataOutQueue->full() && activeVisChannel < 0) {
|
||||
DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer();
|
||||
|
||||
bool doVis = false;
|
||||
|
||||
if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
|
||||
doVis = true;
|
||||
}
|
||||
|
||||
iqDataOut->setRefCount(1 + (doVis?1:0));
|
||||
|
||||
iqDataOut->frequency = data_in->frequency;
|
||||
iqDataOut->sampleRate = data_in->sampleRate;
|
||||
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
|
||||
|
||||
iqDataOutQueue->push(iqDataOut);
|
||||
if (doVis) {
|
||||
iqVisualQueue->push(iqDataOut);
|
||||
}
|
||||
}
|
||||
|
||||
busy_demod.lock();
|
||||
|
||||
int activeDemods = 0;
|
||||
bool pushedData = false;
|
||||
if (frequency != data_in->frequency) {
|
||||
frequency = data_in->frequency;
|
||||
doRefresh.store(true);
|
||||
}
|
||||
|
||||
if (doRefresh.load()) {
|
||||
updateActiveDemodulators();
|
||||
updateChannels();
|
||||
doRefresh.store(false);
|
||||
}
|
||||
|
||||
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||
int activeDemodChannel = -1;
|
||||
|
||||
if (demodulators.size() || iqDataOutQueue != NULL) {
|
||||
std::vector<DemodulatorInstance *>::iterator demod_i;
|
||||
for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) {
|
||||
DemodulatorInstance *demod = *demod_i;
|
||||
if (demod->getFrequency() != data_in->frequency
|
||||
&& abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
||||
// Find active demodulators
|
||||
if (nRunDemods || (activeVisChannel >= 0)) {
|
||||
|
||||
// for (int i = 0; i < numChannels; i++) {
|
||||
// firpfbch_crcf_set_channel_state(channelizer, i, (demodChannelActive[i]>0)?1:0);
|
||||
// }
|
||||
|
||||
// channelize data
|
||||
// firpfbch output rate is (input rate / channels)
|
||||
for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels) {
|
||||
firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]);
|
||||
}
|
||||
|
||||
for (int i = 0, iMax = numChannels; i < iMax; i++) {
|
||||
demodChannelActive[i] = 0;
|
||||
}
|
||||
|
||||
// Find nearest channel for each demodulator
|
||||
for (int i = 0; i < nRunDemods; i++) {
|
||||
DemodulatorInstance *demod = runDemods[i];
|
||||
demodChannel[i] = getChannelAt(demod->getFrequency());
|
||||
if (demod == activeDemod) {
|
||||
activeDemodChannel = demodChannel[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < nRunDemods; i++) {
|
||||
// cache channel usage refcounts
|
||||
if (demodChannel[i] >= 0) {
|
||||
demodChannelActive[demodChannel[i]]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Run channels
|
||||
for (int i = 0; i < numChannels+1; i++) {
|
||||
int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full())?1:0;
|
||||
int doVis = 0;
|
||||
|
||||
// if (activeVisChannel == i) {
|
||||
// doVis = (((iqDataOutQueue != NULL))?1:0) + ((iqVisualQueue != NULL && !iqVisualQueue->full())?1:0);
|
||||
// }
|
||||
|
||||
if (!doVis && !doDemodVis && demodChannelActive[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
activeDemods++;
|
||||
}
|
||||
|
||||
if (iqDataOutQueue != NULL) {
|
||||
activeDemods++;
|
||||
}
|
||||
|
||||
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
||||
|
||||
// std::lock_guard < std::mutex > lock(demodDataOut->m_mutex);
|
||||
demodDataOut->frequency = data_in->frequency;
|
||||
demodDataOut->sampleRate = data_in->sampleRate;
|
||||
demodDataOut->setRefCount(activeDemods);
|
||||
demodDataOut->data.assign(dataOut.begin(), dataOut.end());
|
||||
|
||||
for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) {
|
||||
DemodulatorInstance *demod = *demod_i;
|
||||
DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe();
|
||||
|
||||
if (abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
||||
if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) {
|
||||
demod->setActive(false);
|
||||
DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData;
|
||||
dummyDataOut->frequency = data_in->frequency;
|
||||
dummyDataOut->sampleRate = data_in->sampleRate;
|
||||
demodQueue->push(dummyDataOut);
|
||||
}
|
||||
|
||||
if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) {
|
||||
wxGetApp().setFrequency(demod->getFrequency());
|
||||
}
|
||||
} else if (!demod->isActive()) {
|
||||
demod->setActive(true);
|
||||
if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) {
|
||||
wxGetApp().getDemodMgr().setActiveDemodulator(demod);
|
||||
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
||||
demodDataOut->setRefCount(demodChannelActive[i] + doVis + doDemodVis);
|
||||
demodDataOut->frequency = chanCenters[i];
|
||||
demodDataOut->sampleRate = chanBw;
|
||||
|
||||
// Calculate channel buffer size
|
||||
int chanDataSize = (outSize/numChannels);
|
||||
|
||||
if (demodDataOut->data.size() != chanDataSize) {
|
||||
if (demodDataOut->data.capacity() < chanDataSize) {
|
||||
demodDataOut->data.reserve(chanDataSize);
|
||||
}
|
||||
demodDataOut->data.resize(chanDataSize);
|
||||
}
|
||||
|
||||
int idx = i;
|
||||
|
||||
// Extra channel wraps lower side band of lowest channel
|
||||
// to fix frequency gap on upper side of spectrum
|
||||
if (i == numChannels) {
|
||||
idx = (numChannels/2);
|
||||
}
|
||||
|
||||
if (!demod->isActive()) {
|
||||
continue;
|
||||
}
|
||||
if (demod->isFollow()) {
|
||||
demod->setFollow(false);
|
||||
}
|
||||
|
||||
demodQueue->push(demodDataOut);
|
||||
pushedData = true;
|
||||
}
|
||||
|
||||
if (iqDataOutQueue != NULL) {
|
||||
if (!iqDataOutQueue->full()) {
|
||||
iqDataOutQueue->push(demodDataOut);
|
||||
pushedData = true;
|
||||
// prepare channel data buffer
|
||||
if (i == 0) { // Channel 0 requires DC correction
|
||||
if (dcBuf.size() != chanDataSize) {
|
||||
dcBuf.resize(chanDataSize);
|
||||
}
|
||||
for (int j = 0; j < chanDataSize; j++) {
|
||||
dcBuf[j] = dataOut[idx];
|
||||
idx += numChannels;
|
||||
}
|
||||
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]);
|
||||
} else {
|
||||
demodDataOut->decRefCount();
|
||||
for (int j = 0; j < chanDataSize; j++) {
|
||||
demodDataOut->data[j] = dataOut[idx];
|
||||
idx += numChannels;
|
||||
}
|
||||
}
|
||||
|
||||
// if (doVis) {
|
||||
// iqDataOutQueue->push(demodDataOut);
|
||||
// if (doVis>1) {
|
||||
// iqVisualQueue->push(demodDataOut);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (doDemodVis) {
|
||||
iqActiveDemodVisualQueue->push(demodDataOut);
|
||||
}
|
||||
|
||||
for (int j = 0; j < nRunDemods; j++) {
|
||||
if (demodChannel[j] == i) {
|
||||
DemodulatorInstance *demod = runDemods[j];
|
||||
demod->getIQInputDataPipe()->push(demodDataOut);
|
||||
// std::cout << "Demodulator " << j << " in channel #" << i << " ctr: " << chanCenters[i] << " dataSize: " << chanDataSize << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!pushedData && iqDataOutQueue == NULL) {
|
||||
demodDataOut->setRefCount(0);
|
||||
}
|
||||
|
||||
bool doUpdate = false;
|
||||
for (int j = 0; j < nRunDemods; j++) {
|
||||
DemodulatorInstance *demod = runDemods[j];
|
||||
if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) {
|
||||
doUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (doUpdate) {
|
||||
updateActiveDemodulators();
|
||||
}
|
||||
|
||||
busy_demod.unlock();
|
||||
}
|
||||
data_in->decRefCount();
|
||||
|
||||
+32
-10
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#if USE_RTL_SDR
|
||||
#include "SDRThread.h"
|
||||
#else
|
||||
#include "SoapySDRThread.h"
|
||||
#endif
|
||||
#include <algorithm>
|
||||
|
||||
class SDRPostThread : public IOThread {
|
||||
@@ -10,26 +14,44 @@ public:
|
||||
|
||||
void bindDemodulator(DemodulatorInstance *demod);
|
||||
void removeDemodulator(DemodulatorInstance *demod);
|
||||
|
||||
void setSwapIQ(bool swapIQ);
|
||||
bool getSwapIQ();
|
||||
|
||||
void run();
|
||||
void terminate();
|
||||
|
||||
void setIQVisualRange(long long frequency, int bandwidth);
|
||||
|
||||
protected:
|
||||
SDRThreadIQDataQueue *iqDataInQueue;
|
||||
DemodulatorThreadInputQueue *iqDataOutQueue;
|
||||
DemodulatorThreadInputQueue *iqVisualQueue;
|
||||
|
||||
DemodulatorThreadInputQueue *iqActiveDemodVisualQueue;
|
||||
|
||||
std::mutex busy_demod;
|
||||
std::vector<DemodulatorInstance *> demodulators;
|
||||
iirfilt_crcf dcFilter;
|
||||
std::atomic_bool swapIQ;
|
||||
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
|
||||
|
||||
|
||||
private:
|
||||
std::vector<liquid_float_complex> _lut;
|
||||
std::vector<liquid_float_complex> _lut_swap;
|
||||
void initPFBChannelizer();
|
||||
void updateActiveDemodulators();
|
||||
void updateChannels();
|
||||
int getChannelAt(long long frequency);
|
||||
|
||||
ReBuffer<DemodulatorThreadIQData> buffers;
|
||||
std::vector<liquid_float_complex> fpData;
|
||||
std::vector<liquid_float_complex> dataOut;
|
||||
std::vector<long long> chanCenters;
|
||||
long long chanBw;
|
||||
|
||||
int nRunDemods;
|
||||
std::vector<DemodulatorInstance *> runDemods;
|
||||
std::vector<int> demodChannel;
|
||||
std::vector<int> demodChannelActive;
|
||||
|
||||
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
|
||||
atomic_bool doRefresh;
|
||||
atomic_llong visFrequency;
|
||||
atomic_int visBandwidth;
|
||||
int numChannels, sampleRate;
|
||||
long long frequency;
|
||||
firpfbch_crcf channelizer;
|
||||
iirfilt_crcf dcFilter;
|
||||
};
|
||||
|
||||
@@ -41,8 +41,12 @@ int SDRThread::enumerate_rtl(std::vector<SDRDeviceInfo *> *devs) {
|
||||
deviceProduct = product;
|
||||
deviceManufacturer = manufact;
|
||||
|
||||
rtlsdr_dev_t *devTest;
|
||||
rtlsdr_open(&devTest, i);
|
||||
rtlsdr_dev_t *devTest = nullptr;
|
||||
if(rtlsdr_open(&devTest, i) < 0)
|
||||
{
|
||||
std::cout << "\tFailed to open device " << i << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "\t Tuner type: ";
|
||||
switch (rtlsdr_get_tuner_type(devTest)) {
|
||||
|
||||
@@ -0,0 +1,423 @@
|
||||
#include "SoapySDRThread.h"
|
||||
#include "CubicSDRDefs.h"
|
||||
#include <vector>
|
||||
#include "CubicSDR.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
SDRThread::SDRThread() : IOThread() {
|
||||
device = NULL;
|
||||
|
||||
deviceConfig.store(NULL);
|
||||
deviceInfo.store(NULL);
|
||||
|
||||
sampleRate.store(DEFAULT_SAMPLE_RATE);
|
||||
frequency.store(0);
|
||||
offset.store(0);
|
||||
ppm.store(0);
|
||||
|
||||
numElems.store(0);
|
||||
|
||||
rate_changed.store(false);
|
||||
freq_changed.store(false);
|
||||
offset_changed.store(false);
|
||||
ppm_changed .store(false);
|
||||
device_changed.store(false);
|
||||
|
||||
hasPPM.store(false);
|
||||
hasHardwareDC.store(false);
|
||||
numChannels.store(8);
|
||||
|
||||
agc_mode.store(true);
|
||||
agc_mode_changed.store(false);
|
||||
gain_value_changed.store(false);
|
||||
setting_value_changed.store(false);
|
||||
}
|
||||
|
||||
SDRThread::~SDRThread() {
|
||||
|
||||
}
|
||||
|
||||
SoapySDR::Kwargs SDRThread::combineArgs(SoapySDR::Kwargs a, SoapySDR::Kwargs b) {
|
||||
SoapySDR::Kwargs c;
|
||||
SoapySDR::Kwargs::iterator i;
|
||||
for (i = a.begin(); i != a.end(); i++) {
|
||||
c[i->first] = i->second;
|
||||
}
|
||||
for (i = b.begin(); i != b.end(); i++) {
|
||||
c[i->first] = i->second;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void SDRThread::init() {
|
||||
SDRDeviceInfo *devInfo = deviceInfo.load();
|
||||
deviceConfig.store(wxGetApp().getConfig()->getDevice(devInfo->getDeviceId()));
|
||||
DeviceConfig *devConfig = deviceConfig.load();
|
||||
|
||||
ppm.store(devConfig->getPPM());
|
||||
ppm_changed.store(true);
|
||||
|
||||
std::string driverName = devInfo->getDriver();
|
||||
|
||||
offset = devConfig->getOffset();
|
||||
|
||||
SoapySDR::Kwargs args = devInfo->getDeviceArgs();
|
||||
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Initializing device."));
|
||||
device = SoapySDR::Device::make(args);
|
||||
stream = device->setupStream(SOAPY_SDR_RX,"CF32", std::vector<size_t>(), combineArgs(devInfo->getStreamArgs(),streamArgs));
|
||||
|
||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Activating stream."));
|
||||
device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load());
|
||||
device->setFrequency(SOAPY_SDR_RX,0,"RF",frequency - offset.load());
|
||||
device->activateStream(stream);
|
||||
SDRDeviceChannel *chan = devInfo->getRxChannel();
|
||||
if (chan->hasCORR()) {
|
||||
hasPPM.store(true);
|
||||
device->setFrequency(SOAPY_SDR_RX,0,"CORR",ppm.load());
|
||||
} else {
|
||||
hasPPM.store(false);
|
||||
}
|
||||
if (chan->hasHardwareDC()) {
|
||||
hasHardwareDC.store(true);
|
||||
// wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Found hardware DC offset correction support, internal disabled."));
|
||||
device->setDCOffsetMode(SOAPY_SDR_RX, chan->getChannel(), true);
|
||||
} else {
|
||||
hasHardwareDC.store(false);
|
||||
}
|
||||
|
||||
device->setGainMode(SOAPY_SDR_RX,0,agc_mode.load());
|
||||
|
||||
numChannels.store(getOptimalChannelCount(sampleRate.load()));
|
||||
numElems.store(getOptimalElementCount(sampleRate.load(), 30));
|
||||
inpBuffer.data.resize(numElems.load());
|
||||
|
||||
buffs[0] = malloc(numElems * 2 * sizeof(float));
|
||||
|
||||
SoapySDR::ArgInfoList settingsInfo = device->getSettingInfo();
|
||||
SoapySDR::ArgInfoList::const_iterator settings_i;
|
||||
|
||||
if (!setting_value_changed.load()) {
|
||||
settings.erase(settings.begin(), settings.end());
|
||||
settingChanged.erase(settingChanged.begin(), settingChanged.end());
|
||||
}
|
||||
|
||||
setting_busy.lock();
|
||||
for (settings_i = settingsInfo.begin(); settings_i != settingsInfo.end(); settings_i++) {
|
||||
SoapySDR::ArgInfo setting = (*settings_i);
|
||||
if ((settingChanged.find(setting.key) != settingChanged.end()) && (settings.find(setting.key) != settings.end())) {
|
||||
device->writeSetting(setting.key, settings[setting.key]);
|
||||
settingChanged[setting.key] = false;
|
||||
} else {
|
||||
settings[setting.key] = device->readSetting(setting.key);
|
||||
settingChanged[setting.key] = false;
|
||||
}
|
||||
}
|
||||
setting_value_changed.store(false);
|
||||
setting_busy.unlock();
|
||||
|
||||
wxGetApp().sdrThreadNotify(SDRThread::SDR_THREAD_INITIALIZED, std::string("Device Initialized."));
|
||||
}
|
||||
|
||||
void SDRThread::deinit() {
|
||||
device->deactivateStream(stream);
|
||||
device->closeStream(stream);
|
||||
SoapySDR::Device::unmake(device);
|
||||
free(buffs[0]);
|
||||
}
|
||||
|
||||
void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) {
|
||||
int flags;
|
||||
long long timeNs;
|
||||
|
||||
|
||||
int n_read = 0;
|
||||
while (n_read != numElems && !terminated) {
|
||||
int n_stream_read = device->readStream(stream, buffs, numElems-n_read, flags, timeNs);
|
||||
if (n_stream_read > 0) {
|
||||
memcpy(&inpBuffer.data[n_read], buffs[0], n_stream_read * sizeof(float) * 2);
|
||||
n_read += n_stream_read;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_read > 0 && !terminated) {
|
||||
SDRThreadIQData *dataOut = buffers.getBuffer();
|
||||
|
||||
// if (hasHardwareDC) {
|
||||
dataOut->data.assign(inpBuffer.data.begin(), inpBuffer.data.begin()+n_read);
|
||||
// } else {
|
||||
// if (dataOut->data.size() != n_read) {
|
||||
// dataOut->data.resize(n_read);
|
||||
// }
|
||||
// iirfilt_crcf_execute_block(dcFilter, &inpBuffer.data[0], n_read, &dataOut->data[0]);
|
||||
// }
|
||||
|
||||
|
||||
dataOut->setRefCount(1);
|
||||
dataOut->frequency = frequency.load();
|
||||
dataOut->sampleRate = sampleRate.load();
|
||||
dataOut->dcCorrected = hasHardwareDC.load();
|
||||
dataOut->numChannels = numChannels.load();
|
||||
|
||||
iqDataOutQueue->push(dataOut);
|
||||
}
|
||||
}
|
||||
|
||||
void SDRThread::readLoop() {
|
||||
SDRThreadIQDataQueue* iqDataOutQueue = (SDRThreadIQDataQueue*) getOutputQueue("IQDataOutput");
|
||||
|
||||
if (iqDataOutQueue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateGains();
|
||||
|
||||
while (!terminated.load()) {
|
||||
updateSettings();
|
||||
readStream(iqDataOutQueue);
|
||||
}
|
||||
|
||||
buffers.purge();
|
||||
}
|
||||
|
||||
void SDRThread::updateGains() {
|
||||
SDRDeviceInfo *devInfo = deviceInfo.load();
|
||||
|
||||
gainValues.erase(gainValues.begin(),gainValues.end());
|
||||
gainChanged.erase(gainChanged.begin(),gainChanged.end());
|
||||
|
||||
std::vector<SDRDeviceRange> gains = devInfo->getRxChannel()->getGains();
|
||||
for (std::vector<SDRDeviceRange>::iterator gi = gains.begin(); gi != gains.end(); gi++) {
|
||||
gainValues[(*gi).getName()] = device->getGain(SOAPY_SDR_RX, devInfo->getRxChannel()->getChannel(), (*gi).getName());
|
||||
gainChanged[(*gi).getName()] = false;
|
||||
}
|
||||
|
||||
gain_value_changed.store(false);
|
||||
}
|
||||
|
||||
void SDRThread::updateSettings() {
|
||||
if (offset_changed.load()) {
|
||||
if (!freq_changed.load()) {
|
||||
frequency.store(frequency.load());
|
||||
freq_changed.store(true);
|
||||
}
|
||||
offset_changed.store(false);
|
||||
}
|
||||
|
||||
if (rate_changed.load()) {
|
||||
device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load());
|
||||
sampleRate.store(device->getSampleRate(SOAPY_SDR_RX,0));
|
||||
numChannels.store(getOptimalChannelCount(sampleRate.load()));
|
||||
numElems.store(getOptimalElementCount(sampleRate.load(), 60));
|
||||
inpBuffer.data.resize(numElems.load());
|
||||
free(buffs[0]);
|
||||
buffs[0] = malloc(numElems.load() * 2 * sizeof(float));
|
||||
rate_changed.store(false);
|
||||
}
|
||||
|
||||
if (ppm_changed.load() && hasPPM.load()) {
|
||||
device->setFrequency(SOAPY_SDR_RX,0,"CORR",ppm.load());
|
||||
ppm_changed.store(false);
|
||||
}
|
||||
|
||||
if (freq_changed.load()) {
|
||||
device->setFrequency(SOAPY_SDR_RX,0,"RF",frequency.load() - offset.load());
|
||||
freq_changed.store(false);
|
||||
}
|
||||
|
||||
if (agc_mode_changed.load()) {
|
||||
SDRDeviceInfo *devInfo = deviceInfo.load();
|
||||
|
||||
device->setGainMode(SOAPY_SDR_RX,devInfo->getRxChannel()->getChannel(),agc_mode.load());
|
||||
agc_mode_changed.store(false);
|
||||
if (!agc_mode.load()) {
|
||||
updateGains();
|
||||
}
|
||||
}
|
||||
|
||||
if (gain_value_changed.load() && !agc_mode.load()) {
|
||||
SDRDeviceInfo *devInfo = deviceInfo.load();
|
||||
|
||||
gain_busy.lock();
|
||||
for (std::map<std::string,bool>::iterator gci = gainChanged.begin(); gci != gainChanged.end(); gci++) {
|
||||
if (gci->second) {
|
||||
device->setGain(SOAPY_SDR_RX, devInfo->getRxChannel()->getChannel(), gci->first, gainValues[gci->first]);
|
||||
gainChanged[gci->first] = false;
|
||||
}
|
||||
}
|
||||
gain_busy.unlock();
|
||||
|
||||
gain_value_changed.store(false);
|
||||
}
|
||||
|
||||
|
||||
if (setting_value_changed.load()) {
|
||||
setting_busy.lock();
|
||||
|
||||
for (std::map<std::string, bool>::iterator sci = settingChanged.begin(); sci != settingChanged.end(); sci++) {
|
||||
if (sci->second) {
|
||||
device->writeSetting(sci->first, settings[sci->first]);
|
||||
settingChanged[sci->first] = false;
|
||||
}
|
||||
}
|
||||
|
||||
setting_value_changed.store(false);
|
||||
setting_busy.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void SDRThread::run() {
|
||||
//#ifdef __APPLE__
|
||||
// pthread_t tID = pthread_self(); // ID of this thread
|
||||
// int priority = sched_get_priority_max( SCHED_FIFO);
|
||||
// sched_param prio = { priority }; // scheduling priority of thread
|
||||
// pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
||||
//#endif
|
||||
|
||||
std::cout << "SDR thread starting." << std::endl;
|
||||
terminated.store(false);
|
||||
|
||||
if (deviceInfo.load() != NULL) {
|
||||
std::cout << "device init()" << std::endl;
|
||||
init();
|
||||
std::cout << "starting readLoop()" << std::endl;
|
||||
readLoop();
|
||||
std::cout << "readLoop() ended." << std::endl;
|
||||
deinit();
|
||||
std::cout << "device deinit()" << std::endl;
|
||||
} else {
|
||||
std::cout << "SDR Thread started with null device?" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "SDR thread done." << std::endl;
|
||||
|
||||
if (!terminated.load()) {
|
||||
terminated.store(true);
|
||||
wxGetApp().sdrThreadNotify(SDRThread::SDR_THREAD_TERMINATED, "Done.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SDRDeviceInfo *SDRThread::getDevice() {
|
||||
return deviceInfo.load();
|
||||
}
|
||||
|
||||
void SDRThread::setDevice(SDRDeviceInfo *dev) {
|
||||
deviceInfo.store(dev);
|
||||
deviceConfig.store(wxGetApp().getConfig()->getDevice(dev->getDeviceId()));
|
||||
}
|
||||
|
||||
int SDRThread::getOptimalElementCount(long long sampleRate, int fps) {
|
||||
int elemCount = (int)floor((double)sampleRate/(double)fps);
|
||||
int nch = numChannels.load();
|
||||
elemCount = int(ceil((double)elemCount/(double)nch))*nch;
|
||||
std::cout << "Calculated optimal " << numChannels.load() << " channel element count of " << elemCount << std::endl;
|
||||
return elemCount;
|
||||
}
|
||||
|
||||
int SDRThread::getOptimalChannelCount(long long sampleRate) {
|
||||
int optimal_rate = CHANNELIZER_RATE_MAX;
|
||||
int optimal_count = int(ceil(double(sampleRate)/double(optimal_rate)));
|
||||
|
||||
if (optimal_count % 2 == 1) {
|
||||
optimal_count--;
|
||||
}
|
||||
|
||||
if (optimal_count < 4) {
|
||||
optimal_count = 4;
|
||||
}
|
||||
|
||||
// if (optimal_count > 16) {
|
||||
// optimal_count = 16;
|
||||
// }
|
||||
return optimal_count;
|
||||
}
|
||||
|
||||
|
||||
void SDRThread::setFrequency(long long freq) {
|
||||
if (freq < sampleRate.load() / 2) {
|
||||
freq = sampleRate.load() / 2;
|
||||
}
|
||||
frequency.store(freq);
|
||||
freq_changed.store(true);
|
||||
}
|
||||
|
||||
long long SDRThread::getFrequency() {
|
||||
return frequency.load();
|
||||
}
|
||||
|
||||
void SDRThread::setOffset(long long ofs) {
|
||||
offset.store(ofs);
|
||||
offset_changed.store(true);
|
||||
std::cout << "Set offset: " << offset.load() << std::endl;
|
||||
}
|
||||
|
||||
long long SDRThread::getOffset() {
|
||||
return offset.load();
|
||||
}
|
||||
|
||||
void SDRThread::setSampleRate(int rate) {
|
||||
sampleRate.store(rate);
|
||||
rate_changed = true;
|
||||
std::cout << "Set sample rate: " << sampleRate.load() << std::endl;
|
||||
}
|
||||
int SDRThread::getSampleRate() {
|
||||
return sampleRate.load();
|
||||
}
|
||||
|
||||
void SDRThread::setPPM(int ppm) {
|
||||
this->ppm.store(ppm);
|
||||
ppm_changed.store(true);
|
||||
std::cout << "Set PPM: " << this->ppm.load() << std::endl;
|
||||
}
|
||||
|
||||
int SDRThread::getPPM() {
|
||||
return ppm.load();
|
||||
}
|
||||
|
||||
void SDRThread::setAGCMode(bool mode) {
|
||||
agc_mode.store(mode);
|
||||
agc_mode_changed.store(true);
|
||||
}
|
||||
|
||||
bool SDRThread::getAGCMode() {
|
||||
return agc_mode.load();
|
||||
}
|
||||
|
||||
void SDRThread::setGain(std::string name, float value) {
|
||||
gain_busy.lock();
|
||||
gainValues[name] = value;
|
||||
gainChanged[name] = true;
|
||||
gain_value_changed.store(true);
|
||||
gain_busy.unlock();
|
||||
}
|
||||
|
||||
float SDRThread::getGain(std::string name) {
|
||||
gain_busy.lock();
|
||||
float val = gainValues[name];
|
||||
gain_busy.unlock();
|
||||
return val;
|
||||
}
|
||||
|
||||
void SDRThread::writeSetting(std::string name, std::string value) {
|
||||
setting_busy.lock();
|
||||
settings[name] = value;
|
||||
settingChanged[name] = true;
|
||||
setting_value_changed.store(true);
|
||||
setting_busy.unlock();
|
||||
}
|
||||
|
||||
std::string SDRThread::readSetting(std::string name) {
|
||||
std::string val;
|
||||
setting_busy.lock();
|
||||
val = device->readSetting(name);
|
||||
setting_busy.unlock();
|
||||
return val;
|
||||
}
|
||||
|
||||
void SDRThread::setStreamArgs(SoapySDR::Kwargs streamArgs_in) {
|
||||
streamArgs = streamArgs_in;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "ThreadQueue.h"
|
||||
#include "DemodulatorMgr.h"
|
||||
#include "SDRDeviceInfo.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
#include <SoapySDR/Version.hpp>
|
||||
#include <SoapySDR/Modules.hpp>
|
||||
#include <SoapySDR/Registry.hpp>
|
||||
#include <SoapySDR/Device.hpp>
|
||||
|
||||
|
||||
class SDRThreadIQData: public ReferenceCounter {
|
||||
public:
|
||||
long long frequency;
|
||||
long long sampleRate;
|
||||
bool dcCorrected;
|
||||
int numChannels;
|
||||
std::vector<liquid_float_complex> data;
|
||||
|
||||
SDRThreadIQData() :
|
||||
frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), dcCorrected(true), numChannels(0) {
|
||||
|
||||
}
|
||||
|
||||
SDRThreadIQData(long long bandwidth, long long frequency, std::vector<signed char> *data) :
|
||||
frequency(frequency), sampleRate(bandwidth) {
|
||||
|
||||
}
|
||||
|
||||
~SDRThreadIQData() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
typedef ThreadQueue<SDRThreadIQData *> SDRThreadIQDataQueue;
|
||||
|
||||
class SDRThread : public IOThread {
|
||||
private:
|
||||
void init();
|
||||
void deinit();
|
||||
void readStream(SDRThreadIQDataQueue* iqDataOutQueue);
|
||||
void readLoop();
|
||||
|
||||
public:
|
||||
SDRThread();
|
||||
~SDRThread();
|
||||
enum SDRThreadState { SDR_THREAD_MESSAGE, SDR_THREAD_INITIALIZED, SDR_THREAD_TERMINATED, SDR_THREAD_FAILED };
|
||||
|
||||
void run();
|
||||
|
||||
SDRDeviceInfo *getDevice();
|
||||
void setDevice(SDRDeviceInfo *dev);
|
||||
int getOptimalElementCount(long long sampleRate, int fps);
|
||||
int getOptimalChannelCount(long long sampleRate);
|
||||
|
||||
void setFrequency(long long freq);
|
||||
long long getFrequency();
|
||||
|
||||
void setOffset(long long ofs);
|
||||
long long getOffset();
|
||||
|
||||
void setSampleRate(int rate);
|
||||
int getSampleRate();
|
||||
|
||||
void setPPM(int ppm);
|
||||
int getPPM();
|
||||
|
||||
void setAGCMode(bool mode);
|
||||
bool getAGCMode();
|
||||
|
||||
void setGain(std::string name, float value);
|
||||
float getGain(std::string name);
|
||||
|
||||
void writeSetting(std::string name, std::string value);
|
||||
std::string readSetting(std::string name);
|
||||
|
||||
void setStreamArgs(SoapySDR::Kwargs streamArgs);
|
||||
|
||||
protected:
|
||||
void updateGains();
|
||||
void updateSettings();
|
||||
SoapySDR::Kwargs combineArgs(SoapySDR::Kwargs a, SoapySDR::Kwargs b);
|
||||
|
||||
SoapySDR::Stream *stream;
|
||||
SoapySDR::Device *device;
|
||||
void *buffs[1];
|
||||
ReBuffer<SDRThreadIQData> buffers;
|
||||
SDRThreadIQData inpBuffer;
|
||||
std::atomic<DeviceConfig *> deviceConfig;
|
||||
std::atomic<SDRDeviceInfo *> deviceInfo;
|
||||
|
||||
std::mutex setting_busy;
|
||||
std::map<std::string, std::string> settings;
|
||||
std::map<std::string, bool> settingChanged;
|
||||
|
||||
std::atomic<uint32_t> sampleRate;
|
||||
std::atomic_llong frequency, offset;
|
||||
std::atomic_int ppm, numElems, numChannels;
|
||||
std::atomic_bool hasPPM, hasHardwareDC;
|
||||
std::atomic_bool agc_mode, rate_changed, freq_changed, offset_changed,
|
||||
ppm_changed, device_changed, agc_mode_changed, gain_value_changed, setting_value_changed;
|
||||
|
||||
std::mutex gain_busy;
|
||||
std::map<std::string, float> gainValues;
|
||||
std::map<std::string, bool> gainChanged;
|
||||
|
||||
SoapySDR::Kwargs streamArgs;
|
||||
};
|
||||
+25
-3
@@ -5,7 +5,7 @@
|
||||
|
||||
using namespace CubicVR;
|
||||
|
||||
GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), transform(mat4::identity()) {
|
||||
GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), visible(true), transform(mat4::identity()) {
|
||||
pos[0] = 0.0f;
|
||||
pos[1] = 0.0f;
|
||||
rot[0] = 0.0f;
|
||||
@@ -19,6 +19,8 @@ GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), transf
|
||||
setCoordinateSystem(GLPANEL_Y_UP);
|
||||
setMarginPx(0);
|
||||
setBorderPx(0);
|
||||
srcBlend = GL_SRC_ALPHA;
|
||||
dstBlend = GL_ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
|
||||
void GLPanel::genArrays() {
|
||||
@@ -174,6 +176,19 @@ void GLPanel::setCoordinateSystem(GLPanelCoordinateSystem coord_in) {
|
||||
genArrays();
|
||||
}
|
||||
|
||||
bool GLPanel::hitTest(CubicVR::vec2 pos, CubicVR::vec2 &result) {
|
||||
CubicVR::vec4 hitPos = CubicVR::mat4::vec4_multiply(CubicVR::vec4(pos.x, pos.y, 0.0, 1.0), transformInverse);
|
||||
|
||||
if (hitPos.x >= -1.0 && hitPos.x <= 1.0 && hitPos.y >= -1.0 && hitPos.y <= 1.0) {
|
||||
result.x = hitPos.x;
|
||||
result.y = hitPos.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void GLPanel::setFill(GLPanelFillType fill_mode) {
|
||||
fillType = fill_mode;
|
||||
genArrays();
|
||||
@@ -210,6 +225,11 @@ void GLPanel::setBorderPx(float bordl, float bordr, float bordt, float bordb) {
|
||||
borderPx.bottom = bordb;
|
||||
}
|
||||
|
||||
void GLPanel::setBlend(GLuint src, GLuint dst) {
|
||||
srcBlend = src;
|
||||
dstBlend = dst;
|
||||
}
|
||||
|
||||
void GLPanel::addChild(GLPanel *childPanel) {
|
||||
std::vector<GLPanel *>::iterator i = std::find(children.begin(), children.end(), childPanel);
|
||||
|
||||
@@ -278,6 +298,8 @@ void GLPanel::calcTransform(mat4 transform_in) {
|
||||
if (marginPx) {
|
||||
transform *= mat4::scale(1.0 - marginPx * 2.0 * pvec.x / size[0], 1.0 - marginPx * 2.0 * pvec.y / size[1], 1);
|
||||
}
|
||||
|
||||
transformInverse = CubicVR::mat4::inverse(transform);
|
||||
}
|
||||
|
||||
void GLPanel::draw() {
|
||||
@@ -285,9 +307,9 @@ void GLPanel::draw() {
|
||||
|
||||
glLoadMatrixf(transform);
|
||||
|
||||
if (fillType != GLPANEL_FILL_NONE) {
|
||||
if (fillType != GLPANEL_FILL_NONE && visible) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendFunc(srcBlend, dstBlend);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, 0, &glPoints[0]);
|
||||
|
||||
+8
-3
@@ -45,8 +45,8 @@ public:
|
||||
GLPanelEdges borderPx;
|
||||
RGBA4f fill[2];
|
||||
RGBA4f borderColor;
|
||||
bool contentsVisible;
|
||||
CubicVR::mat4 transform;
|
||||
bool contentsVisible, visible;
|
||||
CubicVR::mat4 transform, transformInverse;
|
||||
CubicVR::mat4 localTransform;
|
||||
float min, mid, max;
|
||||
// screen dimensions
|
||||
@@ -55,7 +55,8 @@ public:
|
||||
CubicVR::vec2 umin, umax, ucenter;
|
||||
// pixel dimensions
|
||||
CubicVR::vec2 pdim, pvec;
|
||||
|
||||
GLuint srcBlend, dstBlend;
|
||||
|
||||
std::vector<GLPanel *> children;
|
||||
|
||||
GLPanel();
|
||||
@@ -68,6 +69,8 @@ public:
|
||||
float getHeightPx();
|
||||
void setCoordinateSystem(GLPanelCoordinateSystem coord);
|
||||
|
||||
bool hitTest(CubicVR::vec2 pos, CubicVR::vec2 &result);
|
||||
|
||||
void setFill(GLPanelFillType fill_mode);
|
||||
void setFillColor(RGBA4f color1);
|
||||
void setFillColor(RGBA4f color1, RGBA4f color2);
|
||||
@@ -77,6 +80,8 @@ public:
|
||||
void setBorderPx(float bord);
|
||||
void setBorderPx(float bordl, float bordr, float bordt, float bordb);
|
||||
|
||||
void setBlend(GLuint src, GLuint dst);
|
||||
|
||||
void addChild(GLPanel *childPanel);
|
||||
void removeChild(GLPanel *childPanel);
|
||||
|
||||
|
||||
+3
-3
@@ -35,10 +35,10 @@ void initGLExtensions() {
|
||||
// const GLubyte *extensions = glGetString(GL_EXTENSIONS);
|
||||
// std::cout << std::endl << "Supported GL Extensions: " << std::endl << extensions << std::endl << std::endl;
|
||||
|
||||
#ifdef __linux__
|
||||
const GLint interval = 2;
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
const GLint interval = 1;
|
||||
#else
|
||||
const GLint interval = 2;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
#include "GainCanvas.h"
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#if !wxUSE_GLCANVAS
|
||||
#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library"
|
||||
#endif
|
||||
|
||||
#include "CubicSDR.h"
|
||||
#include "CubicSDRDefs.h"
|
||||
#include "AppFrame.h"
|
||||
#include <algorithm>
|
||||
|
||||
wxBEGIN_EVENT_TABLE(GainCanvas, wxGLCanvas) EVT_PAINT(GainCanvas::OnPaint)
|
||||
EVT_IDLE(GainCanvas::OnIdle)
|
||||
EVT_MOTION(GainCanvas::OnMouseMoved)
|
||||
EVT_LEFT_DOWN(GainCanvas::OnMouseDown)
|
||||
EVT_LEFT_UP(GainCanvas::OnMouseReleased)
|
||||
EVT_LEAVE_WINDOW(GainCanvas::OnMouseLeftWindow)
|
||||
EVT_ENTER_WINDOW(GainCanvas::OnMouseEnterWindow)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
GainCanvas::GainCanvas(wxWindow *parent, int *attribList) :
|
||||
InteractiveCanvas(parent, attribList) {
|
||||
|
||||
glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
|
||||
bgPanel.setCoordinateSystem(GLPanel::GLPANEL_Y_UP);
|
||||
bgPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_X);
|
||||
|
||||
numGains = 1;
|
||||
spacing = 2.0/numGains;
|
||||
barWidth = (1.0/numGains)*0.8;
|
||||
startPos = spacing/2.0;
|
||||
barHeight = 0.8;
|
||||
}
|
||||
|
||||
GainCanvas::~GainCanvas() {
|
||||
|
||||
}
|
||||
|
||||
void GainCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
wxPaintDC dc(this);
|
||||
const wxSize ClientSize = GetClientSize();
|
||||
|
||||
glContext->SetCurrent(*this);
|
||||
initGLExtensions();
|
||||
|
||||
glViewport(0, 0, ClientSize.x, ClientSize.y);
|
||||
|
||||
float i = 0;
|
||||
for (std::vector<GainInfo *>::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) {
|
||||
GainInfo *gInfo = (*gi);
|
||||
float midPos = -1.0+startPos+spacing*i;
|
||||
|
||||
gInfo->labelPanel.setSize(spacing/2.0,(15.0/float(ClientSize.y)));
|
||||
gInfo->labelPanel.setPosition(midPos, -barHeight-(20.0/float(ClientSize.y)));
|
||||
|
||||
gInfo->valuePanel.setSize(spacing/2.0,(15.0/float(ClientSize.y)));
|
||||
gInfo->valuePanel.setPosition(midPos, barHeight+(20.0/float(ClientSize.y)));
|
||||
|
||||
i+=1.0;
|
||||
}
|
||||
|
||||
bgPanel.draw();
|
||||
|
||||
SwapBuffers();
|
||||
}
|
||||
|
||||
void GainCanvas::OnIdle(wxIdleEvent &event) {
|
||||
if (mouseTracker.mouseInView()) {
|
||||
Refresh();
|
||||
} else {
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
for (std::vector<GainInfo *>::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) {
|
||||
GainInfo *gInfo = (*gi);
|
||||
if (gInfo->changed) {
|
||||
wxGetApp().setGain(gInfo->name, gInfo->current);
|
||||
gInfo->changed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GainCanvas::GetPanelHit(CubicVR::vec2 &result) {
|
||||
std::vector<GainInfo *>::iterator gi;
|
||||
|
||||
int i = 0;
|
||||
for (gi = gainInfo.begin(); gi != gainInfo.end(); gi++) {
|
||||
GainInfo *gInfo = (*gi);
|
||||
|
||||
CubicVR::vec2 hitResult;
|
||||
if (gInfo->panel.hitTest(CubicVR::vec2((mouseTracker.getMouseX()-0.5)*2.0, (mouseTracker.getMouseY()-0.5)*2.0), hitResult)) {
|
||||
// std::cout << "Hit #" << i << " result: " << hitResult << std::endl;
|
||||
result = (hitResult + CubicVR::vec2(1.0,1.0)) * 0.5;
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void GainCanvas::SetLevel() {
|
||||
CubicVR::vec2 hitResult;
|
||||
int panelHit = GetPanelHit(hitResult);
|
||||
|
||||
if (panelHit >= 0) {
|
||||
gainInfo[panelHit]->levelPanel.setSize(1.0, hitResult.y);
|
||||
gainInfo[panelHit]->levelPanel.setPosition(0.0, (-1.0+(hitResult.y)));
|
||||
gainInfo[panelHit]->current = gainInfo[panelHit]->low+(hitResult.y * (gainInfo[panelHit]->high-gainInfo[panelHit]->low));
|
||||
gainInfo[panelHit]->changed = true;
|
||||
gainInfo[panelHit]->valuePanel.setText(std::to_string(int(gainInfo[panelHit]->current)));
|
||||
}
|
||||
}
|
||||
|
||||
void GainCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseMoved(event);
|
||||
|
||||
CubicVR::vec2 hitResult;
|
||||
int panelHit = GetPanelHit(hitResult);
|
||||
|
||||
if (panelHit >= 0) {
|
||||
gainInfo[panelHit]->highlightPanel.setSize(1.0, hitResult.y);
|
||||
gainInfo[panelHit]->highlightPanel.setPosition(0.0, (-1.0+(hitResult.y)));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (std::vector<GainInfo *>::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) {
|
||||
(*gi)->highlightPanel.visible = (i==panelHit);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (mouseTracker.mouseDown()) {
|
||||
SetLevel();
|
||||
}
|
||||
}
|
||||
|
||||
void GainCanvas::OnMouseDown(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseDown(event);
|
||||
SetLevel();
|
||||
}
|
||||
|
||||
void GainCanvas::OnMouseWheelMoved(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseWheelMoved(event);
|
||||
// Refresh();
|
||||
}
|
||||
|
||||
void GainCanvas::OnMouseReleased(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseReleased(event);
|
||||
// Refresh();
|
||||
}
|
||||
|
||||
void GainCanvas::OnMouseLeftWindow(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseLeftWindow(event);
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
|
||||
int i = 0;
|
||||
for (std::vector<GainInfo *>::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) {
|
||||
(*gi)->highlightPanel.visible = false;
|
||||
i++;
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void GainCanvas::OnMouseEnterWindow(wxMouseEvent& event) {
|
||||
InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event);
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
// Refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GainCanvas::setHelpTip(std::string tip) {
|
||||
helpTip = tip;
|
||||
}
|
||||
|
||||
void GainCanvas::updateGainUI() {
|
||||
const wxSize ClientSize = GetClientSize();
|
||||
|
||||
SDRDeviceInfo *devInfo = wxGetApp().getDevice();
|
||||
|
||||
std::vector<SDRDeviceRange> &gains = devInfo->getRxChannel()->getGains();
|
||||
std::vector<SDRDeviceRange>::iterator gi;
|
||||
|
||||
numGains = gains.size();
|
||||
float i = 0;
|
||||
|
||||
if (!numGains) {
|
||||
return;
|
||||
}
|
||||
|
||||
spacing = 2.0/numGains;
|
||||
barWidth = (1.0/numGains)*0.7;
|
||||
startPos = spacing/2.0;
|
||||
barHeight = 0.8;
|
||||
|
||||
RGBA4f c1, c2;
|
||||
|
||||
while (gainInfo.size()) {
|
||||
GainInfo *giDel;
|
||||
giDel = gainInfo.back();
|
||||
gainInfo.pop_back();
|
||||
|
||||
giDel->panel.removeChild(&giDel->levelPanel);
|
||||
bgPanel.removeChild(&(giDel->labelPanel));
|
||||
bgPanel.removeChild(&(giDel->valuePanel));
|
||||
bgPanel.removeChild(&(giDel->panel));
|
||||
delete giDel;
|
||||
}
|
||||
|
||||
for (gi = gains.begin(); gi != gains.end(); gi++) {
|
||||
GainInfo *gInfo = new GainInfo;
|
||||
float midPos = -1.0+startPos+spacing*i;
|
||||
|
||||
gInfo->name = (*gi).getName();
|
||||
gInfo->low = (*gi).getLow();
|
||||
gInfo->high = (*gi).getHigh();
|
||||
gInfo->current = wxGetApp().getGain(gInfo->name);
|
||||
|
||||
gInfo->panel.setBorderPx(1);
|
||||
gInfo->panel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X);
|
||||
gInfo->panel.setPosition(midPos, 0);
|
||||
gInfo->panel.setSize(barWidth, barHeight);
|
||||
gInfo->panel.setBlend(GL_ONE, GL_ONE);
|
||||
|
||||
gInfo->levelPanel.setBorderPx(0);
|
||||
gInfo->levelPanel.setMarginPx(1);
|
||||
gInfo->levelPanel.setSize(1.0,0.8);
|
||||
float levelVal = float(gInfo->current-gInfo->low)/float(gInfo->high-gInfo->low);
|
||||
gInfo->levelPanel.setSize(1.0, levelVal);
|
||||
gInfo->levelPanel.setPosition(0.0, (-1.0+(levelVal)));
|
||||
gInfo->levelPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X);
|
||||
gInfo->levelPanel.setBlend(GL_ONE, GL_ONE);
|
||||
|
||||
gInfo->panel.addChild(&gInfo->levelPanel);
|
||||
|
||||
gInfo->highlightPanel.setBorderPx(0);
|
||||
gInfo->highlightPanel.setMarginPx(1);
|
||||
gInfo->highlightPanel.setSize(1.0,0.8);
|
||||
gInfo->highlightPanel.setPosition(0.0,-0.2);
|
||||
gInfo->highlightPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X);
|
||||
gInfo->highlightPanel.setBlend(GL_ONE, GL_ONE);
|
||||
gInfo->highlightPanel.visible = false;
|
||||
|
||||
gInfo->panel.addChild(&gInfo->highlightPanel);
|
||||
|
||||
gInfo->labelPanel.setSize(spacing/2.0,(15.0/float(ClientSize.y)));
|
||||
gInfo->labelPanel.setPosition(midPos, -barHeight-(20.0/float(ClientSize.y)));
|
||||
gInfo->labelPanel.setText((*gi).getName());
|
||||
gInfo->labelPanel.setFill(GLPanel::GLPANEL_FILL_NONE);
|
||||
|
||||
bgPanel.addChild(&(gInfo->labelPanel));
|
||||
|
||||
gInfo->valuePanel.setSize(spacing/2.0,(15.0/float(ClientSize.y)));
|
||||
gInfo->valuePanel.setPosition(midPos, barHeight+(20.0/float(ClientSize.y)));
|
||||
gInfo->valuePanel.setText(std::to_string(int(gInfo->current)));
|
||||
gInfo->valuePanel.setFill(GLPanel::GLPANEL_FILL_NONE);
|
||||
|
||||
bgPanel.addChild(&(gInfo->valuePanel));
|
||||
|
||||
bgPanel.addChild(&(gInfo->panel));
|
||||
gainInfo.push_back(gInfo);
|
||||
i++;
|
||||
}
|
||||
|
||||
setThemeColors();
|
||||
}
|
||||
|
||||
void GainCanvas::setThemeColors() {
|
||||
std::vector<GainInfo *>::iterator gi;
|
||||
|
||||
RGBA4f c1, c2;
|
||||
|
||||
c1 = ThemeMgr::mgr.currentTheme->generalBackground;
|
||||
c2 = ThemeMgr::mgr.currentTheme->generalBackground * 0.5;
|
||||
|
||||
bgPanel.setFillColor(c1, c2);
|
||||
|
||||
for (gi = gainInfo.begin(); gi != gainInfo.end(); gi++) {
|
||||
GainInfo *gInfo = (*gi);
|
||||
|
||||
c1 = ThemeMgr::mgr.currentTheme->generalBackground;
|
||||
c2 = ThemeMgr::mgr.currentTheme->generalBackground * 0.5;
|
||||
c1.a = 1.0;
|
||||
c2.a = 1.0;
|
||||
gInfo->panel.setFillColor(c1, c2);
|
||||
|
||||
c1 = ThemeMgr::mgr.currentTheme->meterLevel * 0.5;
|
||||
c2 = ThemeMgr::mgr.currentTheme->meterLevel;
|
||||
c1.a = 1.0;
|
||||
c2.a = 1.0;
|
||||
gInfo->levelPanel.setFillColor(c1, c2);
|
||||
|
||||
c1 = RGBA4f(0.3,0.3,0.3,1.0);
|
||||
c2 = RGBA4f(0.65,0.65,0.65,1.0);;
|
||||
gInfo->highlightPanel.setFillColor(c1, c2);
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "wx/glcanvas.h"
|
||||
#include "wx/timer.h"
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "InteractiveCanvas.h"
|
||||
#include "MouseTracker.h"
|
||||
#include "GLPanel.h"
|
||||
#include "PrimaryGLContext.h"
|
||||
|
||||
#include "fftw3.h"
|
||||
#include "Timer.h"
|
||||
|
||||
class GainInfo {
|
||||
public:
|
||||
std::string name;
|
||||
float low, high, current;
|
||||
bool changed;
|
||||
GLPanel panel;
|
||||
GLPanel levelPanel;
|
||||
GLPanel highlightPanel;
|
||||
GLTextPanel labelPanel;
|
||||
GLTextPanel valuePanel;
|
||||
};
|
||||
|
||||
class GainCanvas: public InteractiveCanvas {
|
||||
public:
|
||||
GainCanvas(wxWindow *parent, int *attribList = NULL);
|
||||
~GainCanvas();
|
||||
|
||||
void setHelpTip(std::string tip);
|
||||
void updateGainUI();
|
||||
void setThemeColors();
|
||||
|
||||
private:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
void OnIdle(wxIdleEvent &event);
|
||||
|
||||
int GetPanelHit(CubicVR::vec2 &result);
|
||||
void SetLevel();
|
||||
|
||||
void OnShow(wxShowEvent& event);
|
||||
void OnMouseMoved(wxMouseEvent& event);
|
||||
void OnMouseDown(wxMouseEvent& event);
|
||||
void OnMouseWheelMoved(wxMouseEvent& event);
|
||||
void OnMouseReleased(wxMouseEvent& event);
|
||||
void OnMouseEnterWindow(wxMouseEvent& event);
|
||||
void OnMouseLeftWindow(wxMouseEvent& event);
|
||||
|
||||
PrimaryGLContext *glContext;
|
||||
std::string helpTip;
|
||||
std::vector<GainInfo *> gainInfo;
|
||||
GLPanel bgPanel;
|
||||
|
||||
float spacing, barWidth, startPos, barHeight, numGains;
|
||||
wxSize clientSize;
|
||||
//
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
InteractiveCanvas::InteractiveCanvas(wxWindow *parent, int *attribList) :
|
||||
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
||||
wxFULL_REPAINT_ON_RESIZE), parent(parent), shiftDown(false), altDown(false), ctrlDown(false), centerFreq(0), bandwidth(0), lastBandwidth(0), isView(
|
||||
wxFULL_REPAINT_ON_RESIZE| wxWANTS_CHARS), parent(parent), shiftDown(false), altDown(false), ctrlDown(false), centerFreq(0), bandwidth(0), lastBandwidth(0), isView(
|
||||
false) {
|
||||
mouseTracker.setTarget(this);
|
||||
}
|
||||
@@ -150,11 +150,11 @@ void InteractiveCanvas::OnMouseEnterWindow(wxMouseEvent& event) {
|
||||
}
|
||||
|
||||
void InteractiveCanvas::setStatusText(std::string statusText) {
|
||||
((wxFrame*) parent)->GetStatusBar()->SetStatusText(statusText);
|
||||
wxGetApp().getAppFrame()->GetStatusBar()->SetStatusText(statusText);
|
||||
}
|
||||
|
||||
void InteractiveCanvas::setStatusText(std::string statusText, int value) {
|
||||
((wxFrame*) parent)->GetStatusBar()->SetStatusText(
|
||||
wxGetApp().getAppFrame()->GetStatusBar()->SetStatusText(
|
||||
wxString::Format(statusText.c_str(), wxNumberFormatter::ToString((long) value, wxNumberFormatter::Style_WithThousandsSep)));
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ public:
|
||||
|
||||
long long getFrequencyAt(float x);
|
||||
|
||||
void setView(long long center_freq_in, int bandwidth_in);
|
||||
void disableView();
|
||||
virtual void setView(long long center_freq_in, int bandwidth_in);
|
||||
virtual void disableView();
|
||||
bool getViewState();
|
||||
|
||||
void setCenterFrequency(long long center_freq_in);
|
||||
|
||||
@@ -25,6 +25,8 @@ EVT_LEFT_UP(SpectrumCanvas::OnMouseReleased)
|
||||
EVT_ENTER_WINDOW(SpectrumCanvas::OnMouseEnterWindow)
|
||||
EVT_LEAVE_WINDOW(SpectrumCanvas::OnMouseLeftWindow)
|
||||
EVT_MOUSEWHEEL(SpectrumCanvas::OnMouseWheelMoved)
|
||||
EVT_RIGHT_DOWN(SpectrumCanvas::OnMouseRightDown)
|
||||
EVT_RIGHT_UP(SpectrumCanvas::OnMouseRightReleased)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
|
||||
@@ -32,10 +34,13 @@ SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
|
||||
|
||||
glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
|
||||
|
||||
mouseTracker.setVertDragLock(true);
|
||||
visualDataQueue.set_max_num_items(1);
|
||||
|
||||
SetCursor(wxCURSOR_SIZEWE);
|
||||
scaleFactor = 1.0;
|
||||
resetScaleFactor = false;
|
||||
scaleFactorEnabled = false;
|
||||
bwChange = 0.0;
|
||||
}
|
||||
|
||||
SpectrumCanvas::~SpectrumCanvas() {
|
||||
@@ -59,6 +64,15 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
}
|
||||
}
|
||||
|
||||
if (resetScaleFactor) {
|
||||
scaleFactor += (1.0-scaleFactor)*0.05;
|
||||
if (fabs(scaleFactor-1.0) < 0.01) {
|
||||
scaleFactor = 1.0;
|
||||
resetScaleFactor = false;
|
||||
}
|
||||
updateScaleFactor(scaleFactor);
|
||||
}
|
||||
|
||||
|
||||
glContext->SetCurrent(*this);
|
||||
initGLExtensions();
|
||||
@@ -137,6 +151,34 @@ bool SpectrumCanvas::getShowDb() {
|
||||
return spectrumPanel.getShowDb();
|
||||
}
|
||||
|
||||
void SpectrumCanvas::setView(long long center_freq_in, int bandwidth_in) {
|
||||
bwChange += bandwidth_in-bandwidth;
|
||||
#define BW_RESET_TH 400000
|
||||
if (bwChange > BW_RESET_TH || bwChange < -BW_RESET_TH) {
|
||||
resetScaleFactor = true;
|
||||
bwChange = 0;
|
||||
}
|
||||
InteractiveCanvas::setView(center_freq_in, bandwidth_in);
|
||||
}
|
||||
|
||||
void SpectrumCanvas::disableView() {
|
||||
InteractiveCanvas::disableView();
|
||||
}
|
||||
|
||||
void SpectrumCanvas::setScaleFactorEnabled(bool en) {
|
||||
scaleFactorEnabled = en;
|
||||
}
|
||||
|
||||
|
||||
void SpectrumCanvas::updateScaleFactor(float factor) {
|
||||
SpectrumVisualProcessor *sp = wxGetApp().getSpectrumProcessor();
|
||||
FFTVisualDataThread *wdt = wxGetApp().getAppFrame()->getWaterfallDataThread();
|
||||
SpectrumVisualProcessor *wp = wdt->getProcessor();
|
||||
|
||||
scaleFactor = factor;
|
||||
sp->setScaleFactor(factor);
|
||||
wp->setScaleFactor(factor);
|
||||
}
|
||||
|
||||
void SpectrumCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseMoved(event);
|
||||
@@ -146,12 +188,32 @@ void SpectrumCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
if (freqChange != 0) {
|
||||
moveCenterFrequency(freqChange);
|
||||
}
|
||||
}
|
||||
else if (scaleFactorEnabled && mouseTracker.mouseRightDown()) {
|
||||
|
||||
float yDelta = mouseTracker.getDeltaMouseY();
|
||||
|
||||
scaleFactor += yDelta*2.0;
|
||||
if (scaleFactor < 0.25) {
|
||||
scaleFactor = 0.25;
|
||||
}
|
||||
if (scaleFactor > 10.0) {
|
||||
scaleFactor = 10.0;
|
||||
}
|
||||
|
||||
resetScaleFactor = false;
|
||||
updateScaleFactor(scaleFactor);
|
||||
} else {
|
||||
setStatusText("Click and drag to adjust center frequency. 'B' to toggle decibels display.");
|
||||
if (scaleFactorEnabled) {
|
||||
setStatusText("Drag horizontal to adjust center frequency. Right-drag or SHIFT+UP/DOWN to adjust vertical scale; right-click to reset. 'B' to toggle decibels display.");
|
||||
} else {
|
||||
setStatusText("Displaying spectrum of active demodulator.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpectrumCanvas::OnMouseDown(wxMouseEvent& event) {
|
||||
mouseTracker.setVertDragLock(true);
|
||||
InteractiveCanvas::OnMouseDown(event);
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
}
|
||||
@@ -161,7 +223,8 @@ void SpectrumCanvas::OnMouseWheelMoved(wxMouseEvent& event) {
|
||||
}
|
||||
|
||||
void SpectrumCanvas::OnMouseReleased(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseReleased(event);
|
||||
mouseTracker.setVertDragLock(false);
|
||||
InteractiveCanvas::OnMouseReleased(event);
|
||||
SetCursor(wxCURSOR_SIZEWE);
|
||||
}
|
||||
|
||||
@@ -182,3 +245,17 @@ void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) {
|
||||
SpectrumVisualDataQueue *SpectrumCanvas::getVisualDataQueue() {
|
||||
return &visualDataQueue;
|
||||
}
|
||||
|
||||
void SpectrumCanvas::OnMouseRightDown(wxMouseEvent& event) {
|
||||
mouseTracker.setHorizDragLock(true);
|
||||
mouseTracker.OnMouseRightDown(event);
|
||||
scaleFactor = wxGetApp().getSpectrumProcessor()->getScaleFactor();
|
||||
}
|
||||
|
||||
void SpectrumCanvas::OnMouseRightReleased(wxMouseEvent& event) {
|
||||
mouseTracker.setHorizDragLock(false);
|
||||
if (!mouseTracker.getOriginDeltaMouseY()) {
|
||||
resetScaleFactor = true;
|
||||
}
|
||||
mouseTracker.OnMouseRightReleased(event);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ public:
|
||||
void setShowDb(bool showDb);
|
||||
bool getShowDb();
|
||||
|
||||
void setView(long long center_freq_in, int bandwidth_in);
|
||||
void disableView();
|
||||
|
||||
void setScaleFactorEnabled(bool en);
|
||||
|
||||
SpectrumVisualDataQueue *getVisualDataQueue();
|
||||
|
||||
private:
|
||||
@@ -35,10 +40,17 @@ private:
|
||||
void OnMouseReleased(wxMouseEvent& event);
|
||||
void OnMouseEnterWindow(wxMouseEvent& event);
|
||||
void OnMouseLeftWindow(wxMouseEvent& event);
|
||||
void OnMouseRightDown(wxMouseEvent& event);
|
||||
void OnMouseRightReleased(wxMouseEvent& event);
|
||||
|
||||
void updateScaleFactor(float factor);
|
||||
|
||||
PrimaryGLContext *glContext;
|
||||
WaterfallCanvas *waterfallCanvas;
|
||||
SpectrumPanel spectrumPanel;
|
||||
float scaleFactor;
|
||||
int bwChange;
|
||||
bool resetScaleFactor, scaleFactorEnabled;
|
||||
|
||||
SpectrumVisualDataQueue visualDataQueue;
|
||||
|
||||
|
||||
@@ -190,8 +190,8 @@ void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) {
|
||||
bw += amount;
|
||||
}
|
||||
|
||||
if (bw > wxGetApp().getSampleRate()) {
|
||||
bw = wxGetApp().getSampleRate();
|
||||
if (bw > CHANNELIZER_RATE_MAX) {
|
||||
bw = CHANNELIZER_RATE_MAX;
|
||||
}
|
||||
|
||||
wxGetApp().getDemodMgr().setLastBandwidth(bw);
|
||||
|
||||
@@ -43,6 +43,7 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
|
||||
lpsIndex = 0;
|
||||
preBuf = false;
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
scaleMove = 0;
|
||||
}
|
||||
|
||||
WaterfallCanvas::~WaterfallCanvas() {
|
||||
@@ -72,19 +73,16 @@ void WaterfallCanvas::attachSpectrumCanvas(SpectrumCanvas *canvas_in) {
|
||||
}
|
||||
|
||||
void WaterfallCanvas::processInputQueue() {
|
||||
if (!glContext) {
|
||||
return;
|
||||
}
|
||||
glContext->SetCurrent(*this);
|
||||
tex_update.lock();
|
||||
|
||||
gTimer.update();
|
||||
|
||||
double targetVis = 1.0 / (double)linesPerSecond;
|
||||
lpsIndex += gTimer.lastUpdateSeconds();
|
||||
|
||||
bool updated = false;
|
||||
if (linesPerSecond) {
|
||||
if (lpsIndex >= targetVis) {
|
||||
tex_update.lock();
|
||||
while (lpsIndex >= targetVis) {
|
||||
SpectrumVisualData *vData;
|
||||
if (!visualDataQueue.empty()) {
|
||||
@@ -94,21 +92,27 @@ void WaterfallCanvas::processInputQueue() {
|
||||
waterfallPanel.setPoints(vData->spectrum_points);
|
||||
waterfallPanel.step();
|
||||
vData->decRefCount();
|
||||
updated = true;
|
||||
}
|
||||
lpsIndex-=targetVis;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tex_update.unlock();
|
||||
}
|
||||
}}
|
||||
}
|
||||
if (updated) {
|
||||
wxClientDC(this);
|
||||
glContext->SetCurrent(*this);
|
||||
waterfallPanel.update();
|
||||
}
|
||||
tex_update.unlock();
|
||||
}
|
||||
|
||||
void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
tex_update.lock();
|
||||
wxPaintDC dc(this);
|
||||
|
||||
processInputQueue();
|
||||
|
||||
const wxSize ClientSize = GetClientSize();
|
||||
long double currentZoom = zoom;
|
||||
|
||||
@@ -120,9 +124,33 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
}
|
||||
}
|
||||
|
||||
if (scaleMove != 0) {
|
||||
SpectrumVisualProcessor *sp = wxGetApp().getSpectrumProcessor();
|
||||
FFTVisualDataThread *wdt = wxGetApp().getAppFrame()->getWaterfallDataThread();
|
||||
SpectrumVisualProcessor *wp = wdt->getProcessor();
|
||||
float factor = sp->getScaleFactor();
|
||||
|
||||
factor += scaleMove * 0.02;
|
||||
|
||||
if (factor < 0.25) {
|
||||
factor = 0.25;
|
||||
}
|
||||
if (factor > 10.0) {
|
||||
factor = 10.0;
|
||||
}
|
||||
|
||||
sp->setScaleFactor(factor);
|
||||
wp->setScaleFactor(factor);
|
||||
}
|
||||
|
||||
if (freqMove != 0.0) {
|
||||
long long newFreq = getCenterFrequency() + (long long)((long double)getBandwidth()*freqMove) * 0.01;
|
||||
|
||||
long long minFreq = bandwidth/2;
|
||||
if (newFreq < minFreq) {
|
||||
newFreq = minFreq;
|
||||
}
|
||||
|
||||
updateCenterFrequency(newFreq);
|
||||
|
||||
if (!freqMoving) {
|
||||
@@ -211,9 +239,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
glContext->BeginDraw(0,0,0);
|
||||
|
||||
waterfallPanel.calcTransform(CubicVR::mat4::identity());
|
||||
tex_update.lock();
|
||||
waterfallPanel.draw();
|
||||
tex_update.unlock();
|
||||
|
||||
std::vector<DemodulatorInstance *> &demods = wxGetApp().getDemodMgr().getDemodulators();
|
||||
|
||||
@@ -293,6 +319,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
glContext->EndDraw();
|
||||
|
||||
SwapBuffers();
|
||||
tex_update.unlock();
|
||||
}
|
||||
|
||||
void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) {
|
||||
@@ -304,14 +331,20 @@ void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) {
|
||||
case 'A':
|
||||
case WXK_UP:
|
||||
case WXK_NUMPAD_UP:
|
||||
scaleMove = 0.0;
|
||||
zoom = 1.0;
|
||||
mouseZoom = 0.95;
|
||||
if (mouseZoom != 1.0) {
|
||||
mouseZoom = 0.95;
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
case WXK_DOWN:
|
||||
case WXK_NUMPAD_DOWN:
|
||||
scaleMove = 0.0;
|
||||
zoom = 1.0;
|
||||
mouseZoom = 1.05;
|
||||
if (mouseZoom != 1.0) {
|
||||
mouseZoom = 1.05;
|
||||
}
|
||||
break;
|
||||
case WXK_LEFT:
|
||||
case WXK_NUMPAD_LEFT:
|
||||
@@ -334,14 +367,22 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
||||
case 'A':
|
||||
case WXK_UP:
|
||||
case WXK_NUMPAD_UP:
|
||||
mouseZoom = 1.0;
|
||||
zoom = 0.95;
|
||||
if (!shiftDown) {
|
||||
mouseZoom = 1.0;
|
||||
zoom = 0.95;
|
||||
} else {
|
||||
scaleMove = 1.0;
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
case WXK_DOWN:
|
||||
case WXK_NUMPAD_DOWN:
|
||||
mouseZoom = 1.0;
|
||||
zoom = 1.05;
|
||||
if (!shiftDown) {
|
||||
mouseZoom = 1.0;
|
||||
zoom = 1.05;
|
||||
} else {
|
||||
scaleMove = -1.0;
|
||||
}
|
||||
break;
|
||||
case WXK_RIGHT:
|
||||
case WXK_NUMPAD_RIGHT:
|
||||
@@ -398,7 +439,7 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
||||
return;
|
||||
}
|
||||
|
||||
long long minFreq = wxGetApp().getSampleRate()/2;
|
||||
long long minFreq = bandwidth/2;
|
||||
if (freq < minFreq) {
|
||||
freq = minFreq;
|
||||
}
|
||||
@@ -409,11 +450,9 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
||||
|
||||
}
|
||||
void WaterfallCanvas::OnIdle(wxIdleEvent &event) {
|
||||
processInputQueue();
|
||||
Refresh();
|
||||
event.RequestMore();
|
||||
if (visualDataQueue.size() > linesPerSecond) {
|
||||
processInputQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
@@ -435,8 +474,8 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
int currentBW = demod->getBandwidth();
|
||||
|
||||
currentBW = currentBW + bwDiff;
|
||||
if (currentBW > wxGetApp().getSampleRate()) {
|
||||
currentBW = wxGetApp().getSampleRate();
|
||||
if (currentBW > CHANNELIZER_RATE_MAX) {
|
||||
currentBW = CHANNELIZER_RATE_MAX;
|
||||
}
|
||||
if (currentBW < MIN_BANDWIDTH) {
|
||||
currentBW = MIN_BANDWIDTH;
|
||||
@@ -802,6 +841,7 @@ void WaterfallCanvas::updateCenterFrequency(long long freq) {
|
||||
}
|
||||
|
||||
void WaterfallCanvas::setLinesPerSecond(int lps) {
|
||||
tex_update.lock();
|
||||
linesPerSecond = lps;
|
||||
while (!visualDataQueue.empty()) {
|
||||
SpectrumVisualData *vData;
|
||||
@@ -811,7 +851,7 @@ void WaterfallCanvas::setLinesPerSecond(int lps) {
|
||||
vData->decRefCount();
|
||||
}
|
||||
}
|
||||
|
||||
tex_update.unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ private:
|
||||
long double freqMove;
|
||||
float hoverAlpha;
|
||||
int linesPerSecond;
|
||||
float scaleMove;
|
||||
|
||||
SpectrumVisualDataQueue visualDataQueue;
|
||||
Timer gTimer;
|
||||
|
||||
Reference in New Issue
Block a user