diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 7139f559..587692d8 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -169,6 +169,7 @@ set(CPPSRC ui_lcr.cpp ui_xylos.cpp ui_freqman.cpp + ui_encoders.cpp # ui_loadmodule.cpp recent_entries.cpp receiver_model.cpp diff --git a/firmware/application/Makefile b/firmware/application/Makefile index 0defcfc3..1ec2c7c2 100644 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -3702,6 +3702,30 @@ ui_debug.cpp.s: cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_debug.cpp.s .PHONY : ui_debug.cpp.s +ui_encoders.obj: ui_encoders.cpp.obj +.PHONY : ui_encoders.obj + +# target to build an object file +ui_encoders.cpp.obj: + cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_encoders.cpp.obj +.PHONY : ui_encoders.cpp.obj + +ui_encoders.i: ui_encoders.cpp.i +.PHONY : ui_encoders.i + +# target to preprocess a source file +ui_encoders.cpp.i: + cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_encoders.cpp.i +.PHONY : ui_encoders.cpp.i + +ui_encoders.s: ui_encoders.cpp.s +.PHONY : ui_encoders.s + +# target to generate assembly for a file +ui_encoders.cpp.s: + cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_encoders.cpp.s +.PHONY : ui_encoders.cpp.s + ui_font_fixed_8x16.obj: ui_font_fixed_8x16.cpp.obj .PHONY : ui_font_fixed_8x16.obj @@ -4591,6 +4615,9 @@ help: @echo "... ui_debug.obj" @echo "... ui_debug.i" @echo "... ui_debug.s" + @echo "... ui_encoders.obj" + @echo "... ui_encoders.i" + @echo "... ui_encoders.s" @echo "... ui_font_fixed_8x16.obj" @echo "... ui_font_fixed_8x16.i" @echo "... ui_font_fixed_8x16.s" diff --git a/firmware/application/ui_encoders.cpp b/firmware/application/ui_encoders.cpp new file mode 100644 index 00000000..b2b92004 --- /dev/null +++ b/firmware/application/ui_encoders.cpp @@ -0,0 +1,312 @@ +/* + * 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 "ui_encoders.hpp" + +#include "baseband_api.hpp" +#include "string_format.hpp" + +#include "portapack_persistent_memory.hpp" + +#include +#include + +using namespace portapack; + +namespace ui { + +void EncodersView::focus() { + bitfields[0].focus(); +} + +EncodersView::~EncodersView() { + transmitter_model.disable(); + baseband::shutdown(); +} + +void EncodersView::generate_frame() { + uint8_t i; + + debug_text.clear(); + i = 0; + for (auto c : encoder_def->format) { + if (c <= 'J') + debug_text += encoder_def->bit_format.at(bitfields[i++].value()); + } + + //text_status.set(debug_text.substr(0, 28)); + + if (visible()) parent()->set_dirty(); + + //afsk::generate_data(lcr_message, lcr_message_data); +} + +void EncodersView::paint(Painter& painter) { + Coord x = 8, x_inc; + Coord y, prev_y = 0; + + painter.fill_rectangle( { 0, 144, 240, 24 }, Color::black() ); + + x_inc = 240 / debug_text.length(); + + for (auto c : debug_text) { + if (c == '0') + y = 23; + else + y = 0; + + if (prev_y != y) painter.draw_rectangle( { x, 144, 1, 24 }, Color::yellow() ); + painter.draw_rectangle( { x, 144 + y, x_inc, 1 }, Color::yellow() ); + + prev_y = y; + x += x_inc; + if (x >= (240 - x_inc)) break; + } +} + +void EncodersView::update_progress() { + /*char str[16]; + + text_status.set(" "); + + if (tx_mode == SINGLE) { + strcpy(str, to_string_dec_uint(repeat_index).c_str()); + strcat(str, "/"); + strcat(str, to_string_dec_uint(portapack::persistent_memory::afsk_repeats()).c_str()); + text_status.set(str); + progress.set_value(repeat_index); + } else if (tx_mode == SCAN) { + strcpy(str, to_string_dec_uint(repeat_index).c_str()); + strcat(str, "/"); + strcat(str, to_string_dec_uint(portapack::persistent_memory::afsk_repeats()).c_str()); + strcat(str, " "); + strcat(str, to_string_dec_uint(scan_index + 1).c_str()); + strcat(str, "/"); + strcat(str, to_string_dec_uint(scan_count).c_str()); + text_status.set(str); + progress.set_value(scan_progress); + } else { + text_status.set("Ready"); + progress.set_value(0); + }*/ +} + +void EncodersView::on_txdone(int n) { + /*char str[16]; + + if (n > 0) { + // Repeating... + repeat_index = n + 1; + if (tx_mode == SCAN) { + scan_progress++; + update_progress(); + } else { + update_progress(); + } + } else { + // Done transmitting + if ((tx_mode == SCAN) && (scan_index < (scan_count - 1))) { + transmitter_model.disable(); + if (abort_scan) { + // Kill scan process + strcpy(str, "Abort @"); + strcat(str, rgsb); + text_status.set(str); + progress.set_value(0); + tx_mode = IDLE; + abort_scan = false; + button_scan.set_style(&style_val); + button_scan.set_text("SCAN"); + } else { + // Next address + scan_index++; + strcpy(rgsb, &scan_list[options_scanlist.selected_index()].addresses[scan_index * 5]); + scan_progress++; + repeat_index = 1; + update_progress(); + start_tx(true); + } + } else { + transmitter_model.disable(); + tx_mode = IDLE; + update_progress(); + button_scan.set_style(&style_val); + button_scan.set_text("SCAN"); + } + }*/ +} + +void EncodersView::start_tx(const bool scan) { + /*if (scan) { + if (tx_mode != SCAN) { + scan_index = 0; + scan_count = scan_list[options_scanlist.selected_index()].count; + scan_progress = 1; + repeat_index = 1; + tx_mode = SCAN; + strcpy(rgsb, &scan_list[options_scanlist.selected_index()].addresses[0]); + progress.set_max(scan_count * afsk_repeats); + update_progress(); + } + } else { + tx_mode = SINGLE; + repeat_index = 1; + progress.set_max(afsk_repeats); + update_progress(); + }*/ + + generate_frame(); + + /* + transmitter_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); + transmitter_model.set_baseband_configuration({ + .mode = 0, + .sampling_rate = 2280000U, + .decimation_factor = 1, + }); + transmitter_model.set_rf_amp(true); + transmitter_model.set_lna(40); + transmitter_model.set_vga(40); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_afsk_data( + lcr_message_data, + 228000 / portapack::persistent_memory::afsk_bitrate(), + portapack::persistent_memory::afsk_mark_freq() * (0x40000 * 256) / (228000 / 25), + portapack::persistent_memory::afsk_space_freq() * (0x40000 * 256) / (228000 / 25), + afsk_repeats, + portapack::persistent_memory::afsk_bw() * 115, // See proc_fsk_lcr.cpp + afsk_format + );*/ +} + +void EncodersView::on_bitfield() { + generate_frame(); +} + +void EncodersView::on_type_change(size_t index) { + enc_type = index; + + encoder_def = &encoder_defs[enc_type]; + + numberfield_clk.set_value(encoder_def->default_frequency / 1000); + + size_t n = 0; + for (auto& bitfield : bitfields) { + if (n < encoder_def->word_length) { + bitfield.hidden(false); + bitfield.set_range(0, encoder_def->bit_states - 1); + } else { + bitfield.hidden(true); + } + n++; + } + + generate_frame(); +} + +void EncodersView::on_show() { + options_enctype.set_selected_index(enc_type); + on_type_change(enc_type); +} + +EncodersView::EncodersView(NavigationView& nav) { + //baseband::run_image(portapack::spi_flash::image_tag_ook); + + add_children({ { + &text_enctype, + &options_enctype, + &text_bitfield, + &text_clk, + &numberfield_clk, + &text_kHz, + &text_bitduration, + &numberfield_bitduration, + &text_us1, + &text_wordduration, + &numberfield_wordduration, + &text_us2, + &text_waveform, + &text_status, + &progress, + &button_transmit + } }); + + options_enctype.on_change = [this](size_t index, int32_t value) { + (void)value; + this->on_type_change(index); + }; + + numberfield_clk.on_change = [this](int32_t value) { + int32_t new_value = 1000000 / (((float)value * 1000) / encoder_def->clk_per_bit); + if (new_value != numberfield_bitduration.value()) { + numberfield_bitduration.set_value(new_value, false); + numberfield_wordduration.set_value(new_value * encoder_def->word_length, false); + } + }; + + numberfield_bitduration.on_change = [this](int32_t value) { + int32_t new_value = 1000000 / (((float)value * 1000) / encoder_def->clk_per_bit); + if (new_value != numberfield_clk.value()) { + numberfield_clk.set_value(new_value, false); + numberfield_wordduration.set_value(value * encoder_def->word_length, false); + } + }; + + numberfield_wordduration.on_change = [this](int32_t value) { + int32_t new_value = value / encoder_def->word_length; + if (new_value != numberfield_bitduration.value()) { + numberfield_bitduration.set_value(new_value, false); + numberfield_clk.set_value(1000000 / (((float)new_value * 1000) / encoder_def->clk_per_bit), false); + } + }; + + const auto bitfield_fn = [this](int32_t value) { + (void)value; + this->on_bitfield(); + }; + + size_t n = 0; + for (auto& bitfield : bitfields) { + bitfield.on_change = bitfield_fn; + bitfield.id = n; + bitfield.set_value(0); + bitfield.set_parent_rect({ + static_cast(16 + 8 * n), + static_cast(80), + 8, 16 + }); + add_child(&bitfield); + n++; + } + + options_enctype.set_selected_index(0); + + button_transmit.set_style(&style_val); + + button_transmit.on_select = [this](Button&) { + if (tx_mode == IDLE) start_tx(false); + }; +} + +} /* namespace ui */ diff --git a/firmware/application/ui_encoders.hpp b/firmware/application/ui_encoders.hpp new file mode 100644 index 00000000..84acd970 --- /dev/null +++ b/firmware/application/ui_encoders.hpp @@ -0,0 +1,248 @@ +/* + * 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 "ui.hpp" +#include "ui_widget.hpp" +#include "ui_textentry.hpp" +#include "message.hpp" +#include "transmitter_model.hpp" + +namespace ui { + +class EncodersView : public View { +public: + EncodersView(NavigationView& nav); + ~EncodersView(); + + void focus() override; + void on_show() override; + void paint(Painter& painter) override; + + std::string title() const override { return "Encoders TX"; }; + +private: + struct encoder_def_t { + uint8_t bit_states; + uint16_t clk_per_bit; + uint16_t clk_per_symbol; + std::vector bit_format; + uint8_t word_length; + std::string format; + bool sync_bit; + uint32_t default_frequency; + uint8_t repeat_min; + }; + + // P = Prelude + // S = Sync + // 0~9 A~J = Address/data bits + const encoder_def_t encoder_defs[7] = { + // PT2260 + { 3, 1024, 128, + { "10001000", "11101110", "10001110" }, + 12, "0123456789ABS", + true, 200000, 1 }, + + // PT2262 + { 3, 32, 4, + { "10001000", "11101110", "10001110" }, + 12, "0123456789ABS", + true, 30000, 4 }, + + // HK526E + { 2, 24, 8, + { "110", "100" }, + 12, "0123456789AB", + false, 20000, 4 }, + + // HT12E + { 2, 3, 1, + { "011", "001" }, + 12, "PS0123456789AB", + true, 3000, 4 }, + + // VD5026 13 bits ? + { 4, 128, 8, + { "1000000010000000", "1111111011111110", "1111111010000000", "1000000011111110" }, + 12, "S0123456789AB", + true, 100000, 4 }, + + // UM3750 + { 2, 96, 32, + { "011", "001" }, + 12, "S0123456789AB", + true, 100000, 4 }, + + // UM3758 + { 3, 96, 16, + { "011011", "001001", "011001" }, + 18, "S0123456789ABCDEFGH", + true, 160000, 4 } + }; + + enum tx_modes { + IDLE = 0, + SINGLE, + SCAN + }; + + uint8_t enc_type = 0; + const encoder_def_t * encoder_def = &encoder_defs[0]; + tx_modes tx_mode = IDLE; + //bool abort_scan = false; + //uint8_t scan_count; + //double scan_progress; + //unsigned int scan_index; + std::string debug_text = "0"; + //rf::Frequency f; + //uint8_t repeat_index; + + void on_bitfield(); + void on_type_change(size_t index); + void generate_frame(); + void update_progress(); + void start_tx(const bool scan); + void on_txdone(int n); + + radio::Configuration ook_radio_config = { + 0, + 2280000, + 2500000, // ? + rf::Direction::Transmit, + true, + 0, + 0, + 1, + }; + + const Style style_val { + .font = font::fixed_8x16, + .background = Color::green(), + .foreground = Color::black(), + }; + const Style style_cancel { + .font = font::fixed_8x16, + .background = Color::red(), + .foreground = Color::black(), + }; + + std::array bitfields; + + Text text_enctype { + { 2 * 8, 3 * 8, 8 * 8, 16 }, + "Encoder:" + }; + OptionsField options_enctype { + { 2 * 8, 5 * 8 }, + 7, + { + { "PT2260 ", 0 }, + { "PT2262 ", 1 }, + { "HK526E ", 2 }, + { "HT12E ", 3 }, + { "VD5026 ", 4 }, + { "UM3750 ", 5 }, + { "UM3758 ", 6 } + } + }; + + Text text_clk { + { 15 * 8, 3 * 8, 4 * 8, 16 }, + "Clk:" + }; + NumberField numberfield_clk { + { 21 * 8, 3 * 8 }, + 3, + { 1, 300 }, + 1, + ' ' + }; + Text text_kHz { + { 24 * 8, 3 * 8, 3 * 8, 16 }, + "kHz" + }; + + Text text_bitduration { + { 15 * 8, 5 * 8, 4 * 8, 16 }, + "Bit:" + }; + NumberField numberfield_bitduration { + { 20 * 8, 5 * 8 }, + 4, + { 300, 9999 }, + 1, + ' ' + }; + Text text_us1 { + { 24 * 8, 5 * 8, 2 * 8, 16 }, + "us" + }; + + Text text_wordduration { + { 14 * 8, 7 * 8, 5 * 8, 16 }, + "Word:" + }; + NumberField numberfield_wordduration { + { 20 * 8, 7 * 8 }, + 5, + { 300, 99999 }, + 100, + ' ' + }; + Text text_us2 { + { 25 * 8, 7 * 8, 2 * 8, 16 }, + "us" + }; + + Text text_bitfield { + { 2 * 8, 8 * 8, 5 * 8, 16 }, + "Word:" + }; + + Text text_waveform { + { 1 * 8, 112, 9 * 8, 16 }, + "Waveform:" + }; + + Text text_status { + { 2 * 8, 224, 128, 16 }, + "Ready" + }; + ProgressBar progress { + { 16, 224 + 20, 208, 16 } + }; + + Button button_transmit { + { 16, 270, 80, 32 }, + "TX" + }; + + MessageHandlerRegistration message_handler_tx_done { + Message::ID::TXDone, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_txdone(message.n); + } + }; +}; + +} /* namespace ui */ diff --git a/firmware/application/ui_menu.cpp b/firmware/application/ui_menu.cpp index 1d665c26..b6f19e6a 100644 --- a/firmware/application/ui_menu.cpp +++ b/firmware/application/ui_menu.cpp @@ -152,7 +152,7 @@ bool MenuView::set_highlighted(const size_t new_value) { return false; } - if ((new_value - offset_ + 1) >= MENU_MAX) { + if ((new_value > offset_) && ((new_value - offset_ + 1) >= MENU_MAX)) { // Shift MenuView up offset_ = new_value - MENU_MAX + 1; update_items(); diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index c53d5043..57e7203d 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -38,15 +38,15 @@ #include "ui_closecall.hpp" // DEBUG #include "ui_freqman.hpp" // DEBUG -#include "ui_setup.hpp" +#include "ui_encoders.hpp" #include "ui_debug.hpp" #include "ui_rds.hpp" #include "ui_xylos.hpp" #include "ui_epar.hpp" #include "ui_lcr.hpp" #include "analog_audio_app.hpp" -#include "ui_audiotx.hpp" -#include "ui_jammer.hpp" +#include "ui_audiotx.hpp" // DEBUG +#include "ui_jammer.hpp" // DEBUG #include "analog_audio_app.hpp" #include "ais_app.hpp" @@ -251,7 +251,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) { //{ "EPAR TX", ui::Color::green(), [&nav](){ nav.push(md5_baseband_tx, EPAR); } }, { "Xylos TX", ui::Color::green(), [&nav](){ nav.push(); } }, { "TEDI/LCR TX", ui::Color::yellow(), [&nav](){ nav.push(); } }, - { "OOK encoder TX", ui::Color::orange(), [&nav](){ nav.push(); } }, + { "OOK encoders TX", ui::Color::orange(), [&nav](){ nav.push(); } }, { "RDS TX", ui::Color::red(), [&nav](){ nav.push(); } }, //{ "Analyze", ui::Color::white(), [&nav](){ nav.push(); } }, { "Setup", ui::Color::white(), [&nav](){ nav.push(); } }, diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index f02f3826..0a49853b 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -1014,18 +1014,28 @@ int32_t NumberField::value() const { return value_; } -void NumberField::set_value(int32_t new_value) { +void NumberField::set_value(int32_t new_value, bool trigger_change) { new_value = clip_value(new_value); if( new_value != value() ) { value_ = new_value; - if( on_change ) { + if( on_change && trigger_change ) { on_change(value_); } set_dirty(); } } +void NumberField::set_value(int32_t new_value) { + set_value(new_value, true); +} + +void NumberField::set_range(const int32_t min, const int32_t max) { + range.first = min; + range.second = max; + set_value(value(), false); +} + void NumberField::paint(Painter& painter) { const auto text = to_string_dec_int(value_, length_, fill_char); diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 2c66df8d..85e630ef 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -109,6 +109,7 @@ public: void set_clean(); void visible(bool v); + bool visible() { return flags.visible; }; bool highlighted() const; void set_highlighted(const bool value); @@ -409,12 +410,18 @@ public: using range_t = std::pair; NumberField(Point parent_pos, size_t length, range_t range, int32_t step, char fill_char); + NumberField( + ) : NumberField { { }, 1, { 0, 1 }, 1, ' ' } + { + } NumberField(const NumberField&) = delete; NumberField(NumberField&&) = delete; int32_t value() const; void set_value(int32_t new_value); + void set_value(int32_t new_value, bool trigger_change); + void set_range(const int32_t min, const int32_t max); void paint(Painter& painter) override; @@ -423,7 +430,7 @@ public: bool on_touch(const TouchEvent event) override; private: - const range_t range; + range_t range; const int32_t step; const size_t length_; const char fill_char; diff --git a/firmware/portapack-h1-firmware.bin b/firmware/portapack-h1-firmware.bin index 600723eb..1ff05982 100644 Binary files a/firmware/portapack-h1-firmware.bin and b/firmware/portapack-h1-firmware.bin differ