Looking glass beep (#2036)

* first draft of looking beep
* fixed beep squelch range in percent
* took out steps 
* gui adjustements
* uniformize calculation and beep squelch in db
* uniformisation, fix 24/48 error
This commit is contained in:
gullradriel 2024-03-24 22:32:13 +01:00 committed by GitHub
parent 6177b08632
commit c078bac0e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 121 additions and 12 deletions

View File

@ -35,6 +35,11 @@ using portapack::memory::map::backup_ram;
namespace ui { namespace ui {
// Function to map the value from one range to another
int32_t LevelView::map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh) {
return toLow + (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow);
}
void LevelView::m4_manage_stat_update() { void LevelView::m4_manage_stat_update() {
if (audio_mode) { if (audio_mode) {
if (radio_mode == WFM_MODULATION || radio_mode == SPEC_MODULATION) { if (radio_mode == WFM_MODULATION || radio_mode == SPEC_MODULATION) {
@ -192,7 +197,7 @@ void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
} }
if (beep && statistics.max_db > beep_squelch) { if (beep && statistics.max_db > beep_squelch) {
baseband::request_audio_beep(((132 + statistics.max_db) * 2000) / 120, 24000, 250); baseband::request_audio_beep(map(statistics.max_db, -100, 20, 400, 2600), 24000, 150);
} }
// refresh sat // refresh sat

View File

@ -56,6 +56,7 @@ class LevelView : public View {
RxRadioState radio_state_{}; RxRadioState radio_state_{};
int32_t map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh);
size_t change_mode(freqman_index_t mod_type); size_t change_mode(freqman_index_t mod_type);
void on_statistics_update(const ChannelStatistics& statistics); void on_statistics_update(const ChannelStatistics& statistics);
void set_display_freq(int64_t freq); void set_display_freq(int64_t freq);
@ -128,7 +129,7 @@ class LevelView : public View {
NumberField field_beep_squelch{ NumberField field_beep_squelch{
{25 * 8, 3 * 16 + 4}, {25 * 8, 3 * 16 + 4},
4, 4,
{-120, 12}, {-100, 20},
1, 1,
' ', ' ',
}; };

View File

@ -25,6 +25,7 @@
#include "convert.hpp" #include "convert.hpp"
#include "file_reader.hpp" #include "file_reader.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "audio.hpp"
using namespace portapack; using namespace portapack;
@ -34,11 +35,39 @@ void GlassView::focus() {
} }
GlassView::~GlassView() { GlassView::~GlassView() {
audio::output::stop();
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }
// Function to map the value from one range to another
int32_t GlassView::map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh) {
return toLow + (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow);
}
void GlassView::update_display_beep() {
if (beep_enabled) {
button_beep_squelch.set_style(&Styles::green);
// <bip:-XXXdb>
button_beep_squelch.set_text("[bip>" + to_string_dec_int(beep_squelch, 4) + "db]");
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
} else {
button_beep_squelch.set_style(&Styles::white);
button_beep_squelch.set_text("[ beep OFF [");
}
}
void GlassView::manage_beep_audio() {
if (beep_enabled) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
} else {
baseband::request_beep_stop();
audio::output::stop();
}
}
void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power) { void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power) {
if (mode == LOOKING_GLASS_SINGLEPASS) { if (mode == LOOKING_GLASS_SINGLEPASS) {
// <20MHz spectrum mode // <20MHz spectrum mode
@ -173,6 +202,8 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
// we actually need SCREEN_W (240) of those bins // we actually need SCREEN_W (240) of those bins
for (uint8_t bin = 0; bin < bin_length; bin++) { for (uint8_t bin = 0; bin < bin_length; bin++) {
get_max_power(spectrum, bin, max_power); get_max_power(spectrum, bin, max_power);
if (max_power > range_max_power)
range_max_power = max_power;
// process dc spike if enable // process dc spike if enable
if (bin == 119) { if (bin == 119) {
uint8_t next_max_power = 0; uint8_t next_max_power = 0;
@ -184,14 +215,21 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
} }
} }
// process actual bin // process actual bin
if (process_bins(&max_power) == true) if (process_bins(&max_power)) {
int8_t power = map(range_max_power, 0, 255, -100, 20);
if (power >= beep_squelch) {
baseband::request_audio_beep(map(range_max_power, 0, 256, 400, 2600), 24000, 250);
}
range_max_power = 0;
return; // new line signaled, return return; // new line signaled, return
}
} }
if (mode != LOOKING_GLASS_SINGLEPASS) { if (mode != LOOKING_GLASS_SINGLEPASS) {
f_center += looking_glass_step; f_center += looking_glass_step;
retune(); retune();
} else } else {
baseband::spectrum_streaming_start(); baseband::spectrum_streaming_start();
}
} }
void GlassView::on_hide() { void GlassView::on_hide() {
@ -327,13 +365,15 @@ GlassView::GlassView(
&field_lna, &field_lna,
&field_vga, &field_vga,
&field_range, &field_range,
&steps_config, //&steps_config,
&scan_type, &scan_type,
&view_config, &view_config,
&level_integration, &level_integration,
&field_volume,
&filter_config, &filter_config,
&field_rf_amp, &field_rf_amp,
&range_presets, &range_presets,
&button_beep_squelch,
&field_marker, &field_marker,
&field_trigger, &field_trigger,
&button_jump, &button_jump,
@ -370,12 +410,12 @@ GlassView::GlassView(
}; };
}; };
steps_config.on_change = [this](size_t, OptionsField::value_t v) { /*steps_config.on_change = [this](size_t, OptionsField::value_t v) {
field_frequency_min.set_step(v); field_frequency_min.set_step(v);
field_frequency_max.set_step(v); field_frequency_max.set_step(v);
steps = v; steps = v;
}; };
steps_config.set_selected_index(0); // 1 Mhz step. steps_config.set_selected_index(0); // 1 Mhz step.*/
scan_type.on_change = [this](size_t, OptionsField::value_t v) { scan_type.on_change = [this](size_t, OptionsField::value_t v) {
mode = v; mode = v;
@ -497,6 +537,27 @@ GlassView::GlassView(
receiver_model.set_baseband_bandwidth(looking_glass_bandwidth); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz receiver_model.set_baseband_bandwidth(looking_glass_bandwidth); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
receiver_model.set_squelch_level(0); receiver_model.set_squelch_level(0);
receiver_model.enable(); receiver_model.enable();
button_beep_squelch.on_select = [this](ButtonWithEncoder& button) {
(void)button;
beep_enabled = 1 - beep_enabled;
manage_beep_audio();
update_display_beep();
};
button_beep_squelch.on_change = [this]() {
int new_beep_squelch = beep_squelch + button_beep_squelch.get_encoder_delta();
if (new_beep_squelch < -100)
new_beep_squelch = -100;
if (new_beep_squelch > 20)
new_beep_squelch = 20;
beep_squelch = new_beep_squelch;
button_beep_squelch.set_encoder_delta(0);
update_display_beep();
};
manage_beep_audio();
update_display_beep();
} }
uint8_t GlassView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value uint8_t GlassView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value

View File

@ -85,6 +85,8 @@ class GlassView : public View {
uint8_t live_frequency_view = 0; // Spectrum uint8_t live_frequency_view = 0; // Spectrum
uint8_t live_frequency_integrate = 3; // Default (3 * old value + new_value) / 4 uint8_t live_frequency_integrate = 3; // Default (3 * old value + new_value) / 4
uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839 uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839
int32_t beep_squelch = 20; // range from -100 to +20, >=20 disabled
bool beep_enabled = false; // activate on bip button click
app_settings::SettingsManager settings_{ app_settings::SettingsManager settings_{
"rx_glass"sv, "rx_glass"sv,
app_settings::Mode::RX, app_settings::Mode::RX,
@ -98,6 +100,8 @@ class GlassView : public View {
{"freq_view"sv, &live_frequency_view}, {"freq_view"sv, &live_frequency_view},
{"freq_integrate"sv, &live_frequency_integrate}, {"freq_integrate"sv, &live_frequency_integrate},
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings. {"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
{"beep_squelch"sv, &beep_squelch},
{"beep_enabled"sv, &beep_enabled},
}}; }};
struct preset_entry { struct preset_entry {
@ -106,7 +110,10 @@ class GlassView : public View {
std::string label{}; std::string label{};
}; };
int32_t map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh);
std::vector<preset_entry> presets_db{}; std::vector<preset_entry> presets_db{};
void manage_beep_audio();
void update_display_beep();
void update_min(int32_t v); void update_min(int32_t v);
void update_max(int32_t v); void update_max(int32_t v);
void update_range_field(); void update_range_field();
@ -153,6 +160,8 @@ class GlassView : public View {
int32_t steps = 1; int32_t steps = 1;
bool locked_range = false; bool locked_range = false;
uint8_t range_max_power = 0;
uint8_t range_max_power_counter = 0;
uint8_t max_power = 0; uint8_t max_power = 0;
rf::Frequency max_freq_hold = 0; rf::Frequency max_freq_hold = 0;
rf::Frequency last_max_freq = 0; rf::Frequency last_max_freq = 0;
@ -166,7 +175,8 @@ class GlassView : public View {
{{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()}, {{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()},
{{0, 2 * 16}, "PRESET:", Color::light_grey()}, {{0, 2 * 16}, "PRESET:", Color::light_grey()},
{{0, 3 * 16}, "MARKER: MHz RXIQCAL", Color::light_grey()}, {{0, 3 * 16}, "MARKER: MHz RXIQCAL", Color::light_grey()},
{{0, 4 * 16}, "RES: STEP:", Color::light_grey()}}; //{{0, 4 * 16}, "RES: STEPS:", Color::light_grey()}};
{{0, 4 * 16}, "RES: VOL:", Color::light_grey()}};
NumberField field_frequency_min{ NumberField field_frequency_min{
{4 * 8, 0 * 16}, {4 * 8, 0 * 16},
@ -206,9 +216,13 @@ class GlassView : public View {
OptionsField range_presets{ OptionsField range_presets{
{7 * 8, 2 * 16}, {7 * 8, 2 * 16},
20, 10,
{}}; {}};
ButtonWithEncoder button_beep_squelch{
{18 * 8, 2 * 16 + 4, 12 * 8, 1 * 8},
""};
TextField field_marker{ TextField field_marker{
{7 * 8, 3 * 16, 9 * 8, 16}, {7 * 8, 3 * 16, 9 * 8, 16},
""}; ""};
@ -228,7 +242,10 @@ class GlassView : public View {
2, 2,
' '}; ' '};
OptionsField steps_config{ AudioVolumeField field_volume{
{13 * 8, 4 * 16}};
/*OptionsField steps_config{
{13 * 8, 4 * 16}, {13 * 8, 4 * 16},
3, 3,
{ {
@ -238,7 +255,7 @@ class GlassView : public View {
{"100", 100}, {"100", 100},
{"250", 250}, {"250", 250},
{"500", 500}, {"500", 500},
}}; }};*/
OptionsField scan_type{ OptionsField scan_type{
{17 * 8, 4 * 16}, {17 * 8, 4 * 16},

View File

@ -60,7 +60,30 @@ void WidebandSpectrum::execute(const buffer_c8_t& buffer) {
} }
} }
void WidebandSpectrum::on_signal_message(const RequestSignalMessage& message) {
if (message.signal == RequestSignalMessage::Signal::BeepStopRequest) {
audio::dma::beep_stop();
}
}
void WidebandSpectrum::on_beep_message(const AudioBeepMessage& message) {
audio::dma::beep_start(message.freq, message.sample_rate, message.duration_ms);
}
void WidebandSpectrum::on_message(const Message* const msg) { void WidebandSpectrum::on_message(const Message* const msg) {
switch (msg->id) {
case Message::ID::RequestSignal:
on_signal_message(*reinterpret_cast<const RequestSignalMessage*>(msg));
return;
case Message::ID::AudioBeep:
on_beep_message(*reinterpret_cast<const AudioBeepMessage*>(msg));
return;
default:
break;
}
const WidebandSpectrumConfigMessage message = *reinterpret_cast<const WidebandSpectrumConfigMessage*>(msg); const WidebandSpectrumConfigMessage message = *reinterpret_cast<const WidebandSpectrumConfigMessage*>(msg);
switch (msg->id) { switch (msg->id) {
@ -84,7 +107,6 @@ void WidebandSpectrum::on_message(const Message* const msg) {
int main() { int main() {
audio::dma::init_audio_out(); // for AudioRX app (enables audio output while this baseband image is running) audio::dma::init_audio_out(); // for AudioRX app (enables audio output while this baseband image is running)
EventDispatcher event_dispatcher{std::make_unique<WidebandSpectrum>()}; EventDispatcher event_dispatcher{std::make_unique<WidebandSpectrum>()};
event_dispatcher.run(); event_dispatcher.run();
return 0; return 0;

View File

@ -43,6 +43,9 @@ class WidebandSpectrum : public BasebandProcessor {
bool configured = false; bool configured = false;
size_t baseband_fs = 20000000; size_t baseband_fs = 20000000;
void on_beep_message(const AudioBeepMessage& message);
void on_signal_message(const RequestSignalMessage& message);
SpectrumCollector channel_spectrum{}; SpectrumCollector channel_spectrum{};
std::array<complex16_t, 256> spectrum{}; std::array<complex16_t, 256> spectrum{};