This commit is contained in:
Kurt Moraw 2020-11-05 19:11:57 +01:00
parent f72c347e30
commit caff38495d
73 changed files with 21836 additions and 225449 deletions

View File

@ -2,9 +2,14 @@
The purpose of this project is to transfer data (pictures...) via a 2,7kHz SSB channel on the narrow band transponder as fast as possible.
# this is work in progress
Version 0.2 is working on my linux PC, Odroid SBC and Raspberry 4 (3B+)
Version 0.2 is working on:
Windows 10 (should work on Win7, not tested)
linux Desktop PC,
Odroid SBC
Raspberry 4 (3B+)
# Prerequisites
* Windopws 10 Desktop PC ... working
* LINUX Desktop PC ... working
* Raspberry PI 4 ... working
* Raspberry PI 3B+ ... working, but not 100% error free in fullduplex mode (RX only or TX only is working)
@ -12,28 +17,33 @@ Version 0.2 is working on my linux PC, Odroid SBC and Raspberry 4 (3B+)
* Odroid C2 ... working
* Odroid C4 ... working
* GNU Radio Version 3.8.x.
* Raspberry: Raspian OS ist NOT working, instead Ubuntu 64bit is required
* Application Software "oscardata.exe" running on Windows, Linux, (possibly MAC-OS, not tested)
# building the software
1. go into the folder "modem"
* Linux
1. go into the folder "hsmodem"
2. run "make"
3. the executable is in folder LinuxRelease
* Windows
1. load hsmodem.sln in VisualStudio-19 and build Release version.
2. the executable is in folder WinRelease
# starting the modem and application
1. go into the folder "modem"
2. run the software: ./qo100modem
command line parameters:
you need to run 2 programs, the first one is "hsmodem" which runs in a termimal without GUI. This is the modem doing all modulation and demodulation staff.
The second program is the user interface "oscardata.exe".
1. go into the folder "WinRelease" or "LinuxRelease"
2. run the software: ./hs100modem.exe or ./hsmodem
optional command line parameter:
no parameter ... normal usage
-m IP ... specify the V4 IP adress of the device where the application software is running. This is useful if you have more than one qo100modem running simultaneously. Without this parameter the app will search the modem automatically.
-e 1 ... do NOT start the GNU Radio files automatically. This is useful if you want to work on the GR Flowgraphs and want to start it manually.
3. start the user application on any PC in your home network. It will find the modem automatically
The file is located in QO-100-modem/oscardata/oscardata/bin/Release
The file is located in oscardata/oscardata/bin/Release
On windows just start oscardata.exe
On Linux start it with: mono oscardata.exe
@ -42,6 +52,9 @@ On Linux start it with: mono oscardata.exe
* QO-100 via IC-9700, IC-7300 or IC-7100 ... working
* Short Wave 6m band via IC-7300, IC-7100 ... working. In case of significant noise, use the lowest bit rate (3000 bit/s)
# TODOs
the current version V0.2 runs very fine on Linux but shows a higher bit error rate on Windows. This has to do with the initialisation of the sound card. The default sound bitrate setting in the Windows-Sound-Settings implement some kind of "filtering". This is currently under evaluation.
# usage
In the IC-9700 activate the DATA mode and the RX filter FIL1 to full range of 3.6kHz.

BIN
WinRelease/bass.dll Executable file

Binary file not shown.

BIN
WinRelease/hsmodem.exe Executable file

Binary file not shown.

BIN
WinRelease/libfftw3-3.dll Executable file

Binary file not shown.

BIN
WinRelease/libgcc_s_dw2-1.dll Executable file

Binary file not shown.

BIN
WinRelease/libliquid.dll Executable file

Binary file not shown.

54
hsmodem.sln Executable file
View File

@ -0,0 +1,54 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30517.126
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hsmodem", "hsmodem\hsmodem.vcxproj", "{E6292FAA-E794-4107-BD89-2310BCDBC858}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
64bit|Any CPU = 64bit|Any CPU
64bit|Mixed Platforms = 64bit|Mixed Platforms
64bit|Win32 = 64bit|Win32
64bit|x64 = 64bit|x64
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E6292FAA-E794-4107-BD89-2310BCDBC858}.64bit|Any CPU.ActiveCfg = 64bit|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.64bit|Any CPU.Build.0 = 64bit|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.64bit|Mixed Platforms.ActiveCfg = 64bit|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.64bit|Mixed Platforms.Build.0 = 64bit|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.64bit|Win32.ActiveCfg = 64bit|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.64bit|Win32.Build.0 = 64bit|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.64bit|x64.ActiveCfg = 64bit|x64
{E6292FAA-E794-4107-BD89-2310BCDBC858}.64bit|x64.Build.0 = 64bit|x64
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Debug|Any CPU.ActiveCfg = Debug|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Debug|Win32.ActiveCfg = Debug|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Debug|Win32.Build.0 = Debug|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Debug|x64.ActiveCfg = Debug|x64
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Debug|x64.Build.0 = Debug|x64
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Release|Any CPU.ActiveCfg = Release|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Release|Any CPU.Build.0 = Release|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Release|Mixed Platforms.Build.0 = Release|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Release|Win32.ActiveCfg = Release|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Release|Win32.Build.0 = Release|Win32
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Release|x64.ActiveCfg = Release|x64
{E6292FAA-E794-4107-BD89-2310BCDBC858}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4CD5C45F-4015-48B4-A51D-E5D8D066732E}
EndGlobalSection
EndGlobal

12
hsmodem/Makefile Executable file
View File

@ -0,0 +1,12 @@
# makefile for dv_serial
CXXFLAGS = -Wall -O3 -std=c++0x -Wno-write-strings -Wno-narrowing
LDFLAGS = -lpthread -lrt -lsndfile -lasound -lm -lbass -lfftw3 -lfftw3_threads -lliquid
OBJ = hsmodem.o constellation.o crc16.o frame_packer.o main_helper.o scrambler.o speed.o fec.o audio.o udp.o fft.o liquid_if.o
default: $(OBJ)
g++ $(CXXFLAGS) -o ../LinuxRelease/hsmodem $(OBJ) $(LDFLAGS)
clean:
rm -f *.o ../LinuxRelease/hsmodem

414
hsmodem/audio.cpp Executable file
View File

@ -0,0 +1,414 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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.
*
* audio.c ... functions to handle audio in/out via a soundcard
* uses the "BASS" library
*
* captures samples from the sound card.
* Samples are 32-bit floats in a range of -1 to +1
* get these samples from the thread safe fifo: cap_read_fifo(&floatvariable)
*
* plays samples to the sound card
* Samples are 32-bit floats in a range of -1 to +1
* play the samples by calling the thread save function: pb_write_fifo(floatsample)
*
*/
#include "hsmodem.h"
BOOL CALLBACK RecordingCallback(HRECORD handle, const void *buffer, DWORD length, void *user);
DWORD CALLBACK WriteStream(HSTREAM handle, float *buffer, DWORD length, void *user);
int pb_read_fifo(float *data, int elements);
void close_audio();
void cap_write_fifo(float sample);
int pb_fifo_freespace(int nolock);
void init_pipes();
#define CHANNELS 1 // no of channels used
HRECORD rchan = 0; // recording channel
BASS_INFO info;
HSTREAM stream = 0;
/*void showDeviceInfo(BASS_DEVICEINFO info)
{
if (info.flags & BASS_DEVICE_ENABLED) printf("%s\n","BASS_DEVICE_ENABLED ");
if (info.flags & BASS_DEVICE_DEFAULT) printf("%s\n","BASS_DEVICE_DEFAULT ");
if (info.flags & BASS_DEVICE_INIT) printf("%s\n","BASS_DEVICE_INIT ");
if (info.flags & BASS_DEVICE_LOOPBACK) printf("%s\n","BASS_DEVICE_LOOPBACK ");
if (info.flags & BASS_DEVICE_TYPE_DIGITAL) printf("%s\n","BASS_DEVICE_TYPE_DIGITAL ");
if (info.flags & BASS_DEVICE_TYPE_DISPLAYPORT) printf("%s\n","BASS_DEVICE_TYPE_DISPLAYPORT ");
if (info.flags & BASS_DEVICE_TYPE_HANDSET) printf("%s\n","BASS_DEVICE_TYPE_HANDSET ");
if (info.flags & BASS_DEVICE_TYPE_HDMI) printf("%s\n","BASS_DEVICE_TYPE_HDMI ");
if (info.flags & BASS_DEVICE_TYPE_HEADPHONES) printf("%s\n","BASS_DEVICE_TYPE_HEADPHONES ");
if (info.flags & BASS_DEVICE_TYPE_HEADSET) printf("%s\n","BASS_DEVICE_TYPE_HEADSET ");
if (info.flags & BASS_DEVICE_TYPE_LINE) printf("%s\n","BASS_DEVICE_TYPE_LINE ");
if (info.flags & BASS_DEVICE_TYPE_MICROPHONE) printf("%s\n","BASS_DEVICE_TYPE_MICROPHONE ");
if (info.flags & BASS_DEVICE_TYPE_NETWORK) printf("%s\n","BASS_DEVICE_TYPE_NETWORK ");
if (info.flags & BASS_DEVICE_TYPE_SPDIF) printf("%s\n","BASS_DEVICE_TYPE_SPDIF ");
if (info.flags & BASS_DEVICE_TYPE_SPEAKERS) printf("%s\n","BASS_DEVICE_TYPE_SPEAKERS ");
}*/
#define MAXDEVSTRLEN 2000
uint8_t devstring[MAXDEVSTRLEN +100];
char PBdevs[100][256]; // stores the device names, just for diagnosis, has no real fuction
char CAPdevs[100][256];
// build string of audio devices, to be sent to application as response to Broadcast search
void enumerateAudioDevices()
{
memset(devstring, 0, sizeof(devstring));
devstring[0] = 3; // ID for this UDP message
// playback devices
int a;
int idx = 0;
BASS_DEVICEINFO info;
strcat((char*)(devstring + 1), "System Default");
strcat((char*)(devstring + 1), "~");
strcpy(PBdevs[idx++], "System Default");
for (a = 1; BASS_GetDeviceInfo(a, &info); a++)
{
printf("PB device:%d = %s\n", a, info.name);
if (strlen((char*)(devstring+1)) > MAXDEVSTRLEN) break;
if (info.flags & BASS_DEVICE_ENABLED)
{
strncpy(PBdevs[idx], info.name, 255);
PBdevs[idx][255] = 0;
idx++;
strcat((char*)(devstring + 1), info.name);
strcat((char*)(devstring + 1), "~"); // audio device separator
}
}
strcat((char*)(devstring + 1), "^"); // PB, CAP separator
// capture devices
BASS_DEVICEINFO recinfo;
idx = 0;
strcat((char*)(devstring + 1), "System Default");
strcat((char*)(devstring + 1), "~");
strcpy(CAPdevs[idx++], "System Default");
for (a = 0; BASS_RecordGetDeviceInfo(a, &recinfo); a++)
{
printf("CAP device:%d = %s\n", a, recinfo.name);
if (strlen((char*)(devstring + 1)) > MAXDEVSTRLEN) break;
if (recinfo.flags & BASS_DEVICE_ENABLED)
{
strncpy(CAPdevs[idx], recinfo.name, 255);
CAPdevs[idx][255] = 0;
idx++;
strcat((char*)(devstring + 1), recinfo.name);
strcat((char*)(devstring + 1), "~");
}
}
}
/*
* Audio Device numbering:
*
* Playback:
* 0 ... no audio, we use 0 for default, which is -1
* 1 ... audio devices
*
* Record:
* 0 ... audio devices
* we insert "Default" at position 0, and let the audio devices start with 1, so we are compatible with playback
* but in init_audio() we have to substract 1
*/
uint8_t* getAudioDevicelist(int *len)
{
*len = strlen((char*)(devstring+1))+1;
return devstring;
}
// pbdev, capdev: -1=default device
int init_audio(int pbdev, int capdev)
{
static int f = 1;
int ocd = capdev;
// PB devices start with 1 (0 not used, but here used for Default which is -1)
if (pbdev == 255 || pbdev == 0) pbdev = -1;
// CAP devices start with 0, but we use 0 for Default (-1)
// so we have to substract 1 from the real devices
if (capdev == 255 || capdev == 0 || capdev == -1) capdev = -1;
else capdev--;
if (f == 1)
{
f = 0;
enumerateAudioDevices();
init_pipes();
}
close_audio();
printf("init audio, caprate:%d\n",caprate);
if (pbdev != -1)
printf("playback device %d: %s\n", pbdev, PBdevs[pbdev]);
else
printf("playback device %d: %s\n", pbdev, "Default");
if (capdev != -1)
printf("capture device %d: %s\n", capdev, CAPdevs[ocd]);
else
printf("capture device %d: %s\n", capdev, "Default");
// check the correct BASS was loaded
if (HIWORD(BASS_GetVersion()) != BASSVERSION)
{
printf("An incorrect version of BASS was loaded\n");
return -1;
}
// initalize default recording device
if (!BASS_RecordInit(capdev))
{
printf("Can't initialize recording device: %d\n", BASS_ErrorGetCode());
return -1;
}
// initialize default output device
if (!BASS_Init(pbdev, caprate, 0, NULL, NULL))
{
printf("Can't initialize output device\n");
return -1;
}
// set capture callback
rchan = BASS_RecordStart(caprate, CHANNELS, BASS_SAMPLE_FLOAT, RecordingCallback, 0);
if (!rchan) {
printf("Can't start capturing: %d\n", BASS_ErrorGetCode());
return -1;
}
// set play callback
BASS_GetInfo(&info);
stream = BASS_StreamCreate(info.freq, CHANNELS, BASS_SAMPLE_FLOAT, (STREAMPROC*)WriteStream, 0); // sample: 32 bit float
BASS_ChannelSetAttribute(stream, BASS_ATTRIB_BUFFER, 0); // no buffering for minimum latency
BASS_ChannelPlay(stream, FALSE); // start it
printf("audio initialized\n");
return 0;
}
void close_audio()
{
if(stream != 0)
{
printf("!close Audio Devices\n");
BASS_ChannelStop(rchan);
int rr = BASS_RecordFree();
if (!rr) printf("Bass_RecordFree error: %d\n", BASS_ErrorGetCode());
BASS_StreamFree(stream);
int r = BASS_Free();
if(!r) printf("Bass_Free error: %d\n", BASS_ErrorGetCode());
stream = 0;
}
}
// capture callback
// length: bytes. short=2byte, 2channels, so it requests samples*4
BOOL CALLBACK RecordingCallback(HRECORD handle, const void *buffer, DWORD length, void *user)
{
//printf("captured %ld samples\n",length/sizeof(float));
//measure_speed(length/sizeof(float));
float *fbuffer = (float *)buffer;
//showbytestringf((char*)"cap:", fbuffer, 10);
//printf("w:%ld ",length/sizeof(float));
for(unsigned int i=0; i<(length/sizeof(float)); i+=CHANNELS)
{
//printf("%f\n",fbuffer[i]);
cap_write_fifo(fbuffer[i]);
}
return TRUE; // continue recording
}
// play callback
// length: bytes. float=4byte, 2channels, so it requests samples*8
DWORD CALLBACK WriteStream(HSTREAM handle, float *buffer, DWORD length, void *user)
{
//printf("requested %ld samples\n", length / sizeof(float));
int ret = pb_read_fifo(buffer, length / sizeof(float));
if(ret == 0)
{
// fifo empty, send 00
memset(buffer,0,length);
}
return length;
}
// ================ thread safe fifo for audio callback routines ===============
#ifdef _WIN32_
CRITICAL_SECTION cap_crit_sec;
CRITICAL_SECTION pb_crit_sec;
#define CAP_LOCK EnterCriticalSection(&cap_crit_sec)
#define PB_LOCK EnterCriticalSection(&pb_crit_sec)
void CAP_UNLOCK()
{
if (&cap_crit_sec != NULL)
LeaveCriticalSection(&cap_crit_sec);
}
void PB_UNLOCK()
{
if (&pb_crit_sec != NULL)
LeaveCriticalSection(&pb_crit_sec);
}
#endif
#ifdef _LINUX_
pthread_mutex_t cap_crit_sec;
pthread_mutex_t pb_crit_sec;
#define CAP_LOCK pthread_mutex_lock(&cap_crit_sec)
void CAP_UNLOCK() { pthread_mutex_unlock(&cap_crit_sec); }
#define PB_LOCK pthread_mutex_lock(&pb_crit_sec)
void PB_UNLOCK() { pthread_mutex_unlock(&pb_crit_sec); }
#endif
#define AUDIO_BUFFERMAXTIME 2 // fifo can buffer this time in [s]
#define AUDIO_PLAYBACK_BUFLEN (48000 * 10) // space for 10 seconds of samples
#define AUDIO_CAPTURE_BUFLEN (48000 * 10)
int cap_wridx=0;
int cap_rdidx=0;
float cap_buffer[AUDIO_CAPTURE_BUFLEN];
int pb_wridx=0;
int pb_rdidx=0;
float pb_buffer[AUDIO_PLAYBACK_BUFLEN];
void init_pipes()
{
#ifdef _WIN32_
if (&cap_crit_sec != NULL) DeleteCriticalSection(&cap_crit_sec);
InitializeCriticalSection(&cap_crit_sec);
if (&pb_crit_sec != NULL) DeleteCriticalSection(&pb_crit_sec);
InitializeCriticalSection(&pb_crit_sec);
#endif
}
// write one sample into the fifo
// overwrite old data if the fifo is full
void cap_write_fifo(float sample)
{
CAP_LOCK;
cap_buffer[cap_wridx] = sample;
if(++cap_wridx >= AUDIO_CAPTURE_BUFLEN) cap_wridx = 0;
CAP_UNLOCK();
}
int cap_read_fifo(float *data)
{
CAP_LOCK;
if (cap_rdidx == cap_wridx)
{
// Fifo empty, no data available
CAP_UNLOCK();
return 0;
}
*data = cap_buffer[cap_rdidx];
if(++cap_rdidx >= AUDIO_CAPTURE_BUFLEN) cap_rdidx = 0;
CAP_UNLOCK();
return 1;
}
void pb_write_fifo(float sample)
{
PB_LOCK;
// check if there is free space in fifo
if(pb_fifo_freespace(1) == 0)
{
PB_UNLOCK();
printf("************* pb fifo full\n");
return;
}
pb_buffer[pb_wridx] = sample;
if(++pb_wridx >= AUDIO_PLAYBACK_BUFLEN) pb_wridx = 0;
PB_UNLOCK();
//printf("write: pbw:%d pbr:%d\n",pb_wridx,pb_rdidx);
}
void pb_write_fifo_clear()
{
pb_wridx = pb_rdidx = 0;
}
int pb_fifo_freespace(int nolock)
{
int freebuf = 0;
if(nolock == 0) PB_LOCK;
int elemInFifo = (pb_wridx + AUDIO_PLAYBACK_BUFLEN - pb_rdidx) % AUDIO_PLAYBACK_BUFLEN;
freebuf = AUDIO_PLAYBACK_BUFLEN - elemInFifo;
if(nolock == 0) PB_UNLOCK();
//printf("fifolen:%d check: pbw:%d pbr:%d freebuf:%d\n",AUDIO_PLAYBACK_BUFLEN,pb_wridx,pb_rdidx,freebuf);
return freebuf;
}
// read elements floats from fifo or return 0 if not enough floats are available
int pb_read_fifo(float *data, int elements)
{
//printf("pb read fifo: %d\n",elements);
PB_LOCK;
int e = AUDIO_PLAYBACK_BUFLEN - pb_fifo_freespace(1);
if(e < elements)
{
// Fifo empty, no data available
PB_UNLOCK();
//printf("pb fifo empty, need:%d have:%d size:%d\n",elements,e,AUDIO_PLAYBACK_BUFLEN);
return 0;
}
for(int i=0; i<elements; i++)
{
data[i] = pb_buffer[pb_rdidx];
if(++pb_rdidx >= AUDIO_PLAYBACK_BUFLEN) pb_rdidx = 0;
}
//printf("read %d floats\n",elements);
PB_UNLOCK();
return 1;
}

1160
hsmodem/bass.h Executable file

File diff suppressed because it is too large Load Diff

BIN
hsmodem/bass.lib Executable file

Binary file not shown.

207
hsmodem/constellation.cpp Executable file
View File

@ -0,0 +1,207 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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 "hsmodem.h"
// functions for non-differential QPSK
// depending on the phase shift rotate a data blocks constellation
//uint8_t headerbytes[HEADERLEN] = {0x53, 0xe1, 0xa6};
// corresponds to these QPSK symbols:
// bits: 01010011 11100001 10100110
// syms: 1 1 0 3 3 2 0 1 2 2 1 2
uint8_t rxbytebuf[UDPBLOCKLEN+100]; // +100 ... reserve, just to be sure
uint8_t *convertQPSKSymToBytes(uint8_t *rxsymbols)
{
int sidx = 0;
for(int i=0; i<UDPBLOCKLEN; i++)
{
rxbytebuf[i] = rxsymbols[sidx++] << (bitsPerSymbol*3);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol*2);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol*1);
rxbytebuf[i] |= rxsymbols[sidx++] << (bitsPerSymbol*0);
}
return rxbytebuf;
}
void convertBytesToSyms_QPSK(uint8_t *bytes, uint8_t *syms, int bytenum)
{
unsigned int symidx = 0;
for(int i=0; i<bytenum; i++)
{
syms[symidx++] = (bytes[i] >> 6) & 3;
syms[symidx++] = (bytes[i] >> 4) & 3;
syms[symidx++] = (bytes[i] >> 2) & 3;
syms[symidx++] = (bytes[i] >> 0) & 3;
}
}
void rotateQPSKsyms(uint8_t *src, uint8_t *dst, int len)
{
for(int i=0; i<len; i++)
{
dst[i] = src[i] + 4;
dst[i]++;
dst[i] %= 4;
}
}
uint8_t QPSK_backbuf[UDPBLOCKLEN*8/2];
uint8_t *rotateBackQPSK(uint8_t *buf, int len, int rotations)
{
memcpy(QPSK_backbuf,buf,len);
for(unsigned int i=0; i<(unsigned int)len; i++)
{
for(int r=0; r<rotations; r++)
{
QPSK_backbuf[i] += 4;
QPSK_backbuf[i]--;
QPSK_backbuf[i] %= 4;
}
}
return QPSK_backbuf;
}
// works ONLY if number of bytes is a multiple of 3 !!!
void convertBytesToSyms_8PSK(uint8_t *bytes, uint8_t *syms, int bytenum)
{
unsigned int symidx = 0;
for(int i=0; i<bytenum; i+=3)
{
// convert next 3 bytes to 8 syms
syms[symidx++] = (bytes[0+i] >> 5) & 7;
syms[symidx++] = (bytes[0+i] >> 2) & 7;
syms[symidx++] = ((bytes[0+i] & 3) << 1) | ((bytes[1+i] >> 7) & 1);
syms[symidx++] = (bytes[1+i] >> 4) & 7;
syms[symidx++] = (bytes[1+i] >> 1) & 7;
syms[symidx++] = ((bytes[1+i] & 1) << 2) | ((bytes[2+i] >> 6) & 3);
syms[symidx++] = (bytes[2+i] >> 3) & 7;
syms[symidx++] = bytes[2+i] & 7;
}
}
void rotate8PSKsyms(uint8_t *src, uint8_t *dst, int len)
{
for(int i=0; i<len; i++)
{
dst[i] = src[i] + 8;
dst[i]++;
dst[i] %= 8;
}
}
void rotate8APSKsyms(uint8_t *src, uint8_t *dst, int len)
{
for(int i=0; i<len; i++)
{
if(src[i] == 0) dst[i] = 0;
else if(src[i] == 1) dst[i] = 4;
else if(src[i] == 2) dst[i] = 3;
else if(src[i] == 3) dst[i] = 1;
else if(src[i] == 4) dst[i] = 5;
else if(src[i] == 5) dst[i] = 7;
else if(src[i] == 6) dst[i] = 2;
else if(src[i] == 7) dst[i] = 6;
}
}
uint8_t _8PSK_backbuf[UDPBLOCKLEN*8/3];
uint8_t *rotateBack8PSK(uint8_t *buf, int len, int rotations)
{
memcpy(_8PSK_backbuf,buf,len);
for(int i=0; i<len; i++)
{
for(int r=0; r<rotations; r++)
{
_8PSK_backbuf[i] += 8;
_8PSK_backbuf[i]--;
_8PSK_backbuf[i] %= 8;
}
}
return _8PSK_backbuf;
}
uint8_t *rotateBack8APSK(uint8_t *buf, int len, int rotations)
{
memcpy(_8PSK_backbuf,buf,len);
for(int i=0; i<len; i++)
{
for(int r=0; r<rotations; r++)
{
if(_8PSK_backbuf[i] == 0) _8PSK_backbuf[i] = 0;
else if(_8PSK_backbuf[i] == 4) _8PSK_backbuf[i] = 1;
else if(_8PSK_backbuf[i] == 3) _8PSK_backbuf[i] = 2;
else if(_8PSK_backbuf[i] == 1) _8PSK_backbuf[i] = 3;
else if(_8PSK_backbuf[i] == 5) _8PSK_backbuf[i] = 4;
else if(_8PSK_backbuf[i] == 7) _8PSK_backbuf[i] = 5;
else if(_8PSK_backbuf[i] == 2) _8PSK_backbuf[i] = 6;
else if(_8PSK_backbuf[i] == 6) _8PSK_backbuf[i] = 7;
}
}
return _8PSK_backbuf;
}
uint8_t *convert8PSKSymToBytes(uint8_t *rxsymbols, int len)
{
int sidx = 0;
// works ONLY if total frame length is a multiple of 3 !
for(int i=0; i<len; i+=3)
{
rxbytebuf[i] = rxsymbols[sidx++] << 5;
rxbytebuf[i] |= rxsymbols[sidx++] << 2;
rxbytebuf[i] |= rxsymbols[sidx] >> 1;
rxbytebuf[i+1] = rxsymbols[sidx++] << 7;
rxbytebuf[i+1] |= rxsymbols[sidx++] << 4;
rxbytebuf[i+1] |= rxsymbols[sidx++] << 1;
rxbytebuf[i+1] |= rxsymbols[sidx] >> 2;
rxbytebuf[i+2] = rxsymbols[sidx++] << 6;
rxbytebuf[i+2] |= rxsymbols[sidx++] << 3;
rxbytebuf[i+2] |= rxsymbols[sidx++];
}
return rxbytebuf;
}
void shiftleft(uint8_t *data, int shiftnum, int len)
{
for(int j=0; j<shiftnum; j++)
{
int b1=0,b2=0;
for(int i=len-1; i>=0; i--)
{
b1 = (data[i] & 0x80)>>7;
data[i] <<= 1;
data[i] |= b2;
b2 = b1;
}
}
}

83
hsmodem/crc16.cpp Executable file
View File

@ -0,0 +1,83 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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 "hsmodem.h"
// since we use a static crc register we need TWO separated registers
// for RX and TX to get it thread safe, no.2 is for file ID generation
uint16_t reg16[3] = {0xffff,0xffff}; // shift register
uint16_t Crc16_bytecalc(int rxtx, uint8_t byt)
{
uint16_t polynom = 0x8408; // generator polynom
for (int i = 0; i < 8; ++i)
{
if ((reg16[rxtx] & 1) != (byt & 1))
reg16[rxtx] = (uint16_t)((reg16[rxtx] >> 1) ^ polynom);
else
reg16[rxtx] >>= 1;
byt >>= 1;
}
return reg16[rxtx];
}
uint16_t Crc16_messagecalc(int rxtx, uint8_t *data,int len)
{
reg16[rxtx] = 0xffff;
for (int i = 0; i < len; i++)
reg16[rxtx] = Crc16_bytecalc(rxtx,data[i]);
return reg16[rxtx];
}
// =================================================================
uint32_t reg32[2] = {0xffffffff,0xffffffff}; // Shiftregister
void crc32_bytecalc(int rxtx, unsigned char byte)
{
int i;
uint32_t polynom = 0xEDB88320; // Generatorpolynom
for (i=0; i<8; ++i)
{
if ((reg32[rxtx]&1) != (byte&1))
reg32[rxtx] = (reg32[rxtx]>>1)^polynom;
else
reg32[rxtx] >>= 1;
byte >>= 1;
}
}
uint32_t crc32_messagecalc(int rxtx, unsigned char *data, int len)
{
int i;
reg32[rxtx] = 0xffffffff;
for(i=0; i<len; i++) {
crc32_bytecalc(rxtx,data[i]);
}
return reg32[rxtx] ^ 0xffffffff;
}

116
hsmodem/fec.cpp Executable file
View File

@ -0,0 +1,116 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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 "hsmodem.h"
#include <cstddef>
#include <iostream>
#include <string>
#include "fec/schifra_galois_field.hpp"
#include "fec/schifra_galois_field_polynomial.hpp"
#include "fec/schifra_sequential_root_generator_polynomial_creator.hpp"
#include "fec/schifra_reed_solomon_encoder.hpp"
#include "fec/schifra_reed_solomon_decoder.hpp"
#include "fec/schifra_reed_solomon_block.hpp"
#include "fec/schifra_error_processes.hpp"
/* Finite Field Parameters */
const std::size_t field_descriptor = 8;
const std::size_t generator_polynomial_index = 120;
const std::size_t generator_polynomial_root_count = FECLEN;
/* Reed Solomon Code Parameters */
const std::size_t code_length = FECBLOCKLEN;
const std::size_t fec_length = FECLEN;
const std::size_t data_length = code_length - fec_length;
/* Instantiate Finite Field and Generator Polynomials */
const schifra::galois::field field(field_descriptor,
schifra::galois::primitive_polynomial_size06,
schifra::galois::primitive_polynomial06);
schifra::galois::field_polynomial generator_polynomial(field);
/* Instantiate Encoder and Decoder (Codec) */
typedef schifra::reed_solomon::encoder<code_length,fec_length,data_length> encoder_t;
typedef schifra::reed_solomon::decoder<code_length,fec_length,data_length> decoder_t;
int cfec_Reconstruct(uint8_t *darr, uint8_t *destination)
{
schifra::reed_solomon::block<code_length,fec_length> rxblock;
for(std::size_t i=0; i<code_length; i++)
rxblock.data[i] = darr[i];
const decoder_t decoder(field, generator_polynomial_index);
if (!decoder.decode(rxblock))
{
// FEC decoding not possible
return 0;
}
for(std::size_t i=0; i<data_length; i++)
destination[i] = rxblock[i];
return 1;
}
void GetFEC(uint8_t *txblock, int len, uint8_t *destArray)
{
schifra::reed_solomon::block<code_length,fec_length> block;
// fill payload into an FEC-block
for(std::size_t i=0; i<data_length; i++)
block.data[i] = txblock[i];
/* Transform message into Reed-Solomon encoded codeword */
const encoder_t encoder(field, generator_polynomial);
if (!encoder.encode(block))
{
// encoding not possible, should never happen
return;
}
// get result out of the FEC block
for(std::size_t i=0; i<code_length; i++)
destArray[i] = block[i];
}
void initFEC()
{
if (!schifra::make_sequential_root_generator_polynomial( field,
generator_polynomial_index,
generator_polynomial_root_count,
generator_polynomial))
{
std::cout << "Error - Failed to create sequential root generator!" << std::endl;
return;
}
}

115
hsmodem/fec.h Normal file
View File

@ -0,0 +1,115 @@
#pragma once
/**
* zfec -- fast forward error correction library with Python interface
* https://tahoe-lafs.org/trac/zfec/
This package implements an "erasure code", or "forward error correction code".
You may use this package under the GNU General Public License, version 2 or, at your option, any later version.
*/
#include <stddef.h>
typedef unsigned char gf;
typedef struct {
unsigned long magic;
unsigned short k, n; /* parameters of the code */
gf* enc_matrix;
} fec_t;
#if defined(_MSC_VER)
// actually, some of the flavors (i.e. Enterprise) do support restrict
//#define restrict __restrict
#define restrict
#endif
/**
* param k the number of blocks required to reconstruct
* param m the total number of blocks created
*/
fec_t* fec_new(unsigned short k, unsigned short m);
void fec_free(fec_t* p);
/**
* @param inpkts the "primary blocks" i.e. the chunks of the input data
* @param fecs buffers into which the secondary blocks will be written
* @param block_nums the numbers of the desired check blocks (the id >= k) which fec_encode() will produce and store into the buffers of the fecs parameter
* @param num_block_nums the length of the block_nums array
* @param sz size of a packet in bytes
*/
void fec_encode(const fec_t* code, const gf** src, gf** fecs, size_t sz);
/**
* @param inpkts an array of packets (size k); If a primary block, i, is present then it must be at index i. Secondary blocks can appear anywhere.
* @param outpkts an array of buffers into which the reconstructed output packets will be written (only packets which are not present in the inpkts input will be reconstructed and written to outpkts)
* @param index an array of the blocknums of the packets in inpkts
* @param sz size of a packet in bytes
*/
void fec_decode(const fec_t* code, const gf** inpkts, gf** outpkts, const unsigned* index, size_t sz);
#if defined(_MSC_VER)
#define alloca _alloca
#else
#ifdef __GNUC__
#ifndef alloca
#define alloca(x) __builtin_alloca(x)
#endif
#else
#include <alloca.h>
#endif
#endif
/**
* zfec -- fast forward error correction library with Python interface
*
* Copyright (C) 2007-2008 Allmydata, Inc.
* Author: Zooko Wilcox-O'Hearn
*
* This file is part of zfec.
*
* See README.rst for licensing information.
*/
/*
* Much of this work is derived from the "fec" software by Luigi Rizzo, et
* al., the copyright notice and licence terms of which are included below
* for reference.
*
* fec.h -- forward error correction based on Vandermonde matrices
* 980614
* (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it)
*
* Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
* Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
* Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
*
* Modifications by Dan Rubenstein (see Modifications.txt for
* their description.
* Modifications (C) 1998 Dan Rubenstein (drubenst@cs.umass.edu)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/

172
hsmodem/fec/schifra_crc.hpp Normal file
View File

@ -0,0 +1,172 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_CRC_HPP
#define INCLUDE_SCHIFRA_CRC_HPP
#include <iostream>
#include <string>
namespace schifra
{
class crc32
{
public:
typedef std::size_t crc32_t;
crc32(const crc32_t& _key, const crc32_t& _state = 0x00)
: key(_key),
state(_state),
initial_state(_state)
{
initialize_crc32_table();
}
void reset()
{
state = initial_state;
}
void update_1byte(const unsigned char data)
{
state = (state >> 8) ^ table[data];
}
void update(const unsigned char data[], const std::size_t& count)
{
for (std::size_t i = 0; i < count; ++i)
{
update_1byte(data[i]);
}
}
void update(char data[], const std::size_t& count)
{
for (std::size_t i = 0; i < count; ++i)
{
update_1byte(static_cast<unsigned char>(data[i]));
}
}
void update(const std::string& data)
{
for (std::size_t i = 0; i < data.size(); ++i)
{
update_1byte(static_cast<unsigned char>(data[i]));
}
}
void update(const std::size_t& data)
{
update_1byte(static_cast<unsigned char>((data ) & 0xFF));
update_1byte(static_cast<unsigned char>((data >> 8) & 0xFF));
update_1byte(static_cast<unsigned char>((data >> 16) & 0xFF));
update_1byte(static_cast<unsigned char>((data >> 24) & 0xFF));
}
crc32_t crc()
{
return state;
}
private:
crc32& operator=(const crc32&);
void initialize_crc32_table()
{
for (std::size_t i = 0; i < 0xFF; ++i)
{
crc32_t reg = i;
for (int j = 0; j < 0x08; ++j)
{
reg = ((reg & 1) ? (reg >> 1) ^ key : reg >> 1);
}
table[i] = reg;
}
}
protected:
crc32_t key;
crc32_t state;
const crc32_t initial_state;
crc32_t table[256];
};
class schifra_crc : public crc32
{
public:
schifra_crc(const crc32_t _key)
: crc32(_key,0xAAAAAAAA)
{}
void update(const unsigned char& data)
{
state = ((state >> 8) ^ table[data]) ^ ((state << 8) ^ table[~data]);
}
void update(const unsigned char data[], const std::size_t& count)
{
for (std::size_t i = 0; i < count; ++i)
{
update_1byte(data[i]);
}
}
void update(const char data[], const std::size_t& count)
{
for (std::size_t i = 0; i < count; ++i)
{
update_1byte(static_cast<unsigned char>(data[i]));
}
}
void update(const std::string& data)
{
for (std::size_t i = 0; i < data.size(); ++i)
{
update_1byte(static_cast<unsigned char>(data[i]));
}
}
void update(const std::size_t& data)
{
update_1byte(static_cast<unsigned char>((data ) & 0xFF));
update_1byte(static_cast<unsigned char>((data >> 8) & 0xFF));
update_1byte(static_cast<unsigned char>((data >> 16) & 0xFF));
update_1byte(static_cast<unsigned char>((data >> 24) & 0xFF));
}
};
} // namespace schifra
#endif

View File

@ -0,0 +1,109 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_ECC_TRAITS_HPP
#define INCLUDE_SCHIFRA_ECC_TRAITS_HPP
namespace schifra
{
namespace traits
{
template <std::size_t code_length> struct symbol;
/* bits per symbol */
template <> struct symbol< 3> { enum {size = 2}; };
template <> struct symbol< 7> { enum {size = 3}; };
template <> struct symbol< 15> { enum {size = 4}; };
template <> struct symbol< 31> { enum {size = 5}; };
template <> struct symbol< 63> { enum {size = 6}; };
template <> struct symbol< 127> { enum {size = 7}; };
template <> struct symbol< 255> { enum {size = 8}; };
template <> struct symbol< 511> { enum {size = 9}; };
template <> struct symbol< 1023> { enum {size = 10}; };
template <> struct symbol< 2047> { enum {size = 11}; };
template <> struct symbol< 4195> { enum {size = 12}; };
template <> struct symbol< 8191> { enum {size = 13}; };
template <> struct symbol<16383> { enum {size = 14}; };
template <> struct symbol<32768> { enum {size = 15}; };
template <> struct symbol<65535> { enum {size = 16}; };
/* Credits: Modern C++ Design - Andrei Alexandrescu */
template <bool> class __static_assert__
{
public:
__static_assert__(...) {}
};
template <> class __static_assert__<true> {};
template <> class __static_assert__<false>;
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length>
struct validate_reed_solomon_code_parameters
{
private:
__static_assert__<(code_length > 0)> assertion1;
__static_assert__<(code_length > fec_length)> assertion2;
__static_assert__<(code_length > data_length)> assertion3;
__static_assert__<(code_length == fec_length + data_length)> assertion4;
};
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length>
struct validate_reed_solomon_block_parameters
{
private:
__static_assert__<(code_length > 0)> assertion1;
__static_assert__<(code_length > fec_length)> assertion2;
__static_assert__<(code_length > data_length)> assertion3;
__static_assert__<(code_length == fec_length + data_length)> assertion4;
};
template <typename Encoder, typename Decoder>
struct equivalent_encoder_decoder
{
private:
__static_assert__<(Encoder::trait::code_length == Decoder::trait::code_length)> assertion1;
__static_assert__<(Encoder::trait::fec_length == Decoder::trait::fec_length) > assertion2;
__static_assert__<(Encoder::trait::data_length == Decoder::trait::data_length)> assertion3;
};
template <std::size_t code_length_, std::size_t fec_length_, std::size_t data_length_ = code_length_ - fec_length_>
class reed_solomon_triat
{
public:
typedef validate_reed_solomon_code_parameters<code_length_,fec_length_,data_length_> vrscp;
enum { code_length = code_length_ };
enum { fec_length = fec_length_ };
enum { data_length = data_length_ };
};
}
} // namespace schifra
#endif

