Merge pull request #129 from cjcliffe/waterfall_speed_avg

Waterfall speed and Spectrum Averaging control
This commit is contained in:
Charles J. Cliffe 2015-08-13 22:10:49 -04:00
commit b73f264d6b
25 changed files with 530 additions and 199 deletions

View File

@ -123,6 +123,8 @@ AppConfig::AppConfig() : configName("") {
themeId.store(0);
snap.store(1);
centerFreq.store(100000000);
waterfallLinesPerSec.store(DEFAULT_WATERFALL_LPS);
spectrumAvgSpeed.store(0.65f);
}
@ -204,6 +206,23 @@ long long AppConfig::getCenterFreq() {
return centerFreq.load();
}
void AppConfig::setWaterfallLinesPerSec(int lps) {
waterfallLinesPerSec.store(lps);
}
int AppConfig::getWaterfallLinesPerSec() {
return waterfallLinesPerSec.load();
}
void AppConfig::setSpectrumAvgSpeed(float avgSpeed) {
spectrumAvgSpeed.store(avgSpeed);
}
float AppConfig::getSpectrumAvgSpeed() {
return spectrumAvgSpeed.load();
}
void AppConfig::setConfigName(std::string configName) {
this->configName = configName;
}
@ -243,6 +262,8 @@ bool AppConfig::save() {
*window_node->newChild("theme") = themeId.load();
*window_node->newChild("snap") = snap.load();
*window_node->newChild("center_freq") = centerFreq.load();
*window_node->newChild("waterfall_lps") = waterfallLinesPerSec.load();
*window_node->newChild("spectrum_avg") = spectrumAvgSpeed.load();
}
DataNode *devices_node = cfg.rootNode()->newChild("devices");
@ -339,7 +360,19 @@ bool AppConfig::load() {
win_node->getNext("center_freq")->element()->get(freqVal);
centerFreq.store(freqVal);
}
}
if (win_node->hasAnother("waterfall_lps")) {
int lpsVal;
win_node->getNext("waterfall_lps")->element()->get(lpsVal);
waterfallLinesPerSec.store(lpsVal);
}
if (win_node->hasAnother("spectrum_avg")) {
float avgVal;
win_node->getNext("spectrum_avg")->element()->get(avgVal);
spectrumAvgSpeed.store(avgVal);
}
}
if (cfg.rootNode()->hasAnother("devices")) {
DataNode *devices_node = cfg.rootNode()->getNext("devices");

View File

@ -61,7 +61,13 @@ public:
void setCenterFreq(long long freqVal);
long long getCenterFreq();
void setWaterfallLinesPerSec(int lps);
int getWaterfallLinesPerSec();
void setSpectrumAvgSpeed(float avgSpeed);
float getSpectrumAvgSpeed();
void setConfigName(std::string configName);
std::string getConfigFileName(bool ignoreName=false);
bool save();
@ -76,4 +82,6 @@ private:
std::atomic_int themeId;
std::atomic_llong snap;
std::atomic_llong centerFreq;
std::atomic_int waterfallLinesPerSec;
std::atomic<float> spectrumAvgSpeed;
};

View File

