diff --git a/firmware/application/apps/analog_audio_app.cpp b/firmware/application/apps/analog_audio_app.cpp index 29be3f9c..3ac619bb 100644 --- a/firmware/application/apps/analog_audio_app.cpp +++ b/firmware/application/apps/analog_audio_app.cpp @@ -148,6 +148,10 @@ AnalogAudioView::AnalogAudioView( record_view.on_error = [&nav](std::string message) { nav.display_modal("Error", message); }; + + waterfall.on_select = [this](int32_t offset) { + field_frequency.set_value(receiver_model.tuning_frequency() + offset); + }; audio::output::start(); diff --git a/firmware/application/apps/analog_audio_app.hpp b/firmware/application/apps/analog_audio_app.hpp index fdbf5b1a..9f879384 100644 --- a/firmware/application/apps/analog_audio_app.hpp +++ b/firmware/application/apps/analog_audio_app.hpp @@ -104,6 +104,8 @@ public: void focus() override; + std::string title() const override { return "Analog audio"; }; + private: static constexpr ui::Dim header_height = 3 * 16; @@ -168,7 +170,7 @@ private: u"AUD_????", RecordView::FileType::WAV, 4096, 4 }; - spectrum::WaterfallWidget waterfall { }; + spectrum::WaterfallWidget waterfall { true }; void on_tuning_frequency_changed(rf::Frequency f); void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); diff --git a/firmware/application/apps/capture_app.cpp b/firmware/application/apps/capture_app.cpp index 07192556..9b4feecd 100644 --- a/firmware/application/apps/capture_app.cpp +++ b/firmware/application/apps/capture_app.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek * * This file is part of PortaPack. * @@ -68,8 +69,8 @@ CaptureAppView::CaptureAppView(NavigationView& nav) { this->field_frequency.set_step(v); }; - option_bandwidth.on_change = [this](size_t, uint32_t divider) { - sampling_rate = 4000000 / divider; + option_bandwidth.on_change = [this](size_t, uint32_t base_rate) { + sampling_rate = 8 * base_rate; waterfall.on_hide(); set_target_frequency(target_frequency()); @@ -88,7 +89,7 @@ CaptureAppView::CaptureAppView(NavigationView& nav) { static_cast(receiver_model.vga()), }); - option_bandwidth.set_selected_index(4); // 500k + option_bandwidth.set_selected_index(7); // 500k record_view.on_error = [&nav](std::string message) { nav.display_modal("Error", message); diff --git a/firmware/application/apps/capture_app.hpp b/firmware/application/apps/capture_app.hpp index cadcb0fc..a91289c3 100644 --- a/firmware/application/apps/capture_app.hpp +++ b/firmware/application/apps/capture_app.hpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek * * This file is part of PortaPack. * @@ -93,13 +94,16 @@ private: OptionsField option_bandwidth { { 5 * 8, 1 * 16 }, - 4, + 5, { - { " 25k", 20 }, - { " 50k", 10 }, - { "100k", 5 }, - { "250k", 2 }, - { "500k", 1 } + { " 8k5", 8500 }, + { " 11k ", 11000 }, + { " 16k ", 16000 }, + { " 25k ", 25000 }, + { " 50k ", 50000 }, + { "100k ", 100000 }, + { "250k ", 250000 }, + { "500k ", 500000 } } }; diff --git a/firmware/application/ui/ui_spectrum.cpp b/firmware/application/ui/ui_spectrum.cpp index 07739ee0..c6b9b5c3 100644 --- a/firmware/application/ui/ui_spectrum.cpp +++ b/firmware/application/ui/ui_spectrum.cpp @@ -61,6 +61,19 @@ void FrequencyScale::set_channel_filter( } } +void FrequencyScale::show_cursor(const bool v) { + if (v != _show_cursor) { + _show_cursor = v; + set_dirty(); + } +} + +void FrequencyScale::set_cursor_position(const int32_t value) { + _show_cursor = true; + cursor_position = value; + set_dirty(); +} + void FrequencyScale::paint(Painter& painter) { const auto r = screen_rect(); @@ -73,6 +86,17 @@ void FrequencyScale::paint(Painter& painter) { draw_filter_ranges(painter, r); draw_frequency_ticks(painter, r); + + if (_show_cursor) { + const Rect r_cursor { + 120 + cursor_position, r.bottom() - filter_band_height, + 2, filter_band_height + }; + painter.fill_rectangle( + r_cursor, + Color::red() + ); + } } void FrequencyScale::clear() { @@ -226,13 +250,24 @@ void WaterfallView::clear() { /* WaterfallWidget *******************************************************/ -WaterfallWidget::WaterfallWidget() { +WaterfallWidget::WaterfallWidget(const bool cursor) { + set_focusable(cursor); + add_children({ &waterfall_view, &frequency_scale, }); } +WaterfallWidget::~WaterfallWidget() { + rtc_time::signal_tick_second -= signal_token_tick_second; +} + +void WaterfallWidget::on_tick_second() { + frequency_scale.show_cursor(_blink); + _blink = !_blink; +} + void WaterfallWidget::on_show() { baseband::spectrum_streaming_start(); } @@ -241,6 +276,41 @@ void WaterfallWidget::on_hide() { baseband::spectrum_streaming_stop(); } +void WaterfallWidget::on_focus() { + _blink = true; + signal_token_tick_second = rtc_time::signal_tick_second += [this]() { + this->on_tick_second(); + }; +} + +void WaterfallWidget::on_blur() { + frequency_scale.show_cursor(false); + rtc_time::signal_tick_second -= signal_token_tick_second; +} + +bool WaterfallWidget::on_encoder(const EncoderEvent delta) { + cursor_position += delta; + + cursor_position = std::min(cursor_position, 119); + cursor_position = std::max(cursor_position, -120); + + frequency_scale.set_cursor_position(cursor_position); + return true; +} + +bool WaterfallWidget::on_key(const KeyEvent key) { + if( key == KeyEvent::Select ) { + if( on_select ) { + on_select((cursor_position * sampling_rate) / 240); + cursor_position = 0; + frequency_scale.set_cursor_position(cursor_position); + return true; + } + } + + return false; +} + void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) { constexpr Dim scale_height = 20; @@ -260,7 +330,8 @@ void WaterfallWidget::paint(Painter& painter) { void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) { waterfall_view.on_channel_spectrum(spectrum); - frequency_scale.set_spectrum_sampling_rate(spectrum.sampling_rate); + sampling_rate = spectrum.sampling_rate; + frequency_scale.set_spectrum_sampling_rate(sampling_rate); frequency_scale.set_channel_filter( spectrum.channel_filter_pass_frequency, spectrum.channel_filter_stop_frequency diff --git a/firmware/application/ui/ui_spectrum.hpp b/firmware/application/ui/ui_spectrum.hpp index ce0a8133..0a207f52 100644 --- a/firmware/application/ui/ui_spectrum.hpp +++ b/firmware/application/ui/ui_spectrum.hpp @@ -41,12 +41,16 @@ public: void set_spectrum_sampling_rate(const int new_sampling_rate); void set_channel_filter(const int pass_frequency, const int stop_frequency); + void show_cursor(const bool v); + void set_cursor_position(const int32_t value); void paint(Painter& painter) override; private: static constexpr int filter_band_height = 4; + bool _show_cursor { false }; + int32_t cursor_position { 0 }; int spectrum_sampling_rate { 0 }; const int spectrum_bins = std::tuple_size::value; int channel_filter_pass_frequency { 0 }; @@ -74,7 +78,10 @@ private: class WaterfallWidget : public View { public: - WaterfallWidget(); + std::function on_select { }; + + WaterfallWidget(const bool cursor = false); + ~WaterfallWidget(); WaterfallWidget(const WaterfallWidget&) = delete; WaterfallWidget(WaterfallWidget&&) = delete; @@ -83,15 +90,27 @@ public: void on_show() override; void on_hide() override; + void on_focus() override; + void on_blur() override; + + bool on_encoder(const EncoderEvent delta) override; + bool on_key(const KeyEvent key) override; void set_parent_rect(const Rect new_parent_rect) override; void paint(Painter& painter) override; private: + void on_tick_second(); + WaterfallView waterfall_view { }; FrequencyScale frequency_scale { }; ChannelSpectrumFIFO* fifo { nullptr }; + + bool _blink { false }; + int sampling_rate { 0 }; + int32_t cursor_position { 0 }; + SignalToken signal_token_tick_second { }; MessageHandlerRegistration message_handler_spectrum_config { Message::ID::ChannelSpectrumConfig, diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin index 64300e57..949cd231 100644 Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