View File

@ -0,0 +1,256 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_ERASURE_CHANNEL_HPP
#define INCLUDE_SCHIFRA_ERASURE_CHANNEL_HPP
#include "schifra_reed_solomon_block.hpp"
#include "schifra_reed_solomon_encoder.hpp"
#include "schifra_reed_solomon_decoder.hpp"
#include "schifra_reed_solomon_interleaving.hpp"
#include "schifra_utilities.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t block_length, std::size_t fec_length>
inline void interleaved_stack_erasure_mapper(const std::vector<std::size_t>& missing_row_index,
std::vector<erasure_locations_t>& erasure_row_list)
{
erasure_row_list.resize(block_length);
for (std::size_t i = 0; i < block_length; ++i)
{
erasure_row_list[i].reserve(fec_length);
}
for (std::size_t i = 0; i < missing_row_index.size(); ++i)
{
for (std::size_t j = 0; j < block_length; ++j)
{
erasure_row_list[j].push_back(missing_row_index[i]);
}
}
}
template <std::size_t code_length, std::size_t fec_length>
inline bool erasure_channel_stack_encode(const encoder<code_length,fec_length>& encoder,
block<code_length,fec_length> (&output)[code_length])
{
for (std::size_t i = 0; i < code_length; ++i)
{
if (!encoder.encode(output[i]))
{
std::cout << "erasure_channel_stack_encode() - Error: Failed to encode block[" << i <<"]" << std::endl;
return false;
}
}
interleave<code_length,fec_length>(output);
return true;
}
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length = code_length - fec_length>
class erasure_code_decoder : public decoder<code_length,fec_length,data_length>
{
public:
typedef decoder<code_length,fec_length,data_length> decoder_type;
typedef typename decoder_type::block_type block_type;
typedef std::vector<galois::field_polynomial> polynomial_list_type;
erasure_code_decoder(const galois::field& gfield,
const unsigned int& gen_initial_index)
: decoder<code_length,fec_length,data_length>(gfield, gen_initial_index)
{
for (std::size_t i = 0; i < code_length; ++i)
{
received_.push_back(galois::field_polynomial(decoder_type::field_, code_length - 1));
syndrome_.push_back(galois::field_polynomial(decoder_type::field_));
}
};
bool decode(block_type rsblock[code_length], const erasure_locations_t& erasure_list) const
{
if (
(!decoder_type::decoder_valid_) ||
(erasure_list.size() != fec_length)
)
{
return false;
}
for (std::size_t i = 0; i < code_length; ++i)
{
decoder_type::load_message (received_[i], rsblock [i]);
decoder_type::compute_syndrome(received_[i], syndrome_[i]);
}
erasure_locations_t erasure_locations;
decoder_type::prepare_erasure_list(erasure_locations,erasure_list);
galois::field_polynomial gamma(galois::field_element(decoder_type::field_, 1));
decoder_type::compute_gamma(gamma,erasure_locations);
std::vector<int> gamma_roots;
find_roots_in_data(gamma,gamma_roots);
polynomial_list_type omega;
for (std::size_t i = 0; i < code_length; ++i)
{
omega.push_back((gamma * syndrome_[i]) % fec_length);
}
galois::field_polynomial gamma_derivative = gamma.derivative();
for (std::size_t i = 0; i < gamma_roots.size(); ++i)
{
int error_location = static_cast<int>(gamma_roots[i]);
galois::field_symbol alpha_inverse = decoder_type::field_.alpha(error_location);
galois::field_element denominator = gamma_derivative(alpha_inverse);
if (denominator == 0)
{
return false;
}
for (std::size_t j = 0; j < code_length; ++j)
{
galois::field_element numerator = (omega[j](alpha_inverse) * decoder_type::root_exponent_table_[error_location]);
/*
A minor optimization can be made in the event the
numerator is equal to zero by not executing the
following line.
*/
rsblock[j][error_location - 1] ^= decoder_type::field_.div(numerator.poly(),denominator.poly());
}
}
return true;
}
private:
void find_roots_in_data(const galois::field_polynomial& poly, std::vector<int>& root_list) const
{
/*
Chien Search, as described in parent, but only
for locations within the data range of the message.
*/
root_list.reserve(fec_length << 1);
root_list.resize(0);
std::size_t polynomial_degree = poly.deg();
std::size_t root_list_size = 0;
for (int i = 1; i <= static_cast<int>(data_length); ++i)
{
if (0 == poly(decoder_type::field_.alpha(i)).poly())
{
root_list.push_back(i);
root_list_size++;
if (root_list_size == polynomial_degree)
{
break;
}
}
}
}
mutable polynomial_list_type received_;
mutable polynomial_list_type syndrome_;
};
template <std::size_t code_length, std::size_t fec_length>
inline bool erasure_channel_stack_decode(const decoder<code_length,fec_length>& general_decoder,
const erasure_locations_t& missing_row_index,
block<code_length,fec_length> (&output)[code_length])
{
if (missing_row_index.empty())
{
return true;
}
interleave<code_length,fec_length>(output);
for (std::size_t i = 0; i < code_length; ++i)
{
if (!general_decoder.decode(output[i],missing_row_index))
{
std::cout << "[2] erasure_channel_stack_decode() - Error: Failed to decode block[" << i <<"]" << std::endl;
return false;
}
}
return true;
}
template <std::size_t code_length, std::size_t fec_length>
inline bool erasure_channel_stack_decode(const erasure_code_decoder<code_length,fec_length>& erasure_decoder,
const erasure_locations_t& missing_row_index,
block<code_length,fec_length> (&output)[code_length])
{
/*
Note: 1. Missing row indicies must be unique.
2. Missing row indicies must exist within
the stack's size.
3. There will be NO errors in the rows (aka output)
4. The information members of the blocks will
not be utilized.
There are NO exceptions to these rules!
*/
if (missing_row_index.empty())
{
return true;
}
else if (missing_row_index.size() == fec_length)
{
interleave<code_length,fec_length>(output);
return erasure_decoder.decode(output,missing_row_index);
}
else
return erasure_channel_stack_decode<code_length,fec_length>(
static_cast<const decoder<code_length,fec_length>&>(erasure_decoder),
missing_row_index,
output);
}
} // namespace reed_solomon
} // namepsace schifra
#endif

View File

@ -0,0 +1,602 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_ERROR_PROCESSES_HPP
#define INCLUDE_SCHIFRA_ERROR_PROCESSES_HPP
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <deque>
#include <vector>
#include "schifra_reed_solomon_block.hpp"
#include "schifra_fileio.hpp"
namespace schifra
{
template <std::size_t code_length, std::size_t fec_length>
inline void add_erasure_error(const std::size_t& position, reed_solomon::block<code_length,fec_length>& block)
{
block[position] = (~block[position]) & 0xFF; // Or one can simply equate to zero
}
template <std::size_t code_length, std::size_t fec_length>
inline void add_error(const std::size_t& position, reed_solomon::block<code_length,fec_length>& block)
{
block[position] = (~block[position]) & 0xFF;
}
template <std::size_t code_length, std::size_t fec_length>
inline void add_error_4bit_symbol(const std::size_t& position, reed_solomon::block<code_length,fec_length>& block)
{
block[position] = (~block[position]) & 0x0F;
}
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_all_errors00(reed_solomon::block<code_length,fec_length>& rsblock,
const std::size_t& start_position,
const std::size_t& scale = 1)
{
for (std::size_t i = 0; i < (fec_length >> 1); ++i)
{
add_error((start_position + scale * i) % code_length,rsblock);
}
}
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_all_errors_wth_mask(reed_solomon::block<code_length,fec_length>& rsblock,
const std::size_t& start_position,
const int& mask,
const std::size_t& scale = 1)
{
for (std::size_t i = 0; i < (fec_length >> 1); ++i)
{
std::size_t position = (start_position + scale * i) % code_length;
rsblock[position] = (~rsblock[position]) & mask;
}
}
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_all_errors(schifra::reed_solomon::block<code_length,fec_length>& rsblock,
const std::size_t error_count,
const std::size_t& start_position,
const std::size_t& scale = 1)
{
for (std::size_t i = 0; i < error_count; ++i)
{
add_error((start_position + scale * i) % code_length,rsblock);
}
}
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_all_erasures00(reed_solomon::block<code_length,fec_length>& rsblock,
reed_solomon::erasure_locations_t& erasure_list,
const std::size_t& start_position,
const std::size_t& scale = 1)
{
std::size_t erasures[code_length];
for (std::size_t i = 0; i < code_length; ++i) erasures[i] = 0;
for (std::size_t i = 0; i < fec_length; ++i)
{
std::size_t error_position = (start_position + scale * i) % code_length;
add_erasure_error(error_position,rsblock);
erasures[error_position] = 1;
}
for (std::size_t i = 0; i < code_length; ++i)
{
if (erasures[i] == 1) erasure_list.push_back(i);
}
}
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_all_erasures(reed_solomon::block<code_length,fec_length>& rsblock,
reed_solomon::erasure_locations_t& erasure_list,
const std::size_t erasure_count,
const std::size_t& start_position,
const std::size_t& scale = 1)
{
std::size_t erasures[code_length];
for (std::size_t i = 0; i < code_length; ++i) erasures[i] = 0;
for (std::size_t i = 0; i < erasure_count; ++i)
{
/* Note: Must make sure duplicate erasures are not added */
std::size_t error_position = (start_position + scale * i) % code_length;
add_erasure_error(error_position,rsblock);
erasures[error_position] = 1;
}
for (std::size_t i = 0; i < code_length; ++i)
{
if (erasures[i] == 1) erasure_list.push_back(i);
}
}
namespace error_mode
{
enum type
{
errors_erasures, // Errors first then erasures
erasures_errors // Erasures first then errors
};
}
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_errors_erasures(reed_solomon::block<code_length,fec_length>& rsblock,
const error_mode::type& mode,
const std::size_t& start_position,
const std::size_t& erasure_count,
reed_solomon::erasure_locations_t& erasure_list,
const std::size_t between_space = 0)
{
std::size_t error_count = (fec_length - erasure_count) >> 1;
if ((2 * error_count) + erasure_count > fec_length)
{
std::cout << "corrupt_message_errors_erasures() - ERROR Too many erasures and errors!" << std::endl;
std::cout << "Error Count: " << error_count << std::endl;
std::cout << "Erasure Count: " << error_count << std::endl;
return;
}
std::size_t erasures[code_length];
for (std::size_t i = 0; i < code_length; ++i) erasures[i] = 0;
std::size_t error_position = 0;
switch (mode)
{
case error_mode::erasures_errors : {
for (std::size_t i = 0; i < erasure_count; ++i)
{
error_position = (start_position + i) % code_length;
add_erasure_error(error_position,rsblock);
erasures[error_position] = 1;
}
for (std::size_t i = 0; i < error_count; ++i)
{
error_position = (start_position + erasure_count + between_space + i) % code_length;
add_error(error_position,rsblock);
}
}
break;
case error_mode::errors_erasures : {
for (std::size_t i = 0; i < error_count; ++i)
{
error_position = (start_position + i) % code_length;
add_error(error_position,rsblock);
}
for (std::size_t i = 0; i < erasure_count; ++i)
{
error_position = (start_position + error_count + between_space + i) % code_length;
add_erasure_error(error_position,rsblock);
erasures[error_position] = 1;
}
}
break;
}
for (std::size_t i = 0; i < code_length; ++i)
{
if (erasures[i] == 1) erasure_list.push_back(i);
}
}
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_interleaved_errors_erasures(reed_solomon::block<code_length,fec_length>& rsblock,
const std::size_t& start_position,
const std::size_t& erasure_count,
reed_solomon::erasure_locations_t& erasure_list)
{
std::size_t error_count = (fec_length - erasure_count) >> 1;
if ((2 * error_count) + erasure_count > fec_length)
{
std::cout << "corrupt_message_interleaved_errors_erasures() - [1] ERROR Too many erasures and errors!" << std::endl;
std::cout << "Error Count: " << error_count << std::endl;
std::cout << "Erasure Count: " << error_count << std::endl;
return;
}
std::size_t erasures[code_length];
for (std::size_t i = 0; i < code_length; ++i) erasures[i] = 0;
std::size_t e = 0;
std::size_t s = 0;
std::size_t i = 0;
while ((e < error_count) || (s < erasure_count) || (i < (error_count + erasure_count)))
{
std::size_t error_position = (start_position + i) % code_length;
if (((i & 0x01) == 0) && (s < erasure_count))
{
add_erasure_error(error_position,rsblock);
erasures[error_position] = 1;
s++;
}
else if (((i & 0x01) == 1) && (e < error_count))
{
e++;
add_error(error_position,rsblock);
}
++i;
}
for (std::size_t j = 0; j < code_length; ++j)
{
if (erasures[j] == 1) erasure_list.push_back(j);
}
if ((2 * e) + erasure_list.size() > fec_length)
{
std::cout << "corrupt_message_interleaved_errors_erasures() - [2] ERROR Too many erasures and errors!" << std::endl;
std::cout << "Error Count: " << error_count << std::endl;
std::cout << "Erasure Count: " << error_count << std::endl;
return;
}
}
namespace details
{
template <std::size_t code_length, std::size_t fec_length, bool t>
struct corrupt_message_all_errors_segmented_impl
{
static void process(reed_solomon::block<code_length,fec_length>& rsblock,
const std::size_t& start_position,
const std::size_t& distance_between_blocks = 1)
{
std::size_t block_1_error_count = (fec_length >> 2);
std::size_t block_2_error_count = (fec_length >> 1) - block_1_error_count;
for (std::size_t i = 0; i < block_1_error_count; ++i)
{
add_error((start_position + i) % code_length,rsblock);
}
std::size_t new_start_position = (start_position + (block_1_error_count)) + distance_between_blocks;
for (std::size_t i = 0; i < block_2_error_count; ++i)
{
add_error((new_start_position + i) % code_length,rsblock);
}
}
};
template <std::size_t code_length, std::size_t fec_length>
struct corrupt_message_all_errors_segmented_impl<code_length,fec_length,false>
{
static void process(reed_solomon::block<code_length,fec_length>&,
const std::size_t&, const std::size_t&)
{}
};
}
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_all_errors_segmented(reed_solomon::block<code_length,fec_length>& rsblock,
const std::size_t& start_position,
const std::size_t& distance_between_blocks = 1)
{
details::corrupt_message_all_errors_segmented_impl<code_length,fec_length,(fec_length > 2)>::
process(rsblock,start_position,distance_between_blocks);
}
inline bool check_for_duplicate_erasures(const std::vector<int>& erasure_list)
{
for (std::size_t i = 0; i < erasure_list.size(); ++i)
{
for (std::size_t j = i + 1; j < erasure_list.size(); ++j)
{
if (erasure_list[i] == erasure_list[j])
{
return false;
}
}
}
return true;
}
inline void dump_erasure_list(const schifra::reed_solomon::erasure_locations_t& erasure_list)
{
for (std::size_t i = 0; i < erasure_list.size(); ++i)
{
std::cout << "[" << i << "," << erasure_list[i] << "] ";
}
std::cout << std::endl;
}
template <std::size_t code_length, std::size_t fec_length>
inline bool is_block_equivelent(const reed_solomon::block<code_length,fec_length>& rsblock,
const std::string& data,
const bool display = false,
const bool all_errors = false)
{
std::string::const_iterator it = data.begin();
bool error_found = false;
for (std::size_t i = 0; i < code_length - fec_length; ++i, ++it)
{
if (static_cast<char>(rsblock.data[i] & 0xFF) != (*it))
{
error_found = true;
if (display)
{
printf("is_block_equivelent() - Error at loc : %02d\td1: %02X\td2: %02X\n",
static_cast<unsigned int>(i),
rsblock.data[i],
static_cast<unsigned char>(*it));
}
if (!all_errors)
return false;
}
}
return !error_found;
}
template <std::size_t code_length, std::size_t fec_length>
inline bool are_blocks_equivelent(const reed_solomon::block<code_length,fec_length>& block1,
const reed_solomon::block<code_length,fec_length>& block2,
const std::size_t span = code_length,
const bool display = false,
const bool all_errors = false)
{
bool error_found = false;
for (std::size_t i = 0; i < span; ++i)
{
if (block1[i] != block2[i])
{
error_found = true;
if (display)
{
printf("are_blocks_equivelent() - Error at loc : %02d\td1: %04X\td2: %04X\n",
static_cast<unsigned int>(i),
block1[i],
block2[i]);
}
if (!all_errors)
return false;
}
}
return !error_found;
}
template <std::size_t code_length, std::size_t fec_length, std::size_t stack_size>
inline bool block_stacks_equivelent(const reed_solomon::block<code_length,fec_length> block_stack1[stack_size],
const reed_solomon::block<code_length,fec_length> block_stack2[stack_size])
{
for (std::size_t i = 0; i < stack_size; ++i)
{
if (!are_blocks_equivelent(block_stack1[i],block_stack2[i]))
{
return false;
}
}
return true;
}
template <std::size_t block_length, std::size_t stack_size>
inline bool block_stacks_equivelent(const reed_solomon::data_block<std::size_t,block_length> block_stack1[stack_size],
const reed_solomon::data_block<std::size_t,block_length> block_stack2[stack_size])
{
for (std::size_t i = 0; i < stack_size; ++i)
{
for (std::size_t j = 0; j < block_length; ++j)
{
if (block_stack1[i][j] != block_stack2[i][j])
{
return false;
}
}
}
return true;
}
inline void corrupt_file_with_burst_errors(const std::string& file_name,
const long& start_position,
const long& burst_length)
{
if (!schifra::fileio::file_exists(file_name))
{
std::cout << "corrupt_file() - Error: " << file_name << " does not exist!" << std::endl;
return;
}
if (static_cast<std::size_t>(start_position + burst_length) >= schifra::fileio::file_size(file_name))
{
std::cout << "corrupt_file() - Error: Burst error out of bounds." << std::endl;
return;
}
std::vector<char> data(burst_length);
std::ifstream ifile(file_name.c_str(), std::ios::in | std::ios::binary);
if (!ifile)
{
return;
}
ifile.seekg(start_position,std::ios_base::beg);
ifile.read(&data[0],burst_length);
ifile.close();
for (long i = 0; i < burst_length; ++i)
{
data[i] = ~data[i];
}
std::ofstream ofile(file_name.c_str(), std::ios::in | std::ios::out | std::ios::binary);
if (!ofile)
{
return;
}
ofile.seekp(start_position,std::ios_base::beg);
ofile.write(&data[0],burst_length);
ofile.close();
}
static const std::size_t global_random_error_index[] =
{
13, 170, 148, 66, 228, 208, 182, 92,
4, 137, 97, 99, 237, 151, 15, 0,
119, 243, 41, 222, 33, 211, 188, 5,
44, 30, 210, 111, 54, 79, 61, 223,
239, 149, 73, 115, 201, 234, 194, 62,
147, 70, 19, 49, 72, 52, 164, 29,
102, 225, 203, 153, 18, 205, 40, 217,
165, 177, 166, 134, 236, 68, 231, 154,
116, 136, 47, 240, 46, 89, 120, 183,
242, 28, 161, 226, 241, 230, 10, 131,
207, 132, 83, 171, 202, 195, 227, 206,
112, 88, 90, 146, 117, 180, 26, 78,
118, 254, 107, 110, 220, 7, 192, 187,
31, 175, 127, 209, 32, 12, 84, 128,
190, 156, 95, 105, 104, 246, 91, 215,
219, 142, 36, 186, 247, 233, 167, 133,
160, 16, 140, 169, 23, 96, 155, 235,
179, 76, 253, 103, 238, 67, 35, 121,
100, 27, 213, 58, 77, 248, 174, 39,
214, 56, 42, 200, 106, 21, 129, 114,
252, 113, 168, 53, 25, 216, 64, 232,
81, 75, 2, 224, 250, 60, 135, 204,
48, 196, 94, 63, 244, 191, 93, 126,
138, 159, 9, 85, 249, 34, 185, 163,
17, 65, 184, 82, 109, 172, 108, 69,
150, 3, 20, 221, 162, 212, 152, 59,
198, 74, 229, 55, 87, 178, 141, 199,
57, 130, 80, 173, 101, 122, 144, 51,
139, 11, 8, 125, 158, 124, 123, 37,
14, 24, 22, 43, 197, 50, 98, 6,
176, 251, 86, 218, 193, 71, 145, 1,
45, 38, 189, 143, 245, 157, 181
};
static const std::size_t error_index_size = sizeof(global_random_error_index) / sizeof(std::size_t);
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_all_errors_at_index(schifra::reed_solomon::block<code_length,fec_length>& rsblock,
const std::size_t error_count,
const std::size_t& error_index_start_position,
const bool display_positions = false)
{
schifra::reed_solomon::block<code_length,fec_length> tmp_rsblock = rsblock;
for (std::size_t i = 0; i < error_count; ++i)
{
std::size_t error_position = (global_random_error_index[(error_index_start_position + i) % error_index_size]) % code_length;
add_error(error_position,rsblock);
if (display_positions)
{
std::cout << "Error index: " << error_position << std::endl;
}
}
}
template <std::size_t code_length, std::size_t fec_length>
inline void corrupt_message_all_errors_at_index(schifra::reed_solomon::block<code_length,fec_length>& rsblock,
const std::size_t error_count,
const std::size_t& error_index_start_position,
const std::vector<std::size_t>& random_error_index,
const bool display_positions = false)
{
for (std::size_t i = 0; i < error_count; ++i)
{
std::size_t error_position = (random_error_index[(error_index_start_position + i) % random_error_index.size()]) % code_length;
add_error(error_position,rsblock);
if (display_positions)
{
std::cout << "Error index: " << error_position << std::endl;
}
}
}
inline void generate_error_index(const std::size_t index_size,
std::vector<std::size_t>& random_error_index,
std::size_t seed)
{
if (0 == seed)
{
seed = 0xA5A5A5A5;
}
::srand(static_cast<unsigned int>(seed));
std::deque<std::size_t> index_list;
for (std::size_t i = 0; i < index_size; ++i)
{
index_list.push_back(i);
}
random_error_index.reserve(index_size);
random_error_index.resize(0);
while (!index_list.empty())
{
// possibly the worst way of doing this.
std::size_t index = ::rand() % index_list.size();
random_error_index.push_back(index_list[index]);
index_list.erase(index_list.begin() + index);
}
}
} // namespace schifra
#endif

View File

@ -0,0 +1,227 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_FILEIO_HPP
#define INCLUDE_SCHIFRA_FILEIO_HPP
#include <iostream>
#include <iterator>
#include <fstream>
#include <string>
#include <vector>
#include "schifra_crc.hpp"
namespace schifra
{
namespace fileio
{
inline void read_into_vector(const std::string& file_name, std::vector<std::string>& buffer)
{
std::ifstream file(file_name.c_str());
if (!file) return;
std::string line;
while (std::getline(file,line))
{
buffer.push_back(line);
}
file.close();
}
inline void write_from_vector(const std::string& file_name, const std::vector<std::string>& buffer)
{
std::ofstream file(file_name.c_str());
if (!file) return;
std::ostream_iterator <std::string> os(file,"\n");
std::copy(buffer.begin(),buffer.end(), os);
file.close();
}
inline bool file_exists(const std::string& file_name)
{
std::ifstream file(file_name.c_str(), std::ios::binary);
return ((!file) ? false : true);
}
inline std::size_t file_size(const std::string& file_name)
{
std::ifstream file(file_name.c_str(),std::ios::binary);
if (!file) return 0;
file.seekg (0, std::ios::end);
return static_cast<std::size_t>(file.tellg());
}
inline void load_file(const std::string& file_name, std::string& buffer)
{
std::ifstream file(file_name.c_str(), std::ios::binary);
if (!file) return;
buffer.assign(std::istreambuf_iterator<char>(file),std::istreambuf_iterator<char>());
file.close();
}
inline void load_file(const std::string& file_name, char** buffer, std::size_t& buffer_size)
{
std::ifstream in_stream(file_name.c_str(),std::ios::binary);
if (!in_stream) return;
buffer_size = file_size(file_name);
*buffer = new char[buffer_size];
in_stream.read(*buffer,static_cast<std::streamsize>(buffer_size));
in_stream.close();
}
inline void write_file(const std::string& file_name, const std::string& buffer)
{
std::ofstream file(file_name.c_str(),std::ios::binary);
file << buffer;
file.close();
}
inline void write_file(const std::string& file_name, char* buffer, const std::size_t& buffer_size)
{
std::ofstream out_stream(file_name.c_str(),std::ios::binary);
if (!out_stream) return;
out_stream.write(buffer,static_cast<std::streamsize>(buffer_size));
out_stream.close();
}
inline bool copy_file(const std::string& src_file_name, const std::string& dest_file_name)
{
std::ifstream src_file(src_file_name.c_str(),std::ios::binary);
std::ofstream dest_file(dest_file_name.c_str(),std::ios::binary);
if (!src_file) return false;
if (!dest_file) return false;
const std::size_t block_size = 1024;
char buffer[block_size];
std::size_t remaining_bytes = file_size(src_file_name);
while (remaining_bytes >= block_size)
{
src_file.read(&buffer[0],static_cast<std::streamsize>(block_size));
dest_file.write(&buffer[0],static_cast<std::streamsize>(block_size));
remaining_bytes -= block_size;
}
if (remaining_bytes > 0)
{
src_file.read(&buffer[0],static_cast<std::streamsize>(remaining_bytes));
dest_file.write(&buffer[0],static_cast<std::streamsize>(remaining_bytes));
remaining_bytes = 0;
}
src_file.close();
dest_file.close();
return true;
}
inline bool files_identical(const std::string& file_name1, const std::string& file_name2)
{
std::ifstream file1(file_name1.c_str(),std::ios::binary);
std::ifstream file2(file_name2.c_str(),std::ios::binary);
if (!file1) return false;
if (!file2) return false;
if (file_size(file_name1) != file_size(file_name2)) return false;
const std::size_t block_size = 1024;
char buffer1[block_size];
char buffer2[block_size];
std::size_t remaining_bytes = file_size(file_name1);
while (remaining_bytes >= block_size)
{
file1.read(&buffer1[0],static_cast<std::streamsize>(block_size));
file2.read(&buffer2[0],static_cast<std::streamsize>(block_size));
for (std::size_t i = 0; i < block_size; ++i)
{
if (buffer1[i] != buffer2[i])
{
return false;
}
}
remaining_bytes -= block_size;
}
if (remaining_bytes > 0)
{
file1.read(&buffer1[0],static_cast<std::streamsize>(remaining_bytes));
file2.read(&buffer2[0],static_cast<std::streamsize>(remaining_bytes));
for (std::size_t i = 0; i < remaining_bytes; ++i)
{
if (buffer1[i] != buffer2[i])
{
return false;
}
}
remaining_bytes = 0;
}
file1.close();
file2.close();
return true;
}
inline std::size_t file_crc(crc32& crc_module, const std::string& file_name)
{
std::ifstream file(file_name.c_str(),std::ios::binary);
if (!file) return 0;
const std::size_t block_size = 1024;
char buffer[block_size];
std::size_t remaining_bytes = file_size(file_name);
crc_module.reset();
while (remaining_bytes >= block_size)
{
file.read(&buffer[0],static_cast<std::streamsize>(block_size));
crc_module.update(buffer,block_size);
remaining_bytes -= block_size;
}
if (remaining_bytes > 0)
{
file.read(&buffer[0],static_cast<std::streamsize>(remaining_bytes));
crc_module.update(buffer,remaining_bytes);
remaining_bytes = 0;
}
return crc_module.crc();
}
} // namespace fileio
} // namespace schifra
#endif

View File

