Update digital_lab

This commit is contained in:
Charles J. Cliffe
2015-11-10 20:53:55 -05:00
85 changed files with 4813 additions and 2202 deletions
-46
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
-70
View File
@@ -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();
-4
View File
@@ -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()
};
+8 -6
View File
@@ -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();
}
}
}
}
+4 -2
View File
@@ -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;
+5 -3
View File
@@ -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;
}
+8 -10
View File
@@ -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;
+267
View File
@@ -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;
}
}
+945
View File
@@ -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>
+34
View File
@@ -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;
};
+106
View File
@@ -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 ) );
}
+73
View File
@@ -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__
+68 -21
View File
@@ -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);
+7 -1
View File
@@ -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;
};
+29 -13
View File
@@ -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;
}
}
}
}
+2
View File
@@ -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;
};
+18 -15
View File
@@ -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();
}
}
+57 -2
View File
@@ -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();
}
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+298
View File
@@ -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;
}
+39
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+6 -2
View File
@@ -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)) {
+423
View File
@@ -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;
}
+112
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+305
View File
@@ -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();
}
+63
View File
@@ -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();
};
+3 -3
View File
@@ -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)));
}
+2 -2
View File
@@ -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);
+80 -3
View File
@@ -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);
}
+12
View File
@@ -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;
+2 -2
View File
@@ -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);
+64 -24
View File
@@ -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();
}
+1
View File
@@ -67,6 +67,7 @@ private:
long double freqMove;
float hoverAlpha;
int linesPerSecond;
float scaleMove;
SpectrumVisualDataQueue visualDataQueue;
Timer gTimer;