From 9040e780bc7186bba2775a72f369a1d20879dc2f Mon Sep 17 00:00:00 2001 From: teixeluis Date: Tue, 8 Jun 2021 23:07:37 +0100 Subject: [PATCH] Added beep on radiosonde packet decoding, and volume widget to control its level. --- firmware/application/apps/ui_sonde.cpp | 36 ++++++++++++- firmware/application/apps/ui_sonde.hpp | 34 +++++++++++- firmware/baseband/proc_sonde.cpp | 71 ++++++++++++++++++++++++++ firmware/baseband/proc_sonde.hpp | 36 ++++++++++++- 4 files changed, 171 insertions(+), 6 deletions(-) diff --git a/firmware/application/apps/ui_sonde.cpp b/firmware/application/apps/ui_sonde.cpp index 66e3c576..278037dd 100644 --- a/firmware/application/apps/ui_sonde.cpp +++ b/firmware/application/apps/ui_sonde.cpp @@ -22,6 +22,7 @@ #include "ui_sonde.hpp" #include "baseband_api.hpp" +#include "audio.hpp" #include "portapack.hpp" #include @@ -50,6 +51,8 @@ SondeView::SondeView(NavigationView& nav) { &field_lna, &field_vga, &rssi, + &field_volume, + &check_beep, &check_log, &check_crc, &text_signature, @@ -80,6 +83,10 @@ SondeView::SondeView(NavigationView& nav) { geopos.set_read_only(true); + check_beep.on_select = [this](Checkbox&, bool v) { + beep = v; + }; + check_log.on_select = [this](Checkbox&, bool v) { logging = v; }; @@ -107,16 +114,30 @@ SondeView::SondeView(NavigationView& nav) { gps_info.lon, 999); //set a dummy heading out of range to draw a cross...probably not ideal? }; - + logger = std::make_unique(); if (logger) logger->append(u"sonde.txt"); + + // initialize audio: + field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); + + field_volume.on_change = [this](int32_t v) { + this->on_headphone_volume_changed(v); + }; + + audio::output::start(); + audio::output::speaker_unmute(); + + baseband::set_pitch_rssi(0, true); } SondeView::~SondeView() { + baseband::set_pitch_rssi(0, false); radio::disable(); baseband::shutdown(); + audio::output::stop(); } void SondeView::focus() { @@ -156,15 +177,26 @@ void SondeView::on_packet(const sonde::Packet &packet) } gps_info = packet.get_GPS_data(); + geopos.set_altitude(gps_info.alt); geopos.set_lat(gps_info.lat); geopos.set_lon(gps_info.lon); - if (logger && logging) + if (logger && logging) { logger->on_packet(packet); + } + + if(beep) { + baseband::request_beep(); + } } } +void SondeView::on_headphone_volume_changed(int32_t v) { + const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max; + receiver_model.set_headphone_volume(new_volume); +} + void SondeView::set_target_frequency(const uint32_t new_value) { target_frequency_ = new_value; radio::set_tuning_frequency(tuning_frequency()); diff --git a/firmware/application/apps/ui_sonde.hpp b/firmware/application/apps/ui_sonde.hpp index 65a397f1..73591eb3 100644 --- a/firmware/application/apps/ui_sonde.hpp +++ b/firmware/application/apps/ui_sonde.hpp @@ -55,7 +55,15 @@ class SondeView : public View { public: static constexpr uint32_t sampling_rate = 2457600; static constexpr uint32_t baseband_bandwidth = 1750000; + static constexpr int rssi_sample_range = 256; + static constexpr float rssi_voltage_min = 0.4; + static constexpr float rssi_voltage_max = 2.2; + static constexpr float adc_voltage_max = 3.3; + static constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max; + static constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max; + static constexpr int raw_delta = raw_max - raw_min; + SondeView(NavigationView& nav); ~SondeView(); @@ -68,10 +76,14 @@ private: uint32_t target_frequency_ { 402700000 }; bool logging { false }; bool use_crc { false }; + bool beep { false }; + sonde::GPS_data gps_info { }; sonde::temp_humid temp_humid_info { }; std::string sonde_id { }; + // AudioOutput audio_output { }; + Labels labels { { { 4 * 8, 2 * 16 }, "Type:", Color::light_grey() }, { { 6 * 8, 3 * 16 }, "ID:", Color::light_grey() }, @@ -103,14 +115,29 @@ private: { 21 * 8, 0, 6 * 8, 4 }, }; + NumberField field_volume { + { 28 * 8, 0 * 16 }, + 2, + { 0, 99 }, + 1, + ' ', + }; + + + Checkbox check_beep { + { 22 * 8, 6 * 16 }, + 3, + "Beep" + }; + Checkbox check_log { - { 23 * 8, 6 * 16 }, + { 22 * 8, 8 * 16 }, 3, "Log" }; Checkbox check_crc { - { 23 * 8, 8 * 16 }, + { 22 * 8, 10 * 16 }, 3, "CRC" }; @@ -170,7 +197,10 @@ private: }; void on_packet(const sonde::Packet& packet); + void on_headphone_volume_changed(int32_t v); + void set_target_frequency(const uint32_t new_value); + uint32_t tuning_frequency() const; }; diff --git a/firmware/baseband/proc_sonde.cpp b/firmware/baseband/proc_sonde.cpp index f670dd36..a593bec9 100644 --- a/firmware/baseband/proc_sonde.cpp +++ b/firmware/baseband/proc_sonde.cpp @@ -26,9 +26,14 @@ #include "event_m4.hpp" +#include "audio_output.hpp" + SondeProcessor::SondeProcessor() { + decim_0.configure(taps_11k0_decim_0.taps, 33554432); decim_1.configure(taps_11k0_decim_1.taps, 131072); + + audio_output.configure(false); } void SondeProcessor::execute(const buffer_c8_t& buffer) { @@ -47,10 +52,76 @@ void SondeProcessor::execute(const buffer_c8_t& buffer) { clock_recovery_fsk_4800(mf.get_output()); } } + + if(pitch_rssi_enabled) { + if(beep_playing) { + beep_loop(); + } + else { + silence_loop(); + } + } +} + +void SondeProcessor::on_message(const Message* const msg) { + switch(msg->id) { + case Message::ID::RequestSignal: + if ((*reinterpret_cast(msg)).signal == RequestSignalMessage::Signal::BeepRequest) { + play_beep(); + chThdSleepMilliseconds(100); + stop_beep(); + } + break; + + case Message::ID::PitchRSSIConfigure: + pitch_rssi_config(*reinterpret_cast(msg)); + break; + + default: + break; + } +} + +void SondeProcessor::play_beep() { + beep_playing = true; +} + +void SondeProcessor::stop_beep() { + beep_playing = false; +} + +void SondeProcessor::beep_loop() { + for (size_t i = 0; i < sizeof(audio_buffer.p); i++) { + audio_buffer.p[i] = (sine_table_i8[(tone_phase & 0xFF000000U) >> 24]) * 128; + tone_phase += tone_delta; + } + + audio_output.write(audio_buffer); +} + +void SondeProcessor::silence_loop() { + for (size_t i = 0; i < sizeof(audio_buffer.p); i++) { + audio_buffer.p[i] = 0; + } + + audio_output.write(audio_buffer); +} + +void SondeProcessor::pitch_rssi_config(const PitchRSSIConfigureMessage& message) { + // rtc::RTC datetime; + // rtcGetTime(&RTCD1, &datetime); + + // log_file.write_entry(datetime, "pitch_rssi_config: message.rssi: " + message.rssi); + + pitch_rssi_enabled = message.enabled; + tone_delta = (message.rssi * 10 + 1000) * ((1ULL << 32) / 24000); + + // log_file.write_entry(datetime, "pitch_rssi_config: tone_delta: " + tone_delta); } int main() { EventDispatcher event_dispatcher { std::make_unique() }; event_dispatcher.run(); + return 0; } diff --git a/firmware/baseband/proc_sonde.hpp b/firmware/baseband/proc_sonde.hpp index 92e53580..a7b4574f 100644 --- a/firmware/baseband/proc_sonde.hpp +++ b/firmware/baseband/proc_sonde.hpp @@ -88,6 +88,13 @@ #include "message.hpp" #include "portapack_shared_memory.hpp" +#include "audio_output.hpp" +#include "tone_gen.hpp" +#include "tonesets.hpp" +#include "sine_table_int8.hpp" + +#include "buffer.hpp" + #include #include #include @@ -97,10 +104,27 @@ public: SondeProcessor(); void execute(const buffer_c8_t& buffer) override; - + void on_message(const Message* const msg); private: + static constexpr size_t baseband_fs = 2457600; - + static constexpr size_t beep_iterations = 60; + + std::array audio { }; + + const buffer_s16_t audio_buffer { + (int16_t*) audio.data(), + sizeof(audio) / sizeof(int16_t) + }; + + AudioOutput audio_output { }; + + bool beep_playing { false }; + bool pitch_rssi_enabled { false }; + + uint32_t tone_delta { 0 }; + uint32_t tone_phase { 0 }; + BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; RSSIThread rssi_thread { NORMALPRIO + 10 }; @@ -149,6 +173,14 @@ private: shared_memory.application_queue.push(message); } }; + + void play_beep(); + void stop_beep(); + + void beep_loop(); + void silence_loop(); + + void pitch_rssi_config(const PitchRSSIConfigureMessage& message); }; #endif/*__PROC_ERT_H__*/