diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 4b02659f..0cb80e31 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -35,7 +35,7 @@ set(USE_OPT "-Os -g --specs=nano.specs") set(USE_COPT "-std=gnu99") # C++ specific options here (added to USE_OPT). -set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions") +set(USE_CPPOPT "-Wl,-Map,foo.map -std=c++11 -fno-rtti -fno-exceptions") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) @@ -145,37 +145,38 @@ set(CPPSRC ${COMMON}/ui_painter.cpp ${COMMON}/ui_focus.cpp ${COMMON}/msgpack.cpp - ui_navigation.cpp - ui_menu.cpp - ui_rssi.cpp - ui_channel.cpp + ui_about.cpp + ui_adsbtx.cpp + ui_afsksetup.cpp + ui_alphanum.cpp ui_audio.cpp - ui_font_fixed_8x16.cpp - ui_setup.cpp - ui_touch_calibration.cpp - ui_debug.cpp ui_baseband_stats_view.cpp - ui_sd_card_status_view.cpp - ui_sd_card_debug.cpp + ui_channel.cpp + ui_debug.cpp + ui_encoders.cpp + ui_font_fixed_8x16.cpp + ui_freqman.cpp + ui_handwrite.cpp + ui_jammer.cpp + ui_lcr.cpp + ui_menu.cpp + ui_navigation.cpp + ui_numbers.cpp + ui_nuoptix.cpp + ui_rds.cpp ui_receiver.cpp ui_record_view.cpp - ui_textentry.cpp - ui_alphanum.cpp - ui_spectrum.cpp - ui_about.cpp - ui_nuoptix.cpp - ui_adsbtx.cpp - ui_handwrite.cpp - ui_afsksetup.cpp - # ui_closecall.cpp + ui_rssi.cpp + ui_sd_card_status_view.cpp + ui_sd_card_debug.cpp + ui_setup.cpp ui_soundboard.cpp - ui_rds.cpp - ui_lcr.cpp + ui_spectrum.cpp + ui_textentry.cpp + ui_touch_calibration.cpp + ui_whipcalc.cpp ui_xylos.cpp - ui_numbers.cpp - ui_freqman.cpp - ui_encoders.cpp - ui_jammer.cpp + # ui_closecall.cpp # ui_loadmodule.cpp recent_entries.cpp receiver_model.cpp diff --git a/firmware/application/Makefile b/firmware/application/Makefile index b4bb4d0c..069a2a24 100644 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -4302,6 +4302,30 @@ ui_touch_calibration.cpp.s: cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_touch_calibration.cpp.s .PHONY : ui_touch_calibration.cpp.s +ui_whipcalc.obj: ui_whipcalc.cpp.obj +.PHONY : ui_whipcalc.obj + +# target to build an object file +ui_whipcalc.cpp.obj: + cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_whipcalc.cpp.obj +.PHONY : ui_whipcalc.cpp.obj + +ui_whipcalc.i: ui_whipcalc.cpp.i +.PHONY : ui_whipcalc.i + +# target to preprocess a source file +ui_whipcalc.cpp.i: + cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_whipcalc.cpp.i +.PHONY : ui_whipcalc.cpp.i + +ui_whipcalc.s: ui_whipcalc.cpp.s +.PHONY : ui_whipcalc.s + +# target to generate assembly for a file +ui_whipcalc.cpp.s: + cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_whipcalc.cpp.s +.PHONY : ui_whipcalc.cpp.s + ui_xylos.obj: ui_xylos.cpp.obj .PHONY : ui_xylos.obj @@ -4858,6 +4882,9 @@ help: @echo "... ui_touch_calibration.obj" @echo "... ui_touch_calibration.i" @echo "... ui_touch_calibration.s" + @echo "... ui_whipcalc.obj" + @echo "... ui_whipcalc.i" + @echo "... ui_whipcalc.s" @echo "... ui_xylos.obj" @echo "... ui_xylos.i" @echo "... ui_xylos.s" diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index cedaed35..aca5c944 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -23,14 +23,15 @@ // Bitmaps generated with: // Gimp image > indexed colors (16), then "xxd -i *.bmp" +//TEST: Imperial in whipcalc //TEST: Numbers //TEST: Jammer //TEST: RDS -//BUG: Unistroke text entry screen doesn't care about string max length parameter +//BUG (fixed ?): Soundboard crashes on exit if no wav files on sd card //BUG (fixed ?): No audio in about when shown second time +//BUG: Unistroke text entry screen doesn't care about string max length parameter //BUG: POCSAG RX sometimes misses the first codeword after SYNC -//BUG: Soundboard crashes on exit if no wav files on sd card //TODO: Use ModalMessageView with yes/no for TX //TODO: Show address/data bit fields in OOK TX diff --git a/firmware/application/ui_freqman.cpp b/firmware/application/ui_freqman.cpp index 1665f242..c453ac23 100644 --- a/firmware/application/ui_freqman.cpp +++ b/firmware/application/ui_freqman.cpp @@ -33,9 +33,27 @@ using namespace portapack; namespace ui { + +void FrequencySaveView::focus() { + button_exit.focus(); +} + +FrequencySaveView::FrequencySaveView( + NavigationView& nav, + const rf::Frequency value +) { + + add_children({ { + &button_exit + } }); -void FreqManView::paint(Painter& painter) { - (void)painter; + button_exit.on_select = [this, &nav](Button&) { + nav.pop(); + }; +} + +void FreqManView::focus() { + button_exit.focus(); } FreqManView::FreqManView( @@ -43,7 +61,7 @@ FreqManView::FreqManView( ) { add_children({ { - &button_ok + &button_exit } }); size_t n = 0; @@ -61,7 +79,7 @@ FreqManView::FreqManView( n++; } - button_ok.on_select = [this, &nav](Button&) { + button_exit.on_select = [this, &nav](Button&) { nav.pop(); }; diff --git a/firmware/application/ui_freqman.hpp b/firmware/application/ui_freqman.hpp index d22208d7..44f8419d 100644 --- a/firmware/application/ui_freqman.hpp +++ b/firmware/application/ui_freqman.hpp @@ -28,23 +28,37 @@ namespace ui { +class FrequencySaveView : public View { +public: + FrequencySaveView(NavigationView& nav, const rf::Frequency value); + //~FrequencySaveView(); + + void focus() override; + + std::string title() const override { return "Save frequency"; }; + +private: + Button button_exit { + { 72, 264, 96, 32 }, + "Exit" + }; +}; + class FreqManView : public View { public: FreqManView(NavigationView& nav); //~FreqManView(); - std::string title() const override { return "Frequency list"; }; + void focus() override; - void paint(Painter& painter) override; - //void on_show() override; - //void on_hide() override; + std::string title() const override { return "Freq. manager"; }; private: std::array text_list; - Button button_ok { + Button button_exit { { 72, 264, 96, 32 }, - "OK" + "Exit" }; }; diff --git a/firmware/application/ui_jammer.cpp b/firmware/application/ui_jammer.cpp index c48451da..a10c4cc4 100644 --- a/firmware/application/ui_jammer.cpp +++ b/firmware/application/ui_jammer.cpp @@ -51,6 +51,7 @@ void JammerView::update_text(uint8_t id, rf::Frequency f) { std::string bw; uint8_t c; + /* auto mhz = to_string_dec_int(f / 1000000, 3); auto hz100 = to_string_dec_int((f / 100) % 10000, 4, '0'); @@ -63,7 +64,8 @@ void JammerView::update_text(uint8_t id, rf::Frequency f) { strcat(finalstr, " "); buttons_freq[id].set_text(finalstr); - + */ + for (c = 0; c < 3; c++) { center = (frequency_range[c].min + frequency_range[c].max) / 2; bw = to_string_dec_int(abs(frequency_range[c].max - frequency_range[c].min) / 1000, 5); @@ -156,7 +158,7 @@ JammerView::JammerView(NavigationView& nav) { button.on_select = button_freq_fn; button.set_parent_rect({ static_cast(13 * 8), - static_cast((n * 52) + 91 + (17 * (n & 1))), + static_cast(((n >> 1) * 52) + 90 + (18 * (n & 1))), 88, 18 }); button.id = n; @@ -180,6 +182,8 @@ JammerView::JammerView(NavigationView& nav) { checkbox_range1.set_value(range_presets[v][0].enabled); checkbox_range2.set_value(range_presets[v][1].enabled); checkbox_range3.set_value(range_presets[v][2].enabled); + + update_text(0, 0); }; options_preset.set_selected_index(8); // Sigfox, because they deserve it diff --git a/firmware/application/ui_jammer.hpp b/firmware/application/ui_jammer.hpp index 0a4ea5cd..59eadeef 100644 --- a/firmware/application/ui_jammer.hpp +++ b/firmware/application/ui_jammer.hpp @@ -95,7 +95,7 @@ private: { false, 0, 0 }}, // Sigfox - {{ true, 868000000, 868220000 }, // Center: 868.2MHz BW: 40kHz + {{ true, 868000000, 868200000 }, // Center: 868.2MHz BW: 40kHz { false, 0, 0 }, { false, 0, 0 }}, diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 0c00aadd..a7bb5b2a 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -36,28 +36,26 @@ #include "ui_debug.hpp" #include "ui_numbers.hpp" +#include "ui_whipcalc.hpp" //#include "ui_closecall.hpp" // DEBUG #include "ui_freqman.hpp" #include "ui_nuoptix.hpp" #include "ui_soundboard.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" // DEBUG #include "ui_adsbtx.hpp" #include "ui_jammer.hpp" - #include "ais_app.hpp" #include "ert_app.hpp" #include "tpms_app.hpp" #include "pocsag_app.hpp" #include "capture_app.hpp" +#include "ui_debug.hpp" #include "core_control.hpp" #include "file.hpp" @@ -182,13 +180,38 @@ void NavigationView::pop() { } } +void NavigationView::pop_modal() { + if( view() == modal_view ) { + modal_view = nullptr; + } + + // Pop modal view and underlying app view + if( view_stack.size() > 2 ) { + free_view(); + view_stack.pop_back(); + free_view(); + view_stack.pop_back(); + + update_view(); + } +} + void NavigationView::display_modal( const std::string& title, const std::string& message +) { + display_modal(title, message, INFO, nullptr); +} + +void NavigationView::display_modal( + const std::string& title, + const std::string& message, + const modal_t type, + const std::function on_choice ) { /* If a modal view is already visible, don't display another */ if( !modal_view ) { - modal_view = push(title, message, false); + modal_view = push(title, message, type, on_choice); } } @@ -272,6 +295,15 @@ TransmitterAudioMenuView::TransmitterAudioMenuView(NavigationView& nav) { on_left = [&nav](){ nav.pop(); }; } +/* UtilitiesView *****************************************************************/ + +UtilitiesView::UtilitiesView(NavigationView& nav) { + add_items<2>({ { + { "Whip antenna calculator", ui::Color::green(), [&nav](){ nav.push(); } }, + { "Notepad", ui::Color::grey(), [&nav](){ nav.push(); } }, + } }); + on_left = [&nav](){ nav.pop(); }; +} /* SystemMenuView ********************************************************/ SystemMenuView::SystemMenuView(NavigationView& nav) { @@ -284,9 +316,10 @@ SystemMenuView::SystemMenuView(NavigationView& nav) { //{ "Close Call RX", ui::Color::cyan(), [&nav](){ nav.push(); } }, { "Jammer", ui::Color::orange(), [&nav](){ nav.push(); } }, { "Frequency manager", ui::Color::white(), [&nav](){ nav.push(); } }, + { "Utilities", ui::Color::purple(), [&nav](){ nav.push(); } }, //{ "Analyze", ui::Color::white(), [&nav](){ nav.push(); } }, { "Setup", ui::Color::white(), [&nav](){ nav.push(); } }, - { "Debug", ui::Color::white(), [&nav](){ nav.push(); } }, + //{ "Debug", ui::Color::white(), [&nav](){ nav.push(); } }, { "HackRF", ui::Color::white(), [&nav](){ nav.push(); } }, { "About", ui::Color::white(), [&nav](){ nav.push(); } } } }); @@ -453,61 +486,66 @@ void NotImplementedView::focus() { } /* ModalMessageView ******************************************************/ - ModalMessageView::ModalMessageView( NavigationView& nav, const std::string& title, const std::string& message, - bool yesno + const modal_t type, + const std::function on_choice ) : title_ { title }, - yesno_ { yesno } + type_ { type }, + on_choice_ { on_choice } { - if (!yesno) { - button_done.on_select = [&nav](Button&){ - nav.pop(); - }; + add_child(&text_message); + + if (type == INFO) { + add_child(&button_ok); - add_children({ { - &text_message, - &button_done - } }); - } else { - button_yes.on_select = [this,&nav](Button&){ - if (on_choice) on_choice(true); + button_ok.on_select = [&nav](Button&){ nav.pop(); }; - button_no.on_select = [this,&nav](Button&){ - if (on_choice) on_choice(false); - nav.pop(); - }; - + } else if (type == YESNO) { add_children({ { - &text_message, &button_yes, &button_no } }); + + button_yes.on_select = [this, &nav](Button&){ + if (on_choice_) on_choice_(true); + nav.pop(); + }; + button_no.on_select = [this, &nav](Button&){ + if (on_choice_) on_choice_(false); + nav.pop(); + }; + } else { + add_child(&button_ok); + + button_ok.on_select = [this, &nav](Button&){ + if (on_choice_) on_choice_(true); + nav.pop_modal(); + }; } - - text_message.set(message); const int text_message_width = message.size() * 8; text_message.set_parent_rect({ - (240 - text_message_width) / 2, 7 * 16, + (240 - text_message_width) / 2, 8 * 16, text_message_width, 16 }); + text_message.set(message); } void ModalMessageView::paint(Painter& painter) { (void)painter; - portapack::display.drawBMP({64, 64}, modal_warning_bmp, false); + portapack::display.drawBMP({96, 64}, modal_warning_bmp, false); } void ModalMessageView::focus() { - if (!yesno_) { - button_done.focus(); - } else { + if (type_ == YESNO) { button_yes.focus(); + } else { + button_ok.focus(); } } diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 181161c6..4a06bd40 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -40,6 +40,12 @@ namespace ui { +enum modal_t { + INFO = 0, + YESNO, + ABORT +}; + class SystemStatusView : public View { public: std::function on_back; @@ -112,8 +118,10 @@ public: void push(View* v); void pop(); + void pop_modal(); void display_modal(const std::string& title, const std::string& message); + void display_modal(const std::string& title, const std::string& message, const modal_t type, const std::function on_choice); void focus() override; @@ -192,6 +200,12 @@ public: std::string title() const override { return "Audio TX"; }; }; +class UtilitiesView : public MenuView { +public: + UtilitiesView(NavigationView& nav); + std::string title() const override { return "Utilities"; }; +}; + class SystemMenuView : public MenuView { public: SystemMenuView(NavigationView& nav); @@ -279,23 +293,23 @@ public: NavigationView& nav, const std::string& title, const std::string& message, - bool yesno + const modal_t type, + const std::function on_choice ); void paint(Painter& painter) override; void focus() override; - std::string title() const override { return title_; }; - - std::function on_choice; + // std::string title() const override { return title_; }; private: const std::string title_; - const bool yesno_; - + const modal_t type_; + const std::function on_choice_; + Text text_message { }; - Button button_done { + Button button_ok { { 10 * 8, 13 * 16, 10 * 8, 24 }, "OK", }; diff --git a/firmware/application/ui_numbers.cpp b/firmware/application/ui_numbers.cpp index 5da6f5de..72e41be0 100644 --- a/firmware/application/ui_numbers.cpp +++ b/firmware/application/ui_numbers.cpp @@ -31,6 +31,8 @@ #include #include +// TODO: Total transmission time (all durations / 44100) + using namespace portapack; using namespace hackrf::one; @@ -38,6 +40,10 @@ namespace ui { void NumbersStationView::focus() { button_exit.focus(); + + if (file_error) { + nav_.display_modal("No files", "Missing files in /numbers/", ABORT, nullptr); + } } NumbersStationView::~NumbersStationView() { @@ -45,53 +51,85 @@ NumbersStationView::~NumbersStationView() { baseband::shutdown(); } -void NumbersStationView::paint(Painter& painter) { - (void)painter; -} - void NumbersStationView::on_tuning_frequency_changed(rf::Frequency f) { transmitter_model.set_tuning_frequency(f); } void NumbersStationView::prepare_audio() { + uint8_t code; + if (cnt >= sample_duration) { - /*if (!check_loop.value()) { - transmitter_model.disable(); - return; - } else { - file.seek(44); - cnt = 0; - }*/ + if (segment == ANNOUNCE) { + if (!announce_loop) { + segment = MESSAGE; + } else { + auto error = file.open("/numbers/announce.wav"); + if (error.is_valid()) return; + + sample_duration = sound_sizes[10]; + + file.seek(44); // Skip header + + announce_loop--; + } + } - // DEBUG - file.seek(44); - cnt = 0; + if (segment == MESSAGE) { + if (id_test == 10) + transmitter_model.disable(); + + code = symfield_code.value(id_test); + + if (code == 10) { + pause = 11025; // p: 0.25s @ 44100Hz + memset(audio_buffer, 0, 1024); + } else if (code == 11) { + pause = 33075; // P: 0.75s @ 44100Hz + memset(audio_buffer, 0, 1024); + } else { + auto error = file.open("/numbers/" + file_names[code] + ".wav"); + if (error.is_valid()) return; + + sample_duration = sound_sizes[code]; + + file.seek(44); // Skip header + } + + id_test++; + } + cnt = 0; } - file.read(audio_buffer, 1024); - - // Unsigned to signed, pretty stupid :/ - for (size_t n = 0; n < 1024; n++) - audio_buffer[n] -= 0x80; - - cnt += 1024; + if (!pause) { + size_t bytes_read = file.read(audio_buffer, 1024).value(); + + // Unsigned to signed, pretty stupid :/ + for (size_t n = 0; n < bytes_read; n++) + audio_buffer[n] -= 0x80; + + cnt += 1024; + } else { + if (pause >= 1024) { + pause -= 1024; + } else { + cnt = sample_duration; + pause = 0; + } + } baseband::set_fifo_data(audio_buffer); } -void NumbersStationView::play_sound(uint16_t id) { +void NumbersStationView::start_tx() { uint32_t divider; - if (sounds[id].size == 0) return; - - auto error = file.open("/numbers/" + filenames[id] + ".wav"); - if (error.is_valid()) return; + sample_duration = sound_sizes[0]; + cnt = sample_duration; - sample_duration = sounds[id].sample_duration; - - cnt = 0; - file.seek(44); // Skip header + id_test = 0; + announce_loop = 2; + segment = ANNOUNCE; prepare_audio(); @@ -116,21 +154,94 @@ void NumbersStationView::play_sound(uint16_t id) { ); } +// TODO: Copied from soundboard, make globally available +uint16_t NumbersStationView::fb_to_uint16(const std::string& fb) { + return (fb[1] << 8) + fb[0]; +} + +uint32_t NumbersStationView::fb_to_uint32(const std::string& fb) { + return (fb[3] << 24) + (fb[2] << 16) + (fb[1] << 8) + fb[0]; +} + NumbersStationView::NumbersStationView( NavigationView& nav -) { - uint8_t m, d, dayofweek; - uint16_t y; +) : nav_ (nav) +{ + uint8_t c; + uint8_t y, m, d, dayofweek; + size_t size; + + char file_buffer[32]; + + c = 0; + for (auto& file_name : file_names) { + auto error = file.open("/numbers/" + file_name + ".wav"); + if (!error.is_valid()) { + file.seek(22); + file.read(file_buffer, 2); + + // Is file mono ? + if (fb_to_uint16(file_buffer) == 1) { + file.seek(40); + file.read(file_buffer, 4); + size = fb_to_uint32(file_buffer); + if (!size) break; + sound_sizes[c] = size; + c++; + } else { + break; + } + } else { + break; + } + } + + if (c != 11) file_error = true; baseband::run_image(portapack::spi_flash::image_tag_audio_tx); add_children({ { &text_title, + &field_frequency, &number_bw, + &symfield_code, + &button_tx, &button_exit } }); - number_bw.set_value(15); + number_bw.set_value(120); + + field_frequency.set_value(transmitter_model.tuning_frequency()); + field_frequency.set_step(50000); + field_frequency.on_change = [this](rf::Frequency f) { + this->on_tuning_frequency_changed(f); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(transmitter_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->on_tuning_frequency_changed(f); + this->field_frequency.set_value(f); + }; + }; + + // DEBUG + symfield_code.set_value(0, 10); + symfield_code.set_value(1, 3); + symfield_code.set_value(2, 4); + symfield_code.set_value(3, 11); + symfield_code.set_value(4, 6); + symfield_code.set_value(5, 1); + symfield_code.set_value(6, 9); + symfield_code.set_value(7, 7); + symfield_code.set_value(8, 8); + symfield_code.set_value(9, 0); + transmitter_model.set_tuning_frequency(103300000); // 103.3MHz + + for (c = 0; c < 10; c++) + symfield_code.set_symbol_list(c, "0123456789pP"); + + rtc::RTC datetime; rtcGetTime(&RTCD1, &datetime); @@ -144,6 +255,10 @@ NumbersStationView::NumbersStationView( text_title.set(day_of_week[dayofweek]); + button_tx.on_select = [this, &nav](Button&){ + this->start_tx(); + }; + button_exit.on_select = [&nav](Button&){ nav.pop(); }; diff --git a/firmware/application/ui_numbers.hpp b/firmware/application/ui_numbers.hpp index 7c797632..a399da38 100644 --- a/firmware/application/ui_numbers.hpp +++ b/firmware/application/ui_numbers.hpp @@ -25,7 +25,7 @@ #include "ui.hpp" #include "ui_widget.hpp" -#include "ui_painter.hpp" +#include "ui_receiver.hpp" #include "ui_navigation.hpp" #include "ui_font_fixed_8x16.hpp" #include "clock_manager.hpp" @@ -41,44 +41,58 @@ public: ~NumbersStationView(); void focus() override; - void paint(Painter& painter) override; - + std::string title() const override { return "Numbers station"; }; private: - // Different from the one in ui_soundboard.hpp, simpler - struct sound { - uint32_t size = 0; - uint32_t sample_duration = 0; + // Sequencing state machine + enum segments { + ANNOUNCE = 0, + MESSAGE, + SIGNOFF }; - sound sounds[11]; + Style style_red { + .font = font::fixed_8x16, + .background = Color::red(), + .foreground = Color::black() + }; - const std::string filenames[11] = { - "zero", - "one", - "two", - "three", - "four", - "five", - "six", - "seven", - "eight", - "nine", - "anounce" + NavigationView& nav_; + + segments segment; + bool file_error = false; + uint32_t sound_sizes[11]; + + const std::vector file_names = { + { "0" }, + { "1" }, + { "2" }, + { "3" }, + { "4" }, + { "5" }, + { "6" }, + { "7" }, + { "8" }, + { "9" }, + { "announce" } }; const uint8_t month_table[12] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 }; - const char * day_of_week[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; + const char * day_of_week[7] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; File file; + uint8_t id_test, announce_loop; uint32_t cnt; uint32_t sample_duration; int8_t audio_buffer[1024]; + uint32_t pause = 0; void on_tuning_frequency_changed(rf::Frequency f); void prepare_audio(); - void play_sound(uint16_t id); + void start_tx(); + uint16_t fb_to_uint16(const std::string& fb); + uint32_t fb_to_uint32(const std::string& fb); // Schedule: save on sd card // For each day of the week, max 8 messages ? @@ -95,14 +109,27 @@ private: "Schedule:" }; + FrequencyField field_frequency { + { 1 * 8, 4 }, + }; NumberField number_bw { - { 11 * 8, 3 * 16 }, + { 12 * 8, 2 * 16 }, 3, {1, 150}, 1, ' ' }; + SymField symfield_code { + { 1*8, 3 * 16 }, + 10, + false + }; + + Button button_tx { + { 21 * 8, 13 * 16, 64, 32 }, + "TX !" + }; Button button_exit { { 21 * 8, 16 * 16, 64, 32 }, "Exit" diff --git a/firmware/application/ui_receiver.cpp b/firmware/application/ui_receiver.cpp index 18a5c613..4e928fec 100644 --- a/firmware/application/ui_receiver.cpp +++ b/firmware/application/ui_receiver.cpp @@ -20,6 +20,7 @@ */ #include "ui_receiver.hpp" +#include "ui_freqman.hpp" #include "portapack.hpp" using namespace portapack; @@ -140,14 +141,22 @@ FrequencyKeypadView::FrequencyKeypadView( button.on_select = button_fn; button.set_parent_rect({ (n % 3) * button_w, - (n / 3) * button_h + button_h, + (n / 3) * button_h + 24, button_w, button_h }); button.set_text(label); n++; } + + add_children({ { + &button_save, + &button_close + } }); + + button_save.on_select = [this, &nav](Button&) { + nav.push(this->value()); + }; - add_child(&button_close); button_close.on_select = [this, &nav](Button&) { if( on_changed ) { on_changed(this->value()); diff --git a/firmware/application/ui_receiver.hpp b/firmware/application/ui_receiver.hpp index 1d4cb6d3..280cc853 100644 --- a/firmware/application/ui_receiver.hpp +++ b/firmware/application/ui_receiver.hpp @@ -198,13 +198,17 @@ private: static constexpr int text_digits = mhz_digits + 1 + submhz_digits; Text text_value { - { 0, 0, text_digits * button_w, button_h } + { 0, 4, 240, 16 } }; std::array buttons; + Button button_save { + { 0, button_h * 4 + button_h, button_w, button_h }, + "Save" + }; Button button_close { - { 0, button_h * 4 + button_h, button_w * 3, button_h }, + { button_w, button_h * 4 + button_h, button_w * 2, button_h }, "Done" }; diff --git a/firmware/application/ui_soundboard.cpp b/firmware/application/ui_soundboard.cpp index 59df3272..13fa4892 100644 --- a/firmware/application/ui_soundboard.cpp +++ b/firmware/application/ui_soundboard.cpp @@ -74,10 +74,11 @@ void SoundBoardView::prepare_audio() { } pbar.set_value(cnt); + + size_t bytes_read = file.read(audio_buffer, 1024).value(); - file.read(audio_buffer, 1024); - - for (size_t n = 0; n < 1024; n++) + // Unsigned to signed, pretty stupid :/ + for (size_t n = 0; n < bytes_read; n++) audio_buffer[n] -= 0x80; cnt += 1024; @@ -87,6 +88,10 @@ void SoundBoardView::prepare_audio() { void SoundBoardView::focus() { buttons[0].focus(); + + if (!max_sound) { + nav_.display_modal("No files", "No files in /wav/ directory", ABORT, nullptr); + } } void SoundBoardView::on_tuning_frequency_changed(rf::Frequency f) { @@ -202,7 +207,7 @@ void SoundBoardView::on_ctcss_changed(uint32_t v) { SoundBoardView::SoundBoardView( NavigationView& nav -) +) : nav_ (nav) { std::vector file_list; std::string file_name; @@ -256,11 +261,6 @@ SoundBoardView::SoundBoardView( } } - if (!c) { - nav.display_modal("No files", "No files in /wav/ directory"); - nav.pop(); - } - max_sound = c; max_page = max_sound / 21; // 21 buttons per page diff --git a/firmware/application/ui_soundboard.hpp b/firmware/application/ui_soundboard.hpp index b544f27e..0df8e72b 100644 --- a/firmware/application/ui_soundboard.hpp +++ b/firmware/application/ui_soundboard.hpp @@ -44,6 +44,8 @@ public: std::string title() const override { return "Soundboard"; }; private: + NavigationView& nav_; + enum tx_modes { NORMAL = 0, RANDOM diff --git a/firmware/application/ui_whipcalc.cpp b/firmware/application/ui_whipcalc.cpp new file mode 100644 index 00000000..0116bcb3 --- /dev/null +++ b/firmware/application/ui_whipcalc.cpp @@ -0,0 +1,111 @@ +/* + * 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_whipcalc.hpp" + +#include "ch.h" +#include "portapack.hpp" +#include "event_m0.hpp" + +#include + +using namespace portapack; + +namespace ui { + +void WhipCalcView::focus() { + field_frequency.focus(); +} + +void WhipCalcView::update_result() { + double length, divider; + + divider = ((double)options_type.selected_index_value() / 8.0); + + // Metric + length = (speed_of_light_mps / (double)field_frequency.value()) * divider; + + auto m = to_string_dec_int((int)length, 2); + auto cm = to_string_dec_int(int(length * 100.0) % 100, 2); + auto mm = to_string_dec_int(int(length * 1000.0) % 10, 1); + + text_result_metric.set(m + "m " + cm + "." + mm + "cm"); + + // ANT500 elements for crude adjustment + length /= 0.14; + if (int(length) <= 4) { + auto elements = to_string_dec_int((int)length, 1); + text_result_ant500.set(elements + " " + frac_str[((int(length * 10.0) % 10) + 1) / 3] + "elements"); + } else { + text_result_ant500.set("-"); + } + + // Imperial + length = (speed_of_light_fps / (double)field_frequency.value()) * divider; + + auto feet = to_string_dec_int((int)length, 3); + auto inch = to_string_dec_int(int(length * 10.0) % 12, 2); + auto inch_c = to_string_dec_int(int(length * 100.0) % 10, 1); + + text_result_imperial.set(feet + "ft " + inch + "." + inch_c + "in"); +} + +WhipCalcView::WhipCalcView( + NavigationView& nav +) { + + add_children({ { + &text_frequency, + &field_frequency, + &text_type, + &options_type, + &text_result_metric, + &text_result_imperial, + &text_result_ant500, + &button_exit + } }); + + options_type.on_change = [this](size_t, OptionsField::value_t) { + this->update_result(); + }; + options_type.set_selected_index(2); // Quarter wave + + field_frequency.set_value(transmitter_model.tuning_frequency()); + field_frequency.set_step(500000); // 500kHz step + field_frequency.on_change = [this](rf::Frequency) { + this->update_result(); + }; + field_frequency.on_edit = [this, &nav]() { + // TODO: Provide separate modal method/scheme? + auto new_view = nav.push(transmitter_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->update_result(); + this->field_frequency.set_value(f); + }; + }; + + button_exit.on_select = [this, &nav](Button&) { + nav.pop(); + }; +} + +} diff --git a/firmware/application/ui_whipcalc.hpp b/firmware/application/ui_whipcalc.hpp new file mode 100644 index 00000000..ae1053e5 --- /dev/null +++ b/firmware/application/ui_whipcalc.hpp @@ -0,0 +1,98 @@ +/* + * 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 __UI_WHIPCALC_H__ +#define __UI_WHIPCALC_H__ + +#include "ui.hpp" +#include "ui_widget.hpp" +#include "ui_receiver.hpp" +#include "ui_navigation.hpp" +#include "string_format.hpp" + +namespace ui { + +class WhipCalcView : public View { +public: + WhipCalcView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Whip calculator"; }; + +private: + const double speed_of_light_mps = 299792458.0; // m/s + const double speed_of_light_fps = 983571087.90472; // feet/s + + const std::string frac_str[4] = { "", "1/4 ", "1/2 ", "3/4 " }; + + void update_result(); + + Text text_frequency { + { 2 * 8, 2 * 16, 10 * 16, 16 }, + "Frequency:" + }; + FrequencyField field_frequency { + { 13 * 8, 2 * 16 }, + }; + + Text text_type { + { 2 * 8, 3 * 16, 5 * 16, 16 }, + "Type:" + }; + OptionsField options_type { + { 8 * 8, 3 * 16 }, + 12, + { + { "Full wave", 8 }, + { "Half wave", 4 }, + { "Quarter wave", 2 }, + { "3/4 wave", 6 }, + { "1/8 wave", 1 }, + { "3/8 wave", 3 }, + { "5/8 wave", 5 }, + { "7/8 wave", 7 } + } + }; + + Text text_result_metric { + { 3 * 8, 5 * 16, 10 * 16, 16 }, + "-" + }; + Text text_result_imperial { + { 2 * 8, 6 * 16, 10 * 16, 16 }, + "-" + }; + Text text_result_ant500 { + { 2 * 8, 8 * 16, 14 * 16, 16 }, + "-" + }; + + Button button_exit { + { 72, 264, 96, 32 }, + "Exit" + }; +}; + +} /* namespace ui */ + +#endif/*__UI_WHIPCALC__*/ diff --git a/firmware/application/ui_xylos.hpp b/firmware/application/ui_xylos.hpp index 5299b870..171ce85b 100644 --- a/firmware/application/ui_xylos.hpp +++ b/firmware/application/ui_xylos.hpp @@ -159,6 +159,12 @@ private: tx_modes tx_mode = IDLE; + // 93.975 + // 94.1625 + // 94.3125 + // 94.425 + // 95.0625 + // 95.925 const rf::Frequency xylos_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 }; char ccir_message[21]; diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp index 6d4a17d0..c5a29e05 100644 --- a/firmware/common/spi_image.hpp +++ b/firmware/common/spi_image.hpp @@ -110,12 +110,12 @@ constexpr region_t bootstrap { constexpr region_t images { .offset = 0x10000, - .size = 0x40000, + .size = 0x70000, }; constexpr region_t application { .offset = 0x80000, - .size = 0x40000, + .size = 0x50000, }; } /* namespace spi_flash */ diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin index 6311d0d9..466adb64 100644 Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ diff --git a/sdcard/numbers/0.wav b/sdcard/numbers/0.wav new file mode 100644 index 00000000..f905ab83 Binary files /dev/null and b/sdcard/numbers/0.wav differ diff --git a/sdcard/numbers/1.wav b/sdcard/numbers/1.wav new file mode 100644 index 00000000..287cb760 Binary files /dev/null and b/sdcard/numbers/1.wav differ diff --git a/sdcard/numbers/2.wav b/sdcard/numbers/2.wav new file mode 100644 index 00000000..8c4daa1f Binary files /dev/null and b/sdcard/numbers/2.wav differ diff --git a/sdcard/numbers/3.wav b/sdcard/numbers/3.wav new file mode 100644 index 00000000..765fea87 Binary files /dev/null and b/sdcard/numbers/3.wav differ diff --git a/sdcard/numbers/4.wav b/sdcard/numbers/4.wav new file mode 100644 index 00000000..1e3583f8 Binary files /dev/null and b/sdcard/numbers/4.wav differ diff --git a/sdcard/numbers/5.wav b/sdcard/numbers/5.wav new file mode 100644 index 00000000..18ca6bc2 Binary files /dev/null and b/sdcard/numbers/5.wav differ diff --git a/sdcard/numbers/6.wav b/sdcard/numbers/6.wav new file mode 100644 index 00000000..43fe9eab Binary files /dev/null and b/sdcard/numbers/6.wav differ diff --git a/sdcard/numbers/7.wav b/sdcard/numbers/7.wav new file mode 100644 index 00000000..a9225586 Binary files /dev/null and b/sdcard/numbers/7.wav differ diff --git a/sdcard/numbers/8.wav b/sdcard/numbers/8.wav new file mode 100644 index 00000000..455cfed9 Binary files /dev/null and b/sdcard/numbers/8.wav differ diff --git a/sdcard/numbers/9.wav b/sdcard/numbers/9.wav new file mode 100644 index 00000000..f77abc14 Binary files /dev/null and b/sdcard/numbers/9.wav differ diff --git a/sdcard/numbers/announce.wav b/sdcard/numbers/announce.wav new file mode 100644 index 00000000..ad38d282 Binary files /dev/null and b/sdcard/numbers/announce.wav differ diff --git a/sdcard/numbers/anounce.wav b/sdcard/numbers/anounce.wav deleted file mode 100644 index 166d590a..00000000 Binary files a/sdcard/numbers/anounce.wav and /dev/null differ