Zoomed-in demodulator waterfall somewhat working

- update demod WF from demodulator is jittery
- adjustments with immediate update is awkward
This commit is contained in:
Charles J. Cliffe 2014-12-28 05:13:46 -05:00
parent a3f8bc08e7
commit 1e970f4373
17 changed files with 461 additions and 203 deletions

View File

@ -28,7 +28,7 @@ EVT_IDLE(AppFrame::OnIdle)
wxEND_EVENT_TABLE() wxEND_EVENT_TABLE()
AppFrame::AppFrame() : AppFrame::AppFrame() :
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) { wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) {
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL);
@ -64,15 +64,22 @@ AppFrame::AppFrame() :
demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0); demodTray->Add(demodOpts, 1, wxEXPAND | wxALL, 0);
demodWaterfallCanvas = new WaterfallCanvas(this, NULL);
demodWaterfallCanvas->Setup(1024,128);
demodWaterfallCanvas->SetView(DEFAULT_FREQ,300000);
demodTray->Add(demodWaterfallCanvas, 7, wxEXPAND | wxALL, 0);
scopeCanvas = new ScopeCanvas(this, NULL); scopeCanvas = new ScopeCanvas(this, NULL);
demodTray->Add(scopeCanvas, 7, wxEXPAND | wxALL, 0); demodTray->Add(scopeCanvas, 7, wxEXPAND | wxALL, 0);
vbox->Add(demodTray, 1, wxEXPAND | wxALL, 0); vbox->Add(demodTray, 1, wxEXPAND | wxALL, 0);
vbox->AddSpacer(2); vbox->AddSpacer(2);
spectrumCanvas = new SpectrumCanvas(this, NULL); spectrumCanvas = new SpectrumCanvas(this, NULL);
spectrumCanvas->Setup(2048);
vbox->Add(spectrumCanvas, 1, wxEXPAND | wxALL, 0); vbox->Add(spectrumCanvas, 1, wxEXPAND | wxALL, 0);
vbox->AddSpacer(2); vbox->AddSpacer(2);
waterfallCanvas = new WaterfallCanvas(this, NULL); waterfallCanvas = new WaterfallCanvas(this, NULL);
waterfallCanvas->Setup(2048,512);
vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0); vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0);
this->SetSizer(vbox); this->SetSizer(vbox);
@ -129,14 +136,27 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
// std::this_thread::sleep_for(std::chrono::milliseconds(4)); // std::this_thread::sleep_for(std::chrono::milliseconds(4));
// std::this_thread::yield(); // std::this_thread::yield();
//#endif //#endif
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
if (demod) {
if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) {
demodWaterfallCanvas->SetCenterFrequency(demod->getParams().frequency);
}
unsigned int demodBw = (unsigned int) ceil((float) demod->getParams().bandwidth * 1.5);
if (demodBw != demodWaterfallCanvas->GetBandwidth()) {
demodWaterfallCanvas->SetBandwidth(demodBw);
}
}
if (!wxGetApp().getIQVisualQueue()->empty()) { if (!wxGetApp().getIQVisualQueue()->empty()) {
DemodulatorThreadIQData *iqData; DemodulatorThreadIQData *iqData;
wxGetApp().getIQVisualQueue()->pop(iqData); wxGetApp().getIQVisualQueue()->pop(iqData);
if (iqData && iqData->data.size()) { if (iqData && iqData->data.size()) {
spectrumCanvas->setData(&iqData->data); spectrumCanvas->setData(iqData);
waterfallCanvas->setData(&iqData->data); waterfallCanvas->setData(iqData);
demodWaterfallCanvas->setData(iqData);
delete iqData; delete iqData;
} else { } else {
std::cout << "Incoming IQ data empty?" << std::endl; std::cout << "Incoming IQ data empty?" << std::endl;
@ -148,8 +168,8 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
AudioThreadInput *demodAudioData; AudioThreadInput *demodAudioData;
wxGetApp().getAudioVisualQueue()->pop(demodAudioData); wxGetApp().getAudioVisualQueue()->pop(demodAudioData);
if (demodAudioData && demodAudioData->data.size()) { if (demodAudioData && demodAudioData->data.size()) {
if (scopeCanvas->waveform_points.size() != demodAudioData->data.size()*2) { if (scopeCanvas->waveform_points.size() != demodAudioData->data.size() * 2) {
scopeCanvas->waveform_points.resize(demodAudioData->data.size()*2); scopeCanvas->waveform_points.resize(demodAudioData->data.size() * 2);
} }
for (int i = 0, iMax = demodAudioData->data.size(); i < iMax; i++) { for (int i = 0, iMax = demodAudioData->data.size(); i < iMax; i++) {

View File

@ -23,7 +23,8 @@ private:
ScopeCanvas *scopeCanvas; ScopeCanvas *scopeCanvas;
SpectrumCanvas *spectrumCanvas; SpectrumCanvas *spectrumCanvas;
WaterfallCanvas *waterfallCanvas; WaterfallCanvas *waterfallCanvas;
WaterfallCanvas *demodWaterfallCanvas;
// event table // event table
wxDECLARE_EVENT_TABLE(); wxDECLARE_EVENT_TABLE();
}; };

View File

@ -28,6 +28,7 @@ bool CubicSDR::OnInit() {
sdrThread = new SDRThread(threadCmdQueueSDR); sdrThread = new SDRThread(threadCmdQueueSDR);
sdrPostThread = new SDRPostThread(); sdrPostThread = new SDRPostThread();
sdrPostThread->setNumVisSamples(2048);
iqPostDataQueue = new SDRThreadIQDataQueue; iqPostDataQueue = new SDRThreadIQDataQueue;
iqVisualQueue = new DemodulatorThreadInputQueue; iqVisualQueue = new DemodulatorThreadInputQueue;

View File

@ -7,7 +7,7 @@
#define BUF_SIZE (16384*4) #define BUF_SIZE (16384*4)
#define SRATE 2500000 #define SRATE 2500000
#endif #endif
#define FFT_SIZE 2048 #define DEFAULT_FFT_SIZE 2048
#define DEFAULT_FREQ 98900000 #define DEFAULT_FREQ 98900000
#define AUDIO_FREQUENCY 44100 #define AUDIO_FREQUENCY 44100

View File

@ -367,7 +367,7 @@ void AudioThread::setActive(bool state) {
while (!inputQueue->empty()) { // flush queue while (!inputQueue->empty()) { // flush queue
inputQueue->pop(dummy); inputQueue->pop(dummy);
if (dummy) { if (dummy) {
delete dummy; dummy->decRefCount();
} }
} }
deviceController[parameters.deviceId]->bindThread(this); deviceController[parameters.deviceId]->bindThread(this);
@ -376,7 +376,7 @@ void AudioThread::setActive(bool state) {
while (!inputQueue->empty()) { // flush queue while (!inputQueue->empty()) { // flush queue
inputQueue->pop(dummy); inputQueue->pop(dummy);
if (dummy) { if (dummy) {
delete dummy; dummy->decRefCount();
} }
} }
} }

View File

@ -9,8 +9,8 @@
DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn, DemodulatorThreadPostInputQueue* pQueueOut, DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn, DemodulatorThreadPostInputQueue* pQueueOut,
DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) : DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) :
inputQueue(pQueueIn), postInputQueue(pQueueOut), terminated(false), initialized(false), audio_resampler(NULL), stereo_resampler(NULL), resample_ratio(1), audio_resample_ratio( inputQueue(pQueueIn), postInputQueue(pQueueOut), terminated(false), initialized(false), audio_resampler(NULL), stereo_resampler(NULL), resample_ratio(
1), resampler(NULL), commandQueue(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl( 1), audio_resample_ratio(1), resampler(NULL), commandQueue(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl(
threadQueueControl) { threadQueueControl) {
float kf = 0.5; // modulation factor float kf = 0.5; // modulation factor
@ -53,7 +53,6 @@ void DemodulatorPreThread::initialize() {
} }
stereo_resampler = msresamp_rrrf_create(audio_resample_ratio, As); stereo_resampler = msresamp_rrrf_create(audio_resample_ratio, As);
initialized = true; initialized = true;
// std::cout << "inputResampleRate " << params.bandwidth << std::endl; // std::cout << "inputResampleRate " << params.bandwidth << std::endl;
@ -69,12 +68,12 @@ DemodulatorPreThread::~DemodulatorPreThread() {
#ifdef __APPLE__ #ifdef __APPLE__
void *DemodulatorPreThread::threadMain() { void *DemodulatorPreThread::threadMain() {
#else #else
void DemodulatorPreThread::threadMain() { void DemodulatorPreThread::threadMain() {
#endif #endif
#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) - 1;
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
@ -162,7 +161,7 @@ void DemodulatorPreThread::threadMain() {
out_buf_data.resize(bufSize); out_buf_data.resize(bufSize);
} }
in_buf_data.assign(inp->data.begin(),inp->data.end()); in_buf_data.assign(inp->data.begin(), inp->data.end());
liquid_float_complex *in_buf = &in_buf_data[0]; liquid_float_complex *in_buf = &in_buf_data[0];
liquid_float_complex *out_buf = &out_buf_data[0]; liquid_float_complex *out_buf = &out_buf_data[0];

View File

@ -6,7 +6,7 @@
#include <deque> #include <deque>
SDRPostThread::SDRPostThread() : SDRPostThread::SDRPostThread() :
sample_rate(SRATE), iqDataOutQueue(NULL), iqDataInQueue(NULL), iqVisualQueue(NULL), terminated(false), dcFilter(NULL) { sample_rate(SRATE), iqDataOutQueue(NULL), iqDataInQueue(NULL), iqVisualQueue(NULL), terminated(false), dcFilter(NULL), num_vis_samples(2048) {
} }
SDRPostThread::~SDRPostThread() { SDRPostThread::~SDRPostThread() {
@ -34,6 +34,14 @@ void SDRPostThread::setIQVisualQueue(DemodulatorThreadInputQueue *iqVisQueue) {
iqVisualQueue = iqVisQueue; iqVisualQueue = iqVisQueue;
} }
void SDRPostThread::setNumVisSamples(int num_vis_samples_in) {
num_vis_samples = num_vis_samples_in;
}
int SDRPostThread::getNumVisSamples() {
return num_vis_samples;
}
void SDRPostThread::threadMain() { void SDRPostThread::threadMain() {
int n_read; int n_read;
double seconds = 0.0; double seconds = 0.0;
@ -89,7 +97,9 @@ void SDRPostThread::threadMain() {
if (iqVisualQueue != NULL && iqVisualQueue.load()->empty()) { if (iqVisualQueue != NULL && iqVisualQueue.load()->empty()) {
DemodulatorThreadIQData *visualDataOut = new DemodulatorThreadIQData; DemodulatorThreadIQData *visualDataOut = new DemodulatorThreadIQData;
visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + FFT_SIZE); visualDataOut->frequency = data_in->frequency;
visualDataOut->bandwidth = data_in->bandwidth;
visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + num_vis_samples);
iqVisualQueue.load()->push(visualDataOut); iqVisualQueue.load()->push(visualDataOut);
} }

View File

@ -15,6 +15,9 @@ public:
void setIQDataOutQueue(DemodulatorThreadInputQueue* iqDataQueue); void setIQDataOutQueue(DemodulatorThreadInputQueue* iqDataQueue);
void setIQVisualQueue(DemodulatorThreadInputQueue* iqVisQueue); void setIQVisualQueue(DemodulatorThreadInputQueue* iqVisQueue);
void setNumVisSamples(int num_vis_samples_in);
int getNumVisSamples();
void threadMain(); void threadMain();
void terminate(); void terminate();
@ -30,4 +33,5 @@ protected:
std::vector<DemodulatorInstance *> demodulators_remove; std::vector<DemodulatorInstance *> demodulators_remove;
std::atomic<bool> terminated; std::atomic<bool> terminated;
iirfilt_crcf dcFilter; iirfilt_crcf dcFilter;
int num_vis_samples;
}; };

View File

@ -92,7 +92,7 @@ GLFont &PrimaryGLContext::getFont(GLFontSize esize) {
return fonts[esize]; return fonts[esize];
} }
void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float g, float b) { void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float g, float b, int center_freq, int srate) {
if (!demod) { if (!demod) {
return; return;
} }
@ -103,7 +103,11 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float
float viewHeight = (float) vp[3]; float viewHeight = (float) vp[3];
float viewWidth = (float) vp[2]; float viewWidth = (float) vp[2];
float uxPos = (float) (demod->getParams().frequency - (wxGetApp().getFrequency() - SRATE / 2)) / (float) SRATE; if (center_freq == -1) {
center_freq = wxGetApp().getFrequency();
}
float uxPos = (float) (demod->getParams().frequency - (center_freq - srate / 2)) / (float) srate;
uxPos = (uxPos - 0.5) * 2.0; uxPos = (uxPos - 0.5) * 2.0;
glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D);
@ -112,7 +116,7 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float
glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR); glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR);
glColor4f(r, g, b, 0.6); glColor4f(r, g, b, 0.6);
float ofs = ((float) demod->getParams().bandwidth) / (float) SRATE; float ofs = ((float) demod->getParams().bandwidth) / (float) srate;
glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR); glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR);
glColor4f(r, g, b, 0.2); glColor4f(r, g, b, 0.2);
@ -149,12 +153,16 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float
} }
void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, float b) { void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, float b, int center_freq, int srate) {
if (!demod) { if (!demod) {
return; return;
} }
float uxPos = (float) (demod->getParams().frequency - (wxGetApp().getFrequency() - SRATE / 2)) / (float) SRATE; if (center_freq == -1) {
center_freq = wxGetApp().getFrequency();
}
float uxPos = (float) (demod->getParams().frequency - (center_freq - srate / 2)) / (float) srate;
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D);
@ -167,7 +175,7 @@ void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, f
glVertex3f((uxPos - 0.5) * 2.0, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0, 1.0, 0.0);
glVertex3f((uxPos - 0.5) * 2.0, -1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0, -1.0, 0.0);
float ofs = ((float) demod->getParams().bandwidth) / (float) SRATE; float ofs = ((float) demod->getParams().bandwidth) / (float) srate;
glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0);
glVertex3f((uxPos - 0.5) * 2.0 - ofs, -1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 - ofs, -1.0, 0.0);
@ -191,7 +199,7 @@ void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, f
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} }
void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b, float w) { void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b, float w, int center_freq, int srate) {
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
int bw = 0; int bw = 0;
@ -218,7 +226,7 @@ void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b,
if (w) { if (w) {
ofs = w; ofs = w;
} else { } else {
ofs = ((float) bw) / (float) SRATE; ofs = ((float) bw) / (float) srate;
} }
glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0); glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0);

