From fb5a7e5b8fe27fb9d323b309fa96c18d6da41ace Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 30 Jan 2015 19:31:32 -0500 Subject: [PATCH 1/5] attempt to improve FFT zoom resolution --- src/visual/WaterfallCanvas.cpp | 163 ++++++++++++++++++++------------- src/visual/WaterfallCanvas.h | 5 +- 2 files changed, 103 insertions(+), 65 deletions(-) diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 3fed1be..89394ae 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -36,7 +36,7 @@ wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : InteractiveCanvas(parent, attribList), spectrumCanvas(NULL), dragState(WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), waterfall_lines( 0), plan( - NULL), in(NULL), out(NULL), resampler(NULL), resamplerRatio(0), lastInputBandwidth(0), zoom(1), mouseZoom(1), otherWaterfallCanvas(NULL), polling(true) { + NULL), in(NULL), out(NULL), resampler(NULL), resamplerRatio(0), lastInputBandwidth(0), zoom(1), mouseZoom(1), otherWaterfallCanvas(NULL), polling(true), last_data_size(0), fft_in_data(NULL), fft_last_data(NULL) { glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); @@ -64,6 +64,14 @@ void WaterfallCanvas::setup(int fft_size_in, int waterfall_lines_in) { free(in); } in = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fft_size); + if (fft_in_data) { + free(fft_in_data); + } + fft_in_data = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fft_size); + if (fft_last_data) { + free(fft_last_data); + } + fft_last_data = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fft_size); if (out) { free(out); } @@ -394,6 +402,8 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { spectrum_points.resize(fft_size * 2); } + unsigned int num_written; + if (isView) { if (!input->frequency || !input->sampleRate) { return; @@ -447,104 +457,129 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { resampleBuffer.resize(out_size); } - unsigned int num_written; msresamp_crcf_execute(resampler, &shiftBuffer[0], input->data.size(), &resampleBuffer[0], &num_written); resampleBuffer.resize(fft_size); if (num_written < fft_size) { for (int i = 0; i < num_written; i++) { - in[i][0] = resampleBuffer[i].real; - in[i][1] = resampleBuffer[i].imag; + fft_in_data[i][0] = resampleBuffer[i].real; + fft_in_data[i][1] = resampleBuffer[i].imag; } for (int i = num_written; i < fft_size; i++) { - in[i][0] = 0; - in[i][1] = 0; + fft_in_data[i][0] = 0; + fft_in_data[i][1] = 0; } } else { for (int i = 0; i < fft_size; i++) { - in[i][0] = resampleBuffer[i].real; - in[i][1] = resampleBuffer[i].imag; + fft_in_data[i][0] = resampleBuffer[i].real; + fft_in_data[i][1] = resampleBuffer[i].imag; } } } else { - + num_written = data->size(); if (data->size() < fft_size) { for (int i = 0, iMax = data->size(); i < iMax; i++) { - in[i][0] = (*data)[i].real; - in[i][1] = (*data)[i].imag; + fft_in_data[i][0] = (*data)[i].real; + fft_in_data[i][1] = (*data)[i].imag; } for (int i = data->size(); i < fft_size; i++) { - in[i][0] = 0; - in[i][1] = 0; + fft_in_data[i][0] = 0; + fft_in_data[i][1] = 0; } } else { for (int i = 0; i < fft_size; i++) { - in[i][0] = (*data)[i].real; - in[i][1] = (*data)[i].imag; + fft_in_data[i][0] = (*data)[i].real; + fft_in_data[i][1] = (*data)[i].imag; } } } - fftwf_execute(plan); + bool execute = false; - float fft_ceil = 0, fft_floor = 1; + if (num_written >= fft_size) { + execute = true; + memcpy(in, fft_in_data, sizeof(fftwf_complex) * fft_size * sizeof(fftwf_complex)); + memcpy(fft_last_data, in, fft_size * sizeof(fftwf_complex)); - 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++) { - n = (i == 0) ? 1 : i; - float a = out[n][0]; - float b = out[n][1]; - float c = sqrt(a * a + b * b); - - float x = out[fft_size / 2 + n][0]; - float y = out[fft_size / 2 + n][1]; - float z = sqrt(x * x + y * y); - - fft_result[i] = (z); - fft_result[fft_size / 2 + i] = (c); - } - - for (int i = 0, iMax = fft_size; i < iMax; i++) { - if (isView) { - 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; + } else { + if (last_data_size + num_written < fft_size) { // priming + unsigned int num_copy = fft_size; + num_copy = fft_size - last_data_size; + if (num_written > num_copy) { + num_copy = num_written; + } + memcpy(fft_last_data, fft_in_data, num_copy * sizeof(fftwf_complex)); + last_data_size += num_copy; } else { - fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65; - fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65; - } - - if (fft_result_maa[i] > fft_ceil) { - fft_ceil = fft_result_maa[i]; - } - if (fft_result_maa[i] < fft_floor) { - fft_floor = fft_result_maa[i]; + unsigned int num_last = (fft_size - num_written); + memcpy(in, fft_last_data + (last_data_size - num_last), num_last * sizeof(fftwf_complex)); + memcpy(in + num_last, fft_in_data, num_written * sizeof(fftwf_complex)); + memcpy(fft_last_data, in, fft_size * sizeof(fftwf_complex)); } } - fft_ceil += 0.25; - fft_floor -= 1; + if (execute) { + fftwf_execute(plan); - fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; - fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; + float fft_ceil = 0, fft_floor = 1; - fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; - fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; + if (fft_result.size() < fft_size) { + fft_result.resize(fft_size); + fft_result_ma.resize(fft_size); + fft_result_maa.resize(fft_size); + } - 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; - } + int n; + for (int i = 0, iMax = fft_size / 2; i < iMax; i++) { + n = (i == 0) ? 1 : i; + float a = out[n][0]; + float b = out[n][1]; + float c = sqrt(a * a + b * b); - if (spectrumCanvas) { - spectrumCanvas->spectrum_points.assign(spectrum_points.begin(), spectrum_points.end()); + float x = out[fft_size / 2 + n][0]; + float y = out[fft_size / 2 + n][1]; + float z = sqrt(x * x + y * y); + + fft_result[i] = (z); + fft_result[fft_size / 2 + i] = (c); + } + + for (int i = 0, iMax = fft_size; i < iMax; i++) { + if (isView) { + 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; + } else { + fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * 0.65; + fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * 0.65; + } + + if (fft_result_maa[i] > fft_ceil) { + fft_ceil = fft_result_maa[i]; + } + if (fft_result_maa[i] < fft_floor) { + fft_floor = fft_result_maa[i]; + } + } + + fft_ceil += 0.25; + fft_floor -= 1; + + fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; + fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; + + fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; + fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; + + 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; + } + + if (spectrumCanvas) { + spectrumCanvas->spectrum_points.assign(spectrum_points.begin(), spectrum_points.end()); + } } } } diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 07ed755..f2cd994 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -56,7 +56,8 @@ private: WaterfallCanvas *otherWaterfallCanvas; bool polling; - fftwf_complex *in, *out; + fftwf_complex *in, *out, *fft_in_data, *fft_last_data; + unsigned int last_data_size; fftwf_plan plan; float fft_ceil_ma, fft_ceil_maa; @@ -84,6 +85,8 @@ private: std::vector shiftBuffer; std::vector resampleBuffer; + + // event table wxDECLARE_EVENT_TABLE(); }; From 19829b6ceb7383f4642744225ef0cfc1e22c4d6d Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Fri, 30 Jan 2015 19:48:06 -0500 Subject: [PATCH 2/5] bad memory init --- src/visual/WaterfallCanvas.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 89394ae..ab5fb01 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -499,7 +499,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { if (num_written >= fft_size) { execute = true; - memcpy(in, fft_in_data, sizeof(fftwf_complex) * fft_size * sizeof(fftwf_complex)); + memcpy(in, fft_in_data, fft_size * sizeof(fftwf_complex)); memcpy(fft_last_data, in, fft_size * sizeof(fftwf_complex)); } else { @@ -516,6 +516,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { memcpy(in, fft_last_data + (last_data_size - num_last), num_last * sizeof(fftwf_complex)); memcpy(in + num_last, fft_in_data, num_written * sizeof(fftwf_complex)); memcpy(fft_last_data, in, fft_size * sizeof(fftwf_complex)); + execute = true; } } From 27b890c8825bb9724c1dd3d558aafc0e390a03b9 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sat, 31 Jan 2015 22:27:11 -0500 Subject: [PATCH 3/5] Experimental zoomed FFT resolution improvements --- src/CubicSDR.cpp | 2 +- src/CubicSDRDefs.h | 2 +- src/sdr/SDRPostThread.cpp | 2 +- src/visual/WaterfallCanvas.cpp | 29 +++++++++++++++++++---------- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 4f428ca..3ae5cfa 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -29,7 +29,7 @@ bool CubicSDR::OnInit() { sdrThread = new SDRThread(threadCmdQueueSDR); sdrPostThread = new SDRPostThread(); - sdrPostThread->setNumVisSamples(2048); + sdrPostThread->setNumVisSamples(16384); iqPostDataQueue = new SDRThreadIQDataQueue; iqVisualQueue = new DemodulatorThreadInputQueue; diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 296bf2d..88b5cbe 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -11,7 +11,7 @@ const char filePathSeparator = #endif #ifdef __APPLE__ -#define BUF_SIZE (16384*2) +#define BUF_SIZE (16384*6) #define DEFAULT_SAMPLE_RATE 2000000 #endif #ifdef __linux__ diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 6951d61..2b3b76a 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -6,7 +6,7 @@ #include SDRPostThread::SDRPostThread() : - iqDataOutQueue(NULL), iqDataInQueue(NULL), iqVisualQueue(NULL), terminated(false), dcFilter(NULL), num_vis_samples(2048) { + iqDataOutQueue(NULL), iqDataInQueue(NULL), iqVisualQueue(NULL), terminated(false), dcFilter(NULL), num_vis_samples(16384) { } SDRPostThread::~SDRPostThread() { diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index ab5fb01..ae63f3b 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -409,6 +409,15 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { return; } + resamplerRatio = (double) (bandwidth) / (double) input->sampleRate; + + int desired_input_size = fft_size / resamplerRatio; + + if (input->data.size() < desired_input_size) { + std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl; + desired_input_size = input->data.size(); + } + if (centerFreq != input->frequency) { if ((centerFreq - input->frequency) != shiftFrequency || lastInputBandwidth != input->sampleRate) { if (abs(input->frequency - centerFreq) < (wxGetApp().getSampleRate() / 2)) { @@ -418,25 +427,23 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { } } - if (shiftBuffer.size() != input->data.size()) { - if (shiftBuffer.capacity() < input->data.size()) { - shiftBuffer.reserve(input->data.size()); + if (shiftBuffer.size() != desired_input_size) { + if (shiftBuffer.capacity() < desired_input_size) { + shiftBuffer.reserve(desired_input_size); } - shiftBuffer.resize(input->data.size()); + shiftBuffer.resize(desired_input_size); } if (shiftFrequency < 0) { - nco_crcf_mix_block_up(freqShifter, &input->data[0], &shiftBuffer[0], input->data.size()); + nco_crcf_mix_block_up(freqShifter, &input->data[0], &shiftBuffer[0], desired_input_size); } else { - nco_crcf_mix_block_down(freqShifter, &input->data[0], &shiftBuffer[0], input->data.size()); + nco_crcf_mix_block_down(freqShifter, &input->data[0], &shiftBuffer[0], desired_input_size); } } else { shiftBuffer.assign(input->data.begin(), input->data.end()); } if (!resampler || bandwidth != lastBandwidth || lastInputBandwidth != input->sampleRate) { - resamplerRatio = (double) (bandwidth) / (double) input->sampleRate; - float As = 120.0f; if (resampler) { @@ -448,7 +455,8 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { lastInputBandwidth = input->sampleRate; } - int out_size = ceil((double) (input->data.size()) * resamplerRatio) + 512; + + int out_size = ceil((double) (desired_input_size) * resamplerRatio) + 512; if (resampleBuffer.size() != out_size) { if (resampleBuffer.capacity() < out_size) { @@ -457,7 +465,8 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { resampleBuffer.resize(out_size); } - msresamp_crcf_execute(resampler, &shiftBuffer[0], input->data.size(), &resampleBuffer[0], &num_written); + + msresamp_crcf_execute(resampler, &shiftBuffer[0], desired_input_size, &resampleBuffer[0], &num_written); resampleBuffer.resize(fft_size); From b77caaf2632db597f084c1c9c0187c346b8b7ef8 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sat, 31 Jan 2015 22:43:44 -0500 Subject: [PATCH 4/5] remove debug msg --- src/visual/WaterfallCanvas.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index ae63f3b..12b0a96 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -414,7 +414,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { int desired_input_size = fft_size / resamplerRatio; if (input->data.size() < desired_input_size) { - std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl; +// std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl; desired_input_size = input->data.size(); } From 129ab337e2491055281255e6fe6dfbeccf74bda5 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 1 Feb 2015 00:08:55 -0500 Subject: [PATCH 5/5] range / suppression adjustments --- src/AppFrame.cpp | 4 ++-- src/visual/WaterfallCanvas.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index a0bcfd5..808c385 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -373,8 +373,8 @@ void AppFrame::OnIdle(wxIdleEvent& event) { if (demodBw > wxGetApp().getSampleRate() / 2) { demodBw = wxGetApp().getSampleRate() / 2; } - if (demodBw < 50000) { - demodBw = 50000; + if (demodBw < 30000) { + demodBw = 30000; } demodWaterfallCanvas->setBandwidth(demodBw); demodSpectrumCanvas->setBandwidth(demodBw); diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 12b0a96..16c9d5c 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -444,7 +444,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) { } if (!resampler || bandwidth != lastBandwidth || lastInputBandwidth != input->sampleRate) { - float As = 120.0f; + float As = 60.0f; if (resampler) { msresamp_crcf_destroy(resampler);