Add full FEC decoding to LDU1 and LDU2.
This commit is contained in:
parent
9423fb25a6
commit
16ec0db6d8
|
@ -191,6 +191,7 @@
|
|||
<ClInclude Include="P25Utils.h" />
|
||||
<ClInclude Include="QR1676.h" />
|
||||
<ClInclude Include="RingBuffer.h" />
|
||||
<ClInclude Include="RS.h" />
|
||||
<ClInclude Include="RS129.h" />
|
||||
<ClInclude Include="SerialController.h" />
|
||||
<ClInclude Include="SHA256.h" />
|
||||
|
@ -250,6 +251,7 @@
|
|||
<ClCompile Include="P25NID.cpp" />
|
||||
<ClCompile Include="P25Utils.cpp" />
|
||||
<ClCompile Include="QR1676.cpp" />
|
||||
<ClCompile Include="RS.cpp" />
|
||||
<ClCompile Include="RS129.cpp" />
|
||||
<ClCompile Include="SerialController.cpp" />
|
||||
<ClCompile Include="SHA256.cpp" />
|
||||
|
|
|
@ -197,6 +197,9 @@
|
|||
<ClInclude Include="DMRNetwork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RS.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BPTC19696.cpp">
|
||||
|
@ -364,5 +367,8 @@
|
|||
<ClCompile Include="DMRNetwork.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RS.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
2
Makefile
2
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
171
P25Control.cpp
171
P25Control.cpp
|
@ -23,8 +23,10 @@
|
|||
#include "Log.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <ctime>
|
||||
|
||||
// #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;
|
||||
}
|
||||
}
|
||||
|
|
16
P25Control.h
16
P25Control.h
|
@ -30,6 +30,8 @@
|
|||
#include "Modem.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
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
|
||||
|
|
14
P25Data.cpp
14
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
13
P25Defines.h
13
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
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);
|
||||
}
|
|
@ -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 <cmath>
|
||||
|
||||
template<int TT> 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
|
Loading…
Reference in New Issue