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