From ce52e787730c8fe65da541bafa804f24a1551a0e Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 22 Jan 2017 16:59:11 +0000 Subject: [PATCH] Add a basic waterfall style display to the UI --- linux-app/ui-test/main.cpp | 68 ++++++++++++++++++++++++++++++++++-- linux-app/ui-test/sa_demo.ui | 15 +++++++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/linux-app/ui-test/main.cpp b/linux-app/ui-test/main.cpp index 7dc3554..dfda9ff 100644 --- a/linux-app/ui-test/main.cpp +++ b/linux-app/ui-test/main.cpp @@ -24,6 +24,7 @@ extern "C" { #include +#include #include using namespace std; @@ -41,8 +42,9 @@ Glib::RefPtr dispMaxAmp, dispMinAmp, rxDispZoom, rxOffset, rxCenterFreq, rxInputGain; Gtk::ComboBox *fftLengthSel, *rxBwSel, *rxInputSel; Gtk::CheckButton *rxAgcEnable; -Gtk::DrawingArea *fft_area = 0; +Gtk::DrawingArea *fft_area, *waterfallArea; Gtk::SpinButton *rxCenterFreqSpinner; +Gtk::Button *autoscaleAmp; Gtk::Scale *rxGainSet; Gtk::Label *rxResolution, *cursorFreq; ulong last_fftLength = -1; @@ -52,6 +54,12 @@ const size_t max_fftLength = 16777216; _Complex double *x_buf, *y_buf; atomic draw_done{false}; atomic fftLength{524288}; + +// Waterfall display points +const int waterfall_wmax = 3840, waterfall_hmax = 2160; +float waterfall_points[waterfall_hmax][waterfall_wmax] = {{0.0}}; +int waterfall_yptr = 0; + void update_fft() { while (true) { @@ -131,6 +139,13 @@ void rxBandwidthChanged() { void rxAgcChanged() { rft->setAgcEnable(rxAgcEnable->get_active()); } +void autoscale_vert() { + double minamp = *min_element(fft_points.begin(), fft_points.end()); + double maxamp = *max_element(fft_points.begin(), fft_points.end()); + dispMinAmp->set_value(floor(minamp - 1)); + dispMaxAmp->set_value(ceil(maxamp + 20)); +} + bool on_fft_draw(const Cairo::RefPtr &cr) { { lock_guard fft_lock_acquire(fft_lock); @@ -151,13 +166,59 @@ bool on_fft_draw(const Cairo::RefPtr &cr) { fftLengthSel->get_active()->get_value(1, fftLengthT); fftLength = fftLengthT; fftr->FitFFTToView(2048, 0, y_buf, fftLength, fft_points); + for (int i = 0; i < fft_points.size(); i++) { + if (i < waterfall_wmax) { + waterfall_points[waterfall_yptr][i] = + (fft_points[i] - fftr->minAmplitude) / + (fftr->maxAmplitude - fftr->minAmplitude); + } + } + waterfall_yptr++; + if (waterfall_yptr >= waterfall_hmax) + waterfall_yptr = 0; fftr->RenderToContext(fft_points, cr); } + waterfallArea->queue_draw(); draw_done = true; return true; } +bool on_wf_draw(const Cairo::RefPtr &cr) { + Gtk::Allocation allocation = waterfallArea->get_allocation(); + int width = allocation.get_width(); + int height = allocation.get_height(); + + uint32_t *argb_bitmap = new uint32_t[height * width]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (x >= waterfall_wmax) + break; + int waterfall_y = + ((waterfall_yptr - y) + waterfall_hmax) % waterfall_hmax; + float fval = waterfall_points[waterfall_y][x]; + uint8_t pixval; + if (fval >= 1) + pixval = 255; + else if (fval <= 0) + pixval = 0; + else + pixval = uint8_t(fval * 255); + argb_bitmap[y * width + x] = 0xff000000 | (uint16_t(pixval) << 8); + } + if (y >= waterfall_hmax) + break; + } + Cairo::RefPtr tmpSurface = Cairo::ImageSurface::create( + reinterpret_cast(argb_bitmap), + Cairo::Format::FORMAT_ARGB32, width, height, width * 4); + cr->set_source(tmpSurface, 0, 0); + cr->paint(); + tmpSurface->finish(); + delete[] argb_bitmap; + return true; +} + int main(int argc, char *argv[]) { x_buf = fftw_alloc_complex(max_fftLength); y_buf = fftw_alloc_complex(max_fftLength); @@ -176,6 +237,7 @@ int main(int argc, char *argv[]) { Gtk::Window *main_win = 0; builder->get_widget("main_window", main_win); builder->get_widget("fftArea", fft_area); + builder->get_widget("waterfallArea", waterfallArea); dispMaxAmp = Glib::RefPtr::cast_dynamic( builder->get_object("dispMaxAmp")); @@ -198,11 +260,13 @@ int main(int argc, char *argv[]) { builder->get_widget("rxGainSet", rxGainSet); builder->get_widget("rxResolution", rxResolution); builder->get_widget("cursorFreq", cursorFreq); + builder->get_widget("autoscaleAmp", autoscaleAmp); if (main_win) { rft = new RFThread(); fft_area->signal_draw().connect(sigc::ptr_fun(on_fft_draw)); + waterfallArea->signal_draw().connect(sigc::ptr_fun(on_wf_draw)); main_win->set_events(Gdk::BUTTON_PRESS_MASK | Gdk::POINTER_MOTION_MASK); fft_area->set_events(Gdk::BUTTON_PRESS_MASK | Gdk::POINTER_MOTION_MASK); fft_area->signal_button_press_event().connect( @@ -216,7 +280,7 @@ int main(int argc, char *argv[]) { rxInputSel->signal_changed().connect(sigc::ptr_fun(inputSelChanged)); rxBwSel->signal_changed().connect(sigc::ptr_fun(rxBandwidthChanged)); rxAgcEnable->signal_toggled().connect(sigc::ptr_fun(rxAgcChanged)); - + autoscaleAmp->signal_clicked().connect(sigc::ptr_fun(autoscale_vert)); rft->start(); Glib::signal_timeout().connect(sigc::ptr_fun(redraw_fft), 17); thread uth(update_fft); diff --git a/linux-app/ui-test/sa_demo.ui b/linux-app/ui-test/sa_demo.ui index 4e68a0a..1e5756d 100644 --- a/linux-app/ui-test/sa_demo.ui +++ b/linux-app/ui-test/sa_demo.ui @@ -210,6 +210,19 @@ 0 + + + True + False + True + True + + + True + True + 1 + + True @@ -662,7 +675,7 @@ False True - 1 + 2