mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-24 04:38:38 -05:00
commit
eaaf6cd02e
@ -2,8 +2,8 @@ cmake_minimum_required (VERSION 2.8)
|
||||
|
||||
SET(CUBICSDR_VERSION_MAJOR "0")
|
||||
SET(CUBICSDR_VERSION_MINOR "1")
|
||||
SET(CUBICSDR_VERSION_PATCH "3")
|
||||
SET(CUBICSDR_VERSION_REL "beta")
|
||||
SET(CUBICSDR_VERSION_PATCH "6")
|
||||
SET(CUBICSDR_VERSION_REL "beta-issue140")
|
||||
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}")
|
||||
@ -264,6 +264,7 @@ SET (cubicsdr_sources
|
||||
src/process/ScopeVisualProcessor.cpp
|
||||
src/process/SpectrumVisualProcessor.cpp
|
||||
src/process/FFTVisualDataThread.cpp
|
||||
src/process/FFTDataDistributor.cpp
|
||||
src/process/SpectrumVisualDataThread.cpp
|
||||
src/ui/GLPanel.cpp
|
||||
external/rtaudio/RtAudio.cpp
|
||||
@ -319,6 +320,7 @@ SET (cubicsdr_headers
|
||||
src/process/ScopeVisualProcessor.h
|
||||
src/process/SpectrumVisualProcessor.h
|
||||
src/process/FFTVisualDataThread.h
|
||||
src/process/FFTDataDistributor.h
|
||||
src/process/SpectrumVisualDataThread.h
|
||||
src/ui/GLPanel.h
|
||||
src/ui/UITestCanvas.cpp
|
||||
@ -423,6 +425,13 @@ IF (MSVC)
|
||||
set(CMAKE_CREATE_WIN32_EXE "/SUBSYSTEM:WINDOWS /ENTRY:\"mainCRTStartup\"")
|
||||
ENDIF(MSVC)
|
||||
|
||||
IF (APPLE)
|
||||
ADD_DEFINITIONS(
|
||||
-DHAVE_TYPE_TRAITS=1
|
||||
-mmacosx-version-min=10.9
|
||||
)
|
||||
ENDIF(APPLE)
|
||||
|
||||
IF (APPLE AND BUNDLE_APP)
|
||||
PROJECT(CubicSDR)
|
||||
SET(MACOSX_BUNDLE_BUNDLE_NAME CubicSDR)
|
||||
@ -431,8 +440,6 @@ IF (APPLE AND BUNDLE_APP)
|
||||
-std=c++0x
|
||||
-pthread
|
||||
-D_OSX_APP_
|
||||
-DHAVE_TYPE_TRAITS=1
|
||||
-mmacosx-version-min=10.9
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(CubicSDR
|
||||
|
@ -42,7 +42,7 @@ Features and Status:
|
||||
- [x] Spectrum
|
||||
- [x] Waterfall
|
||||
- [x] Add faint grid for sense of scale
|
||||
- [ ] Audio Spectrum
|
||||
- [x] Audio Spectrum
|
||||
- [ ] X/Y Scope
|
||||
- [ ] Indicate outer spectrum edges when zoomed
|
||||
- [ ] 3D visuals
|
||||
@ -67,8 +67,8 @@ Features and Status:
|
||||
- [x] Display audio output selection
|
||||
- [x] Volume control
|
||||
- [x] Direct frequency input
|
||||
- [ ] Mute
|
||||
- [ ] Waterfall speed
|
||||
- [x] Mute
|
||||
- [x] Waterfall speed
|
||||
- [ ] RTL-SDR Gain
|
||||
- Basic Input Controls
|
||||
- [x] Drag spectrum to change center frequency
|
||||
@ -123,7 +123,7 @@ Features and Status:
|
||||
- Optimization
|
||||
- [x] Eliminate large waterfall texture uploads
|
||||
- [ ] Update visuals to OpenGL 3.x / OpenGL ES
|
||||
- [ ] Resolve constant refresh on visuals that don't change often
|
||||
- [x] Resolve constant refresh on visuals that don't change often
|
||||
- [ ] Resolve all driver/platform vertical sync issues
|
||||
- [ ] Group and divide IQ data distribution workload instead of 100% distribution per instance
|
||||
|
||||
|
BIN
external/msvc/x86/libgcc_s_dw2-1.dll
vendored
BIN
external/msvc/x86/libgcc_s_dw2-1.dll
vendored
Binary file not shown.
@ -49,7 +49,7 @@ AppFrame::AppFrame() :
|
||||
wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
int attribList[] = { WX_GL_RGBA, WX_GL_STENCIL_SIZE, 8, WX_GL_BUFFER_SIZE, 24, WX_GL_DOUBLEBUFFER, 0 };
|
||||
int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
|
||||
|
||||
demodModeSelector = new ModeSelectorCanvas(this, attribList);
|
||||
demodModeSelector->addChoice(DEMOD_TYPE_FM, "FM");
|
||||
@ -104,6 +104,7 @@ AppFrame::AppFrame() :
|
||||
demodSpectrumCanvas->attachWaterfallCanvas(demodWaterfallCanvas);
|
||||
demodVisuals->Add(demodWaterfallCanvas, 6, wxEXPAND | wxALL, 0);
|
||||
wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodWaterfallCanvas->getVisualDataQueue());
|
||||
demodWaterfallCanvas->getVisualDataQueue()->set_max_num_items(3);
|
||||
|
||||
demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0);
|
||||
|
||||
@ -148,6 +149,7 @@ AppFrame::AppFrame() :
|
||||
demodMuteButton->setHighlightColor(RGBA4f(0.8,0.2,0.2));
|
||||
demodMuteButton->setHelpTip("Demodulator Mute Toggle");
|
||||
demodMuteButton->setToggleMode(true);
|
||||
demodMuteButton->setSelection(-1);
|
||||
|
||||
demodGainTray->Add(demodMuteButton, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
@ -422,6 +424,7 @@ AppFrame::AppFrame() :
|
||||
|
||||
waterfallSpeedMeter->setLevel(sqrt(wflps));
|
||||
waterfallDataThread->setLinesPerSecond(wflps);
|
||||
waterfallCanvas->setLinesPerSecond(wflps);
|
||||
|
||||
ThemeMgr::mgr.setTheme(wxGetApp().getConfig()->getTheme());
|
||||
|
||||
@ -534,6 +537,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
|
||||
spectrumCanvas->setBandwidth(wxGetApp().getSampleRate());
|
||||
spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
|
||||
waterfallDataThread->setLinesPerSecond(DEFAULT_WATERFALL_LPS);
|
||||
waterfallCanvas->setLinesPerSecond(DEFAULT_WATERFALL_LPS);
|
||||
waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS));
|
||||
wxGetApp().getSpectrumProcessor()->setFFTAverageRate(0.65);
|
||||
spectrumAvgMeter->setLevel(0.65);
|
||||
@ -893,6 +897,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
float val = waterfallSpeedMeter->getInputValue();
|
||||
waterfallSpeedMeter->setLevel(val);
|
||||
waterfallDataThread->setLinesPerSecond((int)ceil(val*val));
|
||||
waterfallCanvas->setLinesPerSecond((int)ceil(val*val));
|
||||
GetStatusBar()->SetStatusText(wxString::Format(wxT("Waterfall max speed changed to %d lines per second."),(int)ceil(val*val)));
|
||||
}
|
||||
|
||||
@ -900,8 +905,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
wproc->setBandwidth(waterfallCanvas->getBandwidth());
|
||||
wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
|
||||
|
||||
waterfallCanvas->processInputQueue();
|
||||
demodWaterfallCanvas->processInputQueue();
|
||||
// waterfallCanvas->processInputQueue();
|
||||
// waterfallCanvas->Refresh();
|
||||
// demodWaterfallCanvas->processInputQueue();
|
||||
// demodWaterfallCanvas->Refresh();
|
||||
|
||||
if (!this->IsActive()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(25));
|
||||
|
@ -89,7 +89,7 @@ bool CubicSDR::OnInit() {
|
||||
pipeSpectrumIQVisualData->set_max_num_items(1);
|
||||
|
||||
pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
|
||||
pipeWaterfallIQVisualData->set_max_num_items(DEFAULT_WATERFALL_LPS);
|
||||
pipeWaterfallIQVisualData->set_max_num_items(128);
|
||||
|
||||
spectrumDistributor.attachOutput(pipeDemodIQVisualData);
|
||||
spectrumDistributor.attachOutput(pipeSpectrumIQVisualData);
|
||||
@ -113,7 +113,7 @@ bool CubicSDR::OnInit() {
|
||||
sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData);
|
||||
|
||||
sdrPostThread = new SDRPostThread();
|
||||
sdrPostThread->setNumVisSamples(BUF_SIZE);
|
||||
// sdrPostThread->setNumVisSamples(BUF_SIZE);
|
||||
sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData);
|
||||
sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
|
||||
sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
|
||||
@ -321,7 +321,7 @@ SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() {
|
||||
return demodVisualThread->getProcessor();
|
||||
}
|
||||
|
||||
VisualDataDistributor<DemodulatorThreadIQData> *CubicSDR::getSpectrumDistributor() {
|
||||
VisualDataReDistributor<DemodulatorThreadIQData> *CubicSDR::getSpectrumDistributor() {
|
||||
return &spectrumDistributor;
|
||||
}
|
||||
|
||||
@ -450,6 +450,10 @@ void CubicSDR::showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetM
|
||||
fdialog.ShowModal();
|
||||
}
|
||||
|
||||
AppFrame *CubicSDR::getAppFrame() {
|
||||
return appframe;
|
||||
}
|
||||
|
||||
void CubicSDR::setFrequencySnap(int snap) {
|
||||
if (snap > 1000000) {
|
||||
snap = 1000000;
|
||||
|
@ -60,7 +60,7 @@ public:
|
||||
ScopeVisualProcessor *getScopeProcessor();
|
||||
SpectrumVisualProcessor *getSpectrumProcessor();
|
||||
SpectrumVisualProcessor *getDemodSpectrumProcessor();
|
||||
VisualDataDistributor<DemodulatorThreadIQData> *getSpectrumDistributor();
|
||||
VisualDataReDistributor<DemodulatorThreadIQData> *getSpectrumDistributor();
|
||||
|
||||
DemodulatorThreadOutputQueue* getAudioVisualQueue();
|
||||
DemodulatorThreadInputQueue* getIQVisualQueue();
|
||||
@ -80,7 +80,8 @@ public:
|
||||
int getPPM();
|
||||
|
||||
void showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode = FrequencyDialog::FDIALOG_TARGET_DEFAULT);
|
||||
|
||||
AppFrame *getAppFrame();
|
||||
|
||||
private:
|
||||
AppFrame *appframe;
|
||||
AppConfig config;
|
||||
@ -110,7 +111,7 @@ private:
|
||||
|
||||
ScopeVisualProcessor scopeProcessor;
|
||||
|
||||
VisualDataDistributor<DemodulatorThreadIQData> spectrumDistributor;
|
||||
VisualDataReDistributor<DemodulatorThreadIQData> spectrumDistributor;
|
||||
|
||||
std::thread *t_SDR;
|
||||
std::thread *t_PostSDR;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "ThreadQueue.h"
|
||||
#include "Timer.h"
|
||||
|
||||
struct map_string_less : public std::binary_function<std::string,std::string,bool>
|
||||
{
|
||||
@ -112,9 +113,9 @@ public:
|
||||
void setOutputQueue(std::string qname, ThreadQueueBase *threadQueue);
|
||||
void *getOutputQueue(std::string qname);
|
||||
|
||||
|
||||
protected:
|
||||
std::map<std::string, ThreadQueueBase *, map_string_less> input_queues;
|
||||
std::map<std::string, ThreadQueueBase *, map_string_less> output_queues;
|
||||
std::atomic_bool terminated;
|
||||
Timer gTimer;
|
||||
};
|
||||
|
@ -81,6 +81,13 @@ public:
|
||||
|
||||
}
|
||||
|
||||
DemodulatorThreadIQData & operator=(const DemodulatorThreadIQData &other) {
|
||||
frequency = other.frequency;
|
||||
sampleRate = other.sampleRate;
|
||||
data.assign(other.data.begin(), other.data.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
~DemodulatorThreadIQData() {
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
DemodulatorMgr::DemodulatorMgr() :
|
||||
activeDemodulator(NULL), lastActiveDemodulator(NULL), activeVisualDemodulator(NULL), lastBandwidth(DEFAULT_DEMOD_BW), lastDemodType(
|
||||
DEFAULT_DEMOD_TYPE), lastSquelchEnabled(false), lastSquelch(0), lastGain(1.0), lastStereo(false) {
|
||||
DEFAULT_DEMOD_TYPE), lastSquelchEnabled(false), lastSquelch(0), lastGain(1.0), lastStereo(false), lastMuted(false) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -696,8 +696,8 @@ void DemodulatorThread::run() {
|
||||
}
|
||||
ati->data.resize(numAudioWritten * 2);
|
||||
for (int i = 0; i < numAudioWritten; i++) {
|
||||
ati->data[i * 2] = (*inputData)[i].real;
|
||||
ati->data[i * 2 + 1] = (*inputData)[i].imag;
|
||||
ati->data[i * 2] = (*inputData)[i].imag;
|
||||
ati->data[i * 2 + 1] = (*inputData)[i].real;
|
||||
}
|
||||
} else if (stereo && inp->sampleRate >= 100000) {
|
||||
ati->channels = 2;
|
||||
|
@ -4,8 +4,16 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "ColorTheme.h"
|
||||
#include "CubicSDRDefs.h"
|
||||
|
||||
SpectrumPanel::SpectrumPanel() : floorValue(0), ceilValue(1), showDb(false), fftSize(2048) {
|
||||
SpectrumPanel::SpectrumPanel() {
|
||||
floorValue = 0;
|
||||
ceilValue = 1;
|
||||
showDb = false;
|
||||
fftSize = DEFAULT_FFT_SIZE;
|
||||
bandwidth = DEFAULT_DEMOD_BW;
|
||||
freq = 0;
|
||||
|
||||
setFill(GLPANEL_FILL_GRAD_Y);
|
||||
setFillColor(ThemeMgr::mgr.currentTheme->fftBackground * 2.0, ThemeMgr::mgr.currentTheme->fftBackground);
|
||||
|
||||
@ -233,15 +241,18 @@ void SpectrumPanel::drawPanelContents() {
|
||||
float dbPanelHeight = (1.0/viewHeight)*14.0;
|
||||
|
||||
|
||||
std::stringstream ssLabel;
|
||||
ssLabel << std::fixed << std::setprecision(1) << (20.0 * log10(2.0*(getCeilValue())/(double)fftSize)) << "dB";
|
||||
|
||||
std::stringstream ssLabel("");
|
||||
if (getCeilValue() != getFloorValue() && fftSize) {
|
||||
ssLabel << std::fixed << std::setprecision(1) << (20.0 * log10(2.0*(getCeilValue())/(double)fftSize)) << "dB";
|
||||
}
|
||||
dbPanelCeil.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT);
|
||||
dbPanelCeil.setSize(dbPanelWidth, dbPanelHeight);
|
||||
dbPanelCeil.setPosition(-1.0 + dbPanelWidth, 1.0 - dbPanelHeight);
|
||||
|
||||
ssLabel.str("");
|
||||
ssLabel << (20.0 * log10(2.0*(getFloorValue())/(double)fftSize)) << "dB";
|
||||
if (getCeilValue() != getFloorValue() && fftSize) {
|
||||
ssLabel << (20.0 * log10(2.0*(getFloorValue())/(double)fftSize)) << "dB";
|
||||
}
|
||||
|
||||
dbPanelFloor.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT);
|
||||
dbPanelFloor.setSize(dbPanelWidth, dbPanelHeight);
|
||||
|
81
src/process/FFTDataDistributor.cpp
Normal file
81
src/process/FFTDataDistributor.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
#include "FFTDataDistributor.h"
|
||||
|
||||
FFTDataDistributor::FFTDataDistributor() : linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0), fftSize(DEFAULT_FFT_SIZE) {
|
||||
}
|
||||
|
||||
void FFTDataDistributor::setFFTSize(int fftSize) {
|
||||
this->fftSize = fftSize;
|
||||
}
|
||||
|
||||
void FFTDataDistributor::setLinesPerSecond(int lines) {
|
||||
this->linesPerSecond = lines;
|
||||
}
|
||||
|
||||
int FFTDataDistributor::getLinesPerSecond() {
|
||||
return this->linesPerSecond;
|
||||
}
|
||||
|
||||
void FFTDataDistributor::process() {
|
||||
while (!input->empty()) {
|
||||
if (!isAnyOutputEmpty()) {
|
||||
return;
|
||||
}
|
||||
DemodulatorThreadIQData *inp;
|
||||
input->pop(inp);
|
||||
|
||||
if (inp) {
|
||||
if (inputBuffer.sampleRate != inp->sampleRate || inputBuffer.frequency != inp->frequency) {
|
||||
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());
|
||||
}
|
||||
inp->decRefCount();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// number of seconds contained in input
|
||||
double inputTime = (double)inputBuffer.data.size() / (double)inputBuffer.sampleRate;
|
||||
// number of lines in input
|
||||
double inputLines = (double)inputBuffer.data.size()/(double)fftSize;
|
||||
|
||||
// ratio required to achieve the desired rate
|
||||
double lineRateStep = ((double)linesPerSecond * inputTime)/(double)inputLines;
|
||||
|
||||
if (inputBuffer.data.size() >= fftSize) {
|
||||
int numProcessed = 0;
|
||||
|
||||
if (lineRateAccum + (lineRateStep * ((double)inputBuffer.data.size()/(double)fftSize)) < 1.0) {
|
||||
// move along, nothing to see here..
|
||||
lineRateAccum += (lineRateStep * ((double)inputBuffer.data.size()/(double)fftSize));
|
||||
numProcessed = inputBuffer.data.size();
|
||||
} else {
|
||||
for (int i = 0, iMax = inputBuffer.data.size(); i < iMax; i += fftSize) {
|
||||
if ((i + fftSize) > iMax) {
|
||||
break;
|
||||
}
|
||||
lineRateAccum += lineRateStep;
|
||||
|
||||
if (lineRateAccum >= 1.0) {
|
||||
DemodulatorThreadIQData *outp = outputBuffers.getBuffer();
|
||||
outp->frequency = inputBuffer.frequency;
|
||||
outp->sampleRate = inputBuffer.sampleRate;
|
||||
outp->data.assign(inputBuffer.data.begin()+i,inputBuffer.data.begin()+i+fftSize);
|
||||
distribute(outp);
|
||||
|
||||
while (lineRateAccum >= 1.0) {
|
||||
lineRateAccum -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
numProcessed += fftSize;
|
||||
}
|
||||
}
|
||||
if (numProcessed) {
|
||||
inputBuffer.data.erase(inputBuffer.data.begin(), inputBuffer.data.begin() + numProcessed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
src/process/FFTDataDistributor.h
Normal file
22
src/process/FFTDataDistributor.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "VisualProcessor.h"
|
||||
#include "DemodDefs.h"
|
||||
#include <cmath>
|
||||
|
||||
class FFTDataDistributor : public VisualProcessor<DemodulatorThreadIQData, DemodulatorThreadIQData> {
|
||||
public:
|
||||
FFTDataDistributor();
|
||||
void setFFTSize(int fftSize);
|
||||
void setLinesPerSecond(int lines);
|
||||
int getLinesPerSecond();
|
||||
|
||||
protected:
|
||||
void process();
|
||||
|
||||
DemodulatorThreadIQData inputBuffer, tempBuffer;
|
||||
ReBuffer<DemodulatorThreadIQData> outputBuffers;
|
||||
int fftSize;
|
||||
int linesPerSecond;
|
||||
double lineRateAccum;
|
||||
};
|
@ -1,7 +1,8 @@
|
||||
#include "FFTVisualDataThread.h"
|
||||
#include "CubicSDR.h"
|
||||
|
||||
FFTVisualDataThread::FFTVisualDataThread() : linesPerSecond(DEFAULT_WATERFALL_LPS) {
|
||||
FFTVisualDataThread::FFTVisualDataThread() {
|
||||
linesPerSecond.store(DEFAULT_WATERFALL_LPS);
|
||||
lpsChanged.store(true);
|
||||
}
|
||||
|
||||
@ -36,7 +37,8 @@ void FFTVisualDataThread::run() {
|
||||
|
||||
while(!terminated) {
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(12));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
// std::this_thread::yield();
|
||||
|
||||
int fftSize = wproc.getDesiredInputSize();
|
||||
|
||||
@ -48,7 +50,7 @@ void FFTVisualDataThread::run() {
|
||||
|
||||
if (lpsChanged.load()) {
|
||||
fftDistrib.setLinesPerSecond(linesPerSecond.load());
|
||||
pipeIQDataIn->set_max_num_items(linesPerSecond.load());
|
||||
// pipeIQDataIn->set_max_num_items(linesPerSecond.load());
|
||||
lpsChanged.store(false);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "IOThread.h"
|
||||
#include "SpectrumVisualProcessor.h"
|
||||
#include "FFTDataDistributor.h"
|
||||
|
||||
class FFTVisualDataThread : public IOThread {
|
||||
public:
|
||||
|
@ -16,7 +16,8 @@ void SpectrumVisualDataThread::run() {
|
||||
std::cout << "Spectrum visual data thread started." << std::endl;
|
||||
|
||||
while(!terminated) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(12));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
// std::this_thread::yield();
|
||||
sproc.run();
|
||||
}
|
||||
|
||||
|
@ -69,75 +69,3 @@ private:
|
||||
std::atomic_int desiredInputSize;
|
||||
std::mutex busy_run;
|
||||
};
|
||||
|
||||
|
||||
class FFTDataDistributor : public VisualProcessor<DemodulatorThreadIQData, DemodulatorThreadIQData> {
|
||||
public:
|
||||
FFTDataDistributor() : linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0) {
|
||||
}
|
||||
|
||||
void setFFTSize(int fftSize) {
|
||||
this->fftSize = fftSize;
|
||||
}
|
||||
|
||||
void setLinesPerSecond(int lines) {
|
||||
this->linesPerSecond = lines;
|
||||
}
|
||||
|
||||
int getLinesPerSecond() {
|
||||
return this->linesPerSecond;
|
||||
}
|
||||
protected:
|
||||
void process() {
|
||||
while (!input->empty()) {
|
||||
if (!isAnyOutputEmpty()) {
|
||||
return;
|
||||
}
|
||||
DemodulatorThreadIQData *inp;
|
||||
input->pop(inp);
|
||||
|
||||
int fftSize = this->fftSize;
|
||||
|
||||
if (fftSize > inp->data.size()) {
|
||||
fftSize = inp->data.size();
|
||||
}
|
||||
|
||||
// number of milliseconds contained in input
|
||||
double inputTime = (double)inp->data.size() / (double)inp->sampleRate;
|
||||
// number of lines in input
|
||||
int inputLines = floor((double)inp->data.size()/(double)fftSize);
|
||||
|
||||
// ratio required to achieve the desired rate
|
||||
double lineRateStep = ((double)linesPerSecond * inputTime)/(double)inputLines;
|
||||
|
||||
if (inp) {
|
||||
if (inp->data.size() >= fftSize) {
|
||||
if (lineRateAccum + (lineRateStep * floor((double)inp->data.size()/(double)fftSize)) < 1.0) {
|
||||
// move along, nothing to see here..
|
||||
lineRateAccum += (lineRateStep * inp->data.size()/fftSize);
|
||||
} else for (int i = 0, iMax = inp->data.size()-fftSize; i <= iMax; i += fftSize) {
|
||||
lineRateAccum += lineRateStep;
|
||||
|
||||
if (lineRateAccum >= 1.0) {
|
||||
DemodulatorThreadIQData *outp = outputBuffers.getBuffer();
|
||||
outp->frequency = inp->frequency;
|
||||
outp->sampleRate = inp->sampleRate;
|
||||
outp->data.assign(inp->data.begin()+i,inp->data.begin()+i+fftSize);
|
||||
distribute(outp);
|
||||
|
||||
while (lineRateAccum >= 1.0) {
|
||||
lineRateAccum -= 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inp->decRefCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReBuffer<DemodulatorThreadIQData> outputBuffers;
|
||||
int fftSize;
|
||||
int linesPerSecond;
|
||||
double lineRateAccum;
|
||||
};
|
||||
|
@ -107,3 +107,25 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class OutputDataType = ReferenceCounter>
|
||||
class VisualDataReDistributor : public VisualProcessor<OutputDataType, OutputDataType> {
|
||||
protected:
|
||||
void process() {
|
||||
while (!VisualProcessor<OutputDataType, OutputDataType>::input->empty()) {
|
||||
if (!VisualProcessor<OutputDataType, OutputDataType>::isAnyOutputEmpty()) {
|
||||
return;
|
||||
}
|
||||
OutputDataType *inp;
|
||||
VisualProcessor<OutputDataType, OutputDataType>::input->pop(inp);
|
||||
|
||||
if (inp) {
|
||||
OutputDataType *outp = buffers.getBuffer();
|
||||
(*outp) = (*inp);
|
||||
inp->decRefCount();
|
||||
VisualProcessor<OutputDataType, OutputDataType>::distribute(outp);
|
||||
}
|
||||
}
|
||||
}
|
||||
ReBuffer<OutputDataType> buffers;
|
||||
};
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <deque>
|
||||
|
||||
SDRPostThread::SDRPostThread() : IOThread(),
|
||||
iqDataInQueue(NULL), iqDataOutQueue(NULL), iqVisualQueue(NULL), dcFilter(NULL), num_vis_samples(16384*2) {
|
||||
iqDataInQueue(NULL), iqDataOutQueue(NULL), iqVisualQueue(NULL), dcFilter(NULL){
|
||||
|
||||
swapIQ.store(false);
|
||||
|
||||
@ -50,14 +50,6 @@ void SDRPostThread::removeDemodulator(DemodulatorInstance *demod) {
|
||||
busy_demod.unlock();
|
||||
}
|
||||
|
||||
void SDRPostThread::setNumVisSamples(int num_vis_samples_in) {
|
||||
num_vis_samples = num_vis_samples_in;
|
||||
}
|
||||
|
||||
int SDRPostThread::getNumVisSamples() {
|
||||
return num_vis_samples;
|
||||
}
|
||||
|
||||
void SDRPostThread::setSwapIQ(bool swapIQ) {
|
||||
this->swapIQ.store(swapIQ);
|
||||
}
|
||||
@ -93,7 +85,6 @@ void SDRPostThread::run() {
|
||||
|
||||
iqDataInQueue->pop(data_in);
|
||||
// std::lock_guard < std::mutex > lock(data_in->m_mutex);
|
||||
int num_vis_samples = this->num_vis_samples;
|
||||
|
||||
if (data_in && data_in->data.size()) {
|
||||
int dataSize = data_in->data.size()/2;
|
||||
@ -118,21 +109,19 @@ void SDRPostThread::run() {
|
||||
|
||||
iirfilt_crcf_execute_block(dcFilter, &fpData[0], dataSize, &dataOut[0]);
|
||||
|
||||
if (iqVisualQueue != NULL && iqVisualQueue->empty()) {
|
||||
if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
|
||||
DemodulatorThreadIQData *visualDataOut = visualDataBuffers.getBuffer();
|
||||
visualDataOut->setRefCount(1);
|
||||
|
||||
if (num_vis_samples > dataOut.size()) {
|
||||
num_vis_samples = dataOut.size();
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -11,9 +11,6 @@ public:
|
||||
void bindDemodulator(DemodulatorInstance *demod);
|
||||
void removeDemodulator(DemodulatorInstance *demod);
|
||||
|
||||
void setNumVisSamples(int num_vis_samples_in);
|
||||
int getNumVisSamples();
|
||||
|
||||
void setSwapIQ(bool swapIQ);
|
||||
bool getSwapIQ();
|
||||
|
||||
@ -28,7 +25,6 @@ protected:
|
||||
std::mutex busy_demod;
|
||||
std::vector<DemodulatorInstance *> demodulators;
|
||||
iirfilt_crcf dcFilter;
|
||||
int num_vis_samples;
|
||||
std::atomic_bool swapIQ;
|
||||
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
|
||||
#include "Timer.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
|
||||
Timer::Timer(void) : time_elapsed(0), system_milliseconds(0), start_time(0), end_time(0), last_update(0), num_updates(0), paused_time(0), offset(0), paused_state(false), lock_state(0), lock_rate(0)
|
||||
#include <iostream>
|
||||
|
||||
Timer::Timer(void) : time_elapsed(0), system_milliseconds(0), start_time(0), end_time(0), last_update(0), num_updates(0), paused_time(0), offset(0), paused_state(false), lock_state(false), lock_rate(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -75,8 +78,10 @@ void Timer::update(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
|
||||
system_milliseconds = timeGetTime ();
|
||||
|
||||
#else
|
||||
gettimeofday(&time_val,&time_zone);
|
||||
|
||||
@ -157,3 +162,14 @@ bool Timer::paused()
|
||||
{
|
||||
return paused_state;
|
||||
}
|
||||
|
||||
void Timer::timerTestFunc() {
|
||||
update();
|
||||
if (getNumUpdates() % 120 == 0) {
|
||||
std::cout << getNumUpdates() << "," << getSeconds() << " Rate: " << ((double)getNumUpdates()/getSeconds()) << "/sec" << std::endl;
|
||||
}
|
||||
if (getNumUpdates() >= 600) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ private:
|
||||
unsigned long paused_time;
|
||||
unsigned long offset;
|
||||
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
struct timeval time_val;
|
||||
struct timezone time_zone;
|
||||
#endif
|
||||
@ -155,6 +155,9 @@ public:
|
||||
* \return Current pause state, true if paused, false otherwise
|
||||
*/
|
||||
bool paused();
|
||||
|
||||
|
||||
void timerTestFunc();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -36,6 +36,7 @@ MeterCanvas::~MeterCanvas() {
|
||||
|
||||
void MeterCanvas::setLevel(float level_in) {
|
||||
level = level_in;
|
||||
Refresh();
|
||||
}
|
||||
float MeterCanvas::getLevel() {
|
||||
return level;
|
||||
@ -43,10 +44,12 @@ float MeterCanvas::getLevel() {
|
||||
|
||||
void MeterCanvas::setMax(float max_in) {
|
||||
level_max = max_in;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void MeterCanvas::setInputValue(float slider_in) {
|
||||
userInputValue = inputValue = slider_in;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
bool MeterCanvas::inputChanged() {
|
||||
@ -87,8 +90,11 @@ void MeterCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
}
|
||||
|
||||
void MeterCanvas::OnIdle(wxIdleEvent &event) {
|
||||
Refresh();
|
||||
event.RequestMore();
|
||||
if (mouseTracker.mouseInView()) {
|
||||
Refresh();
|
||||
} else {
|
||||
event.Skip();
|
||||
}
|
||||
}
|
||||
|
||||
void MeterCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
@ -107,15 +113,18 @@ void MeterCanvas::OnMouseDown(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseDown(event);
|
||||
userInputValue = mouseTracker.getMouseY() * level_max;
|
||||
mouseTracker.setHorizDragLock(true);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void MeterCanvas::OnMouseWheelMoved(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseWheelMoved(event);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void MeterCanvas::OnMouseReleased(wxMouseEvent& event) {
|
||||
InteractiveCanvas::OnMouseReleased(event);
|
||||
userInputValue = mouseTracker.getMouseY() * level_max;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void MeterCanvas::OnMouseLeftWindow(wxMouseEvent& event) {
|
||||
@ -127,6 +136,7 @@ void MeterCanvas::OnMouseLeftWindow(wxMouseEvent& event) {
|
||||
void MeterCanvas::OnMouseEnterWindow(wxMouseEvent& event) {
|
||||
InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event);
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void MeterCanvas::setHelpTip(std::string tip) {
|
||||
|
@ -79,8 +79,11 @@ void ModeSelectorCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
}
|
||||
|
||||
void ModeSelectorCanvas::OnIdle(wxIdleEvent &event) {
|
||||
Refresh();
|
||||
event.RequestMore();
|
||||
if (mouseTracker.mouseInView()) {
|
||||
Refresh();
|
||||
} else {
|
||||
event.Skip();
|
||||
}
|
||||
}
|
||||
|
||||
void ModeSelectorCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
@ -120,6 +123,7 @@ void ModeSelectorCanvas::OnMouseReleased(wxMouseEvent& event) {
|
||||
currentSelection = selectedButton;
|
||||
|
||||
SetCursor (wxCURSOR_HAND);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ModeSelectorCanvas::OnMouseLeftWindow(wxMouseEvent& event) {
|
||||
@ -134,6 +138,7 @@ void ModeSelectorCanvas::OnMouseEnterWindow(wxMouseEvent& event) {
|
||||
if (!helpTip.empty()) {
|
||||
setStatusText(helpTip);
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ModeSelectorCanvas::setHelpTip(std::string tip) {
|
||||
@ -142,6 +147,7 @@ void ModeSelectorCanvas::setHelpTip(std::string tip) {
|
||||
|
||||
void ModeSelectorCanvas::setNumChoices(int numChoices_in) {
|
||||
numChoices = numChoices_in;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ModeSelectorCanvas::addChoice(int value, std::string label) {
|
||||
@ -157,6 +163,7 @@ void ModeSelectorCanvas::setSelection(int value) {
|
||||
}
|
||||
}
|
||||
currentSelection = -1;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
int ModeSelectorCanvas::getSelection() {
|
||||
|
@ -39,7 +39,9 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
|
||||
dragOfs(0), mouseZoom(1), zoom(1), freqMove(0.0), freqMoving(false), hoverAlpha(1.0) {
|
||||
|
||||
glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
|
||||
|
||||
linesPerSecond = 30;
|
||||
lpsIndex = 0;
|
||||
preBuf = false;
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
}
|
||||
|
||||
@ -54,6 +56,7 @@ void WaterfallCanvas::setup(int fft_size_in, int waterfall_lines_in) {
|
||||
waterfall_lines = waterfall_lines_in;
|
||||
|
||||
waterfallPanel.setup(fft_size, waterfall_lines);
|
||||
gTimer.start();
|
||||
}
|
||||
|
||||
WaterfallCanvas::DragState WaterfallCanvas::getDragState() {
|
||||
@ -74,23 +77,38 @@ void WaterfallCanvas::processInputQueue() {
|
||||
}
|
||||
glContext->SetCurrent(*this);
|
||||
|
||||
while (!visualDataQueue.empty()) {
|
||||
SpectrumVisualData *vData;
|
||||
|
||||
visualDataQueue.pop(vData);
|
||||
|
||||
if (vData) {
|
||||
waterfallPanel.setPoints(vData->spectrum_points);
|
||||
waterfallPanel.step();
|
||||
vData->decRefCount();
|
||||
gTimer.update();
|
||||
|
||||
double targetVis = 1.0 / (double)linesPerSecond;
|
||||
lpsIndex += gTimer.lastUpdateSeconds();
|
||||
|
||||
if (linesPerSecond) {
|
||||
if (lpsIndex >= targetVis) {
|
||||
tex_update.lock();
|
||||
while (lpsIndex >= targetVis) {
|
||||
SpectrumVisualData *vData;
|
||||
if (!visualDataQueue.empty()) {
|
||||
visualDataQueue.pop(vData);
|
||||
|
||||
if (vData) {
|
||||
waterfallPanel.setPoints(vData->spectrum_points);
|
||||
waterfallPanel.step();
|
||||
vData->decRefCount();
|
||||
}
|
||||
lpsIndex-=targetVis;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
tex_update.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
// wxClientDC dc(this);
|
||||
wxPaintDC dc(this);
|
||||
|
||||
processInputQueue();
|
||||
|
||||
const wxSize ClientSize = GetClientSize();
|
||||
long double currentZoom = zoom;
|
||||
|
||||
@ -193,7 +211,9 @@ 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();
|
||||
|
||||
@ -391,7 +411,9 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
||||
void WaterfallCanvas::OnIdle(wxIdleEvent &event) {
|
||||
Refresh();
|
||||
event.RequestMore();
|
||||
// event.Skip();
|
||||
if (visualDataQueue.size() > linesPerSecond) {
|
||||
processInputQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||
@ -779,3 +801,17 @@ void WaterfallCanvas::updateCenterFrequency(long long freq) {
|
||||
|
||||
}
|
||||
|
||||
void WaterfallCanvas::setLinesPerSecond(int lps) {
|
||||
linesPerSecond = lps;
|
||||
while (!visualDataQueue.empty()) {
|
||||
SpectrumVisualData *vData;
|
||||
visualDataQueue.pop(vData);
|
||||
|
||||
if (vData) {
|
||||
vData->decRefCount();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "MouseTracker.h"
|
||||
#include "SpectrumCanvas.h"
|
||||
#include "WaterfallPanel.h"
|
||||
|
||||
#include "Timer.h"
|
||||
|
||||
class WaterfallCanvas: public InteractiveCanvas {
|
||||
public:
|
||||
@ -29,6 +29,8 @@ public:
|
||||
void processInputQueue();
|
||||
SpectrumVisualDataQueue *getVisualDataQueue();
|
||||
|
||||
void setLinesPerSecond(int lps);
|
||||
|
||||
private:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
void OnKeyDown(wxKeyEvent& event);
|
||||
@ -64,9 +66,13 @@ private:
|
||||
bool freqMoving;
|
||||
long double freqMove;
|
||||
float hoverAlpha;
|
||||
|
||||
int linesPerSecond;
|
||||
|
||||
SpectrumVisualDataQueue visualDataQueue;
|
||||
|
||||
Timer gTimer;
|
||||
double lpsIndex;
|
||||
bool preBuf;
|
||||
std::mutex tex_update;
|
||||
// event table
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user