diff --git a/firmware/application/apps/ui_looking_glass_app.cpp b/firmware/application/apps/ui_looking_glass_app.cpp index 5d696476..6e03eda2 100644 --- a/firmware/application/apps/ui_looking_glass_app.cpp +++ b/firmware/application/apps/ui_looking_glass_app.cpp @@ -28,7 +28,7 @@ namespace ui { void GlassView::focus() { - field_marker.focus(); + button_marker.focus(); } GlassView::~GlassView() @@ -43,11 +43,6 @@ namespace ui 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) { int64_t span = *f_max - *f_min; int64_t num_intervals = span / width; @@ -130,7 +125,7 @@ namespace ui last_max_freq = max_freq_hold ; freq_stats.set( "MAX HOLD: "+to_string_short_freq( max_freq_hold ) ); } - PlotMarker(field_marker.value()); + PlotMarker( marker ); } else { @@ -144,13 +139,84 @@ namespace ui // Each having the radio signal power for it's corresponding frequency slot void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum) { + // default fast scan offset + uint8_t offset = 2 ; baseband::spectrum_streaming_stop(); 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 - // Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored - // All things said and done, we actually need 240 of those bins: + // Spectrum.db has 256 bins. + // All things said and done, we actually need 240 of those bins 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) { @@ -165,38 +231,6 @@ namespace ui 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 += 240 * each_bin_size ; // Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH - } - 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) @@ -209,7 +243,7 @@ namespace ui 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 + 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 @@ -218,7 +252,7 @@ namespace ui bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel } } - f_center += 120 * each_bin_size ; + f_center += bin_length * each_bin_size ; } radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving chThdSleepMilliseconds(5); @@ -243,9 +277,7 @@ namespace ui f_min = field_frequency_min.value(); f_max = field_frequency_max.value(); 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 ) { button_range.set_text(">"+to_string_dec_uint(search_span)+"<"); @@ -260,25 +292,16 @@ namespace ui adjust_range( &f_min , &f_max , 240 ); 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 max_power = 0; 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_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_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); - 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()); receiver_model.set_tuning_frequency(f_center_ini); // tune rx for this slice } void GlassView::PlotMarker(rf::Frequency pos) { - pos = pos * MHZ_DIV; pos -= f_min; pos = pos / marker_pixel_step; // Real pixel 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 ; } @@ -329,8 +358,7 @@ namespace ui &filter_config, &field_rf_amp, &range_presets, - &field_marker, - &text_marker_pm, + &button_marker, &field_trigger, &button_jump, &button_rst, @@ -509,16 +537,21 @@ namespace ui 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(); - f_center = f_center * MHZ_DIV; - receiver_model.set_tuning_frequency(f_center); // Center tune rx in marker freq. + receiver_model.set_tuning_frequency(marker); // Center tune rx in marker freq. receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO nav_.pop(); nav_.push(); // Jump into audio view diff --git a/firmware/application/apps/ui_looking_glass_app.hpp b/firmware/application/apps/ui_looking_glass_app.hpp index 5b1df967..3b05edb7 100644 --- a/firmware/application/apps/ui_looking_glass_app.hpp +++ b/firmware/application/apps/ui_looking_glass_app.hpp @@ -37,7 +37,7 @@ namespace ui { -#define LOOKING_GLASS_SLICE_WIDTH_MAX 19999920 +#define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000 #define MHZ_DIV 1000000 #define X2_MHZ_DIV 2000000 @@ -81,14 +81,13 @@ namespace ui std::vector presets_db{}; - // Each slice bandwidth 20 MHz and a multiple of 240 - // since we are using LOOKING_GLASS_SLICE_WIDTH/240 as the each_bin_size + // Each slice bandwidth 20 MHz and a multiple of 256 + // 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 - int64_t LOOKING_GLASS_SLICE_WIDTH = 19999920; + int64_t LOOKING_GLASS_SLICE_WIDTH = 20000000; // frequency rounding helpers 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 on_channel_spectrum(const ChannelSpectrum& spectrum); @@ -108,8 +107,9 @@ namespace ui rf::Frequency search_span { 0 }; rf::Frequency f_center { 0 }; rf::Frequency f_center_ini { 0 }; + rf::Frequency marker { 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 }; uint8_t min_color_power { 0 }; uint32_t pixel_index { 0 }; @@ -129,7 +129,7 @@ namespace ui {{0, 0}, "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 +/- MHz", Color::light_grey()}, + {{0, 3 * 16}, "MARKER: MHz", Color::light_grey()}, {{0, 4 * 16}, "RES: STEP:", Color::light_grey()} }; @@ -180,16 +180,10 @@ namespace ui {" NONE (WIFI 2.4GHz)", 0}, }}; - NumberField field_marker{ - {7 * 8, 3 * 16}, - 4, - {0, 7200}, - 25, - ' '}; - - Text text_marker_pm{ - {20 * 8, 3 * 16, 2 * 8, 16}, - ""}; + ButtonWithEncoder button_marker{ + {7 * 8, 3 * 16 , 10 * 8 , 16}, + " " + }; NumberField field_trigger{ {4 * 8, 4 * 16}, diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index bb92087c..1e11e9cf 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -410,6 +410,10 @@ namespace ui { bool load_mem_at_startup = false ; File pmem_flag_file_handle ; + + std::string folder = "SETTINGS"; + make_new_directory(folder); + std::string pmem_flag_file = "/SETTINGS/PMEM_FILEFLAG" ; auto result = pmem_flag_file_handle.open(pmem_flag_file); if(!result.is_valid()) diff --git a/firmware/application/apps/ui_siggen.hpp b/firmware/application/apps/ui_siggen.hpp index f55d19d1..58ed49b8 100644 --- a/firmware/application/apps/ui_siggen.hpp +++ b/firmware/application/apps/ui_siggen.hpp @@ -48,14 +48,16 @@ private: void update_tone(); void on_tx_progress(const uint32_t progress, const bool done); - const std::string shape_strings[7] = { - "CW", - "Sine", - "Triangle", - "Saw up", - "Saw down", - "Square", - "Noise" + const std::string shape_strings[9] = { + "CW ", + "Sine ", + "Triangle ", + "Saw up ", + "Saw down ", + "Square ", + "Noise n20Khz", + "Noise n10khz", + "Noise n5khz " }; bool auto_update { false }; @@ -78,7 +80,9 @@ private: { &bitmap_sig_saw_up, 3 }, { &bitmap_sig_saw_down, 4 }, { &bitmap_sig_square, 5 }, - { &bitmap_sig_noise, 6 } + { &bitmap_sig_noise, 6 }, + { &bitmap_sig_noise, 7 }, + { &bitmap_sig_noise, 8 } } }; diff --git a/firmware/application/ui/ui_alphanum.cpp b/firmware/application/ui/ui_alphanum.cpp index 16d2f300..f9161fc5 100644 --- a/firmware/application/ui/ui_alphanum.cpp +++ b/firmware/application/ui/ui_alphanum.cpp @@ -30,10 +30,6 @@ namespace ui { -void AlphanumView::paint(Painter&) { - draw_cursor(); -} - AlphanumView::AlphanumView( NavigationView& nav, std::string& str, @@ -41,7 +37,7 @@ AlphanumView::AlphanumView( ) : TextEntryView(nav, str, max_length) { size_t n; - + add_children({ &button_mode, &text_raw, @@ -77,16 +73,7 @@ AlphanumView::AlphanumView( field_raw.set_value('0'); field_raw.on_select = [this](NumberField&) { 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) { @@ -120,8 +107,6 @@ void AlphanumView::on_button(Button& button) { char_delete(); else char_add(c); - - update_text(); } bool AlphanumView::on_encoder(const EncoderEvent delta) { diff --git a/firmware/application/ui/ui_alphanum.hpp b/firmware/application/ui/ui_alphanum.hpp index 1e2e42ac..d61ee7fe 100644 --- a/firmware/application/ui/ui_alphanum.hpp +++ b/firmware/application/ui/ui_alphanum.hpp @@ -40,7 +40,6 @@ public: AlphanumView& operator=(const AlphanumView&) = delete; AlphanumView& operator=(AlphanumView&&) = delete; - void paint(Painter& painter) override; bool on_encoder(const EncoderEvent delta) override; private: diff --git a/firmware/application/ui/ui_textentry.cpp b/firmware/application/ui/ui_textentry.cpp index e4e008c1..d867ae62 100644 --- a/firmware/application/ui/ui_textentry.cpp +++ b/firmware/application/ui/ui_textentry.cpp @@ -45,76 +45,191 @@ void text_prompt(NavigationView& nav, std::string& str, const size_t max_length, }*/ } -void TextEntryView::update_text() { - 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::TextField( + std::string& str, + size_t max_length, + Point position, + uint32_t length +) : Widget{ { position, { 8 * static_cast(length), 16 } } }, + text_{ str }, + max_length_{ std::max(max_length, 1) }, + char_count_{ std::max(length, 1) }, + cursor_pos_{ text_.length() }, + insert_mode_{ true } +{ + set_focusable(true); } +const std::string& TextField::value() const { + return text_; +} + +void TextField::set(const std::string& str) { + // 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(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(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(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() { - if (!cursor_pos) return; - - cursor_pos--; - _str.resize(cursor_pos); + text_input.char_delete(); } void TextEntryView::char_add(const char c) { - if (cursor_pos >= _max_length) return; - - _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() - ); + text_input.char_add(c); } void TextEntryView::focus() { - button_ok.focus(); + text_input.focus(); } TextEntryView::TextEntryView( NavigationView& nav, std::string& str, size_t max_length -) : _str(str), - _max_length(max_length) +) : text_input{ str, max_length, { 0, 0 } } { - - // Trim from right - //_str->erase(std::find_if(_str->rbegin(), _str->rend(), std::not1(std::ptr_fun(std::isspace))).base(), _str->end()); - if (_str.length() > _max_length) - _str.resize(_max_length); - _str.reserve(_max_length); - - cursor_pos = _str.length(); - add_children({ &text_input, &button_ok }); - - button_ok.on_select = [this, &nav](Button&) { - _str.resize(cursor_pos); + + button_ok.on_select = [this, &str, &nav](Button&) { + str.shrink_to_fit(); // NB: str is the TextField string. if (on_changed) - on_changed(_str); + on_changed(str); nav.pop(); }; } diff --git a/firmware/application/ui/ui_textentry.hpp b/firmware/application/ui/ui_textentry.hpp index 9cd6f519..3641f2be 100644 --- a/firmware/application/ui/ui_textentry.hpp +++ b/firmware/application/ui/ui_textentry.hpp @@ -28,6 +28,54 @@ 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 { public: std::function on_changed { }; @@ -45,17 +93,8 @@ protected: void char_add(const char c); 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 { { 10 * 8, 33 * 8, 9 * 8, 32 }, "OK" diff --git a/firmware/baseband/proc_siggen.cpp b/firmware/baseband/proc_siggen.cpp index dce9d479..2fdc5545 100644 --- a/firmware/baseband/proc_siggen.cpp +++ b/firmware/baseband/proc_siggen.cpp @@ -49,7 +49,7 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) { // Sine sample = (sine_table_i8[(tone_phase & 0xFF000000) >> 24]); } else if (tone_shape == 2) { - // Tri + // Triangle int8_t a = (tone_phase & 0xFF000000) >> 24; sample = (a & 0x80) ? ((a << 1) ^ 0xFF) - 0x80 : (a << 1) + 0x80; } else if (tone_shape == 3) { @@ -61,24 +61,34 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) { } else if (tone_shape == 5) { // Square sample = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128; - } else if (tone_shape == 6) { - // Noise - sample = (lfsr & 0xFF000000) >> 24; - feedback = ((lfsr >> 31) ^ (lfsr >> 29) ^ (lfsr >> 15) ^ (lfsr >> 11)) & 1; - lfsr = (lfsr << 1) | feedback; - if (!lfsr) lfsr = 0x1337; // Shouldn't do this :( + } 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 + // White Noise generator, pseudo random noise generator, 8 bits linear-feedback shift register (LFSR) algorithm, variant Fibonacci. + // https://en.wikipedia.org/wiki/Linear-feedback_shift_register + bit = ((lfsr >> 2) ^ (lfsr >> 3)) & 1; + lfsr = (lfsr >> 1) | (bit << 7); + 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; phase += delta; sphase = phase + (64 << 24); re = (sine_table_i8[(sphase & 0xFF000000) >> 24]); - im = (sine_table_i8[(phase & 0xFF000000) >> 24]); + im = (sine_table_i8[( phase & 0xFF000000) >> 24]); } buffer.p[i] = {re, im}; @@ -104,7 +114,8 @@ void SigGenProcessor::on_message(const Message* const msg) { fm_delta = message.bw * (0xFFFFFFULL / 1536000); tone_shape = message.shape; - lfsr = 0x54DF0119; + // lfsr = 0x54DF0119; + lfsr = seed_value ; configured = true; break; diff --git a/firmware/baseband/proc_siggen.hpp b/firmware/baseband/proc_siggen.hpp index f547cc0f..c4adcd8b 100644 --- a/firmware/baseband/proc_siggen.hpp +++ b/firmware/baseband/proc_siggen.hpp @@ -38,13 +38,14 @@ private: BasebandThread baseband_thread { 1536000, this, NORMALPRIO + 20, baseband::Direction::Transmit }; - uint32_t tone_delta { 0 }, fm_delta { }; - uint32_t lfsr { }, feedback { }, tone_shape { }; + uint32_t tone_delta { 0 }, fm_delta { },tone_phase { 0 }; + uint8_t tone_shape { }; uint32_t sample_count { 0 }; bool auto_off { }; - uint32_t tone_phase { 0 }, phase { 0 }, delta { 0 }, sphase { 0 }; - int8_t sample { 0 }; - int8_t re { 0 }, im { 0 }; + int32_t phase { 0 }, sphase { 0 }, delta { 0 }; // they may have sign . + int8_t sample { 0 }, re { 0 }, im { 0 }; // they may have sign . + 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 { }; }; diff --git a/firmware/common/ui_painter.cpp b/firmware/common/ui_painter.cpp index 2cb0d93f..b390668b 100644 --- a/firmware/common/ui_painter.cpp +++ b/firmware/common/ui_painter.cpp @@ -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, - const Color background, const std::string text) { + const Color background, const std::string& text) { bool escape = false; size_t width = 0; @@ -71,7 +71,7 @@ int Painter::draw_string(Point p, const Font& font, const Color foreground, 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); } diff --git a/firmware/common/ui_painter.hpp b/firmware/common/ui_painter.hpp index 369e3a57..0fb867c7 100644 --- a/firmware/common/ui_painter.hpp +++ b/firmware/common/ui_painter.hpp @@ -49,8 +49,8 @@ public: int draw_char(const Point p, const Style& style, const char c); int draw_string(Point p, const Font& font, const Color foreground, - const Color background, const std::string text); - int draw_string(Point p, const Style& style, const std::string text); + const Color background, 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); diff --git a/flashing/hackrf_one_usb.bin b/flashing/hackrf_one_usb.bin old mode 100644 new mode 100755 index 6e7935ec..e35de267 Binary files a/flashing/hackrf_one_usb.bin and b/flashing/hackrf_one_usb.bin differ diff --git a/flashing/hackrf_one_usb.dfu b/flashing/hackrf_one_usb.dfu index be690312..2e9d5f72 100644 Binary files a/flashing/hackrf_one_usb.dfu and b/flashing/hackrf_one_usb.dfu differ