From 7a4604e24e08a6ba976dfff3230ff8e8f91c55e4 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 16 Dec 2015 19:33:30 -0800 Subject: [PATCH] Move code out of ui_spectrum.hpp. --- firmware/application/ui_spectrum.cpp | 254 ++++++++++++++++++++++++++- firmware/application/ui_spectrum.hpp | 248 +++----------------------- 2 files changed, 273 insertions(+), 229 deletions(-) diff --git a/firmware/application/ui_spectrum.cpp b/firmware/application/ui_spectrum.cpp index b0a81b07..d450b4b6 100644 --- a/firmware/application/ui_spectrum.cpp +++ b/firmware/application/ui_spectrum.cpp @@ -21,6 +21,258 @@ #include "ui_spectrum.hpp" -namespace ui { +#include "spectrum_color_lut.hpp" +#include "portapack.hpp" +using namespace portapack; + +#include "string_format.hpp" + +#include +#include + +namespace ui { +namespace spectrum { + +/* FrequencyScale ********************************************************/ + +void FrequencyScale::on_show() { + clear(); +} + +void FrequencyScale::set_spectrum_sampling_rate(const uint32_t new_sampling_rate, const size_t new_spectrum_bins) { + if( (spectrum_sampling_rate != new_sampling_rate) || + (spectrum_bins != new_spectrum_bins) ) { + spectrum_sampling_rate = new_sampling_rate; + spectrum_bins = new_spectrum_bins; + set_dirty(); + } +} + +void FrequencyScale::set_channel_filter( + const uint32_t pass_frequency, + const uint32_t stop_frequency +) { + if( (channel_filter_pass_frequency != pass_frequency) || + (channel_filter_stop_frequency != stop_frequency) ) { + channel_filter_pass_frequency = pass_frequency; + channel_filter_stop_frequency = stop_frequency; + set_dirty(); + } +} + +void FrequencyScale::paint(Painter& painter) { + const auto r = screen_rect(); + + clear_background(painter, r); + + if( !spectrum_sampling_rate || !spectrum_bins ) { + // Can't draw without non-zero scale. + return; + } + + draw_filter_ranges(painter, r); + draw_frequency_ticks(painter, r); +} + +void FrequencyScale::clear() { + spectrum_sampling_rate = 0; + spectrum_bins = 0; + set_dirty(); +} + +void FrequencyScale::clear_background(Painter& painter, const Rect r) { + painter.fill_rectangle(r, Color::black()); +} + +void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) { + const auto x_center = r.width() / 2; + + const Rect tick { + static_cast(r.left() + x_center), r.top(), + 1, r.height() + }; + painter.fill_rectangle(tick, Color::white()); + + constexpr uint32_t tick_count_max = 4; + float rough_tick_interval = float(spectrum_sampling_rate) / tick_count_max; + size_t magnitude = 1; + size_t magnitude_n = 0; + while(rough_tick_interval >= 10.0f) { + rough_tick_interval /= 10; + magnitude *= 10; + magnitude_n += 1; + } + const size_t tick_interval = std::ceil(rough_tick_interval); + + size_t tick_offset = tick_interval; + while((tick_offset * magnitude) < spectrum_sampling_rate / 2) { + const Dim pixel_offset = tick_offset * magnitude * spectrum_bins / spectrum_sampling_rate; + + const std::string zero_pad = + ((magnitude_n % 3) == 0) ? "" : + ((magnitude_n % 3) == 1) ? "0" : "00"; + const std::string unit = + (magnitude_n >= 6) ? "M" : + (magnitude_n >= 3) ? "k" : ""; + const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit; + + const Coord offset_low = static_cast(r.left() + x_center - pixel_offset); + const Rect tick_low { offset_low, r.top(), 1, r.height() }; + painter.fill_rectangle(tick_low, Color::white()); + painter.draw_string({ static_cast(offset_low + 2), r.top() }, style(), label ); + + const Coord offset_high = static_cast(r.left() + x_center + pixel_offset); + const Rect tick_high { offset_high, r.top(), 1, r.height() }; + painter.fill_rectangle(tick_high, Color::white()); + painter.draw_string({ static_cast(offset_high + 2), r.top() }, style(), label ); + + tick_offset += tick_interval; + } +} + +void FrequencyScale::draw_filter_ranges(Painter& painter, const Rect r) { + if( channel_filter_pass_frequency ) { + const auto x_center = r.width() / 2; + + const auto pass_offset = channel_filter_pass_frequency * spectrum_bins / spectrum_sampling_rate; + const auto stop_offset = channel_filter_stop_frequency * spectrum_bins / spectrum_sampling_rate; + + const auto pass_x_lo = x_center - pass_offset; + const auto pass_x_hi = x_center + pass_offset; + + if( channel_filter_stop_frequency ) { + const auto stop_x_lo = x_center - stop_offset; + const auto stop_x_hi = x_center + stop_offset; + + const Rect r_stop_lo { + static_cast(r.left() + stop_x_lo), static_cast(r.bottom() - filter_band_height), + static_cast(pass_x_lo - stop_x_lo), filter_band_height + }; + painter.fill_rectangle( + r_stop_lo, + Color::yellow() + ); + + const Rect r_stop_hi { + static_cast(r.left() + pass_x_hi), static_cast(r.bottom() - filter_band_height), + static_cast(stop_x_hi - pass_x_hi), filter_band_height + }; + painter.fill_rectangle( + r_stop_hi, + Color::yellow() + ); + } + + const Rect r_pass { + static_cast(r.left() + pass_x_lo), static_cast(r.bottom() - filter_band_height), + static_cast(pass_x_hi - pass_x_lo), filter_band_height + }; + painter.fill_rectangle( + r_pass, + Color::green() + ); + } +} + +/* WaterfallView *********************************************************/ + +void WaterfallView::on_show() { + clear(); + + const auto screen_r = screen_rect(); + display.scroll_set_area(screen_r.top(), screen_r.bottom()); +} + +void WaterfallView::on_hide() { + /* TODO: Clear region to eliminate brief flash of content at un-shifted + * position? + */ + display.scroll_disable(); +} + +void WaterfallView::paint(Painter& painter) { + // Do nothing. + (void)painter; +} + +void WaterfallView::on_channel_spectrum( + const ChannelSpectrum& spectrum +) { + /* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */ + + std::array pixel_row; + for(size_t i=0; i<120; i++) { + const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]]; + pixel_row[i] = pixel_color; + } + + for(size_t i=120; i<240; i++) { + const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]]; + pixel_row[i] = pixel_color; + } + + const auto draw_y = display.scroll(1); + + display.draw_pixels( + { { 0, draw_y }, { pixel_row.size(), 1 } }, + pixel_row + ); +} + +void WaterfallView::clear() { + display.fill_rectangle( + screen_rect(), + Color::black() + ); +} + +/* WaterfallWidget *******************************************************/ + +WaterfallWidget::WaterfallWidget() { + add_children({ + &waterfall_view, + &frequency_scale, + }); +} + +void WaterfallWidget::on_show() { + context().message_map().register_handler(Message::ID::ChannelSpectrum, + [this](const Message* const p) { + this->on_channel_spectrum(reinterpret_cast(p)->spectrum); + } + ); +} + +void WaterfallWidget::on_hide() { + context().message_map().unregister_handler(Message::ID::ChannelSpectrum); +} + +void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) { + constexpr Dim scale_height = 20; + + View::set_parent_rect(new_parent_rect); + frequency_scale.set_parent_rect({ 0, 0, new_parent_rect.width(), scale_height }); + waterfall_view.set_parent_rect({ + 0, scale_height, + new_parent_rect.width(), + static_cast(new_parent_rect.height() - scale_height) + }); +} + +void WaterfallWidget::paint(Painter& painter) { + // TODO: + (void)painter; +} + +void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) { + waterfall_view.on_channel_spectrum(spectrum); + frequency_scale.set_spectrum_sampling_rate(spectrum.sampling_rate, spectrum.db_count); + frequency_scale.set_channel_filter( + spectrum.channel_filter_pass_frequency, + spectrum.channel_filter_stop_frequency + ); +} + +} /* namespace spectrum */ } /* namespace ui */ diff --git a/firmware/application/ui_spectrum.hpp b/firmware/application/ui_spectrum.hpp index fe2fab91..21c4479e 100644 --- a/firmware/application/ui_spectrum.hpp +++ b/firmware/application/ui_spectrum.hpp @@ -24,62 +24,23 @@ #include "ui.hpp" #include "ui_widget.hpp" -#include "spectrum_color_lut.hpp" - -#include "portapack.hpp" -using namespace portapack; #include "message.hpp" #include -#include -#include - -#include "string_format.hpp" +#include namespace ui { namespace spectrum { class FrequencyScale : public Widget { public: - void on_show() override { - clear(); - } + void on_show() override; - void set_spectrum_sampling_rate(const uint32_t new_sampling_rate, const size_t new_spectrum_bins) { - if( (spectrum_sampling_rate != new_sampling_rate) || - (spectrum_bins != new_spectrum_bins) ) { - spectrum_sampling_rate = new_sampling_rate; - spectrum_bins = new_spectrum_bins; - set_dirty(); - } - } + void set_spectrum_sampling_rate(const uint32_t new_sampling_rate, const size_t new_spectrum_bins); + void set_channel_filter(const uint32_t pass_frequency, const uint32_t stop_frequency); - void set_channel_filter( - const uint32_t pass_frequency, - const uint32_t stop_frequency - ) { - if( (channel_filter_pass_frequency != pass_frequency) || - (channel_filter_stop_frequency != stop_frequency) ) { - channel_filter_pass_frequency = pass_frequency; - channel_filter_stop_frequency = stop_frequency; - set_dirty(); - } - } - - void paint(Painter& painter) override { - const auto r = screen_rect(); - - clear_background(painter, r); - - if( !spectrum_sampling_rate || !spectrum_bins ) { - // Can't draw without non-zero scale. - return; - } - - draw_filter_ranges(painter, r); - draw_frequency_ticks(painter, r); - } + void paint(Painter& painter) override; private: static constexpr Dim filter_band_height = 4; @@ -89,211 +50,42 @@ private: uint32_t channel_filter_pass_frequency { 0 }; uint32_t channel_filter_stop_frequency { 0 }; - void clear() { - spectrum_sampling_rate = 0; - spectrum_bins = 0; - set_dirty(); - } + void clear(); + void clear_background(Painter& painter, const Rect r); - void clear_background(Painter& painter, const Rect r) { - painter.fill_rectangle(r, Color::black()); - } - - void draw_frequency_ticks(Painter& painter, const Rect r) { - const auto x_center = r.width() / 2; - - const Rect tick { - static_cast(r.left() + x_center), r.top(), - 1, r.height() - }; - painter.fill_rectangle(tick, Color::white()); - - constexpr uint32_t tick_count_max = 4; - float rough_tick_interval = float(spectrum_sampling_rate) / tick_count_max; - size_t magnitude = 1; - size_t magnitude_n = 0; - while(rough_tick_interval >= 10.0f) { - rough_tick_interval /= 10; - magnitude *= 10; - magnitude_n += 1; - } - const size_t tick_interval = std::ceil(rough_tick_interval); - - size_t tick_offset = tick_interval; - while((tick_offset * magnitude) < spectrum_sampling_rate / 2) { - const Dim pixel_offset = tick_offset * magnitude * spectrum_bins / spectrum_sampling_rate; - - const std::string zero_pad = - ((magnitude_n % 3) == 0) ? "" : - ((magnitude_n % 3) == 1) ? "0" : "00"; - const std::string unit = - (magnitude_n >= 6) ? "M" : - (magnitude_n >= 3) ? "k" : ""; - const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit; - - const Coord offset_low = static_cast(r.left() + x_center - pixel_offset); - const Rect tick_low { offset_low, r.top(), 1, r.height() }; - painter.fill_rectangle(tick_low, Color::white()); - painter.draw_string({ static_cast(offset_low + 2), r.top() }, style(), label ); - - const Coord offset_high = static_cast(r.left() + x_center + pixel_offset); - const Rect tick_high { offset_high, r.top(), 1, r.height() }; - painter.fill_rectangle(tick_high, Color::white()); - painter.draw_string({ static_cast(offset_high + 2), r.top() }, style(), label ); - - tick_offset += tick_interval; - } - } - - void draw_filter_ranges(Painter& painter, const Rect r) { - if( channel_filter_pass_frequency ) { - const auto x_center = r.width() / 2; - - const auto pass_offset = channel_filter_pass_frequency * spectrum_bins / spectrum_sampling_rate; - const auto stop_offset = channel_filter_stop_frequency * spectrum_bins / spectrum_sampling_rate; - - const auto pass_x_lo = x_center - pass_offset; - const auto pass_x_hi = x_center + pass_offset; - - if( channel_filter_stop_frequency ) { - const auto stop_x_lo = x_center - stop_offset; - const auto stop_x_hi = x_center + stop_offset; - - const Rect r_stop_lo { - static_cast(r.left() + stop_x_lo), static_cast(r.bottom() - filter_band_height), - static_cast(pass_x_lo - stop_x_lo), filter_band_height - }; - painter.fill_rectangle( - r_stop_lo, - Color::yellow() - ); - - const Rect r_stop_hi { - static_cast(r.left() + pass_x_hi), static_cast(r.bottom() - filter_band_height), - static_cast(stop_x_hi - pass_x_hi), filter_band_height - }; - painter.fill_rectangle( - r_stop_hi, - Color::yellow() - ); - } - - const Rect r_pass { - static_cast(r.left() + pass_x_lo), static_cast(r.bottom() - filter_band_height), - static_cast(pass_x_hi - pass_x_lo), filter_band_height - }; - painter.fill_rectangle( - r_pass, - Color::green() - ); - } - } + void draw_frequency_ticks(Painter& painter, const Rect r); + void draw_filter_ranges(Painter& painter, const Rect r); }; class WaterfallView : public Widget { public: - void on_show() override { - clear(); + void on_show() override; + void on_hide() override; - const auto screen_r = screen_rect(); - display.scroll_set_area(screen_r.top(), screen_r.bottom()); - } + void paint(Painter& painter) override; - void on_hide() override { - /* TODO: Clear region to eliminate brief flash of content at un-shifted - * position? - */ - display.scroll_disable(); - } - - void paint(Painter& painter) override { - // Do nothing. - (void)painter; - } - - void on_channel_spectrum( - const ChannelSpectrum& spectrum - ) { - /* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */ - - std::array pixel_row; - for(size_t i=0; i<120; i++) { - const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]]; - pixel_row[i] = pixel_color; - } - - for(size_t i=120; i<240; i++) { - const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]]; - pixel_row[i] = pixel_color; - } - - const auto draw_y = display.scroll(1); - - display.draw_pixels( - { { 0, draw_y }, { pixel_row.size(), 1 } }, - pixel_row - ); - } + void on_channel_spectrum(const ChannelSpectrum& spectrum); private: - void clear() { - display.fill_rectangle( - screen_rect(), - Color::black() - ); - } + void clear(); }; class WaterfallWidget : public View { public: - WaterfallWidget() { - add_children({ - &waterfall_view, - &frequency_scale, - }); - } + WaterfallWidget(); - void on_show() override { - context().message_map().register_handler(Message::ID::ChannelSpectrum, - [this](const Message* const p) { - this->on_channel_spectrum(reinterpret_cast(p)->spectrum); - } - ); - } + void on_show() override; + void on_hide() override; - void on_hide() override { - context().message_map().unregister_handler(Message::ID::ChannelSpectrum); - } + void set_parent_rect(const Rect new_parent_rect) override; - void set_parent_rect(const Rect new_parent_rect) override { - constexpr Dim scale_height = 20; - - View::set_parent_rect(new_parent_rect); - frequency_scale.set_parent_rect({ 0, 0, new_parent_rect.width(), scale_height }); - waterfall_view.set_parent_rect({ - 0, scale_height, - new_parent_rect.width(), - static_cast(new_parent_rect.height() - scale_height) - }); - } - - void paint(Painter& painter) override { - // TODO: - (void)painter; - } + void paint(Painter& painter) override; private: WaterfallView waterfall_view; FrequencyScale frequency_scale; - void on_channel_spectrum(const ChannelSpectrum& spectrum) { - waterfall_view.on_channel_spectrum(spectrum); - frequency_scale.set_spectrum_sampling_rate(spectrum.sampling_rate, spectrum.db_count); - frequency_scale.set_channel_filter( - spectrum.channel_filter_pass_frequency, - spectrum.channel_filter_stop_frequency - ); - } + void on_channel_spectrum(const ChannelSpectrum& spectrum); }; } /* namespace spectrum */