Merge pull request #21 from cjcliffe/waterfall-zoom

Waterfall zoom
This commit is contained in:
Charles J. Cliffe 2015-01-02 19:37:01 -05:00
commit 6f5f97d26f
6 changed files with 188 additions and 75 deletions

View File

@ -79,6 +79,7 @@ AppFrame::AppFrame() :
demodWaterfallCanvas->Setup(1024, 256);
demodWaterfallCanvas->SetView(DEFAULT_FREQ, 300000);
demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas);
demodSpectrumCanvas->attachWaterfallCanvas(demodWaterfallCanvas);
demodVisuals->Add(demodWaterfallCanvas, 3, wxEXPAND | wxALL, 0);
demodTray->Add(demodVisuals, 30, wxEXPAND | wxALL, 0);
@ -103,6 +104,7 @@ AppFrame::AppFrame() :
waterfallCanvas = new WaterfallCanvas(this, NULL);
waterfallCanvas->Setup(2048, 512);
waterfallCanvas->attachSpectrumCanvas(spectrumCanvas);
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0);
this->SetSizer(vbox);

View File

@ -119,7 +119,7 @@ void DemodulatorThread::threadMain() {
freqdem_reset(fdem);
}
int out_size = ceil((double) (bufSize) * inp->resample_ratio) + 32;
int out_size = ceil((double) (bufSize) * inp->resample_ratio) + 512;
if (agc_data.size() != out_size) {
if (agc_data.capacity() < out_size) {
@ -144,7 +144,7 @@ void DemodulatorThread::threadMain() {
demod_output.resize(num_written);
}
int audio_out_size = ceil((double) (num_written) * audio_resample_ratio) + 32;
int audio_out_size = ceil((double) (num_written) * audio_resample_ratio) + 512;
agc_crcf_execute_block(agc, &resampled_data[0], num_written, &agc_data[0]);

View File

@ -15,6 +15,7 @@
#include "AppFrame.h"
#include <algorithm>
#include <wx/numformatter.h>
#include "WaterfallCanvas.h"
wxBEGIN_EVENT_TABLE(SpectrumCanvas, wxGLCanvas) EVT_PAINT(SpectrumCanvas::OnPaint)
EVT_IDLE(SpectrumCanvas::OnIdle)
@ -27,7 +28,8 @@ wxEND_EVENT_TABLE()
SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
wxFULL_REPAINT_ON_RESIZE), parent(parent), fft_size(0), in(NULL), out(NULL), plan(NULL), center_freq(0), bandwidth(0), isView(0) {
wxFULL_REPAINT_ON_RESIZE), parent(parent), fft_size(0), in(NULL), out(NULL), plan(NULL), waterfallCanvas(NULL), center_freq(0), bandwidth(0), isView(
0) {
glContext = new SpectrumContext(this, &wxGetApp().GetContext(this));
@ -57,7 +59,6 @@ void SpectrumCanvas::Setup(int fft_size_in) {
}
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;
}
@ -171,8 +172,6 @@ void SpectrumCanvas::setData(DemodulatorThreadIQData *input) {
}
}
void SpectrumCanvas::SetView(int center_freq_in, int bandwidth_in) {
isView = true;
center_freq = center_freq_in;
@ -181,7 +180,10 @@ void SpectrumCanvas::SetView(int center_freq_in, int bandwidth_in) {
void SpectrumCanvas::DisableView() {
isView = false;
center_freq = wxGetApp().getFrequency();
bandwidth = SRATE;
}
void SpectrumCanvas::SetCenterFrequency(unsigned int center_freq_in) {
center_freq = center_freq_in;
}
@ -217,12 +219,32 @@ void SpectrumCanvas::mouseMoved(wxMouseEvent& event) {
if (freqChange != 0) {
int freq = wxGetApp().getFrequency();
freq -= freqChange;
wxGetApp().setFrequency(freq);
((wxFrame*) parent)->GetStatusBar()->SetStatusText(
wxString::Format(wxT("Set center frequency: %s"),
wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep)));
if (isView) {
center_freq = center_freq - freqChange;
if (waterfallCanvas) {
waterfallCanvas->SetCenterFrequency(center_freq);
}
int bw = (int)bandwidth;
int bwOfs = ((int)center_freq>freq)?((int)bandwidth/2):(-(int)bandwidth/2);
int freqEdge = ((int)center_freq+bwOfs);
if (abs(freq-freqEdge) > (SRATE/2)) {
freqChange = -(((int)center_freq>freq)?(freqEdge-freq-(SRATE/2)):(freqEdge-freq+(SRATE/2)));
} else {
freqChange = 0;
}
}
if (freqChange) {
freq -= freqChange;
wxGetApp().setFrequency(freq);
((wxFrame*) parent)->GetStatusBar()->SetStatusText(
wxString::Format(wxT("Set center frequency: %s"),
wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep)));
}
}
}
}
@ -246,6 +268,9 @@ void SpectrumCanvas::mouseLeftWindow(wxMouseEvent& event) {
SetCursor(wxCURSOR_SIZEWE);
}
void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) {
waterfallCanvas = canvas_in;
}
//void SpectrumCanvas::rightClick(wxMouseEvent& event) {}
//void SpectrumCanvas::keyPressed(wxKeyEvent& event) {}
//void SpectrumCanvas::keyReleased(wxKeyEvent& event) {}

View File

