From 541ab5614c79e456f0e4d8cb0296ecb9dd8d21f4 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 12 Nov 2014 21:55:11 -0500 Subject: [PATCH] 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]; +};