mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-14 18:43:01 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f537c7896e | |||
| 4a1479957c | |||
| dc9a16c54b | |||
| a476647d70 | |||
| 95a48e5693 |
@@ -64,7 +64,6 @@ class LevelView : public View {
|
||||
|
||||
// TODO: needed?
|
||||
int32_t db{0};
|
||||
long long int MAX_UFREQ = {7200000000}; // maximum usable freq
|
||||
rf::Frequency freq_ = {0};
|
||||
|
||||
Labels labels{
|
||||
|
||||
@@ -74,8 +74,8 @@ rf::Frequency GlassView::get_freq_from_bin_pos(uint8_t pos) {
|
||||
|
||||
void GlassView::on_marker_change() {
|
||||
marker = get_freq_from_bin_pos(marker_pixel_index);
|
||||
button_marker.set_text(to_string_short_freq(marker));
|
||||
PlotMarker(marker_pixel_index); // Refresh marker on screen
|
||||
field_marker.set_text(to_string_short_freq(marker));
|
||||
plot_marker(marker_pixel_index); // Refresh marker on screen
|
||||
}
|
||||
|
||||
void GlassView::retune() {
|
||||
@@ -87,23 +87,13 @@ void GlassView::retune() {
|
||||
baseband::spectrum_streaming_start(); // Do the RX
|
||||
}
|
||||
|
||||
void GlassView::on_lna_changed(int32_t v_db) {
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
|
||||
void GlassView::on_vga_changed(int32_t v_db) {
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
|
||||
void GlassView::reset_live_view(bool clear_screen) {
|
||||
void GlassView::reset_live_view() {
|
||||
max_freq_hold = 0;
|
||||
max_freq_power = -1000;
|
||||
if (clear_screen) {
|
||||
// only clear screen in peak mode
|
||||
if (live_frequency_view == 2) {
|
||||
display.fill_rectangle({{0, 108 + 16}, {SCREEN_W, 320 - (108 + 16)}}, {0, 0, 0});
|
||||
}
|
||||
}
|
||||
|
||||
// Clear screen in peak mode.
|
||||
if (live_frequency_view == 2)
|
||||
display.fill_rectangle({{0, 108 + 16}, {SCREEN_W, SCREEN_H - (108 + 16)}}, {0, 0, 0});
|
||||
}
|
||||
|
||||
void GlassView::add_spectrum_pixel(uint8_t power) {
|
||||
@@ -134,15 +124,15 @@ void GlassView::add_spectrum_pixel(uint8_t power) {
|
||||
uint8_t color_gradient = (point * 255) / 212;
|
||||
// clear if not in peak view
|
||||
if (live_frequency_view != 2) {
|
||||
display.fill_rectangle({{xpos, 108 + 16}, {1, 320 - point}}, {0, 0, 0});
|
||||
display.fill_rectangle({{xpos, 108 + 16}, {1, SCREEN_H - point}}, {0, 0, 0});
|
||||
}
|
||||
display.fill_rectangle({{xpos, 320 - point}, {1, point}}, {color_gradient, 0, uint8_t(255 - color_gradient)});
|
||||
display.fill_rectangle({{xpos, SCREEN_H - point}, {1, point}}, {color_gradient, 0, uint8_t(255 - color_gradient)});
|
||||
}
|
||||
if (last_max_freq != max_freq_hold) {
|
||||
last_max_freq = max_freq_hold;
|
||||
freq_stats.set("MAX HOLD: " + to_string_short_freq(max_freq_hold));
|
||||
}
|
||||
PlotMarker(marker_pixel_index);
|
||||
plot_marker(marker_pixel_index);
|
||||
} else {
|
||||
display.draw_pixels({{0, display.scroll(1)}, {SCREEN_W, 1}}, spectrum_row); // new line at top, one less var, speedier
|
||||
}
|
||||
@@ -151,8 +141,8 @@ void GlassView::add_spectrum_pixel(uint8_t power) {
|
||||
}
|
||||
|
||||
bool GlassView::process_bins(uint8_t* powerlevel) {
|
||||
bins_Hz_size += each_bin_size; // add pixel to fulfilled bag of Hz
|
||||
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
|
||||
bins_hz_size += each_bin_size; // add pixel to fulfilled bag of Hz
|
||||
if (bins_hz_size >= marker_pixel_step) // new pixel fullfilled
|
||||
{
|
||||
if (*powerlevel > min_color_power)
|
||||
add_spectrum_pixel(*powerlevel); // Pixel will represent max_power
|
||||
@@ -162,7 +152,7 @@ bool GlassView::process_bins(uint8_t* powerlevel) {
|
||||
|
||||
if (!pixel_index) // Received indication that a waterfall line has been completed
|
||||
{
|
||||
bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin"
|
||||
bins_hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin"
|
||||
if (mode != LOOKING_GLASS_SINGLEPASS) {
|
||||
f_center = f_center_ini;
|
||||
retune();
|
||||
@@ -170,18 +160,18 @@ bool GlassView::process_bins(uint8_t* powerlevel) {
|
||||
baseband::spectrum_streaming_start();
|
||||
return true; // signal a new line
|
||||
}
|
||||
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
|
||||
bins_hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apparently, the spectrum object returns an array of SPEC_NB_BINS (256) bins
|
||||
// Each having the radio signal power for it's corresponding frequency slot
|
||||
// Each having the radio signal power for its corresponding frequency slot
|
||||
void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
baseband::spectrum_streaming_stop();
|
||||
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
|
||||
// we actually need SCREEN_W (240) of those bins
|
||||
for (bin = 0; bin < bin_length; bin++) {
|
||||
for (uint8_t bin = 0; bin < bin_length; bin++) {
|
||||
get_max_power(spectrum, bin, max_power);
|
||||
// process dc spike if enable
|
||||
if (bin == 119) {
|
||||
@@ -215,7 +205,7 @@ void GlassView::on_show() {
|
||||
}
|
||||
|
||||
void GlassView::on_range_changed() {
|
||||
reset_live_view(true);
|
||||
reset_live_view();
|
||||
f_min = field_frequency_min.value();
|
||||
f_max = field_frequency_max.value();
|
||||
f_min = f_min * MHZ_DIV; // Transpose into full frequency realm
|
||||
@@ -231,8 +221,7 @@ void GlassView::on_range_changed() {
|
||||
looking_glass_sampling_rate = looking_glass_bandwidth;
|
||||
each_bin_size = looking_glass_bandwidth / SCREEN_W;
|
||||
looking_glass_step = looking_glass_bandwidth;
|
||||
f_center_ini = f_min + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
|
||||
portapack::display.fill_rectangle({17 * 8, 4 * 16, 2 * 8, 16}, Color::black()); // Clear old marker and whole marker rectangle btw
|
||||
f_center_ini = f_min + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
|
||||
} else {
|
||||
// view is made in multiple pass, use original bin picking
|
||||
mode = scan_type.selected_index_value();
|
||||
@@ -253,19 +242,13 @@ void GlassView::on_range_changed() {
|
||||
}
|
||||
search_span = looking_glass_range / MHZ_DIV;
|
||||
marker_pixel_step = looking_glass_range / SCREEN_W; // Each pixel value in Hz
|
||||
//
|
||||
button_range.set_text(" "); // clear up to 6 chars
|
||||
if (locked_range) {
|
||||
button_range.set_text(">" + to_string_dec_uint(search_span) + "<");
|
||||
} else {
|
||||
button_range.set_text(" " + to_string_dec_uint(search_span) + " ");
|
||||
}
|
||||
|
||||
pixel_index = 0; // reset pixel counter
|
||||
max_power = 0; // reset save max power level
|
||||
bins_Hz_size = 0; // reset amount of Hz filled up by pixels
|
||||
//
|
||||
pixel_index = 0;
|
||||
max_power = 0;
|
||||
bins_hz_size = 0;
|
||||
|
||||
on_marker_change();
|
||||
update_range_field();
|
||||
|
||||
// set the sample rate and bandwidth
|
||||
receiver_model.set_sampling_rate(looking_glass_sampling_rate);
|
||||
@@ -273,11 +256,11 @@ void GlassView::on_range_changed() {
|
||||
|
||||
receiver_model.set_squelch_level(0);
|
||||
f_center = f_center_ini; // Reset sweep into first slice
|
||||
baseband::set_spectrum(looking_glass_bandwidth, field_trigger.value());
|
||||
baseband::set_spectrum(looking_glass_bandwidth, trigger);
|
||||
receiver_model.set_target_frequency(f_center); // tune rx for this slice
|
||||
}
|
||||
|
||||
void GlassView::PlotMarker(uint8_t pos) {
|
||||
void GlassView::plot_marker(uint8_t pos) {
|
||||
uint8_t shift_y = 0;
|
||||
if (live_frequency_view > 0) // plot one line down when in live view
|
||||
{
|
||||
@@ -289,7 +272,7 @@ void GlassView::PlotMarker(uint8_t pos) {
|
||||
portapack::display.fill_rectangle({pos, 106 + shift_y, 1, 2}, Color::red()); // Red marker bottom
|
||||
}
|
||||
|
||||
void GlassView::clip_min(int32_t v) {
|
||||
void GlassView::update_min(int32_t v) {
|
||||
int32_t min_size = steps;
|
||||
if (locked_range)
|
||||
min_size = search_span;
|
||||
@@ -299,15 +282,14 @@ void GlassView::clip_min(int32_t v) {
|
||||
v = 7200 - min_size;
|
||||
}
|
||||
if (v > (field_frequency_max.value() - min_size))
|
||||
field_frequency_max.set_value(v + min_size);
|
||||
field_frequency_max.set_value(v + min_size, false);
|
||||
if (locked_range)
|
||||
field_frequency_max.set_value(v + min_size);
|
||||
field_frequency_max.set_value(v + min_size, false);
|
||||
else
|
||||
field_frequency_min.set_value(v);
|
||||
on_range_changed();
|
||||
field_frequency_min.set_value(v, false);
|
||||
}
|
||||
|
||||
void GlassView::clip_max(int32_t v) {
|
||||
void GlassView::update_max(int32_t v) {
|
||||
int32_t min_size = steps;
|
||||
if (locked_range)
|
||||
min_size = search_span;
|
||||
@@ -317,12 +299,21 @@ void GlassView::clip_max(int32_t v) {
|
||||
v = min_size;
|
||||
}
|
||||
if (v < (field_frequency_min.value() + min_size))
|
||||
field_frequency_min.set_value(v - min_size);
|
||||
field_frequency_min.set_value(v - min_size, false);
|
||||
if (locked_range)
|
||||
field_frequency_min.set_value(v - min_size);
|
||||
field_frequency_min.set_value(v - min_size, false);
|
||||
else
|
||||
field_frequency_max.set_value(v);
|
||||
on_range_changed();
|
||||
field_frequency_max.set_value(v, false);
|
||||
}
|
||||
|
||||
void GlassView::update_range_field() {
|
||||
if (!locked_range) {
|
||||
field_range.set_style(&Styles::white);
|
||||
field_range.set_text(" " + to_string_dec_uint(search_span) + " ");
|
||||
} else {
|
||||
field_range.set_style(&Styles::red);
|
||||
field_range.set_text(">" + to_string_dec_uint(search_span) + "<");
|
||||
}
|
||||
}
|
||||
|
||||
GlassView::GlassView(
|
||||
@@ -335,7 +326,7 @@ GlassView::GlassView(
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&button_range,
|
||||
&field_range,
|
||||
&steps_config,
|
||||
&scan_type,
|
||||
&view_config,
|
||||
@@ -343,175 +334,156 @@ GlassView::GlassView(
|
||||
&filter_config,
|
||||
&field_rf_amp,
|
||||
&range_presets,
|
||||
&button_marker,
|
||||
&field_marker,
|
||||
&field_trigger,
|
||||
&button_jump,
|
||||
&button_rst,
|
||||
&freq_stats});
|
||||
|
||||
load_Presets(); // Load available presets from TXT files (or default)
|
||||
load_presets(); // Load available presets from TXT files (or default).
|
||||
preset_index = clip<uint8_t>(preset_index, 0, presets_db.size());
|
||||
|
||||
field_frequency_min.set_value(f_min / MHZ_DIV);
|
||||
field_frequency_min.on_change = [this](int32_t v) {
|
||||
clip_min(v);
|
||||
range_presets.set_selected_index(0); // Manual
|
||||
update_min(v);
|
||||
on_range_changed();
|
||||
};
|
||||
field_frequency_min.set_value(presets_db[0].min); // Defaults to first preset
|
||||
field_frequency_min.set_step(steps);
|
||||
|
||||
field_frequency_min.on_select = [this, &nav](NumberField& field) {
|
||||
auto new_view = nav_.push<FrequencyKeypadView>(field_frequency_min.value() * MHZ_DIV);
|
||||
new_view->on_changed = [this, &field](rf::Frequency f) {
|
||||
clip_min(f / MHZ_DIV);
|
||||
field_frequency_min.set_value(f / MHZ_DIV);
|
||||
};
|
||||
};
|
||||
|
||||
field_frequency_max.set_value(f_max / MHZ_DIV);
|
||||
field_frequency_max.on_change = [this](int32_t v) {
|
||||
clip_max(v);
|
||||
range_presets.set_selected_index(0); // Manual
|
||||
update_max(v);
|
||||
on_range_changed();
|
||||
};
|
||||
field_frequency_max.set_value(presets_db[0].max); // Defaults to first preset
|
||||
field_frequency_max.set_step(steps);
|
||||
|
||||
field_frequency_max.on_select = [this, &nav](NumberField& field) {
|
||||
auto new_view = nav_.push<FrequencyKeypadView>(field_frequency_max.value() * MHZ_DIV);
|
||||
new_view->on_changed = [this, &field](rf::Frequency f) {
|
||||
clip_max(f / MHZ_DIV);
|
||||
field_frequency_max.set_value(f / MHZ_DIV);
|
||||
};
|
||||
};
|
||||
|
||||
field_lna.on_change = [this](int32_t v) {
|
||||
reset_live_view(true);
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
|
||||
field_vga.on_change = [this](int32_t v_db) {
|
||||
reset_live_view(true);
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
|
||||
steps_config.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
steps_config.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
field_frequency_min.set_step(v);
|
||||
field_frequency_max.set_step(v);
|
||||
steps = v;
|
||||
};
|
||||
steps_config.set_selected_index(0); // default of 1 Mhz steps
|
||||
steps_config.set_selected_index(0); // 1 Mhz step.
|
||||
|
||||
scan_type.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
scan_type.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
mode = v;
|
||||
on_range_changed();
|
||||
};
|
||||
scan_type.set_selected_index(0); // default legacy fast scan
|
||||
scan_type.set_selected_index(mode);
|
||||
|
||||
view_config.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
// clear between changes
|
||||
reset_live_view(true);
|
||||
if (v == 0) {
|
||||
live_frequency_view = 0;
|
||||
level_integration.hidden(true);
|
||||
freq_stats.hidden(true);
|
||||
button_jump.hidden(true);
|
||||
button_rst.hidden(true);
|
||||
display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates
|
||||
} else if (v == 1) {
|
||||
display.fill_rectangle({{0, 108}, {SCREEN_W, 24}}, {0, 0, 0});
|
||||
live_frequency_view = 1;
|
||||
display.scroll_disable();
|
||||
level_integration.hidden(false);
|
||||
freq_stats.hidden(false);
|
||||
button_jump.hidden(false);
|
||||
button_rst.hidden(false);
|
||||
} else if (v == 2) {
|
||||
display.fill_rectangle({{0, 108}, {SCREEN_W, 24}}, {0, 0, 0});
|
||||
live_frequency_view = 2;
|
||||
display.scroll_disable();
|
||||
level_integration.hidden(false);
|
||||
freq_stats.hidden(false);
|
||||
button_jump.hidden(false);
|
||||
button_rst.hidden(false);
|
||||
view_config.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
reset_live_view(); // Clear between changes.
|
||||
live_frequency_view = v;
|
||||
|
||||
switch (v) {
|
||||
case 0: // SPEC
|
||||
level_integration.hidden(true);
|
||||
freq_stats.hidden(true);
|
||||
button_jump.hidden(true);
|
||||
button_rst.hidden(true);
|
||||
display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates.
|
||||
break;
|
||||
|
||||
case 1: // LEVEL
|
||||
display.fill_rectangle({{0, 108}, {SCREEN_W, 24}}, {0, 0, 0});
|
||||
display.scroll_disable();
|
||||
level_integration.hidden(false);
|
||||
freq_stats.hidden(false);
|
||||
button_jump.hidden(false);
|
||||
button_rst.hidden(false);
|
||||
break;
|
||||
|
||||
case 2: // PEAK
|
||||
default:
|
||||
display.fill_rectangle({{0, 108}, {SCREEN_W, 24}}, {0, 0, 0});
|
||||
display.scroll_disable();
|
||||
level_integration.hidden(false);
|
||||
freq_stats.hidden(false);
|
||||
button_jump.hidden(false);
|
||||
button_rst.hidden(false);
|
||||
break;
|
||||
}
|
||||
|
||||
set_dirty();
|
||||
};
|
||||
view_config.set_selected_index(0); // default spectrum
|
||||
view_config.set_selected_index(live_frequency_view);
|
||||
|
||||
level_integration.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
reset_live_view(true);
|
||||
level_integration.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
reset_live_view();
|
||||
live_frequency_integrate = v;
|
||||
};
|
||||
level_integration.set_selected_index(3); // default integration of ( 3 * old value + new_value ) / 4
|
||||
level_integration.set_selected_index(live_frequency_integrate);
|
||||
|
||||
filter_config.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
reset_live_view(true);
|
||||
filter_config.on_change = [this](size_t ix, OptionsField::value_t v) {
|
||||
reset_live_view();
|
||||
min_color_power = v;
|
||||
filter_index = ix;
|
||||
};
|
||||
filter_config.set_selected_index(0);
|
||||
filter_config.set_selected_index(filter_index);
|
||||
|
||||
range_presets.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
range_presets.on_change = [this](size_t ix, OptionsField::value_t v) {
|
||||
preset_index = ix;
|
||||
if (ix == 0) return; // Don't update range for "Manual".
|
||||
|
||||
// NB: Don't trigger updates, presets directly set the range
|
||||
// values without applying step or range lock.
|
||||
field_frequency_min.set_value(presets_db[v].min, false);
|
||||
field_frequency_max.set_value(presets_db[v].max, false);
|
||||
on_range_changed();
|
||||
};
|
||||
range_presets.set_selected_index(preset_index);
|
||||
|
||||
button_marker.on_change = [this]() {
|
||||
if (((int)marker_pixel_index + button_marker.get_encoder_delta()) < 0) {
|
||||
marker_pixel_index = 0;
|
||||
} else if (((int)marker_pixel_index + button_marker.get_encoder_delta()) > SCREEN_W) {
|
||||
marker_pixel_index = SCREEN_W;
|
||||
} else {
|
||||
marker_pixel_index = marker_pixel_index + button_marker.get_encoder_delta();
|
||||
}
|
||||
field_marker.on_encoder_change = [this](TextField&, EncoderEvent delta) {
|
||||
marker_pixel_index = clip<uint8_t>(marker_pixel_index + delta, 0, SCREEN_W);
|
||||
on_marker_change();
|
||||
button_marker.set_encoder_delta(0);
|
||||
};
|
||||
|
||||
button_marker.on_select = [this](ButtonWithEncoder&) {
|
||||
receiver_model.set_target_frequency(marker); // Center tune rx in marker freq.
|
||||
auto settings = receiver_model.settings();
|
||||
settings.frequency_step = MHZ_DIV; // Preset a 1 MHz frequency step into RX -> AUDIO
|
||||
nav_.replace<AnalogAudioView>(settings); // Jump into audio view
|
||||
field_marker.on_select = [this](TextField&) {
|
||||
// Launch Audio with marker frequency.
|
||||
launch_audio(marker);
|
||||
};
|
||||
|
||||
field_trigger.on_change = [this](int32_t v) {
|
||||
baseband::set_spectrum(looking_glass_bandwidth, v);
|
||||
trigger = v;
|
||||
baseband::set_spectrum(looking_glass_bandwidth, trigger);
|
||||
};
|
||||
field_trigger.set_value(32); // Defaults to 32, as normal triggering resolution
|
||||
field_trigger.set_value(trigger);
|
||||
|
||||
button_range.on_select = [this](Button&) {
|
||||
if (locked_range) {
|
||||
locked_range = false;
|
||||
button_range.set_style(&Styles::white);
|
||||
button_range.set_text(" " + to_string_dec_uint(search_span) + " ");
|
||||
} else {
|
||||
locked_range = true;
|
||||
button_range.set_style(&Styles::red);
|
||||
button_range.set_text(">" + to_string_dec_uint(search_span) + "<");
|
||||
}
|
||||
field_range.on_select = [this](TextField&) {
|
||||
locked_range = !locked_range;
|
||||
update_range_field();
|
||||
};
|
||||
|
||||
button_jump.on_select = [this](Button&) {
|
||||
receiver_model.set_target_frequency(max_freq_hold); // Center tune rx in marker freq.
|
||||
auto settings = receiver_model.settings();
|
||||
settings.frequency_step = MHZ_DIV; // Preset a 1 MHz frequency step into RX -> AUDIO
|
||||
nav_.replace<AnalogAudioView>(settings); // Jump into audio view
|
||||
// Launch Audio with peak frequency.
|
||||
launch_audio(max_freq_hold);
|
||||
};
|
||||
|
||||
button_rst.on_select = [this](Button&) {
|
||||
reset_live_view(true);
|
||||
reset_live_view();
|
||||
};
|
||||
|
||||
display.scroll_set_area(109, 319);
|
||||
baseband::set_spectrum(looking_glass_bandwidth, field_trigger.value()); // trigger:
|
||||
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute ,
|
||||
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
|
||||
// at which time it pushes the buffer up with channel_spectrum.feed
|
||||
|
||||
marker_pixel_index = 120;
|
||||
on_range_changed();
|
||||
// trigger:
|
||||
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute,
|
||||
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
|
||||
// at which time it pushes the buffer up with channel_spectrum.feed
|
||||
baseband::set_spectrum(looking_glass_bandwidth, trigger);
|
||||
|
||||
marker_pixel_index = SCREEN_W / 2;
|
||||
on_range_changed(); // Force a UI update.
|
||||
|
||||
receiver_model.set_sampling_rate(looking_glass_sampling_rate); // 20mhz
|
||||
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
|
||||
@@ -519,11 +491,14 @@ GlassView::GlassView(
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
void GlassView::load_Presets() {
|
||||
void GlassView::load_presets() {
|
||||
File presets_file;
|
||||
auto error = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear();
|
||||
|
||||
// Add the "Manual" entry.
|
||||
presets_db.push_back({0, 0, "Manual"});
|
||||
|
||||
if (!error) {
|
||||
auto reader = FileLineReader(presets_file);
|
||||
for (const auto& line : reader) {
|
||||
@@ -546,14 +521,10 @@ void GlassView::load_Presets() {
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't load any from the file, load a default instead.
|
||||
if (presets_db.empty())
|
||||
presets_Default();
|
||||
|
||||
populate_Presets();
|
||||
populate_presets();
|
||||
}
|
||||
|
||||
void GlassView::populate_Presets() {
|
||||
void GlassView::populate_presets() {
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t entries;
|
||||
@@ -561,12 +532,14 @@ void GlassView::populate_Presets() {
|
||||
for (const auto& preset : presets_db)
|
||||
entries.emplace_back(preset.label, entries.size());
|
||||
|
||||
range_presets.set_options(entries);
|
||||
range_presets.set_options(std::move(entries));
|
||||
}
|
||||
|
||||
void GlassView::presets_Default() {
|
||||
presets_db.clear();
|
||||
presets_db.push_back({2320, 2560, "DEFAULT WIFI 2.4GHz"});
|
||||
void GlassView::launch_audio(rf::Frequency center_freq) {
|
||||
receiver_model.set_target_frequency(center_freq);
|
||||
auto settings = receiver_model.settings();
|
||||
settings.frequency_step = MHZ_DIV; // Preset a 1 MHz frequency step into RX -> AUDIO
|
||||
nav_.replace<AnalogAudioView>(settings); // Jump into audio view
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -72,8 +72,28 @@ class GlassView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{ReceiverModel::Mode::SpectrumAnalysis};
|
||||
// Settings
|
||||
rf::Frequency f_min = 260 * MHZ_DIV; // Default to 315/433 remote range.
|
||||
rf::Frequency f_max = 500 * MHZ_DIV;
|
||||
uint8_t preset_index = 0; // Manual
|
||||
uint8_t filter_index = 0; // OFF
|
||||
uint8_t trigger = 32;
|
||||
uint8_t mode = LOOKING_GLASS_FASTSCAN;
|
||||
uint8_t live_frequency_view = 0; // Spectrum
|
||||
uint8_t live_frequency_integrate = 3; // Default (3 * old value + new_value) / 4
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_glass", app_settings::Mode::RX};
|
||||
"rx_glass"sv,
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"min"sv, &f_min},
|
||||
{"max"sv, &f_max},
|
||||
{"preset"sv, &preset_index},
|
||||
{"filter"sv, &filter_index},
|
||||
{"trigger"sv, &trigger},
|
||||
{"scan_mode"sv, &mode},
|
||||
{"freq_view"sv, &live_frequency_view},
|
||||
{"freq_integrate"sv, &live_frequency_integrate},
|
||||
}};
|
||||
|
||||
struct preset_entry {
|
||||
rf::Frequency min{};
|
||||
@@ -82,8 +102,9 @@ class GlassView : public View {
|
||||
};
|
||||
|
||||
std::vector<preset_entry> presets_db{};
|
||||
void clip_min(int32_t v);
|
||||
void clip_max(int32_t v);
|
||||
void update_min(int32_t v);
|
||||
void update_max(int32_t v);
|
||||
void update_range_field();
|
||||
void get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power);
|
||||
rf::Frequency get_freq_from_bin_pos(uint8_t pos);
|
||||
void on_marker_change();
|
||||
@@ -94,57 +115,52 @@ class GlassView : public View {
|
||||
int64_t next_mult_of(int64_t num, int64_t multiplier);
|
||||
void adjust_range(int64_t* f_min, int64_t* f_max, int64_t width);
|
||||
void on_range_changed();
|
||||
void on_lna_changed(int32_t v_db);
|
||||
void on_vga_changed(int32_t v_db);
|
||||
void reset_live_view(bool clear_screen);
|
||||
void reset_live_view();
|
||||
void add_spectrum_pixel(uint8_t power);
|
||||
void PlotMarker(uint8_t pos);
|
||||
void load_Presets();
|
||||
void txtline_process(std::string& line);
|
||||
void populate_Presets();
|
||||
void presets_Default();
|
||||
void plot_marker(uint8_t pos);
|
||||
void load_presets();
|
||||
void populate_presets();
|
||||
void launch_audio(rf::Frequency center_freq);
|
||||
|
||||
rf::Frequency f_min{0}, f_max{0};
|
||||
rf::Frequency search_span{0};
|
||||
rf::Frequency f_center{0};
|
||||
rf::Frequency f_center_ini{0};
|
||||
|
||||
rf::Frequency marker{0};
|
||||
uint8_t marker_pixel_index{0};
|
||||
rf::Frequency marker_pixel_step{0};
|
||||
// size of one spectrum bin in Hz
|
||||
|
||||
// Size of one spectrum bin in Hz.
|
||||
rf::Frequency each_bin_size{0};
|
||||
// consumed number of Hz, used to know if we have filled a 'bag' , a corresponding pixel length on screen
|
||||
rf::Frequency bins_Hz_size{0};
|
||||
// Bandwidth of a single spectrum bin.
|
||||
rf::Frequency bins_hz_size{0};
|
||||
rf::Frequency looking_glass_sampling_rate{0};
|
||||
rf::Frequency looking_glass_bandwidth{0};
|
||||
rf::Frequency looking_glass_range{0};
|
||||
rf::Frequency looking_glass_step{0};
|
||||
uint8_t min_color_power{0};
|
||||
uint8_t min_color_power{0}; // Filter cutoff level.
|
||||
uint32_t pixel_index{0};
|
||||
std::array<Color, SCREEN_W> spectrum_row = {0};
|
||||
std::array<uint8_t, SCREEN_W> spectrum_data = {0};
|
||||
ChannelSpectrumFIFO* fifo{nullptr};
|
||||
uint8_t max_power = 0;
|
||||
int32_t steps = 0;
|
||||
uint8_t live_frequency_view = 0;
|
||||
int16_t live_frequency_integrate = 3;
|
||||
int64_t max_freq_hold = 0;
|
||||
int16_t max_freq_power = -1000;
|
||||
|
||||
std::array<Color, SCREEN_W> spectrum_row{};
|
||||
std::array<uint8_t, SCREEN_W> spectrum_data{};
|
||||
ChannelSpectrumFIFO* fifo{};
|
||||
|
||||
int32_t steps = 1;
|
||||
bool locked_range = false;
|
||||
|
||||
uint8_t max_power = 0;
|
||||
rf::Frequency max_freq_hold = 0;
|
||||
rf::Frequency last_max_freq = 0;
|
||||
int16_t max_freq_power = -1000;
|
||||
uint8_t bin_length = SCREEN_W;
|
||||
uint8_t real_bin_length = SCREEN_W;
|
||||
uint8_t offset = 0;
|
||||
uint8_t tune_offset = 0;
|
||||
uint8_t bin = 0;
|
||||
int64_t last_max_freq = 0;
|
||||
uint8_t mode = LOOKING_GLASS_FASTSCAN;
|
||||
uint8_t ignore_dc = 0;
|
||||
|
||||
Labels labels{
|
||||
{{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()},
|
||||
{{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()},
|
||||
{{0, 0 * 16}, "MIN: MAX: LNA VGA ", Color::light_grey()},
|
||||
{{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()},
|
||||
{{0, 2 * 16}, "PRESET:", Color::light_grey()},
|
||||
{{0, 3 * 16}, "MARKER: MHz", Color::light_grey()},
|
||||
{{0, 3 * 16}, "MARKER: MHz", Color::light_grey()},
|
||||
{{0, 4 * 16}, "RES: STEP:", Color::light_grey()}};
|
||||
|
||||
NumberField field_frequency_min{
|
||||
@@ -167,8 +183,8 @@ class GlassView : public View {
|
||||
VGAGainField field_vga{
|
||||
{27 * 8, 0 * 16}};
|
||||
|
||||
Button button_range{
|
||||
{7 * 8, 1 * 16, 4 * 8, 16},
|
||||
TextField field_range{
|
||||
{6 * 8, 1 * 16, 6 * 8, 16},
|
||||
""};
|
||||
|
||||
OptionsField filter_config{
|
||||
@@ -176,23 +192,21 @@ class GlassView : public View {
|
||||
4,
|
||||
{
|
||||
{"OFF ", 0},
|
||||
{"MID ", 118}, // 85 +25 (110) + a bit more to kill all blue
|
||||
{"MID ", 118}, // 85 + 25 (110) + a bit more to kill all blue
|
||||
{"HIGH", 202}, // 168 + 25 (193)
|
||||
}};
|
||||
|
||||
RFAmpField field_rf_amp{
|
||||
{29 * 8, 1 * 16}};
|
||||
{28 * 8, 1 * 16}};
|
||||
|
||||
OptionsField range_presets{
|
||||
{7 * 8, 2 * 16},
|
||||
20,
|
||||
{
|
||||
{" NONE (WIFI 2.4GHz)", 0},
|
||||
}};
|
||||
{}};
|
||||
|
||||
ButtonWithEncoder button_marker{
|
||||
{7 * 8, 3 * 16, 10 * 8, 16},
|
||||
" "};
|
||||
TextField field_marker{
|
||||
{7 * 8, 3 * 16, 9 * 8, 16},
|
||||
""};
|
||||
|
||||
NumberField field_trigger{
|
||||
{4 * 8, 4 * 16},
|
||||
|
||||
@@ -31,9 +31,6 @@
|
||||
#include "ui_navigation.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
// maximum usable freq
|
||||
#define MAX_UFREQ 7200000000
|
||||
|
||||
// 1Mhz helper
|
||||
#ifdef OneMHz
|
||||
#undef OneMHz
|
||||
|
||||
@@ -31,8 +31,6 @@ namespace fs = std::filesystem;
|
||||
|
||||
namespace ui {
|
||||
|
||||
static const fs::path default_scan_file{u"FREQMAN/SCANNER.TXT"};
|
||||
|
||||
ScannerThread::ScannerThread(std::vector<rf::Frequency> frequency_list)
|
||||
: frequency_list_{std::move(frequency_list)} {
|
||||
_manual_search = false;
|
||||
@@ -234,7 +232,7 @@ void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
|
||||
|
||||
if (!manual_search) {
|
||||
if (entries.size() > 0)
|
||||
text_current_index.set(to_string_dec_uint(freq_idx + 1, 3));
|
||||
field_current_index.set_text(to_string_dec_uint(freq_idx + 1, 3));
|
||||
|
||||
if (freq_idx < entries.size() && entries[freq_idx].description.size() > 1)
|
||||
text_current_desc.set(entries[freq_idx].description); // Show description from file
|
||||
@@ -243,9 +241,20 @@ void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
|
||||
}
|
||||
}
|
||||
|
||||
void ScannerView::handle_encoder(EncoderEvent delta) {
|
||||
auto index_step = delta > 0 ? 1 : -1;
|
||||
|
||||
if (scan_thread)
|
||||
scan_thread->set_index_stepper(index_step);
|
||||
|
||||
// Restart browse timer when frequency changes.
|
||||
if (browse_timer != 0)
|
||||
browse_timer = 1;
|
||||
}
|
||||
|
||||
std::string ScannerView::loaded_filename() const {
|
||||
auto filename = loaded_path.filename().string();
|
||||
if (filename.length() > 23) { // Truncate and add ellipses if long file name
|
||||
auto filename = freqman_file;
|
||||
if (filename.length() > 23) { // Truncate long file name.
|
||||
filename.resize(22);
|
||||
filename = filename + "+";
|
||||
}
|
||||
@@ -266,7 +275,7 @@ ScannerView::~ScannerView() {
|
||||
}
|
||||
|
||||
void ScannerView::show_max_index() { // show total number of freqs to scan
|
||||
text_current_index.set("---");
|
||||
field_current_index.set_text("---");
|
||||
|
||||
if (entries.size() == FREQMAN_MAX_PER_FILE) {
|
||||
text_max_index.set_style(&Styles::red);
|
||||
@@ -293,7 +302,7 @@ ScannerView::ScannerView(
|
||||
&button_load,
|
||||
&button_clear,
|
||||
&rssi,
|
||||
&text_current_index,
|
||||
&field_current_index,
|
||||
&text_max_index,
|
||||
&text_current_desc,
|
||||
&big_display,
|
||||
@@ -319,10 +328,7 @@ ScannerView::ScannerView(
|
||||
field_step.set_by_value(receiver_model.frequency_step()); // Default step interval (Hz)
|
||||
change_mode((freqman_index_t)field_mode.selected_index_value());
|
||||
|
||||
rf::Frequency stored_freq = receiver_model.target_frequency();
|
||||
frequency_range.min = stored_freq - 1000000;
|
||||
button_manual_start.set_text(to_string_short_freq(frequency_range.min));
|
||||
frequency_range.max = stored_freq + 1000000;
|
||||
button_manual_end.set_text(to_string_short_freq(frequency_range.max));
|
||||
|
||||
// Button to load a Freqman file.
|
||||
@@ -339,14 +345,14 @@ ScannerView::ScannerView(
|
||||
};
|
||||
};
|
||||
|
||||
// Button to clear in-memory frequency list
|
||||
// Button to clear in-memory frequency list.
|
||||
button_clear.on_select = [this, &nav](Button&) {
|
||||
if (scan_thread && entries.size()) {
|
||||
scan_thread->stop(); // STOP SCANNER THREAD
|
||||
entries.clear();
|
||||
|
||||
show_max_index(); // UPDATE new list size on screen
|
||||
text_current_index.set("");
|
||||
field_current_index.set_text("");
|
||||
text_current_desc.set(loaded_filename());
|
||||
scan_thread->set_freq_lock(0); // Reset the scanner lock
|
||||
|
||||
@@ -354,7 +360,7 @@ ScannerView::ScannerView(
|
||||
}
|
||||
};
|
||||
|
||||
// Button to configure starting frequency for a manual range search
|
||||
// Button to configure starting frequency for a manual range search.
|
||||
button_manual_start.on_select = [this, &nav](Button& button) {
|
||||
auto new_view = nav_.push<FrequencyKeypadView>(frequency_range.min);
|
||||
new_view->on_changed = [this, &button](rf::Frequency f) {
|
||||
@@ -363,7 +369,7 @@ ScannerView::ScannerView(
|
||||
};
|
||||
};
|
||||
|
||||
// Button to configure ending frequency for a manual range search
|
||||
// Button to configure ending frequency for a manual range search.
|
||||
button_manual_end.on_select = [this, &nav](Button& button) {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(frequency_range.max);
|
||||
new_view->on_changed = [this, &button](rf::Frequency f) {
|
||||
@@ -372,7 +378,7 @@ ScannerView::ScannerView(
|
||||
};
|
||||
};
|
||||
|
||||
// Button to pause/resume scan (note that some other buttons will trigger resume also)
|
||||
// Button to pause/resume scan (note that some other buttons will trigger resume also).
|
||||
button_pause.on_select = [this](ButtonWithEncoder&) {
|
||||
if (userpause)
|
||||
user_resume();
|
||||
@@ -383,19 +389,14 @@ ScannerView::ScannerView(
|
||||
}
|
||||
};
|
||||
|
||||
// Encoder dial causes frequency change when focus is on pause button
|
||||
// Encoder dial causes frequency change when focus is on pause button or current index.
|
||||
button_pause.on_change = [this]() {
|
||||
int32_t encoder_delta{(button_pause.get_encoder_delta() > 0) ? 1 : -1};
|
||||
|
||||
if (scan_thread)
|
||||
scan_thread->set_index_stepper(encoder_delta);
|
||||
|
||||
// Restart browse timer when frequency changes
|
||||
if (browse_timer != 0)
|
||||
browse_timer = 1;
|
||||
|
||||
handle_encoder(button_pause.get_encoder_delta());
|
||||
button_pause.set_encoder_delta(0);
|
||||
};
|
||||
field_current_index.on_encoder_change = [this](TextField&, EncoderEvent delta) {
|
||||
handle_encoder(delta);
|
||||
};
|
||||
|
||||
// Button to switch to Audio app
|
||||
button_audio_app.on_select = [this](Button&) {
|
||||
@@ -488,7 +489,7 @@ ScannerView::ScannerView(
|
||||
// Button to add current frequency (found during Search) to the Scan Frequency List
|
||||
button_add.on_select = [this](Button&) {
|
||||
FreqmanDB db;
|
||||
if (db.open(loaded_path, /*create*/ true)) {
|
||||
if (db.open(get_freqman_path(freqman_file), /*create*/ true)) {
|
||||
freqman_entry entry{
|
||||
.frequency_a = current_frequency,
|
||||
.type = freqman_type::Single,
|
||||
@@ -514,7 +515,7 @@ ScannerView::ScannerView(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nav_.display_modal("Error", "Cannot open " + loaded_path.filename().string() + "\nfor appending freq.");
|
||||
nav_.display_modal("Error", "Cannot open " + freqman_file + ".TXT\nfor appending freq.");
|
||||
bigdisplay_update(-1); // Need to poke this control after displaying modal?
|
||||
}
|
||||
};
|
||||
@@ -534,7 +535,7 @@ ScannerView::ScannerView(
|
||||
receiver_model.set_squelch_level(0);
|
||||
|
||||
// LOAD FREQUENCIES
|
||||
frequency_file_load(default_scan_file);
|
||||
frequency_file_load(get_freqman_path(freqman_file));
|
||||
}
|
||||
|
||||
void ScannerView::frequency_file_load(const fs::path& path) {
|
||||
@@ -545,12 +546,11 @@ void ScannerView::frequency_file_load(const fs::path& path) {
|
||||
FreqmanDB db;
|
||||
if (!db.open(path)) {
|
||||
text_current_desc.set("NO " + path.filename().string());
|
||||
loaded_path = default_scan_file;
|
||||
return;
|
||||
}
|
||||
|
||||
entries.clear();
|
||||
loaded_path = path;
|
||||
freqman_file = path.stem().string();
|
||||
Optional<scanner_range_t> range;
|
||||
|
||||
for (auto entry : db) {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
#define SCANNER_SLEEP_MS 50 // ms that Scanner Thread sleeps per loop
|
||||
#define STATISTICS_UPDATES_PER_SEC 10
|
||||
#define MAX_FREQ_LOCK 10 //# of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous
|
||||
#define MAX_FREQ_LOCK 10 //# of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spurious
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -110,12 +110,16 @@ class ScannerView : public View {
|
||||
std::string title() const override { return "Scanner"; };
|
||||
|
||||
private:
|
||||
static constexpr const char* default_freqman_file = "SCANNER";
|
||||
|
||||
RxRadioState radio_state_{};
|
||||
|
||||
// Settings
|
||||
uint32_t browse_wait{5};
|
||||
uint32_t lock_wait{2};
|
||||
int32_t squelch{-30};
|
||||
scanner_range_t frequency_range{0, MAX_UFREQ};
|
||||
std::string freqman_file{default_freqman_file};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_scanner"sv,
|
||||
app_settings::Mode::RX,
|
||||
@@ -123,6 +127,9 @@ class ScannerView : public View {
|
||||
{"browse_wait"sv, &browse_wait},
|
||||
{"lock_wait"sv, &lock_wait},
|
||||
{"scanner_squelch"sv, &squelch},
|
||||
{"range_min"sv, &frequency_range.min},
|
||||
{"range_max"sv, &frequency_range.max},
|
||||
{"file"sv, &freqman_file},
|
||||
}};
|
||||
|
||||
NavigationView& nav_;
|
||||
@@ -139,17 +146,15 @@ class ScannerView : public View {
|
||||
void update_squelch_while_paused(int32_t max_db);
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
void handle_retune(int64_t freq, uint32_t freq_idx);
|
||||
|
||||
void handle_encoder(EncoderEvent delta);
|
||||
std::string loaded_filename() const;
|
||||
|
||||
scanner_range_t frequency_range{0, 0};
|
||||
uint32_t browse_timer{0};
|
||||
uint32_t lock_timer{0};
|
||||
uint32_t color_timer{0};
|
||||
int32_t bigdisplay_current_color{-2};
|
||||
rf::Frequency bigdisplay_current_frequency{0};
|
||||
|
||||
std::filesystem::path loaded_path{};
|
||||
std::vector<scanner_entry_t> entries{};
|
||||
uint32_t current_index{0};
|
||||
rf::Frequency current_frequency{0};
|
||||
@@ -221,8 +226,9 @@ class ScannerView : public View {
|
||||
{0 * 16, 2 * 16, 15 * 16, 8},
|
||||
};
|
||||
|
||||
Text text_current_index{
|
||||
TextField field_current_index{
|
||||
{0, 3 * 16, 3 * 8, 16},
|
||||
{},
|
||||
};
|
||||
|
||||
Text text_max_index{
|
||||
@@ -233,9 +239,9 @@ class ScannerView : public View {
|
||||
{0, 4 * 16, 240 - 6 * 8, 16},
|
||||
};
|
||||
|
||||
BigFrequency big_display{// Show frequency in glamour
|
||||
{4, 6 * 16, 28 * 8, 52},
|
||||
0};
|
||||
BigFrequency big_display{
|
||||
{4, 6 * 16, 28 * 8, 52},
|
||||
0};
|
||||
|
||||
Button button_manual_start{
|
||||
{0 * 8, 11 * 16, 11 * 8, 28},
|
||||
|
||||
@@ -33,7 +33,6 @@ BtnGridView::BtnGridView(
|
||||
bool keep_highlight)
|
||||
: keep_highlight{keep_highlight} {
|
||||
set_parent_rect(new_parent_rect);
|
||||
|
||||
set_focusable(true);
|
||||
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
@@ -47,10 +46,6 @@ BtnGridView::BtnGridView(
|
||||
|
||||
BtnGridView::~BtnGridView() {
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void BtnGridView::set_max_rows(int rows) {
|
||||
@@ -68,31 +63,30 @@ void BtnGridView::set_parent_rect(const Rect new_parent_rect) {
|
||||
arrow_more.set_parent_rect({228, (Coord)(displayed_max * button_h), 8, 8});
|
||||
displayed_max *= rows_;
|
||||
|
||||
// TODO: Clean this up :(
|
||||
if (menu_item_views.size()) {
|
||||
for (auto item : menu_item_views) {
|
||||
remove_child(item);
|
||||
delete item;
|
||||
}
|
||||
// Delete any existing buttons.
|
||||
if (!menu_item_views.empty()) {
|
||||
for (auto& item : menu_item_views)
|
||||
remove_child(item.get());
|
||||
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
button_w = 240 / rows_;
|
||||
button_w = screen_width / rows_;
|
||||
for (size_t c = 0; c < displayed_max; c++) {
|
||||
auto item = new NewButton{};
|
||||
menu_item_views.push_back(item);
|
||||
add_child(item);
|
||||
|
||||
auto item = std::make_unique<NewButton>();
|
||||
add_child(item.get());
|
||||
item->set_parent_rect({(int)(c % rows_) * button_w,
|
||||
(int)(c / rows_) * button_h,
|
||||
button_w, button_h});
|
||||
|
||||
menu_item_views.push_back(std::move(item));
|
||||
}
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
void BtnGridView::set_arrow_enabled(bool new_value) {
|
||||
if (new_value) {
|
||||
void BtnGridView::set_arrow_enabled(bool enabled) {
|
||||
if (enabled) {
|
||||
add_child(&arrow_more);
|
||||
} else {
|
||||
remove_child(&arrow_more);
|
||||
@@ -118,6 +112,7 @@ void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
|
||||
for (auto item : new_items) {
|
||||
menu_items.push_back(item);
|
||||
}
|
||||
|
||||
update_items();
|
||||
}
|
||||
|
||||
@@ -130,7 +125,7 @@ void BtnGridView::update_items() {
|
||||
} else
|
||||
more = false;
|
||||
|
||||
for (NewButton* item : menu_item_views) {
|
||||
for (auto& item : menu_item_views) {
|
||||
if ((i + offset) >= menu_items.size()) {
|
||||
item->hidden(true);
|
||||
item->set_text(" ");
|
||||
@@ -152,7 +147,7 @@ void BtnGridView::update_items() {
|
||||
}
|
||||
|
||||
NewButton* BtnGridView::item_view(size_t index) const {
|
||||
return menu_item_views[index];
|
||||
return menu_item_views[index].get();
|
||||
}
|
||||
|
||||
bool BtnGridView::set_highlighted(int32_t new_value) {
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
#include "signal.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -62,7 +64,7 @@ class BtnGridView : public View {
|
||||
uint32_t highlighted_index();
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void set_arrow_enabled(bool new_value);
|
||||
void set_arrow_enabled(bool enabled);
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
@@ -77,7 +79,7 @@ class BtnGridView : public View {
|
||||
|
||||
SignalToken signal_token_tick_second{};
|
||||
std::vector<GridItem> menu_items{};
|
||||
std::vector<NewButton*> menu_item_views{};
|
||||
std::vector<std::unique_ptr<NewButton>> menu_item_views{};
|
||||
|
||||
Image arrow_more{
|
||||
{228, 320 - 8, 8, 8},
|
||||
|
||||
@@ -103,10 +103,6 @@ MenuView::MenuView(
|
||||
|
||||
MenuView::~MenuView() {
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuView::set_parent_rect(const Rect new_parent_rect) {
|
||||
@@ -116,23 +112,22 @@ void MenuView::set_parent_rect(const Rect new_parent_rect) {
|
||||
arrow_more.set_parent_rect({228, (Coord)(displayed_max * item_height), 8, 8});
|
||||
|
||||
// TODO: Clean this up :(
|
||||
if (menu_item_views.size()) {
|
||||
for (auto item : menu_item_views) {
|
||||
remove_child(item);
|
||||
delete item;
|
||||
}
|
||||
if (!menu_item_views.empty()) {
|
||||
for (auto& item : menu_item_views)
|
||||
remove_child(item.get());
|
||||
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < displayed_max; c++) {
|
||||
auto item = new MenuItemView{keep_highlight};
|
||||
menu_item_views.push_back(item);
|
||||
add_child(item);
|
||||
auto item = std::make_unique<MenuItemView>(keep_highlight);
|
||||
add_child(item.get());
|
||||
|
||||
Coord y_pos = c * item_height;
|
||||
item->set_parent_rect({{0, y_pos},
|
||||
{size().width(), (Coord)item_height}});
|
||||
|
||||
menu_item_views.push_back(std::move(item));
|
||||
}
|
||||
|
||||
update_items();
|
||||
@@ -150,9 +145,8 @@ void MenuView::on_tick_second() {
|
||||
}
|
||||
|
||||
void MenuView::clear() {
|
||||
for (auto item : menu_item_views) {
|
||||
for (auto& item : menu_item_views)
|
||||
item->set_item(nullptr);
|
||||
}
|
||||
|
||||
menu_items.clear();
|
||||
highlighted_item = 0;
|
||||
@@ -184,7 +178,7 @@ void MenuView::update_items() {
|
||||
} else
|
||||
more = false;
|
||||
|
||||
for (auto item : menu_item_views) {
|
||||
for (auto& item : menu_item_views) {
|
||||
if (i >= menu_items.size()) break;
|
||||
|
||||
// Assign item data to MenuItemViews according to offset
|
||||
@@ -201,7 +195,7 @@ void MenuView::update_items() {
|
||||
}
|
||||
|
||||
MenuItemView* MenuView::item_view(size_t index) const {
|
||||
return menu_item_views[index];
|
||||
return menu_item_views[index].get();
|
||||
}
|
||||
|
||||
bool MenuView::set_highlighted(int32_t new_value) {
|
||||
|
||||
@@ -30,8 +30,10 @@
|
||||
#include "signal.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -75,7 +77,8 @@ class MenuView : public View {
|
||||
std::function<void(void)> on_left{};
|
||||
std::function<void(void)> on_highlight{nullptr};
|
||||
|
||||
MenuView(Rect new_parent_rect = {0, 0, 240, 304}, bool keep_highlight = false);
|
||||
MenuView(Rect new_parent_rect = {0, 0, screen_width, screen_height - 16},
|
||||
bool keep_highlight = false);
|
||||
|
||||
~MenuView();
|
||||
|
||||
@@ -103,10 +106,10 @@ class MenuView : public View {
|
||||
|
||||
SignalToken signal_token_tick_second{};
|
||||
std::vector<MenuItem> menu_items{};
|
||||
std::vector<MenuItemView*> menu_item_views{};
|
||||
std::vector<std::unique_ptr<MenuItemView>> menu_item_views{};
|
||||
|
||||
Image arrow_more{
|
||||
{228, 320 - 8, 8, 8},
|
||||
{228, screen_height - 8, 8, 8},
|
||||
&bitmap_more,
|
||||
Color::white(),
|
||||
Color::black()};
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#define MAX_UFREQ 7200000000 // maximum usable frequency
|
||||
|
||||
namespace ui {
|
||||
|
||||
class FrequencyField : public Widget {
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
#include "random.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
// TODO move to class members SpectrumPainterProcessor
|
||||
complex16_t* current_line_data = nullptr;
|
||||
complex16_t* volatile next_line_data = nullptr;
|
||||
std::unique_ptr<complex16_t[]> current_line_data;
|
||||
std::unique_ptr<complex16_t[]> next_line_data;
|
||||
uint32_t current_line_index = 0;
|
||||
uint32_t current_line_width = 0;
|
||||
int32_t current_bw = 0;
|
||||
@@ -41,20 +43,17 @@ int max_val = 127;
|
||||
// This is called at 3072000/2048 = 1500Hz
|
||||
void SpectrumPainterProcessor::execute(const buffer_c8_t& buffer) {
|
||||
for (uint32_t i = 0; i < buffer.count; i++) {
|
||||
if (current_line_data != nullptr) {
|
||||
if (current_line_data) {
|
||||
auto data = current_line_data[(current_line_index++ * current_bw / 3072) % current_line_width];
|
||||
buffer.p[i] = {(int8_t)data.real(), (int8_t)data.imag()};
|
||||
} else
|
||||
buffer.p[i] = {0, 0};
|
||||
}
|
||||
|
||||
// collect new data
|
||||
if (next_line_data != nullptr) {
|
||||
if (current_line_data != nullptr)
|
||||
delete current_line_data;
|
||||
|
||||
current_line_data = next_line_data;
|
||||
next_line_data = nullptr;
|
||||
// Move "next line" into "current line" if set.
|
||||
if (next_line_data) {
|
||||
current_line_data = std::move(next_line_data);
|
||||
next_line_data.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +64,7 @@ void SpectrumPainterProcessor::run() {
|
||||
init_genrand(22267);
|
||||
|
||||
while (true) {
|
||||
if (fifo.is_empty() == false && next_line_data == nullptr) {
|
||||
if (fifo.is_empty() == false && !next_line_data) {
|
||||
std::vector<uint8_t> data;
|
||||
fifo.out(data);
|
||||
|
||||
@@ -74,8 +73,9 @@ void SpectrumPainterProcessor::run() {
|
||||
auto fft_width = picture_width * 2;
|
||||
auto qu = fft_width / 4;
|
||||
|
||||
complex16_t* v = new complex16_t[fft_width];
|
||||
complex16_t* tmp = new complex16_t[fft_width];
|
||||
// TODO: can these be statically allocated?
|
||||
auto v = std::make_unique<complex16_t[]>(fft_width);
|
||||
auto tmp = std::make_unique<complex16_t[]>(fft_width);
|
||||
|
||||
for (uint32_t fft_index = 0; fft_index < fft_width; fft_index++) {
|
||||
if (fft_index < qu) {
|
||||
@@ -103,10 +103,10 @@ void SpectrumPainterProcessor::run() {
|
||||
}
|
||||
}
|
||||
|
||||
ifft<complex16_t>(v, fft_width, tmp);
|
||||
ifft<complex16_t>(v.get(), fft_width, tmp.get());
|
||||
|
||||
// normalize
|
||||
volatile int32_t maximum = 1;
|
||||
int32_t maximum = 1;
|
||||
for (uint32_t i = 0; i < fft_width; i++) {
|
||||
if (v[i].real() > maximum)
|
||||
maximum = v[i].real();
|
||||
@@ -127,8 +127,7 @@ void SpectrumPainterProcessor::run() {
|
||||
}
|
||||
}
|
||||
|
||||
delete tmp;
|
||||
next_line_data = v;
|
||||
next_line_data = std::move(v);
|
||||
ui++;
|
||||
} else {
|
||||
chThdSleepMilliseconds(1);
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
|
||||
#include "rssi_dma.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "hal.h"
|
||||
#include "gpdma.hpp"
|
||||
@@ -98,8 +99,8 @@ struct buffers_config_t {
|
||||
|
||||
static buffers_config_t buffers_config;
|
||||
|
||||
static sample_t* samples{nullptr};
|
||||
static gpdma::channel::LLI* lli{nullptr};
|
||||
static std::unique_ptr<sample_t[]> samples;
|
||||
static std::unique_ptr<gpdma::channel::LLI[]> lli;
|
||||
|
||||
static ThreadWait thread_wait;
|
||||
|
||||
@@ -128,8 +129,8 @@ void allocate(size_t buffer_count, size_t items_per_buffer) {
|
||||
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_ADC1->DR[portapack::adc1_rssi_input]) + 1;
|
||||
const auto control_value = control(gpdma::buffer_words(buffers_config.items_per_buffer, 1));
|
||||
|
||||
samples = new sample_t[buffers_config.count * buffers_config.items_per_buffer];
|
||||
lli = new gpdma::channel::LLI[buffers_config.count];
|
||||
samples = std::make_unique<sample_t[]>(buffers_config.count * buffers_config.items_per_buffer);
|
||||
lli = std::make_unique<gpdma::channel::LLI[]>(buffers_config.count);
|
||||
|
||||
for (size_t i = 0; i < buffers_config.count; i++) {
|
||||
const auto memory = reinterpret_cast<uint32_t>(&samples[i * buffers_config.items_per_buffer]);
|
||||
@@ -141,8 +142,8 @@ void allocate(size_t buffer_count, size_t items_per_buffer) {
|
||||
}
|
||||
|
||||
void free() {
|
||||
delete samples;
|
||||
delete lli;
|
||||
samples.reset();
|
||||
lli.reset();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
|
||||
@@ -97,8 +97,6 @@ void init() {
|
||||
}
|
||||
|
||||
void allocate() {
|
||||
// samples = new sample_t[channel_samples_per_frame];
|
||||
// lli = new gpdma::channel::LLI;
|
||||
lli.srcaddr = reinterpret_cast<uint32_t>(&LPC_ADC0->DR[0]);
|
||||
lli.destaddr = reinterpret_cast<uint32_t>(&shared_memory.touch_adc_frame.dr[0]);
|
||||
lli.lli = lli_pointer(&lli);
|
||||
@@ -106,8 +104,6 @@ void allocate() {
|
||||
}
|
||||
|
||||
void free() {
|
||||
// delete samples;
|
||||
// delete lli;
|
||||
}
|
||||
|
||||
void enable() {
|
||||
|
||||
@@ -366,16 +366,17 @@ void Text::paint(Painter& painter) {
|
||||
const auto rect = screen_rect();
|
||||
auto s = has_focus() ? style().invert() : style();
|
||||
auto max_len = (unsigned)rect.width() / s.font.char_width();
|
||||
auto text_view = std::string_view{text};
|
||||
|
||||
painter.fill_rectangle(rect, s.background);
|
||||
|
||||
if (text.length() > max_len)
|
||||
text.resize(max_len);
|
||||
if (text_view.length() > max_len)
|
||||
text_view = text_view.substr(0, max_len);
|
||||
|
||||
painter.draw_string(
|
||||
rect.location(),
|
||||
s,
|
||||
text);
|
||||
text_view);
|
||||
}
|
||||
|
||||
/* Labels ****************************************************************/
|
||||
@@ -1764,6 +1765,24 @@ bool TextField::on_key(KeyEvent key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextField::on_encoder(EncoderEvent delta) {
|
||||
if (on_encoder_change) {
|
||||
on_encoder_change(*this, delta);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextField::on_touch(TouchEvent event) {
|
||||
if (event.type == TouchEvent::Type::Start) {
|
||||
focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* NumberField ***********************************************************/
|
||||
|
||||
NumberField::NumberField(
|
||||
|
||||
@@ -208,6 +208,10 @@ class Text : public Widget {
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
protected:
|
||||
// NB: Don't truncate this string. The UI will only render
|
||||
// as many characters as will fit in its rectange.
|
||||
// Apps expect to be able to retrieve this string to avoid
|
||||
// needing to hold additional copies in memory.
|
||||
std::string text;
|
||||
};
|
||||
|
||||
@@ -694,6 +698,7 @@ class TextField : public Text {
|
||||
public:
|
||||
std::function<void(TextField&)> on_select{};
|
||||
std::function<void(TextField&)> on_change{};
|
||||
std::function<void(TextField&, EncoderEvent)> on_encoder_change{};
|
||||
|
||||
TextField(Rect parent_rect, std::string text);
|
||||
|
||||
@@ -701,6 +706,8 @@ class TextField : public Text {
|
||||
void set_text(std::string_view value);
|
||||
|
||||
bool on_key(KeyEvent key) override;
|
||||
bool on_encoder(EncoderEvent delta) override;
|
||||
bool on_touch(TouchEvent event) override;
|
||||
|
||||
private:
|
||||
using Text::set;
|
||||
|
||||
Reference in New Issue
Block a user