From 7bb53cf704bed917ef8eb12c24e7e7499cce61f0 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Tue, 26 Jan 2016 18:06:31 +0000 Subject: [PATCH] Beginnings of DMR data support. --- CRC.cpp | 17 ++--- CRC.h | 2 +- CSBK.cpp | 5 +- DMRDataHeader.cpp | 81 +++++++++++++++++++++++ DMRDataHeader.h | 49 ++++++++++++++ DMRDefines.h | 6 ++ DMRSlot.cpp | 135 +++++++++++++++++++++++++++++++++----- DMRSlot.h | 1 + MMDVMHost.vcxproj | 2 + MMDVMHost.vcxproj.filters | 6 ++ Makefile | 19 +++--- 11 files changed, 290 insertions(+), 33 deletions(-) create mode 100644 DMRDataHeader.cpp create mode 100644 DMRDataHeader.h diff --git a/CRC.cpp b/CRC.cpp index b118f20..d24421d 100644 --- a/CRC.cpp +++ b/CRC.cpp @@ -19,6 +19,7 @@ #include "CRC.h" #include "Utils.h" +#include "Log.h" #include #include @@ -72,18 +73,16 @@ unsigned char CCRC::encodeEightBit(const unsigned char *in, unsigned int length) return crc; } -bool CCRC::checkCSBK(const unsigned char *in) +bool CCRC::checkCCITT16(const unsigned char *in, unsigned int length) { + assert(in != NULL); + assert(length > 2U); + unsigned short crc16 = 0U; - // Run through all 12 bits - for (unsigned int a = 0; a < 12U; a++) { + for (unsigned int a = 0; a < (length - 2U); a++) { unsigned char val = in[a]; - // Allow for the CSBK CRC mask - if (a > 9U) - val ^= 0xA5U; - for (unsigned int i = 0U; i < 8U; i++) { bool c15 = (crc16 >> 15 & 0x01U) == 0x01U; bool bit = (val >> (7 - i) & 0x01U) == 0x01U; @@ -93,7 +92,9 @@ bool CCRC::checkCSBK(const unsigned char *in) } } - return crc16 == 0x1D0FU; + LogMessage("CCITT16, %04X == %02X %02X", crc16, in[length - 2U], in[length - 1U]); + + return crc16 == (in[length - 2U] << 8 | in[length - 1U]); } unsigned char CCRC::crc8(const unsigned char *in, unsigned int length) diff --git a/CRC.h b/CRC.h index bbbc5b5..bae8a33 100644 --- a/CRC.h +++ b/CRC.h @@ -25,7 +25,7 @@ public: static bool checkFiveBit(bool* in, unsigned int tcrc); static void encodeFiveBit(const bool* in, unsigned int& tcrc); - static bool checkCSBK(const unsigned char* in); + static bool checkCCITT16(const unsigned char* in, unsigned int length); static unsigned char encodeEightBit(const unsigned char* in, unsigned int length); diff --git a/CSBK.cpp b/CSBK.cpp index 10ace99..b670a55 100644 --- a/CSBK.cpp +++ b/CSBK.cpp @@ -39,7 +39,10 @@ m_valid(false) unsigned char data[12U]; bptc.decode(bytes, data); - m_valid = CCRC::checkCSBK(data); + data[10U] ^= CSBK_CRC_MASK[0U]; + data[11U] ^= CSBK_CRC_MASK[1U]; + + m_valid = CCRC::checkCCITT16(data, 12U); m_CSBKO = CSBKO(data[0U] & 0x3FU); m_FID = data[1U]; diff --git a/DMRDataHeader.cpp b/DMRDataHeader.cpp new file mode 100644 index 0000000..a4369a6 --- /dev/null +++ b/DMRDataHeader.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 by Ian Wraith + * 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. + */ + +#include "DMRDataHeader.h" +#include "DMRDefines.h" +#include "CRC.h" +#include "Log.h" + +#include +#include + +CDMRDataHeader::CDMRDataHeader(const unsigned char* data) : +m_bptc(), +m_valid(false), +m_gi(false), +m_srcId(0U), +m_dstId(0U), +m_blocks(0U) +{ + assert(data != NULL); + + unsigned char header[12U]; + m_bptc.decode(data, header); + + header[10U] ^= DATA_HEADER_CRC_MASK[0U]; + header[11U] ^= DATA_HEADER_CRC_MASK[1U]; + + m_valid = CCRC::checkCCITT16(header, 12U); + + m_gi = (header[0U] & 0x80U) == 0x80U; + + m_dstId = data[2U] << 16 | data[3U] << 8 | data[4U]; + m_srcId = data[5U] << 16 | data[6U] << 8 | data[7U]; + + m_blocks = data[8U] & 0x7FU; +} + +CDMRDataHeader::~CDMRDataHeader() +{ +} + +bool CDMRDataHeader::isValid() const +{ + return m_valid; +} + +bool CDMRDataHeader::getGI() const +{ + return m_gi; +} + +unsigned int CDMRDataHeader::getSrcId() const +{ + return m_srcId; +} + +unsigned int CDMRDataHeader::getDstId() const +{ + return m_dstId; +} + +unsigned int CDMRDataHeader::getBlocks() const +{ + return m_blocks; +} diff --git a/DMRDataHeader.h b/DMRDataHeader.h new file mode 100644 index 0000000..c51cb82 --- /dev/null +++ b/DMRDataHeader.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef DMRDataHeader_H +#define DMRDataHeader_H + +#include "BPTC19696.h" + +class CDMRDataHeader +{ +public: + CDMRDataHeader(const unsigned char* data); + ~CDMRDataHeader(); + + bool isValid() const; + + bool getGI() const; + + unsigned int getSrcId() const; + unsigned int getDstId() const; + + unsigned int getBlocks() const; + +private: + CBPTC19696 m_bptc; + bool m_valid; + bool m_gi; + unsigned int m_srcId; + unsigned int m_dstId; + unsigned int m_blocks; +}; + +#endif + diff --git a/DMRDefines.h b/DMRDefines.h index b402266..e9f297e 100644 --- a/DMRDefines.h +++ b/DMRDefines.h @@ -64,6 +64,7 @@ const unsigned char PAYLOAD_RIGHT_MASK[] = {0x0FU, 0xFFU, 0xFFU, 0xFFU, 0xF const unsigned char VOICE_LC_HEADER_CRC_MASK[] = {0x96U, 0x96U, 0x96U}; const unsigned char TERMINATOR_WITH_LC_CRC_MASK[] = {0x99U, 0x99U, 0x99U}; +const unsigned char DATA_HEADER_CRC_MASK[] = {0xCCU, 0xCCU}; const unsigned char CSBK_CRC_MASK[] = {0xA5U, 0xA5U}; const unsigned int DMR_SLOT_TIME = 60U; @@ -74,8 +75,13 @@ const unsigned char DT_VOICE_PI_HEADER = 0x00U; const unsigned char DT_VOICE_LC_HEADER = 0x01U; const unsigned char DT_TERMINATOR_WITH_LC = 0x02U; const unsigned char DT_CSBK = 0x03U; +const unsigned char DT_MBC_HEADER = 0x04U; +const unsigned char DT_MBC_CONTINUATION = 0x05U; const unsigned char DT_DATA_HEADER = 0x06U; +const unsigned char DT_RATE_12_DATA = 0x07U; +const unsigned char DT_RATE_34_DATA = 0x08U; const unsigned char DT_IDLE = 0x09U; +const unsigned char DT_RATE_1_DATA = 0x0AU; // Dummy values const unsigned char DT_VOICE_SYNC = 0xF0U; diff --git a/DMRSlot.cpp b/DMRSlot.cpp index ca32fb5..d197ba5 100644 --- a/DMRSlot.cpp +++ b/DMRSlot.cpp @@ -11,6 +11,7 @@ * GNU General Public License for more details. */ +#include "DMRDataHeader.h" #include "SlotType.h" #include "ShortLC.h" #include "DMRSlot.h" @@ -51,6 +52,7 @@ m_timeoutTimer(1000U, timeout), m_packetTimer(1000U, 0U, 100U), m_elapsed(), m_frames(0U), +m_blocks(0U), m_lost(0U), m_fec(), m_bits(0U), @@ -185,6 +187,20 @@ void CDMRSlot::writeModem(unsigned char *data) if (m_state == RS_RELAYING_RF_DATA) return; + CDMRDataHeader dataHeader(data + 2U); + // if (!dataHeader.isValid()) { + // LogMessage("DMR Slot %u: unable to decode the data header", m_slotNo); + // return; + // } + + bool gi = dataHeader.getGI(); + unsigned int srcId = dataHeader.getSrcId(); + unsigned int dstId = dataHeader.getDstId(); + + m_blocks = dataHeader.getBlocks(); + + m_lc = new CLC(gi ? FLCO_GROUP : FLCO_USER_USER, srcId, dstId); + // Regenerate the Slot Type slotType.getData(data + 2U); @@ -206,14 +222,18 @@ void CDMRSlot::writeModem(unsigned char *data) } m_state = RS_RELAYING_RF_DATA; - // setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); - // m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); + setShortLC(m_slotNo, m_lc->getDstId(), gi ? FLCO_GROUP : FLCO_USER_USER); - // LogMessage("DMR Slot %u, received RF data header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); - LogMessage("DMR Slot %u, received RF data header", m_slotNo); + m_display->writeDMR(m_slotNo, srcId, gi, dstId); + + LogMessage("DMR Slot %u, received RF data header from %u to %s%u, %u blocks", m_slotNo, srcId, gi ? "TG ": "", dstId, m_blocks); } else if (dataType == DT_CSBK) { CCSBK csbk(data + 2U); + // if (!csbk.isValid()) { + // LogMessage("DMR Slot %u: unable to decode the CSBK", m_slotNo); + // return; + // } CSBKO csbko = csbk.getCSBKO(); switch (csbko) { @@ -224,6 +244,9 @@ void CDMRSlot::writeModem(unsigned char *data) case CSBKO_UUANSRSP: case CSBKO_NACKRSP: case CSBKO_PRECCSBK: { + // Regenerate the Slot Type + slotType.getData(data + 2U); + // Convert the Data Sync to be from the BS CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); @@ -244,7 +267,10 @@ void CDMRSlot::writeModem(unsigned char *data) LogWarning("DMR Slot %u, unhandled RF CSBK type - 0x%02X", m_slotNo, csbko); break; } - } else { + } else if (dataType == DT_RATE_12_DATA || dataType == DT_RATE_34_DATA || dataType == DT_RATE_1_DATA) { + if (m_state != RS_RELAYING_RF_DATA) + return; + // Regenerate the Slot Type slotType.getData(data + 2U); @@ -252,11 +278,21 @@ void CDMRSlot::writeModem(unsigned char *data) CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); - data[0U] = TAG_DATA; + m_blocks--; + + data[0U] = m_blocks == 0U ? TAG_EOT : TAG_DATA; data[1U] = 0x00U; writeNetwork(data, dataType); writeQueue(data); + + if (m_blocks == 0U) { + LogMessage("DMR Slot %u, ended RF data transmission", m_slotNo); + writeEndOfTransmission(); + } + } else { + // Unhandled data type + LogWarning("DMR Slot %u, unhandled RF data type - 0x%02X", m_slotNo, dataType); } } else if (audioSync) { if (m_state == RS_RELAYING_RF_AUDIO) { @@ -514,6 +550,20 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) if (m_state == RS_RELAYING_NETWORK_DATA) return; + CDMRDataHeader dataHeader(data + 2U); + // if (!dataHeader.isValid()) { + // LogMessage("DMR Slot %u: unable to decode the data header", m_slotNo); + // return; + // } + + bool gi = dataHeader.getGI(); + unsigned int srcId = dataHeader.getSrcId(); + unsigned int dstId = dataHeader.getDstId(); + + m_blocks = dataHeader.getBlocks(); + + m_lc = new CLC(gi ? FLCO_GROUP : FLCO_USER_USER, srcId, dstId); + // Regenerate the Slot Type CSlotType slotType; slotType.setColorCode(m_colorCode); @@ -536,12 +586,11 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) m_state = RS_RELAYING_NETWORK_DATA; - // setShortLC(m_slotNo, m_lc->getDstId(), m_lc->getFLCO()); + setShortLC(m_slotNo, dmrData.getDstId(), gi ? FLCO_GROUP : FLCO_USER_USER); - // m_display->writeDMR(m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP, m_lc->getDstId()); + m_display->writeDMR(m_slotNo, dmrData.getSrcId(), gi, dmrData.getDstId()); - // LogMessage("DMR Slot %u, received network data header from %u to %s%u", m_slotNo, m_lc->getSrcId(), m_lc->getFLCO() == FLCO_GROUP ? "TG " : "", m_lc->getDstId()); - LogMessage("DMR Slot %u, received network data header", m_slotNo); + LogMessage("DMR Slot %u, received network data header from %u to %s%u, %u blocks", m_slotNo, dmrData.getSrcId(), gi ? "TG ": "", dmrData.getDstId(), m_blocks); } else if (dataType == DT_VOICE_SYNC) { if (m_state == RS_LISTENING) { m_lc = new CLC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); @@ -650,8 +699,55 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) #if defined(DUMP_DMR) writeFile(data); #endif - } else { - // Change the Color Code of the Slot Type + } else if (dataType == DT_CSBK) { + CCSBK csbk(data + 2U); + // if (!csbk.isValid()) { + // LogMessage("DMR Slot %u: unable to decode the CSBK", m_slotNo); + // return; + // } + + CSBKO csbko = csbk.getCSBKO(); + switch (csbko) { + case CSBKO_BSDWNACT: + return; + + case CSBKO_UUVREQ: + case CSBKO_UUANSRSP: + case CSBKO_NACKRSP: + case CSBKO_PRECCSBK: { + // Regenerate the Slot Type + CSlotType slotType; + slotType.putData(data + 2U); + slotType.setColorCode(m_colorCode); + slotType.getData(data + 2U); + + // Convert the Data Sync to be from the BS + CDMRSync sync; + sync.addSync(data + 2U, DST_BS_DATA); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeQueue(data); + +#if defined(DUMP_DMR) + openFile(); + writeFile(data); + closeFile(); +#endif + LogMessage("DMR Slot %u, received network CSBK from %u to %u", m_slotNo, csbk.getSrcId(), csbk.getDstId()); + } + break; + + default: + LogWarning("DMR Slot %u, unhandled network CSBK type - 0x%02X", m_slotNo, csbko); + break; + } + } else if (dataType == DT_RATE_12_DATA || dataType == DT_RATE_34_DATA || dataType == DT_RATE_1_DATA) { + if (m_state != RS_RELAYING_NETWORK_DATA) + return; + + // Regenerate the Slot Type CSlotType slotType; slotType.putData(data + 2U); slotType.setColorCode(m_colorCode); @@ -661,14 +757,23 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) CDMRSync sync; sync.addSync(data + 2U, DST_BS_DATA); - data[0U] = TAG_DATA; - data[1U] = 0x00U; + m_blocks--; - writeQueue(data); + data[0U] = m_blocks == 0U ? TAG_EOT : TAG_DATA; + data[1U] = 0x00U; #if defined(DUMP_DMR) writeFile(data); #endif + writeQueue(data); + + if (m_blocks == 0U) { + LogMessage("DMR Slot %u, ended network data transmission", m_slotNo); + writeEndOfTransmission(); + } + } else { + // Unhandled data type + LogWarning("DMR Slot %u, unhandled network data type - 0x%02X", m_slotNo, dataType); } } diff --git a/DMRSlot.h b/DMRSlot.h index f60e84d..75794c3 100644 --- a/DMRSlot.h +++ b/DMRSlot.h @@ -61,6 +61,7 @@ private: CTimer m_packetTimer; CStopWatch m_elapsed; unsigned int m_frames; + unsigned int m_blocks; unsigned int m_lost; CAMBEFEC m_fec; unsigned int m_bits; diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index e8121cb..4c9423e 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -155,6 +155,7 @@ + @@ -198,6 +199,7 @@ + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index 3719e56..37bf08f 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -137,6 +137,9 @@ Header Files + + Header Files + @@ -247,5 +250,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index d40a51d..a0ae707 100644 --- a/Makefile +++ b/Makefile @@ -5,12 +5,12 @@ LDFLAGS = -g all: MMDVMHost -MMDVMHost: AMBEFEC.o BPTC19696.o Conf.o CRC.o CSBK.o Display.o DMRControl.o DMRData.o DMRSlot.o DMRSync.o DStarEcho.o DStarNetwork.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 DMRSlot.o DMRSync.o DStarEcho.o DStarNetwork.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) +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 DStarNetwork.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 \ + DStarNetwork.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) AMBEFEC.o: AMBEFEC.cpp AMBEFEC.h Golay24128.h $(CC) $(CFLAGS) -c AMBEFEC.cpp @@ -35,9 +35,12 @@ DMRControl.o: DMRControl.cpp DMRControl.h DMRSlot.h DMRData.h Modem.h HomebrewDM DMRData.o: DMRData.cpp DMRData.h DMRDefines.h Utils.h Log.h $(CC) $(CFLAGS) -c DMRData.cpp - + +DMRDataHeader.o: DMRDataHeader.cpp DMRDataHeader.h DMRDefines.h Utils.h Log.h CRC.h BPTC19696.h + $(CC) $(CFLAGS) -c DMRDataHeader.cpp + DMRSlot.o: DMRSlot.cpp DMRSlot.h DMRData.h Modem.h HomebrewDMRIPSC.h Defines.h Log.h EmbeddedLC.h RingBuffer.h Timer.h LC.h SlotType.h DMRSync.h FullLC.h \ - EMB.h CRC.h CSBK.h ShortLC.h Utils.h Display.h StopWatch.h AMBEFEC.h + EMB.h CRC.h CSBK.h ShortLC.h Utils.h Display.h StopWatch.h AMBEFEC.h DMRDataHeader.h $(CC) $(CFLAGS) -c DMRSlot.cpp DMRSync.o: DMRSync.cpp DMRSync.h DMRDefines.h