mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2025-08-02 21:52:25 -04:00
Merge pull request #164 from cjcliffe/soapysdr-pfbch-single
High-bandwidth and general optimizations
This commit is contained in:
commit
ad1668abee
@ -2,8 +2,8 @@ cmake_minimum_required (VERSION 2.8)
|
|||||||
|
|
||||||
SET(CUBICSDR_VERSION_MAJOR "0")
|
SET(CUBICSDR_VERSION_MAJOR "0")
|
||||||
SET(CUBICSDR_VERSION_MINOR "1")
|
SET(CUBICSDR_VERSION_MINOR "1")
|
||||||
SET(CUBICSDR_VERSION_PATCH "8")
|
SET(CUBICSDR_VERSION_PATCH "10")
|
||||||
SET(CUBICSDR_VERSION_REL "beta-issue64")
|
SET(CUBICSDR_VERSION_REL "alpha-pfbch-single-issue150")
|
||||||
SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}-${CUBICSDR_VERSION_REL}")
|
SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}-${CUBICSDR_VERSION_REL}")
|
||||||
|
|
||||||
SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}")
|
SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}")
|
||||||
|
33
README.md
33
README.md
@ -5,12 +5,12 @@ Cross-Platform Software-Defined Radio Application
|
|||||||
|
|
||||||
Utilizes:
|
Utilizes:
|
||||||
--------
|
--------
|
||||||
- liquid-dsp (http://liquidsdr.org/ https://github.com/jgaeddert/liquid-dsp)
|
- liquid-dsp (http://liquidsdr.org/ -- https://github.com/jgaeddert/liquid-dsp)
|
||||||
- FFTW (http://www.fftw.org/ https://github.com/FFTW/fftw3)
|
- SoapySDR (http://www.pothosware.com/ -- https://github.com/pothosware/SoapySDR)
|
||||||
- RtAudio (http://www.music.mcgill.ca/~gary/rtaudio/ http://github.com/thestk/rtaudio/)
|
- FFTW (http://www.fftw.org/ -- https://github.com/FFTW/fftw3)
|
||||||
- Osmocom RTLSDR (http://sdr.osmocom.org/trac/wiki/rtl-sdr)
|
- RtAudio (http://www.music.mcgill.ca/~gary/rtaudio/ -- http://github.com/thestk/rtaudio/)
|
||||||
- LodePNG (http://lodev.org/lodepng/)
|
- LodePNG (http://lodev.org/lodepng/)
|
||||||
- BMFont (http://www.angelcode.com/ http://www.angelcode.com/products/bmfont/)
|
- BMFont (http://www.angelcode.com/ -- http://www.angelcode.com/products/bmfont/)
|
||||||
- Bitstream Vera font (http://en.wikipedia.org/wiki/Bitstream_Vera)
|
- Bitstream Vera font (http://en.wikipedia.org/wiki/Bitstream_Vera)
|
||||||
- OpenGL (https://www.opengl.org/)
|
- OpenGL (https://www.opengl.org/)
|
||||||
- wxWidgets (https://www.wxwidgets.org/)
|
- wxWidgets (https://www.wxwidgets.org/)
|
||||||
@ -20,9 +20,19 @@ Features and Status:
|
|||||||
--------------------
|
--------------------
|
||||||
- Simple UI
|
- Simple UI
|
||||||
- Devices
|
- Devices
|
||||||
- [x] RTL-SDR
|
- [x] SoapySDR Device support (known working checked)
|
||||||
|
- [x] SoapySDRPlay for SDRPlay (Maintained by C.J.)
|
||||||
|
- [x] SoapyRTLSDR for RTL-SDR (Maintained by C.J.)
|
||||||
|
- [x] SoapyHackRF for HackRF
|
||||||
|
- [x] SoapyBladeRF for BladeRF
|
||||||
|
- [ ] SoapyUHD for Ettus USRP
|
||||||
|
- [x] SoapyRemote, use any SoapySDR Device via network (works on Pi)
|
||||||
|
- [x] SoapyOsmo for GrOsmoSDR devices
|
||||||
|
- [ ] OsmoSDR
|
||||||
|
- [ ] MiriSDR
|
||||||
|
- [ ] RFSpace
|
||||||
|
- [x] AirSpy
|
||||||
- [ ] rtl_tcp client
|
- [ ] rtl_tcp client
|
||||||
- [ ] gr-osmosdr
|
|
||||||
- Basic Features
|
- Basic Features
|
||||||
- [x] Device Selection
|
- [x] Device Selection
|
||||||
- [x] Bandwidth
|
- [x] Bandwidth
|
||||||
@ -30,11 +40,14 @@ Features and Status:
|
|||||||
- [x] Load/Save session
|
- [x] Load/Save session
|
||||||
- [x] Audio sample rate
|
- [x] Audio sample rate
|
||||||
- [x] Device PPM
|
- [x] Device PPM
|
||||||
|
- [x] Waterfall speed
|
||||||
|
- [x] Spectrum average speed
|
||||||
|
- [ ] Gain Controls
|
||||||
|
- [ ] Bookmarks
|
||||||
|
- [ ] History
|
||||||
- [ ] Default preferences
|
- [ ] Default preferences
|
||||||
- [ ] Audio defaults
|
- [ ] Audio defaults
|
||||||
- [x] Device defaults
|
- [x] Device defaults
|
||||||
- [ ] Bookmarks
|
|
||||||
- [ ] History
|
|
||||||
- [ ] Run as rtl_tcp server and visualize control
|
- [ ] Run as rtl_tcp server and visualize control
|
||||||
- Neat Visuals
|
- Neat Visuals
|
||||||
- [ ] 2D visuals
|
- [ ] 2D visuals
|
||||||
@ -68,8 +81,6 @@ Features and Status:
|
|||||||
- [x] Volume control
|
- [x] Volume control
|
||||||
- [x] Direct frequency input
|
- [x] Direct frequency input
|
||||||
- [x] Mute
|
- [x] Mute
|
||||||
- [x] Waterfall speed
|
|
||||||
- [ ] RTL-SDR Gain
|
|
||||||
- Basic Input Controls
|
- Basic Input Controls
|
||||||
- [x] Drag spectrum to change center frequency
|
- [x] Drag spectrum to change center frequency
|
||||||
- [x] Hold shift and click on waterfall to create a new demodulator
|
- [x] Hold shift and click on waterfall to create a new demodulator
|
||||||
|
@ -280,14 +280,14 @@ AppFrame::AppFrame() :
|
|||||||
sampleRateMenuItems[wxID_BANDWIDTH_2000M] = menu->AppendRadioItem(wxID_BANDWIDTH_2000M, "2.0M");
|
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_2048M] = menu->AppendRadioItem(wxID_BANDWIDTH_2048M, "2.048M");
|
||||||
sampleRateMenuItems[wxID_BANDWIDTH_2160M] = menu->AppendRadioItem(wxID_BANDWIDTH_2160M, "2.16M");
|
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_2400M] = menu->AppendRadioItem(wxID_BANDWIDTH_2400M, "2.4M");
|
||||||
sampleRateMenuItems[wxID_BANDWIDTH_2560M] = menu->AppendRadioItem(wxID_BANDWIDTH_2560M, "2.56M");
|
sampleRateMenuItems[wxID_BANDWIDTH_2500M] = menu->AppendRadioItem(wxID_BANDWIDTH_2500M, "2.5M");
|
||||||
sampleRateMenuItems[wxID_BANDWIDTH_2880M] = menu->AppendRadioItem(wxID_BANDWIDTH_2880M, "2.88M");
|
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_3000M] = menu->AppendRadioItem(wxID_BANDWIDTH_3000M, "3.0M");
|
||||||
sampleRateMenuItems[wxID_BANDWIDTH_3200M] = menu->AppendRadioItem(wxID_BANDWIDTH_3200M, "3.2M");
|
sampleRateMenuItems[wxID_BANDWIDTH_3200M] = menu->AppendRadioItem(wxID_BANDWIDTH_3200M, "3.2M");
|
||||||
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = menu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, "Manual Entry");
|
sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = menu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, "Manual Entry");
|
||||||
|
|
||||||
sampleRateMenuItems[wxID_BANDWIDTH_2400M]->Check(true);
|
sampleRateMenuItems[wxID_BANDWIDTH_2500M]->Check(true);
|
||||||
|
|
||||||
menuBar->Append(menu, wxT("&Input Bandwidth"));
|
menuBar->Append(menu, wxT("&Input Bandwidth"));
|
||||||
|
|
||||||
@ -574,11 +574,8 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
|
|||||||
case wxID_BANDWIDTH_2160M:
|
case wxID_BANDWIDTH_2160M:
|
||||||
wxGetApp().setSampleRate(2160000);
|
wxGetApp().setSampleRate(2160000);
|
||||||
break;
|
break;
|
||||||
case wxID_BANDWIDTH_2400M:
|
case wxID_BANDWIDTH_2500M:
|
||||||
wxGetApp().setSampleRate(2400000);
|
wxGetApp().setSampleRate(2500000);
|
||||||
break;
|
|
||||||
case wxID_BANDWIDTH_2560M:
|
|
||||||
wxGetApp().setSampleRate(2560000);
|
|
||||||
break;
|
break;
|
||||||
case wxID_BANDWIDTH_2880M:
|
case wxID_BANDWIDTH_2880M:
|
||||||
wxGetApp().setSampleRate(2880000);
|
wxGetApp().setSampleRate(2880000);
|
||||||
@ -671,8 +668,6 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||||
|
|
||||||
if (demod) {
|
if (demod) {
|
||||||
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
|
||||||
|
|
||||||
if (demod->isTracking()) {
|
if (demod->isTracking()) {
|
||||||
if (spectrumCanvas->getViewState()) {
|
if (spectrumCanvas->getViewState()) {
|
||||||
long long diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()) + (demod->getBandwidth()/2) + (demod->getBandwidth()/4);
|
long long diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()) + (demod->getBandwidth()/2) + (demod->getBandwidth()/4);
|
||||||
@ -816,7 +811,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0));
|
wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0));
|
||||||
|
|
||||||
wxGetApp().getScopeProcessor()->run();
|
wxGetApp().getScopeProcessor()->run();
|
||||||
wxGetApp().getSpectrumDistributor()->run();
|
// wxGetApp().getSpectrumDistributor()->run();
|
||||||
|
|
||||||
SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor();
|
SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor();
|
||||||
|
|
||||||
@ -857,6 +852,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
wproc->setView(waterfallCanvas->getViewState());
|
wproc->setView(waterfallCanvas->getViewState());
|
||||||
wproc->setBandwidth(waterfallCanvas->getBandwidth());
|
wproc->setBandwidth(waterfallCanvas->getBandwidth());
|
||||||
wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
|
wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
|
||||||
|
wxGetApp().getSDRPostThread()->setIQVisualRange(waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth());
|
||||||
|
|
||||||
// waterfallCanvas->processInputQueue();
|
// waterfallCanvas->processInputQueue();
|
||||||
// waterfallCanvas->Refresh();
|
// waterfallCanvas->Refresh();
|
||||||
|
@ -41,8 +41,8 @@
|
|||||||
#define wxID_BANDWIDTH_2000M 2156
|
#define wxID_BANDWIDTH_2000M 2156
|
||||||
#define wxID_BANDWIDTH_2048M 2157
|
#define wxID_BANDWIDTH_2048M 2157
|
||||||
#define wxID_BANDWIDTH_2160M 2158
|
#define wxID_BANDWIDTH_2160M 2158
|
||||||
#define wxID_BANDWIDTH_2400M 2159
|
//#define wxID_BANDWIDTH_2400M 2159
|
||||||
#define wxID_BANDWIDTH_2560M 2160
|
#define wxID_BANDWIDTH_2500M 2160
|
||||||
#define wxID_BANDWIDTH_2880M 2161
|
#define wxID_BANDWIDTH_2880M 2161
|
||||||
//#define wxID_BANDWIDTH_3000M 2162
|
//#define wxID_BANDWIDTH_3000M 2162
|
||||||
#define wxID_BANDWIDTH_3200M 2163
|
#define wxID_BANDWIDTH_3200M 2163
|
||||||
|
@ -59,22 +59,14 @@ bool CubicSDR::OnInit() {
|
|||||||
pipeIQVisualData = new DemodulatorThreadInputQueue();
|
pipeIQVisualData = new DemodulatorThreadInputQueue();
|
||||||
pipeIQVisualData->set_max_num_items(1);
|
pipeIQVisualData->set_max_num_items(1);
|
||||||
|
|
||||||
spectrumDistributor.setInput(pipeIQVisualData);
|
|
||||||
|
|
||||||
pipeDemodIQVisualData = new DemodulatorThreadInputQueue();
|
pipeDemodIQVisualData = new DemodulatorThreadInputQueue();
|
||||||
pipeDemodIQVisualData->set_max_num_items(1);
|
pipeDemodIQVisualData->set_max_num_items(1);
|
||||||
|
|
||||||
pipeSpectrumIQVisualData = new DemodulatorThreadInputQueue();
|
|
||||||
pipeSpectrumIQVisualData->set_max_num_items(1);
|
|
||||||
|
|
||||||
pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
|
pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
|
||||||
pipeWaterfallIQVisualData->set_max_num_items(128);
|
pipeWaterfallIQVisualData->set_max_num_items(128);
|
||||||
|
|
||||||
spectrumDistributor.attachOutput(pipeDemodIQVisualData);
|
|
||||||
spectrumDistributor.attachOutput(pipeSpectrumIQVisualData);
|
|
||||||
|
|
||||||
getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData);
|
getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData);
|
||||||
getSpectrumProcessor()->setInput(pipeSpectrumIQVisualData);
|
getSpectrumProcessor()->setInput(pipeIQVisualData);
|
||||||
|
|
||||||
pipeAudioVisualData = new DemodulatorThreadOutputQueue();
|
pipeAudioVisualData = new DemodulatorThreadOutputQueue();
|
||||||
pipeAudioVisualData->set_max_num_items(1);
|
pipeAudioVisualData->set_max_num_items(1);
|
||||||
@ -89,19 +81,17 @@ bool CubicSDR::OnInit() {
|
|||||||
sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData);
|
sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData);
|
||||||
|
|
||||||
sdrPostThread = new SDRPostThread();
|
sdrPostThread = new SDRPostThread();
|
||||||
// sdrPostThread->setNumVisSamples(BUF_SIZE);
|
|
||||||
sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData);
|
sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData);
|
||||||
sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
|
sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
|
||||||
sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
|
sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
|
||||||
|
sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData);
|
||||||
|
|
||||||
t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread);
|
t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread);
|
||||||
t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread);
|
t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread);
|
||||||
t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread);
|
t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread);
|
||||||
|
|
||||||
// t_SDR = new std::thread(&SDRThread::threadMain, sdrThread);
|
|
||||||
sdrEnum = new SDREnumerator();
|
sdrEnum = new SDREnumerator();
|
||||||
|
|
||||||
|
|
||||||
appframe = new AppFrame();
|
appframe = new AppFrame();
|
||||||
t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum);
|
t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum);
|
||||||
|
|
||||||
@ -371,11 +361,6 @@ SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() {
|
|||||||
return demodVisualThread->getProcessor();
|
return demodVisualThread->getProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
VisualDataDistributor<DemodulatorThreadIQData> *CubicSDR::getSpectrumDistributor() {
|
|
||||||
return &spectrumDistributor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() {
|
DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() {
|
||||||
return pipeAudioVisualData;
|
return pipeAudioVisualData;
|
||||||
}
|
}
|
||||||
@ -392,6 +377,10 @@ DemodulatorMgr &CubicSDR::getDemodMgr() {
|
|||||||
return demodMgr;
|
return demodMgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDRPostThread *CubicSDR::getSDRPostThread() {
|
||||||
|
return sdrPostThread;
|
||||||
|
}
|
||||||
|
|
||||||
void CubicSDR::bindDemodulator(DemodulatorInstance *demod) {
|
void CubicSDR::bindDemodulator(DemodulatorInstance *demod) {
|
||||||
if (!demod) {
|
if (!demod) {
|
||||||
return;
|
return;
|
||||||
|
@ -70,13 +70,15 @@ public:
|
|||||||
ScopeVisualProcessor *getScopeProcessor();
|
ScopeVisualProcessor *getScopeProcessor();
|
||||||
SpectrumVisualProcessor *getSpectrumProcessor();
|
SpectrumVisualProcessor *getSpectrumProcessor();
|
||||||
SpectrumVisualProcessor *getDemodSpectrumProcessor();
|
SpectrumVisualProcessor *getDemodSpectrumProcessor();
|
||||||
VisualDataDistributor<DemodulatorThreadIQData> *getSpectrumDistributor();
|
|
||||||
|
|
||||||
DemodulatorThreadOutputQueue* getAudioVisualQueue();
|
DemodulatorThreadOutputQueue* getAudioVisualQueue();
|
||||||
DemodulatorThreadInputQueue* getIQVisualQueue();
|
DemodulatorThreadInputQueue* getIQVisualQueue();
|
||||||
DemodulatorThreadInputQueue* getWaterfallVisualQueue();
|
DemodulatorThreadInputQueue* getWaterfallVisualQueue();
|
||||||
|
DemodulatorThreadInputQueue* getActiveDemodVisualQueue();
|
||||||
DemodulatorMgr &getDemodMgr();
|
DemodulatorMgr &getDemodMgr();
|
||||||
|
|
||||||
|
SDRPostThread *getSDRPostThread();
|
||||||
|
|
||||||
void bindDemodulator(DemodulatorInstance *demod);
|
void bindDemodulator(DemodulatorInstance *demod);
|
||||||
void removeDemodulator(DemodulatorInstance *demod);
|
void removeDemodulator(DemodulatorInstance *demod);
|
||||||
|
|
||||||
@ -122,18 +124,15 @@ private:
|
|||||||
SpectrumVisualDataThread *spectrumVisualThread;
|
SpectrumVisualDataThread *spectrumVisualThread;
|
||||||
SpectrumVisualDataThread *demodVisualThread;
|
SpectrumVisualDataThread *demodVisualThread;
|
||||||
|
|
||||||
// SDRThreadCommandQueue* pipeSDRCommand;
|
|
||||||
SDRThreadIQDataQueue* pipeSDRIQData;
|
SDRThreadIQDataQueue* pipeSDRIQData;
|
||||||
DemodulatorThreadInputQueue* pipeIQVisualData;
|
DemodulatorThreadInputQueue* pipeIQVisualData;
|
||||||
DemodulatorThreadOutputQueue* pipeAudioVisualData;
|
DemodulatorThreadOutputQueue* pipeAudioVisualData;
|
||||||
DemodulatorThreadInputQueue* pipeDemodIQVisualData;
|
DemodulatorThreadInputQueue* pipeDemodIQVisualData;
|
||||||
DemodulatorThreadInputQueue* pipeSpectrumIQVisualData;
|
|
||||||
DemodulatorThreadInputQueue* pipeWaterfallIQVisualData;
|
DemodulatorThreadInputQueue* pipeWaterfallIQVisualData;
|
||||||
|
DemodulatorThreadInputQueue* pipeActiveDemodIQVisualData;
|
||||||
|
|
||||||
ScopeVisualProcessor scopeProcessor;
|
ScopeVisualProcessor scopeProcessor;
|
||||||
|
|
||||||
VisualDataDistributor<DemodulatorThreadIQData> spectrumDistributor;
|
|
||||||
|
|
||||||
SDRDevicesDialog *deviceSelectorDialog;
|
SDRDevicesDialog *deviceSelectorDialog;
|
||||||
|
|
||||||
std::thread *t_SDR, *t_SDREnum, *t_PostSDR, *t_SpectrumVisual, *t_DemodVisual;
|
std::thread *t_SDR, *t_SDREnum, *t_PostSDR, *t_SpectrumVisual, *t_DemodVisual;
|
||||||
|
@ -27,10 +27,12 @@ const char filePathSeparator =
|
|||||||
|
|
||||||
#define BUF_SIZE (16384*6)
|
#define BUF_SIZE (16384*6)
|
||||||
|
|
||||||
#define DEFAULT_SAMPLE_RATE 2400000
|
#define DEFAULT_SAMPLE_RATE 2500000
|
||||||
#define DEFAULT_FFT_SIZE 2048
|
#define DEFAULT_FFT_SIZE 2048
|
||||||
|
|
||||||
#define DEFAULT_DEMOD_TYPE 1
|
#define DEFAULT_DEMOD_TYPE 1
|
||||||
#define DEFAULT_DEMOD_BW 200000
|
#define DEFAULT_DEMOD_BW 200000
|
||||||
|
|
||||||
#define DEFAULT_WATERFALL_LPS 30
|
#define DEFAULT_WATERFALL_LPS 30
|
||||||
|
|
||||||
|
#define CHANNELIZER_RATE_MAX 400000
|
@ -155,18 +155,20 @@ void DemodulatorPreThread::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
|
inp->decRefCount();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requested frequency is not center, shift it into the center!
|
// Requested frequency is not center, shift it into the center!
|
||||||
if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) {
|
if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) {
|
||||||
shiftFrequency = params.frequency - inp->frequency;
|
shiftFrequency = params.frequency - inp->frequency;
|
||||||
if (abs(shiftFrequency) <= (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) {
|
if (abs(shiftFrequency) <= (int) ((double) (inp->sampleRate / 2) * 1.5)) {
|
||||||
nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) wxGetApp().getSampleRate())));
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ void DemodulatorThread::run() {
|
|||||||
nco_crcf_pll_set_bandwidth(stereoPilot, 0.25f);
|
nco_crcf_pll_set_bandwidth(stereoPilot, 0.25f);
|
||||||
|
|
||||||
// half band filter used for side-band elimination
|
// 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
|
// Automatic IQ gain
|
||||||
iqAutoGain = agc_crcf_create();
|
iqAutoGain = agc_crcf_create();
|
||||||
@ -192,13 +192,13 @@ void DemodulatorThread::run() {
|
|||||||
switch (demodulatorType.load()) {
|
switch (demodulatorType.load()) {
|
||||||
case DEMOD_TYPE_LSB:
|
case DEMOD_TYPE_LSB:
|
||||||
for (int i = 0; i < bufSize; i++) { // Reject upper band
|
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]);
|
ampmodem_demodulate(demodAM, x, &demodOutputData[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DEMOD_TYPE_USB:
|
case DEMOD_TYPE_USB:
|
||||||
for (int i = 0; i < bufSize; i++) { // Reject lower band
|
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]);
|
ampmodem_demodulate(demodAM, y, &demodOutputData[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -487,7 +487,7 @@ void DemodulatorThread::run() {
|
|||||||
firhilbf_destroy(firStereoR2C);
|
firhilbf_destroy(firStereoR2C);
|
||||||
firhilbf_destroy(firStereoC2R);
|
firhilbf_destroy(firStereoC2R);
|
||||||
nco_crcf_destroy(stereoPilot);
|
nco_crcf_destroy(stereoPilot);
|
||||||
resamp2_cccf_destroy(ssbFilt);
|
resamp2_crcf_destroy(ssbFilt);
|
||||||
|
|
||||||
outputBuffers.purge();
|
outputBuffers.purge();
|
||||||
|
|
||||||
|
@ -91,15 +91,46 @@ void WaterfallPanel::step() {
|
|||||||
waterfall_slice[i] = (unsigned char) floor(wv * 255.0);
|
waterfall_slice[i] = (unsigned char) floor(wv * 255.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int newBufSize = (half_fft_size*lines_buffered+half_fft_size);
|
||||||
|
if (lineBuffer[j].size() < newBufSize) {
|
||||||
|
lineBuffer[j].resize(newBufSize);
|
||||||
|
rLineBuffer[j].resize(newBufSize);
|
||||||
|
}
|
||||||
|
memcpy(&(lineBuffer[j][half_fft_size*lines_buffered]), waterfall_slice, sizeof(unsigned char) * half_fft_size);
|
||||||
|
}
|
||||||
|
lines_buffered++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaterfallPanel::update() {
|
||||||
|
int half_fft_size = fft_size / 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < lines_buffered; i++) {
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
|
memcpy(&(rLineBuffer[j][i*half_fft_size]),
|
||||||
|
&(lineBuffer[j][((lines_buffered-1)*half_fft_size)-(i*half_fft_size)]), sizeof(unsigned char) * half_fft_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_ofs = 0;
|
||||||
|
while (lines_buffered) {
|
||||||
|
int run_lines = lines_buffered;
|
||||||
|
if (run_lines > waterfall_ofs[0]) {
|
||||||
|
run_lines = waterfall_ofs[0];
|
||||||
|
}
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
glBindTexture(GL_TEXTURE_2D, waterfall[j]);
|
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) {
|
if (waterfall_ofs[j] == 0) {
|
||||||
waterfall_ofs[j] = waterfall_lines;
|
waterfall_ofs[j] = waterfall_lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
waterfall_ofs[j]--;
|
|
||||||
}
|
}
|
||||||
|
run_ofs += run_lines*half_fft_size;
|
||||||
|
lines_buffered-=run_lines;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ public:
|
|||||||
void refreshTheme();
|
void refreshTheme();
|
||||||
void setPoints(std::vector<float> &points);
|
void setPoints(std::vector<float> &points);
|
||||||
void step();
|
void step();
|
||||||
|
void update();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void drawPanelContents();
|
void drawPanelContents();
|
||||||
@ -21,6 +22,9 @@ private:
|
|||||||
int fft_size;
|
int fft_size;
|
||||||
int waterfall_lines;
|
int waterfall_lines;
|
||||||
unsigned char *waterfall_slice;
|
unsigned char *waterfall_slice;
|
||||||
|
std::vector<unsigned char> lineBuffer[2];
|
||||||
|
std::vector<unsigned char> rLineBuffer[2];
|
||||||
|
int lines_buffered;
|
||||||
|
|
||||||
ColorTheme *activeTheme;
|
ColorTheme *activeTheme;
|
||||||
};
|
};
|
||||||
|
@ -124,7 +124,7 @@ std::vector<SDRDeviceInfo *> *SDREnumerator::enumerate_devices(std::string remot
|
|||||||
|
|
||||||
if (isRemote) {
|
if (isRemote) {
|
||||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Querying remote " + remoteAddr + " device #" + std::to_string(i));
|
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Querying remote " + remoteAddr + " device #" + std::to_string(i));
|
||||||
deviceArgs["remote"] = remoteAddr;
|
// deviceArgs["remote"] = remoteAddr;
|
||||||
if (deviceArgs.count("rtl") != 0) {
|
if (deviceArgs.count("rtl") != 0) {
|
||||||
streamArgs["remote:mtu"] = "8192";
|
streamArgs["remote:mtu"] = "8192";
|
||||||
streamArgs["remote:format"] = "CS8";
|
streamArgs["remote:format"] = "CS8";
|
||||||
|
@ -5,26 +5,23 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
SDRPostThread::SDRPostThread() : IOThread(),
|
SDRPostThread::SDRPostThread() : IOThread() {
|
||||||
iqDataInQueue(NULL), iqDataOutQueue(NULL), iqVisualQueue(NULL), dcFilter(NULL){
|
iqDataInQueue = NULL;
|
||||||
|
iqDataOutQueue = NULL;
|
||||||
|
iqVisualQueue = NULL;
|
||||||
|
|
||||||
swapIQ.store(false);
|
swapIQ.store(false);
|
||||||
|
numChannels = 0;
|
||||||
|
channelizer = NULL;
|
||||||
|
|
||||||
// create a lookup table
|
sampleRate = 0;
|
||||||
for (unsigned int i = 0; i <= 0xffff; i++) {
|
nRunDemods = 0;
|
||||||
liquid_float_complex tmp,tmp_swap;
|
|
||||||
# if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
visFrequency.store(0);
|
||||||
tmp_swap.imag = tmp.real = (float(i & 0xff) - 127.4f) * (1.0f/128.0f);
|
visBandwidth.store(0);
|
||||||
tmp_swap.real = tmp.imag = (float(i >> 8) - 127.4f) * (1.0f/128.0f);
|
|
||||||
_lut.push_back(tmp);
|
doRefresh.store(false);
|
||||||
_lut_swap.push_back(tmp_swap);
|
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005);
|
||||||
#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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDRPostThread::~SDRPostThread() {
|
SDRPostThread::~SDRPostThread() {
|
||||||
@ -33,6 +30,7 @@ SDRPostThread::~SDRPostThread() {
|
|||||||
void SDRPostThread::bindDemodulator(DemodulatorInstance *demod) {
|
void SDRPostThread::bindDemodulator(DemodulatorInstance *demod) {
|
||||||
busy_demod.lock();
|
busy_demod.lock();
|
||||||
demodulators.push_back(demod);
|
demodulators.push_back(demod);
|
||||||
|
doRefresh.store(true);
|
||||||
busy_demod.unlock();
|
busy_demod.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +44,7 @@ void SDRPostThread::removeDemodulator(DemodulatorInstance *demod) {
|
|||||||
|
|
||||||
if (i != demodulators.end()) {
|
if (i != demodulators.end()) {
|
||||||
demodulators.erase(i);
|
demodulators.erase(i);
|
||||||
|
doRefresh.store(true);
|
||||||
}
|
}
|
||||||
busy_demod.unlock();
|
busy_demod.unlock();
|
||||||
}
|
}
|
||||||
@ -58,44 +57,140 @@ bool SDRPostThread::getSwapIQ() {
|
|||||||
return this->swapIQ.load();
|
return this->swapIQ.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
void SDRPostThread::run() {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
pthread_t tID = pthread_self(); // ID of this thread
|
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
|
sched_param prio = {priority}; // scheduling priority of thread
|
||||||
pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005);
|
|
||||||
|
|
||||||
std::cout << "SDR post-processing thread started.." << std::endl;
|
std::cout << "SDR post-processing thread started.." << std::endl;
|
||||||
|
|
||||||
iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput");
|
iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput");
|
||||||
iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput");
|
iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput");
|
||||||
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput");
|
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput");
|
||||||
|
iqActiveDemodVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQActiveDemodVisualDataOutput");
|
||||||
ReBuffer<DemodulatorThreadIQData> buffers;
|
|
||||||
std::vector<liquid_float_complex> fpData;
|
|
||||||
std::vector<liquid_float_complex> dataOut;
|
|
||||||
|
|
||||||
iqDataInQueue->set_max_num_items(0);
|
iqDataInQueue->set_max_num_items(0);
|
||||||
|
|
||||||
|
std::vector<liquid_float_complex> dcBuf;
|
||||||
|
|
||||||
while (!terminated) {
|
while (!terminated) {
|
||||||
SDRThreadIQData *data_in;
|
SDRThreadIQData *data_in;
|
||||||
|
|
||||||
iqDataInQueue->pop(data_in);
|
iqDataInQueue->pop(data_in);
|
||||||
// std::lock_guard < std::mutex > lock(data_in->m_mutex);
|
// std::lock_guard < std::mutex > lock(data_in->m_mutex);
|
||||||
|
|
||||||
if (data_in && data_in->data.size()) {
|
if (data_in && data_in->data.size() && data_in->numChannels) {
|
||||||
int dataSize = data_in->data.size()/2;
|
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) {
|
||||||
|
numChannels = data_in->numChannels;
|
||||||
if (dataSize > dataOut.capacity()) {
|
sampleRate = data_in->sampleRate;
|
||||||
dataOut.reserve(dataSize);
|
initPFBChannelizer();
|
||||||
}
|
doRefresh.store(true);
|
||||||
if (dataSize != dataOut.size()) {
|
|
||||||
dataOut.resize(dataSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int dataSize = data_in->data.size();
|
||||||
|
int outSize = data_in->data.size();
|
||||||
|
|
||||||
|
if (outSize > dataOut.capacity()) {
|
||||||
|
dataOut.reserve(outSize);
|
||||||
|
}
|
||||||
|
if (outSize != dataOut.size()) {
|
||||||
|
dataOut.resize(outSize);
|
||||||
|
}
|
||||||
|
|
||||||
// if (swapIQ) {
|
// if (swapIQ) {
|
||||||
// for (int i = 0; i < dataSize; i++) {
|
// for (int i = 0; i < dataSize; i++) {
|
||||||
@ -107,120 +202,153 @@ void SDRPostThread::run() {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (data_in->dcCorrected) {
|
int activeVisChannel = -1;
|
||||||
for (int i = 0; i < dataSize; i++) {
|
|
||||||
dataOut[i].real = data_in->data[i*2];
|
// if (visBandwidth.load() && visBandwidth.load() < (chanBw/2)) {
|
||||||
dataOut[i].imag = data_in->data[i*2+1];
|
// activeVisChannel = getChannelAt(visFrequency);
|
||||||
}
|
// }
|
||||||
} else {
|
|
||||||
if (dataSize > fpData.capacity()) {
|
if (iqDataOutQueue != NULL && !iqDataOutQueue->full() && activeVisChannel < 0) {
|
||||||
fpData.reserve(dataSize);
|
DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer();
|
||||||
}
|
|
||||||
if (dataSize != fpData.size()) {
|
bool doVis = false;
|
||||||
fpData.resize(dataSize);
|
|
||||||
|
if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
|
||||||
|
doVis = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < dataSize; i++) {
|
iqDataOut->setRefCount(1 + (doVis?1:0));
|
||||||
fpData[i].real = data_in->data[i*2];
|
|
||||||
fpData[i].imag = data_in->data[i*2+1];
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
busy_demod.lock();
|
busy_demod.lock();
|
||||||
|
|
||||||
int activeDemods = 0;
|
if (frequency != data_in->frequency) {
|
||||||
bool pushedData = false;
|
frequency = data_in->frequency;
|
||||||
|
doRefresh.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (demodulators.size() || iqDataOutQueue != NULL) {
|
if (doRefresh.load()) {
|
||||||
std::vector<DemodulatorInstance *>::iterator demod_i;
|
updateActiveDemodulators();
|
||||||
for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) {
|
updateChannels();
|
||||||
DemodulatorInstance *demod = *demod_i;
|
doRefresh.store(false);
|
||||||
if (demod->getFrequency() != data_in->frequency
|
}
|
||||||
&& abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
|
||||||
|
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||||
|
int activeDemodChannel = -1;
|
||||||
|
|
||||||
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
activeDemods++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iqDataOutQueue != NULL) {
|
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
||||||
activeDemods++;
|
demodDataOut->setRefCount(demodChannelActive[i] + doVis + doDemodVis);
|
||||||
}
|
demodDataOut->frequency = chanCenters[i];
|
||||||
|
demodDataOut->sampleRate = chanBw;
|
||||||
|
|
||||||
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
// Calculate channel buffer size
|
||||||
|
int chanDataSize = (outSize/numChannels);
|
||||||
|
|
||||||
// std::lock_guard < std::mutex > lock(demodDataOut->m_mutex);
|
if (demodDataOut->data.size() != chanDataSize) {
|
||||||
demodDataOut->frequency = data_in->frequency;
|
if (demodDataOut->data.capacity() < chanDataSize) {
|
||||||
demodDataOut->sampleRate = data_in->sampleRate;
|
demodDataOut->data.reserve(chanDataSize);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
demodDataOut->data.resize(chanDataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!demod->isActive()) {
|
int idx = i;
|
||||||
continue;
|
|
||||||
}
|
// Extra channel wraps lower side band of lowest channel
|
||||||
if (demod->isFollow()) {
|
// to fix frequency gap on upper side of spectrum
|
||||||
demod->setFollow(false);
|
if (i == numChannels) {
|
||||||
|
idx = (numChannels/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
demodQueue->push(demodDataOut);
|
// prepare channel data buffer
|
||||||
pushedData = true;
|
if (i == 0) { // Channel 0 requires DC correction
|
||||||
}
|
if (dcBuf.size() != chanDataSize) {
|
||||||
|
dcBuf.resize(chanDataSize);
|
||||||
if (iqDataOutQueue != NULL) {
|
}
|
||||||
if (!iqDataOutQueue->full()) {
|
for (int j = 0; j < chanDataSize; j++) {
|
||||||
iqDataOutQueue->push(demodDataOut);
|
idx += numChannels;
|
||||||
pushedData = true;
|
dcBuf[j] = dataOut[idx];
|
||||||
|
}
|
||||||
|
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]);
|
||||||
} else {
|
} else {
|
||||||
demodDataOut->decRefCount();
|
for (int j = 0; j < chanDataSize; j++) {
|
||||||
|
idx += numChannels;
|
||||||
|
demodDataOut->data[j] = dataOut[idx];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!pushedData && iqDataOutQueue == NULL) {
|
// if (doVis) {
|
||||||
demodDataOut->setRefCount(0);
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,19 +21,41 @@ public:
|
|||||||
void run();
|
void run();
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
|
void setIQVisualRange(long long frequency, int bandwidth);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SDRThreadIQDataQueue *iqDataInQueue;
|
SDRThreadIQDataQueue *iqDataInQueue;
|
||||||
DemodulatorThreadInputQueue *iqDataOutQueue;
|
DemodulatorThreadInputQueue *iqDataOutQueue;
|
||||||
DemodulatorThreadInputQueue *iqVisualQueue;
|
DemodulatorThreadInputQueue *iqVisualQueue;
|
||||||
|
DemodulatorThreadInputQueue *iqActiveDemodVisualQueue;
|
||||||
|
|
||||||
std::mutex busy_demod;
|
std::mutex busy_demod;
|
||||||
std::vector<DemodulatorInstance *> demodulators;
|
std::vector<DemodulatorInstance *> demodulators;
|
||||||
iirfilt_crcf dcFilter;
|
|
||||||
std::atomic_bool swapIQ;
|
std::atomic_bool swapIQ;
|
||||||
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<liquid_float_complex> _lut;
|
void initPFBChannelizer();
|
||||||
std::vector<liquid_float_complex> _lut_swap;
|
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;
|
||||||
};
|
};
|
||||||
|
@ -28,6 +28,9 @@ SDRThread::SDRThread() : IOThread() {
|
|||||||
|
|
||||||
hasPPM.store(false);
|
hasPPM.store(false);
|
||||||
hasHardwareDC.store(false);
|
hasHardwareDC.store(false);
|
||||||
|
numChannels.store(8);
|
||||||
|
|
||||||
|
// dcFilter = iirfilt_crcf_create_dc_blocker(0.0005);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDRThread::~SDRThread() {
|
SDRThread::~SDRThread() {
|
||||||
@ -77,7 +80,7 @@ void SDRThread::init() {
|
|||||||
}
|
}
|
||||||
if (chan->hasHardwareDC()) {
|
if (chan->hasHardwareDC()) {
|
||||||
hasHardwareDC.store(true);
|
hasHardwareDC.store(true);
|
||||||
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Found hardware DC offset correction support, internal disabled."));
|
// wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Found hardware DC offset correction support, internal disabled."));
|
||||||
device->setDCOffsetMode(SOAPY_SDR_RX, chan->getChannel(), true);
|
device->setDCOffsetMode(SOAPY_SDR_RX, chan->getChannel(), true);
|
||||||
} else {
|
} else {
|
||||||
hasHardwareDC.store(false);
|
hasHardwareDC.store(false);
|
||||||
@ -85,7 +88,9 @@ void SDRThread::init() {
|
|||||||
|
|
||||||
device->setGainMode(SOAPY_SDR_RX,0,true);
|
device->setGainMode(SOAPY_SDR_RX,0,true);
|
||||||
|
|
||||||
numElems = getOptimalElementCount(sampleRate.load(), 60);
|
numChannels.store(getOptimalChannelCount(sampleRate.load()));
|
||||||
|
numElems.store(getOptimalElementCount(sampleRate.load(), 30));
|
||||||
|
inpBuffer.data.resize(numElems.load());
|
||||||
|
|
||||||
buffs[0] = malloc(numElems * 2 * sizeof(float));
|
buffs[0] = malloc(numElems * 2 * sizeof(float));
|
||||||
}
|
}
|
||||||
@ -101,30 +106,36 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) {
|
|||||||
int flags;
|
int flags;
|
||||||
long long timeNs;
|
long long timeNs;
|
||||||
|
|
||||||
SDRThreadIQData *dataOut = buffers.getBuffer();
|
|
||||||
if (dataOut->data.size() != numElems * 2) {
|
|
||||||
dataOut->data.resize(numElems * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int n_read = 0;
|
int n_read = 0;
|
||||||
while (n_read != numElems) {
|
while (n_read != numElems && !terminated) {
|
||||||
int n_stream_read = device->readStream(stream, buffs, numElems-n_read, flags, timeNs);
|
int n_stream_read = device->readStream(stream, buffs, numElems-n_read, flags, timeNs);
|
||||||
if (n_stream_read > 0) {
|
if (n_stream_read > 0) {
|
||||||
memcpy(&dataOut->data[n_read * 2], buffs[0], n_stream_read * sizeof(float) * 2);
|
memcpy(&inpBuffer.data[n_read], buffs[0], n_stream_read * sizeof(float) * 2);
|
||||||
n_read += n_stream_read;
|
n_read += n_stream_read;
|
||||||
} else {
|
} else {
|
||||||
dataOut->data.resize(n_read);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout << n_read << std::endl;
|
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]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
if (n_read > 0) {
|
|
||||||
dataOut->setRefCount(1);
|
dataOut->setRefCount(1);
|
||||||
dataOut->frequency = frequency;
|
dataOut->frequency = frequency.load();
|
||||||
dataOut->sampleRate = sampleRate.load();
|
dataOut->sampleRate = sampleRate.load();
|
||||||
dataOut->dcCorrected = hasHardwareDC;
|
dataOut->dcCorrected = hasHardwareDC.load();
|
||||||
|
dataOut->numChannels = numChannels.load();
|
||||||
|
|
||||||
iqDataOutQueue->push(dataOut);
|
iqDataOutQueue->push(dataOut);
|
||||||
}
|
}
|
||||||
@ -148,7 +159,9 @@ void SDRThread::readLoop() {
|
|||||||
if (rate_changed.load()) {
|
if (rate_changed.load()) {
|
||||||
device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load());
|
device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load());
|
||||||
sampleRate.store(device->getSampleRate(SOAPY_SDR_RX,0));
|
sampleRate.store(device->getSampleRate(SOAPY_SDR_RX,0));
|
||||||
|
numChannels.store(getOptimalChannelCount(sampleRate.load()));
|
||||||
numElems.store(getOptimalElementCount(sampleRate.load(), 60));
|
numElems.store(getOptimalElementCount(sampleRate.load(), 60));
|
||||||
|
inpBuffer.data.resize(numElems.load());
|
||||||
free(buffs[0]);
|
free(buffs[0]);
|
||||||
buffs[0] = malloc(numElems.load() * 2 * sizeof(float));
|
buffs[0] = malloc(numElems.load() * 2 * sizeof(float));
|
||||||
rate_changed.store(false);
|
rate_changed.store(false);
|
||||||
@ -174,7 +187,7 @@ void SDRThread::readLoop() {
|
|||||||
void SDRThread::run() {
|
void SDRThread::run() {
|
||||||
//#ifdef __APPLE__
|
//#ifdef __APPLE__
|
||||||
// pthread_t tID = pthread_self(); // ID of this thread
|
// 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
|
// sched_param prio = { priority }; // scheduling priority of thread
|
||||||
// pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
// pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
||||||
//#endif
|
//#endif
|
||||||
@ -214,11 +227,31 @@ void SDRThread::setDevice(SDRDeviceInfo *dev) {
|
|||||||
|
|
||||||
int SDRThread::getOptimalElementCount(long long sampleRate, int fps) {
|
int SDRThread::getOptimalElementCount(long long sampleRate, int fps) {
|
||||||
int elemCount = (int)floor((double)sampleRate/(double)fps);
|
int elemCount = (int)floor((double)sampleRate/(double)fps);
|
||||||
elemCount = int(ceil((double)elemCount/512.0)*512.0);
|
int nch = numChannels.load();
|
||||||
std::cout << "Calculated optimal element count of " << elemCount << std::endl;
|
elemCount = int(ceil((double)elemCount/(double)nch))*nch;
|
||||||
|
std::cout << "Calculated optimal " << numChannels.load() << " channel element count of " << elemCount << std::endl;
|
||||||
return elemCount;
|
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) {
|
void SDRThread::setFrequency(long long freq) {
|
||||||
if (freq < sampleRate.load() / 2) {
|
if (freq < sampleRate.load() / 2) {
|
||||||
freq = sampleRate.load() / 2;
|
freq = sampleRate.load() / 2;
|
||||||
|
@ -18,10 +18,11 @@ public:
|
|||||||
long long frequency;
|
long long frequency;
|
||||||
long long sampleRate;
|
long long sampleRate;
|
||||||
bool dcCorrected;
|
bool dcCorrected;
|
||||||
std::vector<float> data;
|
int numChannels;
|
||||||
|
std::vector<liquid_float_complex> data;
|
||||||
|
|
||||||
SDRThreadIQData() :
|
SDRThreadIQData() :
|
||||||
frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), dcCorrected(true) {
|
frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), dcCorrected(true), numChannels(0) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ public:
|
|||||||
SDRDeviceInfo *getDevice();
|
SDRDeviceInfo *getDevice();
|
||||||
void setDevice(SDRDeviceInfo *dev);
|
void setDevice(SDRDeviceInfo *dev);
|
||||||
int getOptimalElementCount(long long sampleRate, int fps);
|
int getOptimalElementCount(long long sampleRate, int fps);
|
||||||
|
int getOptimalChannelCount(long long sampleRate);
|
||||||
|
|
||||||
void setFrequency(long long freq);
|
void setFrequency(long long freq);
|
||||||
long long getFrequency();
|
long long getFrequency();
|
||||||
@ -75,13 +77,13 @@ protected:
|
|||||||
SoapySDR::Device *device;
|
SoapySDR::Device *device;
|
||||||
void *buffs[1];
|
void *buffs[1];
|
||||||
ReBuffer<SDRThreadIQData> buffers;
|
ReBuffer<SDRThreadIQData> buffers;
|
||||||
|
SDRThreadIQData inpBuffer;
|
||||||
std::atomic<DeviceConfig *> deviceConfig;
|
std::atomic<DeviceConfig *> deviceConfig;
|
||||||
std::atomic<SDRDeviceInfo *> deviceInfo;
|
std::atomic<SDRDeviceInfo *> deviceInfo;
|
||||||
|
|
||||||
std::atomic<uint32_t> sampleRate;
|
std::atomic<uint32_t> sampleRate;
|
||||||
std::atomic_llong frequency, offset;
|
std::atomic_llong frequency, offset;
|
||||||
std::atomic_int ppm, direct_sampling_mode, numElems;
|
std::atomic_int ppm, direct_sampling_mode, numElems, numChannels;
|
||||||
std::atomic_bool hasPPM, hasHardwareDC;
|
std::atomic_bool hasPPM, hasHardwareDC;
|
||||||
|
|
||||||
std::atomic_bool rate_changed, freq_changed, offset_changed,
|
std::atomic_bool rate_changed, freq_changed, offset_changed,
|
||||||
|
@ -190,8 +190,8 @@ void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) {
|
|||||||
bw += amount;
|
bw += amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bw > wxGetApp().getSampleRate()) {
|
if (bw > CHANNELIZER_RATE_MAX) {
|
||||||
bw = wxGetApp().getSampleRate();
|
bw = CHANNELIZER_RATE_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxGetApp().getDemodMgr().setLastBandwidth(bw);
|
wxGetApp().getDemodMgr().setLastBandwidth(bw);
|
||||||
|
@ -100,6 +100,7 @@ void WaterfallCanvas::processInputQueue() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
waterfallPanel.update();
|
||||||
tex_update.unlock();
|
tex_update.unlock();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -435,8 +436,8 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) {
|
|||||||
int currentBW = demod->getBandwidth();
|
int currentBW = demod->getBandwidth();
|
||||||
|
|
||||||
currentBW = currentBW + bwDiff;
|
currentBW = currentBW + bwDiff;
|
||||||
if (currentBW > wxGetApp().getSampleRate()) {
|
if (currentBW > CHANNELIZER_RATE_MAX) {
|
||||||
currentBW = wxGetApp().getSampleRate();
|
currentBW = CHANNELIZER_RATE_MAX;
|
||||||
}
|
}
|
||||||
if (currentBW < MIN_BANDWIDTH) {
|
if (currentBW < MIN_BANDWIDTH) {
|
||||||
currentBW = MIN_BANDWIDTH;
|
currentBW = MIN_BANDWIDTH;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user