@ -0,0 +1,518 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_GALOIS_FIELD_HPP
#define INCLUDE_SCHIFRA_GALOIS_FIELD_HPP
#include <algorithm>
#include <iostream>
#include <vector>
#include <limits>
#include <string>
namespace schifra
{
namespace galois
{
typedef int field_symbol;
const field_symbol GFERROR = -1;
class field
{
public:
field(const int pwr, const std::size_t primpoly_deg, const unsigned int* primitive_poly);
~field();
bool operator==(const field& gf) const;
bool operator!=(const field& gf) const;
inline field_symbol index(const field_symbol value) const
{
return index_of_[value];
}
inline field_symbol alpha(const field_symbol value) const
{
return alpha_to_[value];
}
inline unsigned int size() const
{
return field_size_;
}
inline unsigned int pwr() const
{
return power_;
}
inline unsigned int mask() const
{
return field_size_;
}
inline field_symbol add(const field_symbol& a, const field_symbol& b) const
{
return (a ^ b);
}
inline field_symbol sub(const field_symbol& a, const field_symbol& b) const
{
return (a ^ b);
}
inline field_symbol normalize(field_symbol x) const
{
while (x < 0)
{
x += static_cast<field_symbol>(field_size_);
}
while (x >= static_cast<field_symbol>(field_size_))
{
x -= static_cast<field_symbol>(field_size_);
x = (x >> power_) + (x & field_size_);
}
return x;
}
inline field_symbol mul(const field_symbol& a, const field_symbol& b) const
{
#if !defined(NO_GFLUT)
return mul_table_[a][b];
#else
if ((a == 0) || (b == 0))
return 0;
else
return alpha_to_[normalize(index_of_[a] + index_of_[b])];
#endif
}
inline field_symbol div(const field_symbol& a, const field_symbol& b) const
{
#if !defined(NO_GFLUT)
return div_table_[a][b];
#else
if ((a == 0) || (b == 0))
return 0;
else
return alpha_to_[normalize(index_of_[a] - index_of_[b] + field_size_)];
#endif
}
inline field_symbol exp(const field_symbol& a, int n) const
{
#if !defined(NO_GFLUT)
if (n >= 0)
return exp_table_[a][n & field_size_];
else
{
while (n < 0) n += field_size_;
return (n ? exp_table_[a][n] : 1);
}
#else
if (a != 0)
{
if (n < 0)
{
while (n < 0) n += field_size_;
return (n ? alpha_to_[normalize(index_of_[a] * n)] : 1);
}
else if (n)
return alpha_to_[normalize(index_of_[a] * static_cast<field_symbol>(n))];
else
return 1;
}
else
return 0;
#endif
}
#ifdef LINEAR_EXP_LUT
inline field_symbol* const linear_exp(const field_symbol& a) const
{
#if !defined(NO_GFLUT)
static const field_symbol upper_bound = 2 * field_size_;
if ((a >= 0) && (a <= upper_bound))
return linear_exp_table_[a];
else
return reinterpret_cast<field_symbol*>(0);
#else
return reinterpret_cast<field_symbol*>(0);
#endif
}
#endif
inline field_symbol inverse(const field_symbol& val) const
{
#if !defined(NO_GFLUT)
return mul_inverse_[val];
#else
return alpha_to_[normalize(field_size_ - index_of_[val])];
#endif
}
inline unsigned int prim_poly_term(const unsigned int index) const
{
return prim_poly_[index];
}
friend std::ostream& operator << (std::ostream& os, const field& gf);
private:
field();
field(const field& gfield);
field& operator=(const field& gfield);
void generate_field(const unsigned int* prim_poly_);
field_symbol gen_mul (const field_symbol& a, const field_symbol& b) const;
field_symbol gen_div (const field_symbol& a, const field_symbol& b) const;
field_symbol gen_exp (const field_symbol& a, const std::size_t& n) const;
field_symbol gen_inverse (const field_symbol& val) const;
std::size_t create_array(char buffer_[],
const std::size_t& length,
const std::size_t offset,
field_symbol** array);
std::size_t create_2d_array(char buffer_[],
std::size_t row_cnt, std::size_t col_cnt,
const std::size_t offset,
field_symbol*** array);
unsigned int power_;
std::size_t prim_poly_deg_;
unsigned int field_size_;
unsigned int prim_poly_hash_;
unsigned int* prim_poly_;
field_symbol* alpha_to_; // aka exponential or anti-log
field_symbol* index_of_; // aka log
field_symbol* mul_inverse_; // multiplicative inverse
field_symbol** mul_table_;
field_symbol** div_table_;
field_symbol** exp_table_;
field_symbol** linear_exp_table_;
char* buffer_;
};
inline field::field(const int pwr, const std::size_t primpoly_deg, const unsigned int* primitive_poly)
: power_(pwr),
prim_poly_deg_(primpoly_deg),
field_size_((1 << power_) - 1)
{
alpha_to_ = new field_symbol [field_size_ + 1];
index_of_ = new field_symbol [field_size_ + 1];
#if !defined(NO_GFLUT)
#ifdef LINEAR_EXP_LUT
static const std::size_t buffer_size = ((6 * (field_size_ + 1) * (field_size_ + 1)) + ((field_size_ + 1) * 2)) * sizeof(field_symbol);
#else
static const std::size_t buffer_size = ((4 * (field_size_ + 1) * (field_size_ + 1)) + ((field_size_ + 1) * 2)) * sizeof(field_symbol);
#endif
buffer_ = new char[buffer_size];
std::size_t offset = 0;
offset = create_2d_array(buffer_,(field_size_ + 1),(field_size_ + 1),offset,&mul_table_);
offset = create_2d_array(buffer_,(field_size_ + 1),(field_size_ + 1),offset,&div_table_);
offset = create_2d_array(buffer_,(field_size_ + 1),(field_size_ + 1),offset,&exp_table_);
#ifdef LINEAR_EXP_LUT
offset = create_2d_array(buffer_,(field_size_ + 1),(field_size_ + 1) * 2,offset,&linear_exp_table_);
#else
linear_exp_table_ = 0;
#endif
offset = create_array(buffer_,(field_size_ + 1) * 2,offset,&mul_inverse_);
#else
buffer_ = 0;
mul_table_ = 0;
div_table_ = 0;
exp_table_ = 0;
mul_inverse_ = 0;
linear_exp_table_ = 0;
#endif
prim_poly_ = new unsigned int [prim_poly_deg_ + 1];
for (unsigned int i = 0; i < (prim_poly_deg_ + 1); ++i)
{
prim_poly_[i] = primitive_poly[i];
}
prim_poly_hash_ = 0xAAAAAAAA;
for (std::size_t i = 0; i < (prim_poly_deg_ + 1); ++i)
{
prim_poly_hash_ += ((i & 1) == 0) ? ( (prim_poly_hash_ << 7) ^ primitive_poly[i] * (prim_poly_hash_ >> 3)) :
(~((prim_poly_hash_ << 11) + (primitive_poly[i] ^ (prim_poly_hash_ >> 5))));
}
generate_field(primitive_poly);
}
inline field::~field()
{
if (0 != alpha_to_) { delete [] alpha_to_; alpha_to_ = 0; }
if (0 != index_of_) { delete [] index_of_; index_of_ = 0; }
if (0 != prim_poly_) { delete [] prim_poly_; prim_poly_ = 0; }
#if !defined(NO_GFLUT)
if (0 != mul_table_) { delete [] mul_table_; mul_table_ = 0; }
if (0 != div_table_) { delete [] div_table_; div_table_ = 0; }
if (0 != exp_table_) { delete [] exp_table_; exp_table_ = 0; }
#ifdef LINEAR_EXP_LUT
if (0 != linear_exp_table_) { delete [] linear_exp_table_; linear_exp_table_ = 0; }
#endif
if (0 != buffer_) { delete [] buffer_; buffer_ = 0; }
#endif
}
inline bool field::operator==(const field& gf) const
{
return (
(this->power_ == gf.power_) &&
(this->prim_poly_hash_ == gf.prim_poly_hash_)
);
}
inline bool field::operator!=(const field& gf) const
{
return !field::operator ==(gf);
}
inline void field::generate_field(const unsigned int* prim_poly)
{
/*
Note: It is assumed that the degree of the primitive
polynomial will be equivelent to the m value as
in GF(2^m)
*/
field_symbol mask = 1;
alpha_to_[power_] = 0;
for (field_symbol i = 0; i < static_cast<field_symbol>(power_); ++i)
{
alpha_to_[i] = mask;
index_of_[alpha_to_[i]] = i;
if (prim_poly[i] != 0)
{
alpha_to_[power_] ^= mask;
}
mask <<= 1;
}
index_of_[alpha_to_[power_]] = power_;
mask >>= 1;
for (field_symbol i = power_ + 1; i < static_cast<field_symbol>(field_size_); ++i)
{
if (alpha_to_[i - 1] >= mask)
alpha_to_[i] = alpha_to_[power_] ^ ((alpha_to_[i - 1] ^ mask) << 1);
else
alpha_to_[i] = alpha_to_[i - 1] << 1;
index_of_[alpha_to_[i]] = i;
}
index_of_[0] = GFERROR;
alpha_to_[field_size_] = 1;
#if !defined(NO_GFLUT)
for (field_symbol i = 0; i < static_cast<field_symbol>(field_size_ + 1); ++i)
{
for (field_symbol j = 0; j < static_cast<field_symbol>(field_size_ + 1); ++j)
{
mul_table_[i][j] = gen_mul(i,j);
div_table_[i][j] = gen_div(i,j);
exp_table_[i][j] = gen_exp(i,j);
}
}
#ifdef LINEAR_EXP_LUT
for (field_symbol i = 0; i < static_cast<field_symbol>(field_size_ + 1); ++i)
{
for (int j = 0; j < static_cast<field_symbol>(2 * field_size_); ++j)
{
linear_exp_table_[i][j] = gen_exp(i,j);
}
}
#endif
for (field_symbol i = 0; i < static_cast<field_symbol>(field_size_ + 1); ++i)
{
mul_inverse_[i] = gen_inverse(i);
mul_inverse_[i + (field_size_ + 1)] = mul_inverse_[i];
}
#endif
}
inline field_symbol field::gen_mul(const field_symbol& a, const field_symbol& b) const
{
if ((a == 0) || (b == 0))
return 0;
else
return alpha_to_[normalize(index_of_[a] + index_of_[b])];
}
inline field_symbol field::gen_div(const field_symbol& a, const field_symbol& b) const
{
if ((a == 0) || (b == 0))
return 0;
else
return alpha_to_[normalize(index_of_[a] - index_of_[b] + field_size_)];
}
inline field_symbol field::gen_exp(const field_symbol& a, const std::size_t& n) const
{
if (a != 0)
return ((n == 0) ? 1 : alpha_to_[normalize(index_of_[a] * static_cast<field_symbol>(n))]);
else
return 0;
}
inline field_symbol field::gen_inverse(const field_symbol& val) const
{
return alpha_to_[normalize(field_size_ - index_of_[val])];
}
inline std::size_t field::create_array(char buffer[],
const std::size_t& length,
const std::size_t offset,
field_symbol** array)
{
const std::size_t row_size = length * sizeof(field_symbol);
(*array) = new(buffer + offset)field_symbol[length];
return row_size + offset;
}
inline std::size_t field::create_2d_array(char buffer[],
std::size_t row_cnt, std::size_t col_cnt,
const std::size_t offset,
field_symbol*** array)
{
const std::size_t row_size = col_cnt * sizeof(field_symbol);
char* buffer__offset = buffer + offset;
(*array) = new field_symbol* [row_cnt];
for (std::size_t i = 0; i < row_cnt; ++i)
{
(*array)[i] = new(buffer__offset + (i * row_size))field_symbol[col_cnt];
}
return (row_cnt * row_size) + offset;
}
inline std::ostream& operator << (std::ostream& os, const field& gf)
{
for (std::size_t i = 0; i < (gf.field_size_ + 1); ++i)
{
os << i << "\t" << gf.alpha_to_[i] << "\t" << gf.index_of_[i] << std::endl;
}
return os;
}
/* 1x^0 + 1x^1 + 0x^2 + 1x^3 */
const unsigned int primitive_polynomial00[] = {1, 1, 0, 1};
const unsigned int primitive_polynomial_size00 = 4;
/* 1x^0 + 1x^1 + 0x^2 + 0x^3 + 1x^4*/
const unsigned int primitive_polynomial01[] = {1, 1, 0, 0, 1};
const unsigned int primitive_polynomial_size01 = 5;
/* 1x^0 + 0x^1 + 1x^2 + 0x^3 + 0x^4 + 1x^5 */
const unsigned int primitive_polynomial02[] = {1, 0, 1, 0, 0, 1};
const unsigned int primitive_polynomial_size02 = 6;
/* 1x^0 + 1x^1 + 0x^2 + 0x^3 + 0x^4 + 0x^5 + 1x^6 */
const unsigned int primitive_polynomial03[] = {1, 1, 0, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size03 = 7;
/* 1x^0 + 0x^1 + 0x^2 + 1x^3 + 0x^4 + 0x^5 + 0x^6 + 1x^7 */
const unsigned int primitive_polynomial04[] = {1, 0, 0, 1, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size04 = 8;
/* 1x^0 + 0x^1 + 1x^2 + 1x^3 + 1x^4 + 0x^5 + 0x^6 + 0x^7 + 1x^8 */
const unsigned int primitive_polynomial05[] = {1, 0, 1, 1, 1, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size05 = 9;
/* 1x^0 + 1x^1 + 1x^2 + 0x^3 + 0x^4 + 0x^5 + 0x^6 + 1x^7 + 1x^8 */
const unsigned int primitive_polynomial06[] = {1, 1, 1, 0, 0, 0, 0, 1, 1};
const unsigned int primitive_polynomial_size06 = 9;
/* 1x^0 + 0x^1 + 0x^2 + 0x^3 + 1x^4 + 0x^5 + 0x^6 + 0x^7 + 0x^8 + 1x^9 */
const unsigned int primitive_polynomial07[] = {1, 0, 0, 0, 1, 0, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size07 = 10;
/* 1x^0 + 0x^1 + 0x^2 + 1x^3 + 0x^4 + 0x^5 + 0x^6 + 0x^7 + 0x^8 + 0x^9 + 1x^10 */
const unsigned int primitive_polynomial08[] = {1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size08 = 11;
/* 1x^0 + 0x^1 + 1x^2 + 0x^3 + 0x^4 + 0x^5 + 0x^6 + 0x^7 + 0x^8 + 0x^9 + 0x^10 + 1x^11 */
const unsigned int primitive_polynomial09[] = {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size09 = 12;
/* 1x^0 + 1x^1 + 0x^2 + 0x^3 + 1x^4 + 0x^5 + 1x^6 + 0x^7 + 0x^8 + 0x^9 + 0x^10 + 0x^11 + 1x^12 */
const unsigned int primitive_polynomial10[] = {1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size10 = 13;
/* 1x^0 + 1x^1 + 0x^2 + 1x^3 + 1x^4 + 0x^5 + 0x^6 + 0x^7 + 0x^8 + 0x^9 + 0x^10 + 0x^11 + 0x^12 + 1x^13 */
const unsigned int primitive_polynomial11[] = {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size11 = 14;
/* 1x^0 + 1x^1 + 0x^2 + 0x^3 + 0x^4 + 0x^5 + 1x^6 + 0x^7 + 0x^8 + 0x^9 + 1x^10 + 0x^11 + 0x^12 + 0x^13 + 1x^14 */
const unsigned int primitive_polynomial12[] = {1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size12 = 15;
/* 1x^0 + 1x^1 + 0x^2 + 0x^3 + 0x^4 + 0x^5 + 0x^6 + 0x^7 + 0x^8 + 0x^9 + 0x^10 + 0x^11 + 0x^12 + 0x^13 + 0x^14 + 1x^15 */
const unsigned int primitive_polynomial13[] = {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size13 = 16;
/* 1x^0 + 1x^1 + 0x^2 + 1x^3 + 0x^4 + 0x^5 + 0x^6 + 0x^7 + 0x^8 + 0x^9 + 0x^10 + 0x^11 + 1x^12 + 0x^13 + 0x^14 + 0x^15 + 1x^16 */
const unsigned int primitive_polynomial14[] = {1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1};
const unsigned int primitive_polynomial_size14 = 17;
} // namespace galois
} // namespace schifra
#endif

View File

@ -0,0 +1,277 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_GALOIS_FIELD_ELEMENT_HPP
#define INCLUDE_SCHIFRA_GALOIS_FIELD_ELEMENT_HPP
#include <iostream>
#include <vector>
#include "schifra_galois_field.hpp"
namespace schifra
{
namespace galois
{
class field_element
{
public:
field_element(const field& gfield)
: field_(gfield),
poly_value_(-1)
{}
field_element(const field& gfield,const field_symbol& v)
: field_(const_cast<field&>(gfield)),
poly_value_(v)
{}
field_element(const field_element& gfe)
: field_(const_cast<field&>(gfe.field_)),
poly_value_(gfe.poly_value_)
{}
~field_element()
{}
inline field_element& operator = (const field_element& gfe)
{
if ((this != &gfe) && (&field_ == &gfe.field_))
{
poly_value_ = gfe.poly_value_;
}
return *this;
}
inline field_element& operator = (const field_symbol& v)
{
poly_value_ = v & field_.size();
return *this;
}
inline field_element& operator += (const field_element& gfe)
{
poly_value_ ^= gfe.poly_value_;
return *this;
}
inline field_element& operator += (const field_symbol& v)
{
poly_value_ ^= v;
return *this;
}
inline field_element& operator -= (const field_element& gfe)
{
*this += gfe;
return *this;
}
inline field_element& operator -= (const field_symbol& v)
{
*this += v;
return *this;
}
inline field_element& operator *= (const field_element& gfe)
{
poly_value_ = field_.mul(poly_value_, gfe.poly_value_);
return *this;
}
inline field_element& operator *= (const field_symbol& v)
{
poly_value_ = field_.mul(poly_value_, v);
return *this;
}
inline field_element& operator /= (const field_element& gfe)
{
poly_value_ = field_.div(poly_value_, gfe.poly_value_);
return *this;
}
inline field_element& operator /= (const field_symbol& v)
{
poly_value_ = field_.div(poly_value_, v);
return *this;
}
inline field_element& operator ^= (const int& n)
{
poly_value_ = field_.exp(poly_value_,n);
return *this;
}
inline bool operator == (const field_element& gfe) const
{
return ((field_ == gfe.field_) && (poly_value_ == gfe.poly_value_));
}
inline bool operator == (const field_symbol& v) const
{
return (poly_value_ == v);
}
inline bool operator != (const field_element& gfe) const
{
return ((field_ != gfe.field_) || (poly_value_ != gfe.poly_value_));
}
inline bool operator != (const field_symbol& v) const
{
return (poly_value_ != v);
}
inline bool operator < (const field_element& gfe)
{
return (poly_value_ < gfe.poly_value_);
}
inline bool operator < (const field_symbol& v)
{
return (poly_value_ < v);
}
inline bool operator > (const field_element& gfe)
{
return (poly_value_ > gfe.poly_value_);
}
inline bool operator > (const field_symbol& v)
{
return (poly_value_ > v);
}
inline field_symbol index() const
{
return field_.index(poly_value_);
}
inline field_symbol poly() const
{
return poly_value_;
}
inline field_symbol& poly()
{
return poly_value_;
}
inline const field& galois_field() const
{
return field_;
}
inline field_symbol inverse() const
{
return field_.inverse(poly_value_);
}
inline void normalize()
{
poly_value_ &= field_.size();
}
friend std::ostream& operator << (std::ostream& os, const field_element& gfe);
private:
const field& field_;
field_symbol poly_value_;
};
inline field_element operator + (const field_element& a, const field_element& b);
inline field_element operator - (const field_element& a, const field_element& b);
inline field_element operator * (const field_element& a, const field_element& b);
inline field_element operator * (const field_element& a, const field_symbol& b);
inline field_element operator * (const field_symbol& a, const field_element& b);
inline field_element operator / (const field_element& a, const field_element& b);
inline field_element operator ^ (const field_element& a, const int& b);
inline std::ostream& operator << (std::ostream& os, const field_element& gfe)
{
os << gfe.poly_value_;
return os;
}
inline field_element operator + (const field_element& a, const field_element& b)
{
field_element result = a;
result += b;
return result;
}
inline field_element operator - (const field_element& a, const field_element& b)
{
field_element result = a;
result -= b;
return result;
}
inline field_element operator * (const field_element& a, const field_element& b)
{
field_element result = a;
result *= b;
return result;
}
inline field_element operator * (const field_element& a, const field_symbol& b)
{
field_element result = a;
result *= b;
return result;
}
inline field_element operator * (const field_symbol& a, const field_element& b)
{
field_element result = b;
result *= a;
return result;
}
inline field_element operator / (const field_element& a, const field_element& b)
{
field_element result = a;
result /= b;
return result;
}
inline field_element operator ^ (const field_element& a, const int& b)
{
field_element result = a;
result ^= b;
return result;
}
} // namespace galois
} // namespace schifra
#endif

View File

@ -0,0 +1,839 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_GALOIS_FIELD_POLYNOMIAL_HPP
#define INCLUDE_SCHIFRA_GALOIS_FIELD_POLYNOMIAL_HPP
#include <cassert>
#include <iostream>
#include <vector>
#include "schifra_galois_field.hpp"
#include "schifra_galois_field_element.hpp"
namespace schifra
{
namespace galois
{
class field_polynomial
{
public:
field_polynomial(const field& gfield);
field_polynomial(const field& gfield, const unsigned int& degree);
field_polynomial(const field& gfield, const unsigned int& degree, const field_element element[]);
field_polynomial(const field_polynomial& polynomial);
field_polynomial(const field_element& gfe);
~field_polynomial() {}
bool valid() const;
int deg() const;
const field& galois_field() const;
void set_degree(const unsigned int& x);
void simplify();
field_polynomial& operator = (const field_polynomial& polynomial);
field_polynomial& operator = (const field_element& element);
field_polynomial& operator += (const field_polynomial& element);
field_polynomial& operator += (const field_element& element);
field_polynomial& operator -= (const field_polynomial& element);
field_polynomial& operator -= (const field_element& element);
field_polynomial& operator *= (const field_polynomial& polynomial);
field_polynomial& operator *= (const field_element& element);
field_polynomial& operator /= (const field_polynomial& divisor);
field_polynomial& operator /= (const field_element& element);
field_polynomial& operator %= (const field_polynomial& divisor);
field_polynomial& operator %= (const unsigned int& power);
field_polynomial& operator ^= (const unsigned int& n);
field_polynomial& operator <<= (const unsigned int& n);
field_polynomial& operator >>= (const unsigned int& n);
field_element& operator[] (const std::size_t& term);
field_element operator() (const field_element& value);
field_element operator() (field_symbol value);
const field_element& operator[](const std::size_t& term) const;
const field_element operator()(const field_element& value) const;
const field_element operator()(field_symbol value) const;
bool operator==(const field_polynomial& polynomial) const;
bool operator!=(const field_polynomial& polynomial) const;
bool monic() const;
field_polynomial derivative() const;
friend std::ostream& operator << (std::ostream& os, const field_polynomial& polynomial);
private:
typedef std::vector<field_element>::iterator poly_iter;
typedef std::vector<field_element>::const_iterator const_poly_iter;
void simplify(field_polynomial& polynomial) const;
field& field_;
std::vector<field_element> poly_;
};
field_polynomial operator + (const field_polynomial& a, const field_polynomial& b);
field_polynomial operator + (const field_polynomial& a, const field_element& b);
field_polynomial operator + (const field_element& a, const field_polynomial& b);
field_polynomial operator + (const field_polynomial& a, const field_symbol& b);
field_polynomial operator + (const field_symbol& a, const field_polynomial& b);
field_polynomial operator - (const field_polynomial& a, const field_polynomial& b);
field_polynomial operator - (const field_polynomial& a, const field_element& b);
field_polynomial operator - (const field_element& a, const field_polynomial& b);
field_polynomial operator - (const field_polynomial& a, const field_symbol& b);
field_polynomial operator - (const field_symbol& a, const field_polynomial& b);
field_polynomial operator * (const field_polynomial& a, const field_polynomial& b);
field_polynomial operator * (const field_element& a, const field_polynomial& b);
field_polynomial operator * (const field_polynomial& a, const field_element& b);
field_polynomial operator / (const field_polynomial& a, const field_polynomial& b);
field_polynomial operator / (const field_polynomial& a, const field_element& b);
field_polynomial operator % (const field_polynomial& a, const field_polynomial& b);
field_polynomial operator % (const field_polynomial& a, const unsigned int& power);
field_polynomial operator ^ (const field_polynomial& a, const int& n);
field_polynomial operator <<(const field_polynomial& a, const unsigned int& n);
field_polynomial operator >>(const field_polynomial& a, const unsigned int& n);
field_polynomial gcd(const field_polynomial& a, const field_polynomial& b);
inline field_polynomial::field_polynomial(const field& gfield)
: field_(const_cast<field&>(gfield))
{
poly_.clear();
poly_.reserve(256);
}
inline field_polynomial::field_polynomial(const field& gfield, const unsigned int& degree)
: field_(const_cast<field&>(gfield))
{
poly_.reserve(256);
poly_.resize(degree + 1,field_element(field_,0));
}
inline field_polynomial::field_polynomial(const field& gfield, const unsigned int& degree, const field_element element[])
: field_(const_cast<field&>(gfield))
{
poly_.reserve(256);
if (element != NULL)
{
/*
It is assumed that element is an array of field elements
with size/element count of degree + 1.
*/
for (unsigned int i = 0; i <= degree; ++i)
{
poly_.push_back(element[i]);
}
}
else
poly_.resize(degree + 1, field_element(field_, 0));
}
inline field_polynomial::field_polynomial(const field_polynomial& polynomial)
: field_(const_cast<field&>(polynomial.field_)),
poly_ (polynomial.poly_)
{}
inline field_polynomial::field_polynomial(const field_element& element)
: field_(const_cast<field&>(element.galois_field()))
{
poly_.resize(1,element);
}
inline bool field_polynomial::valid() const
{
return (poly_.size() > 0);
}
inline int field_polynomial::deg() const
{
return static_cast<int>(poly_.size()) - 1;
}
inline const field& field_polynomial::galois_field() const
{
return field_;
}
inline void field_polynomial::set_degree(const unsigned int& x)
{
poly_.resize(x - 1,field_element(field_,0));
}
inline field_polynomial& field_polynomial::operator = (const field_polynomial& polynomial)
{
if ((this != &polynomial) && (&field_ == &(polynomial.field_)))
{
poly_ = polynomial.poly_;
}
return *this;
}
inline field_polynomial& field_polynomial::operator = (const field_element& element)
{
if (&field_ == &(element.galois_field()))
{
poly_.resize(1,element);
}
return *this;
}
inline field_polynomial& field_polynomial::operator += (const field_polynomial& polynomial)
{
if (&field_ == &(polynomial.field_))
{
if (poly_.size() < polynomial.poly_.size())
{
const_poly_iter it0 = polynomial.poly_.begin();
for (poly_iter it1 = poly_.begin(); it1 != poly_.end(); ++it0, ++it1)
{
(*it1) += (*it0);
}
while (it0 != polynomial.poly_.end())
{
poly_.push_back(*it0);
++it0;
}
}
else
{
poly_iter it0 = poly_.begin();
for (const_poly_iter it1 = polynomial.poly_.begin(); it1 != polynomial.poly_.end(); ++it0, ++it1)
{
(*it0) += (*it1);
}
}
simplify(*this);
}
return *this;
}
inline field_polynomial& field_polynomial::operator += (const field_element& element)
{
poly_[0] += element;
return *this;
}
inline field_polynomial& field_polynomial::operator -= (const field_polynomial& element)
{
return (*this += element);
}
inline field_polynomial& field_polynomial::operator -= (const field_element& element)
{
poly_[0] -= element;
return *this;
}
inline field_polynomial& field_polynomial::operator *= (const field_polynomial& polynomial)
{
if (&field_ == &(polynomial.field_))
{
field_polynomial product(field_,deg() + polynomial.deg() + 1);
poly_iter result_it = product.poly_.begin();
for (poly_iter it0 = poly_.begin(); it0 != poly_.end(); ++it0)
{
poly_iter current_result_it = result_it;
for (const_poly_iter it1 = polynomial.poly_.begin(); it1 != polynomial.poly_.end(); ++it1)
{
(*current_result_it) += (*it0) * (*it1);
++current_result_it;
}
++result_it;
}
simplify(product);
poly_ = product.poly_;
}
return *this;
}
inline field_polynomial& field_polynomial::operator *= (const field_element& element)
{
if (field_ == element.galois_field())
{
for (poly_iter it = poly_.begin(); it != poly_.end(); ++it)
{
(*it) *= element;
}
}
return *this;
}
inline field_polynomial& field_polynomial::operator /= (const field_polynomial& divisor)
{
if (
(&field_ == &divisor.field_) &&
(deg() >= divisor.deg()) &&
(divisor.deg() >= 0)
)
{
field_polynomial quotient (field_, deg() - divisor.deg() + 1);
field_polynomial remainder(field_, divisor.deg() - 1);
for (int i = static_cast<int>(deg()); i >= 0; i--)
{
if (i <= static_cast<int>(quotient.deg()))
{
quotient[i] = remainder[remainder.deg()] / divisor[divisor.deg()];
for (int j = static_cast<int>(remainder.deg()); j > 0; --j)
{
remainder[j] = remainder[j - 1] + (quotient[i] * divisor[j]);
}
remainder[0] = poly_[i] + (quotient[i] * divisor[0]);
}
else
{
for (int j = static_cast<int>(remainder.deg()); j > 0; --j)
{
remainder[j] = remainder[j - 1];
}
remainder[0] = poly_[i];
}
}
simplify(quotient);
poly_ = quotient.poly_;
}
return *this;
}
inline field_polynomial& field_polynomial::operator /= (const field_element& element)
{
if (field_ == element.galois_field())
{
for (poly_iter it = poly_.begin(); it != poly_.end(); ++it)
{
(*it) /= element;
}
}
return *this;
}
inline field_polynomial& field_polynomial::operator %= (const field_polynomial& divisor)
{
if (
(field_ == divisor.field_) &&
(deg() >= divisor.deg() ) &&
(divisor.deg() >= 0 )
)
{
field_polynomial quotient (field_, deg() - divisor.deg() + 1);
field_polynomial remainder(field_, divisor.deg() - 1);
for (int i = static_cast<int>(deg()); i >= 0; i--)
{
if (i <= static_cast<int>(quotient.deg()))
{
quotient[i] = remainder[remainder.deg()] / divisor[divisor.deg()];
for (int j = static_cast<int>(remainder.deg()); j > 0; --j)
{
remainder[j] = remainder[j - 1] + (quotient[i] * divisor[j]);
}
remainder[0] = poly_[i] + (quotient[i] * divisor[0]);
}
else
{
for (int j = static_cast<int>(remainder.deg()); j > 0; --j)
{
remainder[j] = remainder[j - 1];
}
remainder[0] = poly_[i];
}
}
poly_ = remainder.poly_;
}
return *this;
}
inline field_polynomial& field_polynomial::operator %= (const unsigned int& power)
{
if (poly_.size() >= power)
{
poly_.resize(power,field_element(field_,0));
simplify(*this);
}
return *this;
}
inline field_polynomial& field_polynomial::operator ^= (const unsigned int& n)
{
field_polynomial result = *this;
for (std::size_t i = 0; i < n; ++i)
{
result *= *this;
}
*this = result;
return *this;
}
inline field_polynomial& field_polynomial::operator <<= (const unsigned int& n)
{
if (poly_.size() > 0)
{
size_t initial_size = poly_.size();
poly_.resize(poly_.size() + n, field_element(field_,0));
for (size_t i = initial_size - 1; static_cast<int>(i) >= 0; --i)
{
poly_[i + n] = poly_[i];
}
for (unsigned int i = 0; i < n; ++i)
{
poly_[i] = 0;
}
}
return *this;
}
inline field_polynomial& field_polynomial::operator >>= (const unsigned int& n)
{
if (n <= poly_.size())
{
for (unsigned int i = 0; i <= deg() - n; ++i)
{
poly_[i] = poly_[i + n];
}
poly_.resize(poly_.size() - n,field_element(field_,0));
}
else if (static_cast<int>(n) >= (deg() + 1))
{
poly_.resize(0,field_element(field_,0));
}
return *this;
}
inline const field_element& field_polynomial::operator [] (const std::size_t& term) const
{
assert(term < poly_.size());
return poly_[term];
}
inline field_element& field_polynomial::operator [] (const std::size_t& term)
{
assert(term < poly_.size());
return poly_[term];
}
inline field_element field_polynomial::operator () (const field_element& value)
{
field_element result(field_,0);
if (!poly_.empty())
{
int i = 0;
field_symbol total_sum = 0 ;
field_symbol value_poly_form = value.poly();
for (poly_iter it = poly_.begin(); it != poly_.end(); ++it, ++i)
{
total_sum ^= field_.mul(field_.exp(value_poly_form,i), (*it).poly());
}
result = total_sum;
}
return result;
}
inline const field_element field_polynomial::operator () (const field_element& value) const
{
if (!poly_.empty())
{
int i = 0;
field_symbol total_sum = 0 ;
field_symbol value_poly_form = value.poly();
for (const_poly_iter it = poly_.begin(); it != poly_.end(); ++it, ++i)
{
total_sum ^= field_.mul(field_.exp(value_poly_form,i), (*it).poly());
}
return field_element(field_,total_sum);
}
return field_element(field_,0);
}
inline field_element field_polynomial::operator () (field_symbol value)
{
if (!poly_.empty())
{
int i = 0;
field_symbol total_sum = 0 ;
for (const_poly_iter it = poly_.begin(); it != poly_.end(); ++it, ++i)
{
total_sum ^= field_.mul(field_.exp(value,i), (*it).poly());
}
return field_element(field_,total_sum);
}
return field_element(field_,0);
}
inline const field_element field_polynomial::operator () (field_symbol value) const
{
if (!poly_.empty())
{
int i = 0;
field_symbol total_sum = 0 ;
for (const_poly_iter it = poly_.begin(); it != poly_.end(); ++it, ++i)
{
total_sum ^= field_.mul(field_.exp(value, i), (*it).poly());
}
return field_element(field_,total_sum);
}
return field_element(field_,0);
}
inline bool field_polynomial::operator == (const field_polynomial& polynomial) const
{
if (field_ == polynomial.field_)
{
if (poly_.size() != polynomial.poly_.size())
return false;
else
{
const_poly_iter it0 = polynomial.poly_.begin();
for (const_poly_iter it1 = poly_.begin(); it1 != poly_.end(); ++it0, ++it1)
{
if ((*it0) != (*it1))
return false;
}
return true;
}
}
else
return false;
}
inline bool field_polynomial::operator != (const field_polynomial& polynomial) const
{
return !(*this == polynomial);
}
inline field_polynomial field_polynomial::derivative() const
{
if ((*this).poly_.size() > 1)
{
field_polynomial deriv(field_,deg());
const std::size_t upper_bound = poly_.size() - 1;
for (std::size_t i = 0; i < upper_bound; i += 2)
{
deriv.poly_[i] = poly_[i + 1];
}
simplify(deriv);
return deriv;
}
return field_polynomial(field_,0);
}
inline bool field_polynomial::monic() const
{
return (poly_[poly_.size() - 1] == static_cast<galois::field_symbol>(1));
}
inline void field_polynomial::simplify()
{
simplify(*this);
}
inline void field_polynomial::simplify(field_polynomial& polynomial) const
{
std::size_t poly_size = polynomial.poly_.size();
if ((poly_size > 0) && (polynomial.poly_.back() == 0))
{
poly_iter it = polynomial.poly_.end ();
poly_iter begin = polynomial.poly_.begin();
std::size_t count = 0;
while ((begin != it) && (*(--it) == 0))
{
++count;
}
if (0 != count)
{
polynomial.poly_.resize(poly_size - count, field_element(field_,0));
}
}
}
inline field_polynomial operator + (const field_polynomial& a, const field_polynomial& b)
{
field_polynomial result = a;
result += b;
return result;
}
inline field_polynomial operator + (const field_polynomial& a, const field_element& b)
{
field_polynomial result = a;
result += b;
return result;
}
inline field_polynomial operator + (const field_element& a, const field_polynomial& b)
{
field_polynomial result = b;
result += a;
return result;
}
inline field_polynomial operator + (const field_polynomial& a, const field_symbol& b)
{
return a + field_element(a.galois_field(),b);
}
inline field_polynomial operator + (const field_symbol& a, const field_polynomial& b)
{
return b + field_element(b.galois_field(),a);
}
inline field_polynomial operator - (const field_polynomial& a, const field_polynomial& b)
{
field_polynomial result = a;
result -= b;
return result;
}
inline field_polynomial operator - (const field_polynomial& a, const field_element& b)
{
field_polynomial result = a;
result -= b;
return result;
}
inline field_polynomial operator - (const field_element& a, const field_polynomial& b)
{
field_polynomial result = b;
result -= a;
return result;
}
inline field_polynomial operator - (const field_polynomial& a, const field_symbol& b)
{
return a - field_element(a.galois_field(),b);
}
inline field_polynomial operator - (const field_symbol& a, const field_polynomial& b)
{
return b - field_element(b.galois_field(),a);
}
inline field_polynomial operator * (const field_polynomial& a, const field_polynomial& b)
{
field_polynomial result = a;
result *= b;
return result;
}
inline field_polynomial operator * (const field_element& a, const field_polynomial& b)
{
field_polynomial result = b;
result *= a;
return result;
}
inline field_polynomial operator * (const field_polynomial& a, const field_element& b)
{
field_polynomial result = a;
result *= b;
return result;
}
inline field_polynomial operator / (const field_polynomial& a, const field_polynomial& b)
{
field_polynomial result = a;
result /= b;
return result;
}
inline field_polynomial operator / (const field_polynomial& a, const field_element& b)
{
field_polynomial result = a;
result /= b;
return result;
}
inline field_polynomial operator % (const field_polynomial& a, const field_polynomial& b)
{
field_polynomial result = a;
result %= b;
return result;
}
inline field_polynomial operator % (const field_polynomial& a, const unsigned int& n)
{
field_polynomial result = a;
result %= n;
return result;
}
inline field_polynomial operator ^ (const field_polynomial& a, const int& n)
{
field_polynomial result = a;
result ^= n;
return result;
}
inline field_polynomial operator << (const field_polynomial& a, const unsigned int& n)
{
field_polynomial result = a;
result <<= n;
return result;
}
inline field_polynomial operator >> (const field_polynomial& a, const unsigned int& n)
{
field_polynomial result = a;
result >>= n;
return result;
}
inline field_polynomial gcd(const field_polynomial& a, const field_polynomial& b)
{
if (&a.galois_field() == &b.galois_field())
{
if ((!a.valid()) && (!b.valid()))
{
field_polynomial error_polynomial(a.galois_field());
return error_polynomial;
}
if (!a.valid()) return b;
if (!b.valid()) return a;
field_polynomial x = a % b;
field_polynomial y = b;
field_polynomial z = x;
while ((z = (y % x)).valid())
{
y = x;
x = z;
}
return x;
}
else
{
field_polynomial error_polynomial(a.galois_field());
return error_polynomial;
}
}
inline field_polynomial generate_X(const field& gfield)
{
const field_element xgfe[2] = {
galois::field_element(gfield, 0),
galois::field_element(gfield, 1)
};
field_polynomial X_(gfield,1,xgfe);
return X_;
}
inline std::ostream& operator << (std::ostream& os, const field_polynomial& polynomial)
{
if (polynomial.deg() >= 0)
{
/*
for (unsigned int i = 0; i < polynomial.poly_.size(); ++i)
{
os << polynomial.poly[i].index()
<< ((i != (polynomial.deg())) ? " " : "");
}
std::cout << " poly form: ";
*/
for (unsigned int i = 0; i < polynomial.poly_.size(); ++i)
{
os << polynomial.poly_[i].poly()
<< " "
<< "x^"
<< i
<< ((static_cast<int>(i) != (polynomial.deg())) ? " + " : "");
}
}
return os;
}
} // namespace galois
} // namespace schifra
#endif

View File

@ -0,0 +1,115 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_GALOIS_UTILITIES_HPP
#define INCLUDE_SCHIFRA_GALOIS_UTILITIES_HPP
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <string>
#include <sstream>
#include "schifra_galois_field.hpp"
#include "schifra_galois_field_polynomial.hpp"
namespace schifra
{
namespace galois
{
inline std::string convert_to_string(const unsigned int& value, const unsigned int& width)
{
std::stringstream stream;
stream << std::setw(width) << std::setfill('0') << value;
return stream.str();
}
inline std::string convert_to_string(const int& value, const unsigned int& width)
{
std::stringstream stream;
stream << std::setw(width) << std::setfill('0') << value;
return stream.str();
}
inline std::string convert_to_bin(const unsigned int& value, const unsigned int& field_descriptor)
{
std::string output = std::string(field_descriptor, ' ');
for (unsigned int i = 0; i < field_descriptor; ++i)
{
output[i] = ((((value >> (field_descriptor - 1 - i)) & 1) == 1) ? '1' : '0');
}
return output;
}
inline void alpha_table(std::ostream& os, const field& gf)
{
std::vector<std::string> str_list;
for (unsigned int i = 0; i < gf.size() + 1; ++i)
{
str_list.push_back("alpha^" + convert_to_string(gf.index(i),2) + "\t" +
convert_to_bin (i,gf.pwr()) + "\t" +
convert_to_string(gf.alpha(i),2));
}
std::sort(str_list.begin(),str_list.end());
std::copy(str_list.begin(),str_list.end(),std::ostream_iterator<std::string>(os,"\n"));
}
inline void polynomial_alpha_form(std::ostream& os, const field_polynomial& polynomial)
{
for (int i = 0; i < (polynomial.deg() + 1); ++i)
{
field_symbol alpha_power = polynomial.galois_field().index(polynomial[i].poly());
if (alpha_power != 0)
os << static_cast<unsigned char>(224) << "^" << convert_to_string(alpha_power,2);
else
os << 1;
os << " * "
<< "x^"
<< i
<< ((i != (polynomial.deg())) ? " + " : "");
}
}
inline void polynomial_alpha_form(std::ostream& os, const std::string& prepend, const field_polynomial& polynomial)
{
os << prepend;
polynomial_alpha_form(os,polynomial);
os << std::endl;
}
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,201 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_BITIO_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_BITIO_HPP
#include <iostream>
namespace schifra
{
namespace reed_solomon
{
namespace bitio
{
template <std::size_t symbol_bit_count> class convert_data_to_symbol;
template <>
class convert_data_to_symbol<2>
{
public:
template <typename BitBlock>
convert_data_to_symbol(const BitBlock data[], const std::size_t data_length, int symbol[])
{
const BitBlock* d_it = & data[0];
int* s_it = &symbol[0];
for (std::size_t i = 0; i < data_length; ++i, ++d_it, s_it+=4)
{
(* s_it ) = (*d_it) & 0x03;
(*(s_it + 1)) = ((*d_it) >> 2) & 0x03;
(*(s_it + 2)) = ((*d_it) >> 4) & 0x03;
(*(s_it + 3)) = ((*d_it) >> 6) & 0x03;
}
}
};
template <>
class convert_data_to_symbol<4>
{
public:
template <typename BitBlock>
convert_data_to_symbol(const BitBlock data[], const std::size_t data_length, int symbol[])
{
const BitBlock* d_it = & data[0];
int* s_it = &symbol[0];
for (std::size_t i = 0; i < data_length; ++i, ++d_it, s_it+=2)
{
(* s_it ) = (*d_it) & 0x0F;
(*(s_it + 1)) = ((*d_it) >> 4) & 0x0F;
}
}
};
template <>
class convert_data_to_symbol<8>
{
public:
template <typename BitBlock>
convert_data_to_symbol(const BitBlock data[], const std::size_t data_length, int symbol[])
{
const BitBlock* d_it = & data[0];
int* s_it = &symbol[0];
for (std::size_t i = 0; i < data_length; ++i, ++d_it, ++s_it)
{
(*s_it) = (*d_it) & 0xFF;
}
}
};
template <>
class convert_data_to_symbol<16>
{
public:
template <typename BitBlock>
convert_data_to_symbol(const BitBlock data[], const std::size_t data_length, int symbol[])
{
const BitBlock* d_it = & data[0];
int* s_it = &symbol[0];
for (std::size_t i = 0; i < data_length; i+=2, d_it+=2, ++s_it)
{
(*s_it) = (*d_it) & 0x000000FF;
(*s_it) |= (static_cast<int>((*(d_it + 1))) << 8) & 0x0000FF00;
}
}
};
template <>
class convert_data_to_symbol<24>
{
public:
template <typename BitBlock>
convert_data_to_symbol(const BitBlock data[], const std::size_t data_length, int symbol[])
{
BitBlock* d_it = & data[0];
int* s_it = &symbol[0];
for (std::size_t i = 0; i < data_length; i+=3, d_it+=3, ++s_it)
{
(*s_it) |= (*d_it) & 0x000000FF;
(*s_it) |= (static_cast<int>((*(d_it + 1))) << 8) & 0x0000FF00;
(*s_it) |= (static_cast<int>((*(d_it + 2))) << 16) & 0x00FF0000;
}
}
};
template <std::size_t symbol_bit_count> class convert_symbol_to_data;
template <>
class convert_symbol_to_data<4>
{
public:
template <typename BitBlock>
convert_symbol_to_data(const int symbol[], BitBlock data[], const std::size_t data_length)
{
BitBlock* d_it = & data[0];
const int* s_it = &symbol[0];
for (std::size_t i = 0; i < data_length; ++i, ++d_it, ++s_it)
{
(*d_it) = (*s_it) & 0x0000000F;
(*d_it) |= ((*(s_it + 1)) & 0x0000000F) << 4;
}
}
};
template <>
class convert_symbol_to_data<8>
{
public:
template <typename BitBlock>
convert_symbol_to_data(const int symbol[], BitBlock data[], const std::size_t data_length)
{
BitBlock* d_it = & data[0];
const int* s_it = &symbol[0];
for (std::size_t i = 0; i < data_length; ++i, ++d_it, ++s_it)
{
(*d_it) = static_cast<BitBlock>((*s_it) & 0xFF);
}
}
};
template <>
class convert_symbol_to_data<16>
{
public:
template <typename BitBlock>
convert_symbol_to_data(const int symbol[], BitBlock data[], const std::size_t data_length)
{
BitBlock* d_it = & data[0];
const int* s_it = &symbol[0];
for (std::size_t i = 0; i < data_length; ++i, ++d_it, ++s_it)
{
(*d_it) = (*s_it) & 0xFFFF;
}
}
};
} // namespace bitio
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,382 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_BLOCK_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_BLOCK_HPP
#include <iostream>
#include <string>
#include "schifra_galois_field.hpp"
#include "schifra_ecc_traits.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length = code_length - fec_length>
struct block
{
public:
typedef galois::field_symbol symbol_type;
typedef traits::reed_solomon_triat<code_length,fec_length,data_length> trait;
typedef traits::symbol<code_length> symbol;
typedef block<code_length,fec_length,data_length> block_t;
enum error_t
{
e_no_error = 0,
e_encoder_error0 = 1,
e_encoder_error1 = 2,
e_decoder_error0 = 3,
e_decoder_error1 = 4,
e_decoder_error2 = 5,
e_decoder_error3 = 6,
e_decoder_error4 = 7
};
block()
: errors_detected (0),
errors_corrected(0),
zero_numerators (0),
unrecoverable(false),
error(e_no_error)
{
traits::validate_reed_solomon_block_parameters<code_length,fec_length,data_length>();
}
block(const std::string& _data, const std::string& _fec)
: errors_detected (0),
errors_corrected(0),
zero_numerators (0),
unrecoverable(false),
error(e_no_error)
{
traits::validate_reed_solomon_block_parameters<code_length,fec_length,data_length>();
for (std::size_t i = 0; i < data_length; ++i)
{
data[i] = static_cast<galois::field_symbol>(_data[i]);
}
for (std::size_t i = 0; i < fec_length; ++i)
{
data[i + data_length] = static_cast<galois::field_symbol>(_fec[i]);
}
}
galois::field_symbol& operator[](const std::size_t& index)
{
return data[index];
}
const galois::field_symbol& operator[](const std::size_t& index) const
{
return data[index];
}
galois::field_symbol& operator()(const std::size_t& index)
{
return operator[](index);
}
galois::field_symbol& fec(const std::size_t& index)
{
return data[data_length + index];
}
bool data_to_string(std::string& data_str) const
{
if (data_str.length() != data_length)
{
return false;
}
for (std::size_t i = 0; i < data_length; ++i)
{
data_str[i] = static_cast<char>(data[i]);
}
return true;
}
bool fec_to_string(std::string& fec_str) const
{
if (fec_str.length() != fec_length)
{
return false;
}
for (std::size_t i = 0; i < fec_length; ++i)
{
fec_str[i] = static_cast<char>(data[data_length + i]);
}
return true;
}
std::string fec_to_string() const
{
std::string fec_str(fec_length,0x00);
fec_to_string(fec_str);
return fec_str;
}
void clear(galois::field_symbol value = 0)
{
for (std::size_t i = 0; i < code_length; ++i)
{
data[i] = value;
}
}
void clear_data(galois::field_symbol value = 0)
{
for (std::size_t i = 0; i < data_length; ++i)
{
data[i] = value;
}
}
void clear_fec(galois::field_symbol value = 0)
{
for (std::size_t i = 0; i < fec_length; ++i)
{
data[data_length + i] = value;
}
}
void reset(galois::field_symbol value = 0)
{
clear(value);
errors_detected = 0;
errors_corrected = 0;
zero_numerators = 0;
unrecoverable = false;
error = e_no_error;
}
template <typename BlockType>
void copy_state(const BlockType& b)
{
errors_detected = b.errors_detected;
errors_corrected = b.errors_corrected;
zero_numerators = b.zero_numerators;
unrecoverable = b.unrecoverable;
error = static_cast<error_t>(b.error);
}
inline std::string error_as_string() const
{
switch (error)
{
case e_no_error : return "No Error";
case e_encoder_error0 : return "Invalid Encoder";
case e_encoder_error1 : return "Incompatible Generator Polynomial";
case e_decoder_error0 : return "Invalid Decoder";
case e_decoder_error1 : return "Decoder Failure - Non-zero Syndrome";
case e_decoder_error2 : return "Decoder Failure - Too Many Errors/Erasures";
case e_decoder_error3 : return "Decoder Failure - Invalid Symbol Correction";
case e_decoder_error4 : return "Decoder Failure - Invalid Codeword Correction";
default : return "Invalid Error Code";
}
}
std::size_t errors_detected;
std::size_t errors_corrected;
std::size_t zero_numerators;
bool unrecoverable;
error_t error;
galois::field_symbol data[code_length];
};
template <std::size_t code_length, std::size_t fec_length>
inline void copy(const block<code_length,fec_length>& src_block, block<code_length,fec_length>& dest_block)
{
for (std::size_t index = 0; index < code_length; ++index)
{
dest_block.data[index] = src_block.data[index];
}
}
template <typename T, std::size_t code_length, std::size_t fec_length>
inline void copy(const T src_data[], block<code_length,fec_length>& dest_block)
{
for (std::size_t index = 0; index < (code_length - fec_length); ++index, ++src_data)
{
dest_block.data[index] = static_cast<typename block<code_length,fec_length>::symbol_type>(*src_data);
}
}
template <typename T, std::size_t code_length, std::size_t fec_length>
inline void copy(const T src_data[],
const std::size_t& src_length,
block<code_length,fec_length>& dest_block)
{
for (std::size_t index = 0; index < src_length; ++index, ++src_data)
{
dest_block.data[index] = static_cast<typename block<code_length,fec_length>::symbol_type>(*src_data);
}
}
template <std::size_t code_length, std::size_t fec_length, std::size_t stack_size>
inline void copy(const block<code_length,fec_length> src_block_stack[stack_size],
block<code_length,fec_length> dest_block_stack[stack_size])
{
for (std::size_t row = 0; row < stack_size; ++row)
{
copy(src_block_stack[row], dest_block_stack[row]);
}
}
template <typename T, std::size_t code_length, std::size_t fec_length, std::size_t stack_size>
inline bool copy(const T src_data[],
const std::size_t src_length,
block<code_length,fec_length> dest_block_stack[stack_size])
{
const std::size_t data_length = code_length - fec_length;
if (src_length > (stack_size * data_length))
{
return false;
}
const std::size_t row_count = src_length / data_length;
for (std::size_t row = 0; row < row_count; ++row, src_data += data_length)
{
copy(src_data, dest_block_stack[row]);
}
if ((src_length % data_length) != 0)
{
copy(src_data, src_length % data_length, dest_block_stack[row_count]);
}
return true;
}
template <typename T, std::size_t code_length, std::size_t fec_length>
inline void full_copy(const block<code_length,fec_length>& src_block,
T dest_data[])
{
for (std::size_t i = 0; i < code_length; ++i, ++dest_data)
{
(*dest_data) = static_cast<T>(src_block[i]);
}
}
template <typename T, std::size_t code_length, std::size_t fec_length, std::size_t stack_size>
inline void copy(const block<code_length,fec_length> src_block_stack[stack_size],
T dest_data[])
{
const std::size_t data_length = code_length - fec_length;
for (std::size_t i = 0; i < stack_size; ++i)
{
for (std::size_t j = 0; j < data_length; ++j, ++dest_data)
{
(*dest_data) = static_cast<T>(src_block_stack[i][j]);
}
}
}
template <std::size_t code_length, std::size_t fec_length>
inline std::ostream& operator<<(std::ostream& os, const block<code_length,fec_length>& rs_block)
{
for (std::size_t i = 0; i < code_length; ++i)
{
os << static_cast<char>(rs_block[i]);
}
return os;
}
template <typename T, std::size_t block_length>
struct data_block
{
public:
typedef T value_type;
T& operator[](const std::size_t index) { return data[index]; }
const T& operator[](const std::size_t index) const { return data[index]; }
T* begin() { return data; }
const T* begin() const { return data; }
T* end() { return data + block_length; }
const T* end() const { return data + block_length; }
void clear(T value = 0)
{
for (std::size_t i = 0; i < block_length; ++i)
{
data[i] = value;
}
}
private:
T data[block_length];
};
template <typename T, std::size_t block_length>
inline void copy(const data_block<T,block_length>& src_block, data_block<T,block_length>& dest_block)
{
for (std::size_t index = 0; index < block_length; ++index)
{
dest_block[index] = src_block[index];
}
}
template <typename T, std::size_t block_length, std::size_t stack_size>
inline void copy(const data_block<T,block_length> src_block_stack[stack_size],
data_block<T,block_length> dest_block_stack[stack_size])
{
for (std::size_t row = 0; row < stack_size; ++row)
{
copy(src_block_stack[row], dest_block_stack[row]);
}
}
template <typename T, std::size_t block_length>
inline void full_copy(const data_block<T,block_length>& src_block, T dest_data[])
{
for (std::size_t i = 0; i < block_length; ++i, ++dest_data)
{
(*dest_data) = static_cast<T>(src_block[i]);
}
}
typedef std::vector<std::size_t> erasure_locations_t;
} // namespace reed_solomon
} // namepsace schifra
#endif

View File

@ -0,0 +1,998 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_CODEC_VALIDATOR_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_CODEC_VALIDATOR_HPP
#include <cstddef>
#include <iostream>
#include <string>
#include "schifra_galois_field.hpp"
#include "schifra_galois_field_polynomial.hpp"
#include "schifra_sequential_root_generator_polynomial_creator.hpp"
#include "schifra_reed_solomon_block.hpp"
#include "schifra_reed_solomon_encoder.hpp"
#include "schifra_reed_solomon_decoder.hpp"
#include "schifra_ecc_traits.hpp"
#include "schifra_error_processes.hpp"
#include "schifra_utilities.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length,
std::size_t fec_length,
typename encoder_type = encoder<code_length,fec_length>,
typename decoder_type = decoder<code_length,fec_length>,
std::size_t data_length = code_length - fec_length>
class codec_validator
{
public:
typedef block<code_length,fec_length> block_type;
codec_validator(const galois::field& gf,
const unsigned int gpii,
const std::string& msg)
: field_(gf),
generator_polynomial_(galois::field_polynomial(field_)),
rs_encoder_(reinterpret_cast<encoder_type*>(0)),
rs_decoder_(reinterpret_cast<decoder_type*>(0)),
message(msg),
genpoly_initial_index_(gpii),
blocks_processed_(0),
block_failures_(0)
{
traits::equivalent_encoder_decoder<encoder_type,decoder_type>();
if (
!make_sequential_root_generator_polynomial(field_,
genpoly_initial_index_,
fec_length,
generator_polynomial_)
)
{
return;
}
rs_encoder_ = new encoder_type(field_,generator_polynomial_);
rs_decoder_ = new decoder_type(field_,genpoly_initial_index_);
if (!rs_encoder_->encode(message,rs_block_original))
{
std::cout << "codec_validator() - ERROR: Encoding process failed!" << std::endl;
return;
}
}
bool execute()
{
schifra::utils::timer timer;
timer.start();
bool result = stage1() &&
stage2() &&
stage3() &&
stage4() &&
stage5() &&
stage6() &&
stage7() &&
stage8() &&
stage9() &&
stage10() &&
stage11() &&
stage12() ;
timer.stop();
double time = timer.time();
print_codec_properties();
std::cout << "Blocks decoded: " << blocks_processed_ <<
"\tDecoding Failures: " << block_failures_ <<
"\tRate: " << ((blocks_processed_ * data_length) * 8.0) / (1048576.0 * time) << "Mbps" << std::endl;
/*
Note: The throughput rate is not only the throughput of reed solomon
encoding and decoding, but also that of the steps needed to add
simulated transmission errors to the reed solomon block such as
the calculation of the positions and additions of errors and
erasures to the reed solomon block, which normally in a true
data transmission medium would not be taken into consideration.
*/
return result;
}
~codec_validator()
{
delete rs_encoder_;
delete rs_decoder_;
}
void print_codec_properties()
{
std::cout << "Codec: RS(" << code_length << "," << data_length << "," << fec_length <<") ";
}
private:
bool stage1()
{
/* Burst Error Only Combinations */
const std::size_t initial_failure_count = block_failures_;
for (std::size_t error_count = 1; error_count <= (fec_length >> 1); ++error_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
block_type rs_block = rs_block_original;
corrupt_message_all_errors
(
rs_block,
error_count,
start_position,
1
);
if (!rs_decoder_->decode(rs_block))
{
print_codec_properties();
std::cout << "stage1() - Decoding Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage1() - Error Correcting Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage1() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != error_count)
{
print_codec_properties();
std::cout << "stage1() - Error In The Number Of Detected Errors! Errors Detected: " << rs_block.errors_detected << std::endl;
++block_failures_;
}
else if (rs_block.errors_corrected != error_count)
{
print_codec_properties();
std::cout << "stage1() - Error In The Number Of Corrected Errors! Errors Corrected: " << rs_block.errors_corrected << std::endl;
++block_failures_;
}
++blocks_processed_;
}
}
return (block_failures_ == initial_failure_count);
}
bool stage2()
{
/* Burst Erasure Only Combinations */
const std::size_t initial_failure_count = block_failures_;
erasure_locations_t erasure_list;
for (std::size_t erasure_count = 1; erasure_count <= fec_length; ++erasure_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
block_type rs_block = rs_block_original;
corrupt_message_all_erasures
(
rs_block,
erasure_list,
erasure_count,
start_position,
1
);
if (!rs_decoder_->decode(rs_block,erasure_list))
{
print_codec_properties();
std::cout << "stage2() - Decoding Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
std::cout << "stage2() - Error Correcting Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage2() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != erasure_count)
{
print_codec_properties();
std::cout << "stage2() - Error In The Number Of Detected Errors! Errors Detected: " << rs_block.errors_detected << std::endl;
++block_failures_;
}
else if (rs_block.errors_corrected != erasure_count)
{
print_codec_properties();
std::cout << "stage2() - Error In The Number Of Corrected Errors! Errors Corrected: " << rs_block.errors_corrected << std::endl;
++block_failures_;
}
++blocks_processed_;
erasure_list.clear();
}
}
return (block_failures_ == initial_failure_count);
}
bool stage3()
{
/* Consecutive Burst Erasure and Error Combinations */
const std::size_t initial_failure_count = block_failures_;
erasure_locations_t erasure_list;
for (std::size_t erasure_count = 1; erasure_count <= fec_length; ++erasure_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
block_type rs_block = rs_block_original;
corrupt_message_errors_erasures
(
rs_block,
error_mode::erasures_errors,
start_position,erasure_count,
erasure_list
);
if (!rs_decoder_->decode(rs_block,erasure_list))
{
print_codec_properties();
std::cout << "stage3() - Decoding Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage3() - Error Correcting Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage3() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
++blocks_processed_;
erasure_list.clear();
}
}
return (block_failures_ == initial_failure_count);
}
bool stage4()
{
/* Consecutive Burst Error and Erasure Combinations */
const std::size_t initial_failure_count = block_failures_;
erasure_locations_t erasure_list;
for (std::size_t erasure_count = 1; erasure_count <= fec_length; ++erasure_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
block_type rs_block = rs_block_original;
corrupt_message_errors_erasures
(
rs_block,
error_mode::errors_erasures,
start_position,
erasure_count,
erasure_list
);
if (!rs_decoder_->decode(rs_block,erasure_list))
{
print_codec_properties();
std::cout << "stage4() - Decoding Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage4() - Error Correcting Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage4() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
++blocks_processed_;
erasure_list.clear();
}
}
return (block_failures_ == initial_failure_count);
}
bool stage5()
{
/* Distanced Burst Erasure and Error Combinations */
const std::size_t initial_failure_count = block_failures_;
erasure_locations_t erasure_list;
for (std::size_t between_distance = 1; between_distance <= 10; ++between_distance)
{
for (std::size_t erasure_count = 1; erasure_count <= fec_length; ++erasure_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
block_type rs_block = rs_block_original;
corrupt_message_errors_erasures
(
rs_block,
error_mode::erasures_errors,
start_position,
erasure_count,
erasure_list,
between_distance
);
if (!rs_decoder_->decode(rs_block,erasure_list))
{
print_codec_properties();
std::cout << "stage5() - Decoding Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage5() - Error Correcting Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage5() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
++blocks_processed_;
erasure_list.clear();
}
}
}
return (block_failures_ == initial_failure_count);
}
bool stage6()
{
/* Distanced Burst Error and Erasure Combinations */
const std::size_t initial_failure_count = block_failures_;
erasure_locations_t erasure_list;
for (std::size_t between_distance = 1; between_distance <= 10; ++between_distance)
{
for (std::size_t erasure_count = 1; erasure_count <= fec_length; ++erasure_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
block_type rs_block = rs_block_original;
corrupt_message_errors_erasures
(
rs_block,
error_mode::errors_erasures,
start_position,
erasure_count,
erasure_list,between_distance
);
if (!rs_decoder_->decode(rs_block,erasure_list))
{
print_codec_properties();
std::cout << "stage6() - Decoding Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage6() - Error Correcting Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage6() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
++blocks_processed_;
erasure_list.clear();
}
}
}
return (block_failures_ == initial_failure_count);
}
bool stage7()
{
/* Intermittent Error Combinations */
const std::size_t initial_failure_count = block_failures_;
for (std::size_t error_count = 1; error_count < (fec_length >> 1); ++error_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
for (std::size_t scale = 1; scale < 5; ++scale)
{
block_type rs_block = rs_block_original;
corrupt_message_all_errors
(
rs_block,
error_count,
start_position,
scale
);
if (!rs_decoder_->decode(rs_block))
{
print_codec_properties();
std::cout << "stage7() - Decoding Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage7() - Error Correcting Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage7() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != error_count)
{
print_codec_properties();
std::cout << "stage7() - Error In The Number Of Detected Errors! Errors Detected: " << rs_block.errors_detected << std::endl;
++block_failures_;
}
else if (rs_block.errors_corrected != error_count)
{
print_codec_properties();
std::cout << "stage7() - Error In The Number Of Corrected Errors! Errors Corrected: " << rs_block.errors_corrected << std::endl;
++block_failures_;
}
++blocks_processed_;
}
}
}
return (block_failures_ == initial_failure_count);
}
bool stage8()
{
/* Intermittent Erasure Combinations */
const std::size_t initial_failure_count = block_failures_;
erasure_locations_t erasure_list;
for (std::size_t erasure_count = 1; erasure_count <= fec_length; ++erasure_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
for (std::size_t scale = 4; scale < 5; ++scale)
{
block_type rs_block = rs_block_original;
corrupt_message_all_erasures
(
rs_block,
erasure_list,
erasure_count,
start_position,
scale
);
if (!rs_decoder_->decode(rs_block,erasure_list))
{
print_codec_properties();
std::cout << "stage8() - Decoding Failure! start position: " << start_position << "\t scale: " << scale << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage8() - Error Correcting Failure! start position: " << start_position << "\t scale: " << scale <<std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != (rs_block.errors_corrected + rs_block.zero_numerators))
{
print_codec_properties();
std::cout << "stage8() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected > erasure_count)
{
print_codec_properties();
std::cout << "stage8() - Error In The Number Of Detected Errors! Errors Detected: " << rs_block.errors_detected << std::endl;
++block_failures_;
}
else if (rs_block.errors_corrected > erasure_count)
{
print_codec_properties();
std::cout << "stage8() - Error In The Number Of Corrected Errors! Errors Corrected: " << rs_block.errors_corrected << std::endl;
++block_failures_;
}
++blocks_processed_;
erasure_list.clear();
}
}
}
return (block_failures_ == initial_failure_count);
}
bool stage9()
{
/* Burst Interleaved Error and Erasure Combinations */
const std::size_t initial_failure_count = block_failures_;
erasure_locations_t erasure_list;
for (std::size_t erasure_count = 1; erasure_count <= fec_length; ++erasure_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
block_type rs_block = rs_block_original;
corrupt_message_interleaved_errors_erasures
(
rs_block,
start_position,
erasure_count,
erasure_list
);
if (!rs_decoder_->decode(rs_block,erasure_list))
{
print_codec_properties();
std::cout << "stage9() - Decoding Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage9() - Error Correcting Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage9() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
++blocks_processed_;
erasure_list.clear();
}
}
return (block_failures_ == initial_failure_count);
}
bool stage10()
{
/* Segmented Burst Errors */
const std::size_t initial_failure_count = block_failures_;
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
for (std::size_t distance_between_blocks = 0; distance_between_blocks < 5; ++distance_between_blocks)
{
block_type rs_block = rs_block_original;
corrupt_message_all_errors_segmented
(
rs_block,
start_position,
distance_between_blocks
);
if (!rs_decoder_->decode(rs_block))
{
print_codec_properties();
std::cout << "stage10() - Decoding Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage10() - Error Correcting Failure! start position: " << start_position << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage10() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
++blocks_processed_;
}
}
return (block_failures_ == initial_failure_count);
}
bool stage11()
{
/* No Errors */
const std::size_t initial_failure_count = block_failures_;
block_type rs_block = rs_block_original;
if (!rs_decoder_->decode(rs_block))
{
print_codec_properties();
std::cout << "stage11() - Decoding Failure!" << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage11() - Error Correcting Failure!" << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != 0)
{
print_codec_properties();
std::cout << "stage11() - Error Correcting Failure!" << std::endl;
++block_failures_;
}
else if (rs_block.errors_corrected != 0)
{
print_codec_properties();
std::cout << "stage11() - Error Correcting Failure!" << std::endl;
++block_failures_;
}
else if (rs_block.unrecoverable)
{
print_codec_properties();
std::cout << "stage11() - Error Correcting Failure!" << std::endl;
++block_failures_;
}
++blocks_processed_;
return (block_failures_ == initial_failure_count);
}
bool stage12()
{
/* Random Errors Only */
const std::size_t initial_failure_count = block_failures_;
std::vector<std::size_t> random_error_index;
generate_error_index((fec_length >> 1),random_error_index,0xA5A5A5A5);
for (std::size_t error_count = 1; error_count <= (fec_length >> 1); ++error_count)
{
for (std::size_t error_index = 0; error_index < error_index_size; ++error_index)
{
block_type rs_block = rs_block_original;
corrupt_message_all_errors_at_index
(
rs_block,
error_count,
error_index,
random_error_index
);
if (!rs_decoder_->decode(rs_block))
{
print_codec_properties();
std::cout << "stage12() - Decoding Failure! error index: " << error_index << std::endl;
++block_failures_;
}
else if (!is_block_equivelent(rs_block,message))
{
print_codec_properties();
std::cout << "stage12() - Error Correcting Failure! error index: " << error_index << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != rs_block.errors_corrected)
{
print_codec_properties();
std::cout << "stage12() - Discrepancy between the number of errors detected and corrected. [" << rs_block.errors_detected << "," << rs_block.errors_corrected << "]" << std::endl;
++block_failures_;
}
else if (rs_block.errors_detected != error_count)
{
print_codec_properties();
std::cout << "stage12() - Error In The Number Of Detected Errors! Errors Detected: " << rs_block.errors_detected << std::endl;
++block_failures_;
}
else if (rs_block.errors_corrected != error_count)
{
print_codec_properties();
std::cout << "stage12() - Error In The Number Of Corrected Errors! Errors Corrected: " << rs_block.errors_corrected << std::endl;
++block_failures_;
}
++blocks_processed_;
}
}
return (block_failures_ == initial_failure_count);
}
protected:
codec_validator() {}
private:
codec_validator(const codec_validator&);
const codec_validator& operator=(const codec_validator&);
const galois::field& field_;
galois::field_polynomial generator_polynomial_;
encoder_type* rs_encoder_;
decoder_type* rs_decoder_;
block_type rs_block_original;
const std::string& message;
const unsigned int genpoly_initial_index_;
unsigned int blocks_processed_;
unsigned int block_failures_;
};
template <std::size_t data_length>
void create_messages(std::vector<std::string>& message_list, const bool full_test_set = false)
{
/* Various message bit patterns */
message_list.clear();
if (full_test_set)
{
for (std::size_t i = 0; i < 256; ++i)
{
message_list.push_back(std::string(data_length, static_cast<unsigned char>(i)));
}
}
else
{
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x00)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xAA)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xA5)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xAC)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xCA)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x5A)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xCC)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xF0)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x0F)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xFF)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x92)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x6D)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x77)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x7A)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xA7)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xE5)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xEB)));
}
std::string tmp_str = std::string(data_length,static_cast<unsigned char>(0x00));
for (std::size_t i = 0; i < data_length; ++i)
{
tmp_str[i] = static_cast<unsigned char>(i);
}
message_list.push_back(tmp_str);
for (int i = data_length - 1; i >= 0; --i)
{
tmp_str[i] = static_cast<unsigned char>(i);
}
message_list.push_back(tmp_str);
for (std::size_t i = 0; i < data_length; ++i)
{
tmp_str[i] = (((i & 0x01) == 1) ? static_cast<unsigned char>(i) : 0x00);
}
message_list.push_back(tmp_str);
for (std::size_t i = 0; i < data_length; ++i)
{
tmp_str[i] = (((i & 0x01) == 0) ? static_cast<unsigned char>(i) : 0x00);
}
message_list.push_back(tmp_str);
for (int i = data_length - 1; i >= 0; --i)
{
tmp_str[i] = (((i & 0x01) == 1) ? static_cast<unsigned char>(i) : 0x00);
}
message_list.push_back(tmp_str);
for (int i = data_length - 1; i >= 0; --i)
{
tmp_str[i] = (((i & 0x01) == 0) ? static_cast<unsigned char>(i) : 0x00);
}
message_list.push_back(tmp_str);
tmp_str = std::string(data_length,static_cast<unsigned char>(0x00));
for (std::size_t i = 0; i < (data_length >> 1); ++i)
{
tmp_str[i] = static_cast<unsigned char>(0xFF);
}
message_list.push_back(tmp_str);
tmp_str = std::string(data_length,static_cast<unsigned char>(0xFF));
for (std::size_t i = 0; i < (data_length >> 1); ++i)
{
tmp_str[i] = static_cast<unsigned char>(0x00);
}
message_list.push_back(tmp_str);
}
template <std::size_t field_descriptor, std::size_t gen_poly_index, std::size_t code_length, std::size_t fec_length>
inline bool codec_validation_test(const std::size_t prim_poly_size,const unsigned int prim_poly[])
{
const unsigned int data_length = code_length - fec_length;
galois::field field(field_descriptor,prim_poly_size,prim_poly);
std::vector<std::string> message_list;
create_messages<data_length>(message_list);
for (std::size_t i = 0; i < message_list.size(); ++i)
{
codec_validator<code_length,fec_length>
validator(field, gen_poly_index, message_list[i]);
if (!validator.execute())
{
return false;
}
}
return true;
}
template <std::size_t field_descriptor,
std::size_t gen_poly_index,
std::size_t code_length,
std::size_t fec_length>
inline bool shortened_codec_validation_test(const std::size_t prim_poly_size,const unsigned int prim_poly[])
{
typedef shortened_encoder<code_length,fec_length> encoder_type;
typedef shortened_decoder<code_length,fec_length> decoder_type;
const unsigned int data_length = code_length - fec_length;
galois::field field(field_descriptor,prim_poly_size,prim_poly);
std::vector<std::string> message_list;
create_messages<data_length>(message_list);
for (std::size_t i = 0; i < message_list.size(); ++i)
{
codec_validator<code_length,fec_length,encoder_type,decoder_type>
validator(field,gen_poly_index,message_list[i]);
if (!validator.execute())
{
return false;
}
}
return true;
}
inline bool codec_validation_test00()
{
return codec_validation_test<8,120,255, 2>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 4>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 6>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 10>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 12>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 14>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 16>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 18>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 20>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 22>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 24>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 32>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 64>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 80>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255, 96>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) &&
codec_validation_test<8,120,255,128>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) ;
}
inline bool codec_validation_test01()
{
return shortened_codec_validation_test<8,120,126,14>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) && /* Intelsat 1 RS Code */
shortened_codec_validation_test<8,120,194,16>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) && /* Intelsat 2 RS Code */
shortened_codec_validation_test<8,120,219,18>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) && /* Intelsat 3 RS Code */
shortened_codec_validation_test<8,120,225,20>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) && /* Intelsat 4 RS Code */
shortened_codec_validation_test<8, 1,204,16>(galois::primitive_polynomial_size05,galois::primitive_polynomial05) && /* DBV/MPEG-2 TSP RS Code */
shortened_codec_validation_test<8, 1,104,27>(galois::primitive_polynomial_size05,galois::primitive_polynomial05) && /* Magnetic Storage Outer RS Code */
shortened_codec_validation_test<8, 1,204,12>(galois::primitive_polynomial_size05,galois::primitive_polynomial05) && /* Magnetic Storage Inner RS Code */
shortened_codec_validation_test<8,120, 72,10>(galois::primitive_polynomial_size06,galois::primitive_polynomial06) ; /* VDL Mode 3 RS Code */
}
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,499 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_DECODER_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_DECODER_HPP
#include "schifra_galois_field.hpp"
#include "schifra_galois_field_element.hpp"
#include "schifra_galois_field_polynomial.hpp"
#include "schifra_reed_solomon_block.hpp"
#include "schifra_ecc_traits.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length = code_length - fec_length>
class decoder
{
public:
typedef traits::reed_solomon_triat<code_length,fec_length,data_length> trait;
typedef block<code_length,fec_length> block_type;
decoder(const galois::field& field, const unsigned int& gen_initial_index = 0)
: decoder_valid_(field.size() == code_length),
field_(field),
X_(galois::generate_X(field_)),
gen_initial_index_(gen_initial_index)
{
if (decoder_valid_)
{
//Note: code_length and field size can be used interchangeably
create_lookup_tables();
}
};
const galois::field& field() const
{
return field_;
}
bool decode(block_type& rsblock) const
{
std::vector<std::size_t> erasure_list;
return decode(rsblock,erasure_list);
}
bool decode(block_type& rsblock, const erasure_locations_t& erasure_list) const
{
if ((!decoder_valid_) || (erasure_list.size() > fec_length))
{
rsblock.errors_detected = 0;
rsblock.errors_corrected = 0;
rsblock.zero_numerators = 0;
rsblock.unrecoverable = true;
rsblock.error = block_type::e_decoder_error0;
return false;
}
galois::field_polynomial received(field_,code_length - 1);
load_message(received,rsblock);
galois::field_polynomial syndrome(field_);
if (compute_syndrome(received,syndrome) == 0)
{
rsblock.errors_detected = 0;
rsblock.errors_corrected = 0;
rsblock.zero_numerators = 0;
rsblock.unrecoverable = false;
return true;
}
galois::field_polynomial lambda(galois::field_element(field_,1));
erasure_locations_t erasure_locations;
if (!erasure_list.empty())
{
prepare_erasure_list(erasure_locations, erasure_list);
compute_gamma(lambda, erasure_locations);
}
if (erasure_list.size() < fec_length)
{
modified_berlekamp_massey_algorithm(lambda, syndrome, erasure_list.size());
}
std::vector<int> error_locations;
find_roots(lambda, error_locations);
if (0 == error_locations.size())
{
/*
Syndrome is non-zero yet no error locations have
been obtained, conclusion:
It is possible that there are MORE errrors in the
message than can be detected and corrected for this
particular code.
*/
rsblock.errors_detected = 0;
rsblock.errors_corrected = 0;
rsblock.zero_numerators = 0;
rsblock.unrecoverable = true;
rsblock.error = block_type::e_decoder_error1;
return false;
}
else if (((2 * error_locations.size()) - erasure_list.size()) > fec_length)
{
/*
Too many errors\erasures! 2E + S <= fec_length
L = E + S
E = L - S
2E = 2L - 2S
2E + S = 2L - 2S + S
= 2L - S
Where:
L : Error Locations
E : Errors
S : Erasures
*/
rsblock.errors_detected = error_locations.size();
rsblock.errors_corrected = 0;
rsblock.zero_numerators = 0;
rsblock.unrecoverable = true;
rsblock.error = block_type::e_decoder_error2;
return false;
}
else
rsblock.errors_detected = error_locations.size();
return forney_algorithm(error_locations, lambda, syndrome, rsblock);
}
private:
decoder();
decoder(const decoder& dec);
decoder& operator=(const decoder& dec);
protected:
void load_message(galois::field_polynomial& received, const block_type& rsblock) const
{
/*
Load message data into received polynomial in reverse order.
*/
for (std::size_t i = 0; i < code_length; ++i)
{
received[code_length - 1 - i] = rsblock[i];
}
}
void create_lookup_tables()
{
root_exponent_table_.reserve(field_.size() + 1);
for (int i = 0; i < static_cast<int>(field_.size() + 1); ++i)
{
root_exponent_table_.push_back(field_.exp(field_.alpha(code_length - i),(1 - gen_initial_index_)));
}
syndrome_exponent_table_.reserve(fec_length);
for (int i = 0; i < static_cast<int>(fec_length); ++i)
{
syndrome_exponent_table_.push_back(field_.alpha(gen_initial_index_ + i));
}
gamma_table_.reserve(field_.size() + 1);
for (int i = 0; i < static_cast<int>(field_.size() + 1); ++i)
{
gamma_table_.push_back((1 + (X_ * galois::field_element(field_,field_.alpha(i)))));
}
}
void prepare_erasure_list(erasure_locations_t& erasure_locations, const erasure_locations_t& erasure_list) const
{
/*
Note: 1. Erasure positions must be unique.
2. Erasure positions must exist within the code block.
There are NO exceptions to these rules!
*/
erasure_locations.resize(erasure_list.size());
for (std::size_t i = 0; i < erasure_list.size(); ++i)
{
erasure_locations[i] = (code_length - 1 - erasure_list[i]);
}
}
int compute_syndrome(const galois::field_polynomial& received,
galois::field_polynomial& syndrome) const
{
int error_flag = 0;
syndrome = galois::field_polynomial(field_,fec_length - 1);
for (std::size_t i = 0; i < fec_length; ++i)
{
syndrome[i] = received(syndrome_exponent_table_[i]);
error_flag |= syndrome[i].poly();
}
return error_flag;
}
void compute_gamma(galois::field_polynomial& gamma, const erasure_locations_t& erasure_locations) const
{
for (std::size_t i = 0; i < erasure_locations.size(); ++i)
{
gamma *= gamma_table_[erasure_locations[i]];
}
}
void find_roots(const galois::field_polynomial& poly, std::vector<int>& root_list) const
{
/*
Chien Search: Find the roots of the error locator polynomial
via an exhaustive search over all non-zero elements in the
given finite field.
*/
root_list.reserve(fec_length << 1);
root_list.resize(0);
const std::size_t polynomial_degree = poly.deg();
for (int i = 1; i <= static_cast<int>(code_length); ++i)
{
if (0 == poly(field_.alpha(i)).poly())
{
root_list.push_back(i);
if (polynomial_degree == root_list.size())
{
break;
}
}
}
}
void compute_discrepancy(galois::field_element& discrepancy,
const galois::field_polynomial& lambda,
const galois::field_polynomial& syndrome,
const std::size_t& l,
const std::size_t& round) const
{
/*
*
Compute the lambda discrepancy at the current round of BMA
min: if(a<b)?a:b
*/
// std::size_t upper_bound = std::min(static_cast<int>(l), lambda.deg());
// does not compile under Windows and has been replaced with:
std::size_t bb = lambda.deg();
std::size_t aa = static_cast<int>(l);
std::size_t upper_bound = 0;
if (aa < bb)
upper_bound = aa;
else
upper_bound = bb;
discrepancy = 0;
for (std::size_t i = 0; i <= upper_bound; ++i)
{
discrepancy += lambda[i] * syndrome[round - i];
}
}
void modified_berlekamp_massey_algorithm(galois::field_polynomial& lambda,
const galois::field_polynomial& syndrome,
const std::size_t erasure_count) const
{
/*
Modified Berlekamp-Massey Algorithm
Identify the shortest length linear feed-back shift register (LFSR)
that will generate the sequence equivalent to the syndrome.
*/
int i = -1;
std::size_t l = erasure_count;
galois::field_element discrepancy(field_,0);
galois::field_polynomial previous_lambda = lambda << 1;
for (std::size_t round = erasure_count; round < fec_length; ++round)
{
compute_discrepancy(discrepancy, lambda, syndrome, l, round);
if (discrepancy != 0)
{
galois::field_polynomial tau = lambda - (discrepancy * previous_lambda);
if (static_cast<int>(l) < (static_cast<int>(round) - i))
{
const std::size_t tmp = round - i;
i = static_cast<int>(round - l);
l = tmp;
previous_lambda = lambda / discrepancy;
}
lambda = tau;
}
previous_lambda <<= 1;
}
}
bool forney_algorithm(const std::vector<int>& error_locations,
const galois::field_polynomial& lambda,
const galois::field_polynomial& syndrome,
block_type& rsblock) const
{
/*
The Forney algorithm for computing the error magnitudes
*/
const galois::field_polynomial omega = (lambda * syndrome) % fec_length;
const galois::field_polynomial lambda_derivative = lambda.derivative();
rsblock.errors_corrected = 0;
rsblock.zero_numerators = 0;
for (std::size_t i = 0; i < error_locations.size(); ++i)
{
const unsigned int error_location = error_locations[i];
const galois::field_symbol alpha_inverse = field_.alpha(error_location);
const galois::field_symbol numerator = (omega(alpha_inverse) * root_exponent_table_[error_location]).poly();
const galois::field_symbol denominator = lambda_derivative(alpha_inverse).poly();
if (0 != numerator)
{
if (0 != denominator)
{
rsblock[error_location - 1] ^= field_.div(numerator, denominator);
rsblock.errors_corrected++;
}
else
{
rsblock.unrecoverable = true;
rsblock.error = block_type::e_decoder_error3;
return false;
}
}
else
++rsblock.zero_numerators;
}
if (lambda.deg() == static_cast<int>(rsblock.errors_detected))
return true;
else
{
rsblock.unrecoverable = true;
rsblock.error = block_type::e_decoder_error4;
return false;
}
}
protected:
bool decoder_valid_;
const galois::field& field_;
std::vector<galois::field_symbol> root_exponent_table_;
std::vector<galois::field_symbol> syndrome_exponent_table_;
std::vector<galois::field_polynomial> gamma_table_;
const galois::field_polynomial X_;
const unsigned int gen_initial_index_;
};
template <std::size_t code_length,
std::size_t fec_length,
std::size_t data_length = code_length - fec_length,
std::size_t natural_length = 255, // Needs to be in-sync with field size
std::size_t padding_length = natural_length - data_length - fec_length>
class shortened_decoder
{
public:
typedef traits::reed_solomon_triat<code_length,fec_length,data_length> trait;
typedef block<code_length,fec_length> block_type;
shortened_decoder(const galois::field& field, const unsigned int gen_initial_index = 0)
: decoder_(field, gen_initial_index)
{}
inline bool decode(block_type& rsblock, const erasure_locations_t& erasure_list) const
{
typename natural_decoder_type::block_type block;
std::fill_n(&block[0], padding_length, typename block_type::symbol_type(0));
for (std::size_t i = 0; i < code_length; ++i)
{
block.data[padding_length + i] = rsblock.data[i];
}
erasure_locations_t shifted_position_erasure_list(erasure_list.size(),0);
for (std::size_t i = 0; i < erasure_list.size(); ++i)
{
shifted_position_erasure_list[i] = erasure_list[i] + padding_length;
}
if (decoder_.decode(block, shifted_position_erasure_list))
{
for (std::size_t i = 0; i < code_length; ++i)
{
rsblock.data[i] = block.data[padding_length + i];
}
rsblock.copy_state(block);
return true;
}
else
{
rsblock.copy_state(block);
return false;
}
}
inline bool decode(block_type& rsblock) const
{
typename natural_decoder_type::block_type block;
std::fill_n(&block[0], padding_length, typename block_type::symbol_type(0));
for (std::size_t i = 0; i < code_length; ++i)
{
block.data[padding_length + i] = rsblock.data[i];
}
if (decoder_.decode(block))
{
for (std::size_t i = 0; i < code_length; ++i)
{
rsblock.data[i] = block.data[padding_length + i];
}
rsblock.copy_state(block);
return true;
}
else
{
rsblock.copy_state(block);
return false;
}
}
private:
typedef decoder<natural_length,fec_length> natural_decoder_type;
const natural_decoder_type decoder_;
};
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,204 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_ENCODER_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_ENCODER_HPP
#include <string>
#include "schifra_galois_field.hpp"
#include "schifra_galois_field_element.hpp"
#include "schifra_galois_field_polynomial.hpp"
#include "schifra_reed_solomon_block.hpp"
#include "schifra_ecc_traits.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length = code_length - fec_length>
class encoder
{
public:
typedef traits::reed_solomon_triat<code_length, fec_length,data_length> trait;
typedef block<code_length, fec_length> block_type;
encoder(const galois::field& gfield, const galois::field_polynomial& generator)
: encoder_valid_(code_length == gfield.size()),
field_(gfield),
generator_(generator)
{}
~encoder()
{}
inline bool encode(block_type& rsblock) const
{
if (!encoder_valid_)
{
rsblock.error = block_type::e_encoder_error0;
return false;
}
const galois::field_polynomial parities = msg_poly(rsblock) % generator_;
const galois::field_symbol mask = field_.mask();
if (parities.deg() == (fec_length - 1))
{
for (std::size_t i = 0; i < fec_length; ++i)
{
rsblock.fec(i) = parities[fec_length - 1 - i].poly() & mask;
}
}
else
{
/*
Note: Encoder should never branch here.
Possible issues to look for:
1. Generator polynomial degree is not equivelent to fec length
2. Field and code length are not consistent.
*/
rsblock.error = block_type::e_encoder_error1;
return false;
}
return true;
}
inline bool encode(const std::string& data, block_type& rsblock) const
{
std::string::const_iterator itr = data.begin();
const galois::field_symbol mask = field_.mask();
for (std::size_t i = 0; i < data_length; ++i, ++itr)
{
rsblock.data[i] = static_cast<typename block_type::symbol_type>(*itr) & mask;
}
return encode(rsblock);
}
private:
encoder();
encoder(const encoder& enc);
encoder& operator=(const encoder& enc);
inline galois::field_polynomial msg_poly(const block_type& rsblock) const
{
galois::field_polynomial message(field_, code_length);
for (std::size_t i = fec_length; i < code_length; ++i)
{
message[i] = rsblock.data[code_length - 1 - i];
}
return message;
}
const bool encoder_valid_;
const galois::field& field_;
const galois::field_polynomial generator_;
};
template <std::size_t code_length,
std::size_t fec_length ,
std::size_t data_length = code_length - fec_length,
std::size_t natural_length = 255, // Needs to be in-sync with field size
std::size_t padding_length = natural_length - data_length - fec_length>
class shortened_encoder
{
public:
typedef traits::reed_solomon_triat<code_length,fec_length,data_length> trait;
typedef block<code_length,fec_length> block_type;
typedef block<natural_length,fec_length> short_block_t;
shortened_encoder(const galois::field& gfield,
const galois::field_polynomial& generator)
: encoder_(gfield, generator)
{}
inline bool encode(block_type& rsblock) const
{
short_block_t block;
std::fill_n(&block[0], padding_length, typename block_type::symbol_type(0));
for (std::size_t i = 0; i < data_length; ++i)
{
block.data[padding_length + i] = rsblock.data[i];
}
if (encoder_.encode(block))
{
for (std::size_t i = 0; i < fec_length; ++i)
{
rsblock.fec(i) = block.fec(i);
}
return true;
}
else
return false;
}
inline bool encode(const std::string& data, block_type& rsblock) const
{
short_block_t block;
std::fill_n(&block[0], padding_length, typename block_type::symbol_type(0));
for (std::size_t i = 0; i < data_length; ++i)
{
block.data[padding_length + i] = data[i];
}
if (encoder_.encode(block))
{
for (std::size_t i = 0; i < code_length; ++i)
{
rsblock.data[i] = block.data[padding_length + i];
}
return true;
}
else
return false;
}
private:
const encoder<natural_length,fec_length> encoder_;
};
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,171 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_FILE_DECODER_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_FILE_DECODER_HPP
#include <iostream>
#include <fstream>
#include "schifra_reed_solomon_block.hpp"
#include "schifra_reed_solomon_decoder.hpp"
#include "schifra_fileio.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length = code_length - fec_length>
class file_decoder
{
public:
typedef decoder<code_length,fec_length> decoder_type;
typedef typename decoder_type::block_type block_type;
file_decoder(const decoder_type& decoder,
const std::string& input_file_name,
const std::string& output_file_name)
: current_block_index_(0)
{
std::size_t remaining_bytes = schifra::fileio::file_size(input_file_name);
if (remaining_bytes == 0)
{
std::cout << "reed_solomon::file_decoder() - Error: input file has ZERO size." << std::endl;
return;
}
std::ifstream in_stream(input_file_name.c_str(),std::ios::binary);
if (!in_stream)
{
std::cout << "reed_solomon::file_decoder() - Error: input file could not be opened." << std::endl;
return;
}
std::ofstream out_stream(output_file_name.c_str(),std::ios::binary);
if (!out_stream)
{
std::cout << "reed_solomon::file_decoder() - Error: output file could not be created." << std::endl;
return;
}
current_block_index_ = 0;
while (remaining_bytes >= code_length)
{
process_complete_block(decoder,in_stream,out_stream);
remaining_bytes -= code_length;
current_block_index_++;
}
if (remaining_bytes > 0)
{
process_partial_block(decoder,in_stream,out_stream,remaining_bytes);
}
in_stream.close();
out_stream.close();
}
private:
inline void process_complete_block(const decoder_type& decoder,
std::ifstream& in_stream,
std::ofstream& out_stream)
{
in_stream.read(&buffer_[0],static_cast<std::streamsize>(code_length));
copy<char,code_length,fec_length>(buffer_,code_length,block_);
if (!decoder.decode(block_))
{
std::cout << "reed_solomon::file_decoder.process_complete_block() - Error during decoding of block " << current_block_index_ << "!" << std::endl;
return;
}
for (std::size_t i = 0; i < data_length; ++i)
{
buffer_[i] = static_cast<char>(block_[i]);
}
out_stream.write(&buffer_[0],static_cast<std::streamsize>(data_length));
}
inline void process_partial_block(const decoder_type& decoder,
std::ifstream& in_stream,
std::ofstream& out_stream,
const std::size_t& read_amount)
{
if (read_amount <= fec_length)
{
std::cout << "reed_solomon::file_decoder.process_partial_block() - Error during decoding of block " << current_block_index_ << "!" << std::endl;
return;
}
in_stream.read(&buffer_[0],static_cast<std::streamsize>(read_amount));
for (std::size_t i = 0; i < (read_amount - fec_length); ++i)
{
block_.data[i] = static_cast<typename block_type::symbol_type>(buffer_[i]);
}
if ((read_amount - fec_length) < data_length)
{
for (std::size_t i = (read_amount - fec_length); i < data_length; ++i)
{
block_.data[i] = 0;
}
}
for (std::size_t i = 0; i < fec_length; ++i)
{
block_.fec(i) = static_cast<typename block_type::symbol_type>(buffer_[(read_amount - fec_length) + i]);
}
if (!decoder.decode(block_))
{
std::cout << "reed_solomon::file_decoder.process_partial_block() - Error during decoding of block " << current_block_index_ << "!" << std::endl;
return;
}
for (std::size_t i = 0; i < (read_amount - fec_length); ++i)
{
buffer_[i] = static_cast<char>(block_.data[i]);
}
out_stream.write(&buffer_[0],static_cast<std::streamsize>(read_amount - fec_length));
}
block_type block_;
std::size_t current_block_index_;
char buffer_[code_length];
};
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,138 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_FILE_ENCODER_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_FILE_ENCODER_HPP
#include <cstring>
#include <iostream>
#include <fstream>
#include "schifra_reed_solomon_block.hpp"
#include "schifra_reed_solomon_encoder.hpp"
#include "schifra_fileio.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length = code_length - fec_length>
class file_encoder
{
public:
typedef encoder<code_length,fec_length> encoder_type;
typedef typename encoder_type::block_type block_type;
file_encoder(const encoder_type& encoder,
const std::string& input_file_name,
const std::string& output_file_name)
{
std::size_t remaining_bytes = schifra::fileio::file_size(input_file_name);
if (remaining_bytes == 0)
{
std::cout << "reed_solomon::file_encoder() - Error: input file has ZERO size." << std::endl;
return;
}
std::ifstream in_stream(input_file_name.c_str(),std::ios::binary);
if (!in_stream)
{
std::cout << "reed_solomon::file_encoder() - Error: input file could not be opened." << std::endl;
return;
}
std::ofstream out_stream(output_file_name.c_str(),std::ios::binary);
if (!out_stream)
{
std::cout << "reed_solomon::file_encoder() - Error: output file could not be created." << std::endl;
return;
}
std::memset(data_buffer_,0,sizeof(data_buffer_));
std::memset(fec_buffer_ ,0,sizeof(fec_buffer_ ));
while (remaining_bytes >= data_length)
{
process_block(encoder,in_stream,out_stream,data_length);
remaining_bytes -= data_length;
}
if (remaining_bytes > 0)
{
process_block(encoder,in_stream,out_stream,remaining_bytes);
}
in_stream.close();
out_stream.close();
}
private:
inline void process_block(const encoder_type& encoder,
std::ifstream& in_stream,
std::ofstream& out_stream,
const std::size_t& read_amount)
{
in_stream.read(&data_buffer_[0],static_cast<std::streamsize>(read_amount));
for (std::size_t i = 0; i < read_amount; ++i)
{
block_.data[i] = (data_buffer_[i] & 0xFF);
}
if (read_amount < data_length)
{
for (std::size_t i = read_amount; i < data_length; ++i)
{
block_.data[i] = 0x00;
}
}
if (!encoder.encode(block_))
{
std::cout << "reed_solomon::file_encoder.process_block() - Error during encoding of block!" << std::endl;
return;
}
for (std::size_t i = 0; i < fec_length; ++i)
{
fec_buffer_[i] = static_cast<char>(block_.fec(i) & 0xFF);
}
out_stream.write(&data_buffer_[0],static_cast<std::streamsize>(read_amount));
out_stream.write(&fec_buffer_[0],fec_length);
}
block_type block_;
char data_buffer_[data_length];
char fec_buffer_[fec_length];
};
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,247 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_FILE_INTERLEAVER_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_FILE_INTERLEAVER_HPP
#include <iostream>
#include <string>
#include "schifra_reed_solomon_interleaving.hpp"
#include "schifra_fileio.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t block_length, std::size_t stack_size>
class file_interleaver
{
public:
file_interleaver(const std::string& input_file_name,
const std::string& output_file_name)
{
std::size_t remaining_bytes = schifra::fileio::file_size(input_file_name);
if (0 == remaining_bytes)
{
std::cout << "reed_solomon::file_interleaver() - Error: input file has ZERO size." << std::endl;
return;
}
std::ifstream in_stream(input_file_name.c_str(),std::ios::binary);
if (!in_stream)
{
std::cout << "reed_solomon::file_interleaver() - Error: input file could not be opened." << std::endl;
return;
}
std::ofstream out_stream(output_file_name.c_str(),std::ios::binary);
if (!out_stream)
{
std::cout << "reed_solomon::file_interleaver() - Error: output file could not be created." << std::endl;
return;
}
while (remaining_bytes >= (block_length * stack_size))
{
process_block(in_stream,out_stream);
remaining_bytes -= (block_length * stack_size);
}
if (remaining_bytes > 0)
{
process_incomplete_block(in_stream,out_stream,remaining_bytes);
}
in_stream.close();
out_stream.close();
}
private:
inline void process_block(std::ifstream& in_stream,
std::ofstream& out_stream)
{
for (std::size_t i = 0; i < stack_size; ++i)
{
in_stream.read(&block_stack_[i][0],static_cast<std::streamsize>(block_length));
}
interleave<char,block_length,stack_size>(block_stack_);
for (std::size_t i = 0; i < stack_size; ++i)
{
out_stream.write(&block_stack_[i][0],static_cast<std::streamsize>(block_length));
}
}
inline void process_incomplete_block(std::ifstream& in_stream,
std::ofstream& out_stream,
const std::size_t amount)
{
std::size_t complete_row_count = amount / block_length;
std::size_t remainder = amount % block_length;
for (std::size_t i = 0; i < complete_row_count; ++i)
{
in_stream.read(&block_stack_[i][0],static_cast<std::streamsize>(block_length));
}
if (remainder != 0)
{
in_stream.read(&block_stack_[complete_row_count][0],static_cast<std::streamsize>(remainder));
}
if (remainder == 0)
interleave<char,block_length,stack_size>(block_stack_,complete_row_count);
else
interleave<char,block_length>(block_stack_,complete_row_count + 1,remainder);
for (std::size_t i = 0; i < complete_row_count; ++i)
{
out_stream.write(&block_stack_[i][0],static_cast<std::streamsize>(block_length));
}
if (remainder != 0)
{
out_stream.write(&block_stack_[complete_row_count][0],static_cast<std::streamsize>(remainder));
}
}
data_block<char,block_length> block_stack_[stack_size];
};
template <std::size_t block_length, std::size_t stack_size>
class file_deinterleaver
{
public:
file_deinterleaver(const std::string& input_file_name,
const std::string& output_file_name)
{
std::size_t input_file_size = schifra::fileio::file_size(input_file_name);
if (input_file_size == 0)
{
std::cout << "reed_solomon::file_deinterleaver() - Error: input file has ZERO size." << std::endl;
return;
}
std::ifstream in_stream(input_file_name.c_str(),std::ios::binary);
if (!in_stream)
{
std::cout << "reed_solomon::file_deinterleaver() - Error: input file could not be opened." << std::endl;
return;
}
std::ofstream out_stream(output_file_name.c_str(),std::ios::binary);
if (!out_stream)
{
std::cout << "reed_solomon::file_deinterleaver() - Error: output file could not be created." << std::endl;
return;
}
for (std::size_t i = 0; i < (input_file_size / (block_length * stack_size)); ++i)
{
process_block(in_stream,out_stream);
}
if ((input_file_size % (block_length * stack_size)) != 0)
{
process_incomplete_block(in_stream,out_stream,(input_file_size % (block_length * stack_size)));
}
in_stream.close();
out_stream.close();
}
private:
inline void process_block(std::ifstream& in_stream,
std::ofstream& out_stream)
{
for (std::size_t i = 0; i < stack_size; ++i)
{
in_stream.read(&block_stack_[i][0],static_cast<std::streamsize>(block_length));
}
deinterleave<char,block_length,stack_size>(block_stack_);
for (std::size_t i = 0; i < stack_size; ++i)
{
out_stream.write(&block_stack_[i][0],static_cast<std::streamsize>(block_length));
}
}
inline void process_incomplete_block(std::ifstream& in_stream,
std::ofstream& out_stream,
const std::size_t amount)
{
std::size_t complete_row_count = amount / block_length;
std::size_t remainder = amount % block_length;
for (std::size_t i = 0; i < complete_row_count; ++i)
{
in_stream.read(&block_stack_[i][0],static_cast<std::streamsize>(block_length));
}
if (remainder != 0)
{
in_stream.read(&block_stack_[complete_row_count][0],static_cast<std::streamsize>(remainder));
}
if (remainder == 0)
deinterleave<char,block_length>(block_stack_,complete_row_count);
else
deinterleave<char,block_length>(block_stack_,complete_row_count + 1,remainder);
for (std::size_t i = 0; i < complete_row_count; ++i)
{
out_stream.write(&block_stack_[i][0],static_cast<std::streamsize>(block_length));
}
if (remainder != 0)
{
out_stream.write(&block_stack_[complete_row_count][0],static_cast<std::streamsize>(remainder));
}
}
data_block<char,block_length> block_stack_[stack_size];
};
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,210 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_GENERAL_CODEC_HPP
#define INCLUDE_SCHIFRA_REED_GENERAL_CODEC_HPP
#include "schifra_galois_field.hpp"
#include "schifra_galois_field_polynomial.hpp"
#include "schifra_sequential_root_generator_polynomial_creator.hpp"
#include "schifra_reed_solomon_block.hpp"
#include "schifra_reed_solomon_encoder.hpp"
#include "schifra_reed_solomon_decoder.hpp"
#include "schifra_ecc_traits.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length, std::size_t fec_length>
void* create_encoder(const galois::field& field,
const std::size_t& gen_poly_index)
{
const std::size_t data_length = code_length - fec_length;
traits::validate_reed_solomon_code_parameters<code_length,fec_length,data_length>();
galois::field_polynomial gen_polynomial(field);
if (
!make_sequential_root_generator_polynomial(field,
gen_poly_index,
fec_length,
gen_polynomial)
)
{
return reinterpret_cast<void*>(0);
}
return new encoder<code_length,fec_length>(field,gen_polynomial);
}
template <std::size_t code_length, std::size_t fec_length>
void* create_decoder(const galois::field& field,
const std::size_t& gen_poly_index)
{
const std::size_t data_length = code_length - fec_length;
traits::validate_reed_solomon_code_parameters<code_length,fec_length,data_length>();
return new decoder<code_length,fec_length>(field,static_cast<unsigned int>(gen_poly_index));
}
template <std::size_t code_length, std::size_t max_fec_length = 128>
class general_codec
{
public:
general_codec(const galois::field& field,
const std::size_t& gen_poly_index)
{
for (std::size_t i = 0; i < max_fec_length; ++i)
{
encoder_[i] = 0;
decoder_[i] = 0;
}
encoder_[ 2] = create_encoder<code_length, 2>(field, gen_poly_index);
encoder_[ 4] = create_encoder<code_length, 4>(field, gen_poly_index);
encoder_[ 6] = create_encoder<code_length, 6>(field, gen_poly_index);
encoder_[ 8] = create_encoder<code_length, 8>(field, gen_poly_index);
encoder_[ 10] = create_encoder<code_length, 10>(field, gen_poly_index);
encoder_[ 12] = create_encoder<code_length, 12>(field, gen_poly_index);
encoder_[ 14] = create_encoder<code_length, 14>(field, gen_poly_index);
encoder_[ 16] = create_encoder<code_length, 16>(field, gen_poly_index);
encoder_[ 18] = create_encoder<code_length, 18>(field, gen_poly_index);
encoder_[ 20] = create_encoder<code_length, 20>(field, gen_poly_index);
encoder_[ 22] = create_encoder<code_length, 22>(field, gen_poly_index);
encoder_[ 24] = create_encoder<code_length, 24>(field, gen_poly_index);
encoder_[ 26] = create_encoder<code_length, 26>(field, gen_poly_index);
encoder_[ 28] = create_encoder<code_length, 28>(field, gen_poly_index);
encoder_[ 30] = create_encoder<code_length, 30>(field, gen_poly_index);
encoder_[ 32] = create_encoder<code_length, 32>(field, gen_poly_index);
encoder_[ 64] = create_encoder<code_length, 64>(field, gen_poly_index);
encoder_[ 80] = create_encoder<code_length, 80>(field, gen_poly_index);
encoder_[ 96] = create_encoder<code_length, 96>(field, gen_poly_index);
encoder_[128] = create_encoder<code_length,128>(field, gen_poly_index);
decoder_[ 2] = create_decoder<code_length, 2>(field, gen_poly_index);
decoder_[ 4] = create_decoder<code_length, 4>(field, gen_poly_index);
decoder_[ 6] = create_decoder<code_length, 6>(field, gen_poly_index);
decoder_[ 8] = create_decoder<code_length, 8>(field, gen_poly_index);
decoder_[ 10] = create_decoder<code_length, 10>(field, gen_poly_index);
decoder_[ 12] = create_decoder<code_length, 12>(field, gen_poly_index);
decoder_[ 14] = create_decoder<code_length, 14>(field, gen_poly_index);
decoder_[ 16] = create_decoder<code_length, 16>(field, gen_poly_index);
decoder_[ 18] = create_decoder<code_length, 18>(field, gen_poly_index);
decoder_[ 20] = create_decoder<code_length, 20>(field, gen_poly_index);
decoder_[ 22] = create_decoder<code_length, 22>(field, gen_poly_index);
decoder_[ 24] = create_decoder<code_length, 24>(field, gen_poly_index);
decoder_[ 26] = create_decoder<code_length, 26>(field, gen_poly_index);
decoder_[ 28] = create_decoder<code_length, 28>(field, gen_poly_index);
decoder_[ 30] = create_decoder<code_length, 30>(field, gen_poly_index);
decoder_[ 32] = create_decoder<code_length, 32>(field, gen_poly_index);
decoder_[ 64] = create_decoder<code_length, 64>(field, gen_poly_index);
decoder_[ 80] = create_decoder<code_length, 80>(field, gen_poly_index);
decoder_[ 96] = create_decoder<code_length, 96>(field, gen_poly_index);
decoder_[128] = create_decoder<code_length,128>(field, gen_poly_index);
}
~general_codec()
{
delete static_cast<reed_solomon::encoder<code_length, 2>*>(encoder_[ 2]);
delete static_cast<reed_solomon::encoder<code_length, 4>*>(encoder_[ 4]);
delete static_cast<reed_solomon::encoder<code_length, 6>*>(encoder_[ 6]);
delete static_cast<reed_solomon::encoder<code_length, 8>*>(encoder_[ 8]);
delete static_cast<reed_solomon::encoder<code_length, 10>*>(encoder_[ 10]);
delete static_cast<reed_solomon::encoder<code_length, 12>*>(encoder_[ 12]);
delete static_cast<reed_solomon::encoder<code_length, 14>*>(encoder_[ 14]);
delete static_cast<reed_solomon::encoder<code_length, 16>*>(encoder_[ 16]);
delete static_cast<reed_solomon::encoder<code_length, 18>*>(encoder_[ 18]);
delete static_cast<reed_solomon::encoder<code_length, 20>*>(encoder_[ 20]);
delete static_cast<reed_solomon::encoder<code_length, 22>*>(encoder_[ 22]);
delete static_cast<reed_solomon::encoder<code_length, 24>*>(encoder_[ 24]);
delete static_cast<reed_solomon::encoder<code_length, 26>*>(encoder_[ 26]);
delete static_cast<reed_solomon::encoder<code_length, 28>*>(encoder_[ 28]);
delete static_cast<reed_solomon::encoder<code_length, 30>*>(encoder_[ 30]);
delete static_cast<reed_solomon::encoder<code_length, 32>*>(encoder_[ 32]);
delete static_cast<reed_solomon::encoder<code_length, 64>*>(encoder_[ 64]);
delete static_cast<reed_solomon::encoder<code_length, 80>*>(encoder_[ 80]);
delete static_cast<reed_solomon::encoder<code_length, 96>*>(encoder_[ 96]);
delete static_cast<reed_solomon::encoder<code_length,128>*>(encoder_[128]);
delete static_cast<reed_solomon::decoder<code_length, 2>*>(decoder_[ 2]);
delete static_cast<reed_solomon::decoder<code_length, 4>*>(decoder_[ 4]);
delete static_cast<reed_solomon::decoder<code_length, 6>*>(decoder_[ 6]);
delete static_cast<reed_solomon::decoder<code_length, 8>*>(decoder_[ 8]);
delete static_cast<reed_solomon::decoder<code_length, 10>*>(decoder_[ 10]);
delete static_cast<reed_solomon::decoder<code_length, 12>*>(decoder_[ 12]);
delete static_cast<reed_solomon::decoder<code_length, 14>*>(decoder_[ 14]);
delete static_cast<reed_solomon::decoder<code_length, 16>*>(decoder_[ 16]);
delete static_cast<reed_solomon::decoder<code_length, 18>*>(decoder_[ 18]);
delete static_cast<reed_solomon::decoder<code_length, 20>*>(decoder_[ 20]);
delete static_cast<reed_solomon::decoder<code_length, 22>*>(decoder_[ 22]);
delete static_cast<reed_solomon::decoder<code_length, 24>*>(decoder_[ 24]);
delete static_cast<reed_solomon::decoder<code_length, 26>*>(decoder_[ 26]);
delete static_cast<reed_solomon::decoder<code_length, 28>*>(decoder_[ 28]);
delete static_cast<reed_solomon::decoder<code_length, 30>*>(decoder_[ 30]);
delete static_cast<reed_solomon::decoder<code_length, 32>*>(decoder_[ 32]);
delete static_cast<reed_solomon::decoder<code_length, 64>*>(decoder_[ 64]);
delete static_cast<reed_solomon::decoder<code_length, 80>*>(decoder_[ 80]);
delete static_cast<reed_solomon::decoder<code_length, 96>*>(decoder_[ 96]);
delete static_cast<reed_solomon::decoder<code_length,128>*>(decoder_[128]);
}
template <typename Block>
bool encode(Block& block) const
{
/*
cl : code length
fl : fec length
*/
typedef reed_solomon::encoder<Block::trait::code_length,Block::trait::fec_length> encoder_type;
traits::__static_assert__<(Block::trait::fec_length <= max_fec_length)>();
if (encoder_[Block::trait::fec_length] == 0)
return false;
else
return static_cast<encoder_type*>(encoder_[Block::trait::fec_length])->encode(block);
}
template <typename Block>
bool decode(Block& block) const
{
typedef reed_solomon::decoder<Block::trait::code_length,Block::trait::fec_length> decoder_type;
traits::__static_assert__<(Block::trait::fec_length <= max_fec_length)>();
if (decoder_[Block::trait::fec_length] == 0)
return false;
else
return static_cast<decoder_type*>(decoder_[Block::trait::fec_length])->decode(block);
}
private:
void* encoder_[max_fec_length + 1];
void* decoder_[max_fec_length + 1];
};
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,639 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_INTERLEAVING_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_INTERLEAVING_HPP
#include <cstddef>
#include <iostream>
#include <string>
#include "schifra_reed_solomon_block.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length, std::size_t fec_length>
inline void interleave(block<code_length,fec_length> (&block_stack)[code_length])
{
for (std::size_t i = 0; i < code_length; ++i)
{
for (std::size_t j = i + 1; j < code_length; ++j)
{
typename block<code_length,fec_length>::symbol_type tmp = block_stack[i][j];
block_stack[i][j] = block_stack[j][i];
block_stack[j][i] = tmp;
}
}
}
template <std::size_t code_length, std::size_t fec_length, std::size_t row_count>
inline void interleave(block<code_length,fec_length> (&block_stack)[row_count])
{
block<code_length,fec_length> auxiliary_stack[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = 0;
for (std::size_t index = 0; index < code_length; ++index)
{
for (std::size_t row = 0; row < row_count; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == code_length)
{
aux_index = 0;
aux_row++;
}
}
}
copy<code_length,fec_length,row_count>(auxiliary_stack,block_stack);
}
template <std::size_t code_length, std::size_t fec_length, std::size_t row_count>
inline void interleave(block<code_length,fec_length,row_count> (&block_stack)[row_count],
const std::size_t partial_code_length)
{
if (partial_code_length == code_length)
{
interleave<code_length,fec_length,row_count>(block_stack);
}
else
{
block<code_length,fec_length,row_count> auxiliary_stack[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = 0;
for (std::size_t index = 0; index < partial_code_length; ++index)
{
for (std::size_t row = 0; row < row_count; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == code_length)
{
aux_index = 0;
aux_row++;
}
}
}
for (std::size_t index = partial_code_length; index < code_length; ++index)
{
for (std::size_t row = 0; row < row_count - 1; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == code_length)
{
aux_index = 0;
aux_row++;
}
}
}
for (std::size_t row = 0; row < row_count - 1; ++row)
{
for (std::size_t index = 0; index < code_length - fec_length; ++index)
{
block_stack[row].data[index] = auxiliary_stack[row].data[index];
}
for (std::size_t index = 0; index < fec_length; ++index)
{
block_stack[row].fec[index] = auxiliary_stack[row].fec[index];
}
}
for (std::size_t index = 0; index < partial_code_length; ++index)
{
block_stack[row_count - 1][index] = auxiliary_stack[row_count - 1][index];
}
}
}
template <typename T, std::size_t block_length>
inline void interleave(data_block<T,block_length> (&block_stack)[block_length])
{
for (std::size_t i = 0; i < block_length; ++i)
{
for (std::size_t j = i + 1; j < block_length; ++j)
{
T tmp = block_stack[i][j];
block_stack[i][j] = block_stack[j][i];
block_stack[j][i] = tmp;
}
}
}
template <typename T, std::size_t block_length, std::size_t row_count>
inline void interleave(data_block<T,block_length> (&block_stack)[row_count])
{
data_block<T,block_length> auxiliary_stack[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = 0;
for (std::size_t index = 0; index < block_length; ++index)
{
for (std::size_t row = 0; row < row_count; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == block_length)
{
aux_index = 0;
aux_row++;
}
}
}
copy<T,block_length,row_count>(auxiliary_stack,block_stack);
}
template <typename T, std::size_t block_length, std::size_t row_count>
inline void interleave(data_block<T,block_length> (&block_stack)[row_count],
const std::size_t partial_block_length)
{
if (partial_block_length == block_length)
{
interleave<T,block_length,row_count>(block_stack);
}
else
{
data_block<T,block_length> auxiliary_stack[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = 0;
for (std::size_t index = 0; index < partial_block_length; ++index)
{
for (std::size_t row = 0; row < row_count; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == block_length)
{
aux_index = 0;
aux_row++;
}
}
}
for (std::size_t index = partial_block_length; index < block_length; ++index)
{
for (std::size_t row = 0; row < row_count - 1; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == block_length)
{
aux_index = 0;
aux_row++;
}
}
}
for (std::size_t row = 0; row < row_count - 1; ++row)
{
for (std::size_t index = 0; index < block_length; ++index)
{
block_stack[row][index] = auxiliary_stack[row][index];
}
}
for (std::size_t index = 0; index < partial_block_length; ++index)
{
block_stack[row_count - 1][index] = auxiliary_stack[row_count - 1][index];
}
}
}
template <typename T, std::size_t block_length>
inline void interleave(data_block<T,block_length> block_stack[],
const std::size_t row_count)
{
data_block<T,block_length>* auxiliary_stack = new data_block<T,block_length>[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = 0;
for (std::size_t index = 0; index < block_length; ++index)
{
for (std::size_t row = 0; row < row_count; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == block_length)
{
aux_index = 0;
aux_row++;
}
}
}
for (std::size_t row = 0; row < row_count; ++row)
{
for (std::size_t index = 0; index < block_length; ++index)
{
block_stack[row][index] = auxiliary_stack[row][index];
}
}
delete[] auxiliary_stack;
}
template <typename T, std::size_t block_length>
inline void interleave(data_block<T,block_length> block_stack[],
const std::size_t row_count,
const std::size_t partial_block_length)
{
data_block<T,block_length>* auxiliary_stack = new data_block<T,block_length>[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = 0;
for (std::size_t index = 0; index < partial_block_length; ++index)
{
for (std::size_t row = 0; row < row_count; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == block_length)
{
aux_index = 0;
aux_row++;
}
}
}
for (std::size_t index = partial_block_length; index < block_length; ++index)
{
for (std::size_t row = 0; row < row_count - 1; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == block_length)
{
aux_index = 0;
aux_row++;
}
}
}
for (std::size_t row = 0; row < row_count - 1; ++row)
{
for (std::size_t index = 0; index < block_length; ++index)
{
block_stack[row][index] = auxiliary_stack[row][index];
}
}
for (std::size_t index = 0; index < partial_block_length; ++index)
{
block_stack[row_count - 1][index] = auxiliary_stack[row_count - 1][index];
}
delete[] auxiliary_stack;
}
template <std::size_t code_length, std::size_t fec_length, std::size_t row_count>
inline void deinterleave(block<code_length,fec_length> (&block_stack)[row_count])
{
block<code_length,fec_length> auxiliary_stack[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = 0;
for (std::size_t row = 0; row < row_count; ++row)
{
for (std::size_t index = 0; index < code_length; ++index)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_row == row_count)
{
aux_row = 0;
aux_index++;
}
}
}
copy<code_length,fec_length,row_count>(auxiliary_stack,block_stack);
}
template <std::size_t code_length, std::size_t fec_length, std::size_t row_count>
inline void deinterleave(block<code_length,fec_length> (&block_stack)[row_count],
const std::size_t partial_code_length)
{
if (partial_code_length == code_length)
{
deinterleave<code_length,fec_length,row_count>(block_stack);
}
else
{
block<code_length,fec_length> auxiliary_stack[row_count];
std::size_t aux_row1 = 0;
std::size_t aux_index1 = 0;
std::size_t aux_row2 = 0;
std::size_t aux_index2 = 0;
for (std::size_t i = 0; i < partial_code_length * row_count; ++i)
{
auxiliary_stack[aux_row1][aux_index1] = block_stack[aux_row2][aux_index2];
if (++aux_row1 == row_count)
{
aux_row1 = 0;
aux_index1++;
}
if (++aux_index2 == code_length)
{
aux_index2 = 0;
aux_row2++;
}
}
for (std::size_t i = 0; aux_index1 < code_length; ++i)
{
auxiliary_stack[aux_row1][aux_index1] = block_stack[aux_row2][aux_index2];
if (++aux_row1 == (row_count - 1))
{
aux_row1 = 0;
aux_index1++;
}
if (++aux_index2 == code_length)
{
aux_index2 = 0;
aux_row2++;
}
}
for (std::size_t row = 0; row < row_count - 1; ++row)
{
for (std::size_t index = 0; index < code_length; ++index)
{
block_stack[row][index] = auxiliary_stack[row][index];
}
}
for (std::size_t index = 0; index < partial_code_length; ++index)
{
block_stack[row_count - 1][index] = auxiliary_stack[row_count - 1][index];
}
}
}
template <typename T, std::size_t block_length>
inline void deinterleave(data_block<T,block_length> (&block_stack)[block_length])
{
data_block<T,block_length> auxiliary_stack[block_length];
for (std::size_t row = 0; row < block_length; ++row)
{
for (std::size_t index = 0; index < block_length; ++index)
{
auxiliary_stack[index][row] = block_stack[row][index];
}
}
copy<T,block_length,block_length>(auxiliary_stack,block_stack);
}
template <typename T, std::size_t block_length, std::size_t row_count>
inline void deinterleave(data_block<T,block_length> (&block_stack)[row_count])
{
data_block<T,block_length> auxiliary_stack[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = 0;
for (std::size_t row = 0; row < row_count; ++row)
{
for (std::size_t index = 0; index < block_length; ++index)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_row == row_count)
{
aux_row = 0;
aux_index++;
}
}
}
copy<T,block_length,row_count>(auxiliary_stack,block_stack);
}
template <typename T, std::size_t block_length>
inline void deinterleave(data_block<T,block_length> block_stack[],
const std::size_t row_count)
{
data_block<T,block_length>* auxiliary_stack = new data_block<T,block_length>[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = 0;
for (std::size_t row = 0; row < row_count; ++row)
{
for (std::size_t index = 0; index < block_length; ++index)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_row == row_count)
{
aux_row = 0;
aux_index++;
}
}
}
for (std::size_t row = 0; row < row_count; ++row)
{
for (std::size_t index = 0; index < block_length; ++index)
{
block_stack[row][index] = auxiliary_stack[row][index];
}
}
delete[] auxiliary_stack;
}
template <typename T, std::size_t block_length>
inline void deinterleave(data_block<T,block_length> block_stack[],
const std::size_t row_count,
const std::size_t partial_block_length)
{
if (row_count == 1) return;
data_block<T,block_length>* auxiliary_stack = new data_block<T,block_length>[row_count];
std::size_t aux_row1 = 0;
std::size_t aux_index1 = 0;
std::size_t aux_row2 = 0;
std::size_t aux_index2 = 0;
for (std::size_t i = 0; i < partial_block_length * row_count; ++i)
{
auxiliary_stack[aux_row1][aux_index1] = block_stack[aux_row2][aux_index2];
if (++aux_row1 == row_count)
{
aux_row1 = 0;
aux_index1++;
}
if (++aux_index2 == block_length)
{
aux_index2 = 0;
aux_row2++;
}
}
for (std::size_t i = 0; aux_index1 < block_length; ++i)
{
auxiliary_stack[aux_row1][aux_index1] = block_stack[aux_row2][aux_index2];
if (++aux_row1 == (row_count - 1))
{
aux_row1 = 0;
aux_index1++;
}
if (++aux_index2 == block_length)
{
aux_index2 = 0;
aux_row2++;
}
}
for (std::size_t row = 0; row < row_count - 1; ++row)
{
for (std::size_t index = 0; index < block_length; ++index)
{
block_stack[row][index] = auxiliary_stack[row][index];
}
}
for (std::size_t index = 0; index < partial_block_length; ++index)
{
block_stack[row_count - 1][index] = auxiliary_stack[row_count - 1][index];
}
delete[] auxiliary_stack;
}
template <typename T, std::size_t block_length, std::size_t skip_columns>
inline void interleave_columnskip(data_block<T,block_length>* block_stack)
{
for (std::size_t i = 0; i < block_length; ++i)
{
for (std::size_t j = i + 1; j < block_length; ++j)
{
std::size_t x1 = i + skip_columns;
std::size_t x2 = j + skip_columns;
T tmp = block_stack[i][x2];
block_stack[i][x2] = block_stack[j][x1];
block_stack[j][x1] = tmp;
}
}
}
template <typename T, std::size_t block_length, std::size_t skip_columns>
inline void interleave_columnskip(data_block<T,block_length>* block_stack, const std::size_t& row_count)
{
data_block<T,block_length>* auxiliary_stack = new data_block<T,block_length>[row_count];
std::size_t aux_row = 0;
std::size_t aux_index = skip_columns;
for (std::size_t index = skip_columns; index < block_length; ++index)
{
for (std::size_t row = 0; row < row_count; ++row)
{
auxiliary_stack[aux_row][aux_index] = block_stack[row][index];
if (++aux_index == block_length)
{
aux_index = skip_columns;
aux_row++;
}
}
}
for (std::size_t row = 0; row < row_count; ++row)
{
for (std::size_t index = skip_columns; index < block_length; ++index)
{
block_stack[row][index] = auxiliary_stack[row][index];
}
}
delete[] auxiliary_stack;
}
template <typename T, std::size_t data_length>
inline void interleave(T* block_stack[data_length])
{
for (std::size_t i = 0; i < data_length; ++i)
{
for (std::size_t j = i + 1; j < data_length; ++j)
{
T tmp = block_stack[i][j];
block_stack[i][j] = block_stack[j][i];
block_stack[j][i] = tmp;
}
}
}
template <typename T, std::size_t data_length, std::size_t skip_columns>
inline void interleave_columnskip(T* block_stack[data_length])
{
for (std::size_t i = skip_columns; i < data_length; ++i)
{
for (std::size_t j = i + 1; j < data_length; ++j)
{
T tmp = block_stack[i][j];
block_stack[i][j] = block_stack[j][i];
block_stack[j][i] = tmp;
}
}
}
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,238 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_PRODUCT_CODE_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_PRODUCT_CODE_HPP
#include <cstddef>
#include <iostream>
#include <fstream>
#include "schifra_reed_solomon_block.hpp"
#include "schifra_reed_solomon_encoder.hpp"
#include "schifra_reed_solomon_decoder.hpp"
#include "schifra_reed_solomon_interleaving.hpp"
#include "schifra_reed_solomon_bitio.hpp"
#include "schifra_ecc_traits.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length = code_length - fec_length>
class square_product_code_encoder
{
public:
typedef encoder<code_length,fec_length> encoder_type;
typedef block<code_length,fec_length> block_type;
typedef traits::reed_solomon_triat<code_length,fec_length,data_length> trait;
typedef unsigned char data_type;
typedef data_type* data_ptr_type;
enum { data_size = data_length * data_length };
enum { total_size = code_length * code_length };
square_product_code_encoder(const encoder_type& enc)
: encoder_(enc)
{}
bool encode(data_ptr_type data)
{
data_ptr_type curr_data_ptr = data;
for (std::size_t row = 0; row < data_length; ++row, curr_data_ptr += data_length)
{
copy(curr_data_ptr, data_length, block_stack_[row]);
if (!encoder_.encode(block_stack_[row]))
{
return false;
}
}
block_type vertical_block;
for (std::size_t col = 0; col < code_length; ++col)
{
for (std::size_t row = 0; row < data_length; ++row)
{
vertical_block[row] = block_stack_[row][col];
}
if (!encoder_.encode(vertical_block))
{
return false;
}
for (std::size_t fec_index = 0; fec_index < fec_length; ++fec_index)
{
block_stack_[data_length + fec_index].fec(fec_index) = vertical_block.fec(fec_index);
}
}
return true;
}
bool encode_and_interleave(data_ptr_type data)
{
if (!encode(data))
{
return false;
}
interleave<code_length,fec_length>(block_stack_);
return true;
}
void output(data_ptr_type output_data)
{
for (std::size_t row = 0; row < code_length; ++row, output_data += code_length)
{
bitio::convert_symbol_to_data<traits::symbol<code_length>::size>(block_stack_[row].data,output_data,code_length);
}
}
void clear()
{
for (std::size_t i = 0; i < code_length; ++i)
{
block_stack_[i].clear();
}
}
private:
square_product_code_encoder(const square_product_code_encoder& spce);
square_product_code_encoder& operator=(const square_product_code_encoder& spce);
block_type block_stack_[code_length];
const encoder_type& encoder_;
};
template <std::size_t code_length, std::size_t fec_length, std::size_t data_length = code_length - fec_length>
class square_product_code_decoder
{
public:
typedef decoder<code_length,fec_length> decoder_type;
typedef block<code_length,fec_length> block_type;
typedef traits::reed_solomon_triat<code_length,fec_length,data_length> trait;
typedef unsigned char data_type;
typedef data_type* data_ptr_type;
enum { data_size = data_length * data_length };
enum { total_size = code_length * code_length };
square_product_code_decoder(const decoder_type& decoder)
: decoder_(decoder)
{}
void decode(data_ptr_type data)
{
copy_proxy(data);
decode_proxy();
}
void deinterleave_and_decode(data_ptr_type data)
{
copy_proxy(data);
interleave<code_length,fec_length>(block_stack_);
decode_proxy();
}
void output(data_ptr_type output_data)
{
for (std::size_t row = 0; row < data_length; ++row, output_data += data_length)
{
bitio::convert_symbol_to_data<traits::symbol<code_length>::size>(block_stack_[row].data,output_data,data_length);
}
}
void clear()
{
for (std::size_t i = 0; i < code_length; ++i)
{
block_stack_[i].clear();
}
}
private:
square_product_code_decoder(const square_product_code_decoder& spcd);
square_product_code_decoder& operator=(const square_product_code_decoder& spcd);
void copy_proxy(data_ptr_type data)
{
for (std::size_t row = 0; row < code_length; ++row, data += code_length)
{
bitio::convert_data_to_symbol<traits::symbol<code_length>::size>(data,code_length,block_stack_[row].data);
}
}
void decode_proxy()
{
bool first_iteration_failure = false;
for (std::size_t row = 0; row < data_length; ++row)
{
if (!decoder_.decode(block_stack_[row]))
{
first_iteration_failure = true;
}
}
if (!first_iteration_failure)
{
/*
Either no errors detected or all errors have
been detected and corrected.
*/
return;
}
block_type vertical_block;
for (std::size_t col = 0; col < code_length; ++col)
{
for (std::size_t row = 0; row < data_length; ++row)
{
vertical_block[row] = block_stack_[row][col];
}
decoder_.decode(vertical_block);
}
}
block_type block_stack_[code_length];
const decoder_type& decoder_;
};
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,411 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_REED_SOLOMON_SPPED_EVALUATOR_HPP
#define INCLUDE_SCHIFRA_REED_SOLOMON_SPPED_EVALUATOR_HPP
#include <cstddef>
#include <cstdio>
#include <iostream>
#include <string>
#include "schifra_galois_field.hpp"
#include "schifra_sequential_root_generator_polynomial_creator.hpp"
#include "schifra_reed_solomon_block.hpp"
#include "schifra_reed_solomon_encoder.hpp"
#include "schifra_reed_solomon_decoder.hpp"
#include "schifra_reed_solomon_file_encoder.hpp"
#include "schifra_reed_solomon_file_decoder.hpp"
#include "schifra_error_processes.hpp"
#include "schifra_utilities.hpp"
namespace schifra
{
namespace reed_solomon
{
template <std::size_t code_length, std::size_t fec_length>
void create_messages(const encoder<code_length,fec_length>& rs_encoder,
std::vector< block<code_length,fec_length> >& original_block_list,
const bool full_test_set = false)
{
const std::size_t data_length = code_length - fec_length;
std::vector<std::string> message_list;
if (full_test_set)
{
for (unsigned int i = 0; i < 256; ++i)
{
message_list.push_back(std::string(data_length,static_cast<unsigned char>(i)));
}
}
else
{
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x00)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xAA)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xA5)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xAC)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xCA)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x5A)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xCC)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xF0)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x0F)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xFF)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x92)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x6D)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x77)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0x7A)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xA7)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xE5)));
message_list.push_back(std::string(data_length,static_cast<unsigned char>(0xEB)));
}
std::string tmp_str = std::string(data_length,static_cast<unsigned char>(0x00));
for (std::size_t i = 0; i < data_length; ++i)
{
tmp_str[i] = static_cast<unsigned char>(i);
}
message_list.push_back(tmp_str);
for (int i = data_length - 1; i >= 0; --i)
{
tmp_str[i] = static_cast<unsigned char>(i);
}
message_list.push_back(tmp_str);
for (std::size_t i = 0; i < data_length; ++i)
{
tmp_str[i] = (((i & 0x01) == 1) ? static_cast<unsigned char>(i) : 0x00);
}
message_list.push_back(tmp_str);
for (std::size_t i = 0; i < data_length; ++i)
{
tmp_str[i] = (((i & 0x01) == 0) ? static_cast<unsigned char>(i) : 0x00);
}
message_list.push_back(tmp_str);
for (int i = data_length - 1; i >= 0; --i)
{
tmp_str[i] = (((i & 0x01) == 1) ? static_cast<unsigned char>(i) : 0x00);
}
message_list.push_back(tmp_str);
for (int i = data_length - 1; i >= 0; --i)
{
tmp_str[i] = (((i & 0x01) == 0) ? static_cast<unsigned char>(i) : 0x00);
}
message_list.push_back(tmp_str);
tmp_str = std::string(data_length,static_cast<unsigned char>(0x00));
for (std::size_t i = 0; i < (data_length >> 1); ++i)
{
tmp_str[i] = static_cast<unsigned char>(0xFF);
}
message_list.push_back(tmp_str);
tmp_str = std::string(data_length,static_cast<unsigned char>(0xFF)) ;
for (std::size_t i = 0; i < (data_length >> 1); ++i)
{
tmp_str[i] = static_cast<unsigned char>(0x00);
}
message_list.push_back(tmp_str);
for (std::size_t i = 0; i < message_list.size(); ++i)
{
block<code_length,fec_length> current_block;
rs_encoder.encode(message_list[i],current_block);
original_block_list.push_back(current_block);
}
}
template <std::size_t field_descriptor,
std::size_t gen_poly_index,
std::size_t code_length,
std::size_t fec_length,
typename RSEncoder = encoder<code_length,fec_length>,
typename RSDecoder = decoder<code_length,fec_length>,
std::size_t data_length = code_length - fec_length>
struct all_errors_decoder_speed_test
{
public:
all_errors_decoder_speed_test(const std::size_t prim_poly_size, const unsigned int prim_poly[])
{
galois::field field(field_descriptor,prim_poly_size,prim_poly);
galois::field_polynomial generator_polynomial(field);
if (
!make_sequential_root_generator_polynomial(field,
gen_poly_index,
fec_length,
generator_polynomial)
)
{
return;
}
RSEncoder rs_encoder(field,generator_polynomial);
RSDecoder rs_decoder(field,gen_poly_index);
std::vector< block<code_length,fec_length> > original_block;
create_messages<code_length,fec_length>(rs_encoder,original_block);
std::vector<block<code_length,fec_length> > rs_block;
std::vector<std::size_t> block_index_list;
for (std::size_t block_index = 0; block_index < original_block.size(); ++block_index)
{
for (std::size_t error_count = 1; error_count <= (fec_length >> 1); ++error_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
block<code_length,fec_length> block = original_block[block_index];
corrupt_message_all_errors(block,error_count,start_position,1);
rs_block.push_back(block);
block_index_list.push_back(block_index);
}
}
}
const std::size_t max_iterations = 100;
std::size_t blocks_decoded = 0;
std::size_t block_failures = 0;
schifra::utils::timer timer;
timer.start();
for (std::size_t j = 0; j < max_iterations; ++j)
{
for (std::size_t i = 0; i < rs_block.size(); ++i)
{
if (!rs_decoder.decode(rs_block[i]))
{
std::cout << "Decoding Failure!" << std::endl;
block_failures++;
}
else if (!are_blocks_equivelent(rs_block[i],original_block[block_index_list[i]]))
{
std::cout << "Error Correcting Failure!" << std::endl;
block_failures++;
}
else
blocks_decoded++;
}
}
timer.stop();
double time = timer.time();
double mbps = ((max_iterations * rs_block.size() * data_length) * 8.0) / (1048576.0 * time);
print_codec_properties();
if (block_failures == 0)
printf("Blocks decoded: %8d Time:%8.3fsec Rate:%8.3fMbps\n",
static_cast<int>(blocks_decoded),
time,
mbps);
else
std::cout << "Blocks decoded: " << blocks_decoded << "\tDecode Failures: " << block_failures <<"\tTime: " << time <<"sec\tRate: " << mbps << "Mbps" << std::endl;
}
void print_codec_properties()
{
printf("[All Errors Test] Codec: RS(%03d,%03d,%03d) ",
static_cast<int>(code_length),
static_cast<int>(data_length),
static_cast<int>(fec_length));
}
};
template <std::size_t field_descriptor,
std::size_t gen_poly_index,
std::size_t code_length,
std::size_t fec_length,
typename RSEncoder = encoder<code_length,fec_length>,
typename RSDecoder = decoder<code_length,fec_length>,
std::size_t data_length = code_length - fec_length>
struct all_erasures_decoder_speed_test
{
public:
all_erasures_decoder_speed_test(const std::size_t prim_poly_size, const unsigned int prim_poly[])
{
galois::field field(field_descriptor,prim_poly_size,prim_poly);
galois::field_polynomial generator_polynomial(field);
if (
!make_sequential_root_generator_polynomial(field,
gen_poly_index,
fec_length,
generator_polynomial)
)
{
return;
}
RSEncoder rs_encoder(field,generator_polynomial);
RSDecoder rs_decoder(field,gen_poly_index);
std::vector< block<code_length,fec_length> > original_block;
create_messages<code_length,fec_length>(rs_encoder,original_block);
std::vector<block<code_length,fec_length> > rs_block;
std::vector<erasure_locations_t> erasure_list;
std::vector<std::size_t> block_index_list;
for (std::size_t block_index = 0; block_index < original_block.size(); ++block_index)
{
for (std::size_t erasure_count = 1; erasure_count <= fec_length; ++erasure_count)
{
for (std::size_t start_position = 0; start_position < code_length; ++start_position)
{
block<code_length,fec_length> block = original_block[block_index];
erasure_locations_t erasures;
corrupt_message_all_erasures(block,erasures,erasure_count,start_position,1);
if (erasure_count != erasures.size())
{
std::cout << "all_erasures_decoder_speed_test() - Failed to properly generate erasures list. Details:";
std::cout << "(" << block_index << "," << erasure_count << "," << start_position << ")" << std::endl;
}
rs_block.push_back(block);
erasure_list.push_back(erasures);
block_index_list.push_back(block_index);
}
}
}
const std::size_t max_iterations = 100;
std::size_t blocks_decoded = 0;
std::size_t block_failures = 0;
schifra::utils::timer timer;
timer.start();
for (std::size_t j = 0; j < max_iterations; ++j)
{
for (std::size_t i = 0; i < rs_block.size(); ++i)
{
if (!rs_decoder.decode(rs_block[i],erasure_list[i]))
{
std::cout << "Decoding Failure!" << std::endl;
block_failures++;
}
else if (!are_blocks_equivelent(rs_block[i],original_block[block_index_list[i]]))
{
std::cout << "Error Correcting Failure!" << std::endl;
block_failures++;
}
else
blocks_decoded++;
}
}
timer.stop();
double time = timer.time();
double mbps = ((max_iterations * rs_block.size() * data_length) * 8.0) / (1048576.0 * time);
print_codec_properties();
if (block_failures == 0)
printf("Blocks decoded: %8d Time:%8.3fsec Rate:%8.3fMbps\n",
static_cast<int>(blocks_decoded),
time,
mbps);
else
std::cout << "Blocks decoded: " << blocks_decoded << "\tDecode Failures: " << block_failures <<"\tTime: " << time <<"sec\tRate: " << mbps << "Mbps" << std::endl;
}
void print_codec_properties()
{
printf("[All Erasures Test] Codec: RS(%03d,%03d,%03d) ",
static_cast<int>(code_length),
static_cast<int>(data_length),
static_cast<int>(fec_length));
}
};
void speed_test_00()
{
all_errors_decoder_speed_test<8,120,255, 2>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 4>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 6>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 8>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 10>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 12>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 14>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 16>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 18>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 20>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 32>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 48>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 64>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 80>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255, 96>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_errors_decoder_speed_test<8,120,255,128>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
}
void speed_test_01()
{
all_erasures_decoder_speed_test<8,120,255, 2>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 4>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 6>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 8>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 10>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 12>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 14>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 16>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 18>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 20>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 32>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 48>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 64>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 80>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255, 96>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
all_erasures_decoder_speed_test<8,120,255,128>(galois::primitive_polynomial_size06,galois::primitive_polynomial06);
}
} // namespace reed_solomon
} // namespace schifra
#endif