View File

@ -23,9 +23,9 @@ public:
void BeginDraw(); void BeginDraw();
void EndDraw(); void EndDraw();
void DrawFreqSelector(float uxPos, float r = 1, float g = 1, float b = 1, float w = 0); void DrawFreqSelector(float uxPos, float r = 1, float g = 1, float b = 1, float w = 0, int center_freq = -1, int srate = SRATE);
void DrawDemod(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1); void DrawDemod(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1, int center_freq = -1, int srate = SRATE);
void DrawDemodInfo(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1); void DrawDemodInfo(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1, int center_freq = -1, int srate = SRATE);
static GLFont &getFont(GLFontSize esize); static GLFont &getFont(GLFontSize esize);

View File

@ -27,20 +27,9 @@ wxEND_EVENT_TABLE()
SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0) { wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), fft_size(0), in(NULL), out(NULL), plan(NULL) {
int in_block_size = FFT_SIZE;
int out_block_size = FFT_SIZE;
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * in_block_size);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size);
plan = fftw_plan_dft_1d(out_block_size, in, out, FFTW_FORWARD, FFTW_MEASURE);
fft_ceil_ma = fft_ceil_maa = 100.0;
fft_floor_ma = fft_floor_maa = 0.0;
glContext = new SpectrumContext(this, &wxGetApp().GetContext(this)); glContext = new SpectrumContext(this, &wxGetApp().GetContext(this));
timer.start();
mTracker.setTarget(this); mTracker.setTarget(this);
mTracker.setVertDragLock(true); mTracker.setVertDragLock(true);
@ -48,6 +37,33 @@ SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
SetCursor(wxCURSOR_SIZEWE); SetCursor(wxCURSOR_SIZEWE);
} }
void SpectrumCanvas::Setup(int fft_size_in) {
if (fft_size == fft_size_in) {
return;
}
fft_size = fft_size_in;
if (in) {
free(in);
}
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
if (out) {
free(out);
}
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
if (plan) {
fftw_destroy_plan(plan);
}
plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE);
fft_ceil_ma = fft_ceil_maa = 100.0;
fft_floor_ma = fft_floor_maa = 0.0;
timer.start();
}
SpectrumCanvas::~SpectrumCanvas() { SpectrumCanvas::~SpectrumCanvas() {
} }
@ -73,14 +89,23 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
SwapBuffers(); SwapBuffers();
} }
void SpectrumCanvas::setData(std::vector<liquid_float_complex> *data) { void SpectrumCanvas::setData(DemodulatorThreadIQData *input) {
if (!input) {
return;
}
std::vector<liquid_float_complex> *data = &input->data;
if (data && data->size()) { if (data && data->size()) {
if (spectrum_points.size() < FFT_SIZE * 2) { if (fft_size != data->size()) {
spectrum_points.resize(FFT_SIZE * 2); Setup(data->size());
}
if (spectrum_points.size() < fft_size * 2) {
if (spectrum_points.capacity() < fft_size * 2) {
spectrum_points.reserve(fft_size * 2);
}
spectrum_points.resize(fft_size * 2);
} }
for (int i = 0; i < FFT_SIZE; i++) { for (int i = 0; i < fft_size; i++) {
in[i][0] = (*data)[i].real; in[i][0] = (*data)[i].real;
in[i][1] = (*data)[i].imag; in[i][1] = (*data)[i].imag;
} }
@ -89,31 +114,34 @@ void SpectrumCanvas::setData(std::vector<liquid_float_complex> *data) {
double fft_ceil = 0, fft_floor = 1; double fft_ceil = 0, fft_floor = 1;
if (fft_result.size() < FFT_SIZE) { if (fft_result.size() != fft_size) {
fft_result.resize(FFT_SIZE); if (fft_result.capacity() < fft_size) {
fft_result_ma.resize(FFT_SIZE); fft_result.reserve(fft_size);
fft_result_maa.resize(FFT_SIZE); fft_result_ma.reserve(fft_size);
fft_result_maa.reserve(fft_size);
}
fft_result.resize(fft_size);
fft_result_ma.resize(fft_size);
fft_result_maa.resize(fft_size);
} }
int n; int n;
for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) { for (int i = 0, iMax = fft_size / 2; i < iMax; i++) {
n = (i == 0) ? 1 : i; n = (i == 0) ? 1 : i;
double a = out[n][0]; double a = out[n][0];
double b = out[n][1]; double b = out[n][1];
double c = sqrt(a * a + b * b); double c = sqrt(a * a + b * b);
// n = (i == FFT_SIZE / 2) ? (FFT_SIZE / 2 + 1) : i; // n = (i == FFT_SIZE / 2) ? (FFT_SIZE / 2 + 1) : i;
double x = out[FFT_SIZE / 2 + n][0]; double x = out[fft_size / 2 + n][0];
double y = out[FFT_SIZE / 2 + n][1]; double y = out[fft_size / 2 + n][1];
double z = sqrt(x * x + y * y); double z = sqrt(x * x + y * y);
fft_result[i] = (z); fft_result[i] = (z);
fft_result[FFT_SIZE / 2 + i] = (c); fft_result[fft_size / 2 + i] = (c);
} }
float time_slice = (float) SRATE / (float) (BUF_SIZE / 2); for (int i = 0, iMax = fft_size; i < iMax; i++) {
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) {
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65; 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_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65;
@ -136,7 +164,7 @@ void SpectrumCanvas::setData(std::vector<liquid_float_complex> *data) {
// fftw_execute(plan[1]); // fftw_execute(plan[1]);
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { for (int i = 0, iMax = fft_size; i < iMax; i++) {
float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa)); float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa));
spectrum_points[i * 2] = ((float) i / (float) iMax); spectrum_points[i * 2] = ((float) i / (float) iMax);
spectrum_points[i * 2 + 1] = v; spectrum_points[i * 2 + 1] = v;

View File

@ -15,9 +15,10 @@
class SpectrumCanvas: public wxGLCanvas { class SpectrumCanvas: public wxGLCanvas {
public: public:
SpectrumCanvas(wxWindow *parent, int *attribList = NULL); SpectrumCanvas(wxWindow *parent, int *attribList = NULL);
void Setup(int fft_size_in);
~SpectrumCanvas(); ~SpectrumCanvas();
void setData(std::vector<liquid_float_complex> *data); void setData(DemodulatorThreadIQData *input);
private: private:
void OnPaint(wxPaintEvent& event); void OnPaint(wxPaintEvent& event);
@ -47,6 +48,7 @@ private:
SpectrumContext *glContext; SpectrumContext *glContext;
Timer timer; Timer timer;
float frameTimer; float frameTimer;
int fft_size;
MouseTracker mTracker; MouseTracker mTracker;
// event table // event table

View File

@ -14,4 +14,5 @@ public:
void Draw(std::vector<float> &points); void Draw(std::vector<float> &points);
private: private:
int fft_size;
}; };

View File

@ -29,39 +29,93 @@ EVT_ENTER_WINDOW(WaterfallCanvas::mouseEnterWindow)
wxEND_EVENT_TABLE() wxEND_EVENT_TABLE()
WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState( wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState(
WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false) { WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan(NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth(0) {
int in_block_size = FFT_SIZE;
int out_block_size = FFT_SIZE;
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * in_block_size);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size);
plan = fftw_plan_dft_1d(out_block_size, in, out, FFTW_FORWARD, FFTW_MEASURE);
fft_ceil_ma = fft_ceil_maa = 100.0;
fft_floor_ma = fft_floor_maa = 0.0;
glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); glContext = new WaterfallContext(this, &wxGetApp().GetContext(this));
timer.start();
nco_shift = nco_crcf_create(LIQUID_NCO);
shift_freq = 0;
mTracker.setTarget(this); mTracker.setTarget(this);
SetCursor(wxCURSOR_CROSS); SetCursor(wxCURSOR_CROSS);
} }
WaterfallCanvas::~WaterfallCanvas() { WaterfallCanvas::~WaterfallCanvas() {
nco_crcf_destroy(nco_shift);
}
void WaterfallCanvas::SetView(int center_freq_in, int bandwidth_in) {
isView = true;
center_freq = center_freq_in;
bandwidth = bandwidth_in;
last_bandwidth = 0;
}
void WaterfallCanvas::DisableView() {
isView = false;
}
void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) {
if (fft_size == fft_size_in && waterfall_lines_in == waterfall_lines) {
return;
}
fft_size = fft_size_in;
waterfall_lines = waterfall_lines_in;
if (in) {
free(in);
}
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
if (out) {
free(out);
}
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * fft_size);
if (plan) {
fftw_destroy_plan(plan);
}
plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE);
fft_ceil_ma = fft_ceil_maa = 100.0;
fft_floor_ma = fft_floor_maa = 0.0;
glContext->Setup(fft_size, waterfall_lines);
timer.start();
} }
int WaterfallCanvas::GetFrequencyAt(float x) { int WaterfallCanvas::GetFrequencyAt(float x) {
int iqCenterFreq = GetCenterFrequency();
int center_freq = wxGetApp().getFrequency(); int iqBandwidth = GetBandwidth();
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) x * (float) SRATE); int freq = iqCenterFreq - (int) (0.5 * (float) iqBandwidth) + (int) ((float) x * (float) iqBandwidth);
return freq; return freq;
} }
void WaterfallCanvas::SetCenterFrequency(unsigned int center_freq_in) {
center_freq = center_freq_in;
}
unsigned int WaterfallCanvas::GetCenterFrequency() {
if (isView) {
return center_freq;
} else {
return (unsigned int)wxGetApp().getFrequency();
}
}
void WaterfallCanvas::SetBandwidth(unsigned int bandwidth_in) {
bandwidth = bandwidth_in;
}
unsigned int WaterfallCanvas::GetBandwidth() {
if (isView) {
return bandwidth;
} else {
return SRATE;
}
}
void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
wxPaintDC dc(this); wxPaintDC dc(this);
const wxSize ClientSize = GetClientSize(); const wxSize ClientSize = GetClientSize();
@ -80,6 +134,9 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
bool isNew = shiftDown bool isNew = shiftDown
|| (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive()); || (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive());
int currentBandwidth = GetBandwidth();
int currentCenterFreq = GetCenterFrequency();
if (mTracker.mouseInView()) { if (mTracker.mouseInView()) {
if (nextDragState == WF_DRAG_RANGE) { if (nextDragState == WF_DRAG_RANGE) {
if (mTracker.mouseDown()) { if (mTracker.mouseDown()) {
@ -87,47 +144,49 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
float centerPos = mTracker.getOriginMouseX() + width / 2.0; float centerPos = mTracker.getOriginMouseX() + width / 2.0;
if (isNew) { if (isNew) {
glContext->DrawDemod(lastActiveDemodulator); glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
glContext->DrawFreqSelector(centerPos, 0, 1, 0, width ? width : (1.0 / (float) ClientSize.x)); glContext->DrawFreqSelector(centerPos, 0, 1, 0, width ? width : (1.0 / (float) ClientSize.x), currentCenterFreq,
currentBandwidth);
} else { } else {
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0); glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth);
glContext->DrawFreqSelector(centerPos, 1, 1, 0, width ? width : (1.0 / (float) ClientSize.x)); glContext->DrawFreqSelector(centerPos, 1, 1, 0, width ? width : (1.0 / (float) ClientSize.x), currentCenterFreq,
currentBandwidth);
} }
} else { } else {
if (isNew) { if (isNew) {
glContext->DrawDemod(lastActiveDemodulator); glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 1.0 / (float) ClientSize.x); glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 1.0 / (float) ClientSize.x, currentCenterFreq, currentBandwidth);
} else { } else {
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0); glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth);
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 1.0 / (float) ClientSize.x); glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 1.0 / (float) ClientSize.x, currentCenterFreq, currentBandwidth);
} }
} }
} else { } else {
if (activeDemodulator == NULL) { if (activeDemodulator == NULL) {
if (lastActiveDemodulator) { if (lastActiveDemodulator) {
if (isNew) { if (isNew) {
glContext->DrawDemod(lastActiveDemodulator); glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0); glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 0, currentCenterFreq, currentBandwidth);
} else { } else {
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0); glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth);
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0); glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 0, currentCenterFreq, currentBandwidth);
} }
} else { } else {
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0); glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 0, currentCenterFreq, currentBandwidth);
} }
} else { } else {
if (lastActiveDemodulator) { if (lastActiveDemodulator) {
glContext->DrawDemod(lastActiveDemodulator); glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
} }
glContext->DrawDemod(activeDemodulator, 1, 1, 0); glContext->DrawDemod(activeDemodulator, 1, 1, 0, currentCenterFreq, currentBandwidth);
} }
} }
} else { } else {
if (activeDemodulator) { if (activeDemodulator) {
glContext->DrawDemod(activeDemodulator); glContext->DrawDemod(activeDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
} }
if (lastActiveDemodulator) { if (lastActiveDemodulator) {
glContext->DrawDemod(lastActiveDemodulator); glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
} }
} }
@ -135,7 +194,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
if (activeDemodulator == demods[i] || lastActiveDemodulator == demods[i]) { if (activeDemodulator == demods[i] || lastActiveDemodulator == demods[i]) {
continue; continue;
} }
glContext->DrawDemod(demods[i]); glContext->DrawDemod(demods[i], 1, 1, 1, currentCenterFreq, currentBandwidth);
} }
glContext->EndDraw(); glContext->EndDraw();
@ -161,102 +220,178 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator(); DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
unsigned int freq; unsigned int freq;
switch (event.GetKeyCode()) { if (!isView) {
case WXK_RIGHT: switch (event.GetKeyCode()) {
freq = wxGetApp().getFrequency(); case WXK_RIGHT:
if (shiftDown) { freq = wxGetApp().getFrequency();
freq += SRATE * 10; if (shiftDown) {
} else { freq += SRATE * 10;
freq += SRATE / 2; } else {
} freq += SRATE / 2;
wxGetApp().setFrequency(freq); }
((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); wxGetApp().setFrequency(freq);
break; ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq));
case WXK_LEFT:
freq = wxGetApp().getFrequency();
if (shiftDown) {
freq -= SRATE * 10;
} else {
freq -= SRATE / 2;
}
wxGetApp().setFrequency(freq);
((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq));
break;
case 'D':
case WXK_DELETE:
if (!activeDemod) {
break; break;
} case WXK_LEFT:
wxGetApp().removeDemodulator(activeDemod); freq = wxGetApp().getFrequency();
wxGetApp().getDemodMgr().deleteThread(activeDemod); if (shiftDown) {
break; freq -= SRATE * 10;
case 'S': } else {
if (!activeDemod) { freq -= SRATE / 2;
}
wxGetApp().setFrequency(freq);
((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq));
break; break;
case 'D':
case WXK_DELETE:
if (!activeDemod) {
break;
}
wxGetApp().removeDemodulator(activeDemod);
wxGetApp().getDemodMgr().deleteThread(activeDemod);
break;
case 'S':
if (!activeDemod) {
break;
}
if (activeDemod->isSquelchEnabled()) {
activeDemod->setSquelchEnabled(false);
} else {
activeDemod->squelchAuto();
}
break;
case WXK_SPACE:
if (!activeDemod) {
break;
}
if (activeDemod->isStereo()) {
activeDemod->setStereo(false);
} else {
activeDemod->setStereo(true);
}
break;
default:
event.Skip();
return;
} }
if (activeDemod->isSquelchEnabled()) {
activeDemod->setSquelchEnabled(false);
} else {
activeDemod->squelchAuto();
}
break;
case WXK_SPACE:
if (!activeDemod) {
break;
}
if (activeDemod->isStereo()) {
activeDemod->setStereo(false);
} else {
activeDemod->setStereo(true);
}
break;
default:
event.Skip();
return;
} }
} }
void WaterfallCanvas::setData(std::vector<liquid_float_complex> *data) { void WaterfallCanvas::setData(DemodulatorThreadIQData *input) {
if (!input) {
return;
}
std::vector<liquid_float_complex> *data = &input->data;
if (data && data->size()) { if (data && data->size()) {
if (spectrum_points.size() < FFT_SIZE * 2) { if (fft_size != data->size() && !isView) {
spectrum_points.resize(FFT_SIZE * 2); Setup(data->size(), waterfall_lines);
} }
for (int i = 0; i < FFT_SIZE; i++) { if (last_bandwidth != bandwidth && !isView) {
in[i][0] = (*data)[i].real; Setup(bandwidth, waterfall_lines);
in[i][1] = (*data)[i].imag; }
if (spectrum_points.size() < fft_size * 2) {
spectrum_points.resize(fft_size * 2);
}
if (isView) {
if (!input->frequency || !input->bandwidth) {
return;
}
if (center_freq != input->frequency) {
if (((int)center_freq - (int)input->frequency) != shift_freq || last_input_bandwidth != input->bandwidth) {
if ((int)input->frequency - abs((int)center_freq) < (int) ((float) ((float) SRATE / 2.0))) {
shift_freq = (int)center_freq - (int)input->frequency;
nco_crcf_reset(nco_shift);
nco_crcf_set_frequency(nco_shift, (2.0 * M_PI) * (((float) abs(shift_freq)) / ((float) input->bandwidth)));
}
}
if (shift_buffer.size() != input->data.size()) {
if (shift_buffer.capacity() < input->data.size()) {
shift_buffer.reserve(input->data.size());
}
shift_buffer.resize(input->data.size());
}
if (shift_freq < 0) {
nco_crcf_mix_block_up(nco_shift, &input->data[0], &shift_buffer[0], input->data.size());
} else {
nco_crcf_mix_block_down(nco_shift, &input->data[0], &shift_buffer[0], input->data.size());
}
} else {
shift_buffer.assign(input->data.begin(), input->data.end());
}
if (!resampler || bandwidth != last_bandwidth || last_input_bandwidth != input->bandwidth) {
resample_ratio = (float) (bandwidth) / (float) input->bandwidth;
float As = 60.0f;
if (resampler) {
msresamp_crcf_destroy(resampler);
}
resampler = msresamp_crcf_create(resample_ratio, As);
last_bandwidth = bandwidth;
last_input_bandwidth = input->bandwidth;
}
int out_size = ceil((float) (input->data.size()) * resample_ratio);
if (resampler_buffer.size() != out_size) {
if (resampler_buffer.capacity() < out_size) {
resampler_buffer.reserve(out_size);
}
resampler_buffer.resize(out_size);
}
unsigned int num_written;
msresamp_crcf_execute(resampler, &shift_buffer[0], input->data.size(), &resampler_buffer[0], &num_written);
resampler_buffer.resize(fft_size);
for (int i = 0; i < fft_size; i++) {
in[i][0] = resampler_buffer[i].real;
in[i][1] = resampler_buffer[i].imag;
}
} else {
for (int i = 0; i < fft_size; i++) {
in[i][0] = (*data)[i].real;
in[i][1] = (*data)[i].imag;
}
} }
fftw_execute(plan); fftw_execute(plan);
double fft_ceil = 0, fft_floor = 1; double fft_ceil = 0, fft_floor = 1;
if (fft_result.size() < FFT_SIZE) { if (fft_result.size() < fft_size) {
fft_result.resize(FFT_SIZE); fft_result.resize(fft_size);
fft_result_ma.resize(FFT_SIZE); fft_result_ma.resize(fft_size);
fft_result_maa.resize(FFT_SIZE); fft_result_maa.resize(fft_size);
} }
int n; int n;
for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) { for (int i = 0, iMax = fft_size / 2; i < iMax; i++) {
n = (i == 0) ? 1 : i; n = (i == 0) ? 1 : i;
double a = out[n][0]; double a = out[n][0];
double b = out[n][1]; double b = out[n][1];
double c = sqrt(a * a + b * b); double c = sqrt(a * a + b * b);
// n = (i == FFT_SIZE / 2) ? (FFT_SIZE / 2 + 1) : i; double x = out[fft_size / 2 + n][0];
double x = out[FFT_SIZE / 2 + n][0]; double y = out[fft_size / 2 + n][1];
double y = out[FFT_SIZE / 2 + n][1];
double z = sqrt(x * x + y * y); double z = sqrt(x * x + y * y);
fft_result[i] = (z); fft_result[i] = (z);
fft_result[FFT_SIZE / 2 + i] = (c); fft_result[fft_size / 2 + i] = (c);
} }
float time_slice = (float) SRATE / (float) (BUF_SIZE / 2); for (int i = 0, iMax = fft_size; i < iMax; i++) {
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) {
fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65; 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_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65;
@ -277,12 +412,11 @@ void WaterfallCanvas::setData(std::vector<liquid_float_complex> *data) {
fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.01; fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.01;
fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.01; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.01;
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { for (int i = 0, iMax = fft_size; i < iMax; i++) {
float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa)); float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa));
spectrum_points[i * 2] = ((float) i / (float) iMax); spectrum_points[i * 2] = ((float) i / (float) iMax);
spectrum_points[i * 2 + 1] = v; spectrum_points[i * 2 + 1] = v;
} }
} }
} }
@ -310,7 +444,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
} }
if (dragState == WF_DRAG_BANDWIDTH_LEFT || dragState == WF_DRAG_BANDWIDTH_RIGHT) { if (dragState == WF_DRAG_BANDWIDTH_LEFT || dragState == WF_DRAG_BANDWIDTH_RIGHT) {
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) SRATE) * 2; int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) GetBandwidth()) * 2;
if (dragState == WF_DRAG_BANDWIDTH_LEFT) { if (dragState == WF_DRAG_BANDWIDTH_LEFT) {
bwDiff = -bwDiff; bwDiff = -bwDiff;
@ -326,8 +460,8 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
if (activeDemodulatorBandwidth < 2000) { if (activeDemodulatorBandwidth < 2000) {
activeDemodulatorBandwidth = 2000; activeDemodulatorBandwidth = 2000;
} }
if (activeDemodulatorBandwidth > SRATE) { if (activeDemodulatorBandwidth > GetBandwidth()) {
activeDemodulatorBandwidth = SRATE; activeDemodulatorBandwidth = GetBandwidth();
} }
command.int_value = activeDemodulatorBandwidth; command.int_value = activeDemodulatorBandwidth;
@ -335,7 +469,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
} }
if (dragState == WF_DRAG_FREQUENCY) { if (dragState == WF_DRAG_FREQUENCY) {
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) SRATE); int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) GetBandwidth());
if (!activeDemodulatorFrequency) { if (!activeDemodulatorFrequency) {
activeDemodulatorFrequency = demod->getParams().frequency; activeDemodulatorFrequency = demod->getParams().frequency;
@ -363,7 +497,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
mTracker.setHorizDragLock(false); mTracker.setHorizDragLock(false);
} else if (demodsHover->size()) { } else if (demodsHover->size()) {
int hovered = -1; int hovered = -1;
int near_dist = SRATE; int near_dist = GetBandwidth();
DemodulatorInstance *activeDemodulator = NULL; DemodulatorInstance *activeDemodulator = NULL;
@ -397,7 +531,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
int freqDiff = ((int) activeDemodulator->getParams().frequency - freqPos); int freqDiff = ((int) activeDemodulator->getParams().frequency - freqPos);
if (abs(freqDiff) > (activeDemodulator->getParams().bandwidth / 3)) { if (abs(freqDiff) > (activeDemodulator->getParams().bandwidth / 3)) {
SetCursor(wxCURSOR_SIZEWE); SetCursor (wxCURSOR_SIZEWE);
if (freqDiff > 0) { if (freqDiff > 0) {
nextDragState = WF_DRAG_BANDWIDTH_LEFT; nextDragState = WF_DRAG_BANDWIDTH_LEFT;
@ -408,14 +542,14 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
mTracker.setVertDragLock(true); mTracker.setVertDragLock(true);
mTracker.setHorizDragLock(false); mTracker.setHorizDragLock(false);
} else { } else {
SetCursor(wxCURSOR_SIZING); SetCursor (wxCURSOR_SIZING);
nextDragState = WF_DRAG_FREQUENCY; nextDragState = WF_DRAG_FREQUENCY;
mTracker.setVertDragLock(true); mTracker.setVertDragLock(true);
mTracker.setHorizDragLock(false); mTracker.setHorizDragLock(false);
} }
} else { } else {
SetCursor(wxCURSOR_CROSS); SetCursor (wxCURSOR_CROSS);
nextDragState = WF_DRAG_NONE; nextDragState = WF_DRAG_NONE;
} }
@ -460,8 +594,8 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
if (mTracker.getOriginDeltaMouseX() == 0 && mTracker.getOriginDeltaMouseY() == 0) { if (mTracker.getOriginDeltaMouseX() == 0 && mTracker.getOriginDeltaMouseY() == 0) {
float pos = mTracker.getMouseX(); float pos = mTracker.getMouseX();
int center_freq = wxGetApp().getFrequency(); int input_center_freq = GetCenterFrequency();
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE); int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth());
if (dragState == WF_DRAG_NONE) { if (dragState == WF_DRAG_NONE) {
if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) { if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) {
@ -497,14 +631,14 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep)));
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false); wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false);
SetCursor(wxCURSOR_SIZING); SetCursor (wxCURSOR_SIZING);
nextDragState = WF_DRAG_FREQUENCY; nextDragState = WF_DRAG_FREQUENCY;
mTracker.setVertDragLock(true); mTracker.setVertDragLock(true);
mTracker.setHorizDragLock(false); mTracker.setHorizDragLock(false);
} else { } else {
float pos = mTracker.getMouseX(); float pos = mTracker.getMouseX();
int center_freq = wxGetApp().getFrequency(); int input_center_freq = GetCenterFrequency();
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE); int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth());
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false); wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false);
nextDragState = WF_DRAG_FREQUENCY; nextDragState = WF_DRAG_FREQUENCY;
@ -513,15 +647,15 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
float width = mTracker.getOriginDeltaMouseX(); float width = mTracker.getOriginDeltaMouseX();
float pos = mTracker.getOriginMouseX() + width / 2.0; float pos = mTracker.getOriginMouseX() + width / 2.0;
int center_freq = wxGetApp().getFrequency(); int input_center_freq = GetCenterFrequency();
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE); unsigned int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth());
int bandwidth = (int) (fabs(width) * (float) SRATE); unsigned int bw = (unsigned int) (fabs(width) * (float) GetBandwidth());
if (bandwidth < 2000) { if (bw < 2000) {
bandwidth = 2000; bw = 2000;
} }
if (!bandwidth) { if (!bw) {
dragState = WF_DRAG_NONE; dragState = WF_DRAG_NONE;
return; return;
} }
@ -531,7 +665,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
} else { } else {
demod = wxGetApp().getDemodMgr().newThread(); demod = wxGetApp().getDemodMgr().newThread();
demod->getParams().frequency = freq; demod->getParams().frequency = freq;
demod->getParams().bandwidth = bandwidth; demod->getParams().bandwidth = bw;
demod->run(); demod->run();
@ -556,7 +690,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
command.int_value = freq; command.int_value = freq;
demod->getCommandQueue()->push(command); demod->getCommandQueue()->push(command);
command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH; command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH;
command.int_value = bandwidth; command.int_value = bw;
demod->getCommandQueue()->push(command); demod->getCommandQueue()->push(command);
} }
@ -565,11 +699,12 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
void WaterfallCanvas::mouseLeftWindow(wxMouseEvent& event) { void WaterfallCanvas::mouseLeftWindow(wxMouseEvent& event) {
mTracker.OnMouseLeftWindow(event); mTracker.OnMouseLeftWindow(event);
SetCursor(wxCURSOR_CROSS); SetCursor (wxCURSOR_CROSS);
wxGetApp().getDemodMgr().setActiveDemodulator(NULL); wxGetApp().getDemodMgr().setActiveDemodulator(NULL);
} }
void WaterfallCanvas::mouseEnterWindow(wxMouseEvent& event) { void WaterfallCanvas::mouseEnterWindow(wxMouseEvent& event) {
mTracker.OnMouseEnterWindow(event); mTracker.OnMouseEnterWindow(event);
SetCursor(wxCURSOR_CROSS); SetCursor (wxCURSOR_CROSS);
} }

