mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-02-28 20:37:20 +00:00
Using new CPLD data (fixes spectrum mirroring)
Scanner bugfix for wide ranges Added squelch parameter for NFM receiver Adjustment to Vumeter widget rendering
This commit is contained in:
parent
042d271a9f
commit
e2f0a03460
@ -28,7 +28,7 @@ set(CHIBIOS_PORTAPACK ${PROJECT_SOURCE_DIR}/chibios-portapack)
|
||||
set(HACKRF_FIRMWARE_FILENAME hackrf_one_usb.dfu)
|
||||
set(HACKRF_FIRMWARE_IMAGE ${PROJECT_SOURCE_DIR}/${HACKRF_FIRMWARE_FILENAME})
|
||||
|
||||
set(HACKRF_CPLD_SVF_FILENAME hackrf_cpld_default.svf)
|
||||
set(HACKRF_CPLD_SVF_FILENAME hackrf_cpld_portapack.svf)
|
||||
set(HACKRF_CPLD_SVF_PATH ${PROJECT_SOURCE_DIR}/${HACKRF_CPLD_SVF_FILENAME})
|
||||
|
||||
set(EXTRACT_CPLD_DATA ${PROJECT_SOURCE_DIR}/tools/extract_cpld_data.py)
|
||||
|
@ -66,12 +66,19 @@ NBFMOptionsView::NBFMOptionsView(
|
||||
add_children({
|
||||
&label_config,
|
||||
&options_config,
|
||||
&text_squelch,
|
||||
&field_squelch
|
||||
});
|
||||
|
||||
options_config.set_selected_index(receiver_model.nbfm_configuration());
|
||||
options_config.on_change = [this](size_t n, OptionsField::value_t) {
|
||||
receiver_model.set_nbfm_configuration(n);
|
||||
};
|
||||
|
||||
field_squelch.set_value(receiver_model.squelch_level());
|
||||
field_squelch.on_change = [this](int32_t v) {
|
||||
receiver_model.set_squelch_level(v);
|
||||
};
|
||||
}
|
||||
|
||||
/* AnalogAudioView *******************************************************/
|
||||
|
@ -78,6 +78,19 @@ private:
|
||||
{ "16k ", 0 },
|
||||
}
|
||||
};
|
||||
|
||||
Text text_squelch {
|
||||
{ 9 * 8, 0 * 16, 7 * 8, 1 * 16 },
|
||||
"SQ /100"
|
||||
};
|
||||
|
||||
NumberField field_squelch {
|
||||
{ 12 * 8, 0 * 16 },
|
||||
3,
|
||||
{ 0, 100 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
};
|
||||
|
||||
class AnalogAudioView : public View {
|
||||
|
@ -52,7 +52,7 @@ void AMConfig::apply() const {
|
||||
audio::set_rate(audio::Rate::Hz_12000);
|
||||
}
|
||||
|
||||
void NBFMConfig::apply() const {
|
||||
void NBFMConfig::apply(const uint8_t squelch_level) const {
|
||||
const NBFMConfigureMessage message {
|
||||
decim_0,
|
||||
decim_1,
|
||||
@ -60,7 +60,8 @@ void NBFMConfig::apply() const {
|
||||
2,
|
||||
deviation,
|
||||
audio_24k_hpf_300hz_config,
|
||||
audio_24k_deemph_300_6_config
|
||||
audio_24k_deemph_300_6_config,
|
||||
squelch_level
|
||||
};
|
||||
send_message(&message);
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
|
@ -48,7 +48,7 @@ struct NBFMConfig {
|
||||
const fir_taps_real<32> channel;
|
||||
const size_t deviation;
|
||||
|
||||
void apply() const;
|
||||
void apply(const uint8_t squelch_level) const;
|
||||
};
|
||||
|
||||
struct WFMConfig {
|
||||
|
@ -153,6 +153,15 @@ void ReceiverModel::set_headphone_volume(volume_t v) {
|
||||
update_headphone_volume();
|
||||
}
|
||||
|
||||
uint8_t ReceiverModel::squelch_level() const {
|
||||
return squelch_level_;
|
||||
}
|
||||
|
||||
void ReceiverModel::set_squelch_level(uint8_t v) {
|
||||
squelch_level_ = v;
|
||||
update_modulation();
|
||||
}
|
||||
|
||||
void ReceiverModel::enable() {
|
||||
enabled_ = true;
|
||||
radio::set_direction(rf::Direction::Receive);
|
||||
@ -286,7 +295,7 @@ size_t ReceiverModel::nbfm_configuration() const {
|
||||
}
|
||||
|
||||
void ReceiverModel::update_nbfm_configuration() {
|
||||
nbfm_configs[nbfm_config_index].apply();
|
||||
nbfm_configs[nbfm_config_index].apply(squelch_level_);
|
||||
}
|
||||
|
||||
size_t ReceiverModel::wfm_configuration() const {
|
||||
|
@ -72,6 +72,9 @@ public:
|
||||
volume_t headphone_volume() const;
|
||||
void set_headphone_volume(volume_t v);
|
||||
|
||||
uint8_t squelch_level() const;
|
||||
void set_squelch_level(uint8_t v);
|
||||
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
@ -99,6 +102,7 @@ private:
|
||||
size_t nbfm_config_index = 0;
|
||||
size_t wfm_config_index = 0;
|
||||
volume_t headphone_volume_ { -43.0_dB };
|
||||
uint8_t squelch_level_ { 80 };
|
||||
|
||||
int32_t tuning_offset();
|
||||
|
||||
|
@ -66,6 +66,14 @@ void ScannerView::do_detection() {
|
||||
rtc::RTC datetime;
|
||||
std::string str_approx, str_timestamp;
|
||||
|
||||
// Display spectrum
|
||||
bin_skip_acc = 0;
|
||||
pixel_index = 0;
|
||||
display.draw_pixels(
|
||||
{ { 0, 88 }, { (Dim)spectrum_row.size(), 1 } },
|
||||
spectrum_row
|
||||
);
|
||||
|
||||
mean_power = mean_acc / (SCAN_BIN_NB_NO_DC * slices_nb);
|
||||
mean_acc = 0;
|
||||
|
||||
@ -79,8 +87,8 @@ void ScannerView::do_detection() {
|
||||
|
||||
if ((power >= mean_power + power_threshold) && (power > power_max)) {
|
||||
power_max = power;
|
||||
bin_max_pixel = slices[slice].max_index;
|
||||
bin_max = bin_max_pixel + (slice * SCAN_BIN_NB);
|
||||
bin_max = slices[slice].max_index + (slice * SCAN_BIN_NB);
|
||||
bin_max_pixel = bin_max / slices_nb;
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,63 +165,65 @@ void ScannerView::do_detection() {
|
||||
scan_counter++;
|
||||
|
||||
// Refresh red tick
|
||||
portapack::display.fill_rectangle({last_pos, 90, 1, 6}, Color::black());
|
||||
portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Color::black());
|
||||
if (bin_max > -1) {
|
||||
if (bin_max_pixel < 120)
|
||||
bin_max_pixel += 2;
|
||||
//if (bin_max_pixel < 120)
|
||||
// bin_max_pixel += 2;
|
||||
//else
|
||||
// bin_max_pixel -= 0;
|
||||
last_pos = (ui::Coord)bin_max_pixel;
|
||||
portapack::display.fill_rectangle({last_pos, 90, 1, 6}, Color::red());
|
||||
last_tick_pos = (Coord)bin_max_pixel;
|
||||
portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Color::red());
|
||||
}
|
||||
}
|
||||
|
||||
void ScannerView::add_spectrum_pixel(Color color) {
|
||||
// Is avoiding floats really needed ?
|
||||
bin_skip_acc += bin_skip_frac;
|
||||
if (bin_skip_acc < 0x10000)
|
||||
return;
|
||||
|
||||
bin_skip_acc -= 0x10000;
|
||||
|
||||
if (pixel_index < 240)
|
||||
spectrum_row[pixel_index++] = color;
|
||||
}
|
||||
|
||||
void ScannerView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
uint8_t power_max = 0;
|
||||
int16_t bin_max = 0;
|
||||
uint8_t max_power = 0;
|
||||
int16_t max_bin = 0;
|
||||
uint8_t power;
|
||||
size_t bin;
|
||||
std::array<Color, 240> pixel_row;
|
||||
|
||||
baseband::spectrum_streaming_stop();
|
||||
|
||||
// Draw spectrum line
|
||||
for (bin = 0; bin < 120; bin++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[134 + bin]]; // 134~253 in 0~119
|
||||
pixel_row[bin] = pixel_color;
|
||||
}
|
||||
for (bin = 120; bin < 240; bin++) {
|
||||
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[bin - 118]]; // 2~121 in 120~239
|
||||
pixel_row[bin] = pixel_color;
|
||||
}
|
||||
display.draw_pixels(
|
||||
{ { 0, 88 + slice_counter * 2 }, { pixel_row.size(), 1 } },
|
||||
pixel_row
|
||||
);
|
||||
|
||||
// Find max power for this slice
|
||||
// Add pixels to spectrum row, and find max power for this slice
|
||||
// Leftmost and rightmost 2 bins are ignored
|
||||
// Center 12 bins are ignored
|
||||
// 256-2-2-12 = 240 bins used
|
||||
for (bin = 0; bin < 120; bin++) {
|
||||
add_spectrum_pixel(spectrum_rgb3_lut[spectrum.db[134 + bin]]); // 134~253 goes in 0~119
|
||||
power = spectrum.db[134 + bin];
|
||||
mean_acc += power;
|
||||
if (power > power_max) {
|
||||
power_max = power;
|
||||
bin_max = bin - 2;
|
||||
if (power > max_power) {
|
||||
max_power = power;
|
||||
max_bin = bin - 2; // To check
|
||||
}
|
||||
}
|
||||
for (bin = 120; bin < 240; bin++) {
|
||||
add_spectrum_pixel(spectrum_rgb3_lut[spectrum.db[bin - 118]]); // 2~121 goes in 120~239
|
||||
power = spectrum.db[bin - 118];
|
||||
mean_acc += power;
|
||||
if (power > power_max) {
|
||||
power_max = power;
|
||||
bin_max = bin + 2;
|
||||
if (power > max_power) {
|
||||
max_power = power;
|
||||
max_bin = bin + 2; // To check
|
||||
}
|
||||
}
|
||||
|
||||
slices[slice_counter].max_power = power_max;
|
||||
slices[slice_counter].max_index = bin_max;
|
||||
slices[slice_counter].max_power = max_power;
|
||||
slices[slice_counter].max_index = max_bin;
|
||||
|
||||
// Slice update
|
||||
if (slices_nb > 1) {
|
||||
// Slice sequence
|
||||
slice_counter++;
|
||||
if (slice_counter >= slices_nb) {
|
||||
do_detection();
|
||||
@ -221,6 +231,7 @@ void ScannerView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
}
|
||||
receiver_model.set_tuning_frequency(slices[slice_counter].center_frequency);
|
||||
} else {
|
||||
// Unique slice
|
||||
do_detection();
|
||||
}
|
||||
|
||||
@ -273,6 +284,8 @@ void ScannerView::on_range_changed() {
|
||||
text_slices.set(" 1");
|
||||
}
|
||||
|
||||
bin_skip_frac = 0x10000 / slices_nb;
|
||||
|
||||
slice_counter = 0;
|
||||
}
|
||||
|
||||
@ -353,7 +366,7 @@ ScannerView::ScannerView(
|
||||
&recent_entries_view
|
||||
});
|
||||
|
||||
baseband::set_spectrum(SCAN_SLICE_WIDTH, 32);
|
||||
baseband::set_spectrum(SCAN_SLICE_WIDTH, 31);
|
||||
|
||||
recent_entries_view.set_parent_rect({ 0, 28 * 8, 240, 12 * 8 });
|
||||
recent_entries_view.on_select = [this, &nav](const ScannerRecentEntry& entry) {
|
||||
|
@ -112,6 +112,9 @@ private:
|
||||
int16_t index;
|
||||
} slices[32];
|
||||
|
||||
uint32_t bin_skip_acc { 0 }, bin_skip_frac { };
|
||||
uint32_t pixel_index { 0 };
|
||||
std::array<Color, 240> spectrum_row { 0 };
|
||||
ChannelSpectrumFIFO* fifo { nullptr };
|
||||
rf::Frequency f_min { 0 }, f_max { 0 };
|
||||
uint8_t detect_timer { 0 }, release_timer { 0 }, timing_div { 0 };
|
||||
@ -123,7 +126,7 @@ private:
|
||||
uint8_t slices_nb { 0 };
|
||||
uint8_t slice_counter { 0 };
|
||||
int16_t last_bin { 0 };
|
||||
Coord last_pos { 0 };
|
||||
Coord last_tick_pos { 0 };
|
||||
rf::Frequency scan_span { 0 }, resolved_frequency { 0 };
|
||||
uint16_t locked_bin { 0 };
|
||||
uint8_t scan_counter { 0 };
|
||||
@ -135,6 +138,7 @@ private:
|
||||
void on_lna_changed(int32_t v_db);
|
||||
void on_vga_changed(int32_t v_db);
|
||||
void do_timers();
|
||||
void add_spectrum_pixel(Color color);
|
||||
|
||||
const RecentEntriesColumns columns { {
|
||||
{ "Frequency", 9 },
|
||||
@ -149,7 +153,7 @@ private:
|
||||
{ { 1 * 8, 4 * 8 }, "Trig: /255 Mean: /255", Color::light_grey() },
|
||||
{ { 1 * 8, 6 * 8 }, "Slices: /32 Rate: Hz", Color::light_grey() },
|
||||
{ { 6 * 8, 10 * 8 }, "Timer Status", Color::light_grey() },
|
||||
{ { 1 * 8, 25 * 8 }, "Accuracy: +/-4.9kHz", Color::light_grey() },
|
||||
{ { 1 * 8, 25 * 8 }, "Accuracy +/-4.9kHz", Color::light_grey() },
|
||||
{ { 26 * 8, 25 * 8 }, "MHz", Color::light_grey() }
|
||||
};
|
||||
|
||||
@ -188,12 +192,12 @@ private:
|
||||
|
||||
VuMeter vu_max {
|
||||
{ 1 * 8, 11 * 8 - 4, 3 * 8, 48 },
|
||||
16,
|
||||
18,
|
||||
false
|
||||
};
|
||||
|
||||
ProgressBar progress_timers {
|
||||
{ 6 * 8, 12 * 8, 5 * 8, 16 }
|
||||
{ 6 * 8, 12 * 8, 6 * 8, 16 }
|
||||
};
|
||||
Text text_infos {
|
||||
{ 13 * 8, 12 * 8, 15 * 8, 16 },
|
||||
@ -203,11 +207,11 @@ private:
|
||||
Checkbox check_snap {
|
||||
{ 6 * 8, 15 * 8 },
|
||||
7,
|
||||
"Adjust:",
|
||||
"Snap to:",
|
||||
true
|
||||
};
|
||||
OptionsField options_snap {
|
||||
{ 15 * 8, 15 * 8 },
|
||||
{ 17 * 8, 15 * 8 },
|
||||
7,
|
||||
{
|
||||
{ "25kHz ", 25000 },
|
||||
|
@ -112,8 +112,7 @@ void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
|
||||
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
|
||||
channel_spectrum.set_decimation_factor(std::floor(channel_filter_output_fs / (channel_filter_pass_f + channel_filter_stop_f)));
|
||||
// TODO: Configurable squelch threshold
|
||||
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, 0.8f);
|
||||
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0);
|
||||
|
||||
synth_acc = 0;
|
||||
|
||||
|
@ -23,8 +23,6 @@
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "dsp_fft.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
|
@ -356,7 +356,8 @@ public:
|
||||
const size_t channel_decimation,
|
||||
const size_t deviation,
|
||||
const iir_biquad_config_t audio_hpf_config,
|
||||
const iir_biquad_config_t audio_deemph_config
|
||||
const iir_biquad_config_t audio_deemph_config,
|
||||
const uint8_t squelch_level
|
||||
) : Message { ID::NBFMConfigure },
|
||||
decim_0_filter(decim_0_filter),
|
||||
decim_1_filter(decim_1_filter),
|
||||
@ -364,7 +365,8 @@ public:
|
||||
channel_decimation { channel_decimation },
|
||||
deviation { deviation },
|
||||
audio_hpf_config(audio_hpf_config),
|
||||
audio_deemph_config(audio_deemph_config)
|
||||
audio_deemph_config(audio_deemph_config),
|
||||
squelch_level(squelch_level)
|
||||
{
|
||||
}
|
||||
|
||||
@ -375,6 +377,7 @@ public:
|
||||
const size_t deviation;
|
||||
const iir_biquad_config_t audio_hpf_config;
|
||||
const iir_biquad_config_t audio_deemph_config;
|
||||
const uint8_t squelch_level;
|
||||
};
|
||||
|
||||
class WFMConfigureMessage : public Message {
|
||||
|
@ -1461,7 +1461,7 @@ VuMeter::VuMeter(
|
||||
show_max_ { show_max }
|
||||
{
|
||||
//set_focusable(false);
|
||||
LED_height = parent_rect.size().height() / LEDs;
|
||||
LED_height = std::max(1UL, parent_rect.size().height() / LEDs);
|
||||
split = 256 / LEDs;
|
||||
}
|
||||
|
||||
@ -1507,7 +1507,7 @@ void VuMeter::paint(Painter& painter) {
|
||||
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);
|
||||
painter.fill_rectangle({ pos.x(), pos.y() + (Coord)(bar * (LED_height + 1)), width, (Coord)LED_height }, color);
|
||||
}
|
||||
prev_value = value_;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user