mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-11 00:59:28 +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__*/
|
||||
Reference in New Issue
Block a user