diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 254ed8196d82118f6d07fe7079c64561765d0850..ae1ab6e8db709dbb4bfa376b14ed842f82c1b84e 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -330,7 +330,7 @@ set(TOPT "-mthumb -DTHUMB") set(CWARN "-Wall -Wextra -Wstrict-prototypes") # Define C++ warning options here -set(CPPWARN "-Wall -Wextra") +set(CPPWARN "-Wall -Wextra -Wno-psabi") # # Compiler settings diff --git a/firmware/application/apps/acars_app.cpp b/firmware/application/apps/acars_app.cpp index 3dc54bf969be10161ed00fc08b6331a4e03b5f91..108a673a2a624b0e59eaa2f1fdd8a3caf5408225 100644 --- a/firmware/application/apps/acars_app.cpp +++ b/firmware/application/apps/acars_app.cpp @@ -32,11 +32,16 @@ using namespace acars; #include "utility.hpp" void ACARSLogger::log_raw_data(const acars::Packet& packet, const uint32_t frequency) { - std::string entry = "Raw: F:" + to_string_dec_uint(frequency) + "Hz "; + (void)frequency; + std::string entry { }; //= "Raw: F:" + to_string_dec_uint(frequency) + "Hz "; + entry.reserve(256); // Raw hex dump of all the bytes - for (size_t c = 0; c < packet.length(); c += 8) - entry += to_string_hex(packet.read(c, 8), 8) + " "; + //for (size_t c = 0; c < packet.length(); c += 32) + // entry += to_string_hex(packet.read(c, 32), 8) + " "; + + for (size_t c = 0; c < 256; c += 32) + entry += to_string_bin(packet.read(c, 32), 32); log_file.write_entry(packet.received_at(), entry); } @@ -107,24 +112,25 @@ void ACARSAppView::focus() { field_frequency.focus(); } -// Useless ? -void ACARSAppView::set_parent_rect(const Rect new_parent_rect) { - View::set_parent_rect(new_parent_rect); -} - void ACARSAppView::on_packet(const acars::Packet& packet) { - std::string alphanum_text = ""; + std::string console_info; - if (!packet.is_valid()) - console.writeln("\n\x1B\x0INVALID PACKET"); - else { - std::string console_info; + /*if (!packet.is_valid()) { + console_info = to_string_datetime(packet.received_at(), HMS); + console_info += " INVALID"; - console_info = "\n" + to_string_datetime(packet.received_at(), HM); - console_info += " REG:" + packet.registration_number(); + console.writeln(console_info); + } else { + console_info = to_string_datetime(packet.received_at(), HMS); + console_info += ":" + to_string_bin(packet.read(0, 32), 32); + //console_info += " REG:" + packet.registration_number(); - console.write(console_info); - } + console.writeln(console_info); + }*/ + + packet_counter++; + if (packet_counter % 10 == 0) + console.writeln("Block #" + to_string_dec_uint(packet_counter)); // Log raw data whatever it contains if (logger && logging) diff --git a/firmware/application/apps/acars_app.hpp b/firmware/application/apps/acars_app.hpp index d6e5f2e71b3a86ebf0f5a7bef6e5ff13a874c195..08fc7f76aab77ca739beb225c25b6e9f7e98b79c 100644 --- a/firmware/application/apps/acars_app.hpp +++ b/firmware/application/apps/acars_app.hpp @@ -51,13 +51,13 @@ public: ACARSAppView(NavigationView& nav); ~ACARSAppView(); - void set_parent_rect(const Rect new_parent_rect) override; void focus() override; - std::string title() const override { return "ACARS RX"; }; + std::string title() const override { return "ACARS (WIP)"; }; private: bool logging { false }; + uint32_t packet_counter { 0 }; RFAmpField field_rf_amp { { 13 * 8, 0 * 16 } @@ -86,7 +86,7 @@ private: }; Console console { - { 0, 4 * 16, 240, 240 } + { 0, 3 * 16, 240, 256 } }; std::unique_ptr<ACARSLogger> logger { }; diff --git a/firmware/application/apps/analog_audio_app.cpp b/firmware/application/apps/analog_audio_app.cpp index a4d9ed12eaa2f0dfc21a896e088ea769fd9f2f25..5bca40f4693328ee3c4c49d97fe28aad6c9665fb 100644 --- a/firmware/application/apps/analog_audio_app.cpp +++ b/firmware/application/apps/analog_audio_app.cpp @@ -86,8 +86,7 @@ NBFMOptionsView::NBFMOptionsView( /* AnalogAudioView *******************************************************/ AnalogAudioView::AnalogAudioView( - NavigationView& nav, - bool eos + NavigationView& nav ) : nav_ (nav) { add_children({ diff --git a/firmware/application/apps/analog_audio_app.hpp b/firmware/application/apps/analog_audio_app.hpp index 1375c24cc64de9d837416ef2dce8e6526604c763..993e611e0ce9b535326ecf5fb103972672c61f8e 100644 --- a/firmware/application/apps/analog_audio_app.hpp +++ b/firmware/application/apps/analog_audio_app.hpp @@ -96,7 +96,7 @@ private: class AnalogAudioView : public View { public: - AnalogAudioView(NavigationView& nav, bool eos); + AnalogAudioView(NavigationView& nav); ~AnalogAudioView(); void on_hide() override; diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 9ca42e42bfec68f1d0e81207d7eb66752cc59e1c..0b788a8f2ddb7b12e621e7b8a92a7c76a96a1368 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -51,26 +51,26 @@ public: private: Labels labels { - { { 8 * 8, 9 * 16 }, "/ / : :", Color::light_grey() }, - { { 4 * 8, 11 * 16 }, "YYYY/MM/DD HH:MM:SS", Color::light_grey() } + { { 6 * 8, 7 * 16 }, "YYYY/MM/DD HH:MM:SS", Color::grey() }, + { { 10 * 8, 9 * 16 }, "/ / : :", Color::light_grey() } }; NumberField field_year { - { 4 * 8, 9 * 16 }, + { 6 * 8, 9 * 16 }, 4, { 2015, 2099 }, 1, '0', }; NumberField field_month { - { 9 * 8, 9 * 16 }, + { 11 * 8, 9 * 16 }, 2, { 1, 12 }, 1, '0', }; NumberField field_day { - { 12 * 8, 9 * 16 }, + { 14 * 8, 9 * 16 }, 2, { 1, 31 }, 1, @@ -78,21 +78,21 @@ private: }; NumberField field_hour { - { 15 * 8, 9 * 16 }, + { 17 * 8, 9 * 16 }, 2, { 0, 23 }, 1, '0', }; NumberField field_minute { - { 18 * 8, 9 * 16 }, + { 20 * 8, 9 * 16 }, 2, { 0, 59 }, 1, '0', }; NumberField field_second { - { 21 * 8, 9 * 16 }, + { 23 * 8, 9 * 16 }, 2, { 0, 59 }, 1, @@ -141,7 +141,7 @@ private: 1, '0', }; - + Checkbox check_bias { { 28, 12 * 16 }, 5, @@ -161,35 +161,6 @@ private: SetFrequencyCorrectionModel form_collect(); }; -class SetTouchCalibView : public View { -public: - SetTouchCalibView(NavigationView& nav); - void focus() override; - bool on_touch(const TouchEvent event) override; - -private: - - Text text_title { - { 64, 32, 40, 16 }, - "UL,UR,DL,DR !", - }; - - Text text_debugx { - { 64, 64, 40, 16 }, - "X", - }; - - Text text_debugy { - { 64, 80, 40, 16 }, - "Y", - }; - - Button button_ok { - { 72, 192, 96, 24 }, - "OK" - }; -}; - class SetUIView : public View { public: SetUIView(NavigationView& nav); @@ -224,7 +195,7 @@ private: }; Checkbox checkbox_showsplash { - { 3 * 8, 8 * 16 }, + { 3 * 8, 9 * 16 }, 11, "Show splash" }; diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index bc1870cc747845f6502eaae507c2c38abb5d7781..0c05f0ccced39036f51c002bd064938f7eb58060 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -661,6 +661,28 @@ static constexpr Bitmap bitmap_icon_freqman { { 16, 16 }, bitmap_icon_freqman_data }; +static constexpr uint8_t bitmap_icon_clk_int_data[] = { + 0x00, + 0x00, + 0xDC, + 0x54, + 0x54, + 0x54, + 0x54, + 0x76, + 0x00, + 0x44, + 0x6C, + 0x38, + 0x38, + 0x6C, + 0x44, + 0x00, +}; +static constexpr Bitmap bitmap_icon_clk_int { + { 8, 16 }, bitmap_icon_clk_int_data +}; + static constexpr uint8_t bitmap_sd_card_ok_data[] = { 0x00, 0x00, 0x00, 0x00, @@ -1699,6 +1721,28 @@ static constexpr Bitmap bitmap_target { { 16, 16 }, bitmap_target_data }; +static constexpr uint8_t bitmap_icon_clk_ext_data[] = { + 0x00, + 0x00, + 0xDC, + 0x54, + 0x54, + 0x54, + 0x54, + 0x76, + 0x00, + 0x10, + 0x38, + 0x7C, + 0x10, + 0x10, + 0x10, + 0x00, +}; +static constexpr Bitmap bitmap_icon_clk_ext { + { 8, 16 }, bitmap_icon_clk_ext_data +}; + static constexpr uint8_t bitmap_icon_sstv_data[] = { 0x10, 0x08, 0x20, 0x04, diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index c8b75ccea488d13b6052ddcf32eaa58132736d69..b808c1cdc220278b1d5d7aec9dd8232feda829e8 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -247,10 +247,10 @@ constexpr ClockControls si5351_clock_control_clkin { si5351_clock_control_common[4] | si5351_clock_control_ms_src_clkin, si5351_clock_control_common[5] | si5351_clock_control_ms_src_clkin, si5351_clock_control_common[6] | si5351_clock_control_ms_src_clkin, - si5351_clock_control_common[7] | si5351_clock_control_ms_src_clkin, + si5351_clock_control_common[7] | si5351_clock_control_ms_src_xtal, }; -void ClockManager::init() { +void ClockManager::init(const bool use_clkin) { /* Must be sure to run the M4 core from IRC when messing with the signal * generator that sources the GP_CLKIN signal that drives the micro- * controller's PLL1 input. @@ -269,7 +269,7 @@ void ClockManager::init() { clock_generator.enable_fanout(); clock_generator.set_pll_input_sources(si5351_pll_input_sources); - const bool use_clkin = false; + //const bool use_clkin = false; clock_generator.set_clock_control( use_clkin ? si5351_clock_control_clkin diff --git a/firmware/application/clock_manager.hpp b/firmware/application/clock_manager.hpp index f139852960aa7894359a3de6ff31ed8e5ce33565..172dbcb7800701c779de714ce32234eb72352529 100644 --- a/firmware/application/clock_manager.hpp +++ b/firmware/application/clock_manager.hpp @@ -42,7 +42,7 @@ public: { } - void init(); + void init(const bool use_clkin); void shutdown(); void run_from_irc(); diff --git a/firmware/application/event_m0.cpp b/firmware/application/event_m0.cpp index 34bc2338a84ae24e3990e5c7ba21dc674d63f26f..c36f1219d2089f8fecacb9229604a69a718d7fb1 100644 --- a/firmware/application/event_m0.cpp +++ b/firmware/application/event_m0.cpp @@ -223,6 +223,8 @@ void EventDispatcher::handle_local_queue() { void EventDispatcher::handle_rtc_tick() { sd_card::poll_inserted(); + + portapack::poll_ext_clock(); portapack::temperature_logger.second_tick(); diff --git a/firmware/application/hw/si5351.hpp b/firmware/application/hw/si5351.hpp index 58104f82ea9189ae249e69d35ce63ad6e194f932..e4c622b67bd40a6d9649db181cbeffd5767fff5e 100644 --- a/firmware/application/hw/si5351.hpp +++ b/firmware/application/hw/si5351.hpp @@ -302,10 +302,6 @@ public: void reset(); - uint8_t device_status() { - return read_register(Register::DeviceStatus); - } - void wait_for_device_ready() { while(device_status() & 0x80); } @@ -382,7 +378,11 @@ public: _clock_control[n] |= ClockControl::CLK_PDN_Mask; write_register(Register::CLKControl_Base + n, _clock_control[n]); } - + + bool clkin_status() { + return ((device_status() & DeviceStatus::LOS_Mask) == DeviceStatus::LOS_ValidClockAtCLKIN); + } + template<size_t N> void write_registers(const uint8_t reg, const std::array<uint8_t, N>& values) { std::array<uint8_t, N + 1> data; @@ -397,6 +397,10 @@ private: const I2C::address_t _address; uint8_t _output_enable; + uint8_t device_status() { + return read_register(Register::DeviceStatus); + } + void update_output_enable_control() { write_register(Register::OutputEnableControl, ~_output_enable); } diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index 73b980aa4f060375f44ffb563b69a443ab6f912c..9d460da9df4320bbfbd20b48636a1691788bdad6 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -29,13 +29,16 @@ // Note about messages: // There can only be one message handler for one kind of message at once -// If an attempt is made to register a second handler, there's a chDbgPanic +// If an attempt is made to register a second handler, there's a chDbgPanic "MsgDblReg" + +// Note about matched filters: see proc_sonde.hpp //TEST: Goertzel tone detect //TEST: Menuview refresh, seems to blink a lot //TEST: Check AFSK transmit end, skips last bits ? //TEST: Imperial in whipcalc +//BUG: Console lock-up if first string to be printed starts with escape character ? //BUG: (Workaround ok) CPLD-related rx ok, tx bad, see portapack.cpp lines 214+ to disable CPLD overlay //BUG: SCANNER Lock on frequency, if frequency jump, still locked on first one //BUG: SCANNER Multiple slices @@ -43,6 +46,7 @@ //GLITCH: Start of tx using ReplayThread plays a small bit of previous transmission (content of 1 buffer ?) // See fifo.reset_in() ? +//TODO: Continue acars receiver. See matched filter, probably doesn't shift the spectrum correctly //TODO: Add larger description text field in frequency load, under menuview //TODO: Allow apps to select a preferred FREQMAN file //TODO: Make play button larger in Replay diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index a3cf5f1859437277bde5a7af9a477db3ec0e9346..eaaef11379bf41553d6cc7a99d920feb231203ee 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -30,6 +30,7 @@ using namespace hackrf::one; #include "clock_manager.hpp" +#include "event_m0.hpp" #include "backlight.hpp" #include "touch_adc.hpp" @@ -82,6 +83,7 @@ TransmitterModel transmitter_model; TemperatureLogger temperature_logger; bool antenna_bias { false }; +bool prev_clkin_status { false }; uint8_t bl_tick_counter { 0 }; void set_antenna_bias(const bool v) { @@ -92,6 +94,22 @@ bool get_antenna_bias() { return antenna_bias; } +bool get_ext_clock() { + return prev_clkin_status; +} + +void poll_ext_clock() { + auto clkin_status = clock_generator.clkin_status(); + + if (clkin_status != prev_clkin_status) { + StatusRefreshMessage message { }; + EventDispatcher::send_message(message); + clock_manager.init(clkin_status); + } + + prev_clkin_status = clkin_status; +} + class Power { public: void init() { @@ -278,7 +296,7 @@ bool init() { led_rx.setup(); led_tx.setup(); - clock_manager.init(); + clock_manager.init(false); clock_manager.set_reference_ppb(persistent_memory::correction_ppb()); clock_manager.run_at_full_speed(); diff --git a/firmware/application/portapack.hpp b/firmware/application/portapack.hpp index 359b14468a76bc16f9d8a41e4e2c49c4ce9c13d0..baf95b4059bdf6058bcd9bc2ce66e6ba5d6baf2f 100644 --- a/firmware/application/portapack.hpp +++ b/firmware/application/portapack.hpp @@ -59,6 +59,9 @@ extern TemperatureLogger temperature_logger; void set_antenna_bias(const bool v); bool get_antenna_bias(); +void poll_ext_clock(); +bool get_ext_clock(); + bool init(); void shutdown(); diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index 32199cd1b0b9d44769c61557381d1be70e112ae9..972585acdb70cdf1705bc1d1d700f937b182713b 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -166,26 +166,26 @@ std::string to_string_datetime(const rtc::RTC& value, const TimeFormat format) { if (format == YMDHMS) { string += to_string_dec_uint(value.year(), 4) + "/" + - to_string_dec_uint(value.month(), 2) + "/" + - to_string_dec_uint(value.day(), 2) + " "; + to_string_dec_uint(value.month(), 2, '0') + "/" + + to_string_dec_uint(value.day(), 2, '0') + " "; } - string += to_string_dec_uint(value.hour(), 2) + ":" + - string += to_string_dec_uint(value.minute(), 2); + string += to_string_dec_uint(value.hour(), 2, '0') + ":" + + string += to_string_dec_uint(value.minute(), 2, '0'); if ((format == YMDHMS) || (format == HMS)) - string += ":" + to_string_dec_uint(value.second(), 2); + string += ":" + to_string_dec_uint(value.second(), 2, '0'); return string; } std::string to_string_timestamp(const rtc::RTC& value) { - return to_string_dec_uint(value.year(), 4) + - to_string_dec_uint(value.month(), 2) + - to_string_dec_uint(value.day(), 2) + - to_string_dec_uint(value.hour(), 2) + - to_string_dec_uint(value.minute(), 2) + - to_string_dec_uint(value.second(), 2); + return to_string_dec_uint(value.year(), 4, '0') + + to_string_dec_uint(value.month(), 2, '0') + + to_string_dec_uint(value.day(), 2, '0') + + to_string_dec_uint(value.hour(), 2, '0') + + to_string_dec_uint(value.minute(), 2, '0') + + to_string_dec_uint(value.second(), 2, '0'); } std::string to_string_FAT_timestamp(const FATTimestamp& timestamp) { diff --git a/firmware/application/ui/ui_spectrum.cpp b/firmware/application/ui/ui_spectrum.cpp index 1051c257419934020ec7f49b9d8b358277b940ae..9940503716d327b139ef6e0c824bcdac5b1b826e 100644 --- a/firmware/application/ui/ui_spectrum.cpp +++ b/firmware/application/ui/ui_spectrum.cpp @@ -74,9 +74,9 @@ void AudioSpectrumView::paint(Painter& painter) { ); } -void AudioSpectrumView::on_audio_spectrum(const AudioSpectrum& spectrum) { - for (size_t i = 0; i < spectrum.db.size(); i++) - audio_spectrum[i] = ((int16_t)spectrum.db[i] - 127) * 256; +void AudioSpectrumView::on_audio_spectrum(const AudioSpectrum* spectrum) { + for (size_t i = 0; i < spectrum->db.size(); i++) + audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256; waveform.set_dirty(); } @@ -353,7 +353,7 @@ void WaterfallWidget::show_audio_spectrum_view(const bool show) { add_child(audio_spectrum_view.get()); update_widgets_rect(); } else { - audio_fifo = nullptr; + audio_spectrum_update = false; remove_child(audio_spectrum_view.get()); audio_spectrum_view.reset(); update_widgets_rect(); @@ -395,8 +395,8 @@ void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) { ); } -void WaterfallWidget::on_audio_spectrum(const AudioSpectrum& spectrum) { - audio_spectrum_view->on_audio_spectrum(spectrum); +void WaterfallWidget::on_audio_spectrum() { + audio_spectrum_view->on_audio_spectrum(audio_spectrum_data); } } /* namespace spectrum */ diff --git a/firmware/application/ui/ui_spectrum.hpp b/firmware/application/ui/ui_spectrum.hpp index ad338e3483b2002b567d9f423e698799ad2cc07e..42579107bbcde395bb726608b23d38e71e6a293c 100644 --- a/firmware/application/ui/ui_spectrum.hpp +++ b/firmware/application/ui/ui_spectrum.hpp @@ -41,7 +41,7 @@ public: void paint(Painter& painter) override; - void on_audio_spectrum(const AudioSpectrum& spectrum); + void on_audio_spectrum(const AudioSpectrum* spectrum); private: static constexpr int cursor_band_height = 4; @@ -150,7 +150,8 @@ private: FrequencyScale frequency_scale { }; ChannelSpectrumFIFO* channel_fifo { nullptr }; - AudioSpectrumFIFO* audio_fifo { nullptr }; + AudioSpectrum* audio_spectrum_data { nullptr }; + bool audio_spectrum_update { false }; std::unique_ptr<AudioSpectrumView> audio_spectrum_view { }; @@ -166,11 +167,12 @@ private: this->channel_fifo = message.fifo; } }; - MessageHandlerRegistration message_handler_audio_spectrum_config { - Message::ID::AudioSpectrumConfig, + MessageHandlerRegistration message_handler_audio_spectrum { + Message::ID::AudioSpectrum, [this](const Message* const p) { - const auto message = *reinterpret_cast<const AudioSpectrumConfigMessage*>(p); - this->audio_fifo = message.fifo; + const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p); + this->audio_spectrum_data = message.data; + this->audio_spectrum_update = true; } }; MessageHandlerRegistration message_handler_frame_sync { @@ -182,18 +184,15 @@ private: this->on_channel_spectrum(channel_spectrum); } } - if( this->audio_fifo ) { - AudioSpectrum audio_spectrum; - while( audio_fifo->out(audio_spectrum) ) { - // Unstack everything available and only use last buffer (should only be one max. ready per frame) - } - this->on_audio_spectrum(audio_spectrum); + if (this->audio_spectrum_update) { + this->audio_spectrum_update = false; + this->on_audio_spectrum(); } } }; void on_channel_spectrum(const ChannelSpectrum& spectrum); - void on_audio_spectrum(const AudioSpectrum& spectrum); + void on_audio_spectrum(); }; } /* namespace spectrum */ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index cd4841d60e0410e6f48f4b645947c96e1f0ac583..6ebb10aa533283fb1e28c90acdf7c6836865fd8d 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -106,6 +106,7 @@ SystemStatusView::SystemStatusView( &button_camera, &button_sleep, &button_bias_tee, + &image_clock_status, &sd_card_status_view, }); @@ -157,6 +158,14 @@ void SystemStatusView::refresh() { button_bias_tee.set_bitmap(&bitmap_icon_biast_off); button_bias_tee.set_foreground(ui::Color::light_grey()); } + + if (portapack::get_ext_clock()) { + image_clock_status.set_bitmap(&bitmap_icon_clk_ext); + button_bias_tee.set_foreground(ui::Color::green()); + } else { + image_clock_status.set_bitmap(&bitmap_icon_clk_int); + button_bias_tee.set_foreground(ui::Color::light_grey()); + } } void SystemStatusView::set_back_enabled(bool new_value) { @@ -334,7 +343,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { { "ACARS: Planes", ui::Color::yellow(),&bitmap_icon_adsb, [&nav](){ nav.replace<ACARSAppView>(); }, }, { "AIS: Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.replace<AISAppView>(); } }, { "AFSK", ui::Color::yellow(),&bitmap_icon_receivers, [&nav](){ nav.replace<AFSKRxView>(); } }, - { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.replace<AnalogAudioView>(false); } }, + { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.replace<AnalogAudioView>(); } }, { "ERT: Utility Meters", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.replace<ERTAppView>(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.replace<POCSAGAppView>(); } }, { "Radiosondes", ui::Color::yellow(),&bitmap_icon_sonde, [&nav](){ nav.replace<SondeView>(); } }, @@ -370,7 +379,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { { "Soundboard", ui::Color::green(), &bitmap_icon_soundboard, [&nav](){ nav.push<SoundBoardView>(); } }, { "SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav](){ nav.push<SSTVTXView>(); } }, { "TEDI/LCR AFSK", ui::Color::yellow(), &bitmap_icon_lcr, [&nav](){ nav.push<LCRView>(); } }, - { "TouchTunes remote", ui::Color::yellow(), &bitmap_icon_remote [&nav](){ nav.push<TouchTunesView>(); } }, + { "TouchTunes remote", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push<TouchTunesView>(); } }, { "Custom remote", ui::Color::grey(), &bitmap_icon_remote, [&nav](){ nav.push<RemoteView>(); } }, }); on_left = [&nav](){ nav.pop(); }; diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 925f0de6f4cd4223d4034ff0de8ccdb55130a7cb..b9c35d5b59c714525fd4e1c58cafb77ce13be5e9 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -125,12 +125,12 @@ private: }; Text title { - { 20, 0, 17 * 8, 1 * 16 }, + { 20, 0, 16 * 8, 1 * 16 }, default_title, }; ImageButton button_stealth { - { 20 * 8, 0, 2 * 8, 1 * 16 }, + { 19 * 8, 0, 2 * 8, 1 * 16 }, &bitmap_icon_stealth, Color::light_grey(), Color::dark_grey() @@ -144,26 +144,33 @@ private: };*/ ImageButton button_camera { - { 22 * 8, 0, 2 * 8, 1 * 16 }, + { 21 * 8, 0, 2 * 8, 1 * 16 }, &bitmap_icon_camera, Color::white(), Color::dark_grey() }; ImageButton button_sleep { - { 24 * 8, 0, 2 * 8, 1 * 16 }, + { 23 * 8, 0, 2 * 8, 1 * 16 }, &bitmap_icon_sleep, Color::white(), Color::dark_grey() }; ImageButton button_bias_tee { - { 26 * 8, 0, 12, 1 * 16 }, + { 25 * 8, 0, 12, 1 * 16 }, &bitmap_icon_biast_off, Color::light_grey(), Color::dark_grey() }; + Image image_clock_status { + { 27 * 8, 0 * 16, 2 * 8, 1 * 16 }, + &bitmap_icon_clk_int, + Color::light_grey(), + Color::dark_grey() + }; + SDCardStatusView sd_card_status_view { { 28 * 8, 0 * 16, 2 * 8, 1 * 16 } }; diff --git a/firmware/baseband/proc_acars.cpp b/firmware/baseband/proc_acars.cpp index e0077f8e1c11bd994563d3bc1da757ed6f6a8588..176e428fb65e248329d2e79f8b27f77ed79d9d1a 100644 --- a/firmware/baseband/proc_acars.cpp +++ b/firmware/baseband/proc_acars.cpp @@ -31,6 +31,7 @@ ACARSProcessor::ACARSProcessor() { decim_0.configure(taps_11k0_decim_0.taps, 33554432); decim_1.configure(taps_11k0_decim_1.taps, 131072); + packet.clear(); } void ACARSProcessor::execute(const buffer_c8_t& buffer) { @@ -54,9 +55,16 @@ void ACARSProcessor::consume_symbol( const float raw_symbol ) { const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0; - const auto decoded_symbol = nrzi_decode(sliced_symbol); + //const auto decoded_symbol = acars_decode(sliced_symbol); - packet_builder.execute(decoded_symbol); + // DEBUG + packet.add(sliced_symbol); + if (packet.size() == 256) { + payload_handler(packet); + packet.clear(); + } + + //packet_builder.execute(decoded_symbol); } void ACARSProcessor::payload_handler( diff --git a/firmware/baseband/proc_acars.hpp b/firmware/baseband/proc_acars.hpp index eeb8befdbd9144955eca17c1023ccde1d2aa0468..69d9097063c00c777b939cf6a85db74b02b7e9fb 100644 --- a/firmware/baseband/proc_acars.hpp +++ b/firmware/baseband/proc_acars.hpp @@ -28,8 +28,6 @@ #include "rssi_thread.hpp" #include "dsp_decimate.hpp" -#include "dsp_demodulate.hpp" -//#include "audio_compressor.hpp" #include "spectrum_collector.hpp" @@ -53,7 +51,48 @@ #include <cstddef> #include <bitset> -#include "ais_baseband.hpp" +// AIS: +// IN: 2457600/8/8 = 38400 +// Offset: 2457600/4 = 614400 (614400/8/8 = 9600) +// Deviation: 2400 +// Symbol: 9600 +// Decimate: 2 +// 4 taps, 1 symbol, 1/4 cycle + +// TPMS: +// IN: 2457600/4/2 = 307200 +// Offset: 2457600/4 = 614400 (614400/4/2 = 76800) +// Deviation: 38400 +// Symbol: 19200 +// Decimate: 8 +// 16 taps, 1 symbol, 2 cycles + +// ACARS: +// IN: 2457600/8/8 = 38400 +// Offset: 2457600/4 = 614400 (614400/8/8 = 9600) +// Deviation: ??? +// Symbol: 2400 +// Decimate: 8 +// 16 taps, 1 symbol, 2 cycles + +// Number of taps: size of one symbol in samples (in/symbol) +// Cycles: + + +// Translate+rectangular filter +// sample=38.4k, deviation=4800, symbol=2400 +// Length: 16 taps, 1 symbol, 2 cycles of sinusoid +// This is actually the same as rect_taps_307k2_38k4_1t_19k2_p +constexpr std::array<std::complex<float>, 16> rect_taps_38k4_4k8_1t_2k4_p { { + { 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f }, + { 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f }, + { -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f }, + { 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f }, + { 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f }, + { 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f }, + { -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f }, + { 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f }, +} }; class ACARSProcessor : public BasebandProcessor { public: @@ -73,23 +112,24 @@ private: dst.size() }; - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; // Translate already done here ! dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; - dsp::matched_filter::MatchedFilter mf { baseband::ais::square_taps_38k4_1t_p, 2 }; + dsp::matched_filter::MatchedFilter mf { rect_taps_38k4_4k8_1t_2k4_p, 8 }; clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery { - 19200, 2400, { 0.0555f }, + 4800, 2400, { 0.0555f }, [this](const float symbol) { this->consume_symbol(symbol); } }; - symbol_coding::NRZIDecoder nrzi_decode { }; - PacketBuilder<BitPattern, NeverMatch, BitPattern> packet_builder { - { 0b1001011010010110, 16, 1 }, // SYN, SYN + symbol_coding::ACARSDecoder acars_decode { }; + /*PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder { + { 0b011010000110100010000000, 24, 1 }, // SYN, SYN, SOH { }, - { 0b11111111, 8, 1 }, + { 128 }, [this](const baseband::Packet& packet) { this->payload_handler(packet); } - }; + };*/ + baseband::Packet packet { }; void consume_symbol(const float symbol); void payload_handler(const baseband::Packet& packet); diff --git a/firmware/baseband/proc_wfm_audio.cpp b/firmware/baseband/proc_wfm_audio.cpp index 27419a9f8e91a247be67c1245b4d211737e318e1..5793e479a38eaf511b4fd0455c247375955604ff 100644 --- a/firmware/baseband/proc_wfm_audio.cpp +++ b/firmware/baseband/proc_wfm_audio.cpp @@ -66,48 +66,58 @@ void WidebandFMAudio::execute(const buffer_c8_t& buffer) { auto audio_2fs = audio_dec_2.execute(audio_4fs, work_audio_buffer); // Input: 96kHz int16_t[64] - // audio_spectrum_decimator piles up 256 bytes before doing FFT computation - // This should send an AudioSpectrum every sample rate/buffer size/(256/64)/refresh scaler = 3072000/2048/4/8 = ~47 Hz + // audio_spectrum_decimator piles up 256 samples before doing FFT computation + // This sends an AudioSpectrum every: sample rate/buffer size/refresh period = 3072000/2048/50 = 30 Hz + // When audio_spectrum_timer expires, the audio spectrum computation is triggered + // 0~3: feed continuous audio // 4~31: ignore, wrap at 31 - if (!(refresh_timer & 0xF8)) { - for (size_t i = 0; i < 64; i++) { - complex_audio[i] = { (int16_t)(work_audio_buffer.p[i] / 32), (int16_t)0 }; - } - audio_spectrum_decimator.feed( - complex_audio_buffer, - [this](const buffer_c16_t& data) { - this->post_message(data); + + audio_spectrum_timer++; + if (audio_spectrum_timer == 50) { + audio_spectrum_timer = 0; + audio_spectrum_state = FEED; + } + + switch (audio_spectrum_state) { + case FEED: + // Convert audio to "complex" just so the FFT can be done :/ + for (size_t i = 0; i < 64; i++) { + complex_audio[i] = { (int16_t)(work_audio_buffer.p[i] / 32), (int16_t)0 }; } - ); - } else { - // Spread the FFT workload in time to avoid making the audio skip - // "8" comes from the log2() of the size of audio_spectrum: log2(256) = 8 - if (fft_stage && (fft_stage <= 8)) { - fft_c_preswapped(audio_spectrum, fft_stage - 1, fft_stage); - fft_stage++; - } else if (fft_stage > 8) { - AudioSpectrum spectrum; - const size_t spectrum_end = spectrum.db.size(); - for(size_t i=0; i<spectrum_end; i++) { - //const auto corrected_sample = spectrum_window_hamming_3(audio_spectrum, i); - const auto corrected_sample = audio_spectrum[i]; - const auto mag2 = magnitude_squared(corrected_sample * (1.0f / 32768.0f)); - const float db = mag2_to_dbv_norm(mag2); - constexpr float mag_scale = 5.0f; - const unsigned int v = (db * mag_scale) + 255.0f; - spectrum.db[i] = std::max(0U, std::min(255U, v)); + audio_spectrum_decimator.feed( + complex_audio_buffer, + [this](const buffer_c16_t& data) { + this->post_message(data); + } + ); + break; + case FFT: + // Spread the FFT workload in time to avoid making the audio skip + // "8" comes from the log2() of the size of audio_spectrum: log2(256) = 8 + if (fft_step < 8) { + fft_c_preswapped(audio_spectrum, fft_step, fft_step + 1); + fft_step++; + } else { + const size_t spectrum_end = spectrum.db.size(); + for(size_t i=0; i<spectrum_end; i++) { + //const auto corrected_sample = spectrum_window_hamming_3(audio_spectrum, i); + const auto corrected_sample = audio_spectrum[i]; + const auto mag2 = magnitude_squared(corrected_sample * (1.0f / 32768.0f)); + const float db = mag2_to_dbv_norm(mag2); + constexpr float mag_scale = 5.0f; + const unsigned int v = (db * mag_scale) + 255.0f; + spectrum.db[i] = std::max(0U, std::min(255U, v)); + } + AudioSpectrumMessage message { &spectrum }; + shared_memory.application_queue.push(message); + audio_spectrum_state = IDLE; } - fifo.in(spectrum); - fft_stage = 0; - } + break; + default: + break; } - if (refresh_timer == 31) - refresh_timer = 0; - else - refresh_timer++; - /* 96kHz int16_t[64] * -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop, gain of 1 * -> 48kHz int16_t[32] */ @@ -121,7 +131,8 @@ void WidebandFMAudio::execute(const buffer_c8_t& buffer) { void WidebandFMAudio::post_message(const buffer_c16_t& data) { // This is called when audio_spectrum_decimator is filled up to 256 samples fft_swap(data, audio_spectrum); - fft_stage = 1; + audio_spectrum_state = FFT; + fft_step = 0; } void WidebandFMAudio::on_message(const Message* const message) { @@ -167,9 +178,6 @@ void WidebandFMAudio::configure(const WFMConfigureMessage& message) { channel_spectrum.set_decimation_factor(1); configured = true; - - AudioSpectrumConfigMessage config_message { &fifo }; - shared_memory.application_queue.push(config_message); } void WidebandFMAudio::capture_config(const CaptureConfigMessage& message) { diff --git a/firmware/baseband/proc_wfm_audio.hpp b/firmware/baseband/proc_wfm_audio.hpp index b84bdbddd03c5d989daf74aac1c2095289cfdd5a..9498ed52236685abd03052be1668f6205823fbd7 100644 --- a/firmware/baseband/proc_wfm_audio.hpp +++ b/firmware/baseband/proc_wfm_audio.hpp @@ -53,6 +53,7 @@ private: dst.data(), dst.size() }; + // work_audio_buffer and dst_buffer use the same data pointer const buffer_s16_t work_audio_buffer { (int16_t*)dst.data(), sizeof(dst) / sizeof(int16_t) @@ -78,11 +79,16 @@ private: // For fs=96kHz FFT streaming BlockDecimator<complex16_t, 256> audio_spectrum_decimator { 1 }; - AudioSpectrum fifo_data[1 << AudioSpectrumConfigMessage::fifo_k] { }; - AudioSpectrumFIFO fifo { fifo_data, AudioSpectrumConfigMessage::fifo_k }; std::array<std::complex<float>, 256> audio_spectrum { }; - uint32_t refresh_timer { 0 }; - uint32_t fft_stage { 0 }; + uint32_t audio_spectrum_timer { 0 }; + enum AudioSpectrumState { + IDLE = 0, + FEED, + FFT + }; + AudioSpectrumState audio_spectrum_state { IDLE }; + AudioSpectrum spectrum { }; + uint32_t fft_step { 0 }; SpectrumCollector channel_spectrum { }; size_t spectrum_interval_samples = 0; diff --git a/firmware/baseband/symbol_coding.hpp b/firmware/baseband/symbol_coding.hpp index 03a424556cd765b77b50d946b15641109c3f5716..8842abdf64c31634a74a45ccd6e957946423eaf7 100644 --- a/firmware/baseband/symbol_coding.hpp +++ b/firmware/baseband/symbol_coding.hpp @@ -39,6 +39,17 @@ private: uint_fast8_t last { 0 }; }; +class ACARSDecoder { +public: + uint_fast8_t operator()(const uint_fast8_t symbol) { + last ^= (~symbol & 1); + return last; + } + +private: + uint_fast8_t last { 0 }; +}; + } /* namespace symbol_coding */ #endif/*__SYMBOL_CODING_H__*/ diff --git a/firmware/chibios/os/kernel/src/chschd.c b/firmware/chibios/os/kernel/src/chschd.c index d5d23b3f97d1ffee411053b483ece416651b678a..650223478b62f2c12cb0daedb5b4a317ef29964b 100755 --- a/firmware/chibios/os/kernel/src/chschd.c +++ b/firmware/chibios/os/kernel/src/chschd.c @@ -148,6 +148,7 @@ static void wakeup(void *p) { #if CH_USE_SEMAPHORES case THD_STATE_WTSEM: chSemFastSignalI((Semaphore *)tp->p_u.wtobjp); + __attribute__ ((fallthrough)); /* Falls into, intentional. */ #endif #if CH_USE_QUEUES diff --git a/firmware/common/acars_packet.cpp b/firmware/common/acars_packet.cpp index fb9595020713d28d740d81620944d010d4332b99..2cf0366e4ff10e71b099239185df4b1decc6ccfb 100644 --- a/firmware/common/acars_packet.cpp +++ b/firmware/common/acars_packet.cpp @@ -33,7 +33,7 @@ size_t Packet::length() const { } bool Packet::is_valid() const { - return length_valid() && crc_ok(); + return true; //length_valid() && crc_ok(); } Timestamp Packet::received_at() const { @@ -51,7 +51,7 @@ std::string Packet::registration_number() const { const size_t character_length = 8; for(size_t i=16; i<(16+7*character_length); i+=character_length) { result += (field_.read(i, character_length) & 0x7F); - } + } return result; } diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index c1f4e506ea3b471547cfacb5309baef6f9258cf0..dfcc49ce328eee9f586516fe0f6cd9a25fd4a145 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -108,7 +108,7 @@ public: AudioLevelReport = 51, CodedSquelch = 52, - AudioSpectrumConfig = 53, + AudioSpectrum = 53, MAX }; @@ -281,20 +281,16 @@ struct AudioSpectrum { //uint32_t sampling_rate { 0 }; }; -using AudioSpectrumFIFO = FIFO<AudioSpectrum>; - -class AudioSpectrumConfigMessage : public Message { +class AudioSpectrumMessage : public Message { public: - static constexpr size_t fifo_k = 2; - - constexpr AudioSpectrumConfigMessage( - AudioSpectrumFIFO* fifo - ) : Message { ID::AudioSpectrumConfig }, - fifo { fifo } + constexpr AudioSpectrumMessage( + AudioSpectrum* data + ) : Message { ID::AudioSpectrum }, + data { data } { } - AudioSpectrumFIFO* fifo { nullptr }; + AudioSpectrum* data { nullptr }; }; struct ChannelSpectrum { diff --git a/firmware/graphics/icon_clk_ext.png b/firmware/graphics/icon_clk_ext.png new file mode 100644 index 0000000000000000000000000000000000000000..5432970c3b0262987fb6a98fb824c47e823e0190 Binary files /dev/null and b/firmware/graphics/icon_clk_ext.png differ diff --git a/firmware/graphics/icon_clk_int.png b/firmware/graphics/icon_clk_int.png new file mode 100644 index 0000000000000000000000000000000000000000..6c1a801a97d940e7b8bb8d8ab3aed3485cc2575b Binary files /dev/null and b/firmware/graphics/icon_clk_int.png differ diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin index b7fe0159cda0528eb2b58df972bf906661a1d780..f2dd63361b533dddf068e2487a588e9bd5733681 100644 Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