diff --git a/.travis.yml b/.travis.yml index 3a9ecd43c..b1edf1262 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,12 +13,10 @@ notifications: # TODO: The "build_number.1" in this URL is almost certainly wrong, but correct value not available from Travis? - "Firmware download : https://portapack-h1-builds.s3.amazonaws.com/%{repository_slug}/%{build_number}/%{build_number}.1/build/firmware/portapack-h1-firmware-%{commit}.tar.bz2" -before_script: - - wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2017q2/gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2 -O /tmp/gcc-arm.tar.bz2 - - tar -xf /tmp/gcc-arm.tar.bz2 - - export PATH=$PWD/gcc-arm-none-eabi-6-2017-q2-update/bin:$PATH - - export CC="arm-none-eabi-gcc" - - export CXX="arm-none-eabi-g++" +before_install: + - sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa -y + - sudo apt-get update -q + - sudo apt-get install gcc-arm-embedded -y script: # TODO: Introduce top-level Makefile, this is lame. @@ -29,14 +27,6 @@ script: - popd addons: - apt: - packages: - - lib32bz2-1.0 - - lib32ncurses5 - - lib32z1 - - cmake - sources: - - kalakris-cmake artifacts: paths: - $(ls build/firmware/portapack-h1-havoc-*.tar.bz2 | tr "\n" ":") diff --git a/firmware/application/audio.cpp b/firmware/application/audio.cpp index c50720e7e..54c9b1542 100644 --- a/firmware/application/audio.cpp +++ b/firmware/application/audio.cpp @@ -34,7 +34,9 @@ namespace audio { namespace { -constexpr i2s::ConfigTX i2s0_config_tx { +// "Master": I2S peripheral generates SCK/WS, transmits to audio codec. + +constexpr i2s::ConfigTX i2s0_config_tx_master_base_clk { .dao = i2s::DAO { .wordwidth = i2s::WordWidth::Bits16, .mono = 0, @@ -56,9 +58,10 @@ constexpr i2s::ConfigTX i2s0_config_tx { .four_pin = 0, .mclk_out_en = 1, }, + .sck_in_sel = 1, }; -constexpr i2s::ConfigRX i2s0_config_rx { +constexpr i2s::ConfigRX i2s0_config_rx_four_wire { .dai = i2s::DAI { .wordwidth = i2s::WordWidth::Bits16, .mono = 0, @@ -75,10 +78,38 @@ constexpr i2s::ConfigRX i2s0_config_rx { .bitrate = 7, }, .rxmode = i2s::Mode { - .clksel = i2s::ClockSelect::BaseAudioClkOrExternalMCLK, + .clksel = i2s::ClockSelect::FractionalDivider, .four_pin = 1, .mclk_out_en = 0, }, + .sck_in_sel = 0, +}; + +// "Slave": I2S controlled by external SCK/WS, received from audio codec. + +constexpr i2s::ConfigTX i2s0_config_tx_slave_base_clk { + .dao = i2s::DAO { + .wordwidth = i2s::WordWidth::Bits16, + .mono = 0, + .stop = 1, + .reset = 0, + .ws_sel = 1, + .ws_halfperiod = 0x0f, + .mute = 1, + }, + .txrate = i2s::MCLKRate { + .x_divider = 0, + .y_divider = 0, + }, + .txbitrate = i2s::BitRate { + .bitrate = 0, + }, + .txmode = i2s::Mode { + .clksel = i2s::ClockSelect::FractionalDivider, + .four_pin = 0, + .mclk_out_en = 1, + }, + .sck_in_sel = 1, }; constexpr i2s::ConfigDMA i2s0_config_dma { @@ -171,17 +202,18 @@ size_t reg_bits() { } /* namespace debug */ void init(audio::Codec* const codec) { - audio_codec = codec; - clock_manager.start_audio_pll(); - audio_codec->init(); + // Configure I2S before activating codec interface. i2s::i2s0::configure( - i2s0_config_tx, - i2s0_config_rx, + i2s0_config_tx_master_base_clk, + i2s0_config_rx_four_wire, i2s0_config_dma ); + audio_codec = codec; + audio_codec->init(); + // Set pin mode, since it's likely GPIO (as left after CPLD JTAG interactions). portapack::pin_i2s0_rx_sda.mode(3); } diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index cb0c24c52..8a1bd1513 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -551,6 +551,28 @@ static constexpr Bitmap bitmap_more { { 8, 8 }, bitmap_more_data }; +static constexpr uint8_t bitmap_icon_transmit_data[] = { + 0xC0, 0x10, + 0x00, 0x21, + 0x1C, 0x22, + 0x63, 0x42, + 0x5D, 0x44, + 0xA2, 0x44, + 0xAA, 0x44, + 0xAA, 0x44, + 0x5D, 0x44, + 0x6B, 0x42, + 0x1C, 0x22, + 0x1C, 0x21, + 0xDD, 0x10, + 0x3E, 0x08, + 0x2A, 0x04, + 0x2A, 0x03, +}; +static constexpr Bitmap bitmap_icon_transmit { + { 16, 16 }, bitmap_icon_transmit_data +}; + static constexpr uint8_t bitmap_icon_freqman_data[] = { 0x00, 0x00, 0x0E, 0x00, @@ -1034,22 +1056,22 @@ static constexpr uint8_t bitmap_tab_edge_data[] = { 0x03, 0x07, 0x07, - 0x07, - 0x0F, - 0x0F, - 0x0F, - 0x1F, - 0x1F, - 0x1F, - 0x1F, - 0x3F, - 0x3F, - 0x3F, - 0x7F, - 0x7F, - 0x7F, - 0xFF, - 0xFF, +0x07, +0x0F, +0x0F, +0x0F, +0x1F, +0x1F, +0x1F, +0x1F, +0x3F, +0x3F, +0x3F, +0x7F, +0x7F, +0x7F, +0xFF, +0xFF, }; static constexpr Bitmap bitmap_tab_edge { { 8, 24 }, bitmap_tab_edge_data diff --git a/firmware/application/ui_about.cpp b/firmware/application/ui_about.cpp index a5986d8c7..43b55af75 100644 --- a/firmware/application/ui_about.cpp +++ b/firmware/application/ui_about.cpp @@ -132,11 +132,7 @@ AboutView::AboutView( NavigationView& nav ) { - add_children({ - &text_cpld_hackrf, - &text_cpld_hackrf_status, - &button_ok, - }); + add_child(&button_ok); for (auto& text : text_line) { text.set(""); @@ -148,12 +144,6 @@ AboutView::AboutView( add_child(&text); } - if( hackrf::cpld::verify_eeprom() ) { - text_cpld_hackrf_status.set(" OK"); - } else { - text_cpld_hackrf_status.set("BAD"); - } - button_ok.on_select = [&nav](Button&){ nav.pop(); }; diff --git a/firmware/application/ui_about.hpp b/firmware/application/ui_about.hpp index baeba329f..2e02b6cd7 100644 --- a/firmware/application/ui_about.hpp +++ b/firmware/application/ui_about.hpp @@ -106,16 +106,6 @@ private: std::array text_line { }; - Text text_cpld_hackrf { - { 0, 252, 11*8, 16 }, - "HackRF CPLD", - }; - - Text text_cpld_hackrf_status { - { 240 - 3*8, 252, 3*8, 16 }, - "???" - }; - Button button_ok { { 72, 272, 96, 24 }, "OK" diff --git a/firmware/application/ui_bht_tx.cpp b/firmware/application/ui_bht_tx.cpp index d878140d3..b2a5741d9 100644 --- a/firmware/application/ui_bht_tx.cpp +++ b/firmware/application/ui_bht_tx.cpp @@ -33,7 +33,7 @@ using namespace portapack; namespace ui { void BHTView::focus() { - relay_states[0].focus(); + tx_view.focus(); } BHTView::~BHTView() { @@ -41,34 +41,15 @@ BHTView::~BHTView() { baseband::shutdown(); } -void BHTView::generate_message() { - if (tx_mode == SINGLE) { - text_message.set( - gen_message_xy(header_code_a.value(), header_code_b.value(), city_code_xy.value(), family_code_xy.value(), - checkbox_wcsubfamily.value(), subfamily_code.value(), checkbox_wcid.value(), receiver_code.value(), - relay_states[0].selected_index(), relay_states[1].selected_index(), - relay_states[2].selected_index(), relay_states[3].selected_index()) - ); - /*} else { - text_message.set( - gen_message_ep(city_code_ep.value(), family_code_ep.selected_index_value(), - relay_states[0].selected_index(), relay_states[1].selected_index()) - );*/ - } else if (tx_mode == SEQUENCE) { - text_message.set( - gen_message_xy(sequence_matin[seq_index].code) - ); - } -} - void BHTView::start_tx() { uint8_t c; - generate_message(); + view_xylos.generate_message(); + view_EPAR.generate_message(); - if (tx_mode == SINGLE) + if (view_xylos.tx_mode == XylosView::tx_modes::SINGLE) progressbar.set_max(20); - else if (tx_mode == SEQUENCE) + else if (view_xylos.tx_mode == XylosView::tx_modes::SEQUENCE) progressbar.set_max(20 * XY_SEQ_COUNT); transmitter_model.set_sampling_rate(1536000); @@ -85,155 +66,166 @@ void BHTView::start_tx() { void BHTView::on_tx_progress(const int progress, const bool done) { uint8_t c; - uint8_t rs; - if (tx_mode == SINGLE) { + if (view_xylos.tx_mode == XylosView::tx_modes::SINGLE) { if (done) { transmitter_model.disable(); progressbar.set_value(0); if (!checkbox_cligno.value()) { - tx_mode = IDLE; + view_xylos.tx_mode = XylosView::tx_modes::IDLE; tx_view.set_transmitting(false); } else { - chThdSleepMilliseconds(tempo_cligno.value() * 1000); // Dirty :( + chThdSleepMilliseconds(field_tempo.value() * 1000); // Dirty :( - // Invert first relay's state - rs = relay_states[0].selected_index(); - if (rs > 0) relay_states[0].set_selected_index(rs ^ 3); + view_EPAR.flip_relays(); + view_xylos.flip_relays(); start_tx(); } } else { progressbar.set_value(progress); } - } else if (tx_mode == SEQUENCE) { + } else if (view_xylos.tx_mode == XylosView::tx_modes::SEQUENCE) { if (done) { transmitter_model.disable(); - if (seq_index < (XY_SEQ_COUNT - 1)) { - for (c = 0; c < sequence_matin[seq_index].delay; c++) + if (view_xylos.seq_index < (XY_SEQ_COUNT - 1)) { + for (c = 0; c < view_xylos.sequence_matin[view_xylos.seq_index].delay; c++) chThdSleepMilliseconds(1000); - seq_index++; + view_xylos.seq_index++; start_tx(); } else { progressbar.set_value(0); - tx_mode = IDLE; + view_xylos.tx_mode = XylosView::tx_modes::IDLE; tx_view.set_transmitting(false); } } else { - progressbar.set_value((seq_index * 20) + progress); + progressbar.set_value((view_xylos.seq_index * 20) + progress); } } } -BHTView::BHTView(NavigationView& nav) { +void EPARView::flip_relays() { + // Invert first relay's state + relay_states[0].set_selected_index(relay_states[0].selected_index() ^ 1); +} + +void EPARView::generate_message() { + //text_message.set( + gen_message_ep(field_city.value(), field_group.selected_index_value(), + relay_states[0].selected_index(), relay_states[1].selected_index()); + //); +} + +EPARView::EPARView() { size_t n; - baseband::run_image(portapack::spi_flash::image_tag_tones); - //baseband::run_image(portapack::spi_flash::image_tag_encoders); + hidden(true); + + //baseband::run_image(portapack::spi_flash::image_tag_ook); add_children({ &labels, - &options_mode, - &header_code_a, - &header_code_b, - &city_code_xy, - &family_code_xy, - &subfamily_code, - &checkbox_wcsubfamily, - &receiver_code, - &checkbox_wcid, - &progressbar, - &text_message, - &checkbox_cligno, - &tempo_cligno, - &button_seq, - &tx_view + &field_city, + &field_group, + &button_scan }); + + field_city.set_value(220); + field_group.set_selected_index(2); - options_mode.set_selected_index(0); // Start up in Xy mode - header_code_a.set_value(0); - header_code_b.set_value(0); - city_code_xy.set_value(10); - //city_code_ep.set_value(220); - family_code_xy.set_value(1); - //family_code_ep.set_selected_index(2); - subfamily_code.set_value(1); - receiver_code.set_value(1); - tempo_cligno.set_value(0); + field_city.on_change = [this](int32_t) { generate_message(); }; + field_group.on_change = [this](size_t, int32_t) { generate_message(); }; -/* options_mode.on_change = [this](size_t mode, OptionsField::value_t) { - _mode = mode; - - if (_mode) { - // EP layout - remove_children({ - &header_code_a, - &header_code_b, - &checkbox_speaker, - &bmp_speaker, - &city_code_xy, - &family_code_xy, - &subfamily_code, - &checkbox_wcsubfamily, - &receiver_code, - &checkbox_wcid, - &relay_states[2], - &relay_states[3] - }); - add_children({ - &city_code_ep, - &family_code_ep - }); - set_dirty(); - } else { - // Xy layout - remove_children({ - &city_code_ep, - &family_code_ep - }); - add_children({ - &header_code_a, - &header_code_b, - &checkbox_speaker, - &bmp_speaker, - &city_code_xy, - &family_code_xy, - &subfamily_code, - &checkbox_wcsubfamily, - &receiver_code, - &checkbox_wcid, - &relay_states[2], - &relay_states[3] - }); - set_dirty(); - }; - generate_message(); - };*/ + const auto relay_state_fn = [this](size_t, OptionsField::value_t) { + this->generate_message(); + }; - header_code_a.on_change = [this](int32_t) { generate_message(); }; - header_code_b.on_change = [this](int32_t) { generate_message(); }; - city_code_xy.on_change = [this](int32_t) { generate_message(); }; - family_code_xy.on_change = [this](int32_t) { generate_message(); }; - subfamily_code.on_change = [this](int32_t) { generate_message(); }; - receiver_code.on_change = [this](int32_t) { generate_message(); }; + n = 0; + for (auto& relay_state : relay_states) { + relay_state.on_change = relay_state_fn; + relay_state.set_parent_rect({ + static_cast(90 + (n * 36)), + 80, + 24, 24 + }); + relay_state.set_options(relay_options); + add_child(&relay_state); + n++; + } +} + +void EPARView::focus() { + field_city.focus(); +} + +void XylosView::flip_relays() { + size_t rs; + + // Invert first relay's state + rs = relay_states[0].selected_index(); + if (rs > 0) relay_states[0].set_selected_index(rs ^ 3); +} + +void XylosView::generate_message() { + if (tx_mode == SINGLE) { + //text_message.set( + gen_message_xy(field_header_a.value(), field_header_b.value(), field_city.value(), field_family.value(), + checkbox_wcsubfamily.value(), field_subfamily.value(), checkbox_wcid.value(), field_receiver.value(), + relay_states[0].selected_index(), relay_states[1].selected_index(), + relay_states[2].selected_index(), relay_states[3].selected_index()); + //); + } else if (tx_mode == SEQUENCE) { + //text_message.set( + gen_message_xy(sequence_matin[seq_index].code); + //); + } +} + +XylosView::XylosView() { + size_t n; + + hidden(true); + + //baseband::run_image(portapack::spi_flash::image_tag_tones); + + add_children({ + &labels, + &field_header_a, + &field_header_b, + &field_city, + &field_family, + &field_subfamily, + &checkbox_wcsubfamily, + &field_receiver, + &checkbox_wcid, + &button_seq, + }); + + field_header_a.set_value(0); + field_header_b.set_value(0); + field_city.set_value(10); + field_family.set_value(1); + field_subfamily.set_value(1); + field_receiver.set_value(1); + + field_header_a.on_change = [this](int32_t) { generate_message(); }; + field_header_b.on_change = [this](int32_t) { generate_message(); }; + field_city.on_change = [this](int32_t) { generate_message(); }; + field_family.on_change = [this](int32_t) { generate_message(); }; + field_subfamily.on_change = [this](int32_t) { generate_message(); }; + field_receiver.on_change = [this](int32_t) { generate_message(); }; checkbox_wcsubfamily.on_select = [this](Checkbox&, bool v) { - if (v) - subfamily_code.set_focusable(false); - else - subfamily_code.set_focusable(true); + field_subfamily.set_focusable(!v); generate_message(); }; checkbox_wcid.on_select = [this](Checkbox&, bool v) { - if (v) - receiver_code.set_focusable(false); - else - receiver_code.set_focusable(true); + field_receiver.set_focusable(!v); generate_message(); }; @@ -248,16 +240,41 @@ BHTView::BHTView(NavigationView& nav) { for (auto& relay_state : relay_states) { relay_state.on_change = relay_state_fn; relay_state.set_parent_rect({ - static_cast(4 + (n * 36)), - 150, + static_cast(54 + (n * 36)), + 134, 24, 24 }); relay_state.set_options(relay_options); add_child(&relay_state); n++; } +} + +void XylosView::focus() { + field_city.focus(); +} + +BHTView::BHTView(NavigationView& nav) { + Rect view_rect = { 0, 3 * 8, 240, 192 }; - generate_message(); + baseband::run_image(portapack::spi_flash::image_tag_tones); + + add_children({ + &tab_view, + &labels, + &view_xylos, + &view_EPAR, + &checkbox_cligno, + &field_tempo, + &progressbar, + &text_message, + &tx_view + }); + + view_xylos.set_parent_rect(view_rect); + view_EPAR.set_parent_rect(view_rect); + + field_tempo.set_value(0); tx_view.on_edit_frequency = [this, &nav]() { auto new_view = nav.push(receiver_model.tuning_frequency()); @@ -266,18 +283,18 @@ BHTView::BHTView(NavigationView& nav) { }; }; - button_seq.on_select = [this, &nav](Button&) { + /*button_seq.on_select = [this, &nav](Button&) { if (tx_mode == IDLE) { seq_index = 0; tx_mode = SEQUENCE; tx_view.set_transmitting(true); start_tx(); } - }; + };*/ tx_view.on_start = [this]() { - if (tx_mode == IDLE) { - tx_mode = SINGLE; + if (view_xylos.tx_mode == XylosView::tx_modes::IDLE) { + view_xylos.tx_mode = XylosView::tx_modes::SINGLE; tx_view.set_transmitting(true); start_tx(); } @@ -286,7 +303,7 @@ BHTView::BHTView(NavigationView& nav) { tx_view.on_stop = [this]() { transmitter_model.disable(); tx_view.set_transmitting(false); - tx_mode = IDLE; + view_xylos.tx_mode = XylosView::tx_modes::IDLE; }; } diff --git a/firmware/application/ui_bht_tx.hpp b/firmware/application/ui_bht_tx.hpp index b9c80b301..f11ff58ec 100644 --- a/firmware/application/ui_bht_tx.hpp +++ b/firmware/application/ui_bht_tx.hpp @@ -22,6 +22,7 @@ #include "ui.hpp" #include "ui_widget.hpp" +#include "ui_tabview.hpp" #include "ui_navigation.hpp" #include "ui_transmitter.hpp" #include "ui_font_fixed_8x16.hpp" @@ -37,23 +38,30 @@ namespace ui { -class BHTView : public View { +class XylosView : public View { public: - BHTView(NavigationView& nav); - ~BHTView(); - + XylosView(); + void focus() override; - std::string title() const override { return "BHT transmit"; }; - -private: + void flip_relays(); + void generate_message(); + + enum tx_modes { + IDLE = 0, + SINGLE, + SEQUENCE + }; + + tx_modes tx_mode = IDLE; + uint32_t seq_index { 0 }; struct sequence_t { const std::string code; const uint32_t delay; }; - + const sequence_t sequence_matin[XY_SEQ_COUNT] = { { "0000189000B1002B0000", 19 }, // 18:9:0:00 R1=OFF (1) { "0000189200B2110B0000", 16 }, // 18:9:2:00 R1=ON (4) @@ -70,124 +78,71 @@ private: { "0000181AAAB1000B0000", 17 }, // 18:1:A:AA { "0000189400B0100B0000", 17 } // 18:9:4:00 R2=OFF (8) }; - - enum tx_modes { - IDLE = 0, - SINGLE, - SEQUENCE - }; - - tx_modes tx_mode = IDLE; - void generate_message(); - void on_tx_progress(const int progress, const bool done); - void start_tx(); - - const Style style_val { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::green(), - }; - const Style style_cancel { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::red(), - }; - const Style style_grey { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::grey(), - }; - +private: Labels labels { - { { 8 * 8, 3 * 8 }, "Header:", Color::light_grey() }, - { { 4 * 8, 5 * 8 }, "Code ville:", Color::light_grey() }, - { { 7 * 8, 7 * 8 }, "Famille:", Color::light_grey() }, - { { 2 * 8, 9 * 8 + 2 }, "Sous-famille:", Color::light_grey() }, - { { 2 * 8, 13 * 8 }, "ID recepteur:", Color::light_grey() }, - { { 1 * 8, 16 * 8 }, "Relais:", Color::light_grey() }, - { { 27 * 8 + 4, 19 * 8 + 4 }, "s.", Color::light_grey() } + { { 8 * 8, 1 * 8 }, "Header:", Color::light_grey() }, + { { 4 * 8, 3 * 8 }, "Code ville:", Color::light_grey() }, + { { 7 * 8, 5 * 8 }, "Famille:", Color::light_grey() }, + { { 2 * 8, 7 * 8 + 2 }, "Sous-famille:", Color::light_grey() }, + { { 2 * 8, 11 * 8 }, "ID recepteur:", Color::light_grey() }, + { { 2 * 8, 14 * 8 }, "Relais:", Color::light_grey() } }; - OptionsField options_mode { - { 10 * 8, 4 }, - 10, - { - { "Mode Xy.", 0 }, - { "Mode EP.", 1 } - } + NumberField field_header_a { + { 16 * 8, 1 * 8 }, + 2, + { 0, 99 }, + 1, + '0' + }; + NumberField field_header_b { + { 18 * 8, 1 * 8 }, + 2, + { 0, 99 }, + 1, + '0' }; - NumberField header_code_a { + NumberField field_city { { 16 * 8, 3 * 8 }, 2, { 0, 99 }, 1, - '0' - }; - NumberField header_code_b { - { 18 * 8, 3 * 8 }, - 2, - { 0, 99 }, - 1, - '0' - }; - - NumberField city_code_xy { - { 16 * 8, 5 * 8 }, - 2, - { 0, 99 }, - 1, ' ' }; - /*NumberField city_code_ep { - { 16 * 8, 5 * 8 }, - 3, - { 0, 255 }, - 1, - ' ' - };*/ - NumberField family_code_xy { - { 16 * 8, 7 * 8 }, + NumberField field_family { + { 16 * 8, 5 * 8 }, 1, { 0, 9 }, 1, ' ' }; - /*OptionsField family_code_ep { - { 16 * 8, 7 * 8 }, - 2, - { - { "A ", 2 }, // See receiver PCB - { "B ", 1 }, - { "C ", 0 }, - { "TP", 3 } - } - };*/ - - NumberField subfamily_code { - { 16 * 8, 9 * 8 + 2 }, + + NumberField field_subfamily { + { 16 * 8, 7 * 8 + 2 }, 1, { 0, 9 }, 1, ' ' }; + Checkbox checkbox_wcsubfamily { - { 20 * 8, 8 * 8 + 6 }, + { 20 * 8, 6 * 8 + 6 }, 6, "Toutes" }; - NumberField receiver_code { - { 16 * 8, 13 * 8 }, + NumberField field_receiver { + { 16 * 8, 11 * 8 }, 2, { 0, 99 }, 1, '0' }; Checkbox checkbox_wcid { - { 20 * 8, 12 * 8 + 4 }, + { 20 * 8, 10 * 8 + 4 }, 4, "Tous" }; @@ -200,30 +155,104 @@ private: { &bitmap_bulb_on, 2 } }; - ProgressBar progressbar { - { 3 * 8, 12 * 16, 20 * 8, 16 }, + Button button_seq { + { 24 * 8, 1 * 8, 40, 32 }, + "Seq" }; - Text text_message { - { 3 * 8, 13 * 16, 20 * 8, 16 }, - "" +}; + +class EPARView : public View { +public: + EPARView(); + + void focus() override; + + void flip_relays(); + void generate_message(); + +private: + Labels labels { + { { 4 * 8, 1 * 8 }, "Code ville:", Color::light_grey() }, + { { 8 * 8, 3 * 8 }, "Groupe:", Color::light_grey() }, + { { 8 * 8, 7 * 8 }, "Relais:", Color::light_grey() } }; - Checkbox checkbox_cligno { - { 18 * 8 + 4, 19 * 8 }, + NumberField field_city { + { 16 * 8, 1 * 8 }, 3, - "Alt" - }; - NumberField tempo_cligno { - { 25 * 8 + 4, 19 * 8 + 4 }, - 2, - { 1, 99 }, + { 0, 255 }, 1, ' ' }; - Button button_seq { - { 24 * 8, 13 * 16, 40, 32 }, - "Seq" + OptionsField field_group { + { 16 * 8, 3 * 8 }, + 2, + { + { "A ", 2 }, // See receiver PCB + { "B ", 1 }, + { "C ", 0 }, + { "TP", 3 } + } + }; + + std::array relay_states { }; + + ImageOptionsField::options_t relay_options = { + { &bitmap_bulb_off, 0 }, + { &bitmap_bulb_on, 1 } + }; + + Button button_scan { + { 22 * 8, 1 * 8, 56, 32 }, + "Scan" + }; +}; + +class BHTView : public View { +public: + BHTView(NavigationView& nav); + ~BHTView(); + + void focus() override; + + std::string title() const override { return "BHT transmit"; }; + +private: + void on_tx_progress(const int progress, const bool done); + void start_tx(); + + XylosView view_xylos { }; + EPARView view_EPAR { }; + + TabView tab_view { + { "Xylos", Color::cyan(), &view_xylos }, + { "EPAR", Color::green(), &view_EPAR } + }; + + Labels labels { + { { 29 * 8, 14 * 16 + 4 }, "s", Color::light_grey() } + }; + + ProgressBar progressbar { + { 1 * 8, 14 * 16, 20 * 8, 16 }, + }; + Text text_message { + { 1 * 8, 15 * 16, 20 * 8, 16 }, + "" + }; + + Checkbox checkbox_cligno { + { 22 * 8, 14 * 16 }, + 1, + "~" + }; + NumberField field_tempo { + { 27 * 8, 14 * 16 + 4 }, + 2, + { 1, 99 }, + 1, + ' ' }; TransmitterView tx_view { diff --git a/firmware/application/ui_debug.cpp b/firmware/application/ui_debug.cpp index 08cb25881..b13bad949 100644 --- a/firmware/application/ui_debug.cpp +++ b/firmware/application/ui_debug.cpp @@ -173,9 +173,9 @@ void RegistersWidget::paint(Painter& painter) { void RegistersWidget::draw_legend(const Coord left, Painter& painter) { const auto pos = screen_pos(); - for(int i=0; i((i / config.registers_per_row()) * row_height) }; const auto text = to_string_hex(i, config.legend_length()); @@ -193,10 +193,10 @@ void RegistersWidget::draw_values( ) { const auto pos = screen_pos(); - for(int i=0; i(left + config.legend_width() + 8 + (i % config.registers_per_row()) * (config.value_width() + 8)), + static_cast((i / config.registers_per_row()) * row_height) }; const auto value = reader(i); diff --git a/firmware/application/ui_debug.hpp b/firmware/application/ui_debug.hpp index 13d4f51aa..2d633b84d 100644 --- a/firmware/application/ui_debug.hpp +++ b/firmware/application/ui_debug.hpp @@ -130,42 +130,42 @@ private: }; struct RegistersWidgetConfig { - int registers_count; - int register_bits; + size_t registers_count; + size_t register_bits; - constexpr int legend_length() const { + constexpr size_t legend_length() const { return (registers_count >= 0x10) ? 2 : 1; } - constexpr int legend_width() const { + constexpr size_t legend_width() const { return legend_length() * 8; } - constexpr int value_length() const { + constexpr size_t value_length() const { return (register_bits + 3) / 4; } - constexpr int value_width() const { + constexpr size_t value_width() const { return value_length() * 8; } - constexpr int registers_per_row() const { + constexpr size_t registers_per_row() const { return (value_length() >= 3) ? 4 : 8; } - constexpr int registers_row_length() const { + constexpr size_t registers_row_length() const { return (registers_per_row() * (value_length() + 1)) - 1; } - constexpr int registers_row_width() const { + constexpr size_t registers_row_width() const { return registers_row_length() * 8; } - constexpr int row_width() const { + constexpr size_t row_width() const { return legend_width() + 8 + registers_row_width(); } - constexpr int rows() const { + constexpr size_t rows() const { return registers_count / registers_per_row(); } }; @@ -185,7 +185,7 @@ private: const RegistersWidgetConfig config; const std::function reader; - static constexpr int row_height = 16; + static constexpr size_t row_height = 16; void draw_legend(const Coord left, Painter& painter); void draw_values(const Coord left, Painter& painter); diff --git a/firmware/application/ui_jammer.cpp b/firmware/application/ui_jammer.cpp index 44a8b4333..8b2265bda 100644 --- a/firmware/application/ui_jammer.cpp +++ b/firmware/application/ui_jammer.cpp @@ -33,28 +33,95 @@ #include using namespace portapack; -using namespace jammer; namespace ui { -void JammerView::focus() { - options_preset.focus(); +void RangeView::focus() { + check_enabled.focus(); +} + +extern constexpr jammer_range_t RangeView::range_presets[]; +extern constexpr Style RangeView::style_info; + +RangeView::RangeView(NavigationView& nav) { + hidden(true); + + add_children({ + &labels, + &check_enabled, + &options_preset, + &button_min, + &button_max, + &text_info + }); + + check_enabled.set_value(false); + + check_enabled.on_select = [this](Checkbox&, bool v) { + frequency_range.enabled = v; + }; + + button_min.on_select = [this, &nav](Button& button) { + rf::Frequency * value_ptr; + + value_ptr = &frequency_range.min; + + auto new_view = nav.push(*value_ptr); + new_view->on_changed = [this, value_ptr, &button](rf::Frequency f) { + *value_ptr = f; + update_button(button, f); + update_range(); + }; + + //update_button(button, f); + }; + + button_max.on_select = [this, &nav](Button& button) { + rf::Frequency * value_ptr; + + value_ptr = &frequency_range.max; + + auto new_view = nav.push(*value_ptr); + new_view->on_changed = [this, value_ptr, &button](rf::Frequency f) { + *value_ptr = f; + update_button(button, f); + update_range(); + }; + + //update_button(button, f); + }; + + text_info.set_style(&style_info); + + options_preset.set_selected_index(8); // ISM 868 + + options_preset.on_change = [this](size_t, OptionsField::value_t v) { + frequency_range.min = range_presets[v].min; + frequency_range.max = range_presets[v].max; + check_enabled.set_value(true); + update_button(button_min, frequency_range.min); + update_button(button_max, frequency_range.max); + update_range(); + }; } -JammerView::~JammerView() { - transmitter_model.disable(); - baseband::shutdown(); -} - -void JammerView::update_range(const uint32_t n) { +void RangeView::update_button(Button& button, rf::Frequency f) { + std::string label; + + auto f_mhz = to_string_dec_int(f / 1000000, 4); + auto f_hz100 = to_string_dec_int((f / 1000) % 1000, 3, '0'); + + label = f_mhz + "." + f_hz100 + "M"; + + button.set_text(label); +} + +void RangeView::update_range() { std::string label; - jammer_range_t * range_ptr; rf::Frequency center, bw_khz; - range_ptr = &frequency_range[n]; - - center = (range_ptr->min + range_ptr->max) / 2; - bw_khz = abs(range_ptr->max - range_ptr->min) / 1000; + center = (frequency_range.min + frequency_range.max) / 2; + bw_khz = abs(frequency_range.max - frequency_range.min) / 1000; label = "C:" + to_string_short_freq(center) + "M W:"; @@ -68,24 +135,16 @@ void JammerView::update_range(const uint32_t n) { while (label.length() < 23) label += " "; - texts_info[n].set(label); + text_info.set(label); } -void JammerView::update_button(const uint32_t id) { - std::string label; - rf::Frequency f; - - if (id & 1) - f = frequency_range[id / 2].max; - else - f = frequency_range[id / 2].min; - - auto f_mhz = to_string_dec_int(f / 1000000, 4); - auto f_hz100 = to_string_dec_int((f / 1000) % 1000, 3, '0'); +void JammerView::focus() { + tab_view.focus(); +} - label = f_mhz + "." + f_hz100 + "M"; - - buttons_freq[id].set_text(label); +JammerView::~JammerView() { + transmitter_model.disable(); + baseband::shutdown(); } void JammerView::on_retune(const rf::Frequency freq, const uint32_t range) { @@ -95,9 +154,11 @@ void JammerView::on_retune(const rf::Frequency freq, const uint32_t range) { } } -JammerView::JammerView(NavigationView& nav) { - size_t n; - +JammerView::JammerView( + NavigationView& nav +) : nav_ { nav } +{ + Rect view_rect = { 0, 3 * 8, 240, 80 }; baseband::run_image(portapack::spi_flash::image_tag_jammer); static constexpr Style style_val { @@ -112,107 +173,28 @@ JammerView::JammerView(NavigationView& nav) { .foreground = Color::red(), }; - static constexpr Style style_info { - .font = font::fixed_8x16, - .background = Color::black(), - .foreground = Color::grey(), - }; - JammerChannel * jammer_channels = (JammerChannel*)shared_memory.bb_data.data; add_children({ + &tab_view, + &view_range_a, + &view_range_b, + &view_range_c, &labels, &options_type, &text_range_number, &text_range_total, &options_speed, - &options_preset, &options_hop, &button_transmit }); - const auto button_freq_fn = [this, &nav](Button& button) { - rf::Frequency * value_ptr; - uint32_t id = button.id; - - if (id & 1) - value_ptr = &frequency_range[id >> 1].max; - else - value_ptr = &frequency_range[id >> 1].min; - - auto new_view = nav.push(*value_ptr); - new_view->on_changed = [this, value_ptr, id](rf::Frequency f) { - *value_ptr = f; - update_button(id); - update_range(id >> 1); - }; - - update_button(id); - }; - - const auto checkbox_fn = [this](Checkbox& checkbox, bool v) { - frequency_range[checkbox.id].enabled = v; - }; - - n = 0; - for (auto& button : buttons_freq) { - button.on_select = button_freq_fn; - button.set_parent_rect({ - static_cast(13 * 8), - static_cast(76 + ((n >> 1) * 58) + (20 * (n & 1))), - 96, 20 - }); - button.id = n; - add_child(&button); - n++; - } - - n = 0; - for (auto& checkbox : checkboxes) { - checkbox.on_select = checkbox_fn; - checkbox.set_parent_rect({ - static_cast(8), - static_cast(86 + (n * 58)), - 24, 24 - }); - checkbox.id = n; - checkbox.set_text("Range " + to_string_dec_uint(n + 1)); - add_child(&checkbox); - n++; - } - - n = 0; - for (auto& text : texts_info) { - text.set_parent_rect({ - static_cast(3 * 8), - static_cast(116 + (n * 58)), - 25 * 8, 16 - }); - text.set_style(&style_info); - add_child(&text); - n++; - } - - options_preset.on_change = [this](size_t, OptionsField::value_t v) { - const jammer_range_t * preset_ptr; - uint32_t c; - - for (c = 0; c < 3; c++) { - preset_ptr = &range_presets[v][c]; - - frequency_range[c].min = preset_ptr->min; - frequency_range[c].max = preset_ptr->max; - checkboxes[c].set_value(preset_ptr->enabled); - update_button(c * 2); - update_button(c * 2 + 1); - update_range(c); - } - - }; + view_range_a.set_parent_rect(view_rect); + view_range_b.set_parent_rect(view_rect); + view_range_c.set_parent_rect(view_rect); options_type.set_selected_index(2); // Sweep options_speed.set_selected_index(3); // 10kHz - options_preset.set_selected_index(8); // ISM 868 options_hop.set_selected_index(1); // 50ms button_transmit.set_style(&style_val); @@ -239,14 +221,14 @@ JammerView::JammerView(NavigationView& nav) { // Convert ranges min/max to center/bw for (size_t r = 0; r < 3; r++) { - if (frequency_range[r].enabled) { - range_bw = abs(frequency_range[r].max - frequency_range[r].min); + if (range_views[r]->frequency_range.enabled) { + range_bw = abs(range_views[r]->frequency_range.max - range_views[r]->frequency_range.min); // Sort - if (frequency_range[r].min < frequency_range[r].max) - start_freq = frequency_range[r].min; + if (range_views[r]->frequency_range.min < range_views[r]->frequency_range.max) + start_freq = range_views[r]->frequency_range.min; else - start_freq = frequency_range[r].max; + start_freq = range_views[r]->frequency_range.max; if (range_bw >= JAMMER_CH_WIDTH) { num_channels = 0; diff --git a/firmware/application/ui_jammer.hpp b/firmware/application/ui_jammer.hpp index 9d0e0e405..86416acc7 100644 --- a/firmware/application/ui_jammer.hpp +++ b/firmware/application/ui_jammer.hpp @@ -24,12 +24,162 @@ #include "ui_widget.hpp" #include "ui_font_fixed_8x16.hpp" #include "ui_navigation.hpp" +#include "ui_tabview.hpp" #include "transmitter_model.hpp" #include "message.hpp" #include "jammer.hpp" +using namespace jammer; + namespace ui { +class RangeView : public View { +public: + RangeView(NavigationView& nav); + + void focus() override; + + jammer_range_t frequency_range { }; + +private: + void update_button(Button& button, rf::Frequency f); + void update_range(); + + static constexpr Style style_info { + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; + + static constexpr jammer_range_t range_presets[] = { + // GSM900 Orange + { true, 935000000, 945000000 }, // BW:10M + // GSM1800 Orange + { false, 1808000000, 1832000000 }, // BW:24M + + // GSM900 SFR + { true, 950000000, 960000000 }, // BW:10M + // GSM1800 SFR + { false, 1832000000, 1853000000 }, // BW:21M + + // GSM900 Bouygues + { true, 925000000, 935000000 }, // BW:10M + // GSM1800 Bouygues + { false, 1858000000, 1880000000 }, // BW:22M + + // GSM900 Free + { true, 945000000, 950000000 }, // BW:5M + + // GSM-R + { true, 921000000, 925000000 }, // BW:4M + + // DECT + { true, 1880000000, 1900000000 }, // BW: 20MHz + + // PMV AFSK + { true, 162930000, 162970000 }, // BW: 40kHz + + // ISM 433 + { true, 433050000, 434790000 }, // Center: 433.92MHz BW: 0.2% + + // ISM 868 + { true, 868000000, 868200000 }, // Center: 868.2MHz BW: 40kHz + + // GPS L1 + { true, 1575420000 - 500000, 1575420000 + 500000 }, // BW: 1MHz + // GPS L2 + { false, 1227600000 - 1000000, 1227600000 + 1000000 }, // BW: 2MHz + + // WLAN 2.4G CH1 + { true, 2412000000 - 11000000, 2412000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH2 + { true, 2417000000 - 11000000, 2417000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH3 + { true, 2422000000 - 11000000, 2422000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH4 + { true, 2427000000 - 11000000, 2427000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH5 + { true, 2432000000 - 11000000, 2432000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH6 + { true, 2437000000 - 11000000, 2437000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH7 + { true, 2442000000 - 11000000, 2442000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH8 + { true, 2447000000 - 11000000, 2447000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH9 + { true, 2452000000 - 11000000, 2452000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH10 + { true, 2457000000 - 11000000, 2457000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH11 + { true, 2462000000 - 11000000, 2462000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH12 + { true, 2467000000 - 11000000, 2467000000 + 11000000}, // BW: 22MHz + // WLAN 2.4G CH13 + { true, 2472000000 - 11000000, 2472000000 + 11000000}, // BW: 22MHz + }; + + Labels labels { + { { 2 * 8, 5 * 8 }, "Preset:", Color::light_grey() }, + { { 5 * 8, 9 * 8 }, "Start:", Color::light_grey() }, + { { 6 * 8, 13 * 8 }, "Stop:", Color::light_grey() }, + }; + + Checkbox check_enabled { + { 44, 1 * 8 }, + 12, + "Enable range", + false + }; + + OptionsField options_preset { + { 9 * 8, 5 * 8 }, + 19, + { + { "GSM900 Orange FR", 0 }, + { "GSM1800 Orange FR", 1 }, + { "GSM900 SFR FR", 2 }, + { "GSM1800 SFR FR", 3 }, + { "GSM900 Bouygues FR", 4 }, + { "GSM1800 Bouygues FR", 5 }, + { "GSM Free FR ", 6 }, + { "GSM-R FR ", 7 }, + { "DECT ", 8 }, + { "Optifib ", 9 }, + { "ISM 433 ", 10 }, + { "ISM 868 ", 11 }, + { "GPS L1 ", 12 }, + { "GPS L2 ", 13 }, + { "WLAN 2.4G CH1 ", 14 }, + { "WLAN 2.4G CH2 ", 15 }, + { "WLAN 2.4G CH3 ", 16 }, + { "WLAN 2.4G CH4 ", 17 }, + { "WLAN 2.4G CH5 ", 18 }, + { "WLAN 2.4G CH6 ", 19 }, + { "WLAN 2.4G CH7 ", 20 }, + { "WLAN 2.4G CH8 ", 21 }, + { "WLAN 2.4G CH9 ", 22 }, + { "WLAN 2.4G CH10 ", 23 }, + { "WLAN 2.4G CH11 ", 24 }, + { "WLAN 2.4G CH12 ", 25 }, + { "WLAN 2.4G CH13 ", 26 } + } + }; + + Button button_min { + { 13 * 8, 4 * 16, 120, 28 }, + "" + }; + Button button_max { + { 13 * 8, 6 * 16, 120, 28 }, + "" + }; + + Text text_info { + { 3 * 8, 8 * 16, 25 * 8, 16 }, + "" + }; +}; + class JammerView : public View { public: JammerView(NavigationView& nav); @@ -38,137 +188,34 @@ public: void focus() override; std::string title() const override { return "Jammer"; }; - + private: - typedef struct jammer_range { - bool enabled; - rf::Frequency min; - rf::Frequency max; - } jammer_range_t; - - jammer_range_t frequency_range[3]; + NavigationView& nav_; - void update_range(const uint32_t n); - void update_button(const uint32_t n); void on_retune(const rf::Frequency freq, const uint32_t range); - const jammer_range_t range_presets[23][3] = { - // Orange - {{ true, 935000000, 945000000 }, // GSM 900 BW:10M - { false, 1808000000, 1832000000 }, // GSM 1800 BW:24M - { false, 0, 0 }}, - - // SFR - {{ true, 950000000, 960000000 }, // GSM 900 BW:10M - { false, 1832000000, 1853000000 }, // GSM 1800 BW:21M - { false, 0, 0 }}, - - // Bouygues - {{ true, 925000000, 935000000 }, // GSM 900 BW:10M - { false, 1858000000, 1880000000 }, // GSM 1800 BW:22M - { false, 0, 0 }}, - - // Free - {{ true, 945000000, 950000000 }, // GSM 900 BW:5M - { false, 0, 0 }, - { false, 0, 0 }}, - - // GSM-R - {{ true, 921000000, 925000000 }, // GSM 900 BW:4M - { false, 0, 0 }, - { false, 0, 0 }}, - - // DECT - {{ true, 1880000000, 1900000000 }, // BW: 20MHz - { false, 0, 0 }, - { false, 0, 0 }}, - - // PMV AFSK - {{ true, 162930000, 162970000 }, // BW: 40kHz - { false, 0, 0 }, - { false, 0, 0 }}, - - // ISM 433 - {{ true, 433050000, 434790000 }, // Center: 433.92MHz BW: 0.2% - { false, 0, 0 }, - { false, 0, 0 }}, - - // ISM 868 - {{ true, 868000000, 868200000 }, // Center: 868.2MHz BW: 40kHz - { false, 0, 0 }, - { false, 0, 0 }}, - - // GPS L1 & L2 - {{ true, 1575420000 - 500000, 1575420000 + 500000 }, // BW: 1MHz - { false, 1227600000 - 1000000, 1227600000 + 1000000 }, // BW: 2MHz - { false, 0, 0 }}, - - // WLAN 2.4G CH1 - {{ true, 2412000000 - 11000000, 2412000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH2 - {{ true, 2417000000 - 11000000, 2417000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH3 - {{ true, 2422000000 - 11000000, 2422000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH4 - {{ true, 2427000000 - 11000000, 2427000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH5 - {{ true, 2432000000 - 11000000, 2432000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH6 - {{ true, 2437000000 - 11000000, 2437000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH7 - {{ true, 2442000000 - 11000000, 2442000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH8 - {{ true, 2447000000 - 11000000, 2447000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH9 - {{ true, 2452000000 - 11000000, 2452000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH10 - {{ true, 2457000000 - 11000000, 2457000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH11 - {{ true, 2462000000 - 11000000, 2462000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH12 - {{ true, 2467000000 - 11000000, 2467000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - // WLAN 2.4G CH13 - {{ true, 2472000000 - 11000000, 2472000000 + 11000000}, // BW: 22MHz - { false, 0, 0 }, - { false, 0, 0 }}, - - }; - bool jamming { false }; + RangeView view_range_a { nav_ }; + RangeView view_range_b { nav_ }; + RangeView view_range_c { nav_ }; + + std::array range_views { &view_range_a, &view_range_b, &view_range_c }; + + TabView tab_view { + { "Range 1", Color::white(), range_views[0] }, + { "Range 2", Color::white(), range_views[1] }, + { "Range 3", Color::white(), range_views[2] }, + }; + Labels labels { - { { 3 * 8, 4 }, "Type:", Color::light_grey() }, - { { 2 * 8, 20 }, "Speed:", Color::light_grey() }, - { { 1 * 8, 36 }, "Preset:", Color::light_grey() }, - { { 5 * 8, 52 }, "Hop:", Color::light_grey() } + { { 3 * 8, 12 * 16 }, "Type:", Color::light_grey() }, + { { 2 * 8, 13 * 16 }, "Speed:", Color::light_grey() }, + { { 5 * 8, 14 * 16 }, "Hop:", Color::light_grey() } }; OptionsField options_type { - { 9 * 8, 4 }, + { 9 * 8, 12 * 16 }, 5, { { "FSK ", 0 }, @@ -178,16 +225,16 @@ private: }; Text text_range_number { - { 18 * 8, 4, 2 * 8, 16 }, + { 18 * 8, 12 * 16, 2 * 8, 16 }, "--" }; Text text_range_total { - { 20 * 8, 4, 3 * 8, 16 }, + { 20 * 8, 12 * 16, 3 * 8, 16 }, "/--" }; OptionsField options_speed { - { 9 * 8, 20 }, + { 9 * 8, 13 * 16 }, 6, { { "10Hz ", 10 }, @@ -198,38 +245,8 @@ private: } }; - OptionsField options_preset { - { 9 * 8, 36 }, - 16, - { - { "GSM Orange FR ", 0 }, - { "GSM SFR FR ", 1 }, - { "GSM Bouygues FR", 2 }, - { "GSM Free FR ", 3 }, - { "GSM-R FR ", 4 }, - { "DECT ", 5 }, - { "Optifib ", 6 }, - { "ISM 433 ", 7 }, - { "ISM 868 ", 8 }, - { "GPS ", 9 }, - { "WLAN 2.4G CH1 ", 10 }, - { "WLAN 2.4G CH2 ", 11 }, - { "WLAN 2.4G CH3 ", 12 }, - { "WLAN 2.4G CH4 ", 13 }, - { "WLAN 2.4G CH5 ", 14 }, - { "WLAN 2.4G CH6 ", 15 }, - { "WLAN 2.4G CH7 ", 16 }, - { "WLAN 2.4G CH8 ", 17 }, - { "WLAN 2.4G CH9 ", 18 }, - { "WLAN 2.4G CH10 ", 19 }, - { "WLAN 2.4G CH11 ", 20 }, - { "WLAN 2.4G CH12 ", 21 }, - { "WLAN 2.4G CH13 ", 22 } - } - }; - OptionsField options_hop { - { 9 * 8, 52 }, + { 9 * 8, 14 * 16 }, 5, { { "10ms ", 1 }, @@ -241,10 +258,6 @@ private: { "10s ", 1000 } } }; - - std::array checkboxes { }; - std::array buttons_freq { }; - std::array texts_info { }; Button button_transmit { { 1 * 8, 16 * 16, 80, 48 }, diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 0ca659167..18d46e322 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -349,7 +349,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) { add_items({ { "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push(); } }, { "Receivers", ui::Color::cyan(), &bitmap_icon_receivers, [&nav](){ nav.push(); } }, - { "Transmitters", ui::Color::green(), nullptr, [&nav](){ nav.push(); } }, + { "Transmitters", ui::Color::green(), &bitmap_icon_transmit, [&nav](){ nav.push(); } }, { "Capture", ui::Color::blue(), &bitmap_icon_capture, [&nav](){ nav.push(); } }, { "Replay", ui::Color::grey(), &bitmap_icon_replay, [&nav](){ nav.push(); } }, { "Scanner/search", ui::Color::orange(), &bitmap_icon_closecall, [&nav](){ nav.push(); } }, diff --git a/firmware/baseband/dsp_decimate.cpp b/firmware/baseband/dsp_decimate.cpp index 93dbc6025..12ed083f3 100644 --- a/firmware/baseband/dsp_decimate.cpp +++ b/firmware/baseband/dsp_decimate.cpp @@ -566,8 +566,8 @@ buffer_c16_t DecimateBy2CIC3::execute( uint32_t t1 = _iq0; uint32_t t2 = _iq1; const uint32_t taps = 0x00000003; - auto s = src.p; - auto d = dst.p; + void* s = src.p; + void* d = dst.p; const auto d_end = &dst.p[src.count / 2]; while(d < d_end) { uint32_t i = __SXTH(t1, 0); /* 1: I0 */ @@ -665,21 +665,21 @@ buffer_c16_t FIRAndDecimateComplex::execute( const auto output_sampling_rate = src.sampling_rate / decimation_factor_; const size_t output_samples = src.count / decimation_factor_; - sample_t* dst_p = dst.p; + void* dst_p = dst.p; const buffer_c16_t result { dst.p, output_samples, output_sampling_rate }; - const sample_t* src_p = src.p; + const void* src_p = src.p; size_t outer_count = output_samples; while(outer_count > 0) { /* Put new samples into delay buffer */ - auto z_new_p = &samples_[taps_count_ - decimation_factor_]; + void* z_new_p = &samples_[taps_count_ - decimation_factor_]; for(size_t i=0; i 0) { *__SIMD32(t)++ = *__SIMD32(s)++; @@ -754,7 +754,7 @@ buffer_c16_t FIRAndDecimateComplex::execute( shift_count = (taps_count_ - decimation_factor_) % unroll_factor; while(shift_count > 0) { - *(t++) = *(s++); + *__SIMD32(t)++ = *__SIMD32(s)++; shift_count--; } diff --git a/firmware/baseband/dsp_demodulate.cpp b/firmware/baseband/dsp_demodulate.cpp index cf79d0d02..9e96998a6 100644 --- a/firmware/baseband/dsp_demodulate.cpp +++ b/firmware/baseband/dsp_demodulate.cpp @@ -34,7 +34,7 @@ buffer_f32_t AM::execute( const buffer_c16_t& src, const buffer_f32_t& dst ) { - const auto src_p = src.p; + const void* src_p = src.p; const auto src_end = &src.p[src.count]; auto dst_p = dst.p; while(src_p < src_end) { @@ -90,7 +90,7 @@ buffer_f32_t FM::execute( ) { auto z = z_; - const auto src_p = src.p; + const void* src_p = src.p; const auto src_end = &src.p[src.count]; auto dst_p = dst.p; while(src_p < src_end) { @@ -113,9 +113,9 @@ buffer_s16_t FM::execute( ) { auto z = z_; - const auto src_p = src.p; + const void* src_p = src.p; const auto src_end = &src.p[src.count]; - auto dst_p = dst.p; + void* dst_p = dst.p; while(src_p < src_end) { const auto s0 = *__SIMD32(src_p)++; const auto s1 = *__SIMD32(src_p)++; diff --git a/firmware/chibios-portapack/os/hal/platforms/LPC43xx/lpc43xx.inc b/firmware/chibios-portapack/os/hal/platforms/LPC43xx/lpc43xx.inc index fb09b7826..94ea0812a 100644 --- a/firmware/chibios-portapack/os/hal/platforms/LPC43xx/lpc43xx.inc +++ b/firmware/chibios-portapack/os/hal/platforms/LPC43xx/lpc43xx.inc @@ -41,6 +41,19 @@ extern "C" { * @brief Product name title=UM10503 Chapter title=LPC43xx Configuration Registers (CREG) Modification date=1/28/2014 Major revision=1 Minor revision=8 */ +typedef struct { + __IO uint32_t ETHMODE : 3; + uint32_t RESERVED0 : 1; + __IO uint32_t CTOUTCTRL : 1; + uint32_t RESERVED1 : 7; + __IO uint32_t I2S0_TX_SCK_IN_SEL : 1; + __IO uint32_t I2S0_RX_SCK_IN_SEL : 1; + __IO uint32_t I2S1_TX_SCK_IN_SEL : 1; + __IO uint32_t I2S1_RX_SCK_IN_SEL : 1; + __IO uint32_t EMC_CLK_SEL : 1; + uint32_t RESERVED2 : 15; +} LPC_CREG_CREG6_Type; + typedef struct { uint32_t RESERVED0; __IO uint32_t CREG0; @@ -52,7 +65,7 @@ typedef struct { __IO uint32_t FLASHCFGA; __IO uint32_t FLASHCFGB; __IO uint32_t ETBCFG; - __IO uint32_t CREG6; + LPC_CREG_CREG6_Type CREG6; __IO uint32_t M4TXEVENT; uint32_t RESERVED3[51]; __I uint32_t CHIPID; diff --git a/firmware/common/ak4951.cpp b/firmware/common/ak4951.cpp index 0567bf0be..3556e0a52 100644 --- a/firmware/common/ak4951.cpp +++ b/firmware/common/ak4951.cpp @@ -29,7 +29,7 @@ using namespace portapack; namespace asahi_kasei { namespace ak4951 { -void AK4951::configure_digital_interface() { +void AK4951::configure_digital_interface_i2s() { // Configure for external slave mode. map.r.mode_control_1.DIF = 0b11; // I2S compatible map.r.mode_control_1.BCKO = 0; // BICK = 32fs @@ -38,22 +38,28 @@ void AK4951::configure_digital_interface() { map.r.mode_control_2.CM = 0b00; // MCKI = 256fs map.r.mode_control_2.FS = 0b1011; // fs = 48kHz update(Register::ModeControl2); +} - // map.r.mode_control_3.DVOLC = 1; // Control L/R channels with DVL (LchDigitalVolumeControl) - // update(Register::ModeControl3); - +void AK4951::configure_digital_interface_external_slave() { map.r.power_management_2.MS = 0; // Slave mode map.r.power_management_2.PMPLL = 0; // EXT mode update(Register::PowerManagement2); } +void AK4951::configure_digital_interface_external_master() { + map.r.power_management_2.MS = 1; // Master mode + map.r.power_management_2.PMPLL = 0; // EXT mode + update(Register::PowerManagement2); +} + void AK4951::init() { reset(); // Write dummy address to "release" the reset. write(0x00, 0x00); - configure_digital_interface(); + configure_digital_interface_i2s(); + configure_digital_interface_external_slave(); map.r.power_management_1.PMVCM = 1; update(Register::PowerManagement1); diff --git a/firmware/common/ak4951.hpp b/firmware/common/ak4951.hpp index 54115481a..ab7d3b813 100644 --- a/firmware/common/ak4951.hpp +++ b/firmware/common/ak4951.hpp @@ -864,7 +864,9 @@ private: Line, }; - void configure_digital_interface(); + void configure_digital_interface_i2s(); + void configure_digital_interface_external_slave(); + void configure_digital_interface_external_master(); void set_digtal_volume_control(const reg_t value); void set_dac_power(const bool enable); void set_headphone_power(const bool enable); diff --git a/firmware/common/i2s.hpp b/firmware/common/i2s.hpp index 0ddaa9664..d715146d9 100644 --- a/firmware/common/i2s.hpp +++ b/firmware/common/i2s.hpp @@ -138,6 +138,7 @@ struct ConfigTX { uint32_t txrate; uint32_t txbitrate; uint32_t txmode; + uint32_t sck_in_sel; }; struct ConfigRX { @@ -145,6 +146,7 @@ struct ConfigRX { uint32_t rxrate; uint32_t rxbitrate; uint32_t rxmode; + uint32_t sck_in_sel; }; struct ConfigDMA { @@ -161,21 +163,13 @@ public: ) { reset(); - /* I2S operates in master mode, use PLL0AUDIO as MCLK source for TX. */ - /* NOTE: Documentation of CREG6 is quite confusing. Refer to "I2S clocking and - * pin connections" and other I2S diagrams for more clarity. - */ if( &p() == LPC_I2S0 ) { - LPC_CREG->CREG6 |= - (1U << 12) - | (1U << 13) - ; + LPC_CREG->CREG6.I2S0_TX_SCK_IN_SEL = config_tx.sck_in_sel; + LPC_CREG->CREG6.I2S0_RX_SCK_IN_SEL = config_rx.sck_in_sel; } if( &p() == LPC_I2S1 ) { - LPC_CREG->CREG6 |= - (1U << 14) - | (1U << 15) - ; + LPC_CREG->CREG6.I2S1_TX_SCK_IN_SEL = config_tx.sck_in_sel; + LPC_CREG->CREG6.I2S1_RX_SCK_IN_SEL = config_rx.sck_in_sel; } p().DAO = config_tx.dao; diff --git a/firmware/common/jammer.hpp b/firmware/common/jammer.hpp index 8f8639abc..140d7559a 100644 --- a/firmware/common/jammer.hpp +++ b/firmware/common/jammer.hpp @@ -28,6 +28,12 @@ namespace jammer { +typedef struct jammer_range { + bool enabled; + int64_t min; + int64_t max; +} jammer_range_t; + enum JammerType : uint32_t { TYPE_FSK = 0, TYPE_TONE = 1, diff --git a/firmware/common/pins.hpp b/firmware/common/pins.hpp index a2b706ab7..a8d611e3f 100644 --- a/firmware/common/pins.hpp +++ b/firmware/common/pins.hpp @@ -78,7 +78,7 @@ constexpr Pin pins[] = { [P2_11] = { 2, 11, { .mode=0, .pd=1, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* RX_AMP/P49: U12.V1(I), U14.V3(I) */ [P2_12] = { 2, 12, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !RX_AMP_PWR/P52: 10K PU, Q1.G(I), power to U13 (RX amp) */ [P2_13] = { 2, 13, { .mode=0, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* P2_13: PortaPack P2_13/DIR */ - [P3_0] = { 3, 0, { .mode=2, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_TX_SCK: PortaPack I2S0_TX_SCK(I) */ + [P3_0] = { 3, 0, { .mode=2, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* I2S0_TX_SCK: PortaPack I2S0_TX_SCK(I) */ [P3_1] = { 3, 1, { .mode=0, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* I2S0_RX_WS: PortaPack I2S0_TX_WS(I). Input enabled to fold back into RX. */ [P3_2] = { 3, 2, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_RX_SDA: PortaPack I2S0_TX_SDA(I) */ //[P3_3] = { 3, 3, { .mode=3, .pd=1, .pu=0, .fast=1, .input=1, .ifilt=0 } }, /* SPIFI_SCK: W25Q80BV.CLK(I), enable input buffer for timing feedback */ diff --git a/firmware/common/tpms_packet.cpp b/firmware/common/tpms_packet.cpp index 8cca23f28..decc23619 100644 --- a/firmware/common/tpms_packet.cpp +++ b/firmware/common/tpms_packet.cpp @@ -108,7 +108,7 @@ Optional Packet::reading_ook_8k4_schrader() const { * Those bits assumed to be 0b0100", which may not be entirely true... */ constexpr uint8_t first_nibble = 0x4; - const auto system_id = (first_nibble << 20) | reader_.read(0, 20); + // const auto system_id = (first_nibble << 20) | reader_.read(0, 20); const auto id = reader_.read(20, 32); const auto value_0 = reader_.read(52, 8); const auto value_1 = reader_.read(60, 8); diff --git a/firmware/common/wm8731.cpp b/firmware/common/wm8731.cpp index c677d3746..25686c1c7 100644 --- a/firmware/common/wm8731.cpp +++ b/firmware/common/wm8731.cpp @@ -25,6 +25,30 @@ namespace wolfson { namespace wm8731 { +void WM8731::configure_interface_i2s_slave() { + write(DigitalAudioInterfaceFormat { + .format = 2, + .iwl = 0, + .lrp = 0, + .lrswap = 0, + .ms = 0, + .bclkinv = 0, + .reserved0 = 0, + }); +} + +void WM8731::configure_interface_i2s_master() { + write(DigitalAudioInterfaceFormat { + .format = 2, + .iwl = 0, + .lrp = 0, + .lrswap = 0, + .ms = 1, + .bclkinv = 0, + .reserved0 = 0, + }); +} + void WM8731::init() { reset(); @@ -49,15 +73,7 @@ void WM8731::init() { // .reserved0 = 0, // }); - write(DigitalAudioInterfaceFormat { - .format = 2, - .iwl = 0, - .lrp = 0, - .lrswap = 0, - .ms = 0, - .bclkinv = 0, - .reserved0 = 0, - }); + configure_interface_i2s_slave(); write(DigitalAudioPathControl { .adchpd = 0, diff --git a/firmware/common/wm8731.hpp b/firmware/common/wm8731.hpp index b2c52778f..0d1b15b78 100644 --- a/firmware/common/wm8731.hpp +++ b/firmware/common/wm8731.hpp @@ -375,6 +375,9 @@ private: RegisterMap map { default_after_reset }; volume_t headphone_volume = -60.0_dB; + void configure_interface_i2s_slave(); + void configure_interface_i2s_master(); + bool write(const Register reg); bool write(const address_t reg_address, const reg_t value); diff --git a/firmware/graphics/icon_transmit.png b/firmware/graphics/icon_transmit.png new file mode 100644 index 000000000..8b4468c2a Binary files /dev/null and b/firmware/graphics/icon_transmit.png differ diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin index 695e08bab..0f2656d4a 100644 Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