mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-06-22 09:58:30 +00:00
Search cleanup, more binder support (#1467)
This commit is contained in:
parent
951890eaff
commit
78713cc2af
@ -153,28 +153,14 @@ FrequencySaveView::FrequencySaveView(
|
|||||||
add_children(
|
add_children(
|
||||||
{&labels,
|
{&labels,
|
||||||
&big_display,
|
&big_display,
|
||||||
&button_clear,
|
|
||||||
&button_edit,
|
|
||||||
&button_save,
|
&button_save,
|
||||||
&text_description});
|
&field_description});
|
||||||
|
|
||||||
entry_.type = freqman_type::Single;
|
entry_.type = freqman_type::Single;
|
||||||
entry_.frequency_a = value;
|
entry_.frequency_a = value;
|
||||||
entry_.description = to_string_timestamp(rtc_time::now());
|
entry_.description = to_string_timestamp(rtc_time::now());
|
||||||
refresh_ui();
|
|
||||||
|
|
||||||
button_clear.on_select = [this, &nav](Button&) {
|
bind(field_description, entry_.description, nav);
|
||||||
entry_.description = "";
|
|
||||||
refresh_ui();
|
|
||||||
};
|
|
||||||
|
|
||||||
button_edit.on_select = [this, &nav](Button&) {
|
|
||||||
temp_buffer_ = entry_.description;
|
|
||||||
text_prompt(nav_, temp_buffer_, desc_edit_max, [this](std::string& new_desc) {
|
|
||||||
entry_.description = new_desc;
|
|
||||||
refresh_ui();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
button_save.on_select = [this, &nav](Button&) {
|
button_save.on_select = [this, &nav](Button&) {
|
||||||
db_.insert_entry(db_.entry_count(), entry_);
|
db_.insert_entry(db_.entry_count(), entry_);
|
||||||
@ -182,9 +168,13 @@ FrequencySaveView::FrequencySaveView(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrequencySaveView::focus() {
|
||||||
|
refresh_ui();
|
||||||
|
FreqManBaseView::focus();
|
||||||
|
}
|
||||||
|
|
||||||
void FrequencySaveView::refresh_ui() {
|
void FrequencySaveView::refresh_ui() {
|
||||||
big_display.set(entry_.frequency_a);
|
big_display.set(entry_.frequency_a);
|
||||||
text_description.set(entry_.description);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FrequencyLoadView *************************************/
|
/* FrequencyLoadView *************************************/
|
||||||
|
@ -87,9 +87,9 @@ class FrequencySaveView : public FreqManBaseView {
|
|||||||
public:
|
public:
|
||||||
FrequencySaveView(NavigationView& nav, const rf::Frequency value);
|
FrequencySaveView(NavigationView& nav, const rf::Frequency value);
|
||||||
std::string title() const override { return "Save freq"; }
|
std::string title() const override { return "Save freq"; }
|
||||||
|
void focus() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string temp_buffer_{};
|
|
||||||
freqman_entry entry_{};
|
freqman_entry entry_{};
|
||||||
|
|
||||||
void refresh_ui();
|
void refresh_ui();
|
||||||
@ -101,15 +101,9 @@ class FrequencySaveView : public FreqManBaseView {
|
|||||||
Labels labels{
|
Labels labels{
|
||||||
{{0 * 8, 6 * 16}, "Description:", Color::white()}};
|
{{0 * 8, 6 * 16}, "Description:", Color::white()}};
|
||||||
|
|
||||||
Text text_description{{0 * 8, 7 * 16, 30 * 8, 1 * 16}};
|
TextField field_description{
|
||||||
|
{0 * 8, 7 * 16, 30 * 8, 1 * 16},
|
||||||
Button button_clear{
|
""};
|
||||||
{4 * 8, 10 * 16, 10 * 8, 2 * 16},
|
|
||||||
"Clear"};
|
|
||||||
|
|
||||||
Button button_edit{
|
|
||||||
{16 * 8, 10 * 16, 10 * 8, 2 * 16},
|
|
||||||
"Edit"};
|
|
||||||
|
|
||||||
Button button_save{
|
Button button_save{
|
||||||
{0 * 8, 17 * 16, 15 * 8, 2 * 16},
|
{0 * 8, 17 * 16, 15 * 8, 2 * 16},
|
||||||
|
@ -23,7 +23,9 @@
|
|||||||
#include "ui_search.hpp"
|
#include "ui_search.hpp"
|
||||||
|
|
||||||
#include "baseband_api.hpp"
|
#include "baseband_api.hpp"
|
||||||
|
#include "binder.hpp"
|
||||||
#include "string_format.hpp"
|
#include "string_format.hpp"
|
||||||
|
#include "ui_freqman.hpp"
|
||||||
|
|
||||||
using namespace portapack;
|
using namespace portapack;
|
||||||
|
|
||||||
@ -47,8 +49,61 @@ void RecentEntriesTable<SearchRecentEntries>::draw(
|
|||||||
painter.draw_string(target_rect.location(), style, to_string_short_freq(entry.frequency) + " " + entry.time + " " + str_duration);
|
painter.draw_string(target_rect.location(), style, to_string_short_freq(entry.frequency) + " " + entry.time + " " + str_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchView::focus() {
|
/* SearchView ********************************************/
|
||||||
field_frequency_min.focus();
|
|
||||||
|
SearchView::SearchView(
|
||||||
|
NavigationView& nav)
|
||||||
|
: nav_(nav) {
|
||||||
|
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||||
|
|
||||||
|
add_children({&labels,
|
||||||
|
&field_frequency_min,
|
||||||
|
&field_frequency_max,
|
||||||
|
&field_lna,
|
||||||
|
&field_vga,
|
||||||
|
&field_threshold,
|
||||||
|
&text_mean,
|
||||||
|
&text_slices,
|
||||||
|
&text_rate,
|
||||||
|
&text_infos,
|
||||||
|
&vu_max,
|
||||||
|
&progress_timers,
|
||||||
|
&check_snap,
|
||||||
|
&options_snap,
|
||||||
|
&big_display,
|
||||||
|
&recent_entries_view});
|
||||||
|
|
||||||
|
baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31);
|
||||||
|
|
||||||
|
recent_entries_view.set_parent_rect({0, 28 * 8, screen_width, 12 * 8});
|
||||||
|
recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) {
|
||||||
|
nav.push<FrequencySaveView>(entry.frequency);
|
||||||
|
};
|
||||||
|
|
||||||
|
text_mean.set_style(&Styles::grey);
|
||||||
|
text_slices.set_style(&Styles::grey);
|
||||||
|
text_rate.set_style(&Styles::grey);
|
||||||
|
progress_timers.set_style(&Styles::grey);
|
||||||
|
big_display.set_style(&Styles::grey);
|
||||||
|
|
||||||
|
field_frequency_min.set_step(100'000);
|
||||||
|
bind(field_frequency_min, settings_.freq_min, nav, [this](auto) {
|
||||||
|
on_range_changed();
|
||||||
|
});
|
||||||
|
|
||||||
|
field_frequency_max.set_step(100'000);
|
||||||
|
bind(field_frequency_max, settings_.freq_max, nav, [this](auto) {
|
||||||
|
on_range_changed();
|
||||||
|
});
|
||||||
|
|
||||||
|
bind(field_threshold, settings_.power_threshold);
|
||||||
|
bind(check_snap, settings_.snap_search);
|
||||||
|
bind(options_snap, settings_.snap_step);
|
||||||
|
|
||||||
|
progress_timers.set_max(DETECT_DELAY);
|
||||||
|
|
||||||
|
on_range_changed();
|
||||||
|
receiver_model.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchView::~SearchView() {
|
SearchView::~SearchView() {
|
||||||
@ -56,6 +111,18 @@ SearchView::~SearchView() {
|
|||||||
baseband::shutdown();
|
baseband::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SearchView::on_show() {
|
||||||
|
baseband::spectrum_streaming_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchView::on_hide() {
|
||||||
|
baseband::spectrum_streaming_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchView::focus() {
|
||||||
|
field_frequency_min.focus();
|
||||||
|
}
|
||||||
|
|
||||||
void SearchView::do_detection() {
|
void SearchView::do_detection() {
|
||||||
uint8_t power_max = 0;
|
uint8_t power_max = 0;
|
||||||
int32_t bin_max = -1;
|
int32_t bin_max = -1;
|
||||||
@ -83,7 +150,7 @@ void SearchView::do_detection() {
|
|||||||
if (power > overall_power_max)
|
if (power > overall_power_max)
|
||||||
overall_power_max = power;
|
overall_power_max = power;
|
||||||
|
|
||||||
if ((power >= mean_power + power_threshold) && (power > power_max)) {
|
if ((power >= mean_power + settings_.power_threshold) && (power > power_max)) {
|
||||||
power_max = power;
|
power_max = power;
|
||||||
bin_max = slices[slice].max_index;
|
bin_max = slices[slice].max_index;
|
||||||
slice_max = slice;
|
slice_max = slice;
|
||||||
@ -104,7 +171,7 @@ void SearchView::do_detection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check range
|
// Check range
|
||||||
if ((resolved_frequency >= f_min) && (resolved_frequency <= f_max)) {
|
if ((resolved_frequency >= settings_.freq_min) && (resolved_frequency <= settings_.freq_max)) {
|
||||||
duration = 0;
|
duration = 0;
|
||||||
|
|
||||||
auto& entry = ::on_packet(recent, resolved_frequency);
|
auto& entry = ::on_packet(recent, resolved_frequency);
|
||||||
@ -159,16 +226,47 @@ void SearchView::do_detection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchView::add_spectrum_pixel(Color color) {
|
void SearchView::do_timers() {
|
||||||
// Is avoiding floats really necessary ?
|
if (timing_div >= 60) {
|
||||||
bin_skip_acc += bin_skip_frac;
|
// ~1Hz
|
||||||
if (bin_skip_acc < 0x10000)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bin_skip_acc -= 0x10000;
|
timing_div = 0;
|
||||||
|
|
||||||
if (pixel_index < 240)
|
// Update scan rate
|
||||||
spectrum_row[pixel_index++] = color;
|
text_rate.set(to_string_dec_uint(search_counter, 3));
|
||||||
|
search_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timing_div % 12 == 0) {
|
||||||
|
// ~5Hz
|
||||||
|
|
||||||
|
// Update power levels
|
||||||
|
text_mean.set(to_string_dec_uint(mean_power, 3));
|
||||||
|
|
||||||
|
vu_max.set_value(overall_power_max);
|
||||||
|
vu_max.set_mark(mean_power + settings_.power_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timing_div % 6 == 0) {
|
||||||
|
// ~10Hz
|
||||||
|
|
||||||
|
// Update timing indicator
|
||||||
|
if (locked) {
|
||||||
|
progress_timers.set_max(RELEASE_DELAY);
|
||||||
|
progress_timers.set_value(RELEASE_DELAY - release_timer);
|
||||||
|
} else {
|
||||||
|
progress_timers.set_max(DETECT_DELAY);
|
||||||
|
progress_timers.set_value(detect_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment timers
|
||||||
|
if (detect_timer < DETECT_DELAY) detect_timer++;
|
||||||
|
if (release_timer < RELEASE_DELAY) release_timer++;
|
||||||
|
|
||||||
|
if (locked) duration++;
|
||||||
|
}
|
||||||
|
|
||||||
|
timing_div++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||||
@ -221,22 +319,14 @@ void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
|||||||
baseband::spectrum_streaming_start();
|
baseband::spectrum_streaming_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchView::on_show() {
|
|
||||||
baseband::spectrum_streaming_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchView::on_hide() {
|
|
||||||
baseband::spectrum_streaming_stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchView::on_range_changed() {
|
void SearchView::on_range_changed() {
|
||||||
rf::Frequency slices_span, center_frequency;
|
rf::Frequency slices_span;
|
||||||
|
rf::Frequency center_frequency;
|
||||||
int64_t offset;
|
int64_t offset;
|
||||||
size_t slice;
|
size_t slice;
|
||||||
|
|
||||||
f_min = field_frequency_min.value();
|
// TODO: enforce min < max?
|
||||||
f_max = field_frequency_max.value();
|
search_span = abs(settings_.freq_max - settings_.freq_min);
|
||||||
search_span = abs(f_max - f_min);
|
|
||||||
|
|
||||||
if (search_span > SEARCH_SLICE_WIDTH) {
|
if (search_span > SEARCH_SLICE_WIDTH) {
|
||||||
// ex: 100M~115M (15M span):
|
// ex: 100M~115M (15M span):
|
||||||
@ -253,14 +343,14 @@ void SearchView::on_range_changed() {
|
|||||||
// offset = 0 + 2.5/2 = 1.25M
|
// offset = 0 + 2.5/2 = 1.25M
|
||||||
offset = ((search_span - slices_span) / 2) + (SEARCH_SLICE_WIDTH / 2);
|
offset = ((search_span - slices_span) / 2) + (SEARCH_SLICE_WIDTH / 2);
|
||||||
// slice_start = 100M + 1.25M = 101.25M
|
// slice_start = 100M + 1.25M = 101.25M
|
||||||
center_frequency = std::min(f_min, f_max) + offset;
|
center_frequency = std::min(settings_.freq_min, settings_.freq_max) + offset;
|
||||||
|
|
||||||
for (slice = 0; slice < slices_nb; slice++) {
|
for (slice = 0; slice < slices_nb; slice++) {
|
||||||
slices[slice].center_frequency = center_frequency;
|
slices[slice].center_frequency = center_frequency;
|
||||||
center_frequency += SEARCH_SLICE_WIDTH;
|
center_frequency += SEARCH_SLICE_WIDTH;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
slices[0].center_frequency = (f_max + f_min) / 2;
|
slices[0].center_frequency = (settings_.freq_max + settings_.freq_min) / 2;
|
||||||
receiver_model.set_target_frequency(slices[0].center_frequency);
|
receiver_model.set_target_frequency(slices[0].center_frequency);
|
||||||
|
|
||||||
slices_nb = 1;
|
slices_nb = 1;
|
||||||
@ -272,139 +362,16 @@ void SearchView::on_range_changed() {
|
|||||||
slice_counter = 0;
|
slice_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchView::on_lna_changed(int32_t v_db) {
|
void SearchView::add_spectrum_pixel(Color color) {
|
||||||
receiver_model.set_lna(v_db);
|
// Is avoiding floats really necessary?
|
||||||
}
|
bin_skip_acc += bin_skip_frac;
|
||||||
|
if (bin_skip_acc < 0x10000)
|
||||||
|
return;
|
||||||
|
|
||||||
void SearchView::on_vga_changed(int32_t v_db) {
|
bin_skip_acc -= 0x10000;
|
||||||
receiver_model.set_vga(v_db);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SearchView::do_timers() {
|
if (pixel_index < screen_width)
|
||||||
if (timing_div >= 60) {
|
spectrum_row[pixel_index++] = color;
|
||||||
// ~1Hz
|
|
||||||
|
|
||||||
timing_div = 0;
|
|
||||||
|
|
||||||
// Update scan rate
|
|
||||||
text_rate.set(to_string_dec_uint(search_counter, 3));
|
|
||||||
search_counter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timing_div % 12 == 0) {
|
|
||||||
// ~5Hz
|
|
||||||
|
|
||||||
// Update power levels
|
|
||||||
text_mean.set(to_string_dec_uint(mean_power, 3));
|
|
||||||
|
|
||||||
vu_max.set_value(overall_power_max);
|
|
||||||
vu_max.set_mark(mean_power + power_threshold);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timing_div % 6 == 0) {
|
|
||||||
// ~10Hz
|
|
||||||
|
|
||||||
// Update timing indicator
|
|
||||||
if (locked) {
|
|
||||||
progress_timers.set_max(RELEASE_DELAY);
|
|
||||||
progress_timers.set_value(RELEASE_DELAY - release_timer);
|
|
||||||
} else {
|
|
||||||
progress_timers.set_max(DETECT_DELAY);
|
|
||||||
progress_timers.set_value(detect_timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment timers
|
|
||||||
if (detect_timer < DETECT_DELAY) detect_timer++;
|
|
||||||
if (release_timer < RELEASE_DELAY) release_timer++;
|
|
||||||
|
|
||||||
if (locked) duration++;
|
|
||||||
}
|
|
||||||
|
|
||||||
timing_div++;
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchView::SearchView(
|
|
||||||
NavigationView& nav)
|
|
||||||
: nav_(nav) {
|
|
||||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
|
||||||
|
|
||||||
add_children({&labels,
|
|
||||||
&field_frequency_min,
|
|
||||||
&field_frequency_max,
|
|
||||||
&field_lna,
|
|
||||||
&field_vga,
|
|
||||||
&field_threshold,
|
|
||||||
&text_mean,
|
|
||||||
&text_slices,
|
|
||||||
&text_rate,
|
|
||||||
&text_infos,
|
|
||||||
&vu_max,
|
|
||||||
&progress_timers,
|
|
||||||
&check_snap,
|
|
||||||
&options_snap,
|
|
||||||
&big_display,
|
|
||||||
&recent_entries_view});
|
|
||||||
|
|
||||||
baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31);
|
|
||||||
|
|
||||||
recent_entries_view.set_parent_rect({0, 28 * 8, 240, 12 * 8});
|
|
||||||
recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) {
|
|
||||||
nav.push<FrequencyKeypadView>(entry.frequency);
|
|
||||||
};
|
|
||||||
|
|
||||||
text_mean.set_style(&Styles::grey);
|
|
||||||
text_slices.set_style(&Styles::grey);
|
|
||||||
text_rate.set_style(&Styles::grey);
|
|
||||||
progress_timers.set_style(&Styles::grey);
|
|
||||||
big_display.set_style(&Styles::grey);
|
|
||||||
|
|
||||||
check_snap.set_value(true);
|
|
||||||
options_snap.set_selected_index(1); // 12.5kHz
|
|
||||||
|
|
||||||
field_threshold.set_value(80);
|
|
||||||
field_threshold.on_change = [this](int32_t value) {
|
|
||||||
power_threshold = value;
|
|
||||||
};
|
|
||||||
|
|
||||||
field_frequency_min.set_value(receiver_model.target_frequency() - 1000000);
|
|
||||||
field_frequency_min.set_step(100000);
|
|
||||||
field_frequency_min.on_change = [this](rf::Frequency) {
|
|
||||||
this->on_range_changed();
|
|
||||||
};
|
|
||||||
field_frequency_min.on_edit = [this, &nav]() {
|
|
||||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency() - 1000000);
|
|
||||||
new_view->on_changed = [this](rf::Frequency f) {
|
|
||||||
this->field_frequency_min.set_value(f);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
field_frequency_max.set_value(receiver_model.target_frequency() + 1000000);
|
|
||||||
field_frequency_max.set_step(100000);
|
|
||||||
field_frequency_max.on_change = [this](rf::Frequency) {
|
|
||||||
this->on_range_changed();
|
|
||||||
};
|
|
||||||
field_frequency_max.on_edit = [this, &nav]() {
|
|
||||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency() + 1000000);
|
|
||||||
new_view->on_changed = [this](rf::Frequency f) {
|
|
||||||
this->field_frequency_max.set_value(f);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
field_lna.set_value(receiver_model.lna());
|
|
||||||
field_lna.on_change = [this](int32_t v) {
|
|
||||||
this->on_lna_changed(v);
|
|
||||||
};
|
|
||||||
|
|
||||||
field_vga.set_value(receiver_model.vga());
|
|
||||||
field_vga.on_change = [this](int32_t v_db) {
|
|
||||||
this->on_vga_changed(v_db);
|
|
||||||
};
|
|
||||||
|
|
||||||
progress_timers.set_max(DETECT_DELAY);
|
|
||||||
|
|
||||||
on_range_changed();
|
|
||||||
|
|
||||||
receiver_model.enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace ui */
|
} /* namespace ui */
|
||||||
|
@ -20,14 +20,13 @@
|
|||||||
* Boston, MA 02110-1301, USA.
|
* Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "app_settings.hpp"
|
||||||
#include "receiver_model.hpp"
|
#include "receiver_model.hpp"
|
||||||
|
#include "recent_entries.hpp"
|
||||||
#include "radio_state.hpp"
|
#include "radio_state.hpp"
|
||||||
|
|
||||||
#include "spectrum_color_lut.hpp"
|
#include "spectrum_color_lut.hpp"
|
||||||
|
|
||||||
#include "ui_receiver.hpp"
|
#include "ui_receiver.hpp"
|
||||||
#include "ui_styles.hpp"
|
#include "ui_styles.hpp"
|
||||||
#include "recent_entries.hpp"
|
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
@ -95,6 +94,26 @@ class SearchView : public View {
|
|||||||
SEARCH_SLICE_WIDTH /* sampling rate */,
|
SEARCH_SLICE_WIDTH /* sampling rate */,
|
||||||
ReceiverModel::Mode::SpectrumAnalysis};
|
ReceiverModel::Mode::SpectrumAnalysis};
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
struct SearchSettings {
|
||||||
|
uint32_t power_threshold = 80;
|
||||||
|
rf::Frequency freq_min = 100'000'000;
|
||||||
|
rf::Frequency freq_max = 400'000'000;
|
||||||
|
bool snap_search = true;
|
||||||
|
uint32_t snap_step = 12'500;
|
||||||
|
};
|
||||||
|
SearchSettings settings_{};
|
||||||
|
app_settings::SettingsManager app_settings_{
|
||||||
|
"rx_search"sv,
|
||||||
|
app_settings::Mode::RX,
|
||||||
|
{
|
||||||
|
{"power_threshold"sv, &settings_.power_threshold},
|
||||||
|
{"freq_min"sv, &settings_.freq_min},
|
||||||
|
{"freq_max"sv, &settings_.freq_max},
|
||||||
|
{"snap_search"sv, &settings_.snap_search},
|
||||||
|
{"snap_step"sv, &settings_.snap_step},
|
||||||
|
}};
|
||||||
|
|
||||||
struct slice_t {
|
struct slice_t {
|
||||||
rf::Frequency center_frequency;
|
rf::Frequency center_frequency;
|
||||||
uint8_t max_power;
|
uint8_t max_power;
|
||||||
@ -103,33 +122,36 @@ class SearchView : public View {
|
|||||||
int16_t index;
|
int16_t index;
|
||||||
} slices[32];
|
} slices[32];
|
||||||
|
|
||||||
uint32_t bin_skip_acc{0}, bin_skip_frac{};
|
uint32_t bin_skip_acc = 0;
|
||||||
uint32_t pixel_index{0};
|
uint32_t bin_skip_frac = 0;
|
||||||
std::array<Color, 240> spectrum_row = {0};
|
uint32_t pixel_index = 0;
|
||||||
ChannelSpectrumFIFO* fifo{nullptr};
|
std::array<Color, 240> spectrum_row{};
|
||||||
rf::Frequency f_min{0}, f_max{0};
|
ChannelSpectrumFIFO* fifo = nullptr;
|
||||||
uint8_t detect_timer{0}, release_timer{0}, timing_div{0};
|
|
||||||
uint8_t overall_power_max{0};
|
|
||||||
uint32_t mean_power{0}, mean_acc{0};
|
|
||||||
uint32_t duration{0};
|
|
||||||
uint32_t power_threshold{80}; // Todo: Put this in persistent / settings
|
|
||||||
rf::Frequency slice_start{0};
|
|
||||||
uint8_t slices_nb{0};
|
|
||||||
uint8_t slice_counter{0};
|
|
||||||
int16_t last_bin{0};
|
|
||||||
uint32_t last_slice{0};
|
|
||||||
Coord last_tick_pos{0};
|
|
||||||
rf::Frequency search_span{0}, resolved_frequency{0};
|
|
||||||
uint16_t locked_bin{0};
|
|
||||||
uint8_t search_counter{0};
|
|
||||||
bool locked{false};
|
|
||||||
|
|
||||||
|
uint8_t detect_timer = 0;
|
||||||
|
uint8_t release_timer = 0;
|
||||||
|
uint8_t timing_div = 0;
|
||||||
|
uint8_t overall_power_max{0};
|
||||||
|
uint32_t mean_power = 0;
|
||||||
|
uint32_t mean_acc = 0;
|
||||||
|
uint32_t duration = 0;
|
||||||
|
|
||||||
|
rf::Frequency slice_start = 0;
|
||||||
|
uint8_t slices_nb = 0;
|
||||||
|
uint8_t slice_counter = 0;
|
||||||
|
int16_t last_bin = 0;
|
||||||
|
uint32_t last_slice = 0;
|
||||||
|
Coord last_tick_pos = 0;
|
||||||
|
rf::Frequency search_span = 0;
|
||||||
|
rf::Frequency resolved_frequency = 0;
|
||||||
|
uint16_t locked_bin = 0;
|
||||||
|
uint8_t search_counter = 0;
|
||||||
|
bool locked = false;
|
||||||
|
|
||||||
|
void do_detection();
|
||||||
|
void do_timers();
|
||||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||||
void on_range_changed();
|
void on_range_changed();
|
||||||
void do_detection();
|
|
||||||
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);
|
void add_spectrum_pixel(Color color);
|
||||||
|
|
||||||
const RecentEntriesColumns columns{{{"Frequency", 9},
|
const RecentEntriesColumns columns{{{"Frequency", 9},
|
||||||
@ -150,6 +172,7 @@ class SearchView : public View {
|
|||||||
{1 * 8, 1 * 16}};
|
{1 * 8, 1 * 16}};
|
||||||
FrequencyField field_frequency_max{
|
FrequencyField field_frequency_max{
|
||||||
{11 * 8, 1 * 16}};
|
{11 * 8, 1 * 16}};
|
||||||
|
|
||||||
LNAGainField field_lna{
|
LNAGainField field_lna{
|
||||||
{22 * 8, 1 * 16}};
|
{22 * 8, 1 * 16}};
|
||||||
VGAGainField field_vga{
|
VGAGainField field_vga{
|
||||||
@ -191,10 +214,10 @@ class SearchView : public View {
|
|||||||
{17 * 8, 15 * 8}, // Position
|
{17 * 8, 15 * 8}, // Position
|
||||||
7, // Length
|
7, // Length
|
||||||
{ // Options
|
{ // Options
|
||||||
{"25kHz ", 25000},
|
{"25kHz ", 25'000},
|
||||||
{"12.5kHz", 12500},
|
{"12.5kHz", 12'500},
|
||||||
{"8.33kHz", 8333},
|
{"8.33kHz", 8'333},
|
||||||
{"2.5kHz", 2500},
|
{"2.5kHz", 2'500},
|
||||||
{"500Hz", 500}}};
|
{"500Hz", 500}}};
|
||||||
|
|
||||||
BigFrequency big_display{
|
BigFrequency big_display{
|
||||||
@ -207,6 +230,7 @@ class SearchView : public View {
|
|||||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||||
this->fifo = message.fifo;
|
this->fifo = message.fifo;
|
||||||
}};
|
}};
|
||||||
|
|
||||||
MessageHandlerRegistration message_handler_frame_sync{
|
MessageHandlerRegistration message_handler_frame_sync{
|
||||||
Message::ID::DisplayFrameSync,
|
Message::ID::DisplayFrameSync,
|
||||||
[this](const Message* const) {
|
[this](const Message* const) {
|
||||||
|
@ -52,6 +52,15 @@ struct NoOp {
|
|||||||
* adequate lifetime of the referenced instances.
|
* adequate lifetime of the referenced instances.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
template <typename T, typename Fn = NoOp>
|
||||||
|
void bind(Checkbox& check, T& value, Fn fn = Fn{}) {
|
||||||
|
check.set_value(value);
|
||||||
|
check.on_select = [&value, fn](Checkbox&, bool b) {
|
||||||
|
value = b;
|
||||||
|
fn(value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename Fn = NoOp>
|
template <typename T, typename Fn = NoOp>
|
||||||
void bind(NumberField& field, T& value, Fn fn = Fn{}) {
|
void bind(NumberField& field, T& value, Fn fn = Fn{}) {
|
||||||
field.set_value(value);
|
field.set_value(value);
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "irq_controls.hpp"
|
||||||
#include "string_format.hpp"
|
#include "string_format.hpp"
|
||||||
|
|
||||||
using namespace portapack;
|
using namespace portapack;
|
||||||
@ -1730,9 +1731,15 @@ bool TextEdit::on_key(const KeyEvent key) {
|
|||||||
cursor_pos_--;
|
cursor_pos_--;
|
||||||
else if (key == KeyEvent::Right && cursor_pos_ < text_.length())
|
else if (key == KeyEvent::Right && cursor_pos_ < text_.length())
|
||||||
cursor_pos_++;
|
cursor_pos_++;
|
||||||
else if (key == KeyEvent::Select)
|
else if (key == KeyEvent::Select) {
|
||||||
insert_mode_ = !insert_mode_;
|
if (key_is_long_pressed(key)) {
|
||||||
else
|
// Delete text to the cursor.
|
||||||
|
text_ = text_.substr(cursor_pos_);
|
||||||
|
set_cursor(0);
|
||||||
|
} else {
|
||||||
|
insert_mode_ = !insert_mode_;
|
||||||
|
}
|
||||||
|
} else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
set_dirty();
|
set_dirty();
|
||||||
@ -1760,6 +1767,19 @@ bool TextEdit::on_touch(const TouchEvent event) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextEdit::on_focus() {
|
||||||
|
// Enable long press on "Select".
|
||||||
|
SwitchesState config;
|
||||||
|
config[toUType(Switch::Sel)] = true;
|
||||||
|
set_switches_long_press_config(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextEdit::on_blur() {
|
||||||
|
// Reset long press.
|
||||||
|
SwitchesState config{};
|
||||||
|
set_switches_long_press_config(config);
|
||||||
|
}
|
||||||
|
|
||||||
/* TextField *************************************************************/
|
/* TextField *************************************************************/
|
||||||
|
|
||||||
TextField::TextField(Rect parent_rect, std::string text)
|
TextField::TextField(Rect parent_rect, std::string text)
|
||||||
|
@ -689,6 +689,9 @@ class TextEdit : public Widget {
|
|||||||
bool on_encoder(const EncoderEvent delta) override;
|
bool on_encoder(const EncoderEvent delta) override;
|
||||||
bool on_touch(const TouchEvent event) override;
|
bool on_touch(const TouchEvent event) override;
|
||||||
|
|
||||||
|
void on_focus() override;
|
||||||
|
void on_blur() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string& text_;
|
std::string& text_;
|
||||||
size_t max_length_;
|
size_t max_length_;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user