View File

@ -19,11 +19,21 @@ public:
}; };
WaterfallCanvas(wxWindow *parent, int *attribList = NULL); WaterfallCanvas(wxWindow *parent, int *attribList = NULL);
void Setup(int fft_size_in, int waterfall_lines_in);
~WaterfallCanvas(); ~WaterfallCanvas();
void setData(std::vector<liquid_float_complex> *data); void setData(DemodulatorThreadIQData *input);
int GetFrequencyAt(float x); int GetFrequencyAt(float x);
void SetView(int center_freq_in, int bandwidth_in);
void DisableView();
void SetCenterFrequency(unsigned int center_freq_in);
unsigned int GetCenterFrequency();
void SetBandwidth(unsigned int bandwidth_in);
unsigned int GetBandwidth();
private: private:
void OnPaint(wxPaintEvent& event); void OnPaint(wxPaintEvent& event);
void OnKeyDown(wxKeyEvent& event); void OnKeyDown(wxKeyEvent& event);
@ -62,7 +72,28 @@ private:
DragState dragState; DragState dragState;
DragState nextDragState; DragState nextDragState;
bool shiftDown;bool altDown;bool ctrlDown; bool shiftDown;
bool altDown;
bool ctrlDown;
int fft_size;
int waterfall_lines;
unsigned int center_freq;
unsigned int bandwidth;
bool isView;
msresamp_crcf resampler;
float resample_ratio;
nco_crcf nco_shift;
int shift_freq;
int last_input_bandwidth;
int last_bandwidth;
std::vector<liquid_float_complex> shift_buffer;
std::vector<liquid_float_complex> resampler_buffer;
// event table // event table
wxDECLARE_EVENT_TABLE(); wxDECLARE_EVENT_TABLE();
}; };