View File

@ -0,0 +1,64 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_SEQUENTIAL_ROOT_GENERATOR_POLYNOMIAL_CREATOR_HPP
#define INCLUDE_SCHIFRA_SEQUENTIAL_ROOT_GENERATOR_POLYNOMIAL_CREATOR_HPP
#include <cstddef>
#include "schifra_galois_field.hpp"
#include "schifra_galois_field_element.hpp"
#include "schifra_galois_field_polynomial.hpp"
namespace schifra
{
inline bool make_sequential_root_generator_polynomial(const galois::field& field,
const std::size_t initial_index,
const std::size_t num_elements,
galois::field_polynomial& generator_polynomial)
{
if (
(initial_index >= field.size()) ||
((initial_index + num_elements) > field.size())
)
{
return false;
}
galois::field_element alpha(field, 2);
galois::field_polynomial X = galois::generate_X(field);
generator_polynomial = galois::field_element(field, 1);
for (std::size_t i = initial_index; i < (initial_index + num_elements); ++i)
{
generator_polynomial *= (X + (alpha ^ static_cast<galois::field_symbol>(i)));
}
return true;
}
} // namespace schifra
#endif

View File

@ -0,0 +1,198 @@
/*
(**************************************************************************)
(* *)
(* Schifra *)
(* Reed-Solomon Error Correcting Code Library *)
(* *)
(* Release Version 0.0.1 *)
(* http://www.schifra.com *)
(* Copyright (c) 2000-2020 Arash Partow, All Rights Reserved. *)
(* *)
(* The Schifra Reed-Solomon error correcting code library and all its *)
(* components are supplied under the terms of the General Schifra License *)
(* agreement. The contents of the Schifra Reed-Solomon error correcting *)
(* code library and all its components may not be copied or disclosed *)
(* except in accordance with the terms of that agreement. *)
(* *)
(* URL: http://www.schifra.com/license.html *)
(* *)
(**************************************************************************)
*/
#ifndef INCLUDE_SCHIFRA_UTILITES_HPP
#define INCLUDE_SCHIFRA_UTILITES_HPP
#include <cstddef>
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
#include <windows.h>
#else
#include <sys/time.h>
#include <sys/types.h>
#endif
namespace schifra
{
namespace utils
{
const std::size_t high_bits_in_char[256] = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
};
template <typename T>
inline std::size_t hamming_distance_element(const T v1, const T v2)
{
std::size_t distance = 0;
const unsigned char* it1 = reinterpret_cast<const unsigned char*>(&v1);
const unsigned char* it2 = reinterpret_cast<const unsigned char*>(&v2);
for (std::size_t i = 0; i < sizeof(T); ++i, ++it1, ++it2)
{
distance += high_bits_in_char[((*it1) ^ (*it2)) & 0xFF];
}
return distance;
}
inline std::size_t hamming_distance(const unsigned char data1[], const unsigned char data2[], const std::size_t length)
{
std::size_t distance = 0;
const unsigned char* it1 = data1;
const unsigned char* it2 = data2;
for (std::size_t i = 0; i < length; ++i, ++it1, ++it2)
{
distance += high_bits_in_char[((*it1) ^ (*it2)) & 0xFF];
}
return distance;
}
template <typename ForwardIterator>
inline std::size_t hamming_distance(ForwardIterator it1_begin, ForwardIterator it2_begin, ForwardIterator it1_end)
{
std::size_t distance = 0;
ForwardIterator it1 = it1_begin;
ForwardIterator it2 = it2_begin;
for (; it1 != it1_end; ++it1, ++it2)
{
distance += hamming_distance_element(*it1,*it2);
}
return distance;
}
class timer
{
public:
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
timer()
: in_use_(false)
{
QueryPerformanceFrequency(&clock_frequency_);
}
inline void start()
{
in_use_ = true;
QueryPerformanceCounter(&start_time_);
}
inline void stop()
{
QueryPerformanceCounter(&stop_time_);
in_use_ = false;
}
inline double time() const
{
return (1.0 * (stop_time_.QuadPart - start_time_.QuadPart)) / (1.0 * clock_frequency_.QuadPart);
}
#else
timer()
: in_use_(false)
{
start_time_.tv_sec = 0;
start_time_.tv_usec = 0;
stop_time_.tv_sec = 0;
stop_time_.tv_usec = 0;
}
inline void start()
{
in_use_ = true;
gettimeofday(&start_time_,0);
}
inline void stop()
{
gettimeofday(&stop_time_, 0);
in_use_ = false;
}
inline unsigned long long int usec_time() const
{
if (!in_use_)
{
if (stop_time_.tv_sec >= start_time_.tv_sec)
{
return 1000000 * (stop_time_.tv_sec - start_time_.tv_sec ) +
(stop_time_.tv_usec - start_time_.tv_usec);
}
else
return std::numeric_limits<unsigned long long int>::max();
}
else
return std::numeric_limits<unsigned long long int>::max();
}
inline double time() const
{
return usec_time() * 0.000001;
}
#endif
inline bool in_use() const
{
return in_use_;
}
private:
bool in_use_;
#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
LARGE_INTEGER start_time_;
LARGE_INTEGER stop_time_;
LARGE_INTEGER clock_frequency_;
#else
struct timeval start_time_;
struct timeval stop_time_;
#endif
};
} // namespace utils
} // namespace schifra
#endif

