Implementation of M17 frame decoder
This commit is contained in:
parent
49bd8ee2f4
commit
5fbd39959c
|
|
@ -43,6 +43,7 @@ openrtx_src = ['openrtx/src/core/state.c',
|
|||
'openrtx/src/protocols/M17/M17Callsign.cpp',
|
||||
'openrtx/src/protocols/M17/M17Modulator.cpp',
|
||||
'openrtx/src/protocols/M17/M17Demodulator.cpp',
|
||||
'openrtx/src/protocols/M17/M17FrameDecoder.cpp',
|
||||
'openrtx/src/protocols/M17/M17Transmitter.cpp',
|
||||
'openrtx/src/protocols/M17/M17LinkSetupFrame.cpp']
|
||||
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ using meta_t = std::array< uint8_t, 14 >; // Data type for LSF metadata fie
|
|||
using payload_t = std::array< uint8_t, 16 >; // Data type for frame payload field
|
||||
using lich_t = std::array< uint8_t, 12 >; // Data type for Golay(24,12) encoded LICH data
|
||||
|
||||
|
||||
static constexpr std::array<uint8_t, 2> LSF_SYNC_WORD = {0x55, 0xF7}; // LSF sync word
|
||||
static constexpr std::array<uint8_t, 2> DATA_SYNC_WORD = {0xFF, 0x5D}; // Stream data sync word
|
||||
static constexpr std::array<uint8_t, 2> LSF_SYNC_WORD = {0x55, 0xF7}; // LSF sync word
|
||||
static constexpr std::array<uint8_t, 2> DATA_SYNC_WORD = {0xFF, 0x5D}; // Stream data sync word
|
||||
static constexpr std::array<uint8_t, 2> PACKET_SYNC_WORD = {0x75, 0xFF}; // Packet data sync word
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2022 by Federico Amedeo Izzo IU2NUO, *
|
||||
* Niccolò Izzo IU2KIN *
|
||||
* Frederik Saraci IU2NRO *
|
||||
* Silvano Seva IU2KWO *
|
||||
* *
|
||||
* 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 3 of the License, 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; if not, see <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef M17FRAMEDECODER_H
|
||||
#define M17FRAMEDECODER_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#error This header is C++ only!
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include "M17LinkSetupFrame.h"
|
||||
#include "M17Viterbi.h"
|
||||
#include "M17StreamFrame.h"
|
||||
|
||||
enum class M17FrameType : uint8_t
|
||||
{
|
||||
PREAMBLE = 0, ///< Frame contains a preamble.
|
||||
LINK_SETUP = 1, ///< Frame is a Link Setup Frame.
|
||||
STREAM = 2, ///< Frame is a stream data frame.
|
||||
PACKET = 3, ///< Frame is a packet data frame.
|
||||
UNKNOWN = 4 ///< Frame is unknown.
|
||||
};
|
||||
|
||||
/**
|
||||
* M17 frame decoder.
|
||||
*/
|
||||
class M17FrameDecoder
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
M17FrameDecoder();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~M17FrameDecoder();
|
||||
|
||||
/**
|
||||
* Clear the internal data structures.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Decode an M17 frame, identifying its type. Frame data must contain the
|
||||
* sync word in the first two bytes.
|
||||
*
|
||||
* @param frame: byte array containg frame data.
|
||||
* @return the type of frame recognized.
|
||||
*/
|
||||
M17FrameType decodeFrame(const std::array< uint8_t, 48 >& frame);
|
||||
|
||||
/**
|
||||
* Get the latest Link Setup Frame decoded. Check of the validity of the
|
||||
* data contained in the LSF is left to application code.
|
||||
*
|
||||
* @return a reference to the latest Link Setup Frame decoded.
|
||||
*/
|
||||
const M17LinkSetupFrame& getLsf()
|
||||
{
|
||||
return lsf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest stream data frame decoded.
|
||||
*
|
||||
* @return a reference to the latest stream data frame decoded.
|
||||
*/
|
||||
const M17StreamFrame& getStreamFrame()
|
||||
{
|
||||
return streamFrame;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Decode Link Setup Frame data and update the internal LSF field with
|
||||
* the new frame data.
|
||||
*
|
||||
* @param data: byte array containg frame data, without sync word.
|
||||
*/
|
||||
void decodeLSF(const std::array< uint8_t, 46 >& data);
|
||||
|
||||
/**
|
||||
* Decode stream data and update the internal LSF field with the new
|
||||
* frame data.
|
||||
*
|
||||
* @param data: byte array containg frame data, without sync word.
|
||||
*/
|
||||
void decodeStream(const std::array< uint8_t, 46 >& data);
|
||||
|
||||
/**
|
||||
* Decode a LICH block.
|
||||
*
|
||||
* @param segment: byte array where to store the decoded Link Setup Frame
|
||||
* segment. The last byte contains the segment number.
|
||||
* @param lich: LICH block to be decoded.
|
||||
* @return true when the LICH block is successfully decoded.
|
||||
*/
|
||||
bool decodeLich(std::array< uint8_t, 6 >& segment, const lich_t& lich);
|
||||
|
||||
|
||||
uint8_t lsfSegmentMap; ///< Bitmap for LSF reassembly from LICH
|
||||
M17LinkSetupFrame lsf; ///< Latest LSF received.
|
||||
M17LinkSetupFrame lsfFromLich; ///< LSF assembled from LICH segments.
|
||||
M17StreamFrame streamFrame; ///< Latest stream dat frame received.
|
||||
M17Viterbi viterbi; ///< Viterbi decoder.
|
||||
};
|
||||
|
||||
#endif /* M17FRAMEDECODER_H */
|
||||
|
|
@ -29,6 +29,8 @@
|
|||
#include <array>
|
||||
#include "M17Datatypes.h"
|
||||
|
||||
class M17FrameDecoder;
|
||||
|
||||
/**
|
||||
* This class describes and handles an M17 Link Setup Frame.
|
||||
* By default the frame contains a broadcast destination address, unless a
|
||||
|
|
@ -157,6 +159,9 @@ private:
|
|||
uint16_t crc; ///< CRC
|
||||
}
|
||||
data; ///< Frame data.
|
||||
|
||||
// Frame decoder class needs to access raw frame data
|
||||
friend class M17FrameDecoder;
|
||||
};
|
||||
|
||||
#endif /* M17_LINKSETUPFRAME_H */
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
#include <string>
|
||||
#include "M17Datatypes.h"
|
||||
|
||||
class M17FrameDecoder;
|
||||
|
||||
/**
|
||||
* This class describes and handles a generic M17 data frame.
|
||||
*/
|
||||
|
|
@ -131,6 +133,9 @@ private:
|
|||
///< Frame data.
|
||||
static constexpr uint16_t EOS_BIT = 0x0080; ///< End Of Stream bit.
|
||||
static constexpr uint16_t FN_MASK = 0x7FFF; ///< Bitmask for frame number.
|
||||
|
||||
// Frame decoder class needs to access raw frame data
|
||||
friend class M17FrameDecoder;
|
||||
};
|
||||
|
||||
#endif /* M17_FRAME_H */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,174 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2022 by Federico Amedeo Izzo IU2NUO, *
|
||||
* Niccolò Izzo IU2KIN *
|
||||
* Frederik Saraci IU2NRO *
|
||||
* Silvano Seva IU2KWO *
|
||||
* *
|
||||
* 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 3 of the License, 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; if not, see <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include <M17/M17Golay.h>
|
||||
#include <M17/M17FrameDecoder.h>
|
||||
#include <M17/M17Interleaver.h>
|
||||
#include <M17/M17Decorrelator.h>
|
||||
#include <M17/M17CodePuncturing.h>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
M17FrameDecoder::M17FrameDecoder() { }
|
||||
|
||||
M17FrameDecoder::~M17FrameDecoder() { }
|
||||
|
||||
void M17FrameDecoder::reset()
|
||||
{
|
||||
lsfSegmentMap = 0;
|
||||
lsf.clear();
|
||||
lsfFromLich.clear();
|
||||
streamFrame.clear();
|
||||
}
|
||||
|
||||
M17FrameType M17FrameDecoder::decodeFrame(const std::array< uint8_t, 48 >& frame)
|
||||
{
|
||||
std::array< uint8_t, 2 > syncWord;
|
||||
std::array< uint8_t, 46 > data;
|
||||
|
||||
std::copy_n(frame.begin(), 2, syncWord.begin());
|
||||
std::copy(frame.begin() + 2, frame.end(), data.begin());
|
||||
|
||||
// Re-correlating data is the same operation as decorrelating
|
||||
decorrelate(data);
|
||||
deinterleave(data);
|
||||
|
||||
// Preamble
|
||||
if((syncWord[0] == 0x77) && (syncWord[1] == 0x77))
|
||||
{
|
||||
return M17FrameType::PREAMBLE;
|
||||
}
|
||||
|
||||
// Link Setup Frame
|
||||
if(syncWord == LSF_SYNC_WORD)
|
||||
{
|
||||
decodeLSF(data);
|
||||
return M17FrameType::LINK_SETUP;
|
||||
}
|
||||
|
||||
// Stream data frame
|
||||
if(syncWord == DATA_SYNC_WORD)
|
||||
{
|
||||
decodeStream(data);
|
||||
return M17FrameType::STREAM;
|
||||
}
|
||||
|
||||
// If we get here, we received an unknown frame
|
||||
return M17FrameType::UNKNOWN;
|
||||
}
|
||||
|
||||
void M17FrameDecoder::decodeLSF(const std::array< uint8_t, 46 >& data)
|
||||
{
|
||||
std::array< uint8_t, sizeof(M17LinkSetupFrame) > tmp;
|
||||
|
||||
viterbi.decodePunctured(data, tmp, LSF_puncture);
|
||||
memcpy(&lsf.data, tmp.data(), tmp.size());
|
||||
}
|
||||
|
||||
void M17FrameDecoder::decodeStream(const std::array< uint8_t, 46 >& data)
|
||||
{
|
||||
// Extract and unpack the LICH segment contained at beginning of frame
|
||||
lich_t lich;
|
||||
std::array < uint8_t, 6 > lsfSegment;
|
||||
|
||||
std::copy_n(data.begin(), lich.size(), lich.begin());
|
||||
bool decodeOk = decodeLich(lsfSegment, lich);
|
||||
|
||||
if(decodeOk)
|
||||
{
|
||||
// Append LICH segment
|
||||
uint8_t segmentNum = lsfSegment[5];
|
||||
uint8_t *ptr = reinterpret_cast < uint8_t * >(&lsfFromLich.data);
|
||||
memcpy(ptr + segmentNum, lsfSegment.data(), 5);
|
||||
|
||||
// Mark this segment as present
|
||||
lsfSegmentMap |= 1 << segmentNum;
|
||||
|
||||
// Check if we have received all the five LICH segments
|
||||
if(lsfSegmentMap == 0x1F)
|
||||
{
|
||||
if(lsfFromLich.valid()) lsf = lsfFromLich;
|
||||
lsfSegmentMap = 0;
|
||||
lsfFromLich.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Extract and decode stream data
|
||||
std::array< uint8_t, 34 > punctured;
|
||||
std::array< uint8_t, sizeof(M17StreamFrame) > tmp;
|
||||
|
||||
auto begin = data.begin();
|
||||
begin += lich.size();
|
||||
std::copy(begin, data.end(), punctured.begin());
|
||||
|
||||
viterbi.decodePunctured(punctured, tmp, Audio_puncture);
|
||||
memcpy(&streamFrame.data, tmp.data(), tmp.size());
|
||||
}
|
||||
|
||||
bool M17FrameDecoder::decodeLich(std::array < uint8_t, 6 >& segment,
|
||||
const lich_t& lich)
|
||||
{
|
||||
/*
|
||||
* Extract and unpack the LICH segment contained in the frame header.
|
||||
* The LICH segment is composed of four blocks of Golay(24,12) encoded data
|
||||
* and carries five bytes of the original Link Setup Frame. The sixth byte
|
||||
* is the segment number, allowing to determine the correct position of the
|
||||
* segment when reassembling the LSF.
|
||||
*
|
||||
* NOTE: LICH data is stored in big-endian format, swap and shift after
|
||||
* memcpy convert it to little-endian.
|
||||
*/
|
||||
|
||||
segment.fill(0x00);
|
||||
|
||||
size_t index = 0;
|
||||
uint32_t block = 0;
|
||||
|
||||
for(size_t i = 0; i < 4; i++)
|
||||
{
|
||||
memcpy(&block, lich.data() + 3*i, 3);
|
||||
block = __builtin_bswap32(block) >> 8;
|
||||
uint16_t decoded = golay24_decode(block);
|
||||
|
||||
// Unrecoverable error, abort decoding
|
||||
if(decoded == 0xFFFF)
|
||||
{
|
||||
segment.fill(0x00);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(i & 1)
|
||||
{
|
||||
segment[index++] |= (decoded >> 8); // upper 4 bits
|
||||
segment[index++] = (decoded & 0xFF); // lower 8 bits
|
||||
}
|
||||
else
|
||||
{
|
||||
segment[index++] |= (decoded >> 4); // upper 8 bits
|
||||
segment[index] = (decoded & 0x0F) << 4; // lower 4 bits
|
||||
}
|
||||
}
|
||||
|
||||
// Last byte of the segment contains the segment number, shift left
|
||||
// by five when packing the LICH.
|
||||
segment[5] >>= 5;
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
Reference in New Issue