MIC TX Now includes RX with Volume and Squelch

You can enable RX and adjust VOLUME  and SQUELCH into your liking.

Sadly enough, you will NOT be able to use VOICE ACTIVATION when RX is enabled (to ensure there will be NO audio feedback defeating the VA sensing)

A "bug" that won over me, but perhaps and hopefully other coder can easily fix:  The Vumeter will momentarily "dissappear" when enabling RX. But it will reappear as soon as you start TX. Or when you turn off RX.

I enabled the PEAK LEVEL MARK on the Vumeter, so you can easily see in which level your input voice / signal is peaking and regulate the MIC gain accordingly in an easier / more robust way.

Side enhancement: Took off the dark green, yellow and red coloring from the vumeter when no signal is present, and replaced it with dark_grey. I know that some coloring is "eye-candy" but the vu-meter is more readable with this new contrast.
This commit is contained in:
euquiq 2020-08-07 00:19:37 -03:00
parent 62821a79e5
commit 72f3eea131
3 changed files with 132 additions and 46 deletions

View File

@ -61,22 +61,22 @@ void MicTXView::configure_baseband() {
void MicTXView::set_tx(bool enable) { void MicTXView::set_tx(bool enable) {
if (enable) { if (enable) {
if (rx_enabled) //If audio RX is enabled
rxaudio(false); //Then turn off audio RX
transmitting = true; transmitting = true;
configure_baseband(); configure_baseband();
transmitter_model.enable(); transmitter_model.enable();
portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming
//gpio_tx.write(1);
//led_tx.on();
} else { } else {
if (transmitting && rogerbeep_enabled) { if (transmitting && rogerbeep_enabled) {
baseband::request_beep(); baseband::request_beep(); //Transmit the roger beep
transmitting = false; transmitting = false; //And flag the end of the transmission so ...
} else { } else { // (if roger beep was enabled, this will be executed after the beep ends transmitting.
transmitting = false; transmitting = false;
configure_baseband(); configure_baseband();
transmitter_model.disable(); transmitter_model.disable();
//gpio_tx.write(0); if (rx_enabled) //If audio RX is enabled and we've been transmitting
//led_tx.off(); rxaudio(true); //Turn back on audio RX
} }
} }
} }
@ -120,6 +120,39 @@ void MicTXView::do_timing() {
void MicTXView::on_tuning_frequency_changed(rf::Frequency f) { void MicTXView::on_tuning_frequency_changed(rf::Frequency f) {
transmitter_model.set_tuning_frequency(f); transmitter_model.set_tuning_frequency(f);
//if ( rx_enabled )
receiver_model.set_tuning_frequency(f); //Update freq also for RX
}
void MicTXView::rxaudio(bool is_on) {
if (is_on) {
baseband::shutdown();
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
//receiver_model.set_sampling_rate(sampling_rate); //**
//receiver_model.set_baseband_bandwidth(1750000); //**
receiver_model.enable();
receiver_model.set_tuning_frequency(field_frequency.value()); //probably this too can be commented out.
audio::output::start();
} else { //These incredibly convoluted steps are required for the vumeter to reappear when stopping RX.
receiver_model.disable();
baseband::shutdown();
baseband::run_image(portapack::spi_flash::image_tag_mic_tx);
audio::input::start();
transmitter_model.enable();
portapack::pin_i2s0_rx_sda.mode(3);
transmitting = false;
configure_baseband();
transmitter_model.disable();
}
}
void MicTXView::on_headphone_volume_changed(int32_t v) {
//if (rx_enabled) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
//}
} }
MicTXView::MicTXView( MicTXView::MicTXView(
@ -142,9 +175,12 @@ MicTXView::MicTXView(
&field_frequency, &field_frequency,
&options_tone_key, &options_tone_key,
&check_rogerbeep, &check_rogerbeep,
&check_rxactive,
&field_volume,
&field_squelch,
&text_ptt &text_ptt
}); });
tone_keys_populate(options_tone_key); tone_keys_populate(options_tone_key);
options_tone_key.on_change = [this](size_t i, int32_t) { options_tone_key.on_change = [this](size_t i, int32_t) {
tone_key_index = i; tone_key_index = i;
@ -168,6 +204,7 @@ MicTXView::MicTXView(
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f); this->on_tuning_frequency_changed(f);
this->field_frequency.set_value(f); this->field_frequency.set_value(f);
set_dirty();
}; };
}; };
@ -178,16 +215,15 @@ MicTXView::MicTXView(
check_va.on_select = [this](Checkbox&, bool v) { check_va.on_select = [this](Checkbox&, bool v) {
va_enabled = v; va_enabled = v;
text_ptt.hidden(v); text_ptt.hidden(v); //hide / show PTT text
set_dirty(); check_rxactive.hidden(v); //hide / show the RX AUDIO
set_dirty(); //Refresh display
}; };
check_va.set_value(false);
check_rogerbeep.on_select = [this](Checkbox&, bool v) { check_rogerbeep.on_select = [this](Checkbox&, bool v) {
rogerbeep_enabled = v; rogerbeep_enabled = v;
}; };
check_rogerbeep.set_value(false);
field_va_level.on_change = [this](int32_t v) { field_va_level.on_change = [this](int32_t v) {
va_level = v; va_level = v;
vumeter.set_mark(v); vumeter.set_mark(v);
@ -203,7 +239,23 @@ MicTXView::MicTXView(
decay_ms = v; decay_ms = v;
}; };
field_va_decay.set_value(1000); field_va_decay.set_value(1000);
check_rxactive.on_select = [this](Checkbox&, bool v) {
//vumeter.set_value(0); //Start with a clean vumeter
rx_enabled = v;
check_va.hidden(v); //Hide or show voice activation
rxaudio(v); //Activate-Deactivate audio rx accordingly
set_dirty(); //Refresh interface
};
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); };
field_squelch.on_change = [this](int32_t v) {
receiver_model.set_squelch_level(100 - v);
};
field_squelch.set_value(0);
transmitter_model.set_sampling_rate(sampling_rate); transmitter_model.set_sampling_rate(sampling_rate);
transmitter_model.set_baseband_bandwidth(1750000); transmitter_model.set_baseband_bandwidth(1750000);
@ -216,6 +268,8 @@ MicTXView::MicTXView(
MicTXView::~MicTXView() { MicTXView::~MicTXView() {
audio::input::stop(); audio::input::stop();
transmitter_model.disable(); transmitter_model.disable();
if (rx_enabled) //Also turn off audio rx if enabled
rxaudio(false);
baseband::shutdown(); baseband::shutdown();
} }

View File

@ -24,12 +24,14 @@
#define __UI_MICTX_H__ #define __UI_MICTX_H__
#include "ui.hpp" #include "ui.hpp"
#include "ui_painter.hpp"
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "tone_key.hpp" #include "tone_key.hpp"
#include "message.hpp" #include "message.hpp"
#include "receiver_model.hpp"
namespace ui { namespace ui {
@ -54,11 +56,11 @@ public:
return false; return false;
}; };
std::string title() const override { return "Microphone TX"; }; std::string title() const override { return "Mic TX RX"; };
private: private:
static constexpr uint32_t sampling_rate = 1536000U; static constexpr uint32_t sampling_rate = 1536000U;
static constexpr uint32_t lcd_frame_duration = (256 * 1000UL) / 60; // 1 frame @ 60fps in ms .8 fixed point static constexpr uint32_t lcd_frame_duration = (256 * 1000UL) / 60; // 1 frame @ 60fps in ms .8 fixed point /60
void update_vumeter(); void update_vumeter();
void do_timing(); void do_timing();
@ -66,10 +68,14 @@ private:
void on_tuning_frequency_changed(rf::Frequency f); void on_tuning_frequency_changed(rf::Frequency f);
void on_tx_progress(const bool done); void on_tx_progress(const bool done);
void configure_baseband(); void configure_baseband();
void rxaudio(bool is_on);
void on_headphone_volume_changed(int32_t v);
bool transmitting { false }; bool transmitting { false };
bool va_enabled { }; bool va_enabled { false };
bool rogerbeep_enabled { }; bool rogerbeep_enabled { false };
bool rx_enabled { false };
uint32_t tone_key_index { }; uint32_t tone_key_index { };
float mic_gain { 1.0 }; float mic_gain { 1.0 };
uint32_t audio_level { 0 }; uint32_t audio_level { 0 };
@ -80,23 +86,25 @@ private:
uint32_t decay_timer { 0 }; uint32_t decay_timer { 0 };
Labels labels { Labels labels {
{ { 7 * 8, 1 * 8 }, "Mic. gain:", Color::light_grey() }, { { 3 * 8, 1 * 8 }, "MIC. GAIN:", Color::light_grey() },
{ { 7 * 8, 4 * 8 }, "Frequency:", Color::light_grey() }, { { 3 * 8, 3 * 8 }, "FREQUENCY:", Color::light_grey() },
{ { 7 * 8, 6 * 8 }, "Bandwidth: kHz", Color::light_grey() }, { { 3 * 8, 5 * 8 }, "BANDWIDTH: kHz", Color::light_grey() },
{ { 9 * 8, 13 * 8 }, "Level: /255", Color::light_grey() }, { { 7 * 8, 11 * 8 }, "LEVEL: /255", Color::light_grey() },
{ { 9 * 8, 15 * 8 }, "Attack: ms", Color::light_grey() }, { { 6 * 8, 13 * 8 }, "ATTACK: ms", Color::light_grey() },
{ { 9 * 8, 17 * 8 }, "Decay: ms", Color::light_grey() }, { { 7 * 8, 15 * 8 }, "DECAY: ms", Color::light_grey() },
{ { 7 * 8, 21 * 8 }, "Tone key:", Color::light_grey() } { { 4 * 8, 18 * 8 }, "TONE KEY:", Color::light_grey() },
{ { 9 * 8, 30 * 8 }, "VOL:", Color::light_grey() },
{ { 5 * 8, 32 * 8 }, "SQUELCH:", Color::light_grey() }
}; };
VuMeter vumeter { VuMeter vumeter {
{ 1 * 8, 2 * 8, 5 * 8, 32 * 8 }, { 0 * 8, 1 * 8, 2 * 8, 33 * 8 },
20, 12,
false true
}; };
OptionsField options_gain { OptionsField options_gain {
{ 17 * 8, 1 * 8 }, { 13 * 8, 1 * 8 },
4, 4,
{ {
{ "x0.5", 5 }, { "x0.5", 5 },
@ -107,10 +115,10 @@ private:
}; };
FrequencyField field_frequency { FrequencyField field_frequency {
{ 17 * 8, 4 * 8 }, { 13 * 8, 3 * 8 },
}; };
NumberField field_bw { NumberField field_bw {
{ 17 * 8, 6 * 8 }, { 13 * 8, 5 * 8 },
3, 3,
{ 0, 150 }, { 0, 150 },
1, 1,
@ -118,28 +126,28 @@ private:
}; };
Checkbox check_va { Checkbox check_va {
{ 7 * 8, 10 * 8 }, { 3 * 8, (9 * 8) - 4 },
7, 7,
"Voice activation", "Voice activation",
false false
}; };
NumberField field_va_level { NumberField field_va_level {
{ 15 * 8, 13 * 8 }, { 13 * 8, 11 * 8 },
3, 3,
{ 0, 255 }, { 0, 255 },
2, 2,
' ' ' '
}; };
NumberField field_va_attack { NumberField field_va_attack {
{ 16 * 8, 15 * 8 }, { 13 * 8, 13 * 8 },
3, 3,
{ 0, 999 }, { 0, 999 },
20, 20,
' ' ' '
}; };
NumberField field_va_decay { NumberField field_va_decay {
{ 15 * 8, 17 * 8 }, { 13 * 8, 15 * 8 },
4, 4,
{ 0, 9999 }, { 0, 9999 },
100, 100,
@ -147,28 +155,52 @@ private:
}; };
OptionsField options_tone_key { OptionsField options_tone_key {
{ 7 * 8, 23 * 8 }, { 10 * 8, 20 * 8 },
23, 23,
{ } { }
}; };
Checkbox check_rogerbeep { Checkbox check_rogerbeep {
{ 7 * 8, 26 * 8 }, { 3 * 8, 23 * 8 },
10, 10,
"Roger beep", "Roger beep",
false false
}; };
Text text_ptt { Checkbox check_rxactive {
{ 7 * 8, 17 * 16, 16 * 8, 16 }, { 3 * 8, (27 * 8) + 4 },
"PTT: RIGHT BUTTON" 8,
"RX audio listening",
false
};
NumberField field_volume {
{ 13 * 8, 30 * 8 },
2,
{ 0, 99 },
1,
' ',
}; };
NumberField field_squelch {
{ 13 * 8, 32 * 8 },
2,
{ 0, 99 },
1,
' ',
};
Text text_ptt {
{ 7 * 8, 35 * 8, 16 * 8, 16 },
"PTT: RIGHT BUTTON"
};
MessageHandlerRegistration message_handler_lcd_sync { MessageHandlerRegistration message_handler_lcd_sync {
Message::ID::DisplayFrameSync, Message::ID::DisplayFrameSync,
[this](const Message* const) { [this](const Message* const) {
this->update_vumeter();
this->do_timing(); this->do_timing();
this->update_vumeter();
} }
}; };

View File

@ -1759,13 +1759,13 @@ void VuMeter::paint(Painter& painter) {
lit = true; lit = true;
if (bar == 0) if (bar == 0)
color = lit ? Color::red() : Color::dark_red(); color = lit ? Color::red() : Color::dark_grey();
else if (bar == 1) else if (bar == 1)
color = lit ? Color::orange() : Color::dark_orange(); color = lit ? Color::orange() : Color::dark_grey();
else if ((bar == 2) || (bar == 3)) else if ((bar == 2) || (bar == 3))
color = lit ? Color::yellow() : Color::dark_yellow(); color = lit ? Color::yellow() : Color::dark_grey();
else else
color = lit ? Color::green() : Color::dark_green(); color = lit ? Color::green() : Color::dark_grey();
painter.fill_rectangle({ pos.x(), pos.y() + (Coord)(bar * (LED_height + 1)), width, (Coord)LED_height }, color); painter.fill_rectangle({ pos.x(), pos.y() + (Coord)(bar * (LED_height + 1)), width, (Coord)LED_height }, color);
} }