126
hsmodem/fft.cpp Executable file
View File

@ -0,0 +1,126 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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 "hsmodem.h"
#ifdef _WIN32_
#include "fftw_lib/fftw3.h"
#endif
#ifdef _LINUX_
#include <fftw3.h>
#endif
#define FFT_AUDIOSAMPLERATE 8000
double *din = NULL; // input data for fft
fftw_complex *cpout = NULL; // ouput data from fft
fftw_plan plan = NULL;
#define fft_rate (FFT_AUDIOSAMPLERATE / 10) // resolution: 10 Hz
int fftidx = 0;
int fftcnt = fft_rate/2+1; // number of output values
uint16_t fftout[FFT_AUDIOSAMPLERATE / 10/2+1];
int downsamp = 0;
int downphase = 0;
uint16_t *make_waterfall(float fre, int *retlen)
{
// Downsampling:
// needed 8000 bit/s
// caprate 48k: downsample by 6
// caprate 44,1k: downsample by 5,5
if (caprate == 48000)
{
if (++downsamp < 6) return NULL;
}
if (caprate == 44100)
{
if (downphase <= 1100)
{
if (++downsamp < 5) return NULL;
}
else
{
if (++downsamp < 6) return NULL;
}
if(++downphase >= 2000) downphase = 0;
}
downsamp = 0;
int fftrdy = 0;
// fre are the float samples
// fill into the fft input buffer
din[fftidx++] = fre;
if(fftidx == fft_rate)
{
fftidx = 0;
// the fft buffer is full, execute the FFT
fftw_execute(plan);
for (int j = 0; j < fftcnt; j++)
{
// calculate absolute value (magnitute without phase)
float fre = (float)cpout[j][0];
float fim = (float)cpout[j][1];
float mag = sqrt((fre * fre) + (fim * fim));
fftout[j] = (uint16_t)mag;
fftrdy = 1;
}
}
if(fftrdy == 1)
{
*retlen = fftcnt;
return fftout;
}
return NULL;
}
void init_fft()
{
char fn[300];
sprintf(fn, "capture_fft_%d", fft_rate); // wisdom file for each capture rate
fftw_import_wisdom_from_filename(fn);
din = (double *)fftw_malloc(sizeof(double) * fft_rate);
cpout = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * fft_rate);
plan = fftw_plan_dft_r2c_1d(fft_rate, din, cpout, FFTW_MEASURE);
fftw_export_wisdom_to_filename(fn);
}
void exit_fft()
{
if(plan) fftw_destroy_plan(plan);
if(din) fftw_free(din);
if(cpout) fftw_free(cpout);
}

