mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-07-26 10:38:52 +00:00
EPIRB TX SGB support (#3263)
* Update BEACONS.TXT Added test beacon with 3 bch1 errors and 2 bch2 errors to test multiple bit error correction. * Fist step to SGB format * Added SGB to manual mode. * SGB support - Added SGB frame to beacons file - Fixed hex display for SGB - Fixed frame type option display - Fixed self-test mode activation logic - Fixed PRN for self-test mode
This commit is contained in:
+198
-24
@@ -143,30 +143,6 @@ static void push_bits(uint8_t* buf, int& pos, uint64_t v, int n) {
|
||||
set_bit(buf, pos++, (v >> i) & 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a beacon to hex string representation
|
||||
* @param frame the frame to convert
|
||||
* @param start true for the first half of the frame, false for the second half
|
||||
* @return the hex string representation of the specieid half of the frame
|
||||
*/
|
||||
std::string beacon_to_hex_string(const uint8_t* frame, bool start) {
|
||||
static const char hex[] = "0123456789ABCDEF";
|
||||
|
||||
std::string out;
|
||||
out.resize(18);
|
||||
|
||||
int offset = start ? 0 : 9;
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
uint8_t b = frame[offset + i];
|
||||
|
||||
out[i * 2] = hex[b >> 4];
|
||||
out[i * 2 + 1] = hex[b & 0x0F];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a beacon in the provided buffer
|
||||
* @param frame the buffer to generate the frame in
|
||||
@@ -351,6 +327,204 @@ size_t generate_beacon(uint8_t* frame, const BeaconParams& params) {
|
||||
return 18;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a beacon hex string representation (generic range)
|
||||
* @param frame the frame data
|
||||
* @param offset_bytes starting byte offset
|
||||
* @param count_bytes number of bytes to convert
|
||||
* @return the hex string representation
|
||||
*/
|
||||
std::string beacon_to_hex_string_range(const uint8_t* frame, int offset_bytes, int count_bytes) {
|
||||
static const char hex[] = "0123456789ABCDEF";
|
||||
std::string out;
|
||||
out.resize(count_bytes * 2);
|
||||
for (int i = 0; i < count_bytes; i++) {
|
||||
uint8_t b = frame[offset_bytes + i];
|
||||
out[i * 2] = hex[b >> 4];
|
||||
out[i * 2 + 1] = hex[b & 0x0F];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SGB (Second Generation Beacon) — T.018 Rev 7, March 2021
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Reference values from T.018 canonical test vector (Appendix B.1)
|
||||
#define SGB_TAC_NUMBER 230 // TAC number (16 bits, 0–65535)
|
||||
#define SGB_SERIAL_NUMBER 573 // Serial number within TAC (14 bits, 0–16383)
|
||||
#define SGB_HOMING_DEVICE 1 // 1 = beacon has 121.5 / 243 MHz homing device
|
||||
#define SGB_RLS_FUNCTION 0 // 0 = no Return Link Service function
|
||||
|
||||
// BCH(250,202) generator polynomial — lower 48 bits (without leading X^48 term)
|
||||
// Full g(x): X^48+X^47+X^46+X^42+X^41+X^40+X^39+X^38+X^37+X^35+X^33+X^32+X^31
|
||||
// +X^26+X^24+X^23+X^22+X^20+X^19+X^18+X^17+X^16+X^13+X^12+X^11+X^10
|
||||
// +X^7+X^4+X^2+X+1
|
||||
// Binary MSB-first: 1110001111110101110000101110111110011110010010111 (T.018 App. B.1)
|
||||
#define SGB_BCH_G_LOWER UINT64_C(0xC7EB85DF3C97)
|
||||
|
||||
/**
|
||||
* Encode latitude for SGB GNSS location protocol (T.018 Appendix C)
|
||||
* Pushes 23 bits: 1-bit N/S flag + 7-bit degrees + 15-bit decimal fraction
|
||||
* @param frame the frame buffer
|
||||
* @param pos current bit position (modified in-place)
|
||||
* @param south true if southern hemisphere
|
||||
* @param lat_mag absolute latitude in decimal degrees (0..90)
|
||||
*/
|
||||
static void push_sgb_latitude(uint8_t* frame, int& pos, bool south, float lat_mag) {
|
||||
push_bits(frame, pos, south ? 1 : 0, 1);
|
||||
uint32_t deg = (uint32_t)lat_mag;
|
||||
if (deg > 90) deg = 90;
|
||||
float frac = lat_mag - (float)deg;
|
||||
uint32_t frac_n = (uint32_t)(frac * 32768.0f + 0.5f);
|
||||
if (frac_n >= 32768) {
|
||||
frac_n = 0;
|
||||
if (deg < 90) deg++;
|
||||
}
|
||||
push_bits(frame, pos, deg, 7);
|
||||
push_bits(frame, pos, frac_n, 15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode longitude for SGB GNSS location protocol (T.018 Appendix C)
|
||||
* Pushes 24 bits: 1-bit E/W flag + 8-bit degrees + 15-bit decimal fraction
|
||||
* @param frame the frame buffer
|
||||
* @param pos current bit position (modified in-place)
|
||||
* @param west true if western hemisphere
|
||||
* @param lon_mag absolute longitude in decimal degrees (0..180)
|
||||
*/
|
||||
static void push_sgb_longitude(uint8_t* frame, int& pos, bool west, float lon_mag) {
|
||||
push_bits(frame, pos, west ? 1 : 0, 1);
|
||||
uint32_t deg = (uint32_t)lon_mag;
|
||||
if (deg > 180) deg = 180;
|
||||
float frac = lon_mag - (float)deg;
|
||||
uint32_t frac_n = (uint32_t)(frac * 32768.0f + 0.5f);
|
||||
if (frac_n >= 32768) {
|
||||
frac_n = 0;
|
||||
if (deg < 180) deg++;
|
||||
}
|
||||
push_bits(frame, pos, deg, 8);
|
||||
push_bits(frame, pos, frac_n, 15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute BCH(250,202) parity using LFSR method (T.018 Appendix B.1)
|
||||
* Reads 202 message bits from buffer positions 6..207 and writes
|
||||
* 48 BCH parity bits to buffer positions 208..255.
|
||||
* @param frame the 32-byte SGB buffer (message already written at bits 6..207)
|
||||
*/
|
||||
static void compute_sgb_bch(uint8_t* frame) {
|
||||
uint64_t reg = 0;
|
||||
for (int i = 0; i < 202; i++) {
|
||||
int bp = i + 6; // buffer bit position (6 = start of message after padding)
|
||||
bool msg_bit = (frame[bp >> 3] >> (7 - (bp & 7))) & 1;
|
||||
bool feedback = ((reg >> 47) & 1) ^ (msg_bit ? 1u : 0u);
|
||||
reg = (reg << 1) & UINT64_C(0xFFFFFFFFFFFF);
|
||||
if (feedback) reg ^= SGB_BCH_G_LOWER;
|
||||
}
|
||||
// Append 48-bit remainder at buffer positions 208..255
|
||||
int pos = 208;
|
||||
push_bits(frame, pos, reg, 48);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map BeaconType to SGB 3-bit beacon type code (T.018 Table 3.1, bits 138-140)
|
||||
* 000=ELT, 001=EPIRB, 010=PLB
|
||||
*/
|
||||
static uint8_t sgb_beacon_type_code(BeaconType t) {
|
||||
switch (t) {
|
||||
case BeaconType::EPIRB:
|
||||
return 0b001;
|
||||
case BeaconType::PLB:
|
||||
return 0b010;
|
||||
default:
|
||||
case BeaconType::ELT:
|
||||
return 0b000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Second Generation Beacon (SGB / T.018) frame.
|
||||
* The 250-bit frame is stored in 32 bytes with 6 leading padding bits
|
||||
* (bits 0..4 = 0, bit 5 = 1).
|
||||
* The rotating field is Type 0 — G.008 Objective Requirements (Table 3.3),
|
||||
* with elapsed_hours and time_last_loc_min updated from elapsed_s.
|
||||
* @param frame 32-byte output buffer
|
||||
* @param params beacon parameters
|
||||
* @param elapsed_s seconds elapsed since beacon activation (drives rotating field)
|
||||
* @return 32 (size of the generated frame in bytes)
|
||||
*/
|
||||
size_t generate_sgb_beacon(uint8_t* frame, const BeaconParams& params, uint32_t elapsed_s) {
|
||||
memset(frame, 0, 32);
|
||||
|
||||
// 6 padding bits at start of 32-byte buffer: [0,0,0,0,1,0] (5th bit (1 based index as per specification) = 1 to indicate self-test mode)
|
||||
set_bit(frame, 4, 1);
|
||||
|
||||
// Message bits start at buffer bit position 6 (= SGB message bit 1)
|
||||
int pos = 6;
|
||||
|
||||
// Bits 1-16: TAC number (16 bits) — T.018 Table 3.1
|
||||
push_bits(frame, pos, SGB_TAC_NUMBER, 16);
|
||||
// Bits 17-30: Serial number within TAC (14 bits)
|
||||
push_bits(frame, pos, SGB_SERIAL_NUMBER, 14);
|
||||
// Bits 31-40: Country code (10 bits, ITU MID)
|
||||
push_bits(frame, pos, params.country & 0x3FFU, 10);
|
||||
// Bit 41: Status of homing device (1 = has 121.5/243 MHz homing device)
|
||||
push_bits(frame, pos, params.has_121_5 ? 1 : 0, 1);
|
||||
// Bit 42: RLS function flag (0 = no RLS)
|
||||
push_bits(frame, pos, SGB_RLS_FUNCTION, 1);
|
||||
// Bit 43: Test protocol flag
|
||||
push_bits(frame, pos, params.is_test ? 1 : 0, 1);
|
||||
|
||||
// Bits 44-66: Encoded GNSS latitude (23 bits) — T.018 Appendix C
|
||||
float lat_mag = params.location.latitude < 0.0f ? -params.location.latitude : params.location.latitude;
|
||||
push_sgb_latitude(frame, pos, params.location.south, lat_mag);
|
||||
// Bits 67-90: Encoded GNSS longitude (24 bits)
|
||||
float lon_mag = params.location.longitude < 0.0f ? -params.location.longitude : params.location.longitude;
|
||||
push_sgb_longitude(frame, pos, params.location.west, lon_mag);
|
||||
|
||||
// Bits 91-137: Vessel ID (47 bits = 3-bit type selector + 44-bit body)
|
||||
// Type 000 = No aircraft/maritime identity; body all zeros
|
||||
push_bits(frame, pos, 0, 47);
|
||||
|
||||
// Bits 138-140: Beacon type (3 bits)
|
||||
push_bits(frame, pos, sgb_beacon_type_code(params.type), 3);
|
||||
|
||||
// Bits 141-154: Spare (14 bits, all 1s in normal operation)
|
||||
push_bits(frame, pos, 0x3FFFU, 14);
|
||||
|
||||
// Bits 155-202: Rotating Field Type 0 — G.008 Objective Requirements (Table 3.3)
|
||||
// Bits 155-158: Identifier (4 bits = 0000)
|
||||
push_bits(frame, pos, 0b0000U, 4);
|
||||
// Bits 159-164: Elapsed time since activation (6 bits, 0-63 h, truncated at 63)
|
||||
uint32_t elapsed_h = elapsed_s / 3600;
|
||||
if (elapsed_h > 63) elapsed_h = 63;
|
||||
push_bits(frame, pos, elapsed_h, 6);
|
||||
// Bits 165-175: Time from last encoded location (11 bits, 0-2046 min; 2047 = no fix)
|
||||
uint32_t loc_age_min = elapsed_s / 60;
|
||||
if (loc_age_min > 2046) loc_age_min = 2046;
|
||||
push_bits(frame, pos, loc_age_min, 11);
|
||||
// Bits 176-185: Altitude of encoded location (10 bits; 0x3FF = no altitude)
|
||||
push_bits(frame, pos, 0x3FFU, 10);
|
||||
// Bits 186-193: Dilution of Precision (4-bit HDOP + 4-bit VDOP; 0 = best HDOP, 0 = best VDOP)
|
||||
push_bits(frame, pos, 0b00000000U, 8);
|
||||
// Bits 194-195: Activation notification (00 = manual by user)
|
||||
push_bits(frame, pos, 0b00U, 2);
|
||||
// Bits 196-198: Remaining battery capacity (101 = >75%)
|
||||
push_bits(frame, pos, 0b101U, 3);
|
||||
// Bits 199-200: GNSS status (10 = 3D fix)
|
||||
push_bits(frame, pos, 0b10U, 2);
|
||||
// Bits 201-202: Spare (00)
|
||||
push_bits(frame, pos, 0b00U, 2);
|
||||
|
||||
// pos == 208: 6 padding + 202 message bits consumed
|
||||
|
||||
// Bits 203-250: BCH(250,202) parity (48 bits) computed and written at positions 208-255
|
||||
compute_sgb_bch(frame);
|
||||
|
||||
return 32;
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::epirb_tx
|
||||
|
||||
#endif /*__BEACON_H__*/
|
||||
+63
-14
@@ -67,12 +67,17 @@ static uint8_t hexToByte(char high, char low) {
|
||||
return (hexval(high) << 4) | hexval(low);
|
||||
}
|
||||
|
||||
std::string EPIRBTXAppView::frame_to_hex_string(bool start) {
|
||||
return beacon_to_hex_string(epirb_tx_message.data, start);
|
||||
std::string EPIRBTXAppView::frame_to_hex_string_range(int offset_bytes, int count_bytes) {
|
||||
return beacon_to_hex_string_range(epirb_tx_message.data, offset_bytes, count_bytes);
|
||||
}
|
||||
|
||||
void EPIRBTXAppView::generate_frame(BeaconParams params) {
|
||||
epirb_tx_message.data_len = generate_beacon(epirb_tx_message.data, params);
|
||||
if (format_sgb) {
|
||||
uint32_t elapsed_s = sgb_start_time > 0 ? (chTimeNow() - sgb_start_time) / 1000 : 0;
|
||||
epirb_tx_message.data_len = generate_sgb_beacon(epirb_tx_message.data, params, elapsed_s);
|
||||
} else {
|
||||
epirb_tx_message.data_len = generate_beacon(epirb_tx_message.data, params);
|
||||
}
|
||||
}
|
||||
|
||||
void EPIRBTXAppView::on_timer() {
|
||||
@@ -125,7 +130,7 @@ void EPIRBTXAppView::update_am_transmission() {
|
||||
if (am_enabled && transmitting && !transmitting_bpsk) {
|
||||
// Start am transmission
|
||||
// Restore am frequency
|
||||
epirb_tx_message.mode_bpsk = false;
|
||||
epirb_tx_message.mode_406 = false;
|
||||
transmitter_model.set_target_frequency(am_frequency);
|
||||
// Send config to baseband
|
||||
baseband::set_epirb_tx_config(epirb_tx_message);
|
||||
@@ -146,10 +151,18 @@ void EPIRBTXAppView::update_frame(bool updateConfig) {
|
||||
file_mode_ui->text_description.set(beacon.description.substr(0, max_text_width_ext));
|
||||
file_mode_ui->text_description_end.set(beacon.description.size() > max_text_width_ext ? "-" + beacon.description.substr(max_text_width_ext, max_text_width_ext + max_text_width_ext - 1) : "");
|
||||
// Udapte frame content on display
|
||||
text_frame.set(beacon.frame.substr(0, 18));
|
||||
text_frame_end.set(beacon.frame.size() > 18 ? beacon.frame.substr(18, 36) : "");
|
||||
bool is_fgb = beacon.frame.size() <= BEACON_HEXA_SIZE_FGB;
|
||||
if (is_fgb) {
|
||||
text_frame.set(beacon.frame.substr(0, BEACON_HEXA_SPLIT_FGB));
|
||||
text_frame_end.set(beacon.frame.size() > BEACON_HEXA_SPLIT_FGB ? beacon.frame.substr(BEACON_HEXA_SPLIT_FGB, BEACON_HEXA_SPLIT_FGB) : "");
|
||||
text_frame_sgb_end.set("");
|
||||
} else {
|
||||
text_frame.set(" " + beacon.frame.substr(1, BEACON_HEXA_SPLIT_SGB - 1));
|
||||
text_frame_end.set(beacon.frame.size() > BEACON_HEXA_SPLIT_SGB ? beacon.frame.substr(BEACON_HEXA_SPLIT_SGB, BEACON_HEXA_SPLIT_SGB) : "");
|
||||
text_frame_sgb_end.set(beacon.frame.size() > 2 * BEACON_HEXA_SPLIT_SGB ? beacon.frame.substr(2 * BEACON_HEXA_SPLIT_SGB, BEACON_HEXA_SPLIT_SGB) : "");
|
||||
}
|
||||
// Prepare tx configuration
|
||||
epirb_tx_message.data_len = std::min<size_t>((beacon.frame.size() / 2), 18);
|
||||
epirb_tx_message.data_len = std::min<size_t>((beacon.frame.size() / 2), BEACON_SIZE_SGB);
|
||||
for (uint8_t i = 0; i < epirb_tx_message.data_len; i++) {
|
||||
epirb_tx_message.data[i] = hexToByte(
|
||||
beacon.frame[2 * i],
|
||||
@@ -159,8 +172,17 @@ void EPIRBTXAppView::update_frame(bool updateConfig) {
|
||||
// In manual mode, generate frame content for current beacon params
|
||||
generate_frame(beacon_params);
|
||||
// Update frame content on display
|
||||
text_frame.set(frame_to_hex_string(true));
|
||||
text_frame_end.set(frame_to_hex_string(false));
|
||||
if (format_sgb) {
|
||||
// SGB: 32 bytes across 3 lines (BEACON_HEXA_SPLIT_SGB = 22 hex chars = 11 bytes)
|
||||
// Remove first nibble and replace it with a space to match COSPAS hexadecimal representation specification
|
||||
text_frame.set(" " + frame_to_hex_string_range(0, BEACON_HEXA_SPLIT_SGB / 2).substr(1));
|
||||
text_frame_end.set(frame_to_hex_string_range(BEACON_HEXA_SPLIT_SGB / 2, BEACON_HEXA_SPLIT_SGB / 2));
|
||||
text_frame_sgb_end.set(frame_to_hex_string_range(BEACON_HEXA_SPLIT_SGB, (BEACON_HEXA_SPLIT_SGB / 2) - 1));
|
||||
} else {
|
||||
text_frame.set(frame_to_hex_string_range(0, BEACON_HEXA_SPLIT_FGB / 2));
|
||||
text_frame_end.set(frame_to_hex_string_range(BEACON_HEXA_SPLIT_FGB / 2, BEACON_HEXA_SPLIT_FGB / 2));
|
||||
text_frame_sgb_end.set("");
|
||||
}
|
||||
}
|
||||
if (updateConfig && send_on_change && loop) {
|
||||
// Need to update config / send new beacon
|
||||
@@ -176,14 +198,14 @@ void EPIRBTXAppView::update_frame(bool updateConfig) {
|
||||
}
|
||||
|
||||
void EPIRBTXAppView::update_config() {
|
||||
if (!epirb_tx_message.mode_bpsk) {
|
||||
if (!epirb_tx_message.mode_406) {
|
||||
// Previously in AM mode => restore bpsk frequency
|
||||
transmitter_model.set_target_frequency(bpsk_frequency);
|
||||
// Update displayed frequency
|
||||
tx_view.on_show();
|
||||
}
|
||||
// Set mode to bpsk
|
||||
epirb_tx_message.mode_bpsk = true;
|
||||
epirb_tx_message.mode_406 = true;
|
||||
transmitting_bpsk = true;
|
||||
// Set pre/post count
|
||||
epirb_tx_message.pre_count = (160 * TONES_SAMPLERATE) / 1000; // 160 ms carrier (COSPAS spec.)
|
||||
@@ -198,6 +220,13 @@ void EPIRBTXAppView::set_tx_button_state(bool active) {
|
||||
}
|
||||
|
||||
void EPIRBTXAppView::start_tx() {
|
||||
if (format_sgb && !mode_file) {
|
||||
// Track start time for the SGB rotating field elapsed-time counter
|
||||
if (!transmitting) sgb_start_time = chTimeNow();
|
||||
// update_frame regenerates the SGB frame with current elapsed time and refreshes display
|
||||
update_frame(false);
|
||||
set_dirty();
|
||||
}
|
||||
last_frame_time = chTimeNow();
|
||||
update_config();
|
||||
loop = loop_enabled;
|
||||
@@ -221,7 +250,7 @@ void EPIRBTXAppView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
transmitting_bpsk = false;
|
||||
if (am_enabled) {
|
||||
// BPSK frame sent, switch back to 121.5 AM signal
|
||||
epirb_tx_message.mode_bpsk = false;
|
||||
epirb_tx_message.mode_406 = false;
|
||||
// Start am transmission
|
||||
update_am_transmission();
|
||||
} else {
|
||||
@@ -310,6 +339,7 @@ void EPIRBTXAppView::update_mode() {
|
||||
options_beacon_type.hidden(mode_file);
|
||||
options_beacon_protocol.hidden(mode_file);
|
||||
options_beacon_country.hidden(mode_file);
|
||||
options_format.hidden(mode_file);
|
||||
text_field_beacon_locator.hidden(mode_file);
|
||||
}
|
||||
|
||||
@@ -323,6 +353,7 @@ EPIRBTXAppView::EPIRBTXAppView(
|
||||
&text_beacon_type,
|
||||
&options_beacon_type,
|
||||
&options_beacon_protocol,
|
||||
&options_format,
|
||||
&text_beacon_country,
|
||||
&options_beacon_country,
|
||||
&checkbox_beacon_internal,
|
||||
@@ -335,6 +366,7 @@ EPIRBTXAppView::EPIRBTXAppView(
|
||||
&text_field_beacon_locator,
|
||||
&text_frame,
|
||||
&text_frame_end,
|
||||
&text_frame_sgb_end,
|
||||
&text_timeout,
|
||||
&checkbox_loop,
|
||||
&field_delay,
|
||||
@@ -376,6 +408,7 @@ EPIRBTXAppView::EPIRBTXAppView(
|
||||
init_from_locator(beacon_params.location);
|
||||
update_mode();
|
||||
update_location();
|
||||
options_format.set_by_value(format_sgb ? 1 : 0);
|
||||
|
||||
options_mode.on_change = [this](size_t, OptionsField::value_t value) {
|
||||
mode_file = (((BeaconMode)value) == BeaconMode::FILE);
|
||||
@@ -405,6 +438,13 @@ EPIRBTXAppView::EPIRBTXAppView(
|
||||
set_dirty();
|
||||
};
|
||||
|
||||
options_format.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
format_sgb = (v == 1);
|
||||
sgb_start_time = 0; // reset elapsed counter when format changes
|
||||
update_frame();
|
||||
set_dirty();
|
||||
};
|
||||
|
||||
options_am_channel.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
bool is_real = false;
|
||||
switch ((AmChannel)v) {
|
||||
@@ -458,6 +498,9 @@ EPIRBTXAppView::EPIRBTXAppView(
|
||||
case BpskChannel::O:
|
||||
bpsk_frequency = BPSK_FREQUENCY_O;
|
||||
break;
|
||||
case BpskChannel::SGB:
|
||||
bpsk_frequency = BPSK_FREQUENCY_SGB;
|
||||
break;
|
||||
default:
|
||||
v = (uint8_t)BpskChannel::HAM;
|
||||
// fallthrough
|
||||
@@ -590,6 +633,9 @@ EPIRBTXAppView::EPIRBTXAppView(
|
||||
case BPSK_FREQUENCY_O:
|
||||
bpsk_channel = (uint8_t)BpskChannel::O;
|
||||
break;
|
||||
case BPSK_FREQUENCY_SGB:
|
||||
bpsk_channel = (uint8_t)BpskChannel::SGB;
|
||||
break;
|
||||
default:
|
||||
bpsk_channel = (uint8_t)BpskChannel::MANUAL;
|
||||
manual_bpsk_frequency = bpsk_frequency;
|
||||
@@ -642,11 +688,14 @@ void EPIRBTXAppView::load_beacons() {
|
||||
Beacon beacon{};
|
||||
beacon.title = trim(cols[0]);
|
||||
beacon.description = trim(cols[1]);
|
||||
// Make sure frame is not longer tha 18 bytes / 36 hex character
|
||||
beacon.frame = trim(cols[2]).substr(0, 36);
|
||||
// 1G uses up to 36 hex chars, 2G uses 64 hex chars (250 bits rounded to 32 bytes).
|
||||
beacon.frame = trim(cols[2]).substr(0, BEACON_HEXA_SIZE_SGB);
|
||||
size_t size = beacon.frame.size();
|
||||
if (size <= 0)
|
||||
continue; // Invalid line.
|
||||
if ((size % 2) != 0) {
|
||||
beacon.frame = "0" + beacon.frame; // Pad with leading 0 to make it even length
|
||||
}
|
||||
// Beacon is valid, add it to the list
|
||||
beacons.emplace_back(std::move(beacon));
|
||||
}
|
||||
|
||||
+27
-6
@@ -33,9 +33,12 @@
|
||||
#include "message.hpp"
|
||||
#include "tonesets.hpp"
|
||||
|
||||
#define BEACON_HEXA_SIZE 36
|
||||
#define BEACON_HEXA_HALF_SIZE 18
|
||||
#define BEACON_SIZE 18
|
||||
#define BEACON_SIZE_FGB 18
|
||||
#define BEACON_SIZE_SGB 32
|
||||
#define BEACON_HEXA_SIZE_FGB BEACON_SIZE_FGB * 2
|
||||
#define BEACON_HEXA_SIZE_SGB BEACON_SIZE_SGB * 2
|
||||
#define BEACON_HEXA_SPLIT_FGB 18
|
||||
#define BEACON_HEXA_SPLIT_SGB 22
|
||||
|
||||
#define AM_TEST_FREQUENCY 121375000
|
||||
#define AM_REAL_FREQUENCY 121500000
|
||||
@@ -49,6 +52,7 @@
|
||||
#define BPSK_FREQUENCY_K 406052000
|
||||
#define BPSK_FREQUENCY_N 406061000
|
||||
#define BPSK_FREQUENCY_O 406064000
|
||||
#define BPSK_FREQUENCY_SGB 406050000
|
||||
|
||||
namespace ui::external_app::epirb_tx {
|
||||
|
||||
@@ -85,7 +89,8 @@ enum class BpskChannel {
|
||||
K = 6,
|
||||
N = 7,
|
||||
O = 8,
|
||||
MANUAL = 10
|
||||
MANUAL = 10,
|
||||
SGB = 100
|
||||
};
|
||||
|
||||
struct Location {
|
||||
@@ -129,7 +134,7 @@ class EPIRBTXAppView : public View {
|
||||
void on_timer();
|
||||
void load_beacons();
|
||||
void set_tx_button_state(bool active);
|
||||
std::string frame_to_hex_string(bool start);
|
||||
std::string frame_to_hex_string_range(int offset_bytes, int count_bytes);
|
||||
void generate_frame(BeaconParams params);
|
||||
void update_frame(bool updateConfig = true);
|
||||
void update_bpsk_frequency();
|
||||
@@ -214,6 +219,7 @@ class EPIRBTXAppView : public View {
|
||||
{"country"sv, &beacon_country},
|
||||
{"internal"sv, &beacon_internal},
|
||||
{"locator"sv, &locator},
|
||||
{"sgb"sv, &format_sgb},
|
||||
}};
|
||||
|
||||
// Time of the last sent frame
|
||||
@@ -224,6 +230,10 @@ class EPIRBTXAppView : public View {
|
||||
bool transmitting_bpsk{false};
|
||||
// True when currently looping on sending beacons
|
||||
bool loop{false};
|
||||
// True when SGB (Second Generation Beacon) format is selected
|
||||
bool format_sgb{false};
|
||||
// System time (ms) when the current SGB transmission session started
|
||||
uint32_t sgb_start_time{0};
|
||||
|
||||
// Current EPIRBTXDataMessage for baseband
|
||||
EPIRBTXDataMessage epirb_tx_message{};
|
||||
@@ -302,10 +312,17 @@ class EPIRBTXAppView : public View {
|
||||
{"PLB", (uint8_t)BeaconType::PLB}}};
|
||||
OptionsField options_beacon_protocol{
|
||||
{UI_POS_X(9 + 7), UI_POS_Y(1)},
|
||||
30,
|
||||
8,
|
||||
{{"User", (uint8_t)BeaconProtocol::USER},
|
||||
{"Standard", (uint8_t)BeaconProtocol::STANDARD},
|
||||
{"National", (uint8_t)BeaconProtocol::NATIONAL}}};
|
||||
// Format selector (FGB / SGB) — manual mode only, same row as type/protocol
|
||||
OptionsField options_format{
|
||||
{UI_POS_X_RIGHT(4), UI_POS_Y(1)},
|
||||
4,
|
||||
{{"FGB", 0},
|
||||
{"SGB", 1}}};
|
||||
|
||||
OptionsField options_beacon_country{
|
||||
{UI_POS_X(9), UI_POS_Y(2)},
|
||||
7,
|
||||
@@ -337,6 +354,9 @@ class EPIRBTXAppView : public View {
|
||||
Text text_frame_end{
|
||||
{UI_POS_X(6), UI_POS_Y(7), UI_POS_WIDTH_REMAINING(6), UI_POS_DEFAULT_HEIGHT},
|
||||
""};
|
||||
Text text_frame_sgb_end{
|
||||
{UI_POS_X(6), UI_POS_Y(8), 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},
|
||||
@@ -389,6 +409,7 @@ class EPIRBTXAppView : public View {
|
||||
{"406.052 MHz (K)", (uint8_t)BpskChannel::K},
|
||||
{"406.061 MHz (N)", (uint8_t)BpskChannel::N},
|
||||
{"406.064 MHz (O)", (uint8_t)BpskChannel::O},
|
||||
{"406.050 MHz (SGB)", (uint8_t)BpskChannel::SGB},
|
||||
{"Manual", (uint8_t)BpskChannel::MANUAL}}};
|
||||
|
||||
// Transmitter view
|
||||
|
||||
@@ -27,6 +27,70 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
uint8_t EPIRBTXProcessor::get_frame_bit(uint16_t bit_pos) const {
|
||||
// Skip first 6 bits (padding for 250 bits -> 32 bytes)
|
||||
bit_pos += 6;
|
||||
if (bit_pos >= frame_sgb_bits_len) {
|
||||
return 0;
|
||||
}
|
||||
const uint8_t byte_value = frame_data[bit_pos >> 3];
|
||||
const uint8_t bit_offset = 7 - (bit_pos & 0x07);
|
||||
return (byte_value >> bit_offset) & 0x01;
|
||||
}
|
||||
|
||||
int8_t EPIRBTXProcessor::compute_sgb_chip_level(SGBChannelState& channel, bool q_channel) {
|
||||
// Table 2.4 behavior: chip = PRN for bit 0, inverted PRN for bit 1.
|
||||
// PRN generator (23-bit LFSR): out = reg[0], feedback = reg[0] XOR reg[18].
|
||||
const uint8_t prn_chip = channel.prn_state & 0x01;
|
||||
|
||||
uint8_t info_bit = 0;
|
||||
const uint32_t bit_in_channel = channel.chip_index / sgb_chips_per_bit;
|
||||
if (bit_in_channel >= sgb_preamble_bits_per_channel) {
|
||||
const uint32_t message_index = bit_in_channel - sgb_preamble_bits_per_channel;
|
||||
if (message_index < (sgb_message_bits / 2)) {
|
||||
const uint16_t frame_bit_pos = q_channel ? (message_index * 2) + 1 : (message_index * 2);
|
||||
info_bit = get_frame_bit(frame_bit_pos);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t xor_chip = info_bit ^ prn_chip;
|
||||
const int8_t chip_level = xor_chip ? -sgb_chip_amplitude : sgb_chip_amplitude;
|
||||
|
||||
const uint8_t feedback = prn_chip ^ ((channel.prn_state >> 18) & 0x01);
|
||||
channel.prn_state = (channel.prn_state >> 1) | (uint32_t(feedback) << 22);
|
||||
channel.chip_index++;
|
||||
|
||||
return chip_level;
|
||||
}
|
||||
|
||||
int8_t EPIRBTXProcessor::sample_sgb_channel(SGBChannelState& channel, bool q_channel) {
|
||||
int8_t level = channel.chip_level;
|
||||
|
||||
if (channel.sample_in_chip == 0) {
|
||||
if (channel.chip_index < sgb_segment_chips) {
|
||||
channel.chip_level = compute_sgb_chip_level(channel, q_channel);
|
||||
level = channel.chip_level;
|
||||
} else {
|
||||
level = 0;
|
||||
channel.chip_level = 0;
|
||||
}
|
||||
}
|
||||
|
||||
channel.sample_in_chip++;
|
||||
if (channel.sample_in_chip >= sgb_samples_per_chip) {
|
||||
channel.sample_in_chip = 0;
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
void EPIRBTXProcessor::init_sgb_channel(SGBChannelState& channel, uint32_t initial_state) {
|
||||
channel.prn_state = initial_state;
|
||||
channel.chip_index = 0;
|
||||
channel.sample_in_chip = 0;
|
||||
channel.chip_level = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing method for this processor
|
||||
*/
|
||||
@@ -43,75 +107,92 @@ void EPIRBTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
}
|
||||
|
||||
if (mode_bpsk) {
|
||||
// BPSK Manchester beacon signal
|
||||
if (bpsk_pre_count < config_pre_count) {
|
||||
// Pre-count state: send carrier only during pre-count
|
||||
bpsk_pre_count++;
|
||||
re = i_carrier;
|
||||
im = q_carrier;
|
||||
} else if (bpsk_post_count > 0) {
|
||||
// Post-count: send carrier only during post-count
|
||||
bpsk_post_count++;
|
||||
re = i_carrier;
|
||||
im = q_carrier;
|
||||
if (bpsk_post_count >= config_post_count) {
|
||||
// End transmission here
|
||||
byte_index = 0;
|
||||
bpsk_post_count = 0;
|
||||
bpsk_pre_count = 0;
|
||||
if (mode_406) {
|
||||
if (mode_sgb) {
|
||||
// 2G SGB DSSS-OQPSK signal
|
||||
const int8_t i_sample = sample_sgb_channel(sgb_i, false);
|
||||
const int8_t q_sample = (sgb_sample_counter < sgb_half_chip_samples)
|
||||
? 0
|
||||
: sample_sgb_channel(sgb_q, true);
|
||||
|
||||
re = i_sample;
|
||||
im = q_sample;
|
||||
|
||||
sgb_sample_counter++;
|
||||
if (sgb_sample_counter >= sgb_total_samples) {
|
||||
sgb_sample_counter = 0;
|
||||
end_of_transmission = true;
|
||||
}
|
||||
} else {
|
||||
if (sample_counter == 0 && manchester_half == false) {
|
||||
if (bit_index == 0) {
|
||||
// Read current byte
|
||||
current_byte = frame_data[byte_index];
|
||||
// Move to next byte
|
||||
byte_index++;
|
||||
}
|
||||
// Get current bit
|
||||
current_bit = (current_byte >> (7 - bit_index)) & 0x01;
|
||||
}
|
||||
|
||||
// Manchester encoding
|
||||
if (current_bit == 1) {
|
||||
// 1 = falling signal
|
||||
if (manchester_half == false) {
|
||||
re = i_pos;
|
||||
im = q_pos;
|
||||
} else {
|
||||
re = i_neg;
|
||||
im = q_neg;
|
||||
// 1G BPSK Manchester beacon signal
|
||||
if (bpsk_pre_count < config_pre_count) {
|
||||
// Pre-count state: send carrier only during pre-count
|
||||
bpsk_pre_count++;
|
||||
re = i_carrier;
|
||||
im = q_carrier;
|
||||
} else if (bpsk_post_count > 0) {
|
||||
// Post-count: send carrier only during post-count
|
||||
bpsk_post_count++;
|
||||
re = i_carrier;
|
||||
im = q_carrier;
|
||||
if (bpsk_post_count >= config_post_count) {
|
||||
// End transmission here
|
||||
byte_index = 0;
|
||||
bpsk_post_count = 0;
|
||||
bpsk_pre_count = 0;
|
||||
end_of_transmission = true;
|
||||
}
|
||||
} else {
|
||||
// 0 = rising signal
|
||||
if (manchester_half == false) {
|
||||
re = i_neg;
|
||||
im = q_neg;
|
||||
} else {
|
||||
re = i_pos;
|
||||
im = q_pos;
|
||||
if (sample_counter == 0 && manchester_half == false) {
|
||||
if (bit_index == 0) {
|
||||
// Read current byte
|
||||
current_byte = frame_data[byte_index];
|
||||
// Move to next byte
|
||||
byte_index++;
|
||||
}
|
||||
// Get current bit
|
||||
current_bit = (current_byte >> (7 - bit_index)) & 0x01;
|
||||
}
|
||||
}
|
||||
// Move to next sample
|
||||
sample_counter++;
|
||||
|
||||
if (sample_counter >= samples_per_halfbit) {
|
||||
// Move to next half-bit
|
||||
sample_counter = 0;
|
||||
manchester_half = !manchester_half;
|
||||
// Manchester encoding
|
||||
if (current_bit == 1) {
|
||||
// 1 = falling signal
|
||||
if (manchester_half == false) {
|
||||
re = i_pos;
|
||||
im = q_pos;
|
||||
} else {
|
||||
re = i_neg;
|
||||
im = q_neg;
|
||||
}
|
||||
} else {
|
||||
// 0 = rising signal
|
||||
if (manchester_half == false) {
|
||||
re = i_neg;
|
||||
im = q_neg;
|
||||
} else {
|
||||
re = i_pos;
|
||||
im = q_pos;
|
||||
}
|
||||
}
|
||||
// Move to next sample
|
||||
sample_counter++;
|
||||
|
||||
// Next bit after two half bits
|
||||
if (manchester_half == false) {
|
||||
// Move to next bit
|
||||
bit_index++;
|
||||
if (bit_index >= 8) {
|
||||
// End of byte
|
||||
bit_index = 0;
|
||||
if (byte_index >= frame_data_len) {
|
||||
// End of frame => move to post-count
|
||||
bpsk_post_count = 1;
|
||||
if (sample_counter >= samples_per_halfbit) {
|
||||
// Move to next half-bit
|
||||
sample_counter = 0;
|
||||
manchester_half = !manchester_half;
|
||||
|
||||
// Next bit after two half bits
|
||||
if (manchester_half == false) {
|
||||
// Move to next bit
|
||||
bit_index++;
|
||||
if (bit_index >= 8) {
|
||||
// End of byte
|
||||
bit_index = 0;
|
||||
if (byte_index >= frame_data_len) {
|
||||
// End of frame => move to post-count
|
||||
bpsk_post_count = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,26 +235,51 @@ void EPIRBTXProcessor::on_message(const Message* const msg) {
|
||||
case Message::ID::EPIRBTXData: {
|
||||
const auto message = *reinterpret_cast<const EPIRBTXDataMessage*>(msg);
|
||||
// Check transmission mode
|
||||
mode_bpsk = message.mode_bpsk;
|
||||
if (mode_bpsk) {
|
||||
// BPSK mode for 406 frame
|
||||
mode_406 = message.mode_406;
|
||||
if (mode_406) {
|
||||
// 406 MHz frame mode:
|
||||
// - FGB if data_len <= 144 bits (i.e. 18 bytes)
|
||||
// - SGB if data_len > 144 bits (actually 250 bits (i.e. 32 bytes))
|
||||
config_pre_count = message.pre_count;
|
||||
config_post_count = message.post_count;
|
||||
|
||||
mode_sgb = message.data_len > frame_data_fgb_max_len; // SGB if data_len > 18 bytes (144 bits)
|
||||
frame_data_len = message.data_len;
|
||||
|
||||
if (mode_sgb) {
|
||||
frame_sgb_bits_len = std::min<uint16_t>(((uint16_t)message.data_len) * 8, sgb_message_bits); // Total bits in the frame
|
||||
frame_data_len = std::min<uint8_t>(frame_data_len, frame_data_sgb_len);
|
||||
// Detect self-test mode: bit 5 (1 based index as per specification) of message.data[0]
|
||||
mode_sgb_selftest = (message.data[0] >> 3) & 0x01;
|
||||
} else {
|
||||
frame_data_len = std::min<uint8_t>(frame_data_len, frame_data_fgb_max_len);
|
||||
}
|
||||
|
||||
// Get the frame data from the message
|
||||
memcpy(frame_data, message.data, std::min(frame_data_len, EPIRBTXDataMessage::max_len));
|
||||
// Init BPSK sequencer
|
||||
sample_counter = 0;
|
||||
bpsk_pre_count = 0;
|
||||
bpsk_post_count = 0;
|
||||
bit_index = 0;
|
||||
byte_index = 0;
|
||||
current_byte = 0;
|
||||
current_bit = 0;
|
||||
memcpy(frame_data, message.data, frame_data_len);
|
||||
|
||||
if (mode_sgb) {
|
||||
// Init SGB DSSS-OQPSK sequencer
|
||||
sgb_sample_counter = 0;
|
||||
const uint32_t init_i = mode_sgb_selftest ? sgb_init_selftest_i : sgb_init_normal_i;
|
||||
const uint32_t init_q = mode_sgb_selftest ? sgb_init_selftest_q : sgb_init_normal_q;
|
||||
init_sgb_channel(sgb_i, init_i);
|
||||
init_sgb_channel(sgb_q, init_q);
|
||||
} else {
|
||||
// Init FGB BPSK sequencer
|
||||
sample_counter = 0;
|
||||
bpsk_pre_count = 0;
|
||||
bpsk_post_count = 0;
|
||||
bit_index = 0;
|
||||
byte_index = 0;
|
||||
current_byte = 0;
|
||||
current_bit = 0;
|
||||
}
|
||||
} else {
|
||||
// AM mode for 121.5 signal => init AM sequencer
|
||||
sweep_phase = 0;
|
||||
audio_phase = 0;
|
||||
mode_sgb = false;
|
||||
}
|
||||
// Tell the processor to start
|
||||
configured = true;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "baseband_thread.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "tonesets.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
/**
|
||||
@@ -42,8 +43,12 @@ class EPIRBTXProcessor : public BasebandProcessor {
|
||||
private:
|
||||
// True when the processor has received a configuration message from the app
|
||||
bool configured{false};
|
||||
// True when in BPSK transmission mode, false for AM transmission mode
|
||||
bool mode_bpsk{false};
|
||||
// True when in 406 MHz transmission mode, false for AM transmission mode
|
||||
bool mode_406{false};
|
||||
// True when current 406 frame is a 2nd generation DSSS-OQPSK frame
|
||||
bool mode_sgb{false};
|
||||
// True when in self-test mode (detected from bit 5 of message.data)
|
||||
bool mode_sgb_selftest{true};
|
||||
|
||||
// True when the transmission has to be stopped (e.g. at the end of a frame)
|
||||
bool end_of_transmission{};
|
||||
@@ -55,10 +60,15 @@ class EPIRBTXProcessor : public BasebandProcessor {
|
||||
// Configured post-count value: used to continue the carrier for config_post_count samples after the end of a frame
|
||||
uint32_t config_post_count = 0;
|
||||
|
||||
// Data of the frame to send in BPSK mode
|
||||
uint8_t frame_data[18]{0};
|
||||
// Size of the frame to send in BPSK mode
|
||||
// Data of the frame to send in 406 mode (FGB BPSK or SGB DSSS-OQPSK)
|
||||
static constexpr uint8_t frame_data_max_len = 32;
|
||||
static constexpr uint8_t frame_data_fgb_max_len = 18;
|
||||
static constexpr uint8_t frame_data_sgb_len = 32; // ceil(250 / 8)
|
||||
uint8_t frame_data[frame_data_max_len]{0};
|
||||
// Size of the frame to send in bytes
|
||||
uint8_t frame_data_len = 0;
|
||||
// Size of the frame payload in bits
|
||||
uint16_t frame_sgb_bits_len = 0;
|
||||
|
||||
// BPSK parameters: Target phase +/-1.1 RAD as per COSPAS/SARSAT specifications
|
||||
static constexpr float phase_rad = 1.1f;
|
||||
@@ -89,6 +99,40 @@ class EPIRBTXProcessor : public BasebandProcessor {
|
||||
// Position in the current manchester bit
|
||||
bool manchester_half = false; // false = first half
|
||||
|
||||
// 2G SGB DSSS-OQPSK parameters
|
||||
static constexpr uint16_t sgb_message_bits = 256; // 250 bits of data + 6 bits padding
|
||||
static constexpr uint32_t sgb_chip_rate = 38400;
|
||||
static constexpr uint32_t sgb_chips_per_bit = 256;
|
||||
static constexpr uint32_t sgb_preamble_bits_per_channel = 25;
|
||||
static constexpr uint32_t sgb_bits_per_channel = 150;
|
||||
static constexpr uint32_t sgb_segment_chips = 38400;
|
||||
static constexpr uint32_t sgb_samples_per_chip = TONES_SAMPLERATE / sgb_chip_rate;
|
||||
static constexpr uint32_t sgb_half_chip_samples = sgb_samples_per_chip / 2;
|
||||
// SGB burst duration is exactly 1 second.
|
||||
static constexpr uint32_t sgb_total_samples = sgb_segment_chips * sgb_samples_per_chip;
|
||||
static constexpr int8_t sgb_chip_amplitude = 90;
|
||||
|
||||
static constexpr uint32_t sgb_init_normal_i = 0x1;
|
||||
static constexpr uint32_t sgb_init_normal_q = 0x1AC1FC;
|
||||
static constexpr uint32_t sgb_init_selftest_i = 0x52C9F0;
|
||||
static constexpr uint32_t sgb_init_selftest_q = 0x3CE928;
|
||||
|
||||
struct SGBChannelState {
|
||||
uint32_t prn_state = 1;
|
||||
uint32_t chip_index = 0;
|
||||
uint32_t sample_in_chip = 0;
|
||||
int8_t chip_level = 0;
|
||||
};
|
||||
|
||||
SGBChannelState sgb_i{};
|
||||
SGBChannelState sgb_q{};
|
||||
uint32_t sgb_sample_counter = 0;
|
||||
|
||||
uint8_t get_frame_bit(uint16_t bit_pos) const;
|
||||
int8_t compute_sgb_chip_level(SGBChannelState& channel, bool q_channel);
|
||||
int8_t sample_sgb_channel(SGBChannelState& channel, bool q_channel);
|
||||
void init_sgb_channel(SGBChannelState& channel, uint32_t initial_state);
|
||||
|
||||
// 127.5 AM signal parameters
|
||||
static const uint32_t sweep_rate = 3; // 3 Hz
|
||||
static const uint32_t f_min = 300; // Sweep min frequency (Hz)
|
||||
|
||||
@@ -1136,11 +1136,11 @@ class SigGenToneMessage : public Message {
|
||||
|
||||
class EPIRBTXDataMessage : public Message {
|
||||
public:
|
||||
static constexpr uint8_t max_len = 18;
|
||||
static constexpr uint8_t max_len = 32;
|
||||
constexpr EPIRBTXDataMessage()
|
||||
: Message{ID::EPIRBTXData} {
|
||||
}
|
||||
bool mode_bpsk = true;
|
||||
bool mode_406 = true;
|
||||
uint8_t data[max_len]{0};
|
||||
uint8_t data_len = 0;
|
||||
uint32_t pre_count = 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#Title; Description; Frame (Hex format : 18 Bytes / 36 Hex char)
|
||||
Selftest; Serial user Location Protocol; FFFED0D6E6202820000C29FF51041775302D
|
||||
Standard Test; ADRASEC02 30/11/2014 N49d16m32s/E3d16m32s; FFFED08e3e0425a8318074fe44b735cd7b46
|
||||
User protocol KO; Radio call sign (wrong BCH1); FFFED03EF613523F81FE0
|
||||
User protocol KO; Radio call sign (wrong BCH1); FFFED03EF613523F81FE00
|
||||
User protocol OK; Radio call sign (valid BCH1); FFFED056E6804002202009655250
|
||||
RLS Location; RLS Location; FFFED08E0D0990014710021963C85C7009F5
|
||||
PLB Location; PLB Location: National Location; FFFED08E3B15F1DFC0FF07FD1F769F3C0672
|
||||
@@ -24,11 +24,13 @@ RLS Location; RLS Location Protocol; FFFED096ED09900149D4D467EE0851A3B2E8
|
||||
Orbitography; Orbitography Protocol (from Toulouse FMCC's LUT); FFFE2FCE3000000000000DBD0E4022417500
|
||||
ELT 24 2; ELT 24 bits; FFFED08DB345B146202DDF3C71F59BAB7072
|
||||
Selftest BCH1 ERR; Serial user Location Protocol; FFFED0D7E6202820000C29FF51041775302D
|
||||
Selftest BCH2 ERR; Serial user Location Protocol; FFFED0D6E6202820000C29FF51041765302D
|
||||
Selftest BCH12 ERR; Serial user Location Protocol; FFFED0D7E6202820000C29FF51041765302D
|
||||
#Selftest BCH2 ERR; Serial user Location Protocol; FFFED0D6E6202820000C29FF51041765302D
|
||||
#Selftest BCH12 ERR; Serial user Location Protocol; FFFED0D7E6202820000C29FF51041765302D
|
||||
Selftest BCH12 3+2 ERR; Serial user Location Protocol; FFFED0D7D6202820000C29FF51041745302D
|
||||
Emergency Cap;EPIRB Emergency Capsizing;FFFED056E68040022021A4CDC675
|
||||
Emergency No Mar.;Emergency non maritime;FFFED056EE8040022021A5623CFE
|
||||
Emergency PLB;Emergency PLB no fire+med+dis;FFFED056E7804002202123F39E36
|
||||
Emergency PLB fire;Emergency PLB fire;FFFED056E7804002202123F39E39
|
||||
EPIRB Sinking;Emergency EPIRB Sinking;FFFED056E58040022021342FAD76
|
||||
EPIRB Call Sign Col.;EPIRB Radio Call Sign Emergency collision;FFFED056ED80400220217185E933
|
||||
SGB;2nd Generation Beacon;BFFFE1A738C95C7BE00BD8BE00000000001FFFF0FFFF7FFFEF0470404A448FD
|
||||
|
||||
Reference in New Issue
Block a user