From b0880d6effe0fa69fa16460de8b77f35fd75930f Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Mon, 29 Jun 2020 18:01:15 -0300 Subject: [PATCH 01/36] Nicer debug -> temperature graph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lowered the scale -10 ºC so it accomodates less than zero temperatures, present sometimes when cold starting the system. Added 1 char for temperature label length. Adjusted the max2837 sensor value -> ºC temp result, by normalizing the conversion to correctly display the standard 25ºC, mentioned in Datasheet. --- firmware/application/apps/ui_debug.cpp | 2 +- firmware/application/apps/ui_debug.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/application/apps/ui_debug.cpp b/firmware/application/apps/ui_debug.cpp index 6d81f620..8622911c 100644 --- a/firmware/application/apps/ui_debug.cpp +++ b/firmware/application/apps/ui_debug.cpp @@ -121,7 +121,7 @@ void TemperatureWidget::paint(Painter& painter) { } TemperatureWidget::temperature_t TemperatureWidget::temperature(const sample_t sensor_value) const { - return -45 + sensor_value * 5; + return -35 + sensor_value * 4; //max2837 datasheet temp 25ºC has sensor value: 15 } std::string TemperatureWidget::temperature_str(const temperature_t temperature) const { diff --git a/firmware/application/apps/ui_debug.hpp b/firmware/application/apps/ui_debug.hpp index 65899698..1e3d6003 100644 --- a/firmware/application/apps/ui_debug.hpp +++ b/firmware/application/apps/ui_debug.hpp @@ -101,10 +101,10 @@ private: std::string temperature_str(const temperature_t temperature) const; - static constexpr temperature_t display_temp_min = 0; + static constexpr temperature_t display_temp_min = -10; //Accomodate negative values, present in cold startup cases static constexpr temperature_t display_temp_scale = 3; static constexpr int bar_width = 1; - static constexpr int temp_len = 3; + static constexpr int temp_len = 4; //Now scale shows up to 4 chars ("-10C") }; class TemperatureView : public View { From 27f566be8f8423ae92140950116057e9db8a12e8 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Mon, 20 Jul 2020 16:43:24 -0300 Subject: [PATCH 02/36] scanner-enhanced-version New ui_scanner, inspired on AlainD's (alain00091) PR: https://github.com/eried/portapack-mayhem/pull/80 It includes the following: 1) A big frequency numbers display. 2) A Manual scan section (you can input a frequency range (START / END), choose a STEP value from an available of standard frequency intervals, and press SCAN button. 3) An AM / WFM / NFM scan mode selector, changing "on the fly". 4) A PAUSE / RESUME button, which will make the scanner to stop upon you listening something of interest 5) AUDIO APP button, a quick shortcut into the analog audio visualizing / recording app, with the mode, frequency, amp, LNA, VGA settings already in tune with the scanner. 6) Two enums are added to freqman.hpp, reserved for compatibility with AlainD's proposed freqman's app and / or further enhancement. More on this topic: ORIGINAL scanner just used one frequency step, when creating scanning frequency ranges, which was unacceptable. AlainD enhanced freqman in order to pass different steppings along with ranges. This seems an excellent idea, and I preserved that aspect on my current implementation of thisscanner, while adding those enums into the freqman just to keep the door open for AlainD's freqman in the future. 7) I did eliminate the extra blank spaces added by function to_string_short_freq() which created unnecessary spacing in every app where there is need for a SHORT string, from a frequency number. (SHORT!, no extra spaces!!) 8) I also maintained AlainD idea of capping the number of frequencies which are dynamically created for each range and stored inside a memory based db. While AlainD capped the number into 400 frequencies, I was able to up that value a bit more, into 500. Cheers! --- firmware/application/apps/ui_scanner.cpp | 373 ++++++++++++++++------- firmware/application/apps/ui_scanner.hpp | 172 ++++++++--- firmware/application/freqman.hpp | 17 ++ firmware/application/string_format.cpp | 3 +- 4 files changed, 421 insertions(+), 144 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 26c00728..1e68d081 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -26,6 +26,7 @@ #include "string_format.hpp" #include "audio.hpp" + using namespace portapack; namespace ui { @@ -38,6 +39,10 @@ ScannerThread::ScannerThread( } ScannerThread::~ScannerThread() { + stop(); +} + +void ScannerThread::stop() { if( thread ) { chThdTerminate(thread); chThdWait(thread); @@ -49,6 +54,18 @@ void ScannerThread::set_scanning(const bool v) { _scanning = v; } +bool ScannerThread::is_scanning() { + return _scanning; +} + +void ScannerThread::set_userpause(const bool v) { + _userpause = v; +} + +bool ScannerThread::is_userpause() { + return _userpause; +} + msg_t ScannerThread::static_fn(void* arg) { auto obj = static_cast(arg); obj->run(); @@ -56,36 +73,33 @@ msg_t ScannerThread::static_fn(void* arg) { } void ScannerThread::run() { - RetuneMessage message { }; - uint32_t frequency_index = 0; - - while( !chThdShouldTerminate() ) { - if (_scanning) { - // Retune - receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); + if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... + RetuneMessage message { }; + uint32_t frequency_index = 0; + while( !chThdShouldTerminate() ) { + if (_scanning) { + // Retune + receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); + message.range = frequency_index; + EventDispatcher::send_message(message); - message.range = frequency_index; - EventDispatcher::send_message(message); - - - frequency_index++; - if (frequency_index >= frequency_list_.size()) - frequency_index = 0; + frequency_index++; + if (frequency_index >= frequency_list_.size()) + frequency_index = 0; + } + chThdSleepMilliseconds(50); //50 is enough for reception stabilization, increase for more precise scanning ? } - - chThdSleepMilliseconds(50); } } void ScannerView::handle_retune(uint32_t i) { - text_cycle.set( to_string_dec_uint(i) + "/" + - to_string_dec_uint(frequency_list.size()) + " : " + - to_string_dec_uint(frequency_list[i]) ); - desc_cycle.set( description_list[i] ); + big_display.set(frequency_list[i]); //Show the big Freq + text_cycle.set( to_string_dec_uint(i + 1,3) ); + if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show } void ScannerView::focus() { - field_lna.focus(); + field_mode.focus(); } ScannerView::~ScannerView() { @@ -94,9 +108,16 @@ ScannerView::~ScannerView() { baseband::shutdown(); } +void ScannerView::show_max() { //show total number of freqs to scan + if (frequency_list.size() == MAX_DB_ENTRY) + text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)"); + else + text_max.set( "/ " + to_string_dec_uint(frequency_list.size())); +} + ScannerView::ScannerView( - NavigationView& -) + NavigationView& nav + ) : nav_ { nav } { add_children({ &labels, @@ -105,104 +126,190 @@ ScannerView::ScannerView( &field_rf_amp, &field_volume, &field_bw, - &field_trigger, &field_squelch, &field_wait, - //&record_view, + &rssi, &text_cycle, + &text_max, &desc_cycle, - //&waterfall, + &big_display, + &button_manual_start, + &button_manual_end, + &field_mode, + &step_mode, + &button_manual_scan, + &button_pause, + &button_audio_app }); - std::string scanner_file = "SCANNER"; - if (load_freqman_file(scanner_file, database)) { - for(auto& entry : database) { - // FIXME - if (entry.type == RANGE) { - for (uint32_t i=entry.frequency_a; i < entry.frequency_b; i+= 1000000) { - frequency_list.push_back(i); - description_list.push_back("RNG " + to_string_dec_uint(entry.frequency_a) + ">" + to_string_dec_uint(entry.frequency_b)); - } - } else { - frequency_list.push_back(entry.frequency_a); - description_list.push_back(entry.description); - } + def_step = change_mode(AM); //Start on AM + field_mode.set_by_value(AM); //Reflect the mode into the manual selector + + big_display.set_style(&style_green); //Start with green color + + button_manual_start.on_select = [this, &nav](Button& button) { + auto new_view = nav_.push(frequency_range.min); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.min = f; + button_manual_start.set_text(to_string_short_freq(f)); + }; + }; + + button_manual_end.on_select = [this, &nav](Button& button) { + auto new_view = nav.push(frequency_range.max); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.max = f; + button_manual_end.set_text(to_string_short_freq(f)); + }; + }; + + button_pause.on_select = [this](Button&) { + if (scan_thread->is_userpause()) { + timer = wait * 10; //Unlock timer pause on_statistics_update + button_pause.set_text("PAUSE"); //resume scanning (show button for pause) + scan_thread->set_userpause(false); + //scan_resume(); + } else { + scan_pause(); + scan_thread->set_userpause(true); + button_pause.set_text("RESUME"); //PAUSED, show resume } - } else { - // DEBUG - // TODO: Clean this - frequency_list.push_back(466025000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466050000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466075000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466175000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466206250); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466231250); - description_list.push_back("POCSAG-France"); - } - - field_bw.set_selected_index(2); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { - receiver_model.set_nbfm_configuration(n); }; - field_wait.on_change = [this](int32_t v) { - wait = v; - }; - field_wait.set_value(5); - - field_trigger.on_change = [this](int32_t v) { - trigger = v; - }; - field_trigger.set_value(30); - - field_squelch.set_value(receiver_model.squelch_level()); - field_squelch.on_change = [this](int32_t v) { - squelch = v; - receiver_model.set_squelch_level(v); + button_audio_app.on_select = [this](Button&) { + if (scan_thread->is_scanning()) + scan_thread->set_scanning(false); + scan_thread->stop(); + nav_.pop(); + nav_.push(); }; + button_manual_scan.on_select = [this](Button&) { + if (!frequency_range.min || !frequency_range.max) { + nav_.display_modal("Error", "Both START and END freqs\nneed a value"); + } else if (frequency_range.min > frequency_range.max) { + nav_.display_modal("Error", "END freq\nis lower than START"); + } else { + scan_thread->stop(); //STOP SCANNER THREAD + frequency_list.clear(); + description_list.clear(); + def_step = step_mode.selected_index_value(); //Use def_step from manual selector + description_list.push_back( + "M:" + to_string_short_freq(frequency_range.min) + ">" + + to_string_short_freq(frequency_range.max) + " S:" + + to_string_short_freq(def_step) + ); + + rf::Frequency frequency = frequency_range.min; + while (frequency_list.size() < MAX_DB_ENTRY && frequency <= frequency_range.max) { //add manual range + frequency_list.push_back(frequency); + description_list.push_back(""); //If empty, will keep showing the last description + frequency+=def_step; + } + + show_max(); + start_scan_thread(); //RESTART SCANNER THREAD + } + }; + + field_mode.on_change = [this](size_t, OptionsField::value_t v) { + if (scan_thread->is_scanning()) + scan_thread->set_scanning(false); // WE STOP SCANNING + audio::output::stop(); + scan_thread->stop(); + receiver_model.disable(); + baseband::shutdown(); + chThdSleepMilliseconds(50); + change_mode(v); + start_scan_thread(); + }; + + //PRE-CONFIGURATION: + field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5); + field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(30); field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - audio::output::start(); - - audio::output::mute(); - baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - receiver_model.set_sampling_rate(3072000); - receiver_model.set_baseband_bandwidth(1750000); - receiver_model.enable(); - receiver_model.set_squelch_level(0); - receiver_model.set_nbfm_configuration(field_bw.selected_index()); - audio::output::unmute(); - - // TODO: Scanning thread here - scan_thread = std::make_unique(frequency_list); + field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; + // LEARN FREQUENCIES + std::string scanner_txt = "SCANNER"; + if ( load_freqman_file(scanner_txt, database) ) { + for(auto& entry : database) { // READ LINE PER LINE + if (frequency_list.size() < MAX_DB_ENTRY) { //We got space! + if (entry.type == RANGE) { //RANGE + switch (entry.step) { + case AM_US: def_step = 10000; break ; + case AM_EUR:def_step = 9000; break ; + case NFM_1: def_step = 12500; break ; + case NFM_2: def_step = 6250; break ; + case FM_1: def_step = 100000; break ; + case FM_2: def_step = 50000; break ; + case N_1: def_step = 25000; break ; + case N_2: def_step = 250000; break ; + case AIRBAND:def_step= 8330; break ; + } + frequency_list.push_back(entry.frequency_a); //Store starting freq and description + description_list.push_back("R:" + to_string_short_freq(entry.frequency_a) + + ">" + to_string_short_freq(entry.frequency_b) + + " S:" + to_string_short_freq(def_step)); + while (frequency_list.size() < MAX_DB_ENTRY && entry.frequency_a <= entry.frequency_b) { //add the rest of the range + entry.frequency_a+=def_step; + frequency_list.push_back(entry.frequency_a); + description_list.push_back(""); //Token (keep showing the last description) + } + } else if ( entry.type == SINGLE) { + frequency_list.push_back(entry.frequency_a); + description_list.push_back("S: " + entry.description); + } + show_max(); + } + else + { + break; //No more space: Stop reading the txt file ! + } + } + } + else + { + desc_cycle.set(" NO SCANNER.TXT FILE ..." ); + } + audio::output::stop(); + step_mode.set_by_value(def_step); //Impose the default step into the manual step selector + start_scan_thread(); } void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { - int32_t max_db = statistics.max_db; - - if (timer <= wait) - timer++; - - if (max_db < -trigger) { - if (timer == wait) { - //audio::output::stop(); - scan_thread->set_scanning(true); + if (!scan_thread->is_userpause()) + { + if (timer >= (wait * 10) ) + { + timer=0; + scan_resume(); + } + else if (!timer) + { + if (statistics.max_db > -squelch) { //There is something on the air... + scan_pause(); + timer++; + } + } + else + { + timer++; } - } else { - //audio::output::start(); - scan_thread->set_scanning(false); - timer = 0; + } +} + +void ScannerView::scan_pause() { + if (scan_thread->is_scanning()) { + scan_thread->set_scanning(false); // WE STOP SCANNING + audio::output::start(); + } +} + +void ScannerView::scan_resume() { + if (!scan_thread->is_scanning()) { + audio::output::stop(); + scan_thread->set_scanning(true); // WE RESCAN } } @@ -211,4 +318,60 @@ void ScannerView::on_headphone_volume_changed(int32_t v) { receiver_model.set_headphone_volume(new_volume); } -} /* namespace ui */ +size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thread->stop(); After this do a start_scan_thread() + using option_t = std::pair; + using options_t = std::vector; + options_t bw; + field_bw.on_change = [this](size_t n, OptionsField::value_t) { }; + + switch (new_mod) { + case NFM: //bw 16k (2) default + bw.emplace_back("8k5", 0); + bw.emplace_back("11k", 0); + bw.emplace_back("16k", 0); + field_bw.set_options(bw); + + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + field_bw.set_selected_index(2); + receiver_model.set_nbfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); + break; + case AM: + bw.emplace_back("DSB", 0); + bw.emplace_back("USB", 0); + bw.emplace_back("LSB", 0); + field_bw.set_options(bw); + + baseband::run_image(portapack::spi_flash::image_tag_am_audio); + receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); + field_bw.set_selected_index(0); + receiver_model.set_am_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; + receiver_model.set_sampling_rate(2000000);receiver_model.set_baseband_bandwidth(2000000); + break; + case WFM: + bw.emplace_back("16k", 0); + field_bw.set_options(bw); + + baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + field_bw.set_selected_index(0); + receiver_model.set_wfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(2000000); + break; + } + + return mod_step[new_mod]; + +} + +void ScannerView::start_scan_thread() { + receiver_model.enable(); + receiver_model.set_squelch_level(0); + scan_thread = std::make_unique(frequency_list); +} + +} /* namespace ui */ \ No newline at end of file diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index b5d17c22..354700af 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -20,20 +20,38 @@ * Boston, MA 02110-1301, USA. */ +#include "ui.hpp" #include "receiver_model.hpp" #include "ui_receiver.hpp" #include "ui_font_fixed_8x16.hpp" +#include "ui_spectrum.hpp" #include "freqman.hpp" +#include "log_file.hpp" +#include "analog_audio_app.hpp" + + +#define MAX_DB_ENTRY 500 namespace ui { +enum modulation_type { AM = 0,WFM,NFM }; + +string const mod_name[3] = {"AM", "WFM", "NFM"}; +size_t const mod_step[3] = {9000, 100000, 12500 }; + class ScannerThread { public: ScannerThread(std::vector frequency_list); ~ScannerThread(); - + void set_scanning(const bool v); + bool is_scanning(); + + void set_userpause(const bool v); + bool is_userpause(); + + void stop(); ScannerThread(const ScannerThread&) = delete; ScannerThread(ScannerThread&&) = delete; @@ -45,38 +63,64 @@ private: Thread* thread { nullptr }; bool _scanning { true }; - + bool _userpause { false }; static msg_t static_fn(void* arg); - void run(); }; class ScannerView : public View { public: - ScannerView(NavigationView&); + ScannerView(NavigationView& nav); ~ScannerView(); void focus() override; + + void big_display_freq(rf::Frequency f); + + const Style style_grey { // scanning + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; - std::string title() const override { return "Scanner"; }; + const Style style_green { //Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; + + std::string title() const override { return "SCANNER"; }; + std::vector frequency_list{ }; + std::vector description_list { }; + +//void set_parent_rect(const Rect new_parent_rect) override; private: + NavigationView& nav_; + + void start_scan_thread(); + size_t change_mode(uint8_t mod_type); + void show_max(); + void scan_pause(); + void scan_resume(); + void on_statistics_update(const ChannelStatistics& statistics); void on_headphone_volume_changed(int32_t v); void handle_retune(uint32_t i); - - std::vector frequency_list { }; - std::vector description_list { }; - int32_t trigger { 0 }; + + jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too... int32_t squelch { 0 }; uint32_t timer { 0 }; uint32_t wait { 0 }; + size_t def_step { 0 }; freqman_db database { }; Labels labels { - { { 0 * 8, 0 * 16 }, "LNA: TRIGGER: /99 VOL:", Color::light_grey() }, - { { 0 * 8, 1 * 16 }, "VGA: SQUELCH: /99 AMP:", Color::light_grey() }, - { { 0 * 8, 2 * 16 }, " BW: WAIT:", Color::light_grey() }, + { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, + { { 0 * 8, 1* 16 }, "BW: SQUELCH: /99 WAIT:", Color::light_grey() }, + { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, + { { 0 * 8, 14 * 16 }, "MODE:", Color::light_grey() }, + { { 11 * 8, 14 * 16 }, "STEP:", Color::light_grey() }, }; LNAGainField field_lna { @@ -84,15 +128,15 @@ private: }; VGAGainField field_vga { - { 4 * 8, 1 * 16 } + { 11 * 8, 0 * 16 } }; RFAmpField field_rf_amp { - { 28 * 8, 1 * 16 } + { 18 * 8, 0 * 16 } }; NumberField field_volume { - { 28 * 8, 0 * 16 }, + { 24 * 8, 0 * 16 }, 2, { 0, 99 }, 1, @@ -100,25 +144,13 @@ private: }; OptionsField field_bw { - { 4 * 8, 2 * 16 }, - 3, - { - { "8k5", 0 }, - { "11k", 0 }, - { "16k", 0 }, - } - }; + { 3 * 8, 1 * 16 }, + 4, + { } + }; - NumberField field_trigger { - { 16 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - NumberField field_squelch { - { 16 * 8, 1 * 16 }, + { 15 * 8, 1 * 16 }, 2, { 0, 99 }, 1, @@ -126,20 +158,84 @@ private: }; NumberField field_wait { - { 16 * 8, 2 * 16 }, + { 26 * 8, 1 * 16 }, 2, { 0, 99 }, 1, ' ', }; + RSSI rssi { + { 0 * 16, 2 * 16, 15 * 16, 8 }, + }; + Text text_cycle { - { 0, 5 * 16, 240, 16 }, - "--/--" + { 0, 3 * 16, 3 * 8, 16 }, }; + + Text text_max { + { 4 * 8, 3 * 16, 18 * 8, 16 }, + }; + Text desc_cycle { - {0, 6 * 16, 240, 16 }, - " " + {0, 4 * 16, 240, 16 }, + }; + + BigFrequency big_display { //Show frequency in glamour + { 4, 6 * 16, 28 * 8, 52 }, + 0 + }; + + Button button_manual_start { + { 0 * 8, 11 * 16, 11 * 8, 28 }, + "" + }; + + Button button_manual_end { + { 12 * 8, 11 * 16, 11 * 8, 28 }, + "" + }; + + Button button_manual_scan { + { 24 * 8, 11 * 16, 6 * 8, 28 }, + "SCAN" + }; + + OptionsField field_mode { + { 5 * 8, 14 * 16 }, + 6, + { + { " AM ", 0 }, + { " WFM ", 1 }, + { " NFM ", 2 }, + } + }; + + OptionsField step_mode { + { 17 * 8, 14 * 16 }, + 12, + { + { "5Khz (SA AM)", 5000 }, + { "9Khz (EU AM)", 9000 }, + { "10Khz(US AM)", 10000 }, + { "50Khz (FM1)", 50000 }, + { "100Khz(FM2)", 100000 }, + { "6.25khz(NFM)", 6250 }, + { "12.5khz(NFM)", 12500 }, + { "25khz (N1)", 25000 }, + { "250khz (N2)", 250000 }, + { "8.33khz(AIR)", 8330 } + } + }; + + Button button_pause { + { 12, 17 * 16, 96, 24 }, + "PAUSE" + }; + + Button button_audio_app { + { 124, 17 * 16, 96, 24 }, + "AUDIO APP" }; std::unique_ptr scan_thread { }; @@ -160,4 +256,4 @@ private: }; }; -} /* namespace ui */ +} /* namespace ui */ \ No newline at end of file diff --git a/firmware/application/freqman.hpp b/firmware/application/freqman.hpp index daaad1ef..dca1f364 100644 --- a/firmware/application/freqman.hpp +++ b/firmware/application/freqman.hpp @@ -48,11 +48,28 @@ enum freqman_entry_type { RANGE }; +//Entry step placed for AlainD freqman version (or any other enhanced version) +enum freqman_entry_step { + STEP_DEF = 0, // default + AM_US, // 10 Khz AM/CB + AM_EUR, // 9 Khz LW/MW + NFM_1, // 12,5 Khz (Analogic PMR 446) + NFM_2, // 6,25 Khz (Digital PMR 446) + FM_1, // 100 Khz + FM_2, // 50 Khz + N_1, // 25 Khz + N_2, // 250 Khz + AIRBAND, // AIRBAND 8,33 Khz + ERROR_STEP +}; + +// freqman_entry_step step added, as above, to provide compatibility / future enhancement. struct freqman_entry { rf::Frequency frequency_a { 0 }; rf::Frequency frequency_b { 0 }; std::string description { }; freqman_entry_type type { }; + freqman_entry_step step { }; }; using freqman_db = std::vector; diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index a62ce1ae..fd7fb1a3 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -113,7 +113,8 @@ std::string to_string_dec_int( } std::string to_string_short_freq(const uint64_t f) { - auto final_str = to_string_dec_int(f / 1000000, 4) + "." + to_string_dec_int((f / 100) % 10000, 4, '0'); + //was... to_string_dec_int(f / 1000000,4) + auto final_str = to_string_dec_int(f / 1000000) + "." + to_string_dec_int((f / 100) % 10000, 4, '0'); return final_str; } From f50b376cbd3d07eca884e9b206b572719a5f74a5 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Mon, 20 Jul 2020 17:43:30 -0300 Subject: [PATCH 03/36] Update ui_scanner.hpp Sorry, I missed this before: These two includes are NOT necessary anymore. --- firmware/application/apps/ui_scanner.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index 354700af..da83e83f 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -25,9 +25,7 @@ #include "ui_receiver.hpp" #include "ui_font_fixed_8x16.hpp" -#include "ui_spectrum.hpp" #include "freqman.hpp" -#include "log_file.hpp" #include "analog_audio_app.hpp" From f08949acd71eeed1504e049e252888378195774d Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Fri, 24 Jul 2020 16:09:21 -0500 Subject: [PATCH 04/36] Add Heading to ADSB and Map Updating --- firmware/application/apps/ais_app.cpp | 2 +- firmware/application/apps/ui_adsb_rx.cpp | 7 +++- firmware/application/apps/ui_adsb_rx.hpp | 6 ++- firmware/application/ui/ui_geomap.cpp | 20 ++++++---- firmware/application/ui/ui_geomap.hpp | 10 ++--- firmware/common/adsb.cpp | 47 +++++++++++++++++++++++- firmware/common/adsb.hpp | 9 +++++ firmware/common/adsb_frame.hpp | 4 ++ 8 files changed, 87 insertions(+), 18 deletions(-) diff --git a/firmware/application/apps/ais_app.cpp b/firmware/application/apps/ais_app.cpp index 89244e87..7d42be86 100644 --- a/firmware/application/apps/ais_app.cpp +++ b/firmware/application/apps/ais_app.cpp @@ -240,7 +240,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) { void AISRecentEntryDetailView::update_position() { if (send_updates) - geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized())); + geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading); } void AISRecentEntryDetailView::focus() { diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index 76d22730..8a174e4c 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -97,7 +97,7 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) { text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14)); if (send_updates) - geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude); + geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading); } ADSBRxDetailsView::~ADSBRxDetailsView() { @@ -172,7 +172,7 @@ ADSBRxDetailsView::ADSBRxDetailsView( GeoPos::alt_unit::FEET, entry_copy.pos.latitude, entry_copy.pos.longitude, - 0, + entry_copy.velo.heading, [this]() { send_updates = false; }); @@ -214,6 +214,7 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { if (frame.get_DF() == DF_ADSB) { uint8_t msg_type = frame.get_msg_type(); + uint8_t msg_sub = frame.get_msg_sub(); uint8_t * raw_data = frame.get_raw_data(); if ((msg_type >= 1) && (msg_type <= 4)) { @@ -236,6 +237,8 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { if (send_updates) details_view->update(entry); } + } else if(msg_type == 19 && (msg_sub >= 1 && msg_sub <= 4)){ + entry.set_frame_velo(frame); } } recent_entries_view.set_dirty(); diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp index c0316623..816992ee 100644 --- a/firmware/application/apps/ui_adsb_rx.hpp +++ b/firmware/application/apps/ui_adsb_rx.hpp @@ -49,7 +49,7 @@ struct AircraftRecentEntry { uint16_t hits { 0 }; uint32_t age { 0 }; adsb_pos pos { false, 0, 0, 0 }; - + adsb_vel velo { false, 0, 0 }; ADSBFrame frame_pos_even { }; ADSBFrame frame_pos_odd { }; @@ -86,6 +86,10 @@ struct AircraftRecentEntry { pos = decode_frame_pos(frame_pos_even, frame_pos_odd); } } + + void set_frame_velo(ADSBFrame& frame){ + velo = decode_frame_velo(frame); + } void set_info_string(std::string& new_info_string) { info_string = new_info_string; diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp index f907825a..20da068e 100644 --- a/firmware/application/ui/ui_geomap.cpp +++ b/firmware/application/ui/ui_geomap.cpp @@ -168,10 +168,15 @@ void GeoMap::paint(Painter& painter) { // Cross display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red()); display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red()); - } else { - draw_bearing(r.center(), angle_, 10, Color::red()); - //center tag above bearing - painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_); + } else if (angle_ < 360){ + //if we have a valid angle just draw bearing + draw_bearing({ 120, 32 + 144 }, angle_, 10, Color::red()); + painter.draw_string({ 120 - ((int)tag_.length() * 8 / 2), 32 + 144 - 32 }, style(), tag_); + } + else { + //draw a small cross + display.fill_rectangle({ r.center() - Point(8, 1), { 16, 2 } }, Color::red()); + display.fill_rectangle({ r.center() - Point(1, 8), { 2, 16 } }, Color::red()); } } @@ -231,7 +236,7 @@ void GeoMap::set_mode(GeoMapMode mode) { mode_ = mode; } -void GeoMap::draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color) { +void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color) { Point arrow_a, arrow_b, arrow_c; for (size_t thickness = 0; thickness < 3; thickness++) { @@ -254,9 +259,10 @@ void GeoMapView::focus() { nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT, nullptr); } -void GeoMapView::update_position(float lat, float lon) { +void GeoMapView::update_position(float lat, float lon, uint16_t angle) { lat_ = lat; lon_ = lon; + angle_ = angle; geopos.set_lat(lat_); geopos.set_lon(lon_); geomap.move(lon_, lat_); @@ -307,7 +313,7 @@ GeoMapView::GeoMapView( GeoPos::alt_unit altitude_unit, float lat, float lon, - float angle, + uint16_t angle, const std::function on_close ) : nav_ (nav), altitude_ (altitude), diff --git a/firmware/application/ui/ui_geomap.hpp b/firmware/application/ui/ui_geomap.hpp index 45a9479b..e4e5ebff 100644 --- a/firmware/application/ui/ui_geomap.hpp +++ b/firmware/application/ui/ui_geomap.hpp @@ -130,7 +130,7 @@ public: } private: - void draw_bearing(const Point origin, const uint32_t angle, uint32_t size, const Color color); + void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color); GeoMapMode mode_ { }; File map_file { }; @@ -141,7 +141,7 @@ private: int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF }; float lat_ { }; float lon_ { }; - float angle_ { }; + uint16_t angle_ { }; std::string tag_ { }; }; @@ -154,7 +154,7 @@ public: GeoPos::alt_unit altitude_unit, float lat, float lon, - float angle, + uint16_t angle, const std::function on_close = nullptr ); GeoMapView(NavigationView& nav, @@ -173,7 +173,7 @@ public: void focus() override; - void update_position(float lat, float lon); + void update_position(float lat, float lon, uint16_t angle); std::string title() const override { return "Map view"; }; @@ -190,7 +190,7 @@ private: GeoPos::alt_unit altitude_unit_ { }; float lat_ { }; float lon_ { }; - float angle_ { }; + uint16_t angle_ { }; std::function on_close_ { nullptr }; bool map_opened { }; diff --git a/firmware/common/adsb.cpp b/firmware/common/adsb.cpp index c730bbec..1831e89d 100644 --- a/firmware/common/adsb.cpp +++ b/firmware/common/adsb.cpp @@ -300,8 +300,8 @@ void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint v_rate_coded = (v_rate / 64) + 1; - velo_ew_abs = abs(velo_ew); - velo_ns_abs = abs(velo_ns); + velo_ew_abs = abs(velo_ew) + 1; + velo_ns_abs = abs(velo_ns) + 1; v_rate_coded_abs = abs(v_rate_coded); make_frame_adsb(frame, ICAO_address); @@ -317,4 +317,47 @@ void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint frame.make_CRC(); } +// Decoding method from dump1090 +adsb_vel decode_frame_velo(ADSBFrame& frame){ + adsb_vel velo {false, 0, 0}; + + uint8_t * frame_data = frame.get_raw_data(); + uint8_t velo_type = frame.get_msg_sub(); + + if(velo_type >= 1 && velo_type <= 4){ //vertical rate is always present + + velo.v_rate = (((frame_data[8] & 0x07 ) << 6) | ((frame_data[9]) >> 2) - 1) * 64; + + if((frame_data[8] & 0x8) >> 3) velo.v_rate *= -1; //check v_rate sign + } + + if(velo_type == 1 || velo_type == 2){ //Ground Speed + int32_t velo_ew = (((frame_data[5] & 0x03) << 8) | frame_data[6]) - 1; + int32_t velo_ns = ((frame_data[7] & 0x7f) << 3) | ((frame_data[8]) >> 5) - 1; + + if (velo_type == 2){ // supersonic indicator so multiply by 4 + velo_ew = velo_ew << 2; + velo_ns = velo_ns << 2; + } + + if((frame_data[5]&4) >> 2) velo_ew *= -1; //check ew direction sign + if((frame_data[7]&0x80) >> 7) velo_ns *= -1; //check ns direction sign + + velo.speed = sqrt(velo_ns*velo_ns + velo_ew*velo_ew); + if(velo.speed){ + //calculate heading in degrees from ew/ns velocities + velo.heading = (uint16_t)(atan2(velo_ew,velo_ns) * 180.0 / pi); + // We don't want negative values but a 0-360 scale. + if (velo.heading < 0) velo.heading += 360.0; + } + + }else if(velo_type == 3 || velo_type == 4){ //Airspeed + velo.valid = frame_data[5] & (1<<2); + velo.heading = ((((frame_data[5] & 0x03)<<8) | frame_data[6]) * 45) << 7; + } + + return velo; + +} + } /* namespace adsb */ diff --git a/firmware/common/adsb.hpp b/firmware/common/adsb.hpp index 4e042ff2..82d177e4 100644 --- a/firmware/common/adsb.hpp +++ b/firmware/common/adsb.hpp @@ -56,6 +56,13 @@ struct adsb_pos { int32_t altitude; }; +struct adsb_vel { + bool valid; + int32_t speed; //knot + uint16_t heading; //degree + int32_t v_rate; //ft/min +}; + const float CPR_MAX_VALUE = 131072.0; const float adsb_lat_lut[58] = { @@ -89,6 +96,8 @@ adsb_pos decode_frame_pos(ADSBFrame& frame_even, ADSBFrame& frame_odd); void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t speed, const float angle, const int32_t v_rate); +adsb_vel decode_frame_velo(ADSBFrame& frame); + //void encode_frame_emergency(ADSBFrame& frame, const uint32_t ICAO_address, const uint8_t code); void encode_frame_squawk(ADSBFrame& frame, const uint32_t squawk); diff --git a/firmware/common/adsb_frame.hpp b/firmware/common/adsb_frame.hpp index 36685843..a93abe6f 100644 --- a/firmware/common/adsb_frame.hpp +++ b/firmware/common/adsb_frame.hpp @@ -41,6 +41,10 @@ public: return (raw_data[4] >> 3); } + uint8_t get_msg_sub() { + return (raw_data[4] & 7); + } + uint32_t get_ICAO_address() { return (raw_data[1] << 16) + (raw_data[2] << 8) + raw_data[3]; } From ea574ef61f244e7ee8a057effd2d2772a3c6131c Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Fri, 24 Jul 2020 16:10:57 -0500 Subject: [PATCH 05/36] Set Sonde heading out of bounds --- firmware/application/apps/ui_sonde.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/application/apps/ui_sonde.cpp b/firmware/application/apps/ui_sonde.cpp index 16506b3f..c08ff642 100644 --- a/firmware/application/apps/ui_sonde.cpp +++ b/firmware/application/apps/ui_sonde.cpp @@ -91,7 +91,7 @@ SondeView::SondeView(NavigationView& nav) { GeoPos::alt_unit::METERS, latitude, longitude, - 0); + 999); //set a dummy heading out of range to draw a cross...probably not ideal? }; logger = std::make_unique(); From 62df30eae3988f9e987b21d69157ebafc2cc9de3 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Fri, 24 Jul 2020 18:56:09 -0500 Subject: [PATCH 06/36] add heading information to adsb details view --- firmware/application/apps/ui_adsb_rx.cpp | 7 +++++-- firmware/application/apps/ui_adsb_rx.hpp | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index 8a174e4c..c44868af 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -92,7 +92,7 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) { text_last_seen.set(to_string_dec_uint(age / 60) + " minutes ago"); text_infos.set(entry_copy.info_string); - + text_info2.set("Hdg:" + to_string_dec_uint(entry_copy.velo.heading)); text_frame_pos_even.set(to_string_hex_array(entry_copy.frame_pos_even.get_raw_data(), 14)); text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14)); @@ -123,6 +123,7 @@ ADSBRxDetailsView::ADSBRxDetailsView( &text_airline, &text_country, &text_infos, + &text_info2, &text_frame_pos_even, &text_frame_pos_odd, &button_see_map @@ -237,8 +238,10 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { if (send_updates) details_view->update(entry); } - } else if(msg_type == 19 && (msg_sub >= 1 && msg_sub <= 4)){ + } else if(msg_type == 19 && msg_sub >= 1 && msg_sub <= 4){ entry.set_frame_velo(frame); + if (send_updates) + details_view->update(entry); } } recent_entries_view.set_dirty(); diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp index 816992ee..6964aa9b 100644 --- a/firmware/application/apps/ui_adsb_rx.hpp +++ b/firmware/application/apps/ui_adsb_rx.hpp @@ -178,6 +178,12 @@ private: { 0 * 8, 6 * 16, 30 * 8, 16 }, "-" }; + + Text text_info2 { + {0*8, 7*16, 30*8, 16}, + "-" + }; + Text text_frame_pos_even { { 0 * 8, 13 * 16, 30 * 8, 16 }, "-" From 542879b74bd30a51e1c43f90a19fb3079365e19b Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 25 Jul 2020 10:21:56 -0500 Subject: [PATCH 07/36] allow negative heading in maths --- firmware/common/adsb.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/firmware/common/adsb.cpp b/firmware/common/adsb.cpp index 1831e89d..6a4412fe 100644 --- a/firmware/common/adsb.cpp +++ b/firmware/common/adsb.cpp @@ -344,11 +344,13 @@ adsb_vel decode_frame_velo(ADSBFrame& frame){ if((frame_data[7]&0x80) >> 7) velo_ns *= -1; //check ns direction sign velo.speed = sqrt(velo_ns*velo_ns + velo_ew*velo_ew); + if(velo.speed){ //calculate heading in degrees from ew/ns velocities - velo.heading = (uint16_t)(atan2(velo_ew,velo_ns) * 180.0 / pi); + int16_t heading_temp = (int16_t)(atan2(velo_ew,velo_ns) * 180.0 / pi); // We don't want negative values but a 0-360 scale. - if (velo.heading < 0) velo.heading += 360.0; + if (heading_temp < 0) heading_temp += 360.0; + velo.heading = (uint16_t)heading_temp; } }else if(velo_type == 3 || velo_type == 4){ //Airspeed From f32584c553c635daa23fbae7eb547530136c471e Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 25 Jul 2020 10:22:21 -0500 Subject: [PATCH 08/36] Add heading and speed to detail view --- firmware/application/apps/ui_adsb_rx.cpp | 8 ++++++-- firmware/application/apps/ui_adsb_rx.hpp | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index c44868af..1fff7e07 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -92,7 +92,11 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) { text_last_seen.set(to_string_dec_uint(age / 60) + " minutes ago"); text_infos.set(entry_copy.info_string); - text_info2.set("Hdg:" + to_string_dec_uint(entry_copy.velo.heading)); + if(entry_copy.velo.heading < 360 && entry_copy.velo.speed >=0){ //I don't like this but... + text_info2.set("Hdg:" + to_string_dec_uint(entry_copy.velo.heading) + " Spd:" + to_string_dec_int(entry_copy.velo.speed)); + }else{ + text_info2.set(""); + } text_frame_pos_even.set(to_string_hex_array(entry_copy.frame_pos_even.get_raw_data(), 14)); text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14)); @@ -200,7 +204,7 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { auto frame = message->frame; uint32_t ICAO_address = frame.get_ICAO_address(); - + if (frame.check_CRC() && frame.get_ICAO_address()) { rtcGetTime(&RTCD1, &datetime); auto& entry = ::on_packet(recent, ICAO_address); diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp index 6964aa9b..55d590bd 100644 --- a/firmware/application/apps/ui_adsb_rx.hpp +++ b/firmware/application/apps/ui_adsb_rx.hpp @@ -49,7 +49,7 @@ struct AircraftRecentEntry { uint16_t hits { 0 }; uint32_t age { 0 }; adsb_pos pos { false, 0, 0, 0 }; - adsb_vel velo { false, 0, 0 }; + adsb_vel velo { false, 0, 999 }; ADSBFrame frame_pos_even { }; ADSBFrame frame_pos_odd { }; From 3ec41bc20957eab08d9daa97430b3ee7af725355 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 25 Jul 2020 10:23:46 -0500 Subject: [PATCH 09/36] Fix angle updating and remove empty callsign --- firmware/application/ui/ui_geomap.cpp | 13 ++++++++----- firmware/application/ui/ui_geomap.hpp | 4 ++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp index 20da068e..88d8b209 100644 --- a/firmware/application/ui/ui_geomap.cpp +++ b/firmware/application/ui/ui_geomap.cpp @@ -59,7 +59,7 @@ GeoPos::GeoPos( set_altitude(0); set_lat(0); set_lon(0); - + const auto changed_fn = [this](int32_t) { float lat_value = lat(); float lon_value = lon(); @@ -169,9 +169,11 @@ void GeoMap::paint(Painter& painter) { display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red()); display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red()); } else if (angle_ < 360){ - //if we have a valid angle just draw bearing + //if we have a valid angle draw bearing draw_bearing({ 120, 32 + 144 }, angle_, 10, Color::red()); - painter.draw_string({ 120 - ((int)tag_.length() * 8 / 2), 32 + 144 - 32 }, style(), tag_); + if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces + painter.draw_string({ 120 - ((int)tag_.length() * 8 / 2), 32 + 144 - 32 }, style(), tag_); + } } else { //draw a small cross @@ -262,9 +264,9 @@ void GeoMapView::focus() { void GeoMapView::update_position(float lat, float lon, uint16_t angle) { lat_ = lat; lon_ = lon; - angle_ = angle; geopos.set_lat(lat_); geopos.set_lon(lon_); + geomap.set_angle(angle); geomap.move(lon_, lat_); geomap.set_dirty(); } @@ -275,7 +277,7 @@ void GeoMapView::setup() { geopos.set_altitude(altitude_); geopos.set_lat(lat_); geopos.set_lon(lon_); - + geopos.on_change = [this](int32_t altitude, float lat, float lon) { altitude_ = altitude; lat_ = lat; @@ -334,6 +336,7 @@ GeoMapView::GeoMapView( geomap.set_mode(mode_); geomap.set_tag(tag); + geomap.set_angle(angle); geomap.move(lon_, lat_); geopos.set_read_only(true); diff --git a/firmware/application/ui/ui_geomap.hpp b/firmware/application/ui/ui_geomap.hpp index e4e5ebff..30fd6983 100644 --- a/firmware/application/ui/ui_geomap.hpp +++ b/firmware/application/ui/ui_geomap.hpp @@ -129,6 +129,10 @@ public: tag_ = new_tag; } + void set_angle(uint16_t new_angle){ + angle_ = new_angle; + } + private: void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color); From e8f6e1389e53e8aeb7b6da61331d1a25a3512326 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Sat, 25 Jul 2020 14:07:03 -0300 Subject: [PATCH 10/36] better "squelch" and coloring big numbers When scanner finds a freq with high dbi, it locks into it "listening" a bit more (less than a second) for either confirm or discard it as an actual high dbi or just a spurious thing. The big number frequency changes color accordingly: Grey = just scanning, yellow = locking in, Green = Found something, allowing the user to listen. --- firmware/application/apps/ui_scanner.cpp | 91 +++++++++++++++++------- firmware/application/apps/ui_scanner.hpp | 12 +++- 2 files changed, 78 insertions(+), 25 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 1e68d081..26c5c776 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -21,7 +21,7 @@ */ #include "ui_scanner.hpp" - +#include "portapack_persistent_memory.hpp" #include "baseband_api.hpp" #include "string_format.hpp" #include "audio.hpp" @@ -66,6 +66,14 @@ bool ScannerThread::is_userpause() { return _userpause; } +void ScannerThread::set_freq_lock(const uint32_t v) { + _freq_lock = v; +} + +uint32_t ScannerThread::is_freq_lock() { + return _freq_lock; +} + msg_t ScannerThread::static_fn(void* arg) { auto obj = static_cast(arg); obj->run(); @@ -75,27 +83,44 @@ msg_t ScannerThread::static_fn(void* arg) { void ScannerThread::run() { if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... RetuneMessage message { }; - uint32_t frequency_index = 0; + uint32_t frequency_index = frequency_list_.size(); + uint32_t freqlock_counter=0; while( !chThdShouldTerminate() ) { if (_scanning) { - // Retune - receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); - message.range = frequency_index; + if (_freq_lock == 0) { //normal scanning + frequency_index++; + if (frequency_index >= frequency_list_.size()) + frequency_index = 0; + receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune + } else { + chThdSleepMilliseconds(25); //Extra time ? + } + + message.range = frequency_index; //Inform freq (for coloring purposes also!) EventDispatcher::send_message(message); - - frequency_index++; - if (frequency_index >= frequency_list_.size()) - frequency_index = 0; } - chThdSleepMilliseconds(50); //50 is enough for reception stabilization, increase for more precise scanning ? + chThdSleepMilliseconds(50); } } } void ScannerView::handle_retune(uint32_t i) { - big_display.set(frequency_list[i]); //Show the big Freq - text_cycle.set( to_string_dec_uint(i + 1,3) ); - if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show + switch (scan_thread->is_freq_lock()) + { + case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING + text_cycle.set( to_string_dec_uint(i + 1,3) ); + if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show + break; + case 1: + big_display.set_style(&style_yellow); //STARTING LOCK FREQ + break; + case MAX_FREQ_LOCK: + big_display.set_style(&style_green); //FREQ LOCK FULL, GREEN! + break; + default: //freq lock is checking the signal, do not update display + return; + } + big_display.set(frequency_list[i]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching) } void ScannerView::focus() { @@ -145,7 +170,14 @@ ScannerView::ScannerView( def_step = change_mode(AM); //Start on AM field_mode.set_by_value(AM); //Reflect the mode into the manual selector - big_display.set_style(&style_green); //Start with green color + big_display.set_style(&style_grey); //Start with gray color + + //HELPER: Pre-setting a manual range, based on stored frequency + rf::Frequency stored_freq = persistent_memory::tuned_frequency(); + frequency_range.min = stored_freq - 1000000; + button_manual_start.set_text(to_string_short_freq(frequency_range.min)); + frequency_range.max = stored_freq + 1000000; + button_manual_end.set_text(to_string_short_freq(frequency_range.max)); button_manual_start.on_select = [this, &nav](Button& button) { auto new_view = nav_.push(frequency_range.min); @@ -165,10 +197,9 @@ ScannerView::ScannerView( button_pause.on_select = [this](Button&) { if (scan_thread->is_userpause()) { - timer = wait * 10; //Unlock timer pause on_statistics_update + timer = wait * 10; //Unlock timer pause on_statistics_update button_pause.set_text("PAUSE"); //resume scanning (show button for pause) - scan_thread->set_userpause(false); - //scan_resume(); + scan_thread->set_userpause(false); //Signal user's will } else { scan_pause(); scan_thread->set_userpause(true); @@ -190,13 +221,14 @@ ScannerView::ScannerView( } else if (frequency_range.min > frequency_range.max) { nav_.display_modal("Error", "END freq\nis lower than START"); } else { + audio::output::stop(); scan_thread->stop(); //STOP SCANNER THREAD frequency_list.clear(); description_list.clear(); def_step = step_mode.selected_index_value(); //Use def_step from manual selector description_list.push_back( - "M:" + to_string_short_freq(frequency_range.min) + ">" + "M:" + to_string_short_freq(frequency_range.min) + " >" + to_string_short_freq(frequency_range.max) + " S:" + to_string_short_freq(def_step) ); @@ -249,7 +281,7 @@ ScannerView::ScannerView( } frequency_list.push_back(entry.frequency_a); //Store starting freq and description description_list.push_back("R:" + to_string_short_freq(entry.frequency_a) - + ">" + to_string_short_freq(entry.frequency_b) + + " >" + to_string_short_freq(entry.frequency_b) + " S:" + to_string_short_freq(def_step)); while (frequency_list.size() < MAX_DB_ENTRY && entry.frequency_a <= entry.frequency_b) { //add the rest of the range entry.frequency_a+=def_step; @@ -287,12 +319,21 @@ void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { } else if (!timer) { - if (statistics.max_db > -squelch) { //There is something on the air... - scan_pause(); - timer++; - } + if (statistics.max_db > -squelch) { //There is something on the air... + if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { //checking time reached + scan_pause(); + timer++; + } else { + scan_thread->set_freq_lock( scan_thread->is_freq_lock() + 1 ); //in lock period, still analyzing the signal + } + } else { //There is NOTHING on the air + if (scan_thread->is_freq_lock() > 0) { //But are we already in freq_lock ? + big_display.set_style(&style_grey); //Back to grey color + scan_thread->set_freq_lock(0); //Reset the scanner lock, since there is no signal + } + } } - else + else //Ongoing wait time { timer++; } @@ -301,6 +342,7 @@ void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { void ScannerView::scan_pause() { if (scan_thread->is_scanning()) { + scan_thread->set_freq_lock(0); //Reset the scanner lock (because user paused, or MAX_FREQ_LOCK reached) for next freq scan scan_thread->set_scanning(false); // WE STOP SCANNING audio::output::start(); } @@ -309,6 +351,7 @@ void ScannerView::scan_pause() { void ScannerView::scan_resume() { if (!scan_thread->is_scanning()) { audio::output::stop(); + big_display.set_style(&style_grey); //Back to grey color scan_thread->set_scanning(true); // WE RESCAN } } diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index da83e83f..f9ee3aa7 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -22,7 +22,6 @@ #include "ui.hpp" #include "receiver_model.hpp" - #include "ui_receiver.hpp" #include "ui_font_fixed_8x16.hpp" #include "freqman.hpp" @@ -30,6 +29,7 @@ #define MAX_DB_ENTRY 500 +#define MAX_FREQ_LOCK 10 //50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous namespace ui { @@ -49,6 +49,9 @@ public: void set_userpause(const bool v); bool is_userpause(); + void set_freq_lock(const uint32_t v); + uint32_t is_freq_lock(); + void stop(); ScannerThread(const ScannerThread&) = delete; @@ -62,6 +65,7 @@ private: bool _scanning { true }; bool _userpause { false }; + uint32_t _freq_lock { 0 }; static msg_t static_fn(void* arg); void run(); }; @@ -81,6 +85,12 @@ public: .foreground = Color::grey(), }; + const Style style_yellow { //Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::dark_yellow(), + }; + const Style style_green { //Found signal .font = font::fixed_8x16, .background = Color::black(), From 01101ecef20c71f33d861822739e0ed0871b3578 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 25 Jul 2020 12:26:10 -0500 Subject: [PATCH 11/36] position string formatting --- firmware/application/apps/ui_adsb_rx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index 1fff7e07..52c55756 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -231,9 +231,9 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { if (entry.pos.valid) { str_info = "Alt:" + to_string_dec_uint(entry.pos.altitude) + - " Lat" + to_string_dec_int(entry.pos.latitude) + + " Lat:" + to_string_dec_int(entry.pos.latitude) + "." + to_string_dec_int((int)abs(entry.pos.latitude * 1000) % 100, 2, '0') + - " Lon" + to_string_dec_int(entry.pos.longitude) + + " Lon:" + to_string_dec_int(entry.pos.longitude) + "." + to_string_dec_int((int)abs(entry.pos.longitude * 1000) % 100, 2, '0'); entry.set_info_string(str_info); From 4c256f65dd952c0e860cbb5e72df8349996d4ebe Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sat, 25 Jul 2020 12:48:47 -0500 Subject: [PATCH 12/36] Re-add bearing position fix --- firmware/application/ui/ui_geomap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp index 88d8b209..d793c223 100644 --- a/firmware/application/ui/ui_geomap.cpp +++ b/firmware/application/ui/ui_geomap.cpp @@ -170,9 +170,10 @@ void GeoMap::paint(Painter& painter) { display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red()); } else if (angle_ < 360){ //if we have a valid angle draw bearing - draw_bearing({ 120, 32 + 144 }, angle_, 10, Color::red()); + draw_bearing(r.center(), angle_, 10, Color::red()); + //center tag above bearing if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces - painter.draw_string({ 120 - ((int)tag_.length() * 8 / 2), 32 + 144 - 32 }, style(), tag_); + painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_); } } else { From 03084251c5c1b901bf43bee2edeae6600b65bb46 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Tue, 28 Jul 2020 01:21:52 -0300 Subject: [PATCH 13/36] Added MIC TX and FREQ DEL buttons MIC TX button :Shortcut for jumping into TX -> MIC app. FREQ DEL button: Deletes currently displayed frequency from temporary scanning memory. Ideal to get rid of those not wanted "noisy" freqs in the middle of a range scan. Also, some code optimizations thrown in. --- firmware/application/apps/ui_scanner.cpp | 123 ++++++++++++++++------- firmware/application/apps/ui_scanner.hpp | 40 ++++++-- 2 files changed, 114 insertions(+), 49 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 26c5c776..eafdf4d1 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -24,8 +24,6 @@ #include "portapack_persistent_memory.hpp" #include "baseband_api.hpp" #include "string_format.hpp" -#include "audio.hpp" - using namespace portapack; @@ -58,14 +56,6 @@ bool ScannerThread::is_scanning() { return _scanning; } -void ScannerThread::set_userpause(const bool v) { - _userpause = v; -} - -bool ScannerThread::is_userpause() { - return _userpause; -} - void ScannerThread::set_freq_lock(const uint32_t v) { _freq_lock = v; } @@ -74,6 +64,10 @@ uint32_t ScannerThread::is_freq_lock() { return _freq_lock; } +void ScannerThread::set_freq_del(const uint32_t v) { + _freq_del = v; +} + msg_t ScannerThread::static_fn(void* arg) { auto obj = static_cast(arg); obj->run(); @@ -81,25 +75,42 @@ msg_t ScannerThread::static_fn(void* arg) { } void ScannerThread::run() { - if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... + if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... RetuneMessage message { }; uint32_t frequency_index = frequency_list_.size(); - uint32_t freqlock_counter=0; while( !chThdShouldTerminate() ) { - if (_scanning) { - if (_freq_lock == 0) { //normal scanning + if (_scanning) + { //Scanning + if (_freq_lock == 0) { //normal scanning (not performing freq_lock) frequency_index++; if (frequency_index >= frequency_list_.size()) frequency_index = 0; receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune - } else { - chThdSleepMilliseconds(25); //Extra time ? } - message.range = frequency_index; //Inform freq (for coloring purposes also!) EventDispatcher::send_message(message); + } + else + { //If not scanning, check for user asking to delete a freq: + if (_freq_del != 0) { //There is a frequency to delete + for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete + if (frequency_list_[i] == _freq_del) + { //found: Erase it + frequency_list_.erase(frequency_list_.begin() + i); + if (i==0) //set scan index one place back to compensate + i=frequency_list_.size(); + else + i--; + + break; + } + } + _freq_del = 0; //deleted. + } + } - chThdSleepMilliseconds(50); + chThdSleepMilliseconds(60); //This value on 50, when scan resume, it does not stabilize for the next freq in list, it jumped one more ahead. + //With 100 it worked fine but scanner is slow. This may need fixing } } } @@ -107,9 +118,10 @@ void ScannerThread::run() { void ScannerView::handle_retune(uint32_t i) { switch (scan_thread->is_freq_lock()) { - case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING + case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING text_cycle.set( to_string_dec_uint(i + 1,3) ); - if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show + current_index = i; //since it is an ongoing scan, this is a new index + if (description_list[current_index].size() > 0) desc_cycle.set( description_list[current_index] ); //Show new description break; case 1: big_display.set_style(&style_yellow); //STARTING LOCK FREQ @@ -120,7 +132,7 @@ void ScannerView::handle_retune(uint32_t i) { default: //freq lock is checking the signal, do not update display return; } - big_display.set(frequency_list[i]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching) + big_display.set(frequency_list[current_index]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching) } void ScannerView::focus() { @@ -164,7 +176,9 @@ ScannerView::ScannerView( &step_mode, &button_manual_scan, &button_pause, - &button_audio_app + &button_audio_app, + &button_remove, + &button_mic_app }); def_step = change_mode(AM); //Start on AM @@ -196,25 +210,47 @@ ScannerView::ScannerView( }; button_pause.on_select = [this](Button&) { - if (scan_thread->is_userpause()) { - timer = wait * 10; //Unlock timer pause on_statistics_update - button_pause.set_text("PAUSE"); //resume scanning (show button for pause) - scan_thread->set_userpause(false); //Signal user's will - } else { + if ( userpause ) + user_resume(); + else { scan_pause(); - scan_thread->set_userpause(true); - button_pause.set_text("RESUME"); //PAUSED, show resume + button_pause.set_text("RESUME"); //PAUSED, show resume + userpause=true; } }; button_audio_app.on_select = [this](Button&) { - if (scan_thread->is_scanning()) - scan_thread->set_scanning(false); + //if (scan_thread->is_scanning()) + // scan_thread->set_scanning(false); scan_thread->stop(); nav_.pop(); nav_.push(); }; + button_mic_app.on_select = [this](Button&) { + //if (scan_thread->is_scanning()) + // scan_thread->set_scanning(false); + scan_thread->stop(); + nav_.pop(); + nav_.push(); + }; + + button_remove.on_select = [this](Button&) { + if (frequency_list.size() > current_index) { + if (scan_thread->is_scanning()) //STOP Scanning if necessary + scan_thread->set_scanning(false); + scan_thread->set_freq_del(frequency_list[current_index]); + description_list.erase(description_list.begin() + current_index); + frequency_list.erase(frequency_list.begin() + current_index); + show_max(); //UPDATE new list size on screen + chThdSleepMilliseconds(300); //"debouncing" pause so user take finger off + timer = wait * 10; //Unlock timer pause on_statistics_update + scan_thread->set_freq_lock(0); //Reset the scanner lock + if ( userpause ) //If user-paused, resume + user_resume(); + } + }; + button_manual_scan.on_select = [this](Button&) { if (!frequency_range.min || !frequency_range.max) { nav_.display_modal("Error", "Both START and END freqs\nneed a value"); @@ -239,21 +275,25 @@ ScannerView::ScannerView( description_list.push_back(""); //If empty, will keep showing the last description frequency+=def_step; } - show_max(); + if ( userpause ) //If user-paused, resume + user_resume(); + big_display.set_style(&style_grey); //Back to grey color start_scan_thread(); //RESTART SCANNER THREAD } }; field_mode.on_change = [this](size_t, OptionsField::value_t v) { if (scan_thread->is_scanning()) - scan_thread->set_scanning(false); // WE STOP SCANNING + scan_thread->set_scanning(false); //STOP SCANNING audio::output::stop(); scan_thread->stop(); receiver_model.disable(); baseband::shutdown(); chThdSleepMilliseconds(50); change_mode(v); + if ( userpause ) //If user-paused, resume + user_resume(); start_scan_thread(); }; @@ -310,7 +350,7 @@ ScannerView::ScannerView( } void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { - if (!scan_thread->is_userpause()) + if ( !userpause ) { if (timer >= (wait * 10) ) { @@ -349,11 +389,16 @@ void ScannerView::scan_pause() { } void ScannerView::scan_resume() { - if (!scan_thread->is_scanning()) { - audio::output::stop(); - big_display.set_style(&style_grey); //Back to grey color - scan_thread->set_scanning(true); // WE RESCAN - } + audio::output::stop(); + big_display.set_style(&style_grey); //Back to grey color + if (!scan_thread->is_scanning()) + scan_thread->set_scanning(true); // RESUME! +} + +void ScannerView::user_resume() { + timer = wait * 10; //Unlock timer pause on_statistics_update ( will trigger a scan_resume() ) + button_pause.set_text("PAUSE"); //Show button for pause + userpause=false; //Resume scanning } void ScannerView::on_headphone_volume_changed(int32_t v) { diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index f9ee3aa7..010a28fe 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -26,6 +26,8 @@ #include "ui_font_fixed_8x16.hpp" #include "freqman.hpp" #include "analog_audio_app.hpp" +#include "audio.hpp" +#include "ui_mictx.hpp" #define MAX_DB_ENTRY 500 @@ -46,12 +48,11 @@ public: void set_scanning(const bool v); bool is_scanning(); - void set_userpause(const bool v); - bool is_userpause(); - void set_freq_lock(const uint32_t v); uint32_t is_freq_lock(); + void set_freq_del(const uint32_t v); + void stop(); ScannerThread(const ScannerThread&) = delete; @@ -64,8 +65,8 @@ private: Thread* thread { nullptr }; bool _scanning { true }; - bool _userpause { false }; uint32_t _freq_lock { 0 }; + uint32_t _freq_del { 0 }; static msg_t static_fn(void* arg); void run(); }; @@ -97,6 +98,12 @@ public: .foreground = Color::green(), }; + const Style style_red { //erasing freq + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::red(), + }; + std::string title() const override { return "SCANNER"; }; std::vector frequency_list{ }; std::vector description_list { }; @@ -111,6 +118,7 @@ private: void show_max(); void scan_pause(); void scan_resume(); + void user_resume(); void on_statistics_update(const ChannelStatistics& statistics); void on_headphone_volume_changed(int32_t v); @@ -122,13 +130,15 @@ private: uint32_t wait { 0 }; size_t def_step { 0 }; freqman_db database { }; + uint32_t current_index { 0 }; + bool userpause { false }; Labels labels { { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, { { 0 * 8, 1* 16 }, "BW: SQUELCH: /99 WAIT:", Color::light_grey() }, { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, - { { 0 * 8, 14 * 16 }, "MODE:", Color::light_grey() }, - { { 11 * 8, 14 * 16 }, "STEP:", Color::light_grey() }, + { { 0 * 8, 27 * 8 }, "MODE:", Color::light_grey() }, + { { 11 * 8, 27 * 8 }, "STEP:", Color::light_grey() }, }; LNAGainField field_lna { @@ -210,7 +220,7 @@ private: }; OptionsField field_mode { - { 5 * 8, 14 * 16 }, + { 5 * 8, 27 * 8 }, 6, { { " AM ", 0 }, @@ -220,7 +230,7 @@ private: }; OptionsField step_mode { - { 17 * 8, 14 * 16 }, + { 17 * 8, 27 * 8 }, 12, { { "5Khz (SA AM)", 5000 }, @@ -237,14 +247,24 @@ private: }; Button button_pause { - { 12, 17 * 16, 96, 24 }, + { 12, 15 * 16, 96, 24 }, "PAUSE" }; Button button_audio_app { - { 124, 17 * 16, 96, 24 }, + { 124, 15 * 16, 96, 24 }, "AUDIO APP" }; + + Button button_remove { + { 12, 17 * 16, 96, 24 }, + "DEL FREQ" + }; + + Button button_mic_app { + { 124, 17 * 16, 96, 24 }, + "MIC TX APP" + }; std::unique_ptr scan_thread { }; From ee67f74fa7e14225e4535846f9856b6a49b737ca Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Tue, 28 Jul 2020 21:05:10 -0300 Subject: [PATCH 14/36] Added two new buttons and other enhancements Added buttons for: Change scanning direction (ascending / descending) Saving current freq into the SCANNER.TXT file (Please notice that, on the other hand -for safety issues- the DEL FQ button, deletes the frequency only from the temp memory on the actual scanning session, but does NOT erases the freq. inside the SCANNER.TXT) Also there are other bug fixes and scanning speed enhancements. --- firmware/application/apps/ui_scanner.cpp | 132 ++++++++++++++++------- firmware/application/apps/ui_scanner.hpp | 43 +++++--- 2 files changed, 125 insertions(+), 50 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index eafdf4d1..63b5f9bb 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -21,9 +21,6 @@ */ #include "ui_scanner.hpp" -#include "portapack_persistent_memory.hpp" -#include "baseband_api.hpp" -#include "string_format.hpp" using namespace portapack; @@ -68,6 +65,12 @@ void ScannerThread::set_freq_del(const uint32_t v) { _freq_del = v; } +void ScannerThread::change_scanning_direction() { + _fwd = !_fwd; + chThdSleepMilliseconds(300); //Give some pause after reversing scanning direction + +} + msg_t ScannerThread::static_fn(void* arg) { auto obj = static_cast(arg); obj->run(); @@ -78,39 +81,47 @@ void ScannerThread::run() { if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... RetuneMessage message { }; uint32_t frequency_index = frequency_list_.size(); + bool restart_scan = false; //Flag whenever scanning is restarting after a pause while( !chThdShouldTerminate() ) { - if (_scanning) - { //Scanning + if (_scanning) { //Scanning if (_freq_lock == 0) { //normal scanning (not performing freq_lock) - frequency_index++; - if (frequency_index >= frequency_list_.size()) - frequency_index = 0; - receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune - } + if (!restart_scan) { //looping at full speed + if (_fwd) { //forward + frequency_index++; + if (frequency_index >= frequency_list_.size()) + frequency_index = 0; + + } else { //reverse + if (frequency_index < 1) + frequency_index = frequency_list_.size(); + frequency_index--; + } + receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune + } + else + restart_scan=false; //Effectively skipping first retuning, giving system time + } message.range = frequency_index; //Inform freq (for coloring purposes also!) EventDispatcher::send_message(message); } - else - { //If not scanning, check for user asking to delete a freq: - if (_freq_del != 0) { //There is a frequency to delete + else { //NOT scanning + restart_scan=true; //Flag the need for skipping a cycle when restarting scan + if (_freq_del != 0) { //There is a frequency to delete for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete if (frequency_list_[i] == _freq_del) - { //found: Erase it + { //found: Erase it frequency_list_.erase(frequency_list_.begin() + i); - if (i==0) //set scan index one place back to compensate + if (i==0) //set scan index one place back to compensate i=frequency_list_.size(); else i--; - break; } } - _freq_del = 0; //deleted. + _freq_del = 0; //deleted. } - } - chThdSleepMilliseconds(60); //This value on 50, when scan resume, it does not stabilize for the next freq in list, it jumped one more ahead. - //With 100 it worked fine but scanner is slow. This may need fixing + chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq } } } @@ -123,11 +134,11 @@ void ScannerView::handle_retune(uint32_t i) { current_index = i; //since it is an ongoing scan, this is a new index if (description_list[current_index].size() > 0) desc_cycle.set( description_list[current_index] ); //Show new description break; - case 1: - big_display.set_style(&style_yellow); //STARTING LOCK FREQ + case 1: //STARTING LOCK FREQ + big_display.set_style(&style_yellow); break; - case MAX_FREQ_LOCK: - big_display.set_style(&style_green); //FREQ LOCK FULL, GREEN! + case MAX_FREQ_LOCK: //FREQ IS STRONG: GREEN and scanner will pause when on_statistics_update() + big_display.set_style(&style_green); break; default: //freq lock is checking the signal, do not update display return; @@ -146,8 +157,10 @@ ScannerView::~ScannerView() { } void ScannerView::show_max() { //show total number of freqs to scan - if (frequency_list.size() == MAX_DB_ENTRY) + if (frequency_list.size() == MAX_DB_ENTRY) { + text_max.set_style(&style_red); text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)"); + } else text_max.set( "/ " + to_string_dec_uint(frequency_list.size())); } @@ -176,15 +189,18 @@ ScannerView::ScannerView( &step_mode, &button_manual_scan, &button_pause, + &button_dir, &button_audio_app, - &button_remove, - &button_mic_app + &button_mic_app, + &button_add, + &button_remove + }); def_step = change_mode(AM); //Start on AM field_mode.set_by_value(AM); //Reflect the mode into the manual selector - big_display.set_style(&style_grey); //Start with gray color + //big_display.set_style(&style_grey); //Start with gray color //HELPER: Pre-setting a manual range, based on stored frequency rf::Frequency stored_freq = persistent_memory::tuned_frequency(); @@ -220,16 +236,12 @@ ScannerView::ScannerView( }; button_audio_app.on_select = [this](Button&) { - //if (scan_thread->is_scanning()) - // scan_thread->set_scanning(false); scan_thread->stop(); nav_.pop(); nav_.push(); }; - button_mic_app.on_select = [this](Button&) { - //if (scan_thread->is_scanning()) - // scan_thread->set_scanning(false); + button_mic_app.on_select = [this](Button&) { scan_thread->stop(); nav_.pop(); nav_.push(); @@ -290,13 +302,59 @@ ScannerView::ScannerView( scan_thread->stop(); receiver_model.disable(); baseband::shutdown(); - chThdSleepMilliseconds(50); change_mode(v); if ( userpause ) //If user-paused, resume user_resume(); start_scan_thread(); }; + button_dir.on_select = [this](Button&) { + scan_thread->change_scanning_direction(); + if ( userpause ) //If user-paused, resume + user_resume(); + big_display.set_style(&style_grey); //Back to grey color + }; + + button_add.on_select = [this](Button&) { //frequency_list[current_index] + File scanner_file; + auto result = scanner_file.open("FREQMAN/SCANNER.TXT"); //First search if freq is already in txt + if (!result.is_valid()) { + std::string frequency_to_add = "f=" + + to_string_dec_uint(frequency_list[current_index] / 1000) + + to_string_dec_uint(frequency_list[current_index] % 1000UL, 3, '0'); + char one_char[1]; //Read it char by char + std::string line; //and put read line in here + bool found=false; + for (size_t pointer=0; pointer < scanner_file.size();pointer++) { + + scanner_file.seek(pointer); + scanner_file.read(one_char, 1); + if ((int)one_char[0] > 31) { //ascii space upwards + line += one_char[0]; //Add it to the textline + } + else if (one_char[0] == '\n') { //New Line + if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) { + found=true; + break; + } + line.clear(); //Ready for next textline + } + } + if (found) { + nav_.display_modal("Error", "Frequency already exists"); + big_display.set(frequency_list[current_index]); //After showing an error + } + else { + auto result = scanner_file.append("FREQMAN/SCANNER.TXT"); //Second: append if it is not there + scanner_file.write_line(frequency_to_add + ",d=ADD FQ"); + } + } else + { + nav_.display_modal("Error", "Cannot open SCANNER.TXT\nfor appending freq."); + big_display.set(frequency_list[current_index]); //After showing an error + } + }; + //PRE-CONFIGURATION: field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5); field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(30); @@ -350,11 +408,11 @@ ScannerView::ScannerView( } void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { - if ( !userpause ) + if ( !userpause ) //Scanning not user-paused { if (timer >= (wait * 10) ) { - timer=0; + timer = 0; scan_resume(); } else if (!timer) @@ -396,7 +454,7 @@ void ScannerView::scan_resume() { } void ScannerView::user_resume() { - timer = wait * 10; //Unlock timer pause on_statistics_update ( will trigger a scan_resume() ) + timer = wait * 10; //Will trigger a scan_resume() on_statistics_update, also advancing to next freq. button_pause.set_text("PAUSE"); //Show button for pause userpause=false; //Resume scanning } diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index 010a28fe..35598e12 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -28,6 +28,10 @@ #include "analog_audio_app.hpp" #include "audio.hpp" #include "ui_mictx.hpp" +#include "portapack_persistent_memory.hpp" +#include "baseband_api.hpp" +#include "string_format.hpp" +#include "file.hpp" #define MAX_DB_ENTRY 500 @@ -53,6 +57,8 @@ public: void set_freq_del(const uint32_t v); + void change_scanning_direction(); + void stop(); ScannerThread(const ScannerThread&) = delete; @@ -65,6 +71,7 @@ private: Thread* thread { nullptr }; bool _scanning { true }; + bool _fwd { true }; uint32_t _freq_lock { 0 }; uint32_t _freq_del { 0 }; static msg_t static_fn(void* arg); @@ -137,8 +144,8 @@ private: { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, { { 0 * 8, 1* 16 }, "BW: SQUELCH: /99 WAIT:", Color::light_grey() }, { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, - { { 0 * 8, 27 * 8 }, "MODE:", Color::light_grey() }, - { { 11 * 8, 27 * 8 }, "STEP:", Color::light_grey() }, + { { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() }, + { { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() }, }; LNAGainField field_lna { @@ -220,7 +227,7 @@ private: }; OptionsField field_mode { - { 5 * 8, 27 * 8 }, + { 5 * 8, (26 * 8) + 4 }, 6, { { " AM ", 0 }, @@ -230,7 +237,7 @@ private: }; OptionsField step_mode { - { 17 * 8, 27 * 8 }, + { 17 * 8, (26 * 8) + 4 }, 12, { { "5Khz (SA AM)", 5000 }, @@ -247,23 +254,33 @@ private: }; Button button_pause { - { 12, 15 * 16, 96, 24 }, + { 0, (15 * 16) - 4, 72, 28 }, "PAUSE" }; - Button button_audio_app { - { 124, 15 * 16, 96, 24 }, - "AUDIO APP" + Button button_dir { + { 0, (35 * 8) - 4, 72, 28 }, + "FW> scan_thread { }; From 6f58c7929a96e68ba17d3045b0c9a3cef5019375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D0=B5=D0=BB=D0=BE=D1=83=D1=81=D0=BE=D0=B2=20=D0=9E?= =?UTF-8?q?=D0=BB=D0=B5=D0=B3?= Date: Fri, 31 Jul 2020 13:47:40 +0300 Subject: [PATCH 15/36] Spectrum Width and Speed Settings --- .../application/apps/analog_audio_app.cpp | 62 +++++++++++++++++-- .../application/apps/analog_audio_app.hpp | 49 ++++++++++++++- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/firmware/application/apps/analog_audio_app.cpp b/firmware/application/apps/analog_audio_app.cpp index 4f3d724a..eef18233 100644 --- a/firmware/application/apps/analog_audio_app.cpp +++ b/firmware/application/apps/analog_audio_app.cpp @@ -36,6 +36,8 @@ using namespace tonekey; #include "string_format.hpp" +#define SPEC 2000000 + namespace ui { /* AMOptionsView *********************************************************/ @@ -83,6 +85,32 @@ NBFMOptionsView::NBFMOptionsView( }; } +/* SPECOptionsView *******************************************************/ + +SPECOptionsView::SPECOptionsView( + AnalogAudioView* view, const Rect parent_rect, const Style* const style +) : View { parent_rect } +{ + set_style(style); + + add_children({ + &label_config, + &options_config, + &text_speed, + &field_speed + }); + + options_config.set_selected_index(view->get_spec_bw_index()); + options_config.on_change = [this, view](size_t n, OptionsField::value_t bw) { + view->set_spec_bw(n, bw); + }; + + field_speed.set_value(view->get_spec_trigger()); + field_speed.on_change = [this, view](int32_t v) { + view->set_spec_trigger(v); + }; +} + /* AnalogAudioView *******************************************************/ AnalogAudioView::AnalogAudioView( @@ -157,6 +185,29 @@ AnalogAudioView::AnalogAudioView( on_modulation_changed(static_cast(modulation)); } +size_t AnalogAudioView::get_spec_bw_index() { + return spec_bw_index; +} + +void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) { + spec_bw_index = index; + spec_bw = bw; + + baseband::set_spectrum(bw, spec_trigger); + receiver_model.set_sampling_rate(bw); + receiver_model.set_baseband_bandwidth(bw/2); +} + +uint16_t AnalogAudioView::get_spec_trigger() { + return spec_trigger; +} + +void AnalogAudioView::set_spec_trigger(uint16_t trigger) { + spec_trigger = trigger; + + baseband::set_spectrum(spec_bw, spec_trigger); +} + AnalogAudioView::~AnalogAudioView() { // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do // both? @@ -272,6 +323,7 @@ void AnalogAudioView::on_show_options_modulation() { break; case ReceiverModel::Mode::SpectrumAnalysis: + widget = std::make_unique(this, nbfm_view_rect, &style_options_group); waterfall.show_audio_spectrum_view(false); text_ctcss.hidden(true); break; @@ -315,15 +367,17 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) { } baseband::run_image(image_tag); - + if (modulation == ReceiverModel::Mode::SpectrumAnalysis) { - baseband::set_spectrum(20000000, 127); + baseband::set_spectrum(spec_bw, spec_trigger); } const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis); receiver_model.set_modulation(modulation); - receiver_model.set_sampling_rate(is_wideband_spectrum_mode ? 20000000 : 3072000); - receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? 12000000 : 1750000); + + receiver_model.set_sampling_rate(is_wideband_spectrum_mode ? spec_bw : 3072000); + receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? spec_bw/2 : 1750000); + receiver_model.enable(); // TODO: This doesn't belong here! There's a better way. diff --git a/firmware/application/apps/analog_audio_app.hpp b/firmware/application/apps/analog_audio_app.hpp index 993e611e..80f3bbe6 100644 --- a/firmware/application/apps/analog_audio_app.hpp +++ b/firmware/application/apps/analog_audio_app.hpp @@ -94,6 +94,43 @@ private: }; }; +class AnalogAudioView; + +class SPECOptionsView : public View { +public: + SPECOptionsView(AnalogAudioView* view, const Rect parent_rect, const Style* const style); + +private: + Text label_config { + { 0 * 8, 0 * 16, 2 * 8, 1 * 16 }, + "BW", + }; + OptionsField options_config { + { 3 * 8, 0 * 16 }, + 4, + { + { "20m ", 20000000 }, + { "10m ", 10000000 }, + { " 5m ", 5000000 }, + { " 2m ", 2000000 }, + { " 1m ", 1000000 }, + { "500k", 500000 }, + } + }; + + Text text_speed { + { 9 * 8, 0 * 16, 8 * 8, 1 * 16 }, + "SP /63" + }; + NumberField field_speed { + { 12 * 8, 0 * 16 }, + 2, + { 0, 63 }, + 1, + ' ', + }; +}; + class AnalogAudioView : public View { public: AnalogAudioView(NavigationView& nav); @@ -106,13 +143,23 @@ public: void focus() override; std::string title() const override { return "Analog audio"; }; - + + size_t get_spec_bw_index(); + void set_spec_bw(size_t index, uint32_t bw); + + uint16_t get_spec_trigger(); + void set_spec_trigger(uint16_t trigger); + private: static constexpr ui::Dim header_height = 3 * 16; const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 }; const Rect nbfm_view_rect { 0 * 8, 1 * 16, 18 * 8, 1 * 16 }; + size_t spec_bw_index = 0; + uint32_t spec_bw = 20000000; + uint16_t spec_trigger = 63; + NavigationView& nav_; //bool exit_on_squelch { false }; From 7ec7a9e07ceb9b8679db79a4d00859a95617a1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D0=B5=D0=BB=D0=BE=D1=83=D1=81=D0=BE=D0=B2=20=D0=9E?= =?UTF-8?q?=D0=BB=D0=B5=D0=B3?= Date: Fri, 31 Jul 2020 13:51:35 +0300 Subject: [PATCH 16/36] Cleanup --- firmware/application/apps/analog_audio_app.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/firmware/application/apps/analog_audio_app.cpp b/firmware/application/apps/analog_audio_app.cpp index eef18233..b8d79e3e 100644 --- a/firmware/application/apps/analog_audio_app.cpp +++ b/firmware/application/apps/analog_audio_app.cpp @@ -36,8 +36,6 @@ using namespace tonekey; #include "string_format.hpp" -#define SPEC 2000000 - namespace ui { /* AMOptionsView *********************************************************/ From 1b2c68b3c0099f09dcd9fc374101bc2f98f2749a Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Sat, 1 Aug 2020 00:58:34 -0300 Subject: [PATCH 17/36] New squelch behavior and fixes Squelch value now goes from -90 to +20 and it's directly compared against the max_db parameter returned from each freq scanned by the radio subsystem, with no adjusts or manipulation (you adjust the number as will be used). Less squelch means weaker signals will trigger it. (as expected). There was a tiny cosmetic bug when you deleted a frequency from the scanning memory: The description was not erased from screen and you could see it while the scan did not resume. There was another bug on the pause button: If you asked for another manual scan range when paused, the button kept the text "RESUME" (its text was not reset to "PAUSE" again). --- firmware/application/apps/ui_scanner.cpp | 28 ++++++++++-------------- firmware/application/apps/ui_scanner.hpp | 6 ++--- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 63b5f9bb..65c4788d 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -105,7 +105,6 @@ void ScannerThread::run() { EventDispatcher::send_message(message); } else { //NOT scanning - restart_scan=true; //Flag the need for skipping a cycle when restarting scan if (_freq_del != 0) { //There is a frequency to delete for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete if (frequency_list_[i] == _freq_del) @@ -120,6 +119,9 @@ void ScannerThread::run() { } _freq_del = 0; //deleted. } + else { + restart_scan=true; //Flag the need for skipping a cycle when restarting scan + } } chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq } @@ -161,8 +163,10 @@ void ScannerView::show_max() { //show total number of freqs to scan text_max.set_style(&style_red); text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)"); } - else + else { + text_max.set_style(&style_grey); text_max.set( "/ " + to_string_dec_uint(frequency_list.size())); + } } ScannerView::ScannerView( @@ -200,8 +204,6 @@ ScannerView::ScannerView( def_step = change_mode(AM); //Start on AM field_mode.set_by_value(AM); //Reflect the mode into the manual selector - //big_display.set_style(&style_grey); //Start with gray color - //HELPER: Pre-setting a manual range, based on stored frequency rf::Frequency stored_freq = persistent_memory::tuned_frequency(); frequency_range.min = stored_freq - 1000000; @@ -255,8 +257,7 @@ ScannerView::ScannerView( description_list.erase(description_list.begin() + current_index); frequency_list.erase(frequency_list.begin() + current_index); show_max(); //UPDATE new list size on screen - chThdSleepMilliseconds(300); //"debouncing" pause so user take finger off - timer = wait * 10; //Unlock timer pause on_statistics_update + desc_cycle.set(" "); //Clean up description (cosmetic detail) scan_thread->set_freq_lock(0); //Reset the scanner lock if ( userpause ) //If user-paused, resume user_resume(); @@ -296,16 +297,12 @@ ScannerView::ScannerView( }; field_mode.on_change = [this](size_t, OptionsField::value_t v) { - if (scan_thread->is_scanning()) - scan_thread->set_scanning(false); //STOP SCANNING - audio::output::stop(); - scan_thread->stop(); receiver_model.disable(); baseband::shutdown(); change_mode(v); - if ( userpause ) //If user-paused, resume - user_resume(); - start_scan_thread(); + if ( !scan_thread->is_scanning() ) //for some motive, audio output gets stopped. + audio::output::start(); //So if scan was stopped we resume audio + receiver_model.enable(); }; button_dir.on_select = [this](Button&) { @@ -357,7 +354,7 @@ ScannerView::ScannerView( //PRE-CONFIGURATION: field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5); - field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(30); + field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(-10); field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; // LEARN FREQUENCIES @@ -417,7 +414,7 @@ void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { } else if (!timer) { - if (statistics.max_db > -squelch) { //There is something on the air... + if (statistics.max_db > squelch ) { //There is something on the air...(statistics.max_db > -squelch) if (scan_thread->is_freq_lock() >= MAX_FREQ_LOCK) { //checking time reached scan_pause(); timer++; @@ -511,7 +508,6 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre } return mod_step[new_mod]; - } void ScannerView::start_scan_thread() { diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index 35598e12..d2befbfe 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -142,7 +142,7 @@ private: Labels labels { { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, - { { 0 * 8, 1* 16 }, "BW: SQUELCH: /99 WAIT:", Color::light_grey() }, + { { 0 * 8, 1* 16 }, "BW: SQUELCH: db WAIT:", Color::light_grey() }, { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, { { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() }, { { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() }, @@ -176,8 +176,8 @@ private: NumberField field_squelch { { 15 * 8, 1 * 16 }, - 2, - { 0, 99 }, + 3, + { -90, 20 }, 1, ' ', }; From bf2a04ab572d8c978dcf2e49f29b8021bd38bc1e Mon Sep 17 00:00:00 2001 From: klockee Date: Mon, 3 Aug 2020 05:48:26 -0400 Subject: [PATCH 18/36] Fix title in splash screen --- firmware/application/ui_navigation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 32fdafcc..b8ada3ba 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -557,6 +557,9 @@ SystemView::SystemView( if (portapack::persistent_memory::config_splash()) navigation_view.push(); + status_view.set_back_enabled(false); + status_view.set_title_image_enabled(true); + this->status_view.set_dirty(); //else // navigation_view.push(); From 9c9021f63b2d47973be6341e44bfa393cc295631 Mon Sep 17 00:00:00 2001 From: klockee Date: Wed, 5 Aug 2020 01:03:51 -0400 Subject: [PATCH 19/36] Added new toolbar in main menu --- firmware/application/ui/ui_btngrid.cpp | 9 +++ firmware/application/ui/ui_btngrid.hpp | 1 + firmware/application/ui_navigation.cpp | 82 ++++++++++++++++++++++++-- firmware/application/ui_navigation.hpp | 58 +++++++++++++++++- firmware/common/message.hpp | 9 +++ firmware/common/ui_widget.cpp | 10 ++++ firmware/common/ui_widget.hpp | 10 ++++ 7 files changed, 174 insertions(+), 5 deletions(-) diff --git a/firmware/application/ui/ui_btngrid.cpp b/firmware/application/ui/ui_btngrid.cpp index 0c756af4..2ad7907e 100644 --- a/firmware/application/ui/ui_btngrid.cpp +++ b/firmware/application/ui/ui_btngrid.cpp @@ -95,6 +95,15 @@ void BtnGridView::set_parent_rect(const Rect new_parent_rect) { update_items(); } +void BtnGridView::set_arrow_enabled(bool new_value) { + if(new_value){ + add_child(&arrow_more); + } + else{ + remove_child(&arrow_more); + } +}; + void BtnGridView::on_tick_second() { if (more && blink) arrow_more.set_foreground(Color::white()); diff --git a/firmware/application/ui/ui_btngrid.hpp b/firmware/application/ui/ui_btngrid.hpp index 1ce14cca..5f922f78 100644 --- a/firmware/application/ui/ui_btngrid.hpp +++ b/firmware/application/ui/ui_btngrid.hpp @@ -62,6 +62,7 @@ public: uint32_t highlighted_index(); void set_parent_rect(const Rect new_parent_rect) override; + void set_arrow_enabled(bool new_value); void on_focus() override; void on_blur() override; bool on_key(const KeyEvent event) override; diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index b8ada3ba..bd2dfce1 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -306,6 +306,56 @@ void SystemStatusView::on_title() { nav_.push(); } +/* Information View *****************************************************/ + +InformationView::InformationView( + NavigationView& nav +) : nav_ (nav) +{ + static constexpr Style style_infobar { + .font = font::fixed_8x16, + .background = {33, 33, 33}, + .foreground = Color::white(), + }; + + add_children({ + &backdrop, + &version, + &test, + &time + }); + + test.on_select = [this](ImageButton&){ + update_time(); + }; + + this->set_focusable(true); + this->set_visible(true); + time.set_focusable(true); + version.set_style(&style_infobar); + + time.on_select = [this](Button&) { + this->on_time(); + }; + + refresh(); +} + +void InformationView::refresh() { + update_time(); + set_dirty(); +} + +void InformationView::on_time(){ + update_time(); +} + +void InformationView::update_time() { + rtcGetTime(&RTCD1, &datetime); + time.set_text(to_string_datetime(datetime)); + set_dirty(); +} + /* Navigation ************************************************************/ bool NavigationView::is_top() const { @@ -502,6 +552,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) { //{ "About", ui::Color::cyan(), nullptr, [&nav](){ nav.push(); } } }); set_max_rows(2); // allow wider buttons + set_arrow_enabled(false); //set_highlighted(1); // Startup selection } @@ -522,6 +573,7 @@ SystemView::SystemView( set_style(&style_default); constexpr ui::Dim status_view_height = 16; + constexpr ui::Dim info_view_height = 16; add_child(&status_view); status_view.set_parent_rect({ @@ -532,18 +584,36 @@ SystemView::SystemView( this->navigation_view.pop(); }; + add_child(&info_view); + info_view.set_parent_rect({ + {0, 19 * 16}, + { parent_rect.width(), info_view_height } + }); + add_child(&navigation_view); navigation_view.set_parent_rect({ { 0, status_view_height }, - { parent_rect.width(), static_cast(parent_rect.height() - status_view_height) } + { parent_rect.width(), static_cast(parent_rect.height() - status_view_height - info_view_height) } }); navigation_view.on_view_changed = [this](const View& new_view) { + + if(!this->navigation_view.is_top()){ + remove_child(&info_view); + } + else{ + add_child(&info_view); + this->info_view.update_time(); + } + this->status_view.set_back_enabled(!this->navigation_view.is_top()); this->status_view.set_title_image_enabled(this->navigation_view.is_top()); - this->status_view.set_dirty(); this->status_view.set_title(new_view.title()); + this->status_view.set_dirty(); + + + //this->info_view.focus(); }; - + // portapack::persistent_memory::set_playdead_sequence(0x8D1); @@ -559,7 +629,7 @@ SystemView::SystemView( navigation_view.push(); status_view.set_back_enabled(false); status_view.set_title_image_enabled(true); - this->status_view.set_dirty(); + status_view.set_dirty(); //else // navigation_view.push(); @@ -570,6 +640,10 @@ Context& SystemView::context() const { return context_; } +void SystemView::on_tick_second() { + this->info_view.update_time(); +} + /* ***********************************************************************/ void BMPView::focus() { diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 8754d35d..d2f16281 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -110,7 +110,7 @@ public: void set_title(const std::string new_value); private: - static constexpr auto default_title = " v1.1.1"; // TODO: Move the version somewhere + static constexpr auto default_title = ""; NavigationView& nav_; @@ -208,6 +208,59 @@ private: }; }; +class InformationView : public View { +public: + InformationView(NavigationView& nav); + void update_time(); + + +private: + void refresh(); + static constexpr auto version_string = "v1.1.1"; + NavigationView& nav_; + + void on_time(); + + rtc::RTC datetime; + + Rectangle backdrop { + { 0, 0 * 16, 240, 16 }, + {33, 33, 33} + }; + + Text version { + {0, 0, 11 * 8, 16}, + version_string + }; + + ImageButton test { + {80, 0, 16, 16}, + &bitmap_icon_camera, + Color::white(), + Color::dark_grey() + }; + + Button time { + {120, 0, 11 * 8, 16}, + "" + }; +/* + LiveDateTime ltime { + {120, 0, 11 * 8, 16} + }; +*/ + + + + MessageHandlerRegistration message_handler_refresh { + Message::ID::InfoRefresh, + [this](const Message* const p) { + (void)p; + this->refresh(); + } + }; +}; + class BMPView : public View { public: BMPView(NavigationView& nav); @@ -261,7 +314,10 @@ public: Context& context() const override; private: + void on_tick_second(); + SystemStatusView status_view { navigation_view }; + InformationView info_view { navigation_view }; NavigationView navigation_view { }; Context& context_; }; diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index eeecc9d0..12d0c068 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -111,6 +111,7 @@ public: AudioLevelReport = 51, CodedSquelch = 52, AudioSpectrum = 53, + InfoRefresh = 54, MAX }; @@ -234,6 +235,14 @@ public: } }; +class InfoRefreshMessage : public Message { +public: + constexpr InfoRefreshMessage( + ) : Message { ID::InfoRefresh } + { + } +}; + class AudioStatisticsMessage : public Message { public: constexpr AudioStatisticsMessage( diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 01639e0c..857221ca 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -1046,6 +1046,16 @@ bool NewButton::on_touch(const TouchEvent event) { return false; } } +/* DateTimeButton ********************************************************/ + +DateTimeButton::DateTimeButton( + Rect parent_rect, + std::string text +) : Button { parent_rect , text_} +{ + set_focusable(true); +} + /* Image *****************************************************************/ diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 61ffeb72..71621e82 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -428,6 +428,16 @@ private: const Bitmap* bitmap_; }; +class DateTimeButton : public Button { +public: + DateTimeButton(Rect parent_rect, std::string text); + void paint(Painter& painter) override; +private: + std::string text_; + void on_tick_second(); + +}; + class Image : public Widget { public: Image(); From 72f3eea13113a8c914503e38f4dda953b6ad56c5 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Fri, 7 Aug 2020 00:19:37 -0300 Subject: [PATCH 20/36] MIC TX Now includes RX with Volume and Squelch You can enable RX and adjust VOLUME and SQUELCH into your liking. Sadly enough, you will NOT be able to use VOICE ACTIVATION when RX is enabled (to ensure there will be NO audio feedback defeating the VA sensing) A "bug" that won over me, but perhaps and hopefully other coder can easily fix: The Vumeter will momentarily "dissappear" when enabling RX. But it will reappear as soon as you start TX. Or when you turn off RX. I enabled the PEAK LEVEL MARK on the Vumeter, so you can easily see in which level your input voice / signal is peaking and regulate the MIC gain accordingly in an easier / more robust way. Side enhancement: Took off the dark green, yellow and red coloring from the vumeter when no signal is present, and replaced it with dark_grey. I know that some coloring is "eye-candy" but the vu-meter is more readable with this new contrast. --- firmware/application/apps/ui_mictx.cpp | 82 ++++++++++++++++++++---- firmware/application/apps/ui_mictx.hpp | 88 ++++++++++++++++++-------- firmware/common/ui_widget.cpp | 8 +-- 3 files changed, 132 insertions(+), 46 deletions(-) diff --git a/firmware/application/apps/ui_mictx.cpp b/firmware/application/apps/ui_mictx.cpp index 8145b28e..e8fc8ce9 100644 --- a/firmware/application/apps/ui_mictx.cpp +++ b/firmware/application/apps/ui_mictx.cpp @@ -61,22 +61,22 @@ void MicTXView::configure_baseband() { void MicTXView::set_tx(bool enable) { if (enable) { + if (rx_enabled) //If audio RX is enabled + rxaudio(false); //Then turn off audio RX transmitting = true; configure_baseband(); transmitter_model.enable(); portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming - //gpio_tx.write(1); - //led_tx.on(); } else { if (transmitting && rogerbeep_enabled) { - baseband::request_beep(); - transmitting = false; - } else { + baseband::request_beep(); //Transmit the roger beep + transmitting = false; //And flag the end of the transmission so ... + } else { // (if roger beep was enabled, this will be executed after the beep ends transmitting. transmitting = false; configure_baseband(); transmitter_model.disable(); - //gpio_tx.write(0); - //led_tx.off(); + if (rx_enabled) //If audio RX is enabled and we've been transmitting + rxaudio(true); //Turn back on audio RX } } } @@ -120,6 +120,39 @@ void MicTXView::do_timing() { void MicTXView::on_tuning_frequency_changed(rf::Frequency f) { transmitter_model.set_tuning_frequency(f); + //if ( rx_enabled ) + receiver_model.set_tuning_frequency(f); //Update freq also for RX +} + +void MicTXView::rxaudio(bool is_on) { + if (is_on) { + baseband::shutdown(); + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + //receiver_model.set_sampling_rate(sampling_rate); //** + //receiver_model.set_baseband_bandwidth(1750000); //** + receiver_model.enable(); + receiver_model.set_tuning_frequency(field_frequency.value()); //probably this too can be commented out. + audio::output::start(); + + } else { //These incredibly convoluted steps are required for the vumeter to reappear when stopping RX. + receiver_model.disable(); + baseband::shutdown(); + baseband::run_image(portapack::spi_flash::image_tag_mic_tx); + audio::input::start(); + transmitter_model.enable(); + portapack::pin_i2s0_rx_sda.mode(3); + transmitting = false; + configure_baseband(); + transmitter_model.disable(); + } +} + +void MicTXView::on_headphone_volume_changed(int32_t v) { + //if (rx_enabled) { + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); + //} } MicTXView::MicTXView( @@ -142,9 +175,12 @@ MicTXView::MicTXView( &field_frequency, &options_tone_key, &check_rogerbeep, + &check_rxactive, + &field_volume, + &field_squelch, &text_ptt }); - + tone_keys_populate(options_tone_key); options_tone_key.on_change = [this](size_t i, int32_t) { tone_key_index = i; @@ -168,6 +204,7 @@ MicTXView::MicTXView( new_view->on_changed = [this](rf::Frequency f) { this->on_tuning_frequency_changed(f); this->field_frequency.set_value(f); + set_dirty(); }; }; @@ -178,16 +215,15 @@ MicTXView::MicTXView( check_va.on_select = [this](Checkbox&, bool v) { va_enabled = v; - text_ptt.hidden(v); - set_dirty(); + text_ptt.hidden(v); //hide / show PTT text + check_rxactive.hidden(v); //hide / show the RX AUDIO + set_dirty(); //Refresh display }; - check_va.set_value(false); check_rogerbeep.on_select = [this](Checkbox&, bool v) { rogerbeep_enabled = v; }; - check_rogerbeep.set_value(false); - + field_va_level.on_change = [this](int32_t v) { va_level = v; vumeter.set_mark(v); @@ -203,7 +239,23 @@ MicTXView::MicTXView( decay_ms = v; }; field_va_decay.set_value(1000); - + + check_rxactive.on_select = [this](Checkbox&, bool v) { + //vumeter.set_value(0); //Start with a clean vumeter + rx_enabled = v; + check_va.hidden(v); //Hide or show voice activation + rxaudio(v); //Activate-Deactivate audio rx accordingly + set_dirty(); //Refresh interface + }; + + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; + + field_squelch.on_change = [this](int32_t v) { + receiver_model.set_squelch_level(100 - v); + }; + field_squelch.set_value(0); + transmitter_model.set_sampling_rate(sampling_rate); transmitter_model.set_baseband_bandwidth(1750000); @@ -216,6 +268,8 @@ MicTXView::MicTXView( MicTXView::~MicTXView() { audio::input::stop(); transmitter_model.disable(); + if (rx_enabled) //Also turn off audio rx if enabled + rxaudio(false); baseband::shutdown(); } diff --git a/firmware/application/apps/ui_mictx.hpp b/firmware/application/apps/ui_mictx.hpp index 4cebe343..f439b895 100644 --- a/firmware/application/apps/ui_mictx.hpp +++ b/firmware/application/apps/ui_mictx.hpp @@ -24,12 +24,14 @@ #define __UI_MICTX_H__ #include "ui.hpp" +#include "ui_painter.hpp" #include "ui_widget.hpp" #include "ui_navigation.hpp" #include "ui_receiver.hpp" #include "transmitter_model.hpp" #include "tone_key.hpp" #include "message.hpp" +#include "receiver_model.hpp" namespace ui { @@ -54,11 +56,11 @@ public: return false; }; - std::string title() const override { return "Microphone TX"; }; + std::string title() const override { return "Mic TX RX"; }; private: static constexpr uint32_t sampling_rate = 1536000U; - static constexpr uint32_t lcd_frame_duration = (256 * 1000UL) / 60; // 1 frame @ 60fps in ms .8 fixed point + static constexpr uint32_t lcd_frame_duration = (256 * 1000UL) / 60; // 1 frame @ 60fps in ms .8 fixed point /60 void update_vumeter(); void do_timing(); @@ -66,10 +68,14 @@ private: void on_tuning_frequency_changed(rf::Frequency f); void on_tx_progress(const bool done); void configure_baseband(); + + void rxaudio(bool is_on); + void on_headphone_volume_changed(int32_t v); bool transmitting { false }; - bool va_enabled { }; - bool rogerbeep_enabled { }; + bool va_enabled { false }; + bool rogerbeep_enabled { false }; + bool rx_enabled { false }; uint32_t tone_key_index { }; float mic_gain { 1.0 }; uint32_t audio_level { 0 }; @@ -80,23 +86,25 @@ private: uint32_t decay_timer { 0 }; Labels labels { - { { 7 * 8, 1 * 8 }, "Mic. gain:", Color::light_grey() }, - { { 7 * 8, 4 * 8 }, "Frequency:", Color::light_grey() }, - { { 7 * 8, 6 * 8 }, "Bandwidth: kHz", Color::light_grey() }, - { { 9 * 8, 13 * 8 }, "Level: /255", Color::light_grey() }, - { { 9 * 8, 15 * 8 }, "Attack: ms", Color::light_grey() }, - { { 9 * 8, 17 * 8 }, "Decay: ms", Color::light_grey() }, - { { 7 * 8, 21 * 8 }, "Tone key:", Color::light_grey() } + { { 3 * 8, 1 * 8 }, "MIC. GAIN:", Color::light_grey() }, + { { 3 * 8, 3 * 8 }, "FREQUENCY:", Color::light_grey() }, + { { 3 * 8, 5 * 8 }, "BANDWIDTH: kHz", Color::light_grey() }, + { { 7 * 8, 11 * 8 }, "LEVEL: /255", Color::light_grey() }, + { { 6 * 8, 13 * 8 }, "ATTACK: ms", Color::light_grey() }, + { { 7 * 8, 15 * 8 }, "DECAY: ms", Color::light_grey() }, + { { 4 * 8, 18 * 8 }, "TONE KEY:", Color::light_grey() }, + { { 9 * 8, 30 * 8 }, "VOL:", Color::light_grey() }, + { { 5 * 8, 32 * 8 }, "SQUELCH:", Color::light_grey() } }; VuMeter vumeter { - { 1 * 8, 2 * 8, 5 * 8, 32 * 8 }, - 20, - false + { 0 * 8, 1 * 8, 2 * 8, 33 * 8 }, + 12, + true }; OptionsField options_gain { - { 17 * 8, 1 * 8 }, + { 13 * 8, 1 * 8 }, 4, { { "x0.5", 5 }, @@ -107,10 +115,10 @@ private: }; FrequencyField field_frequency { - { 17 * 8, 4 * 8 }, + { 13 * 8, 3 * 8 }, }; NumberField field_bw { - { 17 * 8, 6 * 8 }, + { 13 * 8, 5 * 8 }, 3, { 0, 150 }, 1, @@ -118,28 +126,28 @@ private: }; Checkbox check_va { - { 7 * 8, 10 * 8 }, + { 3 * 8, (9 * 8) - 4 }, 7, "Voice activation", false }; NumberField field_va_level { - { 15 * 8, 13 * 8 }, + { 13 * 8, 11 * 8 }, 3, { 0, 255 }, 2, ' ' }; NumberField field_va_attack { - { 16 * 8, 15 * 8 }, + { 13 * 8, 13 * 8 }, 3, { 0, 999 }, 20, ' ' }; NumberField field_va_decay { - { 15 * 8, 17 * 8 }, + { 13 * 8, 15 * 8 }, 4, { 0, 9999 }, 100, @@ -147,28 +155,52 @@ private: }; OptionsField options_tone_key { - { 7 * 8, 23 * 8 }, + { 10 * 8, 20 * 8 }, 23, { } }; Checkbox check_rogerbeep { - { 7 * 8, 26 * 8 }, + { 3 * 8, 23 * 8 }, 10, "Roger beep", false }; - - Text text_ptt { - { 7 * 8, 17 * 16, 16 * 8, 16 }, - "PTT: RIGHT BUTTON" + + Checkbox check_rxactive { + { 3 * 8, (27 * 8) + 4 }, + 8, + "RX audio listening", + false + }; + + NumberField field_volume { + { 13 * 8, 30 * 8 }, + 2, + { 0, 99 }, + 1, + ' ', }; + NumberField field_squelch { + { 13 * 8, 32 * 8 }, + 2, + { 0, 99 }, + 1, + ' ', + }; + + Text text_ptt { + { 7 * 8, 35 * 8, 16 * 8, 16 }, + "PTT: RIGHT BUTTON" + }; + + MessageHandlerRegistration message_handler_lcd_sync { Message::ID::DisplayFrameSync, [this](const Message* const) { - this->update_vumeter(); this->do_timing(); + this->update_vumeter(); } }; diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 01639e0c..9ba90180 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -1759,13 +1759,13 @@ void VuMeter::paint(Painter& painter) { lit = true; if (bar == 0) - color = lit ? Color::red() : Color::dark_red(); + color = lit ? Color::red() : Color::dark_grey(); else if (bar == 1) - color = lit ? Color::orange() : Color::dark_orange(); + color = lit ? Color::orange() : Color::dark_grey(); else if ((bar == 2) || (bar == 3)) - color = lit ? Color::yellow() : Color::dark_yellow(); + color = lit ? Color::yellow() : Color::dark_grey(); else - color = lit ? Color::green() : Color::dark_green(); + color = lit ? Color::green() : Color::dark_grey(); painter.fill_rectangle({ pos.x(), pos.y() + (Coord)(bar * (LED_height + 1)), width, (Coord)LED_height }, color); } From d50ea3cc50b48e3aeac148fcc8cc48e6852cf9b4 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Fri, 7 Aug 2020 16:33:17 -0300 Subject: [PATCH 21/36] fixed a bug, and some fine-tuning Earlier code did not start with squelch totally open, but a tiny bit closed. (now at app loading, squelch is truly set up with the same value it shows on screen). I also hardcoded the NFM sampling rate and baseband bandwidth. It seemed "the right thing to do". --- firmware/application/apps/ui_mictx.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/firmware/application/apps/ui_mictx.cpp b/firmware/application/apps/ui_mictx.cpp index e8fc8ce9..0d7b2367 100644 --- a/firmware/application/apps/ui_mictx.cpp +++ b/firmware/application/apps/ui_mictx.cpp @@ -126,15 +126,15 @@ void MicTXView::on_tuning_frequency_changed(rf::Frequency f) { void MicTXView::rxaudio(bool is_on) { if (is_on) { + audio::input::stop(); baseband::shutdown(); baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - //receiver_model.set_sampling_rate(sampling_rate); //** - //receiver_model.set_baseband_bandwidth(1750000); //** - receiver_model.enable(); + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); receiver_model.set_tuning_frequency(field_frequency.value()); //probably this too can be commented out. - audio::output::start(); - + receiver_model.enable(); + audio::output::start(); } else { //These incredibly convoluted steps are required for the vumeter to reappear when stopping RX. receiver_model.disable(); baseband::shutdown(); @@ -255,6 +255,7 @@ MicTXView::MicTXView( receiver_model.set_squelch_level(100 - v); }; field_squelch.set_value(0); + receiver_model.set_squelch_level(0); transmitter_model.set_sampling_rate(sampling_rate); transmitter_model.set_baseband_bandwidth(1750000); From 6d131ccfd478d7084e83630b5b2f93108971cd3f Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Fri, 7 Aug 2020 16:35:06 -0300 Subject: [PATCH 22/36] sorry, little detail That include is not needed --- firmware/application/apps/ui_mictx.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware/application/apps/ui_mictx.hpp b/firmware/application/apps/ui_mictx.hpp index f439b895..0f27206c 100644 --- a/firmware/application/apps/ui_mictx.hpp +++ b/firmware/application/apps/ui_mictx.hpp @@ -24,7 +24,6 @@ #define __UI_MICTX_H__ #include "ui.hpp" -#include "ui_painter.hpp" #include "ui_widget.hpp" #include "ui_navigation.hpp" #include "ui_receiver.hpp" From b49ad91d2425ab79993fea2163fec6c9888ecfe1 Mon Sep 17 00:00:00 2001 From: Erwin Ried <1091420+eried@users.noreply.github.com> Date: Fri, 7 Aug 2020 22:20:54 +0200 Subject: [PATCH 23/36] Update ui_view_wav.cpp --- firmware/application/apps/ui_view_wav.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/firmware/application/apps/ui_view_wav.cpp b/firmware/application/apps/ui_view_wav.cpp index daa12a8b..0ec1dba6 100644 --- a/firmware/application/apps/ui_view_wav.cpp +++ b/firmware/application/apps/ui_view_wav.cpp @@ -81,15 +81,6 @@ void ViewWavView::load_wav(std::filesystem::path file_path) { int16_t sample; uint32_t average; - if (!wav_reader->open(file_path)) { - nav_.display_modal("Error", "Couldn't open file.", INFO, nullptr); - return; - } - - if ((wav_reader->channels() != 1) || (wav_reader->bits_per_sample() != 16)) { - nav_.display_modal("Error", "Wrong format.\nWav viewer only accepts\n16-bit mono files.", INFO, nullptr); - return; - } text_filename.set(file_path.filename().string()); auto ms_duration = wav_reader->ms_duration(); @@ -148,10 +139,18 @@ ViewWavView::ViewWavView( &field_cursor_b, &text_delta }); - + reset_controls(); button_open.on_select = [this, &nav](Button&) { auto open_view = nav.push(".WAV"); open_view->on_changed = [this](std::filesystem::path file_path) { + if (!wav_reader->open(file_path)) { + nav_.display_modal("Error", "Couldn't open file.", INFO, nullptr); + return; + } + if ((wav_reader->channels() != 1) || (wav_reader->bits_per_sample() != 16)) { + nav_.display_modal("Error", "Wrong format.\nWav viewer only accepts\n16-bit mono files.", INFO, nullptr); + return; + } load_wav(file_path); field_pos_seconds.focus(); }; @@ -176,7 +175,6 @@ ViewWavView::ViewWavView( refresh_measurements(); }; - reset_controls(); } void ViewWavView::focus() { From b300cc258e317f19168b407c7bd835bc46c49acc Mon Sep 17 00:00:00 2001 From: klockee Date: Sat, 8 Aug 2020 04:24:57 -0400 Subject: [PATCH 24/36] Cleaned up and tweaked --- firmware/application/ui_navigation.cpp | 51 ++++++-------------------- firmware/application/ui_navigation.hpp | 35 ++---------------- firmware/common/message.hpp | 9 ----- firmware/common/ui_widget.cpp | 29 +++++++++------ firmware/common/ui_widget.hpp | 18 ++++----- 5 files changed, 38 insertions(+), 104 deletions(-) diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index bd2dfce1..d3f403be 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -321,38 +321,14 @@ InformationView::InformationView( add_children({ &backdrop, &version, - &test, - &time + <ime }); - test.on_select = [this](ImageButton&){ - update_time(); - }; - - this->set_focusable(true); - this->set_visible(true); - time.set_focusable(true); version.set_style(&style_infobar); + ltime.set_style(&style_infobar); + ltime.set_seconds_enabled(true); + ltime.set_date_enabled(false); - time.on_select = [this](Button&) { - this->on_time(); - }; - - refresh(); -} - -void InformationView::refresh() { - update_time(); - set_dirty(); -} - -void InformationView::on_time(){ - update_time(); -} - -void InformationView::update_time() { - rtcGetTime(&RTCD1, &datetime); - time.set_text(to_string_datetime(datetime)); set_dirty(); } @@ -584,17 +560,18 @@ SystemView::SystemView( this->navigation_view.pop(); }; + add_child(&navigation_view); + navigation_view.set_parent_rect({ + { 0, status_view_height }, + { parent_rect.width(), static_cast(parent_rect.height() - status_view_height) } + }); + add_child(&info_view); info_view.set_parent_rect({ {0, 19 * 16}, { parent_rect.width(), info_view_height } }); - add_child(&navigation_view); - navigation_view.set_parent_rect({ - { 0, status_view_height }, - { parent_rect.width(), static_cast(parent_rect.height() - status_view_height - info_view_height) } - }); navigation_view.on_view_changed = [this](const View& new_view) { if(!this->navigation_view.is_top()){ @@ -602,7 +579,7 @@ SystemView::SystemView( } else{ add_child(&info_view); - this->info_view.update_time(); + //this->info_view.set_dirty(); } this->status_view.set_back_enabled(!this->navigation_view.is_top()); @@ -610,8 +587,6 @@ SystemView::SystemView( this->status_view.set_title(new_view.title()); this->status_view.set_dirty(); - - //this->info_view.focus(); }; @@ -640,10 +615,6 @@ Context& SystemView::context() const { return context_; } -void SystemView::on_tick_second() { - this->info_view.update_time(); -} - /* ***********************************************************************/ void BMPView::focus() { diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index d2f16281..0f1da9b3 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -211,54 +211,27 @@ private: class InformationView : public View { public: InformationView(NavigationView& nav); - void update_time(); - private: void refresh(); static constexpr auto version_string = "v1.1.1"; NavigationView& nav_; - void on_time(); - rtc::RTC datetime; Rectangle backdrop { - { 0, 0 * 16, 240, 16 }, - {33, 33, 33} + { 0, 0 * 16, 240, 16 }, + {33, 33, 33} }; Text version { {0, 0, 11 * 8, 16}, version_string }; - - ImageButton test { - {80, 0, 16, 16}, - &bitmap_icon_camera, - Color::white(), - Color::dark_grey() - }; - Button time { - {120, 0, 11 * 8, 16}, - "" - }; -/* LiveDateTime ltime { - {120, 0, 11 * 8, 16} + {176, 0, 8 * 8, 16} }; -*/ - - - - MessageHandlerRegistration message_handler_refresh { - Message::ID::InfoRefresh, - [this](const Message* const p) { - (void)p; - this->refresh(); - } - }; }; class BMPView : public View { @@ -314,8 +287,6 @@ public: Context& context() const override; private: - void on_tick_second(); - SystemStatusView status_view { navigation_view }; InformationView info_view { navigation_view }; NavigationView navigation_view { }; diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 12d0c068..eeecc9d0 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -111,7 +111,6 @@ public: AudioLevelReport = 51, CodedSquelch = 52, AudioSpectrum = 53, - InfoRefresh = 54, MAX }; @@ -235,14 +234,6 @@ public: } }; -class InfoRefreshMessage : public Message { -public: - constexpr InfoRefreshMessage( - ) : Message { ID::InfoRefresh } - { - } -}; - class AudioStatisticsMessage : public Message { public: constexpr AudioStatisticsMessage( diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 857221ca..1372410d 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -409,10 +409,17 @@ void Labels::paint(Painter& painter) { void LiveDateTime::on_tick_second() { rtcGetTime(&RTCD1, &datetime); + text = ""; - text = to_string_dec_uint(datetime.month(), 2, '0') + "/" + to_string_dec_uint(datetime.day(), 2, '0') + " " + - to_string_dec_uint(datetime.hour(), 2, '0') + ":" + to_string_dec_uint(datetime.minute(), 2, '0'); + if(date_enabled){ + text = to_string_dec_uint(datetime.month(), 2, '0') + "/" + to_string_dec_uint(datetime.day(), 2, '0') + " "; + } + text = text + to_string_dec_uint(datetime.hour(), 2, '0') + ":" + to_string_dec_uint(datetime.minute(), 2, '0'); + + if(seconds_enabled){ + text = text + ":" + to_string_dec_uint(datetime.second(), 2, '0'); + } set_dirty(); } @@ -444,6 +451,14 @@ void LiveDateTime::paint(Painter& painter) { ); } +void LiveDateTime::set_date_enabled(bool new_value){ + this->date_enabled = new_value; +} + +void LiveDateTime::set_seconds_enabled(bool new_value) { + this->seconds_enabled = new_value; +} + /* BigFrequency **********************************************************/ BigFrequency::BigFrequency( @@ -1046,16 +1061,6 @@ bool NewButton::on_touch(const TouchEvent event) { return false; } } -/* DateTimeButton ********************************************************/ - -DateTimeButton::DateTimeButton( - Rect parent_rect, - std::string text -) : Button { parent_rect , text_} -{ - set_focusable(true); -} - /* Image *****************************************************************/ diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 71621e82..5b592c48 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -243,7 +243,10 @@ public: ~LiveDateTime(); void paint(Painter& painter) override; - + + void set_seconds_enabled(bool new_value); + void set_date_enabled(bool new_value); + std::string& string() { return text; } @@ -251,6 +254,9 @@ public: private: void on_tick_second(); + bool date_enabled = true; + bool seconds_enabled = false; + rtc::RTC datetime { }; SignalToken signal_token_tick_second { }; std::string text { }; @@ -428,16 +434,6 @@ private: const Bitmap* bitmap_; }; -class DateTimeButton : public Button { -public: - DateTimeButton(Rect parent_rect, std::string text); - void paint(Painter& painter) override; -private: - std::string text_; - void on_tick_second(); - -}; - class Image : public Widget { public: Image(); From 90d2dbb49ce32c1359f1a39e894c4fa09b8da972 Mon Sep 17 00:00:00 2001 From: klockee Date: Sat, 8 Aug 2020 04:48:29 -0400 Subject: [PATCH 25/36] More cleanup --- firmware/application/ui_navigation.cpp | 1 - firmware/application/ui_navigation.hpp | 3 --- 2 files changed, 4 deletions(-) diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index d3f403be..42b2fdb3 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -579,7 +579,6 @@ SystemView::SystemView( } else{ add_child(&info_view); - //this->info_view.set_dirty(); } this->status_view.set_back_enabled(!this->navigation_view.is_top()); diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 0f1da9b3..e626c115 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -213,12 +213,9 @@ public: InformationView(NavigationView& nav); private: - void refresh(); static constexpr auto version_string = "v1.1.1"; NavigationView& nav_; - rtc::RTC datetime; - Rectangle backdrop { { 0, 0 * 16, 240, 16 }, {33, 33, 33} From c7082455c10bc84490fcfa5128a77fd27539e04d Mon Sep 17 00:00:00 2001 From: Erwin Ried <1091420+eried@users.noreply.github.com> Date: Sat, 8 Aug 2020 23:06:53 +0200 Subject: [PATCH 26/36] Super simple about --- firmware/application/CMakeLists.txt | 2 +- firmware/application/apps/ui_about_simple.cpp | 75 +++++++++++++++++++ firmware/application/apps/ui_about_simple.hpp | 40 ++++++++++ firmware/application/ui_navigation.cpp | 2 +- 4 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 firmware/application/apps/ui_about_simple.cpp create mode 100644 firmware/application/apps/ui_about_simple.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index dc9e41d5..f551f676 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -214,7 +214,7 @@ set(CPPSRC ui/ui_tabview.cpp ui/ui_textentry.cpp ui/ui_transmitter.cpp - apps/ui_about.cpp + apps/ui_about_simple.cpp apps/ui_adsb_rx.cpp apps/ui_adsb_tx.cpp apps/ui_afsk_rx.cpp diff --git a/firmware/application/apps/ui_about_simple.cpp b/firmware/application/apps/ui_about_simple.cpp new file mode 100644 index 00000000..2c6f796e --- /dev/null +++ b/firmware/application/apps/ui_about_simple.cpp @@ -0,0 +1,75 @@ +#include "ui_about_simple.hpp" + +namespace ui +{ + AboutView::AboutView(NavigationView &nav) + { + add_children({&console, &button_ok}); + + button_ok.on_select = [&nav](Button &) { + nav.pop(); + }; + + console.writeln("\x1B\x07List of contributors:\x1B\x10"); + console.writeln(""); + } + + void AboutView::update() + { + if (++timer > 200) + { + timer = 0; + + switch (++frame) + { + case 1: + // TODO: Generate this automatically from github + // https://github.com/eried/portapack-mayhem/graphs/contributors?to=2022-01-01&from=2020-04-12&type=c + console.writeln("\x1B\x06Mayhem:\x1B\x10"); + console.writeln("eried,euquiq,gregoryfenton"); + console.writeln("johnelder,jwetzell,nnemanjan00"); + console.writeln("N0vaPixel,klockee,jamesshao8"); + console.writeln(""); + break; + + case 2: + // https://github.com/eried/portapack-mayhem/graphs/contributors?to=2020-04-12&from=2015-07-31&type=c + console.writeln("\x1B\x06Havoc:\x1B\x10"); + console.writeln("furrtek,mrmookie,notpike"); + console.writeln("mjwaxios,ImDroided,Giorgiofox"); + console.writeln("F4GEV,z4ziggy,xmycroftx"); + console.writeln("troussos,silascutler"); + console.writeln("nickbouwhuis,msoose,leres"); + console.writeln("joakar,dhoetger,clem-42"); + console.writeln("brianlechthaler,ZeroChaos-..."); + console.writeln(""); + break; + + case 3: + // https://github.com/eried/portapack-mayhem/graphs/contributors?from=2014-07-05&to=2015-07-31&type=c + console.writeln("\x1B\x06PortaPack:\x1B\x10"); + console.writeln("jboone,argilo"); + console.writeln(""); + break; + + case 4: + // https://github.com/mossmann/hackrf/graphs/contributors + console.writeln("\x1B\x06HackRF:\x1B\x10"); + console.writeln("mossmann,dominicgs,bvernoux"); + console.writeln("bgamari,schneider42,miek"); + console.writeln("willcode,hessu,Sec42"); + console.writeln("yhetti,ckuethe,smunaut"); + console.writeln("wishi,mrbubble62,scateu..."); + console.writeln(""); + frame = 0; // Loop + break; + } + } + } + + void AboutView::focus() + { + button_ok.focus(); + } + +} /* namespace ui */ \ No newline at end of file diff --git a/firmware/application/apps/ui_about_simple.hpp b/firmware/application/apps/ui_about_simple.hpp new file mode 100644 index 00000000..a0590119 --- /dev/null +++ b/firmware/application/apps/ui_about_simple.hpp @@ -0,0 +1,40 @@ +#ifndef __UI_ABOUT_SIMPLE_H__ +#define __UI_ABOUT_SIMPLE_H__ + +#include "ui_widget.hpp" +#include "ui_navigation.hpp" +#include "ui_font_fixed_8x16.hpp" + +#include + +namespace ui +{ + class AboutView : public View + { + public: + AboutView(NavigationView &nav); + void focus() override; + std::string title() const override { return "About"; }; + int32_t timer{180}; + short frame{0}; + + private: + void update(); + + Console console{ + {0, 10, 240, 240}}; + + Button button_ok{ + {240/3, 270, 240/3, 24}, + "OK", + }; + + MessageHandlerRegistration message_handler_update{ + Message::ID::DisplayFrameSync, + [this](const Message *const) { + this->update(); + }}; + }; +} // namespace ui + +#endif /*__UI_ABOUT_SIMPLE_H__*/ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 42b2fdb3..62092b81 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -30,7 +30,7 @@ #include "bmp_modal_warning.hpp" #include "portapack_persistent_memory.hpp" -#include "ui_about.hpp" +#include "ui_about_simple.hpp" #include "ui_adsb_rx.hpp" #include "ui_adsb_tx.hpp" #include "ui_afsk_rx.hpp" From bbae5047d19d5cc8b7727b20ea80db7f7b5b2c3f Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Mon, 10 Aug 2020 22:55:20 -0500 Subject: [PATCH 27/36] Fix ADSB heading math and add heading to ADSB log --- firmware/application/apps/ui_adsb_rx.cpp | 3 +++ firmware/common/adsb.cpp | 11 +++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index 52c55756..bd158620 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -244,6 +244,9 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { } } else if(msg_type == 19 && msg_sub >= 1 && msg_sub <= 4){ entry.set_frame_velo(frame); + logentry += "Type:" + to_string_dec_uint(msg_sub) + + " Hdg:" + to_string_dec_uint(entry.velo.heading) + + " Spd: "+ to_string_dec_int(entry.velo.speed); if (send_updates) details_view->update(entry); } diff --git a/firmware/common/adsb.cpp b/firmware/common/adsb.cpp index 6a4412fe..3f8f4c23 100644 --- a/firmware/common/adsb.cpp +++ b/firmware/common/adsb.cpp @@ -332,16 +332,19 @@ adsb_vel decode_frame_velo(ADSBFrame& frame){ } if(velo_type == 1 || velo_type == 2){ //Ground Speed - int32_t velo_ew = (((frame_data[5] & 0x03) << 8) | frame_data[6]) - 1; - int32_t velo_ns = ((frame_data[7] & 0x7f) << 3) | ((frame_data[8]) >> 5) - 1; + int32_t raw_ew = ((frame_data[5] & 0x03) << 8) | frame_data[6]; + int32_t velo_ew = raw_ew - 1; //velocities are all offset by one (this is part of the spec) + + int32_t raw_ns = ((frame_data[7] & 0x7f) << 3) | (frame_data[8] >> 5); + int32_t velo_ns = raw_ns - 1; if (velo_type == 2){ // supersonic indicator so multiply by 4 velo_ew = velo_ew << 2; velo_ns = velo_ns << 2; } - if((frame_data[5]&4) >> 2) velo_ew *= -1; //check ew direction sign - if((frame_data[7]&0x80) >> 7) velo_ns *= -1; //check ns direction sign + if(frame_data[5]&0x04) velo_ew *= -1; //check ew direction sign + if(frame_data[7]&0x80) velo_ns *= -1; //check ns direction sign velo.speed = sqrt(velo_ns*velo_ns + velo_ew*velo_ew); From 13ba70f6c574ecc9bdd2481a81c6a1a2016a2c3f Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Tue, 11 Aug 2020 11:26:36 -0500 Subject: [PATCH 28/36] Fix negative altitude on DetailsView --- firmware/application/apps/ui_adsb_rx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/application/apps/ui_adsb_rx.cpp b/firmware/application/apps/ui_adsb_rx.cpp index bd158620..ba080481 100644 --- a/firmware/application/apps/ui_adsb_rx.cpp +++ b/firmware/application/apps/ui_adsb_rx.cpp @@ -230,7 +230,7 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) { entry.set_frame_pos(frame, raw_data[6] & 4); if (entry.pos.valid) { - str_info = "Alt:" + to_string_dec_uint(entry.pos.altitude) + + str_info = "Alt:" + to_string_dec_int(entry.pos.altitude) + " Lat:" + to_string_dec_int(entry.pos.latitude) + "." + to_string_dec_int((int)abs(entry.pos.latitude * 1000) % 100, 2, '0') + " Lon:" + to_string_dec_int(entry.pos.longitude) + From f6437adc60af56d0c9f94f8d8d66a593f0d2f1ae Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Tue, 11 Aug 2020 11:26:54 -0500 Subject: [PATCH 29/36] Reorganixe ADSBDetailsView --- firmware/application/apps/ui_adsb_rx.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firmware/application/apps/ui_adsb_rx.hpp b/firmware/application/apps/ui_adsb_rx.hpp index 55d590bd..dd7adb5a 100644 --- a/firmware/application/apps/ui_adsb_rx.hpp +++ b/firmware/application/apps/ui_adsb_rx.hpp @@ -150,8 +150,8 @@ private: { { 0 * 8, 2 * 16 }, "Last seen:", Color::light_grey() }, { { 0 * 8, 3 * 16 }, "Airline:", Color::light_grey() }, { { 0 * 8, 5 * 16 }, "Country:", Color::light_grey() }, - { { 0 * 8, 12 * 16 }, "Even position frame:", Color::light_grey() }, - { { 0 * 8, 14 * 16 }, "Odd position frame:", Color::light_grey() } + { { 0 * 8, 13 * 16 }, "Even position frame:", Color::light_grey() }, + { { 0 * 8, 15 * 16 }, "Odd position frame:", Color::light_grey() } }; Text text_callsign { @@ -185,16 +185,16 @@ private: }; Text text_frame_pos_even { - { 0 * 8, 13 * 16, 30 * 8, 16 }, + { 0 * 8, 14 * 16, 30 * 8, 16 }, "-" }; Text text_frame_pos_odd { - { 0 * 8, 15 * 16, 30 * 8, 16 }, + { 0 * 8, 16 * 16, 30 * 8, 16 }, "-" }; Button button_see_map { - { 8 * 8, 8 * 16, 14 * 8, 3 * 16 }, + { 8 * 8, 9 * 16, 14 * 8, 3 * 16 }, "See on map" }; }; From c7b0fbc35914ded0236f6c5154238760dd80ab87 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Fri, 14 Aug 2020 15:51:12 -0300 Subject: [PATCH 30/36] Radiosonde-app-Vaisala-rs41-decoding Added the Vaisala RS41 data packet decoding. Changed the default freq from 402.0 to 402.7 Mhz, since it is more popular freq. Lowered the frequency stepping, so it is easier to fine-tune the exact freq center, if needed. Sonde's Serial ID is passed into the VIEW MAP, so now the sonde is labelled on the map. --- firmware/application/apps/ui_sonde.cpp | 27 +++-- firmware/application/apps/ui_sonde.hpp | 7 +- firmware/baseband/proc_sonde.hpp | 3 +- firmware/common/sonde_packet.cpp | 132 +++++++++++++++++++------ firmware/common/sonde_packet.hpp | 19 ++-- 5 files changed, 132 insertions(+), 56 deletions(-) diff --git a/firmware/application/apps/ui_sonde.cpp b/firmware/application/apps/ui_sonde.cpp index 16506b3f..e61139ac 100644 --- a/firmware/application/apps/ui_sonde.cpp +++ b/firmware/application/apps/ui_sonde.cpp @@ -54,7 +54,7 @@ SondeView::SondeView(NavigationView& nav) { }); field_frequency.set_value(target_frequency_); - field_frequency.set_step(10000); + field_frequency.set_step(500); //euquiq: was 10000, but we are using this for fine-tunning field_frequency.on_change = [this](rf::Frequency f) { set_target_frequency(f); field_frequency.set_value(f); @@ -86,12 +86,12 @@ SondeView::SondeView(NavigationView& nav) { button_see_map.on_select = [this, &nav](Button&) { nav.push( - "", - altitude, + sonde_id, + gps_info.alt, GeoPos::alt_unit::METERS, - latitude, - longitude, - 0); + gps_info.lat, + gps_info.lon, + 999); //set a dummy heading out of range to draw a cross...probably not ideal? }; logger = std::make_unique(); @@ -113,16 +113,15 @@ void SondeView::on_packet(const sonde::Packet& packet) { //const auto hex_formatted = packet.symbols_formatted(); text_signature.set(packet.type_string()); - text_serial.set(packet.serial_number()); + sonde_id = packet.serial_number(); //used also as tag on the geomap + text_serial.set(sonde_id); text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 3) + "V"); + + gps_info = packet.get_GPS_data(); - altitude = packet.GPS_altitude(); - latitude = packet.GPS_latitude(); - longitude = packet.GPS_longitude(); - - geopos.set_altitude(altitude); - geopos.set_lat(latitude); - geopos.set_lon(longitude); + geopos.set_altitude(gps_info.alt); + geopos.set_lat(gps_info.lat); + geopos.set_lon(gps_info.lon); if (logger && logging) { logger->on_packet(packet); diff --git a/firmware/application/apps/ui_sonde.hpp b/firmware/application/apps/ui_sonde.hpp index 5dc7fe86..9e7743b1 100644 --- a/firmware/application/apps/ui_sonde.hpp +++ b/firmware/application/apps/ui_sonde.hpp @@ -65,11 +65,10 @@ public: private: std::unique_ptr logger { }; - uint32_t target_frequency_ { 402000000 }; + uint32_t target_frequency_ { 402700000 }; bool logging { false }; - int32_t altitude { 0 }; - float latitude { 0 }; - float longitude { 0 }; + sonde::GPS_data gps_info; + std::string sonde_id; Labels labels { { { 0 * 8, 2 * 16 }, "Signature:", Color::light_grey() }, diff --git a/firmware/baseband/proc_sonde.hpp b/firmware/baseband/proc_sonde.hpp index de15fff0..92e53580 100644 --- a/firmware/baseband/proc_sonde.hpp +++ b/firmware/baseband/proc_sonde.hpp @@ -140,7 +140,8 @@ private: } }; PacketBuilder packet_builder_fsk_4800_Vaisala { - { 0b00001000011011010101001110001000, 32, 1 }, + { 0b00001000011011010101001110001000, 32, 1 }, //euquiq Header detects 4 of 8 bytes 0x10B6CA11 /this is in raw format) (these bits are not passed at the beginning of packet) + //{ 0b0000100001101101010100111000100001000100011010010100100000011111, 64, 1 }, //euquiq whole header detection would be 8 bytes. { }, { 320 * 8 }, [this](const baseband::Packet& packet) { diff --git a/firmware/common/sonde_packet.cpp b/firmware/common/sonde_packet.cpp index 22f54793..b1c22430 100644 --- a/firmware/common/sonde_packet.cpp +++ b/firmware/common/sonde_packet.cpp @@ -22,9 +22,25 @@ #include "sonde_packet.hpp" #include "string_format.hpp" +#include +//#include namespace sonde { +//Defines for Vaisala RS41, from https://github.com/rs1729/RS/blob/master/rs41/rs41sg.c +#define MASK_LEN 64 +#define pos_FrameNb 0x37 //0x03B // 2 byte +#define pos_SondeID 0x39 //0x03D // 8 byte +#define pos_Voltage 0x041 //0x045 // 3 bytes (but first one is the important one) voltage x 10 ie: 26 = 2.6v +#define pos_CalData 0x04E //0x052 // 1 byte, counter 0x00..0x32 +#define pos_GPSweek 0x091 //0x095 // 2 byte +#define pos_GPSTOW 0x093 //0x097 // 4 byte +#define pos_GPSecefX 0x110 //0x114 // 4 byte +#define pos_GPSecefY 0x114 //0x118 // 4 byte (not actually used since Y and Z are following X, and grabbed in that same loop) +#define pos_GPSecefZ 0x118 //0x11C // 4 byte (same as Y) + +#define PI 3.1415926535897932384626433832795 //3.1416 //(3.1415926535897932384626433832795) + Packet::Packet( const baseband::Packet& packet, const Type type @@ -60,37 +76,65 @@ Packet::Type Packet::type() const { return type_; } -/*uint8_t Packet::vaisala_descramble(const uint32_t pos) { - return reader_raw.read(pos * 8, 8) ^ vaisala_mask[pos & 63]; -};*/ +//euquiq here: +//RS41SG 320 bits header, 320bytes frame (or more if it is an "extended frame") +//The raw data is xor-scrambled with the values in the 64 bytes vaisala_mask (see.hpp) -uint32_t Packet::GPS_altitude() const { - if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2)) - return (reader_bi_m.read(22 * 8, 32) / 1000) - 48; - else if (type_ == Type::Vaisala_RS41_SG) { - /*uint32_t altitude_ecef = 0; - for (uint32_t i = 0; i < 4; i++) - altitude_ecef = (altitude_ecef << 8) + vaisala_descramble(0x11C + i);*/ - // TODO: and a bunch of maths (see ecef2elli() from RS1729) - return 0; - } else - return 0; // Unknown -} -float Packet::GPS_latitude() const { - if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2)) - return reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0); - //else if (type_ == Type::Vaisala_RS41_SG) - // return vaisala_descramble(); - else - return 0; // Unknown -} +uint8_t Packet::vaisala_descramble(const uint32_t pos) const { + //return reader_raw.read(pos * 8, 8) ^ vaisala_mask[pos & 63]; + // packet_[i]; its a bit; packet_.size the total (should be 2560 bits) + uint8_t value = 0; + for (uint8_t i = 0; i < 8; i++) + value = (value << 1) | packet_[(pos * 8) + (7 -i)]; //get the byte from the bits collection -float Packet::GPS_longitude() const { - if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2)) - return reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0); - else - return 0; // Unknown + //packetReader reader { packet_ }; //This works just as above. + //value = reader.read(pos * 8,8); + //shift pos because first 4 bytes are consumed by proc_sonde in finding the vaisala signature + uint32_t mask_pos = pos + 4; + value = value ^ vaisala_mask[mask_pos % MASK_LEN]; //descramble with the xor pseudorandom table + return value; +}; + +GPS_data Packet::get_GPS_data() const { + GPS_data result; + if ((type_ == Type::Meteomodem_M10) || (type_ == Type::Meteomodem_M2K2)) { + + result.alt = (reader_bi_m.read(22 * 8, 32) / 1000) - 48; + result.lat = reader_bi_m.read(14 * 8, 32) / ((1ULL << 32) / 360.0); + result.lon = reader_bi_m.read(18 * 8, 32) / ((1ULL << 32) / 360.0); + + } else if (type_ == Type::Vaisala_RS41_SG) { + + uint8_t XYZ_bytes[4]; + int32_t XYZ; // 32bit + double_t X[3]; + for (int32_t k = 0; k < 3; k++) { //Get X,Y,Z ECEF position from GPS + for (int32_t i = 0; i < 4; i++) //each one is 4 bytes (32 bits) + XYZ_bytes[i] = vaisala_descramble(pos_GPSecefX + (4*k) + i); + memcpy(&XYZ, XYZ_bytes, 4); + X[k] = XYZ / 100.0; + } + + double_t a = 6378137.0; + double_t b = 6356752.31424518; + double_t e = sqrt( (a*a - b*b) / (a*a) ); + double_t ee = sqrt( (a*a - b*b) / (b*b) ); + + double_t lam = atan2( X[1] , X[0] ); + double_t p = sqrt( X[0]*X[0] + X[1]*X[1] ); + double_t t = atan2( X[2]*a , p*b ); + double_t phi = atan2( X[2] + ee*ee * b * sin(t)*sin(t)*sin(t) , + p - e*e * a * cos(t)*cos(t)*cos(t) ); + + double_t R = a / sqrt( 1 - e*e*sin(phi)*sin(phi) ); + + result.alt = p / cos(phi) - R; + result.lat = phi*180/PI; + result.lon = lam*180/PI; + + } + return result; } uint32_t Packet::battery_voltage() const { @@ -98,8 +142,13 @@ uint32_t Packet::battery_voltage() const { return (reader_bi_m.read(69 * 8, 8) + (reader_bi_m.read(70 * 8, 8) << 8)) * 1000 / 150; else if (type_ == Type::Meteomodem_M2K2) return reader_bi_m.read(69 * 8, 8) * 66; // Actually 65.8 - else + else if (type_ == Type::Vaisala_RS41_SG) { + uint32_t voltage = vaisala_descramble(pos_Voltage) * 100; //byte 69 = voltage * 10 (check if this value needs to be multiplied) + return voltage; + } + else { return 0; // Unknown + } } std::string Packet::type_string() const { @@ -127,12 +176,33 @@ std::string Packet::serial_number() const { to_string_dec_uint(reader_bi_m.read(93 * 8 + 24, 3), 1) + to_string_dec_uint(reader_bi_m.read(93 * 8 + 27, 13), 4, '0'); - } else + } else if(type() == Type::Vaisala_RS41_SG) { + std::string serial_id = ""; + uint8_t achar; + for (uint8_t i=0; i<8; i++) { //euquiq: Serial ID is 8 bytes long, each byte a char + achar = vaisala_descramble(pos_SondeID + i); + if (achar < 32 || achar > 126) return "?"; //Maybe there are ids with less than 8 bytes and this is not OK. + serial_id += (char)achar; + } + return serial_id; + } else return "?"; } FormattedSymbols Packet::symbols_formatted() const { - return format_symbols(decoder_); + if (type() == Type::Vaisala_RS41_SG) { //Euquiq: now we distinguish different types + uint32_t bytes = packet_.size() / 8; //Need the byte amount, which if full, it SHOULD be 320 size() should return 2560 + std::string hex_data; + std::string hex_error; + hex_data.reserve(bytes * 2); //2 hexa chars per byte + hex_error.reserve(1); + for (uint32_t i=0; i < bytes; i++) //log will show the packet starting on the last 4 bytes from signature 93DF1A60 + hex_data += to_string_hex(vaisala_descramble(i),2); + return { hex_data, hex_error }; + + } else { + return format_symbols(decoder_); + } } bool Packet::crc_ok() const { diff --git a/firmware/common/sonde_packet.hpp b/firmware/common/sonde_packet.hpp index 4ecbc08a..746d42e3 100644 --- a/firmware/common/sonde_packet.hpp +++ b/firmware/common/sonde_packet.hpp @@ -32,6 +32,12 @@ namespace sonde { + struct GPS_data { + uint32_t alt { 0 }; + float lat { 0 }; + float lon { 0 }; + }; + class Packet { public: enum class Type : uint32_t { @@ -41,7 +47,7 @@ public: Meteomodem_M2K2 = 3, Vaisala_RS41_SG = 4, }; - + Packet(const baseband::Packet& packet, const Type type); size_t length() const; @@ -56,9 +62,7 @@ public: std::string serial_number() const; uint32_t battery_voltage() const; - uint32_t GPS_altitude() const; - float GPS_latitude() const; - float GPS_longitude() const; + GPS_data get_GPS_data() const; FormattedSymbols symbols_formatted() const; @@ -75,17 +79,20 @@ private: 0xD0, 0xBC, 0xB4, 0xB6, 0x06, 0xAA, 0xF4, 0x23, 0x78, 0x6E, 0x3B, 0xAE, 0xBF, 0x7B, 0x4C, 0xC1 }; + + GPS_data ecef_to_gps() const; - //uint8_t vaisala_descramble(const uint32_t pos); + uint8_t vaisala_descramble(uint32_t pos) const; const baseband::Packet packet_; const BiphaseMDecoder decoder_; const FieldReader reader_bi_m; Type type_; + using packetReader = FieldReader; //baseband::Packet instead of BiphaseMDecoder bool crc_ok_M10() const; }; } /* namespace sonde */ -#endif/*__SONDE_PACKET_H__*/ +#endif/*__SONDE_PACKET_H__*/ \ No newline at end of file From 27a549dac76d42750634a79128893c3f33f73f07 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Fri, 14 Aug 2020 15:46:45 -0500 Subject: [PATCH 31/36] allow printing tag even without a valid heading --- firmware/application/ui/ui_geomap.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/firmware/application/ui/ui_geomap.cpp b/firmware/application/ui/ui_geomap.cpp index d793c223..91fcb545 100644 --- a/firmware/application/ui/ui_geomap.cpp +++ b/firmware/application/ui/ui_geomap.cpp @@ -163,7 +163,10 @@ void GeoMap::paint(Painter& painter) { prev_x_pos = x_pos; prev_y_pos = y_pos; } - + //center tag above point + if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces + painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_); + } if (mode_ == PROMPT) { // Cross display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red()); @@ -171,10 +174,6 @@ void GeoMap::paint(Painter& painter) { } else if (angle_ < 360){ //if we have a valid angle draw bearing draw_bearing(r.center(), angle_, 10, Color::red()); - //center tag above bearing - if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces - painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_); - } } else { //draw a small cross From 7b19c0a35fcd907bfe119329bfbc523b94279eb8 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Fri, 14 Aug 2020 20:11:24 -0300 Subject: [PATCH 32/36] Update ui_navigation.cpp Now the Sonde App icon is in green: All sondes in the original proposal are working! --- firmware/application/ui_navigation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index e4ee6490..ee46bc61 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -416,7 +416,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { { "Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, { "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push(); } }, - { "Radiosnde", ui::Color::yellow(), &bitmap_icon_sonde, [&nav](){ nav.push(); } }, + { "Radiosnde", ui::Color::green(), &bitmap_icon_sonde, [&nav](){ nav.push(); } }, { "TPMS Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.push(); } }, /*{ "APRS", ui::Color::dark_grey(), &bitmap_icon_aprs, [&nav](){ nav.push(); } }, { "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push(); } }, From 004fddf4dec1914a065719b44846e07ef4ffb758 Mon Sep 17 00:00:00 2001 From: Erwin Ried <1091420+eried@users.noreply.github.com> Date: Sat, 15 Aug 2020 03:20:55 +0200 Subject: [PATCH 33/36] Skip splash when pressing titlebar, if enabled --- firmware/application/ui_navigation.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 62092b81..19cbf01d 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -303,7 +303,10 @@ void SystemStatusView::on_camera() { } void SystemStatusView::on_title() { - nav_.push(); + if(nav_.is_top()) + nav_.push(); + else + nav_.pop(); } /* Information View *****************************************************/ From 8251cf7dbb0be9b9e12d9c8afa2677315b777b4b Mon Sep 17 00:00:00 2001 From: Erwin Ried <1091420+eried@users.noreply.github.com> Date: Sat, 15 Aug 2020 15:46:53 +0200 Subject: [PATCH 34/36] Hide the seconds at the beginning --- firmware/common/ui_widget.cpp | 14 ++++++++++++-- firmware/common/ui_widget.hpp | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 1372410d..43669923 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -418,7 +418,16 @@ void LiveDateTime::on_tick_second() { text = text + to_string_dec_uint(datetime.hour(), 2, '0') + ":" + to_string_dec_uint(datetime.minute(), 2, '0'); if(seconds_enabled){ - text = text + ":" + to_string_dec_uint(datetime.second(), 2, '0'); + text += ":"; + + if(init_delay==0) + text += to_string_dec_uint(datetime.second(), 2, '0'); + else + { + // Placeholder while the seconds are not updated + text += "XX"; + init_delay--; + } } set_dirty(); } @@ -640,7 +649,8 @@ void Console::write(std::string message) { void Console::writeln(std::string message) { write(message); - crlf(); + //crlf(); + write("\n"); } void Console::paint(Painter&) { diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 5b592c48..51de4277 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -254,6 +254,7 @@ public: private: void on_tick_second(); + uint16_t init_delay = 4; bool date_enabled = true; bool seconds_enabled = false; From ccef06c0888fe02b92afcdc912748095d16aa339 Mon Sep 17 00:00:00 2001 From: Erwin Ried <1091420+eried@users.noreply.github.com> Date: Sat, 15 Aug 2020 15:47:23 +0200 Subject: [PATCH 35/36] Margin for footer elements --- firmware/application/ui_navigation.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index e626c115..02aaf133 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -222,12 +222,12 @@ private: }; Text version { - {0, 0, 11 * 8, 16}, + {2, 0, 11 * 8, 16}, version_string }; LiveDateTime ltime { - {176, 0, 8 * 8, 16} + {174, 0, 8 * 8, 16} }; }; From 303d9c8a9daa87cdd481f3eb2d70dada5e6f0124 Mon Sep 17 00:00:00 2001 From: Erwin Ried <1091420+eried@users.noreply.github.com> Date: Sat, 15 Aug 2020 16:01:15 +0200 Subject: [PATCH 36/36] Version bump --- firmware/application/ui_navigation.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 02aaf133..6d95eefe 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -213,7 +213,7 @@ public: InformationView(NavigationView& nav); private: - static constexpr auto version_string = "v1.1.1"; + static constexpr auto version_string = "v1.2"; NavigationView& nav_; Rectangle backdrop {