/* * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Furrtek * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ #include "ui_encoders.hpp" #include "baseband_api.hpp" #include "string_format.hpp" using namespace portapack; namespace ui { EncodersConfigView::EncodersConfigView( NavigationView& nav, Rect parent_rect ) { using option_t = std::pair; std::vector enc_options; size_t i; set_parent_rect(parent_rect); hidden(true); // Default encoder def encoder_def = &encoder_defs[0]; add_children({ &labels, &options_enctype, &field_clk, &field_frameduration, &symfield_word, &text_format, &waveform }); // Load encoder types in option field for (i = 0; i < ENC_TYPES_COUNT; i++) enc_options.emplace_back(std::make_pair(encoder_defs[i].name, i)); options_enctype.on_change = [this](size_t index, int32_t) { on_type_change(index); }; options_enctype.set_options(enc_options); options_enctype.set_selected_index(0); symfield_word.on_change = [this]() { generate_frame(); }; // Selecting input clock changes symbol and word duration field_clk.on_change = [this](int32_t value) { // value is in kHz, new_value is in us int32_t new_value = (encoder_def->clk_per_symbol * 1000000) / (value * 1000); if (new_value != field_frameduration.value()) field_frameduration.set_value(new_value * encoder_def->word_length, false); }; // Selecting word duration changes input clock and symbol duration field_frameduration.on_change = [this](int32_t value) { // value is in us, new_value is in kHz int32_t new_value = (value * 1000) / (encoder_def->word_length * encoder_def->clk_per_symbol); if (new_value != field_clk.value()) field_clk.set_value(1000000 / new_value, false); }; } void EncodersConfigView::focus() { options_enctype.focus(); } void EncodersConfigView::on_type_change(size_t index) { std::string format_string = ""; size_t word_length; char symbol_type; encoder_def = &encoder_defs[index]; field_clk.set_value(encoder_def->default_speed / 1000); // SymField setup word_length = encoder_def->word_length; symfield_word.set_length(word_length); size_t n = 0, i = 0; while (n < word_length) { symbol_type = encoder_def->word_format.at(i++); if (symbol_type == 'A') { symfield_word.set_symbol_list(n++, encoder_def->address_symbols); format_string += 'A'; } else if (symbol_type == 'D') { symfield_word.set_symbol_list(n++, encoder_def->data_symbols); format_string += 'D'; } } // Ugly :( Pad to erase format_string.append(24 - format_string.size(), ' '); text_format.set(format_string); generate_frame(); } void EncodersConfigView::on_show() { options_enctype.set_selected_index(0); on_type_change(0); } void EncodersConfigView::draw_waveform() { size_t length = frame_symbols.length(); for (size_t n = 0; n < length; n++) { if (frame_symbols[n] == '0') waveform_buffer[n] = 0; else waveform_buffer[n] = 1; } waveform.set_length(length); waveform.set_dirty(); } void EncodersConfigView::generate_frame() { size_t i = 0; frame_symbols.clear(); for (auto c : encoder_def->word_format) { if (c == 'S') frame_symbols += encoder_def->sync; else frame_symbols += encoder_def->bit_format[symfield_word.get_sym(i++)]; } draw_waveform(); } uint8_t EncodersConfigView::repeat_min() { return encoder_def->repeat_min; } uint32_t EncodersConfigView::samples_per_bit() { return OOK_SAMPLERATE / ((field_clk.value() * 1000) / encoder_def->clk_per_fragment); } uint32_t EncodersConfigView::pause_symbols() { return encoder_def->pause_symbols; } void EncodersScanView::focus() { field_debug.focus(); } EncodersScanView::EncodersScanView( NavigationView& nav, Rect parent_rect ) { set_parent_rect(parent_rect); hidden(true); add_children({ &labels, &field_debug, &text_debug, &text_length }); // DEBUG field_debug.on_change = [this](int32_t value) { uint32_t l; size_t length; de_bruijn debruijn_seq; length = debruijn_seq.init(value); l = 1; l <<= value; l--; if (l > 25) l = 25; text_debug.set(to_string_bin(debruijn_seq.compute(l), 25)); text_length.set(to_string_dec_uint(length)); }; } void EncodersView::focus() { tab_view.focus(); } EncodersView::~EncodersView() { transmitter_model.disable(); baseband::shutdown(); } void EncodersView::update_progress() { std::string str_buffer; // text_status.set(" "); if (tx_mode == SINGLE) { str_buffer = to_string_dec_uint(repeat_index) + "/" + to_string_dec_uint(repeat_min); text_status.set(str_buffer); progress.set_value(repeat_index); /*} else if (tx_mode == SCAN) { strcpy(str, to_string_dec_uint(repeat_index).c_str()); strcat(str, "/"); strcat(str, to_string_dec_uint(portapack::persistent_memory::afsk_repeats()).c_str()); strcat(str, " "); strcat(str, to_string_dec_uint(scan_index + 1).c_str()); strcat(str, "/"); strcat(str, to_string_dec_uint(scan_count).c_str()); text_status.set(str); progress.set_value(scan_progress);*/ } else { text_status.set("Ready"); progress.set_value(0); } } void EncodersView::on_txdone(int n, const bool txdone) { //char str[16]; if (!txdone) { // Repeating... repeat_index = n + 1; /*if (tx_mode == SCAN) { scan_progress++; update_progress(); } else {*/ update_progress(); //} } else { // Done transmitting /*if ((tx_mode == SCAN) && (scan_index < (scan_count - 1))) { transmitter_model.disable(); if (abort_scan) { // Kill scan process strcpy(str, "Abort @"); strcat(str, rgsb); text_status.set(str); progress.set_value(0); tx_mode = IDLE; abort_scan = false; button_scan.set_style(&style_val); button_scan.set_text("SCAN"); } else { // Next address scan_index++; strcpy(rgsb, &scan_list[options_scanlist.selected_index()].addresses[scan_index * 5]); scan_progress++; repeat_index = 1; update_progress(); start_tx(true); } } else {*/ transmitter_model.disable(); tx_mode = IDLE; text_status.set("Done"); progress.set_value(0); tx_view.set_transmitting(false); //} } } void EncodersView::start_tx(const bool scan) { (void)scan; uint8_t byte = 0; size_t bitstream_length =0; uint8_t * bitstream = shared_memory.bb_data.data; repeat_min = view_config.repeat_min(); /*if (scan) { if (tx_mode != SCAN) { scan_index = 0; scan_count = scan_list[options_scanlist.selected_index()].count; scan_progress = 1; repeat_index = 1; tx_mode = SCAN; strcpy(rgsb, &scan_list[options_scanlist.selected_index()].addresses[0]); progress.set_max(scan_count * afsk_repeats); update_progress(); } } else {*/ tx_mode = SINGLE; repeat_index = 1; progress.set_max(repeat_min); update_progress(); //} view_config.generate_frame(); for (auto c : view_config.frame_symbols) { byte <<= 1; if (c != '0') byte |= 1; if ((bitstream_length & 7) == 7) bitstream[bitstream_length >> 3] = byte; bitstream_length++; } transmitter_model.set_sampling_rate(OOK_SAMPLERATE); transmitter_model.set_rf_amp(true); transmitter_model.set_baseband_bandwidth(1750000); transmitter_model.enable(); baseband::set_ook_data( bitstream_length, view_config.samples_per_bit(), repeat_min, view_config.pause_symbols() ); } EncodersView::EncodersView( NavigationView& nav ) : nav_ { nav } { baseband::run_image(portapack::spi_flash::image_tag_ook); add_children({ &tab_view, &view_config, &view_scan, &text_status, &progress, &tx_view }); tx_view.on_edit_frequency = [this, &nav]() { auto new_view = nav.push(transmitter_model.tuning_frequency()); new_view->on_changed = [this](rf::Frequency f) { transmitter_model.set_tuning_frequency(f); }; }; tx_view.on_start = [this]() { tx_view.set_transmitting(true); start_tx(false); }; tx_view.on_stop = [this]() { tx_view.set_transmitting(false); }; } } /* namespace ui */