From 16ec0db6d8c0df4306eded8fbf49266f7f41dfa4 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 15 Sep 2016 20:28:56 +0100 Subject: [PATCH] Add full FEC decoding to LDU1 and LDU2. --- MMDVMHost.vcxproj | 2 + MMDVMHost.vcxproj.filters | 6 + Makefile | 2 +- Makefile.Pi.Adafruit | 2 +- Makefile.Pi.HD44780 | 2 +- Makefile.Pi.OLED | 2 +- Makefile.Pi.PCF8574 | 2 +- Makefile.Solaris | 2 +- P25Control.cpp | 171 +++++++++++++- P25Control.h | 16 ++ P25Data.cpp | 14 +- P25Data.h | 4 + P25Defines.h | 13 +- P25Network.cpp | 44 +++- P25Network.h | 8 +- RS.cpp | 282 +++++++++++++++++++++++ RS.h | 465 ++++++++++++++++++++++++++++++++++++++ 17 files changed, 1017 insertions(+), 20 deletions(-) create mode 100644 RS.cpp create mode 100644 RS.h diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index e87912f..766a80e 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -191,6 +191,7 @@ + @@ -250,6 +251,7 @@ + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index 2e05124..480dfb2 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -197,6 +197,9 @@ Header Files + + Header Files + @@ -364,5 +367,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index f0c581e..2e8f0f4 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ Golay24128.o Hamming.o Log.o MMDVMHost.o Modem.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o \ - QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \ + QR1676.o RS.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \ YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit index e108428..8af888f 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ Golay24128.o Hamming.o HD44780.o Log.o MMDVMHost.o Modem.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ - P25Utils.o QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o \ + P25Utils.o QR1676.o RS.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o \ YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index 9c12ba4..fbcd68e 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ Golay24128.o Hamming.o HD44780.o Log.o MMDVMHost.o Modem.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ - P25Utils.o QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o \ + P25Utils.o QR1676.o RS.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o \ YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index b9a4dae..0d40a9c 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ Golay24128.o Hamming.o OLED.o Log.o MMDVMHost.o Modem.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ - P25Utils.o QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o \ + P25Utils.o QR1676.o RS.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o \ YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 index 332489a..9cf610e 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ Golay24128.o Hamming.o HD44780.o Log.o MMDVMHost.o Modem.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o \ - P25Utils.o QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o \ + P25Utils.o QR1676.o RS.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o \ YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Solaris b/Makefile.Solaris index 52c6358..b32468e 100644 --- a/Makefile.Solaris +++ b/Makefile.Solaris @@ -10,7 +10,7 @@ OBJECTS = \ AMBEFEC.o BPTC19696.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedLC.o DMRFullLC.o DMRLookup.o DMRLC.o \ DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o \ Golay24128.o Hamming.o Log.o MMDVMHost.o Modem.o Nextion.o NullDisplay.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Utils.o \ - QR1676.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \ + QR1676.o RS.o RS129.o SerialController.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \ YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/P25Control.cpp b/P25Control.cpp index a44d8cc..f34fff9 100644 --- a/P25Control.cpp +++ b/P25Control.cpp @@ -23,8 +23,10 @@ #include "Log.h" #include "Utils.h" -#include #include +#include + +// #define DUMP_P25 const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; @@ -44,9 +46,14 @@ m_rfState(RS_RF_LISTENING), m_netState(RS_NET_IDLE), m_rfTimeout(1000U, timeout), m_netTimeout(1000U, timeout), +m_networkWatchdog(1000U, 0U, 1500U), m_rfFrames(0U), m_rfBits(0U), m_rfErrs(0U), +m_netFrames(0U), +m_netBits(0U), +m_netErrs(0U), +m_netLost(0U), m_nid(), m_audio(), m_rfData(), @@ -73,10 +80,14 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) if (data[0U] == TAG_LOST) { LogMessage("P25, transmission lost, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); - m_display->clearP25(); + if (m_netState == RS_NET_IDLE) + m_display->clearP25(); m_rfState = RS_RF_LISTENING; m_rfTimeout.stop(); m_rfData.reset(); +#if defined(DUMP_P25) + closeFile(); +#endif return false; } @@ -110,6 +121,13 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) m_rfBits = 1U; m_rfTimeout.start(); +#if defined(DUMP_P25) + openFile(); + writeFile(data + 2U, len - 2U); +#endif + + writeNetwork(data + 2U, P25_DUID_HEADER); + if (m_duplex) { data[0U] = TAG_HEADER; data[1U] = 0x00U; @@ -123,6 +141,9 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) m_rfErrs = 0U; m_rfBits = 1U; m_rfTimeout.start(); +#if defined(DUMP_P25) + openFile(); +#endif } // Regenerate Sync @@ -145,6 +166,12 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) // Add busy bits addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); +#if defined(DUMP_P25) + writeFile(data + 2U, len - 2U); +#endif + + writeNetwork(data + 2U, P25_DUID_LDU1); + if (m_duplex) { data[0U] = TAG_DATA; data[1U] = 0x00U; @@ -184,6 +211,12 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) // Add busy bits addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); +#if defined(DUMP_P25) + writeFile(data + 2U, len - 2U); +#endif + + writeNetwork(data + 2U, P25_DUID_LDU2); + if (m_duplex) { data[0U] = TAG_DATA; data[1U] = 0x00U; @@ -209,6 +242,12 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) LogMessage("P25, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); m_display->clearP25(); +#if defined(DUMP_P25) + closeFile(); +#endif + + writeNetwork(data + 2U, P25_DUID_TERM_LC); + if (m_duplex) { data[0U] = TAG_EOT; data[1U] = 0x00U; @@ -231,6 +270,12 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) LogMessage("P25, received RF end of transmission, %.1f seconds, BER: %.1f%%", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); m_display->clearP25(); +#if defined(DUMP_P25) + closeFile(); +#endif + + writeNetwork(data + 2U, P25_DUID_TERM); + if (m_duplex) { data[0U] = TAG_EOT; data[1U] = 0x00U; @@ -258,10 +303,40 @@ unsigned int CP25Control::readModem(unsigned char* data) return len; } +void CP25Control::writeNetwork() +{ + unsigned char data[200U]; + unsigned int length = m_network->read(data, 200U); + if (length == 0U) + return; + + if (m_rfState != RS_RF_LISTENING && m_netState == RS_NET_IDLE) + return; + + m_networkWatchdog.start(); + + +} + void CP25Control::clock(unsigned int ms) { + if (m_network != NULL) + writeNetwork(); + m_rfTimeout.clock(ms); m_netTimeout.clock(ms); + + if (m_netState == RS_NET_AUDIO) { + m_networkWatchdog.clock(ms); + + if (m_networkWatchdog.hasExpired()) { + LogMessage("P25, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_netFrames) / 5.56F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + m_display->clearP25(); + m_networkWatchdog.stop(); + m_netState = RS_NET_IDLE; + m_netTimeout.stop(); + } + } } void CP25Control::writeQueueRF(const unsigned char* data, unsigned int length) @@ -283,6 +358,56 @@ void CP25Control::writeQueueRF(const unsigned char* data, unsigned int length) m_queue.addData(data, len); } +void CP25Control::writeQueueNet(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + if (m_netTimeout.isRunning() && m_netTimeout.hasExpired()) + return; + + unsigned int space = m_queue.freeSpace(); + if (space < (length + 1U)) { + LogError("P25, overflow in the P25 RF queue"); + return; + } + + unsigned char len = length; + m_queue.addData(&len, 1U); + + m_queue.addData(data, len); +} + +void CP25Control::writeNetwork(const unsigned char *data, unsigned char type) +{ + assert(data != NULL); + + if (m_network == NULL) + return; + + if (m_rfTimeout.isRunning() && m_rfTimeout.hasExpired()) + return; + + switch (type) + { + case P25_DUID_HEADER: + m_network->writeHeader(data); + break; + case P25_DUID_LDU1: + m_network->writeLDU1(data); + break; + case P25_DUID_LDU2: + m_network->writeLDU2(data); + break; + case P25_DUID_TERM: + case P25_DUID_TERM_LC: + m_network->writeTerminator(data); + break; + default: + m_network->writeEnd(); + break; + } +} + void CP25Control::addBusyBits(unsigned char* data, unsigned int length, bool b1, bool b2) { assert(data != NULL); @@ -293,3 +418,45 @@ void CP25Control::addBusyBits(unsigned char* data, unsigned int length, bool b1, WRITE_BIT(data, ss1Pos, b2); } } + +bool CP25Control::openFile() +{ + if (m_fp != NULL) + return true; + + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + + char name[100U]; + ::sprintf(name, "P25_%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("P25", 1U, 3U, m_fp); + + return true; +} + +bool CP25Control::writeFile(const unsigned char* data, unsigned char length) +{ + if (m_fp == NULL) + return false; + + ::fwrite(&length, 1U, 1U, m_fp); + + ::fwrite(data, 1U, length, m_fp); + + return true; +} + +void CP25Control::closeFile() +{ + if (m_fp != NULL) { + ::fclose(m_fp); + m_fp = NULL; + } +} diff --git a/P25Control.h b/P25Control.h index 56f3825..3a3d46c 100644 --- a/P25Control.h +++ b/P25Control.h @@ -30,6 +30,8 @@ #include "Modem.h" #include "Timer.h" +#include + class CP25Control { public: CP25Control(unsigned int nac, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup, int rssiMultiplier, int rssiOffset); @@ -54,16 +56,30 @@ private: RPT_NET_STATE m_netState; CTimer m_rfTimeout; CTimer m_netTimeout; + CTimer m_networkWatchdog; unsigned int m_rfFrames; unsigned int m_rfBits; unsigned int m_rfErrs; + unsigned int m_netFrames; + unsigned int m_netBits; + unsigned int m_netErrs; + unsigned int m_netLost; CP25NID m_nid; CP25Audio m_audio; CP25Data m_rfData; CP25Data m_netData; + FILE* m_fp; void writeQueueRF(const unsigned char* data, unsigned int length); + void writeQueueNet(const unsigned char* data, unsigned int length); + void writeNetwork(const unsigned char *data, unsigned char type); + void writeNetwork(); + void addBusyBits(unsigned char* data, unsigned int length, bool b1, bool b2); + + bool openFile(); + bool writeFile(const unsigned char* data, unsigned char length); + void closeFile(); }; #endif diff --git a/P25Data.cpp b/P25Data.cpp index 1daf832..1f78e55 100644 --- a/P25Data.cpp +++ b/P25Data.cpp @@ -31,7 +31,9 @@ const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04 CP25Data::CP25Data() : m_source(0U), m_group(true), -m_dest(0U) +m_dest(0U), +m_rs241213(), +m_rs24169() { } @@ -72,7 +74,9 @@ void CP25Data::processLDU1(unsigned char* data) CP25Utils::decode(data, raw, 1356U, 1398U); decodeLDUHamming(raw, rs + 15U); - CUtils::dump(1U, "P25, LDU1 Data after Hamming", rs, 18U); + m_rs241213.decode(rs); + + CUtils::dump(1U, "P25, LDU1 Data", rs, 9U); switch (rs[0U]) { case P25_LCF_GROUP: @@ -90,8 +94,6 @@ void CP25Data::processLDU1(unsigned char* data) break; } - // XXX Need to add FEC code - encodeLDUHamming(raw, rs + 0U); CP25Utils::encode(raw, data, 410U, 452U); @@ -134,9 +136,9 @@ void CP25Data::processLDU2(unsigned char* data) CP25Utils::decode(data, raw, 1356U, 1398U); decodeLDUHamming(raw, rs + 15U); - CUtils::dump(1U, "P25, LDU2 Data after Hamming", rs, 18U); + m_rs24169.decode(rs); - // XXX Need to add FEC code + CUtils::dump(1U, "P25, LDU2 Data", rs, 12U); encodeLDUHamming(raw, rs + 0U); CP25Utils::encode(raw, data, 410U, 452U); diff --git a/P25Data.h b/P25Data.h index 6f20f72..5928e98 100644 --- a/P25Data.h +++ b/P25Data.h @@ -19,6 +19,8 @@ #if !defined(P25Data_H) #define P25Data_H +#include "RS.h" + class CP25Data { public: CP25Data(); @@ -43,6 +45,8 @@ private: unsigned int m_source; bool m_group; unsigned int m_dest; + CRS241213 m_rs241213; + CRS24169 m_rs24169; void decodeLDUHamming(const unsigned char* raw, unsigned char* data); void encodeLDUHamming(unsigned char* data, const unsigned char* raw); diff --git a/P25Defines.h b/P25Defines.h index fd51bfd..79d006b 100644 --- a/P25Defines.h +++ b/P25Defines.h @@ -53,5 +53,16 @@ const unsigned char P25_DUID_LDU2 = 0x0AU; const unsigned char P25_DUID_PDU = 0x0CU; const unsigned char P25_DUID_TERM_LC = 0x0FU; -#endif +const unsigned char P25_RAW_HDR[] = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x08U, 0xDCU, 0x60U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x18U, 0xEBU, 0x5EU, 0x09U, 0x54U, + 0x9FU, 0x0AU, 0x88U, 0xD5U, 0x03U, 0x67U, 0xCDU, 0x1FU, 0xF1U, 0xD4U, 0x1EU, 0x42U, 0x1EU, 0xA2U, 0x35U, 0x8BU, + 0xFCU, 0xC4U, 0xA9U, 0x78U, 0xDCU, 0x61U, 0x2AU, 0x59U, 0x46U, 0xE3U, 0x0AU, 0x4FU, 0x80U, 0xC7U, 0x54U, 0xC7U, + 0x51U}; +const unsigned char P25_RAW_LDU2[] = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x83U, + 0x80U, 0x00U, 0x00U, 0x00U, 0xAEU, 0x8BU, 0x48U, 0xB6U, 0x49U, 0x9AU, 0xBDU, 0x3CU, 0x7FU, 0x58U}; + +#endif diff --git a/P25Network.cpp b/P25Network.cpp index 10fadeb..de736d1 100644 --- a/P25Network.cpp +++ b/P25Network.cpp @@ -59,7 +59,12 @@ bool CP25Network::open() return m_socket.open(); } -bool CP25Network::writeHeader(const unsigned char* header, unsigned int length) +bool CP25Network::writeStart() +{ + return true; +} + +bool CP25Network::writeHeader(const unsigned char* header) { assert(header != NULL); #ifdef notdef @@ -75,9 +80,9 @@ bool CP25Network::writeHeader(const unsigned char* header, unsigned int length) return true; } -bool CP25Network::writeData(const unsigned char* data, unsigned int length) +bool CP25Network::writeLDU1(const unsigned char* ldu1) { - assert(data != NULL); + assert(ldu1 != NULL); #ifdef notdef unsigned char buffer[30U]; @@ -89,6 +94,39 @@ bool CP25Network::writeData(const unsigned char* data, unsigned int length) return true; } +bool CP25Network::writeLDU2(const unsigned char* ldu2) +{ + assert(ldu2 != NULL); +#ifdef notdef + unsigned char buffer[30U]; + + if (m_debug) + CUtils::dump(1U, "P25 Network Data Sent", buffer, length + 9U); + + return m_socket.write(buffer, length + 9U, m_address, m_port); +#endif + return true; +} + +bool CP25Network::writeTerminator(const unsigned char* term) +{ + assert(term != NULL); +#ifdef notdef + unsigned char buffer[30U]; + + if (m_debug) + CUtils::dump(1U, "P25 Network Data Sent", buffer, length + 9U); + + return m_socket.write(buffer, length + 9U, m_address, m_port); +#endif + return true; +} + +bool CP25Network::writeEnd() +{ + return true; +} + bool CP25Network::writePoll() { #ifdef notdef diff --git a/P25Network.h b/P25Network.h index cd23818..2001f5d 100644 --- a/P25Network.h +++ b/P25Network.h @@ -35,8 +35,12 @@ public: void enable(bool enabled); - bool writeHeader(const unsigned char* header, unsigned int length); - bool writeData(const unsigned char* data, unsigned int length); + bool writeStart(); + bool writeHeader(const unsigned char* header); + bool writeLDU1(const unsigned char* ldu1); + bool writeLDU2(const unsigned char* ldu2); + bool writeTerminator(const unsigned char* term); + bool writeEnd(); unsigned int read(unsigned char* data, unsigned int length); diff --git a/RS.cpp b/RS.cpp new file mode 100644 index 0000000..8c8208f --- /dev/null +++ b/RS.cpp @@ -0,0 +1,282 @@ +/* +* Copyright (C) 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 "RS.h" + +#include +#include + +const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; + +#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + +static int bin2Hex(const unsigned char* input, unsigned int offset) +{ + int output = 0; + + output |= READ_BIT(input, offset + 0U) ? 0x20 : 0x00; + output |= READ_BIT(input, offset + 1U) ? 0x10 : 0x00; + output |= READ_BIT(input, offset + 2U) ? 0x08 : 0x00; + output |= READ_BIT(input, offset + 3U) ? 0x04 : 0x00; + output |= READ_BIT(input, offset + 4U) ? 0x02 : 0x00; + output |= READ_BIT(input, offset + 5U) ? 0x01 : 0x00; + + return output; +} + +static void hex2Bin(int input, unsigned char* output, unsigned int offset) +{ + WRITE_BIT(output, offset + 0U, input & 0x20); + WRITE_BIT(output, offset + 1U, input & 0x10); + WRITE_BIT(output, offset + 2U, input & 0x08); + WRITE_BIT(output, offset + 3U, input & 0x04); + WRITE_BIT(output, offset + 4U, input & 0x02); + WRITE_BIT(output, offset + 5U, input & 0x01); +} + +// tt = (dd-1)/2 +// dd = 17 --> tt = 8 +CRS362017::CRS362017() : +CReedSolomon63<8>() +{ +} + +CRS362017::~CRS362017() +{ +} + +/** +* Does a Reed-Solomon decode adapting the input and output to the expected DSD data format. +* \param hex_data Data packed bits, originally a char[20][6], so containing 20 hex works, each char +* is a bit. Bits are corrected in place. +* \param hex_parity Parity packed bits, originally a char[16][6], 16 hex words. +* \return 1 if irrecoverable errors have been detected, 0 otherwise. +*/ +bool CRS362017::decode(unsigned char* data) +{ + assert(data != NULL); + + int input[63]; + int output[63]; + + // Fill up with zeros to complete the 47 expected hex words of data + for (unsigned int i = 0U; i < 63U; i++) + input[i] = 0; + + // First put the parity data, 16 hex words + unsigned int offset = 120U; + for (unsigned int i = 0U; i < 16U; i++, offset += 6U) + input[i] = bin2Hex(data, offset); + + // Then the 20 hex words of data + offset = 0U; + for (unsigned int i = 16U; i < 36U; i++, offset += 6U) + input[i] = bin2Hex(data, offset); + + // Now we can call decode on the base class + int irrecoverable_errors = CReedSolomon63<8>::decode(input, output); + if (irrecoverable_errors != 0) + return false; + + // Convert it back to binary and put it into hex_data. + offset = 0U; + for (unsigned int i = 16U; i < 36U; i++, offset += 6U) + hex2Bin(output[i], data, offset); + + return true; +} + +void CRS362017::encode(unsigned char* data) +{ + assert(data != NULL); + + int input[47]; + int output[63]; + + // Fill up with zeros to complete the 47 expected hex words of data + for (unsigned int i = 0U; i < 47U; i++) + input[i] = 0; + + // Put the 20 hex words of data + unsigned int offset = 0U; + for (unsigned int i = 0U; i < 20U; i++, offset += 6U) + input[i] = bin2Hex(data, offset); + + // Now we can call encode on the base class + CReedSolomon63<8>::encode(input, output); + + // Convert it back to binary form and put it into the parity + offset = 120U; + for (unsigned int i = 0U; i < 16U; i++, offset += 6U) + hex2Bin(output[i], data, offset); +} + +// tt = (dd-1)/2 +// dd = 13 --> tt = 6 +CRS241213::CRS241213() : +CReedSolomon63<6>() +{ +} + +CRS241213::~CRS241213() +{ +} + +/** +* Does a Reed-Solomon decode adapting the input and output to the expected DSD data format. +* \param hex_data Data packed bits, originally a char[12][6], so containing 12 hex works, each char +* is a bit. Bits are corrected in place. +* \param hex_parity Parity packed bits, originally a char[12][6], 12 hex words. +* \return 1 if irrecoverable errors have been detected, 0 otherwise. +*/ +bool CRS241213::decode(unsigned char* data) +{ + assert(data != NULL); + + int input[63]; + int output[63]; + + // Fill up with zeros to complete the 51 expected hex words of data + for (unsigned int i = 0U; i < 63U; i++) + input[i] = 0; + + // First put the parity data, 12 hex words + unsigned int offset = 72U; + for (unsigned int i = 0U; i < 12U; i++, offset += 6U) + input[i] = bin2Hex(data, offset); + + // Then the 12 hex words of data + offset = 0U; + for (unsigned int i = 12U; i < 24U; i++, offset += 6U) + input[i] = bin2Hex(data, offset); + + // Now we can call decode on the base class + int irrecoverable_errors = CReedSolomon63<6>::decode(input, output); + if (irrecoverable_errors != 0) + return false; + + // Convert it back to binary and put it into hex_data. + offset = 0U; + for (unsigned int i = 12U; i < 24U; i++, offset += 6U) + hex2Bin(output[i], data, offset); + + return true; +} + +void CRS241213::encode(unsigned char* data) +{ + assert(data != NULL); + + int input[51]; + int output[63]; + + // Fill up with zeros to complete the 51 expected hex words of data + for (unsigned int i = 0U; i < 51U; i++) + input[i] = 0; + + // Put the 12 hex words of data + unsigned int offset = 0U; + for (unsigned int i = 0U; i < 12U; i++, offset += 6U) + input[i] = bin2Hex(data, offset); + + // Now we can call encode on the base class + CReedSolomon63<6>::encode(input, output); + + // Convert it back to binary form and put it into the parity + offset = 72U; + for (unsigned int i = 0U; i < 12U; i++, offset += 6U) + hex2Bin(output[i], data, offset); +} + +// tt = (dd-1)/2 +// dd = 9 --> tt = 4 +CRS24169::CRS24169() : +CReedSolomon63<4>() +{ +} + +CRS24169::~CRS24169() +{ +} + +/** +* Does a Reed-Solomon decode adapting the input and output to the expected DSD data format. +* \param hex_data Data packed bits, originally a char[16][6], so containing 16 hex works, each char +* is a bit. Bits are corrected in place. +* \param hex_parity Parity packed bits, originally a char[8][6], 8 hex words. +* \return 1 if irrecoverable errors have been detected, 0 otherwise. +*/ +bool CRS24169::decode(unsigned char* data) +{ + assert(data != NULL); + + int input[63]; + int output[63]; + + // Fill up with zeros to complete the 55 expected hex words of data + for (unsigned int i = 0U; i < 63U; i++) + input[i] = 0; + + // First put the parity data, 8 hex words + unsigned int offset = 96U; + for (unsigned int i = 0U; i < 8U; i++, offset += 6U) + input[i] = bin2Hex(data, offset); + + // Then the 16 hex words of data + offset = 0U; + for (unsigned int i = 8U; i < 24U; i++, offset += 6U) + input[i] = bin2Hex(data, offset); + + // Now we can call decode on the base class + int irrecoverable_errors = CReedSolomon63<4>::decode(input, output); + if (irrecoverable_errors != 0) + return false; + + // Convert it back to binary and put it into hex_data. + offset = 0U; + for (unsigned int i = 8U; i < 24U; i++, offset += 6U) + hex2Bin(output[i], data, offset); + + return true; +} + +void CRS24169::encode(unsigned char* data) +{ + assert(data != NULL); + + int input[55]; + int output[63]; + + // Fill up with zeros to complete the 55 expected hex words of data + for (unsigned int i = 0U; i < 55U; i++) + input[i] = 0; + + // Put the 16 hex words of data + unsigned int offset = 0U; + for (unsigned int i = 0U; i < 16U; i++, offset += 6U) + input[i] = bin2Hex(data, offset); + + // Now we can call encode on the base class + CReedSolomon63<4>::encode(input, output); + + // Convert it back to binary form and put it into the parity + offset = 96U; + for (unsigned int i = 0U; i < 8U; i++, offset += 6U) + hex2Bin(output[i], data, offset); +} diff --git a/RS.h b/RS.h new file mode 100644 index 0000000..a38e6b0 --- /dev/null +++ b/RS.h @@ -0,0 +1,465 @@ +#ifndef REEDSOLOMON_HPP_b1405fdab6374ba2a4e65e8d45ec3d80 +#define REEDSOLOMON_HPP_b1405fdab6374ba2a4e65e8d45ec3d80 + +/** +* Code taken and adapted from www.eccpage.com/rs.c +* Credit goes to Mr Simon Rockliff. +* +* Tried before with the implementation from ITPP library but couldn't make it produce the same outputs +* expected from the P25 transmissions that I have tested. This implementation does work. +*/ + +/* This program is an encoder/decoder for Reed-Solomon codes. Encoding is in +systematic form, decoding via the Berlekamp iterative algorithm. +In the present form , the constants mm, nn, tt, and kk=nn-2tt must be +specified (the double letters are used simply to avoid clashes with +other n,k,t used in other programs into which this was incorporated!) +Also, the irreducible polynomial used to generate GF(2**mm) must also be +entered -- these can be found in Lin and Costello, and also Clark and Cain. + +The representation of the elements of GF(2**m) is either in index form, +where the number is the power of the primitive element alpha, which is +convenient for multiplication (add the powers modulo 2**m-1) or in +polynomial form, where the bits represent the coefficients of the +polynomial representation of the number, which is the most convenient form +for addition. The two forms are swapped between via lookup tables. +This leads to fairly messy looking expressions, but unfortunately, there +is no easy alternative when working with Galois arithmetic. + +The code is not written in the most elegant way, but to the best +of my knowledge, (no absolute guarantees!), it works. +However, when including it into a simulation program, you may want to do +some conversion of global variables (used here because I am lazy!) to +local variables where appropriate, and passing parameters (eg array +addresses) to the functions may be a sensible move to reduce the number +of global variables and thus decrease the chance of a bug being introduced. + +This program does not handle erasures at present, but should not be hard +to adapt to do this, as it is just an adjustment to the Berlekamp-Massey +algorithm. It also does not attempt to decode past the BCH bound -- see +Blahut "Theory and practice of error control codes" for how to do this. + +Simon Rockliff, University of Adelaide 21/9/89 + +26/6/91 Slight modifications to remove a compiler dependent bug which hadn't +previously surfaced. A few extra comments added for clarity. +Appears to all work fine, ready for posting to net! + +Notice +-------- +This program may be freely modified and/or given to whoever wants it. +A condition of such distribution is that the author's contribution be +acknowledged by his name being left in the comments heading the program, +however no responsibility is accepted for any financial or other loss which +may result from some unforseen errors or malfunctioning of the program +during use. +Simon Rockliff, 26th June 1991 +*/ + +#include + +template class CReedSolomon63 +{ +private: + static const int MM = 6; /* RS code over GF(2**mm) */ + static const int NN = 63; /* nn=2**mm -1 length of codeword */ + //int tt; /* number of errors that can be corrected */ + //int kk; /* kk = nn-2*tt */ + static const int KK = NN - 2 * TT; + // distance = nn-kk+1 = 2*tt+1 + + int* alpha_to; + int* index_of; + int* gg; + + void generate_gf(int* generator_polinomial) + /* generate GF(2**mm) from the irreducible polynomial p(X) in pp[0]..pp[mm] + lookup tables: index->polynomial form alpha_to[] contains j=alpha**i; + polynomial form -> index form index_of[j=alpha**i] = i + alpha=2 is the primitive element of GF(2**mm) + */ + { + register int i, mask; + + mask = 1; + alpha_to[MM] = 0; + for (i = 0; i < MM; i++) { + alpha_to[i] = mask; + index_of[alpha_to[i]] = i; + if (generator_polinomial[i] != 0) + alpha_to[MM] ^= mask; + mask <<= 1; + } + index_of[alpha_to[MM]] = MM; + mask >>= 1; + for (i = MM + 1; i < NN; i++) { + if (alpha_to[i - 1] >= mask) + alpha_to[i] = alpha_to[MM] ^ ((alpha_to[i - 1] ^ mask) << 1); + else + alpha_to[i] = alpha_to[i - 1] << 1; + index_of[alpha_to[i]] = i; + } + index_of[0] = -1; + } + + void gen_poly() + /* Obtain the generator polynomial of the tt-error correcting, length + nn=(2**mm -1) Reed Solomon code from the product of (X+alpha**i), i=1..2*tt + */ + { + register int i, j; + + gg[0] = 2; /* primitive element alpha = 2 for GF(2**mm) */ + gg[1] = 1; /* g(x) = (X+alpha) initially */ + for (i = 2; i <= NN - KK; i++) { + gg[i] = 1; + for (j = i - 1; j > 0; j--) + if (gg[j] != 0) + gg[j] = gg[j - 1] ^ alpha_to[(index_of[gg[j]] + i) % NN]; + else + gg[j] = gg[j - 1]; + gg[0] = alpha_to[(index_of[gg[0]] + i) % NN]; /* gg[0] can never be zero */ + } + /* convert gg[] to index form for quicker encoding */ + for (i = 0; i <= NN - KK; i++) + gg[i] = index_of[gg[i]]; + } + +public: + CReedSolomon63() + { + alpha_to = new int[NN + 1]; + index_of = new int[NN + 1]; + gg = new int[NN - KK + 1]; + + // Polynom used in P25 is alpha**6+alpha+1 + int generator_polinomial[] = { 1, 1, 0, 0, 0, 0, 1 }; /* specify irreducible polynomial coeffts */ + + generate_gf(generator_polinomial); + + gen_poly(); + } + + virtual ~CReedSolomon63() + { + delete[] gg; + delete[] index_of; + delete[] alpha_to; + } + + void encode(const int* data, int* bb) + /* take the string of symbols in data[i], i=0..(k-1) and encode systematically + to produce 2*tt parity symbols in bb[0]..bb[2*tt-1] + data[] is input and bb[] is output in polynomial form. + Encoding is done by using a feedback shift register with appropriate + connections specified by the elements of gg[], which was generated above. + Codeword is c(X) = data(X)*X**(nn-kk)+ b(X) */ + { + register int i, j; + int feedback; + + for (i = 0; i < NN - KK; i++) + bb[i] = 0; + for (i = KK - 1; i >= 0; i--) { + feedback = index_of[data[i] ^ bb[NN - KK - 1]]; + if (feedback != -1) { + for (j = NN - KK - 1; j > 0; j--) + if (gg[j] != -1) + bb[j] = bb[j - 1] ^ alpha_to[(gg[j] + feedback) % NN]; + else + bb[j] = bb[j - 1]; + bb[0] = alpha_to[(gg[0] + feedback) % NN]; + } + else { + for (j = NN - KK - 1; j > 0; j--) + bb[j] = bb[j - 1]; + bb[0] = 0; + } + } + } + + int decode(const int* input, int* recd) + /* assume we have received bits grouped into mm-bit symbols in recd[i], + i=0..(nn-1), and recd[i] is polynomial form. + We first compute the 2*tt syndromes by substituting alpha**i into rec(X) and + evaluating, storing the syndromes in s[i], i=1..2tt (leave s[0] zero) . + Then we use the Berlekamp iteration to find the error location polynomial + elp[i]. If the degree of the elp is >tt, we cannot correct all the errors + and hence just put out the information symbols uncorrected. If the degree of + elp is <=tt, we substitute alpha**i , i=1..n into the elp to get the roots, + hence the inverse roots, the error location numbers. If the number of errors + located does not equal the degree of the elp, we have more than tt errors + and cannot correct them. Otherwise, we then solve for the error value at + the error location and correct the error. The procedure is that found in + Lin and Costello. For the cases where the number of errors is known to be too + large to correct, the information symbols as received are output (the + advantage of systematic encoding is that hopefully some of the information + symbols will be okay and that if we are in luck, the errors are in the + parity part of the transmitted codeword). Of course, these insoluble cases + can be returned as error flags to the calling routine if desired. */ + { + register int i, j, u, q; + int elp[NN - KK + 2][NN - KK], d[NN - KK + 2], l[NN - KK + 2], u_lu[NN - KK + + 2], s[NN - KK + 1]; + int count = 0, syn_error = 0, root[TT], loc[TT], z[TT + 1], err[NN], reg[TT + + 1]; + + int irrecoverable_error = 0; + + for (int i = 0; i < NN; i++) + recd[i] = index_of[input[i]]; /* put recd[i] into index form (ie as powers of alpha) */ + + /* first form the syndromes */ + for (i = 1; i <= NN - KK; i++) { + s[i] = 0; + for (j = 0; j < NN; j++) + if (recd[j] != -1) + s[i] ^= alpha_to[(recd[j] + i * j) % NN]; /* recd[j] in index form */ + /* convert syndrome from polynomial form to index form */ + if (s[i] != 0) + syn_error = 1; /* set flag if non-zero syndrome => error */ + s[i] = index_of[s[i]]; + } + + if (syn_error) /* if errors, try and correct */ + { + /* compute the error location polynomial via the Berlekamp iterative algorithm, + following the terminology of Lin and Costello : d[u] is the 'mu'th + discrepancy, where u='mu'+1 and 'mu' (the Greek letter!) is the step number + ranging from -1 to 2*tt (see L&C), l[u] is the + degree of the elp at that step, and u_l[u] is the difference between the + step number and the degree of the elp. + */ + /* initialise table entries */ + d[0] = 0; /* index form */ + d[1] = s[1]; /* index form */ + elp[0][0] = 0; /* index form */ + elp[1][0] = 1; /* polynomial form */ + for (i = 1; i < NN - KK; i++) { + elp[0][i] = -1; /* index form */ + elp[1][i] = 0; /* polynomial form */ + } + l[0] = 0; + l[1] = 0; + u_lu[0] = -1; + u_lu[1] = 0; + u = 0; + + do { + u++; + if (d[u] == -1) { + l[u + 1] = l[u]; + for (i = 0; i <= l[u]; i++) { + elp[u + 1][i] = elp[u][i]; + elp[u][i] = index_of[elp[u][i]]; + } + } + else + /* search for words with greatest u_lu[q] for which d[q]!=0 */ + { + q = u - 1; + while ((d[q] == -1) && (q > 0)) + q--; + /* have found first non-zero d[q] */ + if (q > 0) { + j = q; + do { + j--; + if ((d[j] != -1) && (u_lu[q] < u_lu[j])) + q = j; + } while (j > 0); + }; + + /* have now found q such that d[u]!=0 and u_lu[q] is maximum */ + /* store degree of new elp polynomial */ + if (l[u] > l[q] + u - q) + l[u + 1] = l[u]; + else + l[u + 1] = l[q] + u - q; + + /* form new elp(x) */ + for (i = 0; i < NN - KK; i++) + elp[u + 1][i] = 0; + for (i = 0; i <= l[q]; i++) + if (elp[q][i] != -1) + elp[u + 1][i + u - q] = alpha_to[(d[u] + NN - d[q] + + elp[q][i]) % NN]; + for (i = 0; i <= l[u]; i++) { + elp[u + 1][i] ^= elp[u][i]; + elp[u][i] = index_of[elp[u][i]]; /*convert old elp value to index*/ + } + } + u_lu[u + 1] = u - l[u + 1]; + + /* form (u+1)th discrepancy */ + if (u < NN - KK) /* no discrepancy computed on last iteration */ + { + if (s[u + 1] != -1) + d[u + 1] = alpha_to[s[u + 1]]; + else + d[u + 1] = 0; + for (i = 1; i <= l[u + 1]; i++) + if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0)) + d[u + 1] ^= alpha_to[(s[u + 1 - i] + + index_of[elp[u + 1][i]]) % NN]; + d[u + 1] = index_of[d[u + 1]]; /* put d[u+1] into index form */ + } + } while ((u < NN - KK) && (l[u + 1] <= TT)); + + u++; + if (l[u] <= TT) /* can correct error */ + { + /* put elp into index form */ + for (i = 0; i <= l[u]; i++) + elp[u][i] = index_of[elp[u][i]]; + + /* find roots of the error location polynomial */ + for (i = 1; i <= l[u]; i++) + reg[i] = elp[u][i]; + count = 0; + for (i = 1; i <= NN; i++) { + q = 1; + for (j = 1; j <= l[u]; j++) + if (reg[j] != -1) { + reg[j] = (reg[j] + j) % NN; + q ^= alpha_to[reg[j]]; + }; + if (!q) /* store root and error location number indices */ + { + root[count] = i; + loc[count] = NN - i; + count++; + }; + }; + + if (count == l[u]) /* no. roots = degree of elp hence <= tt errors */ + { + /* form polynomial z(x) */ + for (i = 1; i <= l[u]; i++) /* Z[0] = 1 always - do not need */ + { + if ((s[i] != -1) && (elp[u][i] != -1)) + z[i] = alpha_to[s[i]] ^ alpha_to[elp[u][i]]; + else if ((s[i] != -1) && (elp[u][i] == -1)) + z[i] = alpha_to[s[i]]; + else if ((s[i] == -1) && (elp[u][i] != -1)) + z[i] = alpha_to[elp[u][i]]; + else + z[i] = 0; + for (j = 1; j < i; j++) + if ((s[j] != -1) && (elp[u][i - j] != -1)) + z[i] ^= alpha_to[(elp[u][i - j] + s[j]) % NN]; + z[i] = index_of[z[i]]; /* put into index form */ + }; + + /* evaluate errors at locations given by error location numbers loc[i] */ + for (i = 0; i < NN; i++) { + err[i] = 0; + if (recd[i] != -1) /* convert recd[] to polynomial form */ + recd[i] = alpha_to[recd[i]]; + else + recd[i] = 0; + } + for (i = 0; i < l[u]; i++) /* compute numerator of error term first */ + { + err[loc[i]] = 1; /* accounts for z[0] */ + for (j = 1; j <= l[u]; j++) + if (z[j] != -1) + err[loc[i]] ^= alpha_to[(z[j] + j * root[i]) % NN]; + if (err[loc[i]] != 0) { + err[loc[i]] = index_of[err[loc[i]]]; + q = 0; /* form denominator of error term */ + for (j = 0; j < l[u]; j++) + if (j != i) + q += index_of[1 + ^ alpha_to[(loc[j] + root[i]) % NN]]; + q = q % NN; + err[loc[i]] = alpha_to[(err[loc[i]] - q + NN) % NN]; + recd[loc[i]] ^= err[loc[i]]; /*recd[i] must be in polynomial form */ + } + } + } + else { + /* no. roots != degree of elp => >tt errors and cannot solve */ + irrecoverable_error = 1; + } + + } + else { + /* elp has degree >tt hence cannot solve */ + irrecoverable_error = 1; + } + + } + else { + /* no non-zero syndromes => no errors: output received codeword */ + for (i = 0; i < NN; i++) + if (recd[i] != -1) /* convert recd[] to polynomial form */ + recd[i] = alpha_to[recd[i]]; + else + recd[i] = 0; + } + + if (irrecoverable_error) { + for (i = 0; i < NN; i++) /* could return error flag if desired */ + if (recd[i] != -1) /* convert recd[] to polynomial form */ + recd[i] = alpha_to[recd[i]]; + else + recd[i] = 0; /* just output received codeword as is */ + } + + return irrecoverable_error; + } +}; + +/** +* Convenience class that does a Reed-Solomon (36,20,17) error correction adapting input and output to +* the DSD data format: hex words packed as char arrays. +*/ +class CRS362017 : public CReedSolomon63<8> +{ +public: + CRS362017(); + ~CRS362017(); + + bool decode(unsigned char* data); + + void encode(unsigned char* data); + +private: +}; + +/** +* Convenience class that does a Reed-Solomon (24,12,13) error correction adapting input and output to +* the DSD data format: hex words packed as char arrays. +*/ +class CRS241213 : public CReedSolomon63<6> +{ +public: + CRS241213(); + ~CRS241213(); + + bool decode(unsigned char* data); + + void encode(unsigned char* data); + +private: +}; + +/** +* Convenience class that does a Reed-Solomon (24,16,9) error correction adapting input and output to +* the DSD data format: hex words packed as char arrays. +*/ +class CRS24169 : public CReedSolomon63<4> +{ +public: + CRS24169(); + ~CRS24169(); + + bool decode(unsigned char* data); + + void encode(unsigned char* data); + +private: +}; + +#endif