mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-22 19:58:39 -05:00
Zoomed-in demodulator waterfall somewhat working
- update demod WF from demodulator is jittery - adjustments with immediate update is awkward
This commit is contained in:
parent
a3f8bc08e7
commit
1e970f4373
@ -28,7 +28,7 @@ EVT_IDLE(AppFrame::OnIdle)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
AppFrame::AppFrame() :
|
||||
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) {
|
||||
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) {
|
||||
|
||||
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL);
|
||||
@ -64,15 +64,22 @@ AppFrame::AppFrame() :
|
||||
|
||||
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);
|
||||
demodTray->Add(scopeCanvas, 7, wxEXPAND | wxALL, 0);
|
||||
|
||||
vbox->Add(demodTray, 1, wxEXPAND | wxALL, 0);
|
||||
vbox->AddSpacer(2);
|
||||
spectrumCanvas = new SpectrumCanvas(this, NULL);
|
||||
spectrumCanvas->Setup(2048);
|
||||
vbox->Add(spectrumCanvas, 1, wxEXPAND | wxALL, 0);
|
||||
vbox->AddSpacer(2);
|
||||
waterfallCanvas = new WaterfallCanvas(this, NULL);
|
||||
waterfallCanvas->Setup(2048,512);
|
||||
vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0);
|
||||
|
||||
this->SetSizer(vbox);
|
||||
@ -129,14 +136,27 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(4));
|
||||
// std::this_thread::yield();
|
||||
//#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()) {
|
||||
DemodulatorThreadIQData *iqData;
|
||||
wxGetApp().getIQVisualQueue()->pop(iqData);
|
||||
|
||||
if (iqData && iqData->data.size()) {
|
||||
spectrumCanvas->setData(&iqData->data);
|
||||
waterfallCanvas->setData(&iqData->data);
|
||||
|
||||
spectrumCanvas->setData(iqData);
|
||||
waterfallCanvas->setData(iqData);
|
||||
demodWaterfallCanvas->setData(iqData);
|
||||
delete iqData;
|
||||
} else {
|
||||
std::cout << "Incoming IQ data empty?" << std::endl;
|
||||
@ -148,8 +168,8 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||
AudioThreadInput *demodAudioData;
|
||||
wxGetApp().getAudioVisualQueue()->pop(demodAudioData);
|
||||
if (demodAudioData && demodAudioData->data.size()) {
|
||||
if (scopeCanvas->waveform_points.size() != demodAudioData->data.size()*2) {
|
||||
scopeCanvas->waveform_points.resize(demodAudioData->data.size()*2);
|
||||
if (scopeCanvas->waveform_points.size() != demodAudioData->data.size() * 2) {
|
||||
scopeCanvas->waveform_points.resize(demodAudioData->data.size() * 2);
|
||||
}
|
||||
|
||||
for (int i = 0, iMax = demodAudioData->data.size(); i < iMax; i++) {
|
||||
|
@ -23,7 +23,8 @@ private:
|
||||
ScopeCanvas *scopeCanvas;
|
||||
SpectrumCanvas *spectrumCanvas;
|
||||
WaterfallCanvas *waterfallCanvas;
|
||||
WaterfallCanvas *demodWaterfallCanvas;
|
||||
|
||||
// event table
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
@ -28,6 +28,7 @@ bool CubicSDR::OnInit() {
|
||||
sdrThread = new SDRThread(threadCmdQueueSDR);
|
||||
|
||||
sdrPostThread = new SDRPostThread();
|
||||
sdrPostThread->setNumVisSamples(2048);
|
||||
|
||||
iqPostDataQueue = new SDRThreadIQDataQueue;
|
||||
iqVisualQueue = new DemodulatorThreadInputQueue;
|
||||
|
@ -7,7 +7,7 @@
|
||||
#define BUF_SIZE (16384*4)
|
||||
#define SRATE 2500000
|
||||
#endif
|
||||
#define FFT_SIZE 2048
|
||||
#define DEFAULT_FFT_SIZE 2048
|
||||
|
||||
#define DEFAULT_FREQ 98900000
|
||||
#define AUDIO_FREQUENCY 44100
|
||||
|
@ -367,7 +367,7 @@ void AudioThread::setActive(bool state) {
|
||||
while (!inputQueue->empty()) { // flush queue
|
||||
inputQueue->pop(dummy);
|
||||
if (dummy) {
|
||||
delete dummy;
|
||||
dummy->decRefCount();
|
||||
}
|
||||
}
|
||||
deviceController[parameters.deviceId]->bindThread(this);
|
||||
@ -376,7 +376,7 @@ void AudioThread::setActive(bool state) {
|
||||
while (!inputQueue->empty()) { // flush queue
|
||||
inputQueue->pop(dummy);
|
||||
if (dummy) {
|
||||
delete dummy;
|
||||
dummy->decRefCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
DemodulatorPreThread::DemodulatorPreThread(DemodulatorThreadInputQueue* pQueueIn, DemodulatorThreadPostInputQueue* pQueueOut,
|
||||
DemodulatorThreadControlCommandQueue *threadQueueControl, DemodulatorThreadCommandQueue* threadQueueNotify) :
|
||||
inputQueue(pQueueIn), postInputQueue(pQueueOut), terminated(false), initialized(false), audio_resampler(NULL), stereo_resampler(NULL), resample_ratio(1), audio_resample_ratio(
|
||||
1), resampler(NULL), commandQueue(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl(
|
||||
inputQueue(pQueueIn), postInputQueue(pQueueOut), terminated(false), initialized(false), audio_resampler(NULL), stereo_resampler(NULL), resample_ratio(
|
||||
1), audio_resample_ratio(1), resampler(NULL), commandQueue(NULL), audioInputQueue(NULL), threadQueueNotify(threadQueueNotify), threadQueueControl(
|
||||
threadQueueControl) {
|
||||
|
||||
float kf = 0.5; // modulation factor
|
||||
@ -53,7 +53,6 @@ void DemodulatorPreThread::initialize() {
|
||||
}
|
||||
stereo_resampler = msresamp_rrrf_create(audio_resample_ratio, As);
|
||||
|
||||
|
||||
initialized = true;
|
||||
// std::cout << "inputResampleRate " << params.bandwidth << std::endl;
|
||||
|
||||
@ -69,12 +68,12 @@ DemodulatorPreThread::~DemodulatorPreThread() {
|
||||
#ifdef __APPLE__
|
||||
void *DemodulatorPreThread::threadMain() {
|
||||
#else
|
||||
void DemodulatorPreThread::threadMain() {
|
||||
void DemodulatorPreThread::threadMain() {
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
pthread_t tID = pthread_self(); // ID of this thread
|
||||
int priority = sched_get_priority_max( SCHED_FIFO )-1;
|
||||
sched_param prio = {priority}; // scheduling priority of thread
|
||||
int priority = sched_get_priority_max( SCHED_FIFO) - 1;
|
||||
sched_param prio = { priority }; // scheduling priority of thread
|
||||
pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
||||
#endif
|
||||
|
||||
@ -162,7 +161,7 @@ void DemodulatorPreThread::threadMain() {
|
||||
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 *out_buf = &out_buf_data[0];
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <deque>
|
||||
|
||||
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() {
|
||||
@ -34,6 +34,14 @@ void SDRPostThread::setIQVisualQueue(DemodulatorThreadInputQueue *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() {
|
||||
int n_read;
|
||||
double seconds = 0.0;
|
||||
@ -89,7 +97,9 @@ void SDRPostThread::threadMain() {
|
||||
|
||||
if (iqVisualQueue != NULL && iqVisualQueue.load()->empty()) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,9 @@ public:
|
||||
void setIQDataOutQueue(DemodulatorThreadInputQueue* iqDataQueue);
|
||||
void setIQVisualQueue(DemodulatorThreadInputQueue* iqVisQueue);
|
||||
|
||||
void setNumVisSamples(int num_vis_samples_in);
|
||||
int getNumVisSamples();
|
||||
|
||||
void threadMain();
|
||||
void terminate();
|
||||
|
||||
@ -30,4 +33,5 @@ protected:
|
||||
std::vector<DemodulatorInstance *> demodulators_remove;
|
||||
std::atomic<bool> terminated;
|
||||
iirfilt_crcf dcFilter;
|
||||
int num_vis_samples;
|
||||
};
|
||||
|
@ -92,7 +92,7 @@ GLFont &PrimaryGLContext::getFont(GLFontSize 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) {
|
||||
return;
|
||||
}
|
||||
@ -103,7 +103,11 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float
|
||||
float viewHeight = (float) vp[3];
|
||||
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;
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
@ -112,7 +116,7 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, float r, float
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR);
|
||||
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);
|
||||
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) {
|
||||
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_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);
|
||||
|
||||
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);
|
||||
@ -191,7 +199,7 @@ void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, float r, float g, f
|
||||
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();
|
||||
|
||||
int bw = 0;
|
||||
@ -218,7 +226,7 @@ void PrimaryGLContext::DrawFreqSelector(float uxPos, float r, float g, float b,
|
||||
if (w) {
|
||||
ofs = w;
|
||||
} else {
|
||||
ofs = ((float) bw) / (float) SRATE;
|
||||
ofs = ((float) bw) / (float) srate;
|
||||
}
|
||||
|
||||
glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0);
|
||||
|
@ -23,9 +23,9 @@ public:
|
||||
void BeginDraw();
|
||||
void EndDraw();
|
||||
|
||||
void DrawFreqSelector(float uxPos, float r = 1, float g = 1, float b = 1, float w = 0);
|
||||
void DrawDemod(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1);
|
||||
void DrawDemodInfo(DemodulatorInstance *demod, float r = 1, float g = 1, float b = 1);
|
||||
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, int center_freq = -1, int srate = SRATE);
|
||||
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);
|
||||
|
||||
|
@ -27,20 +27,9 @@ wxEND_EVENT_TABLE()
|
||||
|
||||
SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
|
||||
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
||||
wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(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;
|
||||
wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), fft_size(0), in(NULL), out(NULL), plan(NULL) {
|
||||
|
||||
glContext = new SpectrumContext(this, &wxGetApp().GetContext(this));
|
||||
timer.start();
|
||||
|
||||
mTracker.setTarget(this);
|
||||
mTracker.setVertDragLock(true);
|
||||
@ -48,6 +37,33 @@ SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
|
||||
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() {
|
||||
|
||||
}
|
||||
@ -73,14 +89,23 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
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 (spectrum_points.size() < FFT_SIZE * 2) {
|
||||
spectrum_points.resize(FFT_SIZE * 2);
|
||||
if (fft_size != data->size()) {
|
||||
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][1] = (*data)[i].imag;
|
||||
}
|
||||
@ -89,31 +114,34 @@ void SpectrumCanvas::setData(std::vector<liquid_float_complex> *data) {
|
||||
|
||||
double fft_ceil = 0, fft_floor = 1;
|
||||
|
||||
if (fft_result.size() < FFT_SIZE) {
|
||||
fft_result.resize(FFT_SIZE);
|
||||
fft_result_ma.resize(FFT_SIZE);
|
||||
fft_result_maa.resize(FFT_SIZE);
|
||||
if (fft_result.size() != fft_size) {
|
||||
if (fft_result.capacity() < fft_size) {
|
||||
fft_result.reserve(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;
|
||||
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;
|
||||
double a = out[n][0];
|
||||
double b = out[n][1];
|
||||
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 y = out[FFT_SIZE / 2 + n][1];
|
||||
double x = out[fft_size / 2 + n][0];
|
||||
double y = out[fft_size / 2 + n][1];
|
||||
double z = sqrt(x * x + y * y);
|
||||
|
||||
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_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]);
|
||||
|
||||
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));
|
||||
spectrum_points[i * 2] = ((float) i / (float) iMax);
|
||||
spectrum_points[i * 2 + 1] = v;
|
||||
|
@ -15,9 +15,10 @@
|
||||
class SpectrumCanvas: public wxGLCanvas {
|
||||
public:
|
||||
SpectrumCanvas(wxWindow *parent, int *attribList = NULL);
|
||||
void Setup(int fft_size_in);
|
||||
~SpectrumCanvas();
|
||||
|
||||
void setData(std::vector<liquid_float_complex> *data);
|
||||
void setData(DemodulatorThreadIQData *input);
|
||||
private:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
|
||||
@ -47,6 +48,7 @@ private:
|
||||
SpectrumContext *glContext;
|
||||
Timer timer;
|
||||
float frameTimer;
|
||||
int fft_size;
|
||||
|
||||
MouseTracker mTracker;
|
||||
// event table
|
||||
|
@ -14,4 +14,5 @@ public:
|
||||
void Draw(std::vector<float> &points);
|
||||
|
||||
private:
|
||||
int fft_size;
|
||||
};
|
||||
|
@ -29,39 +29,93 @@ EVT_ENTER_WINDOW(WaterfallCanvas::mouseEnterWindow)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
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(
|
||||
WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false) {
|
||||
|
||||
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;
|
||||
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) {
|
||||
|
||||
glContext = new WaterfallContext(this, &wxGetApp().GetContext(this));
|
||||
timer.start();
|
||||
|
||||
nco_shift = nco_crcf_create(LIQUID_NCO);
|
||||
shift_freq = 0;
|
||||
|
||||
mTracker.setTarget(this);
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
}
|
||||
|
||||
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 center_freq = wxGetApp().getFrequency();
|
||||
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) x * (float) SRATE);
|
||||
int iqCenterFreq = GetCenterFrequency();
|
||||
int iqBandwidth = GetBandwidth();
|
||||
int freq = iqCenterFreq - (int) (0.5 * (float) iqBandwidth) + (int) ((float) x * (float) iqBandwidth);
|
||||
|
||||
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)) {
|
||||
wxPaintDC dc(this);
|
||||
const wxSize ClientSize = GetClientSize();
|
||||
@ -80,6 +134,9 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
bool isNew = shiftDown
|
||||
|| (wxGetApp().getDemodMgr().getLastActiveDemodulator() && !wxGetApp().getDemodMgr().getLastActiveDemodulator()->isActive());
|
||||
|
||||
int currentBandwidth = GetBandwidth();
|
||||
int currentCenterFreq = GetCenterFrequency();
|
||||
|
||||
if (mTracker.mouseInView()) {
|
||||
if (nextDragState == WF_DRAG_RANGE) {
|
||||
if (mTracker.mouseDown()) {
|
||||
@ -87,47 +144,49 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||
float centerPos = mTracker.getOriginMouseX() + width / 2.0;
|
||||
|
||||
if (isNew) {
|
||||
glContext->DrawDemod(lastActiveDemodulator);
|
||||
glContext->DrawFreqSelector(centerPos, 0, 1, 0, width ? width : (1.0 / (float) ClientSize.x));
|
||||
glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||
glContext->DrawFreqSelector(centerPos, 0, 1, 0, width ? width : (1.0 / (float) ClientSize.x), currentCenterFreq,
|
||||
currentBandwidth);
|
||||
} else {
|
||||
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0);
|
||||
glContext->DrawFreqSelector(centerPos, 1, 1, 0, width ? width : (1.0 / (float) ClientSize.x));
|
||||
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||
glContext->DrawFreqSelector(centerPos, 1, 1, 0, width ? width : (1.0 / (float) ClientSize.x), currentCenterFreq,
|
||||
currentBandwidth);
|
||||
}
|
||||
} else {
|
||||
if (isNew) {
|
||||
glContext->DrawDemod(lastActiveDemodulator);
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 1.0 / (float) ClientSize.x);
|
||||
glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 1.0 / (float) ClientSize.x, currentCenterFreq, currentBandwidth);
|
||||
} else {
|
||||
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0);
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 1.0 / (float) ClientSize.x);
|
||||
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 1.0 / (float) ClientSize.x, currentCenterFreq, currentBandwidth);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (activeDemodulator == NULL) {
|
||||
if (lastActiveDemodulator) {
|
||||
if (isNew) {
|
||||
glContext->DrawDemod(lastActiveDemodulator);
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0);
|
||||
glContext->DrawDemod(lastActiveDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 0, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||
} else {
|
||||
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0);
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0);
|
||||
glContext->DrawDemod(lastActiveDemodulator, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||
}
|
||||
} else {
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0);
|
||||
glContext->DrawFreqSelector(mTracker.getMouseX(), 1, 1, 0, 0, currentCenterFreq, currentBandwidth);
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
if (activeDemodulator) {
|
||||
glContext->DrawDemod(activeDemodulator);
|
||||
glContext->DrawDemod(activeDemodulator, 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||
}
|
||||
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]) {
|
||||
continue;
|
||||
}
|
||||
glContext->DrawDemod(demods[i]);
|
||||
glContext->DrawDemod(demods[i], 1, 1, 1, currentCenterFreq, currentBandwidth);
|
||||
}
|
||||
|
||||
glContext->EndDraw();
|
||||
@ -161,102 +220,178 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
||||
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getActiveDemodulator();
|
||||
|
||||
unsigned int freq;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
wxGetApp().removeDemodulator(activeDemod);
|
||||
wxGetApp().getDemodMgr().deleteThread(activeDemod);
|
||||
break;
|
||||
case 'S':
|
||||
if (!activeDemod) {
|
||||
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;
|
||||
}
|
||||
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 (spectrum_points.size() < FFT_SIZE * 2) {
|
||||
spectrum_points.resize(FFT_SIZE * 2);
|
||||
if (fft_size != data->size() && !isView) {
|
||||
Setup(data->size(), waterfall_lines);
|
||||
}
|
||||
|
||||
for (int i = 0; i < FFT_SIZE; i++) {
|
||||
in[i][0] = (*data)[i].real;
|
||||
in[i][1] = (*data)[i].imag;
|
||||
if (last_bandwidth != bandwidth && !isView) {
|
||||
Setup(bandwidth, waterfall_lines);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
double fft_ceil = 0, fft_floor = 1;
|
||||
|
||||
if (fft_result.size() < FFT_SIZE) {
|
||||
fft_result.resize(FFT_SIZE);
|
||||
fft_result_ma.resize(FFT_SIZE);
|
||||
fft_result_maa.resize(FFT_SIZE);
|
||||
if (fft_result.size() < fft_size) {
|
||||
fft_result.resize(fft_size);
|
||||
fft_result_ma.resize(fft_size);
|
||||
fft_result_maa.resize(fft_size);
|
||||
}
|
||||
|
||||
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;
|
||||
double a = out[n][0];
|
||||
double b = out[n][1];
|
||||
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 y = out[FFT_SIZE / 2 + n][1];
|
||||
double x = out[fft_size / 2 + n][0];
|
||||
double y = out[fft_size / 2 + n][1];
|
||||
double z = sqrt(x * x + y * y);
|
||||
|
||||
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_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_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));
|
||||
spectrum_points[i * 2] = ((float) i / (float) iMax);
|
||||
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) {
|
||||
|
||||
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) SRATE) * 2;
|
||||
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) GetBandwidth()) * 2;
|
||||
|
||||
if (dragState == WF_DRAG_BANDWIDTH_LEFT) {
|
||||
bwDiff = -bwDiff;
|
||||
@ -326,8 +460,8 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
||||
if (activeDemodulatorBandwidth < 2000) {
|
||||
activeDemodulatorBandwidth = 2000;
|
||||
}
|
||||
if (activeDemodulatorBandwidth > SRATE) {
|
||||
activeDemodulatorBandwidth = SRATE;
|
||||
if (activeDemodulatorBandwidth > GetBandwidth()) {
|
||||
activeDemodulatorBandwidth = GetBandwidth();
|
||||
}
|
||||
|
||||
command.int_value = activeDemodulatorBandwidth;
|
||||
@ -335,7 +469,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
||||
}
|
||||
|
||||
if (dragState == WF_DRAG_FREQUENCY) {
|
||||
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) SRATE);
|
||||
int bwDiff = (int) (mTracker.getDeltaMouseX() * (float) GetBandwidth());
|
||||
|
||||
if (!activeDemodulatorFrequency) {
|
||||
activeDemodulatorFrequency = demod->getParams().frequency;
|
||||
@ -363,7 +497,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
||||
mTracker.setHorizDragLock(false);
|
||||
} else if (demodsHover->size()) {
|
||||
int hovered = -1;
|
||||
int near_dist = SRATE;
|
||||
int near_dist = GetBandwidth();
|
||||
|
||||
DemodulatorInstance *activeDemodulator = NULL;
|
||||
|
||||
@ -397,7 +531,7 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
||||
int freqDiff = ((int) activeDemodulator->getParams().frequency - freqPos);
|
||||
|
||||
if (abs(freqDiff) > (activeDemodulator->getParams().bandwidth / 3)) {
|
||||
SetCursor(wxCURSOR_SIZEWE);
|
||||
SetCursor (wxCURSOR_SIZEWE);
|
||||
|
||||
if (freqDiff > 0) {
|
||||
nextDragState = WF_DRAG_BANDWIDTH_LEFT;
|
||||
@ -408,14 +542,14 @@ void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {
|
||||
mTracker.setVertDragLock(true);
|
||||
mTracker.setHorizDragLock(false);
|
||||
} else {
|
||||
SetCursor(wxCURSOR_SIZING);
|
||||
SetCursor (wxCURSOR_SIZING);
|
||||
nextDragState = WF_DRAG_FREQUENCY;
|
||||
|
||||
mTracker.setVertDragLock(true);
|
||||
mTracker.setHorizDragLock(false);
|
||||
}
|
||||
} else {
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
SetCursor (wxCURSOR_CROSS);
|
||||
nextDragState = WF_DRAG_NONE;
|
||||
}
|
||||
|
||||
@ -460,8 +594,8 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
||||
|
||||
if (mTracker.getOriginDeltaMouseX() == 0 && mTracker.getOriginDeltaMouseY() == 0) {
|
||||
float pos = mTracker.getMouseX();
|
||||
int center_freq = wxGetApp().getFrequency();
|
||||
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE);
|
||||
int input_center_freq = GetCenterFrequency();
|
||||
int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth());
|
||||
|
||||
if (dragState == WF_DRAG_NONE) {
|
||||
if (!isNew && wxGetApp().getDemodMgr().getDemodulators().size()) {
|
||||
@ -497,14 +631,14 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
||||
wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep)));
|
||||
|
||||
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getLastActiveDemodulator(), false);
|
||||
SetCursor(wxCURSOR_SIZING);
|
||||
SetCursor (wxCURSOR_SIZING);
|
||||
nextDragState = WF_DRAG_FREQUENCY;
|
||||
mTracker.setVertDragLock(true);
|
||||
mTracker.setHorizDragLock(false);
|
||||
} else {
|
||||
float pos = mTracker.getMouseX();
|
||||
int center_freq = wxGetApp().getFrequency();
|
||||
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE);
|
||||
int input_center_freq = GetCenterFrequency();
|
||||
int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth());
|
||||
|
||||
wxGetApp().getDemodMgr().setActiveDemodulator(wxGetApp().getDemodMgr().getActiveDemodulator(), false);
|
||||
nextDragState = WF_DRAG_FREQUENCY;
|
||||
@ -513,15 +647,15 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
||||
float width = mTracker.getOriginDeltaMouseX();
|
||||
float pos = mTracker.getOriginMouseX() + width / 2.0;
|
||||
|
||||
int center_freq = wxGetApp().getFrequency();
|
||||
int freq = center_freq - (int) (0.5 * (float) SRATE) + (int) ((float) pos * (float) SRATE);
|
||||
int bandwidth = (int) (fabs(width) * (float) SRATE);
|
||||
int input_center_freq = GetCenterFrequency();
|
||||
unsigned int freq = input_center_freq - (int) (0.5 * (float) GetBandwidth()) + (int) ((float) pos * (float) GetBandwidth());
|
||||
unsigned int bw = (unsigned int) (fabs(width) * (float) GetBandwidth());
|
||||
|
||||
if (bandwidth < 2000) {
|
||||
bandwidth = 2000;
|
||||
if (bw < 2000) {
|
||||
bw = 2000;
|
||||
}
|
||||
|
||||
if (!bandwidth) {
|
||||
if (!bw) {
|
||||
dragState = WF_DRAG_NONE;
|
||||
return;
|
||||
}
|
||||
@ -531,7 +665,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
||||
} else {
|
||||
demod = wxGetApp().getDemodMgr().newThread();
|
||||
demod->getParams().frequency = freq;
|
||||
demod->getParams().bandwidth = bandwidth;
|
||||
demod->getParams().bandwidth = bw;
|
||||
|
||||
demod->run();
|
||||
|
||||
@ -556,7 +690,7 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
||||
command.int_value = freq;
|
||||
demod->getCommandQueue()->push(command);
|
||||
command.cmd = DemodulatorThreadCommand::DEMOD_THREAD_CMD_SET_BANDWIDTH;
|
||||
command.int_value = bandwidth;
|
||||
command.int_value = bw;
|
||||
demod->getCommandQueue()->push(command);
|
||||
}
|
||||
|
||||
@ -565,11 +699,12 @@ void WaterfallCanvas::mouseReleased(wxMouseEvent& event) {
|
||||
|
||||
void WaterfallCanvas::mouseLeftWindow(wxMouseEvent& event) {
|
||||
mTracker.OnMouseLeftWindow(event);
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
SetCursor (wxCURSOR_CROSS);
|
||||
wxGetApp().getDemodMgr().setActiveDemodulator(NULL);
|
||||
}
|
||||
|
||||
void WaterfallCanvas::mouseEnterWindow(wxMouseEvent& event) {
|
||||
mTracker.OnMouseEnterWindow(event);
|
||||
SetCursor(wxCURSOR_CROSS);
|
||||
SetCursor (wxCURSOR_CROSS);
|
||||
}
|
||||
|
||||
|
@ -19,11 +19,21 @@ public:
|
||||
};
|
||||
|
||||
WaterfallCanvas(wxWindow *parent, int *attribList = NULL);
|
||||
void Setup(int fft_size_in, int waterfall_lines_in);
|
||||
~WaterfallCanvas();
|
||||
|
||||
void setData(std::vector<liquid_float_complex> *data);
|
||||
void setData(DemodulatorThreadIQData *input);
|
||||
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:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
void OnKeyDown(wxKeyEvent& event);
|
||||
@ -62,7 +72,28 @@ private:
|
||||
DragState dragState;
|
||||
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
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
@ -3,7 +3,31 @@
|
||||
#include "CubicSDR.h"
|
||||
|
||||
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_DEPTH_TEST);
|
||||
|
||||
@ -23,26 +47,19 @@ WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedC
|
||||
|
||||
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);
|
||||
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_B, 256, &(grad.getBlue())[0]);
|
||||
|
||||
}
|
||||
|
||||
void WaterfallContext::Draw(std::vector<float> &points) {
|
||||
|
||||
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 wv = v;
|
||||
@ -57,7 +74,7 @@ void WaterfallContext::Draw(std::vector<float> &points) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -3,8 +3,6 @@
|
||||
#include "PrimaryGLContext.h"
|
||||
#include "Gradient.h"
|
||||
|
||||
#define NUM_WATERFALL_LINES 512
|
||||
|
||||
class WaterfallCanvas;
|
||||
|
||||
class WaterfallContext: public PrimaryGLContext {
|
||||
@ -12,9 +10,12 @@ public:
|
||||
WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext);
|
||||
|
||||
void Draw(std::vector<float> &points);
|
||||
void Setup(int fft_size_in, int num_waterfall_lines_in);
|
||||
|
||||
private:
|
||||
Gradient grad;
|
||||
GLuint waterfall;
|
||||
unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES];
|
||||
unsigned char *waterfall_tex;
|
||||
int fft_size;
|
||||
int waterfall_lines;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user