diff --git a/DStarControl.cpp b/DStarControl.cpp new file mode 100644 index 0000000..a2c34a3 --- /dev/null +++ b/DStarControl.cpp @@ -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 +#include + +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; +} diff --git a/DStarControl.h b/DStarControl.h new file mode 100644 index 0000000..8b722e1 --- /dev/null +++ b/DStarControl.h @@ -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 + +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 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 diff --git a/DStarDefines.h b/DStarDefines.h index ae93f79..cee49f8 100644 --- a/DStarDefines.h +++ b/DStarDefines.h @@ -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 diff --git a/DStarEcho.cpp b/DStarEcho.cpp deleted file mode 100644 index 8398e20..0000000 --- a/DStarEcho.cpp +++ /dev/null @@ -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); -} diff --git a/DStarEcho.h b/DStarEcho.h deleted file mode 100644 index b667c92..0000000 --- a/DStarEcho.h +++ /dev/null @@ -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 m_buffer; - CTimer m_timer; -}; - -#endif diff --git a/DStarHeader.cpp b/DStarHeader.cpp index c9ad5fa..91ca3df 100644 --- a/DStarHeader.cpp +++ b/DStarHeader.cpp @@ -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); diff --git a/DStarHeader.h b/DStarHeader.h index 1e3917e..733dc2f 100644 --- a/DStarHeader.h +++ b/DStarHeader.h @@ -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; diff --git a/DStarNetwork.cpp b/DStarNetwork.cpp index a20c3bc..7759c45 100644 --- a/DStarNetwork.cpp +++ b/DStarNetwork.cpp @@ -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); } diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 1487822..c88b48a 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -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 @@ -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(); diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 89d2185..ad43975 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -159,8 +159,8 @@ + - @@ -204,7 +204,7 @@ - + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index 5b70b06..15f9e3e 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -47,9 +47,6 @@ Header Files - - Header Files - Header Files @@ -146,6 +143,9 @@ Header Files + + Header Files + @@ -175,9 +175,6 @@ Source Files - - Source Files - Source Files @@ -265,5 +262,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index fbad51b..75d10f5 100644 --- a/Makefile +++ b/Makefile @@ -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