diff --git a/openrtx/include/protocols/M17/M17Transmitter.h b/openrtx/include/protocols/M17/M17Transmitter.h new file mode 100644 index 00000000..462f89f1 --- /dev/null +++ b/openrtx/include/protocols/M17/M17Transmitter.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2021 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 M17TRANSMITTER_H +#define M17TRANSMITTER_H + +#ifndef __cplusplus +#error This header is C++ only! +#endif + +#include +#include +#include "M17ConvolutionalEncoder.h" +#include "M17LinkSetupFrame.h" +#include "M17Frame.h" +#include "M17Modulator.h" + +/** + * M17 transmitter. + */ +class M17Transmitter +{ +public: + + /** + * Constructor. + * + * @param modulator: reference to M17 4FSK modulator driver. + */ + M17Transmitter(M17Modulator& modulator); + + /** + * Destructor. + */ + ~M17Transmitter(); + + /** + * Start a new data stream with broadcast destination callsign. + * + * @param src: source callsign. + */ + void start(const std::string& src); + + /** + * Start a new data stream with given destination callsign. If destination + * callsing is empty, the stream falls back to broadcast transmission. + * + * @param src: source callsign. + * @param dst: destination callsign. + */ + void start(const std::string& src, const std::string& dst); + + /** + * Send a block of data. + * + * @param payload: payload data. + * @param isLast: if true, current frame is marked as the last one to be + * transmitted. + */ + void send(const payload_t& payload, const bool isLast = false); + +private: + + M17ConvolutionalEncoder encoder; ///< Convolutional encoder. + M17LinkSetupFrame lsf; ///< Link Setup Frame handler. + M17Frame dataFrame; ///< Data frame Handler. + M17Modulator& modulator; ///< 4FSK modulator. + std::array< lich_t, 6 > lichSegments; ///< Encoded LSF chunks for LICH generation. + uint8_t currentLich; ///< Index of current LSF chunk. + uint16_t frameNumber; ///< Current frame number. +}; + +#endif /* M17TRANSMITTER_H */ diff --git a/openrtx/src/protocols/M17/M17Transmitter.cpp b/openrtx/src/protocols/M17/M17Transmitter.cpp new file mode 100644 index 00000000..e6dceb0b --- /dev/null +++ b/openrtx/src/protocols/M17/M17Transmitter.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + * Copyright (C) 2021 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 + +static constexpr std::array LSF_SYNC_WORD = {0x55, 0xF7}; +static constexpr std::array DATA_SYNC_WORD = {0xFF, 0x5D}; + +M17Transmitter::M17Transmitter(M17Modulator& modulator) : modulator(modulator), + currentLich(0), frameNumber(0) +{ + +} + +M17Transmitter::~M17Transmitter() +{ + +} + +void M17Transmitter::start(const std::string& src) +{ + // Just call start() with an empty string for destination callsign. + std::string empty; + start(src, empty); +} + +void M17Transmitter::start(const std::string& src, const std::string& dst) +{ + // Reset LICH and frame counters + currentLich = 0; + frameNumber = 0; + + // Fill the Link Setup Frame + lsf.clear(); + lsf.setSource(src); + if(!dst.empty()) lsf.setDestination(dst); + + streamType_t type; + type.stream = 1; // Stream + type.dataType = 2; // Voice data + type.CAN = 0xA; // Channel access number + + lsf.setType(type); + 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(lsf_t)); + encoded[60] = encoder.flush(); + + std::array punctured; + puncture(encoded, punctured, LSF_puncture); + interleave(punctured); + decorrelate(punctured); + + // Send preamble + std::array preamble_sync; + std::array preamble_bytes; + preamble_sync.fill(0x77); + preamble_bytes.fill(0x77); + modulator.send(preamble_sync, preamble_bytes); + + // Send LSF + modulator.send(LSF_SYNC_WORD, punctured); +} + +void M17Transmitter::send(const payload_t& payload, const bool isLast) +{ + dataFrame.clear(); + dataFrame.setFrameNumber(frameNumber); + frameNumber = (frameNumber + 1) & 0x07FF; + if(isLast) dataFrame.lastFrame(); + std::copy(payload.begin(), payload.end(), dataFrame.payload().begin()); + + // Encode frame + std::array encoded; + encoder.reset(); + encoder.encode(&dataFrame.getData(), encoded.data(), sizeof(dataFrame_t)); + encoded[36] = encoder.flush(); + + std::array punctured; + puncture(encoded, punctured, Audio_puncture); + + // Add LICH segment to coded data and send + 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); + modulator.send(DATA_SYNC_WORD, frame); +}