mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-10 08:39:29 +00:00
Airband VOR receiver and trasmitter (#3244)
* First test * Reorder includes for consistency and fix string dereference in VorCdiIndicator * Add memory section and linker script entry for vor_rx application * Testing VOR TX * Moving VOR TX to SD * Code refactoring to align with guidelines * Add TODO comment to verify radial sign convention in vor_tx_config * Remove unnecessary set_dirty() calls in VorRxView methods * Update Course Deviation Indicator label and adjust UI element positions * Update VorTxView to run prepared image from memory map * Update build condition in nightly release workflow to include workflow_dispatch event * Reverting action changes * Adjust Course Deviation Indicator layout and refine painting logic * Refactor VorRxView UI elements for improved layout and clarity * Add calibration field and update radial calculation logic in VorRxView * Enhance VOR processing: add renormalization to phase oscillator, improve signal handling, and update configuration parameters * Implement radial smoothing and TO/FROM state handling in VorRxView; update VOR processing logic * Setting comments inline * Refactor VorCdiIndicator and VorRxView: change normalize_signed_degrees to use int32_t, update radial smoothing to use fixed point arithmetic to reduce flash usage * Update external app address end to 0xAE0B0000 * Update set_vor_tx_config to include enabled parameter for consistency * Refactor VorRx to omit decim_2 stage and update comments for clarity on VOR processing * Add RSSI, Channel, and Audio fields to VorRxView to mimic existing apps * Refactor VorRxView labels and status messages * Update VorRxView calibration field range to allow full circle input * Completing Ident transmission logic * Fixing TX gain and moving log label * Refactor VOR phase calculations and update radial offset handling for accurate bearing representation * Add low-pass filters to strip 9960 Hz subcarrier from audio output
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2026 PortaPack Mayhem
|
||||
*
|
||||
* 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; if not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_vor_tx.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::vor_tx {
|
||||
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<VorTxView>();
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::vor_tx
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_vor_tx.application_information"), used)) application_information_t _application_information_vor_tx = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::vor_tx::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "VOR TX",
|
||||
/*.bitmap_data = */ {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x80,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x03,
|
||||
0xE0,
|
||||
0x07,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF8,
|
||||
0x1F,
|
||||
0xF8,
|
||||
0x1F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xE0,
|
||||
0x07,
|
||||
0xC0,
|
||||
0x03,
|
||||
0x80,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
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_vor_tx */ {'P', 'V', 'T', 'X'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2026 PortaPack Mayhem
|
||||
*
|
||||
* 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; if not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui_vor_tx.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::vor_tx {
|
||||
|
||||
VorTxView::VorTxView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({&labels,
|
||||
&field_radial,
|
||||
&text_radial_unit,
|
||||
&check_ident,
|
||||
&button_ident_text,
|
||||
&text_status,
|
||||
&tx_view});
|
||||
|
||||
field_radial.set_value(radial_);
|
||||
field_radial.on_change = [this](int32_t v) {
|
||||
radial_ = v;
|
||||
if (transmitting_) {
|
||||
update_config();
|
||||
}
|
||||
};
|
||||
|
||||
check_ident.set_value(ident_);
|
||||
check_ident.on_select = [this](Checkbox&, bool v) {
|
||||
ident_ = v;
|
||||
if (transmitting_) {
|
||||
update_config();
|
||||
}
|
||||
};
|
||||
|
||||
button_ident_text.set_text(ident_text_);
|
||||
button_ident_text.on_select = [this](Button&) {
|
||||
text_prompt(nav_, ident_text_, 7, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string&) {
|
||||
button_ident_text.set_text(ident_text_);
|
||||
if (transmitting_) {
|
||||
update_config();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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_acknowledged_) {
|
||||
start_tx();
|
||||
return;
|
||||
}
|
||||
nav_.display_modal(
|
||||
"TX WARNING",
|
||||
"VOR is an aviation nav\nband! TX only into a\ndummy load.",
|
||||
YESNO,
|
||||
[this](bool choice) {
|
||||
if (choice) {
|
||||
tx_acknowledged_ = true;
|
||||
start_tx();
|
||||
} else {
|
||||
tx_view.set_transmitting(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
tx_view.on_stop = [this]() {
|
||||
stop_tx();
|
||||
};
|
||||
}
|
||||
|
||||
VorTxView::~VorTxView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void VorTxView::focus() {
|
||||
tx_view.focus();
|
||||
}
|
||||
|
||||
void VorTxView::update_config() {
|
||||
baseband::set_vor_tx_config(static_cast<uint16_t>(radial_), ident_, ident_text_);
|
||||
}
|
||||
|
||||
void VorTxView::start_tx() {
|
||||
transmitter_model.set_sampling_rate(1536000);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
update_config();
|
||||
transmitting_ = true;
|
||||
tx_view.set_transmitting(true);
|
||||
text_status.set("TX radial " + to_string_dec_uint(radial_));
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void VorTxView::stop_tx() {
|
||||
transmitter_model.disable();
|
||||
transmitting_ = false;
|
||||
tx_view.set_transmitting(false);
|
||||
text_status.set("Idle");
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::vor_tx
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2026 PortaPack Mayhem
|
||||
*
|
||||
* 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; if not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_VOR_TX_H__
|
||||
#define __UI_VOR_TX_H__
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
namespace ui::external_app::vor_tx {
|
||||
|
||||
class VorTxView : public View {
|
||||
public:
|
||||
VorTxView(NavigationView& nav);
|
||||
~VorTxView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "VOR TX"; }
|
||||
|
||||
private:
|
||||
void start_tx();
|
||||
void stop_tx();
|
||||
void update_config();
|
||||
|
||||
NavigationView& nav_;
|
||||
bool transmitting_{false};
|
||||
bool tx_acknowledged_{false};
|
||||
|
||||
uint32_t radial_{0};
|
||||
bool ident_{true};
|
||||
std::string ident_text_{"VOR"};
|
||||
|
||||
TxRadioState radio_state_{
|
||||
113'500'000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_vor",
|
||||
app_settings::Mode::TX,
|
||||
{
|
||||
{"radial"sv, &radial_},
|
||||
{"ident"sv, &ident_},
|
||||
{"ident_text"sv, &ident_text_},
|
||||
}};
|
||||
|
||||
Labels labels{
|
||||
{{UI_POS_X(0), UI_POS_Y(1)}, "Radial:", Theme::getInstance()->fg_light->foreground},
|
||||
{{UI_POS_X(0), UI_POS_Y(5)}, "Ident:", Theme::getInstance()->fg_light->foreground},
|
||||
{{UI_POS_X(0), UI_POS_Y(8)}, "Status:", Theme::getInstance()->fg_light->foreground},
|
||||
{{UI_POS_X(0), UI_POS_Y(10)}, "Lab use: set any freq &", Theme::getInstance()->fg_light->foreground},
|
||||
{{UI_POS_X(0), UI_POS_Y(11)}, "TX into a dummy load.", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
NumberField field_radial{
|
||||
{UI_POS_X(8), UI_POS_Y(1)},
|
||||
3,
|
||||
{0, 359},
|
||||
1,
|
||||
'0'};
|
||||
|
||||
Text text_radial_unit{
|
||||
{UI_POS_X(12), UI_POS_Y(1), UI_POS_WIDTH(4), UI_POS_HEIGHT(1)},
|
||||
"deg"};
|
||||
|
||||
Checkbox check_ident{
|
||||
{UI_POS_X(0), UI_POS_Y(3)},
|
||||
13,
|
||||
"CW identifier",
|
||||
true};
|
||||
|
||||
Button button_ident_text{
|
||||
{UI_POS_X(8), UI_POS_Y(5), UI_POS_WIDTH(10), UI_POS_HEIGHT(2)},
|
||||
"VOR"};
|
||||
|
||||
Text text_status{
|
||||
{UI_POS_X(8), UI_POS_Y(8), UI_POS_WIDTH_REMAINING(8), UI_POS_HEIGHT(1)},
|
||||
"Idle"};
|
||||
|
||||
TransmitterView tx_view{
|
||||
(int16_t)UI_POS_Y_BOTTOM(4),
|
||||
8333,
|
||||
1750000};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::vor_tx
|
||||
|
||||
#endif // __UI_VOR_TX_H__
|
||||
Reference in New Issue
Block a user