mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-10 16:49:36 +00:00
Multi screen support, with dyn alignment (#2801)
This commit is contained in:
+158
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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 "bht.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
namespace ui::external_app::bht_tx {
|
||||
|
||||
size_t gen_message_ep(uint8_t city_code, size_t family_code_ep, uint32_t relay_number, uint32_t relay_state) {
|
||||
size_t c;
|
||||
const encoder_def_t* um3750_def;
|
||||
uint8_t bits[12];
|
||||
std::string ep_fragments;
|
||||
|
||||
// Pre-allocate to avoid fragmentation.
|
||||
ep_fragments.reserve(384);
|
||||
|
||||
// Repeated 2x 26 times
|
||||
// Whole frame + space = 128ms, data only = 64ms
|
||||
|
||||
um3750_def = &encoder_defs[ENCODER_UM3750];
|
||||
|
||||
// City code is bit-reversed
|
||||
for (c = 0; c < 8; c++)
|
||||
bits[c] = (city_code >> c) & 1;
|
||||
|
||||
bits[8] = (family_code_ep >> 1) & 1;
|
||||
bits[9] = family_code_ep & 1;
|
||||
bits[10] = relay_number & 1;
|
||||
bits[11] = relay_state ? 1 : 0;
|
||||
|
||||
// Text for display
|
||||
// for (c = 0; c < 12; c++)
|
||||
// ep_message[c] = bits[c] + '0';
|
||||
|
||||
c = 0;
|
||||
for (auto ch : um3750_def->word_format) {
|
||||
if (ch == 'S')
|
||||
ep_fragments += um3750_def->sync;
|
||||
else
|
||||
ep_fragments += um3750_def->bit_format[bits[c++]];
|
||||
}
|
||||
|
||||
// Return bitstream length
|
||||
return make_bitstream(ep_fragments);
|
||||
}
|
||||
|
||||
std::string gen_message_xy(const std::string& ascii_code) {
|
||||
std::string local_code = ascii_code;
|
||||
uint8_t ccir_message[XY_TONE_COUNT];
|
||||
uint8_t translate;
|
||||
uint32_t c;
|
||||
|
||||
// Replace repeats with E code
|
||||
for (c = 1; c < XY_TONE_COUNT; c++)
|
||||
if (local_code[c] == local_code[c - 1]) local_code[c] = 'E';
|
||||
|
||||
for (c = 0; c < XY_TONE_COUNT; c++) {
|
||||
if (local_code[c] <= '9')
|
||||
translate = local_code[c] - '0';
|
||||
else
|
||||
translate = local_code[c] - 'A' + 10;
|
||||
ccir_message[c] = (translate < 16) ? translate : 0; // Sanitize
|
||||
}
|
||||
|
||||
// Copy for baseband
|
||||
memcpy(shared_memory.bb_data.tones_data.message, ccir_message, XY_TONE_COUNT);
|
||||
|
||||
// Return as text for display
|
||||
return local_code;
|
||||
}
|
||||
|
||||
std::string gen_message_xy(size_t header_code_a, size_t header_code_b, size_t city_code, size_t family_code, bool subfamily_wc, size_t subfamily_code, bool id_wc, size_t receiver_code, size_t relay_state_A, size_t relay_state_B, size_t relay_state_C, size_t relay_state_D) {
|
||||
uint8_t ccir_message[XY_TONE_COUNT];
|
||||
size_t c;
|
||||
|
||||
// Header
|
||||
ccir_message[0] = (header_code_a / 10);
|
||||
ccir_message[1] = (header_code_a % 10);
|
||||
ccir_message[2] = (header_code_b / 10);
|
||||
ccir_message[3] = (header_code_b % 10);
|
||||
|
||||
// Addresses
|
||||
ccir_message[4] = (city_code / 10);
|
||||
ccir_message[5] = (city_code % 10);
|
||||
ccir_message[6] = family_code;
|
||||
|
||||
if (subfamily_wc)
|
||||
ccir_message[7] = 0xA; // Wildcard
|
||||
else
|
||||
ccir_message[7] = subfamily_code;
|
||||
|
||||
if (id_wc) {
|
||||
ccir_message[8] = 0xA; // Wildcard
|
||||
ccir_message[9] = 0xA; // Wildcard
|
||||
} else {
|
||||
ccir_message[8] = (receiver_code / 10);
|
||||
ccir_message[9] = (receiver_code % 10);
|
||||
}
|
||||
|
||||
ccir_message[10] = 0xB;
|
||||
|
||||
// Relay states
|
||||
ccir_message[11] = relay_state_A;
|
||||
ccir_message[12] = relay_state_B;
|
||||
ccir_message[13] = relay_state_C;
|
||||
ccir_message[14] = relay_state_D;
|
||||
|
||||
ccir_message[15] = 0xB;
|
||||
|
||||
// End
|
||||
for (c = 16; c < XY_TONE_COUNT; c++)
|
||||
ccir_message[c] = 0;
|
||||
|
||||
// Replace repeats with E code
|
||||
for (c = 1; c < XY_TONE_COUNT; c++)
|
||||
if (ccir_message[c] == ccir_message[c - 1]) ccir_message[c] = 0xE;
|
||||
|
||||
// Copy for baseband
|
||||
memcpy(shared_memory.bb_data.tones_data.message, ccir_message, XY_TONE_COUNT);
|
||||
|
||||
// Return as text for display
|
||||
return ccir_to_ascii(ccir_message);
|
||||
}
|
||||
|
||||
std::string ccir_to_ascii(uint8_t* ccir) {
|
||||
std::string ascii;
|
||||
|
||||
for (size_t c = 0; c < XY_TONE_COUNT; c++) {
|
||||
if (ccir[c] > 9)
|
||||
ascii += (char)(ccir[c] - 10 + 'A');
|
||||
else
|
||||
ascii += (char)(ccir[c] + '0');
|
||||
}
|
||||
|
||||
return ascii;
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::bht_tx
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
#include "tonesets.hpp"
|
||||
#include "encoders.hpp"
|
||||
|
||||
using namespace encoders;
|
||||
|
||||
namespace ui::external_app::bht_tx {
|
||||
|
||||
#define XY_TONE_DURATION ((TONES_SAMPLERATE * 0.1) - 1) // 100ms
|
||||
#define XY_SILENCE (TONES_SAMPLERATE * 0.4) // 400ms
|
||||
#define XY_TONE_COUNT 20
|
||||
#define XY_MAX_CITY 99
|
||||
|
||||
#define EPAR_BIT_DURATION (OOK_SAMPLERATE / 580)
|
||||
#define EPAR_REPEAT_COUNT 26
|
||||
#define EPAR_MAX_CITY 255
|
||||
|
||||
struct bht_city {
|
||||
std::string name;
|
||||
uint8_t freq_index;
|
||||
bool recent;
|
||||
};
|
||||
|
||||
size_t gen_message_ep(uint8_t city_code, size_t family_code_ep, uint32_t relay_state_A, uint32_t relay_state_B);
|
||||
std::string gen_message_xy(const std::string& code);
|
||||
std::string gen_message_xy(size_t header_code_a, size_t header_code_b, size_t city_code, size_t family_code, bool subfamily_wc, size_t subfamily_code, bool id_wc, size_t receiver_code, size_t relay_state_A, size_t relay_state_B, size_t relay_state_C, size_t relay_state_D);
|
||||
std::string ccir_to_ascii(uint8_t* ccir);
|
||||
|
||||
} // namespace ui::external_app::bht_tx
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2024 EPIRB Decoder Implementation
|
||||
*
|
||||
* 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.hpp"
|
||||
#include "ui_bht_tx.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::bht_tx {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<BHTView>();
|
||||
}
|
||||
} // namespace ui::external_app::bht_tx
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_bht_tx.application_information"), used)) application_information_t _application_information_bht_tx = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::bht_tx::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "BHT TX",
|
||||
/*.bitmap_data = */ {
|
||||
0x00,
|
||||
0x00,
|
||||
0xE0,
|
||||
0x07,
|
||||
0xF8,
|
||||
0x08,
|
||||
0x9C,
|
||||
0x07,
|
||||
0x0C,
|
||||
0x00,
|
||||
0x8E,
|
||||
0x0A,
|
||||
0x46,
|
||||
0x12,
|
||||
0x26,
|
||||
0x22,
|
||||
0x06,
|
||||
0x02,
|
||||
0x06,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_tones */ {'P', 'T', 'O', 'N'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* 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_bht_tx.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui::external_app::bht_tx {
|
||||
|
||||
void BHTView::focus() {
|
||||
tx_view.focus();
|
||||
}
|
||||
|
||||
void BHTView::start_tx() {
|
||||
baseband::shutdown();
|
||||
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
|
||||
if (target_system == XYLOS) {
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
view_xylos.generate_message();
|
||||
|
||||
// if (tx_mode == SINGLE) {
|
||||
progressbar.set_max(XY_TONE_COUNT);
|
||||
/*} else if (tx_mode == SCAN) {
|
||||
progressbar.set_max(XY_TONE_COUNT * view_xylos.get_scan_remaining());
|
||||
}*/
|
||||
|
||||
transmitter_model.set_sampling_rate(TONES_SAMPLERATE);
|
||||
transmitter_model.enable();
|
||||
|
||||
// Setup tones
|
||||
for (size_t c = 0; c < ccir_deltas.size(); c++)
|
||||
baseband::set_tone(c, ccir_deltas[c], XY_TONE_DURATION);
|
||||
|
||||
baseband::set_tones_config(transmitter_model.channel_bandwidth(), XY_SILENCE, XY_TONE_COUNT, false, false);
|
||||
|
||||
} else if (target_system == EPAR) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_ook);
|
||||
|
||||
auto bitstream_length = view_EPAR.generate_message();
|
||||
|
||||
// if (tx_mode == SINGLE) {
|
||||
progressbar.set_max(2 * EPAR_REPEAT_COUNT);
|
||||
/*} else if (tx_mode == SCAN) {
|
||||
progressbar.set_max(2 * EPAR_REPEAT_COUNT * view_EPAR.get_scan_remaining());
|
||||
}*/
|
||||
|
||||
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_ook_data(
|
||||
bitstream_length,
|
||||
EPAR_BIT_DURATION,
|
||||
EPAR_REPEAT_COUNT,
|
||||
encoder_defs[ENCODER_UM3750].pause_symbols);
|
||||
}
|
||||
}
|
||||
|
||||
void BHTView::stop_tx() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
tx_mode = IDLE;
|
||||
tx_view.set_transmitting(false);
|
||||
progressbar.set_value(0);
|
||||
}
|
||||
|
||||
void BHTView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
if (target_system == XYLOS) {
|
||||
if (done) {
|
||||
if (tx_mode == SINGLE) {
|
||||
if (checkbox_flashing.value()) {
|
||||
// TODO: Thread !
|
||||
chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :(
|
||||
view_xylos.flip_relays();
|
||||
start_tx();
|
||||
} else
|
||||
stop_tx();
|
||||
} else if (tx_mode == SCAN) {
|
||||
if (view_xylos.increment_address())
|
||||
start_tx();
|
||||
else
|
||||
stop_tx(); // Reached end of scan range
|
||||
}
|
||||
} else
|
||||
progressbar.set_value(progress);
|
||||
} else if (target_system == EPAR) {
|
||||
if (done) {
|
||||
if (!view_EPAR.half) {
|
||||
view_EPAR.half = true;
|
||||
start_tx(); // Start second half of transmission
|
||||
} else {
|
||||
view_EPAR.half = false;
|
||||
if (tx_mode == SINGLE) {
|
||||
if (checkbox_flashing.value()) {
|
||||
// TODO: Thread !
|
||||
chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :(
|
||||
view_EPAR.flip_relays();
|
||||
start_tx();
|
||||
} else
|
||||
stop_tx();
|
||||
} else if (tx_mode == SCAN) {
|
||||
if (view_EPAR.increment_address())
|
||||
start_tx();
|
||||
else
|
||||
stop_tx(); // Reached end of scan range
|
||||
}
|
||||
}
|
||||
} else
|
||||
progressbar.set_value(progress);
|
||||
}
|
||||
}
|
||||
|
||||
BHTView::~BHTView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
BHTView::BHTView(NavigationView& nav) {
|
||||
add_children({&tab_view,
|
||||
&labels,
|
||||
&view_xylos,
|
||||
&view_EPAR,
|
||||
&checkbox_scan,
|
||||
&checkbox_flashing,
|
||||
&field_speed,
|
||||
&progressbar,
|
||||
&tx_view});
|
||||
|
||||
field_speed.set_value(1);
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
tx_view.on_start = [this]() {
|
||||
if (tx_mode == IDLE) {
|
||||
if (checkbox_scan.value()) {
|
||||
tx_mode = SCAN;
|
||||
} else {
|
||||
tx_mode = SINGLE;
|
||||
}
|
||||
|
||||
progressbar.set_value(0);
|
||||
tx_view.set_transmitting(true);
|
||||
target_system = (target_system_t)tab_view.selected();
|
||||
view_EPAR.half = false;
|
||||
|
||||
start_tx();
|
||||
}
|
||||
};
|
||||
|
||||
tx_view.on_stop = [this]() {
|
||||
stop_tx();
|
||||
};
|
||||
}
|
||||
|
||||
bool EPARView::increment_address() {
|
||||
auto city_code = field_city.value();
|
||||
|
||||
if (city_code < EPAR_MAX_CITY) {
|
||||
field_city.set_value(city_code + 1);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t EPARView::get_scan_remaining() {
|
||||
return EPAR_MAX_CITY - field_city.value();
|
||||
}
|
||||
|
||||
void EPARView::flip_relays() {
|
||||
// Invert first relay's state
|
||||
relay_states[0].set_selected_index(relay_states[0].selected_index() ^ 1);
|
||||
}
|
||||
|
||||
size_t EPARView::generate_message() {
|
||||
// R2, then R1
|
||||
return gen_message_ep(field_city.value(), field_group.selected_index_value(),
|
||||
half ? 0 : 1, relay_states[half].selected_index());
|
||||
}
|
||||
|
||||
EPARView::EPARView(
|
||||
Rect parent_rect)
|
||||
: View(parent_rect) {
|
||||
hidden(true);
|
||||
|
||||
add_children({&labels,
|
||||
&field_city,
|
||||
&field_group});
|
||||
|
||||
field_city.set_value(0);
|
||||
field_group.set_selected_index(2);
|
||||
field_city.on_change = [this](int32_t) { generate_message(); };
|
||||
field_group.on_change = [this](size_t, int32_t) { generate_message(); };
|
||||
|
||||
const auto relay_state_fn = [this](size_t, OptionsField::value_t) {
|
||||
generate_message();
|
||||
};
|
||||
|
||||
size_t n = 0;
|
||||
for (auto& relay_state : relay_states) {
|
||||
relay_state.set_parent_rect({static_cast<Coord>(90 + (n * 36)),
|
||||
80,
|
||||
24, 24});
|
||||
relay_state.set_options(relay_options);
|
||||
relay_state.on_change = relay_state_fn; // NB: set after set_options to avoid startup call.
|
||||
add_child(&relay_state);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
void EPARView::focus() {
|
||||
field_city.focus();
|
||||
}
|
||||
|
||||
void XylosView::flip_relays() {
|
||||
// Invert first relay's state if not ignored
|
||||
size_t rs = relay_states[0].selected_index();
|
||||
|
||||
if (rs > 0)
|
||||
relay_states[0].set_selected_index(rs ^ 3);
|
||||
}
|
||||
|
||||
bool XylosView::increment_address() {
|
||||
auto city_code = field_city.value();
|
||||
|
||||
if (city_code < XY_MAX_CITY) {
|
||||
field_city.set_value(city_code + 1);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t XylosView::get_scan_remaining() {
|
||||
return XY_MAX_CITY - field_city.value();
|
||||
}
|
||||
|
||||
void XylosView::generate_message() {
|
||||
gen_message_xy(field_header_a.value(), field_header_b.value(), field_city.value(), field_family.value(),
|
||||
checkbox_wcsubfamily.value(), field_subfamily.value(), checkbox_wcid.value(), field_receiver.value(),
|
||||
relay_states[0].selected_index(), relay_states[1].selected_index(),
|
||||
relay_states[2].selected_index(), relay_states[3].selected_index());
|
||||
}
|
||||
|
||||
XylosView::XylosView(
|
||||
Rect parent_rect)
|
||||
: View(parent_rect) {
|
||||
hidden(true);
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&field_header_a,
|
||||
&field_header_b,
|
||||
&field_city,
|
||||
&field_family,
|
||||
&field_subfamily,
|
||||
&checkbox_wcsubfamily,
|
||||
&field_receiver,
|
||||
&checkbox_wcid,
|
||||
//&button_seq,
|
||||
});
|
||||
|
||||
field_header_a.set_value(0);
|
||||
field_header_b.set_value(0);
|
||||
field_city.set_value(10);
|
||||
field_family.set_value(1);
|
||||
field_subfamily.set_value(1);
|
||||
field_receiver.set_value(1);
|
||||
|
||||
const auto field_fn = [this](int32_t) {
|
||||
generate_message();
|
||||
};
|
||||
|
||||
field_header_a.on_change = field_fn;
|
||||
field_header_b.on_change = field_fn;
|
||||
field_city.on_change = field_fn;
|
||||
field_family.on_change = field_fn;
|
||||
field_subfamily.on_change = field_fn;
|
||||
field_receiver.on_change = field_fn;
|
||||
|
||||
checkbox_wcsubfamily.on_select = [this](Checkbox&, bool v) {
|
||||
field_subfamily.set_focusable(!v);
|
||||
generate_message();
|
||||
};
|
||||
|
||||
checkbox_wcid.on_select = [this](Checkbox&, bool v) {
|
||||
field_receiver.set_focusable(!v);
|
||||
generate_message();
|
||||
};
|
||||
|
||||
checkbox_wcsubfamily.set_value(true);
|
||||
checkbox_wcid.set_value(true);
|
||||
|
||||
const auto relay_state_fn = [this](size_t, OptionsField::value_t) {
|
||||
generate_message();
|
||||
};
|
||||
|
||||
size_t n = 0;
|
||||
for (auto& relay_state : relay_states) {
|
||||
relay_state.set_parent_rect({static_cast<Coord>(54 + (n * 36)),
|
||||
134,
|
||||
24, 24});
|
||||
relay_state.set_options(relay_options);
|
||||
relay_state.on_change = relay_state_fn; // NB: set after set_options to avoid startup call.
|
||||
add_child(&relay_state);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
void XylosView::focus() {
|
||||
field_city.focus();
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::bht_tx
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
#include "bht.hpp"
|
||||
#include "bitmap.hpp"
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "encoders.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui::external_app::bht_tx {
|
||||
|
||||
class XylosView : public View {
|
||||
public:
|
||||
XylosView(Rect parent_rect);
|
||||
|
||||
void focus() override;
|
||||
|
||||
void flip_relays();
|
||||
void generate_message();
|
||||
bool increment_address();
|
||||
uint32_t get_scan_remaining();
|
||||
|
||||
private:
|
||||
Labels labels{
|
||||
{{8 * 8, 1 * 8}, "Header:", Theme::getInstance()->fg_light->foreground},
|
||||
{{4 * 8, 3 * 8}, "City code:", Theme::getInstance()->fg_light->foreground},
|
||||
{{7 * 8, 5 * 8}, "Family:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 7 * 8 + 2}, "Subfamily:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 11 * 8}, "Receiver ID:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 14 * 8}, "Relay:", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
NumberField field_header_a{
|
||||
{16 * 8, 1 * 8},
|
||||
2,
|
||||
{0, 99},
|
||||
1,
|
||||
'0'};
|
||||
NumberField field_header_b{
|
||||
{18 * 8, 1 * 8},
|
||||
2,
|
||||
{0, 99},
|
||||
1,
|
||||
'0'};
|
||||
|
||||
NumberField field_city{
|
||||
{16 * 8, 3 * 8},
|
||||
2,
|
||||
{0, XY_MAX_CITY},
|
||||
1,
|
||||
' '};
|
||||
|
||||
NumberField field_family{
|
||||
{16 * 8, 5 * 8},
|
||||
1,
|
||||
{0, 9},
|
||||
1,
|
||||
' '};
|
||||
|
||||
NumberField field_subfamily{
|
||||
{16 * 8, 7 * 8 + 2},
|
||||
1,
|
||||
{0, 9},
|
||||
1,
|
||||
' '};
|
||||
|
||||
Checkbox checkbox_wcsubfamily{
|
||||
{20 * 8, 6 * 8 + 6},
|
||||
3,
|
||||
"All"};
|
||||
|
||||
NumberField field_receiver{
|
||||
{16 * 8, 11 * 8},
|
||||
2,
|
||||
{0, 99},
|
||||
1,
|
||||
'0'};
|
||||
Checkbox checkbox_wcid{
|
||||
{20 * 8, 10 * 8 + 4},
|
||||
3,
|
||||
"All"};
|
||||
|
||||
std::array<ImageOptionsField, 4> relay_states{};
|
||||
|
||||
ImageOptionsField::options_t relay_options = {
|
||||
{&bitmap_bulb_ignore, 0},
|
||||
{&bitmap_bulb_off, 1},
|
||||
{&bitmap_bulb_on, 2}};
|
||||
};
|
||||
|
||||
class EPARView : public View {
|
||||
public:
|
||||
EPARView(Rect parent_rect);
|
||||
|
||||
void focus() override;
|
||||
|
||||
void flip_relays();
|
||||
size_t generate_message();
|
||||
bool increment_address();
|
||||
uint32_t get_scan_remaining();
|
||||
|
||||
bool half{false};
|
||||
|
||||
private:
|
||||
Labels labels{
|
||||
{{4 * 8, 1 * 8}, "City code:", Theme::getInstance()->fg_light->foreground},
|
||||
{{8 * 8, 3 * 8}, "Group:", Theme::getInstance()->fg_light->foreground},
|
||||
{{8 * 8, 7 * 8}, "Relay:", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
NumberField field_city{
|
||||
{16 * 8, 1 * 8},
|
||||
3,
|
||||
{0, EPAR_MAX_CITY},
|
||||
1,
|
||||
'0'};
|
||||
|
||||
OptionsField field_group{
|
||||
{16 * 8, 3 * 8},
|
||||
2,
|
||||
{{"A ", 2}, // See receiver PCB
|
||||
{"B ", 1},
|
||||
{"C ", 0},
|
||||
{"TP", 3}}};
|
||||
|
||||
std::array<ImageOptionsField, 2> relay_states{};
|
||||
|
||||
ImageOptionsField::options_t relay_options = {
|
||||
{&bitmap_bulb_off, 0},
|
||||
{&bitmap_bulb_on, 1}};
|
||||
};
|
||||
|
||||
class BHTView : public View {
|
||||
public:
|
||||
BHTView(NavigationView& nav);
|
||||
~BHTView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "BHT TX"; };
|
||||
|
||||
private:
|
||||
TxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_bht", app_settings::Mode::TX};
|
||||
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
void start_tx();
|
||||
void stop_tx();
|
||||
|
||||
enum target_system_t {
|
||||
XYLOS = 0,
|
||||
EPAR = 1
|
||||
};
|
||||
|
||||
target_system_t target_system = {};
|
||||
|
||||
enum tx_modes {
|
||||
IDLE = 0,
|
||||
SINGLE,
|
||||
SCAN
|
||||
};
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
Rect view_rect = {0, 3 * 8, screen_width, 176};
|
||||
|
||||
XylosView view_xylos{view_rect};
|
||||
EPARView view_EPAR{view_rect};
|
||||
|
||||
TabView tab_view{
|
||||
{"Xylos", Theme::getInstance()->fg_cyan->foreground, &view_xylos},
|
||||
{"EPAR", Theme::getInstance()->fg_green->foreground, &view_EPAR}};
|
||||
|
||||
Labels labels{
|
||||
{{29 * 8, 14 * 16 + 4}, "s", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
Checkbox checkbox_scan{
|
||||
{1 * 8, 25 * 8},
|
||||
4,
|
||||
"Scan"};
|
||||
|
||||
Checkbox checkbox_flashing{
|
||||
{16 * 8, 25 * 8},
|
||||
8,
|
||||
"Flashing"};
|
||||
NumberField field_speed{
|
||||
{26 * 8, 25 * 8 + 4},
|
||||
2,
|
||||
{1, 99},
|
||||
1,
|
||||
' '};
|
||||
|
||||
ProgressBar progressbar{
|
||||
{UI_POS_X(0), UI_POS_Y_BOTTOM(5), screen_width, 16},
|
||||
};
|
||||
|
||||
TransmitterView tx_view{
|
||||
(int16_t)UI_POS_Y_BOTTOM(4),
|
||||
10000,
|
||||
12};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress, message.done);
|
||||
}};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::bht_tx
|
||||
Reference in New Issue
Block a user