mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-04 22:09:03 +00:00
5aecd4097e
* First step to epirb-tx app * Next step to epirb_tx app * Fixed merge * EPIRB TX app progress - Fixed UI - Added automatic frame resend after delay - Added load of beacons from EPIRB/BEACONS.TXT file * First step to adding 121.5 MHz signal * Refactored 121.5 signal generation code * Added frequency change when switching from BPSK to AM * Added checkbox for AM signal. Added START/STOP button Added AM frequency First step to editor mode. * Next step to frame editor * Fixed location bits. * Refactored code + fixed location * Added location fields * UI fixes + settings backup Fixed lat/long display Added send on change checkbox Added settings backup Fixed locator edition Fixed BEACONS.TXT to move to Self test beacons. * Update ui_epirb_tx.cpp Fixed timeout reset * Added national location protocol. Added national location protocol. Fixed send on change. * UI fixes Added country selection Separated beacon protocol and beacon type selectors * Standard protocol Added standard protocol Added internal checkbox * Fixed fomratting * Code cleanup * Comments / doc * More comments / doc * More comments / doc Fixed pre count duration. Added frequency restore after leaving * Prepare merge * Prepare merge * Finished merge * More comments and doc * Update ui_epirb_tx.cpp Fixed out of memory error when using AlphanumView * Update proc_epirb_tx.hpp Fixed comment * Fixed copilot PR comments. * Update proc_epirb_tx.hpp Fixed formatting * Update external.ld Merge with kiss_tnc app
333 lines
10 KiB
C++
333 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2026 Frederic BORRY - ADRASEC 31
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef __EPIRB_TX_H__
|
|
#define __EPIRB_TX_H__
|
|
|
|
#include "app_settings.hpp"
|
|
#include "radio_state.hpp"
|
|
#include "ui.hpp"
|
|
#include "ui_widget.hpp"
|
|
#include "ui_navigation.hpp"
|
|
#include "ui_transmitter.hpp"
|
|
|
|
#include "portapack.hpp"
|
|
#include "message.hpp"
|
|
#include "tonesets.hpp"
|
|
|
|
#define BEACON_HEXA_SIZE 36
|
|
#define BEACON_HEXA_HALF_SIZE 18
|
|
#define BEACON_SIZE 18
|
|
|
|
namespace ui::external_app::epirb_tx {
|
|
|
|
enum class BeaconType {
|
|
EPIRB = 0,
|
|
ELT = 1,
|
|
PLB = 2
|
|
};
|
|
|
|
enum class BeaconProtocol {
|
|
USER = 0,
|
|
STANDARD = 1,
|
|
NATIONAL = 2
|
|
};
|
|
|
|
struct Location {
|
|
std::string locator;
|
|
bool south;
|
|
uint16_t lat_deg;
|
|
uint8_t lat_min;
|
|
uint8_t lat_sec;
|
|
float latitude;
|
|
bool west;
|
|
uint16_t long_deg;
|
|
uint8_t long_min;
|
|
uint8_t long_sec;
|
|
float longitude;
|
|
};
|
|
|
|
struct BeaconParams {
|
|
BeaconType type;
|
|
BeaconProtocol protocol;
|
|
uint32_t country;
|
|
bool is_test;
|
|
bool is_internal;
|
|
bool has_121_5;
|
|
Location location;
|
|
};
|
|
|
|
class EPIRBTXAppView : public View {
|
|
public:
|
|
EPIRBTXAppView(NavigationView& nav);
|
|
~EPIRBTXAppView();
|
|
|
|
void focus() override;
|
|
|
|
std::string title() const override { return "EPIRB TX"; };
|
|
|
|
private:
|
|
void start_tx();
|
|
void stop_tx();
|
|
void update_config();
|
|
void on_tx_progress(const uint32_t progress, const bool done);
|
|
void on_timer();
|
|
void load_beacons();
|
|
void set_tx_button_state(bool active);
|
|
std::string frame_to_hex_string(bool start);
|
|
void generate_frame(BeaconParams params);
|
|
void update_frame(bool updateConfig = true);
|
|
void update_mode();
|
|
void update_location(bool updateLocatorField = true);
|
|
|
|
struct Beacon {
|
|
std::string title{};
|
|
std::string description{};
|
|
std::string frame{};
|
|
};
|
|
std::vector<Beacon> beacons{};
|
|
Beacon default_beacon{"Self test", "Serial User Location Protocol", "FFFED0D6E6202820000C29FF51041775302D"};
|
|
|
|
BeaconParams beacon_params{BeaconType::ELT, BeaconProtocol::STANDARD, 227, true, true, true, {"JN03RO", false, 0, 0, 0, 0, false, 0, 0, 0, 0}};
|
|
|
|
// Currently selected beacon index (from BEACONS.TXT file / options_frame combo)
|
|
uint32_t selected_beacon{0};
|
|
|
|
// Frequency of the transmitter before starting the app (used to restore frequency when leaving)
|
|
rf::Frequency original_frequency{0};
|
|
// Frequency of the AM emergency signal
|
|
rf::Frequency am_frequency{121500000};
|
|
// Frequency of the 406 MHz BPSK signal
|
|
rf::Frequency bpsk_frequency{406025000};
|
|
|
|
// True when using a beacon from the BEACONS.TXT file
|
|
bool mode_file{true};
|
|
// True when looping on sending beacons is enabled
|
|
bool loop_enabled{true};
|
|
// True if AM emergency signal transmission is enabled
|
|
bool am_enabled{true};
|
|
// True if we want to send a new frame each time the user changes the current beacon
|
|
bool send_on_change{false};
|
|
// The current locator string
|
|
std::string locator{"JN03RO"};
|
|
// The delay between each frame when on loop mode
|
|
uint32_t delay{50};
|
|
uint8_t beacon_type{0};
|
|
// Currently selected beacon protocol
|
|
uint8_t beacon_protocol{0};
|
|
// Currently selected beacon country
|
|
uint32_t beacon_country{227};
|
|
// Current beacon's internal state (true for internal location system)
|
|
bool beacon_internal{true};
|
|
|
|
TxRadioState radio_state_{
|
|
0 /* frequency */,
|
|
1750000 /* bandwidth */,
|
|
TONES_SAMPLERATE /* sampling rate */
|
|
};
|
|
app_settings::SettingsManager settings_{
|
|
"tx_epirb",
|
|
app_settings::Mode::TX,
|
|
{
|
|
{"sbeacon"sv, &selected_beacon},
|
|
{"amfreq"sv, &am_frequency},
|
|
{"bpskfreq"sv, &bpsk_frequency},
|
|
{"loop"sv, &loop_enabled},
|
|
{"delay"sv, &delay},
|
|
{"file"sv, &mode_file},
|
|
{"am"sv, &am_enabled},
|
|
{"soc"sv, &send_on_change},
|
|
{"type"sv, &beacon_type},
|
|
{"proto"sv, &beacon_protocol},
|
|
{"country"sv, &beacon_country},
|
|
{"internal"sv, &beacon_internal},
|
|
{"locator"sv, &locator},
|
|
}};
|
|
|
|
// Time of the last sent frame
|
|
uint32_t last_frame_time{0};
|
|
// True when transmission is enabled
|
|
bool transmitting{false};
|
|
// True when currently looping on sending beacons
|
|
bool loop{false};
|
|
|
|
// Current EPIRBTXDataMessage for baseband
|
|
EPIRBTXDataMessage epirb_tx_message{};
|
|
|
|
const size_t max_text_width = UI_POS_WIDTH_REMAINING(6) / UI_POS_DEFAULT_WIDTH;
|
|
const size_t max_text_width_ext = UI_POS_WIDTH_REMAINING(0) / UI_POS_DEFAULT_WIDTH;
|
|
|
|
Labels labels{
|
|
{{UI_POS_X(0), UI_POS_Y(0)}, "Source:", Theme::getInstance()->fg_light->foreground},
|
|
{{UI_POS_X(0), UI_POS_Y(6)}, "Frame:", Theme::getInstance()->fg_light->foreground},
|
|
{{UI_POS_X(0), UI_POS_Y(10)}, "Next frame in s.", Theme::getInstance()->fg_light->foreground},
|
|
{{UI_POS_X(0), UI_POS_Y(12)}, "AM frequency MHz", Theme::getInstance()->fg_light->foreground},
|
|
{{UI_POS_X(17), UI_POS_Y(9)}, "s.", Theme::getInstance()->fg_light->foreground}};
|
|
|
|
// For file mode
|
|
Text text_beacon{
|
|
{UI_POS_X(0), UI_POS_Y(1), UI_POS_WIDTH(7), UI_POS_DEFAULT_HEIGHT},
|
|
"Beacon:"};
|
|
// Beacon selection from BEACONS.TXT
|
|
OptionsField options_frame{
|
|
{UI_POS_X(7), UI_POS_Y(1)},
|
|
30,
|
|
{}};
|
|
Text text_description_label{
|
|
{UI_POS_X(0), UI_POS_Y(2), UI_POS_WIDTH(12), UI_POS_DEFAULT_HEIGHT},
|
|
"Description:"};
|
|
Text text_description{
|
|
{UI_POS_X(0), UI_POS_Y(3), UI_POS_WIDTH_REMAINING(0), UI_POS_DEFAULT_HEIGHT},
|
|
""};
|
|
Text text_description_end{
|
|
{UI_POS_X(0), UI_POS_Y(4), UI_POS_WIDTH_REMAINING(0), UI_POS_DEFAULT_HEIGHT},
|
|
""};
|
|
|
|
// For manual mode
|
|
Text text_beacon_type{
|
|
{UI_POS_X(0), UI_POS_Y(1), UI_POS_WIDTH(8), UI_POS_DEFAULT_HEIGHT},
|
|
"Type:"};
|
|
Text text_beacon_country{
|
|
{UI_POS_X(0), UI_POS_Y(2), UI_POS_WIDTH(8), UI_POS_DEFAULT_HEIGHT},
|
|
"Country:"};
|
|
Checkbox checkbox_beacon_internal{
|
|
{UI_POS_X_RIGHT(12), UI_POS_Y(2)},
|
|
12,
|
|
"Internal",
|
|
true};
|
|
Text text_beacon_locator{
|
|
{UI_POS_X(0), UI_POS_Y(3), UI_POS_WIDTH(8), UI_POS_DEFAULT_HEIGHT},
|
|
"Locator:"};
|
|
Text text_beacon_latitude{
|
|
{UI_POS_X(0), UI_POS_Y(4), UI_POS_WIDTH(8), UI_POS_DEFAULT_HEIGHT},
|
|
"Lat.:"};
|
|
Text text_beacon_longitude{
|
|
{UI_POS_X(0), UI_POS_Y(5), UI_POS_WIDTH(8), UI_POS_DEFAULT_HEIGHT},
|
|
"Long.:"};
|
|
Text text_beacon_latitude_value{
|
|
{UI_POS_X(7), UI_POS_Y(4), UI_POS_WIDTH(12), UI_POS_DEFAULT_HEIGHT},
|
|
""};
|
|
Text text_beacon_longitude_value{
|
|
{UI_POS_X(7), UI_POS_Y(5), UI_POS_WIDTH(12), UI_POS_DEFAULT_HEIGHT},
|
|
""};
|
|
OptionsField options_beacon_type{
|
|
{UI_POS_X(9), UI_POS_Y(1)},
|
|
7,
|
|
{{"EPIRB", 0},
|
|
{"ELT", 1},
|
|
{"PLB", 2}}};
|
|
OptionsField options_beacon_protocol{
|
|
{UI_POS_X(9 + 7), UI_POS_Y(1)},
|
|
30,
|
|
{{"User", 0},
|
|
{"Standard", 1},
|
|
{"National", 2}}};
|
|
OptionsField options_beacon_country{
|
|
{UI_POS_X(9), UI_POS_Y(2)},
|
|
7,
|
|
{{"France", 227},
|
|
{"USA", 366},
|
|
{"Germany", 211},
|
|
{"Russia", 273},
|
|
{"Spain", 224},
|
|
{"Japan", 431},
|
|
{"UK", 232}}};
|
|
TextField text_field_beacon_locator{
|
|
{UI_POS_X(9), UI_POS_Y(3), UI_POS_WIDTH(10), UI_POS_DEFAULT_HEIGHT},
|
|
"JN03RO"};
|
|
Button button_mangps{
|
|
{UI_POS_X_RIGHT(9), UI_POS_Y(3), UI_POS_WIDTH(9), UI_POS_HEIGHT(2)},
|
|
"Set pos."};
|
|
|
|
// Mode selection (file/manual)
|
|
OptionsField options_mode{
|
|
{UI_POS_X(7), UI_POS_Y(0)},
|
|
30,
|
|
{{"File (BEACONS.TXT)", 0},
|
|
{"Manual (Editor)", 1}}};
|
|
|
|
// Frame content
|
|
Text text_frame{
|
|
{UI_POS_X(6), UI_POS_Y(6), UI_POS_WIDTH_REMAINING(6), UI_POS_DEFAULT_HEIGHT},
|
|
""};
|
|
Text text_frame_end{
|
|
{UI_POS_X(6), UI_POS_Y(7), UI_POS_WIDTH_REMAINING(6), UI_POS_DEFAULT_HEIGHT},
|
|
""};
|
|
|
|
Text text_timeout{
|
|
{UI_POS_X(14), UI_POS_Y(10), UI_POS_WIDTH(2), UI_POS_DEFAULT_HEIGHT},
|
|
""};
|
|
|
|
// Transmission settings
|
|
Checkbox checkbox_loop{
|
|
{UI_POS_X(0), UI_POS_Y(9)},
|
|
10,
|
|
"Resend every",
|
|
true};
|
|
NumberField field_delay{
|
|
{UI_POS_X(15), UI_POS_Y(9)},
|
|
2,
|
|
{1, 99},
|
|
1,
|
|
' '};
|
|
Checkbox checkbox_am{
|
|
{UI_POS_X(0), UI_POS_Y(11)},
|
|
10,
|
|
"AM signal",
|
|
true};
|
|
FrequencyField field_am_frequency{
|
|
{UI_POS_X(13), UI_POS_Y(12)}};
|
|
Checkbox checkbox_send_on_change{
|
|
{UI_POS_X(0), UI_POS_Y(13)},
|
|
14,
|
|
"Send on change",
|
|
true};
|
|
Button button_tx{
|
|
{UI_POS_X_RIGHT(9), UI_POS_Y(9), UI_POS_WIDTH(9), UI_POS_HEIGHT(2)},
|
|
"START"};
|
|
const Style& style_tx_start = *Theme::getInstance()->fg_green;
|
|
const Style& style_tx_stop = *Theme::getInstance()->fg_red;
|
|
|
|
// Transmitter view
|
|
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);
|
|
}};
|
|
|
|
MessageHandlerRegistration message_handler_frame_sync{
|
|
// Use frame sync for our applcation timer callback
|
|
Message::ID::DisplayFrameSync,
|
|
[this](const Message* const) {
|
|
this->on_timer();
|
|
}};
|
|
};
|
|
|
|
} // namespace ui::external_app::epirb_tx
|
|
|
|
#endif /*__EPIRB_TX_H__*/
|