Microphone tx is mostly working, Voice activation, PTT, CTCSS...
Transmit bandwidth bugfix TX LED is now only lit when using rf amp VU-meter widget Added gain parameter for baseband audio TX
@ -104,10 +104,12 @@ void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phas
|
|||||||
send_message(&message);
|
send_message(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_audiotx_data(const uint32_t divider, const uint32_t bw, const bool ctcss_enabled, const uint32_t ctcss_phase_inc) {
|
void set_audiotx_data(const uint32_t divider, const uint32_t bw, const uint32_t gain_x10,
|
||||||
|
const bool ctcss_enabled, const uint32_t ctcss_phase_inc) {
|
||||||
const AudioTXConfigMessage message {
|
const AudioTXConfigMessage message {
|
||||||
divider,
|
divider,
|
||||||
bw,
|
bw,
|
||||||
|
gain_x10,
|
||||||
ctcss_phase_inc,
|
ctcss_phase_inc,
|
||||||
ctcss_enabled
|
ctcss_enabled
|
||||||
};
|
};
|
||||||
|
@ -57,7 +57,8 @@ struct WFMConfig {
|
|||||||
|
|
||||||
void set_tones_data(const uint32_t bw, const uint32_t pre_silence, const uint16_t tone_count,
|
void set_tones_data(const uint32_t bw, const uint32_t pre_silence, const uint16_t tone_count,
|
||||||
const bool dual_tone, const bool audio_out);
|
const bool dual_tone, const bool audio_out);
|
||||||
void set_audiotx_data(const uint32_t divider, const uint32_t bw, const bool ctcss_enabled, const uint32_t ctcss_phase_inc);
|
void set_audiotx_data(const uint32_t divider, const uint32_t bw, const uint32_t gain_x10,
|
||||||
|
const bool ctcss_enabled, const uint32_t ctcss_phase_inc);
|
||||||
void set_fifo_data(const int8_t * data);
|
void set_fifo_data(const int8_t * data);
|
||||||
void set_pwmrssi(int32_t avg, bool enabled);
|
void set_pwmrssi(int32_t avg, bool enabled);
|
||||||
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
|
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
|
||||||
|
@ -126,7 +126,7 @@ static constexpr uint8_t bitmap_stripes_data[] = {
|
|||||||
0x1F, 0x00, 0xFE,
|
0x1F, 0x00, 0xFE,
|
||||||
0x0F, 0x00, 0xFF,
|
0x0F, 0x00, 0xFF,
|
||||||
0x07, 0x80, 0xFF,
|
0x07, 0x80, 0xFF,
|
||||||
};
|
};
|
||||||
static constexpr Bitmap bitmap_stripes {
|
static constexpr Bitmap bitmap_stripes {
|
||||||
{ 24, 8 }, bitmap_stripes_data
|
{ 24, 8 }, bitmap_stripes_data
|
||||||
};
|
};
|
||||||
@ -499,20 +499,20 @@ static constexpr Bitmap bitmap_sd_card_ok {
|
|||||||
|
|
||||||
static constexpr uint8_t bitmap_icon_microphone_data[] = {
|
static constexpr uint8_t bitmap_icon_microphone_data[] = {
|
||||||
0xC0, 0x03,
|
0xC0, 0x03,
|
||||||
0x60, 0x05,
|
0xA0, 0x06,
|
||||||
0xB0, 0x0A,
|
|
||||||
0x50, 0x0D,
|
|
||||||
0xB0, 0x0E,
|
|
||||||
0x60, 0x05,
|
0x60, 0x05,
|
||||||
0xE0, 0x07,
|
0xE0, 0x07,
|
||||||
0x20, 0x04,
|
0xEC, 0x37,
|
||||||
0x20, 0x04,
|
0xEC, 0x37,
|
||||||
0xE0, 0x04,
|
0xE8, 0x17,
|
||||||
0xE0, 0x04,
|
0xE8, 0x17,
|
||||||
0xC0, 0x02,
|
0xE8, 0x17,
|
||||||
0x40, 0x02,
|
0xC8, 0x13,
|
||||||
0x40, 0x02,
|
0x18, 0x18,
|
||||||
0x40, 0x02,
|
0xF0, 0x0F,
|
||||||
|
0xC0, 0x03,
|
||||||
|
0x80, 0x01,
|
||||||
|
0x80, 0x01,
|
||||||
0xC0, 0x03,
|
0xC0, 0x03,
|
||||||
};
|
};
|
||||||
static constexpr Bitmap bitmap_icon_microphone {
|
static constexpr Bitmap bitmap_icon_microphone {
|
||||||
@ -541,6 +541,28 @@ static constexpr Bitmap bitmap_icon_numbers {
|
|||||||
{ 16, 16 }, bitmap_icon_numbers_data
|
{ 16, 16 }, bitmap_icon_numbers_data
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr uint8_t bitmap_icon_setup_data[] = {
|
||||||
|
0x00, 0x00,
|
||||||
|
0x18, 0x18,
|
||||||
|
0x18, 0x7E,
|
||||||
|
0x18, 0x7E,
|
||||||
|
0x18, 0x7E,
|
||||||
|
0x18, 0x42,
|
||||||
|
0x18, 0x42,
|
||||||
|
0x18, 0x42,
|
||||||
|
0x18, 0x18,
|
||||||
|
0x7E, 0x18,
|
||||||
|
0x7E, 0x18,
|
||||||
|
0x7E, 0x18,
|
||||||
|
0x42, 0x18,
|
||||||
|
0x42, 0x18,
|
||||||
|
0x42, 0x18,
|
||||||
|
0x18, 0x18,
|
||||||
|
};
|
||||||
|
static constexpr Bitmap bitmap_icon_setup {
|
||||||
|
{ 16, 16 }, bitmap_icon_setup_data
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr uint8_t bitmap_icon_rds_data[] = {
|
static constexpr uint8_t bitmap_icon_rds_data[] = {
|
||||||
0x00, 0x00,
|
0x00, 0x00,
|
||||||
0x00, 0x00,
|
0x00, 0x00,
|
||||||
@ -791,8 +813,8 @@ static constexpr uint8_t bitmap_icon_remote_data[] = {
|
|||||||
0xD0, 0x08,
|
0xD0, 0x08,
|
||||||
0x10, 0x08,
|
0x10, 0x08,
|
||||||
0x10, 0x08,
|
0x10, 0x08,
|
||||||
0x10, 0x08,
|
0x30, 0x0C,
|
||||||
0x10, 0x08,
|
0xF0, 0x0F,
|
||||||
0xE0, 0x07,
|
0xE0, 0x07,
|
||||||
};
|
};
|
||||||
static constexpr Bitmap bitmap_icon_remote {
|
static constexpr Bitmap bitmap_icon_remote {
|
||||||
@ -865,6 +887,28 @@ static constexpr Bitmap bitmap_icon_keyboard {
|
|||||||
{ 16, 16 }, bitmap_icon_keyboard_data
|
{ 16, 16 }, bitmap_icon_keyboard_data
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr uint8_t bitmap_icon_utilities_data[] = {
|
||||||
|
0xC0, 0x03,
|
||||||
|
0x80, 0x0F,
|
||||||
|
0x00, 0x1F,
|
||||||
|
0x08, 0x1E,
|
||||||
|
0x18, 0x3E,
|
||||||
|
0x3C, 0x3E,
|
||||||
|
0x3C, 0x39,
|
||||||
|
0x98, 0x3A,
|
||||||
|
0x48, 0x1D,
|
||||||
|
0xA0, 0x1E,
|
||||||
|
0x70, 0x0F,
|
||||||
|
0xF8, 0x07,
|
||||||
|
0xE0, 0x07,
|
||||||
|
0xC0, 0x03,
|
||||||
|
0xC0, 0x03,
|
||||||
|
0xC0, 0x03,
|
||||||
|
};
|
||||||
|
static constexpr Bitmap bitmap_icon_utilities {
|
||||||
|
{ 16, 16 }, bitmap_icon_utilities_data
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr uint8_t bitmap_icon_capture_data[] = {
|
static constexpr uint8_t bitmap_icon_capture_data[] = {
|
||||||
0xEF, 0x29,
|
0xEF, 0x29,
|
||||||
0xEF, 0x69,
|
0xEF, 0x69,
|
||||||
@ -933,19 +977,19 @@ static constexpr Bitmap bitmap_icon_replay {
|
|||||||
|
|
||||||
static constexpr uint8_t bitmap_icon_soundboard_data[] = {
|
static constexpr uint8_t bitmap_icon_soundboard_data[] = {
|
||||||
0x00, 0x00,
|
0x00, 0x00,
|
||||||
0xDE, 0x7B,
|
0xFF, 0xFF,
|
||||||
0x63, 0x8C,
|
0x63, 0x8C,
|
||||||
0x21, 0x84,
|
0x21, 0x84,
|
||||||
0x21, 0x84,
|
0x21, 0x84,
|
||||||
0xDE, 0x7B,
|
0xDF, 0xFB,
|
||||||
0x63, 0x8C,
|
0x63, 0x8C,
|
||||||
0x21, 0x84,
|
0x21, 0x84,
|
||||||
0x21, 0x84,
|
0x21, 0x84,
|
||||||
0xDE, 0x7B,
|
0xDF, 0xFB,
|
||||||
0x63, 0x8C,
|
0x63, 0x8C,
|
||||||
0x21, 0x84,
|
0x21, 0x84,
|
||||||
0x21, 0x84,
|
0x21, 0x84,
|
||||||
0xDE, 0x7B,
|
0xFF, 0xFF,
|
||||||
0x00, 0x00,
|
0x00, 0x00,
|
||||||
0x00, 0x00,
|
0x00, 0x00,
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ctcss.hpp"
|
#include "ctcss.hpp"
|
||||||
|
#include "string_format.hpp"
|
||||||
|
|
||||||
|
namespace ctcss {
|
||||||
|
|
||||||
const ctcss_tone ctcss_tones[CTCSS_TONES_NB] = {
|
const ctcss_tone ctcss_tones[CTCSS_TONES_NB] = {
|
||||||
{ "XZ", 0, 67.000 },
|
{ "XZ", 0, 67.000 },
|
||||||
{ "WZ", 1, 69.400 },
|
{ "WZ", 1, 69.400 },
|
||||||
@ -74,3 +77,23 @@ const ctcss_tone ctcss_tones[CTCSS_TONES_NB] = {
|
|||||||
{ "--", 38, 250.300 },
|
{ "--", 38, 250.300 },
|
||||||
{ "0Z", 50, 254.100 }
|
{ "0Z", 50, 254.100 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void ctcss_populate(OptionsField& field) {
|
||||||
|
using option_t = std::pair<std::string, int32_t>;
|
||||||
|
using options_t = std::vector<option_t>;
|
||||||
|
options_t ctcss_options;
|
||||||
|
std::string f_string;
|
||||||
|
uint32_t c, f;
|
||||||
|
|
||||||
|
ctcss_options.emplace_back(std::make_pair("None", 0));
|
||||||
|
for (c = 0; c < CTCSS_TONES_NB; c++) {
|
||||||
|
f = (uint32_t)(ctcss_tones[c].frequency * 10);
|
||||||
|
f_string = ctcss_tones[c].PL_code;
|
||||||
|
f_string += " " + to_string_dec_uint(f / 10) + "." + to_string_dec_uint(f % 10);
|
||||||
|
ctcss_options.emplace_back(f_string, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
field.set_options(ctcss_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -24,9 +24,14 @@
|
|||||||
#define __CTCSS_H_
|
#define __CTCSS_H_
|
||||||
|
|
||||||
#include "ui.hpp"
|
#include "ui.hpp"
|
||||||
|
#include "ui_widget.hpp"
|
||||||
|
|
||||||
|
using namespace ui;
|
||||||
|
|
||||||
#define CTCSS_TONES_NB 50
|
#define CTCSS_TONES_NB 50
|
||||||
|
|
||||||
|
namespace ctcss {
|
||||||
|
|
||||||
struct ctcss_tone {
|
struct ctcss_tone {
|
||||||
char PL_code[3];
|
char PL_code[3];
|
||||||
uint16_t num_code;
|
uint16_t num_code;
|
||||||
@ -35,4 +40,8 @@ struct ctcss_tone {
|
|||||||
|
|
||||||
extern const ctcss_tone ctcss_tones[CTCSS_TONES_NB];
|
extern const ctcss_tone ctcss_tones[CTCSS_TONES_NB];
|
||||||
|
|
||||||
|
void ctcss_populate(OptionsField& field);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif/*__CTCSS_H_*/
|
#endif/*__CTCSS_H_*/
|
||||||
|
@ -24,11 +24,12 @@
|
|||||||
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
||||||
|
|
||||||
//BUG: RDS Radiotext is not recognized in Redsea (and car radio)
|
//BUG: RDS Radiotext is not recognized in Redsea (and car radio)
|
||||||
//BUG: Check AFSK transmit end, skips last bits ?
|
|
||||||
//BUG: RDS doesn't stop baseband when stopping tx ?
|
//BUG: RDS doesn't stop baseband when stopping tx ?
|
||||||
|
//BUG: Check AFSK transmit end, skips last bits ?
|
||||||
|
|
||||||
//TEST: Imperial in whipcalc
|
//TEST: Imperial in whipcalc
|
||||||
|
|
||||||
|
//TODO: Roger beep in mic tx
|
||||||
//TODO: Morse use prosigns
|
//TODO: Morse use prosigns
|
||||||
//TODO: Morse live keying mode ?
|
//TODO: Morse live keying mode ?
|
||||||
/*
|
/*
|
||||||
|
@ -141,6 +141,13 @@ bool set_tuning_frequency(const rf::Frequency frequency) {
|
|||||||
|
|
||||||
void set_rf_amp(const bool rf_amp) {
|
void set_rf_amp(const bool rf_amp) {
|
||||||
rf_path.set_rf_amp(rf_amp);
|
rf_path.set_rf_amp(rf_amp);
|
||||||
|
|
||||||
|
if (direction == rf::Direction::Transmit) {
|
||||||
|
if (rf_amp)
|
||||||
|
led_tx.on();
|
||||||
|
else
|
||||||
|
led_tx.off();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_lna_gain(const int_fast8_t db) {
|
void set_lna_gain(const int_fast8_t db) {
|
||||||
|
@ -153,7 +153,7 @@ ADSBTxView::ADSBTxView(NavigationView& nav) {
|
|||||||
field_lon_seconds.set_value(0);
|
field_lon_seconds.set_value(0);
|
||||||
|
|
||||||
for (c = 0; c < 4; c++)
|
for (c = 0; c < 4; c++)
|
||||||
field_squawk.set_value(c, 0);
|
field_squawk.set_sym(c, 0);
|
||||||
|
|
||||||
generate_frame();
|
generate_frame();
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||||
|
* Copyright (C) 2016 Furrtek
|
||||||
*
|
*
|
||||||
* This file is part of PortaPack.
|
* This file is part of PortaPack.
|
||||||
*
|
*
|
||||||
@ -22,53 +23,102 @@
|
|||||||
#include "ui_audiotx.hpp"
|
#include "ui_audiotx.hpp"
|
||||||
|
|
||||||
#include "baseband_api.hpp"
|
#include "baseband_api.hpp"
|
||||||
#include "ui_alphanum.hpp"
|
#include "hackrf_gpio.hpp"
|
||||||
#include "audio.hpp"
|
#include "audio.hpp"
|
||||||
#include "portapack.hpp"
|
#include "portapack.hpp"
|
||||||
#include "pins.hpp"
|
#include "pins.hpp"
|
||||||
#include "string_format.hpp"
|
#include "string_format.hpp"
|
||||||
|
#include "irq_controls.hpp"
|
||||||
#include "portapack_shared_memory.hpp"
|
#include "portapack_shared_memory.hpp"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace ctcss;
|
||||||
using namespace portapack;
|
using namespace portapack;
|
||||||
|
using namespace hackrf::one;
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
void AudioTXView::focus() {
|
void AudioTXView::focus() {
|
||||||
button_transmit.focus();
|
field_frequency.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTXView::paint(Painter& painter) {
|
void AudioTXView::update_vumeter() {
|
||||||
_painter = &painter;
|
vumeter.set_value(audio_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTXView::draw_vumeter() {
|
void AudioTXView::set_tx(bool enable) {
|
||||||
uint32_t bar;
|
uint32_t ctcss_index;
|
||||||
Color color;
|
bool ctcss_enabled;
|
||||||
bool lit = true;
|
|
||||||
uint32_t bar_level = audio_level / 15;
|
|
||||||
|
|
||||||
if (bar_level > 16) bar_level = 16;
|
if (enable) {
|
||||||
|
ctcss_index = options_ctcss.selected_index();
|
||||||
for (bar = 0; bar < 16; bar++) {
|
|
||||||
if (bar >= bar_level)
|
|
||||||
lit = false;
|
|
||||||
|
|
||||||
if (bar < 11)
|
if (ctcss_index) {
|
||||||
color = lit ? Color::green() : Color::dark_green();
|
ctcss_enabled = true;
|
||||||
else if ((bar >= 11) && (bar < 13))
|
ctcss_index--;
|
||||||
color = lit ? Color::yellow() : Color::dark_yellow();
|
} else
|
||||||
else if ((bar >= 13) && (bar < 15))
|
ctcss_enabled = false;
|
||||||
color = lit ? Color::orange() : Color::dark_orange();
|
|
||||||
else
|
|
||||||
color = lit ? Color::red() : Color::dark_red();
|
|
||||||
|
|
||||||
_painter->fill_rectangle({ 100, (Coord)(210 - (bar * 12)), 40, 10 }, color);
|
baseband::set_audiotx_data(
|
||||||
|
1536000U / 20, // 20Hz level update
|
||||||
|
transmitter_model.bandwidth(),
|
||||||
|
mic_gain_x10,
|
||||||
|
ctcss_enabled,
|
||||||
|
(uint32_t)((ctcss_tones[ctcss_index].frequency / 1536000.0) * 0xFFFFFFFFULL)
|
||||||
|
);
|
||||||
|
gpio_tx.write(1);
|
||||||
|
led_tx.on();
|
||||||
|
transmitting = true;
|
||||||
|
} else {
|
||||||
|
baseband::set_audiotx_data(
|
||||||
|
1536000U / 20, // 20Hz level update
|
||||||
|
0, // BW 0 = TX off
|
||||||
|
mic_gain_x10,
|
||||||
|
false, // Ignore CTCSS
|
||||||
|
0
|
||||||
|
);
|
||||||
|
gpio_tx.write(0);
|
||||||
|
led_tx.off();
|
||||||
|
transmitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTXView::do_timing() {
|
||||||
|
if (va_enabled) {
|
||||||
|
if (!transmitting) {
|
||||||
|
// Attack
|
||||||
|
if (audio_level >= va_level) {
|
||||||
|
if ((attack_timer >> 8) >= attack_ms) {
|
||||||
|
decay_timer = 0;
|
||||||
|
attack_timer = 0;
|
||||||
|
set_tx(true);
|
||||||
|
} else {
|
||||||
|
attack_timer += ((256 * 1000) / 60); // 1 frame @ 60fps in ms .8 fixed point
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attack_timer = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Decay
|
||||||
|
if (audio_level < va_level) {
|
||||||
|
if ((decay_timer >> 8) >= decay_ms) {
|
||||||
|
decay_timer = 0;
|
||||||
|
attack_timer = 0;
|
||||||
|
set_tx(false);
|
||||||
|
} else {
|
||||||
|
decay_timer += ((256 * 1000) / 60); // 1 frame @ 60fps in ms .8 fixed point
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decay_timer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// PTT disable :(
|
||||||
|
const auto switches_state = get_switches_state();
|
||||||
|
if (!switches_state[1]) // Left button
|
||||||
|
set_tx(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
//text_power.set(to_string_hex(LPC_I2S0->STATE, 8) + " " + to_string_dec_uint(audio_level) + " ");
|
|
||||||
text_power.set(to_string_dec_uint(audio_level) + " ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioTXView::on_tuning_frequency_changed(rf::Frequency f) {
|
void AudioTXView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||||
@ -82,16 +132,31 @@ AudioTXView::AudioTXView(
|
|||||||
pins[P6_2].mode(3); // I2S0_RX_SDA !
|
pins[P6_2].mode(3); // I2S0_RX_SDA !
|
||||||
|
|
||||||
baseband::run_image(portapack::spi_flash::image_tag_mic_tx);
|
baseband::run_image(portapack::spi_flash::image_tag_mic_tx);
|
||||||
|
|
||||||
transmitter_model.set_tuning_frequency(92200000);
|
|
||||||
|
|
||||||
add_children({
|
add_children({
|
||||||
&text_power,
|
&labels,
|
||||||
|
&vumeter,
|
||||||
|
&options_gain,
|
||||||
|
&check_va,
|
||||||
|
&field_va_level,
|
||||||
|
&field_va_attack,
|
||||||
|
&field_va_decay,
|
||||||
|
&field_bw,
|
||||||
&field_frequency,
|
&field_frequency,
|
||||||
&button_transmit,
|
&options_ctcss,
|
||||||
|
//&check_rogerbeep,
|
||||||
|
&text_ptt,
|
||||||
&button_exit
|
&button_exit
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ctcss_populate(options_ctcss);
|
||||||
|
options_ctcss.set_selected_index(0);
|
||||||
|
|
||||||
|
options_gain.on_change = [this](size_t, int32_t v) {
|
||||||
|
mic_gain_x10 = v;
|
||||||
|
};
|
||||||
|
options_gain.set_selected_index(1); // x1.0
|
||||||
|
|
||||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||||
field_frequency.set_step(receiver_model.frequency_step());
|
field_frequency.set_step(receiver_model.frequency_step());
|
||||||
field_frequency.on_change = [this](rf::Frequency f) {
|
field_frequency.on_change = [this](rf::Frequency f) {
|
||||||
@ -106,26 +171,46 @@ AudioTXView::AudioTXView(
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
button_transmit.on_select = [](Button&){
|
field_bw.on_change = [this](uint32_t v) {
|
||||||
transmitter_model.set_sampling_rate(1536000U);
|
transmitter_model.set_bandwidth(v * 1000);
|
||||||
transmitter_model.set_rf_amp(true);
|
|
||||||
transmitter_model.set_lna(40);
|
|
||||||
transmitter_model.set_vga(40);
|
|
||||||
transmitter_model.set_baseband_bandwidth(1750000);
|
|
||||||
transmitter_model.enable();
|
|
||||||
|
|
||||||
baseband::set_audiotx_data(
|
|
||||||
76800, // 20Hz level update
|
|
||||||
10000, // 10kHz bw
|
|
||||||
false,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
field_bw.set_value(10);
|
||||||
|
|
||||||
|
check_va.on_select = [this](Checkbox&, bool v) {
|
||||||
|
va_enabled = v;
|
||||||
|
text_ptt.hidden(v);
|
||||||
|
set_dirty();
|
||||||
|
};
|
||||||
|
check_va.set_value(false);
|
||||||
|
|
||||||
|
field_va_level.on_change = [this](int32_t v) {
|
||||||
|
va_level = v;
|
||||||
|
vumeter.set_mark(v);
|
||||||
|
};
|
||||||
|
field_va_level.set_value(40);
|
||||||
|
|
||||||
|
field_va_attack.on_change = [this](int32_t v) {
|
||||||
|
attack_ms = v;
|
||||||
|
};
|
||||||
|
field_va_attack.set_value(500);
|
||||||
|
|
||||||
|
field_va_decay.on_change = [this](int32_t v) {
|
||||||
|
decay_ms = v;
|
||||||
|
};
|
||||||
|
field_va_decay.set_value(2000);
|
||||||
|
|
||||||
button_exit.on_select = [&nav](Button&){
|
button_exit.on_select = [&nav](Button&){
|
||||||
nav.pop();
|
nav.pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Run baseband as soon as the app starts to get audio levels without transmitting (rf amp off)
|
||||||
|
transmitter_model.set_sampling_rate(1536000U);
|
||||||
|
transmitter_model.set_rf_amp(true);
|
||||||
|
transmitter_model.set_baseband_bandwidth(1750000);
|
||||||
|
transmitter_model.enable();
|
||||||
|
|
||||||
|
set_tx(false);
|
||||||
|
|
||||||
audio::set_rate(audio::Rate::Hz_24000);
|
audio::set_rate(audio::Rate::Hz_24000);
|
||||||
audio::input::start();
|
audio::input::start();
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,9 @@
|
|||||||
#include "ui_navigation.hpp"
|
#include "ui_navigation.hpp"
|
||||||
#include "ui_font_fixed_8x16.hpp"
|
#include "ui_font_fixed_8x16.hpp"
|
||||||
#include "message.hpp"
|
#include "message.hpp"
|
||||||
#include "volume.hpp"
|
|
||||||
#include "ui_receiver.hpp"
|
#include "ui_receiver.hpp"
|
||||||
#include "transmitter_model.hpp"
|
#include "transmitter_model.hpp"
|
||||||
|
#include "ctcss.hpp"
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
@ -46,40 +46,130 @@ public:
|
|||||||
AudioTXView& operator=(AudioTXView&&) = delete;
|
AudioTXView& operator=(AudioTXView&&) = delete;
|
||||||
|
|
||||||
void focus() override;
|
void focus() override;
|
||||||
void paint(Painter& painter) override;
|
|
||||||
|
// PTT: Enable through KeyEvent (only works with presses), disable by polling :(
|
||||||
|
bool on_key(const KeyEvent key) {
|
||||||
|
if ((key == KeyEvent::Left) && (!va_enabled)) {
|
||||||
|
set_tx(true);
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
std::string title() const override { return "Microphone TX"; };
|
std::string title() const override { return "Microphone TX"; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void draw_vumeter();
|
void update_vumeter();
|
||||||
|
void do_timing();
|
||||||
|
void set_tx(bool enable);
|
||||||
void on_tuning_frequency_changed(rf::Frequency f);
|
void on_tuning_frequency_changed(rf::Frequency f);
|
||||||
|
void on_ctcss_changed(uint32_t v);
|
||||||
|
|
||||||
|
bool transmitting { false };
|
||||||
|
bool va_enabled { };
|
||||||
|
uint32_t mic_gain_x10 { };
|
||||||
uint32_t audio_level { 0 };
|
uint32_t audio_level { 0 };
|
||||||
Painter * _painter { };
|
uint32_t va_level { };
|
||||||
|
uint32_t attack_ms { };
|
||||||
|
uint32_t decay_ms { };
|
||||||
|
uint32_t attack_timer { 0 };
|
||||||
|
uint32_t decay_timer { 0 };
|
||||||
|
|
||||||
Text text_power {
|
Labels labels {
|
||||||
{ 6 * 8, 28 * 8, 16 * 8, 16 },
|
{ { 7 * 8, 1 * 8 }, "Mic. gain:", Color::light_grey() },
|
||||||
"-"
|
{ { 7 * 8, 4 * 8 }, "Voice activation:", Color::light_grey() },
|
||||||
|
{ { 8 * 8, 9 * 8 }, "Level: /255", Color::light_grey() },
|
||||||
|
{ { 8 * 8, 11 * 8 }, "Attack: ms", Color::light_grey() },
|
||||||
|
{ { 8 * 8, 13 * 8 }, "Decay: ms", Color::light_grey() },
|
||||||
|
{ { 7 * 8, 17 * 8 }, "Bandwidth: kHz", Color::light_grey() },
|
||||||
|
{ { 7 * 8, 19 * 8 }, "Frequency:", Color::light_grey() },
|
||||||
|
{ { 11 * 8, 21 * 8 }, "CTCSS:", Color::light_grey() }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VuMeter vumeter {
|
||||||
|
{ 1 * 8, 2 * 8, 5 * 8, 26 * 8 },
|
||||||
|
16
|
||||||
|
};
|
||||||
|
|
||||||
|
OptionsField options_gain {
|
||||||
|
{ 17 * 8, 1 * 8 },
|
||||||
|
4,
|
||||||
|
{
|
||||||
|
{ "x0.5", 5 },
|
||||||
|
{ "x1.0", 10 },
|
||||||
|
{ "x1.5", 15 },
|
||||||
|
{ "x2.0", 20 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Checkbox check_va {
|
||||||
|
{ 8 * 8, 6 * 8 },
|
||||||
|
7,
|
||||||
|
"Enabled",
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
NumberField field_va_level {
|
||||||
|
{ 14 * 8, 9 * 8 },
|
||||||
|
3,
|
||||||
|
{ 0, 255 },
|
||||||
|
2,
|
||||||
|
' '
|
||||||
|
};
|
||||||
|
NumberField field_va_attack {
|
||||||
|
{ 15 * 8, 11 * 8 },
|
||||||
|
3,
|
||||||
|
{ 0, 999 },
|
||||||
|
20,
|
||||||
|
' '
|
||||||
|
};
|
||||||
|
NumberField field_va_decay {
|
||||||
|
{ 14 * 8, 13 * 8 },
|
||||||
|
4,
|
||||||
|
{ 0, 9999 },
|
||||||
|
100,
|
||||||
|
' '
|
||||||
|
};
|
||||||
|
|
||||||
|
NumberField field_bw {
|
||||||
|
{ 17 * 8, 17 * 8 },
|
||||||
|
3,
|
||||||
|
{ 0, 150 },
|
||||||
|
1,
|
||||||
|
' '
|
||||||
|
};
|
||||||
FrequencyField field_frequency {
|
FrequencyField field_frequency {
|
||||||
{ 6 * 8, 30 * 8 },
|
{ 17 * 8, 19 * 8 },
|
||||||
};
|
};
|
||||||
|
|
||||||
Button button_transmit {
|
OptionsField options_ctcss {
|
||||||
{ 1 * 8, 33 * 8, 12 * 8, 32 },
|
{ 17 * 8, 21 * 8 },
|
||||||
"Transmit"
|
8,
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
Checkbox check_rogerbeep {
|
||||||
|
{ 8 * 8, 23 * 8 },
|
||||||
|
10,
|
||||||
|
"Roger beep",
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
Text text_ptt {
|
||||||
|
{ 7 * 8, 28 * 8, 16 * 8, 16 },
|
||||||
|
"PTT: LEFT BUTTON"
|
||||||
};
|
};
|
||||||
|
|
||||||
Button button_exit {
|
Button button_exit {
|
||||||
{ 16 * 8, 33 * 8, 12 * 8, 32 },
|
{ 18 * 8, 32 * 8, 10 * 8, 40 },
|
||||||
"Exit"
|
"Exit"
|
||||||
};
|
};
|
||||||
|
|
||||||
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->draw_vumeter();
|
this->update_vumeter();
|
||||||
|
this->do_timing();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ void EncodersView::generate_frame() {
|
|||||||
if (c == 'S')
|
if (c == 'S')
|
||||||
debug_text += encoder_def->sync;
|
debug_text += encoder_def->sync;
|
||||||
else
|
else
|
||||||
debug_text += encoder_def->bit_format[symfield_word.value(i++)];
|
debug_text += encoder_def->bit_format[symfield_word.get_sym(i++)];
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_waveform();
|
draw_waveform();
|
||||||
|
@ -189,11 +189,11 @@ bool MenuView::set_highlighted(int32_t new_value) {
|
|||||||
if (new_value >= item_count)
|
if (new_value >= item_count)
|
||||||
new_value = item_count - 1;
|
new_value = item_count - 1;
|
||||||
|
|
||||||
if ((new_value > offset_) && ((new_value - offset_) >= displayed_max_)) {
|
if (((uint32_t)new_value > offset_) && ((new_value - offset_) >= displayed_max_)) {
|
||||||
// Shift MenuView up
|
// Shift MenuView up
|
||||||
offset_ = new_value - displayed_max_ + 1;
|
offset_ = new_value - displayed_max_ + 1;
|
||||||
update_items();
|
update_items();
|
||||||
} else if (new_value < offset_) {
|
} else if ((uint32_t)new_value < offset_) {
|
||||||
// Shift MenuView down
|
// Shift MenuView down
|
||||||
offset_ = new_value;
|
offset_ = new_value;
|
||||||
update_items();
|
update_items();
|
||||||
|
@ -102,8 +102,6 @@ bool MorseView::start_tx() {
|
|||||||
|
|
||||||
transmitter_model.set_sampling_rate(1536000U);
|
transmitter_model.set_sampling_rate(1536000U);
|
||||||
transmitter_model.set_rf_amp(true);
|
transmitter_model.set_rf_amp(true);
|
||||||
transmitter_model.set_lna(40);
|
|
||||||
transmitter_model.set_vga(40);
|
|
||||||
transmitter_model.set_baseband_bandwidth(1750000);
|
transmitter_model.set_baseband_bandwidth(1750000);
|
||||||
transmitter_model.enable();
|
transmitter_model.enable();
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ TransmitterAudioMenuView::TransmitterAudioMenuView(NavigationView& nav) {
|
|||||||
add_items<4>({ {
|
add_items<4>({ {
|
||||||
{ "Soundboard", ui::Color::green(), &bitmap_icon_soundboard, [&nav](){ nav.push<SoundBoardView>(); } },
|
{ "Soundboard", ui::Color::green(), &bitmap_icon_soundboard, [&nav](){ nav.push<SoundBoardView>(); } },
|
||||||
{ "Numbers station", ui::Color::orange(),&bitmap_icon_numbers, [&nav](){ nav.push<NumbersStationView>(); } },
|
{ "Numbers station", ui::Color::orange(),&bitmap_icon_numbers, [&nav](){ nav.push<NumbersStationView>(); } },
|
||||||
{ "Microphone", ui::Color::orange(),&bitmap_icon_microphone, [&nav](){ nav.push<AudioTXView>(); } },
|
{ "Microphone", ui::Color::green(), &bitmap_icon_microphone, [&nav](){ nav.push<AudioTXView>(); } },
|
||||||
{ "Whistle", ui::Color::yellow(),&bitmap_icon_whistle, [&nav](){ nav.push<WhistleView>(); } },
|
{ "Whistle", ui::Color::yellow(),&bitmap_icon_whistle, [&nav](){ nav.push<WhistleView>(); } },
|
||||||
} });
|
} });
|
||||||
on_left = [&nav](){ nav.pop(); };
|
on_left = [&nav](){ nav.pop(); };
|
||||||
@ -357,14 +357,14 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
|
|||||||
add_items<12>({ {
|
add_items<12>({ {
|
||||||
{ "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push<PlayDeadView>(); } },
|
{ "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push<PlayDeadView>(); } },
|
||||||
{ "Receivers", ui::Color::cyan(), &bitmap_icon_receivers, [&nav](){ nav.push<ReceiverMenuView>(); } },
|
{ "Receivers", ui::Color::cyan(), &bitmap_icon_receivers, [&nav](){ nav.push<ReceiverMenuView>(); } },
|
||||||
{ "Capture", ui::Color::cyan(), &bitmap_icon_capture, [&nav](){ nav.push<CaptureAppView>(); } }, //CaptureAppView
|
{ "Capture", ui::Color::blue(), &bitmap_icon_capture, [&nav](){ nav.push<CaptureAppView>(); } },
|
||||||
{ "Replay", ui::Color::grey(), &bitmap_icon_replay, [&nav](){ nav.push<NotImplementedView>(); } },
|
{ "Replay", ui::Color::grey(), &bitmap_icon_replay, [&nav](){ nav.push<NotImplementedView>(); } },
|
||||||
{ "Code transmitters", ui::Color::green(), &bitmap_icon_codetx, [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
|
{ "Code transmitters", ui::Color::green(), &bitmap_icon_codetx, [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
|
||||||
{ "Audio transmitters", ui::Color::green(), &bitmap_icon_audiotx, [&nav](){ nav.push<TransmitterAudioMenuView>(); } },
|
{ "Audio transmitters", ui::Color::green(), &bitmap_icon_audiotx, [&nav](){ nav.push<TransmitterAudioMenuView>(); } },
|
||||||
{ "Close Call", ui::Color::orange(),&bitmap_icon_closecall, [&nav](){ nav.push<CloseCallView>(); } },
|
{ "Close Call", ui::Color::orange(),&bitmap_icon_closecall, [&nav](){ nav.push<CloseCallView>(); } },
|
||||||
{ "Jammer", ui::Color::orange(),&bitmap_icon_jammer, [&nav](){ nav.push<JammerView>(); } },
|
{ "Jammer", ui::Color::orange(),&bitmap_icon_jammer, [&nav](){ nav.push<JammerView>(); } },
|
||||||
{ "Utilities", ui::Color::purple(),nullptr, [&nav](){ nav.push<UtilitiesView>(); } },
|
{ "Utilities", ui::Color::purple(),&bitmap_icon_utilities, [&nav](){ nav.push<UtilitiesView>(); } },
|
||||||
{ "Setup", ui::Color::white(), nullptr, [&nav](){ nav.push<SetupMenuView>(); } },
|
{ "Setup", ui::Color::white(), &bitmap_icon_setup, [&nav](){ nav.push<SetupMenuView>(); } },
|
||||||
//{ "Debug", ui::Color::white(), nullptr, [&nav](){ nav.push<DebugMenuView>(); } },
|
//{ "Debug", ui::Color::white(), nullptr, [&nav](){ nav.push<DebugMenuView>(); } },
|
||||||
{ "HackRF mode", ui::Color::white(), &bitmap_icon_hackrf, [this, &nav](){ hackrf_mode(nav); } },
|
{ "HackRF mode", ui::Color::white(), &bitmap_icon_hackrf, [this, &nav](){ hackrf_mode(nav); } },
|
||||||
{ "About", ui::Color::white(), nullptr, [&nav](){ nav.push<AboutView>(); } }
|
{ "About", ui::Color::white(), nullptr, [&nav](){ nav.push<AboutView>(); } }
|
||||||
|
@ -66,7 +66,7 @@ void NumbersStationView::prepare_audio() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (segment == MESSAGE) {
|
if (segment == MESSAGE) {
|
||||||
code = symfield_code.value(code_index);
|
code = symfield_code.get_sym(code_index);
|
||||||
|
|
||||||
if (code_index == 25)
|
if (code_index == 25)
|
||||||
transmitter_model.disable();
|
transmitter_model.disable();
|
||||||
@ -131,6 +131,7 @@ void NumbersStationView::start_tx() {
|
|||||||
baseband::set_audiotx_data(
|
baseband::set_audiotx_data(
|
||||||
(1536000 / 44100) - 1,
|
(1536000 / 44100) - 1,
|
||||||
number_bw.value(),
|
number_bw.value(),
|
||||||
|
1,
|
||||||
false,
|
false,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
@ -213,17 +214,17 @@ NumbersStationView::NumbersStationView(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
symfield_code.set_value(0, 10);
|
symfield_code.set_sym(0, 10);
|
||||||
symfield_code.set_value(1, 3);
|
symfield_code.set_sym(1, 3);
|
||||||
symfield_code.set_value(2, 4);
|
symfield_code.set_sym(2, 4);
|
||||||
symfield_code.set_value(3, 11);
|
symfield_code.set_sym(3, 11);
|
||||||
symfield_code.set_value(4, 6);
|
symfield_code.set_sym(4, 6);
|
||||||
symfield_code.set_value(5, 1);
|
symfield_code.set_sym(5, 1);
|
||||||
symfield_code.set_value(6, 9);
|
symfield_code.set_sym(6, 9);
|
||||||
symfield_code.set_value(7, 7);
|
symfield_code.set_sym(7, 7);
|
||||||
symfield_code.set_value(8, 8);
|
symfield_code.set_sym(8, 8);
|
||||||
symfield_code.set_value(9, 0);
|
symfield_code.set_sym(9, 0);
|
||||||
symfield_code.set_value(10, 12); // End
|
symfield_code.set_sym(10, 12); // End
|
||||||
|
|
||||||
for (c = 0; c < 25; c++)
|
for (c = 0; c < 25; c++)
|
||||||
symfield_code.set_symbol_list(c, "0123456789pPE");
|
symfield_code.set_symbol_list(c, "0123456789pPE");
|
||||||
|
@ -153,7 +153,7 @@ POCSAGTXView::POCSAGTXView(
|
|||||||
// TODO: set_value for whole symfield
|
// TODO: set_value for whole symfield
|
||||||
reload_address = portapack::persistent_memory::pocsag_address();
|
reload_address = portapack::persistent_memory::pocsag_address();
|
||||||
for (c = 0; c < 7; c++) {
|
for (c = 0; c < 7; c++) {
|
||||||
field_address.set_value(6 - c, reload_address % 10);
|
field_address.set_sym(6 - c, reload_address % 10);
|
||||||
reload_address /= 10;
|
reload_address /= 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,10 +132,10 @@ RDSView::RDSView(NavigationView& nav) {
|
|||||||
check_TA.set_value(true);
|
check_TA.set_value(true);
|
||||||
check_TP.set_value(true);
|
check_TP.set_value(true);
|
||||||
|
|
||||||
sym_pi_code.set_value(0, 0xF);
|
sym_pi_code.set_sym(0, 0xF);
|
||||||
sym_pi_code.set_value(1, 0x3);
|
sym_pi_code.set_sym(1, 0x3);
|
||||||
sym_pi_code.set_value(2, 0xE);
|
sym_pi_code.set_sym(2, 0xE);
|
||||||
sym_pi_code.set_value(3, 0x0);
|
sym_pi_code.set_sym(3, 0x0);
|
||||||
sym_pi_code.on_change = [this]() {
|
sym_pi_code.on_change = [this]() {
|
||||||
rds_flags.PI_code = sym_pi_code.value_hex_u64();
|
rds_flags.PI_code = sym_pi_code.value_hex_u64();
|
||||||
};
|
};
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace ctcss;
|
||||||
using namespace portapack;
|
using namespace portapack;
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
@ -137,6 +138,7 @@ void SoundBoardView::play_sound(uint16_t id) {
|
|||||||
baseband::set_audiotx_data(
|
baseband::set_audiotx_data(
|
||||||
divider,
|
divider,
|
||||||
number_bw.value() * 1000,
|
number_bw.value() * 1000,
|
||||||
|
1,
|
||||||
ctcss_enabled,
|
ctcss_enabled,
|
||||||
(uint32_t)((ctcss_tones[ctcss_index].frequency / 1536000.0) * 0xFFFFFFFFULL)
|
(uint32_t)((ctcss_tones[ctcss_index].frequency / 1536000.0) * 0xFFFFFFFFULL)
|
||||||
);
|
);
|
||||||
@ -193,13 +195,9 @@ SoundBoardView::SoundBoardView(
|
|||||||
NavigationView& nav
|
NavigationView& nav
|
||||||
) : nav_ (nav)
|
) : nav_ (nav)
|
||||||
{
|
{
|
||||||
using option_t = std::pair<std::string, int32_t>;
|
|
||||||
using options_t = std::vector<option_t>;
|
|
||||||
options_t ctcss_options;
|
|
||||||
std::vector<std::filesystem::path> file_list;
|
std::vector<std::filesystem::path> file_list;
|
||||||
std::string title, f_string;
|
std::string title;
|
||||||
uint8_t c;
|
uint8_t c;
|
||||||
uint32_t f;
|
|
||||||
|
|
||||||
reader = std::make_unique<WAVFileReader>();
|
reader = std::make_unique<WAVFileReader>();
|
||||||
|
|
||||||
@ -248,17 +246,7 @@ SoundBoardView::SoundBoardView(
|
|||||||
&button_exit
|
&button_exit
|
||||||
});
|
});
|
||||||
|
|
||||||
// Populate CTCSS list
|
ctcss_populate(options_ctcss);
|
||||||
ctcss_options.emplace_back(std::make_pair("None", 0));
|
|
||||||
for (c = 0; c < CTCSS_TONES_NB; c++) {
|
|
||||||
f = (uint32_t)(ctcss_tones[c].frequency * 10);
|
|
||||||
f_string = ctcss_tones[c].PL_code;
|
|
||||||
f_string += " " + to_string_dec_uint(f / 10) + "." + to_string_dec_uint(f % 10);
|
|
||||||
ctcss_options.emplace_back(f_string, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
options_ctcss.set_options(ctcss_options);
|
|
||||||
|
|
||||||
options_ctcss.set_selected_index(0);
|
options_ctcss.set_selected_index(0);
|
||||||
|
|
||||||
const auto button_fn = [this](Button& button) {
|
const auto button_fn = [this](Button& button) {
|
||||||
|
@ -122,7 +122,7 @@ TransmitterView::TransmitterView(
|
|||||||
});
|
});
|
||||||
|
|
||||||
field_bw.on_change = [this](int32_t bandwidth) {
|
field_bw.on_change = [this](int32_t bandwidth) {
|
||||||
on_bandwidth_changed(bandwidth);
|
on_bandwidth_changed(bandwidth * 1000);
|
||||||
};
|
};
|
||||||
field_bw.set_value(bandwidth);
|
field_bw.set_value(bandwidth);
|
||||||
}
|
}
|
||||||
|
@ -32,28 +32,9 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
void AudioInput::configure(
|
|
||||||
const iir_biquad_config_t& hpf_config,
|
|
||||||
const float squelch_threshold
|
|
||||||
) {
|
|
||||||
//hpf.configure(hpf_config);
|
|
||||||
//squelch.set_threshold(squelch_threshold);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioInput::read_audio_buffer(buffer_s16_t& audio) {
|
void AudioInput::read_audio_buffer(buffer_s16_t& audio) {
|
||||||
//std::array<int16_t, 32> audio_int;
|
|
||||||
|
|
||||||
auto audio_buffer = audio::dma::rx_empty_buffer();
|
auto audio_buffer = audio::dma::rx_empty_buffer();
|
||||||
|
|
||||||
for(size_t i=0; i<audio_buffer.count; i++) {
|
for (size_t i=0; i<audio_buffer.count; i++)
|
||||||
//const int32_t sample_int = audio.p[i] * k;
|
|
||||||
//const int32_t sample_saturated = __SSAT(sample_int, 16);
|
|
||||||
audio.p[i] = audio_buffer.p[i].left;
|
audio.p[i] = audio_buffer.p[i].left;
|
||||||
//audio_int[i] = sample_saturated;
|
|
||||||
}
|
|
||||||
/*if( stream && send_to_fifo ) {
|
|
||||||
stream->write(audio_int.data(), audio_buffer.count * sizeof(audio_int[0]));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//feed_audio_stats(audio);
|
|
||||||
}
|
}
|
||||||
|
@ -35,27 +35,13 @@
|
|||||||
|
|
||||||
class AudioInput {
|
class AudioInput {
|
||||||
public:
|
public:
|
||||||
void configure(
|
|
||||||
const iir_biquad_config_t& hpf_config,
|
|
||||||
const float squelch_threshold = 0.0f
|
|
||||||
);
|
|
||||||
|
|
||||||
void read_audio_buffer(buffer_s16_t& audio);
|
void read_audio_buffer(buffer_s16_t& audio);
|
||||||
|
|
||||||
/*void set_stream(std::unique_ptr<StreamInput> new_stream) {
|
|
||||||
stream = std::move(new_stream);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr float k = 32768.0f;
|
/*static constexpr float k = 32768.0f;
|
||||||
static constexpr float ki = 1.0f / k;
|
static constexpr float ki = 1.0f / k;
|
||||||
|
|
||||||
IIRBiquadFilter hpf { };
|
IIRBiquadFilter hpf { };*/
|
||||||
//FMSquelch squelch { };
|
|
||||||
|
|
||||||
//std::unique_ptr<StreamInput> stream { };
|
|
||||||
|
|
||||||
//AudioStatsCollector audio_stats { };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif/*__AUDIO_INPUT_H__*/
|
#endif/*__AUDIO_INPUT_H__*/
|
||||||
|
@ -37,13 +37,14 @@ void MicTXProcessor::execute(const buffer_c8_t& buffer){
|
|||||||
|
|
||||||
for (size_t i = 0; i<buffer.count; i++) {
|
for (size_t i = 0; i<buffer.count; i++) {
|
||||||
|
|
||||||
sample = audio_buffer.p[i >> 6] >> 8;
|
sample = audio_buffer.p[i >> 6] >> 8; // 1536000 / 64 = 24000
|
||||||
|
sample = (sample * (int32_t)gain_x10) / 10;
|
||||||
|
|
||||||
power += (sample < 0) ? -sample : sample;
|
power += (sample < 0) ? -sample : sample; // Power mean for UI vu-meter
|
||||||
|
|
||||||
if (!as) {
|
if (!as) {
|
||||||
as = divider;
|
as = divider;
|
||||||
level_message.value = power / (divider / 4);
|
level_message.value = power / (divider / 4); // Why ?
|
||||||
shared_memory.application_queue.push(level_message);
|
shared_memory.application_queue.push(level_message);
|
||||||
power = 0;
|
power = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -59,13 +60,18 @@ void MicTXProcessor::execute(const buffer_c8_t& buffer){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FM
|
// FM
|
||||||
delta = sample_mixed * fm_delta;
|
if (fm_delta) {
|
||||||
|
delta = sample_mixed * fm_delta;
|
||||||
phase += delta;
|
|
||||||
sphase = phase + (64 << 24);
|
phase += delta;
|
||||||
|
sphase = phase + (64 << 24);
|
||||||
|
|
||||||
re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]);
|
re = (sine_table_i8[(sphase & 0xFF000000U) >> 24]);
|
||||||
im = (sine_table_i8[(phase & 0xFF000000U) >> 24]);
|
im = (sine_table_i8[(phase & 0xFF000000U) >> 24]);
|
||||||
|
} else {
|
||||||
|
re = 0;
|
||||||
|
im = 0;
|
||||||
|
}
|
||||||
|
|
||||||
buffer.p[i] = {re, im};
|
buffer.p[i] = {re, im};
|
||||||
}
|
}
|
||||||
@ -77,6 +83,7 @@ void MicTXProcessor::on_message(const Message* const msg) {
|
|||||||
switch(msg->id) {
|
switch(msg->id) {
|
||||||
case Message::ID::AudioTXConfig:
|
case Message::ID::AudioTXConfig:
|
||||||
fm_delta = message.fm_delta * (0xFFFFFFULL / 1536000);
|
fm_delta = message.fm_delta * (0xFFFFFFULL / 1536000);
|
||||||
|
gain_x10 = message.gain_x10;
|
||||||
divider = message.divider;
|
divider = message.divider;
|
||||||
ctcss_enabled = message.ctcss_enabled;
|
ctcss_enabled = message.ctcss_enabled;
|
||||||
ctcss_phase_inc = message.ctcss_phase_inc;
|
ctcss_phase_inc = message.ctcss_phase_inc;
|
||||||
|
@ -46,7 +46,7 @@ private:
|
|||||||
|
|
||||||
AudioInput audio_input { };
|
AudioInput audio_input { };
|
||||||
|
|
||||||
uint32_t divider { };
|
uint32_t divider { }, gain_x10 { };
|
||||||
uint32_t as { 0 };
|
uint32_t as { 0 };
|
||||||
uint32_t fm_delta { 0 };
|
uint32_t fm_delta { 0 };
|
||||||
bool ctcss_enabled { false };
|
bool ctcss_enabled { false };
|
||||||
|
@ -645,11 +645,13 @@ public:
|
|||||||
constexpr AudioTXConfigMessage(
|
constexpr AudioTXConfigMessage(
|
||||||
const uint32_t divider,
|
const uint32_t divider,
|
||||||
const uint32_t fm_delta,
|
const uint32_t fm_delta,
|
||||||
|
const uint32_t gain_x10,
|
||||||
const uint32_t ctcss_phase_inc,
|
const uint32_t ctcss_phase_inc,
|
||||||
const bool ctcss_enabled
|
const bool ctcss_enabled
|
||||||
) : Message { ID::AudioTXConfig },
|
) : Message { ID::AudioTXConfig },
|
||||||
divider(divider),
|
divider(divider),
|
||||||
fm_delta(fm_delta),
|
fm_delta(fm_delta),
|
||||||
|
gain_x10(gain_x10),
|
||||||
ctcss_phase_inc(ctcss_phase_inc),
|
ctcss_phase_inc(ctcss_phase_inc),
|
||||||
ctcss_enabled(ctcss_enabled)
|
ctcss_enabled(ctcss_enabled)
|
||||||
{
|
{
|
||||||
@ -657,6 +659,7 @@ public:
|
|||||||
|
|
||||||
const uint32_t divider;
|
const uint32_t divider;
|
||||||
const uint32_t fm_delta;
|
const uint32_t fm_delta;
|
||||||
|
const uint32_t gain_x10;
|
||||||
const uint32_t ctcss_phase_inc;
|
const uint32_t ctcss_phase_inc;
|
||||||
const bool ctcss_enabled;
|
const bool ctcss_enabled;
|
||||||
};
|
};
|
||||||
|
@ -376,20 +376,6 @@ void Labels::paint(Painter& painter) {
|
|||||||
|
|
||||||
/* BigFrequency **********************************************************/
|
/* BigFrequency **********************************************************/
|
||||||
|
|
||||||
const uint8_t big_segment_font[11] = {
|
|
||||||
0b00111111, // 0: ABCDEF
|
|
||||||
0b00000110, // 1: AB
|
|
||||||
0b01011011, // 2: ABDEG
|
|
||||||
0b01001111, // 3: ABCDG
|
|
||||||
0b01100110, // 4: BCFG
|
|
||||||
0b01101101, // 5: ACDFG
|
|
||||||
0b01111101, // 6: ACDEFG
|
|
||||||
0b00000111, // 7: ABC
|
|
||||||
0b01111111, // 8: ABCDEFG
|
|
||||||
0b01101111, // 9: ABCDFG
|
|
||||||
0b01000000 // -: G
|
|
||||||
};
|
|
||||||
|
|
||||||
BigFrequency::BigFrequency(
|
BigFrequency::BigFrequency(
|
||||||
Rect parent_rect,
|
Rect parent_rect,
|
||||||
rf::Frequency frequency
|
rf::Frequency frequency
|
||||||
@ -404,7 +390,7 @@ void BigFrequency::set(const rf::Frequency frequency) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BigFrequency::paint(Painter& painter) {
|
void BigFrequency::paint(Painter& painter) {
|
||||||
uint8_t i, digit_def;
|
uint32_t i, digit_def;
|
||||||
char digits[7];
|
char digits[7];
|
||||||
char digit;
|
char digit;
|
||||||
Coord digit_x, digit_y;
|
Coord digit_x, digit_y;
|
||||||
@ -443,7 +429,7 @@ void BigFrequency::paint(Painter& painter) {
|
|||||||
digit = digits[i];
|
digit = digits[i];
|
||||||
digit_y = rect.location().y();
|
digit_y = rect.location().y();
|
||||||
if (digit < 16) {
|
if (digit < 16) {
|
||||||
digit_def = big_segment_font[(uint8_t)digit];
|
digit_def = segment_font[(uint8_t)digit];
|
||||||
if (digit_def & 0x01) painter.fill_rectangle({{digit_x + 4, digit_y}, {20, 4}}, segment_color);
|
if (digit_def & 0x01) painter.fill_rectangle({{digit_x + 4, digit_y}, {20, 4}}, segment_color);
|
||||||
if (digit_def & 0x02) painter.fill_rectangle({{digit_x + 24, digit_y + 4}, {4, 20}}, segment_color);
|
if (digit_def & 0x02) painter.fill_rectangle({{digit_x + 24, digit_y + 4}, {4, 20}}, segment_color);
|
||||||
if (digit_def & 0x04) painter.fill_rectangle({{digit_x + 24, digit_y + 28}, {4, 20}}, segment_color);
|
if (digit_def & 0x04) painter.fill_rectangle({{digit_x + 24, digit_y + 28}, {4, 20}}, segment_color);
|
||||||
@ -1236,13 +1222,13 @@ uint64_t SymField::value_hex_u64() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t SymField::value(const uint32_t index) {
|
uint32_t SymField::get_sym(const uint32_t index) {
|
||||||
if (index >= length_) return 0;
|
if (index >= length_) return 0;
|
||||||
|
|
||||||
return values_[index];
|
return values_[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymField::set_value(const uint32_t index, const uint32_t new_value) {
|
void SymField::set_sym(const uint32_t index, const uint32_t new_value) {
|
||||||
if (index >= length_) return;
|
if (index >= length_) return;
|
||||||
|
|
||||||
uint32_t clipped_value = clip_value(index, new_value);
|
uint32_t clipped_value = clip_value(index, new_value);
|
||||||
@ -1263,7 +1249,7 @@ void SymField::set_length(const uint32_t new_length) {
|
|||||||
|
|
||||||
// Clip eventual garbage from previous shorter word
|
// Clip eventual garbage from previous shorter word
|
||||||
for (size_t n = 0; n < length_; n++)
|
for (size_t n = 0; n < length_; n++)
|
||||||
set_value(n, values_[n]);
|
set_sym(n, values_[n]);
|
||||||
|
|
||||||
erase_prev_ = true;
|
erase_prev_ = true;
|
||||||
set_dirty();
|
set_dirty();
|
||||||
@ -1276,7 +1262,7 @@ void SymField::set_symbol_list(const uint32_t index, const std::string symbol_li
|
|||||||
symbol_list_[index] = symbol_list;
|
symbol_list_[index] = symbol_list;
|
||||||
|
|
||||||
// Re-clip symbol's value
|
// Re-clip symbol's value
|
||||||
set_value(index, values_[index]);
|
set_sym(index, values_[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymField::paint(Painter& painter) {
|
void SymField::paint(Painter& painter) {
|
||||||
@ -1338,7 +1324,7 @@ bool SymField::on_encoder(const EncoderEvent delta) {
|
|||||||
int32_t new_value = (int)values_[selected_] + delta;
|
int32_t new_value = (int)values_[selected_] + delta;
|
||||||
|
|
||||||
if (new_value >= 0)
|
if (new_value >= 0)
|
||||||
set_value(selected_, values_[selected_] + delta);
|
set_sym(selected_, values_[selected_] + delta);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1419,10 +1405,10 @@ void Waveform::paint(Painter& painter) {
|
|||||||
|
|
||||||
if (n) {
|
if (n) {
|
||||||
if (y != prev_y)
|
if (y != prev_y)
|
||||||
painter.draw_vline( {x, y_offset}, h, color_);
|
painter.draw_vline( {(Coord)x, y_offset}, h, color_);
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.draw_hline( {x, y_offset + y}, ceil(x_inc), color_);
|
painter.draw_hline( {(Coord)x, y_offset + y}, ceil(x_inc), color_);
|
||||||
|
|
||||||
prev_y = y;
|
prev_y = y;
|
||||||
x += x_inc;
|
x += x_inc;
|
||||||
@ -1443,4 +1429,89 @@ void Waveform::paint(Painter& painter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* VuMeter **************************************************************/
|
||||||
|
|
||||||
|
VuMeter::VuMeter(
|
||||||
|
Rect parent_rect,
|
||||||
|
uint32_t LEDs
|
||||||
|
) : Widget { parent_rect },
|
||||||
|
LEDs_ { LEDs },
|
||||||
|
height { parent_rect.size().height() }
|
||||||
|
{
|
||||||
|
//set_focusable(false);
|
||||||
|
LED_height = height / LEDs;
|
||||||
|
split = 256 / LEDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VuMeter::set_value(const uint8_t new_value) {
|
||||||
|
value_ = new_value;
|
||||||
|
set_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VuMeter::set_mark(const uint8_t new_mark) {
|
||||||
|
if (new_mark != mark) {
|
||||||
|
mark = new_mark;
|
||||||
|
set_dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VuMeter::paint(Painter& painter) {
|
||||||
|
Point pos = screen_rect().location();
|
||||||
|
Dim width = screen_rect().size().width() - 4;
|
||||||
|
|
||||||
|
if (value_ != prev_value) {
|
||||||
|
uint32_t bar;
|
||||||
|
Color color;
|
||||||
|
bool lit = false;
|
||||||
|
uint32_t bar_level = LEDs_ - ((value_ + 1) / split);
|
||||||
|
// Draw LEDs
|
||||||
|
for (bar = 0; bar < LEDs_; bar++) {
|
||||||
|
if (bar >= bar_level)
|
||||||
|
lit = true;
|
||||||
|
|
||||||
|
if (bar == 0)
|
||||||
|
color = lit ? Color::red() : Color::dark_red();
|
||||||
|
else if (bar == 1)
|
||||||
|
color = lit ? Color::orange() : Color::dark_orange();
|
||||||
|
else if ((bar == 2) || (bar == 3))
|
||||||
|
color = lit ? Color::yellow() : Color::dark_yellow();
|
||||||
|
else
|
||||||
|
color = lit ? Color::green() : Color::dark_green();
|
||||||
|
|
||||||
|
painter.fill_rectangle({ pos.x(), pos.y() + (Coord)(bar * LED_height), width, (Coord)LED_height - 2 }, color);
|
||||||
|
}
|
||||||
|
prev_value = value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update max level
|
||||||
|
if (value_ > max) {
|
||||||
|
max = value_;
|
||||||
|
hold_timer = 30; // 0.5s @ 60Hz
|
||||||
|
} else {
|
||||||
|
if (hold_timer) {
|
||||||
|
hold_timer--;
|
||||||
|
} else {
|
||||||
|
if (max) max--; // Let it drop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw max level
|
||||||
|
if (max != prev_max) {
|
||||||
|
painter.draw_hline({ pos.x() + width, pos.y() + height - (height * prev_max) / 256 }, 8, Color::black());
|
||||||
|
painter.draw_hline({ pos.x() + width, pos.y() + height - (height * max) / 256 }, 8, Color::white());
|
||||||
|
if (prev_max == mark)
|
||||||
|
prev_mark = 0; // Force mark refresh
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw mark
|
||||||
|
if ((mark != prev_mark) && (mark)) {
|
||||||
|
painter.draw_hline({ pos.x() + width, pos.y() + height - (height * prev_mark) / 256 }, 8, Color::black());
|
||||||
|
painter.draw_hline({ pos.x() + width, pos.y() + height - (height * mark) / 256 }, 8, Color::grey());
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_max = max;
|
||||||
|
prev_mark = mark;
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace ui */
|
} /* namespace ui */
|
||||||
|
@ -245,6 +245,20 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
rf::Frequency _frequency;
|
rf::Frequency _frequency;
|
||||||
|
|
||||||
|
const uint8_t segment_font[11] = {
|
||||||
|
0b00111111, // 0: ABCDEF
|
||||||
|
0b00000110, // 1: AB
|
||||||
|
0b01011011, // 2: ABDEG
|
||||||
|
0b01001111, // 3: ABCDG
|
||||||
|
0b01100110, // 4: BCFG
|
||||||
|
0b01101101, // 5: ACDFG
|
||||||
|
0b01111101, // 6: ACDEFG
|
||||||
|
0b00000111, // 7: ABC
|
||||||
|
0b01111111, // 8: ABCDEFG
|
||||||
|
0b01101111, // 9: ABCDFG
|
||||||
|
0b01000000 // -: G
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProgressBar : public Widget {
|
class ProgressBar : public Widget {
|
||||||
@ -511,8 +525,8 @@ public:
|
|||||||
SymField(const SymField&) = delete;
|
SymField(const SymField&) = delete;
|
||||||
SymField(SymField&&) = delete;
|
SymField(SymField&&) = delete;
|
||||||
|
|
||||||
uint32_t value(const uint32_t index);
|
uint32_t get_sym(const uint32_t index);
|
||||||
void set_value(const uint32_t index, const uint32_t new_value);
|
void set_sym(const uint32_t index, const uint32_t new_value);
|
||||||
void set_length(const uint32_t new_length);
|
void set_length(const uint32_t new_length);
|
||||||
void set_symbol_list(const uint32_t index, const std::string symbol_list);
|
void set_symbol_list(const uint32_t index, const std::string symbol_list);
|
||||||
uint32_t value_dec_u32();
|
uint32_t value_dec_u32();
|
||||||
@ -558,6 +572,24 @@ private:
|
|||||||
Color color_;
|
Color color_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VuMeter : public Widget {
|
||||||
|
public:
|
||||||
|
|
||||||
|
VuMeter(Rect parent_rect, uint32_t LEDs);
|
||||||
|
|
||||||
|
void set_value(const uint8_t new_value);
|
||||||
|
void set_mark(const uint8_t new_mark);
|
||||||
|
|
||||||
|
void paint(Painter& painter) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t LEDs_, LED_height { 0 };
|
||||||
|
uint32_t value_ { 0 }, prev_value { 0 };
|
||||||
|
uint32_t split { 0 };
|
||||||
|
uint16_t max { 0 }, prev_max { 0 }, hold_timer { 0 }, mark { 0 }, prev_mark { 0 };
|
||||||
|
int height;
|
||||||
|
};
|
||||||
|
|
||||||
} /* namespace ui */
|
} /* namespace ui */
|
||||||
|
|
||||||
#endif/*__UI_WIDGET_H__*/
|
#endif/*__UI_WIDGET_H__*/
|
||||||
|
Before Width: | Height: | Size: 135 B After Width: | Height: | Size: 138 B |
Before Width: | Height: | Size: 112 B After Width: | Height: | Size: 121 B |
BIN
firmware/graphics/icon_setup.png
Normal file
After Width: | Height: | Size: 108 B |
Before Width: | Height: | Size: 109 B After Width: | Height: | Size: 115 B |
BIN
firmware/graphics/icon_utilities.png
Normal file
After Width: | Height: | Size: 144 B |