diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index 389981195..84c10b05a 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -23,6 +23,8 @@ // Color bitmaps generated with: // Gimp image > indexed colors (16), then "xxd -i *.bmp" +//BUG: Bad console scroll init + //TEST: Imperial in whipcalc //TEST: Numbers //TEST: Jammer diff --git a/firmware/application/pocsag_app.cpp b/firmware/application/pocsag_app.cpp index d587dd70b..e2e14aa97 100644 --- a/firmware/application/pocsag_app.cpp +++ b/firmware/application/pocsag_app.cpp @@ -51,6 +51,7 @@ static std::string bitrate_str(BitRate bitrate) { static std::string flag_str(PacketFlag packetflag) { switch (packetflag) { case PacketFlag::NORMAL: return "OK"; + case PacketFlag::IDLE: return "IDLE"; case PacketFlag::TIMED_OUT: return "TIMED OUT"; case PacketFlag::TOO_LONG: return "TOO LONG"; default: return ""; @@ -108,10 +109,12 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) { &rssi, &channel, &options_freq, - &button_setfreq, &field_rf_amp, &field_lna, &field_vga, + &button_setfreq, + &options_bitrate, + &check_log, &console }); @@ -124,6 +127,16 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) { static_cast(receiver_model.lna()), static_cast(receiver_model.vga()) }); + + check_log.set_value(logging); + check_log.on_select = [this](Checkbox&) { + logging = check_log.value(); + }; + + options_bitrate.set_selected_index(1); // 1200bps + options_bitrate.on_change = [this](size_t, OptionsField::value_t v) { + this->on_bitrate_changed(v); + }; options_freq.on_change = [this](size_t, OptionsField::value_t v) { this->on_band_changed(v); @@ -170,7 +183,7 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) { ascii_data |= ((codeword >> 11) & 0xFFFFF); ascii_idx += 20; - // Packet 20 bits to 7 bit reversed ASCII + // Raw 20 bits to 7 bit reversed ASCII while (ascii_idx >= 7) { ascii_idx -= 7; ascii_char = (ascii_data >> ascii_idx) & 0x7F; @@ -202,7 +215,7 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) { } } - if (logger) logger->on_packet(message->packet, target_frequency()); + if (logger && logging) logger->on_packet(message->packet, target_frequency()); if (eom) { std::string console_info; @@ -221,7 +234,8 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) { if (logger) logger->on_decoded(message->packet, console_info, output_text); } else { - console_info += pocsag::format::bitrate_str(message->packet.bitrate()) + " Tone only"; + console_info += pocsag::format::bitrate_str(message->packet.bitrate()) + " "; + console_info += pocsag::format::flag_str(message->packet.flag()); console.writeln(console_info); } @@ -237,6 +251,16 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage * message) { } } +void POCSAGAppView::on_bitrate_changed(const uint32_t new_bitrate) { + const pocsag::BitRate bitrates[3] = { + pocsag::BitRate::FSK512, + pocsag::BitRate::FSK1200, + pocsag::BitRate::FSK2400 + }; + + baseband::set_pocsag(bitrates[new_bitrate]); +} + void POCSAGAppView::on_band_changed(const uint32_t new_band_frequency) { if (new_band_frequency) set_target_frequency(new_band_frequency); } diff --git a/firmware/application/pocsag_app.hpp b/firmware/application/pocsag_app.hpp index 2e121ab67..ad8e29016 100644 --- a/firmware/application/pocsag_app.hpp +++ b/firmware/application/pocsag_app.hpp @@ -45,7 +45,7 @@ public: void on_decoded(const pocsag::POCSAGPacket& packet, const std::string info, const std::string text); private: - LogFile log_file; + LogFile log_file { }; }; namespace ui { @@ -68,9 +68,12 @@ private: static constexpr uint32_t sampling_rate = 1536000; static constexpr uint32_t baseband_bandwidth = 1750000; + bool logging { true }; uint32_t batch_cnt = 0; - uint32_t address, function; - uint32_t ascii_data, ascii_idx; + uint32_t address { 0 }; + uint32_t function { 0 }; + uint32_t ascii_data { 0 }; + uint32_t ascii_idx { 0 }; std::string output_text = ""; MessageHandlerRegistration message_handler_packet { @@ -93,7 +96,7 @@ private: OptionsField options_freq { { 0 * 8, 0 * 16 }, - 8, + 7, { { "Entered", 0 }, { "FR .025", 466025000 }, @@ -104,10 +107,26 @@ private: { "FR .231", 466231250 } } }; + Button button_setfreq { { 0, 20, 12 * 8, 20 }, "----.----" }; + OptionsField options_bitrate { + { 13 * 8, 22 }, + 8, + { + { "512 bps ", 0 }, + { "1200 bps", 1 }, + { "2400 bps", 2 } + } + }; + Checkbox check_log { + { 22 * 8, 22 }, + 3, + "LOG", + true + }; Console console { { 0, 48, 240, 256 } @@ -125,7 +144,7 @@ private: { 18 * 8, 0 * 16 } }; - std::unique_ptr logger; + std::unique_ptr logger { }; uint32_t target_frequency_ = initial_target_frequency; @@ -135,6 +154,7 @@ private: void on_show_list(); void on_band_changed(const uint32_t new_band_frequency); + void on_bitrate_changed(const uint32_t new_bitrate); uint32_t target_frequency() const; void set_target_frequency(const uint32_t new_value); diff --git a/firmware/application/radio.cpp b/firmware/application/radio.cpp index dceb55764..b15de1db3 100644 --- a/firmware/application/radio.cpp +++ b/firmware/application/radio.cpp @@ -111,6 +111,11 @@ void set_direction(const rf::Direction new_direction) { rf_path.set_direction(direction); baseband_codec.set_mode((direction == rf::Direction::Transmit) ? max5864::Mode::Transmit : max5864::Mode::Receive); + + if (direction == rf::Direction::Receive) + led_rx.on(); + else + led_tx.on(); } bool set_tuning_frequency(const rf::Frequency frequency) { @@ -169,6 +174,9 @@ void disable() { second_if.set_mode(max2837::Mode::Standby); first_if.disable(); set_rf_amp(false); + + led_rx.off(); + led_tx.off(); } void enable(Configuration configuration) { diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index d9e8d1574..1f157e843 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -282,7 +282,7 @@ ReceiverMenuView::ReceiverMenuView(NavigationView& nav) { { "Audio", ui::Color::green(), nullptr, [&nav](){ nav.push(); } }, { "CCIR", ui::Color::grey(), nullptr, [&nav](){ nav.push(); } }, { "Nordic/BTLE", ui::Color::grey(), &bitmap_icon_nordic, [&nav](){ nav.push(); } }, - { "POCSAG 1200", ui::Color::cyan(), nullptr, [&nav](){ nav.push(); } }, + { "POCSAG", ui::Color::cyan(), nullptr, [&nav](){ nav.push(); } }, { "SIGFOX", ui::Color::grey(), &bitmap_icon_foxhunt, [&nav](){ nav.push(); } }, // SIGFRXView { "Transponders", ui::Color::green(), nullptr, [&nav](){ nav.push(); } }, } }); diff --git a/firmware/baseband/proc_pocsag.cpp b/firmware/baseband/proc_pocsag.cpp index 6e72137e9..eb3ffe225 100644 --- a/firmware/baseband/proc_pocsag.cpp +++ b/firmware/baseband/proc_pocsag.cpp @@ -118,7 +118,10 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) { if ((rx_data == POCSAG_IDLE) && (!(last_rx_data & 0x80000000))) { // SYNC then IDLE always means end of message ? - push_packet(pocsag::PacketFlag::NORMAL); + if (frame_counter) + push_packet(pocsag::PacketFlag::NORMAL); + else + push_packet(pocsag::PacketFlag::IDLE); rx_state = WAITING; } else { if (frame_counter < 15) { diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index 858806ec9..d376b05b6 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -460,17 +460,17 @@ void ProgressBar::set_value(const uint32_t value) { } void ProgressBar::paint(Painter& painter) { - uint32_t v_sized; + int v_scaled; - const auto rect = screen_rect(); + const auto sr = screen_rect(); const auto s = style(); - v_sized = (rect.size().width() * _value) / _max; + v_scaled = (sr.size().width() * _value) / _max; - painter.fill_rectangle({rect.location(), {v_sized, rect.size().height()}}, style().foreground); - painter.fill_rectangle({{rect.location().x() + v_sized, rect.location().y()}, {rect.size().width() - v_sized, rect.size().height()}}, s.background); + painter.fill_rectangle({sr.location(), {v_scaled, sr.size().height()}}, style().foreground); + painter.fill_rectangle({{sr.location().x() + v_scaled, sr.location().y()}, {sr.size().width() - v_scaled, sr.size().height()}}, s.background); - painter.draw_rectangle(rect, s.foreground); + painter.draw_rectangle(sr, s.foreground); } /* Console ***************************************************************/ @@ -479,7 +479,6 @@ Console::Console( Rect parent_rect ) : Widget { parent_rect } { - display.scroll_set_position(0); } void Console::clear() { @@ -523,14 +522,14 @@ void Console::writeln(std::string message) { crlf(); } -void Console::paint(Painter& painter) { - (void)painter; +void Console::paint(Painter&) { write(buffer); } void Console::on_show() { const auto screen_r = screen_rect(); display.scroll_set_area(screen_r.top(), screen_r.bottom()); + display.scroll_set_position(0); clear(); visible = true; } @@ -562,10 +561,17 @@ void Console::crlf() { Checkbox::Checkbox( Point parent_pos, size_t length, - std::string text -) : Widget { { parent_pos, { static_cast((8 * length) + 24), 24 } } }, - text_ { text } + std::string text, + bool small +) : Widget { }, + text_ { text }, + small_ { small } { + if (!small_) + set_parent_rect({ parent_pos, { static_cast((8 * length) + 24), 24 } }); + else + set_parent_rect({ parent_pos, { static_cast((8 * length) + 16), 16 } }); + set_focusable(true); } @@ -576,14 +582,13 @@ void Checkbox::set_text(const std::string value) { bool Checkbox::set_value(const bool value) { value_ = value; + set_dirty(); if( on_select ) { on_select(*this); return true; } - set_dirty(); - return false; } @@ -596,37 +601,64 @@ void Checkbox::paint(Painter& painter) { const auto paint_style = (has_focus() || highlighted()) ? style().invert() : style(); - painter.draw_rectangle({ r.location().x(), r.location().y(), 24, 24 }, style().foreground); - - painter.fill_rectangle( - { - static_cast(r.location().x() + 1), static_cast(r.location().y() + 1), - static_cast(24 - 2), static_cast(24 - 2) - }, - style().background - ); - - painter.draw_rectangle({ r.location().x() + 2, r.location().y() + 2, 24-4, 24-4 }, paint_style.background); - - if (value_ == true) { - // Check - portapack::display.draw_line( {r.location().x()+2, r.location().y()+14}, {r.location().x()+6, r.location().y()+18}, ui::Color::green()); - portapack::display.draw_line( {r.location().x()+6, r.location().y()+18}, {r.location().x()+20, r.location().y()+4}, ui::Color::green()); - } else { - // Cross - portapack::display.draw_line( {r.location().x()+1, r.location().y()+1}, {r.location().x()+24-2, r.location().y()+24-2}, ui::Color::red()); - portapack::display.draw_line( {r.location().x()+24-2, r.location().y()+1}, {r.location().x()+1, r.location().y()+24-2}, ui::Color::red()); - } + const auto x = r.location().x(); + const auto y = r.location().y(); const auto label_r = paint_style.font.size_of(text_); - painter.draw_string( - { - static_cast(r.location().x() + 24 + 4), - static_cast(r.location().y() + (24 - label_r.height()) / 2) - }, - paint_style, - text_ - ); + + if (!small_) { + painter.draw_rectangle({ { r.location() }, { 24, 24 } }, style().foreground); + + painter.fill_rectangle({ x + 1, y + 1, 24 - 2, 24 - 2 }, style().background); + + // Highlight + painter.draw_rectangle({ x + 2, y + 2, 24 - 4, 24 - 4 }, paint_style.background); + + if (value_ == true) { + // Check + portapack::display.draw_line( {x + 2, y + 14}, {x + 6, y + 18}, ui::Color::green()); + portapack::display.draw_line( {x + 6, y + 18}, {x + 20, y + 4}, ui::Color::green()); + } else { + // Cross + portapack::display.draw_line( {x + 1, y + 1}, {x + 24 - 2, y + 24 - 2}, ui::Color::red()); + portapack::display.draw_line( {x + 24 - 2, y + 1}, {x + 1, y + 24 - 2}, ui::Color::red()); + } + + painter.draw_string( + { + static_cast(x + 24 + 4), + static_cast(y + (24 - label_r.height()) / 2) + }, + paint_style, + text_ + ); + } else { + painter.draw_rectangle({ { r.location() }, { 16, 16 } }, style().foreground); + + painter.fill_rectangle({ x + 1, y + 1, 16 - 2, 16 - 2 }, style().background); + + // Highlight + painter.draw_rectangle({ x + 1, y + 1, 16 - 2, 16 - 2 }, paint_style.background); + + if (value_ == true) { + // Check + portapack::display.draw_line( {x + 2, y + 8}, {x + 6, y + 12}, ui::Color::green()); + portapack::display.draw_line( {x + 6, y + 12}, {x + 13, y + 5}, ui::Color::green()); + } else { + // Cross + portapack::display.draw_line( {x + 1, y + 1}, {x + 16 - 2, y + 16 - 2}, ui::Color::red()); + portapack::display.draw_line( {x + 16 - 2, y + 1}, {x + 1, y + 16 - 2}, ui::Color::red()); + } + + painter.draw_string( + { + static_cast(x + 16), + static_cast(y + (16 - label_r.height()) / 2) + }, + paint_style, + text_ + ); + } } bool Checkbox::on_key(const KeyEvent key) { diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 25888b0e2..31759437e 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -245,7 +245,7 @@ public: void write(std::string message); void writeln(std::string message); - void paint(Painter& painter) override; + void paint(Painter&) override; void on_show() override; void on_hide() override; @@ -262,7 +262,14 @@ class Checkbox : public Widget { public: std::function on_select { }; - Checkbox(Point parent_pos, size_t length, std::string text); + Checkbox(Point parent_pos, size_t length, std::string text, bool small); + Checkbox( + Point parent_pos, + size_t length, + std::string text + ) : Checkbox { parent_pos, length, text, false } + { + } Checkbox( ) : Checkbox { { }, { }, { } } @@ -285,7 +292,8 @@ public: private: std::string text_; - bool value_ = false; + bool small_ { false }; + bool value_ { false }; const Style* style_ { nullptr }; };