mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-11 00:59:28 +00:00
EPIRB RX app rework + EPIRB TX app fixes (#3172)
* First step to EPIRB rx app fix * Added spike filtering on carrier detection for more robustness. * Created dedicated packet builder * First step to fixing UI. * First step to epirb-rx ui rework. * Made static methods static members * Code optim * Code cleanup + first step to new UI * Fixed display * Added BeaconList widget + BeaconDB * Epirb TX app memory optim * Fixed OOM when opening Map Display or Locator editor on EPIRB TX app * First step to tabs + code cleanup to free some space... * Added spectrum analyzer + fixed display * Fixed display refresh issue after leaving spectrum. * More code optim + first step to detail view. * Fixed float coord display. * Fixed protocol names * Fixed location display * Externalized country base to sd card + more code optim * Fixed country display * Fixed path * Added protocol description and resource manager * Added main/aux loc device * Removed specan to save space * First step to adding QR code * Used a custom TextArea component instead of Console * Fixed QR code display for map URL * Added beacon selection management + fixes * First step to map/data qr. * QR tab : added data + detail/map toggle * Removed sqhelch to save space * Removed audio to save space * Removed freq label to save space * Removed packet timestamp to save space * Moved frequency values to resource file * Fixed detection parameters to suite real beacons (slower phase shift time). * Fixed countdown field size * Fixed frequency list. * Fixed detail view on select. * First step to fix settings. * Fixed squelch for a 0-99 range. * Added bch code correction (single bit error). * More code optim, disable country cache, removed inline from location to reduce code size. * Removed inline, code formatting. * Added bch code correction display. * Fixed focus bug, fixed packet size check. Added BCH1/BCH2 correction test cases in BEACONS.TXT file for EPIRB TX application. * Fixed merge * Added countdown setting. * Added selection display to beacon list * Restored squelch control. Added countdown reset. * Code optim on formatSummary * More code optim * Made ResourceManager methods static * Externalized beacon strings to res file *Removed frame parameter from methods * Added test beacons for emergency. Removed old res file. * Fixed resource manager * Fixed resources. Added emergency display. * BCH + res fixes * Fixed beacons * Fixed app color. Fixed HexId and Serial calculation. * Fixed frequency list. Comments. * Code cleanup and comments * Added slideshow mode to EPIRB TX application. Code comments / cleanup. * Fixed frame end after wrong code cleanup * More code optim and comments. Removed touch from beacon list. * Added beacon selection with encoder on detail view. * Added beacon paging in header. Fixed timeout display * Fixed build for PRALINE * Fixed formatting * Potential fix for pull request finding * stop on carriage return * empty string if unknown * zeroing data buffer before use * fix typo * add include * fix case if pair == 0 * fix scpectrum_on to spectrum_on * fix typos * replaced 3 inlined offset blocks with apply_offset calls * set_beacon consolidation
This commit is contained in:
+856
@@ -0,0 +1,856 @@
|
||||
/*
|
||||
* 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 __BEACON_RX_H__
|
||||
#define __BEACON_RX_H__
|
||||
|
||||
#include "country.hpp"
|
||||
#include "location.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
#include "resources.hpp"
|
||||
|
||||
namespace ui::external_app::epirb_rx {
|
||||
|
||||
// Size for beacon frame buffer
|
||||
#define BEACON_DATA_SIZE 18 // Max 144 bits => 18 bytes
|
||||
|
||||
// Constants for BCH control code calculation
|
||||
#define BCH_21_POLYNOMIAL 0b1001101101100111100011UL
|
||||
#define BCH_21_POLY_LENGTH 22
|
||||
#define BCH_12_POLYNOMIAL 0b1010100111001UL
|
||||
#define BCH_12_POLY_LENGTH 13
|
||||
|
||||
// See content of sdcard/EPIRB/RES/BEACON.RES
|
||||
#define ADDITIONAL_DATA_RESOURCE_START 25
|
||||
#define LONG_ADDITIONAL_DATA_RESOURCE_START 33
|
||||
#define EMERGENCY_RESOURCE_START 39
|
||||
#define EMERGENCY_OTHER_RESOURCE_START 49
|
||||
#define AUX_DEVICE_RESOURCE_START 53
|
||||
|
||||
/**
|
||||
* This is the main class for beaon handling
|
||||
*/
|
||||
class Beacon {
|
||||
public:
|
||||
// Real beacon or test beacon ?
|
||||
enum class FrameMode { NORMAL,
|
||||
SELF_TEST,
|
||||
UNKNOWN };
|
||||
// Type of main location device
|
||||
enum class MainLocatingDevice { UNDEFINED,
|
||||
INTERNAL_NAV,
|
||||
EXTERNAL_NAV };
|
||||
// Type of aux location device
|
||||
enum class AuxLocatingDevice { UNDEFINED,
|
||||
NONE,
|
||||
NONE_OR_OTHER,
|
||||
OTHER,
|
||||
MHZ121_5,
|
||||
SART };
|
||||
|
||||
// Protcol type
|
||||
enum class ProtocolType {
|
||||
UNKNOWN,
|
||||
USER,
|
||||
STANDARD_LOCATION,
|
||||
NATIONAL_LOCATION,
|
||||
RLS_LOCATION,
|
||||
ELT_DT_LOCATION,
|
||||
SPARE
|
||||
};
|
||||
|
||||
// Protocol
|
||||
enum class Protocol {
|
||||
USER_EPIRB_MARITIME,
|
||||
USER_EPIRB_RADIO,
|
||||
USER_ELT,
|
||||
USER_SERIAL,
|
||||
USER_TEST,
|
||||
USER_ORB,
|
||||
USER_NAT,
|
||||
USER_2G,
|
||||
STD_EPIRB,
|
||||
STD_ELT_24,
|
||||
STD_ELT_SERIAL,
|
||||
STD_ELT_AIRCRAFT,
|
||||
STD_EPIRB_SERIAL,
|
||||
STD_PLB_SERIAL,
|
||||
STD_SHIP,
|
||||
STD_TEST,
|
||||
NAT_ELT,
|
||||
NAT_EPIRB,
|
||||
NAT_PLB,
|
||||
NAT_TEST,
|
||||
RLS,
|
||||
ELT_DT,
|
||||
SPARE,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
#define UNKNOWN_LABEL "Unk."
|
||||
|
||||
// Returns true for User protocol beacons
|
||||
bool protocolIsUser() { return (protocolType == ProtocolType::USER); };
|
||||
// Returns true for National Location protocol beacons
|
||||
bool protocolIsNational() { return (protocolType == ProtocolType::NATIONAL_LOCATION); };
|
||||
// Returns true for Standard Location protocol beacons
|
||||
bool protocolIsStandard() { return (protocolType == ProtocolType::STANDARD_LOCATION); };
|
||||
// Returns true for RLS location protocol beacons
|
||||
bool protocolIsRls() { return (protocolType == ProtocolType::RLS_LOCATION); };
|
||||
// Returns true for RLS or ELT Location protocol beacons
|
||||
bool protocolIsRlsOrElt() { return (protocolType == ProtocolType::RLS_LOCATION || protocolType == ProtocolType::ELT_DT_LOCATION); };
|
||||
// Returns true for unknown protocol beacons
|
||||
bool protocolIsUnknown() { return (protocolType == ProtocolType::UNKNOWN); };
|
||||
|
||||
// Returns the protocol name
|
||||
const char* getProtocolName() {
|
||||
switch (protocolType) {
|
||||
case ProtocolType::STANDARD_LOCATION:
|
||||
return "Standard";
|
||||
case ProtocolType::NATIONAL_LOCATION:
|
||||
return "National";
|
||||
case ProtocolType::RLS_LOCATION:
|
||||
return "RLS";
|
||||
case ProtocolType::ELT_DT_LOCATION:
|
||||
return "ELT(DT)";
|
||||
case ProtocolType::USER:
|
||||
return longFrame ? "User Location" : "User";
|
||||
case ProtocolType::SPARE:
|
||||
return "Spare";
|
||||
default:
|
||||
return UNKNOWN_LABEL;
|
||||
}
|
||||
}
|
||||
|
||||
// Get protocol type for this beacon
|
||||
ProtocolType getProtocolType() {
|
||||
switch (protocol) {
|
||||
case Protocol::USER_EPIRB_MARITIME:
|
||||
case Protocol::USER_EPIRB_RADIO:
|
||||
case Protocol::USER_ELT:
|
||||
case Protocol::USER_SERIAL:
|
||||
case Protocol::USER_TEST:
|
||||
case Protocol::USER_ORB:
|
||||
case Protocol::USER_NAT:
|
||||
case Protocol::USER_2G:
|
||||
return ProtocolType::USER;
|
||||
case Protocol::STD_EPIRB:
|
||||
case Protocol::STD_ELT_24:
|
||||
case Protocol::STD_ELT_SERIAL:
|
||||
case Protocol::STD_ELT_AIRCRAFT:
|
||||
case Protocol::STD_PLB_SERIAL:
|
||||
case Protocol::STD_SHIP:
|
||||
case Protocol::STD_TEST:
|
||||
return ProtocolType::STANDARD_LOCATION;
|
||||
case Protocol::NAT_ELT:
|
||||
case Protocol::NAT_EPIRB:
|
||||
case Protocol::NAT_PLB:
|
||||
case Protocol::NAT_TEST:
|
||||
return ProtocolType::NATIONAL_LOCATION;
|
||||
case Protocol::RLS:
|
||||
return ProtocolType::RLS_LOCATION;
|
||||
case Protocol::ELT_DT:
|
||||
return ProtocolType::ELT_DT_LOCATION;
|
||||
case Protocol::SPARE:
|
||||
return ProtocolType::SPARE;
|
||||
case Protocol::UNKNOWN:
|
||||
default:
|
||||
return ProtocolType::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// True for long frames, fals for short grames
|
||||
bool longFrame{true};
|
||||
// True if protocol flag is set for this frame
|
||||
bool protocolFlag{false};
|
||||
// Frame mode (Test or Real)
|
||||
FrameMode frameMode{FrameMode::UNKNOWN};
|
||||
// Main location device
|
||||
MainLocatingDevice mainLocatingDevice{MainLocatingDevice::UNDEFINED};
|
||||
// Axiliary location device
|
||||
AuxLocatingDevice auxLocatingDevice{AuxLocatingDevice::UNDEFINED};
|
||||
// Protocol code for this beacon
|
||||
long protocolCode{0};
|
||||
// Protocol of this beacon
|
||||
Protocol protocol{Protocol::UNKNOWN};
|
||||
// Protocol type of this beacon
|
||||
ProtocolType protocolType{ProtocolType::UNKNOWN};
|
||||
// Country of this beacon
|
||||
Country country{};
|
||||
// Frame data
|
||||
uint8_t frame[BEACON_DATA_SIZE];
|
||||
// Location of this beacon
|
||||
Location location{};
|
||||
// HexId of this beacon
|
||||
uint64_t identifier{0};
|
||||
// Date of reception of this beacon
|
||||
rtc::RTC date{};
|
||||
// Trus if this beacon has additional data
|
||||
bool hasAdditionalData{false};
|
||||
// The additional data tring
|
||||
char additionalData[48]{0};
|
||||
// True if this beacon has a serial number
|
||||
bool hasSerialNumber{false};
|
||||
// The serial number string
|
||||
char serialNumber[32]{0};
|
||||
// Beacon's Hex ID string
|
||||
char hexId[32]{0};
|
||||
// BCH1 code value
|
||||
uint32_t bch1{};
|
||||
// BCH1 calculated value
|
||||
uint32_t computedBch1{};
|
||||
// True if data has been corrected to match BCH1 code
|
||||
bool bch1Corrected{false};
|
||||
// True if this beacon has a BCH2 correction code
|
||||
bool hasBch2{false};
|
||||
// BCH2 code value
|
||||
uint32_t bch2{};
|
||||
// BCH2 calculated value
|
||||
uint32_t computedBch2{};
|
||||
// True if data has been corrected to match BCH2 code
|
||||
bool bch2Corrected{false};
|
||||
// True if this beacon is empty (not initialized)
|
||||
bool isEmpty{true};
|
||||
// True if thsi beacon contains emergency data
|
||||
bool hasEmergency{false};
|
||||
// True if emenrgency data is automatic for this beacon
|
||||
bool isAutoamticEmergency{false};
|
||||
// True if this beacon is a Maritime beacon
|
||||
bool isMaritime{false};
|
||||
// Emergency type string
|
||||
char emergencyType[32]{0};
|
||||
|
||||
/**
|
||||
* Returns the requested bits in this beacon's frame
|
||||
*/
|
||||
uint64_t getBits(int startBit, int endBit) {
|
||||
uint64_t result = 0;
|
||||
startBit--;
|
||||
int numBits = endBit - startBit;
|
||||
const uint8_t* pData = &(frame[startBit / 8]);
|
||||
uint8_t b = *pData;
|
||||
int bitOffset = 7 - (startBit % 8);
|
||||
for (int i = 0; i < numBits; ++i) {
|
||||
result <<= 1;
|
||||
result |= ((b >> bitOffset) & 0x01);
|
||||
if (--bitOffset < 0) {
|
||||
b = *(++pData);
|
||||
bitOffset = 7;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a BCH control code for the given bits and poly
|
||||
*/
|
||||
uint64_t computeBCH(int startBit, int endBit, unsigned long poly, int polyLength) {
|
||||
int dataLength = endBit - startBit + 1;
|
||||
int totalLength = dataLength + polyLength - 1;
|
||||
uint64_t result = getBits(startBit, startBit + polyLength - 1);
|
||||
for (int i = polyLength; i <= totalLength; i++) {
|
||||
bool firstBit = result >> (polyLength - 1);
|
||||
if (firstBit) result = result ^ poly;
|
||||
if (i < totalLength) {
|
||||
result = result << 1;
|
||||
if (i < dataLength) result |= getBits(startBit + i, startBit + i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute BCH1 code
|
||||
*/
|
||||
uint64_t computeBCH1() { return computeBCH(25, 85, BCH_21_POLYNOMIAL, BCH_21_POLY_LENGTH); }
|
||||
/**
|
||||
* Compute BCH2 code
|
||||
*/
|
||||
uint64_t computeBCH2() { return computeBCH(107, 132, BCH_12_POLYNOMIAL, BCH_12_POLY_LENGTH); }
|
||||
|
||||
/**
|
||||
* Convert this frame's data to an hex string
|
||||
*/
|
||||
static size_t toHexString(char* buffer, uint8_t* frame, bool withSpace, int start, int end) {
|
||||
size_t result = 0;
|
||||
for (uint8_t i = start; i < end; i++) {
|
||||
if (withSpace && i > start) buffer[result++] = ' ';
|
||||
result += sprintf((buffer + result), "%02X", frame[i]);
|
||||
}
|
||||
buffer[result] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
Beacon() {}
|
||||
Beacon(const Beacon& other) = delete;
|
||||
Beacon& operator=(const Beacon& other) = delete;
|
||||
|
||||
/**
|
||||
* Set the data for this Beacon and parse it
|
||||
*/
|
||||
void setFrame(const uint8_t* frameBuffer) {
|
||||
frameMode = FrameMode::UNKNOWN;
|
||||
mainLocatingDevice = MainLocatingDevice::UNDEFINED;
|
||||
auxLocatingDevice = AuxLocatingDevice::UNDEFINED;
|
||||
protocolCode = 0;
|
||||
protocol = Protocol::UNKNOWN;
|
||||
protocolType = ProtocolType::UNKNOWN;
|
||||
country.code = 0;
|
||||
country.alphaCode[0] = 0;
|
||||
country.shortName[0] = 0;
|
||||
location.clear();
|
||||
identifier = 0;
|
||||
hasAdditionalData = false;
|
||||
additionalData[0] = 0;
|
||||
hasSerialNumber = false;
|
||||
serialNumber[0] = 0;
|
||||
hexId[0] = 0;
|
||||
bch1 = 0;
|
||||
computedBch1 = 0;
|
||||
hasBch2 = false;
|
||||
bch1Corrected = false;
|
||||
bch2 = 0;
|
||||
computedBch2 = 0;
|
||||
bch2Corrected = false;
|
||||
isEmpty = true;
|
||||
hasEmergency = false;
|
||||
isAutoamticEmergency = false;
|
||||
isMaritime = false;
|
||||
emergencyType[0] = 0;
|
||||
std::memcpy(frame, (const void*)frameBuffer, BEACON_DATA_SIZE);
|
||||
parseFrame();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flip the specified bit in this beacon's data
|
||||
*/
|
||||
void flipBit(int bit) {
|
||||
int byteIdx = (bit - 1) / 8;
|
||||
int bitIdx = 7 - ((bit - 1) % 8);
|
||||
frame[byteIdx] ^= (1 << bitIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Single bit error correction to match BCH1 or BCH2 code
|
||||
*/
|
||||
void simpleCorrection(bool isBCH1) {
|
||||
for (int i = (isBCH1 ? 25 : 107); i <= (isBCH1 ? 85 : 132); i++) {
|
||||
flipBit(i);
|
||||
if ((isBCH1 ? (computeBCH1() == bch1) : (computeBCH2() == bch2))) {
|
||||
if (isBCH1) {
|
||||
computedBch1 = bch1;
|
||||
bch1Corrected = true;
|
||||
} else {
|
||||
computedBch2 = bch2;
|
||||
bch2Corrected = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
flipBit(i); // Restore bit value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the string representation of this beacon's type
|
||||
*/
|
||||
const char* getType() {
|
||||
if ((protocol == Protocol::USER_EPIRB_MARITIME) || (protocol == Protocol::USER_EPIRB_RADIO) || (protocol == Protocol::STD_EPIRB) || (protocol == Protocol::STD_EPIRB_SERIAL) || (protocol == Protocol::NAT_EPIRB)) return "EPIRB";
|
||||
if ((protocol == Protocol::USER_ELT) || (protocol == Protocol::STD_ELT_24) || (protocol == Protocol::STD_ELT_SERIAL) || (protocol == Protocol::STD_ELT_AIRCRAFT) || (protocol == Protocol::NAT_ELT) || (protocol == Protocol::ELT_DT)) return "ELT";
|
||||
if ((protocol == Protocol::STD_PLB_SERIAL) || (protocol == Protocol::NAT_PLB)) return "PLB";
|
||||
if ((protocol == Protocol::USER_TEST) || (protocol == Protocol::STD_TEST) || (protocol == Protocol::NAT_TEST)) return "TEST";
|
||||
if (protocol == Protocol::USER_SERIAL) return "SRIAL";
|
||||
if (protocol == Protocol::USER_ORB) return "ORB";
|
||||
if (protocol == Protocol::USER_NAT) return "NAT";
|
||||
if (protocol == Protocol::USER_2G) return "2G";
|
||||
if (protocol == Protocol::STD_SHIP) return "SHIP";
|
||||
if (protocol == Protocol::RLS) return "RLS";
|
||||
if (protocol == Protocol::SPARE) return "SPARE";
|
||||
return UNKNOWN_LABEL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this beacon has a main location device
|
||||
*/
|
||||
bool hasMainLocatingDevice() { return (mainLocatingDevice != MainLocatingDevice::UNDEFINED); }
|
||||
|
||||
/**
|
||||
* Returns this beacon's main location device string representation
|
||||
*/
|
||||
const char* getMainLocatingDeviceName() {
|
||||
if (mainLocatingDevice == MainLocatingDevice::EXTERNAL_NAV) return "Exernal";
|
||||
if (mainLocatingDevice == MainLocatingDevice::INTERNAL_NAV) return "Internal";
|
||||
return UNKNOWN_LABEL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this beacon has a main location device
|
||||
*/
|
||||
bool hasAuxLocatingDevice() { return (auxLocatingDevice != AuxLocatingDevice::UNDEFINED); }
|
||||
|
||||
/**
|
||||
* Returns true if this beacon has a main location device
|
||||
*/
|
||||
const char* getAuxLocatingDeviceName() {
|
||||
return ResourceManager::get_beacon_resource(AUX_DEVICE_RESOURCE_START + ((int)auxLocatingDevice));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the serial number for this beacon
|
||||
*/
|
||||
void setSerialNumber(uint32_t serial) {
|
||||
sprintf(serialNumber, "%ld (0x%08lX)", serial, serial);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if BCH1 code is valid
|
||||
*/
|
||||
bool isBch1Valid() { return (bch1 == computedBch1); }
|
||||
|
||||
/**
|
||||
* Returns true if BCH2 code is valid
|
||||
*/
|
||||
|
||||
bool isBch2Valid() { return (bch2 == computedBch2); }
|
||||
|
||||
/**
|
||||
* Returns true if frame is valid
|
||||
*/
|
||||
bool isFrameValid();
|
||||
|
||||
/**
|
||||
* Returns true if frame is orbito
|
||||
*/
|
||||
bool isOrbito() { return (protocol == Protocol::USER_ORB); }
|
||||
|
||||
/**
|
||||
* Write the hex preresentation of this frame in the provided buffer
|
||||
*/
|
||||
size_t hexString(char* buffer, bool withHeader) {
|
||||
return toHexString(buffer, frame, false, (withHeader ? 0 : 3), (longFrame ? 18 : 14));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the short ID for this beacon
|
||||
*/
|
||||
std::string shortId();
|
||||
|
||||
/**
|
||||
* Returns the string representation of the status of this frame
|
||||
*/
|
||||
std::string getSatus() {
|
||||
if (isFrameValid())
|
||||
return "OK";
|
||||
else
|
||||
return "KO";
|
||||
}
|
||||
|
||||
/**
|
||||
* Format this beacon's time
|
||||
*/
|
||||
size_t formatTime(char* buffer);
|
||||
|
||||
/**
|
||||
* Format this beacon's summary
|
||||
*/
|
||||
size_t formatSummary(char* buffer, bool with_time);
|
||||
|
||||
private:
|
||||
/* Baudot code matrix */
|
||||
static constexpr char BAUDOT_CODE[64] = {' ', '5', ' ', '9', ' ', ' ', ' ', ' ', ' ', ' ', '4', ' ', '8', '0', ' ', ' ',
|
||||
'3', ' ', ' ', ' ', ' ', '6', ' ', '/', '-', '2', ' ', ' ', '7', '1', ' ', ' ',
|
||||
' ', 'T', ' ', 'O', ' ', 'H', 'N', 'M', ' ', 'L', 'R', 'G', 'I', 'P', 'C', 'V',
|
||||
'E', 'Z', 'D', 'B', 'S', 'Y', 'F', 'X', 'A', 'W', 'J', ' ', 'U', 'Q', 'K', '\0'};
|
||||
|
||||
/**
|
||||
* Parse this beacon's protocol
|
||||
*/
|
||||
void parseProtocol() {
|
||||
protocolFlag = getBits(26, 26);
|
||||
if (protocolFlag)
|
||||
protocolCode = getBits(37, 39);
|
||||
else
|
||||
protocolCode = getBits(37, 40);
|
||||
|
||||
if (!longFrame || protocolFlag == 1) {
|
||||
switch (protocolCode) {
|
||||
case 0b000:
|
||||
protocol = Protocol::USER_ORB;
|
||||
break;
|
||||
case 0b001:
|
||||
protocol = Protocol::USER_ELT;
|
||||
break;
|
||||
case 0b010:
|
||||
protocol = Protocol::USER_EPIRB_MARITIME;
|
||||
isMaritime = true;
|
||||
break;
|
||||
case 0b011:
|
||||
protocol = Protocol::USER_SERIAL;
|
||||
break;
|
||||
case 0b100:
|
||||
protocol = Protocol::USER_NAT;
|
||||
break;
|
||||
case 0b101:
|
||||
protocol = Protocol::USER_2G;
|
||||
break;
|
||||
case 0b110:
|
||||
protocol = Protocol::USER_EPIRB_RADIO;
|
||||
isMaritime = true;
|
||||
break;
|
||||
case 0b111:
|
||||
protocol = Protocol::USER_TEST;
|
||||
break;
|
||||
default:
|
||||
protocol = Protocol::UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
switch (protocolCode) {
|
||||
case 0b0010:
|
||||
protocol = Protocol::STD_EPIRB;
|
||||
break;
|
||||
case 0b0011:
|
||||
protocol = Protocol::STD_ELT_24;
|
||||
break;
|
||||
case 0b0100:
|
||||
protocol = Protocol::STD_ELT_SERIAL;
|
||||
break;
|
||||
case 0b0101:
|
||||
protocol = Protocol::STD_ELT_AIRCRAFT;
|
||||
break;
|
||||
case 0b0110:
|
||||
protocol = Protocol::STD_EPIRB_SERIAL;
|
||||
break;
|
||||
case 0b0111:
|
||||
protocol = Protocol::STD_PLB_SERIAL;
|
||||
break;
|
||||
case 0b1000:
|
||||
protocol = Protocol::NAT_ELT;
|
||||
break;
|
||||
case 0b1001:
|
||||
protocol = Protocol::ELT_DT;
|
||||
break;
|
||||
case 0b1010:
|
||||
protocol = Protocol::NAT_EPIRB;
|
||||
break;
|
||||
case 0b1011:
|
||||
protocol = Protocol::NAT_PLB;
|
||||
break;
|
||||
case 0b1100:
|
||||
protocol = Protocol::STD_SHIP;
|
||||
break;
|
||||
case 0b1101:
|
||||
protocol = Protocol::RLS;
|
||||
break;
|
||||
case 0b1110:
|
||||
protocol = Protocol::STD_TEST;
|
||||
break;
|
||||
case 0b1111:
|
||||
protocol = Protocol::NAT_TEST;
|
||||
break;
|
||||
default:
|
||||
protocol = Protocol::UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse this beacon's additional data
|
||||
*/
|
||||
void parseAdditionalData() {
|
||||
hasAdditionalData = false;
|
||||
hasSerialNumber = false;
|
||||
if (protocolFlag) {
|
||||
if (protocolCode == 0b011) {
|
||||
uint8_t serialUserProtocol = getBits(40, 42);
|
||||
hasAdditionalData = true;
|
||||
switch (serialUserProtocol) {
|
||||
case 0b100:
|
||||
case 0b010:
|
||||
isMaritime = true;
|
||||
// fallthrough
|
||||
case 0b000:
|
||||
case 0b001:
|
||||
case 0b011:
|
||||
case 0b110:
|
||||
strcpy(additionalData, ResourceManager::get_beacon_resource(ADDITIONAL_DATA_RESOURCE_START + serialUserProtocol));
|
||||
break;
|
||||
default:
|
||||
hasAdditionalData = false;
|
||||
}
|
||||
hasSerialNumber = true;
|
||||
setSerialNumber((serialUserProtocol == 0b011) ? getBits(44, 67) : (serialUserProtocol == 0b001) ? getBits(62, 73)
|
||||
: getBits(44, 63));
|
||||
}
|
||||
if (!longFrame) {
|
||||
hasEmergency = getBits(107, 107);
|
||||
if (hasEmergency) {
|
||||
isAutoamticEmergency = getBits(108, 108);
|
||||
if (isMaritime) {
|
||||
uint8_t emmergency = getBits(109, 112);
|
||||
switch (emmergency) {
|
||||
case 0b0001:
|
||||
case 0b0010:
|
||||
case 0b0011:
|
||||
case 0b0100:
|
||||
case 0b0101:
|
||||
case 0b0110:
|
||||
case 0b0111:
|
||||
case 0b1000:
|
||||
strcpy(emergencyType, ResourceManager::get_beacon_resource(EMERGENCY_RESOURCE_START + emmergency));
|
||||
break;
|
||||
default:
|
||||
strcpy(emergencyType, ResourceManager::get_beacon_resource(EMERGENCY_RESOURCE_START));
|
||||
}
|
||||
} else {
|
||||
bool fire = getBits(109, 109);
|
||||
bool med = getBits(110, 110);
|
||||
bool disabled = getBits(111, 111);
|
||||
char* emmergency_pointer = emergencyType;
|
||||
if (fire) emmergency_pointer += sprintf(emmergency_pointer, "%s", ResourceManager::get_beacon_resource(EMERGENCY_OTHER_RESOURCE_START));
|
||||
if (med) {
|
||||
if (fire) emmergency_pointer += sprintf(emmergency_pointer, "%c", '+');
|
||||
emmergency_pointer += sprintf(emmergency_pointer, "%s", ResourceManager::get_beacon_resource(EMERGENCY_OTHER_RESOURCE_START + 1));
|
||||
}
|
||||
if (disabled) {
|
||||
if (fire || med) emmergency_pointer += sprintf(emmergency_pointer, "%c", '+');
|
||||
sprintf(emmergency_pointer, "%s", ResourceManager::get_beacon_resource(EMERGENCY_OTHER_RESOURCE_START + 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (longFrame) {
|
||||
switch (protocolCode) {
|
||||
case 0b0010:
|
||||
hasSerialNumber = true;
|
||||
setSerialNumber(getBits(61, 64));
|
||||
break;
|
||||
case 0b1100: {
|
||||
hasAdditionalData = true;
|
||||
uint32_t mmsi = getBits(41, 60);
|
||||
sprintf(additionalData, ResourceManager::get_beacon_resource(LONG_ADDITIONAL_DATA_RESOURCE_START), mmsi, mmsi);
|
||||
} break;
|
||||
case 0b0011: {
|
||||
hasAdditionalData = true;
|
||||
hasSerialNumber = true;
|
||||
setSerialNumber(getBits(41, 64));
|
||||
strcpy(additionalData, ResourceManager::get_beacon_resource(LONG_ADDITIONAL_DATA_RESOURCE_START + 1));
|
||||
} break;
|
||||
case 0b0100:
|
||||
case 0b0110:
|
||||
case 0b0111: {
|
||||
hasAdditionalData = true;
|
||||
hasSerialNumber = true;
|
||||
uint32_t csTaNumber = getBits(41, 50);
|
||||
sprintf(additionalData, ResourceManager::get_beacon_resource(LONG_ADDITIONAL_DATA_RESOURCE_START + 2), csTaNumber);
|
||||
setSerialNumber(getBits(51, 64));
|
||||
} break;
|
||||
case 0b0101: {
|
||||
hasAdditionalData = true;
|
||||
hasSerialNumber = true;
|
||||
uint32_t data = getBits(41, 45);
|
||||
char char1 = BAUDOT_CODE[data + 32];
|
||||
data = getBits(46, 50);
|
||||
char char2 = BAUDOT_CODE[data + 32];
|
||||
data = getBits(51, 55);
|
||||
char char3 = BAUDOT_CODE[data + 32];
|
||||
sprintf(additionalData, ResourceManager::get_beacon_resource(LONG_ADDITIONAL_DATA_RESOURCE_START + 3), char1, char2, char3);
|
||||
setSerialNumber(getBits(56, 64));
|
||||
} break;
|
||||
case 0b1000:
|
||||
case 0b1010:
|
||||
case 0b1011:
|
||||
case 0b1111: {
|
||||
hasSerialNumber = true;
|
||||
hasAdditionalData = true;
|
||||
setSerialNumber(getBits(41, 58));
|
||||
uint32_t natNum = getBits(127, 132);
|
||||
sprintf(additionalData, ResourceManager::get_beacon_resource(LONG_ADDITIONAL_DATA_RESOURCE_START + 4), natNum);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse this beacon's locating device
|
||||
*/
|
||||
void parseLocatingDevices() {
|
||||
bool mainLoc;
|
||||
if (protocolIsStandard() || protocolIsNational()) {
|
||||
mainLoc = getBits(111, 111);
|
||||
mainLocatingDevice = mainLoc ? MainLocatingDevice::INTERNAL_NAV : MainLocatingDevice::EXTERNAL_NAV;
|
||||
} else if (protocolIsUser() || protocolIsRls()) {
|
||||
mainLoc = getBits(107, 107);
|
||||
mainLocatingDevice = mainLoc ? MainLocatingDevice::INTERNAL_NAV : MainLocatingDevice::EXTERNAL_NAV;
|
||||
}
|
||||
if (protocolIsStandard() || protocolIsNational()) {
|
||||
auxLocatingDevice = getBits(112, 112) ? AuxLocatingDevice::MHZ121_5 : AuxLocatingDevice::NONE_OR_OTHER;
|
||||
} else if (protocolIsRls()) {
|
||||
auxLocatingDevice = getBits(108, 108) ? AuxLocatingDevice::MHZ121_5 : AuxLocatingDevice::NONE_OR_OTHER;
|
||||
} else if (protocolIsUser() && protocolCode != 0b100) {
|
||||
uint8_t aux = getBits(84, 85);
|
||||
if (aux == 0b00)
|
||||
auxLocatingDevice = AuxLocatingDevice::NONE;
|
||||
else if (aux == 0b01)
|
||||
auxLocatingDevice = AuxLocatingDevice::MHZ121_5;
|
||||
else if (aux == 0b10)
|
||||
auxLocatingDevice = AuxLocatingDevice::SART;
|
||||
else
|
||||
auxLocatingDevice = AuxLocatingDevice::OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse this beacon's frame data
|
||||
*/
|
||||
void parseFrame() {
|
||||
long latofmin, latofsec, lonofmin, lonofsec;
|
||||
bool latoffset, lonoffset;
|
||||
|
||||
longFrame = getBits(25, 25);
|
||||
parseProtocol();
|
||||
protocolType = getProtocolType();
|
||||
CountryManager::get_country(getBits(27, 36), country);
|
||||
|
||||
if (frame[2] == 0xD0)
|
||||
frameMode = FrameMode::SELF_TEST;
|
||||
else if (frame[2] == 0x2F)
|
||||
frameMode = FrameMode::NORMAL;
|
||||
else
|
||||
frameMode = FrameMode::UNKNOWN;
|
||||
|
||||
if (longFrame) {
|
||||
if (protocolIsUser() && !isOrbito()) {
|
||||
location.latitude.orientation = (frame[13] & 0x10) >> 4;
|
||||
location.latitude.degrees = ((frame[13] & 0x0F) << 3 | (frame[14] & 0xE0) >> 5);
|
||||
location.latitude.minutes = ((frame[14] & 0x1E) >> 1) * 4;
|
||||
location.longitude.orientation = (frame[14] & 0x01);
|
||||
location.longitude.degrees = (frame[15]);
|
||||
location.longitude.minutes = ((frame[16] & 0xF0) >> 4) * 4;
|
||||
} else if (protocolIsNational()) {
|
||||
latoffset = (frame[14] & 0x80) >> 7;
|
||||
location.latitude.orientation = (frame[7] & 0x20) >> 5;
|
||||
location.latitude.degrees = ((frame[7] & 0x1F) << 2 | (frame[8] & 0xC0) >> 6);
|
||||
location.latitude.minutes = ((frame[8] & 0x3E) >> 1) * 2;
|
||||
latofmin = (frame[14] & 0x60) >> 5;
|
||||
latofsec = ((frame[14] & 0x1E) >> 1) * 4;
|
||||
location.latitude.apply_offset(latoffset, latofmin, latofsec);
|
||||
lonoffset = (frame[14] & 0x01);
|
||||
location.longitude.orientation = (frame[8] & 0x01);
|
||||
location.longitude.degrees = (frame[9]);
|
||||
location.longitude.minutes = ((frame[10] & 0xF8) >> 3) * 2;
|
||||
lonofmin = (frame[15] & 0xC0) >> 6;
|
||||
lonofsec = ((frame[15] & 0x3C) >> 2) * 4;
|
||||
location.longitude.apply_offset(lonoffset, lonofmin, lonofsec);
|
||||
} else if (protocolIsStandard()) {
|
||||
latoffset = (frame[14] & 0x80) >> 7;
|
||||
location.latitude.orientation = (frame[8] & 0x80) >> 7;
|
||||
location.latitude.degrees = (frame[8] & 0x7F);
|
||||
location.latitude.minutes = ((frame[9] & 0xC0) >> 6) * 15;
|
||||
latofmin = (frame[14] & 0x7C) >> 2;
|
||||
latofsec = ((frame[14] & 0x03) << 2 | (frame[15] & 0xC0) >> 6) * 4;
|
||||
location.latitude.apply_offset(latoffset, latofmin, latofsec);
|
||||
lonoffset = (frame[15] & 0x20) >> 5;
|
||||
location.longitude.orientation = (frame[9] & 0x20) >> 5;
|
||||
location.longitude.degrees = ((frame[9] & 0x1F) << 3 | (frame[10] & 0xE0) >> 5);
|
||||
location.longitude.minutes = ((frame[10] & 0x18) >> 3) * 15;
|
||||
lonofmin = (frame[15] & 0x1F);
|
||||
lonofsec = ((frame[16] & 0xF0) >> 4) * 4;
|
||||
location.longitude.apply_offset(lonoffset, lonofmin, lonofsec);
|
||||
} else if (protocolIsRlsOrElt()) {
|
||||
latoffset = (frame[14] & 0x20) >> 5;
|
||||
location.latitude.orientation = (frame[8] & 0x20) >> 5;
|
||||
location.latitude.degrees = ((frame[8] & 0x1F) << 2) | ((frame[9] & 0xC0) >> 6);
|
||||
location.latitude.minutes = ((frame[9] & 0x20) >> 5) * 30;
|
||||
latofmin = (frame[14] & 0x1E) >> 1;
|
||||
latofsec = ((frame[14] & 0x01) << 3 | (frame[15] & 0xE0) >> 5) * 4;
|
||||
location.latitude.apply_offset(latoffset, latofmin, latofsec);
|
||||
lonoffset = (frame[15] & 0x10) >> 4;
|
||||
location.longitude.orientation = (frame[9] & 0x10) >> 4;
|
||||
location.longitude.degrees = ((frame[9] & 0x0F) << 4 | (frame[10] & 0xF0) >> 4);
|
||||
location.longitude.minutes = ((frame[10] & 0x08) >> 3) * 30;
|
||||
lonofmin = (frame[15] & 0x0F);
|
||||
lonofsec = ((frame[16] & 0xF0) >> 4) * 4;
|
||||
location.longitude.apply_offset(lonoffset, lonofmin, lonofsec);
|
||||
}
|
||||
}
|
||||
|
||||
parseAdditionalData();
|
||||
parseLocatingDevices();
|
||||
|
||||
identifier = getBits(26, 85);
|
||||
|
||||
uint32_t hexIdHigh = (uint32_t)(identifier >> 32);
|
||||
uint32_t hexIdLow = (uint32_t)identifier;
|
||||
|
||||
if (protocolIsStandard()) {
|
||||
// default value of bits 65 to 74 = 0 111111111 / default value of bits 75 to 85 = 0 1111111111
|
||||
hexIdLow &= 0b11111111'11100000'00000000'00000000;
|
||||
hexIdLow |= 0b00000000'00001111'11111011'11111111;
|
||||
} else if (protocolIsNational()) {
|
||||
// default value of bits 59 to 71 = 0 1111111 00000 / default value of bits 72 to 85 = 0 11111111 00000
|
||||
hexIdLow &= 0b11111000'00000000'00000000'00000000;
|
||||
hexIdLow |= 0b00000011'11111000'00011111'11100000;
|
||||
} else if (protocolIsRlsOrElt()) {
|
||||
// default value of bits 67 to 75 = 0 11111111 / default value of bits 76 to 85 = 0 111111111
|
||||
hexIdLow &= 0b11111111'11111000'00000000'00000000;
|
||||
hexIdLow |= 0b00000000'00000011'11111101'11111111;
|
||||
}
|
||||
std::sprintf(hexId, "%07lX%08lX", hexIdHigh, hexIdLow);
|
||||
|
||||
if (isOrbito() && !longFrame) {
|
||||
isEmpty = true;
|
||||
for (size_t i = 3; i < BEACON_DATA_SIZE; i++) {
|
||||
if (frame[i] != 0) {
|
||||
isEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
isEmpty = false;
|
||||
|
||||
bch1 = getBits(86, 106);
|
||||
computedBch1 = computeBCH1();
|
||||
// Try and correct single bit error on BCH1 (bits 25-85)
|
||||
if (computedBch1 != bch1) {
|
||||
simpleCorrection(true);
|
||||
}
|
||||
|
||||
hasBch2 = longFrame && !isOrbito();
|
||||
if (hasBch2) {
|
||||
bch2 = getBits(133, 144);
|
||||
computedBch2 = computeBCH2();
|
||||
// Try and correct single bit error on BCH2 (bits 107 - 132)
|
||||
if (computedBch2 != bch2) {
|
||||
simpleCorrection(false);
|
||||
}
|
||||
}
|
||||
static bool isCorrecting = false;
|
||||
// If BCH1 or BCH2 has been corrected, we need to parse frame again to update beacon properties
|
||||
if (!isCorrecting && (bch1Corrected || bch2Corrected)) {
|
||||
// Prevent recursion
|
||||
isCorrecting = true;
|
||||
parseFrame();
|
||||
isCorrecting = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace ui::external_app::epirb_rx
|
||||
|
||||
#endif // __BEACON_RX_H__
|
||||
Reference in New Issue
Block a user