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 | 64 ++++++++++++++++++++++++++++++++++---- src/PrimaryGLContext.h | 14 ++++++++- 6 files changed, 144 insertions(+), 10 deletions(-) create mode 100644 src/Gradient.cpp create mode 100644 src/Gradient.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 72ad9410..db83905a 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 ed1593c3..62c2d3a1 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 00000000..fa33abad --- /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 00000000..ce3b9740 --- /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 636a852f..58d7a034 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 9d258445..dd6b9141 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 @@ class TestGLCanvas: public wxGLCanvas { 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 | 124 ++++++++++++++++++++------------------- src/PrimaryGLContext.h | 14 ++--- 5 files changed, 115 insertions(+), 86 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index eb1046f1..3730bce5 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 62c2d3a1..b1d55e3d 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 ce3b9740..6a529cc3 100644 --- a/src/Gradient.h +++ b/src/Gradient.h @@ -21,21 +21,34 @@ class 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); + 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); 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 @@ class Gradient { 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 @@ class Gradient { } 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 58d7a034..8ffa11b1 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); - + memmove(waterfall_tex + FFT_SIZE, 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); - } + 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 dd6b9141..8e0aac09 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 @@ class TestGLCanvas: public wxGLCanvas { 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 792a2f01..3927d2ca 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 6d085c4b..cbf483e1 100644 --- a/src/Demodulator.h +++ b/src/Demodulator.h @@ -31,14 +31,22 @@ class Demodulator { 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 8ffa11b1..a2f55fed 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 8e0aac09..813c4593 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 @@ class TestGLCanvas: public wxGLCanvas { 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 3927d2ca..e73f0df3 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 a2f55fed..243b9d4f 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 813c4593..700a220e 100644 --- a/src/PrimaryGLContext.h +++ b/src/PrimaryGLContext.h @@ -48,6 +48,7 @@ class TestGLCanvas: public wxGLCanvas { 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 e73f0df3..c2af8f3c 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 db83905a..e7a44f6d 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 3730bce5..dfeb8232 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 50cfe8b8..fdac9057 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 @@ class AppFrame: public wxFrame { 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 1f93b674..54dba5fd 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 b1d55e3d..244b2d26 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 fa33abad..0dba7caf 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 6a529cc3..10080cda 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); - } - - 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); - - int p = 0; - r_val.resize(len); - g_val.resize(len); - b_val.resize(len); + Gradient(); - 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); - } + void addColor(GradientColor c); - 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; + std::vector &getRed();; + std::vector &getGreen(); + std::vector &getBlue(); - 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; + void generate(unsigned int len); - 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 243b9d4f..6cde9a71 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 700a220e..5a926051 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); - - 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 { -public: - TestGLCanvas(wxWindow *parent, int *attribList = NULL); - ~TestGLCanvas(); + PrimaryGLContext(wxGLCanvas *canvas, wxGLContext *sharedContext); - void setData(std::vector *data); + static wxString glGetwxString(GLenum name); + static void CheckGLError(); 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 00000000..e9d749f5 --- /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 00000000..851edb1d --- /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 00000000..899ab363 --- /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 00000000..bfd00e3c --- /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 e7a44f6d..d2b37bdd 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 dfeb8232..f9665e03 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 fdac9057..5fb80bce 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 @@ class AppFrame: public wxFrame { 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 244b2d26..d772066a 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 e9d749f5..f6ab56ea 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 851edb1d..03faf9e0 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 @@ class ScopeCanvas: public wxGLCanvas { 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; + std::vector waveform_points; - 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 899ab363..26368e24 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 bfd00e3c..41cad0dc 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 00000000..17ca1fbb --- /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 00000000..649e8ed3 --- /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 00000000..d90d06ce --- /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 00000000..be5a62fe --- /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 00000000..6cb3bd46 --- /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 00000000..3d365938 --- /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 00000000..dddb87c3 --- /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 00000000..a488f6dc --- /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]; +};