From 27f566be8f8423ae92140950116057e9db8a12e8 Mon Sep 17 00:00:00 2001 From: euquiq <31453004+euquiq@users.noreply.github.com> Date: Mon, 20 Jul 2020 16:43:24 -0300 Subject: [PATCH] scanner-enhanced-version New ui_scanner, inspired on AlainD's (alain00091) PR: https://github.com/eried/portapack-mayhem/pull/80 It includes the following: 1) A big frequency numbers display. 2) A Manual scan section (you can input a frequency range (START / END), choose a STEP value from an available of standard frequency intervals, and press SCAN button. 3) An AM / WFM / NFM scan mode selector, changing "on the fly". 4) A PAUSE / RESUME button, which will make the scanner to stop upon you listening something of interest 5) AUDIO APP button, a quick shortcut into the analog audio visualizing / recording app, with the mode, frequency, amp, LNA, VGA settings already in tune with the scanner. 6) Two enums are added to freqman.hpp, reserved for compatibility with AlainD's proposed freqman's app and / or further enhancement. More on this topic: ORIGINAL scanner just used one frequency step, when creating scanning frequency ranges, which was unacceptable. AlainD enhanced freqman in order to pass different steppings along with ranges. This seems an excellent idea, and I preserved that aspect on my current implementation of thisscanner, while adding those enums into the freqman just to keep the door open for AlainD's freqman in the future. 7) I did eliminate the extra blank spaces added by function to_string_short_freq() which created unnecessary spacing in every app where there is need for a SHORT string, from a frequency number. (SHORT!, no extra spaces!!) 8) I also maintained AlainD idea of capping the number of frequencies which are dynamically created for each range and stored inside a memory based db. While AlainD capped the number into 400 frequencies, I was able to up that value a bit more, into 500. Cheers! --- firmware/application/apps/ui_scanner.cpp | 373 ++++++++++++++++------- firmware/application/apps/ui_scanner.hpp | 172 ++++++++--- firmware/application/freqman.hpp | 17 ++ firmware/application/string_format.cpp | 3 +- 4 files changed, 421 insertions(+), 144 deletions(-) diff --git a/firmware/application/apps/ui_scanner.cpp b/firmware/application/apps/ui_scanner.cpp index 26c00728..1e68d081 100644 --- a/firmware/application/apps/ui_scanner.cpp +++ b/firmware/application/apps/ui_scanner.cpp @@ -26,6 +26,7 @@ #include "string_format.hpp" #include "audio.hpp" + using namespace portapack; namespace ui { @@ -38,6 +39,10 @@ ScannerThread::ScannerThread( } ScannerThread::~ScannerThread() { + stop(); +} + +void ScannerThread::stop() { if( thread ) { chThdTerminate(thread); chThdWait(thread); @@ -49,6 +54,18 @@ void ScannerThread::set_scanning(const bool v) { _scanning = v; } +bool ScannerThread::is_scanning() { + return _scanning; +} + +void ScannerThread::set_userpause(const bool v) { + _userpause = v; +} + +bool ScannerThread::is_userpause() { + return _userpause; +} + msg_t ScannerThread::static_fn(void* arg) { auto obj = static_cast(arg); obj->run(); @@ -56,36 +73,33 @@ msg_t ScannerThread::static_fn(void* arg) { } void ScannerThread::run() { - RetuneMessage message { }; - uint32_t frequency_index = 0; - - while( !chThdShouldTerminate() ) { - if (_scanning) { - // Retune - receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); + if (frequency_list_.size()) { //IF THERE IS A FREQUENCY LIST ... + RetuneMessage message { }; + uint32_t frequency_index = 0; + while( !chThdShouldTerminate() ) { + if (_scanning) { + // Retune + receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); + message.range = frequency_index; + EventDispatcher::send_message(message); - message.range = frequency_index; - EventDispatcher::send_message(message); - - - frequency_index++; - if (frequency_index >= frequency_list_.size()) - frequency_index = 0; + frequency_index++; + if (frequency_index >= frequency_list_.size()) + frequency_index = 0; + } + chThdSleepMilliseconds(50); //50 is enough for reception stabilization, increase for more precise scanning ? } - - chThdSleepMilliseconds(50); } } void ScannerView::handle_retune(uint32_t i) { - text_cycle.set( to_string_dec_uint(i) + "/" + - to_string_dec_uint(frequency_list.size()) + " : " + - to_string_dec_uint(frequency_list[i]) ); - desc_cycle.set( description_list[i] ); + big_display.set(frequency_list[i]); //Show the big Freq + text_cycle.set( to_string_dec_uint(i + 1,3) ); + if (description_list[i].size() > 0) desc_cycle.set( description_list[i] ); //If this is a new description: show } void ScannerView::focus() { - field_lna.focus(); + field_mode.focus(); } ScannerView::~ScannerView() { @@ -94,9 +108,16 @@ ScannerView::~ScannerView() { baseband::shutdown(); } +void ScannerView::show_max() { //show total number of freqs to scan + if (frequency_list.size() == MAX_DB_ENTRY) + text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)"); + else + text_max.set( "/ " + to_string_dec_uint(frequency_list.size())); +} + ScannerView::ScannerView( - NavigationView& -) + NavigationView& nav + ) : nav_ { nav } { add_children({ &labels, @@ -105,104 +126,190 @@ ScannerView::ScannerView( &field_rf_amp, &field_volume, &field_bw, - &field_trigger, &field_squelch, &field_wait, - //&record_view, + &rssi, &text_cycle, + &text_max, &desc_cycle, - //&waterfall, + &big_display, + &button_manual_start, + &button_manual_end, + &field_mode, + &step_mode, + &button_manual_scan, + &button_pause, + &button_audio_app }); - std::string scanner_file = "SCANNER"; - if (load_freqman_file(scanner_file, database)) { - for(auto& entry : database) { - // FIXME - if (entry.type == RANGE) { - for (uint32_t i=entry.frequency_a; i < entry.frequency_b; i+= 1000000) { - frequency_list.push_back(i); - description_list.push_back("RNG " + to_string_dec_uint(entry.frequency_a) + ">" + to_string_dec_uint(entry.frequency_b)); - } - } else { - frequency_list.push_back(entry.frequency_a); - description_list.push_back(entry.description); - } + def_step = change_mode(AM); //Start on AM + field_mode.set_by_value(AM); //Reflect the mode into the manual selector + + big_display.set_style(&style_green); //Start with green color + + button_manual_start.on_select = [this, &nav](Button& button) { + auto new_view = nav_.push(frequency_range.min); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.min = f; + button_manual_start.set_text(to_string_short_freq(f)); + }; + }; + + button_manual_end.on_select = [this, &nav](Button& button) { + auto new_view = nav.push(frequency_range.max); + new_view->on_changed = [this, &button](rf::Frequency f) { + frequency_range.max = f; + button_manual_end.set_text(to_string_short_freq(f)); + }; + }; + + button_pause.on_select = [this](Button&) { + if (scan_thread->is_userpause()) { + timer = wait * 10; //Unlock timer pause on_statistics_update + button_pause.set_text("PAUSE"); //resume scanning (show button for pause) + scan_thread->set_userpause(false); + //scan_resume(); + } else { + scan_pause(); + scan_thread->set_userpause(true); + button_pause.set_text("RESUME"); //PAUSED, show resume } - } else { - // DEBUG - // TODO: Clean this - frequency_list.push_back(466025000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466050000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466075000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466175000); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466206250); - description_list.push_back("POCSAG-France"); - frequency_list.push_back(466231250); - description_list.push_back("POCSAG-France"); - } - - field_bw.set_selected_index(2); - field_bw.on_change = [this](size_t n, OptionsField::value_t) { - receiver_model.set_nbfm_configuration(n); }; - field_wait.on_change = [this](int32_t v) { - wait = v; - }; - field_wait.set_value(5); - - field_trigger.on_change = [this](int32_t v) { - trigger = v; - }; - field_trigger.set_value(30); - - field_squelch.set_value(receiver_model.squelch_level()); - field_squelch.on_change = [this](int32_t v) { - squelch = v; - receiver_model.set_squelch_level(v); + button_audio_app.on_select = [this](Button&) { + if (scan_thread->is_scanning()) + scan_thread->set_scanning(false); + scan_thread->stop(); + nav_.pop(); + nav_.push(); }; + button_manual_scan.on_select = [this](Button&) { + if (!frequency_range.min || !frequency_range.max) { + nav_.display_modal("Error", "Both START and END freqs\nneed a value"); + } else if (frequency_range.min > frequency_range.max) { + nav_.display_modal("Error", "END freq\nis lower than START"); + } else { + scan_thread->stop(); //STOP SCANNER THREAD + frequency_list.clear(); + description_list.clear(); + def_step = step_mode.selected_index_value(); //Use def_step from manual selector + description_list.push_back( + "M:" + to_string_short_freq(frequency_range.min) + ">" + + to_string_short_freq(frequency_range.max) + " S:" + + to_string_short_freq(def_step) + ); + + rf::Frequency frequency = frequency_range.min; + while (frequency_list.size() < MAX_DB_ENTRY && frequency <= frequency_range.max) { //add manual range + frequency_list.push_back(frequency); + description_list.push_back(""); //If empty, will keep showing the last description + frequency+=def_step; + } + + show_max(); + start_scan_thread(); //RESTART SCANNER THREAD + } + }; + + field_mode.on_change = [this](size_t, OptionsField::value_t v) { + if (scan_thread->is_scanning()) + scan_thread->set_scanning(false); // WE STOP SCANNING + audio::output::stop(); + scan_thread->stop(); + receiver_model.disable(); + baseband::shutdown(); + chThdSleepMilliseconds(50); + change_mode(v); + start_scan_thread(); + }; + + //PRE-CONFIGURATION: + field_wait.on_change = [this](int32_t v) { wait = v; }; field_wait.set_value(5); + field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(30); field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99); - field_volume.on_change = [this](int32_t v) { - this->on_headphone_volume_changed(v); - }; - - audio::output::start(); - - audio::output::mute(); - baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); - receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); - receiver_model.set_sampling_rate(3072000); - receiver_model.set_baseband_bandwidth(1750000); - receiver_model.enable(); - receiver_model.set_squelch_level(0); - receiver_model.set_nbfm_configuration(field_bw.selected_index()); - audio::output::unmute(); - - // TODO: Scanning thread here - scan_thread = std::make_unique(frequency_list); + field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); }; + // LEARN FREQUENCIES + std::string scanner_txt = "SCANNER"; + if ( load_freqman_file(scanner_txt, database) ) { + for(auto& entry : database) { // READ LINE PER LINE + if (frequency_list.size() < MAX_DB_ENTRY) { //We got space! + if (entry.type == RANGE) { //RANGE + switch (entry.step) { + case AM_US: def_step = 10000; break ; + case AM_EUR:def_step = 9000; break ; + case NFM_1: def_step = 12500; break ; + case NFM_2: def_step = 6250; break ; + case FM_1: def_step = 100000; break ; + case FM_2: def_step = 50000; break ; + case N_1: def_step = 25000; break ; + case N_2: def_step = 250000; break ; + case AIRBAND:def_step= 8330; break ; + } + frequency_list.push_back(entry.frequency_a); //Store starting freq and description + description_list.push_back("R:" + to_string_short_freq(entry.frequency_a) + + ">" + to_string_short_freq(entry.frequency_b) + + " S:" + to_string_short_freq(def_step)); + while (frequency_list.size() < MAX_DB_ENTRY && entry.frequency_a <= entry.frequency_b) { //add the rest of the range + entry.frequency_a+=def_step; + frequency_list.push_back(entry.frequency_a); + description_list.push_back(""); //Token (keep showing the last description) + } + } else if ( entry.type == SINGLE) { + frequency_list.push_back(entry.frequency_a); + description_list.push_back("S: " + entry.description); + } + show_max(); + } + else + { + break; //No more space: Stop reading the txt file ! + } + } + } + else + { + desc_cycle.set(" NO SCANNER.TXT FILE ..." ); + } + audio::output::stop(); + step_mode.set_by_value(def_step); //Impose the default step into the manual step selector + start_scan_thread(); } void ScannerView::on_statistics_update(const ChannelStatistics& statistics) { - int32_t max_db = statistics.max_db; - - if (timer <= wait) - timer++; - - if (max_db < -trigger) { - if (timer == wait) { - //audio::output::stop(); - scan_thread->set_scanning(true); + if (!scan_thread->is_userpause()) + { + if (timer >= (wait * 10) ) + { + timer=0; + scan_resume(); + } + else if (!timer) + { + if (statistics.max_db > -squelch) { //There is something on the air... + scan_pause(); + timer++; + } + } + else + { + timer++; } - } else { - //audio::output::start(); - scan_thread->set_scanning(false); - timer = 0; + } +} + +void ScannerView::scan_pause() { + if (scan_thread->is_scanning()) { + scan_thread->set_scanning(false); // WE STOP SCANNING + audio::output::start(); + } +} + +void ScannerView::scan_resume() { + if (!scan_thread->is_scanning()) { + audio::output::stop(); + scan_thread->set_scanning(true); // WE RESCAN } } @@ -211,4 +318,60 @@ void ScannerView::on_headphone_volume_changed(int32_t v) { receiver_model.set_headphone_volume(new_volume); } -} /* namespace ui */ +size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thread->stop(); After this do a start_scan_thread() + using option_t = std::pair; + using options_t = std::vector; + options_t bw; + field_bw.on_change = [this](size_t n, OptionsField::value_t) { }; + + switch (new_mod) { + case NFM: //bw 16k (2) default + bw.emplace_back("8k5", 0); + bw.emplace_back("11k", 0); + bw.emplace_back("16k", 0); + field_bw.set_options(bw); + + baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); + field_bw.set_selected_index(2); + receiver_model.set_nbfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_nbfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000); + break; + case AM: + bw.emplace_back("DSB", 0); + bw.emplace_back("USB", 0); + bw.emplace_back("LSB", 0); + field_bw.set_options(bw); + + baseband::run_image(portapack::spi_flash::image_tag_am_audio); + receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); + field_bw.set_selected_index(0); + receiver_model.set_am_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); }; + receiver_model.set_sampling_rate(2000000);receiver_model.set_baseband_bandwidth(2000000); + break; + case WFM: + bw.emplace_back("16k", 0); + field_bw.set_options(bw); + + baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + field_bw.set_selected_index(0); + receiver_model.set_wfm_configuration(field_bw.selected_index()); + field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_wfm_configuration(n); }; + receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(2000000); + break; + } + + return mod_step[new_mod]; + +} + +void ScannerView::start_scan_thread() { + receiver_model.enable(); + receiver_model.set_squelch_level(0); + scan_thread = std::make_unique(frequency_list); +} + +} /* namespace ui */ \ No newline at end of file diff --git a/firmware/application/apps/ui_scanner.hpp b/firmware/application/apps/ui_scanner.hpp index b5d17c22..354700af 100644 --- a/firmware/application/apps/ui_scanner.hpp +++ b/firmware/application/apps/ui_scanner.hpp @@ -20,20 +20,38 @@ * Boston, MA 02110-1301, USA. */ +#include "ui.hpp" #include "receiver_model.hpp" #include "ui_receiver.hpp" #include "ui_font_fixed_8x16.hpp" +#include "ui_spectrum.hpp" #include "freqman.hpp" +#include "log_file.hpp" +#include "analog_audio_app.hpp" + + +#define MAX_DB_ENTRY 500 namespace ui { +enum modulation_type { AM = 0,WFM,NFM }; + +string const mod_name[3] = {"AM", "WFM", "NFM"}; +size_t const mod_step[3] = {9000, 100000, 12500 }; + class ScannerThread { public: ScannerThread(std::vector frequency_list); ~ScannerThread(); - + void set_scanning(const bool v); + bool is_scanning(); + + void set_userpause(const bool v); + bool is_userpause(); + + void stop(); ScannerThread(const ScannerThread&) = delete; ScannerThread(ScannerThread&&) = delete; @@ -45,38 +63,64 @@ private: Thread* thread { nullptr }; bool _scanning { true }; - + bool _userpause { false }; static msg_t static_fn(void* arg); - void run(); }; class ScannerView : public View { public: - ScannerView(NavigationView&); + ScannerView(NavigationView& nav); ~ScannerView(); void focus() override; + + void big_display_freq(rf::Frequency f); + + const Style style_grey { // scanning + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::grey(), + }; - std::string title() const override { return "Scanner"; }; + const Style style_green { //Found signal + .font = font::fixed_8x16, + .background = Color::black(), + .foreground = Color::green(), + }; + + std::string title() const override { return "SCANNER"; }; + std::vector frequency_list{ }; + std::vector description_list { }; + +//void set_parent_rect(const Rect new_parent_rect) override; private: + NavigationView& nav_; + + void start_scan_thread(); + size_t change_mode(uint8_t mod_type); + void show_max(); + void scan_pause(); + void scan_resume(); + void on_statistics_update(const ChannelStatistics& statistics); void on_headphone_volume_changed(int32_t v); void handle_retune(uint32_t i); - - std::vector frequency_list { }; - std::vector description_list { }; - int32_t trigger { 0 }; + + jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too... int32_t squelch { 0 }; uint32_t timer { 0 }; uint32_t wait { 0 }; + size_t def_step { 0 }; freqman_db database { }; Labels labels { - { { 0 * 8, 0 * 16 }, "LNA: TRIGGER: /99 VOL:", Color::light_grey() }, - { { 0 * 8, 1 * 16 }, "VGA: SQUELCH: /99 AMP:", Color::light_grey() }, - { { 0 * 8, 2 * 16 }, " BW: WAIT:", Color::light_grey() }, + { { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() }, + { { 0 * 8, 1* 16 }, "BW: SQUELCH: /99 WAIT:", Color::light_grey() }, + { { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() }, + { { 0 * 8, 14 * 16 }, "MODE:", Color::light_grey() }, + { { 11 * 8, 14 * 16 }, "STEP:", Color::light_grey() }, }; LNAGainField field_lna { @@ -84,15 +128,15 @@ private: }; VGAGainField field_vga { - { 4 * 8, 1 * 16 } + { 11 * 8, 0 * 16 } }; RFAmpField field_rf_amp { - { 28 * 8, 1 * 16 } + { 18 * 8, 0 * 16 } }; NumberField field_volume { - { 28 * 8, 0 * 16 }, + { 24 * 8, 0 * 16 }, 2, { 0, 99 }, 1, @@ -100,25 +144,13 @@ private: }; OptionsField field_bw { - { 4 * 8, 2 * 16 }, - 3, - { - { "8k5", 0 }, - { "11k", 0 }, - { "16k", 0 }, - } - }; + { 3 * 8, 1 * 16 }, + 4, + { } + }; - NumberField field_trigger { - { 16 * 8, 0 * 16 }, - 2, - { 0, 99 }, - 1, - ' ', - }; - NumberField field_squelch { - { 16 * 8, 1 * 16 }, + { 15 * 8, 1 * 16 }, 2, { 0, 99 }, 1, @@ -126,20 +158,84 @@ private: }; NumberField field_wait { - { 16 * 8, 2 * 16 }, + { 26 * 8, 1 * 16 }, 2, { 0, 99 }, 1, ' ', }; + RSSI rssi { + { 0 * 16, 2 * 16, 15 * 16, 8 }, + }; + Text text_cycle { - { 0, 5 * 16, 240, 16 }, - "--/--" + { 0, 3 * 16, 3 * 8, 16 }, }; + + Text text_max { + { 4 * 8, 3 * 16, 18 * 8, 16 }, + }; + Text desc_cycle { - {0, 6 * 16, 240, 16 }, - " " + {0, 4 * 16, 240, 16 }, + }; + + BigFrequency big_display { //Show frequency in glamour + { 4, 6 * 16, 28 * 8, 52 }, + 0 + }; + + Button button_manual_start { + { 0 * 8, 11 * 16, 11 * 8, 28 }, + "" + }; + + Button button_manual_end { + { 12 * 8, 11 * 16, 11 * 8, 28 }, + "" + }; + + Button button_manual_scan { + { 24 * 8, 11 * 16, 6 * 8, 28 }, + "SCAN" + }; + + OptionsField field_mode { + { 5 * 8, 14 * 16 }, + 6, + { + { " AM ", 0 }, + { " WFM ", 1 }, + { " NFM ", 2 }, + } + }; + + OptionsField step_mode { + { 17 * 8, 14 * 16 }, + 12, + { + { "5Khz (SA AM)", 5000 }, + { "9Khz (EU AM)", 9000 }, + { "10Khz(US AM)", 10000 }, + { "50Khz (FM1)", 50000 }, + { "100Khz(FM2)", 100000 }, + { "6.25khz(NFM)", 6250 }, + { "12.5khz(NFM)", 12500 }, + { "25khz (N1)", 25000 }, + { "250khz (N2)", 250000 }, + { "8.33khz(AIR)", 8330 } + } + }; + + Button button_pause { + { 12, 17 * 16, 96, 24 }, + "PAUSE" + }; + + Button button_audio_app { + { 124, 17 * 16, 96, 24 }, + "AUDIO APP" }; std::unique_ptr scan_thread { }; @@ -160,4 +256,4 @@ private: }; }; -} /* namespace ui */ +} /* namespace ui */ \ No newline at end of file diff --git a/firmware/application/freqman.hpp b/firmware/application/freqman.hpp index daaad1ef..dca1f364 100644 --- a/firmware/application/freqman.hpp +++ b/firmware/application/freqman.hpp @@ -48,11 +48,28 @@ enum freqman_entry_type { RANGE }; +//Entry step placed for AlainD freqman version (or any other enhanced version) +enum freqman_entry_step { + STEP_DEF = 0, // default + AM_US, // 10 Khz AM/CB + AM_EUR, // 9 Khz LW/MW + NFM_1, // 12,5 Khz (Analogic PMR 446) + NFM_2, // 6,25 Khz (Digital PMR 446) + FM_1, // 100 Khz + FM_2, // 50 Khz + N_1, // 25 Khz + N_2, // 250 Khz + AIRBAND, // AIRBAND 8,33 Khz + ERROR_STEP +}; + +// freqman_entry_step step added, as above, to provide compatibility / future enhancement. struct freqman_entry { rf::Frequency frequency_a { 0 }; rf::Frequency frequency_b { 0 }; std::string description { }; freqman_entry_type type { }; + freqman_entry_step step { }; }; using freqman_db = std::vector; diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index a62ce1ae..fd7fb1a3 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -113,7 +113,8 @@ std::string to_string_dec_int( } std::string to_string_short_freq(const uint64_t f) { - auto final_str = to_string_dec_int(f / 1000000, 4) + "." + to_string_dec_int((f / 100) % 10000, 4, '0'); + //was... to_string_dec_int(f / 1000000,4) + auto final_str = to_string_dec_int(f / 1000000) + "." + to_string_dec_int((f / 100) % 10000, 4, '0'); return final_str; }