kopia lustrzana https://github.com/OpenRTX/OpenRTX
Reorganized code for M17 transmission: removed M17Transmitter class, use M17FrameEncoder to assemble LSF and data frames, modified API of M17Modulator class.
rodzic
48cd81ee7c
commit
a22aceb576
|
@ -57,7 +57,6 @@ openrtx_src = ['openrtx/src/core/state.c',
|
|||
'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']
|
||||
|
||||
openrtx_inc = ['openrtx/include',
|
||||
|
|
|
@ -62,31 +62,32 @@ public:
|
|||
*/
|
||||
void terminate();
|
||||
|
||||
/**
|
||||
* Start baseband transmission and send an 80ms preamble.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Generate and transmit the baseband signal obtained by 4FSK modulation of
|
||||
* a given block of data. When called for the first time, this function
|
||||
* starts baseband transmission.
|
||||
* a given block of data.
|
||||
*
|
||||
* @param sync: synchronisation word to be prepended to data block.
|
||||
* @param data: data block to be transmitted.
|
||||
* @param frame: M17 frame to be sent.
|
||||
* @param isLast: flag signalling that current block is the last one being
|
||||
* transmitted.
|
||||
*/
|
||||
void send(const std::array< uint8_t, 2 >& sync,
|
||||
const std::array< uint8_t, 46 >& data,
|
||||
const bool isLast = false);
|
||||
void send(const frame_t& frame, const bool isLast);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Generate baseband stream from symbol stream.
|
||||
*/
|
||||
void generateBaseband();
|
||||
void symbolsToBaseband();
|
||||
|
||||
/**
|
||||
* Emit the baseband stream towards the output stage, platform dependent.
|
||||
*/
|
||||
void emitBaseband();
|
||||
void sendBaseband();
|
||||
|
||||
static constexpr size_t M17_TX_SAMPLE_RATE = 48000;
|
||||
static constexpr size_t M17_SAMPLES_PER_SYMBOL = M17_TX_SAMPLE_RATE / M17_SYMBOL_RATE;
|
||||
|
@ -100,7 +101,7 @@ private:
|
|||
static constexpr float M17_RRC_OFFSET = 0.0f;
|
||||
#endif
|
||||
|
||||
std::array< int16_t, M17_FRAME_SYMBOLS > symbols;
|
||||
std::array< int8_t, M17_FRAME_SYMBOLS > symbols;
|
||||
std::unique_ptr< int16_t[] > baseband_buffer; ///< Buffer for baseband audio handling.
|
||||
stream_sample_t *idleBuffer; ///< Half baseband buffer, free for processing.
|
||||
streamId outStream; ///< Baseband output stream ID.
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2021 - 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 M17TRANSMITTER_H
|
||||
#define M17TRANSMITTER_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#error This header is C++ only!
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include "M17ConvolutionalEncoder.hpp"
|
||||
#include "M17LinkSetupFrame.hpp"
|
||||
#include "M17StreamFrame.hpp"
|
||||
#include "M17Modulator.hpp"
|
||||
|
||||
namespace M17
|
||||
{
|
||||
|
||||
/**
|
||||
* 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.
|
||||
M17StreamFrame 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.
|
||||
};
|
||||
|
||||
} /* M17 */
|
||||
|
||||
#endif /* M17TRANSMITTER_H */
|
|
@ -22,7 +22,7 @@
|
|||
#define OPMODE_M17_H
|
||||
|
||||
#include <M17/M17FrameDecoder.hpp>
|
||||
#include <M17/M17Transmitter.hpp>
|
||||
#include <M17/M17FrameEncoder.hpp>
|
||||
#include <M17/M17Demodulator.hpp>
|
||||
#include <M17/M17Modulator.hpp>
|
||||
#include "OpMode.hpp"
|
||||
|
@ -126,8 +126,8 @@ private:
|
|||
bool locked; ///< Demodulator locked on data stream.
|
||||
M17::M17Modulator modulator; ///< M17 modulator.
|
||||
M17::M17Demodulator demodulator; ///< M17 demodulator.
|
||||
M17::M17Transmitter m17Tx; ///< M17 transmission manager.
|
||||
M17::M17FrameDecoder decoder; ///< M17 frame decoder
|
||||
M17::M17FrameEncoder encoder; ///< M17 frame encoder
|
||||
};
|
||||
|
||||
#endif /* OPMODE_M17_H */
|
||||
|
|
|
@ -47,8 +47,7 @@ void M17Modulator::init()
|
|||
{
|
||||
/*
|
||||
* Allocate a chunk of memory to contain two complete buffers for baseband
|
||||
* audio. Split this chunk in two separate blocks for double buffering using
|
||||
* placement new.
|
||||
* audio.
|
||||
*/
|
||||
|
||||
baseband_buffer = std::make_unique< int16_t[] >(2 * M17_FRAME_SAMPLES);
|
||||
|
@ -72,30 +71,55 @@ void M17Modulator::terminate()
|
|||
baseband_buffer.reset();
|
||||
}
|
||||
|
||||
void M17Modulator::send(const std::array< uint8_t, 2 >& sync,
|
||||
const std::array< uint8_t, 46 >& data,
|
||||
const bool isLast)
|
||||
void M17::M17Modulator::start()
|
||||
{
|
||||
auto sync1 = byteToSymbols(sync[0]);
|
||||
auto sync2 = byteToSymbols(sync[1]);
|
||||
if(txRunning) return;
|
||||
|
||||
auto it = std::copy(sync1.begin(), sync1.end(), symbols.begin());
|
||||
it = std::copy(sync2.begin(), sync2.end(), it);
|
||||
txRunning = true;
|
||||
stopTx = false;
|
||||
|
||||
for(size_t i = 0; i < data.size(); i++)
|
||||
// Fill symbol buffer with preamble, made of alternated +3 and -3 symbols
|
||||
for(size_t i = 0; i < symbols.size(); i += 2)
|
||||
{
|
||||
auto sym = byteToSymbols(data[i]);
|
||||
symbols[i] = +3;
|
||||
symbols[i + 1] = -3;
|
||||
}
|
||||
|
||||
// Generate baseband signal and then start transmission
|
||||
symbolsToBaseband();
|
||||
#ifndef PLATFORM_LINUX
|
||||
outStream = outputStream_start(SINK_RTX, PRIO_TX, baseband_buffer.get(),
|
||||
2*M17_FRAME_SAMPLES, BUF_CIRC_DOUBLE,
|
||||
M17_TX_SAMPLE_RATE);
|
||||
idleBuffer = outputStream_getIdleBuffer(outStream);
|
||||
#else
|
||||
sendBaseband();
|
||||
#endif
|
||||
|
||||
// Repeat baseband generation and transmission, this makes the preamble to
|
||||
// be long 80ms (two frames)
|
||||
symbolsToBaseband();
|
||||
sendBaseband();
|
||||
}
|
||||
|
||||
|
||||
void M17Modulator::send(const frame_t& frame, const bool isLast)
|
||||
{
|
||||
auto it = symbols.begin();
|
||||
for(size_t i = 0; i < frame.size(); i++)
|
||||
{
|
||||
auto sym = byteToSymbols(frame[i]);
|
||||
it = std::copy(sym.begin(), sym.end(), it);
|
||||
}
|
||||
|
||||
// If last frame, signal stop of transmission
|
||||
if(isLast) stopTx = true;
|
||||
|
||||
generateBaseband();
|
||||
emitBaseband();
|
||||
symbolsToBaseband();
|
||||
sendBaseband();
|
||||
}
|
||||
|
||||
void M17Modulator::generateBaseband()
|
||||
void M17Modulator::symbolsToBaseband()
|
||||
{
|
||||
memset(idleBuffer, 0x00, M17_FRAME_SAMPLES * sizeof(stream_sample_t));
|
||||
|
||||
|
@ -110,33 +134,20 @@ void M17Modulator::generateBaseband()
|
|||
elem = M17::rrc_48k(elem * M17_RRC_GAIN) - M17_RRC_OFFSET;
|
||||
idleBuffer[i] = static_cast< int16_t >(elem);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PLATFORM_LINUX
|
||||
void M17Modulator::emitBaseband()
|
||||
{
|
||||
#if defined(PLATFORM_MD3x0) || defined(PLATFORM_MDUV3x0)
|
||||
dsp_pwmCompensate(&pwmFilterState, idleBuffer, M17_FRAME_SAMPLES);
|
||||
dsp_invertPhase(idleBuffer, M17_FRAME_SAMPLES);
|
||||
#endif
|
||||
}
|
||||
|
||||
if(txRunning == false)
|
||||
{
|
||||
// First run, start transmission
|
||||
outStream = outputStream_start(SINK_RTX,
|
||||
PRIO_TX,
|
||||
baseband_buffer.get(),
|
||||
2*M17_FRAME_SAMPLES,
|
||||
BUF_CIRC_DOUBLE,
|
||||
M17_TX_SAMPLE_RATE);
|
||||
txRunning = true;
|
||||
stopTx = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Transmission is ongoing, syncronise with stream end before proceeding
|
||||
outputStream_sync(outStream, true);
|
||||
}
|
||||
#ifndef PLATFORM_LINUX
|
||||
void M17Modulator::sendBaseband()
|
||||
{
|
||||
if(txRunning == false) return;
|
||||
|
||||
// Transmission is ongoing, syncronise with stream end before proceeding
|
||||
outputStream_sync(outStream, true);
|
||||
|
||||
// Check if transmission stop is requested, if so stop the output stream
|
||||
// and wait until its effective termination.
|
||||
|
@ -157,7 +168,7 @@ void M17Modulator::emitBaseband()
|
|||
}
|
||||
}
|
||||
#else
|
||||
void M17Modulator::emitBaseband()
|
||||
void M17Modulator::sendBaseband()
|
||||
{
|
||||
FILE *outfile = fopen("/tmp/m17_output.raw", "ab");
|
||||
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
/***************************************************************************
|
||||
* 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 <http://www.gnu.org/licenses/> *
|
||||
***************************************************************************/
|
||||
|
||||
#include <M17/M17CodePuncturing.hpp>
|
||||
#include <M17/M17Decorrelator.hpp>
|
||||
#include <M17/M17Interleaver.hpp>
|
||||
#include <M17/M17Transmitter.hpp>
|
||||
|
||||
using namespace M17;
|
||||
|
||||
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.fields.stream = 1; // Stream
|
||||
type.fields.dataType = 2; // Voice data
|
||||
type.fields.CAN = 0; // 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<uint8_t, 61> encoded;
|
||||
encoder.reset();
|
||||
encoder.encode(lsf.getData(), encoded.data(), sizeof(M17LinkSetupFrame));
|
||||
encoded[60] = encoder.flush();
|
||||
|
||||
std::array<uint8_t, 46> punctured;
|
||||
puncture(encoded, punctured, LSF_PUNCTURE);
|
||||
interleave(punctured);
|
||||
decorrelate(punctured);
|
||||
|
||||
// Send preamble
|
||||
std::array<uint8_t, 2> preamble_sync;
|
||||
std::array<uint8_t, 46> 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<uint8_t, 37> encoded;
|
||||
encoder.reset();
|
||||
encoder.encode(dataFrame.getData(), encoded.data(), sizeof(M17StreamFrame));
|
||||
encoded[36] = encoder.flush();
|
||||
|
||||
std::array<uint8_t, 34> punctured;
|
||||
puncture(encoded, punctured, DATA_PUNCTURE);
|
||||
|
||||
// Add LICH segment to coded data and send
|
||||
std::array<uint8_t, 46> 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(STREAM_SYNC_WORD, frame, isLast);
|
||||
}
|
|
@ -29,8 +29,7 @@
|
|||
using namespace std;
|
||||
using namespace M17;
|
||||
|
||||
OpMode_M17::OpMode_M17() : startRx(false), startTx(false), locked(false),
|
||||
m17Tx(modulator)
|
||||
OpMode_M17::OpMode_M17() : startRx(false), startTx(false), locked(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -190,18 +189,37 @@ void OpMode_M17::rxState(rtxStatus_t *const status)
|
|||
|
||||
void OpMode_M17::txState(rtxStatus_t *const status)
|
||||
{
|
||||
frame_t m17Frame;
|
||||
|
||||
if(startTx)
|
||||
{
|
||||
startTx = false;
|
||||
|
||||
std::string src(status->source_address);
|
||||
std::string dst(status->destination_address);
|
||||
M17LinkSetupFrame lsf;
|
||||
|
||||
lsf.clear();
|
||||
lsf.setSource(src);
|
||||
if(!dst.empty()) lsf.setDestination(dst);
|
||||
|
||||
streamType_t type;
|
||||
type.fields.stream = 1; // Stream
|
||||
type.fields.dataType = 2; // Voice data
|
||||
type.fields.CAN = 0; // Channel access number
|
||||
|
||||
lsf.setType(type);
|
||||
lsf.updateCrc();
|
||||
|
||||
encoder.reset();
|
||||
encoder.encodeLsf(lsf, m17Frame);
|
||||
|
||||
audio_enableMic();
|
||||
codec_startEncode(SOURCE_MIC);
|
||||
|
||||
radio_enableTx();
|
||||
|
||||
std::string source_address(status->source_address);
|
||||
std::string destination_address(status->destination_address);
|
||||
m17Tx.start(source_address, destination_address);
|
||||
|
||||
startTx = false;
|
||||
modulator.start();
|
||||
modulator.send(m17Frame, false);
|
||||
}
|
||||
|
||||
payload_t dataFrame;
|
||||
|
@ -218,5 +236,6 @@ void OpMode_M17::txState(rtxStatus_t *const status)
|
|||
status->opStatus = OFF;
|
||||
}
|
||||
|
||||
m17Tx.send(dataFrame, lastFrame);
|
||||
encoder.encodeStreamFrame(dataFrame, m17Frame, lastFrame);
|
||||
modulator.send(m17Frame, lastFrame);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue