First cut of the D-Star repeater control.

This commit is contained in:
Jonathan Naylor 2016-01-27 20:01:50 +00:00
parent b34cd230c7
commit fc3a688196
12 changed files with 861 additions and 148 deletions

706
DStarControl.cpp Normal file
View file

@ -0,0 +1,706 @@
/*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
*
* 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; version 2 of the License.
*
* 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.
*/
#include "DStarControl.h"
#include "Utils.h"
#include "Log.h"
#include <cassert>
#include <ctime>
const unsigned int MAX_SYNC_BIT_ERRORS = 2U;
// #define DUMP_DSTAR
CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, CDStarNetwork* network, IDisplay* display, unsigned int timeout, bool duplex) :
m_callsign(NULL),
m_gateway(NULL),
m_network(network),
m_display(display),
m_duplex(duplex),
m_queue(1000U),
m_state(RS_LISTENING),
m_net(false),
m_slowData(),
m_n(0U),
m_networkWatchdog(1000U, 0U, 1500U),
m_holdoffTimer(1000U, 0U, 500U),
m_timeoutTimer(1000U, timeout),
m_packetTimer(1000U, 0U, 300U),
m_elapsed(),
m_frames(0U),
m_lost(0U),
m_fec(),
m_bits(0U),
m_errs(0U),
m_lastFrame(NULL),
m_fp(NULL)
{
assert(display != NULL);
m_callsign = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
m_gateway = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
m_lastFrame = new unsigned char[DSTAR_FRAME_LENGTH_BYTES + 1U];
std::string call = callsign;
call.resize(DSTAR_LONG_CALLSIGN_LENGTH - 1U, ' ');
std::string mod = module;
mod.resize(1U, ' ');
call.append(mod);
std::string gate = callsign;
gate.resize(DSTAR_LONG_CALLSIGN_LENGTH - 1U, ' ');
gate.append("G");
for (unsigned int i = 0U; i < DSTAR_LONG_CALLSIGN_LENGTH; i++) {
m_callsign[i] = call.at(i);
m_gateway[i] = gate.at(i);
}
}
CDStarControl::~CDStarControl()
{
delete[] m_callsign;
delete[] m_gateway;
delete[] m_lastFrame;
}
void CDStarControl::writeModem(unsigned char *data)
{
unsigned char type = data[0U];
if (type == TAG_LOST && m_state == RS_RELAYING_RF_AUDIO) {
LogMessage("D-Star, transmission lost, BER: %u%%", (m_errs * 100U) / m_bits);
writeEndOfTransmission();
return;
}
if (type == TAG_LOST && (m_state == RS_LATE_ENTRY || m_state == RS_RELAYING_RF_DATA)) {
m_state = RS_LISTENING;
return;
}
if (type == TAG_HEADER) {
if (m_state == RS_RELAYING_RF_AUDIO)
return;
CDStarHeader header(data + 1U);
// Is this a transmission destined for a repeater?
if (!header.isRepeater())
return;
unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH];
header.getRPTCall1(callsign);
// Is it for us?
if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0)
return;
unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH];
header.getRPTCall2(gateway);
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
header.getMyCall1(my1);
unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH];
header.getMyCall2(my2);
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
header.getYourCall(your);
m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0;
if (m_state == RS_LISTENING) {
m_networkWatchdog.stop();
m_timeoutTimer.start();
m_holdoffTimer.stop();
m_frames = 1U;
m_lost = 0U;
m_n = 0U;
m_bits = 1U;
m_errs = 0U;
if (m_duplex) {
// Modify the header
header.setRepeater(false);
header.setRPTCall1(m_callsign);
header.setRPTCall2(m_callsign);
header.get(data + 1U);
writeQueueHeader(data);
}
if (m_net) {
// Modify the header
header.setRepeater(false);
header.setRPTCall1(m_gateway);
header.setRPTCall2(m_callsign);
header.get(data + 1U);
for (unsigned i = 0U; i < 3U; i++)
writeNetworkHeader(data, false);
}
m_state = RS_RELAYING_RF_AUDIO;
m_display->writeDStar(std::string((char*)my1, 8U), std::string((char*)my2, 4U));
LogMessage("D-Star, received RF header from %.8ss/%.4ss to %.8s", my1, my2, your);
} else if (m_state == RS_RELAYING_NETWORK_AUDIO) {
if (m_net) {
for (unsigned i = 0U; i < 3U; i++)
writeNetworkHeader(data, true);
}
LogMessage("D-Star, received RF busy header from %.8ss/%.4ss to %.8s", my1, my2, your);
}
} else if (type == TAG_EOT) {
if (m_state == RS_RELAYING_RF_AUDIO) {
unsigned int errors = m_fec.regenerateDStar(data + 1U);
m_errs += errors;
m_bits += 48U;
if (m_net) {
for (unsigned int i = 0U; i < 2U; i++)
writeNetworkData(data, errors, true, false);
}
if (m_duplex) {
for (unsigned int i = 0U; i < 3U; i++)
writeQueueData(data);
}
LogMessage("D-Star, received RF end of transmission, BER: %u%%", (m_errs * 100U) / m_bits);
writeEndOfTransmission();
} else if (m_state == RS_RELAYING_NETWORK_AUDIO) {
m_fec.regenerateDStar(data + 1U);
if (m_net) {
for (unsigned int i = 0U; i < 2U; i++)
writeNetworkData(data, 0U, true, true);
}
}
} else {
if (m_state == RS_LISTENING) {
unsigned int bits = matchSync(data + 1U);
if (bits <= MAX_SYNC_BIT_ERRORS) {
m_slowData.start();
m_state = RS_LATE_ENTRY;
}
} else if (m_state == RS_RELAYING_RF_AUDIO) {
unsigned int errors = m_fec.regenerateDStar(data + 1U);
m_errs += errors;
m_bits += 48U;
unsigned int bits = matchSync(data + 1U);
if (bits <= MAX_SYNC_BIT_ERRORS)
m_n = 0U;
// Regenerate the sync
if (m_n == 0U)
::memcpy(data + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES);
m_n = (m_n + 1U) % 21U;
if (m_net)
writeNetworkData(data, errors, false, false);
if (m_duplex) {
blankDTMF(data + 1U);
writeQueueData(data);
}
} else if (m_state == RS_RELAYING_NETWORK_AUDIO) {
m_fec.regenerateDStar(data + 1U);
if (m_net)
writeNetworkData(data, 0U, false, true);
} else if (m_state == RS_LATE_ENTRY) {
unsigned int bits = matchSync(data + 1U);
if (bits <= MAX_SYNC_BIT_ERRORS) {
m_slowData.reset();
return;
}
CDStarHeader* header = m_slowData.add(data + 1U);
if (header == NULL)
return;
// Is this a transmission destined for a repeater?
if (!header->isRepeater())
return;
unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH];
header->getRPTCall1(callsign);
// Is it for us?
if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0)
return;
unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH];
header->getRPTCall2(gateway);
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
header->getMyCall1(my1);
unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH];
header->getMyCall2(my2);
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
header->getYourCall(your);
m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0;
// Create a dummy start frame to replace the received frame
m_networkWatchdog.stop();
m_timeoutTimer.start();
m_frames = 1U;
m_lost = 0U;
m_n = 1U;
if (m_duplex) {
unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U];
start[0U] = TAG_HEADER;
// Modify the header
header->setRepeater(false);
header->setRPTCall1(m_callsign);
header->setRPTCall2(m_callsign);
header->get(start + 1U);
writeQueueHeader(start);
}
if (m_net) {
unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U];
start[0U] = TAG_HEADER;
// Modify the header
header->setRepeater(false);
header->setRPTCall1(m_gateway);
header->setRPTCall2(m_callsign);
header->get(start + 1U);
for (unsigned int i = 0U; i < 3U; i++)
writeNetworkHeader(start, false);
}
delete header;
unsigned int errors = m_fec.regenerateDMR(data + 1U);
m_errs = errors;
m_bits = 48U;
if (m_net)
writeNetworkData(data, errors, false, false);
if (m_duplex) {
blankDTMF(data + 1U);
writeQueueData(data);
}
m_state = RS_RELAYING_RF_AUDIO;
m_display->writeDStar(std::string((char*)my1, 8U), std::string((char*)my2, 4U));
LogMessage("D-Star, received RF late entry from %.8s/%.4s to %s", my1, my2, your);
}
}
}
unsigned int CDStarControl::readModem(unsigned char* data)
{
if (m_queue.isEmpty())
return 0U;
// Don't relay data until the timer has stopped.
if (m_holdoffTimer.isRunning())
return 0U;
unsigned char len = 0U;
m_queue.getData(&len, 1U);
m_queue.getData(data, len);
return len;
}
void CDStarControl::writeEndOfTransmission()
{
m_state = RS_LISTENING;
m_display->clearDStar();
m_networkWatchdog.stop();
m_timeoutTimer.stop();
m_packetTimer.stop();
m_frames = 0U;
m_lost = 0U;
m_errs = 0U;
m_bits = 0U;
#if defined(DUMP_DSTAR)
closeFile();
#endif
}
void CDStarControl::writeNetwork()
{
assert(m_network != NULL);
unsigned char data[DSTAR_HEADER_LENGTH_BYTES + 2U];
unsigned int length = m_network->read(data, DSTAR_HEADER_LENGTH_BYTES + 2U);
if (length == 0U)
return;
if (m_state == RS_RELAYING_RF_AUDIO || m_state == RS_LATE_ENTRY)
return;
m_networkWatchdog.start();
unsigned char type = data[0U];
unsigned char n = data[1U];
if (type == TAG_HEADER) {
if (m_state == RS_RELAYING_NETWORK_AUDIO)
return;
CDStarHeader header(data + 2U);
// Is this a transmission destined for a repeater?
if (!header.isRepeater())
return;
unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH];
header.getRPTCall1(callsign);
// Is it for us?
if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0)
return;
unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH];
header.getRPTCall2(gateway);
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
header.getMyCall1(my1);
unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH];
header.getMyCall2(my2);
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
header.getYourCall(your);
m_timeoutTimer.start();
m_elapsed.start();
m_frames = 0U;
m_lost = 0U;
m_n = 0U;
m_bits = 1U;
m_errs = 0U;
data[1U] = TAG_HEADER;
writeQueueHeader(data + 1U);
#if defined(DUMP_DSTAR)
openFile();
writeFile(data + 1U, length - 1U);
#endif
m_state = RS_RELAYING_NETWORK_AUDIO;
m_display->writeDStar(std::string((char*)my1, 8U), std::string((char*)my2, 4U));
LogMessage("D-Star, received network header from %.8ss/%.4ss to %.8s", my1, my2, your);
} else if (type == TAG_EOT) {
if (m_state != RS_RELAYING_NETWORK_AUDIO)
return;
data[1U] = TAG_EOT;
for (unsigned int i = 0U; i < 3U; i++)
writeQueueData(data + 1U);
#if defined(DUMP_DSTAR)
writeFile(data + 1U, length - 1U);
closeFile();
#endif
// We've received the header and EOT haven't we?
m_frames += 2U;
LogMessage("D-Star, received network end of voice transmission, %u%% packet loss, BER: %u%%", (m_lost * 100U) / m_frames, (m_errs * 100U) / m_bits);
writeEndOfTransmission();
} else {
if (m_state != RS_RELAYING_NETWORK_AUDIO)
return;
insertSilence(data + 2U, n);
m_errs += m_fec.regenerateDStar(data + 2U);
m_bits += 48U;
blankDTMF(data + 2U);
// Regenerate the sync
if (n == 0U)
::memcpy(data + DSTAR_VOICE_FRAME_LENGTH_BYTES + 2U, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES);
m_n = n;
m_packetTimer.start();
m_frames++;
data[1U] = TAG_DATA;
#if defined(DUMP_DSTAR)
writeFile(data + 1U, length - 1U);
#endif
writeQueueData(data + 1U);
}
}
void CDStarControl::clock(unsigned int ms)
{
if (m_network != NULL)
writeNetwork();
m_holdoffTimer.clock(ms);
if (m_holdoffTimer.isRunning() && m_holdoffTimer.hasExpired())
m_holdoffTimer.stop();
m_timeoutTimer.clock(ms);
if (m_state == RS_RELAYING_NETWORK_AUDIO) {
m_networkWatchdog.clock(ms);
if (m_networkWatchdog.hasExpired()) {
LogMessage("D-Star, network watchdog has expired, %u%% packet loss, BER: %u%%", (m_lost * 100U) / m_frames, (m_errs * 100U) / m_bits);
writeEndOfTransmission();
#if defined(DUMP_DSTAR)
closeFile();
#endif
}
}
if (m_state == RS_RELAYING_NETWORK_AUDIO) {
m_packetTimer.clock(ms);
if (m_packetTimer.isRunning() && m_packetTimer.hasExpired()) {
unsigned int frames = m_elapsed.elapsed() / DSTAR_FRAME_TIME;
if (frames > m_frames) {
unsigned int count = frames - m_frames;
if (count > 3U) {
LogMessage("D-Star, lost audio for 300ms filling in, %u %u", frames, m_frames);
insertSilence(count - 1U);
}
}
m_packetTimer.start();
}
}
}
void CDStarControl::writeQueueHeader(const unsigned char *data)
{
assert(data != NULL);
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired())
return;
unsigned char len = DSTAR_HEADER_LENGTH_BYTES + 1U;
m_queue.addData(&len, 1U);
m_queue.addData(data, len);
}
void CDStarControl::writeQueueData(const unsigned char *data)
{
assert(data != NULL);
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired())
return;
unsigned char len = DSTAR_FRAME_LENGTH_BYTES + 1U;
m_queue.addData(&len, 1U);
m_queue.addData(data, len);
}
void CDStarControl::writeNetworkHeader(const unsigned char* data, bool busy)
{
assert(data != NULL);
if (m_network == NULL)
return;
// Don't send to the network if the timeout has expired
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired())
return;
m_network->writeHeader(data + 1U, DSTAR_HEADER_LENGTH_BYTES, busy);
}
void CDStarControl::writeNetworkData(const unsigned char* data, unsigned int errors, bool end, bool busy)
{
assert(data != NULL);
if (m_network == NULL)
return;
// Don't send to the network if the timeout has expired
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired())
return;
// XXX
m_network->writeData(data + 1U, DSTAR_FRAME_LENGTH_BYTES, errors, end, busy);
}
bool CDStarControl::openFile()
{
if (m_fp != NULL)
return true;
time_t t;
::time(&t);
struct tm* tm = ::localtime(&t);
char name[100U];
::sprintf(name, "DStar_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
m_fp = ::fopen(name, "wb");
if (m_fp == NULL)
return false;
::fwrite("DSTAR", 1U, 4U, m_fp);
return true;
}
bool CDStarControl::writeFile(const unsigned char* data, unsigned int length)
{
if (m_fp == NULL)
return false;
::fwrite(data, 1U, length, m_fp);
return true;
}
void CDStarControl::closeFile()
{
if (m_fp != NULL) {
::fclose(m_fp);
m_fp = NULL;
}
}
void CDStarControl::insertSilence(const unsigned char* data, unsigned char seqNo)
{
assert(data != NULL);
// Check to see if we have any spaces to fill?
unsigned char seq = (m_n + 1U) % 21U;
if (seq == seqNo) {
// Just copy the data, nothing else to do here
::memcpy(m_lastFrame, data, DSTAR_FRAME_LENGTH_BYTES + 1U);
return;
}
unsigned int oldSeqNo = (m_n + 1U) % 21U;
unsigned int newSeqNo = seqNo;
unsigned int count;
if (newSeqNo > oldSeqNo)
count = newSeqNo - oldSeqNo;
else
count = (21U + newSeqNo) - oldSeqNo;
if (count < 10U)
insertSilence(count);
::memcpy(m_lastFrame, data, DSTAR_FRAME_LENGTH_BYTES + 1U);
}
void CDStarControl::insertSilence(unsigned int count)
{
unsigned char n = (m_n + 1U) % 21U;
for (unsigned int i = 0U; i < count; i++) {
unsigned char data[DSTAR_FRAME_LENGTH_BYTES + 1U];
::memcpy(data, m_lastFrame, DSTAR_FRAME_LENGTH_BYTES + 1U);
if (n == 0U) {
// Regenerate the sync
::memcpy(data + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES);
} else {
// Dummy slow data values
::memcpy(data + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U, DSTAR_NULL_SLOW_DATA_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES);
}
data[0U] = TAG_DATA;
writeQueueData(data);
m_n = n;
m_frames++;
m_lost++;
n = (n + 1U) % 21U;
}
LogMessage("D-Star, inserted %u audio frames", count);
}
void CDStarControl::blankDTMF(unsigned char* data) const
{
assert(data != NULL);
// DTMF begins with these byte values
if ((data[0] & DSTAR_DTMF_MASK[0]) == DSTAR_DTMF_SIG[0] && (data[1] & DSTAR_DTMF_MASK[1]) == DSTAR_DTMF_SIG[1] &&
(data[2] & DSTAR_DTMF_MASK[2]) == DSTAR_DTMF_SIG[2] && (data[3] & DSTAR_DTMF_MASK[3]) == DSTAR_DTMF_SIG[3] &&
(data[4] & DSTAR_DTMF_MASK[4]) == DSTAR_DTMF_SIG[4] && (data[5] & DSTAR_DTMF_MASK[5]) == DSTAR_DTMF_SIG[5] &&
(data[6] & DSTAR_DTMF_MASK[6]) == DSTAR_DTMF_SIG[6] && (data[7] & DSTAR_DTMF_MASK[7]) == DSTAR_DTMF_SIG[7] &&
(data[8] & DSTAR_DTMF_MASK[8]) == DSTAR_DTMF_SIG[8])
::memcpy(data, DSTAR_NULL_AMBE_DATA_BYTES, DSTAR_VOICE_FRAME_LENGTH_BYTES);
}
unsigned int CDStarControl::matchSync(const unsigned char* data) const
{
unsigned char bits[DSTAR_DATA_FRAME_LENGTH_BYTES];
bits[0U] = data[9U] ^ DSTAR_SYNC_BYTES[0U];
bits[1U] = data[10U] ^ DSTAR_SYNC_BYTES[1U];
bits[2U] = data[11U] ^ DSTAR_SYNC_BYTES[2U];
unsigned int errors = 0U;
for (unsigned int i = 0U; i < DSTAR_DATA_FRAME_LENGTH_BYTES; i++) {
while (bits[i] != 0x00U) {
bits[i] &= bits[i] - 1U;
errors++;
}
}
return errors;
}

91
DStarControl.h Normal file
View file

@ -0,0 +1,91 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(DStarControl_H)
#define DStarControl_H
#include "DStarNetwork.h"
#include "DStarSlowData.h"
#include "DStarDefines.h"
#include "DStarHeader.h"
#include "StopWatch.h"
#include "RingBuffer.h"
#include "AMBEFEC.h"
#include "Display.h"
#include "Defines.h"
#include "Timer.h"
#include "Modem.h"
#include <string>
class CDStarControl {
public:
CDStarControl(const std::string& callsign, const std::string& module, CDStarNetwork* network, IDisplay* display, unsigned int timeout, bool duplex);
~CDStarControl();
void writeModem(unsigned char* data);
unsigned int readModem(unsigned char* data);
void clock(unsigned int ms);
private:
unsigned char* m_callsign;
unsigned char* m_gateway;
CDStarNetwork* m_network;
IDisplay* m_display;
bool m_duplex;
CRingBuffer<unsigned char> m_queue;
RPT_STATE m_state;
bool m_net;
CDStarSlowData m_slowData;
unsigned char m_n;
CTimer m_networkWatchdog;
CTimer m_holdoffTimer;
CTimer m_timeoutTimer;
CTimer m_packetTimer;
CStopWatch m_elapsed;
unsigned int m_frames;
unsigned int m_lost;
CAMBEFEC m_fec;
unsigned int m_bits;
unsigned int m_errs;
unsigned char* m_lastFrame;
FILE* m_fp;
void writeNetwork();
void writeQueueHeader(const unsigned char* data);
void writeQueueData(const unsigned char* data);
void writeNetworkHeader(const unsigned char* data, bool busy);
void writeNetworkData(const unsigned char* data, unsigned int errors, bool end, bool busy);
void writeEndOfTransmission();
bool openFile();
bool writeFile(const unsigned char* data, unsigned int length);
void closeFile();
void insertSilence(const unsigned char* data, unsigned char seqNo);
void insertSilence(unsigned int count);
void blankDTMF(unsigned char* data) const;
unsigned int matchSync(const unsigned char* data) const;
};
#endif

View file

@ -55,4 +55,9 @@ const unsigned char DSTAR_URGENT_MASK = 0x08U;
const unsigned char DSTAR_SYNC_BYTES[] = {0x55U, 0x2DU, 0x16U};
const unsigned char DSTAR_DTMF_MASK[] = { 0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U };
const unsigned char DSTAR_DTMF_SIG[] = { 0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U };
const unsigned int DSTAR_FRAME_TIME = 20U;
#endif

View file

@ -1,74 +0,0 @@
/*
* Copyright (C) 2015 by Jonathan Naylor G4KLX
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "DStarEcho.h"
CDStarEcho::CDStarEcho(unsigned int delay, unsigned int space) :
m_buffer(space),
m_timer(1000U, delay)
{
}
CDStarEcho::~CDStarEcho()
{
}
unsigned int CDStarEcho::readData(unsigned char* data)
{
if (!hasData())
return 0U;
unsigned char len;
m_buffer.getData(&len, 1U);
m_buffer.getData(data, len);
if (!hasData())
m_timer.stop();
return len;
}
bool CDStarEcho::writeData(const unsigned char* data, unsigned int length)
{
bool ret = m_buffer.hasSpace(length + 1U);
if (!ret)
return false;
unsigned char len = length;
m_buffer.addData(&len, 1U);
m_buffer.addData(data, length);
m_timer.start();
return true;
}
bool CDStarEcho::hasData()
{
if (m_timer.isRunning() && m_timer.hasExpired())
return m_buffer.hasData();
else
return false;
}
void CDStarEcho::clock(unsigned int ms)
{
m_timer.clock(ms);
}

View file

@ -1,43 +0,0 @@
/*
* Copyright (C) 2015 by Jonathan Naylor G4KLX
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(DSTARECHO_H)
#define DSTARECHO_H
#include "RingBuffer.h"
#include "Timer.h"
class CDStarEcho {
public:
CDStarEcho(unsigned int delay, unsigned int space);
~CDStarEcho();
unsigned int readData(unsigned char* data);
bool writeData(const unsigned char* data, unsigned int length);
bool hasData();
void clock(unsigned int ms);
private:
CRingBuffer<unsigned char> m_buffer;
CTimer m_timer;
};
#endif

View file

@ -72,6 +72,16 @@ void CDStarHeader::getRPTCall2(unsigned char* call2) const
::memcpy(call2, m_header + 3U, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::setRPTCall1(const unsigned char* call1)
{
::memcpy(m_header + 11U, call1, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::setRPTCall2(const unsigned char* call2)
{
::memcpy(m_header + 3U, call2, DSTAR_LONG_CALLSIGN_LENGTH);
}
void CDStarHeader::getYourCall(unsigned char* call) const
{
::memcpy(call, m_header + 19U, DSTAR_LONG_CALLSIGN_LENGTH);

View file

@ -33,6 +33,9 @@ public:
void getRPTCall1(unsigned char* call1) const;
void getRPTCall2(unsigned char* call2) const;
void setRPTCall1(const unsigned char* call1);
void setRPTCall2(const unsigned char* call2);
void getYourCall(unsigned char* call) const;
void get(unsigned char* header) const;

View file

@ -209,13 +209,13 @@ void CDStarNetwork::clock(unsigned int ms)
if (m_inId == 0U && m_enabled) {
m_inId = buffer[5] * 256U + buffer[6];
unsigned char c = length - 6U;
unsigned char c = length - 7U;
m_buffer.addData(&c, 1U);
c = TAG_HEADER;
m_buffer.addData(&c, 1U);
m_buffer.addData(buffer + 7U, length - 7U);
m_buffer.addData(buffer + 8U, length - 8U);
}
break;
@ -225,17 +225,21 @@ void CDStarNetwork::clock(unsigned int ms)
// Check that the stream id matches the valid header, reject otherwise
if (id == m_inId && m_enabled) {
unsigned char c = length - 8U;
m_buffer.addData(&c, 1U);
unsigned char ctrl[3U];
ctrl[0U] = length - 7U;
// Is this the last packet in the stream?
if ((buffer[7] & 0x40) == 0x40) {
m_inId = 0U;
c = TAG_EOT;
ctrl[1U] = TAG_EOT;
} else {
c = TAG_DATA;
ctrl[1U] = TAG_DATA;
}
m_buffer.addData(&c, 1U);
ctrl[2U] = buffer[7] & 0x3FU;
m_buffer.addData(ctrl, 3U);
m_buffer.addData(buffer + 9U, length - 9U);
}

View file

@ -21,11 +21,10 @@
#include "Version.h"
#include "StopWatch.h"
#include "Defines.h"
#include "DStarControl.h"
#include "DMRControl.h"
#include "TFTSerial.h"
#include "NullDisplay.h"
#include "DStarEcho.h"
#include "YSFEcho.h"
#include <cstdio>
@ -135,9 +134,20 @@ int CMMDVMHost::run()
CStopWatch stopWatch;
stopWatch.start();
CDStarEcho* dstar = NULL;
if (m_dstarEnabled)
dstar = new CDStarEcho(2U, 10000U);
CDStarControl* dstar = NULL;
if (m_dstarEnabled) {
std::string callsign = m_conf.getCallsign();
std::string module = m_conf.getDStarModule();
unsigned int timeout = m_conf.getTimeout();
bool duplex = m_conf.getDuplex();
LogInfo("D-Star Parameters");
LogInfo(" Callsign: %s", callsign.c_str());
LogInfo(" Module: %s", module.c_str());
LogInfo(" Timeout: %us", timeout);
dstar = new CDStarControl(callsign, module, m_dstarNetwork, m_display, timeout, duplex);
}
CDMRControl* dmr = NULL;
if (m_dmrEnabled) {
@ -179,7 +189,7 @@ int CMMDVMHost::run()
}
if (mode == MODE_DSTAR) {
dstar->writeData(data, len);
dstar->writeModem(data);
modeTimer.start();
}
}
@ -266,9 +276,9 @@ int CMMDVMHost::run()
if (dstar != NULL) {
ret = m_modem->hasDStarSpace();
if (ret) {
len = dstar->readData(data);
len = dstar->readModem(data);
if (len > 0U && mode != MODE_DSTAR) {
if (len > 0U && mode == MODE_IDLE) {
LogMessage("Mode set to D-Star");
mode = MODE_DSTAR;
m_display->setDStar();
@ -331,7 +341,7 @@ int CMMDVMHost::run()
if (ret) {
len = ysf->readData(data);
if (len > 0U && mode != MODE_YSF) {
if (len > 0U && mode == MODE_IDLE) {
LogMessage("Mode set to System Fusion");
mode = MODE_YSF;
m_display->setFusion();

View file

@ -159,8 +159,8 @@
<ClInclude Include="DMRDefines.h" />
<ClInclude Include="DMRSlot.h" />
<ClInclude Include="DMRSync.h" />
<ClInclude Include="DStarControl.h" />
<ClInclude Include="DStarDefines.h" />
<ClInclude Include="DStarEcho.h" />
<ClInclude Include="DStarHeader.h" />
<ClInclude Include="DStarNetwork.h" />
<ClInclude Include="DStarSlowData.h" />
@ -204,7 +204,7 @@
<ClCompile Include="DMRDataHeader.cpp" />
<ClCompile Include="DMRSlot.cpp" />
<ClCompile Include="DMRSync.cpp" />
<ClCompile Include="DStarEcho.cpp" />
<ClCompile Include="DStarControl.cpp" />
<ClCompile Include="DStarHeader.cpp" />
<ClCompile Include="DStarNetwork.cpp" />
<ClCompile Include="DStarSlowData.cpp" />

View file

@ -47,9 +47,6 @@
<ClInclude Include="DStarDefines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarEcho.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EMB.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -146,6 +143,9 @@
<ClInclude Include="DStarSlowData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DStarControl.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="BPTC19696.cpp">
@ -175,9 +175,6 @@
<ClCompile Include="DMRSync.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DStarEcho.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EMB.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -265,5 +262,8 @@
<ClCompile Include="DStarSlowData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DStarControl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -5,10 +5,10 @@ LDFLAGS = -g
all: MMDVMHost
MMDVMHost: AMBEFEC.o BPTC19696.o Conf.o CRC.o CSBK.o Display.o DMRControl.o DMRData.o DMRDataHeader.o DMRSlot.o DMRSync.o DStarEcho.o DStarHeader.o DStarNetwork.o \
DStarSlowData.o EMB.o EmbeddedLC.o FullLC.o Golay2087.o Golay24128.o Hamming.o HomebrewDMRIPSC.o LC.o Log.o MMDVMHost.o Modem.o NullDisplay.o QR1676.o \
RS129.o SerialController.o SHA256.o ShortLC.o SlotType.o StopWatch.o TFTSerial.o Timer.o UDPSocket.o Utils.o YSFEcho.o
$(CC) $(LDFLAGS) -o MMDVMHost AMBEFEC.o BPTC19696.o Conf.o CRC.o CSBK.o Display.o DMRControl.o DMRData.o DMRDataHeader.o DMRSlot.o DMRSync.o DStarEcho.o \
MMDVMHost: AMBEFEC.o BPTC19696.o Conf.o CRC.o CSBK.o Display.o DMRControl.o DMRData.o DMRDataHeader.o DMRSlot.o DMRSync.o DStarControl.o DStarHeader.o \
DStarNetwork.o DStarSlowData.o EMB.o EmbeddedLC.o FullLC.o Golay2087.o Golay24128.o Hamming.o HomebrewDMRIPSC.o LC.o Log.o MMDVMHost.o Modem.o \
NullDisplay.o QR1676.o RS129.o SerialController.o SHA256.o ShortLC.o SlotType.o StopWatch.o TFTSerial.o Timer.o UDPSocket.o Utils.o YSFEcho.o
$(CC) $(LDFLAGS) -o MMDVMHost AMBEFEC.o BPTC19696.o Conf.o CRC.o CSBK.o Display.o DMRControl.o DMRData.o DMRDataHeader.o DMRSlot.o DMRSync.o DStarControl.o \
DStarHeader.o DStarNetwork.o DStarSlowData.o EMB.o EmbeddedLC.o FullLC.o Golay2087.o Golay24128.o Hamming.o HomebrewDMRIPSC.o LC.o Log.o MMDVMHost.o \
Modem.o NullDisplay.o QR1676.o RS129.o SerialController.o SHA256.o ShortLC.o SlotType.o StopWatch.o TFTSerial.o Timer.o UDPSocket.o Utils.o YSFEcho.o \
$(LIBS)
@ -47,8 +47,9 @@ DMRSlot.o: DMRSlot.cpp DMRSlot.h DMRData.h Modem.h HomebrewDMRIPSC.h Defines.h L
DMRSync.o: DMRSync.cpp DMRSync.h DMRDefines.h
$(CC) $(CFLAGS) -c DMRSync.cpp
DStarEcho.o: DStarEcho.cpp DStarEcho.h RingBuffer.h Timer.h
$(CC) $(CFLAGS) -c DStarEcho.cpp
DStarControl.o: DStarControl.cpp DStarControl.h RingBuffer.h Timer.h DStarDefines.h DStarHeader.h DStarSlowData.h DStarNetwork.h Defines.h Log.h StopWatch.h \
AMBEFEC.h Display.h Modem.h Utils.h
$(CC) $(CFLAGS) -c DStarControl.cpp
DStarHeader.o: DStarHeader.cpp DStarHeader.h DStarDefines.h CRC.h
$(CC) $(CFLAGS) -c DStarHeader.cpp
@ -86,7 +87,7 @@ LC.o: LC.cpp LC.h Utils.h DMRDefines.h
Log.o: Log.cpp Log.h
$(CC) $(CFLAGS) -c Log.cpp
MMDVMHost.o: MMDVMHost.cpp MMDVMHost.h Conf.h Log.h Version.h Modem.h StopWatch.h Defines.h DMRSync.h DStarEcho.h YSFEcho.h DMRControl.h HomebrewDMRIPSC.h \
MMDVMHost.o: MMDVMHost.cpp MMDVMHost.h Conf.h Log.h Version.h Modem.h StopWatch.h Defines.h DMRSync.h DStarControl.h YSFEcho.h DMRControl.h HomebrewDMRIPSC.h \
Display.h TFTSerial.h NullDisplay.h DStarNetwork.h
$(CC) $(CFLAGS) -c MMDVMHost.cpp