415
hsmodem/fftw_lib/fftw3.h Normal file
View File

@ -0,0 +1,415 @@
/*
* Copyright (c) 2003, 2007-14 Matteo Frigo
* Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
*
* The following statement of license applies *only* to this header file,
* and *not* to the other files distributed with FFTW or derived therefrom:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/***************************** NOTE TO USERS *********************************
*
* THIS IS A HEADER FILE, NOT A MANUAL
*
* If you want to know how to use FFTW, please read the manual,
* online at http://www.fftw.org/doc/ and also included with FFTW.
* For a quick start, see the manual's tutorial section.
*
* (Reading header files to learn how to use a library is a habit
* stemming from code lacking a proper manual. Arguably, it's a
* *bad* habit in most cases, because header files can contain
* interfaces that are not part of the public, stable API.)
*
****************************************************************************/
#ifndef FFTW3_H
#define FFTW3_H
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/* If <complex.h> is included, use the C99 complex type. Otherwise
define a type bit-compatible with C99 complex */
#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
# define FFTW_DEFINE_COMPLEX(R, C) typedef R _Complex C
#else
# define FFTW_DEFINE_COMPLEX(R, C) typedef R C[2]
#endif
#define FFTW_CONCAT(prefix, name) prefix ## name
#define FFTW_MANGLE_DOUBLE(name) FFTW_CONCAT(fftw_, name)
#define FFTW_MANGLE_FLOAT(name) FFTW_CONCAT(fftwf_, name)
#define FFTW_MANGLE_LONG_DOUBLE(name) FFTW_CONCAT(fftwl_, name)
#define FFTW_MANGLE_QUAD(name) FFTW_CONCAT(fftwq_, name)
/* IMPORTANT: for Windows compilers, you should add a line
*/
#define FFTW_DLL
/*
here and in kernel/ifftw.h if you are compiling/using FFTW as a
DLL, in order to do the proper importing/exporting, or
alternatively compile with -DFFTW_DLL or the equivalent
command-line flag. This is not necessary under MinGW/Cygwin, where
libtool does the imports/exports automatically. */
#if defined(FFTW_DLL) && (defined(_WIN32) || defined(__WIN32__))
/* annoying Windows syntax for shared-library declarations */
# if defined(COMPILING_FFTW) /* defined in api.h when compiling FFTW */
# define FFTW_EXTERN extern __declspec(dllexport)
# else /* user is calling FFTW; import symbol */
# define FFTW_EXTERN extern __declspec(dllimport)
# endif
#else
# define FFTW_EXTERN extern
#endif
enum fftw_r2r_kind_do_not_use_me {
FFTW_R2HC=0, FFTW_HC2R=1, FFTW_DHT=2,
FFTW_REDFT00=3, FFTW_REDFT01=4, FFTW_REDFT10=5, FFTW_REDFT11=6,
FFTW_RODFT00=7, FFTW_RODFT01=8, FFTW_RODFT10=9, FFTW_RODFT11=10
};
struct fftw_iodim_do_not_use_me {
int n; /* dimension size */
int is; /* input stride */
int os; /* output stride */
};
#include <stddef.h> /* for ptrdiff_t */
struct fftw_iodim64_do_not_use_me {
ptrdiff_t n; /* dimension size */
ptrdiff_t is; /* input stride */
ptrdiff_t os; /* output stride */
};
typedef void (*fftw_write_char_func_do_not_use_me)(char c, void *);
typedef int (*fftw_read_char_func_do_not_use_me)(void *);
/*
huge second-order macro that defines prototypes for all API
functions. We expand this macro for each supported precision
X: name-mangling macro
R: real data type
C: complex data type
*/
#define FFTW_DEFINE_API(X, R, C) \
\
FFTW_DEFINE_COMPLEX(R, C); \
\
typedef struct X(plan_s) *X(plan); \
\
typedef struct fftw_iodim_do_not_use_me X(iodim); \
typedef struct fftw_iodim64_do_not_use_me X(iodim64); \
\
typedef enum fftw_r2r_kind_do_not_use_me X(r2r_kind); \
\
typedef fftw_write_char_func_do_not_use_me X(write_char_func); \
typedef fftw_read_char_func_do_not_use_me X(read_char_func); \
\
FFTW_EXTERN void X(execute)(const X(plan) p); \
\
FFTW_EXTERN X(plan) X(plan_dft)(int rank, const int *n, \
C *in, C *out, int sign, unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_dft_1d)(int n, C *in, C *out, int sign, \
unsigned flags); \
FFTW_EXTERN X(plan) X(plan_dft_2d)(int n0, int n1, \
C *in, C *out, int sign, unsigned flags); \
FFTW_EXTERN X(plan) X(plan_dft_3d)(int n0, int n1, int n2, \
C *in, C *out, int sign, unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_many_dft)(int rank, const int *n, \
int howmany, \
C *in, const int *inembed, \
int istride, int idist, \
C *out, const int *onembed, \
int ostride, int odist, \
int sign, unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_guru_dft)(int rank, const X(iodim) *dims, \
int howmany_rank, \
const X(iodim) *howmany_dims, \
C *in, C *out, \
int sign, unsigned flags); \
FFTW_EXTERN X(plan) X(plan_guru_split_dft)(int rank, const X(iodim) *dims, \
int howmany_rank, \
const X(iodim) *howmany_dims, \
R *ri, R *ii, R *ro, R *io, \
unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_guru64_dft)(int rank, \
const X(iodim64) *dims, \
int howmany_rank, \
const X(iodim64) *howmany_dims, \
C *in, C *out, \
int sign, unsigned flags); \
FFTW_EXTERN X(plan) X(plan_guru64_split_dft)(int rank, \
const X(iodim64) *dims, \
int howmany_rank, \
const X(iodim64) *howmany_dims, \
R *ri, R *ii, R *ro, R *io, \
unsigned flags); \
\
FFTW_EXTERN void X(execute_dft)(const X(plan) p, C *in, C *out); \
FFTW_EXTERN void X(execute_split_dft)(const X(plan) p, R *ri, R *ii, \
R *ro, R *io); \
\
FFTW_EXTERN X(plan) X(plan_many_dft_r2c)(int rank, const int *n, \
int howmany, \
R *in, const int *inembed, \
int istride, int idist, \
C *out, const int *onembed, \
int ostride, int odist, \
unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_dft_r2c)(int rank, const int *n, \
R *in, C *out, unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_dft_r2c_1d)(int n,R *in,C *out,unsigned flags); \
FFTW_EXTERN X(plan) X(plan_dft_r2c_2d)(int n0, int n1, \
R *in, C *out, unsigned flags); \
FFTW_EXTERN X(plan) X(plan_dft_r2c_3d)(int n0, int n1, \
int n2, \
R *in, C *out, unsigned flags); \
\
\
FFTW_EXTERN X(plan) X(plan_many_dft_c2r)(int rank, const int *n, \
int howmany, \
C *in, const int *inembed, \
int istride, int idist, \
R *out, const int *onembed, \
int ostride, int odist, \
unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_dft_c2r)(int rank, const int *n, \
C *in, R *out, unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_dft_c2r_1d)(int n,C *in,R *out,unsigned flags); \
FFTW_EXTERN X(plan) X(plan_dft_c2r_2d)(int n0, int n1, \
C *in, R *out, unsigned flags); \
FFTW_EXTERN X(plan) X(plan_dft_c2r_3d)(int n0, int n1, \
int n2, \
C *in, R *out, unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_guru_dft_r2c)(int rank, const X(iodim) *dims, \
int howmany_rank, \
const X(iodim) *howmany_dims, \
R *in, C *out, \
unsigned flags); \
FFTW_EXTERN X(plan) X(plan_guru_dft_c2r)(int rank, const X(iodim) *dims, \
int howmany_rank, \
const X(iodim) *howmany_dims, \
C *in, R *out, \
unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_guru_split_dft_r2c)( \
int rank, const X(iodim) *dims, \
int howmany_rank, \
const X(iodim) *howmany_dims, \
R *in, R *ro, R *io, \
unsigned flags); \
FFTW_EXTERN X(plan) X(plan_guru_split_dft_c2r)( \
int rank, const X(iodim) *dims, \
int howmany_rank, \
const X(iodim) *howmany_dims, \
R *ri, R *ii, R *out, \
unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_guru64_dft_r2c)(int rank, \
const X(iodim64) *dims, \
int howmany_rank, \
const X(iodim64) *howmany_dims, \
R *in, C *out, \
unsigned flags); \
FFTW_EXTERN X(plan) X(plan_guru64_dft_c2r)(int rank, \
const X(iodim64) *dims, \
int howmany_rank, \
const X(iodim64) *howmany_dims, \
C *in, R *out, \
unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_guru64_split_dft_r2c)( \
int rank, const X(iodim64) *dims, \
int howmany_rank, \
const X(iodim64) *howmany_dims, \
R *in, R *ro, R *io, \
unsigned flags); \
FFTW_EXTERN X(plan) X(plan_guru64_split_dft_c2r)( \
int rank, const X(iodim64) *dims, \
int howmany_rank, \
const X(iodim64) *howmany_dims, \
R *ri, R *ii, R *out, \
unsigned flags); \
\
FFTW_EXTERN void X(execute_dft_r2c)(const X(plan) p, R *in, C *out); \
FFTW_EXTERN void X(execute_dft_c2r)(const X(plan) p, C *in, R *out); \
\
FFTW_EXTERN void X(execute_split_dft_r2c)(const X(plan) p, \
R *in, R *ro, R *io); \
FFTW_EXTERN void X(execute_split_dft_c2r)(const X(plan) p, \
R *ri, R *ii, R *out); \
\
FFTW_EXTERN X(plan) X(plan_many_r2r)(int rank, const int *n, \
int howmany, \
R *in, const int *inembed, \
int istride, int idist, \
R *out, const int *onembed, \
int ostride, int odist, \
const X(r2r_kind) *kind, unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_r2r)(int rank, const int *n, R *in, R *out, \
const X(r2r_kind) *kind, unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_r2r_1d)(int n, R *in, R *out, \
X(r2r_kind) kind, unsigned flags); \
FFTW_EXTERN X(plan) X(plan_r2r_2d)(int n0, int n1, R *in, R *out, \
X(r2r_kind) kind0, X(r2r_kind) kind1, \
unsigned flags); \
FFTW_EXTERN X(plan) X(plan_r2r_3d)(int n0, int n1, int n2, \
R *in, R *out, X(r2r_kind) kind0, \
X(r2r_kind) kind1, X(r2r_kind) kind2, \
unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_guru_r2r)(int rank, const X(iodim) *dims, \
int howmany_rank, \
const X(iodim) *howmany_dims, \
R *in, R *out, \
const X(r2r_kind) *kind, unsigned flags); \
\
FFTW_EXTERN X(plan) X(plan_guru64_r2r)(int rank, const X(iodim64) *dims, \
int howmany_rank, \
const X(iodim64) *howmany_dims, \
R *in, R *out, \
const X(r2r_kind) *kind, unsigned flags); \
\
FFTW_EXTERN void X(execute_r2r)(const X(plan) p, R *in, R *out); \
\
FFTW_EXTERN void X(destroy_plan)(X(plan) p); \
FFTW_EXTERN void X(forget_wisdom)(void); \
FFTW_EXTERN void X(cleanup)(void); \
\
FFTW_EXTERN void X(set_timelimit)(double t); \
\
FFTW_EXTERN void X(plan_with_nthreads)(int nthreads); \
FFTW_EXTERN int X(init_threads)(void); \
FFTW_EXTERN void X(cleanup_threads)(void); \
FFTW_EXTERN void X(make_planner_thread_safe)(void); \
\
FFTW_EXTERN int X(export_wisdom_to_filename)(const char *filename); \
FFTW_EXTERN void X(export_wisdom_to_file)(FILE *output_file); \
FFTW_EXTERN char *X(export_wisdom_to_string)(void); \
FFTW_EXTERN void X(export_wisdom)(X(write_char_func) write_char, \
void *data); \
FFTW_EXTERN int X(import_system_wisdom)(void); \
FFTW_EXTERN int X(import_wisdom_from_filename)(const char *filename); \
FFTW_EXTERN int X(import_wisdom_from_file)(FILE *input_file); \
FFTW_EXTERN int X(import_wisdom_from_string)(const char *input_string); \
FFTW_EXTERN int X(import_wisdom)(X(read_char_func) read_char, void *data); \
\
FFTW_EXTERN void X(fprint_plan)(const X(plan) p, FILE *output_file); \
FFTW_EXTERN void X(print_plan)(const X(plan) p); \
FFTW_EXTERN char *X(sprint_plan)(const X(plan) p); \
\
FFTW_EXTERN void *X(malloc)(size_t n); \
FFTW_EXTERN R *X(alloc_real)(size_t n); \
FFTW_EXTERN C *X(alloc_complex)(size_t n); \
FFTW_EXTERN void X(free)(void *p); \
\
FFTW_EXTERN void X(flops)(const X(plan) p, \
double *add, double *mul, double *fmas); \
FFTW_EXTERN double X(estimate_cost)(const X(plan) p); \
FFTW_EXTERN double X(cost)(const X(plan) p); \
\
FFTW_EXTERN int X(alignment_of)(R *p); \
FFTW_EXTERN const char X(version)[]; \
FFTW_EXTERN const char X(cc)[]; \
FFTW_EXTERN const char X(codelet_optim)[];
/* end of FFTW_DEFINE_API macro */
FFTW_DEFINE_API(FFTW_MANGLE_DOUBLE, double, fftw_complex)
FFTW_DEFINE_API(FFTW_MANGLE_FLOAT, float, fftwf_complex)
FFTW_DEFINE_API(FFTW_MANGLE_LONG_DOUBLE, long double, fftwl_complex)
/* __float128 (quad precision) is a gcc extension on i386, x86_64, and ia64
for gcc >= 4.6 (compiled in FFTW with --enable-quad-precision) */
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) \
&& !(defined(__ICC) || defined(__INTEL_COMPILER) || defined(__CUDACC__) || defined(__PGI)) \
&& (defined(__i386__) || defined(__x86_64__) || defined(__ia64__))
# if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
/* note: __float128 is a typedef, which is not supported with the _Complex
keyword in gcc, so instead we use this ugly __attribute__ version.
However, we can't simply pass the __attribute__ version to
FFTW_DEFINE_API because the __attribute__ confuses gcc in pointer
types. Hence redefining FFTW_DEFINE_COMPLEX. Ugh. */
# undef FFTW_DEFINE_COMPLEX
# define FFTW_DEFINE_COMPLEX(R, C) typedef _Complex float __attribute__((mode(TC))) C
# endif
FFTW_DEFINE_API(FFTW_MANGLE_QUAD, __float128, fftwq_complex)
#endif
#define FFTW_FORWARD (-1)
#define FFTW_BACKWARD (+1)
#define FFTW_NO_TIMELIMIT (-1.0)
/* documented flags */
#define FFTW_MEASURE (0U)
#define FFTW_DESTROY_INPUT (1U << 0)
#define FFTW_UNALIGNED (1U << 1)
#define FFTW_CONSERVE_MEMORY (1U << 2)
#define FFTW_EXHAUSTIVE (1U << 3) /* NO_EXHAUSTIVE is default */
#define FFTW_PRESERVE_INPUT (1U << 4) /* cancels FFTW_DESTROY_INPUT */
#define FFTW_PATIENT (1U << 5) /* IMPATIENT is default */
#define FFTW_ESTIMATE (1U << 6)
#define FFTW_WISDOM_ONLY (1U << 21)
/* undocumented beyond-guru flags */
#define FFTW_ESTIMATE_PATIENT (1U << 7)
#define FFTW_BELIEVE_PCOST (1U << 8)
#define FFTW_NO_DFT_R2HC (1U << 9)
#define FFTW_NO_NONTHREADED (1U << 10)
#define FFTW_NO_BUFFERING (1U << 11)
#define FFTW_NO_INDIRECT_OP (1U << 12)
#define FFTW_ALLOW_LARGE_GENERIC (1U << 13) /* NO_LARGE_GENERIC is default */
#define FFTW_NO_RANK_SPLITS (1U << 14)
#define FFTW_NO_VRANK_SPLITS (1U << 15)
#define FFTW_NO_VRECURSE (1U << 16)
#define FFTW_NO_SIMD (1U << 17)
#define FFTW_NO_SLOW (1U << 18)
#define FFTW_NO_FIXED_RADIX_LARGE_N (1U << 19)
#define FFTW_ALLOW_PRUNING (1U << 20)
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* FFTW3_H */

BIN
hsmodem/fftw_lib/libfftw3-3.lib Executable file

Binary file not shown.

320
hsmodem/frame_packer.cpp Executable file
View File

