diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 0025e270ffe69268438c2bdfdd962ee11e181cdf..876caee76135b63d3ca8d126d667187fa0283508 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -143,7 +143,7 @@ set(CPPSRC ${COMMON}/portapack_persistent_memory.cpp ${COMMON}/portapack_shared_memory.cpp ${COMMON}/sonde_packet.cpp - ${COMMON}/test_packet.cpp + # ${COMMON}/test_packet.cpp ${COMMON}/tpms_packet.cpp ${COMMON}/ui.cpp ${COMMON}/ui_focus.cpp @@ -239,7 +239,7 @@ set(CPPSRC apps/ui_sonde.cpp apps/ui_soundboard.cpp apps/ui_sstvtx.cpp - apps/ui_test.cpp + # apps/ui_test.cpp apps/ui_tone_search.cpp apps/ui_touch_calibration.cpp apps/ui_touchtunes.cpp diff --git a/firmware/application/apps/analog_audio_app.cpp b/firmware/application/apps/analog_audio_app.cpp index 448ed602a25aa4bbf339fa7747faed64ebae5178..02b52f0d7a35d5ceaa6293636a2f99c4501ffddf 100644 --- a/firmware/application/apps/analog_audio_app.cpp +++ b/firmware/application/apps/analog_audio_app.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek * * This file is part of PortaPack. * @@ -100,10 +101,8 @@ AnalogAudioView::AnalogAudioView( &field_volume, &text_ctcss, &record_view, - &waterfall, + &waterfall }); - - //exit_on_squelch = eos; field_frequency.set_value(receiver_model.tuning_frequency()); field_frequency.set_step(receiver_model.frequency_step()); @@ -177,7 +176,7 @@ void AnalogAudioView::on_hide() { void AnalogAudioView::set_parent_rect(const Rect new_parent_rect) { View::set_parent_rect(new_parent_rect); - + const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; waterfall.set_parent_rect(waterfall_rect); } @@ -208,9 +207,9 @@ void AnalogAudioView::remove_options_widget() { remove_child(options_widget.get()); options_widget.reset(); } - + text_ctcss.hidden(true); - + field_lna.set_style(nullptr); options_modulation.set_style(nullptr); field_frequency.set_style(nullptr); @@ -258,12 +257,23 @@ void AnalogAudioView::on_show_options_modulation() { switch(modulation) { case ReceiverModel::Mode::AMAudio: widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group); + waterfall.set_fft_widget(false); break; case ReceiverModel::Mode::NarrowbandFMAudio: widget = std::make_unique<NBFMOptionsView>(nbfm_view_rect, &style_options_group); + waterfall.set_fft_widget(false); break; - + + case ReceiverModel::Mode::WidebandFMAudio: + waterfall.set_fft_widget(true); + waterfall.on_show(); + break; + + case ReceiverModel::Mode::SpectrumAnalysis: + waterfall.set_fft_widget(false); + break; + default: break; } @@ -341,6 +351,7 @@ void AnalogAudioView::handle_coded_squelch(const uint32_t value) { size_t min_idx { 0 }; size_t c; + // Find nearest match for (c = 0; c < tone_keys.size(); c++) { diff = abs(((float)value / 100.0) - tone_keys[c].second); if (diff < min_diff) { @@ -349,6 +360,7 @@ void AnalogAudioView::handle_coded_squelch(const uint32_t value) { } } + // Arbitrary confidence threshold if (min_diff < 40) text_ctcss.set("CTCSS " + tone_keys[min_idx].first); else diff --git a/firmware/application/apps/analog_audio_app.hpp b/firmware/application/apps/analog_audio_app.hpp index 58c1fd5037260a1cdb7acdeb0fc9c200ccd59aa4..1375c24cc64de9d837416ef2dce8e6526604c763 100644 --- a/firmware/application/apps/analog_audio_app.hpp +++ b/firmware/application/apps/analog_audio_app.hpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek * * This file is part of PortaPack. * diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index ec7a4a842dc754ec4bea2f6aacf2e8b56ce92c8d..6a29e0add398ab97f63060548303cc4edbd7c8b3 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -27,6 +27,10 @@ // Check what ends up in the BSS section by looking at the map files ! // Use constexpr where possible or make sure const are in .cpp files, not headers ! +// Note about messages: +// There can only be one message handler for one kind of message at once +// If an attempt is made to register a second handler, there's a chDbgPanic + //TEST: Goertzel tone detect //TEST: Menuview refresh, seems to blink a lot //TEST: Check AFSK transmit end, skips last bits ? @@ -36,7 +40,11 @@ //BUG: SCANNER Lock on frequency, if frequency jump, still locked on first one //BUG: SCANNER Multiple slices //GLITCH: The about view scroller sometimes misses lines because of a race condition between the display scrolling and drawing the line +//GLITCH: Start of tx using ReplayThread plays a small bit of previous transmission (content of 1 buffer ?) +// See fifo.reset_in() ? +//TODO: DCS decoder +//TODO: Make CTCSS display only when squelch is opened //TODO: Make play button larger in Replay //TODO: Put LNA and VGA controls in Soundboard //TODO: Add default headphones volume setting in Audio settings diff --git a/firmware/application/ui/ui_spectrum.cpp b/firmware/application/ui/ui_spectrum.cpp index c6b9b5c35d2273b3d99f1463bd9efac8beee0a48..912ce10c7fa744953ed0df83f7e3dc234bcddceb 100644 --- a/firmware/application/ui/ui_spectrum.cpp +++ b/firmware/application/ui/ui_spectrum.cpp @@ -255,7 +255,7 @@ WaterfallWidget::WaterfallWidget(const bool cursor) { add_children({ &waterfall_view, - &frequency_scale, + &frequency_scale }); } @@ -311,16 +311,51 @@ bool WaterfallWidget::on_key(const KeyEvent key) { return false; } +void WaterfallWidget::on_audio_spectrum(const AudioSpectrum& spectrum) { + if (fft_widget) { + for (size_t i = 0; i < spectrum.db.size(); i++) + audio_spectrum[i] = ((int16_t)spectrum.db[i] - 127) * 256; + fft_widget->set_dirty(); + } +} + +void WaterfallWidget::set_fft_widget(const bool show) { + if (fft_widget) { + audio_fifo = nullptr; + remove_child(fft_widget.get()); + fft_widget.reset(); + } + + if (show) { + waterfall_view.set_parent_rect(waterfall_reduced_rect); + waterfall_view.on_show(); + fft_widget = std::make_unique<Waveform>( + fft_widget_rect, + audio_spectrum, + 128, + 0, + false, + Color::white()); + add_child(fft_widget.get()); + } else { + waterfall_view.set_parent_rect(waterfall_normal_rect); + waterfall_view.on_show(); + } +} + void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) { constexpr Dim scale_height = 20; View::set_parent_rect(new_parent_rect); + + waterfall_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height}; + waterfall_reduced_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height }; + 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(), - new_parent_rect.height() - scale_height - }); + waterfall_view.set_parent_rect(waterfall_normal_rect); + waterfall_view.on_show(); + + fft_widget_rect = { 0, new_parent_rect.height() - audio_spectrum_height, new_parent_rect.width(), audio_spectrum_height }; } void WaterfallWidget::paint(Painter& painter) { diff --git a/firmware/application/ui/ui_spectrum.hpp b/firmware/application/ui/ui_spectrum.hpp index 0a207f52e65084d4097a2c91edec5fc0954af96a..425a347cb12623d873576ddbc3f3c2176ece4e20 100644 --- a/firmware/application/ui/ui_spectrum.hpp +++ b/firmware/application/ui/ui_spectrum.hpp @@ -97,41 +97,67 @@ public: bool on_key(const KeyEvent key) override; void set_parent_rect(const Rect new_parent_rect) override; + + void set_fft_widget(const bool show); void paint(Painter& painter) override; private: void on_tick_second(); + static constexpr ui::Dim audio_spectrum_height = 2 * 16; + WaterfallView waterfall_view { }; FrequencyScale frequency_scale { }; - ChannelSpectrumFIFO* fifo { nullptr }; + ChannelSpectrumFIFO* channel_fifo { nullptr }; + AudioSpectrumFIFO* audio_fifo { nullptr }; + + std::unique_ptr<Widget> fft_widget { }; bool _blink { false }; int sampling_rate { 0 }; int32_t cursor_position { 0 }; SignalToken signal_token_tick_second { }; + int16_t audio_spectrum[128] { 0 }; + ui::Rect waterfall_normal_rect { }; + ui::Rect waterfall_reduced_rect { }; + ui::Rect fft_widget_rect { }; - MessageHandlerRegistration message_handler_spectrum_config { + MessageHandlerRegistration message_handler_channel_spectrum_config { Message::ID::ChannelSpectrumConfig, [this](const Message* const p) { const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p); - this->fifo = message.fifo; + this->channel_fifo = message.fifo; + } + }; + MessageHandlerRegistration message_handler_audio_spectrum_config { + Message::ID::AudioSpectrumConfig, + [this](const Message* const p) { + const auto message = *reinterpret_cast<const AudioSpectrumConfigMessage*>(p); + this->audio_fifo = message.fifo; } }; MessageHandlerRegistration message_handler_frame_sync { Message::ID::DisplayFrameSync, [this](const Message* const) { - if( this->fifo ) { + if( this->channel_fifo ) { ChannelSpectrum channel_spectrum; - while( fifo->out(channel_spectrum) ) { + while( channel_fifo->out(channel_spectrum) ) { this->on_channel_spectrum(channel_spectrum); } } + if( this->audio_fifo ) { + AudioSpectrum audio_spectrum; + while( audio_fifo->out(audio_spectrum) ) { + // Unstack everything until and only use last buffer (should only be one max. ready per frame) + } + this->on_audio_spectrum(audio_spectrum); + } } }; void on_channel_spectrum(const ChannelSpectrum& spectrum); + void on_audio_spectrum(const AudioSpectrum& spectrum); }; } /* namespace spectrum */ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index ff4615fe39871127b3cf1692a0a8e49be3391b20..0f4a0de548d203a94907ba96b9e6cc287eccf062 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -60,7 +60,7 @@ #include "ui_sonde.hpp" #include "ui_soundboard.hpp" #include "ui_sstvtx.hpp" -#include "ui_test.hpp" +//#include "ui_test.hpp" #include "ui_tone_search.hpp" #include "ui_touchtunes.hpp" #include "ui_view_wav.hpp" diff --git a/firmware/baseband/proc_wfm_audio.cpp b/firmware/baseband/proc_wfm_audio.cpp index 89abe9d35c99576b110b15bc24cd44a23c95d1d7..27419a9f8e91a247be67c1245b4d211737e318e1 100644 --- a/firmware/baseband/proc_wfm_audio.cpp +++ b/firmware/baseband/proc_wfm_audio.cpp @@ -22,8 +22,9 @@ #include "proc_wfm_audio.hpp" +#include "portapack_shared_memory.hpp" #include "audio_output.hpp" - +#include "dsp_fft.hpp" #include "event_m4.hpp" #include <cstdint> @@ -63,7 +64,50 @@ void WidebandFMAudio::execute(const buffer_c8_t& buffer) { * -> 4th order CIC decimation by 2, gain of 1 * -> 96kHz int16_t[64] */ auto audio_2fs = audio_dec_2.execute(audio_4fs, work_audio_buffer); - + + // Input: 96kHz int16_t[64] + // audio_spectrum_decimator piles up 256 bytes before doing FFT computation + // This should send an AudioSpectrum every sample rate/buffer size/(256/64)/refresh scaler = 3072000/2048/4/8 = ~47 Hz + // 0~3: feed continuous audio + // 4~31: ignore, wrap at 31 + if (!(refresh_timer & 0xF8)) { + for (size_t i = 0; i < 64; i++) { + complex_audio[i] = { (int16_t)(work_audio_buffer.p[i] / 32), (int16_t)0 }; + } + audio_spectrum_decimator.feed( + complex_audio_buffer, + [this](const buffer_c16_t& data) { + this->post_message(data); + } + ); + } else { + // Spread the FFT workload in time to avoid making the audio skip + // "8" comes from the log2() of the size of audio_spectrum: log2(256) = 8 + if (fft_stage && (fft_stage <= 8)) { + fft_c_preswapped(audio_spectrum, fft_stage - 1, fft_stage); + fft_stage++; + } else if (fft_stage > 8) { + AudioSpectrum spectrum; + const size_t spectrum_end = spectrum.db.size(); + for(size_t i=0; i<spectrum_end; i++) { + //const auto corrected_sample = spectrum_window_hamming_3(audio_spectrum, i); + const auto corrected_sample = audio_spectrum[i]; + const auto mag2 = magnitude_squared(corrected_sample * (1.0f / 32768.0f)); + const float db = mag2_to_dbv_norm(mag2); + constexpr float mag_scale = 5.0f; + const unsigned int v = (db * mag_scale) + 255.0f; + spectrum.db[i] = std::max(0U, std::min(255U, v)); + } + fifo.in(spectrum); + fft_stage = 0; + } + } + + if (refresh_timer == 31) + refresh_timer = 0; + else + refresh_timer++; + /* 96kHz int16_t[64] * -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop, gain of 1 * -> 48kHz int16_t[32] */ @@ -74,6 +118,12 @@ void WidebandFMAudio::execute(const buffer_c8_t& buffer) { } +void WidebandFMAudio::post_message(const buffer_c16_t& data) { + // This is called when audio_spectrum_decimator is filled up to 256 samples + fft_swap(data, audio_spectrum); + fft_stage = 1; +} + void WidebandFMAudio::on_message(const Message* const message) { switch(message->id) { case Message::ID::UpdateSpectrum: @@ -117,6 +167,9 @@ void WidebandFMAudio::configure(const WFMConfigureMessage& message) { channel_spectrum.set_decimation_factor(1); configured = true; + + AudioSpectrumConfigMessage config_message { &fifo }; + shared_memory.application_queue.push(config_message); } void WidebandFMAudio::capture_config(const CaptureConfigMessage& message) { diff --git a/firmware/baseband/proc_wfm_audio.hpp b/firmware/baseband/proc_wfm_audio.hpp index 669410c6bf324325b708152000f5c7ccbd744661..b84bdbddd03c5d989daf74aac1c2095289cfdd5a 100644 --- a/firmware/baseband/proc_wfm_audio.hpp +++ b/firmware/baseband/proc_wfm_audio.hpp @@ -27,8 +27,10 @@ #include "baseband_thread.hpp" #include "rssi_thread.hpp" +#include "dsp_types.hpp" #include "dsp_decimate.hpp" #include "dsp_demodulate.hpp" +#include "block_decimator.hpp" #include "audio_output.hpp" #include "spectrum_collector.hpp" @@ -56,6 +58,12 @@ private: sizeof(dst) / sizeof(int16_t) }; + std::array<complex16_t, 64> complex_audio { }; + const buffer_c16_t complex_audio_buffer { + complex_audio.data(), + complex_audio.size() + }; + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; uint32_t channel_filter_pass_f = 0; @@ -67,6 +75,14 @@ private: dsp::decimate::FIR64AndDecimateBy2Real audio_filter { }; AudioOutput audio_output { }; + + // For fs=96kHz FFT streaming + BlockDecimator<complex16_t, 256> audio_spectrum_decimator { 1 }; + AudioSpectrum fifo_data[1 << AudioSpectrumConfigMessage::fifo_k] { }; + AudioSpectrumFIFO fifo { fifo_data, AudioSpectrumConfigMessage::fifo_k }; + std::array<std::complex<float>, 256> audio_spectrum { }; + uint32_t refresh_timer { 0 }; + uint32_t fft_stage { 0 }; SpectrumCollector channel_spectrum { }; size_t spectrum_interval_samples = 0; @@ -75,6 +91,7 @@ private: bool configured { false }; void configure(const WFMConfigureMessage& message); void capture_config(const CaptureConfigMessage& message); + void post_message(const buffer_c16_t& data); }; #endif/*__PROC_WFM_AUDIO_H__*/ diff --git a/firmware/baseband/spectrum_collector.cpp b/firmware/baseband/spectrum_collector.cpp index ebe55a24d269937402b67b8ba5e26ff382e0750d..b43daadb5d4d3b0a77321449522142218e7436a1 100644 --- a/firmware/baseband/spectrum_collector.cpp +++ b/firmware/baseband/spectrum_collector.cpp @@ -132,7 +132,7 @@ void SpectrumCollector::update() { // Called from idle thread (after EVT_MASK_SPECTRUM is flagged) if( streaming && channel_spectrum_request_update ) { /* Decimated buffer is full. Compute spectrum. */ - fft_c_preswapped(channel_spectrum); + fft_c_preswapped(channel_spectrum, 0, 8); ChannelSpectrum spectrum; spectrum.sampling_rate = channel_spectrum_sampling_rate; diff --git a/firmware/common/dsp_fft.hpp b/firmware/common/dsp_fft.hpp index 65dd80987f337aeb74f6586ac5a61f64a6484acf..c3884689b27cb1fbfd84a3fac3ca887b8fa28550 100644 --- a/firmware/common/dsp_fft.hpp +++ b/firmware/common/dsp_fft.hpp @@ -100,25 +100,26 @@ void fft_swap_in_place(std::array<T, N>& data) { /* http://www.drdobbs.com/cpp/a-simple-and-efficient-fft-implementatio/199500857?pgno=3 */ template<typename T, size_t N> -void fft_c_preswapped(std::array<T, N>& data) { +void fft_c_preswapped(std::array<T, N>& data, const size_t from, const size_t to) { static_assert(power_of_two(N), "only defined for N == power of two"); constexpr auto K = log_2(N); + if ((to > K) || (from > K)) return; constexpr size_t K_max = 8; static_assert(K <= K_max, "No FFT twiddle factors for K > 8"); static constexpr std::array<std::complex<float>, K_max> wp_table { { - { -2.0f, 0.0f }, - { -1.0f, -1.0f }, - { -0.2928932188134524756f, -0.7071067811865475244f }, - { -0.076120467488713243872f, -0.38268343236508977173f }, - { -0.019214719596769550874f, -0.19509032201612826785f }, - { -0.0048152733278031137552f, -0.098017140329560601994f }, - { -0.0012045437948276072852f, -0.049067674327418014255f }, - { -0.00030118130379577988423f, -0.024541228522912288032f }, + { -2.0f, 0.0f }, // 2 + { -1.0f, -1.0f }, // 4 + { -0.2928932188134524756f, -0.7071067811865475244f }, // 8 + { -0.076120467488713243872f, -0.38268343236508977173f }, // 16 + { -0.019214719596769550874f, -0.19509032201612826785f }, // 32 + { -0.0048152733278031137552f, -0.098017140329560601994f }, // 64 + { -0.0012045437948276072852f, -0.049067674327418014255f }, // 128 + { -0.00030118130379577988423f, -0.024541228522912288032f }, // 256 } }; /* Provide data to this function, pre-swapped. */ - for(size_t k = 0; k < log_2(N); k++) { + for(size_t k = from; k < to; k++) { const size_t mmax = 1 << k; const auto wp = wp_table[k]; T w { 1.0f, 0.0f }; diff --git a/firmware/common/lcd_ili9341.cpp b/firmware/common/lcd_ili9341.cpp index c4f2fcbba69316dc432cea8efe7104dedb486f71..db7a808b798c36fcbf0e566e4731cd2aa548f94a 100644 --- a/firmware/common/lcd_ili9341.cpp +++ b/firmware/common/lcd_ili9341.cpp @@ -297,6 +297,15 @@ void ILI9341::fill_rectangle(ui::Rect r, const ui::Color c) { } } +void ILI9341::fill_rectangle_unrolled8(ui::Rect r, const ui::Color c) { + const auto r_clipped = r.intersect(screen_rect()); + if( !r_clipped.is_empty() ) { + lcd_start_ram_write(r_clipped); + size_t count = r_clipped.width() * r_clipped.height(); + io.lcd_write_pixels_unrolled8(c, count); + } +} + void ILI9341::render_line(const ui::Point p, const uint8_t count, const ui::Color* line_buffer) { lcd_start_ram_write(p, { count, 1 }); io.lcd_write_pixels(line_buffer, count); diff --git a/firmware/common/lcd_ili9341.hpp b/firmware/common/lcd_ili9341.hpp index 817d0a75a21f8da314c586315ae0f2f8ed2dedd8..ddcb12ef992e63e4fbd4150e91fc71675899f378 100644 --- a/firmware/common/lcd_ili9341.hpp +++ b/firmware/common/lcd_ili9341.hpp @@ -49,6 +49,7 @@ public: void wake(); void fill_rectangle(ui::Rect r, const ui::Color c); + void fill_rectangle_unrolled8(ui::Rect r, const ui::Color c); void draw_line(const ui::Point start, const ui::Point end, const ui::Color color); void fill_circle( const ui::Point center, diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 37ba5af3f7942f7e9e83fca8b299ecc20dc3d552..3f2a1752f8ac30f1263b228dfbfa42f91afafbfe 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -106,6 +106,7 @@ public: AudioLevelReport = 51, CodedSquelch = 52, + AudioSpectrumConfig = 53, MAX }; @@ -273,6 +274,27 @@ public: size_t trigger { 0 }; }; +struct AudioSpectrum { + std::array<uint8_t, 128> db { { 0 } }; + //uint32_t sampling_rate { 0 }; +}; + +using AudioSpectrumFIFO = FIFO<AudioSpectrum>; + +class AudioSpectrumConfigMessage : public Message { +public: + static constexpr size_t fifo_k = 2; + + constexpr AudioSpectrumConfigMessage( + AudioSpectrumFIFO* fifo + ) : Message { ID::AudioSpectrumConfig }, + fifo { fifo } + { + } + + AudioSpectrumFIFO* fifo { nullptr }; +}; + struct ChannelSpectrum { std::array<uint8_t, 256> db { { 0 } }; uint32_t sampling_rate { 0 }; diff --git a/firmware/common/portapack_io.hpp b/firmware/common/portapack_io.hpp index a698c1b44ca40c864cccdce8609db1adfc94f832..a8eb9a2b17a64240b742f7709966fc776f04daf2 100644 --- a/firmware/common/portapack_io.hpp +++ b/firmware/common/portapack_io.hpp @@ -158,6 +158,21 @@ public: } } + void lcd_write_pixels_unrolled8(const ui::Color pixel, size_t n) { + auto v = pixel.v; + n >>= 3; + while(n--) { + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + lcd_write_data(v); + } + } + void lcd_write_pixels(const ui::Color* const pixels, size_t n) { for(size_t i=0; i<n; i++) { lcd_write_pixel(pixels[i]); diff --git a/firmware/common/ui_painter.cpp b/firmware/common/ui_painter.cpp index 90f22bbb24be25fabe4d92814e2849000e71282b..2cb0d93f56a82f96532c2be4c212ba090933effa 100644 --- a/firmware/common/ui_painter.cpp +++ b/firmware/common/ui_painter.cpp @@ -98,6 +98,10 @@ void Painter::fill_rectangle(const Rect r, const Color c) { display.fill_rectangle(r, c); } +void Painter::fill_rectangle_unrolled8(const Rect r, const Color c) { + display.fill_rectangle_unrolled8(r, c); +} + void Painter::paint_widget_tree(Widget* const w) { if( ui::is_dirty() ) { paint_widget(w); diff --git a/firmware/common/ui_painter.hpp b/firmware/common/ui_painter.hpp index b2ea26626cca0e59fd74663c2dfb8a3806d08b37..369e3a57ac9bb0b8ad5ea6baa762dce0cfd129ad 100644 --- a/firmware/common/ui_painter.hpp +++ b/firmware/common/ui_painter.hpp @@ -56,6 +56,7 @@ public: void draw_rectangle(const Rect r, const Color c); void fill_rectangle(const Rect r, const Color c); + void fill_rectangle_unrolled8(const Rect r, const Color c); void paint_widget_tree(Widget* const w); diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 756ac5133d64a5f1ceae0ec67fb3c492db63d463..a456fceec3293bbf2793263226f63d2dd57279ad 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -1493,6 +1493,7 @@ Waveform::Waveform( color_ { color } { //set_focusable(false); + //previous_data.resize(length_, 0); } void Waveform::set_cursor(const uint32_t i, const int16_t position) { @@ -1522,19 +1523,31 @@ void Waveform::set_length(const uint32_t new_length) { void Waveform::paint(Painter& painter) { size_t n; Coord y, y_offset = screen_rect().location().y(); - Coord prev_x = screen_rect().location().x(), prev_y; + Coord prev_x, prev_y; float x, x_inc; Dim h = screen_rect().size().height(); const float y_scale = (float)(h - 1) / 65536.0; int16_t * data_start = data_ + offset_; - // Clear - painter.fill_rectangle(screen_rect(), Color::black()); - if (!length_) return; x_inc = (float)screen_rect().size().width() / length_; + // Clear + painter.fill_rectangle_unrolled8(screen_rect(), Color::black()); + /*prev_x = screen_rect().location().x(); + prev_y = previous_data[0]; + x = prev_x + x_inc; + for (n = 1; n < length_; n++) { + y = previous_data[n]; + display.draw_line( {prev_x, prev_y}, {(Coord)x, y}, Color::black()); + + prev_x = x; + prev_y = y; + x += x_inc; + }*/ + prev_x = screen_rect().location().x(); + if (digital_) { // Digital waveform: each value is an horizontal line x = 0; @@ -1557,8 +1570,10 @@ void Waveform::paint(Painter& painter) { x = prev_x + x_inc; h /= 2; prev_y = y_offset + h - (*(data_start++) * y_scale); + //previous_data[0] = prev_y; for (n = 1; n < length_; n++) { y = y_offset + h - (*(data_start++) * y_scale); + //previous_data[n] = y; display.draw_line( {prev_x, prev_y}, {(Coord)x, y}, color_); prev_x = x; diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 49b40bd137c23168c65f987bd16e8209bcc262f3..685d8a52bfea8d629e09984e7ecfba8313451975 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -617,6 +617,7 @@ private: const Color cursor_colors[2] = { Color::cyan(), Color::magenta() }; int16_t * data_; + //std::vector<int16_t> previous_data { }; uint32_t length_; uint32_t offset_; bool digital_ { false };