mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-11 17:19:29 +00:00
Aprs tx gps added, and save config added, path added (#2937)
This commit is contained in:
@@ -68,6 +68,9 @@ void BoundSetting::parse(std::string_view value) {
|
|||||||
as<bool>() = (parsed != 0);
|
as<bool>() = (parsed != 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SettingType::Float:
|
||||||
|
as<float>() = atof(value.data());
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +104,11 @@ void BoundSetting::write(File& file) const {
|
|||||||
case SettingType::Bool:
|
case SettingType::Bool:
|
||||||
file.write(as<bool>() ? "1" : "0", 1);
|
file.write(as<bool>() ? "1" : "0", 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SettingType::Float: {
|
||||||
|
auto float_str = to_string_decimal(as<float>(), 6);
|
||||||
|
file.write(float_str.c_str(), float_str.length());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.write("\r\n", 2);
|
file.write("\r\n", 2);
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ class BoundSetting {
|
|||||||
U8,
|
U8,
|
||||||
String,
|
String,
|
||||||
Bool,
|
Bool,
|
||||||
|
Float
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -73,6 +74,9 @@ class BoundSetting {
|
|||||||
BoundSetting(std::string_view name, bool* target)
|
BoundSetting(std::string_view name, bool* target)
|
||||||
: name_{name}, target_{target}, type_{SettingType::Bool} {}
|
: name_{name}, target_{target}, type_{SettingType::Bool} {}
|
||||||
|
|
||||||
|
BoundSetting(std::string_view name, float* target)
|
||||||
|
: name_{name}, target_{target}, type_{SettingType::Float} {}
|
||||||
|
|
||||||
std::string_view name() const { return name_; }
|
std::string_view name() const { return name_; }
|
||||||
void parse(std::string_view value);
|
void parse(std::string_view value);
|
||||||
void write(File& file) const;
|
void write(File& file) const;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "baseband_api.hpp"
|
#include "baseband_api.hpp"
|
||||||
#include "portapack_shared_memory.hpp"
|
#include "portapack_shared_memory.hpp"
|
||||||
#include "portapack_persistent_memory.hpp"
|
#include "portapack_persistent_memory.hpp"
|
||||||
|
#include "ui_geomap.hpp"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -49,10 +50,24 @@ APRSTXView::~APRSTXView() {
|
|||||||
|
|
||||||
void APRSTXView::start_tx() {
|
void APRSTXView::start_tx() {
|
||||||
// TODO: Clean up this API to take string_views to avoid allocations.
|
// TODO: Clean up this API to take string_views to avoid allocations.
|
||||||
|
std::string new_payload = payload;
|
||||||
|
|
||||||
|
std::string gps = text_gps_coord.get();
|
||||||
|
std::string token = "?GPS?";
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = new_payload.find(token, pos)) != std::string::npos) {
|
||||||
|
new_payload.replace(pos, token.size(), gps);
|
||||||
|
pos += gps.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
text_payload.set(new_payload);
|
||||||
|
|
||||||
|
std::string path = text_path.get();
|
||||||
|
|
||||||
make_aprs_frame(
|
make_aprs_frame(
|
||||||
sym_source.to_string().c_str(), num_ssid_source.value(),
|
sym_source.to_string().c_str(), num_ssid_source.value(),
|
||||||
sym_dest.to_string().c_str(), num_ssid_dest.value(),
|
sym_dest.to_string().c_str(), num_ssid_dest.value(),
|
||||||
payload);
|
new_payload, path);
|
||||||
|
|
||||||
// uint8_t * bb_data_ptr = shared_memory.bb_data.data;
|
// uint8_t * bb_data_ptr = shared_memory.bb_data.data;
|
||||||
// text_payload.set(to_string_hex_array(bb_data_ptr + 56, 15));
|
// text_payload.set(to_string_hex_array(bb_data_ptr + 56, 15));
|
||||||
@@ -77,6 +92,61 @@ void APRSTXView::on_tx_progress(const uint32_t progress, const bool done) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APRSTXView::process_coordinates(float lat_, float lon_) {
|
||||||
|
std::string s;
|
||||||
|
|
||||||
|
char ns = lat_ >= 0 ? 'N' : 'S';
|
||||||
|
float lat = (lat_ >= 0) ? lat_ : -lat_;
|
||||||
|
uint32_t lat_d = (uint32_t)lat;
|
||||||
|
uint32_t lat_m = (uint32_t)((lat - lat_d) * 6000.0f + 0.5f);
|
||||||
|
|
||||||
|
if (lat_m >= 6000) {
|
||||||
|
lat_m = 0;
|
||||||
|
lat_d++;
|
||||||
|
}
|
||||||
|
|
||||||
|
s += to_string_dec_uint(lat_d, 2, '0');
|
||||||
|
s += to_string_dec_uint(lat_m / 100, 2, '0');
|
||||||
|
s += ".";
|
||||||
|
s += to_string_dec_uint(lat_m % 100, 2, '0');
|
||||||
|
s += ns;
|
||||||
|
|
||||||
|
s += "/";
|
||||||
|
|
||||||
|
char ew = lon_ >= 0 ? 'E' : 'W';
|
||||||
|
float lon = (lon_ >= 0) ? lon_ : -lon_;
|
||||||
|
uint32_t lon_d = (uint32_t)lon;
|
||||||
|
uint32_t lon_m = (uint32_t)((lon - lon_d) * 6000.0f + 0.5f);
|
||||||
|
|
||||||
|
if (lon_m >= 6000) {
|
||||||
|
lon_m = 0;
|
||||||
|
lon_d++;
|
||||||
|
}
|
||||||
|
|
||||||
|
s += to_string_dec_uint(lon_d, 3, '0');
|
||||||
|
s += to_string_dec_uint(lon_m / 100, 2, '0');
|
||||||
|
s += ".";
|
||||||
|
s += to_string_dec_uint(lon_m % 100, 2, '0');
|
||||||
|
s += ew;
|
||||||
|
text_gps_coord.set(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APRSTXView::on_gps(const GPSPosDataMessage* message) {
|
||||||
|
if (manual_gps_mode) {
|
||||||
|
return; // no more update once set manually
|
||||||
|
}
|
||||||
|
if (message->lat < -90.0 || message->lat > 90.0 ||
|
||||||
|
message->lon < -180.0 || message->lon > 180.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message->lat == 0.0f && message->lon == 0.0f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last_lat = message->lat;
|
||||||
|
last_lon = message->lon;
|
||||||
|
process_coordinates(message->lat, message->lon);
|
||||||
|
}
|
||||||
|
|
||||||
APRSTXView::APRSTXView(NavigationView& nav) {
|
APRSTXView::APRSTXView(NavigationView& nav) {
|
||||||
baseband::run_image(portapack::spi_flash::image_tag_afsk);
|
baseband::run_image(portapack::spi_flash::image_tag_afsk);
|
||||||
|
|
||||||
@@ -87,8 +157,33 @@ APRSTXView::APRSTXView(NavigationView& nav) {
|
|||||||
&num_ssid_dest,
|
&num_ssid_dest,
|
||||||
&text_payload,
|
&text_payload,
|
||||||
&button_set,
|
&button_set,
|
||||||
|
&text_gps_coord,
|
||||||
|
&button_mangps,
|
||||||
|
&gps_is_manual,
|
||||||
|
&text_path,
|
||||||
|
&button_setpath,
|
||||||
&tx_view});
|
&tx_view});
|
||||||
|
|
||||||
|
sym_source.set_value(symsrc);
|
||||||
|
num_ssid_source.set_value(ssidsrc);
|
||||||
|
sym_dest.set_value(symdst);
|
||||||
|
num_ssid_dest.set_value(ssiddst);
|
||||||
|
text_payload.set(payload);
|
||||||
|
text_path.set(path_cache);
|
||||||
|
|
||||||
|
sym_source.on_change = [this](SymField&) {
|
||||||
|
symsrc = sym_source.to_string();
|
||||||
|
};
|
||||||
|
num_ssid_source.on_change = [this](int32_t v) {
|
||||||
|
ssidsrc = v;
|
||||||
|
};
|
||||||
|
sym_dest.on_change = [this](SymField&) {
|
||||||
|
symdst = sym_dest.to_string();
|
||||||
|
};
|
||||||
|
num_ssid_dest.on_change = [this](int32_t v) {
|
||||||
|
ssiddst = v;
|
||||||
|
};
|
||||||
|
|
||||||
button_set.on_select = [this, &nav](Button&) {
|
button_set.on_select = [this, &nav](Button&) {
|
||||||
text_prompt(
|
text_prompt(
|
||||||
nav,
|
nav,
|
||||||
@@ -99,6 +194,17 @@ APRSTXView::APRSTXView(NavigationView& nav) {
|
|||||||
text_payload.set(s);
|
text_payload.set(s);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
button_setpath.on_select = [this, &nav](Button&) {
|
||||||
|
text_prompt(
|
||||||
|
nav,
|
||||||
|
path_cache,
|
||||||
|
60,
|
||||||
|
ENTER_KEYBOARD_MODE_ALPHA,
|
||||||
|
[this](std::string& s) {
|
||||||
|
text_path.set(s);
|
||||||
|
path_cache = s;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
tx_view.on_edit_frequency = [this, &nav]() {
|
tx_view.on_edit_frequency = [this, &nav]() {
|
||||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
|
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
|
||||||
@@ -116,6 +222,23 @@ APRSTXView::APRSTXView(NavigationView& nav) {
|
|||||||
tx_view.set_transmitting(false);
|
tx_view.set_transmitting(false);
|
||||||
transmitter_model.disable();
|
transmitter_model.disable();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
button_mangps.on_select = [this, &nav](Button&) {
|
||||||
|
nav.push<GeoMapView>(
|
||||||
|
0,
|
||||||
|
GeoPos::alt_unit::METERS,
|
||||||
|
GeoPos::spd_unit::HIDDEN,
|
||||||
|
last_lat,
|
||||||
|
last_lon,
|
||||||
|
[this](int32_t, float lat, float lon, int32_t) {
|
||||||
|
last_lat = lat;
|
||||||
|
last_lon = lon;
|
||||||
|
manual_gps_mode = true;
|
||||||
|
gps_is_manual.set("MANUAL");
|
||||||
|
process_coordinates(lat, lon);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// process_coordinates(last_lat, last_lon); //don't load last, so won't confuse users
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace ui */
|
} /* namespace ui */
|
||||||
|
|||||||
@@ -50,59 +50,109 @@ class APRSTXView : public View {
|
|||||||
1750000 /* bandwidth */,
|
1750000 /* bandwidth */,
|
||||||
AFSK_TX_SAMPLERATE /* sampling rate */
|
AFSK_TX_SAMPLERATE /* sampling rate */
|
||||||
};
|
};
|
||||||
app_settings::SettingsManager settings_{
|
|
||||||
"tx_aprs", app_settings::Mode::TX};
|
|
||||||
|
|
||||||
|
std::string symsrc = "";
|
||||||
|
std::string symdst = "";
|
||||||
std::string payload{""};
|
std::string payload{""};
|
||||||
|
std::string path_cache = "";
|
||||||
|
int32_t ssidsrc = 0;
|
||||||
|
int32_t ssiddst = 0;
|
||||||
|
|
||||||
|
bool manual_gps_mode = false;
|
||||||
|
float last_lat = 0.0f;
|
||||||
|
float last_lon = 0.0f;
|
||||||
|
|
||||||
|
app_settings::SettingsManager settings_{
|
||||||
|
"tx_aprs",
|
||||||
|
app_settings::Mode::TX,
|
||||||
|
{{"symsrc"sv, &symsrc},
|
||||||
|
{"symdst"sv, &symdst},
|
||||||
|
{"ssidsrc"sv, &ssidsrc},
|
||||||
|
{"ssiddst"sv, &ssiddst},
|
||||||
|
{"payload"sv, &payload},
|
||||||
|
{"last_lat"sv, &last_lat},
|
||||||
|
{"last_lon"sv, &last_lon},
|
||||||
|
{"path"sv, &path_cache}}};
|
||||||
|
|
||||||
void start_tx();
|
void start_tx();
|
||||||
void generate_frame();
|
void generate_frame();
|
||||||
void generate_frame_pos();
|
void generate_frame_pos();
|
||||||
void on_tx_progress(const uint32_t progress, const bool done);
|
void on_tx_progress(const uint32_t progress, const bool done);
|
||||||
|
void on_gps(const GPSPosDataMessage* message);
|
||||||
|
void process_coordinates(float lat, float lon);
|
||||||
|
|
||||||
Labels labels{
|
Labels labels{
|
||||||
{{UI_POS_X(0), 1 * 16}, "Source: SSID:", Theme::getInstance()->fg_light->foreground}, // 6 alphanum + SSID
|
{{UI_POS_X(0), UI_POS_Y(0)}, "Source: SSID:", Theme::getInstance()->fg_light->foreground}, // 6 alphanum + SSID
|
||||||
{{UI_POS_X(0), 2 * 16}, " Dest.: SSID:", Theme::getInstance()->fg_light->foreground},
|
{{UI_POS_X(0), UI_POS_Y(1)}, " Dest.: SSID:", Theme::getInstance()->fg_light->foreground},
|
||||||
{{UI_POS_X(0), 4 * 16}, "Info field:", Theme::getInstance()->fg_light->foreground},
|
{{UI_POS_X(0), UI_POS_Y(2)}, "Path: (ex.: WIDE1-1,WIDE2-1)", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{UI_POS_X(0), UI_POS_Y(6)}, "Info field:", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{UI_POS_X(0), UI_POS_Y(10)}, "GPS:", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{UI_POS_X_CENTER(22), UI_POS_Y(14)}, "Use ?GPS? in the info", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{UI_POS_X_CENTER(17), UI_POS_Y(15)}, "as a placeholder.", Theme::getInstance()->fg_light->foreground},
|
||||||
|
};
|
||||||
|
|
||||||
|
Text text_path{
|
||||||
|
{UI_POS_X(0), UI_POS_Y(3), UI_POS_MAXWIDTH, UI_POS_HEIGHT(1)},
|
||||||
|
"WIDE1-1",
|
||||||
|
};
|
||||||
|
Text gps_is_manual{
|
||||||
|
{UI_POS_X(6), UI_POS_Y(10), UI_POS_WIDTH(10), UI_POS_HEIGHT(1)},
|
||||||
|
"",
|
||||||
};
|
};
|
||||||
|
|
||||||
SymField sym_source{
|
SymField sym_source{
|
||||||
{7 * 8, 1 * 16},
|
{UI_POS_X(7), UI_POS_Y(0)},
|
||||||
6,
|
6,
|
||||||
SymField::Type::Alpha};
|
SymField::Type::Alpha};
|
||||||
|
|
||||||
NumberField num_ssid_source{
|
NumberField num_ssid_source{
|
||||||
{19 * 8, 1 * 16},
|
{UI_POS_X(19), UI_POS_Y(0)},
|
||||||
2,
|
2,
|
||||||
{0, 15},
|
{0, 15},
|
||||||
1,
|
1,
|
||||||
' '};
|
' '};
|
||||||
|
|
||||||
SymField sym_dest{
|
SymField sym_dest{
|
||||||
{7 * 8, 2 * 16},
|
{UI_POS_X(7), UI_POS_Y(1)},
|
||||||
6,
|
6,
|
||||||
SymField::Type::Alpha};
|
SymField::Type::Alpha};
|
||||||
|
|
||||||
NumberField num_ssid_dest{
|
NumberField num_ssid_dest{
|
||||||
{19 * 8, 2 * 16},
|
{UI_POS_X(19), UI_POS_Y(1)},
|
||||||
2,
|
2,
|
||||||
{0, 15},
|
{0, 15},
|
||||||
1,
|
1,
|
||||||
' '};
|
' '};
|
||||||
|
|
||||||
Text text_payload{
|
Text text_payload{
|
||||||
{UI_POS_X(0), 5 * 16, screen_width, 16},
|
{UI_POS_X(0), UI_POS_Y(7), UI_POS_MAXWIDTH, UI_POS_HEIGHT(1)},
|
||||||
"-"};
|
""};
|
||||||
Button button_set{
|
Button button_set{
|
||||||
{UI_POS_X(0), 6 * 16, 80, 32},
|
{UI_POS_X(0), UI_POS_Y(8), UI_POS_WIDTH(10), UI_POS_HEIGHT(2)},
|
||||||
"Set"};
|
"Set"};
|
||||||
|
|
||||||
|
Text text_gps_coord{
|
||||||
|
{UI_POS_X(0), UI_POS_Y(11), UI_POS_WIDTH(20), UI_POS_HEIGHT(1)},
|
||||||
|
"-"};
|
||||||
|
|
||||||
|
Button button_mangps{
|
||||||
|
{UI_POS_X(0), UI_POS_Y(12), UI_POS_WIDTH(14), UI_POS_HEIGHT(2)},
|
||||||
|
"Manual GPS"};
|
||||||
|
Button button_setpath{
|
||||||
|
{UI_POS_X(0), UI_POS_Y(4), UI_POS_WIDTH(14), UI_POS_HEIGHT(2)},
|
||||||
|
"Set Path"};
|
||||||
TransmitterView tx_view{
|
TransmitterView tx_view{
|
||||||
(int16_t)UI_POS_Y_BOTTOM(4),
|
(int16_t)UI_POS_Y_BOTTOM(4),
|
||||||
5000,
|
5000,
|
||||||
0 // disable setting bandwith, since APRS used fixed 10k bandwidth
|
0 // disable setting bandwith, since APRS used fixed 10k bandwidth
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MessageHandlerRegistration message_handler_gps{
|
||||||
|
Message::ID::GPSPosData,
|
||||||
|
[this](Message* const p) {
|
||||||
|
const auto message = static_cast<const GPSPosDataMessage*>(p);
|
||||||
|
this->on_gps(message);
|
||||||
|
}};
|
||||||
MessageHandlerRegistration message_handler_tx_progress{
|
MessageHandlerRegistration message_handler_tx_progress{
|
||||||
Message::ID::TXProgress,
|
Message::ID::TXProgress,
|
||||||
[this](const Message* const p) {
|
[this](const Message* const p) {
|
||||||
|
|||||||
@@ -31,20 +31,67 @@ using namespace ax25;
|
|||||||
|
|
||||||
namespace aprs {
|
namespace aprs {
|
||||||
|
|
||||||
void make_aprs_frame(const char* src_address, const uint32_t src_ssid, const char* dest_address, const uint32_t dest_ssid, const std::string& payload) {
|
void make_aprs_frame(const char* src_address, const uint32_t src_ssid, const char* dest_address, const uint32_t dest_ssid, const std::string& payload, const std::string& path) {
|
||||||
AX25Frame frame;
|
AX25Frame frame;
|
||||||
|
|
||||||
char address[14] = {0};
|
char address[14] = {0};
|
||||||
|
memset(address, ' ', 14);
|
||||||
|
|
||||||
memcpy(&address[0], dest_address, 6);
|
size_t dest_len = strlen(dest_address);
|
||||||
memcpy(&address[7], src_address, 6);
|
if (dest_len > 6) dest_len = 6;
|
||||||
|
memcpy(&address[0], dest_address, dest_len);
|
||||||
|
size_t src_len = strlen(src_address);
|
||||||
|
if (src_len > 6) src_len = 6;
|
||||||
|
memcpy(&address[7], src_address, src_len);
|
||||||
// euquiq: According to ax.25 doc section 2.2.13.x.x and 2.4.1.2
|
// euquiq: According to ax.25 doc section 2.2.13.x.x and 2.4.1.2
|
||||||
// SSID need bits 5.6 set, so later when shifted it will end up being 011xxxx0 (xxxx = SSID number)
|
// SSID need bits 5.6 set, so later when shifted it will end up being 011xxxx0 (xxxx = SSID number)
|
||||||
// Notice that if need to signal usage of AX.25 V2.0, (dest_ssid | 112); (MSb will need to be set at the end)
|
// Notice that if need to signal usage of AX.25 V2.0, (dest_ssid | 112); (MSb will need to be set at the end)
|
||||||
address[6] = (dest_ssid | 48);
|
address[6] = (dest_ssid | 0x30);
|
||||||
address[13] = (src_ssid | 48);
|
address[13] = (src_ssid | 0x30);
|
||||||
|
|
||||||
frame.make_ui_frame(address, 0x03, protocol_id_t::NO_LAYER3, payload);
|
// fix path ssid bits. the result will be the same as above
|
||||||
|
std::string fixed_path = "";
|
||||||
|
|
||||||
|
const char* p = path.c_str();
|
||||||
|
|
||||||
|
while (*p) {
|
||||||
|
while (*p == ' ') p++;
|
||||||
|
if (!*p) break;
|
||||||
|
|
||||||
|
char call[6] = {' ', ' ', ' ', ' ', ' ', ' '};
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
while (*p && *p != '-' && *p != ',') {
|
||||||
|
if (*p != ' ') {
|
||||||
|
if (idx < 6) {
|
||||||
|
char c = *p;
|
||||||
|
if (c >= 'a' && c <= 'z') c -= 32;
|
||||||
|
call[idx++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ssid = 0;
|
||||||
|
if (*p == '-') {
|
||||||
|
p++;
|
||||||
|
while (*p == ' ') p++;
|
||||||
|
while (*p >= '0' && *p <= '9') {
|
||||||
|
ssid = ssid * 10 + (*p - '0');
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssid >= 0 && ssid <= 15) {
|
||||||
|
for (int i = 0; i < 6; i++) fixed_path += call[i];
|
||||||
|
fixed_path += (char)(ssid | 0x30);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*p && *p != ',') p++;
|
||||||
|
if (*p == ',') p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.make_ui_frame(address, 0x03, protocol_id_t::NO_LAYER3, payload, fixed_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace aprs */
|
} /* namespace aprs */
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ void make_aprs_frame(
|
|||||||
const uint32_t src_ssid,
|
const uint32_t src_ssid,
|
||||||
const char* dest_address,
|
const char* dest_address,
|
||||||
const uint32_t dest_ssid,
|
const uint32_t dest_ssid,
|
||||||
const std::string& payload);
|
const std::string& payload,
|
||||||
|
const std::string& path = "");
|
||||||
|
|
||||||
} /* namespace aprs */
|
} /* namespace aprs */
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,16 @@
|
|||||||
|
|
||||||
namespace ax25 {
|
namespace ax25 {
|
||||||
|
|
||||||
void AX25Frame::make_extended_field(char* const data, size_t length) {
|
void AX25Frame::make_extended_field(char* const data, size_t length, bool is_last) {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
for (i = 0; i < length - 1; i++)
|
for (i = 0; i < length - 1; i++)
|
||||||
add_data(data[i] << 1);
|
add_data(data[i] << 1);
|
||||||
|
|
||||||
add_data((data[i] << 1) | 1);
|
if (is_last)
|
||||||
|
add_data((data[i] << 1) | 1);
|
||||||
|
else
|
||||||
|
add_data((data[i] << 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AX25Frame::NRZI_add_bit(const uint32_t bit) {
|
void AX25Frame::NRZI_add_bit(const uint32_t bit) {
|
||||||
@@ -92,7 +95,7 @@ void AX25Frame::add_checksum() {
|
|||||||
add_byte(checksum >> 8, false, false);
|
add_byte(checksum >> 8, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AX25Frame::make_ui_frame(char* const address, const uint8_t control, const uint8_t protocol, const std::string& info) {
|
void AX25Frame::make_ui_frame(char* const address, const uint8_t control, const uint8_t protocol, const std::string& info, const std::string& path) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
bb_data_ptr = (uint16_t*)shared_memory.bb_data.data;
|
bb_data_ptr = (uint16_t*)shared_memory.bb_data.data;
|
||||||
@@ -103,22 +106,27 @@ void AX25Frame::make_ui_frame(char* const address, const uint8_t control, const
|
|||||||
ones_counter = 0;
|
ones_counter = 0;
|
||||||
crc_ccitt.reset();
|
crc_ccitt.reset();
|
||||||
|
|
||||||
add_flag();
|
add_flag(); // 0x73
|
||||||
add_flag();
|
add_flag(); // 0x73
|
||||||
add_flag();
|
add_flag(); // 0x73
|
||||||
add_flag();
|
add_flag(); // 0x73
|
||||||
|
|
||||||
make_extended_field(address, 14);
|
// path must not contain the - character! just the callsign and SSID next to each other
|
||||||
add_data(control);
|
bool has_path = (path.size() > 0 && path.size() <= 63 && path.size() % 7 == 0);
|
||||||
add_data(protocol);
|
make_extended_field(address, 14, !has_path);
|
||||||
|
if (has_path) {
|
||||||
|
make_extended_field(const_cast<char*>(path.data()), path.size(), true);
|
||||||
|
}
|
||||||
|
add_data(control); // 0x03
|
||||||
|
add_data(protocol); // 0xf0
|
||||||
|
|
||||||
for (i = 0; i < info.size(); i++)
|
for (i = 0; i < info.size(); i++)
|
||||||
add_data(info[i]);
|
add_data(info[i]);
|
||||||
|
|
||||||
add_checksum();
|
add_checksum(); // fcs
|
||||||
|
|
||||||
add_flag();
|
add_flag(); // 0x73
|
||||||
add_flag();
|
add_flag(); // 0x73
|
||||||
|
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ enum protocol_id_t {
|
|||||||
|
|
||||||
class AX25Frame {
|
class AX25Frame {
|
||||||
public:
|
public:
|
||||||
void make_ui_frame(char* const address, const uint8_t control, const uint8_t protocol, const std::string& info);
|
void make_ui_frame(char* const address, const uint8_t control, const uint8_t protocol, const std::string& info, const std::string& path = "");
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void NRZI_add_bit(const uint32_t bit);
|
void NRZI_add_bit(const uint32_t bit);
|
||||||
void make_extended_field(char* const data, size_t length);
|
void make_extended_field(char* const data, size_t length, bool is_last = false);
|
||||||
void add_byte(uint8_t byte, bool is_flag, bool is_data);
|
void add_byte(uint8_t byte, bool is_flag, bool is_data);
|
||||||
void add_data(uint8_t byte);
|
void add_data(uint8_t byte);
|
||||||
void add_checksum();
|
void add_checksum();
|
||||||
|
|||||||
@@ -103,8 +103,10 @@ class APRSPacket {
|
|||||||
std::string get_digipeaters_formatted() {
|
std::string get_digipeaters_formatted() {
|
||||||
uint8_t position = DIGIPEATER_START;
|
uint8_t position = DIGIPEATER_START;
|
||||||
bool has_more = parse_address(SOURCE_START, REPEATER);
|
bool has_more = parse_address(SOURCE_START, REPEATER);
|
||||||
|
if (!has_more) {
|
||||||
std::string repeaters = "";
|
return "";
|
||||||
|
}
|
||||||
|
std::string repeaters = ",";
|
||||||
while (has_more) {
|
while (has_more) {
|
||||||
has_more = parse_address(position, REPEATER);
|
has_more = parse_address(position, REPEATER);
|
||||||
repeaters += std::string(address_buffer);
|
repeaters += std::string(address_buffer);
|
||||||
@@ -112,7 +114,7 @@ class APRSPacket {
|
|||||||
position += ADDRESS_SIZE;
|
position += ADDRESS_SIZE;
|
||||||
|
|
||||||
if (has_more) {
|
if (has_more) {
|
||||||
repeaters += ">";
|
repeaters += ",";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +148,8 @@ class APRSPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string get_stream_text() {
|
std::string get_stream_text() {
|
||||||
std::string stream = get_source_formatted() + ">" + get_destination_formatted() + ";" + get_digipeaters_formatted() + ";" + get_information_text_formatted();
|
// get_digipeaters_formatted will add the "," to the beginning if there are digipeaters
|
||||||
|
std::string stream = get_source_formatted() + ">" + get_destination_formatted() + get_digipeaters_formatted() + ":" + get_information_text_formatted();
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -384,6 +384,10 @@ void Text::set(std::string_view value) {
|
|||||||
set_dirty();
|
set_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Text::get() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
void Text::getAccessibilityText(std::string& result) {
|
void Text::getAccessibilityText(std::string& result) {
|
||||||
result = text;
|
result = text;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ class Text : public Widget {
|
|||||||
Text(Rect parent_rect);
|
Text(Rect parent_rect);
|
||||||
|
|
||||||
void set(std::string_view value);
|
void set(std::string_view value);
|
||||||
|
std::string get();
|
||||||
|
|
||||||
void paint(Painter& painter) override;
|
void paint(Painter& painter) override;
|
||||||
void getAccessibilityText(std::string& result) override;
|
void getAccessibilityText(std::string& result) override;
|
||||||
|
|||||||
Reference in New Issue
Block a user