Reorganized code for M17 transmission: removed M17Transmitter class, use M17FrameEncoder to assemble LSF and data frames, modified API of M17Modulator class.

pull/114/head
Silvano Seva 2022-08-11 19:50:38 +02:00
rodzic 48cd81ee7c
commit a22aceb576
7 zmienionych plików z 88 dodań i 277 usunięć

Wyświetl plik

@ -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',

Wyświetl plik

@ -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.

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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");

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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);
}