mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-07-26 10:38:52 +00:00
c34a6940ca
* 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
856 lines
33 KiB
C++
856 lines
33 KiB
C++
/*
|
|
* 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__
|