@ -35,11 +35,10 @@ EVT_CLOSE(AppFrame::OnClose)
EVT_MENU(wxID_ANY, AppFrame::OnMenu)
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread)
EVT_IDLE(AppFrame::OnIdle)
EVT_TIMER(FRAME_TIMER_ID, AppFrame::OnTimer)
wxEND_EVENT_TABLE()
AppFrame::AppFrame() :
wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL), frame_timer(this, FRAME_TIMER_ID) {
wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) {
#ifdef __linux__
SetIcon(wxICON(cubicsdr));
@ -63,8 +62,6 @@ AppFrame::AppFrame() :
demodModeSelector->setHelpTip("Choose modulation type: Frequency Modulation, Amplitude Modulation and Lower, Upper or Double Side-Band.");
demodTray->Add(demodModeSelector, 2, wxEXPAND | wxALL, 0);
// demodTray->AddSpacer(2);
wxGetApp().getDemodSpectrumProcesor()->setup(1024);
demodSpectrumCanvas = new SpectrumCanvas(this, attribList);
demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
@ -109,23 +106,59 @@ AppFrame::AppFrame() :
demodGainMeter = new MeterCanvas(this, attribList);
demodGainMeter->setMax(2.0);
demodGainMeter->setHelpTip("Current Demodulator Gain Level. Click / Drag to set Gain level.");
demodGainMeter->setShowUserInput(false);
demodTray->Add(demodGainMeter, 1, wxEXPAND | wxALL, 0);
vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0);
vbox->AddSpacer(1);
wxBoxSizer *spectrumSizer = new wxBoxSizer(wxHORIZONTAL);
wxGetApp().getSpectrumProcesor()->setup(2048);
spectrumCanvas = new SpectrumCanvas(this, attribList);
vbox->Add(spectrumCanvas, 5, wxEXPAND | wxALL, 0);
vbox->AddSpacer(1);
wxGetApp().getSpectrumProcesor()->attachOutput(spectrumCanvas->getVisualDataQueue());
spectrumAvgMeter = new MeterCanvas(this, attribList);
spectrumAvgMeter->setHelpTip("Spectrum averaging speed, click or drag to adjust.");
spectrumAvgMeter->setMax(1.0);
spectrumAvgMeter->setLevel(0.65);
spectrumAvgMeter->setShowUserInput(false);
spectrumSizer->Add(spectrumCanvas, 63, wxEXPAND | wxALL, 0);
spectrumSizer->AddSpacer(1);
spectrumSizer->Add(spectrumAvgMeter, 1, wxEXPAND | wxALL, 0);
vbox->Add(spectrumSizer, 5, wxEXPAND | wxALL, 0);
vbox->AddSpacer(1);
wxBoxSizer *wfSizer = new wxBoxSizer(wxHORIZONTAL);
wxGetApp().getWaterfallProcesor()->setup(2048);
waterfallCanvas = new WaterfallCanvas(this, attribList);
waterfallCanvas->setup(2048, 512);
fftDistrib.setInput(wxGetApp().getWaterfallVisualQueue());
fftDistrib.attachOutput(&fftQueue);
wxGetApp().getWaterfallProcesor()->setInput(&fftQueue);
wxGetApp().getWaterfallProcesor()->attachOutput(waterfallCanvas->getVisualDataQueue());
waterfallSpeedMeter = new MeterCanvas(this, 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);
wfSizer->Add(waterfallCanvas, 63, wxEXPAND | wxALL, 0);
wfSizer->AddSpacer(1);
wfSizer->Add(waterfallSpeedMeter, 1, wxEXPAND | wxALL, 0);
vbox->Add(wfSizer, 20, wxEXPAND | wxALL, 0);
// TODO: refactor these..
waterfallCanvas->attachSpectrumCanvas(spectrumCanvas);
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
vbox->Add(waterfallCanvas, 20, wxEXPAND | wxALL, 0);
wxGetApp().getSpectrumProcesor()->attachOutput(waterfallCanvas->getVisualDataQueue());
/*
vbox->AddSpacer(1);
testCanvas = new UITestCanvas(this, attribList);
@ -334,6 +367,16 @@ AppFrame::AppFrame() :
long long freqSnap = wxGetApp().getConfig()->getSnap();
wxGetApp().setFrequencySnap(freqSnap);
float spectrumAvg = wxGetApp().getConfig()->getSpectrumAvgSpeed();
spectrumAvgMeter->setLevel(spectrumAvg);
wxGetApp().getSpectrumProcesor()->setFFTAverageRate(spectrumAvg);
int wflps =wxGetApp().getConfig()->getWaterfallLinesPerSec();
waterfallSpeedMeter->setLevel(sqrt(wflps));
fftDistrib.setLinesPerSecond(wflps);
ThemeMgr::mgr.setTheme(wxGetApp().getConfig()->getTheme());
@ -351,8 +394,6 @@ AppFrame::AppFrame() :
wxAcceleratorTable accel(3, entries);
SetAcceleratorTable(accel);
// frame rate = 1000 / 30 = 33ms
frame_timer.Start(33);
// static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
// wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not");
// ShowFullScreen(true);
@ -435,7 +476,22 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
} else if (event.GetId() == wxID_RESET) {
wxGetApp().getDemodMgr().terminateAll();
wxGetApp().setFrequency(100000000);
wxGetApp().setOffset(0);
wxGetApp().getDemodMgr().setLastDemodulatorType(DEMOD_TYPE_FM);
demodModeSelector->setSelection(1);
wxGetApp().getDemodMgr().setLastStereo(false);
wxGetApp().getDemodMgr().setLastBandwidth(DEFAULT_DEMOD_BW);
wxGetApp().getDemodMgr().setLastGain(1.0);
wxGetApp().getDemodMgr().setLastSquelchLevel(0);
waterfallCanvas->setBandwidth(wxGetApp().getSampleRate());
waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency());
spectrumCanvas->setBandwidth(wxGetApp().getSampleRate());
spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency());
fftDistrib.setLinesPerSecond(DEFAULT_WATERFALL_LPS);
waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS));
wxGetApp().getSpectrumProcesor()->setFFTAverageRate(0.65);
spectrumAvgMeter->setLevel(0.65);
demodModeSelector->Refresh();
demodTuner->Refresh();
SetTitle(CUBICSDR_TITLE);
currentSessionFile = "";
} else if (event.GetId() == wxID_EXIT) {
@ -556,6 +612,8 @@ void AppFrame::OnClose(wxCloseEvent& event) {
wxGetApp().getConfig()->setTheme(ThemeMgr::mgr.getTheme());
wxGetApp().getConfig()->setSnap(wxGetApp().getFrequencySnap());
wxGetApp().getConfig()->setCenterFreq(wxGetApp().getFrequency());
wxGetApp().getConfig()->setSpectrumAvgSpeed(wxGetApp().getSpectrumProcesor()->getFFTAverageRate());
wxGetApp().getConfig()->setWaterfallLinesPerSec(fftDistrib.getLinesPerSecond());
wxGetApp().getConfig()->save();
event.Skip();
}
@ -569,10 +627,6 @@ void AppFrame::OnThread(wxCommandEvent& event) {
}
void AppFrame::OnIdle(wxIdleEvent& event) {
event.Skip();
}
void AppFrame::OnTimer(wxTimerEvent& event) {
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
@ -687,12 +741,20 @@ void AppFrame::OnTimer(wxTimerEvent& event) {
wxGetApp().getScopeProcessor()->run();
wxGetApp().getSpectrumDistributor()->run();
SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcesor();
proc->setView(waterfallCanvas->getViewState());
proc->setBandwidth(waterfallCanvas->getBandwidth());
proc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
if (spectrumAvgMeter->inputChanged()) {
float val = spectrumAvgMeter->getInputValue();
spectrumAvgMeter->setLevel(val);
proc->setFFTAverageRate(val);
GetStatusBar()->SetStatusText(wxString::Format(wxT("Spectrum averaging speed changed to %0.2f%%."),val*100.0));
}
proc->setView(spectrumCanvas->getViewState());
proc->setBandwidth(spectrumCanvas->getBandwidth());
proc->setCenterFrequency(spectrumCanvas->getCenterFrequency());
proc->run();
@ -704,25 +766,54 @@ void AppFrame::OnTimer(wxTimerEvent& event) {
dproc->run();
scopeCanvas->Refresh();
waterfallCanvas->Refresh();
spectrumCanvas->Refresh();
demodWaterfallCanvas->Refresh();
demodSpectrumCanvas->Refresh();
demodSignalMeter->Refresh();
demodGainMeter->Refresh();
SpectrumVisualProcessor *wproc = wxGetApp().getWaterfallProcesor();
if (demodTuner->getMouseTracker()->mouseInView() || demodTuner->changed()) {
demodTuner->Refresh();
}
if (demodModeSelector->getMouseTracker()->mouseInView()) {
demodModeSelector->Refresh();
int fftSize = wproc->getDesiredInputSize();
if (fftSize) {
fftDistrib.setFFTSize(fftSize);
} else {
fftDistrib.setFFTSize(DEFAULT_FFT_SIZE);
}
event.Skip();
if (waterfallSpeedMeter->inputChanged()) {
float val = waterfallSpeedMeter->getInputValue();
waterfallSpeedMeter->setLevel(val);
fftDistrib.setLinesPerSecond((int)ceil(val*val));
wxGetApp().getWaterfallVisualQueue()->set_max_num_items((int)ceil(val*val));
GetStatusBar()->SetStatusText(wxString::Format(wxT("Waterfall max speed changed to %d lines per second."),(int)ceil(val*val)));
}
fftDistrib.run();
wproc->setView(waterfallCanvas->getViewState());
wproc->setBandwidth(waterfallCanvas->getBandwidth());
wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
while (!wproc->isInputEmpty()) {
wproc->run();
}
waterfallCanvas->processInputQueue();
demodWaterfallCanvas->processInputQueue();
if (this->IsVisible()) {
waterfallCanvas->DoPaint();
demodWaterfallCanvas->DoPaint();
#ifdef __APPLE__
usleep(5000);
#endif
} else {
#ifndef _WIN32
usleep(15000);
#else
Sleep(15);
#endif
}
event.RequestMore();
}
void AppFrame::saveSession(std::string fileName) {

View File

@ -50,8 +50,6 @@
#define wxID_AUDIO_BANDWIDTH_BASE 9000
#define wxID_AUDIO_DEVICE_MULTIPLIER 50
#define FRAME_TIMER_ID 1000
// Define a new frame type
class AppFrame: public wxFrame {
public:
@ -69,7 +67,6 @@ private:
void OnClose(wxCloseEvent& event);
void OnNewWindow(wxCommandEvent& event);
void OnIdle(wxIdleEvent& event);
void OnTimer(wxTimerEvent& event);
ScopeCanvas *scopeCanvas;
SpectrumCanvas *spectrumCanvas;
@ -80,7 +77,9 @@ private:
MeterCanvas *demodSignalMeter;
MeterCanvas *demodGainMeter;
TuningCanvas *demodTuner;
UITestCanvas *testCanvas;
// UITestCanvas *testCanvas;
MeterCanvas *spectrumAvgMeter;
MeterCanvas *waterfallSpeedMeter;
DemodulatorInstance *activeDemodulator;
@ -94,7 +93,9 @@ private:
wxMenuItem *iqSwapMenuItem;
std::string currentSessionFile;
wxTimer frame_timer;
FFTDataDistributor fftDistrib;
DemodulatorThreadInputQueue fftQueue;
wxDECLARE_EVENT_TABLE();
};

View File

@ -58,10 +58,13 @@ bool CubicSDR::OnInit() {
spectrumDistributor.setInput(pipeIQVisualData);
pipeDemodIQVisualData = new DemodulatorThreadInputQueue();
pipeIQVisualData->set_max_num_items(1);
pipeDemodIQVisualData->set_max_num_items(1);
pipeSpectrumIQVisualData = new DemodulatorThreadInputQueue();
pipeIQVisualData->set_max_num_items(1);
pipeSpectrumIQVisualData->set_max_num_items(1);
pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
pipeWaterfallIQVisualData->set_max_num_items(DEFAULT_WATERFALL_LPS);
spectrumDistributor.attachOutput(pipeDemodIQVisualData);
spectrumDistributor.attachOutput(pipeSpectrumIQVisualData);
@ -75,18 +78,21 @@ bool CubicSDR::OnInit() {
scopeProcessor.setInput(pipeAudioVisualData);
// I/Q Data
pipeSDRIQData = new SDRThreadIQDataQueue;
pipeSDRIQData = new SDRThreadIQDataQueue();
pipeSDRCommand = new SDRThreadCommandQueue();
pipeSDRIQData->set_max_num_items(1);
sdrThread = new SDRThread();
sdrThread->setInputQueue("SDRCommandQueue",pipeSDRCommand);
sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData);
sdrPostThread = new SDRPostThread();
sdrPostThread->setNumVisSamples(16384 * 2);
sdrPostThread->setNumVisSamples(BUF_SIZE);
sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData);
sdrPostThread->setOutputQueue("IQVisualDataOut", pipeIQVisualData);
sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
std::vector<SDRDeviceInfo *>::iterator devs_i;
SDRThread::enumerate_rtl(&devs);
@ -276,6 +282,10 @@ SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcesor() {
return &demodSpectrumProcessor;
}
SpectrumVisualProcessor *CubicSDR::getWaterfallProcesor() {
return &waterfallProcessor;
}
VisualDataDistributor<DemodulatorThreadIQData> *CubicSDR::getSpectrumDistributor() {
return &spectrumDistributor;
}
@ -289,6 +299,10 @@ DemodulatorThreadInputQueue* CubicSDR::getIQVisualQueue() {
return pipeIQVisualData;
}
DemodulatorThreadInputQueue* CubicSDR::getWaterfallVisualQueue() {
return pipeWaterfallIQVisualData;
}
DemodulatorMgr &CubicSDR::getDemodMgr() {
return demodMgr;
}

View File

@ -58,10 +58,12 @@ public:
ScopeVisualProcessor *getScopeProcessor();
SpectrumVisualProcessor *getSpectrumProcesor();
SpectrumVisualProcessor *getDemodSpectrumProcesor();
SpectrumVisualProcessor *getWaterfallProcesor();
VisualDataDistributor<DemodulatorThreadIQData> *getSpectrumDistributor();
DemodulatorThreadOutputQueue* getAudioVisualQueue();
DemodulatorThreadInputQueue* getIQVisualQueue();
DemodulatorThreadInputQueue* getWaterfallVisualQueue();
DemodulatorMgr &getDemodMgr();
void bindDemodulator(DemodulatorInstance *demod);
@ -101,9 +103,11 @@ private:
DemodulatorThreadOutputQueue* pipeAudioVisualData;
DemodulatorThreadInputQueue* pipeDemodIQVisualData;
DemodulatorThreadInputQueue* pipeSpectrumIQVisualData;
DemodulatorThreadInputQueue* pipeWaterfallIQVisualData;
ScopeVisualProcessor scopeProcessor;
SpectrumVisualProcessor spectrumProcessor;
SpectrumVisualProcessor waterfallProcessor;
SpectrumVisualProcessor demodSpectrumProcessor;
VisualDataDistributor<DemodulatorThreadIQData> spectrumDistributor;

View File

@ -33,3 +33,4 @@ const char filePathSeparator =
#define DEFAULT_DEMOD_TYPE 1
#define DEFAULT_DEMOD_BW 200000
#define DEFAULT_WATERFALL_LPS 30

View File

@ -5,6 +5,7 @@
#include <deque>
#include <map>
#include <string>
#include <iostream>
#include "ThreadQueue.h"
@ -37,6 +38,8 @@ protected:
};
#define REBUFFER_GC_LIMIT 100
template<class BufferType = ReferenceCounter>
class ReBuffer {
@ -44,11 +47,27 @@ public:
BufferType *getBuffer() {
BufferType* buf = NULL;
for (outputBuffersI = outputBuffers.begin(); outputBuffersI != outputBuffers.end(); outputBuffersI++) {
if ((*outputBuffersI)->getRefCount() <= 0) {
return (*outputBuffersI);
if (!buf && (*outputBuffersI)->getRefCount() <= 0) {
buf = (*outputBuffersI);
(*outputBuffersI)->setRefCount(0);
} else if ((*outputBuffersI)->getRefCount() <= 0) {
(*outputBuffersI)->decRefCount();
}
}
if (buf) {
if (outputBuffers.back()->getRefCount() < -REBUFFER_GC_LIMIT) {
BufferType *ref = outputBuffers.back();
outputBuffers.pop_back();
delete ref;
}
return buf;
}
// if (outputBuffers.size() > 100) {
// std::cout << "Buffer over 100.." << std::endl;
// }
buf = new BufferType();
outputBuffers.push_back(buf);

View File

@ -94,7 +94,7 @@ void DemodulatorThread::run() {
while (!terminated) {
DemodulatorThreadPostIQData *inp;
iqInputQueue->pop(inp);
std::lock_guard < std::mutex > lock(inp->m_mutex);
// std::lock_guard < std::mutex > lock(inp->m_mutex);
int bufSize = inp->data.size();

View File

@ -58,18 +58,18 @@ void SpectrumPanel::drawPanelContents() {
if (points.size()) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
float range = ceilValue-floorValue;
float ranges[3][4] = { { 90.0, 5000.0, 10.0, 100.0 }, { 20.0, 150.0, 10.0, 10.0 }, { -20.0, 30.0, 10.0, 1.0 } };
double range = ceilValue-floorValue;
double ranges[3][4] = { { 90.0, 5000.0, 10.0, 100.0 }, { 20.0, 150.0, 10.0, 10.0 }, { -20.0, 30.0, 10.0, 1.0 } };
for (int i = 0; i < 3; i++) {
float p = 0;
float rangeMin = ranges[i][0];
float rangeMax = ranges[i][1];
float rangeTrans = ranges[i][2];
float rangeStep = ranges[i][3];
double p = 0;
double rangeMin = ranges[i][0];
double rangeMax = ranges[i][1];
double rangeTrans = ranges[i][2];
double rangeStep = ranges[i][3];
if (range >= rangeMin && range <= rangeMax) {
float a = 1.0;
double a = 1.0;
if (range <= rangeMin+rangeTrans) {
a *= (range-rangeMin)/rangeTrans;
@ -80,7 +80,7 @@ void SpectrumPanel::drawPanelContents() {
glColor4f(0.12, 0.12, 0.12, a);
glBegin(GL_LINES);
for (float l = floorValue; l<=ceilValue+rangeStep; l+=rangeStep) {
for (double l = floorValue; l<=ceilValue+rangeStep; l+=rangeStep) {
p += rangeStep/range;
glVertex2f(0,p); glVertex2f(1,p);
}
@ -104,29 +104,34 @@ void SpectrumPanel::drawPanelContents() {
float viewHeight = (float) vp[3];
float viewWidth = (float) vp[2];
long long leftFreq = (float) freq - ((float) bandwidth / 2.0);
long long rightFreq = leftFreq + (float) bandwidth;
long long leftFreq = (double) freq - ((double) bandwidth / 2.0);
long long rightFreq = leftFreq + (double) bandwidth;
long long firstMhz = (leftFreq / 1000000) * 1000000;
long double mhzStart = ((long double) (firstMhz - leftFreq) / (long double) (rightFreq - leftFreq)) * 2.0;
long double mhzStep = (100000.0 / (long double) (rightFreq - leftFreq)) * 2.0;
float mhzVisualStep = 0.1f;
double mhzVisualStep = 0.1f;
if (mhzStep * 0.5 * viewWidth < 40) {
mhzStep = (250000.0 / (long double) (rightFreq - leftFreq)) * 2.0;
mhzVisualStep = 0.25f;
}
if (mhzStep * 0.5 * viewWidth > 400) {
mhzStep = (10000.0 / (long double) (rightFreq - leftFreq)) * 2.0;
mhzVisualStep = 0.01f;
}
long double currentMhz = trunc(floor(firstMhz / 1000000.0));
std::stringstream label;
label.precision(2);
float hPos = 1.0 - (16.0 / viewHeight);
float lMhzPos = 1.0 - (5.0 / viewHeight);
double hPos = 1.0 - (16.0 / viewHeight);
double lMhzPos = 1.0 - (5.0 / viewHeight);
for (float m = -1.0 + mhzStart, mMax = 1.0 + ((mhzStart>0)?mhzStart:-mhzStart); m <= mMax; m += mhzStep) {
for (double m = -1.0 + mhzStart, mMax = 1.0 + ((mhzStart>0)?mhzStart:-mhzStart); m <= mMax; m += mhzStep) {
label << std::fixed << currentMhz;
double fractpart, intpart;

View File

@ -17,6 +17,7 @@ void ScopeVisualProcessor::process() {
return;
}
audioInputData->busy_update.lock();
ScopeRenderData *renderData = outputBuffers.getBuffer();
renderData->channels = audioInputData->channels;
@ -44,6 +45,8 @@ void ScopeVisualProcessor::process() {
renderData->waveform_points[i * 2 + 1] = audioInputData->data[i] / peak;
}
}
distribute(renderData);
audioInputData->busy_update.unlock();
}
}

View File

@ -14,6 +14,8 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last
fft_ceil_ma = fft_ceil_maa = 100.0;
fft_floor_ma = fft_floor_maa = 0.0;
desiredInputSize = 0;
fft_average_rate = 0.65;
}
SpectrumVisualProcessor::~SpectrumVisualProcessor() {
@ -28,6 +30,13 @@ void SpectrumVisualProcessor::setView(bool bView) {
is_view.store(bView);
}
void SpectrumVisualProcessor::setFFTAverageRate(float fftAverageRate) {
this->fft_average_rate = fftAverageRate;
}
float SpectrumVisualProcessor::getFFTAverageRate() {
return this->fft_average_rate;
}
void SpectrumVisualProcessor::setCenterFrequency(long long centerFreq_in) {
centerFreq.store(centerFreq_in);
@ -45,8 +54,13 @@ long SpectrumVisualProcessor::getBandwidth() {
return bandwidth.load();
}
int SpectrumVisualProcessor::getDesiredInputSize() {
return desiredInputSize;
}
void SpectrumVisualProcessor::setup(int fftSize_in) {
fftSize = fftSize_in;
desiredInputSize = fftSize;
if (fftwInput) {
free(fftwInput);
@ -83,6 +97,12 @@ void SpectrumVisualProcessor::process() {
input->pop(iqData);
if (!iqData) {
return;
}
iqData->busy_rw.lock();
std::vector<liquid_float_complex> *data = &iqData->data;
if (data && data->size()) {
@ -96,6 +116,8 @@ void SpectrumVisualProcessor::process() {
if (is_view.load()) {
if (!iqData->frequency || !iqData->sampleRate) {
iqData->decRefCount();
iqData->busy_rw.unlock();
return;
}
@ -103,6 +125,8 @@ void SpectrumVisualProcessor::process() {
int desired_input_size = fftSize / resamplerRatio;
this->desiredInputSize = desired_input_size;
if (iqData->data.size() < desired_input_size) {
// std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl;
desired_input_size = iqData->data.size();
@ -244,11 +268,11 @@ void SpectrumVisualProcessor::process() {
for (int i = 0, iMax = fftSize; i < iMax; i++) {
if (is_view.load()) {
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65;
fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65;
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;
} else {
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65;
fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65;
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;
}
if (fft_result_maa[i] > fft_ceil) {
@ -280,5 +304,8 @@ void SpectrumVisualProcessor::process() {
distribute(output);
}
iqData->decRefCount();
iqData->busy_rw.unlock();
}

View File

@ -3,6 +3,7 @@
#include "VisualProcessor.h"
#include "DemodDefs.h"
#include "fftw3.h"
#include <cmath>
class SpectrumVisualData : public ReferenceCounter {
public:
@ -20,12 +21,17 @@ public:
bool isView();
void setView(bool bView);
void setFFTAverageRate(float fftAverageRate);
float getFFTAverageRate();
void setCenterFrequency(long long centerFreq_in);
long long getCenterFrequency();
void setBandwidth(long bandwidth_in);
long getBandwidth();
int getDesiredInputSize();
void setup(int fftSize);
protected:
@ -45,12 +51,13 @@ private:
unsigned int lastDataSize;
fftwf_plan fftw_plan;
float fft_ceil_ma, fft_ceil_maa;
float fft_floor_ma, fft_floor_maa;
double fft_ceil_ma, fft_ceil_maa;
double fft_floor_ma, fft_floor_maa;
float fft_average_rate;
std::vector<float> fft_result;
std::vector<float> fft_result_ma;
std::vector<float> fft_result_maa;
std::vector<double> fft_result;
std::vector<double> fft_result_ma;
std::vector<double> fft_result_maa;
msresamp_crcf resampler;
double resamplerRatio;
@ -59,4 +66,74 @@ private:
std::vector<liquid_float_complex> shiftBuffer;
std::vector<liquid_float_complex> resampleBuffer;
int desiredInputSize;
};
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) {
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;
};

View File

@ -11,6 +11,28 @@ public:
virtual ~VisualProcessor() {
}
bool isInputEmpty() {
return input->empty();
}
bool isOutputEmpty() {
for (outputs_i = outputs.begin(); outputs_i != outputs.end(); outputs_i++) {
if ((*outputs_i)->full()) {
return false;
}
}
return true;
}
bool isAnyOutputEmpty() {
for (outputs_i = outputs.begin(); outputs_i != outputs.end(); outputs_i++) {
if (!(*outputs_i)->full()) {
return true;
}
}
return false;
}
void setInput(ThreadQueue<InputDataType *> *vis_in) {
busy_update.lock();
@ -60,24 +82,6 @@ protected:
}
}
}
bool isOutputEmpty() {
for (outputs_i = outputs.begin(); outputs_i != outputs.end(); outputs_i++) {
if (!(*outputs_i)->empty()) {
return false;
}
}
return true;
}
bool isAnyOutputEmpty() {
for (outputs_i = outputs.begin(); outputs_i != outputs.end(); outputs_i++) {
if ((*outputs_i)->empty()) {
return true;
}
}
return false;
}
ThreadQueue<InputDataType *> *input;
std::vector<ThreadQueue<OutputDataType *> *> outputs;
@ -90,10 +94,10 @@ template<class OutputDataType = ReferenceCounter>
class VisualDataDistributor : public VisualProcessor<OutputDataType, OutputDataType> {
protected:
void process() {
if (!VisualProcessor<OutputDataType, OutputDataType>::isOutputEmpty()) {
return;
}
while (!VisualProcessor<OutputDataType, OutputDataType>::input->empty()) {
if (!VisualProcessor<OutputDataType, OutputDataType>::isAnyOutputEmpty()) {
return;
}
OutputDataType *inp;
VisualProcessor<OutputDataType, OutputDataType>::input->pop(inp);
if (inp) {

View File

@ -76,24 +76,25 @@ void SDRPostThread::run() {
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005);
DemodulatorThreadIQData *visualDataOut = new DemodulatorThreadIQData;
std::cout << "SDR post-processing thread started.." << std::endl;
iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput");
iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput");
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOut");
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput");
ReBuffer<DemodulatorThreadIQData> buffers;
std::vector<liquid_float_complex> fpData;
std::vector<liquid_float_complex> dataOut;
iqDataInQueue->set_max_num_items(0);
while (!terminated) {
SDRThreadIQData *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);
int num_vis_samples = this->num_vis_samples;
if (data_in && data_in->data.size()) {
int dataSize = data_in->data.size()/2;
if (dataSize > fpData.capacity()) {
@ -104,7 +105,7 @@ void SDRPostThread::run() {
fpData.resize(dataSize);
dataOut.resize(dataSize);
}
if (swapIQ) {
for (int i = 0; i < dataSize; i++) {
fpData[i] = _lut_swap[*((uint16_t*)&data_in->data[2*i])];
@ -114,21 +115,16 @@ void SDRPostThread::run() {
fpData[i] = _lut[*((uint16_t*)&data_in->data[2*i])];
}
}
iirfilt_crcf_execute_block(dcFilter, &fpData[0], dataSize, &dataOut[0]);
if (iqDataOutQueue != NULL) {
DemodulatorThreadIQData *pipeDataOut = new DemodulatorThreadIQData;
pipeDataOut->frequency = data_in->frequency;
pipeDataOut->sampleRate = data_in->sampleRate;
pipeDataOut->data.assign(dataOut.begin(), dataOut.end());
iqDataOutQueue->push(pipeDataOut);
}
if (iqVisualQueue != NULL && iqVisualQueue->empty()) {
visualDataOut->busy_rw.lock();
DemodulatorThreadIQData *visualDataOut = visualDataBuffers.getBuffer();
visualDataOut->setRefCount(1);
if (num_vis_samples > dataOut.size()) {
num_vis_samples = dataOut.size();
}
if (visualDataOut->data.size() < num_vis_samples) {
if (visualDataOut->data.capacity() < num_vis_samples) {
@ -136,84 +132,90 @@ void SDRPostThread::run() {
}
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);
visualDataOut->busy_rw.unlock();
}
busy_demod.lock();
int activeDemods = 0;
bool pushedData = false;
if (demodulators.size()) {
std::vector<DemodulatorInstance *>::iterator i;
for (i = demodulators.begin(); i != demodulators.end(); i++) {
DemodulatorInstance *demod = *i;
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)) {
&& abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
continue;
}
activeDemods++;
}
if (demodulators.size()) {
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());
std::vector<DemodulatorInstance *>::iterator i;
for (i = demodulators.begin(); i != demodulators.end(); i++) {
DemodulatorInstance *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);
}
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->isActive()) {
continue;
if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) {
wxGetApp().setFrequency(demod->getFrequency());
}
if (demod->isFollow()) {
demod->setFollow(false);
} else if (!demod->isActive()) {
demod->setActive(true);
if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) {
wxGetApp().getDemodMgr().setActiveDemodulator(demod);
}
demodQueue->push(demodDataOut);
pushedData = true;
}
if (!pushedData) {
demodDataOut->setRefCount(0);
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;
} else {
demodDataOut->decRefCount();
}
}
if (!pushedData && iqDataOutQueue == NULL) {
demodDataOut->setRefCount(0);
}
}
busy_demod.unlock();
}
data_in->decRefCount();
@ -226,7 +228,7 @@ void SDRPostThread::run() {
iqVisualQueue->pop(visualDataDummy);
}
delete visualDataOut;
visualDataBuffers.purge();
std::cout << "SDR post-processing thread done." << std::endl;
}

View File

@ -30,6 +30,8 @@ protected:
iirfilt_crcf dcFilter;
int num_vis_samples;
std::atomic_bool swapIQ;
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
private:
std::vector<liquid_float_complex> _lut;

View File

@ -36,7 +36,11 @@ void initGLExtensions() {
std::cout << std::endl << "Supported GL Extensions: " << std::endl << extensions << std::endl << std::endl;
#ifdef __linux__
const GLint interval = 2;
#else
const GLint interval = 1;
#endif
#ifdef _WIN32
if (GLExtSupported("WGL_EXT_swap_control")) {

View File

@ -25,7 +25,7 @@ EVT_ENTER_WINDOW(MeterCanvas::OnMouseEnterWindow)
wxEND_EVENT_TABLE()
MeterCanvas::MeterCanvas(wxWindow *parent, int *attribList) :
InteractiveCanvas(parent, attribList), level(0), level_max(1), inputValue(0), userInputValue(0) {
InteractiveCanvas(parent, attribList), level(0), level_max(1), inputValue(0), userInputValue(0), showUserInput(true) {
glContext = new MeterContext(this, &wxGetApp().GetContext(this));
}
@ -58,6 +58,10 @@ float MeterCanvas::getInputValue() {
return userInputValue;
}
void MeterCanvas::setShowUserInput(bool showUserInput) {
this->showUserInput = showUserInput;
}
void MeterCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
wxPaintDC dc(this);
const wxSize ClientSize = GetClientSize();
@ -68,18 +72,23 @@ void MeterCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
glViewport(0, 0, ClientSize.x, ClientSize.y);
glContext->DrawBegin();
glContext->Draw(ThemeMgr::mgr.currentTheme->generalBackground.r, ThemeMgr::mgr.currentTheme->generalBackground.g, ThemeMgr::mgr.currentTheme->generalBackground.b, 0.5, 1.0);
if (mouseTracker.mouseInView()) {
glContext->Draw(0.4, 0.4, 0.4, 0.5, mouseTracker.getMouseY());
}
glContext->Draw(ThemeMgr::mgr.currentTheme->meterLevel.r, ThemeMgr::mgr.currentTheme->meterLevel.g, ThemeMgr::mgr.currentTheme->meterLevel.b, 0.5, level / level_max);
glContext->Draw(ThemeMgr::mgr.currentTheme->meterValue.r, ThemeMgr::mgr.currentTheme->meterValue.g, ThemeMgr::mgr.currentTheme->meterValue.b, 0.5, userInputValue / level_max);
if (showUserInput) {
glContext->Draw(ThemeMgr::mgr.currentTheme->meterValue.r, ThemeMgr::mgr.currentTheme->meterValue.g, ThemeMgr::mgr.currentTheme->meterValue.b, 0.5, userInputValue / level_max);
}
glContext->DrawEnd();
SwapBuffers();
}
void MeterCanvas::OnIdle(wxIdleEvent &event) {
event.Skip();
Refresh();
event.RequestMore();
}
void MeterCanvas::OnMouseMoved(wxMouseEvent& event) {

View File

@ -26,6 +26,7 @@ public:
void setInputValue(float slider_in);
bool inputChanged();
float getInputValue();
void setShowUserInput(bool showUserInput);
void setHelpTip(std::string tip);
@ -48,6 +49,8 @@ private:
float inputValue;
float userInputValue;
bool showUserInput;
std::string helpTip;
//
wxDECLARE_EVENT_TABLE();

View File

@ -72,7 +72,8 @@ void ModeSelectorCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
}
void ModeSelectorCanvas::OnIdle(wxIdleEvent &event) {
event.Skip();
Refresh();
event.RequestMore();
}
void ModeSelectorCanvas::OnMouseMoved(wxMouseEvent& event) {

View File

@ -87,7 +87,8 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
}
void ScopeCanvas::OnIdle(wxIdleEvent &event) {
event.Skip();
Refresh();
event.RequestMore();
}
ScopeRenderDataQueue *ScopeCanvas::getInputQueue() {

View File

@ -32,7 +32,8 @@ 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);
}
@ -84,7 +85,8 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
void SpectrumCanvas::OnIdle(wxIdleEvent &event) {
event.Skip();
Refresh();
event.RequestMore();
}

View File

@ -252,6 +252,10 @@ void TuningCanvas::OnIdle(wxIdleEvent &event) {
dragging = false;
}
}
if (mouseTracker.mouseInView() || changed()) {
Refresh();
}
event.RequestMore();
}
void TuningCanvas::OnMouseMoved(wxMouseEvent& event) {

View File

@ -68,8 +68,32 @@ void WaterfallCanvas::attachSpectrumCanvas(SpectrumCanvas *canvas_in) {
spectrumCanvas = canvas_in;
}
void WaterfallCanvas::processInputQueue() {
if (!glContext) {
return;
}
glContext->SetCurrent(*this);
while (!visualDataQueue.empty()) {
SpectrumVisualData *vData;
visualDataQueue.pop(vData);
if (vData) {
waterfallPanel.setPoints(vData->spectrum_points);
waterfallPanel.step();
vData->decRefCount();
}
}
}
void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
wxPaintDC dc(this);
// event.Skip();
}
void WaterfallCanvas::DoPaint() {
wxClientDC dc(this);
// wxPaintDC dc(this);
const wxSize ClientSize = GetClientSize();
long double currentZoom = zoom;
@ -90,8 +114,8 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
centerFreq = getCenterFrequency();
bw = getBandwidth();
bw = (long long) ceil((long double) bw * currentZoom);
if (bw < 100000) {
bw = 100000;
if (bw < 30000) {
bw = 30000;
}
if (mouseTracker.mouseInView()) {
long long mfreqA = getFrequencyAt(mouseTracker.getMouseX());
@ -139,18 +163,6 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
glContext->SetCurrent(*this);
initGLExtensions();
glViewport(0, 0, ClientSize.x, ClientSize.y);
if (!visualDataQueue.empty()) {
SpectrumVisualData *vData;
visualDataQueue.pop(vData);
if (vData) {
waterfallPanel.setPoints(vData->spectrum_points);
waterfallPanel.step();
vData->decRefCount();
}
}
glContext->BeginDraw(0,0,0);
@ -347,6 +359,8 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
}
}
void WaterfallCanvas::OnIdle(wxIdleEvent &event) {
// Refresh();
// event.RequestMore();
event.Skip();
}

View File

@ -26,7 +26,9 @@ public:
DragState getNextDragState();
void attachSpectrumCanvas(SpectrumCanvas *canvas_in);
void processInputQueue();
SpectrumVisualDataQueue *getVisualDataQueue();
void DoPaint();
private:
void OnPaint(wxPaintEvent& event);