diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index ae1ab6e8..a3a3c1b3 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -36,7 +36,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++14 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") +set(USE_CPPOPT "-std=c++17 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) @@ -238,7 +238,6 @@ set(CPPSRC apps/ui_settings.cpp apps/ui_siggen.cpp apps/ui_sonde.cpp - apps/ui_soundboard.cpp apps/ui_sstvtx.cpp # apps/ui_test.cpp apps/ui_tone_search.cpp @@ -247,13 +246,14 @@ set(CPPSRC apps/ui_view_wav.cpp apps/ui_whipcalc.cpp apps/acars_app.cpp - apps/analog_audio_app.cpp apps/ais_app.cpp - apps/tpms_app.cpp - apps/pocsag_app.cpp - apps/ert_app.cpp + apps/analog_audio_app.cpp apps/capture_app.cpp + apps/ert_app.cpp + apps/pocsag_app.cpp apps/replay_app.cpp + apps/soundboard_app.cpp + apps/tpms_app.cpp protocols/aprs.cpp protocols/ax25.cpp protocols/bht.cpp diff --git a/firmware/application/apps/capture_app.cpp b/firmware/application/apps/capture_app.cpp index 9b4feecd..3e9bf862 100644 --- a/firmware/application/apps/capture_app.cpp +++ b/firmware/application/apps/capture_app.cpp @@ -27,9 +27,6 @@ #include "portapack.hpp" using namespace portapack; -#include "portapack_persistent_memory.hpp" -using namespace portapack; - namespace ui { CaptureAppView::CaptureAppView(NavigationView& nav) { @@ -49,20 +46,20 @@ CaptureAppView::CaptureAppView(NavigationView& nav) { &waterfall, }); - field_frequency.set_value(target_frequency()); + field_frequency.set_value(receiver_model.tuning_frequency()); field_frequency.set_step(receiver_model.frequency_step()); field_frequency.on_change = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); + this->on_tuning_frequency_changed(f); }; field_frequency.on_edit = [this, &nav]() { // TODO: Provide separate modal method/scheme? - auto new_view = nav.push(this->target_frequency()); + auto new_view = nav.push(receiver_model.tuning_frequency()); new_view->on_changed = [this](rf::Frequency f) { - this->on_target_frequency_changed(f); + this->on_tuning_frequency_changed(f); this->field_frequency.set_value(f); }; }; - + field_frequency_step.set_by_value(receiver_model.frequency_step()); field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) { receiver_model.set_frequency_step(v); @@ -70,26 +67,19 @@ CaptureAppView::CaptureAppView(NavigationView& nav) { }; option_bandwidth.on_change = [this](size_t, uint32_t base_rate) { - sampling_rate = 8 * base_rate; + sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side waterfall.on_hide(); - set_target_frequency(target_frequency()); record_view.set_sampling_rate(sampling_rate); - radio::set_baseband_rate(sampling_rate); + receiver_model.set_sampling_rate(sampling_rate); waterfall.on_show(); }; - - radio::enable({ - tuning_frequency(), - sampling_rate, - baseband_bandwidth, - rf::Direction::Receive, - receiver_model.rf_amp(), - static_cast(receiver_model.lna()), - static_cast(receiver_model.vga()), - }); option_bandwidth.set_selected_index(7); // 500k + + receiver_model.set_modulation(ReceiverModel::Mode::Capture); + receiver_model.set_baseband_bandwidth(baseband_bandwidth); + receiver_model.enable(); record_view.on_error = [&nav](std::string message) { nav.display_modal("Error", message); @@ -97,8 +87,7 @@ CaptureAppView::CaptureAppView(NavigationView& nav) { } CaptureAppView::~CaptureAppView() { - radio::disable(); - + receiver_model.disable(); baseband::shutdown(); } @@ -120,21 +109,8 @@ void CaptureAppView::focus() { record_view.focus(); } -void CaptureAppView::on_target_frequency_changed(rf::Frequency f) { - set_target_frequency(f); -} - -void CaptureAppView::set_target_frequency(const rf::Frequency new_value) { - persistent_memory::set_tuned_frequency(new_value);; - radio::set_tuning_frequency(tuning_frequency()); -} - -rf::Frequency CaptureAppView::target_frequency() const { - return persistent_memory::tuned_frequency(); -} - -rf::Frequency CaptureAppView::tuning_frequency() const { - return target_frequency() - (sampling_rate / 4); +void CaptureAppView::on_tuning_frequency_changed(rf::Frequency f) { + receiver_model.set_tuning_frequency(f); } } /* namespace ui */ diff --git a/firmware/application/apps/capture_app.hpp b/firmware/application/apps/capture_app.hpp index a91289c3..944b048c 100644 --- a/firmware/application/apps/capture_app.hpp +++ b/firmware/application/apps/capture_app.hpp @@ -29,9 +29,6 @@ #include "ui_record_view.hpp" #include "ui_spectrum.hpp" -#include -#include - namespace ui { class CaptureAppView : public View { @@ -53,12 +50,7 @@ private: uint32_t sampling_rate = 0; static constexpr uint32_t baseband_bandwidth = 2500000; - void on_target_frequency_changed(rf::Frequency f); - - rf::Frequency target_frequency() const; - void set_target_frequency(const rf::Frequency new_value); - - rf::Frequency tuning_frequency() const; + void on_tuning_frequency_changed(rf::Frequency f); Labels labels { { { 0 * 8, 1 * 16 }, "Rate:", Color::light_grey() }, diff --git a/firmware/application/apps/soundboard_app.cpp b/firmware/application/apps/soundboard_app.cpp new file mode 100644 index 00000000..3c267a4b --- /dev/null +++ b/firmware/application/apps/soundboard_app.cpp @@ -0,0 +1,250 @@ +/* + * 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. + */ + +// To prepare samples: for f in ./*.wav; do sox "$f" -r 48000 -c 1 -b8 --norm "conv/$f"; done + +#include "soundboard_app.hpp" +#include "string_format.hpp" +#include "tonesets.hpp" + +using namespace tonekey; +using namespace portapack; + +namespace ui { + +bool SoundBoardView::is_active() const { + return (bool)replay_thread; +} + +void SoundBoardView::stop() { + if (is_active()) + replay_thread.reset(); + + transmitter_model.disable(); + tx_view.set_transmitting(false); + + //button_play.set_bitmap(&bitmap_play); + ready_signal = false; +} + +void SoundBoardView::handle_replay_thread_done(const uint32_t return_code) { + stop(); + progressbar.set_value(0); + + if (return_code == ReplayThread::END_OF_FILE) { + if (check_random.value()) { + lfsr_v = lfsr_iterate(lfsr_v); + playing_id = lfsr_v % file_list.size(); + menu_view.set_highlighted(playing_id); + start_tx(playing_id); + } else if (check_loop.value()) { + start_tx(playing_id); + } + } else if (return_code == ReplayThread::READ_ERROR) { + file_error(); + } +} + +void SoundBoardView::set_ready() { + ready_signal = true; +} + +void SoundBoardView::focus() { + menu_view.focus(); +} + +void SoundBoardView::file_error() { + nav_.display_modal("Error", "File read error."); +} + +void SoundBoardView::start_tx(const uint32_t id) { + auto reader = std::make_unique(); + uint32_t tone_key_index = options_tone_key.selected_index(); + uint32_t sample_rate; + + stop(); + + if (!reader->open(u"/WAV/" + file_list[id].native())) { + file_error(); + return; + } + + playing_id = id; + + progressbar.set_max(reader->sample_count()); + + //button_play.set_bitmap(&bitmap_stop); + + sample_rate = reader->sample_rate(); + + replay_thread = std::make_unique( + std::move(reader), + read_size, buffer_count, + &ready_signal, + [](uint32_t return_code) { + ReplayThreadDoneMessage message { return_code }; + EventDispatcher::send_message(message); + } + ); + + baseband::set_audiotx_config( + 1536000 / 20, // Update vu-meter at 20Hz + transmitter_model.channel_bandwidth(), + 0, // Gain is unused + TONES_F2D(tone_key_frequency(tone_key_index), 1536000) + ); + baseband::set_sample_rate(sample_rate); + + transmitter_model.set_sampling_rate(1536000); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + tx_view.set_transmitting(true); +} + +/*void SoundBoardView::show_infos() { + if (!reader->open(file_list[menu_view.highlighted_index()])) + return; + + text_duration.set(to_string_time_ms(reader->ms_duration())); + text_title.set(reader->title().substr(0, 15)); +}*/ + +void SoundBoardView::on_tx_progress(const uint32_t progress) { + progressbar.set_value(progress); +} + +void SoundBoardView::on_select_entry() { + tx_view.focus(); +} + +void SoundBoardView::refresh_list() { + auto reader = std::make_unique(); + + file_list.clear(); + + // List directories and files, put directories up top + for (const auto& entry : std::filesystem::directory_iterator(u"WAV", u"*")) { + if (std::filesystem::is_regular_file(entry.status())) { + if (entry.path().string().length()) { + + auto entry_extension = entry.path().extension().string(); + + for (auto &c: entry_extension) + c = toupper(c); + + if (entry_extension == ".WAV") { + + if (reader->open(u"/WAV/" + entry.path().native())) { + if ((reader->channels() == 1) && (reader->bits_per_sample() == 8)) { + //sounds[c].ms_duration = reader->ms_duration(); + //sounds[c].path = u"WAV/" + entry.path().native(); + file_list.push_back(entry.path()); + if (file_list.size() == 100) + break; + } + } + } + } + } + } + + if (!file_list.size()) { + // Hide widgets, show warning + menu_view.hidden(true); + text_empty.hidden(false); + set_dirty(); + } else { + // Hide warning, show widgets + menu_view.hidden(false); + text_empty.hidden(true); + set_dirty(); + + menu_view.clear(); + + for (size_t n = 0; n < file_list.size(); n++) { + menu_view.add_item({ + file_list[n].string().substr(0, 30), + ui::Color::white(), + nullptr, + [this](){ + on_select_entry(); + } + }); + } + + menu_view.set_highlighted(0); // Refresh + } +} + +SoundBoardView::SoundBoardView( + NavigationView& nav +) : nav_ (nav) +{ + baseband::run_image(portapack::spi_flash::image_tag_audio_tx); + + add_children({ + &labels, + &menu_view, + &text_empty, + &options_tone_key, + &text_title, + &text_duration, + &progressbar, + &check_loop, + &check_random, + &tx_view + }); + + refresh_list(); + + text_title.set(to_string_dec_uint(file_list.size())); + + tone_keys_populate(options_tone_key); + options_tone_key.set_selected_index(0); + + check_loop.set_value(false); + check_random.set_value(false); + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + start_tx(menu_view.highlighted_index()); + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + stop(); + }; +} + +SoundBoardView::~SoundBoardView() { + transmitter_model.disable(); + baseband::shutdown(); +} + +} diff --git a/firmware/application/apps/soundboard_app.hpp b/firmware/application/apps/soundboard_app.hpp new file mode 100644 index 00000000..2bd64b60 --- /dev/null +++ b/firmware/application/apps/soundboard_app.hpp @@ -0,0 +1,161 @@ +/* + * 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_SOUNDBOARD_H__ +#define __UI_SOUNDBOARD_H__ + +#include "ui_widget.hpp" +#include "ui_transmitter.hpp" +#include "replay_thread.hpp" +#include "baseband_api.hpp" +#include "lfsr_random.hpp" +#include "io_wave.hpp" +#include "tone_key.hpp" + +namespace ui { + +class SoundBoardView : public View { +public: + SoundBoardView(NavigationView& nav); + ~SoundBoardView(); + + SoundBoardView(const SoundBoardView&) = delete; + SoundBoardView(SoundBoardView&&) = delete; + SoundBoardView& operator=(const SoundBoardView&) = delete; + SoundBoardView& operator=(SoundBoardView&&) = delete; + + void focus() override; + + std::string title() const override { return "Soundboard"; }; + +private: + NavigationView& nav_; + + enum tx_modes { + NORMAL = 0, + RANDOM + }; + + tx_modes tx_mode = NORMAL; + + uint32_t playing_id { }; + + std::vector file_list { }; + + const size_t read_size { 2048 }; // Less ? + const size_t buffer_count { 3 }; + std::unique_ptr replay_thread { }; + bool ready_signal { false }; + lfsr_word_t lfsr_v = 1; + + //void show_infos(); + void start_tx(const uint32_t id); + //void on_ctcss_changed(uint32_t v); + void stop(); + bool is_active() const; + void set_ready(); + void handle_replay_thread_done(const uint32_t return_code); + void file_error(); + void on_tx_progress(const uint32_t progress); + void refresh_list(); + void on_select_entry(); + + Labels labels { + { { 0, 20 * 8 + 4 }, "Title:", Color::light_grey() }, + { { 0, 23 * 8 }, "Key:", Color::light_grey() } + }; + + MenuView menu_view { + { 0, 2 * 8, 240, 20 * 8 }, + true + }; + Text text_empty { + { 7 * 8, 12 * 8, 16 * 8, 16 }, + "Empty directory !", + }; + + Text text_title { + { 6 * 8, 20 * 8 + 4, 15 * 8, 16 } + }; + + Text text_duration { + { 22 * 8, 20 * 8 + 4, 6 * 8, 16 } + }; + + OptionsField options_tone_key { + { 4 * 8, 23 * 8 }, + 18, + { } + }; + + Checkbox check_loop { + { 8, 25 * 8 + 4 }, + 4, + "Loop" + }; + + Checkbox check_random { + { 10 * 8, 25 * 8 + 4 }, + 6, + "Random" + }; + + ProgressBar progressbar { + { 0 * 8, 30 * 8 - 4, 30 * 8, 16 } + }; + + TransmitterView tx_view { + 16 * 16, + 5000, + 12 + }; + + MessageHandlerRegistration message_handler_replay_thread_error { + Message::ID::ReplayThreadDone, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->handle_replay_thread_done(message.return_code); + } + }; + + MessageHandlerRegistration message_handler_fifo_signal { + Message::ID::RequestSignal, + [this](const Message* const p) { + const auto message = static_cast(p); + if (message->signal == RequestSignalMessage::Signal::FillRequest) { + this->set_ready(); + } + } + }; + + MessageHandlerRegistration message_handler_tx_progress { + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress); + } + }; +}; + +} /* namespace ui */ + +#endif/*__UI_SOUNDBOARD_H__*/ diff --git a/firmware/application/clock_manager.cpp b/firmware/application/clock_manager.cpp index b808c1cd..fcddba0f 100644 --- a/firmware/application/clock_manager.cpp +++ b/firmware/application/clock_manager.cpp @@ -247,10 +247,10 @@ constexpr ClockControls si5351_clock_control_clkin { si5351_clock_control_common[4] | si5351_clock_control_ms_src_clkin, si5351_clock_control_common[5] | si5351_clock_control_ms_src_clkin, si5351_clock_control_common[6] | si5351_clock_control_ms_src_clkin, - si5351_clock_control_common[7] | si5351_clock_control_ms_src_xtal, + si5351_clock_control_common[7] | si5351_clock_control_ms_src_clkin, }; -void ClockManager::init(const bool use_clkin) { +void ClockManager::init() { /* Must be sure to run the M4 core from IRC when messing with the signal * generator that sources the GP_CLKIN signal that drives the micro- * controller's PLL1 input. @@ -269,11 +269,23 @@ void ClockManager::init(const bool use_clkin) { clock_generator.enable_fanout(); clock_generator.set_pll_input_sources(si5351_pll_input_sources); - //const bool use_clkin = false; + const auto clkin_present = !clock_generator.clkin_loss_of_signal(); + auto clkin_valid = false; + + if( clkin_present ) { + // Measure Si5351B CLKIN frequency against LPC43xx IRC oscillator + set_gp_clkin_to_clkin_direct(); + start_frequency_monitor_measurement(cgu::CLK_SEL::GP_CLKIN); + wait_For_frequency_monitor_measurement_done(); + const auto clkin_frequency = get_frequency_monitor_measurement_in_hertz(); + + // CLKIN is required to be 10MHz. FREQ_MON measurement is accurate to 1.5% + // due to LPC43xx IRC oscillator precision. + clkin_valid = (clkin_frequency >= 9850000) && (clkin_frequency <= 10150000); + } + clock_generator.set_clock_control( - use_clkin ? - si5351_clock_control_clkin - : si5351_clock_control_xtal + clkin_valid ? si5351_clock_control_clkin : si5351_clock_control_xtal ); clock_generator.write(si5351_pll_a_xtal_reg); @@ -422,6 +434,38 @@ void ClockManager::disable_gp_clkin_source() { clock_generator.disable_output(clock_generator_output_mcu_clkin); } +void ClockManager::set_gp_clkin_to_clkin_direct() { + clock_generator.set_clock_control( + clock_generator_output_mcu_clkin, + { ClockControl::CLK_IDRV_2mA | ClockControl::CLK_SRC_CLKIN | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_On } + ); + enable_gp_clkin_source(); +} + +void ClockManager::start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel) { + // Measure a clock input for 480 cycles of the LPC43xx IRC. + LPC_CGU->FREQ_MON = LPC_CGU_FREQ_MON_Type { + .RCNT = 480, + .FCNT = 0, + .MEAS = 0, + .CLK_SEL = toUType(clk_sel), + .RESERVED0 = 0 + }; + LPC_CGU->FREQ_MON.MEAS = 1; +} + +void ClockManager::wait_For_frequency_monitor_measurement_done() { + // FREQ_MON mechanism fails to finish if there's no clock present on selected input?! + while(LPC_CGU->FREQ_MON.MEAS == 1); +} + +uint32_t ClockManager::get_frequency_monitor_measurement_in_hertz() { + // Measurement is only as accurate as the LPC43xx IRC oscillator, + // which is +/- 1.5%. Measurement is for 480 IRC clcocks. Scale + // the cycle count to get a value in Hertz. + return LPC_CGU->FREQ_MON.FCNT * 25000; +} + void ClockManager::enable_xtal_oscillator() { LPC_CGU->XTAL_OSC_CTRL.BYPASS = 0; LPC_CGU->XTAL_OSC_CTRL.ENABLE = 1; diff --git a/firmware/application/clock_manager.hpp b/firmware/application/clock_manager.hpp index 172dbcb7..5d4799ee 100644 --- a/firmware/application/clock_manager.hpp +++ b/firmware/application/clock_manager.hpp @@ -42,7 +42,7 @@ public: { } - void init(const bool use_clkin); + void init(); void shutdown(); void run_from_irc(); @@ -66,6 +66,8 @@ public: void set_reference_ppb(const int32_t ppb); + uint32_t get_frequency_monitor_measurement_in_hertz(); + private: I2C& i2c0; si5351::Si5351& clock_generator; @@ -75,6 +77,10 @@ private: void enable_gp_clkin_source(); void disable_gp_clkin_source(); + void set_gp_clkin_to_clkin_direct(); + + void start_frequency_monitor_measurement(const cgu::CLK_SEL clk_sel); + void wait_For_frequency_monitor_measurement_done(); void enable_xtal_oscillator(); void disable_xtal_oscillator(); diff --git a/firmware/application/hw/si5351.hpp b/firmware/application/hw/si5351.hpp index e4c622b6..c7508ba9 100644 --- a/firmware/application/hw/si5351.hpp +++ b/firmware/application/hw/si5351.hpp @@ -306,6 +306,10 @@ public: while(device_status() & 0x80); } + bool clkin_loss_of_signal() { + return (device_status() >> 4) & 1; + } + void enable_fanout() { write_register(Register::FanoutEnable, 0b11010000); } @@ -369,6 +373,11 @@ public: update_all_clock_control(); } + void set_clock_control(const size_t n, const ClockControl::Type clock_control) { + _clock_control[n] = clock_control; + write_register(Register::CLKControl_Base + n, _clock_control[n]); + } + void enable_clock(const size_t n) { _clock_control[n] &= ~ClockControl::CLK_PDN_Mask; write_register(Register::CLKControl_Base + n, _clock_control[n]); diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index eaaef113..04dac6df 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -104,7 +104,7 @@ void poll_ext_clock() { if (clkin_status != prev_clkin_status) { StatusRefreshMessage message { }; EventDispatcher::send_message(message); - clock_manager.init(clkin_status); + clock_manager.init(); } prev_clkin_status = clkin_status; @@ -296,7 +296,7 @@ bool init() { led_rx.setup(); led_tx.setup(); - clock_manager.init(false); + clock_manager.init(); clock_manager.set_reference_ppb(persistent_memory::correction_ppb()); clock_manager.run_at_full_speed(); diff --git a/firmware/application/receiver_model.cpp b/firmware/application/receiver_model.cpp index c8b49967..782ab94a 100644 --- a/firmware/application/receiver_model.cpp +++ b/firmware/application/receiver_model.cpp @@ -275,6 +275,7 @@ void ReceiverModel::update_modulation() { break; case Mode::SpectrumAnalysis: + case Mode::Capture: break; } } diff --git a/firmware/application/receiver_model.hpp b/firmware/application/receiver_model.hpp index 139f0d67..5b2a82f3 100644 --- a/firmware/application/receiver_model.hpp +++ b/firmware/application/receiver_model.hpp @@ -37,6 +37,7 @@ public: NarrowbandFMAudio = 1, WidebandFMAudio = 2, SpectrumAnalysis = 3, + Capture = 4 }; rf::Frequency tuning_frequency() const; diff --git a/firmware/application/tone_key.cpp b/firmware/application/tone_key.cpp index b53c0749..a208c96e 100644 --- a/firmware/application/tone_key.cpp +++ b/firmware/application/tone_key.cpp @@ -78,8 +78,8 @@ const tone_key_t tone_keys = { { "38 --", 250.300 }, { "50 0Z", 254.100 }, { "Axient 28kHz", 28000.0 }, - { "Sennheiser 32.768k", 32768.0 }, - { "Sennheiser 32.000k", 32000.0 }, + { "Senn. 32.768k", 32768.0 }, + { "Senn. 32.000k", 32000.0 }, { "Sony 32.382k", 32382.0 }, { "Shure 19kHz", 19000.0 } }; diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 6ebb10aa..34e765dc 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -58,7 +58,6 @@ #include "ui_settings.hpp" #include "ui_siggen.hpp" #include "ui_sonde.hpp" -#include "ui_soundboard.hpp" #include "ui_sstvtx.hpp" //#include "ui_test.hpp" #include "ui_tone_search.hpp" @@ -73,6 +72,7 @@ #include "ert_app.hpp" #include "pocsag_app.hpp" #include "replay_app.hpp" +#include "soundboard_app.hpp" #include "tpms_app.hpp" #include "core_control.hpp" @@ -166,6 +166,8 @@ void SystemStatusView::refresh() { image_clock_status.set_bitmap(&bitmap_icon_clk_int); button_bias_tee.set_foreground(ui::Color::light_grey()); } + + set_dirty(); } void SystemStatusView::set_back_enabled(bool new_value) { @@ -186,9 +188,7 @@ void SystemStatusView::on_stealth() { portapack::persistent_memory::set_stealth_mode(mode); - button_stealth.set_foreground(mode ? ui::Color::green() : ui::Color::light_grey()); - - button_stealth.set_dirty(); + button_stealth.set_foreground(mode ? Color::green() : Color::light_grey()); } void SystemStatusView::on_bias_tee() { @@ -196,6 +196,7 @@ void SystemStatusView::on_bias_tee() { nav_.display_modal("Bias voltage", "Enable DC voltage on\nantenna connector ?", YESNO, [this](bool v) { if (v) { portapack::set_antenna_bias(true); + //radio::set_antenna_bias(true); receiver_model.set_antenna_bias(); transmitter_model.set_antenna_bias(); refresh(); @@ -203,6 +204,7 @@ void SystemStatusView::on_bias_tee() { }); } else { portapack::set_antenna_bias(false); + //radio::set_antenna_bias(false); receiver_model.set_antenna_bias(); transmitter_model.set_antenna_bias(); refresh(); @@ -371,7 +373,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push(); } }, { "Microphone", ui::Color::green(), &bitmap_icon_microphone, [&nav](){ nav.push(); } }, { "Morse code", ui::Color::green(), &bitmap_icon_morse, [&nav](){ nav.push(); } }, - { "NTTWorks burger pager", ui::Color::yellow(), &bitmap_icon_burger, [&nav](){ nav.push(); } }, + { "Burger pagers", ui::Color::yellow(), &bitmap_icon_burger, [&nav](){ nav.push(); } }, //{ "Nuoptix DTMF timecode", ui::Color::green(), &bitmap_icon_nuoptix, [&nav](){ nav.push(); } }, { "OOK encoders", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push(); } }, @@ -478,10 +480,12 @@ SystemView::SystemView( navigation_view.push(); } else {*/ + navigation_view.push(); + if (portapack::persistent_memory::config_splash()) navigation_view.push(); - else - navigation_view.push(); + //else + // navigation_view.push(); //} } @@ -504,7 +508,6 @@ BMPView::BMPView(NavigationView& nav) { button_done.on_select = [this, &nav](Button&){ nav.pop(); - nav.push(); }; } diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 94311c06..179e0b18 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -36,7 +36,7 @@ set(USE_OPT "-O3 -g -falign-functions=16 -fno-math-errno --specs=nano.specs") set(USE_COPT "-std=gnu99") # C++ specific options here (added to USE_OPT). -set(USE_CPPOPT "-std=c++14 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") +set(USE_CPPOPT "-std=c++17 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) diff --git a/firmware/baseband/proc_audiotx.cpp b/firmware/baseband/proc_audiotx.cpp index f18a545a..b87cfc78 100644 --- a/firmware/baseband/proc_audiotx.cpp +++ b/firmware/baseband/proc_audiotx.cpp @@ -23,7 +23,6 @@ #include "proc_audiotx.hpp" #include "portapack_shared_memory.hpp" #include "sine_table_int8.hpp" -//#include "audio_output.hpp" #include "event_m4.hpp" #include @@ -32,17 +31,18 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer){ if (!configured) return; - if( stream ) { - const size_t bytes_to_read = (buffer.count / 32); // /32 (oversampling) should be == 64 - bytes_read += stream->read(audio_buffer.p, bytes_to_read); - } - - // Fill and "stretch" + // Zero-order hold (poop) for (size_t i = 0; i < buffer.count; i++) { - if (!(i & 31)) - audio_sample = audio_buffer.p[i >> 5] - 0x80; + resample_acc += resample_inc; + if (resample_acc >= 0x10000) { + resample_acc -= 0x10000; + if (stream) { + stream->read(&audio_sample, 1); + bytes_read++; + } + } - sample = tone_gen.process(audio_sample); + sample = tone_gen.process(audio_sample - 0x80); // FM delta = sample * fm_delta; @@ -50,22 +50,20 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer){ phase += delta; sphase = phase + (64 << 24); - re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]); - im = (sine_table_i8[(phase & 0xFF000000U) >> 24]); + re = sine_table_i8[(sphase & 0xFF000000U) >> 24]; + im = sine_table_i8[(phase & 0xFF000000U) >> 24]; buffer.p[i] = { (int8_t)re, (int8_t)im }; } - spectrum_samples += buffer.count; - if( spectrum_samples >= spectrum_interval_samples ) { - spectrum_samples -= spectrum_interval_samples; + progress_samples += buffer.count; + if (progress_samples >= progress_interval_samples) { + progress_samples -= progress_interval_samples; txprogress_message.progress = bytes_read; // Inform UI about progress txprogress_message.done = false; shared_memory.application_queue.push(txprogress_message); } - - //AudioOutput::fill_audio_buffer(preview_audio_buffer, true); } void AudioTXProcessor::on_message(const Message* const message) { @@ -96,12 +94,8 @@ void AudioTXProcessor::on_message(const Message* const message) { void AudioTXProcessor::audio_config(const AudioTXConfigMessage& message) { fm_delta = message.deviation_hz * (0xFFFFFFULL / baseband_fs); tone_gen.configure(message.tone_key_delta, message.tone_key_mix_weight); -} - -void AudioTXProcessor::samplerate_config(const SamplerateConfigMessage& message) { - baseband_fs = message.sample_rate; - baseband_thread.set_sampling_rate(baseband_fs); - spectrum_interval_samples = baseband_fs / 20; + progress_interval_samples = message.divider; + resample_acc = 0; } void AudioTXProcessor::replay_config(const ReplayConfigMessage& message) { @@ -116,6 +110,10 @@ void AudioTXProcessor::replay_config(const ReplayConfigMessage& message) { } } +void AudioTXProcessor::samplerate_config(const SamplerateConfigMessage& message) { + resample_inc = (((uint64_t)message.sample_rate) << 16) / baseband_fs; // 16.16 fixed point message.sample_rate +} + int main() { EventDispatcher event_dispatcher { std::make_unique() }; event_dispatcher.run(); diff --git a/firmware/baseband/proc_audiotx.hpp b/firmware/baseband/proc_audiotx.hpp index b15f8524..3f1bace7 100644 --- a/firmware/baseband/proc_audiotx.hpp +++ b/firmware/baseband/proc_audiotx.hpp @@ -35,35 +35,22 @@ public: void on_message(const Message* const msg) override; private: - size_t baseband_fs = 0; + static constexpr size_t baseband_fs = 1536000; BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - std::array audio { }; - const buffer_t audio_buffer { - audio.data(), - audio.size(), - baseband_fs / 8 - }; - std::unique_ptr stream { }; ToneGen tone_gen { }; + uint32_t resample_inc { }, resample_acc { }; uint32_t fm_delta { 0 }; uint32_t phase { 0 }, sphase { 0 }; - int8_t out_sample { }; - int32_t sample { 0 }, audio_sample { 0 }, delta { }; + uint8_t audio_sample { }; + int32_t sample { 0 }, delta { }; int8_t re { 0 }, im { 0 }; - size_t spectrum_interval_samples = 0; - size_t spectrum_samples = 0; - - //int16_t audio_data[64]; - /*const buffer_s16_t preview_audio_buffer { - audio_data, - sizeof(int16_t)*64 - };*/ + size_t progress_interval_samples, progress_samples = 0; bool configured { false }; uint32_t bytes_read { 0 }; diff --git a/firmware/bootstrap/CMakeLists.txt b/firmware/bootstrap/CMakeLists.txt index 0c5e4a29..1c3fde75 100644 --- a/firmware/bootstrap/CMakeLists.txt +++ b/firmware/bootstrap/CMakeLists.txt @@ -35,7 +35,7 @@ set(USE_OPT "-Os -g -falign-functions=16 -fno-math-errno --specs=nano.specs") set(USE_COPT "-std=gnu99") # C++ specific options here (added to USE_OPT). -set(USE_CPPOPT "-std=c++14 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") +set(USE_CPPOPT "-std=c++17 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") # Enable this if you want the linker to remove unused code and data set(USE_LINK_GC yes) diff --git a/firmware/chibios/os/ports/common/ARMCMx/CMSIS/include/core_cmFunc.h b/firmware/chibios/os/ports/common/ARMCMx/CMSIS/include/core_cmFunc.h index 585d2bb5..a260cf6b 100755 --- a/firmware/chibios/os/ports/common/ARMCMx/CMSIS/include/core_cmFunc.h +++ b/firmware/chibios/os/ports/common/ARMCMx/CMSIS/include/core_cmFunc.h @@ -307,6 +307,10 @@ __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) #elif defined ( __GNUC__ ) /*------------------ GNU Compiler ---------------------*/ /* GNU gcc specific functions */ +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static inline +#endif + /** \brief Enable IRQ Interrupts This function enables IRQ interrupts by clearing the I-bit in the CPSR. @@ -403,15 +407,15 @@ __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_xPSR(void) /** \brief Get Process Stack Pointer - This function returns the current value of the Process Stack Pointer (PSP). + \details Returns the current value of the Process Stack Pointer (PSP). \return PSP Register value */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) +__STATIC_FORCEINLINE uint32_t __get_PSP(void) { - register uint32_t result; + uint32_t result; - __ASM volatile ("MRS %0, psp\n" : "=r" (result) ); + __ASM volatile ("MRS %0, psp" : "=r" (result) ); return(result); } @@ -430,19 +434,18 @@ __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOf /** \brief Get Main Stack Pointer - This function returns the current value of the Main Stack Pointer (MSP). + \details Returns the current value of the Main Stack Pointer (MSP). \return MSP Register value */ -__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) +__STATIC_FORCEINLINE uint32_t __get_MSP(void) { - register uint32_t result; + uint32_t result; - __ASM volatile ("MRS %0, msp\n" : "=r" (result) ); + __ASM volatile ("MRS %0, msp" : "=r" (result) ); return(result); } - /** \brief Set Main Stack Pointer This function assigns the given value to the Main Stack Pointer (MSP). diff --git a/firmware/common/lpc43xx_cpp.hpp b/firmware/common/lpc43xx_cpp.hpp index 92ca64ec..b4c124cd 100644 --- a/firmware/common/lpc43xx_cpp.hpp +++ b/firmware/common/lpc43xx_cpp.hpp @@ -108,7 +108,7 @@ inline void clear() { namespace cgu { -enum class CLK_SEL { +enum class CLK_SEL : uint8_t { RTC_32KHZ = 0x00, IRC = 0x01, ENET_RX_CLK = 0x02, diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp index 49b40bd1..db3bdda4 100644 --- a/firmware/common/ui_widget.hpp +++ b/firmware/common/ui_widget.hpp @@ -37,6 +37,7 @@ #include #include #include +#include namespace ui { diff --git a/firmware/graphics/splash.bmp b/firmware/graphics/splash.bmp deleted file mode 100755 index f7138934..00000000 Binary files a/firmware/graphics/splash.bmp and /dev/null differ