@ -0,0 +1,320 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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 "hsmodem.h"
void Insert(uint8_t bit);
uint8_t* getPayload(uint8_t* rxb);
uint8_t rxbuffer[UDPBLOCKLEN*8/2+100]; // 3...bits per symbol QPSK, enough space also for QPSK and 8PSK, +100 ... reserve, just to be sure
uint8_t rx_status = 0;
int framecounter = 0;
int lastframenum = 0;
// header for TX,
uint8_t TXheaderbytes[HEADERLEN] = {0x53, 0xe1, 0xa6};
// corresponds to these QPSK symbols:
// bits: 01010011 11100001 10100110
// QPSK:
// syms: 1 1 0 3 3 2 0 1 2 2 1 2
// 8PSK:
// syms: 2 4 7 6 0 6 4 6
// QPSK
// each header has 12 symbols
// we have 4 constellations
uint8_t QPSK_headertab[4][HEADERLEN*8/2];
// 8PSK
// each header has 8 symbols
// we have 8 constellations
uint8_t _8PSK_headertab[8][HEADERLEN*8/3];
/*
8CONST: . Len 8: 02 04 07 06 00 06 04 06
8CONST: . Len 8: 03 05 06 02 00 02 05 02
8CONST: . Len 8: 01 07 02 03 00 03 07 03
8CONST: . Len 8: 04 06 03 01 00 01 06 01
8CONST: . Len 8: 05 02 01 04 00 04 02 04
8CONST: . Len 8: 07 03 04 05 00 05 03 05
8CONST: . Len 8: 06 01 05 07 00 07 01 07
8CONST: . Len 8: 02 04 07 06 00 06 04 06
*/
// init header tables
void init_packer()
{
// create the QPSK symbol table for the HEADER
// in all possible rotations
convertBytesToSyms_QPSK(TXheaderbytes, QPSK_headertab[0], 3);
for(int i=1; i<4; i++)
rotateQPSKsyms(QPSK_headertab[i-1], QPSK_headertab[i], 12);
// create the 8PSK symbol table for the HEADER
// in all possible rotations
convertBytesToSyms_8PSK(TXheaderbytes, _8PSK_headertab[0], 3);
for(int i=1; i<8; i++)
{
rotate8APSKsyms(_8PSK_headertab[i-1], _8PSK_headertab[i], 8);
}
for(int i=0; i<8; i++)
showbytestring((char*)"8CONST: ",_8PSK_headertab[i],8);
}
// packs a payload into an udp data block
// the payload has a size of PAYLOADLEN
// type ... inserted in the "frame type information" field
// status ... specifies first/last frame of a data stream
uint8_t *Pack(uint8_t *payload, int type, int status, int *plen)
{
FRAME frame; // raw frame without fec
// polulate the raw frame
// make the frame counter
if(status & (1<<4))
framecounter = 0; // first block of a stream
else
framecounter++;
// insert frame counter and status bits
frame.counter_LSB = framecounter & 0xff;
int framecnt_MSB = (framecounter >> 8) & 0x03; // Bit 8+9 of framecounter
frame.status = framecnt_MSB << 6;
frame.status += ((status & 0x03)<<4);
frame.status += (type & 0x0f);
// insert the payload
memcpy(frame.payload, payload, PAYLOADLEN);
// calculate and insert the CRC16
uint16_t crc16 = Crc16_messagecalc(CRC16TX,(uint8_t *)(&frame), CRCSECUREDLEN);
frame.crc16_MSB = (uint8_t)(crc16 >> 8);
frame.crc16_LSB = (uint8_t)(crc16 & 0xff);
// make the final arry for transmission
static uint8_t txblock[UDPBLOCKLEN];
// calculate the fec and insert into txblock (leave space for the header)
GetFEC((uint8_t *)(&frame), DATABLOCKLEN, txblock+HEADERLEN);
// scramble
TX_Scramble(txblock+HEADERLEN, FECBLOCKLEN); // scramble all data
// insert the header
memcpy(txblock,TXheaderbytes,HEADERLEN);
*plen = UDPBLOCKLEN;
return txblock;
}
#define MAXHEADERRS 0
/*
* Header erros will not cause any data errors because the CRC will filter out
* false header detects,
* but it will cause higher CPU load due to excessive execution of FEC and CRC
*/
int seekHeadersyms()
{
if(constellationSize == 4)
{
// QPSK
for(int tab=0; tab<4; tab++)
{
int errs = 0;
for(int i=0; i<HEADERLEN*8/2; i++)
{
if(rxbuffer[i] != QPSK_headertab[tab][i])
{
errs++;
}
}
if(errs <= MAXHEADERRS) return tab;
}
}
else
{
// 8PSK
for(int tab=0; tab<8; tab++)
{
int errs = 0;
for(int i=0; i<HEADERLEN*8/3; i++)
{
if(rxbuffer[i] != _8PSK_headertab[tab][i])
{
errs++;
}
}
if(errs <= MAXHEADERRS) return tab;
}
}
return -1;
}
// unpacks a received data frame
// pdata, len ... symbols received from the modem
// returns ... payload, one full frame
uint8_t *unpack_data(uint8_t *rxd, int len)
{
int framerdy = 0;
static uint8_t payload[PAYLOADLEN+10];
rx_status = 0;
// shift all received symbols through rxbuffer
// until a header is detected
for(int sym=0; sym<len; sym++)
{
// shift rxbuffer right by one symbol (=byte)
// frmlen ... number of symbols
int frmlen = UDPBLOCKLEN*8/bitsPerSymbol;
memmove(rxbuffer,rxbuffer+1,frmlen-1);
// insert new symbol at the top
rxbuffer[frmlen-1] = rxd[sym];
//showbytestring((char*)"rx: ",rxbuffer,30);
int rotations = seekHeadersyms();
if(rotations != -1)
{
//printf("Header found, rotation: %d\n",rotations);
// rxbuffer contains all symbols of the received frame
// convert to bytes
uint8_t *rxbytes = NULL;
if(constellationSize == 4)
{
uint8_t *backbuf = rotateBackQPSK(rxbuffer,frmlen,rotations);
rxbytes = convertQPSKSymToBytes(backbuf);
}
else
{
uint8_t *backbuf = rotateBack8APSK(rxbuffer,frmlen,rotations);
rxbytes = convert8PSKSymToBytes(backbuf,UDPBLOCKLEN);
}
if(rxbytes == NULL) return NULL;
// check if we found a data block
uint8_t *pl = getPayload(rxbytes);
if (pl != NULL)
{
memcpy(payload,pl, PAYLOADLEN+10);
framerdy = 1;
}
}
}
if(framerdy)
return payload;
return NULL;
}
// inserts bit per bit into the rxbuffer
// rxbuffer has the size of a complete frame
// so if the header is detected, rxbuffer contains the frame
void Insert(uint8_t bit)
{
if (bit != 0) bit = 1;
// bitshift right
for (int i = 0; i < UDPBLOCKLEN; i++)
{
rxbuffer[i] <<= 1;
if (i == (UDPBLOCKLEN - 1)) break;
uint8_t ov = (uint8_t)(rxbuffer[i + 1] & 0x80);
if (ov != 0) rxbuffer[i] |= 1;
else rxbuffer[i] &= 0xfe;
}
// insert new bit
rxbuffer[UDPBLOCKLEN - 1] &= 0xfe;
rxbuffer[UDPBLOCKLEN - 1] |= (uint8_t)bit;
}
uint8_t *getPayload(uint8_t *rxb)
{
//showbytestring((char *)"orig: ",rxb+HEADERLEN,30);
// unscramble
uint8_t *rxarray = RX_Scramble(rxb+HEADERLEN, FECBLOCKLEN);
// calculate the FEC over the received data
// and fill a frame structure
FRAME frame;
int ret = cfec_Reconstruct(rxarray,(uint8_t *)(&frame));
if(ret == 0)
{
//printf("fec ERROR\n");
return NULL; // fec impossible
}
//printf("fec ok\n");
// check the CRC
uint16_t crc = Crc16_messagecalc(CRC16RX,(uint8_t *)(&frame), CRCSECUREDLEN);
uint16_t rxcrc = frame.crc16_MSB;
rxcrc <<= 8;
rxcrc += frame.crc16_LSB;
if (crc != rxcrc)
{
//printf("crc ERROR\n");
return NULL; // no data found
}
//printf("crc OK\n");
// frame counter verification: lost frame detection
int framenumrx = (frame.status & 0xc0)>>6; // frame counter MSB
framenumrx <<= 8;
framenumrx += frame.counter_LSB; // frame counter LSB
if (lastframenum != framenumrx) rx_status |= 4;
lastframenum = framenumrx;
if (++lastframenum >= 1024) lastframenum = 0; // 1024 = 2^10 (10 bit frame number)
// extract information and build the string for the application
// we have 10 Management Byte then the payload follows
static uint8_t payload[PAYLOADLEN+10];
payload[0] = frame.status & 0x0f; // frame type
payload[1] = (frame.status & 0xc0)>>6; // frame counter MSB
payload[2] = frame.counter_LSB; // frame counter LSB
payload[3] = (frame.status & 0x30)>>4; // first/last frame marker
payload[4] = rx_status; // frame lost information
payload[5] = speed >> 8; // measured line speed
payload[6] = speed;
payload[7] = 0; // free for later use
payload[8] = 0;
payload[9] = 0;
//printf("Frame no.: %d, type:%d, minfo:%d\n",framenumrx,payload[0],payload[3]);
memcpy(payload+10,frame.payload,PAYLOADLEN);
return payload;
}

87
hsmodem/frameformat.h Normal file
View File

@ -0,0 +1,87 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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.
*
*/
/*
* The total length of the FEC-secured part is 255,
* this is a requirement of the Shifra FEC routine, which
* is the best FEC that I have seen so far, highly recommended
*/
// total "on the air" frame size
// the total length must be a multiple of 2 and 3, so QPSK and 8PSK symbols fit into full bytes
// this is the case with a total length of 258
#define HEADERLEN 3
#define FECBLOCKLEN 255
#define UDPBLOCKLEN (HEADERLEN + FECBLOCKLEN)
/* !!! IMPORTANT for GNU RADIO !!!
* the UDP payload size for TX MUST be exactly UDPBLOCKLEN (258 in this case) or
* the transmitter will not align bits to symbols correctly !
*
* RX payload size is not that important. But the currect size for
* QPSK is UDPBLOCKLEN*8/2 = 1032 and for 8PSK UDPBLOCKLEN*8/3 = 688
* so we can use 344 which are 2 blocks for 8PSK and 3 blocks for QPSK
* */
// size of the elements inside an FECblock
// sum must be 255
#define FECLEN 32 // supported: 16,32,64,128
#define STATUSLEN 2
#define CRCLEN 2
#define PAYLOADLEN (FECBLOCKLEN - FECLEN - CRCLEN - STATUSLEN)
#define CRCSECUREDLEN (PAYLOADLEN + STATUSLEN)
#define DATABLOCKLEN (PAYLOADLEN + CRCLEN + STATUSLEN)
// the header is not FEC secured therefore we give some room for bit
// errors. Only 24 out of the 32 bits must be correct for
// a valid frame detection
extern uint8_t header[HEADERLEN];
typedef struct {
// the total size of the following data must be 255 - 32 = 223 bytes
// the FEC is calculated on FRAME with a length of 223 and returns
// a data block with length 255.
// we use a 10 bits frame counter -> 1024 values
// so we can transmit a data block with a maximum
// size of 255 * 1024 = 261kByte. With the maximum modem speed
// this would be a transmission time of 5,8 minutes which
// is more then enough for a single data block
uint8_t counter_LSB; // lower 8 bits of the frame counter
// the status byte contains these information:
// bit 0..3 : 4 bit (16 values) frame type information
// bit 4 : first frame of a block if "1"
// bit 5 : last frame of a block if "1"
// bit 6..7 : MSB of the frame counter
uint8_t status;
// payload
uint8_t payload[PAYLOADLEN];
// CRC16
uint8_t crc16_MSB;
uint8_t crc16_LSB;
} FRAME;

439
hsmodem/hsmodem.cpp Executable file
View File

@ -0,0 +1,439 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
* made for: AMSAT-DL
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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.
*
*/
/*
* this is a console program
* it can be compiled under Linux: make
* and under Windows: Visual-Studio
*
* 3rd party libraries:
* 1) BASS Audio from https://www.un4seen.com/
copy bass.h and bass.lib into source directory
Windows: copy bass.dll into executable directory
Linux: copy libbass.so into shared-lib folder, usually /usr/local/lib
! NOTE: for PC-Linux and ARM-Linux you need different libraries !
2) liquid-DSP
Linux Install Script:
this installs it from source
sudo apt install git autoconf libsndfile-dev libasound-dev
git clone git://github.com/jgaeddert/liquid-dsp.git
cd liquid-dsp
./bootstrap.sh
./configure
make -j 8
sudo make install
sudo ldconfig
a working copy of the source code is in ../3rdParty/liquid-dsp
to use this source simply remove the "git clone" line from above script
it installs libliquid.so into /usr/local/lib (Ubuntu) and
liquid.h into /usr/local/include/liquid/
Windows:
ready libraries are in ../3rdParty/liquid-dsp-windows
copy liquid.h and liquid.lib into source directory
copy liquid.dll into executable directory
*/
#include "hsmodem.h"
void toGR_sendData(uint8_t* data, int type, int status);
void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock);
void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock);
void startModem();
// threads will exit if set to 0
int keeprunning = 1;
// UDP I/O
int BC_sock_AppToModem = -1;
int DATA_sock_AppToModem = -1;
int DATA_sock_from_GR = -1;
int DATA_sock_FFT_from_GR = -1;
int DATA_sock_I_Q_from_GR = -1;
int UdpBCport_AppToModem = 40131;
int UdpDataPort_AppToModem = 40132;
int UdpDataPort_ModemToApp = 40133;
int UdpDataPort_toGR = 40134;
int UdpDataPort_fromGR = 40135;
int UdpDataPort_fromGR_FFT = 40136;
int UdpDataPort_fromGR_I_Q = 40137;
// op mode depending values
// default mode if not set by the app
int speedmode = 7;
int bitsPerSymbol = 2; // QPSK=2, 8PSK=3
int constellationSize = 4; // QPSK=4, 8PSK=8
char localIP[] = { "127.0.0.1" };
char ownfilename[] = { "hsmodem" };
char appIP[20] = { 0 };
int fixappIP = 0;
int restart_modems = 0;
int caprate = 44100;
int txinterpolfactor = 20;
int rxPreInterpolfactor = 5;
int captureDeviceNo = -1;
int playbackDeviceNo = -1;
int main(int argc, char* argv[])
{
int opt = 0;
char* modemip = NULL;
#ifdef _LINUX_
while ((opt = getopt(argc, argv, "m:")) != -1)
{
switch (opt)
{
case 'm':
// specify IP of application: hsmodem -m 192.168.0.1
modemip = optarg;
memset(appIP, 0, 20);
int len = strlen(modemip);
if (len < 16)
{
memcpy(appIP, modemip, len);
fixappIP = 1;
printf("Application IP set to: %s\n", modemip);
}
else
{
printf("invalid Application IP: %s\n", modemip);
exit(0);
}
break;
}
}
if (isRunning(ownfilename) == 1)
exit(0);
install_signal_handler();
#endif
#ifdef _WIN32_
if (argc != 1 && argc != 3)
{
printf("invalid argument\n");
exit(0);
}
if (argc == 3)
{
memset(appIP, 0, 20);
int len = strlen(argv[2]);
if (len < 16)
{
memcpy(appIP, argv[2], len);
fixappIP = 1;
printf("Application IP set to: %s\n", argv[2]);
}
else
{
printf("invalid Application IP: %s\n", modemip);
exit(0);
}
}
#endif
init_packer();
initFEC();
init_fft();
int ar = init_audio(playbackDeviceNo, captureDeviceNo);
if (ar == -1)
{
keeprunning = 0;
exit(0);
}
// start udp RX to listen for broadcast search message from Application
UdpRxInit(&BC_sock_AppToModem, UdpBCport_AppToModem, &bc_rxdata, &keeprunning);
// start udp RX for data from application
UdpRxInit(&DATA_sock_AppToModem, UdpDataPort_AppToModem, &appdata_rxdata, &keeprunning);
// start udp RX to listen for data from GR Receiver
UdpRxInit(&DATA_sock_from_GR, UdpDataPort_fromGR, &GRdata_rxdata, &keeprunning);
printf("QO100modem initialised and running\n");
while (keeprunning)
{
if (restart_modems == 1)
{
startModem();
restart_modems = 0;
}
//doArraySend();
if (demodulator() == 0)
sleep_ms(100);
}
printf("stopped: %d\n", keeprunning);
#ifdef _LINUX_
close(BC_sock_AppToModem);
#endif
#ifdef _WIN32_
closesocket(BC_sock_AppToModem);
#endif
return 0;
}
typedef struct {
int audio;
int tx;
int rx;
int bpsym;
} SPEEDRATE;
SPEEDRATE sr[10] = {
// QPSK modes
{48000, 32, 8, 2}, // AudioRate, TX-Resampler, RX-Resampler/4, bit/symbol
{44100, 28, 7, 2}, // see samprate.ods
{44100, 24, 6, 2},
{48000, 24, 6, 2},
{44100, 20, 5, 2},
{48000, 20, 5, 2},
// 8PSK modes
{44100, 24, 6, 3},
{48000, 24, 6, 3},
{44100, 20, 5, 3},
{48000, 20, 5, 3}
};
void startModem()
{
bitsPerSymbol = sr[speedmode].bpsym;
constellationSize = (1 << bitsPerSymbol); // QPSK=4, 8PSK=8
caprate = sr[speedmode].audio;
txinterpolfactor = sr[speedmode].tx;
rxPreInterpolfactor = sr[speedmode].rx;
// int TX audio and modulator
close_dsp();
init_audio(playbackDeviceNo, captureDeviceNo);
init_dsp();
}
void setAudioDevices(int pb, int cap)
{
//printf("%d %d\n", pb, cap);
if (pb != playbackDeviceNo || cap != captureDeviceNo)
{
restart_modems = 1;
playbackDeviceNo = pb;
captureDeviceNo = cap;
}
}
// called from UDP RX thread for Broadcast-search from App
void bc_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
{
if (len > 0 && pdata[0] == 0x3c)
{
setAudioDevices(pdata[1], pdata[2]);
char rxip[20];
strcpy(rxip, inet_ntoa(rxsock->sin_addr));
if (fixappIP == 0)
{
if (strcmp(appIP, rxip))
{
printf("new app IP: %s, restarting modems\n", rxip);
restart_modems = 1;
}
strcpy(appIP, rxip);
//printf("app (%s) is searching modem. Sending modem IP to the app\n",appIP);
// App searches for the modem IP, mirror the received messages
// so the app gets an UDP message with this local IP
int alen;
uint8_t* txdata = getAudioDevicelist(&alen);
sendUDP(appIP, UdpDataPort_ModemToApp, txdata, alen);
}
else
{
// appIP is fixed, answer only to this IP
if (!strcmp(appIP, rxip))
{
//printf("app (%s) is searching modem. Sending modem IP to the app\n",appIP);
restart_modems = 1;
// App searches for the modem IP, mirror the received messages
// so the app gets an UDP message with this local IP
int alen;
uint8_t* txdata = getAudioDevicelist(&alen);
sendUDP(appIP, UdpDataPort_ModemToApp, txdata, alen);
}
}
}
}
// called by UDP RX thread for data from App
void appdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
{
uint8_t type = pdata[0];
uint8_t minfo = pdata[1];
if (len != (PAYLOADLEN + 2))
{
printf("data from app: wrong length:%d (should be %d)\n", len - 2, PAYLOADLEN);
return;
}
// type values: see oscardata config.cs: frame types
if (type == 16)
{
// Byte 1 contains the resampler ratio for TX and RX modem
speedmode = pdata[1];
printf("set speedmode to %d\n", speedmode);
restart_modems = 1;
return;
}
if (type == 17)
{
// auto send file
// TODO
// for testing only:
// simulate sending a text file with 1kB length
/*int testlen = 100000;
uint8_t arr[100000];
char c = 'A';
for (int i = 0; i < testlen; i++)
{
arr[i] = c;
if (++c > 'Z') c = 'A';
}
arraySend(arr, testlen, 3, (char*)"testfile.txt");*/
return;
}
if (type == 18)
{
// auto send folder
// TODO
}
if (type == 19)
{
// shut down this modem PC
int r = system("sudo shutdown now");
exit(r);
}
if (type == 20)
{
// reset liquid RX modem
resetModem();
}
//if (getSending() == 1) return; // already sending (Array sending)
if (minfo == 0)
{
// this is the first frame of a larger file
// send it multiple times, like a preamble, to give the
// receiver some time for synchronisation
// duration: 3 seconds
// caprate: samples/s. This are symbols: caprate/txinterpolfactor
// and bits: symbols * bitsPerSymbol
// and bytes/second: bits/8 = (caprate/txinterpolfactor) * bitsPerSymbol / 8
// one frame has 258 bytes, so we need for 5s: 5* ((caprate/txinterpolfactor) * bitsPerSymbol / 8) /258 + 1 frames
int numframespreamble = 3 * ((caprate / txinterpolfactor) * bitsPerSymbol / 8) / 258 + 1;
for (int i = 0; i < numframespreamble; i++)
toGR_sendData(pdata + 2, type, minfo);
}
else if ((len - 2) < PAYLOADLEN)
{
// if not enough data for a full payload add Zeros
uint8_t payload[PAYLOADLEN];
memset(payload, 0, PAYLOADLEN);
memcpy(payload, pdata + 2, len - 2);
toGR_sendData(payload, type, minfo);
}
else
{
toGR_sendData(pdata + 2, type, minfo);
}
}
void toGR_sendData(uint8_t* data, int type, int status)
{
int len = 0;
uint8_t* txdata = Pack(data, type, status, &len);
//showbytestring((char *)"BERtx: ", txdata, len);
if (txdata != NULL)
sendToModulator(txdata, len);
}
// called by UDP RX thread or liquid demodulator for received data
void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock)
{
static int fnd = 0;
// raw symbols
uint8_t* pl = unpack_data(pdata, len);
if (pl != NULL)
{
// complete frame received
// send payload to app
uint8_t txpl[PAYLOADLEN + 10 + 1];
memcpy(txpl + 1, pl, PAYLOADLEN + 10);
txpl[0] = 1; // type 1: payload data follows
sendUDP(appIP, UdpDataPort_ModemToApp, txpl, PAYLOADLEN + 10 + 1);
fnd = 0;
}
else
{
// no frame found
// if longer ws seconds nothing found, reset liquid RX modem
// comes here with symbol rate, i.e. 4000 S/s
int ws = 2;
int wt = sr[speedmode].audio / sr[speedmode].tx;
if (++fnd >= (wt * ws))
{
fnd = 0;
//printf("no signal detected %d, reset RX modem\n", wt);
resetModem();
}
}
}

136
hsmodem/hsmodem.h Executable file
View File

@ -0,0 +1,136 @@
#ifdef _WIN32
#define _WIN32_
// ignore senseless warnings invented by M$ to confuse developers
#pragma warning( disable : 4091 )
#pragma warning( disable : 4003 )
#else
#define _LINUX_
#endif
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <wchar.h>
#ifdef _WIN32_
#include "Winsock2.h"
#include "io.h"
#include <Windows.h>
#include <iostream>
#include <process.h>
#include <Tlhelp32.h>
#include <winbase.h>
#include <Shlobj.h>
#define _USE_MATH_DEFINES
#include <math.h>
#pragma comment(lib, "bass.lib")
#pragma comment(lib, "libliquid.lib")
#pragma comment(lib, "fftw_lib/libfftw3-3.lib")
#endif
#ifdef _LINUX_
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <sys/file.h>
#include <pthread.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <pwd.h>
#include <math.h>
#endif
#include "bass.h"
#include "liquid.h"
#include "frameformat.h"
#include "fec.h"
#include "udp.h"
#define jpg_tempfilename "rxdata.jpg"
#define CRC16TX 0
#define CRC16RX 1
#define CRC16FILE 2
void init_packer();
uint8_t* Pack(uint8_t* payload, int type, int status, int* plen);
uint8_t* unpack_data(uint8_t* rxd, int len);
void convertBytesToSyms_QPSK(uint8_t* bytes, uint8_t* syms, int bytenum);
void convertBytesToSyms_8PSK(uint8_t* bytes, uint8_t* syms, int bytenum);
uint8_t* convertQPSKSymToBytes(uint8_t* rxsymbols);
uint8_t* convert8PSKSymToBytes(uint8_t* rxsymbols, int len);
void rotateQPSKsyms(uint8_t* src, uint8_t* dst, int len);
void rotate8PSKsyms(uint8_t* src, uint8_t* dst, int len);
void rotate8APSKsyms(uint8_t* src, uint8_t* dst, int len);
uint8_t* rotateBackQPSK(uint8_t* buf, int len, int rotations);
uint8_t* rotateBack8PSK(uint8_t* buf, int len, int rotations);
uint8_t* rotateBack8APSK(uint8_t* buf, int len, int rotations);
void TX_Scramble(uint8_t* data, int len);
uint8_t* RX_Scramble(uint8_t* data, int len);
uint16_t Crc16_messagecalc(int rxtx, uint8_t* data, int len);
void showbytestring(char* title, uint8_t* data, int anz);
void measure_speed(int len);
void initFEC();
void GetFEC(uint8_t* txblock, int len, uint8_t* destArray);
int cfec_Reconstruct(uint8_t* darr, uint8_t* destination);
int init_audio(int pbdev, int capdev);
int pb_fifo_freespace(int nolock);
void pb_write_fifo_clear();
void pb_write_fifo(float sample);
int cap_read_fifo(float* data);
uint8_t* getAudioDevicelist(int* len);
void sleep_ms(int ms);
void GRdata_rxdata(uint8_t* pdata, int len, struct sockaddr_in* rxsock);
void modulator(uint8_t sym_in);
void init_dsp();
int demodulator();
void sendToModulator(uint8_t* d, int len);
void resetModem();
void close_dsp();
void init_fft();
void exit_fft();
void showbytestringf(char* title, float* data, int anz);
uint16_t* make_waterfall(float fre, int* retlen);
extern int speedmode;
extern int bitsPerSymbol;
extern int constellationSize;
extern int speed;
extern int keeprunning;
extern int caprate;
extern int BC_sock_AppToModem;
extern int UdpDataPort_ModemToApp;
extern int txinterpolfactor;
extern int rxPreInterpolfactor;
extern char appIP[20];
#ifdef _LINUX_
int isRunning(char* prgname);
void install_signal_handler();
int isRunning(char* prgname);
#endif

247
hsmodem/hsmodem.vcxproj Executable file
View File

@ -0,0 +1,247 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="64bit|Win32">
<Configuration>64bit</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="64bit|x64">
<Configuration>64bit</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E6292FAA-E794-4107-BD89-2310BCDBC858}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>hsmodem</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
<ProjectName>hsmodem</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='64bit|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='64bit|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='64bit|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='64bit|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\WinRelease\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='64bit|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\..\Release\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='64bit|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='64bit|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='64bit|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>wsock32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="bass.h" />
<ClInclude Include="fftw3.h" />
<ClInclude Include="fftw_lib\fftw3.h" />
<ClInclude Include="frameformat.h" />
<ClInclude Include="hsmodem.h" />
<ClInclude Include="liquid.h" />
<ClInclude Include="udp.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="audio.cpp" />
<ClCompile Include="constellation.cpp" />
<ClCompile Include="crc16.cpp" />
<ClCompile Include="fec.cpp" />
<ClCompile Include="fft.cpp" />
<ClCompile Include="frame_packer.cpp" />
<ClCompile Include="hsmodem.cpp" />
<ClCompile Include="liquid_if.cpp" />
<ClCompile Include="main_helper.cpp" />
<ClCompile Include="scrambler.cpp" />
<ClCompile Include="speed.cpp" />
<ClCompile Include="udp.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

78
hsmodem/hsmodem.vcxproj.filters Executable file
View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="hsmodem.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="constellation.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="frame_packer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="crc16.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="scrambler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="speed.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main_helper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="fec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="audio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udp.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="fft.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="liquid_if.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="hsmodem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="liquid.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="frameformat.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="bass.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="udp.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fftw3.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fftw_lib\fftw3.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

7
hsmodem/hsmodem.vcxproj.user Executable file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LocalDebuggerEnvironment>$(LocalDebuggerEnvironment)</LocalDebuggerEnvironment>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

BIN
hsmodem/libliquid.lib Executable file

Binary file not shown.

8823
hsmodem/liquid.h Executable file

File diff suppressed because it is too large Load Diff

385
hsmodem/liquid_if.cpp Executable file
View File

@ -0,0 +1,385 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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.
*
* liquid_if.c ... functions using liquid-dsp
*
* liquid-dsp must be previously installed by running ./liquid-dsp-install (under linux)
*
*/
#include "hsmodem.h"
void modulator(uint8_t sym_in);
void init_demodulator();
void close_demodulator();
void init_modulator();
void close_modulator();
void init_dsp()
{
init_modulator();
pb_write_fifo_clear();
init_demodulator();
}
void close_dsp()
{
close_modulator();
close_demodulator();
}
modulation_scheme getMod()
{
if(bitsPerSymbol == 2)
return LIQUID_MODEM_QPSK;
//return LIQUID_MODEM_APSK4;
else
return LIQUID_MODEM_APSK8;
}
// =========== MODULATOR ==================================================
// modem objects
modem mod = NULL;
// NCOs for mixing baseband <-> 1500 Hz
#define FREQUENCY 1500
int type = LIQUID_NCO; // nco type
nco_crcf upnco = NULL;
// TX-Interpolator Filter Parameters
// 44100 input rate for 2205 Sym/s = 20
// change for other rates
firinterp_crcf TX_interpolator = NULL;
unsigned int k_SampPerSymb = 20; // 44100 / (4410/2)
unsigned int m_filterDelay_Symbols = 15; // not too short for good filter
float beta_excessBW = 0.2f; // filter excess bandwidth factor
float tau_FracSymbOffset = -0.2f; // fractional symbol offset
void init_modulator()
{
close_dsp();
printf("init TX modulator\n");
k_SampPerSymb = txinterpolfactor;
// create modulator
mod = modem_create(getMod());
// create NCO for upmixing to 1500 Hz
float RADIANS_PER_SAMPLE = ((2.0f*(float)M_PI*(float)FREQUENCY)/(float)caprate);
upnco = nco_crcf_create(LIQUID_NCO);
nco_crcf_set_phase(upnco, 0.0f);
nco_crcf_set_frequency(upnco, RADIANS_PER_SAMPLE);
// TX: Interpolator Filter
// compute delay
while (tau_FracSymbOffset < 0) tau_FracSymbOffset += 1.0f; // ensure positive tau
float g = k_SampPerSymb*tau_FracSymbOffset; // number of samples offset
int ds=(int)floorf(g); // additional symbol delay
float dt = (g - (float)ds); // fractional sample offset
// force dt to be in [0.5,0.5]
if (dt > 0.5f)
{
dt -= 1.0f;
ds++;
}
// calculate filter coeffs
unsigned int h_len_NumFilterCoeefs = 2 * k_SampPerSymb * m_filterDelay_Symbols + 1;
float h[1000];
if (h_len_NumFilterCoeefs >= 1000)
{
printf("h in h_len_NumFilterCoeefs too small\n");
return;
}
liquid_firdes_prototype( LIQUID_FIRFILT_RRC,
k_SampPerSymb,
m_filterDelay_Symbols,
beta_excessBW,
dt,
h);
// create the filter
TX_interpolator = firinterp_crcf_create(k_SampPerSymb,h,h_len_NumFilterCoeefs);
printf("DSP created\n");
return;
}
void close_modulator()
{
if(mod != NULL) modem_destroy(mod);
if(upnco != NULL) nco_crcf_destroy(upnco);
if(TX_interpolator != NULL) firinterp_crcf_destroy(TX_interpolator);
mod = NULL;
upnco = NULL;
TX_interpolator = NULL;
}
// d ... symbols to send
// len ... number of symbols in d
void sendToModulator(uint8_t *d, int len)
{
if(upnco == NULL) return;
int symanz = len * 8 / bitsPerSymbol;
uint8_t syms[10000];
if (symanz >= 10000)
{
printf("syms in symanz too small\n");
return;
}
if(bitsPerSymbol == 2)
convertBytesToSyms_QPSK(d, syms, len);
else
convertBytesToSyms_8PSK(d, syms, len);
for(int i=0; i<symanz; i++)
{
// remove gray code
// this adds gray code, liquid adds it again which removes it
if(bitsPerSymbol == 2) syms[i] ^= (syms[i]>>1);
modulator(syms[i]);
}
}
// call for every symbol
// modulates, filters and upmixes symbols and send it to soundcard
void modulator(uint8_t sym_in)
{
liquid_float_complex sample;
modem_modulate(mod, sym_in, &sample);
//printf("TX ================= sample: %f + i%f\n", sample.real, sample.imag);
// interpolate by k_SampPerSymb
liquid_float_complex y[100];
if (k_SampPerSymb >= 100)
{
printf("y in k_SampPerSymb too small\n");
return;
}
firinterp_crcf_execute(TX_interpolator, sample, y);
for(unsigned int i=0; i<k_SampPerSymb; i++)
{
// move sample to 1,5kHz carrier
nco_crcf_step(upnco);
liquid_float_complex c;
nco_crcf_mix_up(upnco,y[i],&c);
float usb = c.real + c.imag;
// adapt speed to soundcard samplerate
int fs;
while(1)
{
fs = pb_fifo_freespace(0);
if(fs) break;
sleep_ms(10);
}
pb_write_fifo(usb * 0.2f); // reduce volume and send to soundcard
}
}
// =========== DEMODULATOR =============================
nco_crcf dnnco = NULL;
symtrack_cccf symtrack = NULL;
modem demod = NULL;
firdecim_crcf decim = NULL;
// decimator parameters
unsigned int m_predec = 8; // filter delay
float As_predec = 40.0f; // stop-band att
// symtrack parameters
int ftype_st = LIQUID_FIRFILT_RRC;
unsigned int k_st = 4; // samples per symbol
unsigned int m_st = 7; // filter delay (symbols)
float beta_st = beta_excessBW;//0.30f; // filter excess bandwidth factor
float bandwidth_st = 0.7f; // loop filter bandwidth
void init_demodulator()
{
printf("init RX demodulator\n");
// downmixer oscillator
float RADIANS_PER_SAMPLE = ((2.0f*(float)M_PI*(float)FREQUENCY)/(float)caprate);
dnnco = nco_crcf_create(LIQUID_NCO);
nco_crcf_set_phase(dnnco, 0.0f);
nco_crcf_set_frequency(dnnco, RADIANS_PER_SAMPLE);
// create pre-decimator
decim = firdecim_crcf_create_kaiser(rxPreInterpolfactor, m_predec, As_predec);
firdecim_crcf_set_scale(decim, 1.0f/(float)rxPreInterpolfactor);
// create symbol tracking synchronizer
//k_st = txinterpolfactor;
symtrack = symtrack_cccf_create(ftype_st,k_st,m_st,beta_st,getMod());
symtrack_cccf_set_bandwidth(symtrack,bandwidth_st);
int ret = symtrack_cccf_set_eq_dd(symtrack);
if (ret != LIQUID_OK)
{
printf("symtrack_cccf_set_eq_dd failed\n");
}
// demodulator
demod = modem_create(getMod());
printf("RX demodulator running\n");
}
void close_demodulator()
{
if(symtrack != NULL) symtrack_cccf_destroy(symtrack);
if(demod != NULL) modem_destroy(demod);
if(decim != NULL) firdecim_crcf_destroy(decim);
symtrack = NULL;
demod = NULL;
decim = NULL;
}
void resetModem()
{
//printf("Reset Symtrack\n");
symtrack_cccf_reset(symtrack);
}
// called for Audio-Samples (FFT)
void make_FFTdata(float f)
{
// send IQ data to FFT for waterfall calculation
int fftlen = 0;
uint16_t* fft = make_waterfall(f, &fftlen);
if (fft != NULL)
{
uint8_t txpl[10000];
if (fftlen > (10000 * 2 + 1))
{
printf("GRdata_FFTdata: txpl too small !!!\n");
return;
}
int bidx = 0;
txpl[bidx++] = 4; // type 4: FFT data follows
for (int i = 0; i < fftlen; i++)
{
txpl[bidx++] = fft[i] >> 8;
txpl[bidx++] = fft[i] & 0xff;
}
sendUDP(appIP, UdpDataPort_ModemToApp, txpl, bidx);
}
}
int demodulator()
{
static liquid_float_complex ccol[100];
static int ccol_idx = 0;
if(dnnco == NULL) return 0;
// get one received sample
float f;
int ret = cap_read_fifo(&f);
if(ret == 0) return 0;
make_FFTdata(f*120);
// downconvert into baseband
// still at soundcard sample rate
nco_crcf_step(dnnco);
liquid_float_complex in;
in.real = f;
in.imag = f;
liquid_float_complex c;
nco_crcf_mix_down(dnnco,in,&c);
// c is the actual sample, converted to complex and shifted to baseband
// this is the first decimator. We need to collect rxPreInterpolfactor number of samples
// then call execute which will give us one decimated sample
ccol[ccol_idx++] = c;
if(ccol_idx < rxPreInterpolfactor) return 1;
ccol_idx = 0;
// we have rxPreInterpolfactor samples in ccol
liquid_float_complex y;
firdecim_crcf_execute(decim, ccol, &y);
unsigned int num_symbols_sync;
liquid_float_complex syms;
symtrack_cccf_execute(symtrack, y, &syms, &num_symbols_sync);
if(num_symbols_sync > 1) printf("symtrack_cccf_execute %d output symbols ???\n",num_symbols_sync);
if(num_symbols_sync != 0)
{
unsigned int sym_out; // output symbol
modem_demodulate(demod, syms, &sym_out);
measure_speed(1);
// try to extract a complete frame
uint8_t symb = sym_out;
if(bitsPerSymbol == 2) symb ^= (symb>>1);
GRdata_rxdata(&symb, 1, NULL);
// send the data "as is" to app for Constellation Diagram
// we have about 2000 S/s, but this many points would make the GUI slow
// so we send only every x
static int ev = 0;
//if (++ev >= 2)
{
ev = 0;
uint32_t re = (uint32_t)(syms.real * 16777216.0);
uint32_t im = (uint32_t)(syms.imag * 16777216.0);
uint8_t txpl[13];
int idx = 0;
txpl[idx++] = 5; // type 5: IQ data follows
uint32_t sy = 0x3e8;
txpl[idx++] = sy >> 24;
txpl[idx++] = sy >> 16;
txpl[idx++] = sy >> 8;
txpl[idx++] = sy;
txpl[idx++] = re >> 24;
txpl[idx++] = re >> 16;
txpl[idx++] = re >> 8;
txpl[idx++] = re;
txpl[idx++] = im >> 24;
txpl[idx++] = im >> 16;
txpl[idx++] = im >> 8;
txpl[idx++] = im;
sendUDP(appIP, UdpDataPort_ModemToApp, txpl, 13);
}
}
return 1;
}

107
hsmodem/main_helper.cpp Executable file
View File

@ -0,0 +1,107 @@
/*
* main_helper
* ===========
* by DJ0ABR
*
* functions useful for every main() program
*
* */
#include "hsmodem.h"
#ifdef _LINUX_
// check if it is already running
int isRunning(char *prgname)
{
int num = 0;
char s[256];
sprintf(s,"ps -e | grep %s",prgname);
FILE *fp = popen(s,"r");
if(fp)
{
// gets the output of the system command
while (fgets(s, sizeof(s)-1, fp) != NULL)
{
if(strstr(s,prgname) && !strstr(s,"grep"))
{
if(++num == 2)
{
printf("%s is already running, do not start twice !",prgname);
pclose(fp);
return 1;
}
}
}
pclose(fp);
}
return 0;
}
// signal handler
void sighandler(int signum)
{
printf("program stopped by signal\n");
exit_fft();
keeprunning = 0;
close(BC_sock_AppToModem);
}
void install_signal_handler()
{
// signal handler, mainly used if the user presses Ctrl-C
struct sigaction sigact;
sigact.sa_handler = sighandler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGABRT, &sigact, NULL); // assert() error
//sigaction(SIGSEGV, &sigact, NULL);
// switch off signal 13 (broken pipe)
// instead handle the return value of the write or send function
signal(SIGPIPE, SIG_IGN);
}
#endif // _LINUX_
void showbytestring(char *title, uint8_t *data, int anz)
{
printf("%s. Len %d: ",title,anz);
for(int i=0; i<anz; i++)
printf("%02X ",data[i]);
printf("\n");
}
void showbytestring16(char *title, uint16_t *data, int anz)
{
printf("%s. Len %d: ",title,anz);
for(int i=0; i<anz; i++)
printf("%04X ",data[i]);
printf("\n");
}
void showbytestringf(char* title, float* data, int anz)
{
printf("%s. Len %d: ", title, anz);
for (int i = 0; i < anz; i++)
printf("%.6f ", data[i]);
printf("\n");
}
#ifdef _LINUX_
void sleep_ms(int ms)
{
usleep(ms * 1000);
}
#endif
#ifdef _WIN32_
void sleep_ms(int ms)
{
Sleep(ms);
}
#endif

