From fe2e56231db1e18a401cfa8e07bffc5a93d5e81d Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Tue, 11 Nov 2014 08:11:12 -0500 Subject: [PATCH 1/7] gradient util, started waterfall implementation --- CMakeLists.txt | 6 ++-- src/CubicSDRDefs.h | 2 +- src/Gradient.cpp | 2 ++ src/Gradient.h | 66 ++++++++++++++++++++++++++++++++++++++++ src/PrimaryGLContext.cpp | 62 ++++++++++++++++++++++++++++++++++--- src/PrimaryGLContext.h | 14 ++++++++- 6 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 src/Gradient.cpp create mode 100644 src/Gradient.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 72ad941..db83905 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,7 @@ endif (DEFINED WIN32) SET (cubicsdr_sources - src/CubicSDR.cpp + src/CubicSDR.cpp src/SDRThread.cpp src/IQBufferThread.cpp src/PrimaryGLContext.cpp @@ -91,10 +91,11 @@ SET (cubicsdr_sources src/SDRThreadQueue.cpp src/SDRThreadTask.cpp src/Demodulator.cpp + src/Gradient.cpp ) SET (cubicsdr_headers - src/CubicSDR.h + src/CubicSDR.h src/SDRThread.h src/IQBufferThread.h src/PrimaryGLContext.h @@ -103,6 +104,7 @@ SET (cubicsdr_headers src/SDRThreadQueue.h src/SDRThreadTask.h src/Demodulator.h + src/Gradient.h ) #configure_files(${PROJECT_SOURCE_DIR}/shaders ${PROJECT_BINARY_DIR}/shaders COPYONLY) #configure_files(${PROJECT_SOURCE_DIR}/png ${PROJECT_BINARY_DIR}/png COPYONLY) diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index ed1593c..62c2d3a 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -2,7 +2,7 @@ #define BUF_SIZE (16 * 32 * 256) #define SRATE 2500000 -#define FFT_SIZE 8192 +#define FFT_SIZE 4096 #define DEFAULT_FREQ 107500000 diff --git a/src/Gradient.cpp b/src/Gradient.cpp new file mode 100644 index 0000000..fa33aba --- /dev/null +++ b/src/Gradient.cpp @@ -0,0 +1,2 @@ +#include "Gradient.h" + diff --git a/src/Gradient.h b/src/Gradient.h new file mode 100644 index 0000000..ce3b974 --- /dev/null +++ b/src/Gradient.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +class GradientColor { +public: + float r,g,b; + float w; + + GradientColor(float r_in, float g_in, float b_in) : r(r_in), g(g_in), b(b_in) { + + }; +}; + +class Gradient { +public: + Gradient() { + + } + + void addColor(GradientColor c) { + colors.push_back(c); + } + + void generate(std::vector *out, unsigned int len) { + int chunk_size = len/(colors.size()-1); + + out->resize(len*3); + + int p = 0; + + for (unsigned int j = 0, jMax = colors.size()-1; j < jMax; j++) { + if (chunk_size*3 < len && j == jMax-1) { + chunk_size += len-chunk_size*3; + } + + for (unsigned int i = 0; i < chunk_size; i++) { + float idx = (float)(i+1)/(float)chunk_size; + + float r1 = colors[j].r; + float g1 = colors[j].g; + float b1 = colors[j].b; + + float r2 = colors[j+1].r; + float g2 = colors[j+1].g; + float b2 = colors[j+1].b; + + float r = r1 + (r2-r1) * idx; + float g = g1 + (g2-g1) * idx; + float b = b1 + (b2-b1) * idx; + + (*out)[p*3] = (unsigned char)(r*255.0); + (*out)[p*3+1] = (unsigned char)(g*255.0); + (*out)[p*3+2] = (unsigned char)(b*255.0); + + p++; + } + } + } + + ~Gradient() { + + } +private: + std::vector colors; +}; \ No newline at end of file diff --git a/src/PrimaryGLContext.cpp b/src/PrimaryGLContext.cpp index 636a852..58d7a03 100644 --- a/src/PrimaryGLContext.cpp +++ b/src/PrimaryGLContext.cpp @@ -62,7 +62,7 @@ PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas) : CheckGLError(); } -void PrimaryGLContext::Plot(std::vector &points, std::vector &points2) { +void PrimaryGLContext::Plot(std::vector &points, std::vector &points2, GLuint tex) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); @@ -91,7 +91,21 @@ void PrimaryGLContext::Plot(std::vector &points, std::vector &poin glDisableClientState(GL_VERTEX_ARRAY); glPopMatrix(); } - + + glEnable(GL_TEXTURE_2D); + // glEnable(GL_COLOR_TABLE); + glBindTexture(GL_TEXTURE_2D, tex); + glBegin(GL_QUADS); + glTexCoord2f(0.0,0.0); + glVertex3f(-0.8,-1.0,0.0); + glTexCoord2f(1.0,0.0); + glVertex3f(1.0,-1.0,0.0); + glTexCoord2f(1.0,1.0); + glVertex3f(1.0,1.0,0.0); + glTexCoord2f(0.0,1.0); + glVertex3f(-1.0,1.0,0.0); + glEnd(); + glFlush(); CheckGLError(); @@ -120,7 +134,40 @@ TestGLCanvas::TestGLCanvas(wxWindow *parent, int *attribList) : fft_ceil_ma = fft_ceil_maa = 1.0; + grad.addColor(GradientColor(0,0.5,1.0)); + grad.addColor(GradientColor(1.0,0,0)); + grad.addColor(GradientColor(0,1.0,1.0)); + + grad.generate(&color_map,256); + glGenTextures(1, &waterfall); + std::cout << waterfall << std::endl; + std::cout << waterfall << std::endl; + std::cout << waterfall << std::endl; + std::cout << waterfall << std::endl; + // for (int i = 0; i < c.size()/3; i++) { + // std::cout << i << ": r[" << (int)c[i*3] << "] g[" << (int)c[i*3+1] << "] b[" << (int)c[i*3+2] << "] " << std::endl; + // } + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D,waterfall); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // glTexImage2D(GL_TEXTURE_2D,0,GL_INTENSITY,FFT_SIZE,NUM_WATERFALL_LINES,0,GL_COLOR_INDEX,GL_UNSIGNED_BYTE,(GLvoid *)waterfall_tex); + float clr[16] = { 255, 0, 0, 0, + 0, 255, 0, 0, + 0, 0, 255, 0, + 255, 255, 255, 0 }; + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,2,2,0,GL_RGBA,GL_UNSIGNED_BYTE,clr); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + + // glColorTable(GL_TEXTURE_2D,GL_RGB8,256,GL_RGB,GL_UNSIGNED_BYTE,&color_map[0]); + } TestGLCanvas::~TestGLCanvas() { @@ -134,9 +181,8 @@ void TestGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { PrimaryGLContext& canvas = wxGetApp().GetContext(this); glViewport(0, 0, ClientSize.x, ClientSize.y); - std::vector null_pts; - canvas.Plot(spectrum_points, test_demod.waveform_points); + canvas.Plot(spectrum_points, test_demod.waveform_points, waterfall); SwapBuffers(); } @@ -233,12 +279,18 @@ void TestGLCanvas::setData(std::vector *data) { // fftw_execute(plan[1]); + + memmove(waterfall_tex+NUM_WATERFALL_LINES,waterfall_tex,(NUM_WATERFALL_LINES-1)*FFT_SIZE); + for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { spectrum_points[i * 2 + 1] = log10(fft_result_maa[i]) / log10(fft_ceil_maa); // spectrum_points[i * 2 + 1] = (fft_result_maa[i]) / (fft_ceil_maa); - spectrum_points[i * 2] = ((double) i / (double) iMax); + float v = ((float) i / (float) iMax); + spectrum_points[i * 2] = v; + waterfall_tex[i] = (unsigned char)(v*255.0); } + test_demod.writeBuffer(data); } } diff --git a/src/PrimaryGLContext.h b/src/PrimaryGLContext.h index 9d25844..dd6b914 100644 --- a/src/PrimaryGLContext.h +++ b/src/PrimaryGLContext.h @@ -10,11 +10,15 @@ #include "fftw3.h" #include "Demodulator.h" +#include "Gradient.h" + +#define NUM_WATERFALL_LINES 256 + class PrimaryGLContext: public wxGLContext { public: PrimaryGLContext(wxGLCanvas *canvas); - void Plot(std::vector &points, std::vector &points2); + void Plot(std::vector &points, std::vector &points2, GLuint tex); private: }; @@ -44,6 +48,14 @@ private: std::vector fft_result_ma; std::vector fft_result_maa; + Gradient grad; + + std::vector color_map; + + unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; + + GLuint waterfall; + Demodulator test_demod; wxDECLARE_EVENT_TABLE(); }; From 86dadb8f2ca4eb6239c4c0d0fe4dc5fc5e63ba90 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Tue, 11 Nov 2014 20:25:16 -0500 Subject: [PATCH 2/7] Waterfall now being displayed 2048x512 Uses indexed color scheme, FFT image is byte array --- src/AppFrame.cpp | 16 ++--- src/CubicSDRDefs.h | 2 +- src/Gradient.h | 45 ++++++++++---- src/PrimaryGLContext.cpp | 126 ++++++++++++++++++++------------------- src/PrimaryGLContext.h | 14 ++--- 5 files changed, 116 insertions(+), 87 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index eb1046f..3730bce 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -66,13 +66,6 @@ AppFrame::AppFrame() : } AppFrame::~AppFrame() { - delete t_SDR; -// delete t_IQBuffer; - delete m_pQueue; -} - -void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { - { wxCriticalSectionLocker enter(m_pThreadCS); if (t_SDR) { @@ -94,6 +87,15 @@ void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { } } } + + delete t_SDR; +// delete t_IQBuffer; + delete m_pQueue; +} + +void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { + + // true is to force the frame to close Close(true); } diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 62c2d3a..b1d55e3 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -2,7 +2,7 @@ #define BUF_SIZE (16 * 32 * 256) #define SRATE 2500000 -#define FFT_SIZE 4096 +#define FFT_SIZE 2048 #define DEFAULT_FREQ 107500000 diff --git a/src/Gradient.h b/src/Gradient.h index ce3b974..6a529cc 100644 --- a/src/Gradient.h +++ b/src/Gradient.h @@ -21,21 +21,34 @@ public: void addColor(GradientColor c) { colors.push_back(c); } - - void generate(std::vector *out, unsigned int len) { + + std::vector &getRed() { + return r_val; + } + + std::vector &getGreen() { + return g_val; + } + + std::vector &getBlue() { + return b_val; + } + + void generate(unsigned int len) { int chunk_size = len/(colors.size()-1); - out->resize(len*3); - int p = 0; + r_val.resize(len); + g_val.resize(len); + b_val.resize(len); for (unsigned int j = 0, jMax = colors.size()-1; j < jMax; j++) { - if (chunk_size*3 < len && j == jMax-1) { - chunk_size += len-chunk_size*3; + if (chunk_size*(jMax+1) < len && j == jMax-1) { + chunk_size += len-chunk_size*(jMax+1); } for (unsigned int i = 0; i < chunk_size; i++) { - float idx = (float)(i+1)/(float)chunk_size; + float idx = (float)(i)/(float)chunk_size; float r1 = colors[j].r; float g1 = colors[j].g; @@ -49,9 +62,16 @@ public: float g = g1 + (g2-g1) * idx; float b = b1 + (b2-b1) * idx; - (*out)[p*3] = (unsigned char)(r*255.0); - (*out)[p*3+1] = (unsigned char)(g*255.0); - (*out)[p*3+2] = (unsigned char)(b*255.0); + if (r<0.0) r = 0.0; + if (r>1.0) r = 1.0; + if (g<0.0) g = 0.0; + if (g>1.0) g = 1.0; + if (b<0.0) b = 0.0; + if (b>1.0) b = 1.0; + + r_val[p] = r; + g_val[p] = g; + b_val[p] = b; p++; } @@ -63,4 +83,7 @@ public: } private: std::vector colors; -}; \ No newline at end of file + std::vector r_val; + std::vector g_val; + std::vector b_val; +}; diff --git a/src/PrimaryGLContext.cpp b/src/PrimaryGLContext.cpp index 58d7a03..8ffa11b 100644 --- a/src/PrimaryGLContext.cpp +++ b/src/PrimaryGLContext.cpp @@ -59,17 +59,57 @@ PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas) : glMatrixMode(GL_PROJECTION); glLoadIdentity(); + glGenTextures(1, &waterfall); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, waterfall); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // glTexImage2D(GL_TEXTURE_2D,0,GL_INTENSITY,FFT_SIZE,NUM_WATERFALL_LINES,0,GL_COLOR_INDEX,GL_UNSIGNED_BYTE,(GLvoid *)waterfall_tex); + float clr[16] = { 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255 }; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + 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); + // glTexImage2D(GL_TEXTURE_2D,0,GL_INTENSITY,FFT_SIZE,NUM_WATERFALL_LINES,0,GL_COLOR_INDEX,GL_UNSIGNED_BYTE,(GLvoid *)waterfall_tex); + + unsigned char rmap[256]; + unsigned char gmap[256]; + unsigned char bmap[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]); + CheckGLError(); } -void PrimaryGLContext::Plot(std::vector &points, std::vector &points2, GLuint tex) { +void PrimaryGLContext::Plot(std::vector &points, std::vector &points2, unsigned char *waterfall_tex) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + glBindTexture(GL_TEXTURE_2D, waterfall); +// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, waterfall_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); // glEnable(GL_LINE_SMOOTH); + glDisable(GL_TEXTURE_2D); + + glColor3f(1.0,1.0,1.0); + if (points.size()) { glPushMatrix(); glTranslatef(-1.0f, -0.9f, 0.0f); @@ -91,21 +131,21 @@ void PrimaryGLContext::Plot(std::vector &points, std::vector &poin glDisableClientState(GL_VERTEX_ARRAY); glPopMatrix(); } - + glEnable(GL_TEXTURE_2D); // glEnable(GL_COLOR_TABLE); - glBindTexture(GL_TEXTURE_2D, tex); + glBindTexture(GL_TEXTURE_2D, waterfall); glBegin(GL_QUADS); - glTexCoord2f(0.0,0.0); - glVertex3f(-0.8,-1.0,0.0); - glTexCoord2f(1.0,0.0); - glVertex3f(1.0,-1.0,0.0); - glTexCoord2f(1.0,1.0); - glVertex3f(1.0,1.0,0.0); - glTexCoord2f(0.0,1.0); - glVertex3f(-1.0,1.0,0.0); + glTexCoord2f(0.0, 0.0); + glVertex3f(-1.0, -1.0, 0.0); + glTexCoord2f(1.0, 0.0); + glVertex3f(1.0, -1.0, 0.0); + glTexCoord2f(1.0, 1.0); + glVertex3f(1.0, 1.0, 0.0); + glTexCoord2f(0.0, 1.0); + glVertex3f(-1.0, 1.0, 0.0); glEnd(); - + glFlush(); CheckGLError(); @@ -116,13 +156,10 @@ EVT_KEY_DOWN(TestGLCanvas::OnKeyDown) EVT_IDLE(TestGLCanvas::OnIdle) wxEND_EVENT_TABLE() - - TestGLCanvas::TestGLCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE), parent(parent) { - int in_block_size = BUF_SIZE / 2; int out_block_size = FFT_SIZE; @@ -134,40 +171,6 @@ TestGLCanvas::TestGLCanvas(wxWindow *parent, int *attribList) : fft_ceil_ma = fft_ceil_maa = 1.0; - grad.addColor(GradientColor(0,0.5,1.0)); - grad.addColor(GradientColor(1.0,0,0)); - grad.addColor(GradientColor(0,1.0,1.0)); - - grad.generate(&color_map,256); - - glGenTextures(1, &waterfall); - std::cout << waterfall << std::endl; - std::cout << waterfall << std::endl; - std::cout << waterfall << std::endl; - std::cout << waterfall << std::endl; - // for (int i = 0; i < c.size()/3; i++) { - // std::cout << i << ": r[" << (int)c[i*3] << "] g[" << (int)c[i*3+1] << "] b[" << (int)c[i*3+2] << "] " << std::endl; - // } - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D,waterfall); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - // glTexImage2D(GL_TEXTURE_2D,0,GL_INTENSITY,FFT_SIZE,NUM_WATERFALL_LINES,0,GL_COLOR_INDEX,GL_UNSIGNED_BYTE,(GLvoid *)waterfall_tex); - float clr[16] = { 255, 0, 0, 0, - 0, 255, 0, 0, - 0, 0, 255, 0, - 255, 255, 255, 0 }; - glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,2,2,0,GL_RGBA,GL_UNSIGNED_BYTE,clr); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - - // glColorTable(GL_TEXTURE_2D,GL_RGB8,256,GL_RGB,GL_UNSIGNED_BYTE,&color_map[0]); - } TestGLCanvas::~TestGLCanvas() { @@ -181,8 +184,7 @@ void TestGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { PrimaryGLContext& canvas = wxGetApp().GetContext(this); glViewport(0, 0, ClientSize.x, ClientSize.y); - - canvas.Plot(spectrum_points, test_demod.waveform_points, waterfall); + canvas.Plot(spectrum_points, test_demod.waveform_points, waterfall_tex); SwapBuffers(); } @@ -279,17 +281,19 @@ void TestGLCanvas::setData(std::vector *data) { // fftw_execute(plan[1]); - - memmove(waterfall_tex+NUM_WATERFALL_LINES,waterfall_tex,(NUM_WATERFALL_LINES-1)*FFT_SIZE); - - for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { - spectrum_points[i * 2 + 1] = log10(fft_result_maa[i]) / log10(fft_ceil_maa); -// spectrum_points[i * 2 + 1] = (fft_result_maa[i]) / (fft_ceil_maa); - float v = ((float) i / (float) iMax); - spectrum_points[i * 2] = v; - waterfall_tex[i] = (unsigned char)(v*255.0); - } + memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); + for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { + float v = log10(fft_result_maa[i]) / log10(fft_ceil_maa); + + float wv = v; + if (wv<0.0) wv = 0.0; + if (wv>1.0) wv = 1.0; + waterfall_tex[i] = (unsigned char) floor(wv * 255.0); + + spectrum_points[i * 2] = ((float) i / (float) iMax); + spectrum_points[i * 2 + 1] = v; + } test_demod.writeBuffer(data); } diff --git a/src/PrimaryGLContext.h b/src/PrimaryGLContext.h index dd6b914..8e0aac0 100644 --- a/src/PrimaryGLContext.h +++ b/src/PrimaryGLContext.h @@ -12,15 +12,19 @@ #include "Gradient.h" -#define NUM_WATERFALL_LINES 256 +#define NUM_WATERFALL_LINES 512 class PrimaryGLContext: public wxGLContext { public: PrimaryGLContext(wxGLCanvas *canvas); - void Plot(std::vector &points, std::vector &points2, GLuint tex); + void Plot(std::vector &points, std::vector &points2, unsigned char *waterfall_tex); private: + + Gradient grad; + + GLuint waterfall; }; class TestGLCanvas: public wxGLCanvas { @@ -48,14 +52,10 @@ private: std::vector fft_result_ma; std::vector fft_result_maa; - Gradient grad; - - std::vector color_map; unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; - GLuint waterfall; - + Demodulator test_demod; wxDECLARE_EVENT_TABLE(); }; From 810533750f638cf2f2c918a7d1eb76c4a30ac1cd Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Tue, 11 Nov 2014 21:42:18 -0500 Subject: [PATCH 3/7] waterfall optimize, fm demod audio filtering Moved waterfall increment into rendering to reduce audio processing impact. Added another test filter and resample stage to FM demod experiment to clean up audio and remove high pitch on stereo channels until it can be demodulated. --- src/Demodulator.cpp | 45 +++++++++++++++++++++++++++++++--------- src/Demodulator.h | 14 ++++++++++--- src/PrimaryGLContext.cpp | 26 ++++++++++++++--------- src/PrimaryGLContext.h | 7 ++----- 4 files changed, 64 insertions(+), 28 deletions(-) diff --git a/src/Demodulator.cpp b/src/Demodulator.cpp index 792a2f0..3927d2c 100644 --- a/src/Demodulator.cpp +++ b/src/Demodulator.cpp @@ -41,10 +41,12 @@ static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned Demodulator::Demodulator() { - bandwidth = 800000; + bandwidth = 300000; resample_ratio = (float) (bandwidth) / (float) SRATE; - audio_frequency = 44100; - audio_resample_ratio = (float) (audio_frequency) / (float) bandwidth; + wbfm_frequency = 32000; + wbfm_resample_ratio = (float) (wbfm_frequency) / (float) bandwidth; + audio_frequency = 48000; + audio_resample_ratio = (float) (audio_frequency) / (float) wbfm_frequency; PaError err; err = Pa_Initialize(); @@ -83,7 +85,7 @@ Demodulator::Demodulator() { stream = NULL; - err = Pa_OpenStream(&stream, NULL, &outputParameters, 44100, 256, paClipOff, &patestCallback, this); + err = Pa_OpenStream(&stream, NULL, &outputParameters, audio_frequency, 256, paClipOff, &patestCallback, this); err = Pa_StartStream(stream); if (err != paNoError) { @@ -103,14 +105,22 @@ Demodulator::Demodulator() { fir_filter = firfilt_crcf_create(h, h_len); + h_len = estimate_req_filter_len(ft, As); + liquid_firdes_kaiser(h_len, 0.3f, As, mu, h); + + fir_audio_filter = firfilt_crcf_create(h, h_len); + // create multi-stage arbitrary resampler object resampler = msresamp_crcf_create(resample_ratio, As); msresamp_crcf_print(resampler); + wbfm_resampler = msresamp_crcf_create(wbfm_resample_ratio, As); + msresamp_crcf_print(wbfm_resampler); + audio_resampler = msresamp_crcf_create(audio_resample_ratio, As); msresamp_crcf_print(audio_resampler); - float kf = 0.1f; // modulation factor + float kf = 0.5f; // modulation factor fdem = freqdem_create(kf); freqdem_print(fdem); @@ -166,17 +176,32 @@ void Demodulator::writeBuffer(std::vector *data) { } } - int audio_out_size = ceil((float) (num_written) * audio_resample_ratio); + + int wbfm_out_size = ceil((float) (num_written) * wbfm_resample_ratio); + liquid_float_complex resampled_wbfm_output[wbfm_out_size]; + + unsigned int num_wbfm_written; + msresamp_crcf_execute(wbfm_resampler, resampled_output, num_written, resampled_wbfm_output, &num_wbfm_written); + + + for (int i = 0; i < num_wbfm_written; i++) { + firfilt_crcf_push(fir_audio_filter, resampled_wbfm_output[i]); + firfilt_crcf_execute(fir_audio_filter, &resampled_wbfm_output[i]); + } + + int audio_out_size = ceil((float) (num_wbfm_written) * audio_resample_ratio); liquid_float_complex resampled_audio_output[audio_out_size]; - unsigned int num_audio_written; // number of values written to buffer - msresamp_crcf_execute(audio_resampler, resampled_output, num_written, resampled_audio_output, &num_audio_written); + unsigned int num_audio_written; + msresamp_crcf_execute(audio_resampler, resampled_wbfm_output, num_wbfm_written, resampled_audio_output, &num_audio_written); std::vector *newBuffer = new std::vector; newBuffer->resize(num_audio_written * 2); for (int i = 0; i < num_audio_written; i++) { - (*newBuffer)[i * 2] = resampled_audio_output[i].real; - (*newBuffer)[i * 2 + 1] = resampled_audio_output[i].real; + liquid_float_complex y = resampled_audio_output[i]; + + (*newBuffer)[i * 2] = y.real; + (*newBuffer)[i * 2 + 1] = y.real; } audio_queue.push(newBuffer); diff --git a/src/Demodulator.h b/src/Demodulator.h index 6d085c4..cbf483e 100644 --- a/src/Demodulator.h +++ b/src/Demodulator.h @@ -31,14 +31,22 @@ public: private: firfilt_crcf fir_filter; + firfilt_crcf fir_audio_filter; + + unsigned int bandwidth; msresamp_crcf resampler; - msresamp_crcf audio_resampler; float resample_ratio; - unsigned int bandwidth; - unsigned int audio_frequency; + + msresamp_crcf wbfm_resampler; + float wbfm_resample_ratio; + unsigned int wbfm_frequency; + + msresamp_crcf audio_resampler; float audio_resample_ratio; + unsigned int audio_frequency; + PaStreamParameters outputParameters; PaStream *stream; freqdem fdem; diff --git a/src/PrimaryGLContext.cpp b/src/PrimaryGLContext.cpp index 8ffa11b..a2f55fe 100644 --- a/src/PrimaryGLContext.cpp +++ b/src/PrimaryGLContext.cpp @@ -95,12 +95,25 @@ PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas) : CheckGLError(); } -void PrimaryGLContext::Plot(std::vector &points, std::vector &points2, unsigned char *waterfall_tex) { +void PrimaryGLContext::Plot(std::vector &points, std::vector &points2) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + if (points.size()) { + memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); + + for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { + float v = points[i*2+1]; + + float wv = v; + if (wv<0.0) wv = 0.0; + if (wv>1.0) wv = 1.0; + waterfall_tex[i] = (unsigned char) floor(wv * 255.0); + } + } + glBindTexture(GL_TEXTURE_2D, waterfall); // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, waterfall_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); @@ -132,6 +145,7 @@ void PrimaryGLContext::Plot(std::vector &points, std::vector &poin glPopMatrix(); } + glEnable(GL_TEXTURE_2D); // glEnable(GL_COLOR_TABLE); glBindTexture(GL_TEXTURE_2D, waterfall); @@ -184,7 +198,7 @@ void TestGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { PrimaryGLContext& canvas = wxGetApp().GetContext(this); glViewport(0, 0, ClientSize.x, ClientSize.y); - canvas.Plot(spectrum_points, test_demod.waveform_points, waterfall_tex); + canvas.Plot(spectrum_points, test_demod.waveform_points); SwapBuffers(); } @@ -281,16 +295,8 @@ void TestGLCanvas::setData(std::vector *data) { // fftw_execute(plan[1]); - memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); - for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { float v = log10(fft_result_maa[i]) / log10(fft_ceil_maa); - - float wv = v; - if (wv<0.0) wv = 0.0; - if (wv>1.0) wv = 1.0; - waterfall_tex[i] = (unsigned char) floor(wv * 255.0); - spectrum_points[i * 2] = ((float) i / (float) iMax); spectrum_points[i * 2 + 1] = v; } diff --git a/src/PrimaryGLContext.h b/src/PrimaryGLContext.h index 8e0aac0..813c459 100644 --- a/src/PrimaryGLContext.h +++ b/src/PrimaryGLContext.h @@ -18,13 +18,14 @@ class PrimaryGLContext: public wxGLContext { public: PrimaryGLContext(wxGLCanvas *canvas); - void Plot(std::vector &points, std::vector &points2, unsigned char *waterfall_tex); + void Plot(std::vector &points, std::vector &points2); private: Gradient grad; GLuint waterfall; + unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; }; class TestGLCanvas: public wxGLCanvas { @@ -52,10 +53,6 @@ private: std::vector fft_result_ma; std::vector fft_result_maa; - - unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; - - Demodulator test_demod; wxDECLARE_EVENT_TABLE(); }; From adc6fcce8556be330692c0a335ce80f96af924a9 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 12 Nov 2014 00:28:20 -0500 Subject: [PATCH 4/7] Waterfall tweaking, automatic range adjustment --- src/Demodulator.cpp | 2 +- src/PrimaryGLContext.cpp | 32 ++++++++++++++++---------------- src/PrimaryGLContext.h | 1 + 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/Demodulator.cpp b/src/Demodulator.cpp index 3927d2c..e73f0df 100644 --- a/src/Demodulator.cpp +++ b/src/Demodulator.cpp @@ -85,7 +85,7 @@ Demodulator::Demodulator() { stream = NULL; - err = Pa_OpenStream(&stream, NULL, &outputParameters, audio_frequency, 256, paClipOff, &patestCallback, this); + err = Pa_OpenStream(&stream, NULL, &outputParameters, audio_frequency, 1024, paClipOff, &patestCallback, this); err = Pa_StartStream(stream); if (err != paNoError) { diff --git a/src/PrimaryGLContext.cpp b/src/PrimaryGLContext.cpp index a2f55fe..243b9d4 100644 --- a/src/PrimaryGLContext.cpp +++ b/src/PrimaryGLContext.cpp @@ -64,8 +64,7 @@ PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas) : glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, waterfall); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // glTexImage2D(GL_TEXTURE_2D,0,GL_INTENSITY,FFT_SIZE,NUM_WATERFALL_LINES,0,GL_COLOR_INDEX,GL_UNSIGNED_BYTE,(GLvoid *)waterfall_tex); - float clr[16] = { 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255 }; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -73,7 +72,6 @@ PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas) : 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)); @@ -81,11 +79,6 @@ PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas) : grad.addColor(GradientColor(1.0, 0.2, 0.0)); grad.generate(256); - // glTexImage2D(GL_TEXTURE_2D,0,GL_INTENSITY,FFT_SIZE,NUM_WATERFALL_LINES,0,GL_COLOR_INDEX,GL_UNSIGNED_BYTE,(GLvoid *)waterfall_tex); - - unsigned char rmap[256]; - unsigned char gmap[256]; - unsigned char bmap[256]; glPixelTransferi(GL_MAP_COLOR, GL_TRUE); glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, &(grad.getRed())[0]); @@ -115,9 +108,7 @@ void PrimaryGLContext::Plot(std::vector &points, std::vector &poin } glBindTexture(GL_TEXTURE_2D, waterfall); -// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, waterfall_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); -// glEnable(GL_LINE_SMOOTH); glDisable(GL_TEXTURE_2D); @@ -183,8 +174,8 @@ TestGLCanvas::TestGLCanvas(wxWindow *parent, int *attribList) : plan[0] = fftw_plan_dft_1d(out_block_size, in, out[0], FFTW_FORWARD, FFTW_MEASURE); plan[1] = fftw_plan_dft_1d(out_block_size, out[0], out[1], FFTW_BACKWARD, FFTW_MEASURE); - fft_ceil_ma = fft_ceil_maa = 1.0; - + fft_ceil_ma = fft_ceil_maa = 100.0; + fft_floor_ma = fft_floor_maa = 0.0; } TestGLCanvas::~TestGLCanvas() { @@ -256,7 +247,7 @@ void TestGLCanvas::setData(std::vector *data) { fftw_execute(plan[0]); - double fft_ceil = 0; + double fft_ceil = 0, fft_floor=1; if (fft_result.size() < FFT_SIZE) { fft_result.resize(FFT_SIZE); @@ -288,15 +279,24 @@ void TestGLCanvas::setData(std::vector *data) { 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_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; - fft_ceil_maa = fft_ceil_maa + (fft_ceil - fft_ceil_maa) * 0.05; + fft_ceil += 1; + fft_floor -= 1; + + fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.01; + fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 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; // fftw_execute(plan[1]); for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { - float v = log10(fft_result_maa[i]) / log10(fft_ceil_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 + 1] = v; } diff --git a/src/PrimaryGLContext.h b/src/PrimaryGLContext.h index 813c459..700a220 100644 --- a/src/PrimaryGLContext.h +++ b/src/PrimaryGLContext.h @@ -48,6 +48,7 @@ private: fftw_plan plan[2]; float fft_ceil_ma, fft_ceil_maa; + float fft_floor_ma, fft_floor_maa; std::vector fft_result; std::vector fft_result_ma; From 02cd0f6516096b1dfce8cbd1cb6f83684b4dff4b Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 12 Nov 2014 19:09:22 -0500 Subject: [PATCH 5/7] FM Demodulator fixes was an implicit float to int in the primary filter cutoff value causing distortion. --- src/Demodulator.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Demodulator.cpp b/src/Demodulator.cpp index e73f0df..c2af8f3 100644 --- a/src/Demodulator.cpp +++ b/src/Demodulator.cpp @@ -41,9 +41,9 @@ static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned Demodulator::Demodulator() { - bandwidth = 300000; + bandwidth = 200000; resample_ratio = (float) (bandwidth) / (float) SRATE; - wbfm_frequency = 32000; + wbfm_frequency = 100000; wbfm_resample_ratio = (float) (wbfm_frequency) / (float) bandwidth; audio_frequency = 48000; audio_resample_ratio = (float) (audio_frequency) / (float) wbfm_frequency; @@ -93,7 +93,7 @@ Demodulator::Demodulator() { std::cout << "\tPortAudio error: " << Pa_GetErrorText(err) << std::endl; } - float fc = 0.5f * (bandwidth / SRATE); // filter cutoff frequency + float fc = 0.5f * ((float)bandwidth / (float)SRATE) * 0.75; // filter cutoff frequency float ft = 0.05f; // filter transition float As = 60.0f; // stop-band attenuation [dB] float mu = 0.0f; // fractional timing offset @@ -106,7 +106,7 @@ Demodulator::Demodulator() { fir_filter = firfilt_crcf_create(h, h_len); h_len = estimate_req_filter_len(ft, As); - liquid_firdes_kaiser(h_len, 0.3f, As, mu, h); + liquid_firdes_kaiser(h_len, 32000.0/(float)wbfm_frequency, As, mu, h); fir_audio_filter = firfilt_crcf_create(h, h_len); @@ -120,7 +120,7 @@ Demodulator::Demodulator() { audio_resampler = msresamp_crcf_create(audio_resample_ratio, As); msresamp_crcf_print(audio_resampler); - float kf = 0.5f; // modulation factor + float kf = 0.75; // modulation factor fdem = freqdem_create(kf); freqdem_print(fdem); From 541ab5614c79e456f0e4d8cb0296ecb9dd8d21f4 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 12 Nov 2014 21:55:11 -0500 Subject: [PATCH 6/7] Separate Primary GL Context and visuals canvas Can now create multiple GL canvases with shared context --- CMakeLists.txt | 12 +- src/AppFrame.cpp | 3 +- src/AppFrame.h | 3 +- src/CubicSDR.cpp | 2 +- src/CubicSDRDefs.h | 2 +- src/Gradient.cpp | 73 ++++++++++ src/Gradient.h | 80 ++--------- src/PrimaryGLContext.cpp | 261 +----------------------------------- src/PrimaryGLContext.h | 46 +------ src/visual/ScopeCanvas.cpp | 171 +++++++++++++++++++++++ src/visual/ScopeCanvas.h | 43 ++++++ src/visual/ScopeContext.cpp | 107 +++++++++++++++ src/visual/ScopeContext.h | 20 +++ 13 files changed, 448 insertions(+), 375 deletions(-) create mode 100644 src/visual/ScopeCanvas.cpp create mode 100644 src/visual/ScopeCanvas.h create mode 100644 src/visual/ScopeContext.cpp create mode 100644 src/visual/ScopeContext.h diff --git a/CMakeLists.txt b/CMakeLists.txt index db83905..e7a44f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,9 @@ SET (cubicsdr_sources src/SDRThreadQueue.cpp src/SDRThreadTask.cpp src/Demodulator.cpp - src/Gradient.cpp + src/Gradient.cpp + src/visual/ScopeCanvas.cpp + src/visual/ScopeContext.cpp ) SET (cubicsdr_headers @@ -104,8 +106,14 @@ SET (cubicsdr_headers src/SDRThreadQueue.h src/SDRThreadTask.h src/Demodulator.h - src/Gradient.h + src/Gradient.h + src/visual/ScopeCanvas.h + src/visual/ScopeContext.h ) + +include_directories ( ${PROJECT_SOURCE_DIR}/src/visual + ${PROJECT_SOURCE_DIR}/src ) + #configure_files(${PROJECT_SOURCE_DIR}/shaders ${PROJECT_BINARY_DIR}/shaders COPYONLY) #configure_files(${PROJECT_SOURCE_DIR}/png ${PROJECT_BINARY_DIR}/png COPYONLY) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 3730bce..dfeb823 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -23,7 +23,7 @@ wxEND_EVENT_TABLE() AppFrame::AppFrame() : wxFrame(NULL, wxID_ANY, wxT("CubicSDR")), frequency(DEFAULT_FREQ) { - canvas = new TestGLCanvas(this, NULL); + canvas = new ScopeCanvas(this, NULL); // SetIcon(wxICON(sample)); @@ -95,7 +95,6 @@ AppFrame::~AppFrame() { void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { - // true is to force the frame to close Close(true); } diff --git a/src/AppFrame.h b/src/AppFrame.h index 50cfe8b..fdac905 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -3,6 +3,7 @@ #include "wx/frame.h" #include "PrimaryGLContext.h" #include "SDRThread.h" +#include "ScopeCanvas.h" // Define a new frame type class AppFrame: public wxFrame { @@ -19,7 +20,7 @@ private: void OnNewWindow(wxCommandEvent& event); void OnIdle(wxIdleEvent& event); - TestGLCanvas *canvas; + ScopeCanvas *canvas; SDRThread *t_SDR; IQBufferThread *t_IQBuffer; wxCriticalSection m_pThreadCS; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 1f93b67..54dba5f 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -42,7 +42,7 @@ int CubicSDR::OnExit() { PrimaryGLContext& CubicSDR::GetContext(wxGLCanvas *canvas) { PrimaryGLContext *glContext; if (!m_glContext) { - m_glContext = new PrimaryGLContext(canvas); + m_glContext = new PrimaryGLContext(canvas, NULL); } glContext = m_glContext; diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index b1d55e3..244b2d2 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -4,5 +4,5 @@ #define SRATE 2500000 #define FFT_SIZE 2048 -#define DEFAULT_FREQ 107500000 +#define DEFAULT_FREQ 105700000 diff --git a/src/Gradient.cpp b/src/Gradient.cpp index fa33aba..0dba7ca 100644 --- a/src/Gradient.cpp +++ b/src/Gradient.cpp @@ -1,2 +1,75 @@ #include "Gradient.h" +Gradient::Gradient() { + +} + +void Gradient::addColor(GradientColor c) { + colors.push_back(c); +} + +std::vector &Gradient::getRed() { + return r_val; +} + +std::vector &Gradient::getGreen() { + return g_val; +} + +std::vector &Gradient::getBlue() { + return b_val; +} + +void Gradient::generate(unsigned int len) { + int chunk_size = len / (colors.size() - 1); + + int p = 0; + r_val.resize(len); + g_val.resize(len); + b_val.resize(len); + + for (unsigned int j = 0, jMax = colors.size() - 1; j < jMax; j++) { + if (chunk_size * (jMax + 1) < len && j == jMax - 1) { + chunk_size += len - chunk_size * (jMax + 1); + } + + for (unsigned int i = 0; i < chunk_size; i++) { + float idx = (float) (i) / (float) chunk_size; + + float r1 = colors[j].r; + float g1 = colors[j].g; + float b1 = colors[j].b; + + float r2 = colors[j + 1].r; + float g2 = colors[j + 1].g; + float b2 = colors[j + 1].b; + + float r = r1 + (r2 - r1) * idx; + float g = g1 + (g2 - g1) * idx; + float b = b1 + (b2 - b1) * idx; + + if (r < 0.0) + r = 0.0; + if (r > 1.0) + r = 1.0; + if (g < 0.0) + g = 0.0; + if (g > 1.0) + g = 1.0; + if (b < 0.0) + b = 0.0; + if (b > 1.0) + b = 1.0; + + r_val[p] = r; + g_val[p] = g; + b_val[p] = b; + + p++; + } + } +} + +Gradient::~Gradient() { + +} diff --git a/src/Gradient.h b/src/Gradient.h index 6a529cc..10080cd 100644 --- a/src/Gradient.h +++ b/src/Gradient.h @@ -4,83 +4,27 @@ class GradientColor { public: - float r,g,b; + float r, g, b; float w; - - GradientColor(float r_in, float g_in, float b_in) : r(r_in), g(g_in), b(b_in) { - - }; + + GradientColor(float r_in, float g_in, float b_in) : + r(r_in), g(g_in), b(b_in), w(1) { + } }; class Gradient { public: - Gradient() { - - } - - void addColor(GradientColor c) { - colors.push_back(c); - } + Gradient(); - std::vector &getRed() { - return r_val; - } + void addColor(GradientColor c); - std::vector &getGreen() { - return g_val; - } + std::vector &getRed();; + std::vector &getGreen(); + std::vector &getBlue(); - std::vector &getBlue() { - return b_val; - } + void generate(unsigned int len); - void generate(unsigned int len) { - int chunk_size = len/(colors.size()-1); - - int p = 0; - r_val.resize(len); - g_val.resize(len); - b_val.resize(len); - - for (unsigned int j = 0, jMax = colors.size()-1; j < jMax; j++) { - if (chunk_size*(jMax+1) < len && j == jMax-1) { - chunk_size += len-chunk_size*(jMax+1); - } - - for (unsigned int i = 0; i < chunk_size; i++) { - float idx = (float)(i)/(float)chunk_size; - - float r1 = colors[j].r; - float g1 = colors[j].g; - float b1 = colors[j].b; - - float r2 = colors[j+1].r; - float g2 = colors[j+1].g; - float b2 = colors[j+1].b; - - float r = r1 + (r2-r1) * idx; - float g = g1 + (g2-g1) * idx; - float b = b1 + (b2-b1) * idx; - - if (r<0.0) r = 0.0; - if (r>1.0) r = 1.0; - if (g<0.0) g = 0.0; - if (g>1.0) g = 1.0; - if (b<0.0) b = 0.0; - if (b>1.0) b = 1.0; - - r_val[p] = r; - g_val[p] = g; - b_val[p] = b; - - p++; - } - } - } - - ~Gradient() { - - } + ~Gradient(); private: std::vector colors; std::vector r_val; diff --git a/src/PrimaryGLContext.cpp b/src/PrimaryGLContext.cpp index 243b9d4..6cde9a7 100644 --- a/src/PrimaryGLContext.cpp +++ b/src/PrimaryGLContext.cpp @@ -15,7 +15,7 @@ #include "AppFrame.h" #include -wxString glGetwxString(GLenum name) { +wxString PrimaryGLContext::glGetwxString(GLenum name) { const GLubyte *v = glGetString(name); if (v == 0) { // The error is not important. It is GL_INVALID_ENUM. @@ -28,7 +28,7 @@ wxString glGetwxString(GLenum name) { return wxString((const char*) v); } -static void CheckGLError() { +void PrimaryGLContext::CheckGLError() { GLenum errLast = GL_NO_ERROR; for (;;) { @@ -49,263 +49,10 @@ static void CheckGLError() { } } -PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas) : - wxGLContext(canvas) { +PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas, wxGLContext *sharedContext) : + wxGLContext(canvas, sharedContext) { SetCurrent(*canvas); - glEnable(GL_CULL_FACE); - glEnable(GL_DEPTH_TEST); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - glGenTextures(1, &waterfall); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, waterfall); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - 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]); - CheckGLError(); } -void PrimaryGLContext::Plot(std::vector &points, std::vector &points2) { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - if (points.size()) { - memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); - - for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { - float v = points[i*2+1]; - - float wv = v; - if (wv<0.0) wv = 0.0; - if (wv>1.0) wv = 1.0; - waterfall_tex[i] = (unsigned char) floor(wv * 255.0); - } - } - - 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); - - glDisable(GL_TEXTURE_2D); - - glColor3f(1.0,1.0,1.0); - - if (points.size()) { - glPushMatrix(); - glTranslatef(-1.0f, -0.9f, 0.0f); - glScalef(2.0f, 1.0f, 1.0f); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, &points[0]); - glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); - glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); - } - - if (points2.size()) { - glPushMatrix(); - glTranslatef(-1.0f, 0.5f, 0.0f); - glScalef(2.0f, 1.0f, 1.0f); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, &points2[0]); - glDrawArrays(GL_LINE_STRIP, 0, points2.size() / 2); - glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); - } - - - glEnable(GL_TEXTURE_2D); - // glEnable(GL_COLOR_TABLE); - glBindTexture(GL_TEXTURE_2D, waterfall); - glBegin(GL_QUADS); - glTexCoord2f(0.0, 0.0); - glVertex3f(-1.0, -1.0, 0.0); - glTexCoord2f(1.0, 0.0); - glVertex3f(1.0, -1.0, 0.0); - glTexCoord2f(1.0, 1.0); - glVertex3f(1.0, 1.0, 0.0); - glTexCoord2f(0.0, 1.0); - glVertex3f(-1.0, 1.0, 0.0); - glEnd(); - - glFlush(); - - CheckGLError(); -} - -wxBEGIN_EVENT_TABLE(TestGLCanvas, wxGLCanvas) EVT_PAINT(TestGLCanvas::OnPaint) -EVT_KEY_DOWN(TestGLCanvas::OnKeyDown) -EVT_IDLE(TestGLCanvas::OnIdle) -wxEND_EVENT_TABLE() - -TestGLCanvas::TestGLCanvas(wxWindow *parent, int *attribList) : - wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent) { - - int in_block_size = BUF_SIZE / 2; - int out_block_size = FFT_SIZE; - - in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * in_block_size); - out[0] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size); - out[1] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size); - plan[0] = fftw_plan_dft_1d(out_block_size, in, out[0], FFTW_FORWARD, FFTW_MEASURE); - plan[1] = fftw_plan_dft_1d(out_block_size, out[0], out[1], FFTW_BACKWARD, FFTW_MEASURE); - - fft_ceil_ma = fft_ceil_maa = 100.0; - fft_floor_ma = fft_floor_maa = 0.0; -} - -TestGLCanvas::~TestGLCanvas() { - -} - -void TestGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { - wxPaintDC dc(this); - const wxSize ClientSize = GetClientSize(); - - PrimaryGLContext& canvas = wxGetApp().GetContext(this); - glViewport(0, 0, ClientSize.x, ClientSize.y); - - canvas.Plot(spectrum_points, test_demod.waveform_points); - - SwapBuffers(); -} - -void TestGLCanvas::OnKeyDown(wxKeyEvent& event) { - float angle = 5.0; - - unsigned int freq; - switch (event.GetKeyCode()) { - case WXK_RIGHT: - freq = ((AppFrame*) parent)->getFrequency(); - freq += 100000; - ((AppFrame*) parent)->setFrequency(freq); - break; - case WXK_LEFT: - freq = ((AppFrame*) parent)->getFrequency(); - freq -= 100000; - ((AppFrame*) parent)->setFrequency(freq); - break; - case WXK_DOWN: - break; - case WXK_UP: - break; - case WXK_SPACE: - break; - default: - event.Skip(); - return; - } -} - -void multiply2(float ar, float aj, float br, float bj, float *cr, float *cj) { - *cr = ar * br - aj * bj; - *cj = aj * br + ar * bj; -} -float polar_discriminant2(float ar, float aj, float br, float bj) { - float cr, cj; - double angle; - multiply2(ar, aj, br, -bj, &cr, &cj); - angle = atan2(cj, cr); - return (angle / M_PI); -} - -void TestGLCanvas::setData(std::vector *data) { - - if (data && data->size()) { - if (spectrum_points.size() < FFT_SIZE * 2) { - spectrum_points.resize(FFT_SIZE * 2); - } - - for (int i = 0; i < BUF_SIZE / 2; i++) { - in[i][0] = (float) (*data)[i * 2] / 127.0f; - in[i][1] = (float) (*data)[i * 2 + 1] / 127.0f; - } - - fftw_execute(plan[0]); - - 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); - } - - for (int j = 0; j < 2; j++) { - for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) { - double a = out[0][i][0]; - double b = out[0][i][1]; - double c = sqrt(a * a + b * b); - - double x = out[0][FFT_SIZE / 2 + i][0]; - double y = out[0][FFT_SIZE / 2 + i][1]; - double z = sqrt(x * x + y * y); - - fft_result[i] = (z); - 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++) { - 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 += 1; - fft_floor -= 1; - - fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.01; - fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 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; - - // fftw_execute(plan[1]); - - 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; - } - - test_demod.writeBuffer(data); - } -} - -void TestGLCanvas::OnIdle(wxIdleEvent &event) { - Refresh(false); -} - diff --git a/src/PrimaryGLContext.h b/src/PrimaryGLContext.h index 700a220..5a92605 100644 --- a/src/PrimaryGLContext.h +++ b/src/PrimaryGLContext.h @@ -7,53 +7,13 @@ #include #include "CubicSDRDefs.h" -#include "fftw3.h" -#include "Demodulator.h" - -#include "Gradient.h" - -#define NUM_WATERFALL_LINES 512 class PrimaryGLContext: public wxGLContext { public: - PrimaryGLContext(wxGLCanvas *canvas); + PrimaryGLContext(wxGLCanvas *canvas, wxGLContext *sharedContext); - void Plot(std::vector &points, std::vector &points2); + static wxString glGetwxString(GLenum name); + static void CheckGLError(); private: - - Gradient grad; - - GLuint waterfall; - unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; -}; - -class TestGLCanvas: public wxGLCanvas { -public: - TestGLCanvas(wxWindow *parent, int *attribList = NULL); - ~TestGLCanvas(); - - void setData(std::vector *data); - -private: - void OnPaint(wxPaintEvent& event); - void OnKeyDown(wxKeyEvent& event); - - void OnIdle(wxIdleEvent &event); - - wxWindow *parent; - std::vector spectrum_points; - - fftw_complex *in, *out[2]; - fftw_plan plan[2]; - - float fft_ceil_ma, fft_ceil_maa; - float fft_floor_ma, fft_floor_maa; - - std::vector fft_result; - std::vector fft_result_ma; - std::vector fft_result_maa; - - Demodulator test_demod; -wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp new file mode 100644 index 0000000..e9d749f --- /dev/null +++ b/src/visual/ScopeCanvas.cpp @@ -0,0 +1,171 @@ +#include "ScopeCanvas.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#if !wxUSE_GLCANVAS +#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" +#endif + +#include "CubicSDR.h" +#include "CubicSDRDefs.h" +#include "AppFrame.h" +#include + +wxBEGIN_EVENT_TABLE(ScopeCanvas, wxGLCanvas) EVT_PAINT(ScopeCanvas::OnPaint) +EVT_KEY_DOWN(ScopeCanvas::OnKeyDown) +EVT_IDLE(ScopeCanvas::OnIdle) +wxEND_EVENT_TABLE() + +ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) : + wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, + wxFULL_REPAINT_ON_RESIZE), parent(parent) { + + int in_block_size = BUF_SIZE / 2; + int out_block_size = FFT_SIZE; + + in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * in_block_size); + out[0] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size); + out[1] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size); + plan[0] = fftw_plan_dft_1d(out_block_size, in, out[0], FFTW_FORWARD, FFTW_MEASURE); + plan[1] = fftw_plan_dft_1d(out_block_size, out[0], out[1], FFTW_BACKWARD, FFTW_MEASURE); + + fft_ceil_ma = fft_ceil_maa = 100.0; + fft_floor_ma = fft_floor_maa = 0.0; + + glContext = new ScopeContext(this, &wxGetApp().GetContext(this)); +} + +ScopeCanvas::~ScopeCanvas() { + +} + +void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { + wxPaintDC dc(this); + const wxSize ClientSize = GetClientSize(); + + glContext->SetCurrent(*this); + glViewport(0, 0, ClientSize.x, ClientSize.y); + + glContext->Plot(spectrum_points, test_demod.waveform_points); + + SwapBuffers(); +} + +void ScopeCanvas::OnKeyDown(wxKeyEvent& event) { + float angle = 5.0; + + unsigned int freq; + switch (event.GetKeyCode()) { + case WXK_RIGHT: + freq = ((AppFrame*) parent)->getFrequency(); + freq += 100000; + ((AppFrame*) parent)->setFrequency(freq); + break; + case WXK_LEFT: + freq = ((AppFrame*) parent)->getFrequency(); + freq -= 100000; + ((AppFrame*) parent)->setFrequency(freq); + break; + case WXK_DOWN: + break; + case WXK_UP: + break; + case WXK_SPACE: + break; + default: + event.Skip(); + return; + } +} + +void multiply2(float ar, float aj, float br, float bj, float *cr, float *cj) { + *cr = ar * br - aj * bj; + *cj = aj * br + ar * bj; +} +float polar_discriminant2(float ar, float aj, float br, float bj) { + float cr, cj; + double angle; + multiply2(ar, aj, br, -bj, &cr, &cj); + angle = atan2(cj, cr); + return (angle / M_PI); +} + +void ScopeCanvas::setData(std::vector *data) { + + if (data && data->size()) { + if (spectrum_points.size() < FFT_SIZE * 2) { + spectrum_points.resize(FFT_SIZE * 2); + } + + for (int i = 0; i < BUF_SIZE / 2; i++) { + in[i][0] = (float) (*data)[i * 2] / 127.0f; + in[i][1] = (float) (*data)[i * 2 + 1] / 127.0f; + } + + fftw_execute(plan[0]); + + 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); + } + + for (int j = 0; j < 2; j++) { + for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) { + double a = out[0][i][0]; + double b = out[0][i][1]; + double c = sqrt(a * a + b * b); + + double x = out[0][FFT_SIZE / 2 + i][0]; + double y = out[0][FFT_SIZE / 2 + i][1]; + double z = sqrt(x * x + y * y); + + fft_result[i] = (z); + 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++) { + 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 += 1; + fft_floor -= 1; + + fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.01; + fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 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; + + // fftw_execute(plan[1]); + + 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; + } + + test_demod.writeBuffer(data); + } +} + +void ScopeCanvas::OnIdle(wxIdleEvent &event) { + Refresh(false); +} diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h new file mode 100644 index 0000000..851edb1 --- /dev/null +++ b/src/visual/ScopeCanvas.h @@ -0,0 +1,43 @@ +#pragma once + +#include "wx/glcanvas.h" +#include "wx/timer.h" + +#include +#include + +#include "ScopeContext.h" + +#include "fftw3.h" +#include "Demodulator.h" + +class ScopeCanvas: public wxGLCanvas { +public: + ScopeCanvas(wxWindow *parent, int *attribList = NULL); + ~ScopeCanvas(); + + void setData(std::vector *data); + +private: + void OnPaint(wxPaintEvent& event); + void OnKeyDown(wxKeyEvent& event); + + void OnIdle(wxIdleEvent &event); + + wxWindow *parent; + std::vector spectrum_points; + + fftw_complex *in, *out[2]; + fftw_plan plan[2]; + + float fft_ceil_ma, fft_ceil_maa; + float fft_floor_ma, fft_floor_maa; + + std::vector fft_result; + std::vector fft_result_ma; + std::vector fft_result_maa; + + Demodulator test_demod; + ScopeContext *glContext;wxDECLARE_EVENT_TABLE(); +}; + diff --git a/src/visual/ScopeContext.cpp b/src/visual/ScopeContext.cpp new file mode 100644 index 0000000..899ab36 --- /dev/null +++ b/src/visual/ScopeContext.cpp @@ -0,0 +1,107 @@ +#include "ScopeContext.h" + +#include "ScopeCanvas.h" + +ScopeContext::ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext) : + PrimaryGLContext(canvas, sharedContext) { + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glGenTextures(1, &waterfall); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, waterfall); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + 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 ScopeContext::Plot(std::vector &points, std::vector &points2) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + if (points.size()) { + memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); + + for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { + float v = points[i * 2 + 1]; + + float wv = v; + if (wv < 0.0) + wv = 0.0; + if (wv > 1.0) + wv = 1.0; + waterfall_tex[i] = (unsigned char) floor(wv * 255.0); + } + } + + 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); + + glDisable(GL_TEXTURE_2D); + + glColor3f(1.0, 1.0, 1.0); + + if (points.size()) { + glPushMatrix(); + glTranslatef(-1.0f, -0.9f, 0.0f); + glScalef(2.0f, 1.0f, 1.0f); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, &points[0]); + glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); + glDisableClientState(GL_VERTEX_ARRAY); + glPopMatrix(); + } + + if (points2.size()) { + glPushMatrix(); + glTranslatef(-1.0f, 0.5f, 0.0f); + glScalef(2.0f, 1.0f, 1.0f); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, &points2[0]); + glDrawArrays(GL_LINE_STRIP, 0, points2.size() / 2); + glDisableClientState(GL_VERTEX_ARRAY); + glPopMatrix(); + } + + glEnable(GL_TEXTURE_2D); + // glEnable(GL_COLOR_TABLE); + glBindTexture(GL_TEXTURE_2D, waterfall); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); + glVertex3f(-1.0, -1.0, 0.0); + glTexCoord2f(1.0, 0.0); + glVertex3f(1.0, -1.0, 0.0); + glTexCoord2f(1.0, 1.0); + glVertex3f(1.0, 1.0, 0.0); + glTexCoord2f(0.0, 1.0); + glVertex3f(-1.0, 1.0, 0.0); + glEnd(); + + glFlush(); + + CheckGLError(); +} diff --git a/src/visual/ScopeContext.h b/src/visual/ScopeContext.h new file mode 100644 index 0000000..bfd00e3 --- /dev/null +++ b/src/visual/ScopeContext.h @@ -0,0 +1,20 @@ +#pragma once + +#include "PrimaryGLContext.h" +#include "Gradient.h" + +#define NUM_WATERFALL_LINES 512 + +class ScopeCanvas; + +class ScopeContext: public PrimaryGLContext { +public: + ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext); + + void Plot(std::vector &points, std::vector &points2); + +private: + Gradient grad; + GLuint waterfall; + unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; +}; From 2e5bdc264618aa7c8f9eddafa50e05a02d3797ae Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sat, 15 Nov 2014 23:41:41 -0500 Subject: [PATCH 7/7] Separate visual components Scope/Spectrum/Waterfall Each now on it's own canvas with shared GL Context --- CMakeLists.txt | 12 ++- src/AppFrame.cpp | 40 +++++--- src/AppFrame.h | 10 +- src/CubicSDRDefs.h | 4 +- src/visual/ScopeCanvas.cpp | 102 +-------------------- src/visual/ScopeCanvas.h | 21 +---- src/visual/ScopeContext.cpp | 74 +-------------- src/visual/ScopeContext.h | 5 +- src/visual/SpectrumCanvas.cpp | 156 ++++++++++++++++++++++++++++++++ src/visual/SpectrumCanvas.h | 43 +++++++++ src/visual/SpectrumContext.cpp | 36 ++++++++ src/visual/SpectrumContext.h | 17 ++++ src/visual/WaterfallCanvas.cpp | 154 +++++++++++++++++++++++++++++++ src/visual/WaterfallCanvas.h | 42 +++++++++ src/visual/WaterfallContext.cpp | 84 +++++++++++++++++ src/visual/WaterfallContext.h | 20 ++++ 16 files changed, 613 insertions(+), 207 deletions(-) create mode 100644 src/visual/SpectrumCanvas.cpp create mode 100644 src/visual/SpectrumCanvas.h create mode 100644 src/visual/SpectrumContext.cpp create mode 100644 src/visual/SpectrumContext.h create mode 100644 src/visual/WaterfallCanvas.cpp create mode 100644 src/visual/WaterfallCanvas.h create mode 100644 src/visual/WaterfallContext.cpp create mode 100644 src/visual/WaterfallContext.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e7a44f6..d2b37bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,7 @@ endif (DEFINED WIN32) SET (cubicsdr_sources - src/CubicSDR.cpp + src/CubicSDR.cpp src/SDRThread.cpp src/IQBufferThread.cpp src/PrimaryGLContext.cpp @@ -94,10 +94,14 @@ SET (cubicsdr_sources src/Gradient.cpp src/visual/ScopeCanvas.cpp src/visual/ScopeContext.cpp + src/visual/SpectrumCanvas.cpp + src/visual/SpectrumContext.cpp + src/visual/WaterfallCanvas.cpp + src/visual/WaterfallContext.cpp ) SET (cubicsdr_headers - src/CubicSDR.h + src/CubicSDR.h src/SDRThread.h src/IQBufferThread.h src/PrimaryGLContext.h @@ -109,6 +113,10 @@ SET (cubicsdr_headers src/Gradient.h src/visual/ScopeCanvas.h src/visual/ScopeContext.h + src/visual/SpectrumCanvas.h + src/visual/SpectrumContext.h + src/visual/WaterfallCanvas.h + src/visual/WaterfallContext.h ) include_directories ( ${PROJECT_SOURCE_DIR}/src/visual diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index dfeb823..f9665e0 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -23,7 +23,18 @@ wxEND_EVENT_TABLE() AppFrame::AppFrame() : wxFrame(NULL, wxID_ANY, wxT("CubicSDR")), frequency(DEFAULT_FREQ) { - canvas = new ScopeCanvas(this, NULL); + wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + + scopeCanvas = new ScopeCanvas(this, NULL); + vbox->Add(scopeCanvas, 1, wxEXPAND | wxALL, 0); + vbox->AddSpacer(2); + spectrumCanvas = new SpectrumCanvas(this, NULL); + vbox->Add(spectrumCanvas, 1, wxEXPAND | wxALL, 0); + vbox->AddSpacer(2); + waterfallCanvas = new WaterfallCanvas(this, NULL); + vbox->Add(waterfallCanvas, 4, wxEXPAND | wxALL, 0); + + this->SetSizer(vbox); // SetIcon(wxICON(sample)); @@ -77,16 +88,16 @@ AppFrame::~AppFrame() { } } - { - wxCriticalSectionLocker enter(m_pThreadCS); - if (t_IQBuffer) { - wxMessageOutputDebug().Printf("CubicSDR: deleting thread"); - if (t_IQBuffer->Delete() != wxTHREAD_NO_ERROR) { - wxLogError - ("Can't delete the thread!"); - } - } - } +// { +// wxCriticalSectionLocker enter(m_pThreadCS); +// if (t_IQBuffer) { +// wxMessageOutputDebug().Printf("CubicSDR: deleting thread"); +// if (t_IQBuffer->Delete() != wxTHREAD_NO_ERROR) { +// wxLogError +// ("Can't delete the thread!"); +// } +// } +// } delete t_SDR; // delete t_IQBuffer; @@ -106,9 +117,10 @@ void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) { void AppFrame::OnEventInput(wxThreadEvent& event) { std::vector *new_buffer = event.GetPayload *>(); -// std::cout << "Got IQ buffer, length: " << new_buffer->size() << std::endl; - - canvas->setData(new_buffer); + test_demod.writeBuffer(new_buffer); + scopeCanvas->setWaveformPoints(test_demod.waveform_points); + spectrumCanvas->setData(new_buffer); + waterfallCanvas->setData(new_buffer); delete new_buffer; } diff --git a/src/AppFrame.h b/src/AppFrame.h index fdac905..5fb80bc 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -4,6 +4,9 @@ #include "PrimaryGLContext.h" #include "SDRThread.h" #include "ScopeCanvas.h" +#include "SpectrumCanvas.h" +#include "WaterfallCanvas.h" +#include "Demodulator.h" // Define a new frame type class AppFrame: public wxFrame { @@ -20,12 +23,17 @@ private: void OnNewWindow(wxCommandEvent& event); void OnIdle(wxIdleEvent& event); - ScopeCanvas *canvas; + ScopeCanvas *scopeCanvas; + SpectrumCanvas *spectrumCanvas; + WaterfallCanvas *waterfallCanvas; SDRThread *t_SDR; IQBufferThread *t_IQBuffer; wxCriticalSection m_pThreadCS; SDRThreadQueue* m_pQueue; unsigned int frequency; + Demodulator test_demod; + +// event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 244b2d2..d772066 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -1,8 +1,8 @@ #pragma once #define BUF_SIZE (16 * 32 * 256) -#define SRATE 2500000 +#define SRATE 2000000 #define FFT_SIZE 2048 -#define DEFAULT_FREQ 105700000 +#define DEFAULT_FREQ 98900000 diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp index e9d749f..f6ab56e 100644 --- a/src/visual/ScopeCanvas.cpp +++ b/src/visual/ScopeCanvas.cpp @@ -24,18 +24,6 @@ ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE), parent(parent) { - int in_block_size = BUF_SIZE / 2; - int out_block_size = FFT_SIZE; - - in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * in_block_size); - out[0] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size); - out[1] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * out_block_size); - plan[0] = fftw_plan_dft_1d(out_block_size, in, out[0], FFTW_FORWARD, FFTW_MEASURE); - plan[1] = fftw_plan_dft_1d(out_block_size, out[0], out[1], FFTW_BACKWARD, FFTW_MEASURE); - - fft_ceil_ma = fft_ceil_maa = 100.0; - fft_floor_ma = fft_floor_maa = 0.0; - glContext = new ScopeContext(this, &wxGetApp().GetContext(this)); } @@ -43,6 +31,10 @@ ScopeCanvas::~ScopeCanvas() { } +void ScopeCanvas::setWaveformPoints(std::vector &waveform_points_in) { + waveform_points = waveform_points_in; +} + void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); @@ -50,7 +42,7 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glContext->SetCurrent(*this); glViewport(0, 0, ClientSize.x, ClientSize.y); - glContext->Plot(spectrum_points, test_demod.waveform_points); + glContext->Plot(waveform_points); SwapBuffers(); } @@ -82,90 +74,6 @@ void ScopeCanvas::OnKeyDown(wxKeyEvent& event) { } } -void multiply2(float ar, float aj, float br, float bj, float *cr, float *cj) { - *cr = ar * br - aj * bj; - *cj = aj * br + ar * bj; -} -float polar_discriminant2(float ar, float aj, float br, float bj) { - float cr, cj; - double angle; - multiply2(ar, aj, br, -bj, &cr, &cj); - angle = atan2(cj, cr); - return (angle / M_PI); -} - -void ScopeCanvas::setData(std::vector *data) { - - if (data && data->size()) { - if (spectrum_points.size() < FFT_SIZE * 2) { - spectrum_points.resize(FFT_SIZE * 2); - } - - for (int i = 0; i < BUF_SIZE / 2; i++) { - in[i][0] = (float) (*data)[i * 2] / 127.0f; - in[i][1] = (float) (*data)[i * 2 + 1] / 127.0f; - } - - fftw_execute(plan[0]); - - 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); - } - - for (int j = 0; j < 2; j++) { - for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) { - double a = out[0][i][0]; - double b = out[0][i][1]; - double c = sqrt(a * a + b * b); - - double x = out[0][FFT_SIZE / 2 + i][0]; - double y = out[0][FFT_SIZE / 2 + i][1]; - double z = sqrt(x * x + y * y); - - fft_result[i] = (z); - 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++) { - 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 += 1; - fft_floor -= 1; - - fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.01; - fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 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; - - // fftw_execute(plan[1]); - - 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; - } - - test_demod.writeBuffer(data); - } -} - void ScopeCanvas::OnIdle(wxIdleEvent &event) { Refresh(false); } diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h index 851edb1..03faf9e 100644 --- a/src/visual/ScopeCanvas.h +++ b/src/visual/ScopeCanvas.h @@ -9,15 +9,13 @@ #include "ScopeContext.h" #include "fftw3.h" -#include "Demodulator.h" class ScopeCanvas: public wxGLCanvas { public: ScopeCanvas(wxWindow *parent, int *attribList = NULL); ~ScopeCanvas(); - void setData(std::vector *data); - + void setWaveformPoints(std::vector &waveform_points_in); private: void OnPaint(wxPaintEvent& event); void OnKeyDown(wxKeyEvent& event); @@ -25,19 +23,10 @@ private: void OnIdle(wxIdleEvent &event); wxWindow *parent; - std::vector spectrum_points; + std::vector waveform_points; - fftw_complex *in, *out[2]; - fftw_plan plan[2]; - - float fft_ceil_ma, fft_ceil_maa; - float fft_floor_ma, fft_floor_maa; - - std::vector fft_result; - std::vector fft_result_ma; - std::vector fft_result_maa; - - Demodulator test_demod; - ScopeContext *glContext;wxDECLARE_EVENT_TABLE(); + ScopeContext *glContext; +// event table +wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/ScopeContext.cpp b/src/visual/ScopeContext.cpp index 899ab36..26368e2 100644 --- a/src/visual/ScopeContext.cpp +++ b/src/visual/ScopeContext.cpp @@ -9,66 +9,22 @@ ScopeContext::ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext) : glMatrixMode(GL_PROJECTION); glLoadIdentity(); - - glGenTextures(1, &waterfall); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, waterfall); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - 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 ScopeContext::Plot(std::vector &points, std::vector &points2) { +void ScopeContext::Plot(std::vector &points) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - if (points.size()) { - memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); - - for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { - float v = points[i * 2 + 1]; - - float wv = v; - if (wv < 0.0) - wv = 0.0; - if (wv > 1.0) - wv = 1.0; - waterfall_tex[i] = (unsigned char) floor(wv * 255.0); - } - } - - 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); - glDisable(GL_TEXTURE_2D); glColor3f(1.0, 1.0, 1.0); if (points.size()) { glPushMatrix(); - glTranslatef(-1.0f, -0.9f, 0.0f); - glScalef(2.0f, 1.0f, 1.0f); + glTranslatef(-1.0f, 0.0f, 0.0f); + glScalef(2.0f, 2.0f, 1.0f); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &points[0]); glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); @@ -76,30 +32,6 @@ void ScopeContext::Plot(std::vector &points, std::vector &points2) glPopMatrix(); } - if (points2.size()) { - glPushMatrix(); - glTranslatef(-1.0f, 0.5f, 0.0f); - glScalef(2.0f, 1.0f, 1.0f); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, &points2[0]); - glDrawArrays(GL_LINE_STRIP, 0, points2.size() / 2); - glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); - } - - glEnable(GL_TEXTURE_2D); - // glEnable(GL_COLOR_TABLE); - glBindTexture(GL_TEXTURE_2D, waterfall); - glBegin(GL_QUADS); - glTexCoord2f(0.0, 0.0); - glVertex3f(-1.0, -1.0, 0.0); - glTexCoord2f(1.0, 0.0); - glVertex3f(1.0, -1.0, 0.0); - glTexCoord2f(1.0, 1.0); - glVertex3f(1.0, 1.0, 0.0); - glTexCoord2f(0.0, 1.0); - glVertex3f(-1.0, 1.0, 0.0); - glEnd(); glFlush(); diff --git a/src/visual/ScopeContext.h b/src/visual/ScopeContext.h index bfd00e3..41cad0d 100644 --- a/src/visual/ScopeContext.h +++ b/src/visual/ScopeContext.h @@ -11,10 +11,7 @@ class ScopeContext: public PrimaryGLContext { public: ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext); - void Plot(std::vector &points, std::vector &points2); + void Plot(std::vector &points); private: - Gradient grad; - GLuint waterfall; - unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; }; diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp new file mode 100644 index 0000000..17ca1fb --- /dev/null +++ b/src/visual/SpectrumCanvas.cpp @@ -0,0 +1,156 @@ +#include "SpectrumCanvas.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#if !wxUSE_GLCANVAS +#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" +#endif + +#include "CubicSDR.h" +#include "CubicSDRDefs.h" +#include "AppFrame.h" +#include + +wxBEGIN_EVENT_TABLE(SpectrumCanvas, wxGLCanvas) EVT_PAINT(SpectrumCanvas::OnPaint) +EVT_KEY_DOWN(SpectrumCanvas::OnKeyDown) +EVT_IDLE(SpectrumCanvas::OnIdle) +wxEND_EVENT_TABLE() + +SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : + wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, + wxFULL_REPAINT_ON_RESIZE), parent(parent) { + + int in_block_size = BUF_SIZE / 2; + 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)); +} + +SpectrumCanvas::~SpectrumCanvas() { + +} + +void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { + wxPaintDC dc(this); + const wxSize ClientSize = GetClientSize(); + + glContext->SetCurrent(*this); + glViewport(0, 0, ClientSize.x, ClientSize.y); + + glContext->Draw(spectrum_points); + + SwapBuffers(); +} + +void SpectrumCanvas::OnKeyDown(wxKeyEvent& event) { + float angle = 5.0; + + unsigned int freq; + switch (event.GetKeyCode()) { + case WXK_RIGHT: + freq = ((AppFrame*) parent)->getFrequency(); + freq += 100000; + ((AppFrame*) parent)->setFrequency(freq); + break; + case WXK_LEFT: + freq = ((AppFrame*) parent)->getFrequency(); + freq -= 100000; + ((AppFrame*) parent)->setFrequency(freq); + break; + case WXK_DOWN: + break; + case WXK_UP: + break; + case WXK_SPACE: + break; + default: + event.Skip(); + return; + } +} + +void SpectrumCanvas::setData(std::vector *data) { + + if (data && data->size()) { + if (spectrum_points.size() < FFT_SIZE * 2) { + spectrum_points.resize(FFT_SIZE * 2); + } + + for (int i = 0; i < BUF_SIZE / 2; i++) { + in[i][0] = (float) (*data)[i * 2] / 127.0f; + in[i][1] = (float) (*data)[i * 2 + 1] / 127.0f; + } + + 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); + } + + for (int j = 0; j < 2; j++) { + for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) { + double a = out[i][0]; + double b = out[i][1]; + double c = sqrt(a * a + b * b); + + double x = out[FFT_SIZE / 2 + i][0]; + double y = out[FFT_SIZE / 2 + i][1]; + double z = sqrt(x * x + y * y); + + fft_result[i] = (z); + 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++) { + 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 += 1; + fft_floor -= 1; + + fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.01; + fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 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; + + // fftw_execute(plan[1]); + + 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; + } + + } +} + +void SpectrumCanvas::OnIdle(wxIdleEvent &event) { + Refresh(false); +} diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h new file mode 100644 index 0000000..649e8ed --- /dev/null +++ b/src/visual/SpectrumCanvas.h @@ -0,0 +1,43 @@ +#pragma once + +#include "wx/glcanvas.h" +#include "wx/timer.h" + +#include +#include + +#include "SpectrumContext.h" + +#include "fftw3.h" +#include "Demodulator.h" + +class SpectrumCanvas: public wxGLCanvas { +public: + SpectrumCanvas(wxWindow *parent, int *attribList = NULL); + ~SpectrumCanvas(); + + void setData(std::vector *data); +private: + void OnPaint(wxPaintEvent& event); + void OnKeyDown(wxKeyEvent& event); + + void OnIdle(wxIdleEvent &event); + + wxWindow *parent; + std::vector spectrum_points; + + fftw_complex *in, *out; + fftw_plan plan; + + float fft_ceil_ma, fft_ceil_maa; + float fft_floor_ma, fft_floor_maa; + + std::vector fft_result; + std::vector fft_result_ma; + std::vector fft_result_maa; + + SpectrumContext *glContext; +// event table +wxDECLARE_EVENT_TABLE(); +}; + diff --git a/src/visual/SpectrumContext.cpp b/src/visual/SpectrumContext.cpp new file mode 100644 index 0000000..d90d06c --- /dev/null +++ b/src/visual/SpectrumContext.cpp @@ -0,0 +1,36 @@ +#include "SpectrumContext.h" + +#include "SpectrumCanvas.h" + +SpectrumContext::SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedContext) : + PrimaryGLContext(canvas, sharedContext) { + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + +} + +void SpectrumContext::Draw(std::vector &points) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDisable(GL_TEXTURE_2D); + + glColor3f(1.0, 1.0, 1.0); + + if (points.size()) { + glPushMatrix(); + glTranslatef(-1.0f, -0.9f, 0.0f); + glScalef(2.0f, 1.8f, 1.0f); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, &points[0]); + glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); + glDisableClientState(GL_VERTEX_ARRAY); + glPopMatrix(); + } + CheckGLError(); +} diff --git a/src/visual/SpectrumContext.h b/src/visual/SpectrumContext.h new file mode 100644 index 0000000..be5a62f --- /dev/null +++ b/src/visual/SpectrumContext.h @@ -0,0 +1,17 @@ +#pragma once + +#include "PrimaryGLContext.h" +#include "Gradient.h" + +#define NUM_WATERFALL_LINES 512 + +class SpectrumCanvas; + +class SpectrumContext: public PrimaryGLContext { +public: + SpectrumContext(SpectrumCanvas *canvas, wxGLContext *sharedContext); + + void Draw(std::vector &points); + +private: +}; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp new file mode 100644 index 0000000..6cb3bd4 --- /dev/null +++ b/src/visual/WaterfallCanvas.cpp @@ -0,0 +1,154 @@ +#include "WaterfallCanvas.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#if !wxUSE_GLCANVAS +#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" +#endif + +#include "CubicSDR.h" +#include "CubicSDRDefs.h" +#include "AppFrame.h" +#include + +wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) EVT_PAINT(WaterfallCanvas::OnPaint) +EVT_KEY_DOWN(WaterfallCanvas::OnKeyDown) +EVT_IDLE(WaterfallCanvas::OnIdle) +wxEND_EVENT_TABLE() + +WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : + wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, + wxFULL_REPAINT_ON_RESIZE), parent(parent) { + + int in_block_size = BUF_SIZE / 2; + 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)); +} + +WaterfallCanvas::~WaterfallCanvas() { + +} + +void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { + wxPaintDC dc(this); + const wxSize ClientSize = GetClientSize(); + + glContext->SetCurrent(*this); + glViewport(0, 0, ClientSize.x, ClientSize.y); + + glContext->Draw(spectrum_points); + + SwapBuffers(); +} + +void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { + float angle = 5.0; + + unsigned int freq; + switch (event.GetKeyCode()) { + case WXK_RIGHT: + freq = ((AppFrame*) parent)->getFrequency(); + freq += 100000; + ((AppFrame*) parent)->setFrequency(freq); + break; + case WXK_LEFT: + freq = ((AppFrame*) parent)->getFrequency(); + freq -= 100000; + ((AppFrame*) parent)->setFrequency(freq); + break; + case WXK_DOWN: + break; + case WXK_UP: + break; + case WXK_SPACE: + break; + default: + event.Skip(); + return; + } +} + +void WaterfallCanvas::setData(std::vector *data) { + + if (data && data->size()) { + if (spectrum_points.size() < FFT_SIZE * 2) { + spectrum_points.resize(FFT_SIZE * 2); + } + + for (int i = 0; i < BUF_SIZE / 2; i++) { + in[i][0] = (float) (*data)[i * 2] / 127.0f; + in[i][1] = (float) (*data)[i * 2 + 1] / 127.0f; + } + + 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); + } + + for (int j = 0; j < 2; j++) { + for (int i = 0, iMax = FFT_SIZE / 2; i < iMax; i++) { + double a = out[i][0]; + double b = out[i][1]; + double c = sqrt(a * a + b * b); + + double x = out[FFT_SIZE / 2 + i][0]; + double y = out[FFT_SIZE / 2 + i][1]; + double z = sqrt(x * x + y * y); + + fft_result[i] = (z); + 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++) { + 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 += 1; + fft_floor -= 1; + + fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.01; + fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 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; + + 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; + } + + } +} + +void WaterfallCanvas::OnIdle(wxIdleEvent &event) { + Refresh(false); +} diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h new file mode 100644 index 0000000..3d36593 --- /dev/null +++ b/src/visual/WaterfallCanvas.h @@ -0,0 +1,42 @@ +#pragma once + +#include "wx/glcanvas.h" +#include "wx/timer.h" + +#include +#include + +#include "WaterfallContext.h" + +#include "fftw3.h" + +class WaterfallCanvas: public wxGLCanvas { +public: + WaterfallCanvas(wxWindow *parent, int *attribList = NULL); + ~WaterfallCanvas(); + + void setData(std::vector *data); +private: + void OnPaint(wxPaintEvent& event); + void OnKeyDown(wxKeyEvent& event); + + void OnIdle(wxIdleEvent &event); + + wxWindow *parent; + std::vector spectrum_points; + + fftw_complex *in, *out; + fftw_plan plan; + + float fft_ceil_ma, fft_ceil_maa; + float fft_floor_ma, fft_floor_maa; + + std::vector fft_result; + std::vector fft_result_ma; + std::vector fft_result_maa; + + WaterfallContext *glContext; +// event table +wxDECLARE_EVENT_TABLE(); +}; + diff --git a/src/visual/WaterfallContext.cpp b/src/visual/WaterfallContext.cpp new file mode 100644 index 0000000..dddb87c --- /dev/null +++ b/src/visual/WaterfallContext.cpp @@ -0,0 +1,84 @@ +#include "WaterfallContext.h" +#include "WaterfallCanvas.h" + +WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext) : + PrimaryGLContext(canvas, sharedContext) { + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glGenTextures(1, &waterfall); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, waterfall); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + 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 &points) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + if (points.size()) { + memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); + + for (int i = 0, iMax = FFT_SIZE; i < iMax; i++) { + float v = points[i * 2 + 1]; + + float wv = v; + if (wv < 0.0) + wv = 0.0; + if (wv > 1.0) + wv = 1.0; + waterfall_tex[i] = (unsigned char) floor(wv * 255.0); + } + } + + 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); + + glDisable(GL_TEXTURE_2D); + + glColor3f(1.0, 1.0, 1.0); + + glEnable(GL_TEXTURE_2D); + // glEnable(GL_COLOR_TABLE); + glBindTexture(GL_TEXTURE_2D, waterfall); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 1.0); + glVertex3f(-1.0, -1.0, 0.0); + glTexCoord2f(1.0, 1.0); + glVertex3f(1.0, -1.0, 0.0); + glTexCoord2f(1.0, 0.0); + glVertex3f(1.0, 1.0, 0.0); + glTexCoord2f(0.0, 0.0); + glVertex3f(-1.0, 1.0, 0.0); + glEnd(); + + glFlush(); + + CheckGLError(); +} diff --git a/src/visual/WaterfallContext.h b/src/visual/WaterfallContext.h new file mode 100644 index 0000000..a488f6d --- /dev/null +++ b/src/visual/WaterfallContext.h @@ -0,0 +1,20 @@ +#pragma once + +#include "PrimaryGLContext.h" +#include "Gradient.h" + +#define NUM_WATERFALL_LINES 512 + +class WaterfallCanvas; + +class WaterfallContext: public PrimaryGLContext { +public: + WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext); + + void Draw(std::vector &points); + +private: + Gradient grad; + GLuint waterfall; + unsigned char waterfall_tex[FFT_SIZE * NUM_WATERFALL_LINES]; +};