diff --git a/firmware/application/Makefile b/firmware/application/Makefile index cb1ab48e..2e5bb800 100755 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -148,6 +148,8 @@ CPPSRC = main.cpp \ encoder.cpp \ lcd_ili9341.cpp \ ui.cpp \ + ui_alphanum.cpp \ + ui_about.cpp \ ui_text.cpp \ ui_widget.cpp \ ui_painter.cpp \ @@ -157,6 +159,9 @@ CPPSRC = main.cpp \ ui_rssi.cpp \ ui_channel.cpp \ ui_audio.cpp \ + ui_lcr.cpp \ + ui_rds.cpp \ + ui_jammer.cpp \ ui_font_fixed_8x16.cpp \ ui_setup.cpp \ ui_debug.cpp \ diff --git a/firmware/application/file.hpp b/firmware/application/file.hpp index 0dc085c7..363bd866 100644 --- a/firmware/application/file.hpp +++ b/firmware/application/file.hpp @@ -19,36 +19,34 @@ * Boston, MA 02110-1301, USA. */ -#include "irq_ipc.hpp" +#ifndef __FILE_H__ +#define __FILE_H__ -#include "event.hpp" +#include "ff.h" -#include "ch.h" -#include "hal.h" +#include +#include -#include "lpc43xx_cpp.hpp" -using namespace lpc43xx; +class File { +public: + ~File(); -void m4txevent_interrupt_enable() { - nvicEnableVector(M4CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M4TXEVENT_IRQ_PRIORITY)); -} + bool open_for_append(const std::string file_path); + bool close(); -void m4txevent_interrupt_disable() { - nvicDisableVector(M4CORE_IRQn); -} + bool is_ready(); -extern "C" { + bool read(void* const data, const size_t bytes_to_read); + bool write(const void* const data, const size_t bytes_to_write); -CH_IRQ_HANDLER(M4Core_IRQHandler) { - CH_IRQ_PROLOGUE(); + bool puts(const std::string string); - chSysLockFromIsr(); - events_flag_isr(EVT_MASK_APPLICATION); - chSysUnlockFromIsr(); + bool sync(); - creg::m4txevent::clear(); +private: + const std::string file_path; + + FIL f; +}; - CH_IRQ_EPILOGUE(); -} - -} +#endif/*__FILE_H__*/ diff --git a/firmware/application/m4_startup.cpp b/firmware/application/m4_startup.cpp index 69345414..42ceeb43 100644 --- a/firmware/application/m4_startup.cpp +++ b/firmware/application/m4_startup.cpp @@ -106,7 +106,7 @@ void m4_switch(const char * hash) { // Ask M4 to enter wait loop in RAM BasebandConfiguration baseband_switch { - .mode = SWITCH, + .mode = 255, // DEBUG .sampling_rate = 0, .decimation_factor = 1, }; diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index a36598cf..0b374a16 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -62,7 +62,6 @@ #include "ui_painter.hpp" #include "ui_navigation.hpp" -#include "irq_ipc.hpp" #include "irq_lcd_frame.hpp" #include "irq_controls.hpp" #include "irq_rtc.hpp" diff --git a/firmware/application/transmitter_model.cpp b/firmware/application/transmitter_model.cpp index 6ca9c390..f04002ed 100644 --- a/firmware/application/transmitter_model.cpp +++ b/firmware/application/transmitter_model.cpp @@ -75,31 +75,8 @@ uint32_t TransmitterModel::sampling_rate() const { return baseband_configuration.sampling_rate; } -void TransmitterModel::set_sampling_rate(uint32_t hz) { - baseband_configuration.sampling_rate = hz; - update_baseband_configuration(); -} - -mode_type TransmitterModel::modulation() const { - return baseband_configuration.mode; -} - -void TransmitterModel::set_modulation(mode_type v) { - baseband_configuration.mode = v; - update_modulation(); -} - -uint32_t TransmitterModel::baseband_oversampling() const { - // TODO: Rename decimation_factor. - return baseband_configuration.decimation_factor; -} - -void TransmitterModel::set_baseband_oversampling(uint32_t v) { - baseband_configuration.decimation_factor = v; - update_baseband_configuration(); -} - void TransmitterModel::enable() { + enabled_ = true; radio::set_direction(rf::Direction::Transmit); update_tuning_frequency(); update_rf_amp(); @@ -107,23 +84,30 @@ void TransmitterModel::enable() { update_vga(); update_baseband_bandwidth(); update_baseband_configuration(); - radio::streaming_enable(); +} + +void TransmitterModel::baseband_disable() { + shared_memory.baseband_queue.push_and_wait( + BasebandConfigurationMessage { + .configuration = { }, + } + ); } void TransmitterModel::disable() { - /* TODO: This is a dumb hack to stop baseband from working so hard. */ - BasebandConfigurationMessage message { - .configuration = { - .mode = NONE, - .sampling_rate = 0, - .decimation_factor = 1, - } - }; - shared_memory.baseband_queue.push(message); + enabled_ = false; + baseband_disable(); + // TODO: Responsibility for enabling/disabling the radio is muddy. + // Some happens in ReceiverModel, some inside radio namespace. radio::disable(); } +uint32_t TransmitterModel::baseband_oversampling() const { + // TODO: Rename decimation_factor. + return baseband_configuration.decimation_factor; +} + int32_t TransmitterModel::tuning_offset() { return -(sampling_rate() / 4); } @@ -148,8 +132,8 @@ void TransmitterModel::update_vga() { radio::set_vga_gain(vga_gain_db_); } -void TransmitterModel::update_modulation() { - update_baseband_configuration(); +uint32_t TransmitterModel::modulation() const { + return baseband_configuration.mode; } void TransmitterModel::set_baseband_configuration(const BasebandConfiguration config) { @@ -158,7 +142,12 @@ void TransmitterModel::set_baseband_configuration(const BasebandConfiguration co } void TransmitterModel::update_baseband_configuration() { - radio::streaming_disable(); + // TODO: Move more low-level radio control stuff to M4. It'll enable tighter + // synchronization for things like wideband (sweeping) spectrum analysis, and + // protocols that need quick RX/TX turn-around. + + // Disabling baseband while changing sampling rates seems like a good idea... + baseband_disable(); clock_manager.set_sampling_frequency(sampling_rate() * baseband_oversampling()); update_tuning_frequency(); @@ -166,7 +155,5 @@ void TransmitterModel::update_baseband_configuration() { BasebandConfigurationMessage message { baseband_configuration }; shared_memory.baseband_queue.push(message); - - radio::streaming_enable(); } diff --git a/firmware/application/transmitter_model.hpp b/firmware/application/transmitter_model.hpp index d8506935..f941e294 100644 --- a/firmware/application/transmitter_model.hpp +++ b/firmware/application/transmitter_model.hpp @@ -38,7 +38,7 @@ public: ) : clock_manager(clock_manager) { } - + rf::Frequency tuning_frequency() const; void set_tuning_frequency(rf::Frequency f); @@ -55,13 +55,10 @@ public: void set_vga(int32_t v_db); uint32_t sampling_rate() const; - void set_sampling_rate(uint32_t hz); - - mode_type modulation() const; - void set_modulation(mode_type v); + uint32_t modulation() const; + uint32_t baseband_oversampling() const; - void set_baseband_oversampling(uint32_t v); void enable(); void disable(); @@ -69,12 +66,14 @@ public: void set_baseband_configuration(const BasebandConfiguration config); private: + rf::Frequency frequency_step_ { 25000 }; + bool enabled_ { false }; bool rf_amp_ { true }; int32_t lna_gain_db_ { 0 }; uint32_t baseband_bandwidth_ { max2837::filter::bandwidth_minimum }; int32_t vga_gain_db_ { 8 }; BasebandConfiguration baseband_configuration { - .mode = NONE, + .mode = 1, .sampling_rate = 2280000, .decimation_factor = 1, }; @@ -89,6 +88,8 @@ private: void update_vga(); void update_modulation(); void update_baseband_configuration(); + + void baseband_disable(); }; #endif/*__TRANSMITTER_MODEL_H__*/ diff --git a/firmware/application/ui_about.cpp b/firmware/application/ui_about.cpp index 978965e3..235d7294 100644 --- a/firmware/application/ui_about.cpp +++ b/firmware/application/ui_about.cpp @@ -26,6 +26,7 @@ #include "ymdata.hpp" #include "portapack.hpp" +#include "event_m0.hpp" #include "ui_about.hpp" #include "touch.hpp" @@ -44,23 +45,21 @@ using namespace portapack; namespace ui { void AboutView::on_show() { - auto& message_map = context().message_map(); - // Just in case - message_map.unregister_handler(Message::ID::DisplayFrameSync); + EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync); // "Vertical blank interrupt" - message_map.register_handler(Message::ID::DisplayFrameSync, + EventDispatcher::message_map().register_handler(Message::ID::DisplayFrameSync, [this](const Message* const) { update(); } ); // Just in case - message_map.unregister_handler(Message::ID::FIFOSignal); + EventDispatcher::message_map().unregister_handler(Message::ID::FIFOSignal); // Handle baseband asking to fill up FIFO - message_map.register_handler(Message::ID::FIFOSignal, + EventDispatcher::message_map().register_handler(Message::ID::FIFOSignal, [this](Message* const p) { FIFODataMessage datamessage; const auto message = static_cast(p); @@ -349,6 +348,7 @@ void AboutView::update() { refresh_cnt++; } + // Slowly increase volume to avoid jumpscare if (headphone_vol < (70<<2)) { portapack::audio_codec.set_headphone_volume(volume_t::decibel((headphone_vol/4) - 99) + wolfson::wm8731::headphone_gain_range.max); headphone_vol++; @@ -370,14 +370,13 @@ void AboutView::ym_init() { } AboutView::AboutView( - NavigationView& nav, - TransmitterModel& transmitter_model -) : transmitter_model(transmitter_model) + NavigationView& nav +) { uint8_t p, c; transmitter_model.set_baseband_configuration({ - .mode = PLAY_AUDIO, + .mode = 5, .sampling_rate = 1536000, .decimation_factor = 1, }); @@ -412,8 +411,7 @@ AboutView::AboutView( ym_init(); button_ok.on_select = [this,&nav](Button&){ - auto& message_map = context().message_map(); - message_map.unregister_handler(Message::ID::DisplayFrameSync); + EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync); if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this nav.pop(); }; diff --git a/firmware/application/ui_about.hpp b/firmware/application/ui_about.hpp index c0ca162c..2db4b8bb 100644 --- a/firmware/application/ui_about.hpp +++ b/firmware/application/ui_about.hpp @@ -34,7 +34,7 @@ namespace ui { class AboutView : public View { public: - AboutView(NavigationView& nav, TransmitterModel& transmitter_model); + AboutView(NavigationView& nav); ~AboutView(); void on_show() override; @@ -54,8 +54,6 @@ private: bool same; } ymreg_t; - TransmitterModel& transmitter_model; - uint16_t headphone_vol = 5<<2; ymreg_t ym_regs[14]; diff --git a/firmware/application/ui_afskrx.cpp b/firmware/application/ui_afskrx.cpp index fa6b6eba..47375efc 100644 --- a/firmware/application/ui_afskrx.cpp +++ b/firmware/application/ui_afskrx.cpp @@ -24,19 +24,17 @@ #include "ui_spectrum.hpp" #include "ui_console.hpp" +#include "event_m0.hpp" #include "ff.h" #include "portapack.hpp" using namespace portapack; -#include "ui_receiver.hpp" - namespace ui { AFSKRXView::AFSKRXView( - NavigationView& nav, - ReceiverModel& receiver_model -) : receiver_model(receiver_model) + NavigationView& nav +) { add_children({ { &button_done, @@ -48,7 +46,7 @@ AFSKRXView::AFSKRXView( }; receiver_model.set_baseband_configuration({ - .mode = RX_AFSK, + .mode = 6, .sampling_rate = 3072000, .decimation_factor = 4, }); @@ -60,8 +58,7 @@ AFSKRXView::~AFSKRXView() { } void AFSKRXView::on_show() { - auto& message_map = context().message_map(); - message_map.register_handler(Message::ID::AFSKData, + EventDispatcher::message_map().register_handler(Message::ID::AFSKData, [this](Message* const p) { const auto message = static_cast(p); this->on_data_afsk(*message); @@ -72,8 +69,7 @@ void AFSKRXView::on_show() { } void AFSKRXView::on_hide() { - auto& message_map = context().message_map(); - message_map.unregister_handler(Message::ID::AFSKData); + EventDispatcher::message_map().unregister_handler(Message::ID::AFSKData); } void AFSKRXView::on_data_afsk(const AFSKDataMessage& message) { diff --git a/firmware/application/ui_afskrx.hpp b/firmware/application/ui_afskrx.hpp index 9651eb55..ccd37a3b 100644 --- a/firmware/application/ui_afskrx.hpp +++ b/firmware/application/ui_afskrx.hpp @@ -42,7 +42,7 @@ namespace ui { class AFSKRXView : public View { public: - AFSKRXView(NavigationView& nav, ReceiverModel& receiver_model); + AFSKRXView(NavigationView& nav); ~AFSKRXView(); void focus() override; @@ -51,8 +51,6 @@ public: void on_hide() override; private: - ReceiverModel& receiver_model; - std::unique_ptr widget_content; Button button_done { diff --git a/firmware/application/ui_afsksetup.cpp b/firmware/application/ui_afsksetup.cpp index 2176fb96..e445b344 100644 --- a/firmware/application/ui_afsksetup.cpp +++ b/firmware/application/ui_afsksetup.cpp @@ -66,9 +66,8 @@ void AFSKSetupView::updfreq(rf::Frequency f) { } AFSKSetupView::AFSKSetupView( - NavigationView& nav, - TransmitterModel& transmitter_model -) : transmitter_model(transmitter_model) + NavigationView& nav +) { uint8_t rpt; diff --git a/firmware/application/ui_afsksetup.hpp b/firmware/application/ui_afsksetup.hpp index 297f4a11..0862d518 100644 --- a/firmware/application/ui_afsksetup.hpp +++ b/firmware/application/ui_afsksetup.hpp @@ -36,15 +36,13 @@ namespace ui { class AFSKSetupView : public View { public: - AFSKSetupView(NavigationView& nav, TransmitterModel& transmitter_model); + AFSKSetupView(NavigationView& nav); void updfreq(rf::Frequency f); void focus() override; void paint(Painter& painter) override; private: - TransmitterModel& transmitter_model; - Text text_title { { 40, 32, 160, 16 }, "AFSK modulator setup" diff --git a/firmware/application/ui_alphanum.cpp b/firmware/application/ui_alphanum.cpp index 9c433868..33578b10 100644 --- a/firmware/application/ui_alphanum.cpp +++ b/firmware/application/ui_alphanum.cpp @@ -110,7 +110,7 @@ void AlphanumView::set_uppercase() { size_t n = 0; for(auto& button : buttons) { - add_child(&button); + //add_child(&button); const std::string label { key_caps[n] }; @@ -125,7 +125,7 @@ void AlphanumView::set_lowercase() { size_t n = 0; for(auto& button : buttons) { - add_child(&button); + //add_child(&button); const std::string label { key_caps[n] }; diff --git a/firmware/application/ui_debug.cpp b/firmware/application/ui_debug.cpp index 7e26d571..cdfdc54c 100644 --- a/firmware/application/ui_debug.cpp +++ b/firmware/application/ui_debug.cpp @@ -245,20 +245,6 @@ void RegistersView::focus() { button_done.focus(); } -void DebugLCRView::paint(Painter& painter) { - const Point offset = { - static_cast(32), - static_cast(32) - }; - - const auto text = to_string_hex(fr, 2); - painter.draw_string( - screen_pos() + offset, - style(), - text - ); -} - char hexify(char in) { if (in > 9) in += 7; return in + 0x30; @@ -301,26 +287,26 @@ void DebugLCRView::focus() { DebugMenuView::DebugMenuView(NavigationView& nav) { add_items<8>({ { - { "Memory", [&nav](){ nav.push(); } }, - { "Radio State", [&nav](){ nav.push(); } }, - { "SD Card", [&nav](){ nav.push(); } }, - { "RFFC5072", [&nav](){ nav.push( + { "Memory", ui::Color::white(), [&nav](){ nav.push(); } }, + { "Radio State", ui::Color::white(), [&nav](){ nav.push(); } }, + { "SD Card", ui::Color::white(), [&nav](){ nav.push(); } }, + { "RFFC5072", ui::Color::white(), [&nav](){ nav.push( "RFFC5072", RegistersWidgetConfig { 31, 2, 4, 4 }, [](const size_t register_number) { return radio::first_if.read(register_number); } ); } }, - { "MAX2837", [&nav](){ nav.push( + { "MAX2837", ui::Color::white(), [&nav](){ nav.push( "MAX2837", RegistersWidgetConfig { 32, 2, 3, 4 }, [](const size_t register_number) { return radio::second_if.read(register_number); } ); } }, - { "Si5351C", [&nav](){ nav.push( + { "Si5351C", ui::Color::white(), [&nav](){ nav.push( "Si5351C", RegistersWidgetConfig { 96, 2, 2, 8 }, [](const size_t register_number) { return portapack::clock_generator.read_register(register_number); } ); } }, - { "WM8731", [&nav](){ nav.push( + { "WM8731", ui::Color::white(), [&nav](){ nav.push( "WM8731", RegistersWidgetConfig { wolfson::wm8731::reg_count, 1, 3, 4 }, [](const size_t register_number) { return portapack::audio_codec.read(register_number); } ); } }, - { "Temperature", [&nav](){ nav.push(); } }, + { "Temperature", ui::Color::white(), [&nav](){ nav.push(); } }, } }); on_left = [&nav](){ nav.pop(); }; } diff --git a/firmware/application/ui_debug.hpp b/firmware/application/ui_debug.hpp index ecae1bdb..3340c969 100644 --- a/firmware/application/ui_debug.hpp +++ b/firmware/application/ui_debug.hpp @@ -246,8 +246,6 @@ public: void focus() override; - void paint(Painter& painter) override; - private: Text text_lcr1 { { 16, 32, 208, 8 }, diff --git a/firmware/application/ui_jammer.cpp b/firmware/application/ui_jammer.cpp index 4707a6c9..4f29b010 100644 --- a/firmware/application/ui_jammer.cpp +++ b/firmware/application/ui_jammer.cpp @@ -29,6 +29,8 @@ #include "hackrf_gpio.hpp" #include "portapack.hpp" #include "radio.hpp" +#include "string_format.hpp" +#include "event_m0.hpp" #include "hackrf_hal.hpp" #include "portapack_shared_memory.hpp" @@ -37,7 +39,7 @@ #include #include -using namespace hackrf::one; +using namespace portapack; namespace ui { @@ -146,9 +148,8 @@ void JammerView::updfreq(uint8_t id, rf::Frequency f) { } JammerView::JammerView( - NavigationView& nav, - TransmitterModel& transmitter_model -) : transmitter_model(transmitter_model) + NavigationView& nav +) { static constexpr Style style_val { @@ -169,7 +170,11 @@ JammerView::JammerView( .foreground = Color::grey(), }; - transmitter_model.set_modulation(TX_JAMMER); + transmitter_model.set_baseband_configuration({ + .mode = 3, + .sampling_rate = 1536000, // ? + .decimation_factor = 1, + }); add_children({ { &text_type, @@ -232,11 +237,9 @@ JammerView::JammerView( button_transmit.on_select = [this,&transmitter_model](Button&) { uint8_t i = 0; rf::Frequency t, range_lower; - auto& message_map = context().message_map(); + EventDispatcher::message_map().unregister_handler(Message::ID::Retune); - message_map.unregister_handler(Message::ID::Retune); - - message_map.register_handler(Message::ID::Retune, + EventDispatcher::message_map().register_handler(Message::ID::Retune, [this,&transmitter_model](Message* const p) { const auto message = static_cast(p); if (message->freq > 0) { diff --git a/firmware/application/ui_jammer.hpp b/firmware/application/ui_jammer.hpp index f7174450..ac30ce01 100644 --- a/firmware/application/ui_jammer.hpp +++ b/firmware/application/ui_jammer.hpp @@ -36,7 +36,7 @@ namespace ui { class JammerView : public View { public: - JammerView(NavigationView& nav, TransmitterModel& transmitter_model); + JammerView(NavigationView& nav); ~JammerView(); void updfreq(uint8_t id, rf::Frequency f); @@ -117,7 +117,6 @@ private: bool jamming = false; rf::Frequency f; - TransmitterModel& transmitter_model; Text text_type { { 1 * 8, 1 * 16, 40, 16 }, diff --git a/firmware/application/ui_lcr.cpp b/firmware/application/ui_lcr.cpp index c3f166f3..76fd9749 100644 --- a/firmware/application/ui_lcr.cpp +++ b/firmware/application/ui_lcr.cpp @@ -31,8 +31,11 @@ #include "ff.h" #include "hackrf_gpio.hpp" #include "portapack.hpp" +#include "event_m0.hpp" #include "radio.hpp" +#include "string_format.hpp" + #include "hackrf_hal.hpp" #include "portapack_shared_memory.hpp" #include "portapack_persistent_memory.hpp" @@ -40,7 +43,7 @@ #include #include -using namespace hackrf::one; +using namespace portapack; namespace ui { @@ -176,9 +179,8 @@ void LCRView::paint(Painter& painter) { } LCRView::LCRView( - NavigationView& nav, - TransmitterModel& transmitter_model -) : transmitter_model(transmitter_model) + NavigationView& nav +) { char finalstr[24] = {0}; @@ -188,7 +190,12 @@ LCRView::LCRView( .foreground = Color::black(), }; - transmitter_model.set_modulation(TX_LCR); + transmitter_model.set_baseband_configuration({ + .mode = 1, + .sampling_rate = 1536000, // Is this right ? + .decimation_factor = 1, + }); + transmitter_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); memset(litteral, 0, 5*8); memset(rgsb, 0, 5); @@ -277,8 +284,6 @@ LCRView::LCRView( }; button_transmit.on_select = [this,&transmitter_model](Button&){ - auto& message_map = context().message_map(); - make_frame(); shared_memory.afsk_samples_per_bit = 228000/portapack::persistent_memory::afsk_bitrate(); @@ -293,7 +298,7 @@ LCRView::LCRView( shared_memory.afsk_transmit_done = false; shared_memory.afsk_repeat = (portapack::persistent_memory::afsk_config() >> 8) & 0xFF; - message_map.register_handler(Message::ID::TXDone, + EventDispatcher::message_map().register_handler(Message::ID::TXDone, [this,&transmitter_model](Message* const p) { char str[8]; const auto message = static_cast(p); @@ -319,7 +324,7 @@ LCRView::LCRView( }; button_txsetup.on_select = [&nav, &transmitter_model](Button&){ - nav.push(new AFSKSetupView { nav, transmitter_model }); + nav.push(new AFSKSetupView { nav }); }; button_exit.on_select = [&nav](Button&){ diff --git a/firmware/application/ui_lcr.hpp b/firmware/application/ui_lcr.hpp index 76368087..76543c64 100644 --- a/firmware/application/ui_lcr.hpp +++ b/firmware/application/ui_lcr.hpp @@ -36,7 +36,7 @@ namespace ui { class LCRView : public View { public: - LCRView(NavigationView& nav, TransmitterModel& transmitter_model); + LCRView(NavigationView& nav); ~LCRView(); void make_frame(); @@ -63,7 +63,6 @@ private: char lcrframe[256]; char lcrframe_f[256]; rf::Frequency f; - TransmitterModel& transmitter_model; Text text_status { { 168, 196, 64, 16 }, diff --git a/firmware/application/ui_loadmodule.cpp b/firmware/application/ui_loadmodule.cpp index 36569ed9..865864c6 100644 --- a/firmware/application/ui_loadmodule.cpp +++ b/firmware/application/ui_loadmodule.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek * * This file is part of PortaPack. * @@ -24,10 +25,12 @@ #include "ch.h" #include "ff.h" +#include "event_m0.hpp" #include "hackrf_gpio.hpp" #include "portapack.hpp" #include "portapack_shared_memory.hpp" #include "hackrf_hal.hpp" +#include "string_format.hpp" #include #include @@ -45,18 +48,17 @@ void LoadModuleView::paint(Painter& painter) { } void LoadModuleView::on_hide() { - auto& message_map = context().message_map(); - message_map.unregister_handler(Message::ID::ReadyForSwitch); + EventDispatcher::message_map().unregister_handler(Message::ID::ReadyForSwitch); + EventDispatcher::message_map().unregister_handler(Message::ID::ModuleID); } void LoadModuleView::on_show() { // Ask for MD5 signature and compare ModuleIDMessage message; - auto& message_map = context().message_map(); + + //message_map.unregister_handler(Message::ID::ModuleID); - message_map.unregister_handler(Message::ID::ModuleID); - - message_map.register_handler(Message::ID::ModuleID, + EventDispatcher::message_map().register_handler(Message::ID::ModuleID, [this](Message* const p) { uint8_t c; const auto message = static_cast(p); @@ -78,13 +80,66 @@ void LoadModuleView::on_show() { shared_memory.baseband_queue.push(message); } +int LoadModuleView::load_image() { + const char magic[6] = {'P', 'P', 'M', ' ', 0x02, 0x00}; + UINT bw; + uint8_t i; + uint32_t cnt; + char md5sum[16]; + FILINFO modinfo; + FIL modfile; + DIR rootdir; + FRESULT res; + + // Scan SD card root directory for files with the right MD5 fingerprint at the right location + if (f_opendir(&rootdir, "/") == FR_OK) { + for (;;) { + res = f_readdir(&rootdir, &modinfo); + if (res != FR_OK || modinfo.fname[0] == 0) break; // Reached last file, abort + // Only care about files with .bin extension + if ((!(modinfo.fattrib & AM_DIR)) && (modinfo.fname[9] == 'B') && (modinfo.fname[10] == 'I') && (modinfo.fname[11] == 'N')) { + res = f_open(&modfile, modinfo.fname, FA_OPEN_EXISTING | FA_READ); + if (res != FR_OK) return 0; + // Magic bytes and version check + f_read(&modfile, &md5sum, 6, &bw); + for (i = 0; i < 6; i++) + if (md5sum[i] != magic[i]) break; + if (i == 6) { + f_lseek(&modfile, 26); + f_read(&modfile, &md5sum, 16, &bw); + for (i = 0; i < 16; i++) + if (md5sum[i] != _hash[i]) break; + // f_read can't read more than 512 bytes at a time ? + if (i == 16) { + f_lseek(&modfile, 512); + for (cnt = 0; cnt < 64; cnt++) { + if (f_read(&modfile, reinterpret_cast(portapack::memory::map::m4_code.base() + (cnt * 512)), 512, &bw)) return 0; + } + f_close(&modfile); + f_closedir(&rootdir); + LPC_RGU->RESET_CTRL[0] = (1 << 13); + return 1; + } + } + f_close(&modfile); + } + } + f_closedir(&rootdir); + } + + return 0; +} + void LoadModuleView::loadmodule() { - auto& message_map = context().message_map(); - message_map.register_handler(Message::ID::ReadyForSwitch, + //message_map.unregister_handler(Message::ID::ReadyForSwitch); + + EventDispatcher::message_map().register_handler(Message::ID::ReadyForSwitch, [this](Message* const p) { (void)p; - if (m4_load_image()) { - text_info.set("Module loaded :)"); + if (load_image()) { + text_info.set(to_string_hex(*((unsigned int*)0x10080000),8)); + //text_infob.set(to_string_hex(*((unsigned int*)0x10080004),8)); + text_infob.set("Module loaded :)"); _mod_loaded = true; } else { text_info.set("Module not found :("); @@ -104,6 +159,7 @@ LoadModuleView::LoadModuleView( { add_children({ { &text_info, + &text_infob, &button_ok } }); diff --git a/firmware/application/ui_loadmodule.hpp b/firmware/application/ui_loadmodule.hpp index ab2dfc67..605e1314 100644 --- a/firmware/application/ui_loadmodule.hpp +++ b/firmware/application/ui_loadmodule.hpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek * * This file is part of PortaPack. * @@ -31,7 +32,7 @@ namespace ui { class LoadModuleView : public View { public: - LoadModuleView(NavigationView& nav, const char * hash, View* new_view); + LoadModuleView(NavigationView& nav, const char * hash, View * new_view); void loadmodule(); void on_show() override; @@ -40,12 +41,17 @@ public: void paint(Painter& painter) override; private: + int load_image(void); const char * _hash; bool _mod_loaded = false; Text text_info { { 8, 64, 224, 16 }, - "Searching module..." + "-" + }; + Text text_infob { + { 8, 64+16, 224, 16 }, + "-" }; Button button_ok { diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 711931ea..960e5c57 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -108,6 +108,10 @@ View* NavigationView::push_view(std::unique_ptr new_view) { return p; } +void NavigationView::push(View* v) { + push_view(std::unique_ptr(v)); +} + void NavigationView::pop() { // Can't pop last item from stack. if( view_stack.size() > 1 ) { @@ -149,9 +153,9 @@ void NavigationView::focus() { TranspondersMenuView::TranspondersMenuView(NavigationView& nav) { add_items<3>({ { - { "AIS: Boats", [&nav](){ nav.push(); } }, - { "ERT: Utility Meters", [&nav](){ nav.push(); } }, - { "TPMS: Cars", [&nav](){ nav.push(); } }, + { "AIS: Boats", ui::Color::white(), [&nav](){ nav.push(); } }, + { "ERT: Utility Meters", ui::Color::white(), [&nav](){ nav.push(); } }, + { "TPMS: Cars", ui::Color::white(), [&nav](){ nav.push(); } }, } }); } @@ -159,8 +163,8 @@ TranspondersMenuView::TranspondersMenuView(NavigationView& nav) { ReceiverMenuView::ReceiverMenuView(NavigationView& nav) { add_items<2>({ { - { "Audio", [&nav](){ nav.push(); } }, - { "Transponders", [&nav](){ nav.push(); } }, + { "Audio", ui::Color::white(), [&nav](){ nav.push(); } }, + { "Transponders", ui::Color::white(), [&nav](){ nav.push(); } }, } }); } @@ -168,24 +172,24 @@ ReceiverMenuView::ReceiverMenuView(NavigationView& nav) { SystemMenuView::SystemMenuView(NavigationView& nav) { add_items<10>({ { - { "Play dead", ui::Color::red(), [&nav](){ nav.push(new PlayDeadView { nav, false }); } }, - { "Receiver", ui::Color::cyan(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband, new ReceiverMenuView { nav, receiver_model }}); } }, + { "Play dead", ui::Color::red(), [&nav](){ nav.push(false); } }, + { "Receiver", ui::Color::cyan(), [&nav](){ nav.push(md5_baseband, new ReceiverMenuView(nav)); } }, //{ "Nordic/BTLE RX", ui::Color::cyan(), [&nav](){ nav.push(new NotImplementedView { nav }); } }, - { "Jammer", ui::Color::white(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband, new JammerView { nav, transmitter_model }}); } }, + { "Jammer", ui::Color::white(), [&nav](){ nav.push(md5_baseband, new JammerView(nav)); } }, //{ "Audio file TX", ui::Color::white(), [&nav](){ nav.push(new NotImplementedView { nav }); } }, //{ "Encoder TX", ui::Color::green(), [&nav](){ nav.push(new NotImplementedView { nav }); } }, //{ "Whistle", ui::Color::purple(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband, new WhistleView { nav, transmitter_model }}); } }, //{ "SIGFOX RX", ui::Color::orange(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband, new SIGFRXView { nav, receiver_model }}); } }, - { "RDS TX", ui::Color::yellow(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new RDSView { nav, transmitter_model }}); } }, - { "Xylos TX", ui::Color::orange(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new XylosView { nav, transmitter_model }}); } }, + { "RDS TX", ui::Color::yellow(), [&nav](){ nav.push(md5_baseband_tx, new RDSView(nav)); } }, + { "Xylos TX", ui::Color::orange(), [&nav](){ nav.push(md5_baseband_tx, new XylosView(nav)); } }, //{ "Xylos RX", ui::Color::green(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new XylosRXView { nav, receiver_model }}); } }, //{ "AFSK RX", ui::Color::cyan(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband, new AFSKRXView { nav, receiver_model }}); } }, - { "TEDI/LCR TX", ui::Color::yellow(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new LCRView { nav, transmitter_model }}); } }, + { "TEDI/LCR TX", ui::Color::yellow(), [&nav](){ nav.push(md5_baseband_tx, new LCRView(nav)); } }, //{ "Numbers station", ui::Color::purple(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new NumbersStationView { nav, transmitter_model }}); } }, - { "Setup", ui::Color::white(), [&nav](){ nav.push(new SetupMenuView { nav }); } }, - { "About", ui::Color::white(), [&nav](){ nav.push(new AboutView { nav, transmitter_model }); } }, - { "Debug", ui::Color::white(), [&nav](){ nav.push(new DebugMenuView { nav }); } }, - { "HackRF", ui::Color::white(), [&nav](){ nav.push(new HackRFFirmwareView { nav }); } }, + { "Setup", ui::Color::white(), [&nav](){ nav.push(); } }, + { "About", ui::Color::white(), [&nav](){ nav.push(); } }, + { "Debug", ui::Color::white(), [&nav](){ nav.push(); } }, + { "HackRF", ui::Color::white(), [&nav](){ nav.push(); } }, } }); } @@ -266,6 +270,41 @@ HackRFFirmwareView::HackRFFirmwareView(NavigationView& nav) { } }); } +/* PlayDeadView **********************************************************/ + +void PlayDeadView::focus() { + button_done.focus(); +} + +PlayDeadView::PlayDeadView(NavigationView& nav, bool booting) { + _booting = booting; + persistent_memory::set_playing_dead(0x59); + + add_children({ { + &text_playdead1, + &text_playdead2, + &button_done, + } }); + + button_done.on_dir = [this,&nav](Button&, KeyEvent key){ + sequence = (sequence<<3) | static_cast::type>(key); + }; + + button_done.on_select = [this,&nav](Button&){ + if (sequence == persistent_memory::playdead_sequence()) { + persistent_memory::set_playing_dead(0); + if (_booting) { + nav.pop(); + nav.push(); + } else { + nav.pop(); + } + } else { + sequence = 0; + } + }; +} + void HackRFFirmwareView::focus() { button_no.focus(); } diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 4e00b4e3..2e902f4d 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -82,6 +82,8 @@ public: T* push(Args&&... args) { return reinterpret_cast(push_view(std::unique_ptr(new T(*this, std::forward(args)...)))); } + + void push(View* v); void pop(); @@ -120,6 +122,29 @@ private: }; }; +class PlayDeadView : public View { +public: + PlayDeadView(NavigationView& nav, bool booting); + void focus() override; + +private: + bool _booting; + uint32_t sequence = 0; + Text text_playdead1 { + { 6 * 8, 7 * 16, 14 * 8, 16 }, + "Firmware error" + }; + Text text_playdead2 { + { 6 * 8, 9 * 16, 16 * 8, 16 }, + "0x1400_0000 : 2C" + }; + + Button button_done { + { 240, 0, 1, 1 }, + "" + }; +}; + class ReceiverMenuView : public MenuView { public: ReceiverMenuView(NavigationView& nav); diff --git a/firmware/application/ui_rds.cpp b/firmware/application/ui_rds.cpp index c9ea6e2d..c94c1f95 100644 --- a/firmware/application/ui_rds.cpp +++ b/firmware/application/ui_rds.cpp @@ -34,7 +34,7 @@ #include -using namespace hackrf::one; +using namespace portapack; namespace ui { @@ -134,9 +134,8 @@ void RDSView::paint(Painter& painter) { } RDSView::RDSView( - NavigationView& nav, - TransmitterModel& transmitter_model -) : transmitter_model(transmitter_model) + NavigationView& nav +) { transmitter_model.set_tuning_frequency(93000000); strcpy(psname, "TEST1234"); diff --git a/firmware/application/ui_rds.hpp b/firmware/application/ui_rds.hpp index e417fcc6..24586647 100644 --- a/firmware/application/ui_rds.hpp +++ b/firmware/application/ui_rds.hpp @@ -86,7 +86,7 @@ private: */ class RDSView : public View { public: - RDSView(NavigationView& nav, TransmitterModel& transmitter_model); + RDSView(NavigationView& nav); ~RDSView(); void focus() override; @@ -94,7 +94,6 @@ public: private: char psname[9]; - TransmitterModel& transmitter_model; Text text_title { { 76, 16, 88, 16 }, diff --git a/firmware/application/ui_setup.cpp b/firmware/application/ui_setup.cpp index 809b42dc..0a0c7062 100644 --- a/firmware/application/ui_setup.cpp +++ b/firmware/application/ui_setup.cpp @@ -20,13 +20,16 @@ */ #include "ui_setup.hpp" - +#include "string_format.hpp" #include "portapack_persistent_memory.hpp" +#include "ui_font_fixed_8x16.hpp" + #include "lpc43xx_cpp.hpp" using namespace lpc43xx; #include "portapack.hpp" using portapack::receiver_model; +using namespace portapack; namespace ui { @@ -167,12 +170,6 @@ void AntennaBiasSetupView::focus() { button_done.focus(); } - - -void AboutView::focus() { - button_ok.focus(); -} - void SetTouchCalibView::focus() { button_ok.focus(); } @@ -433,13 +430,13 @@ void ModInfoView::focus() { SetupMenuView::SetupMenuView(NavigationView& nav) { add_items<7>({ { - { "SD card modules", ui::Color::white(), [&nav](){ nav.push(new ModInfoView { nav }); } }, - { "Date/Time", ui::Color::white(), [&nav](){ nav.push(new SetDateTimeView { nav }); } }, + { "SD card modules", ui::Color::white(), [&nav](){ nav.push(); } }, + { "Date/Time", ui::Color::white(), [&nav](){ nav.push(); } }, { "Frequency correction", ui::Color::white(), [&nav](){ nav.push(); } }, - { "Antenna Bias Voltage", [&nav](){ nav.push(); } }, - { "Touch screen", ui::Color::white(), [&nav](){ nav.push(new SetTouchCalibView { nav }); } }, - { "Play dead", ui::Color::red(), [&nav](){ nav.push(new SetPlayDeadView { nav }); } }, - { "UI", ui::Color::white(), [&nav](){ nav.push(new SetUIView { nav }); } }, + { "Antenna Bias Voltage", ui::Color::white(), [&nav](){ nav.push(); } }, + { "Touch screen", ui::Color::white(), [&nav](){ nav.push(); } }, + { "Play dead", ui::Color::red(), [&nav](){ nav.push(); } }, + { "UI", ui::Color::white(), [&nav](){ nav.push(); } }, } }); on_left = [&nav](){ nav.pop(); }; } diff --git a/firmware/application/ui_setup.hpp b/firmware/application/ui_setup.hpp index a1383851..9f650b43 100644 --- a/firmware/application/ui_setup.hpp +++ b/firmware/application/ui_setup.hpp @@ -25,6 +25,7 @@ #include "ui_widget.hpp" #include "ui_menu.hpp" #include "ui_navigation.hpp" +#include "ff.h" #include @@ -252,6 +253,132 @@ private: }; }; +class SetUIView : public View { +public: + SetUIView(NavigationView& nav); + void focus() override; + +private: + Checkbox checkbox_showsplash { + { 3 * 8, 2 * 16}, + "Show splash" + }; + + Checkbox checkbox_bloff { + { 3 * 8, 4 * 16}, + "Backlight off after:" + }; + + OptionsField options_bloff { + { 10 * 8, 5 * 16 + 4 }, + 10, + { + { "5 seconds ", 0 }, + { "15 seconds", 1 }, + { "1 minute ", 2 }, + { "5 minutes ", 3 }, + { "10 minutes", 4 } + } + }; + + Button button_ok { + { 4 * 8, 272, 64, 24 }, + "Ok" + }; +}; + +class SetPlayDeadView : public View { +public: + SetPlayDeadView(NavigationView& nav); + void focus() override; +private: + bool entermode = false; + uint32_t sequence = 0; + uint8_t keycount, key_code; + char sequence_txt[11]; + + Text text_sequence { + { 64, 32, 14 * 8, 16 }, + "Enter sequence", + }; + + Button button_enter { + { 16, 192, 96, 24 }, + "Enter" + }; + Button button_cancel { + { 128, 192, 96, 24 }, + "Cancel" + }; +}; + +class ModInfoView : public View { +public: + ModInfoView(NavigationView& nav); + void focus() override; + void on_show() override; + +private: + void update_infos(uint8_t modn); + + typedef struct moduleinfo_t{ + char filename[9]; + uint16_t version; + uint32_t size; + char md5[16]; + char name[16]; + char description[214]; + } moduleinfo_t; + + moduleinfo_t module_list[8]; // 8 max for now + + Text text_modcount { + { 2 * 8, 1 * 16, 18 * 8, 16 }, + "Searching..." + }; + + OptionsField option_modules { + { 2 * 8, 2 * 16 }, + 24, + { { "-", 0 } + } + }; + + Text text_name { + { 2 * 8, 4 * 16, 5 * 8, 16 }, + "Name:" + }; + Text text_namestr { + { 8 * 8, 4 * 16, 16 * 8, 16 }, + "..." + }; + Text text_size { + { 2 * 8, 5 * 16, 5 * 8, 16 }, + "Size:" + }; + Text text_sizestr { + { 8 * 8, 5 * 16, 16 * 8, 16 }, + "..." + }; + Text text_md5 { + { 2 * 8, 6 * 16, 4 * 8, 16 }, + "MD5:" + }; + Text text_md5_a { + { 7 * 8, 6 * 16, 16 * 8, 16 }, + "..." + }; + Text text_md5_b { + { 7 * 8, 7 * 16, 16 * 8, 16 }, + "..." + }; + + Button button_ok { + { 4 * 8, 272, 64, 24 }, + "Ok" + }; +}; + class SetupMenuView : public MenuView { public: SetupMenuView(NavigationView& nav); diff --git a/firmware/application/ui_sigfrx.cpp b/firmware/application/ui_sigfrx.cpp index 514e3963..bc6c5f6e 100644 --- a/firmware/application/ui_sigfrx.cpp +++ b/firmware/application/ui_sigfrx.cpp @@ -25,11 +25,13 @@ #include "ch.h" #include "evtimer.h" +#include "event_m0.hpp" #include "ff.h" #include "hackrf_gpio.hpp" #include "portapack.hpp" #include "radio.hpp" -//#include "fox_bmp.hpp" + +#include "string_format.hpp" #include "hackrf_hal.hpp" #include "portapack_shared_memory.hpp" @@ -38,7 +40,7 @@ #include #include -using namespace hackrf::one; +using namespace portapack; namespace ui { @@ -98,20 +100,23 @@ void SIGFRXView::on_channel_spectrum(const ChannelSpectrum& spectrum) { } void SIGFRXView::on_show() { - context().message_map().register_handler(Message::ID::ChannelSpectrum, + /*EventDispatcher::message_map().register_handler(Message::ID::ChannelSpectrum, [this](const Message* const p) { this->on_channel_spectrum(reinterpret_cast(p)->spectrum); } - ); + );*/ +} + +void SIGFRXView::on_hide() { + //EventDispatcher::message_map().unregister_handler(Message::ID::ChannelSpectrum); } SIGFRXView::SIGFRXView( - NavigationView& nav, - ReceiverModel& receiver_model -) : receiver_model(receiver_model) + NavigationView& nav +) { receiver_model.set_baseband_configuration({ - .mode = RX_SIGFOX, + .mode = 255, // DEBUG .sampling_rate = 3072000, .decimation_factor = 4, }); diff --git a/firmware/application/ui_sigfrx.hpp b/firmware/application/ui_sigfrx.hpp index aebe474c..3b83ac07 100644 --- a/firmware/application/ui_sigfrx.hpp +++ b/firmware/application/ui_sigfrx.hpp @@ -36,17 +36,16 @@ namespace ui { class SIGFRXView : public View { public: - SIGFRXView(NavigationView& nav, ReceiverModel& receiver_model); + SIGFRXView(NavigationView& nav); ~SIGFRXView(); void on_channel_spectrum(const ChannelSpectrum& spectrum); void on_show() override; + void on_hide() override; void focus() override; void paint(Painter& painter) override; -private: - ReceiverModel& receiver_model; - +private: uint8_t last_channel; uint8_t detect_counter = 0; diff --git a/firmware/application/ui_xylos.cpp b/firmware/application/ui_xylos.cpp index c62f1163..d80164c8 100644 --- a/firmware/application/ui_xylos.cpp +++ b/firmware/application/ui_xylos.cpp @@ -25,6 +25,7 @@ #include "ch.h" #include "hackrf_hal.hpp" +#include "event_m0.hpp" #include "ui_alphanum.hpp" #include "ff.h" #include "hackrf_gpio.hpp" @@ -38,7 +39,7 @@ #include #include -using namespace hackrf::one; +using namespace portapack; namespace ui { @@ -91,9 +92,8 @@ void XylosRXView::on_show() { } XylosRXView::XylosRXView( - NavigationView& nav, - ReceiverModel& receiver_model -) : receiver_model(receiver_model) + NavigationView& nav +) { char ccirdebug[21] = { 0,0,0,0,1,8,1,10,10,10,11,1,1,2,0,11,0,0,0,0,0xFF }; @@ -211,9 +211,8 @@ void XylosView::journuit() { } XylosView::XylosView( - NavigationView& nav, - TransmitterModel& transmitter_model -) : transmitter_model(transmitter_model) + NavigationView& nav +) { static constexpr Style style_val { .font = font::fixed_8x16, @@ -228,13 +227,11 @@ XylosView::XylosView( }; transmitter_model.set_baseband_configuration({ - .mode = TX_XYLOS, + .mode = 4, .sampling_rate = 1536000, .decimation_factor = 1, }); - - transmitter_model.set_modulation(TX_XYLOS); // Useless ? - + add_children({ { &text_title, &button_txtest, @@ -323,11 +320,9 @@ XylosView::XylosView( button_txtest.on_select = [this,&transmitter_model](Button&) { const uint8_t ccirtest[21] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,13,12,11,0xFF }; if (txing == false) { - auto& message_map = context().message_map(); + EventDispatcher::message_map().unregister_handler(Message::ID::TXDone); - message_map.unregister_handler(Message::ID::TXDone); - - message_map.register_handler(Message::ID::TXDone, + EventDispatcher::message_map().register_handler(Message::ID::TXDone, [this,&transmitter_model](Message* const p) { const auto message = static_cast(p); if (message->n == 25) { @@ -359,11 +354,9 @@ XylosView::XylosView( if (txing == false) { upd_message(); - auto& message_map = context().message_map(); + EventDispatcher::message_map().unregister_handler(Message::ID::TXDone); - message_map.unregister_handler(Message::ID::TXDone); - - message_map.register_handler(Message::ID::TXDone, + EventDispatcher::message_map().register_handler(Message::ID::TXDone, [this,&transmitter_model](Message* const p) { uint8_t c; char progress[21]; diff --git a/firmware/application/ui_xylos.hpp b/firmware/application/ui_xylos.hpp index 34e52dce..37fd8fb7 100644 --- a/firmware/application/ui_xylos.hpp +++ b/firmware/application/ui_xylos.hpp @@ -45,7 +45,7 @@ void do_something(); class XylosRXView : public View { public: - XylosRXView(NavigationView& nav, ReceiverModel& receiver_model); + XylosRXView(NavigationView& nav); ~XylosRXView(); void talk(); @@ -91,8 +91,6 @@ private: "trailer.wav" }; char ccir_received[21]; - - ReceiverModel& receiver_model; Text text_title { { 1 * 8, 1 * 16, 11, 16 }, @@ -142,7 +140,7 @@ private: class XylosView : public View { public: - XylosView(NavigationView& nav, TransmitterModel& transmitter_model); + XylosView(NavigationView& nav); ~XylosView(); void journuit(); @@ -184,8 +182,6 @@ private: }; char ccirmessage[21]; char ccir_received[21]; - - TransmitterModel& transmitter_model; Text text_title { { 1 * 8, 1 * 16, 11, 16 }, diff --git a/firmware/baseband-tx.bin b/firmware/baseband-tx.bin index 62631276..226a8ead 100644 Binary files a/firmware/baseband-tx.bin and b/firmware/baseband-tx.bin differ diff --git a/firmware/baseband-tx/Makefile b/firmware/baseband-tx/Makefile index c95199a1..8ce24acb 100755 --- a/firmware/baseband-tx/Makefile +++ b/firmware/baseband-tx/Makefile @@ -124,16 +124,20 @@ CSRC = $(PORTSRC) \ # setting. CPPSRC = main.cpp \ message_queue.cpp \ + event.cpp \ event_m4.cpp \ - irq_ipc_m4.cpp \ gpdma.cpp \ baseband_dma.cpp \ + baseband_sgpio.cpp \ portapack_shared_memory.cpp \ + baseband_thread.cpp \ baseband_processor.cpp \ - channel_decimator.cpp \ + baseband_stats_collector.cpp \ dsp_decimate.cpp \ dsp_demodulate.cpp \ matched_filter.cpp \ + spectrum_collector.cpp \ + proc_rds.cpp \ proc_jammer.cpp \ proc_fsk_lcr.cpp \ proc_xylos.cpp \ @@ -144,11 +148,15 @@ CPPSRC = main.cpp \ packet_builder.cpp \ dsp_fft.cpp \ dsp_fir_taps.cpp \ + dsp_iir.cpp \ fxpt_atan2.cpp \ rssi.cpp \ rssi_dma.cpp \ + rssi_thread.cpp \ audio.cpp \ + audio_output.cpp \ audio_dma.cpp \ + audio_stats_collector.cpp \ touch_dma.cpp \ ../common/utility.cpp \ ../common/chibios_cpp.cpp \ diff --git a/firmware/baseband-tx/audio_dma.cpp b/firmware/baseband-tx/audio_dma.cpp index 57ff5bc4..60e85096 100644 --- a/firmware/baseband-tx/audio_dma.cpp +++ b/firmware/baseband-tx/audio_dma.cpp @@ -121,7 +121,7 @@ constexpr gpdma::channel::Config config_rx() { /* TODO: Clean up terminology around "buffer", "transfer", "samples" */ -constexpr size_t buffer_samples_log2n = 8; // Bumped to 8, to allow filling at 750Hz +constexpr size_t buffer_samples_log2n = 7; constexpr size_t buffer_samples = (1 << buffer_samples_log2n); constexpr size_t transfers_per_buffer_log2n = 2; constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n); @@ -208,8 +208,8 @@ void enable() { } void disable() { - gpdma_channel_i2s0_tx.disable_force(); - gpdma_channel_i2s0_rx.disable_force(); + gpdma_channel_i2s0_tx.disable(); + gpdma_channel_i2s0_rx.disable(); } buffer_t tx_empty_buffer() { diff --git a/firmware/baseband-tx/audio_output.cpp b/firmware/baseband-tx/audio_output.cpp new file mode 100644 index 00000000..6e29fa16 --- /dev/null +++ b/firmware/baseband-tx/audio_output.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "audio_output.hpp" + +#include "portapack_shared_memory.hpp" + +#include "audio_dma.hpp" + +#include "message.hpp" + +#include +#include +#include + +void AudioOutput::configure( + const iir_biquad_config_t& hpf_config, + const iir_biquad_config_t& deemph_config, + const float squelch_threshold +) { + hpf.configure(hpf_config); + deemph.configure(deemph_config); + squelch.set_threshold(squelch_threshold); +} + +void AudioOutput::write( + const buffer_s16_t& audio +) { + std::array audio_f; + for(size_t i=0; i + +class AudioOutput { +public: + void configure( + const iir_biquad_config_t& hpf_config, + const iir_biquad_config_t& deemph_config = iir_config_passthrough, + const float squelch_threshold = 0.0f + ); + + void write(const buffer_s16_t& audio); + void write(const buffer_f32_t& audio); + +private: + IIRBiquadFilter hpf; + IIRBiquadFilter deemph; + FMSquelch squelch; + + AudioStatsCollector audio_stats; + + uint64_t audio_present_history = 0; + + void fill_audio_buffer(const buffer_f32_t& audio); + void feed_audio_stats(const buffer_f32_t& audio); +}; + +#endif/*__AUDIO_OUTPUT_H__*/ diff --git a/firmware/baseband-tx/audio_stats_collector.cpp b/firmware/baseband-tx/audio_stats_collector.cpp new file mode 100644 index 00000000..227d5887 --- /dev/null +++ b/firmware/baseband-tx/audio_stats_collector.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "audio_stats_collector.hpp" + +#include "utility.hpp" + +void AudioStatsCollector::consume_audio_buffer(const buffer_f32_t& src) { + auto src_p = src.p; + const auto src_end = &src.p[src.count]; + while(src_p < src_end) { + const auto sample = *(src_p++); + const auto sample_squared = sample * sample; + squared_sum += sample_squared; + if( sample_squared > max_squared ) { + max_squared = sample_squared; + } + } +} + +bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t sampling_rate) { + count += sample_count; + + const size_t samples_per_update = sampling_rate * update_interval; + + if( count >= samples_per_update ) { + statistics.rms_db = complex16_mag_squared_to_dbv_norm(squared_sum / count); + statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared); + statistics.count = count; + + squared_sum = 0; + max_squared = 0; + count = 0; + + return true; + } else { + return false; + } +} + +bool AudioStatsCollector::feed(const buffer_f32_t& src) { + consume_audio_buffer(src); + + return update_stats(src.count, src.sampling_rate); +} + +bool AudioStatsCollector::mute(const size_t sample_count, const size_t sampling_rate) { + return update_stats(sample_count, sampling_rate); +} diff --git a/firmware/baseband-tx/audio_stats_collector.hpp b/firmware/baseband-tx/audio_stats_collector.hpp index 189fff80..10e17edc 100644 --- a/firmware/baseband-tx/audio_stats_collector.hpp +++ b/firmware/baseband-tx/audio_stats_collector.hpp @@ -22,9 +22,8 @@ #ifndef __AUDIO_STATS_COLLECTOR_H__ #define __AUDIO_STATS_COLLECTOR_H__ -#include "buffer.hpp" +#include "dsp_types.hpp" #include "message.hpp" -#include "utility.hpp" #include #include @@ -32,64 +31,33 @@ class AudioStatsCollector { public: template - void feed(buffer_s16_t src, Callback callback) { - consume_audio_buffer(src); - - if( update_stats(src.count, src.sampling_rate) ) { + void feed(const buffer_f32_t& src, Callback callback) { + if( feed(src) ) { callback(statistics); } } template void mute(const size_t sample_count, const size_t sampling_rate, Callback callback) { - if( update_stats(sample_count, sampling_rate) ) { + if( mute(sample_count, sampling_rate) ) { callback(statistics); } } private: static constexpr float update_interval { 0.1f }; - uint64_t squared_sum { 0 }; - uint32_t max_squared { 0 }; + float squared_sum { 0 }; + float max_squared { 0 }; size_t count { 0 }; AudioStatistics statistics; - void consume_audio_buffer(buffer_s16_t src) { - auto src_p = src.p; - const auto src_end = &src.p[src.count]; - while(src_p < src_end) { - const auto sample = *(src_p++); - const uint64_t sample_squared = sample * sample; - squared_sum += sample_squared; - if( sample_squared > max_squared ) { - max_squared = sample_squared; - } - } - } + void consume_audio_buffer(const buffer_f32_t& src); - bool update_stats(const size_t sample_count, const size_t sampling_rate) { - count += sample_count; + bool update_stats(const size_t sample_count, const size_t sampling_rate); - const size_t samples_per_update = sampling_rate * update_interval; - - if( count >= samples_per_update ) { - const float squared_sum_f = squared_sum; - const float max_squared_f = max_squared; - const float squared_avg_f = squared_sum_f / count; - statistics.rms_db = complex16_mag_squared_to_dbv_norm(squared_avg_f); - statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared_f); - statistics.count = count; - - squared_sum = 0; - max_squared = 0; - count = 0; - - return true; - } else { - return false; - } - } + bool feed(const buffer_f32_t& src); + bool mute(const size_t sample_count, const size_t sampling_rate); }; #endif/*__AUDIO_STATS_COLLECTOR_H__*/ diff --git a/firmware/baseband-tx/baseband_dma.cpp b/firmware/baseband-tx/baseband_dma.cpp index aa119dc6..7467f551 100644 --- a/firmware/baseband-tx/baseband_dma.cpp +++ b/firmware/baseband-tx/baseband_dma.cpp @@ -20,7 +20,6 @@ */ #include "baseband_dma.hpp" -#include "portapack_shared_memory.hpp" #include #include @@ -35,8 +34,6 @@ using namespace lpc43xx; namespace baseband { namespace dma { - - int quitt = 0; constexpr uint32_t gpdma_ahb_master_sgpio = 0; constexpr uint32_t gpdma_ahb_master_memory = 1; @@ -102,17 +99,12 @@ constexpr size_t msg_count = transfers_per_buffer - 1; static std::array lli_loop; static constexpr auto& gpdma_channel_sgpio = gpdma::channels[portapack::sgpio_gpdma_channel_number]; -//static Mailbox mailbox; -//static std::array messages; static Semaphore semaphore; static volatile const gpdma::channel::LLI* next_lli = nullptr; -void transfer_complete() { +static void transfer_complete() { next_lli = gpdma_channel_sgpio.next_lli(); - quitt = 0; - /* TODO: Is Mailbox the proper synchronization mechanism for this? */ - //chMBPostI(&mailbox, 0); chSemSignalI(&semaphore); } @@ -121,7 +113,6 @@ static void dma_error() { } void init() { - //chMBInit(&mailbox, messages.data(), messages.size()); chSemInit(&semaphore, 0); gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error); @@ -148,7 +139,6 @@ void enable(const baseband::Direction direction) { const auto gpdma_config = config(direction); gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config); - //chMBReset(&mailbox); chSemReset(&semaphore, 0); gpdma_channel_sgpio.enable(); @@ -159,14 +149,11 @@ bool is_enabled() { } void disable() { - gpdma_channel_sgpio.disable_force(); + gpdma_channel_sgpio.disable(); } baseband::buffer_t wait_for_rx_buffer() { - //msg_t msg; - //const auto status = chMBFetch(&mailbox, &msg, TIME_INFINITE); const auto status = chSemWait(&semaphore); - if (quitt) return { nullptr, 0 }; if( status == RDY_OK ) { const auto next = next_lli; if( next ) { @@ -174,28 +161,10 @@ baseband::buffer_t wait_for_rx_buffer() { const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; return { reinterpret_cast(lli_loop[free_index].destaddr), transfer_samples }; } else { - return { nullptr, 0 }; + return { }; } } else { - return { nullptr, 0 }; - } -} - -baseband::buffer_t wait_for_tx_buffer() { - //msg_t msg; - //const auto status = chMBFetch(&mailbox, &msg, TIME_INFINITE); - const auto status = chSemWait(&semaphore); - if( status == RDY_OK ) { - const auto next = next_lli; - if( next ) { - const size_t next_index = next - &lli_loop[0]; - const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; - return { reinterpret_cast(lli_loop[free_index].srcaddr), transfer_samples }; - } else { - return { nullptr, 0 }; - } - } else { - return { nullptr, 0 }; + return { }; } } diff --git a/firmware/baseband-tx/baseband_processor.cpp b/firmware/baseband-tx/baseband_processor.cpp index 1e9ffaf6..4113dec5 100644 --- a/firmware/baseband-tx/baseband_processor.cpp +++ b/firmware/baseband-tx/baseband_processor.cpp @@ -23,104 +23,14 @@ #include "portapack_shared_memory.hpp" -#include "dsp_fft.hpp" - -#include "audio_dma.hpp" - #include "message.hpp" -#include "event_m4.hpp" -#include "utility.hpp" -#include -#include - -void BasebandProcessor::update_spectrum() { - // Called from idle thread (after EVT_MASK_SPECTRUM is flagged) - if( channel_spectrum_request_update ) { - /* Decimated buffer is full. Compute spectrum. */ - channel_spectrum_request_update = false; - fft_c_preswapped(channel_spectrum); - - ChannelSpectrumMessage spectrum_message; - for(size_t i=0; i .magnitude, or something more (less!) accurate. */ - spectrum_message.spectrum.db_count = spectrum_message.spectrum.db.size(); - spectrum_message.spectrum.sampling_rate = channel_spectrum_sampling_rate; - spectrum_message.spectrum.channel_filter_pass_frequency = channel_filter_pass_frequency; - spectrum_message.spectrum.channel_filter_stop_frequency = channel_filter_stop_frequency; - shared_memory.application_queue.push(spectrum_message); - } -} - -void BasebandProcessor::feed_channel_stats(const buffer_c16_t channel) { +void BasebandProcessor::feed_channel_stats(const buffer_c16_t& channel) { channel_stats.feed( channel, - [this](const ChannelStatistics statistics) { - this->post_channel_stats_message(statistics); + [](const ChannelStatistics& statistics) { + const ChannelStatisticsMessage channel_stats_message { statistics }; + shared_memory.application_queue.push(channel_stats_message); } ); } - -void BasebandProcessor::feed_channel_spectrum( - const buffer_c16_t channel, - const uint32_t filter_pass_frequency, - const uint32_t filter_stop_frequency -) { - channel_filter_pass_frequency = filter_pass_frequency; - channel_filter_stop_frequency = filter_stop_frequency; - channel_spectrum_decimator.feed( - channel, - [this](const buffer_c16_t data) { - this->post_channel_spectrum_message(data); - } - ); -} - -void BasebandProcessor::fill_audio_buffer(const buffer_s16_t audio) { - auto audio_buffer = audio::dma::tx_empty_buffer();; - for(size_t i=0; ipost_audio_stats_message(statistics); - } - ); -} - -void BasebandProcessor::post_audio_stats_message(const AudioStatistics statistics) { - audio_stats_message.statistics = statistics; - shared_memory.application_queue.push(audio_stats_message); -} - -void BasebandProcessor::fill_buffer(int8_t * inptr) { - (void)inptr; -} diff --git a/firmware/baseband-tx/baseband_processor.hpp b/firmware/baseband-tx/baseband_processor.hpp index d7f87e38..28ef5063 100644 --- a/firmware/baseband-tx/baseband_processor.hpp +++ b/firmware/baseband-tx/baseband_processor.hpp @@ -23,55 +23,24 @@ #define __BASEBAND_PROCESSOR_H__ #include "dsp_types.hpp" -#include "complex.hpp" -#include "block_decimator.hpp" #include "channel_stats_collector.hpp" -#include "audio_stats_collector.hpp" -#include -#include -#include +#include "message.hpp" class BasebandProcessor { public: virtual ~BasebandProcessor() = default; - virtual void execute(buffer_c8_t buffer) = 0; - virtual void fill_buffer(int8_t * inptr); + virtual void execute(const buffer_c8_t& buffer) = 0; - void update_spectrum(); + virtual void on_message(const Message* const) { }; protected: - void feed_channel_stats(const buffer_c16_t channel); - - void feed_channel_spectrum( - const buffer_c16_t channel, - const uint32_t filter_pass_frequency, - const uint32_t filter_stop_frequency - ); - - void fill_audio_buffer(const buffer_s16_t audio); - - volatile bool channel_spectrum_request_update { false }; - std::array, 256> channel_spectrum; - uint32_t channel_spectrum_sampling_rate { 0 }; - uint32_t channel_filter_pass_frequency { 0 }; - uint32_t channel_filter_stop_frequency { 0 }; + void feed_channel_stats(const buffer_c16_t& channel); private: - BlockDecimator<256> channel_spectrum_decimator { 4 }; - ChannelStatsCollector channel_stats; - ChannelStatisticsMessage channel_stats_message; - - AudioStatsCollector audio_stats; - AudioStatisticsMessage audio_stats_message; - - void post_channel_stats_message(const ChannelStatistics statistics); - void post_channel_spectrum_message(const buffer_c16_t data); - void feed_audio_stats(const buffer_s16_t audio); - void post_audio_stats_message(const AudioStatistics statistics); }; #endif/*__BASEBAND_PROCESSOR_H__*/ diff --git a/firmware/baseband-tx/baseband_stats_collector.cpp b/firmware/baseband-tx/baseband_stats_collector.cpp new file mode 100644 index 00000000..8eec12c5 --- /dev/null +++ b/firmware/baseband-tx/baseband_stats_collector.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "baseband_stats_collector.hpp" + +#include "lpc43xx_cpp.hpp" + +bool BasebandStatsCollector::process(const buffer_c8_t& buffer) { + samples += buffer.count; + + const size_t report_samples = buffer.sampling_rate * report_interval; + const auto report_delta = samples - samples_last_report; + return report_delta >= report_samples; +} + +BasebandStatistics BasebandStatsCollector::capture_statistics() { + BasebandStatistics statistics; + + const auto idle_ticks = thread_idle->total_ticks; + statistics.idle_ticks = (idle_ticks - last_idle_ticks); + last_idle_ticks = idle_ticks; + + const auto main_ticks = thread_main->total_ticks; + statistics.main_ticks = (main_ticks - last_main_ticks); + last_main_ticks = main_ticks; + + const auto rssi_ticks = thread_rssi->total_ticks; + statistics.rssi_ticks = (rssi_ticks - last_rssi_ticks); + last_rssi_ticks = rssi_ticks; + + const auto baseband_ticks = thread_baseband->total_ticks; + statistics.baseband_ticks = (baseband_ticks - last_baseband_ticks); + last_baseband_ticks = baseband_ticks; + + statistics.saturation = lpc43xx::m4::flag_saturation(); + lpc43xx::m4::clear_flag_saturation(); + + samples_last_report = samples; + + return statistics; +} diff --git a/firmware/baseband-tx/baseband_stats_collector.hpp b/firmware/baseband-tx/baseband_stats_collector.hpp index a44c2936..b628a8b2 100644 --- a/firmware/baseband-tx/baseband_stats_collector.hpp +++ b/firmware/baseband-tx/baseband_stats_collector.hpp @@ -26,7 +26,6 @@ #include "dsp_types.hpp" #include "message.hpp" -#include "utility_m4.hpp" #include #include @@ -46,36 +45,9 @@ public: } template - void process(buffer_c8_t buffer, Callback callback) { - samples += buffer.count; - - const size_t report_samples = buffer.sampling_rate * report_interval; - const auto report_delta = samples - samples_last_report; - if( report_delta >= report_samples ) { - BasebandStatistics statistics; - - const auto idle_ticks = thread_idle->total_ticks; - statistics.idle_ticks = (idle_ticks - last_idle_ticks); - last_idle_ticks = idle_ticks; - - const auto main_ticks = thread_main->total_ticks; - statistics.main_ticks = (main_ticks - last_main_ticks); - last_main_ticks = main_ticks; - - const auto rssi_ticks = thread_rssi->total_ticks; - statistics.rssi_ticks = (rssi_ticks - last_rssi_ticks); - last_rssi_ticks = rssi_ticks; - - const auto baseband_ticks = thread_baseband->total_ticks; - statistics.baseband_ticks = (baseband_ticks - last_baseband_ticks); - last_baseband_ticks = baseband_ticks; - - statistics.saturation = m4_flag_saturation(); - clear_m4_flag_saturation(); - - callback(statistics); - - samples_last_report = samples; + void process(const buffer_c8_t& buffer, Callback callback) { + if( process(buffer) ) { + callback(capture_statistics()); } } @@ -91,6 +63,9 @@ private: uint32_t last_rssi_ticks { 0 }; const Thread* const thread_baseband; uint32_t last_baseband_ticks { 0 }; + + bool process(const buffer_c8_t& buffer); + BasebandStatistics capture_statistics(); }; #endif/*__BASEBAND_STATS_COLLECTOR_H__*/ diff --git a/firmware/baseband-tx/baseband_thread.cpp b/firmware/baseband-tx/baseband_thread.cpp new file mode 100644 index 00000000..b25789b9 --- /dev/null +++ b/firmware/baseband-tx/baseband_thread.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "baseband_thread.hpp" + +#include "dsp_types.hpp" + +#include "baseband.hpp" +#include "baseband_stats_collector.hpp" +#include "baseband_sgpio.hpp" +#include "baseband_dma.hpp" + +#include "rssi.hpp" +#include "i2s.hpp" + +#include "proc_xylos.hpp" +#include "proc_fsk_lcr.hpp" +#include "proc_jammer.hpp" +#include "proc_rds.hpp" +#include "proc_playaudio.hpp" + +#include "portapack_shared_memory.hpp" + +#include + +static baseband::SGPIO baseband_sgpio; + +WORKING_AREA(baseband_thread_wa, 4096); + +Thread* BasebandThread::start(const tprio_t priority) { + return chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa), + priority, ThreadBase::fn, + this + ); +} + +void BasebandThread::set_configuration(const BasebandConfiguration& new_configuration) { + if( new_configuration.mode != baseband_configuration.mode ) { + disable(); + + // TODO: Timing problem around disabling DMA and nulling and deleting old processor + auto old_p = baseband_processor; + baseband_processor = nullptr; + delete old_p; + + baseband_processor = create_processor(new_configuration.mode); + + enable(); + } + + baseband_configuration = new_configuration; +} + +void BasebandThread::on_message(const Message* const message) { + if( message->id == Message::ID::BasebandConfiguration ) { + set_configuration(reinterpret_cast(message)->configuration); + } else { + if( baseband_processor ) { + baseband_processor->on_message(message); + } + } +} + +void BasebandThread::run() { + baseband_sgpio.init(); + baseband::dma::init(); + + const auto baseband_buffer = new std::array(); + baseband::dma::configure( + baseband_buffer->data(), + direction() + ); + //baseband::dma::allocate(4, 2048); + + BasebandStatsCollector stats { + chSysGetIdleThread(), + thread_main, + thread_rssi, + chThdSelf() + }; + + while(true) { + // TODO: Place correct sampling rate into buffer returned here: + const auto buffer_tmp = baseband::dma::wait_for_rx_buffer(); + if( buffer_tmp ) { + buffer_c8_t buffer { + buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate + }; + + if( baseband_processor ) { + baseband_processor->execute(buffer); + } + + stats.process(buffer, + [](const BasebandStatistics& statistics) { + const BasebandStatisticsMessage message { statistics }; + shared_memory.application_queue.push(message); + } + ); + } + } + + delete baseband_buffer; +} + +BasebandProcessor* BasebandThread::create_processor(const int32_t mode) { + switch(mode) { + case 0: return new RDSProcessor(); + case 1: return new LCRFSKProcessor(); + case 2: return nullptr; //new ToneProcessor(); + case 3: return new JammerProcessor(); + case 4: return new XylosProcessor(); + case 5: return new PlayAudioProcessor(); + case 6: return nullptr; //new AFSKRXProcessor(); + default: return nullptr; + } +} + +void BasebandThread::disable() { + if( baseband_processor ) { + i2s::i2s0::tx_mute(); + baseband::dma::disable(); + baseband_sgpio.streaming_disable(); + rf::rssi::stop(); + } +} + +void BasebandThread::enable() { + if( baseband_processor ) { + if( direction() == baseband::Direction::Receive ) { + rf::rssi::start(); + } + baseband_sgpio.configure(direction()); + baseband::dma::enable(direction()); + baseband_sgpio.streaming_enable(); + } +} diff --git a/firmware/baseband-tx/baseband_thread.hpp b/firmware/baseband-tx/baseband_thread.hpp new file mode 100644 index 00000000..81d35433 --- /dev/null +++ b/firmware/baseband-tx/baseband_thread.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __BASEBAND_THREAD_H__ +#define __BASEBAND_THREAD_H__ + +#include "thread_base.hpp" +#include "message.hpp" +#include "baseband_processor.hpp" + +#include + +class BasebandThread : public ThreadBase { +public: + BasebandThread( + ) : ThreadBase { "baseband" } + { + } + + Thread* start(const tprio_t priority); + + void on_message(const Message* const message); + + // This getter should die, it's just here to leak information to code that + // isn't in the right place to begin with. + baseband::Direction direction() const { + return baseband::Direction::Receive; + } + + Thread* thread_main { nullptr }; + Thread* thread_rssi { nullptr }; + BasebandProcessor* baseband_processor { nullptr }; + +private: + BasebandConfiguration baseband_configuration; + + void run() override; + + BasebandProcessor* create_processor(const int32_t mode); + + void disable(); + void enable(); + + void set_configuration(const BasebandConfiguration& new_configuration); +}; + +#endif/*__BASEBAND_THREAD_H__*/ diff --git a/firmware/baseband-tx/description b/firmware/baseband-tx/description index f2606500..4af72ebe 100644 --- a/firmware/baseband-tx/description +++ b/firmware/baseband-tx/description @@ -1 +1 @@ -More specific stuff for testing :o +More or less experimental stuff, mainly TX. diff --git a/firmware/baseband-tx/dsp_iir.cpp b/firmware/baseband-tx/dsp_iir.cpp new file mode 100644 index 00000000..443183ba --- /dev/null +++ b/firmware/baseband-tx/dsp_iir.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "dsp_iir.hpp" + +#include + +void IIRBiquadFilter::configure(const iir_biquad_config_t& new_config) { + config = new_config; +} + +void IIRBiquadFilter::execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out) { + const auto a_ = config.a; + const auto b_ = config.b; + + auto x_ = x; + auto y_ = y; + + // TODO: Assert that buffer_out.count == buffer_in.count. + for(size_t i=0; i b; - const std::array a; + std::array b; + std::array a; +}; + +constexpr iir_biquad_config_t iir_config_passthrough { + { { 1.0f, 0.0f, 0.0f } }, + { { 0.0f, 0.0f, 0.0f } }, +}; + +constexpr iir_biquad_config_t iir_config_no_pass { + { { 0.0f, 0.0f, 0.0f } }, + { { 0.0f, 0.0f, 0.0f } }, }; class IIRBiquadFilter { public: // http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + constexpr IIRBiquadFilter( + ) : IIRBiquadFilter(iir_config_no_pass) + { + } // Assume all coefficients are normalized so that a0=1.0 constexpr IIRBiquadFilter( @@ -42,34 +56,15 @@ public: { } - void execute(buffer_s16_t buffer_in, buffer_s16_t buffer_out) { - // TODO: Assert that buffer_out.count == buffer_in.count. - for(size_t i=0; i x { { 0.0f, 0.0f, 0.0f } }; std::array y { { 0.0f, 0.0f, 0.0f } }; - - float execute_sample(const float in) { - x[0] = x[1]; - x[1] = x[2]; - x[2] = in; - - y[0] = y[1]; - y[1] = y[2]; - y[2] = config.b[0] * x[2] + config.b[1] * x[1] + config.b[2] * x[0] - - config.a[1] * y[1] - config.a[2] * y[0]; - - return y[2]; - } }; #endif/*__DSP_IIR_H__*/ diff --git a/firmware/baseband-tx/dsp_squelch.cpp b/firmware/baseband-tx/dsp_squelch.cpp index 4cfe651a..b4a2d3a4 100644 --- a/firmware/baseband-tx/dsp_squelch.cpp +++ b/firmware/baseband-tx/dsp_squelch.cpp @@ -24,22 +24,30 @@ #include #include -bool FMSquelch::execute(buffer_s16_t audio) { +bool FMSquelch::execute(const buffer_f32_t& audio) { + if( threshold_squared == 0.0f ) { + return true; + } + // TODO: No hard-coded array size. - std::array squelch_energy_buffer; - const buffer_s16_t squelch_energy { + std::array squelch_energy_buffer; + const buffer_f32_t squelch_energy { squelch_energy_buffer.data(), squelch_energy_buffer.size() }; non_audio_hpf.execute(audio, squelch_energy); - uint64_t max_squared = 0; + float non_audio_max_squared = 0; for(const auto sample : squelch_energy_buffer) { - const uint64_t sample_squared = sample * sample; - if( sample_squared > max_squared ) { - max_squared = sample_squared; + const float sample_squared = sample * sample; + if( sample_squared > non_audio_max_squared ) { + non_audio_max_squared = sample_squared; } } - return (max_squared < (threshold * threshold)); + return (non_audio_max_squared < threshold_squared); +} + +void FMSquelch::set_threshold(const float new_value) { + threshold_squared = new_value * new_value; } diff --git a/firmware/baseband-tx/dsp_squelch.hpp b/firmware/baseband-tx/dsp_squelch.hpp index 06cb2cdd..4701b9ac 100644 --- a/firmware/baseband-tx/dsp_squelch.hpp +++ b/firmware/baseband-tx/dsp_squelch.hpp @@ -31,14 +31,14 @@ class FMSquelch { public: - bool execute(buffer_s16_t audio); + bool execute(const buffer_f32_t& audio); + + void set_threshold(const float new_value); private: static constexpr size_t N = 32; - static constexpr int16_t threshold = 3072; + float threshold_squared { 0.0f }; - // nyquist = 48000 / 2.0 - // scipy.signal.iirdesign(wp=8000 / nyquist, ws= 4000 / nyquist, gpass=1, gstop=18, ftype='ellip') IIRBiquadFilter non_audio_hpf { non_audio_hpf_config }; }; diff --git a/firmware/baseband-tx/event_m4.cpp b/firmware/baseband-tx/event_m4.cpp index 50b0c0b7..0e90cd15 100644 --- a/firmware/baseband-tx/event_m4.cpp +++ b/firmware/baseband-tx/event_m4.cpp @@ -21,10 +21,99 @@ #include "event_m4.hpp" +#include "portapack_shared_memory.hpp" + +#include "message_queue.hpp" + #include "ch.h" -Thread* thread_event_loop = nullptr; +#include "lpc43xx_cpp.hpp" +using namespace lpc43xx; -void events_initialize(Thread* const event_loop_thread) { - thread_event_loop = event_loop_thread; +#include +#include + +extern "C" { + +CH_IRQ_HANDLER(MAPP_IRQHandler) { + CH_IRQ_PROLOGUE(); + + chSysLockFromIsr(); + EventDispatcher::events_flag_isr(EVT_MASK_BASEBAND); + chSysUnlockFromIsr(); + + creg::m0apptxevent::clear(); + + CH_IRQ_EPILOGUE(); +} + +} + +Thread* EventDispatcher::thread_event_loop = nullptr; + +void EventDispatcher::run() { + thread_event_loop = chThdSelf(); + lpc43xx::creg::m0apptxevent::enable(); + + baseband_thread.thread_main = chThdSelf(); + baseband_thread.thread_rssi = rssi_thread.start(NORMALPRIO + 10); + baseband_thread.start(NORMALPRIO + 20); + + while(is_running) { + const auto events = wait(); + dispatch(events); + } + + lpc43xx::creg::m0apptxevent::disable(); +} + +void EventDispatcher::request_stop() { + is_running = false; +} + +eventmask_t EventDispatcher::wait() { + return chEvtWaitAny(ALL_EVENTS); +} + +void EventDispatcher::dispatch(const eventmask_t events) { + if( events & EVT_MASK_BASEBAND ) { + handle_baseband_queue(); + } + + if( events & EVT_MASK_SPECTRUM ) { + handle_spectrum(); + } +} + +void EventDispatcher::handle_baseband_queue() { + std::array message_buffer; + while(Message* const message = shared_memory.baseband_queue.peek(message_buffer)) { + on_message(message); + shared_memory.baseband_queue.skip(); + } +} + +void EventDispatcher::on_message(const Message* const message) { + switch(message->id) { + case Message::ID::Shutdown: + on_message_shutdown(*reinterpret_cast(message)); + break; + + default: + on_message_default(message); + break; + } +} + +void EventDispatcher::on_message_shutdown(const ShutdownMessage&) { + request_stop(); +} + +void EventDispatcher::on_message_default(const Message* const message) { + baseband_thread.on_message(message); +} + +void EventDispatcher::handle_spectrum() { + const UpdateSpectrumMessage message; + baseband_thread.on_message(&message); } diff --git a/firmware/baseband-tx/event_m4.hpp b/firmware/baseband-tx/event_m4.hpp index be113ca8..ad9871d1 100644 --- a/firmware/baseband-tx/event_m4.hpp +++ b/firmware/baseband-tx/event_m4.hpp @@ -22,25 +22,54 @@ #ifndef __EVENT_M4_H__ #define __EVENT_M4_H__ +#include "event.hpp" + +#include "baseband_thread.hpp" +#include "rssi_thread.hpp" + +#include "message.hpp" + #include "ch.h" constexpr auto EVT_MASK_BASEBAND = EVENT_MASK(0); constexpr auto EVT_MASK_SPECTRUM = EVENT_MASK(1); -void events_initialize(Thread* const event_loop_thread); +class EventDispatcher { +public: + void run(); + void request_stop(); -extern Thread* thread_event_loop; - -inline void events_flag(const eventmask_t events) { - if( thread_event_loop ) { - chEvtSignal(thread_event_loop, events); + static inline void events_flag(const eventmask_t events) { + if( thread_event_loop ) { + chEvtSignal(thread_event_loop, events); + } } -} -inline void events_flag_isr(const eventmask_t events) { - if( thread_event_loop ) { - chEvtSignalI(thread_event_loop, events); + static inline void events_flag_isr(const eventmask_t events) { + if( thread_event_loop ) { + chEvtSignalI(thread_event_loop, events); + } } -} + +private: + static Thread* thread_event_loop; + + BasebandThread baseband_thread; + RSSIThread rssi_thread; + + bool is_running = true; + + eventmask_t wait(); + + void dispatch(const eventmask_t events); + + void handle_baseband_queue(); + + void on_message(const Message* const message); + void on_message_shutdown(const ShutdownMessage&); + void on_message_default(const Message* const message); + + void handle_spectrum(); +}; #endif/*__EVENT_M4_H__*/ diff --git a/firmware/baseband-tx/main.cpp b/firmware/baseband-tx/main.cpp index 586f245c..3222755d 100755 --- a/firmware/baseband-tx/main.cpp +++ b/firmware/baseband-tx/main.cpp @@ -15,13 +15,11 @@ * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to - * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ #include "ch.h" -#include "test.h" #include "lpc43xx_cpp.hpp" @@ -30,34 +28,13 @@ #include "gpdma.hpp" -#include "baseband.hpp" -#include "baseband_dma.hpp" - #include "event_m4.hpp" -#include "irq_ipc_m4.hpp" - #include "touch_dma.hpp" -#include "modules.h" - -#include "dsp_decimate.hpp" -#include "dsp_demodulate.hpp" -#include "dsp_fft.hpp" -#include "dsp_fir_taps.hpp" -#include "dsp_iir.hpp" -#include "dsp_iir_config.hpp" -#include "dsp_squelch.hpp" - -#include "channel_decimator.hpp" +#include "baseband_thread.hpp" +#include "rssi_thread.hpp" #include "baseband_processor.hpp" -#include "proc_fsk_lcr.hpp" -#include "proc_jammer.hpp" -#include "proc_xylos.hpp" -#include "proc_playaudio.hpp" - -#include "clock_recovery.hpp" -#include "packet_builder.hpp" #include "message_queue.hpp" @@ -73,55 +50,87 @@ #include #include #include -#include -#include -#include -static baseband::Direction direction = baseband::Direction::Receive; +extern "C" { -class ThreadBase { -public: - constexpr ThreadBase( - const char* const name - ) : name { name } - { +void __late_init(void) { + /* + * System initializations. + * - HAL initialization, this also initializes the configured device drivers + * and performs the board-specific initializations. + * - Kernel initialization, the main() function becomes a thread and the + * RTOS is active. + */ + halInit(); + + /* After this call, scheduler, systick, heap, etc. are available. */ + /* By doing chSysInit() here, it runs before C++ constructors, which may + * require the heap. + */ + chSysInit(); +} + +} + +static void init() { + i2s::i2s0::configure( + audio::i2s0_config_tx, + audio::i2s0_config_rx, + audio::i2s0_config_dma + ); + + audio::dma::init(); + audio::dma::configure(); + audio::dma::enable(); + + i2s::i2s0::tx_start(); + i2s::i2s0::rx_start(); + + LPC_CREG->DMAMUX = portapack::gpdma_mux; + gpdma::controller.enable(); + nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); + + touch::dma::init(); + touch::dma::allocate(); + touch::dma::enable(); +} + +static void halt() { + port_disable(); + while(true) { + port_wait_for_interrupt(); } +} - static msg_t fn(void* arg) { - auto obj = static_cast(arg); - chRegSetThreadName(obj->name); - obj->run(); +static void shutdown() { + // TODO: Is this complete? + + nvicDisableVector(DMA_IRQn); + + chSysDisable(); - return 0; - } + systick_stop(); - virtual void run() = 0; + ShutdownMessage shutdown_message; + shared_memory.application_queue.push(shutdown_message); -private: - const char* const name; -}; + halt(); +} -class BasebandThread : public ThreadBase { -public: - BasebandThread( - ) : ThreadBase { "baseband" } - { - } +int main(void) { + init(); - Thread* start(const tprio_t priority) { - return chThdCreateStatic(wa, sizeof(wa), - priority, ThreadBase::fn, - this - ); - } + /* TODO: Ensure DMAs are configured to point at first LLI in chain. */ - Thread* thread_main { nullptr }; - BasebandProcessor* baseband_processor { nullptr }; - BasebandConfiguration baseband_configuration; + EventDispatcher event_dispatcher; + event_dispatcher.run(); -private: - WORKING_AREA(wa, 2048); + shutdown(); + return 0; +} + +/* void run() override { while(true) { if (direction == baseband::Direction::Transmit) { @@ -149,167 +158,6 @@ private: } }; -#define SAMPLES_PER_BIT 192 -#define FILTER_SIZE 576 -#define SAMPLE_BUFFER_SIZE SAMPLES_PER_BIT + FILTER_SIZE - -static int32_t waveform_biphase[] = { - 165,167,168,168,167,166,163,160, - 157,152,147,141,134,126,118,109, - 99,88,77,66,53,41,27,14, - 0,-14,-29,-44,-59,-74,-89,-105, - -120,-135,-150,-165,-179,-193,-206,-218, - -231,-242,-252,-262,-271,-279,-286,-291, - -296,-299,-301,-302,-302,-300,-297,-292, - -286,-278,-269,-259,-247,-233,-219,-202, - -185,-166,-145,-124,-101,-77,-52,-26, - 0,27,56,85,114,144,175,205, - 236,266,296,326,356,384,412,439, - 465,490,513,535,555,574,590,604, - 616,626,633,637,639,638,633,626, - 616,602,586,565,542,515,485,451, - 414,373,329,282,232,178,121,62, - 0,-65,-132,-202,-274,-347,-423,-500, - -578,-656,-736,-815,-894,-973,-1051,-1128, - -1203,-1276,-1347,-1415,-1479,-1540,-1596,-1648, - -1695,-1736,-1771,-1799,-1820,-1833,-1838,-1835, - -1822,-1800,-1767,-1724,-1670,-1605,-1527,-1437, - -1334,-1217,-1087,-943,-785,-611,-423,-219, - 0,235,487,755,1040,1341,1659,1994, - 2346,2715,3101,3504,3923,4359,4811,5280, - 5764,6264,6780,7310,7856,8415,8987,9573, - 10172,10782,11404,12036,12678,13329,13989,14656, - 15330,16009,16694,17382,18074,18767,19461,20155, - 20848,21539,22226,22909,23586,24256,24918,25571, - 26214,26845,27464,28068,28658,29231,29787,30325, - 30842,31339,31814,32266,32694,33097,33473,33823, - 34144,34437,34699,34931,35131,35299,35434,35535, - 35602,35634,35630,35591,35515,35402,35252,35065, - 34841,34579,34279,33941,33566,33153,32702,32214, - 31689,31128,30530,29897,29228,28525,27788,27017, - 26214,25379,24513,23617,22693,21740,20761,19755, - 18725,17672,16597,15501,14385,13251,12101,10935, - 9755,8563,7360,6148,4927,3701,2470,1235, - 0,-1235,-2470,-3701,-4927,-6148,-7360,-8563, - -9755,-10935,-12101,-13251,-14385,-15501,-16597,-17672, - -18725,-19755,-20761,-21740,-22693,-23617,-24513,-25379, - -26214,-27017,-27788,-28525,-29228,-29897,-30530,-31128, - -31689,-32214,-32702,-33153,-33566,-33941,-34279,-34579, - -34841,-35065,-35252,-35402,-35515,-35591,-35630,-35634, - -35602,-35535,-35434,-35299,-35131,-34931,-34699,-34437, - -34144,-33823,-33473,-33097,-32694,-32266,-31814,-31339, - -30842,-30325,-29787,-29231,-28658,-28068,-27464,-26845, - -26214,-25571,-24918,-24256,-23586,-22909,-22226,-21539, - -20848,-20155,-19461,-18767,-18074,-17382,-16694,-16009, - -15330,-14656,-13989,-13329,-12678,-12036,-11404,-10782, - -10172,-9573,-8987,-8415,-7856,-7310,-6780,-6264, - -5764,-5280,-4811,-4359,-3923,-3504,-3101,-2715, - -2346,-1994,-1659,-1341,-1040,-755,-487,-235, - 0,219,423,611,785,943,1087,1217, - 1334,1437,1527,1605,1670,1724,1767,1800, - 1822,1835,1838,1833,1820,1799,1771,1736, - 1695,1648,1596,1540,1479,1415,1347,1276, - 1203,1128,1051,973,894,815,736,656, - 578,500,423,347,274,202,132,65, - 0,-62,-121,-178,-232,-282,-329,-373, - -414,-451,-485,-515,-542,-565,-586,-602, - -616,-626,-633,-638,-639,-637,-633,-626, - -616,-604,-590,-574,-555,-535,-513,-490, - -465,-439,-412,-384,-356,-326,-296,-266, - -236,-205,-175,-144,-114,-85,-56,-27, - 0,26,52,77,101,124,145,166, - 185,202,219,233,247,259,269,278, - 286,292,297,300,302,302,301,299, - 296,291,286,279,271,262,252,242, - 231,218,206,193,179,165,150,135, - 120,105,89,74,59,44,29,14, - 0,-14,-27,-41,-53,-66,-77,-88, - -99,-109,-118,-126,-134,-141,-147,-152, - -157,-160,-163,-166,-167,-168,-168,-167 -}; - -class RDSProcessor : public BasebandProcessor { -public: - void execute(buffer_c8_t buffer) override { - - for (size_t i = 0; i= 9) { - s = 0; - if(sample_count >= SAMPLES_PER_BIT) { - cur_bit = (shared_memory.rdsdata[(bit_pos / 26) & 15]>>(25-(bit_pos % 26))) & 1; - prev_output = cur_output; - cur_output = prev_output ^ cur_bit; - - int32_t *src = waveform_biphase; - int idx = in_sample_index; - - for(int j=0; j= SAMPLE_BUFFER_SIZE) idx = 0; - } - - in_sample_index += SAMPLES_PER_BIT; - if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE; - - bit_pos++; - sample_count = 0; - } - - sample = sample_buffer[out_sample_index]; - sample_buffer[out_sample_index] = 0; - out_sample_index++; - if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0; - - //AM @ 228k/4=57kHz - switch (mphase) { - case 0: - case 2: sample = 0; break; - case 1: break; - case 3: sample = -sample; break; - } - mphase++; - if (mphase >= 4) mphase = 0; - - sample_count++; - } else { - s++; - } - - //FM - frq = (sample>>16) * 386760; - - phase = (phase + frq); - sphase = phase + (256<<16); - - //re = sintab[(sphase & 0x03FF0000)>>16]; - //im = sintab[(phase & 0x03FF0000)>>16]; - - buffer.p[i] = {(int8_t)re,(int8_t)im}; - } - } - -private: - int8_t re, im; - uint8_t mphase, s; - uint32_t bit_pos; - int32_t sample_buffer[SAMPLE_BUFFER_SIZE] = {0}; - int32_t val; - uint8_t prev_output = 0; - uint8_t cur_output = 0; - uint8_t cur_bit = 0; - int sample_count = SAMPLES_PER_BIT; - int in_sample_index = 0; - int32_t sample; - int out_sample_index = SAMPLE_BUFFER_SIZE-1; - uint32_t phase, sphase; - int32_t sig, frq, frq_im, rdsc; - int32_t k; -}; - class ToneProcessor : public BasebandProcessor { public: void execute(buffer_c8_t buffer) override { @@ -348,130 +196,6 @@ private: int32_t sample, sig, frq; }; -extern "C" { - -void __late_init(void) { - /* - * System initializations. - * - HAL initialization, this also initializes the configured device drivers - * and performs the board-specific initializations. - * - Kernel initialization, the main() function becomes a thread and the - * RTOS is active. - */ - halInit(); - - /* After this call, scheduler, systick, heap, etc. are available. */ - /* By doing chSysInit() here, it runs before C++ constructors, which may - * require the heap. - */ - chSysInit(); -} - -} - -static BasebandThread baseband_thread; - -static void init() { - i2s::i2s0::configure( - audio::i2s0_config_tx, - audio::i2s0_config_rx, - audio::i2s0_config_dma - ); - - audio::dma::init(); - audio::dma::configure(); - audio::dma::enable(); - - i2s::i2s0::tx_start(); - i2s::i2s0::rx_start(); - - LPC_CREG->DMAMUX = portapack::gpdma_mux; - gpdma::controller.enable(); - nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); - - baseband::dma::init(); - - touch::dma::init(); - - const auto thread_main = chThdSelf(); - - baseband_thread.thread_main = thread_main; - - baseband_thread.start(NORMALPRIO + 20); -} - -static void shutdown() { - // TODO: Is this complete? - - nvicDisableVector(DMA_IRQn); - - m0apptxevent_interrupt_disable(); - - chSysDisable(); - - systick_stop(); -} - -static void halt() { - port_disable(); - while(true) { - port_wait_for_interrupt(); - } -} - -class EventDispatcher { -public: - MessageHandlerMap& message_handlers() { - return message_map; - } - - void run() { - while(is_running) { - const auto events = wait(); - dispatch(events); - } - } - - void request_stop() { - is_running = false; - } - -private: - MessageHandlerMap message_map; - - bool is_running = true; - - eventmask_t wait() { - return chEvtWaitAny(ALL_EVENTS); - } - - void dispatch(const eventmask_t events) { - if( events & EVT_MASK_BASEBAND ) { - handle_baseband_queue(); - } - - if( events & EVT_MASK_SPECTRUM ) { - handle_spectrum(); - } - } - - void handle_baseband_queue() { - std::array message_buffer; - while(Message* const message = shared_memory.baseband_queue.pop(message_buffer)) { - message_map.send(message); - } - } - - void handle_spectrum() { - if( baseband_thread.baseband_processor ) { - baseband_thread.baseband_processor->update_spectrum(); - } - } -}; - -const auto baseband_buffer = - new std::array(); - char ram_loop[32]; typedef int (*fn_ptr)(void); fn_ptr loop_ptr; @@ -586,27 +310,4 @@ int main(void) { event_dispatcher.request_stop(); } ); - - /* TODO: Ensure DMAs are configured to point at first LLI in chain. */ - - touch::dma::allocate(); - touch::dma::enable(); - - baseband::dma::configure( - baseband_buffer->data(), - direction - ); - - //baseband::dma::allocate(4, 2048); - - event_dispatcher.run(); - - shutdown(); - - ShutdownMessage shutdown_message; - shared_memory.application_queue.push(shutdown_message); - - halt(); - - return 0; -} +*/ diff --git a/firmware/baseband-tx/name b/firmware/baseband-tx/name index ff5e208f..dd4d2a91 100644 --- a/firmware/baseband-tx/name +++ b/firmware/baseband-tx/name @@ -1 +1 @@ -Second module +Experimental diff --git a/firmware/baseband-tx/proc_audiotx.cpp b/firmware/baseband-tx/proc_audiotx.cpp index 8d51287f..c9c9c4bd 100644 --- a/firmware/baseband-tx/proc_audiotx.cpp +++ b/firmware/baseband-tx/proc_audiotx.cpp @@ -26,6 +26,6 @@ #include -void AudioTXProcessor::execute(buffer_c8_t buffer) { +void AudioTXProcessor::execute(const buffer_c8_t& buffer) { } diff --git a/firmware/baseband-tx/proc_audiotx.hpp b/firmware/baseband-tx/proc_audiotx.hpp index 30a1c030..53c639e2 100644 --- a/firmware/baseband-tx/proc_audiotx.hpp +++ b/firmware/baseband-tx/proc_audiotx.hpp @@ -29,7 +29,8 @@ class AudioTXProcessor : public BasebandProcessor { public: - void execute(buffer_c8_t buffer) override; + void execute(const buffer_c8_t& buffer) override; + private: int8_t audio_fifo[SAMPLERATE]; diff --git a/firmware/baseband-tx/proc_fsk_lcr.cpp b/firmware/baseband-tx/proc_fsk_lcr.cpp index 4e0b8112..a7de6a09 100644 --- a/firmware/baseband-tx/proc_fsk_lcr.cpp +++ b/firmware/baseband-tx/proc_fsk_lcr.cpp @@ -25,7 +25,7 @@ #include -void LCRFSKProcessor::execute(buffer_c8_t buffer) { +void LCRFSKProcessor::execute(const buffer_c8_t& buffer) { for (size_t i = 0; i 3000000) { - s = 0; - feedback = lfsr & 1; - lfsr >>= 1; - if (feedback == 1) - lfsr ^= POLY_MASK_32; - } else { - s++; - } - - aphase += lfsr;*/ - - /*if (s >= 10) { - s = 0; - aphase += 353205; // DEBUG - } else { - s++; - } - - sample = sintab[(aphase & 0x03FF0000)>>16];*/ - - // Duration timer - // if (s >= 10000) { //shared_memory.jammer_ranges[ir].duration s = 0; for (;;) { diff --git a/firmware/baseband-tx/proc_jammer.hpp b/firmware/baseband-tx/proc_jammer.hpp index 9a75940a..50cca639 100644 --- a/firmware/baseband-tx/proc_jammer.hpp +++ b/firmware/baseband-tx/proc_jammer.hpp @@ -26,7 +26,7 @@ class JammerProcessor : public BasebandProcessor { public: - void execute(buffer_c8_t buffer) override; + void execute(const buffer_c8_t& buffer) override; private: int32_t lfsr32 = 0xABCDE; diff --git a/firmware/baseband-tx/proc_playaudio.cpp b/firmware/baseband-tx/proc_playaudio.cpp index 816c7fd1..c9fcaba3 100644 --- a/firmware/baseband-tx/proc_playaudio.cpp +++ b/firmware/baseband-tx/proc_playaudio.cpp @@ -33,7 +33,7 @@ void PlayAudioProcessor::fill_buffer(int8_t * inptr) { asked = false; } -void PlayAudioProcessor::execute(buffer_c8_t buffer) { +void PlayAudioProcessor::execute(const buffer_c8_t& buffer){ // This is called at 1536000/2048 = 750Hz @@ -69,5 +69,5 @@ void PlayAudioProcessor::execute(buffer_c8_t buffer) { buffer.p[i] = {(int8_t)re,(int8_t)im}; } - fill_audio_buffer(preview_audio_buffer); + //fill_audio_buffer(preview_audio_buffer); } diff --git a/firmware/baseband-tx/proc_playaudio.hpp b/firmware/baseband-tx/proc_playaudio.hpp index 07c67412..e07c7b01 100644 --- a/firmware/baseband-tx/proc_playaudio.hpp +++ b/firmware/baseband-tx/proc_playaudio.hpp @@ -27,7 +27,7 @@ class PlayAudioProcessor : public BasebandProcessor { public: - void execute(buffer_c8_t buffer) override; + void execute(const buffer_c8_t& buffer) override; void fill_buffer(int8_t * inptr); private: diff --git a/firmware/baseband-tx/proc_rds.cpp b/firmware/baseband-tx/proc_rds.cpp new file mode 100644 index 00000000..d6fb366f --- /dev/null +++ b/firmware/baseband-tx/proc_rds.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "proc_rds.hpp" +#include "portapack_shared_memory.hpp" +#include "sine_table.hpp" + +#include + +void RDSProcessor::execute(const buffer_c8_t& buffer) { + + for (size_t i = 0; i= 9) { + s = 0; + if(sample_count >= SAMPLES_PER_BIT) { + cur_bit = (shared_memory.rdsdata[(bit_pos / 26) & 15]>>(25-(bit_pos % 26))) & 1; + prev_output = cur_output; + cur_output = prev_output ^ cur_bit; + + int32_t *src = waveform_biphase; + int idx = in_sample_index; + + for(int j=0; j= SAMPLE_BUFFER_SIZE) idx = 0; + } + + in_sample_index += SAMPLES_PER_BIT; + if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE; + + bit_pos++; + sample_count = 0; + } + + sample = sample_buffer[out_sample_index]; + sample_buffer[out_sample_index] = 0; + out_sample_index++; + if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0; + + //AM @ 228k/4=57kHz + switch (mphase) { + case 0: + case 2: sample = 0; break; + case 1: break; + case 3: sample = -sample; break; + } + mphase++; + if (mphase >= 4) mphase = 0; + + sample_count++; + } else { + s++; + } + + //FM + frq = (sample>>16) * 386760; + + phase = (phase + frq); + sphase = phase + (256<<16); + + //re = sintab[(sphase & 0x03FF0000)>>16]; + //im = sintab[(phase & 0x03FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } +} + diff --git a/firmware/baseband-tx/proc_rds.hpp b/firmware/baseband-tx/proc_rds.hpp new file mode 100644 index 00000000..17faadaa --- /dev/null +++ b/firmware/baseband-tx/proc_rds.hpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PROC_RDS_H__ +#define __PROC_RDS_H__ + +#include "baseband_processor.hpp" + +#define SAMPLES_PER_BIT 192 +#define FILTER_SIZE 576 +#define SAMPLE_BUFFER_SIZE SAMPLES_PER_BIT + FILTER_SIZE + +class RDSProcessor : public BasebandProcessor { +public: + void execute(const buffer_c8_t& buffer) override; + +private: + int8_t re, im; + uint8_t mphase, s; + uint32_t bit_pos; + int32_t sample_buffer[SAMPLE_BUFFER_SIZE] = {0}; + int32_t val; + uint8_t prev_output = 0; + uint8_t cur_output = 0; + uint8_t cur_bit = 0; + int sample_count = SAMPLES_PER_BIT; + int in_sample_index = 0; + int32_t sample; + int out_sample_index = SAMPLE_BUFFER_SIZE-1; + uint32_t phase, sphase; + int32_t sig, frq, frq_im, rdsc; + int32_t k; + + int32_t waveform_biphase[576] = { + 165,167,168,168,167,166,163,160, + 157,152,147,141,134,126,118,109, + 99,88,77,66,53,41,27,14, + 0,-14,-29,-44,-59,-74,-89,-105, + -120,-135,-150,-165,-179,-193,-206,-218, + -231,-242,-252,-262,-271,-279,-286,-291, + -296,-299,-301,-302,-302,-300,-297,-292, + -286,-278,-269,-259,-247,-233,-219,-202, + -185,-166,-145,-124,-101,-77,-52,-26, + 0,27,56,85,114,144,175,205, + 236,266,296,326,356,384,412,439, + 465,490,513,535,555,574,590,604, + 616,626,633,637,639,638,633,626, + 616,602,586,565,542,515,485,451, + 414,373,329,282,232,178,121,62, + 0,-65,-132,-202,-274,-347,-423,-500, + -578,-656,-736,-815,-894,-973,-1051,-1128, + -1203,-1276,-1347,-1415,-1479,-1540,-1596,-1648, + -1695,-1736,-1771,-1799,-1820,-1833,-1838,-1835, + -1822,-1800,-1767,-1724,-1670,-1605,-1527,-1437, + -1334,-1217,-1087,-943,-785,-611,-423,-219, + 0,235,487,755,1040,1341,1659,1994, + 2346,2715,3101,3504,3923,4359,4811,5280, + 5764,6264,6780,7310,7856,8415,8987,9573, + 10172,10782,11404,12036,12678,13329,13989,14656, + 15330,16009,16694,17382,18074,18767,19461,20155, + 20848,21539,22226,22909,23586,24256,24918,25571, + 26214,26845,27464,28068,28658,29231,29787,30325, + 30842,31339,31814,32266,32694,33097,33473,33823, + 34144,34437,34699,34931,35131,35299,35434,35535, + 35602,35634,35630,35591,35515,35402,35252,35065, + 34841,34579,34279,33941,33566,33153,32702,32214, + 31689,31128,30530,29897,29228,28525,27788,27017, + 26214,25379,24513,23617,22693,21740,20761,19755, + 18725,17672,16597,15501,14385,13251,12101,10935, + 9755,8563,7360,6148,4927,3701,2470,1235, + 0,-1235,-2470,-3701,-4927,-6148,-7360,-8563, + -9755,-10935,-12101,-13251,-14385,-15501,-16597,-17672, + -18725,-19755,-20761,-21740,-22693,-23617,-24513,-25379, + -26214,-27017,-27788,-28525,-29228,-29897,-30530,-31128, + -31689,-32214,-32702,-33153,-33566,-33941,-34279,-34579, + -34841,-35065,-35252,-35402,-35515,-35591,-35630,-35634, + -35602,-35535,-35434,-35299,-35131,-34931,-34699,-34437, + -34144,-33823,-33473,-33097,-32694,-32266,-31814,-31339, + -30842,-30325,-29787,-29231,-28658,-28068,-27464,-26845, + -26214,-25571,-24918,-24256,-23586,-22909,-22226,-21539, + -20848,-20155,-19461,-18767,-18074,-17382,-16694,-16009, + -15330,-14656,-13989,-13329,-12678,-12036,-11404,-10782, + -10172,-9573,-8987,-8415,-7856,-7310,-6780,-6264, + -5764,-5280,-4811,-4359,-3923,-3504,-3101,-2715, + -2346,-1994,-1659,-1341,-1040,-755,-487,-235, + 0,219,423,611,785,943,1087,1217, + 1334,1437,1527,1605,1670,1724,1767,1800, + 1822,1835,1838,1833,1820,1799,1771,1736, + 1695,1648,1596,1540,1479,1415,1347,1276, + 1203,1128,1051,973,894,815,736,656, + 578,500,423,347,274,202,132,65, + 0,-62,-121,-178,-232,-282,-329,-373, + -414,-451,-485,-515,-542,-565,-586,-602, + -616,-626,-633,-638,-639,-637,-633,-626, + -616,-604,-590,-574,-555,-535,-513,-490, + -465,-439,-412,-384,-356,-326,-296,-266, + -236,-205,-175,-144,-114,-85,-56,-27, + 0,26,52,77,101,124,145,166, + 185,202,219,233,247,259,269,278, + 286,292,297,300,302,302,301,299, + 296,291,286,279,271,262,252,242, + 231,218,206,193,179,165,150,135, + 120,105,89,74,59,44,29,14, + 0,-14,-27,-41,-53,-66,-77,-88, + -99,-109,-118,-126,-134,-141,-147,-152, + -157,-160,-163,-166,-167,-168,-168,-167 + }; + +}; + +#endif diff --git a/firmware/baseband-tx/proc_xylos.cpp b/firmware/baseband-tx/proc_xylos.cpp index bd377175..b3da393d 100644 --- a/firmware/baseband-tx/proc_xylos.cpp +++ b/firmware/baseband-tx/proc_xylos.cpp @@ -21,6 +21,10 @@ */ #include "proc_xylos.hpp" + +#include "dsp_iir_config.hpp" +//#include "audio_output.hpp" + #include "portapack_shared_memory.hpp" #include "sine_table.hpp" @@ -31,7 +35,7 @@ // 14 13 12 11: // 2108 989 2259 931 -void XylosProcessor::execute(buffer_c8_t buffer) { +void XylosProcessor::execute(const buffer_c8_t& buffer) { // This is called at 1536000/2048 = 750Hz @@ -88,5 +92,5 @@ void XylosProcessor::execute(buffer_c8_t buffer) { buffer.p[i] = {(int8_t)re,(int8_t)im}; } - fill_audio_buffer(preview_audio_buffer); + //audio_output.write(preview_audio_buffer); } diff --git a/firmware/baseband-tx/proc_xylos.hpp b/firmware/baseband-tx/proc_xylos.hpp index 09e322a9..0add6712 100644 --- a/firmware/baseband-tx/proc_xylos.hpp +++ b/firmware/baseband-tx/proc_xylos.hpp @@ -25,12 +25,18 @@ #include "baseband_processor.hpp" +#include "dsp_decimate.hpp" +#include "dsp_demodulate.hpp" + +//#include "audio_output.hpp" +#include "baseband_processor.hpp" + #define CCIR_TONELENGTH 15360-1 // 1536000/10/10 #define PHASEV 436.91 // (65536*1024)/1536000*10 class XylosProcessor : public BasebandProcessor { public: - void execute(buffer_c8_t buffer) override; + void execute(const buffer_c8_t& buffer) override; private: int16_t audio_data[64]; @@ -67,6 +73,8 @@ private: uint32_t aphase, phase, sphase; int32_t sample, frq; TXDoneMessage message; + + //AudioOutput audio_output; }; #endif diff --git a/firmware/baseband-tx/rssi_dma.cpp b/firmware/baseband-tx/rssi_dma.cpp index 5b060604..91f3b496 100644 --- a/firmware/baseband-tx/rssi_dma.cpp +++ b/firmware/baseband-tx/rssi_dma.cpp @@ -157,7 +157,7 @@ bool is_enabled() { } void disable() { - gpdma_channel.disable_force(); + gpdma_channel.disable(); } rf::rssi::buffer_t wait_for_buffer() { diff --git a/firmware/baseband-tx/rssi_thread.cpp b/firmware/baseband-tx/rssi_thread.cpp new file mode 100644 index 00000000..0bf15334 --- /dev/null +++ b/firmware/baseband-tx/rssi_thread.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "rssi_thread.hpp" + +#include "rssi.hpp" +#include "rssi_dma.hpp" +#include "rssi_stats_collector.hpp" + +#include "message.hpp" +#include "portapack_shared_memory.hpp" + +WORKING_AREA(rssi_thread_wa, 128); + +Thread* RSSIThread::start(const tprio_t priority) { + return chThdCreateStatic(rssi_thread_wa, sizeof(rssi_thread_wa), + priority, ThreadBase::fn, + this + ); +} + +void RSSIThread::run() { + rf::rssi::init(); + rf::rssi::dma::allocate(4, 400); + + RSSIStatisticsCollector stats; + + while(true) { + // TODO: Place correct sampling rate into buffer returned here: + const auto buffer_tmp = rf::rssi::dma::wait_for_buffer(); + const rf::rssi::buffer_t buffer { + buffer_tmp.p, buffer_tmp.count, sampling_rate + }; + + stats.process( + buffer, + [](const RSSIStatistics& statistics) { + const RSSIStatisticsMessage message { statistics }; + shared_memory.application_queue.push(message); + } + ); + } + + rf::rssi::dma::free(); +} diff --git a/firmware/baseband-tx/rssi_thread.hpp b/firmware/baseband-tx/rssi_thread.hpp new file mode 100644 index 00000000..0a00ef8e --- /dev/null +++ b/firmware/baseband-tx/rssi_thread.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __RSSI_THREAD_H__ +#define __RSSI_THREAD_H__ + +#include "thread_base.hpp" + +#include + +#include + +class RSSIThread : public ThreadBase { +public: + RSSIThread( + ) : ThreadBase { "rssi" } + { + } + + Thread* start(const tprio_t priority); + +private: + void run() override; + + const uint32_t sampling_rate { 400000 }; +}; + +#endif/*__RSSI_THREAD_H__*/ diff --git a/firmware/baseband-tx/spectrum_collector.cpp b/firmware/baseband-tx/spectrum_collector.cpp new file mode 100644 index 00000000..2b661c9a --- /dev/null +++ b/firmware/baseband-tx/spectrum_collector.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "spectrum_collector.hpp" + +#include "dsp_fft.hpp" + +#include "utility.hpp" +#include "event_m4.hpp" +#include "portapack_shared_memory.hpp" + +#include "event_m4.hpp" + +#include + +void SpectrumCollector::on_message(const Message* const message) { + switch(message->id) { + case Message::ID::UpdateSpectrum: + update(); + break; + + case Message::ID::SpectrumStreamingConfig: + set_state(*reinterpret_cast(message)); + break; + + default: + break; + } +} + +void SpectrumCollector::set_state(const SpectrumStreamingConfigMessage& message) { + if( message.mode == SpectrumStreamingConfigMessage::Mode::Running ) { + start(); + } else { + stop(); + } +} + +void SpectrumCollector::start() { + streaming = true; + ChannelSpectrumConfigMessage message { &fifo }; + shared_memory.application_queue.push(message); +} + +void SpectrumCollector::stop() { + streaming = false; + fifo.reset_in(); +} + +void SpectrumCollector::set_decimation_factor( + const size_t decimation_factor +) { + channel_spectrum_decimator.set_factor(decimation_factor); +} + +/* TODO: Refactor to register task with idle thread? + * It's sad that the idle thread has to call all the way back here just to + * perform the deferred task on the buffer of data we prepared. + */ + +void SpectrumCollector::feed( + const buffer_c16_t& channel, + const uint32_t filter_pass_frequency, + const uint32_t filter_stop_frequency +) { + // Called from baseband processing thread. + channel_filter_pass_frequency = filter_pass_frequency; + channel_filter_stop_frequency = filter_stop_frequency; + + channel_spectrum_decimator.feed( + channel, + [this](const buffer_c16_t& data) { + this->post_message(data); + } + ); +} + +void SpectrumCollector::post_message(const buffer_c16_t& data) { + // Called from baseband processing thread. + if( streaming && !channel_spectrum_request_update ) { + fft_swap(data, channel_spectrum); + channel_spectrum_sampling_rate = data.sampling_rate; + channel_spectrum_request_update = true; + EventDispatcher::events_flag(EVT_MASK_SPECTRUM); + } +} + +void SpectrumCollector::update() { + // Called from idle thread (after EVT_MASK_SPECTRUM is flagged) + if( streaming && channel_spectrum_request_update ) { + /* Decimated buffer is full. Compute spectrum. */ + fft_c_preswapped(channel_spectrum); + + ChannelSpectrum spectrum; + spectrum.sampling_rate = channel_spectrum_sampling_rate; + spectrum.channel_filter_pass_frequency = channel_filter_pass_frequency; + spectrum.channel_filter_stop_frequency = channel_filter_stop_frequency; + for(size_t i=0; i +#include + +#include "message.hpp" + +class SpectrumCollector { +public: + constexpr SpectrumCollector( + ) : channel_spectrum_decimator { 1 } + { + } + + void on_message(const Message* const message); + + void set_decimation_factor(const size_t decimation_factor); + + void feed( + const buffer_c16_t& channel, + const uint32_t filter_pass_frequency, + const uint32_t filter_stop_frequency + ); + +private: + BlockDecimator<256> channel_spectrum_decimator; + ChannelSpectrumFIFO fifo; + + volatile bool channel_spectrum_request_update { false }; + bool streaming { false }; + std::array, 256> channel_spectrum; + uint32_t channel_spectrum_sampling_rate { 0 }; + uint32_t channel_filter_pass_frequency { 0 }; + uint32_t channel_filter_stop_frequency { 0 }; + + void post_message(const buffer_c16_t& data); + + void set_state(const SpectrumStreamingConfigMessage& message); + void start(); + void stop(); + + void update(); +}; + +#endif/*__SPECTRUM_COLLECTOR_H__*/ diff --git a/firmware/baseband-tx/thread_base.hpp b/firmware/baseband-tx/thread_base.hpp new file mode 100644 index 00000000..9b5e8a99 --- /dev/null +++ b/firmware/baseband-tx/thread_base.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __THREAD_BASE_H__ +#define __THREAD_BASE_H__ + +#include + +class ThreadBase { +public: + constexpr ThreadBase( + const char* const name + ) : name { name } + { + } + +protected: + static msg_t fn(void* arg) { + auto obj = static_cast(arg); + chRegSetThreadName(obj->name); + obj->run(); + + return 0; + } + +private: + const char* const name; + + virtual void run() = 0; +}; + +#endif/*__THREAD_BASE_H__*/ diff --git a/firmware/baseband-tx/touch_dma.cpp b/firmware/baseband-tx/touch_dma.cpp index aa120ff3..ce66617a 100644 --- a/firmware/baseband-tx/touch_dma.cpp +++ b/firmware/baseband-tx/touch_dma.cpp @@ -122,7 +122,7 @@ bool is_enabled() { } void disable() { - gpdma_channel.disable_force(); + gpdma_channel.disable(); } } /* namespace dma */ diff --git a/firmware/baseband.bin b/firmware/baseband.bin index 6ff2307a..41e3840f 100644 Binary files a/firmware/baseband.bin and b/firmware/baseband.bin differ diff --git a/firmware/baseband/description b/firmware/baseband/description index 517d61c1..884ea0b0 100644 --- a/firmware/baseband/description +++ b/firmware/baseband/description @@ -1 +1 @@ -Basic RX/TX stuff for testing :) +Original firmware's functionalities: Receiver. diff --git a/firmware/baseband/main.cpp b/firmware/baseband/main.cpp index eea7e572..d6a895a7 100755 --- a/firmware/baseband/main.cpp +++ b/firmware/baseband/main.cpp @@ -50,121 +50,6 @@ #include #include #include -#include -#include - -class ThreadBase { -public: - constexpr ThreadBase( - const char* const name - ) : name { name } - { - } - - static msg_t fn(void* arg) { - auto obj = static_cast(arg); - chRegSetThreadName(obj->name); - obj->run(); - - return 0; - } - - virtual void run() = 0; - -private: - const char* const name; -}; - -class BasebandThread : public ThreadBase { -public: - BasebandThread( - ) : ThreadBase { "baseband" } - { - } - - Thread* start(const tprio_t priority) { - return chThdCreateStatic(wa, sizeof(wa), - priority, ThreadBase::fn, - this - ); - } - - Thread* thread_main { nullptr }; - Thread* thread_rssi { nullptr }; - BasebandProcessor* baseband_processor { nullptr }; - BasebandConfiguration baseband_configuration; - -private: - WORKING_AREA(wa, 2048); - - void run() override { - BasebandStatsCollector stats { - chSysGetIdleThread(), - thread_main, - thread_rssi, - chThdSelf() - }; - - while(true) { - // TODO: Place correct sampling rate into buffer returned here: - const auto buffer_tmp = baseband::dma::wait_for_rx_buffer(); - const buffer_c8_t buffer { - buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate - }; - - if( baseband_processor ) { - baseband_processor->execute(buffer); - } - - stats.process(buffer, - [](const BasebandStatistics statistics) { - const BasebandStatisticsMessage message { statistics }; - shared_memory.application_queue.push(message); - } - ); - } - } -}; - -class RSSIThread : public ThreadBase { -public: - RSSIThread( - ) : ThreadBase { "rssi" } - { - } - - Thread* start(const tprio_t priority) { - return chThdCreateStatic(wa, sizeof(wa), - priority, ThreadBase::fn, - this - ); - } - - uint32_t sampling_rate { 400000 }; - -private: - WORKING_AREA(wa, 128); - - void run() override { - RSSIStatisticsCollector stats; - - while(true) { - // TODO: Place correct sampling rate into buffer returned here: - const auto buffer_tmp = rf::rssi::dma::wait_for_buffer(); - const rf::rssi::buffer_t buffer { - buffer_tmp.p, buffer_tmp.count, sampling_rate - }; - - stats.process( - buffer, - [](const RSSIStatistics statistics) { - const RSSIStatisticsMessage message { statistics }; - shared_memory.application_queue.push(message); - } - ); - } - } -}; extern "C" { @@ -187,9 +72,6 @@ void __late_init(void) { } -static BasebandThread baseband_thread; -static RSSIThread rssi_thread; - static void init() { i2s::i2s0::configure( audio::i2s0_config_tx, @@ -208,31 +90,9 @@ static void init() { gpdma::controller.enable(); nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); - baseband::dma::init(); - - rf::rssi::init(); touch::dma::init(); - - const auto thread_main = chThdSelf(); - - const auto thread_rssi = rssi_thread.start(NORMALPRIO + 10); - - baseband_thread.thread_main = thread_main; - baseband_thread.thread_rssi = thread_rssi; - - baseband_thread.start(NORMALPRIO + 20); -} - -static void shutdown() { - // TODO: Is this complete? - - nvicDisableVector(DMA_IRQn); - - m0apptxevent_interrupt_disable(); - - chSysDisable(); - - systick_stop(); + touch::dma::allocate(); + touch::dma::enable(); } static void halt() { @@ -242,146 +102,27 @@ static void halt() { } } -class EventDispatcher { -public: - MessageHandlerMap& message_handlers() { - return message_map; - } +static void shutdown() { + // TODO: Is this complete? + + nvicDisableVector(DMA_IRQn); + + chSysDisable(); - void run() { - while(is_running) { - const auto events = wait(); - dispatch(events); - } - } + systick_stop(); - void request_stop() { - is_running = false; - } + ShutdownMessage shutdown_message; + shared_memory.application_queue.push(shutdown_message); -private: - MessageHandlerMap message_map; - - bool is_running = true; - - eventmask_t wait() { - return chEvtWaitAny(ALL_EVENTS); - } - - void dispatch(const eventmask_t events) { - if( events & EVT_MASK_BASEBAND ) { - handle_baseband_queue(); - } - - if( events & EVT_MASK_SPECTRUM ) { - handle_spectrum(); - } - } - - void handle_baseband_queue() { - std::array message_buffer; - while(Message* const message = shared_memory.baseband_queue.pop(message_buffer)) { - message_map.send(message); - } - } - - void handle_spectrum() { - if( baseband_thread.baseband_processor ) { - baseband_thread.baseband_processor->update_spectrum(); - } - } -}; - -static constexpr auto direction = baseband::Direction::Receive; + halt(); +} int main(void) { init(); - events_initialize(chThdSelf()); - m0apptxevent_interrupt_enable(); - - EventDispatcher event_dispatcher; - auto& message_handlers = event_dispatcher.message_handlers(); - - message_handlers.register_handler(Message::ID::BasebandConfiguration, - [&message_handlers](const Message* const p) { - auto message = reinterpret_cast(p); - if( message->configuration.mode != baseband_thread.baseband_configuration.mode ) { - - if( baseband_thread.baseband_processor ) { - i2s::i2s0::tx_mute(); - baseband::dma::disable(); - rf::rssi::stop(); - } - - // TODO: Timing problem around disabling DMA and nulling and deleting old processor - auto old_p = baseband_thread.baseband_processor; - baseband_thread.baseband_processor = nullptr; - delete old_p; - - switch(message->configuration.mode) { - case 0: - baseband_thread.baseband_processor = new NarrowbandAMAudio(); - break; - - case 1: - baseband_thread.baseband_processor = new NarrowbandFMAudio(); - break; - - case 2: - baseband_thread.baseband_processor = new WidebandFMAudio(); - break; - - case 3: - baseband_thread.baseband_processor = new AISProcessor(); - break; - - case 4: - baseband_thread.baseband_processor = new WidebandSpectrum(); - break; - - case 5: - baseband_thread.baseband_processor = new TPMSProcessor(); - break; - - default: - break; - } - - if( baseband_thread.baseband_processor ) { - if( direction == baseband::Direction::Receive ) { - rf::rssi::start(); - } - baseband::dma::enable(direction); - } - } - - baseband_thread.baseband_configuration = message->configuration; - } - ); - - message_handlers.register_handler(Message::ID::Shutdown, - [&event_dispatcher](const Message* const) { - event_dispatcher.request_stop(); - } - ); - /* TODO: Ensure DMAs are configured to point at first LLI in chain. */ - if( direction == baseband::Direction::Receive ) { - rf::rssi::dma::allocate(4, 400); - } - - touch::dma::allocate(); - touch::dma::enable(); - - const auto baseband_buffer = - new std::array(); - baseband::dma::configure( - baseband_buffer->data(), - direction - ); - + EventDispatcher event_dispatcher; event_dispatcher.run(); shutdown(); diff --git a/firmware/baseband/name b/firmware/baseband/name index 8d194a1d..dae227ea 100644 --- a/firmware/baseband/name +++ b/firmware/baseband/name @@ -1 +1 @@ -First module +Receiver diff --git a/firmware/baseband/proc_afskrx.cpp b/firmware/baseband/proc_afskrx.cpp index a7514954..ad3e3bbb 100644 --- a/firmware/baseband/proc_afskrx.cpp +++ b/firmware/baseband/proc_afskrx.cpp @@ -25,30 +25,17 @@ using namespace lpc43xx; -void AFSKRXProcessor::execute(buffer_c8_t buffer) { +void AFSKRXProcessor::execute(const buffer_c8_t& buffer) { + if( !configured ) { + return; + } /* Called every 2048/3072000 second -- 1500Hz. */ - - auto decimator_out = decimator.execute(buffer); - - const buffer_c16_t work_baseband_buffer { - (complex16_t*)decimator_out.p, - sizeof(*decimator_out.p) * decimator_out.count - }; - - /* 96kHz complex[64] - * -> FIR filter, <6kHz (0.063fs) pass, gain 1.0 - * -> 48kHz int16_t[32] */ - auto channel = channel_filter.execute(decimator_out, work_baseband_buffer); - - const buffer_s16_t work_audio_buffer { - (int16_t*)decimator_out.p, - sizeof(*decimator_out.p) * decimator_out.count - }; - /* 48kHz complex[32] - * -> FM demodulation - * -> 48kHz int16_t[32] */ - auto audio = demod.execute(channel, work_audio_buffer); + 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); + + auto audio = demod.execute(channel_out, work_audio_buffer); /*static uint64_t audio_present_history = 0; const auto audio_present_now = squelch.execute(audio); @@ -65,7 +52,7 @@ void AFSKRXProcessor::execute(buffer_c8_t buffer) { }*/ //} - audio_hpf.execute_in_place(audio); + //audio_hpf.execute_in_place(audio); for(size_t i=0; i 10) { @@ -97,9 +84,9 @@ void AFSKRXProcessor::execute(buffer_c8_t buffer) { if (sc >= 600) { sc = 0; - AFSKDataMessage message; - memcpy(message.data,aud,128*2); - shared_memory.application_queue.push(message); + //AFSKDataMessage message; + //memcpy(message.data,aud,128*2); + //shared_memory.application_queue.push(message); audc = 0; } else { sc++; @@ -110,7 +97,7 @@ void AFSKRXProcessor::execute(buffer_c8_t buffer) { audc++; } - fill_audio_buffer(audio); + audio_output.write(audio); } void AFSKRXProcessor::data_handler( diff --git a/firmware/baseband/proc_afskrx.hpp b/firmware/baseband/proc_afskrx.hpp index 275d174c..8a06743c 100644 --- a/firmware/baseband/proc_afskrx.hpp +++ b/firmware/baseband/proc_afskrx.hpp @@ -24,38 +24,35 @@ #include "baseband_processor.hpp" -#include "channel_decimator.hpp" #include "dsp_decimate.hpp" #include "dsp_demodulate.hpp" -#include "dsp_fir_taps.hpp" -#include "dsp_iir.hpp" -#include "dsp_iir_config.hpp" -#include "dsp_squelch.hpp" +#include "audio_output.hpp" #include "message.hpp" -#include -#include -#include - class AFSKRXProcessor : public BasebandProcessor { public: - AFSKRXProcessor() { - decimator.set_decimation_factor(ChannelDecimator::DecimationFactor::By32); - channel_filter.configure(channel_filter_taps.taps, 2); - } - - void execute(buffer_c8_t buffer) override; + void execute(const buffer_c8_t& buffer) override; private: - ChannelDecimator decimator; - const fir_taps_real<64>& channel_filter_taps = taps_64_lp_042_078_tfilter; - dsp::decimate::FIRAndDecimateComplex channel_filter; - dsp::demodulate::FM demod { 48000, 5000 }; - - IIRBiquadFilter audio_hpf { audio_hpf_config }; - //FMSquelch squelch; + std::array dst; + const buffer_c16_t dst_buffer { + dst.data(), + dst.size() + }; + const buffer_f32_t work_audio_buffer { + (float*)dst.data(), + sizeof(dst) / sizeof(float) + }; + dsp::decimate::FIRAndDecimateComplex channel_filter; + dsp::demodulate::FM demod; // 48000 5000 + + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0; + dsp::decimate::FIRC16xR16x32Decim8 decim_1; + + AudioOutput audio_output; + uint16_t bit_timer = 0, freq_timer = 0; uint16_t sc; uint8_t audc, spur, sign, prev_sign, bit = 0; @@ -63,6 +60,9 @@ private: int16_t aud[128]; void data_handler(const double data); + + bool configured { false }; + void configure(const NBFMConfigureMessage& message); }; #endif/*__PROC_TPMS_H__*/ diff --git a/firmware/baseband/proc_sigfrx.cpp b/firmware/baseband/proc_sigfrx.cpp index c6ca3a8e..1dbfbed7 100644 --- a/firmware/baseband/proc_sigfrx.cpp +++ b/firmware/baseband/proc_sigfrx.cpp @@ -24,46 +24,6 @@ #include #include -void SIGFRXProcessor::execute(buffer_c8_t buffer) { +void SIGFRXProcessor::execute(const buffer_c8_t& buffer) { /* Called every 2048/3072000 second -- 1500Hz. */ - - auto decimator_out = decimator.execute(buffer); - - const buffer_c16_t work_baseband_buffer { - (complex16_t*)decimator_out.p, - sizeof(*decimator_out.p) * decimator_out.count - }; - - /* 192kHz complex[64] - * -> 96kHz int16_t[32] */ - //auto channel = channel_filter.execute(decimator_out, work_baseband_buffer); - - // TODO: Feed channel_stats post-decimation data? - feed_channel_spectrum( - decimator_out, - 41000, //decimator_out.sampling_rate * channel_filter_taps.pass_frequency_normalized, - 70000 //decimator_out.sampling_rate * channel_filter_taps.stop_frequency_normalized - ); - - /*const buffer_s16_t work_audio_buffer { - (int16_t*)decimator_out.p, - sizeof(*decimator_out.p) * decimator_out.count - }; - * - auto audio = demod.execute(channel, work_audio_buffer); - - static uint64_t audio_present_history = 0; - const auto audio_present_now = squelch.execute(audio); - audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0); - const bool audio_present = (audio_present_history != 0); - - if( !audio_present ) { - // Zero audio buffer. - for(size_t i=0; i& channel_filter_taps = taps_64_lp_410_700_tfilter; //taps_64_lp_104_140_tfilter - //dsp::decimate::FIRAndDecimateComplex channel_filter; - dsp::demodulate::FM demod { 48000, 7500 }; - IIRBiquadFilter audio_hpf { audio_hpf_config }; - FMSquelch squelch; }; #endif diff --git a/firmware/bootstrap/bootstrap.bin b/firmware/bootstrap/bootstrap.bin new file mode 100755 index 00000000..c234f8ae Binary files /dev/null and b/firmware/bootstrap/bootstrap.bin differ diff --git a/firmware/chibios-portapack/ext/fatfs/src/ff.c b/firmware/chibios-portapack/ext/fatfs/src/ff.c index 9c887c45..385063f5 100644 --- a/firmware/chibios-portapack/ext/fatfs/src/ff.c +++ b/firmware/chibios-portapack/ext/fatfs/src/ff.c @@ -2579,7 +2579,6 @@ FRESULT f_read ( UINT rcnt, cc; BYTE csect, *rbuff = (BYTE*)buff; - *br = 0; /* Clear read byte counter */ res = validate(fp); /* Check validity */ diff --git a/firmware/chibios-portapack/os/hal/platforms/LPC43xx/sdc_lld.c b/firmware/chibios-portapack/os/hal/platforms/LPC43xx/sdc_lld.c index 835abd80..e02accbe 100644 --- a/firmware/chibios-portapack/os/hal/platforms/LPC43xx/sdc_lld.c +++ b/firmware/chibios-portapack/os/hal/platforms/LPC43xx/sdc_lld.c @@ -619,7 +619,7 @@ void sdc_lld_start_clk(SDCDriver *sdcp) { (void)sdcp; sdio_cclk_set_400khz(); /* TODO: Reset card using CMD0 + init flag? */ - sdio_send_command(sdcp, 0 | (1U << 15), 0); + if (sdio_send_command(sdcp, 0 | (1U << 15), 0) != CH_SUCCESS) for(;;); } /** diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 50afc442..95657f99 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -419,12 +419,14 @@ public: int64_t freq = 0; }; -class DisplayFrameSyncMessage : public Message { +class AFSKDataMessage : public Message { public: - constexpr DisplayFrameSyncMessage( - ) : Message { ID::DisplayFrameSync } + constexpr AFSKDataMessage( + ) : Message { ID::AFSKData } { } + + int16_t data[128] = {0}; }; class FIFOSignalMessage : public Message { diff --git a/firmware/common/modules.h b/firmware/common/modules.h index 5fb77add..a6c9c36b 100644 --- a/firmware/common/modules.h +++ b/firmware/common/modules.h @@ -1,2 +1,2 @@ -const char md5_baseband[16] = {0x0f,0xf7,0xa8,0x6f,0x0f,0xb3,0x88,0x4c,0xec,0x45,0xcf,0x8d,0xd5,0xf8,0x11,0x92,}; -const char md5_baseband_tx[16] = {0x77,0xa8,0x27,0xec,0xb4,0xcb,0xe6,0x17,0x06,0x70,0x49,0x01,0xed,0x48,0x1f,0x54,}; +const char md5_baseband[16] = {0xf8,0xf3,0x7b,0x36,0x68,0xd3,0xa1,0x85,0x26,0xb1,0x76,0x99,0x46,0x95,0xfd,0xec,}; +const char md5_baseband_tx[16] = {0xfc,0xe9,0x57,0x6b,0xfb,0xac,0x72,0x61,0x65,0x4e,0x3d,0x7f,0xb4,0x78,0xbd,0xd2,}; diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 26fee967..78fcde43 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -1,24 +1,3 @@ -/* - * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. - * - * This file is part of PortaPack. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - #include "ui_widget.hpp" #include "ui_painter.hpp" @@ -305,77 +284,91 @@ void Text::set(const std::string value) { } void Text::paint(Painter& painter) { - if (style_ == nullptr) style_ = &style(); const auto rect = screen_rect(); + const auto s = style(); painter.fill_rectangle(rect, s.background); painter.draw_string( rect.pos, - (*style_), + s, text ); } -/* Button ****************************************************************/ +/* Checkbox **************************************************************/ - Button::Button( - Rect parent_rect, - std::string text -) : Widget { parent_rect }, - text_ { text } -{ - flags.focusable = true; -} - -void Button::set_text(const std::string value) { +void Checkbox::set_text(const std::string value) { text_ = value; set_dirty(); } -std::string Button::text() const { +std::string Checkbox::text() const { return text_; } -void Button::paint(Painter& painter) { +void Checkbox::set_value(const bool value) { + value_ = value; + set_dirty(); +} + +bool Checkbox::value() const { + return value_; +} + +void Checkbox::paint(Painter& painter) { const auto r = screen_rect(); - if (style_ == nullptr) style_ = &style(); - - const auto paint_style = (has_focus() || flags.highlighted) ? style_->invert() : *(style_); - - painter.draw_rectangle(r, style().foreground); + const auto paint_style = (has_focus() || flags.highlighted) ? style().invert() : style(); + + painter.draw_rectangle({ r.pos.x, r.pos.y, 24, 24 }, style().foreground); painter.fill_rectangle( - { r.pos.x + 1, r.pos.y + 1, r.size.w - 2, r.size.h - 2 }, - paint_style.background + { + static_cast(r.pos.x + 1), static_cast(r.pos.y + 1), + static_cast(24 - 2), static_cast(24 - 2) + }, + style().background ); - + + painter.draw_rectangle({ r.pos.x+2, r.pos.y+2, 24-4, 24-4 }, paint_style.background); + + if (value_ == true) { + // Check + portapack::display.draw_line( {r.pos.x+2, r.pos.y+14}, {r.pos.x+6, r.pos.y+18}, ui::Color::green()); + portapack::display.draw_line( {r.pos.x+6, r.pos.y+18}, {r.pos.x+20, r.pos.y+4}, ui::Color::green()); + } else { + // Cross + portapack::display.draw_line( {r.pos.x+1, r.pos.y+1}, {r.pos.x+24-2, r.pos.y+24-2}, ui::Color::red()); + portapack::display.draw_line( {r.pos.x+24-2, r.pos.y+1}, {r.pos.x+1, r.pos.y+24-2}, ui::Color::red()); + } + const auto label_r = paint_style.font.size_of(text_); painter.draw_string( - { r.pos.x + (r.size.w - label_r.w) / 2, r.pos.y + (r.size.h - label_r.h) / 2 }, + { + static_cast(r.pos.x + 24 + 4), + static_cast(r.pos.y + (24 - label_r.h) / 2) + }, paint_style, text_ ); } -bool Button::on_key(const KeyEvent key) { +bool Checkbox::on_key(const KeyEvent key) { if( key == KeyEvent::Select ) { + value_ = not value_; + set_dirty(); + if( on_select ) { on_select(*this); return true; } - } else { - if( on_dir ) { - on_dir(*this, key); - return false; - } } return false; } -bool Button::on_touch(const TouchEvent event) { +bool Checkbox::on_touch(const TouchEvent event) { switch(event.type) { case TouchEvent::Type::Start: flags.highlighted = true; @@ -385,6 +378,7 @@ bool Button::on_touch(const TouchEvent event) { case TouchEvent::Type::End: flags.highlighted = false; + value_ = not value_; set_dirty(); if( on_select ) { on_select(*this); @@ -427,6 +421,199 @@ bool Button::on_touch(const TouchEvent event) { #endif } +/* Button ****************************************************************/ + + Button::Button( + Rect parent_rect, + std::string text +) : Widget { parent_rect }, + text_ { text } +{ + flags.focusable = true; +} + +void Button::set_text(const std::string value) { + text_ = value; + set_dirty(); +} + +std::string Button::text() const { + return text_; +} + +void Button::paint(Painter& painter) { + const auto r = screen_rect(); + + const auto paint_style = (has_focus() || flags.highlighted) ? style().invert() : style(); + + painter.draw_rectangle(r, style().foreground); + + painter.fill_rectangle( + { r.pos.x + 1, r.pos.y + 1, r.size.w - 2, r.size.h - 2 }, + paint_style.background + ); + + const auto label_r = paint_style.font.size_of(text_); + painter.draw_string( + { r.pos.x + (r.size.w - label_r.w) / 2, r.pos.y + (r.size.h - label_r.h) / 2 }, + paint_style, + text_ + ); +} + +bool Button::on_key(const KeyEvent key) { + if( key == KeyEvent::Select ) { + if( on_select ) { + on_select(*this); + return true; + } + } + + return false; +} + +bool Button::on_touch(const TouchEvent event) { + switch(event.type) { + case TouchEvent::Type::Start: + flags.highlighted = true; + set_dirty(); + return true; + + + case TouchEvent::Type::End: + flags.highlighted = false; + set_dirty(); + if( on_select ) { + on_select(*this); + } + return true; + + default: + return false; + } +#if 0 + switch(event.type) { + case TouchEvent::Type::Start: + flags.highlighted = true; + set_dirty(); + return true; + case TouchEvent::Type::Move: + { + const bool new_highlighted = screen_rect().contains(event.point); + if( flags.highlighted != new_highlighted ) { + flags.highlighted = new_highlighted; + set_dirty(); + } + } + return true; + case TouchEvent::Type::End: + if( flags.highlighted ) { + flags.highlighted = false; + set_dirty(); + if( on_select ) { + on_select(*this); + } + } + return true; + default: + return false; + } +#endif +} + +/* Image *****************************************************************/ + +Image::Image( +) : Image { { }, nullptr, Color::white(), Color::black() } +{ +} + +Image::Image( + const Rect parent_rect, + const Bitmap* bitmap, + const Color foreground, + const Color background +) : Widget { parent_rect }, + bitmap_ { bitmap }, + foreground_ { foreground }, + background_ { background } +{ +} + +void Image::set_bitmap(const Bitmap* bitmap) { + bitmap_ = bitmap; + set_dirty(); +} + +void Image::set_foreground(const Color color) { + foreground_ = color; + set_dirty(); +} + +void Image::set_background(const Color color) { + background_ = color; + set_dirty(); +} + +void Image::paint(Painter& painter) { + if( bitmap_ ) { + // Code also handles ImageButton behavior. + const bool selected = (has_focus() || flags.highlighted); + painter.draw_bitmap( + screen_pos(), + *bitmap_, + selected ? background_ : foreground_, + selected ? foreground_ : background_ + ); + } +} + +/* ImageButton ***********************************************************/ + +// TODO: Virtually all this code is duplicated from Button. Base class? + +ImageButton::ImageButton( + const Rect parent_rect, + const Bitmap* bitmap, + const Color foreground, + const Color background +) : Image { parent_rect, bitmap, foreground, background } +{ + flags.focusable = true; +} + +bool ImageButton::on_key(const KeyEvent key) { + if( key == KeyEvent::Select ) { + if( on_select ) { + on_select(*this); + return true; + } + } + + return false; +} + +bool ImageButton::on_touch(const TouchEvent event) { + switch(event.type) { + case TouchEvent::Type::Start: + flags.highlighted = true; + set_dirty(); + return true; + + + case TouchEvent::Type::End: + flags.highlighted = false; + set_dirty(); + if( on_select ) { + on_select(*this); + } + return true; + + default: + return false; + } +} + /* OptionsField **********************************************************/ OptionsField::OptionsField( @@ -480,6 +667,12 @@ void OptionsField::paint(Painter& painter) { } } +void OptionsField::on_focus() { + if( on_show_options ) { + on_show_options(); + } +} + bool OptionsField::on_encoder(const EncoderEvent delta) { set_selected_index(selected_index() + delta); return true; diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index b6bfeb0c..80d3de46 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -188,48 +188,11 @@ public: Text(Rect parent_rect); void set(const std::string value); - void set_style(const Style* new_style); - + void paint(Painter& painter) override; private: std::string text; - const Style* style_ { nullptr }; -}; - -class Checkbox : public Widget { -public: - std::function on_select; - - Checkbox( - Point parent_point, - std::string text - ) : Widget { parent_point }, - text_ { text } - { - flags.focusable = true; - } - - Checkbox( - ) : Checkbox { { }, { } } - { - } - - void set_text(const std::string value); - void set_style(const Style* new_style); - std::string text() const; - void set_value(const bool value); - bool value() const; - - void paint(Painter& painter) override; - - bool on_key(const KeyEvent key) override; - bool on_touch(const TouchEvent event) override; - -private: - std::string text_; - bool value_ = false; - const Style* style_ { nullptr }; }; class Button : public Widget { @@ -244,8 +207,6 @@ public: } void set_text(const std::string value); - void set_text(const int value); - void set_style(const Style* new_style); std::string text() const; void paint(Painter& painter) override; @@ -255,7 +216,43 @@ public: private: std::string text_; - const Style* style_ { nullptr }; +}; + +class Image : public Widget { +public: + Image(); + Image( + const Rect parent_rect, + const Bitmap* bitmap, + const Color foreground, + const Color background + ); + + void set_bitmap(const Bitmap* bitmap); + void set_foreground(const Color color); + void set_background(const Color color); + + void paint(Painter& painter) override; + +private: + const Bitmap* bitmap_; + Color foreground_; + Color background_; +}; + +class ImageButton : public Image { +public: + std::function on_select; + + ImageButton( + const Rect parent_rect, + const Bitmap* bitmap, + const Color foreground, + const Color background + ); + + bool on_key(const KeyEvent key) override; + bool on_touch(const TouchEvent event) override; }; class OptionsField : public Widget { @@ -266,6 +263,7 @@ public: using options_t = std::vector; std::function on_change; + std::function on_show_options; OptionsField(Point parent_pos, size_t length, options_t options); @@ -276,6 +274,7 @@ public: void paint(Painter& painter) override; + void on_focus() override; bool on_encoder(const EncoderEvent delta) override; bool on_touch(const TouchEvent event) override; diff --git a/firmware/portapack-h1-firmware.bin b/firmware/portapack-h1-firmware.bin index a17ff1f5..719b8ad6 100644 Binary files a/firmware/portapack-h1-firmware.bin and b/firmware/portapack-h1-firmware.bin differ diff --git a/firmware/tools/make_baseband_file.py b/firmware/tools/make_baseband_file.py index 7f741569..5e30acd5 100755 --- a/firmware/tools/make_baseband_file.py +++ b/firmware/tools/make_baseband_file.py @@ -55,8 +55,9 @@ sys.argv = sys.argv[1:] # Format for module file: # Magic (4), Version (2), Length (4), Name (16), MD5 (16), Description (214) -# Module binary... -# MD5 (16) +# 0x00 pad bytes (256) +# Module binary (padded to 32768-16) +# MD5 (16) again, so that module code can read it (dirty...) for args in sys.argv: data = read_image(args + '/build/' + args + '.bin') @@ -66,7 +67,7 @@ for args in sys.argv: info = 'PPM ' # Version - info += struct.pack('H', 1) + info += struct.pack('H', 2) # Length info += struct.pack('I', len(data)) @@ -94,9 +95,12 @@ for args in sys.argv: description += (data_default_byte * pad_size) info += description - # Padding + # Header padding to fit in SD card sector + info += (data_default_byte * 256) + + # Binary padding data = info + data - pad_size = (32768 + 256 - 16) - len(data) + pad_size = (32768 + 512 - 16) - len(data) data += (data_default_byte * pad_size) data += digest write_file(data, args + '.bin') @@ -108,6 +112,6 @@ for args in sys.argv: h_data += 'const char md5_' + args.replace('-','_') + '[16] = {' + md5sum + '};\n' # Update original binary with MD5 footprint - write_file(data[256:(32768+256)], args + '/build/' + args + '.bin') + write_file(data[512:(32768+512)], args + '/build/' + args + '.bin') write_file(h_data, 'common/modules.h')