diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index 463246fdf..8d08d31c8 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -117,9 +117,12 @@ void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration) { send_message(&message); } -void set_afsk(const uint32_t bitrate) { +void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) { const AFSKRxConfigureMessage message { - bitrate + baudrate, + word_length, + trigger_value, + trigger_word }; send_message(&message); } diff --git a/firmware/application/baseband_api.hpp b/firmware/application/baseband_api.hpp index e10e2a1cd..274af133b 100644 --- a/firmware/application/baseband_api.hpp +++ b/firmware/application/baseband_api.hpp @@ -67,7 +67,7 @@ void set_pwmrssi(int32_t avg, bool enabled); void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space, const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count); void kill_afsk(); -void set_afsk(const uint32_t bitrate); +void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat, const uint32_t pause_symbols); void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift, diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index 231195e65..154dff249 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -32,6 +32,8 @@ //BUG: SCANNER Lock on frequency, if frequency jump, still locked on first one //BUG: SCANNER Multiple slices +//TODO: Frequency and bw settings were removed from modemsetup, put those back in LCR TX +//TODO: Use separate thread for scanning in LCR TX //TODO: REPLAY Convert C16 to C8 on M0 core //TODO: Use TabView //TODO: De bruijn sequence scanner for encoders diff --git a/firmware/application/modems.cpp b/firmware/application/modems.cpp index 29a047e42..dfd106d60 100644 --- a/firmware/application/modems.cpp +++ b/firmware/application/modems.cpp @@ -79,4 +79,41 @@ void generate_data(const std::string& in_message, uint16_t * out_data) { out_data[bytes] = 0; // End marker } +// This accepts a word with start and stop bits removed ! +uint32_t deframe_word(uint32_t raw_word) { + serial_format_t serial_format; + uint32_t parity, cur_bit, deframed_word { 0 }; + size_t bit, bits; + + serial_format = persistent_memory::serial_format(); + + /*if (serial_format.parity == ODD) + parity = 1; + else + parity = 0;*/ + + bits = serial_format.data_bits; + + // Ignore parity for now + if (serial_format.parity) + raw_word >>= 1; + + if (serial_format.bit_order == LSB_FIRST) { + // Reverse data bits + for (bit = 0; bit < bits; bit++) { + cur_bit = raw_word & 1; + + deframed_word <<= 1; + deframed_word |= cur_bit; + + //parity += cur_bit; + + raw_word >>= 1; + } + + return deframed_word; + } else + return raw_word; +} + } /* namespace modems */ diff --git a/firmware/application/modems.hpp b/firmware/application/modems.hpp index 7bb317476..e39fd3916 100644 --- a/firmware/application/modems.hpp +++ b/firmware/application/modems.hpp @@ -31,16 +31,16 @@ namespace modems { #define MODEM_DEF_COUNT 7 -enum modulation_enum { +enum ModemModulation { AFSK = 0, FSK, PSK, - SSB + AM // SSB }; struct modem_def_t { std::string name; - modulation_enum modulation; + ModemModulation modulation; uint16_t mark_freq; uint16_t space_freq; uint16_t baudrate; @@ -52,11 +52,12 @@ const modem_def_t modem_defs[MODEM_DEF_COUNT] = { { "V21", AFSK, 980, 1180, 300 }, { "V23 M1", AFSK, 1300, 1700, 600 }, { "V23 M2", AFSK, 1300, 2100, 1200 }, - { "RTTY US", SSB, 2295, 2125, 45 }, - { "RTTY EU", SSB, 2125, 1955, 45 } + { "RTTY US", AM, 2295, 2125, 45 }, + { "RTTY EU", AM, 2125, 1955, 45 } }; void generate_data(const std::string& in_message, uint16_t * out_data); +uint32_t deframe_word(uint32_t raw_word); } /* namespace modems */ diff --git a/firmware/application/pocsag_app.cpp b/firmware/application/pocsag_app.cpp index 97c15d9b2..374388c01 100644 --- a/firmware/application/pocsag_app.cpp +++ b/firmware/application/pocsag_app.cpp @@ -23,8 +23,6 @@ #include "pocsag_app.hpp" #include "baseband_api.hpp" - -#include "portapack.hpp" #include "portapack_persistent_memory.hpp" using namespace portapack; diff --git a/firmware/application/serializer.cpp b/firmware/application/serializer.cpp index aac2b4cbf..778d80a3c 100644 --- a/firmware/application/serializer.cpp +++ b/firmware/application/serializer.cpp @@ -39,11 +39,8 @@ namespace serializer { * Diff M.: ... */ -uint8_t symbol_count() { - serial_format_t serial_format; - uint8_t count; - - serial_format = persistent_memory::serial_format(); +size_t symbol_count(const serial_format_t& serial_format) { + size_t count; count = 1 + serial_format.data_bits; // Start if (serial_format.parity) count++; diff --git a/firmware/application/serializer.hpp b/firmware/application/serializer.hpp index dedfcad51..3aedfb247 100644 --- a/firmware/application/serializer.hpp +++ b/firmware/application/serializer.hpp @@ -21,16 +21,14 @@ */ #include "ui.hpp" -#include -#include + +#include "portapack_persistent_memory.hpp" #ifndef __SERIALIZER_H__ #define __SERIALIZER_H__ namespace serializer { -uint8_t symbol_count(); - enum parity_enum : uint8_t { NONE = 0, EVEN = 1, @@ -49,6 +47,8 @@ struct serial_format_t { order_enum bit_order; }; +size_t symbol_count(const serial_format_t& serial_format); + /*{ "7-Even-1 R", "7E1", 7, EVEN, 1, false, false }, { "7E1 LUT ", "7Ea", 7, EVEN, 1, true, true }, { "7-Odd-1 ", "7o1", 7, ODD, 1, true, false }, diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index 80e8b9e2b..2ad90b1f7 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -61,7 +61,7 @@ std::string to_string_bin( { char p[33]; for (uint8_t c = 0; c < l; c++) { - if (n & (1 << (l - c))) + if (n & (1 << (l - 1 - c))) p[c] = '1'; else p[c] = '0'; diff --git a/firmware/application/ui_adsb_rx.cpp b/firmware/application/ui_adsb_rx.cpp index fcaebd7ab..508f2c55c 100644 --- a/firmware/application/ui_adsb_rx.cpp +++ b/firmware/application/ui_adsb_rx.cpp @@ -25,13 +25,9 @@ #include "rtc_time.hpp" #include "string_format.hpp" -#include "portapack.hpp" #include "baseband_api.hpp" #include "portapack_persistent_memory.hpp" -#include -#include - using namespace portapack; namespace ui { diff --git a/firmware/application/ui_afsk_rx.cpp b/firmware/application/ui_afsk_rx.cpp index d8efec463..6fa437cc8 100644 --- a/firmware/application/ui_afsk_rx.cpp +++ b/firmware/application/ui_afsk_rx.cpp @@ -21,11 +21,24 @@ */ #include "ui_afsk_rx.hpp" -#include "baseband_api.hpp" +#include "ui_modemsetup.hpp" -//#include "string_format.hpp" +#include "modems.hpp" +#include "audio.hpp" +#include "rtc_time.hpp" +#include "baseband_api.hpp" +#include "string_format.hpp" +#include "portapack_persistent_memory.hpp" using namespace portapack; +using namespace modems; + +void AFSKLogger::log_raw_data(const std::string& data) { + rtc::RTC datetime; + rtcGetTime(&RTCD1, &datetime); + + log_file.write_entry(datetime, data); +} namespace ui { @@ -37,10 +50,6 @@ void AFSKRxView::update_freq(rf::Frequency f) { receiver_model.set_tuning_frequency(f); } -void AFSKRxView::on_bitrate_changed(const uint32_t new_bitrate) { - baseband::set_afsk(new_bitrate); -} - AFSKRxView::AFSKRxView(NavigationView& nav) { baseband::run_image(portapack::spi_flash::image_tag_afsk_rx); @@ -51,16 +60,24 @@ AFSKRxView::AFSKRxView(NavigationView& nav) { &field_lna, &field_vga, &field_frequency, - &options_bitrate, + &text_debug, + &button_modem_setup, &console }); - //receiver_model.set_sampling_rate(3072000); - //receiver_model.set_baseband_bandwidth(1750000); - //receiver_model.enable(); + // Auto-configure modem for LCR RX (will be removed later) + update_freq(462713300); // 162950000 + auto def_bell202 = &modem_defs[0]; + persistent_memory::set_modem_baudrate(def_bell202->baudrate); + serial_format_t serial_format; + serial_format.data_bits = 7; + serial_format.parity = EVEN; + serial_format.stop_bits = 1; + serial_format.bit_order = LSB_FIRST; + persistent_memory::set_serial_format(serial_format); field_frequency.set_value(receiver_model.tuning_frequency()); - field_frequency.set_step(receiver_model.frequency_step()); + field_frequency.set_step(100); field_frequency.on_change = [this](rf::Frequency f) { update_freq(f); }; @@ -72,19 +89,69 @@ AFSKRxView::AFSKRxView(NavigationView& nav) { field_frequency.set_value(f); }; }; - - options_bitrate.on_change = [this](size_t, OptionsField::value_t v) { - on_bitrate_changed(v); + + button_modem_setup.on_select = [&nav](Button&) { + nav.push(); }; - options_bitrate.set_selected_index(1); // 1200bps + + logger = std::make_unique(); + if (logger) + logger->append("AFSK_LOG.TXT"); + + // Auto-configure modem for LCR RX (will be removed later) + baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false); + + audio::set_rate(audio::Rate::Hz_24000); + audio::output::start(); + + receiver_model.set_sampling_rate(3072000); + receiver_model.set_baseband_bandwidth(1750000); + receiver_model.enable(); } -void AFSKRxView::on_data(uint_fast8_t byte) { - std::string str_byte(1, byte); - console.write(str_byte); +void AFSKRxView::on_data(uint32_t value, bool is_data) { + std::string str_byte = "\x1B"; + + str_byte += (char)((console_color & 3) + 9); + + if (is_data) { + //value = deframe_word(value); + + value &= 0xFF; // ABCDEFGH + value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD + value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB + value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA + value &= 0x7F; + + if ((value >= 32) && (value < 127)) + str_byte += (char)value ; // Printable + else + str_byte += "[" + to_string_hex(value, 2) + "]"; // Not printable + + //str_byte = to_string_bin(value & 0xFF, 8) + " "; + + console.write(str_byte); + + if (logger) str_log += str_byte; + + if ((value != 0x7F) && (prev_value == 0x7F)) { + console.writeln(""); + console_color++; + + if (logger) { + logger->log_raw_data(str_log); + str_log = ""; + } + } + prev_value = value; + } else { + // Baudrate estimation + text_debug.set("~" + to_string_dec_uint(value)); + } } AFSKRxView::~AFSKRxView() { + audio::output::stop(); receiver_model.disable(); baseband::shutdown(); } diff --git a/firmware/application/ui_afsk_rx.hpp b/firmware/application/ui_afsk_rx.hpp index 4b99f1c26..9881b32b9 100644 --- a/firmware/application/ui_afsk_rx.hpp +++ b/firmware/application/ui_afsk_rx.hpp @@ -26,9 +26,22 @@ #include "ui.hpp" #include "ui_navigation.hpp" #include "ui_receiver.hpp" -#include "ui_widget.hpp" + +#include "log_file.hpp" #include "utility.hpp" +class AFSKLogger { +public: + Optional append(const std::string& filename) { + return log_file.append(filename); + } + + void log_raw_data(const std::string& data); + +private: + LogFile log_file { }; +}; + namespace ui { class AFSKRxView : public View { @@ -41,7 +54,11 @@ public: std::string title() const override { return "AFSK RX (beta)"; }; private: - void on_data(uint_fast8_t byte); + void on_data(uint32_t value, bool is_data); + + uint8_t console_color { 0 }; + uint32_t prev_value { 0 }; + std::string str_log { "" }; RFAmpField field_rf_amp { { 13 * 8, 0 * 16 } @@ -60,33 +77,34 @@ private: }; FrequencyField field_frequency { - { 0 * 8, 0 * 8 }, + { 0 * 8, 0 * 16 }, }; - OptionsField options_bitrate { - { 12 * 8, 21 }, - 7, - { - { "600bps ", 600 }, - { "1200bps", 1200 }, - { "2400bps", 2400 } - } + + Text text_debug { + { 0 * 8, 1 * 16, 10 * 8, 16 }, + "DEBUG" + }; + + + Button button_modem_setup { + { 12 * 8, 1 * 16, 96, 24 }, + "Modem setup" }; Console console { - { 0, 4 * 16, 240, 240 } + { 0, 3 * 16, 240, 240 } }; void update_freq(rf::Frequency f); - - void on_bitrate_changed(const uint32_t new_bitrate); - void on_data_afsk(const AFSKDataMessage& message); + std::unique_ptr logger { }; + MessageHandlerRegistration message_handler_packet { Message::ID::AFSKData, [this](Message* const p) { const auto message = static_cast(p); - this->on_data(message->byte); + this->on_data(message->value, message->is_data); } }; }; diff --git a/firmware/application/ui_encoders.hpp b/firmware/application/ui_encoders.hpp index e47f52b8d..c7521da87 100644 --- a/firmware/application/ui_encoders.hpp +++ b/firmware/application/ui_encoders.hpp @@ -177,7 +177,7 @@ private: void start_tx(const bool scan); void on_txdone(int n, const bool txdone); - const Style style_address { + /*const Style style_address { .font = font::fixed_8x16, .background = Color::black(), .foreground = Color::red(), @@ -186,7 +186,7 @@ private: .font = font::fixed_8x16, .background = Color::black(), .foreground = Color::blue(), - }; + };*/ Rect view_rect = { 0, 4 * 8, 240, 168 }; diff --git a/firmware/application/ui_freqman.cpp b/firmware/application/ui_freqman.cpp index 7a6e1229c..6c0dfab60 100644 --- a/firmware/application/ui_freqman.cpp +++ b/firmware/application/ui_freqman.cpp @@ -30,31 +30,28 @@ using namespace portapack; namespace ui { FreqManBaseView::FreqManBaseView( - NavigationView& nav, - Widget& default_focus_widget -) : nav_ (nav), - default_focus_widget_ (default_focus_widget) + NavigationView& nav +) : nav_ (nav) { file_list = get_freqman_files(); - if (!file_list.size()) { + if (!file_list.size()) error_ = ERROR_NOFILES; - return; - } add_children({ &label_category, - &options_category, &button_exit }); - // Populate categories OptionsField - populate_categories(); + if (file_list.size()) { + add_child(&options_category); + populate_categories(); + } + // Default function on_change_category = [this](int32_t category_id) { change_category(category_id); }; - //change_category(0); button_exit.on_select = [this, &nav](Button&) { nav.pop(); @@ -62,26 +59,21 @@ FreqManBaseView::FreqManBaseView( }; void FreqManBaseView::focus() { - /*if (error_ == ERROR_ACCESS) { - nav_.display_modal("Error", "File acces error", ABORT, nullptr); - } else if (error_ == ERROR_DUPLICATE) { - nav_.display_modal("Error", "Frequency already saved", INFO, nullptr); - error_ = NO_ERROR; - }*/ + button_exit.focus(); - if (error_ == ERROR_NOFILES) { + if (error_ == ERROR_ACCESS) { + nav_.display_modal("Error", "File acces error", ABORT, nullptr); + } else if (error_ == ERROR_NOFILES) { nav_.display_modal("Error", "No database files", ABORT, nullptr); } else { - default_focus_widget_.focus(); + options_category.focus(); } } bool FreqManBaseView::populate_categories() { - size_t n; - categories.clear(); - for (n = 0; n < file_list.size(); n++) + for (size_t n = 0; n < file_list.size(); n++) categories.emplace_back(std::make_pair(file_list[n], n)); options_category.set_options(categories); @@ -96,10 +88,13 @@ bool FreqManBaseView::populate_categories() { } void FreqManBaseView::change_category(int32_t category_id) { + + if (!file_list.size()) return; + current_category_id = category_id; if (!load_freqman_file(file_list[current_category_id], database)) - error_ = ERROR_ACCESS; // Todo + error_ = ERROR_ACCESS; else refresh_list(); } @@ -158,7 +153,7 @@ FrequencySaveView::~FrequencySaveView() { FrequencySaveView::FrequencySaveView( NavigationView& nav, const rf::Frequency value -) : FreqManBaseView(nav, options_category), +) : FreqManBaseView(nav), value_ (value) { desc_buffer.reserve(28); @@ -204,7 +199,7 @@ void FrequencyLoadView::refresh_widgets(const bool v) { FrequencyLoadView::FrequencyLoadView( NavigationView& nav -) : FreqManBaseView(nav, options_category) +) : FreqManBaseView(nav) { on_refresh_widgets = [this](bool v) { refresh_widgets(v); @@ -278,7 +273,7 @@ FrequencyManagerView::~FrequencyManagerView() { FrequencyManagerView::FrequencyManagerView( NavigationView& nav -) : FreqManBaseView(nav, options_category) +) : FreqManBaseView(nav) { on_refresh_widgets = [this](bool v) { refresh_widgets(v); diff --git a/firmware/application/ui_freqman.hpp b/firmware/application/ui_freqman.hpp index 87e74f089..6e8da9002 100644 --- a/firmware/application/ui_freqman.hpp +++ b/firmware/application/ui_freqman.hpp @@ -35,7 +35,7 @@ namespace ui { class FreqManBaseView : public View { public: FreqManBaseView( - NavigationView& nav, Widget& default_focus_widget + NavigationView& nav ); void focus() override; @@ -48,7 +48,6 @@ protected: NavigationView& nav_; freqman_error error_ { NO_ERROR }; - Widget& default_focus_widget_; options_t categories { }; std::function on_change_category { nullptr }; std::function on_select_frequency { nullptr }; diff --git a/firmware/application/ui_lcr.cpp b/firmware/application/ui_lcr.cpp index e9c6df997..0a596e04d 100644 --- a/firmware/application/ui_lcr.cpp +++ b/firmware/application/ui_lcr.cpp @@ -23,15 +23,12 @@ #include "ui_lcr.hpp" #include "ui_modemsetup.hpp" -#include "modems.hpp" #include "lcr.hpp" +#include "modems.hpp" #include "baseband_api.hpp" #include "string_format.hpp" -#include "portapack_persistent_memory.hpp" - -#include -#include +#include "serializer.hpp" using namespace portapack; @@ -47,7 +44,6 @@ LCRView::~LCRView() { } void LCRView::paint(Painter& painter) { - size_t i; std::string final_str; static constexpr Style style_orange { @@ -61,7 +57,7 @@ void LCRView::paint(Painter& painter) { static_cast(68) }; - for (i = 0; i < 5; i++) { + for (size_t i = 0; i < 5; i++) { painter.draw_string( screen_pos() + offset, style_orange, @@ -181,7 +177,7 @@ void LCRView::start_tx(const bool scan) { persistent_memory::afsk_space_freq(), repeats, persistent_memory::modem_bw(), - serializer::symbol_count() + serializer::symbol_count(persistent_memory::serial_format()) ); } @@ -201,7 +197,7 @@ LCRView::LCRView(NavigationView& nav) { &text_recap, &options_ec, &button_setrgsb, - &button_txsetup, + &button_modem_setup, &text_status, &progress, &button_transmit, @@ -266,7 +262,7 @@ LCRView::LCRView(NavigationView& nav) { text_prompt(nav, &rgsb, 4); }; - button_txsetup.on_select = [&nav](Button&) { + button_modem_setup.on_select = [&nav](Button&) { nav.push(); }; @@ -294,11 +290,9 @@ LCRView::LCRView(NavigationView& nav) { }; button_clear.on_select = [this, &nav](Button&) { - size_t n; - if (tx_mode == IDLE) { options_ec.set_selected_index(0); // Auto - for (n = 0; n < 5; n++) { + for (size_t n = 0; n < 5; n++) { litteral[n] = " "; checkboxes[n].set_value(true); } diff --git a/firmware/application/ui_lcr.hpp b/firmware/application/ui_lcr.hpp index b1cfd4695..5b5d76862 100644 --- a/firmware/application/ui_lcr.hpp +++ b/firmware/application/ui_lcr.hpp @@ -134,7 +134,7 @@ private: { 8, 24, 80, 32 }, "RGSB" }; - Button button_txsetup { + Button button_modem_setup { { 13 * 8, 24, 128, 32 }, "Modem setup" }; diff --git a/firmware/application/ui_menu.cpp b/firmware/application/ui_menu.cpp index 22c4e5190..128a44a53 100644 --- a/firmware/application/ui_menu.cpp +++ b/firmware/application/ui_menu.cpp @@ -93,10 +93,7 @@ MenuView::MenuView( bool keep_highlight ) : keep_highlight_ { keep_highlight } { - View::set_parent_rect(new_parent_rect); - - displayed_max_ = (parent_rect().size().height() / 24); - arrow_more.set_parent_rect( { 228, (Coord)(displayed_max_ * item_height), 8, 8 } ); + set_parent_rect(new_parent_rect); set_focusable(true); @@ -116,6 +113,14 @@ MenuView::~MenuView() { } } +void MenuView::set_parent_rect(const Rect new_parent_rect) { + + View::set_parent_rect(new_parent_rect); + + displayed_max_ = (parent_rect().size().height() / 24); + arrow_more.set_parent_rect( { 228, (Coord)(displayed_max_ * item_height), 8, 8 } ); +} + void MenuView::on_tick_second() { if (more_ && blink_) arrow_more.set_foreground(Color::white()); diff --git a/firmware/application/ui_menu.hpp b/firmware/application/ui_menu.hpp index c974117cf..5e9e8f88e 100644 --- a/firmware/application/ui_menu.hpp +++ b/firmware/application/ui_menu.hpp @@ -84,11 +84,11 @@ public: size_t highlighted() const; bool set_highlighted(int32_t new_value); + void set_parent_rect(const Rect new_parent_rect) override; void on_focus() override; void on_blur() override; bool on_key(const KeyEvent event) override; bool on_encoder(const EncoderEvent event) override; - private: void update_items(); diff --git a/firmware/application/ui_modemsetup.cpp b/firmware/application/ui_modemsetup.cpp index b4983f610..0b4f2efef 100644 --- a/firmware/application/ui_modemsetup.cpp +++ b/firmware/application/ui_modemsetup.cpp @@ -29,42 +29,36 @@ #include "portapack_shared_memory.hpp" #include "portapack_persistent_memory.hpp" -#include -#include - using namespace portapack; using namespace modems; namespace ui { void ModemSetupView::focus() { - button_setfreq.focus(); + field_baudrate.focus(); } -void ModemSetupView::update_freq(rf::Frequency f) { +/*void ModemSetupView::update_freq(rf::Frequency f) { persistent_memory::set_tuned_frequency(f); button_setfreq.set_text(to_string_short_freq(f)); -} +}*/ ModemSetupView::ModemSetupView( NavigationView& nav ) { - using name_t = std::string; - using value_t = int32_t; - using option_t = std::pair; + using option_t = std::pair; using options_t = std::vector; options_t modem_options; - size_t i; add_children({ &labels, - &button_setfreq, + //&button_setfreq, &field_baudrate, &field_mark, &field_space, - &field_bw, + //&field_bw, &field_repeat, &options_modem, &button_set_modem, @@ -72,11 +66,10 @@ ModemSetupView::ModemSetupView( &button_save }); - for (i = 0; i < MODEM_DEF_COUNT; i++) { + for (size_t i = 0; i < MODEM_DEF_COUNT; i++) { if (modem_defs[i].modulation == AFSK) modem_options.emplace_back(std::make_pair(modem_defs[i].name, i)); } - options_modem.set_options(modem_options); options_modem.set_selected_index(0); @@ -90,19 +83,19 @@ ModemSetupView::ModemSetupView( sym_format.set_sym(2, persistent_memory::serial_format().stop_bits); sym_format.set_sym(3, persistent_memory::serial_format().bit_order); - update_freq(persistent_memory::tuned_frequency()); + //update_freq(persistent_memory::tuned_frequency()); field_mark.set_value(persistent_memory::afsk_mark_freq()); field_space.set_value(persistent_memory::afsk_space_freq()); - field_bw.set_value(persistent_memory::modem_bw() / 1000); + //field_bw.set_value(persistent_memory::modem_bw() / 1000); field_repeat.set_value(persistent_memory::modem_repeat()); - button_setfreq.on_select = [this, &nav](Button&) { + /*button_setfreq.on_select = [this, &nav](Button&) { auto new_view = nav.push(persistent_memory::tuned_frequency()); new_view->on_changed = [this](rf::Frequency f) { update_freq(f); }; - }; + };*/ field_baudrate.set_value(persistent_memory::modem_baudrate()); @@ -121,7 +114,7 @@ ModemSetupView::ModemSetupView( persistent_memory::set_afsk_space(field_space.value()); persistent_memory::set_modem_baudrate(field_baudrate.value()); - persistent_memory::set_modem_bw(field_bw.value() * 1000); + //persistent_memory::set_modem_bw(field_bw.value() * 1000); persistent_memory::set_modem_repeat(field_repeat.value()); serial_format.data_bits = sym_format.get_sym(0) + 6; diff --git a/firmware/application/ui_modemsetup.hpp b/firmware/application/ui_modemsetup.hpp index 045cd92b7..fc47ee0fe 100644 --- a/firmware/application/ui_modemsetup.hpp +++ b/firmware/application/ui_modemsetup.hpp @@ -41,23 +41,23 @@ private: void update_freq(rf::Frequency f); Labels labels { - { { 2 * 8, 4 * 8 }, "Frequency:", Color::light_grey() }, - { { 2 * 8, 11 * 8 }, "Speed: Bps", Color::light_grey() }, + //{ { 2 * 8, 4 * 8 }, "Frequency:", Color::light_grey() }, + { { 2 * 8, 11 * 8 }, "Baudrate:", Color::light_grey() }, { { 2 * 8, 13 * 8 }, "Mark: Hz", Color::light_grey() }, { { 2 * 8, 15 * 8 }, "Space: Hz", Color::light_grey() }, - { { 140, 13 * 8 }, "BW: kHz", Color::light_grey() }, + //{ { 140, 13 * 8 }, "BW: kHz", Color::light_grey() }, { { 140, 15 * 8 }, "Repeat:", Color::light_grey() }, - { { 2 * 8, 19 * 8 }, "Modem preset:", Color::light_grey() }, + { { 1 * 8, 6 * 8 }, "Modem preset:", Color::light_grey() }, { { 2 * 8, 22 * 8 }, "Serial format:", Color::light_grey() } }; - Button button_setfreq { + /*Button button_setfreq { { 13 * 8, 3 * 8, 12 * 8, 32 }, "----.----" - }; + };*/ NumberField field_baudrate { - { 64, 88 }, + { 11 * 8, 11 * 8 }, 5, { 50, 9600 }, 25, @@ -65,7 +65,7 @@ private: }; NumberField field_mark { - { 64, 104 }, + { 8 * 8, 13 * 8 }, 5, { 100, 15000 }, 25, @@ -73,23 +73,23 @@ private: }; NumberField field_space { - { 64, 120 }, + { 8 * 8, 15 * 8 }, 5, { 100, 15000 }, 25, ' ' }; - NumberField field_bw { + /*NumberField field_bw { { 172, 104 }, 2, { 1, 50 }, 1, ' ' - }; + };*/ NumberField field_repeat { - { 204, 120 }, + { 204, 15 * 8 }, 2, { 1, 99 }, 1, @@ -97,7 +97,7 @@ private: }; OptionsField options_modem { - { 16 * 8, 19 * 8 }, + { 15 * 8, 6 * 8 }, 7, { } @@ -110,12 +110,12 @@ private: }; Button button_set_modem { - { 24 * 8, 19 * 8 - 4, 5 * 8, 24 }, + { 23 * 8, 6 * 8 - 4, 6 * 8, 24 }, "SET" }; Button button_save { - { 72, 250, 96, 40 }, + { 9 * 8, 250, 96, 40 }, "Save" }; }; diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 43706c336..949ce9b68 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -285,7 +285,8 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { add_items({ { "ADS-B: Planes", ui::Color::green(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, { "AIS: Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push(); } }, - { "APRS", ui::Color::orange(),&bitmap_icon_aprs, [&nav](){ nav.push(); } }, + { "AFSK", ui::Color::yellow(),&bitmap_icon_receivers, [&nav](){ nav.push(); } }, + { "APRS", ui::Color::grey(), &bitmap_icon_aprs, [&nav](){ nav.push(); } }, { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.push(false); } }, { "ERT: Utility Meters", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push(); } }, @@ -295,7 +296,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { { "TPMS: Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav](){ nav.push(); } }, }); on_left = [&nav](){ nav.pop(); }; - //set_highlighted(3); // Default selection is "Audio" + //set_highlighted(4); // Default selection is "Audio" } /* TransmittersMenuView **************************************************/ diff --git a/firmware/baseband/proc_afskrx.cpp b/firmware/baseband/proc_afskrx.cpp index 9820a55ca..004773757 100644 --- a/firmware/baseband/proc_afskrx.cpp +++ b/firmware/baseband/proc_afskrx.cpp @@ -25,54 +25,119 @@ #include "event_m4.hpp" -#include -#include - void AFSKRxProcessor::execute(const buffer_c8_t& buffer) { - // This is called at 1500Hz + // This is called at 3072000 / 2048 = 1500Hz if (!configured) return; - const auto decim_0_out = decim_0.execute(buffer, dst_buffer); - const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); - const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); + // FM demodulation + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // 2048 / 8 = 256 (512 I/Q samples) + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // 256 / 8 = 32 (64 I/Q samples) + const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // 32 / 2 = 16 (32 I/Q samples) feed_channel_stats(channel_out); auto audio = demod.execute(channel_out, audio_buffer); + + audio_output.write(audio); + // Audio signal processing for (size_t c = 0; c < audio.count; c++) { const int32_t sample_int = audio.p[c] * 32768.0f; - const int32_t audio_sample = __SSAT(sample_int, 16); + int32_t current_sample = __SSAT(sample_int, 16); - /*slicer_sr <<= 1; - slicer_sr |= (audio_sample < 0); // Do we need hysteresis ? - - // Detect transitions to adjust clock - if ((slicer_sr ^ (slicer_sr >> 1)) & 1) { - if (sphase < (0x8000u - sphase_delta_half)) - sphase += sphase_delta_eighth; + current_sample /= 128; + + // Delay line put + delay_line[delay_line_index & 0x3F] = current_sample; + + // Delay line get, and LPF + sample_mixed = (delay_line[(delay_line_index - (samples_per_bit/2)) & 0x3F] * current_sample) / 4; + sample_filtered = prev_mixed + sample_mixed + (prev_filtered / 2); + + delay_line_index++; + + prev_filtered = sample_filtered; + prev_mixed = sample_mixed; + + // Slice + sample_bits <<= 1; + sample_bits |= (sample_filtered < -20) ? 1 : 0; + + // Check for "clean" transition: either 0011 or 1100 + if ((((sample_bits >> 2) ^ sample_bits) & 3) == 3) { + // Adjust phase + if (phase < 0x8000) + phase += 0x800; // Is this a proper value ? else - sphase -= sphase_delta_eighth; + phase -= 0x800; } - sphase += sphase_delta;*/ + phase += phase_inc; - // Symbol time elapsed - //if (sphase >= 0x10000u) { - // sphase &= 0xFFFFu; + if (phase >= 0x10000) { + phase &= 0xFFFF; - rx_data <<= 1; - rx_data |= 1; - - bit_count++; - if (bit_count == 8) { - data_message.byte = rx_data; - shared_memory.application_queue.push(data_message); - bit_count = 0; + if (trigger_word) { + + // Continuous-stream value-triggered mode (AX.25) - UNTESTED + word_bits <<= 1; + word_bits |= (sample_bits & 1); + + bit_counter++; + + if (triggered) { + if (bit_counter == word_length) { + bit_counter = 0; + + data_message.is_data = true; + data_message.value = word_bits & word_mask; + shared_memory.application_queue.push(data_message); + } + } else { + if ((word_bits & word_mask) == trigger_value) { + triggered = !triggered; + bit_counter = 0; + + data_message.is_data = true; + data_message.value = trigger_value; + shared_memory.application_queue.push(data_message); + } + } + + } else { + + // RS232-like modem mode + if (state == WAIT_START) { + if (!(sample_bits & 1)) { + // Got start bit + state = RECEIVE; + bit_counter = 0; + } + } else if (state == WAIT_STOP) { + if (sample_bits & 1) { + // Got stop bit + state = WAIT_START; + } + } else { + word_bits <<= 1; + word_bits |= (sample_bits & 1); + + bit_counter++; + } + + if (bit_counter == word_length) { + bit_counter = 0; + state = WAIT_STOP; + + data_message.is_data = true; + data_message.value = word_bits; + shared_memory.application_queue.push(data_message); + } + } - //} + } } } @@ -82,26 +147,39 @@ void AFSKRxProcessor::on_message(const Message* const message) { } void AFSKRxProcessor::configure(const AFSKRxConfigureMessage& message) { - constexpr size_t decim_0_input_fs = baseband_fs; + /*constexpr size_t decim_0_input_fs = baseband_fs; constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor; constexpr size_t decim_1_input_fs = decim_0_output_fs; constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; - + constexpr size_t channel_filter_input_fs = decim_1_output_fs; const size_t channel_filter_output_fs = channel_filter_input_fs / 2; - const size_t demod_input_fs = channel_filter_output_fs; - - decim_0.configure(taps_16k0_decim_0.taps, 33554432); - decim_1.configure(taps_16k0_decim_1.taps, 131072); - channel_filter.configure(taps_16k0_channel.taps, 2); - demod.configure(demod_input_fs, 5000); + const size_t demod_input_fs = channel_filter_output_fs;*/ - bitrate = message.bitrate; - sphase_delta = 0x10000u * bitrate / 24000; - sphase_delta_half = sphase_delta / 2; // Just for speed - sphase_delta_eighth = sphase_delta / 8; + decim_0.configure(taps_11k0_decim_0.taps, 33554432); + decim_1.configure(taps_11k0_decim_1.taps, 131072); + channel_filter.configure(taps_11k0_channel.taps, 2); + demod.configure(audio_fs, 5000); + + audio_output.configure(audio_24k_hpf_300hz_config, audio_24k_deemph_300_6_config, 0); + + samples_per_bit = audio_fs / message.baudrate; + + phase_inc = (0x10000 * message.baudrate) / audio_fs; + phase = 0; + + trigger_word = message.trigger_word; + word_length = message.word_length; + trigger_value = message.trigger_value; + word_mask = (1 << word_length) - 1; + + // Delay line + delay_line_index = 0; + + triggered = false; + state = WAIT_START; configured = true; } diff --git a/firmware/baseband/proc_afskrx.hpp b/firmware/baseband/proc_afskrx.hpp index 72868a0e6..6504da7a9 100644 --- a/firmware/baseband/proc_afskrx.hpp +++ b/firmware/baseband/proc_afskrx.hpp @@ -30,6 +30,8 @@ #include "dsp_decimate.hpp" #include "dsp_demodulate.hpp" +#include "audio_output.hpp" + #include "fifo.hpp" #include "message.hpp" @@ -41,6 +43,15 @@ public: private: static constexpr size_t baseband_fs = 3072000; + static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; + + size_t samples_per_bit { }; + + enum State { + WAIT_START = 0, + WAIT_STOP, + RECEIVE + }; BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; RSSIThread rssi_thread { NORMALPRIO + 10 }; @@ -56,27 +67,37 @@ private: audio.size() }; - // Can't use FIFO class here since it only allows power-of-two sizes - std::array delay_line { 0 }; + // Array size ok down to 375 bauds (24000 / 375) + std::array delay_line { 0 }; dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; dsp::decimate::FIRAndDecimateComplex channel_filter { }; dsp::demodulate::FM demod { }; + + AudioOutput audio_output { }; - uint32_t bitrate { }; - uint32_t sphase { 0 }; - uint32_t sphase_delta { 0 }; - uint32_t sphase_delta_half { 0 }; - uint32_t sphase_delta_eighth { 0 }; - uint32_t rx_data { 0 }; - uint32_t bit_count { 0 }; + State state { }; + size_t delay_line_index { }; + uint32_t bit_counter { 0 }; + uint32_t word_bits { 0 }; + uint32_t sample_bits { 0 }; + uint32_t phase { }, phase_inc { }; + int32_t sample_mixed { }, prev_mixed { }, sample_filtered { }, prev_filtered { }; + uint32_t word_length { }; + uint32_t word_mask { }; + uint32_t trigger_value { }; bool configured { false }; + bool wait_start { }; + bool bit_value { }; + bool trigger_word { }; + bool triggered { }; + void configure(const AFSKRxConfigureMessage& message); - AFSKDataMessage data_message { 0 }; + AFSKDataMessage data_message { false, 0 }; }; #endif/*__PROC_TPMS_H__*/ diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index d9686c7f2..d193cad80 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -336,13 +336,16 @@ public: class AFSKDataMessage : public Message { public: constexpr AFSKDataMessage( - const uint_fast8_t byte + const bool is_data, + const uint32_t value ) : Message { ID::AFSKData }, - byte { byte } + is_data { is_data }, + value { value } { } - uint_fast8_t byte; + bool is_data; + uint32_t value; }; class ShutdownMessage : public Message { @@ -615,13 +618,22 @@ public: class AFSKRxConfigureMessage : public Message { public: constexpr AFSKRxConfigureMessage( - const uint32_t bitrate + const uint32_t baudrate, + const uint32_t word_length, + const uint32_t trigger_value, + const bool trigger_word ) : Message { ID::AFSKRxConfigure }, - bitrate(bitrate) + baudrate(baudrate), + word_length(word_length), + trigger_value(trigger_value), + trigger_word(trigger_word) { } - const uint32_t bitrate; + const uint32_t baudrate; + const uint32_t word_length; + const uint32_t trigger_value; + const bool trigger_word; }; class PWMRSSIConfigureMessage : public Message { diff --git a/firmware/common/portapack_shared_memory.hpp b/firmware/common/portapack_shared_memory.hpp index 9809ad5f0..b00b45073 100644 --- a/firmware/common/portapack_shared_memory.hpp +++ b/firmware/common/portapack_shared_memory.hpp @@ -26,7 +26,6 @@ #include #include -#include "portapack_shared_memory.hpp" #include "message_queue.hpp" struct JammerChannel { diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 82ee290ae..7a6a10efb 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -538,7 +538,7 @@ void Console::clear() { void Console::write(std::string message) { bool escape = false; - if (visible) { + if (!hidden() && visible()) { const Style& s = style(); const Font& font = s.font; const auto rect = screen_rect(); @@ -546,7 +546,10 @@ void Console::write(std::string message) { for (const auto c : message) { if (escape) { - pen_color = term_colors[c & 7]; + if (c <= 15) + pen_color = term_colors[c & 15]; + else + pen_color = s.foreground; escape = false; } else { if (c == '\n') { @@ -570,7 +573,7 @@ void Console::write(std::string message) { } buffer = message; } else { - buffer += message; + if (buffer.size() < 256) buffer += message; } } @@ -588,7 +591,7 @@ void Console::on_show() { display.scroll_set_area(screen_r.top(), screen_r.bottom()); display.scroll_set_position(0); clear(); - visible = true; + //visible = true; } void Console::on_hide() { @@ -596,9 +599,12 @@ void Console::on_hide() { * position? */ display.scroll_disable(); + //visible = false; } void Console::crlf() { + if (hidden() || !visible()) return; + const Style& s = style(); const auto sr = screen_rect(); const auto line_height = s.font.line_height(); diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 3620cfbe6..437b3958d 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -289,7 +289,7 @@ public: void on_hide() override; private: - bool visible = false; + //bool visible = false; Point pos { 0, 0 }; std::string buffer { }; diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin index d06545efe..4d2072c75 100644 Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