Merge branch 'eried:next' into meteomodem-M20-radiosondes-altitude

This commit is contained in:
joyel24
2023-05-03 02:09:20 +02:00
committed by GitHub
14 changed files with 379 additions and 194 deletions
@@ -28,7 +28,7 @@ namespace ui
{ {
void GlassView::focus() void GlassView::focus()
{ {
field_marker.focus(); button_marker.focus();
} }
GlassView::~GlassView() GlassView::~GlassView()
@@ -43,11 +43,6 @@ namespace ui
return ((num / multiplier) + 1) * multiplier; return ((num / multiplier) + 1) * multiplier;
} }
// Returns the previous multiple of num that is a multiple of multiplier
//int64_t GlassView::prev_mult_of(int64_t num, int64_t multiplier) {
// return (num / multiplier) * multiplier;
//}
void GlassView::adjust_range(int64_t* f_min, int64_t* f_max, int64_t width) { void GlassView::adjust_range(int64_t* f_min, int64_t* f_max, int64_t width) {
int64_t span = *f_max - *f_min; int64_t span = *f_max - *f_min;
int64_t num_intervals = span / width; int64_t num_intervals = span / width;
@@ -130,7 +125,7 @@ namespace ui
last_max_freq = max_freq_hold ; last_max_freq = max_freq_hold ;
freq_stats.set( "MAX HOLD: "+to_string_short_freq( max_freq_hold ) ); freq_stats.set( "MAX HOLD: "+to_string_short_freq( max_freq_hold ) );
} }
PlotMarker(field_marker.value()); PlotMarker( marker );
} }
else else
{ {
@@ -144,13 +139,84 @@ namespace ui
// Each having the radio signal power for it's corresponding frequency slot // Each having the radio signal power for it's corresponding frequency slot
void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum) void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum)
{ {
// default fast scan offset
uint8_t offset = 2 ;
baseband::spectrum_streaming_stop(); baseband::spectrum_streaming_stop();
if( fast_scan || ( LOOKING_GLASS_SLICE_WIDTH < LOOKING_GLASS_SLICE_WIDTH_MAX ) ) if( fast_scan || ( LOOKING_GLASS_SLICE_WIDTH < LOOKING_GLASS_SLICE_WIDTH_MAX ) )
{ {
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels // Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
// Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored // Spectrum.db has 256 bins.
// All things said and done, we actually need 240 of those bins: // All things said and done, we actually need 240 of those bins
for (uint8_t bin = 0; bin < 240; bin++) for (uint8_t bin = 0; bin < 240; bin++)
{
// if the view is done in one pass, show it like in analog_audio_app
if( ( LOOKING_GLASS_SLICE_WIDTH < LOOKING_GLASS_SLICE_WIDTH_MAX ) )
{
// Center 16 bins are ignored (DC spike is blanked)
if (bin < 120)
{
if (spectrum.db[256 - 120 + bin] > max_power) // 134
max_power = spectrum.db[256 - 120 + bin];
}
else
{
if (spectrum.db[ bin - 120] > max_power) // 118
max_power = spectrum.db[bin - 120];
}
}
else // view is made in multiple pass, use original bin picking
{
// Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored
if (bin < 120)
{
if (spectrum.db[134 + bin] > max_power) // 134
max_power = spectrum.db[134 + bin];
}
else
{
if (spectrum.db[bin - 118] > max_power) // 118
max_power = spectrum.db[bin - 118];
}
}
if( bin == 120 )
{
bins_Hz_size += 12 * each_bin_size; // add DC bin Hz count into the "pixel fulfilled bag of Hz"
}
else
{
bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz"
}
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
{
if (min_color_power < max_power)
add_spectrum_pixel(max_power); // Pixel will represent max_power
else
add_spectrum_pixel(0); // Filtered out, show black
max_power = 0;
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"
f_center = f_center_ini - offset * each_bin_size ; // Start a new sweep
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
chThdSleepMilliseconds(10);
baseband::spectrum_streaming_start(); // Do the RX
return;
}
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
}
}
f_center += ( 256 - ( 2 * offset ) ) * each_bin_size ; // Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH
// lost bins are taken in account so next slice first ignored bins overlap previous kept ones
}
else //slow scan
{
offset = 32 ;
uint8_t bin_length = 80 ;
for (uint8_t bin = offset ; bin < bin_length + offset ; bin++)
{ {
if (bin < 120) if (bin < 120)
{ {
@@ -177,7 +243,7 @@ namespace ui
if (!pixel_index) // Received indication that a waterfall line has been completed 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"
f_center = f_center_ini - 2 * each_bin_size ; // Start a new sweep f_center = f_center_ini - offset * each_bin_size ; // Start a new sweep
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
chThdSleepMilliseconds(10); chThdSleepMilliseconds(10);
baseband::spectrum_streaming_start(); // Do the RX baseband::spectrum_streaming_start(); // Do the RX
@@ -186,39 +252,7 @@ namespace ui
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
} }
} }
f_center += 240 * each_bin_size ; // Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH f_center += bin_length * each_bin_size ;
}
else //slow scan
{
for (uint8_t bin = 0; bin < 120 ; bin++)
{
if (spectrum.db[134 + bin] > max_power) // 134
max_power = spectrum.db[134 + bin];
bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz"
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
{
if (min_color_power < max_power)
add_spectrum_pixel(max_power); // Pixel will represent max_power
else
add_spectrum_pixel(0); // Filtered out, show black
max_power = 0;
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"
f_center = f_center_ini - 2 * each_bin_size ; // Start a new sweep
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
chThdSleepMilliseconds(10);
baseband::spectrum_streaming_start(); // Do the RX
return;
}
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
}
}
f_center += 120 * each_bin_size ;
} }
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
chThdSleepMilliseconds(5); chThdSleepMilliseconds(5);
@@ -244,8 +278,6 @@ namespace ui
f_max = field_frequency_max.value(); f_max = field_frequency_max.value();
search_span = f_max - f_min; search_span = f_max - f_min;
field_marker.set_range(f_min, f_max); // Move the marker between range
field_marker.set_value(f_min + (search_span / 2)); // Put MARKER AT MIDDLE RANGE
if( locked_range ) if( locked_range )
{ {
button_range.set_text(">"+to_string_dec_uint(search_span)+"<"); button_range.set_text(">"+to_string_dec_uint(search_span)+"<");
@@ -260,25 +292,16 @@ namespace ui
adjust_range( &f_min , &f_max , 240 ); adjust_range( &f_min , &f_max , 240 );
marker_pixel_step = (f_max - f_min) / 240; // Each pixel value in Hz marker_pixel_step = (f_max - f_min) / 240; // Each pixel value in Hz
text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision marker = f_min + (f_max - f_min) / 2 ;
button_marker.set_text( to_string_short_freq( marker ) );
PlotMarker( marker ); // Refresh marker on screen
int32_t marker_step = marker_pixel_step / MHZ_DIV;
if (!marker_step)
field_marker.set_step(1); // in case selected range is less than 240 (pixels)
else
field_marker.set_step(marker_step); // step needs to be a pixel wide.
f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2); // Initial center frequency for sweep
PlotMarker(field_marker.value()); // Refresh marker on screen
f_center = f_center_ini; // Reset sweep into first slice
pixel_index = 0; // reset pixel counter pixel_index = 0; // reset pixel counter
max_power = 0; max_power = 0;
bins_Hz_size = 0; // reset amount of Hz filled up by pixels bins_Hz_size = 0; // reset amount of Hz filled up by pixels
if( next_mult_of( (f_max - f_min) , 240 ) <= LOOKING_GLASS_SLICE_WIDTH_MAX ) if( (f_max - f_min) <= LOOKING_GLASS_SLICE_WIDTH_MAX )
{ {
LOOKING_GLASS_SLICE_WIDTH = next_mult_of( (f_max - f_min) , 240 ); LOOKING_GLASS_SLICE_WIDTH = (f_max - f_min) ;
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH);
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH/2); receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH/2);
} }
@@ -288,20 +311,26 @@ namespace ui
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH);
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH);
} }
if( next_mult_of( LOOKING_GLASS_SLICE_WIDTH , 256 ) > LOOKING_GLASS_SLICE_WIDTH_MAX )
LOOKING_GLASS_SLICE_WIDTH = LOOKING_GLASS_SLICE_WIDTH_MAX ;
else
LOOKING_GLASS_SLICE_WIDTH = next_mult_of( LOOKING_GLASS_SLICE_WIDTH , 256 );
receiver_model.set_squelch_level(0); receiver_model.set_squelch_level(0);
each_bin_size = LOOKING_GLASS_SLICE_WIDTH / 240 ; each_bin_size = LOOKING_GLASS_SLICE_WIDTH / 256 ;
f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2) ; // Initial center frequency for sweep
f_center = f_center_ini ; // Reset sweep into first slice
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value());
receiver_model.set_tuning_frequency(f_center_ini); // tune rx for this slice receiver_model.set_tuning_frequency(f_center_ini); // tune rx for this slice
} }
void GlassView::PlotMarker(rf::Frequency pos) void GlassView::PlotMarker(rf::Frequency pos)
{ {
pos = pos * MHZ_DIV;
pos -= f_min; pos -= f_min;
pos = pos / marker_pixel_step; // Real pixel pos = pos / marker_pixel_step; // Real pixel
uint8_t shift_y = 0 ; uint8_t shift_y = 0 ;
if( live_frequency_view > 0 ) if( live_frequency_view > 0 ) // plot one line down when in live view
{ {
shift_y = 16 ; shift_y = 16 ;
} }
@@ -329,8 +358,7 @@ namespace ui
&filter_config, &filter_config,
&field_rf_amp, &field_rf_amp,
&range_presets, &range_presets,
&field_marker, &button_marker,
&text_marker_pm,
&field_trigger, &field_trigger,
&button_jump, &button_jump,
&button_rst, &button_rst,
@@ -509,16 +537,21 @@ namespace ui
this->on_range_changed(); this->on_range_changed();
}; };
field_marker.on_change = [this](int32_t v) button_marker.on_change = [this]()
{ {
PlotMarker(v); // Refresh marker on screen marker = marker + button_marker.get_encoder_delta() * marker_pixel_step ;
if( marker < f_min )
marker = f_min ;
if( marker > f_max )
marker = f_max ;
button_marker.set_text( to_string_short_freq( marker ) );
button_marker.set_encoder_delta( 0 );
PlotMarker( marker ); // Refresh marker on screen
}; };
field_marker.on_select = [this](NumberField &) button_marker.on_select = [this](ButtonWithEncoder &)
{ {
f_center = field_marker.value(); receiver_model.set_tuning_frequency(marker); // Center tune rx in marker freq.
f_center = f_center * MHZ_DIV;
receiver_model.set_tuning_frequency(f_center); // Center tune rx in marker freq.
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
nav_.pop(); nav_.pop();
nav_.push<AnalogAudioView>(); // Jump into audio view nav_.push<AnalogAudioView>(); // Jump into audio view
@@ -37,7 +37,7 @@
namespace ui namespace ui
{ {
#define LOOKING_GLASS_SLICE_WIDTH_MAX 19999920 #define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000
#define MHZ_DIV 1000000 #define MHZ_DIV 1000000
#define X2_MHZ_DIV 2000000 #define X2_MHZ_DIV 2000000
@@ -81,14 +81,13 @@ namespace ui
std::vector<preset_entry> presets_db{}; std::vector<preset_entry> presets_db{};
// Each slice bandwidth 20 MHz and a multiple of 240 // Each slice bandwidth 20 MHz and a multiple of 256
// since we are using LOOKING_GLASS_SLICE_WIDTH/240 as the each_bin_size // since we are using LOOKING_GLASS_SLICE_WIDTH/256 as the each_bin_size
// it should also be a multiple of 2 since we are using LOOKING_GLASS_SLICE_WIDTH / 2 as centering freq // it should also be a multiple of 2 since we are using LOOKING_GLASS_SLICE_WIDTH / 2 as centering freq
int64_t LOOKING_GLASS_SLICE_WIDTH = 19999920; int64_t LOOKING_GLASS_SLICE_WIDTH = 20000000;
// frequency rounding helpers // frequency rounding helpers
int64_t next_mult_of(int64_t num, int64_t multiplier); int64_t next_mult_of(int64_t num, int64_t multiplier);
//int64_t prev_mult_of(int64_t num, int64_t multiplier);
void adjust_range(int64_t* f_min, int64_t* f_max, int64_t width); void adjust_range(int64_t* f_min, int64_t* f_max, int64_t width);
void on_channel_spectrum(const ChannelSpectrum& spectrum); void on_channel_spectrum(const ChannelSpectrum& spectrum);
@@ -108,8 +107,9 @@ namespace ui
rf::Frequency search_span { 0 }; rf::Frequency search_span { 0 };
rf::Frequency f_center { 0 }; rf::Frequency f_center { 0 };
rf::Frequency f_center_ini { 0 }; rf::Frequency f_center_ini { 0 };
rf::Frequency marker { 0 };
rf::Frequency marker_pixel_step { 0 }; rf::Frequency marker_pixel_step { 0 };
rf::Frequency each_bin_size { LOOKING_GLASS_SLICE_WIDTH / 240 }; rf::Frequency each_bin_size { LOOKING_GLASS_SLICE_WIDTH / 256 };
rf::Frequency bins_Hz_size { 0 }; rf::Frequency bins_Hz_size { 0 };
uint8_t min_color_power { 0 }; uint8_t min_color_power { 0 };
uint32_t pixel_index { 0 }; uint32_t pixel_index { 0 };
@@ -129,7 +129,7 @@ namespace ui
{{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()}, {{0, 0}, "MIN: MAX: LNA VGA ", Color::light_grey()},
{{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()}, {{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()},
{{0, 2 * 16}, "PRESET:", Color::light_grey()}, {{0, 2 * 16}, "PRESET:", Color::light_grey()},
{{0, 3 * 16}, "MARKER: MHz +/- MHz", Color::light_grey()}, {{0, 3 * 16}, "MARKER: MHz", Color::light_grey()},
{{0, 4 * 16}, "RES: STEP:", Color::light_grey()} {{0, 4 * 16}, "RES: STEP:", Color::light_grey()}
}; };
@@ -180,16 +180,10 @@ namespace ui
{" NONE (WIFI 2.4GHz)", 0}, {" NONE (WIFI 2.4GHz)", 0},
}}; }};
NumberField field_marker{ ButtonWithEncoder button_marker{
{7 * 8, 3 * 16}, {7 * 8, 3 * 16 , 10 * 8 , 16},
4, " "
{0, 7200}, };
25,
' '};
Text text_marker_pm{
{20 * 8, 3 * 16, 2 * 8, 16},
""};
NumberField field_trigger{ NumberField field_trigger{
{4 * 8, 4 * 16}, {4 * 8, 4 * 16},
@@ -410,6 +410,10 @@ namespace ui {
bool load_mem_at_startup = false ; bool load_mem_at_startup = false ;
File pmem_flag_file_handle ; File pmem_flag_file_handle ;
std::string folder = "SETTINGS";
make_new_directory(folder);
std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG" ; std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG" ;
auto result = pmem_flag_file_handle.open(pmem_flag_file); auto result = pmem_flag_file_handle.open(pmem_flag_file);
if(!result.is_valid()) if(!result.is_valid())
+7 -3
View File
@@ -48,14 +48,16 @@ private:
void update_tone(); void update_tone();
void on_tx_progress(const uint32_t progress, const bool done); void on_tx_progress(const uint32_t progress, const bool done);
const std::string shape_strings[7] = { const std::string shape_strings[9] = {
"CW ", "CW ",
"Sine ", "Sine ",
"Triangle ", "Triangle ",
"Saw up ", "Saw up ",
"Saw down ", "Saw down ",
"Square ", "Square ",
"Noise" "Noise n20Khz",
"Noise n10khz",
"Noise n5khz "
}; };
bool auto_update { false }; bool auto_update { false };
@@ -78,7 +80,9 @@ private:
{ &bitmap_sig_saw_up, 3 }, { &bitmap_sig_saw_up, 3 },
{ &bitmap_sig_saw_down, 4 }, { &bitmap_sig_saw_down, 4 },
{ &bitmap_sig_square, 5 }, { &bitmap_sig_square, 5 },
{ &bitmap_sig_noise, 6 } { &bitmap_sig_noise, 6 },
{ &bitmap_sig_noise, 7 },
{ &bitmap_sig_noise, 8 }
} }
}; };
-15
View File
@@ -30,10 +30,6 @@
namespace ui { namespace ui {
void AlphanumView::paint(Painter&) {
draw_cursor();
}
AlphanumView::AlphanumView( AlphanumView::AlphanumView(
NavigationView& nav, NavigationView& nav,
std::string& str, std::string& str,
@@ -77,16 +73,7 @@ AlphanumView::AlphanumView(
field_raw.set_value('0'); field_raw.set_value('0');
field_raw.on_select = [this](NumberField&) { field_raw.on_select = [this](NumberField&) {
char_add(field_raw.value()); char_add(field_raw.value());
update_text();
}; };
button_ok.on_select = [this, &nav](Button&) {
if (on_changed)
on_changed(_str);
nav.pop();
};
update_text();
} }
void AlphanumView::set_mode(const uint32_t new_mode) { void AlphanumView::set_mode(const uint32_t new_mode) {
@@ -120,8 +107,6 @@ void AlphanumView::on_button(Button& button) {
char_delete(); char_delete();
else else
char_add(c); char_add(c);
update_text();
} }
bool AlphanumView::on_encoder(const EncoderEvent delta) { bool AlphanumView::on_encoder(const EncoderEvent delta) {
-1
View File
@@ -40,7 +40,6 @@ public:
AlphanumView& operator=(const AlphanumView&) = delete; AlphanumView& operator=(const AlphanumView&) = delete;
AlphanumView& operator=(AlphanumView&&) = delete; AlphanumView& operator=(AlphanumView&&) = delete;
void paint(Painter& painter) override;
bool on_encoder(const EncoderEvent delta) override; bool on_encoder(const EncoderEvent delta) override;
private: private:
+162 -47
View File
@@ -45,76 +45,191 @@ void text_prompt(NavigationView& nav, std::string& str, const size_t max_length,
}*/ }*/
} }
void TextEntryView::update_text() { /* TextField ***********************************************************/
if (cursor_pos < 30)
text_input.set(_str + std::string(_max_length - _str.length(), ' '));
else
text_input.set('<' + _str.substr(cursor_pos - 29, 29));
draw_cursor(); TextField::TextField(
std::string& str,
size_t max_length,
Point position,
uint32_t length
) : Widget{ { position, { 8 * static_cast<int>(length), 16 } } },
text_{ str },
max_length_{ std::max<size_t>(max_length, 1) },
char_count_{ std::max<uint32_t>(length, 1) },
cursor_pos_{ text_.length() },
insert_mode_{ true }
{
set_focusable(true);
} }
void TextEntryView::char_delete() { const std::string& TextField::value() const {
if (!cursor_pos) return; return text_;
}
cursor_pos--; void TextField::set(const std::string& str) {
_str.resize(cursor_pos); // Assume that setting the string implies we want the whole thing.
max_length_ = std::max(max_length_, str.length());
text_ = str;
cursor_pos_ = str.length();
set_cursor(str.length());
}
void TextField::set_cursor(uint32_t pos) {
cursor_pos_ = std::min<size_t>(pos, text_.length());
set_dirty();
}
void TextField::set_max_length(size_t max_length) {
// Doesn't make sense, ignore.
if (max_length == 0)
return;
if (max_length < text_.length()) {
text_.erase(max_length - 1);
text_.shrink_to_fit();
} else {
text_.reserve(max_length);
}
max_length_ = max_length;
set_cursor(cursor_pos_);
}
void TextField::set_insert_mode() {
insert_mode_ = true;
}
void TextField::set_overwrite_mode() {
insert_mode_ = false;
}
void TextField::char_add(char c) {
// Don't add if inserting and at max_length and
// don't overwrite if past the end of the text.
if ((text_.length() >= max_length_ && insert_mode_) ||
(cursor_pos_ >= text_.length() && !insert_mode_))
return;
if (insert_mode_)
text_.insert(cursor_pos_, 1, c);
else
text_[cursor_pos_] = c;
cursor_pos_++;
set_dirty();
}
void TextField::char_delete() {
if (cursor_pos_ == 0)
return;
cursor_pos_--;
text_.erase(cursor_pos_, 1);
set_dirty();
}
void TextField::paint(Painter& painter) {
constexpr int char_width = 8;
auto rect = screen_rect();
auto text_style = has_focus() ? style().invert() : style();
auto offset = 0;
// Does the string need to be shifted?
if (cursor_pos_ >= char_count_)
offset = cursor_pos_ - char_count_ + 1;
// Clear the control.
painter.fill_rectangle(rect, text_style.background);
// Draw the text starting at the offset.
for (uint32_t i = 0; i < char_count_ && i + offset < text_.length(); i++) {
painter.draw_char(
{ rect.location().x() + (static_cast<int>(i) * char_width), rect.location().y() },
text_style,
text_[i + offset]
);
}
// Determine cursor position on screen (either the cursor position or the last char).
int32_t cursor_x = char_width * (offset > 0 ? char_count_ - 1 : cursor_pos_);
Point cursor_point{ screen_pos().x() + cursor_x, screen_pos().y() };
auto cursor_style = text_style.invert();
// Invert the cursor character when in overwrite mode.
if (!insert_mode_ && (cursor_pos_) < text_.length())
painter.draw_char(cursor_point, cursor_style, text_[cursor_pos_]);
// Draw the cursor.
Rect cursor_box{ cursor_point, { char_width, 16 } };
painter.draw_rectangle(cursor_box, cursor_style.background);
}
bool TextField::on_key(const KeyEvent key) {
if (key == KeyEvent::Left && cursor_pos_ > 0)
cursor_pos_--;
else if (key == KeyEvent::Right && cursor_pos_ < text_.length())
cursor_pos_++;
else if (key == KeyEvent::Select)
insert_mode_ = !insert_mode_;
else
return false;
set_dirty();
return true;
}
bool TextField::on_encoder(const EncoderEvent delta) {
int32_t new_pos = cursor_pos_ + delta;
// Let the encoder wrap around the ends of the text.
if (new_pos < 0)
new_pos = text_.length();
else if (static_cast<size_t>(new_pos) > text_.length())
new_pos = 0;
set_cursor(new_pos);
return true;
}
bool TextField::on_touch(const TouchEvent event) {
if (event.type == TouchEvent::Type::Start)
focus();
set_dirty();
return true;
}
/* TextEntryView ***********************************************************/
void TextEntryView::char_delete() {
text_input.char_delete();
} }
void TextEntryView::char_add(const char c) { void TextEntryView::char_add(const char c) {
if (cursor_pos >= _max_length) return; text_input.char_add(c);
_str += c;
cursor_pos++;
}
void TextEntryView::draw_cursor() {
Point draw_pos;
draw_pos = { text_input.screen_rect().location().x() + std::min((Coord)cursor_pos, (Coord)28) * 8,
text_input.screen_rect().location().y() + 16 };
// Erase previous
display.fill_rectangle(
{ { text_input.screen_rect().location().x(), draw_pos.y() }, { text_input.screen_rect().size().width(), 4 } },
Color::black()
);
// Draw new
display.fill_rectangle(
{ draw_pos, { 8, 4 } },
Color::white()
);
} }
void TextEntryView::focus() { void TextEntryView::focus() {
button_ok.focus(); text_input.focus();
} }
TextEntryView::TextEntryView( TextEntryView::TextEntryView(
NavigationView& nav, NavigationView& nav,
std::string& str, std::string& str,
size_t max_length size_t max_length
) : _str(str), ) : text_input{ str, max_length, { 0, 0 } }
_max_length(max_length)
{ {
// Trim from right
//_str->erase(std::find_if(_str->rbegin(), _str->rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), _str->end());
if (_str.length() > _max_length)
_str.resize(_max_length);
_str.reserve(_max_length);
cursor_pos = _str.length();
add_children({ add_children({
&text_input, &text_input,
&button_ok &button_ok
}); });
button_ok.on_select = [this, &nav](Button&) { button_ok.on_select = [this, &str, &nav](Button&) {
_str.resize(cursor_pos); str.shrink_to_fit(); // NB: str is the TextField string.
if (on_changed) if (on_changed)
on_changed(_str); on_changed(str);
nav.pop(); nav.pop();
}; };
} }
+49 -10
View File
@@ -28,6 +28,54 @@
namespace ui { namespace ui {
// A TextField is bound to a string reference and allows the string
// to be manipulated. The field itself does not provide the UI for
// setting the value. It provides the UI of rendering the text,
// a cursor, and an API to edit the string content.
class TextField : public Widget {
public:
TextField(std::string& str, Point position, uint32_t length = 30)
: TextField{str, 64, position, length} { }
// Str: the string containing the content to edit.
// Max_length: max length the string is allowed to use.
// Position: the top-left corner of the control.
// Length: the number of characters to display.
// - Characters are 8 pixels wide.
// - The screen can show 30 characters max.
// - The control is 16 pixels tall.
TextField(std::string& str, size_t max_length, Point position, uint32_t length = 30);
TextField(const TextField&) = delete;
TextField(TextField&&) = delete;
TextField& operator=(const TextField&) = delete;
TextField& operator=(TextField&&) = delete;
const std::string& value() const;
void set(const std::string& str);
void set_cursor(uint32_t pos);
void set_max_length(size_t max_length);
void set_insert_mode();
void set_overwrite_mode();
void char_add(char c);
void char_delete();
void paint(Painter& painter) override;
bool on_key(const KeyEvent key) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
protected:
std::string& text_;
size_t max_length_;
uint32_t char_count_;
uint32_t cursor_pos_;
bool insert_mode_;
};
class TextEntryView : public View { class TextEntryView : public View {
public: public:
std::function<void(std::string&)> on_changed { }; std::function<void(std::string&)> on_changed { };
@@ -45,17 +93,8 @@ protected:
void char_add(const char c); void char_add(const char c);
void char_delete(); void char_delete();
void draw_cursor();
void update_text();
std::string& _str;
size_t _max_length;
uint32_t cursor_pos { 0 };
Text text_input {
{ 0, 0, 240, 16 }
};
TextField text_input;
Button button_ok { Button button_ok {
{ 10 * 8, 33 * 8, 9 * 8, 32 }, { 10 * 8, 33 * 8, 9 * 8, 32 },
"OK" "OK"
+20 -9
View File
@@ -49,7 +49,7 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
// Sine // Sine
sample = (sine_table_i8[(tone_phase & 0xFF000000) >> 24]); sample = (sine_table_i8[(tone_phase & 0xFF000000) >> 24]);
} else if (tone_shape == 2) { } else if (tone_shape == 2) {
// Tri // Triangle
int8_t a = (tone_phase & 0xFF000000) >> 24; int8_t a = (tone_phase & 0xFF000000) >> 24;
sample = (a & 0x80) ? ((a << 1) ^ 0xFF) - 0x80 : (a << 1) + 0x80; sample = (a & 0x80) ? ((a << 1) ^ 0xFF) - 0x80 : (a << 1) + 0x80;
} else if (tone_shape == 3) { } else if (tone_shape == 3) {
@@ -61,17 +61,27 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
} else if (tone_shape == 5) { } else if (tone_shape == 5) {
// Square // Square
sample = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128; sample = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128;
} else if (tone_shape == 6) { } else if (tone_shape == 6) { // taps: 6 5; feedback polynomial: x^6 + x^5 + 1 , Periode 63 = 2^n-1,it generates armonincs n x 20Khz
// Noise // White Noise generator, pseudo random noise generator, 8 bits linear-feedback shift register (LFSR) algorithm, variant Fibonacci.
sample = (lfsr & 0xFF000000) >> 24; // https://en.wikipedia.org/wiki/Linear-feedback_shift_register
feedback = ((lfsr >> 31) ^ (lfsr >> 29) ^ (lfsr >> 15) ^ (lfsr >> 11)) & 1; bit = ((lfsr >> 2) ^ (lfsr >> 3)) & 1;
lfsr = (lfsr << 1) | feedback; lfsr = (lfsr >> 1) | (bit << 7);
if (!lfsr) lfsr = 0x1337; // Shouldn't do this :( sample = lfsr;
} else if (tone_shape == 7) { // taps: 7 6; feedback polynomial: x^7 + x^6 + 1 , Periode 127 = 2^n-1,it generates armonincs n x 10Khz
bit = ((lfsr >> 1) ^ (lfsr >> 2)) & 1;
lfsr = (lfsr >> 1) | (bit << 7);
sample = lfsr;
} else if (tone_shape == 8) { //taps:8,6,5,4;feedback polynomial: x^8 + x^6 + x^5 + x^4 + 1,Periode 255= 2^n-1, armonics n x 5khz
bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1;
lfsr = (lfsr >> 1) | (bit << 7);
sample = lfsr;
} }
if (tone_shape < 6) {
tone_phase += tone_delta; tone_phase += tone_delta;
}
// Do FM // Do FM modulation
delta = sample * fm_delta; delta = sample * fm_delta;
phase += delta; phase += delta;
@@ -104,7 +114,8 @@ void SigGenProcessor::on_message(const Message* const msg) {
fm_delta = message.bw * (0xFFFFFFULL / 1536000); fm_delta = message.bw * (0xFFFFFFULL / 1536000);
tone_shape = message.shape; tone_shape = message.shape;
lfsr = 0x54DF0119; // lfsr = 0x54DF0119;
lfsr = seed_value ;
configured = true; configured = true;
break; break;
+6 -5
View File
@@ -38,13 +38,14 @@ private:
BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit };
uint32_t tone_delta { 0 }, fm_delta { }; uint32_t tone_delta { 0 }, fm_delta { },tone_phase { 0 };
uint32_t lfsr { }, feedback { }, tone_shape { }; uint8_t tone_shape { };
uint32_t sample_count { 0 }; uint32_t sample_count { 0 };
bool auto_off { }; bool auto_off { };
uint32_t tone_phase { 0 }, phase { 0 }, delta { 0 }, sphase { 0 }; int32_t phase { 0 }, sphase { 0 }, delta { 0 }; // they may have sign .
int8_t sample { 0 }; int8_t sample { 0 }, re { 0 }, im { 0 }; // they may have sign .
int8_t re { 0 }, im { 0 }; uint8_t seed_value = {0x56}; // seed : any nonzero start state will work.
uint8_t lfsr { }, bit { }; // bit must be 8-bit to allow bit<<7 later in the code */
TXProgressMessage txprogress_message { }; TXProgressMessage txprogress_message { };
}; };
+2 -2
View File
@@ -43,7 +43,7 @@ int Painter::draw_char(const Point p, const Style& style, const char c) {
} }
int Painter::draw_string(Point p, const Font& font, const Color foreground, int Painter::draw_string(Point p, const Font& font, const Color foreground,
const Color background, const std::string text) { const Color background, const std::string& text) {
bool escape = false; bool escape = false;
size_t width = 0; size_t width = 0;
@@ -71,7 +71,7 @@ int Painter::draw_string(Point p, const Font& font, const Color foreground,
return width; return width;
} }
int Painter::draw_string(Point p, const Style& style, const std::string text) { int Painter::draw_string(Point p, const Style& style, const std::string& text) {
return draw_string(p, style.font, style.foreground, style.background, text); return draw_string(p, style.font, style.foreground, style.background, text);
} }
+2 -2
View File
@@ -49,8 +49,8 @@ public:
int draw_char(const Point p, const Style& style, const char c); int draw_char(const Point p, const Style& style, const char c);
int draw_string(Point p, const Font& font, const Color foreground, int draw_string(Point p, const Font& font, const Color foreground,
const Color background, const std::string text); const Color background, const std::string& text);
int draw_string(Point p, const Style& style, const std::string text); int draw_string(Point p, const Style& style, const std::string& text);
void draw_bitmap(const Point p, const Bitmap& bitmap, const Color background, const Color foreground); void draw_bitmap(const Point p, const Bitmap& bitmap, const Color background, const Color foreground);
Regular → Executable
BIN
View File
Binary file not shown.
Binary file not shown.