View File

@ -3,7 +3,31 @@
#include "CubicSDR.h" #include "CubicSDR.h"
WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext) : WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext) :
PrimaryGLContext(canvas, sharedContext) { PrimaryGLContext(canvas, sharedContext), waterfall(0), waterfall_tex(NULL) {
grad.addColor(GradientColor(0, 0, 0));
grad.addColor(GradientColor(0, 0, 1.0));
grad.addColor(GradientColor(0, 1.0, 0));
grad.addColor(GradientColor(1.0, 1.0, 0));
grad.addColor(GradientColor(1.0, 0.2, 0.0));
grad.generate(256);
}
void WaterfallContext::Setup(int fft_size_in, int num_waterfall_lines_in) {
if (waterfall) {
glDeleteTextures(1, &waterfall);
waterfall = 0;
}
if (waterfall_tex) {
delete waterfall_tex;
}
waterfall_lines = num_waterfall_lines_in;
fft_size = fft_size_in;
waterfall_tex = new unsigned char[fft_size * waterfall_lines];
memset(waterfall_tex,0,fft_size * waterfall_lines);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
@ -23,26 +47,19 @@ WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedC
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
grad.addColor(GradientColor(0, 0, 0));
grad.addColor(GradientColor(0, 0, 1.0));
grad.addColor(GradientColor(0, 1.0, 0));
grad.addColor(GradientColor(1.0, 1.0, 0));
grad.addColor(GradientColor(1.0, 0.2, 0.0));
grad.generate(256);
glPixelTransferi(GL_MAP_COLOR, GL_TRUE); glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, &(grad.getRed())[0]); glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, &(grad.getRed())[0]);
glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 256, &(grad.getGreen())[0]); glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 256, &(grad.getGreen())[0]);
glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 256, &(grad.getBlue())[0]); glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 256, &(grad.getBlue())[0]);
} }
void WaterfallContext::Draw(std::vector<float> &points) { void WaterfallContext::Draw(std::vector<float> &points) {
if (points.size()) { if (points.size()) {
memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); memmove(waterfall_tex + fft_size, waterfall_tex, (waterfall_lines - 1) * fft_size);
for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { for (int i = 0, iMax = fft_size; i < iMax; i++) {
float v = points[i * 2 + 1]; float v = points[i * 2 + 1];
float wv = v; float wv = v;
@ -57,7 +74,7 @@ void WaterfallContext::Draw(std::vector<float> &points) {
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, waterfall); glBindTexture(GL_TEXTURE_2D, waterfall);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex);
glColor3f(1.0, 1.0, 1.0); glColor3f(1.0, 1.0, 1.0);

View File

@ -3,8 +3,6 @@
#include "PrimaryGLContext.h" #include "PrimaryGLContext.h"
#include "Gradient.h" #include "Gradient.h"
#define NUM_WATERFALL_LINES 512
class WaterfallCanvas; class WaterfallCanvas;
class WaterfallContext: public PrimaryGLContext { class WaterfallContext: public PrimaryGLContext {
@ -12,9 +10,12 @@ public:
WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext); WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext);
void Draw(std::vector<float> &points); void Draw(std::vector<float> &points);
void Setup(int fft_size_in, int num_waterfall_lines_in);
private: private:
Gradient grad; Gradient grad;
GLuint waterfall; GLuint waterfall;
unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; unsigned char *waterfall_tex;
int fft_size;
int waterfall_lines;
}; };