diff --git a/meson.build b/meson.build index afbe40b6..17d5b323 100644 --- a/meson.build +++ b/meson.build @@ -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/M17FrameEncoder.cpp', 'openrtx/src/protocols/M17/M17FrameDecoder.cpp', 'openrtx/src/protocols/M17/M17Transmitter.cpp', 'openrtx/src/protocols/M17/M17LinkSetupFrame.cpp'] diff --git a/openrtx/include/protocols/M17/M17Datatypes.h b/openrtx/include/protocols/M17/M17Datatypes.h index 70fec9d3..710fbdc9 100644 --- a/openrtx/include/protocols/M17/M17Datatypes.h +++ b/openrtx/include/protocols/M17/M17Datatypes.h @@ -33,6 +33,7 @@ using call_t = std::array< uint8_t, 6 >; // Data type for encoded callsign using meta_t = std::array< uint8_t, 14 >; // Data type for LSF metadata field 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 +using frame_t = std::array< uint8_t, 48 >; // Data type for a full M17 data frame, including sync word static constexpr std::array LSF_SYNC_WORD = {0x55, 0xF7}; // LSF sync word static constexpr std::array STREAM_SYNC_WORD = {0xFF, 0x5D}; // Stream data sync word diff --git a/openrtx/include/protocols/M17/M17FrameDecoder.h b/openrtx/include/protocols/M17/M17FrameDecoder.h index cdf586a3..f9fbfb4f 100755 --- a/openrtx/include/protocols/M17/M17FrameDecoder.h +++ b/openrtx/include/protocols/M17/M17FrameDecoder.h @@ -69,7 +69,7 @@ public: * @param frame: byte array containg frame data. * @return the type of frame recognized. */ - M17FrameType decodeFrame(const std::array< uint8_t, 48 >& frame); + M17FrameType decodeFrame(const frame_t& frame); /** * Get the latest Link Setup Frame decoded. Check of the validity of the diff --git a/openrtx/include/protocols/M17/M17FrameEncoder.h b/openrtx/include/protocols/M17/M17FrameEncoder.h new file mode 100644 index 00000000..80436f15 --- /dev/null +++ b/openrtx/include/protocols/M17/M17FrameEncoder.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * 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 * + ***************************************************************************/ + +#ifndef M17FRAMEENCODER_H +#define M17FRAMEENCODER_H + +#ifndef __cplusplus +#error This header is C++ only! +#endif + +#include +#include +#include "M17ConvolutionalEncoder.h" +#include "M17LinkSetupFrame.h" +#include "M17StreamFrame.h" + +/** + * M17 frame encoder. + */ +class M17FrameEncoder +{ +public: + + /** + * Constructor. + */ + M17FrameEncoder(); + + /** + * Destructor. + */ + ~M17FrameEncoder(); + + /** + * Clear the internal data structures, reset the counter for frame sequence + * number in stream data frames and reset the counter for LICH segment + * sequence. + */ + void reset(); + + /** + * Encode a Link Setup Frame into a frame ready for transmission, prepended + * with the corresponding sync word. Link Setup data is also copied to an + * internal data structure and used to generate the LICH segments to be + * placed in each stream frame. + * + * @param lsf: Link Setup Frame to be encoded. + * @param output: destination buffer for the encoded data. + */ + void encodeLsf(M17LinkSetupFrame& lsf, frame_t& output); + + /** + * Prepare and encode a stream data frame into a frame ready for + * transmission, prepended with the corresponding sync word. The frame + * sequence number is incremented by one on each function call and cleared + * when the reset() function is called. The LICH segment field is filled + * with data obtained from the latest Link Setup Frame encoded. + * + * @param payload: payload data. + * @param output: destination buffer for the encoded data. + * @param isLast: if true, current frame is marked as the last one to be + * transmitted. + * @return the frame sequence number. + */ + uint16_t encodeStreamFrame(const payload_t& payload, frame_t& output, + const bool isLast = false); +private: + + M17ConvolutionalEncoder encoder; ///< Convolutional encoder. + std::array< lich_t, 6 > lichSegments; ///< Encoded LSF chunks for LICH generation. + uint8_t currentLich; ///< Index of current LSF chunk. + uint16_t streamFrameNumber; ///< Current frame number. +}; + +#endif /* M17FRAMEENCODER_H */ diff --git a/openrtx/src/protocols/M17/M17FrameDecoder.cpp b/openrtx/src/protocols/M17/M17FrameDecoder.cpp index 915a78fd..946e26f5 100644 --- a/openrtx/src/protocols/M17/M17FrameDecoder.cpp +++ b/openrtx/src/protocols/M17/M17FrameDecoder.cpp @@ -38,7 +38,7 @@ void M17FrameDecoder::reset() streamFrame.clear(); } -M17FrameType M17FrameDecoder::decodeFrame(const std::array< uint8_t, 48 >& frame) +M17FrameType M17FrameDecoder::decodeFrame(const frame_t& frame) { std::array< uint8_t, 2 > syncWord; std::array< uint8_t, 46 > data; diff --git a/openrtx/src/protocols/M17/M17FrameEncoder.cpp b/openrtx/src/protocols/M17/M17FrameEncoder.cpp new file mode 100644 index 00000000..97e79437 --- /dev/null +++ b/openrtx/src/protocols/M17/M17FrameEncoder.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** + * 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 * + ***************************************************************************/ + +#include +#include +#include +#include + +M17FrameEncoder::M17FrameEncoder() : currentLich(0), streamFrameNumber(0) +{ + reset(); +} + +M17FrameEncoder::~M17FrameEncoder() +{ + +} + +void M17FrameEncoder::reset() +{ + // Clear counters + currentLich = 0; + streamFrameNumber = 0; + + // Clear all the LICH segments + for(auto& segment : lichSegments) + { + segment.fill(0x00); + } +} + +void M17FrameEncoder::encodeLsf(M17LinkSetupFrame& lsf, frame_t& output) +{ + // Ensure the LSF to be encoded has a valid CRC field + lsf.updateCrc(); + + // Generate the Golay(24,12) LICH segments + for(size_t i = 0; i < lichSegments.size(); i++) + { + lichSegments[i] = lsf.generateLichSegment(i); + } + + // Encode the LSF, then puncture and decorrelate its data + std::array encoded; + encoder.reset(); + encoder.encode(lsf.getData(), encoded.data(), sizeof(M17LinkSetupFrame)); + encoded[60] = encoder.flush(); + + std::array punctured; + puncture(encoded, punctured, LSF_PUNCTURE); + interleave(punctured); + decorrelate(punctured); + + // Copy data to output buffer, prepended with sync word. + auto it = std::copy(LSF_SYNC_WORD.begin(), LSF_SYNC_WORD.end(), + output.begin()); + std::copy(punctured.begin(), punctured.end(), it); +} + +uint16_t M17FrameEncoder::encodeStreamFrame(const payload_t& payload, + frame_t& output, const bool isLast) +{ + M17StreamFrame streamFrame; + + streamFrame.setFrameNumber(streamFrameNumber); + streamFrameNumber = (streamFrameNumber + 1) & 0x07FF; + if(isLast) streamFrame.lastFrame(); + std::copy(payload.begin(), payload.end(), streamFrame.payload().begin()); + + // Encode frame + std::array encoded; + encoder.reset(); + encoder.encode(streamFrame.getData(), encoded.data(), sizeof(M17StreamFrame)); + encoded[36] = encoder.flush(); + + std::array punctured; + puncture(encoded, punctured, DATA_PUNCTURE); + + // Add LICH segment to coded data + std::array frame; + auto it = std::copy(lichSegments[currentLich].begin(), + lichSegments[currentLich].end(), + frame.begin()); + std::copy(punctured.begin(), punctured.end(), it); + + // Increment LICH counter after copy + currentLich = (currentLich + 1) % lichSegments.size(); + + interleave(frame); + decorrelate(frame); + + // Copy data to output buffer, prepended with sync word. + auto oIt = std::copy(STREAM_SYNC_WORD.begin(), STREAM_SYNC_WORD.end(), + output.begin()); + std::copy(frame.begin(), frame.end(), oIt); + + return streamFrame.getFrameNumber(); +}