@ -11,6 +11,8 @@
#include "fftw3.h"
#include "MouseTracker.h"
class WaterfallCanvas;
class SpectrumCanvas: public wxGLCanvas {
public:
std::vector<float> spectrum_points;
@ -30,6 +32,8 @@ public:
void SetBandwidth(unsigned int bandwidth_in);
unsigned int GetBandwidth();
void attachWaterfallCanvas(WaterfallCanvas *canvas_in);
private:
void OnPaint(wxPaintEvent& event);
@ -56,6 +60,7 @@ private:
std::vector<double> fft_result_maa;
SpectrumContext *glContext;
WaterfallCanvas *waterfallCanvas;
int fft_size;
unsigned int center_freq;

View File

@ -35,7 +35,7 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
wxFULL_REPAINT_ON_RESIZE), parent(parent), spectrumCanvas(NULL), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState(
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) {
0), zoom(0) {
glContext = new WaterfallContext(this, &wxGetApp().GetContext(this));
@ -62,6 +62,9 @@ void WaterfallCanvas::SetView(int center_freq_in, int bandwidth_in) {
void WaterfallCanvas::DisableView() {
isView = false;
center_freq = wxGetApp().getFrequency();
bandwidth = SRATE;
last_bandwidth = 0;
}
void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) {
@ -221,8 +224,14 @@ void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) {
shiftDown = event.ShiftDown();
altDown = event.AltDown();
ctrlDown = event.ControlDown();
// switch (event.GetKeyCode()) {
// }
switch (event.GetKeyCode()) {
case 'A':
zoom = 0;
break;
case 'Z':
zoom = 0;
break;
}
}
void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
@ -235,60 +244,65 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
unsigned int freq;
if (!isView) {
switch (event.GetKeyCode()) {
case WXK_RIGHT:
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 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;
}
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;
unsigned int bw;
switch (event.GetKeyCode()) {
case 'A':
zoom = 1;
break;
case 'Z':
zoom = -1;
break;
case WXK_RIGHT:
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 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;
}
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;
}
}
@ -297,16 +311,72 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) {
return;
}
unsigned int bw;
if (zoom) {
int freq = wxGetApp().getFrequency();
if (zoom > 0) {
center_freq = GetCenterFrequency();
bw = GetBandwidth();
bw = (unsigned int) ceil((float) bw * 0.95);
if (bw < 80000) {
bw = 80000;
}
if (mTracker.mouseInView()) {
int mfreqA = GetFrequencyAt(mTracker.getMouseX());
SetBandwidth(bw);
int mfreqB = GetFrequencyAt(mTracker.getMouseX());
center_freq += mfreqA - mfreqB;
}
SetView(center_freq, bw);
if (spectrumCanvas) {
spectrumCanvas->SetView(center_freq, bw);
}
} else {
if (isView) {
bw = GetBandwidth();
bw = (unsigned int) ceil((float) bw * 1.05);
if ((int) bw >= SRATE) {
bw = (unsigned int) SRATE;
DisableView();
if (spectrumCanvas) {
spectrumCanvas->DisableView();
}
} else {
if (mTracker.mouseInView()) {
int freq = wxGetApp().getFrequency();
int mfreqA = GetFrequencyAt(mTracker.getMouseX());
SetBandwidth(bw);
int mfreqB = GetFrequencyAt(mTracker.getMouseX());
center_freq += mfreqA - mfreqB;
}
SetView(GetCenterFrequency(), bw);
if (spectrumCanvas) {
spectrumCanvas->SetView(center_freq, bw);
}
}
}
}
if (center_freq < freq && (center_freq - bandwidth / 2) < (freq - SRATE / 2)) {
center_freq = (freq - SRATE / 2) + bandwidth / 2;
}
if (center_freq > freq && (center_freq + bandwidth / 2) > (freq + SRATE / 2)) {
center_freq = (freq + SRATE / 2) - bandwidth / 2;
}
}
std::vector<liquid_float_complex> *data = &input->data;
if (data && data->size()) {
if (fft_size != data->size() && !isView) {
Setup(data->size(), waterfall_lines);
}
// if (fft_size != data->size() && !isView) {
// Setup(data->size(), waterfall_lines);
// }
if (last_bandwidth != bandwidth && !isView) {
Setup(bandwidth, waterfall_lines);
}
// if (last_bandwidth != bandwidth && !isView) {
// Setup(bandwidth, waterfall_lines);
// }
if (spectrum_points.size() < fft_size * 2) {
spectrum_points.resize(fft_size * 2);
@ -356,7 +426,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) {
last_input_bandwidth = input->bandwidth;
}
int out_size = ceil((double) (input->data.size()) * resample_ratio) + 32;
int out_size = ceil((double) (input->data.size()) * resample_ratio) + 512;
if (resampler_buffer.size() != out_size) {
if (resampler_buffer.capacity() < out_size) {
@ -648,6 +718,10 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
if (DemodulatorInstance *last = wxGetApp().getDemodMgr().getLastActiveDemodulator()) {
demod->getParams().bandwidth = last->getParams().bandwidth;
demod->setDemodulatorType(last->getDemodulatorType());
demod->setSquelchLevel(last->getSquelchLevel());
demod->setSquelchEnabled(last->isSquelchEnabled());
demod->setStereo(last->isStereo());
}
demod->run();
@ -708,7 +782,12 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
demod = wxGetApp().getDemodMgr().newThread();
demod->getParams().frequency = freq;
demod->getParams().bandwidth = bw;
if (DemodulatorInstance *last = wxGetApp().getDemodMgr().getLastActiveDemodulator()) {
demod->setDemodulatorType(last->getDemodulatorType());
demod->setSquelchLevel(last->getSquelchLevel());
demod->setSquelchEnabled(last->isSquelchEnabled());
demod->setStereo(last->isStereo());
}
demod->run();
wxGetApp().bindDemodulator(demod);

View File

@ -95,6 +95,8 @@ private:
int last_input_bandwidth;
int last_bandwidth;
int zoom;
std::vector<liquid_float_complex> shift_buffer;
std::vector<liquid_float_complex> resampler_buffer;