91
hsmodem/scrambler.cpp Executable file
View File

@ -0,0 +1,91 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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 "hsmodem.h"
uint8_t scr[400] = {
130 , 239 , 223 , 19 , 146 , 254 , 12 , 86 , 106 , 68 ,
77 , 213 , 243 , 216 , 102 , 227 , 108 , 113 , 229 , 89 ,
26 , 64 , 138 , 216 , 225 , 121 , 194 , 137 , 152 , 64 ,
51 , 175 , 68 , 200 , 37 , 104 , 247 , 68 , 193 , 50 ,
19 , 14 , 196 , 81 , 4 , 236 , 191 , 249 , 83 , 25 ,
161 , 171 , 167 , 29 , 33 , 139 , 7 , 152 , 230 , 144 ,
125 , 206 , 34 , 236 , 112 , 78 , 219 , 34 , 181 , 161 ,
7 , 45 , 198 , 235 , 62 , 115 , 194 , 100 , 209 , 95 ,
186 , 161 , 53 , 10 , 110 , 246 , 122 , 246 , 207 , 194 ,
178 , 63 , 232 , 93 , 158 , 234 , 231 , 73 , 214 , 64,
130 , 239 , 223 , 19 , 146 , 254 , 12 , 86 , 106 , 68 ,
77 , 213 , 243 , 216 , 102 , 227 , 108 , 113 , 229 , 89 ,
26 , 64 , 138 , 216 , 225 , 121 , 194 , 137 , 152 , 64 ,
51 , 175 , 68 , 200 , 37 , 104 , 247 , 68 , 193 , 50 ,
19 , 14 , 196 , 81 , 4 , 236 , 191 , 249 , 83 , 25 ,
161 , 171 , 167 , 29 , 33 , 139 , 7 , 152 , 230 , 144 ,
125 , 206 , 34 , 236 , 112 , 78 , 219 , 34 , 181 , 161 ,
7 , 45 , 198 , 235 , 62 , 115 , 194 , 100 , 209 , 95 ,
186 , 161 , 53 , 10 , 110 , 246 , 122 , 246 , 207 , 194 ,
178 , 63 , 232 , 93 , 158 , 234 , 231 , 73 , 214 , 64,
130 , 239 , 223 , 19 , 146 , 254 , 12 , 86 , 106 , 68 ,
77 , 213 , 243 , 216 , 102 , 227 , 108 , 113 , 229 , 89 ,
26 , 64 , 138 , 216 , 225 , 121 , 194 , 137 , 152 , 64 ,
51 , 175 , 68 , 200 , 37 , 104 , 247 , 68 , 193 , 50 ,
19 , 14 , 196 , 81 , 4 , 236 , 191 , 249 , 83 , 25 ,
161 , 171 , 167 , 29 , 33 , 139 , 7 , 152 , 230 , 144 ,
125 , 206 , 34 , 236 , 112 , 78 , 219 , 34 , 181 , 161 ,
7 , 45 , 198 , 235 , 62 , 115 , 194 , 100 , 209 , 95 ,
186 , 161 , 53 , 10 , 110 , 246 , 122 , 246 , 207 , 194 ,
178 , 63 , 232 , 93 , 158 , 234 , 231 , 73 , 214 , 64,
130 , 239 , 223 , 19 , 146 , 254 , 12 , 86 , 106 , 68 ,
77 , 213 , 243 , 216 , 102 , 227 , 108 , 113 , 229 , 89 ,
26 , 64 , 138 , 216 , 225 , 121 , 194 , 137 , 152 , 64 ,
51 , 175 , 68 , 200 , 37 , 104 , 247 , 68 , 193 , 50 ,
19 , 14 , 196 , 81 , 4 , 236 , 191 , 249 , 83 , 25 ,
161 , 171 , 167 , 29 , 33 , 139 , 7 , 152 , 230 , 144 ,
125 , 206 , 34 , 236 , 112 , 78 , 219 , 34 , 181 , 161 ,
7 , 45 , 198 , 235 , 62 , 115 , 194 , 100 , 209 , 95 ,
186 , 161 , 53 , 10 , 110 , 246 , 122 , 246 , 207 , 194 ,
178 , 63 , 232 , 93 , 158 , 234 , 231 , 73 , 214 , 64
};
uint8_t rx_scrbuf[400];
void TX_Scramble(uint8_t *data, int len)
{
if (len > 400) return;
for(int i=0; i<len; i++)
data[i] ^= scr[i];
}
uint8_t *RX_Scramble(uint8_t *data, int len)
{
if (len > 400) return data;
memcpy(rx_scrbuf,data,len);
for(int i=0; i<len; i++)
rx_scrbuf[i] ^= scr[i];
return rx_scrbuf;
}

110
hsmodem/speed.cpp Executable file
View File

@ -0,0 +1,110 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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 "hsmodem.h"
int speed = 0;
#define MAXSPDARR 5
int spdarr[MAXSPDARR];
#ifdef _LINUX_
int getus()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000 + tv.tv_usec;
}
#endif
#ifdef _WIN32_
int getus()
{
int actms;
SYSTEMTIME st;
GetSystemTime(&st);
actms = st.wSecond * 1000 + (int)st.wMilliseconds;
return actms * 1000;
}
#endif
int meanval(int v)
{
static int f=1;
if(f)
{
for(int i=0; i<MAXSPDARR; i++) spdarr[i] = -1;
f=0;
}
for(int i=(MAXSPDARR-1); i>0; i--)
spdarr[i] = spdarr[i-1];
spdarr[0] = v;
int ssum=0;
int cnt = 0;
for(int i=0; i<MAXSPDARR; i++)
{
if(spdarr[i] != -1)
{
ssum += spdarr[i];
cnt++;
}
}
ssum /= cnt;
return ssum;
}
// len ... number of symbols
// measures and calculates the speed in bit / s
void measure_speed(int len)
{
static int lasttim = 0;
static int elems = 0;
int tim = getus();
int timespan = tim - lasttim;
if(timespan < 0)
{
lasttim = tim;
return;
}
elems += len;
if(timespan < 2000000) return;
double dspd = elems;
dspd = dspd * 1e6 / timespan;
speed = meanval((int)dspd) * bitsPerSymbol;
// here we have number of elements after 1s
//printf("%d items/s\n",speed);
elems=0;
lasttim = tim;
}

162
hsmodem/udp.cpp Executable file
View File

@ -0,0 +1,162 @@
/*
* High Speed modem to transfer data in a 2,7kHz SSB channel
* =========================================================
* Author: DJ0ABR
*
* (c) DJ0ABR
* www.dj0abr.de
*
* 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 "hsmodem.h"
#ifdef _LINUX_
void* threadfunction(void* param);
#endif
#ifdef _WIN32_
void threadfunction(void* param);
#endif
#define MAXUDPTHREADS 20
RXCFG rxcfg[MAXUDPTHREADS];
int rxcfg_idx = 0;
// start UDP reception
// sock ... pointer to a socket (just a pointer to an int)
// port ... own port, messages only to this port are received
// rxfunc ... pointer to a callback function, will be called for received data
// keeprunning ... pointer to an int. If it is set to 0, the function exits
void UdpRxInit(int *sock, int port, void (*rxfunc)(uint8_t *, int, struct sockaddr_in*), int *keeprunning)
{
if(rxcfg_idx >= MAXUDPTHREADS)
{
printf("max number of UDP threads\n");
exit(0);
}
rxcfg[rxcfg_idx].sock = sock;
rxcfg[rxcfg_idx].port = port;
rxcfg[rxcfg_idx].rxfunc = rxfunc;
rxcfg[rxcfg_idx].keeprunning = keeprunning;
// bind port
struct sockaddr_in sin;
#ifdef _WIN32_
WSADATA wsaData = { 0 };
int ires = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (ires != 0)
printf("WSAStartup failed: %d\n", ires);
#endif
*sock = socket(PF_INET, SOCK_DGRAM, 0);
if (*sock == -1){
printf("Failed to create Socket\n");
exit(0);
}
char enable = 1;
setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = INADDR_ANY;
if (bind(*sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) != 0)
{
printf("Failed to bind socket, port:%d\n",port);
#ifdef _LINUX_
close(*sock);
#endif
#ifdef _WIN32_
closesocket(*sock);
#endif
exit(0);
}
printf("port %d sucessfully bound\n", port);
// port sucessfully bound
// create the receive thread
#ifdef _LINUX_
pthread_t rxthread;
pthread_create(&rxthread, NULL, threadfunction, &(rxcfg[rxcfg_idx]));
#endif
#ifdef _WIN32_
_beginthread(threadfunction, 0, &(rxcfg[rxcfg_idx]));
#endif
rxcfg_idx++;
}
#ifdef _LINUX_
void* threadfunction(void* param) {
socklen_t fromlen;
#endif
#ifdef _WIN32_
void threadfunction(void* param) {
int fromlen;
#endif
RXCFG rxcfg;
memcpy((uint8_t *)(&rxcfg), (uint8_t *)param, sizeof(RXCFG));
int recvlen;
char rxbuf[256];
struct sockaddr_in fromSock;
fromlen = sizeof(struct sockaddr_in);
while(*rxcfg.keeprunning)
{
recvlen = recvfrom(*rxcfg.sock, rxbuf, 256, 0, (struct sockaddr *)&fromSock, &fromlen);
if (recvlen > 0)
{
// data received, send it to callback function
(*rxcfg.rxfunc)((uint8_t *)rxbuf,recvlen, &fromSock);
}
}
#ifdef _LINUX_
return NULL;
#endif
}
// send UDP message
void sendUDP(char *destIP, int destPort, uint8_t *pdata, int len)
{
int sockfd;
struct sockaddr_in servaddr;
//printf("%d %d %02X\n",destPort,len,pdata[0]);
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
printf("sendUDP: socket creation failed\n");
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(destPort);
//printf("Send to <%s><%d> Len:%d\n",destIP,destPort,len);
servaddr.sin_addr.s_addr=inet_addr(destIP);
sendto(sockfd, (char *)pdata, len, 0, (const struct sockaddr *) &servaddr, sizeof(servaddr));
#ifdef _LINUX_
close(sockfd);
#endif
#ifdef _WIN32_
closesocket(sockfd);
#endif
}

9
hsmodem/udp.h Normal file
View File

@ -0,0 +1,9 @@
void UdpRxInit(int *sock, int port, void (*rxfunc)(uint8_t *, int, struct sockaddr_in*), int *keeprunning);
void sendUDP(char *destIP, int destPort, uint8_t *pdata, int len);
typedef struct {
int *sock;
int port;
void (*rxfunc)(uint8_t *, int, struct sockaddr_in*);
int *keeprunning;
} RXCFG;

Binary file not shown.

View File

@ -71,7 +71,14 @@
this.bt_file_html = new System.Windows.Forms.Button();
this.bt_file_ascii = new System.Windows.Forms.Button();
this.tabPage5 = new System.Windows.Forms.TabPage();
this.textBox1 = new System.Windows.Forms.TextBox();
this.textBox3 = new System.Windows.Forms.TextBox();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.cb_audioCAP = new System.Windows.Forms.ComboBox();
this.label3 = new System.Windows.Forms.Label();
this.cb_audioPB = new System.Windows.Forms.ComboBox();
this.bt_resetmodem = new System.Windows.Forms.Button();
this.tb_shutdown = new System.Windows.Forms.TextBox();
this.bt_shutdown = new System.Windows.Forms.Button();
this.cb_savegoodfiles = new System.Windows.Forms.CheckBox();
this.cb_stampcall = new System.Windows.Forms.CheckBox();
@ -80,6 +87,9 @@
this.cb_speed = new System.Windows.Forms.ComboBox();
this.label_speed = new System.Windows.Forms.Label();
this.timer_searchmodem = new System.Windows.Forms.Timer(this.components);
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.groupBox4 = new System.Windows.Forms.GroupBox();
this.statusStrip1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.tabPage2.SuspendLayout();
@ -89,6 +99,9 @@
this.tabControl1.SuspendLayout();
this.tabPage3.SuspendLayout();
this.tabPage5.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox3.SuspendLayout();
this.groupBox4.SuspendLayout();
this.SuspendLayout();
//
// timer_udpTX
@ -498,12 +511,9 @@
//
// tabPage5
//
this.tabPage5.Controls.Add(this.textBox1);
this.tabPage5.Controls.Add(this.bt_shutdown);
this.tabPage5.Controls.Add(this.cb_savegoodfiles);
this.tabPage5.Controls.Add(this.cb_stampcall);
this.tabPage5.Controls.Add(this.tb_callsign);
this.tabPage5.Controls.Add(this.label1);
this.tabPage5.Controls.Add(this.groupBox4);
this.tabPage5.Controls.Add(this.groupBox3);
this.tabPage5.Controls.Add(this.groupBox2);
this.tabPage5.Location = new System.Drawing.Point(4, 22);
this.tabPage5.Name = "tabPage5";
this.tabPage5.Size = new System.Drawing.Size(1291, 553);
@ -511,22 +521,91 @@
this.tabPage5.Text = "Setup";
this.tabPage5.UseVisualStyleBackColor = true;
//
// textBox1
// textBox3
//
this.textBox1.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.textBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.textBox1.ForeColor = System.Drawing.Color.Red;
this.textBox1.Location = new System.Drawing.Point(379, 78);
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(259, 55);
this.textBox1.TabIndex = 5;
this.textBox1.Text = "before switching off the modem SBC\r\nclick here to avoid defective SD-cards.\r\nWAIT" +
" 1 minute before powering OFF the modem.";
this.textBox3.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.textBox3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.textBox3.ForeColor = System.Drawing.Color.Black;
this.textBox3.Location = new System.Drawing.Point(138, 73);
this.textBox3.Multiline = true;
this.textBox3.Name = "textBox3";
this.textBox3.Size = new System.Drawing.Size(177, 19);
this.textBox3.TabIndex = 12;
this.textBox3.Text = "(HDMI is usually not used)";
//
// textBox2
//
this.textBox2.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.textBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.textBox2.ForeColor = System.Drawing.Color.Black;
this.textBox2.Location = new System.Drawing.Point(189, 48);
this.textBox2.Multiline = true;
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(126, 50);
this.textBox2.TabIndex = 11;
this.textBox2.Text = "in case the RX has sync\r\nproblems, it can be\r\nre-initialized here.";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(12, 50);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(112, 13);
this.label4.TabIndex = 10;
this.label4.Text = "Audio Record Device:";
//
// cb_audioCAP
//
this.cb_audioCAP.FormattingEnabled = true;
this.cb_audioCAP.Location = new System.Drawing.Point(138, 46);
this.cb_audioCAP.Name = "cb_audioCAP";
this.cb_audioCAP.Size = new System.Drawing.Size(230, 21);
this.cb_audioCAP.TabIndex = 9;
this.cb_audioCAP.Text = "Default";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 23);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(121, 13);
this.label3.TabIndex = 8;
this.label3.Text = "Audio Playback Device:";
//
// cb_audioPB
//
this.cb_audioPB.FormattingEnabled = true;
this.cb_audioPB.Location = new System.Drawing.Point(138, 19);
this.cb_audioPB.Name = "cb_audioPB";
this.cb_audioPB.Size = new System.Drawing.Size(230, 21);
this.cb_audioPB.TabIndex = 7;
this.cb_audioPB.Text = "Default";
//
// bt_resetmodem
//
this.bt_resetmodem.Location = new System.Drawing.Point(189, 19);
this.bt_resetmodem.Name = "bt_resetmodem";
this.bt_resetmodem.Size = new System.Drawing.Size(117, 23);
this.bt_resetmodem.TabIndex = 6;
this.bt_resetmodem.Text = "Reset RX Modem";
this.bt_resetmodem.UseVisualStyleBackColor = true;
this.bt_resetmodem.Click += new System.EventHandler(this.bt_resetmodem_Click);
//
// tb_shutdown
//
this.tb_shutdown.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.tb_shutdown.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.tb_shutdown.ForeColor = System.Drawing.Color.Red;
this.tb_shutdown.Location = new System.Drawing.Point(17, 48);
this.tb_shutdown.Multiline = true;
this.tb_shutdown.Name = "tb_shutdown";
this.tb_shutdown.Size = new System.Drawing.Size(155, 50);
this.tb_shutdown.TabIndex = 5;
this.tb_shutdown.Text = "before switching off the \r\nmodem SBC click here to \r\navoid defective SD-cards.\r\n";
//
// bt_shutdown
//
this.bt_shutdown.Location = new System.Drawing.Point(379, 49);
this.bt_shutdown.Location = new System.Drawing.Point(17, 19);
this.bt_shutdown.Name = "bt_shutdown";
this.bt_shutdown.Size = new System.Drawing.Size(155, 23);
this.bt_shutdown.TabIndex = 4;
@ -539,7 +618,7 @@
this.cb_savegoodfiles.AutoSize = true;
this.cb_savegoodfiles.Checked = true;
this.cb_savegoodfiles.CheckState = System.Windows.Forms.CheckState.Checked;
this.cb_savegoodfiles.Location = new System.Drawing.Point(106, 136);
this.cb_savegoodfiles.Location = new System.Drawing.Point(71, 90);
this.cb_savegoodfiles.Name = "cb_savegoodfiles";
this.cb_savegoodfiles.Size = new System.Drawing.Size(159, 17);
this.cb_savegoodfiles.TabIndex = 3;
@ -551,7 +630,7 @@
this.cb_stampcall.AutoSize = true;
this.cb_stampcall.Checked = true;
this.cb_stampcall.CheckState = System.Windows.Forms.CheckState.Checked;
this.cb_stampcall.Location = new System.Drawing.Point(106, 113);
this.cb_stampcall.Location = new System.Drawing.Point(71, 67);
this.cb_stampcall.Name = "cb_stampcall";
this.cb_stampcall.Size = new System.Drawing.Size(146, 17);
this.cb_stampcall.TabIndex = 2;
@ -561,7 +640,7 @@
// tb_callsign
//
this.tb_callsign.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper;
this.tb_callsign.Location = new System.Drawing.Point(106, 49);
this.tb_callsign.Location = new System.Drawing.Point(71, 28);
this.tb_callsign.Name = "tb_callsign";
this.tb_callsign.Size = new System.Drawing.Size(151, 20);
this.tb_callsign.TabIndex = 1;
@ -569,7 +648,7 @@
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(49, 52);
this.label1.Location = new System.Drawing.Point(14, 31);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(46, 13);
this.label1.TabIndex = 0;
@ -579,19 +658,21 @@
//
this.cb_speed.FormattingEnabled = true;
this.cb_speed.Items.AddRange(new object[] {
"3000 QPSK BW: 1800 Hz ",
"3150 QPSK BW: 1900 Hz ",
"3675 QPSK BW: 2200 Hz ",
"3000 QPSK BW: 1700 Hz ",
"3150 QPSK BW: 1800 Hz ",
"3675 QPSK BW: 2100 Hz ",
"4000 QPSK BW: 2400 Hz ",
"4410 QPSK BW: 2700 Hz (default QO-100)",
"4800 QPSK BW: 2900 Hz (experimental)",
"4410 QPSK BW: 2500 Hz (QO-100)",
"4800 QPSK BW: 2700 Hz",
"5500 8PSK BW: 2300 Hz",
"6000 8PSK BW: 2500 Hz (QO-100 beacon)"});
"6000 8PSK BW: 2500 Hz (QO-100)",
"6600 8PSK BW: 2600 Hz",
"7200 8PSK BW: 2700 Hz"});
this.cb_speed.Location = new System.Drawing.Point(636, 644);
this.cb_speed.Name = "cb_speed";
this.cb_speed.Size = new System.Drawing.Size(324, 21);
this.cb_speed.TabIndex = 11;
this.cb_speed.Text = "4410 QPSK BW: 2700 Hz (default QO-100)";
this.cb_speed.Text = "4410 QPSK BW: 2500 Hz (QO-100)";
this.cb_speed.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
//
// label_speed
@ -608,6 +689,46 @@
this.timer_searchmodem.Interval = 1000;
this.timer_searchmodem.Tick += new System.EventHandler(this.timer_searchmodem_Tick);
//
// groupBox2
//
this.groupBox2.Controls.Add(this.tb_callsign);
this.groupBox2.Controls.Add(this.label1);
this.groupBox2.Controls.Add(this.cb_stampcall);
this.groupBox2.Controls.Add(this.cb_savegoodfiles);
this.groupBox2.Location = new System.Drawing.Point(12, 13);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(384, 126);
this.groupBox2.TabIndex = 13;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Personal Settings";
//
// groupBox3
//
this.groupBox3.Controls.Add(this.cb_audioPB);
this.groupBox3.Controls.Add(this.label3);
this.groupBox3.Controls.Add(this.textBox3);
this.groupBox3.Controls.Add(this.cb_audioCAP);
this.groupBox3.Controls.Add(this.label4);
this.groupBox3.Location = new System.Drawing.Point(12, 146);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(384, 107);
this.groupBox3.TabIndex = 14;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "Transceiver Audio";
//
// groupBox4
//
this.groupBox4.Controls.Add(this.bt_shutdown);
this.groupBox4.Controls.Add(this.tb_shutdown);
this.groupBox4.Controls.Add(this.bt_resetmodem);
this.groupBox4.Controls.Add(this.textBox2);
this.groupBox4.Location = new System.Drawing.Point(12, 259);
this.groupBox4.Name = "groupBox4";
this.groupBox4.Size = new System.Drawing.Size(384, 105);
this.groupBox4.TabIndex = 15;
this.groupBox4.TabStop = false;
this.groupBox4.Text = "Maintenance";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -622,7 +743,7 @@
this.ForeColor = System.Drawing.SystemColors.ControlText;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "Form1";
this.Text = "QO-100 NB Transponder HS Transmission AMSAT-DL V0.1 by DJ0ABR";
this.Text = "QO-100 NB Transponder HS Transmission AMSAT-DL V0.2 by DJ0ABR";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
@ -637,7 +758,12 @@
this.tabPage3.ResumeLayout(false);
this.tabPage3.PerformLayout();
this.tabPage5.ResumeLayout(false);
this.tabPage5.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.groupBox3.ResumeLayout(false);
this.groupBox3.PerformLayout();
this.groupBox4.ResumeLayout(false);
this.groupBox4.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@ -693,8 +819,18 @@
private System.Windows.Forms.Label label1;
private System.Windows.Forms.CheckBox cb_stampcall;
private System.Windows.Forms.CheckBox cb_savegoodfiles;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox tb_shutdown;
private System.Windows.Forms.Button bt_shutdown;
private System.Windows.Forms.Button bt_resetmodem;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.ComboBox cb_audioCAP;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.ComboBox cb_audioPB;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.GroupBox groupBox4;
private System.Windows.Forms.GroupBox groupBox3;
private System.Windows.Forms.GroupBox groupBox2;
}
}

View File

@ -52,7 +52,11 @@ namespace oscardata
OperatingSystem osversion = System.Environment.OSVersion;
statics.OSversion = osversion.Platform.ToString();
if (osversion.VersionString.Contains("indow"))
{
statics.ostype = 0;
tb_shutdown.Visible = false;
bt_shutdown.Visible = false;
}
else
statics.ostype = 1; // Linux
@ -90,8 +94,8 @@ namespace oscardata
{
if (Udp.GetBufferCount() > 3) return;
Byte[] txdata = new byte[statics.PayloadLen+2];
Byte[] txdata = new byte[statics.PayloadLen + 2];
txdata[0] = (Byte)statics.BERtest; // BER Test Marker
txdata[1] = frameinfo;
@ -173,6 +177,22 @@ namespace oscardata
comboBox1_SelectedIndexChanged(null, null); // send speed to modem
}
}
if (statics.GotAudioDevices == 1)
{
statics.GotAudioDevices = 2;
// populate combo boxes
foreach (String s in statics.AudioPBdevs)
{
if(s.Length > 1)
cb_audioPB.Items.Add(s);
}
foreach (String s in statics.AudioCAPdevs)
{
if (s.Length > 1)
cb_audioCAP.Items.Add(s);
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
@ -188,6 +208,7 @@ namespace oscardata
int speed;
int tmpnum = 0;
int file_lostframes = 0;
int last_fileid = 0;
private void timer_udprx_Tick(object sender, EventArgs e)
{
while (true)
@ -246,7 +267,11 @@ namespace oscardata
//Console.WriteLine("first, single");
rxdata = ArraySend.GetAndRemoveHeader(rxdata);
if (rxdata == null) return;
if (last_fileid == ArraySend.FileID) return; // got first frame for this ID already
last_fileid = ArraySend.FileID;
}
else
last_fileid = 0;
// collect all received data into zip_RXtempfilename
Byte[] ba = null;
@ -280,6 +305,11 @@ namespace oscardata
// reduce for the real file length
Byte[] fc = File.ReadAllBytes(statics.zip_RXtempfilename);
Byte[] fdst = new byte[ArraySend.FileSize];
if(fc.Length < ArraySend.FileSize)
{
Console.WriteLine("len=" + fc.Length + " fz=" + ArraySend.FileSize);
return;
}
Array.Copy(fc, 0, fdst, 0, ArraySend.FileSize);
File.WriteAllBytes(statics.zip_RXtempfilename, fdst);
@ -330,8 +360,11 @@ namespace oscardata
if (minfo == statics.FirstFrame)
{
rxdata = ArraySend.GetAndRemoveHeader(rxdata);
if (rxdata == null) return;
if (last_fileid == ArraySend.FileID) return; // got first frame for this ID already
last_fileid = ArraySend.FileID;
}
else
last_fileid = 0;
Byte[] ba = null;
Byte[] nba;
@ -410,8 +443,11 @@ namespace oscardata
{
//Console.WriteLine("first, single");
rxdata = ArraySend.GetAndRemoveHeader(rxdata);
if (rxdata == null) return;
if (last_fileid == ArraySend.FileID) return; // got first frame for this ID already
last_fileid = ArraySend.FileID;
}
else
last_fileid = 0;
// collect all received data into zip_RXtempfilename
Byte[] ba = null;
@ -445,6 +481,12 @@ namespace oscardata
// reduce for the real file length
Byte[] fc = File.ReadAllBytes(statics.zip_RXtempfilename);
Byte[] fdst = new byte[ArraySend.FileSize];
if (fc.Length < ArraySend.FileSize)
{
Console.WriteLine("len=" + fc.Length + " fz=" + ArraySend.FileSize);
return;
}
Console.WriteLine("copy final binary file");
Array.Copy(fc, 0, fdst, 0, ArraySend.FileSize);
File.WriteAllBytes(statics.zip_RXtempfilename, fdst);
@ -587,35 +629,24 @@ namespace oscardata
private void timer_qpsk_Tick(object sender, EventArgs e)
{
panel_constel.Invalidate();
if(Udp.IQavail())
panel_constel.Invalidate();
panel_txspectrum.Invalidate();
}
private void panel_constel_Paint(object sender, PaintEventArgs e)
{
Pen pen = new Pen(Brushes.LightGray);
e.Graphics.DrawEllipse(pen, 0, 0, panel_constel.Size.Width-1, panel_constel.Size.Height-1);
e.Graphics.DrawLine(pen, panel_constel.Size.Width / 2, 0, panel_constel.Size.Width / 2, panel_constel.Size.Height);
e.Graphics.DrawLine(pen, 0, panel_constel.Size.Height / 2, panel_constel.Size.Width, panel_constel.Size.Height/2);
while (true)
Bitmap bm = Udp.UdpBitmap();
if (bm != null)
{
qpskitem qi = Udp.UdpGetIQ();
if (qi == null) break;
Pen pen = new Pen(Brushes.LightGray);
e.Graphics.DrawEllipse(pen, 0, 0, panel_constel.Size.Width - 1, panel_constel.Size.Height - 1);
e.Graphics.DrawLine(pen, panel_constel.Size.Width / 2, 0, panel_constel.Size.Width / 2, panel_constel.Size.Height);
e.Graphics.DrawLine(pen, 0, panel_constel.Size.Height / 2, panel_constel.Size.Width, panel_constel.Size.Height / 2);
// re and im are in the range of +/- 2^24 (16777216)
// scale it to +/- 128
double fre = qi.re;
double fim = qi.im;
fre = fre * panel_constel.Size.Width / 2 / 16777216.0;
fim = fim * panel_constel.Size.Width / 2 / 16777216.0;
// scale it to the picture
int x = panel_constel.Size.Width / 2 + (int)fre - 2;
int y = panel_constel.Size.Height / 2 + (int)fim - 2;
e.Graphics.FillEllipse(Brushes.Blue, x, y, 2, 2);
e.Graphics.DrawImage(bm, 0, 0);
bm.Dispose();
}
}
@ -959,6 +990,35 @@ namespace oscardata
else
line += " sequence OK";
int bits = rxframecounter * 258 * 8;
int bytes = rxframecounter * 258;
String sbit = "b";
String sbyt = "B";
if (bits > 1000)
{
bits /= 1000;
sbit = "kb";
}
if (bits > 1000)
{
bits /= 1000;
sbit = "Mb";
}
if (bytes > 1000)
{
bytes /= 1000;
sbyt = "kB";
}
if (bytes > 1000)
{
bytes /= 1000;
sbyt = "MB";
}
line += " " + bits.ToString() + " " + sbit + " " + bytes.ToString() + " " + sbyt;
line += " BER: " + string.Format("{0:#.##E+0}", ber); // ber.ToString("E3");
line += "\r\n";
@ -1062,16 +1122,38 @@ namespace oscardata
return ip;
}
Byte getPBaudioDevice()
{
String s = cb_audioPB.Text;
Byte x = (Byte)cb_audioPB.Items.IndexOf(s);
//if (s.ToUpper() == "DEFAULT") x = 255;
return x;
}
Byte getCAPaudioDevice()
{
String s = cb_audioCAP.Text;
Byte x = (Byte)cb_audioCAP.Items.IndexOf(s);
//if (s.ToUpper() == "DEFAULT") x = 255;
return x;
}
/*
* search for the modem IP:
* send a search message (2 bytes) via UDP to port UdpBCport
* send a search message via UDP to port UdpBCport
* if a modem receives this message, it returns with an
* UDP message to UdpBCport containing a String with it's IP address
* this message also contains the selected Audio Devices
*/
private void search_modem()
{
Udp.UdpBCsend(new Byte[] { (Byte)0x3c }, GetMyBroadcastIP(), statics.UdpBCport_AppToModem);
Byte[] txb = new byte[3];
txb[0] = 0x3c; // ID of this message
txb[1] = getPBaudioDevice();
txb[2] = getCAPaudioDevice();
Udp.UdpBCsend(txb, GetMyBroadcastIP(), statics.UdpBCport_AppToModem);
Udp.searchtimeout++;
if (Udp.searchtimeout >= 3)
@ -1161,6 +1243,8 @@ namespace oscardata
case 5: real_rate = 4800; break;
case 6: real_rate = 5525; break;
case 7: real_rate = 6000; break;
case 8: real_rate = 6615; break;
case 9: real_rate = 7200; break;
}
statics.setDatarate(real_rate);
@ -1211,17 +1295,6 @@ namespace oscardata
}
/// <summary>
// TEST ONLY: tell modem to send a file
private void button1_Click(object sender, EventArgs e)
{
Byte[] txdata = new byte[statics.PayloadLen + 2];
txdata[0] = (Byte)statics.AutosendFile;
// and transmit it
Udp.UdpSend(txdata);
}
private void bt_openrxfile_Click(object sender, EventArgs e)
{
if (statics.ostype == 0)
@ -1288,6 +1361,8 @@ namespace oscardata
cb_stampcall.Checked = (s == "1");
s = ReadString(sr);
cb_savegoodfiles.Checked = (s == "1");
cb_audioPB.Text = ReadString(sr);
cb_audioCAP.Text = ReadString(sr);
}
}
catch
@ -1295,6 +1370,9 @@ namespace oscardata
tb_callsign.Text = "";
cb_speed.Text = "4000 QPSK BW: 2400 Hz (default QO-100)";
}
if (cb_audioPB.Text.Length <= 1) cb_audioPB.Text = "Default";
if (cb_audioCAP.Text.Length <= 1) cb_audioCAP.Text = "Default";
}
void save_Setup()
@ -1307,6 +1385,8 @@ namespace oscardata
sw.WriteLine(cb_speed.Text);
sw.WriteLine(cb_stampcall.Checked?"1":"0");
sw.WriteLine(cb_savegoodfiles.Checked ? "1" : "0");
sw.WriteLine(cb_audioPB.Text);
sw.WriteLine(cb_audioCAP.Text);
}
}
catch { }
@ -1326,5 +1406,25 @@ namespace oscardata
MessageBox.Show("Please wait abt. 1 minute before powering OFF the modem", "Shut Down Modem", MessageBoxButtons.OK);
}
}
/// <summary>
// TEST ONLY: tell modem to send a file
private void button1_Click(object sender, EventArgs e)
{
Byte[] txdata = new byte[statics.PayloadLen + 2];
txdata[0] = (Byte)statics.AutosendFile;
// and transmit it
Udp.UdpSend(txdata);
}
private void bt_resetmodem_Click(object sender, EventArgs e)
{
Byte[] txdata = new byte[statics.PayloadLen + 2];
txdata[0] = (Byte)statics.ResetModem;
// and transmit it
Udp.UdpSend(txdata);
}
}
}

View File

@ -25,6 +25,7 @@ namespace oscardata
public static Byte AutosendFile = 17;
public static Byte AutosendFolder = 18;
public static Byte Modem_shutdown = 19;
public static Byte ResetModem = 20;
// frame sequence, modem needs that for i.e. sending a preamble
public static Byte FirstFrame = 0;
@ -52,6 +53,9 @@ namespace oscardata
public static String RXimageStorage = "RXimages";
public static String OSversion = "";
public static int ostype = 0; // 0=Windows, 1=Linux
public static int GotAudioDevices = 0;
public static String[] AudioPBdevs;
public static String[] AudioCAPdevs;
public static void setDatarate(int rate)
{

View File

@ -12,9 +12,11 @@
using System;
using System.Collections;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms.VisualStyles;
namespace oscardata
{
@ -91,6 +93,13 @@ namespace oscardata
{
statics.ModemIP = RemoteEndpoint.Address.ToString();
searchtimeout = 0;
// message b contains audio devices
String s = statics.ByteArrayToString(b);
String[] sa1 = s.Split(new char[] { '^' });
statics.AudioPBdevs = sa1[0].Split(new char[] { '~' });
statics.AudioCAPdevs = sa1[1].Split(new char[] { '~' });
if(statics.GotAudioDevices == 0)
statics.GotAudioDevices = 1;
}
// FFT data
@ -108,10 +117,11 @@ namespace oscardata
lastb[0] = b[i];
// test if aligned
int re = 0, im = 0;
if (lastb[0] == 0 && lastb[1] == 0 && lastb[2] == 3 && lastb[3] == 0xe8)
{
// we are aligned to a re value
int re = lastb[4];
re = lastb[4];
re <<= 8;
re += lastb[5];
re <<= 8;
@ -119,23 +129,18 @@ namespace oscardata
re <<= 8;
re += lastb[7];
int im = lastb[8];
im = lastb[8];
im <<= 8;
im += lastb[9];
im <<= 8;
im += lastb[10];
im <<= 8;
im += lastb[11];
qpskitem q = new qpskitem();
q.re = re;
q.im = im;
uq_iq.Add(q);
}
else if (lastb[0] == 0xe8 && lastb[1] == 3 && lastb[2] == 0 && lastb[3] == 0)
{
// we are aligned to a re value
int re = lastb[7];
re = lastb[7];
re <<= 8;
re += lastb[6];
re <<= 8;
@ -143,19 +148,16 @@ namespace oscardata
re <<= 8;
re += lastb[4];
int im = lastb[11];
im = lastb[11];
im <<= 8;
im += lastb[10];
im <<= 8;
im += lastb[9];
im <<= 8;
im += lastb[8];
qpskitem q = new qpskitem();
q.re = re;
q.im = im;
uq_iq.Add(q);
}
drawBitmap(re, im);
}
}
}
@ -164,6 +166,39 @@ namespace oscardata
}
}
static int panelw = 75, panelh = 75;
static int maxdrawanz = 250;
static int drawanz = 0;
static Bitmap bm;
static void drawBitmap(int re, int im)
{
if (re == 0 && im == 0) return;
if (++drawanz >= maxdrawanz && uq_iq.Count() <= 1)
{
drawanz = 0;
uq_iq.Add(bm);
bm = new Bitmap(75, 75);
}
using (Graphics gr = Graphics.FromImage(bm))
{
// re and im are in the range of +/- 2^24 (16777216)
// scale it to +/- 128
double fre = re;
double fim = im;
fre = fre * panelw / 2 / 16777216.0;
fim = fim * panelh / 2 / 16777216.0;
// scale it to the picture
int x = panelw / 2 + (int)fre;
int y = panelh / 2 + (int)fim;
int et = 1;
gr.FillEllipse(Brushes.Blue, x - et, y - et, et * 2, et * 2);
}
}
static AutoResetEvent autoEvent = new AutoResetEvent(false);
// Udp TX Loop runs in its own thread
@ -265,6 +300,19 @@ namespace oscardata
return uq_iq.GetQPSKitem();
}
public static Bitmap UdpBitmap()
{
if (uq_iq.Count() == 0) return null;
return uq_iq.GetBitmap();
}
public static bool IQavail()
{
if (uq_iq.Count() == 0) return false;
return true;
}
}
// this class is a thread safe queue wich is used
@ -289,13 +337,32 @@ namespace oscardata
}
}
public Byte [] Getarr()
public void Add(Bitmap bm)
{
lock (myQ.SyncRoot)
{
myQ.Enqueue(bm);
}
}
public Bitmap GetBitmap()
{
Bitmap b;
lock (myQ.SyncRoot)
{
b = (Bitmap)myQ.Dequeue();
}
return b;
}
public Byte[] Getarr()
{
Byte[] b;
lock (myQ.SyncRoot)
{
b = (Byte [])myQ.Dequeue();
b = (Byte[])myQ.Dequeue();
}
return b;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff