This commit is contained in:
dtiller 2022-05-14 17:15:56 +00:00 committed by GitHub
commit 553413ff62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 2412 additions and 14 deletions

View File

@ -85,6 +85,8 @@ list(APPEND direwolf_SOURCES
dwgpsnmea.c
dwgpsd.c
mheard.c
eotd.c
bch.c
)
if(LINUX)
@ -276,6 +278,7 @@ list(APPEND gen_packets_SOURCES
dtmf.c
textcolor.c
dsp.c
eotd_send.c
)
add_executable(gen_packets
@ -317,6 +320,8 @@ list(APPEND atest_SOURCES
symbols.c
tt_text.c
textcolor.c
eotd.c
bch.c
)
if(WIN32 OR CYGWIN)
@ -469,6 +474,32 @@ if(WIN32 OR CYGWIN)
endif()
list(APPEND pkttest_SOURCES
pkttest.c
bch.c
eotd.c
textcolor.c
)
add_executable(pkttest
${pkttest_SOURCES}
)
target_link_libraries(pkttest
${MISC_LIBRARIES}
Threads::Threads
)
list(APPEND bchapply_SOURCES
bchapply.c
bch.c
)
add_executable(bchapply
${bchapply_SOURCES}
)
install(TARGETS direwolf DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS decode_aprs DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS text2tt DESTINATION ${INSTALL_BIN_DIR})
@ -482,6 +513,8 @@ install(TARGETS atest DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS ttcalc DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS kissutil DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS appserver DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS pkttest DESTINATION ${INSTALL_BIN_DIR})
install(TARGETS bchapply DESTINATION ${INSTALL_BIN_DIR})
if(UDEV_FOUND)
install(TARGETS cm108 DESTINATION ${INSTALL_BIN_DIR})
endif()

View File

@ -280,6 +280,9 @@ int main (int argc, char *argv[])
else if (strcasecmp(optarg, "EAS") == 0) {
B_opt = 23456; // See special case below.
}
else if (strcasecmp(optarg, "EOTD") == 0) {
B_opt = 34567;
}
else {
B_opt = atoi(optarg);
}
@ -475,6 +478,12 @@ int main (int argc, char *argv[])
my_audio_config.achan[0].space_freq = 1563; // Actually 1562.5 - logic 0.
strlcpy (my_audio_config.achan[0].profiles, "D", sizeof(my_audio_config.achan[0].profiles));
}
else if (my_audio_config.achan[0].baud == 34567) {
my_audio_config.achan[0].modem_type = MODEM_EOTD;
my_audio_config.achan[0].baud = 1200;
my_audio_config.achan[0].mark_freq = 1200;
my_audio_config.achan[0].space_freq = 1800;
}
else {
my_audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
my_audio_config.achan[0].mark_freq = 0;

View File

@ -148,7 +148,7 @@ struct audio_s {
/* Could all be the same or different. */
enum modem_t { MODEM_AFSK, MODEM_BASEBAND, MODEM_SCRAMBLE, MODEM_QPSK, MODEM_8PSK, MODEM_OFF, MODEM_16_QAM, MODEM_64_QAM, MODEM_AIS, MODEM_EAS } modem_type;
enum modem_t { MODEM_AFSK, MODEM_BASEBAND, MODEM_SCRAMBLE, MODEM_QPSK, MODEM_8PSK, MODEM_OFF, MODEM_16_QAM, MODEM_64_QAM, MODEM_AIS, MODEM_EAS, MODEM_EOTD } modem_type;
/* Usual AFSK. */
/* Baseband signal. Not used yet. */

600
src/bch.c Normal file
View File

@ -0,0 +1,600 @@
/* BCH processing, library-style. Copyright (2022) David E. Tiller, K4DET
This file was adapted from a program written by Robert Morelos-Zaragoza
(robert@spectra.eng.hawaii.edu) whose original Copyright appears below.
*/
/*
* File: bch3.c
* Title: Encoder/decoder for binary BCH codes in C (Version 3.1)
* Author: Robert Morelos-Zaragoza
* Date: August 1994
* Revised: June 13, 1997
*
* =============== Encoder/Decoder for binary BCH codes in C =================
*
* Version 1: Original program. The user provides the generator polynomial
* of the code (cumbersome!).
* Version 2: Computes the generator polynomial of the code.
* Version 3: No need to input the coefficients of a primitive polynomial of
* degree m, used to construct the Galois Field GF(2**m). The
* program now works for any binary BCH code of length such that:
* 2**(m-1) - 1 < length <= 2**m - 1
*
* Note: You may have to change the size of the arrays to make it work.
*
* The encoding and decoding methods used in this program are based on the
* book "Error Control Coding: Fundamentals and Applications", by Lin and
* Costello, Prentice Hall, 1983.
*
* Thanks to Patrick Boyle (pboyle@era.com) for his observation that 'bch2.c'
* did not work for lengths other than 2**m-1 which led to this new version.
* Portions of this program are from 'rs.c', a Reed-Solomon encoder/decoder
* in C, written by Simon Rockliff (simon@augean.ua.oz.au) on 21/9/89. The
* previous version of the BCH encoder/decoder in C, 'bch2.c', was written by
* Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) on 5/19/92.
*
* NOTE:
* The author is not responsible for any malfunctioning of
* this program, nor for any damage caused by it. Please include the
* original program along with these comments in any redistribution.
*
* For more information, suggestions, or other ideas on implementing error
* correcting codes, please contact me at:
*
* Robert Morelos-Zaragoza
* 5120 Woodway, Suite 7036
* Houston, Texas 77056
*
* email: r.morelos-zaragoza@ieee.org
*
* COPYRIGHT NOTICE: This computer program is free for non-commercial purposes.
* You may implement this program for any non-commercial application. You may
* also implement this program for commercial purposes, provided that you
* obtain my written permission. Any modification of this program is covered
* by this copyright.
*
* == Copyright (c) 1994-7, Robert Morelos-Zaragoza. All rights reserved. ==
*
* m = order of the Galois field GF(2**m)
* n = 2**m - 1 = size of the multiplicative group of GF(2**m)
* length = length of the BCH code
* t = error correcting capability (max. no. of errors the code corrects)
* d = 2*t + 1 = designed min. distance = no. of consecutive roots of g(x) + 1
* k = n - deg(g(x)) = dimension (no. of information bits/codeword) of the code
* p[] = coefficients of a primitive polynomial used to generate GF(2**m)
* g[] = coefficients of the generator polynomial, g(x)
* alpha_to [] = log table of GF(2**m)
* index_of[] = antilog table of GF(2**m)
* data[] = information bits = coefficients of data polynomial, i(x)
* bb[] = coefficients of redundancy polynomial x^(length-k) i(x) modulo g(x)
* numerr = number of errors
* errpos[] = error positions
* recd[] = coefficients of the received polynomial
* decerror = number of decoding errors (in _message_ positions)
*
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "bch.h"
int init_bch(bch_t *bch, int m, int length, int t) {
int p[21], n;
if (bch == NULL) {
return -1;
}
if (m < 2 || m > 20) {
return -2;
}
bch->m = m;
bch->length = length;
bch->t = t;
for (int i=1; i<m; i++) {
p[i] = 0;
}
p[0] = p[m] = 1;
if (m == 2) p[1] = 1;
else if (m == 3) p[1] = 1;
else if (m == 4) p[1] = 1;
else if (m == 5) p[2] = 1;
else if (m == 6) p[1] = 1;
else if (m == 7) p[1] = 1;
else if (m == 8) p[4] = p[5] = p[6] = 1;
else if (m == 9) p[4] = 1;
else if (m == 10) p[3] = 1;
else if (m == 11) p[2] = 1;
else if (m == 12) p[3] = p[4] = p[7] = 1;
else if (m == 13) p[1] = p[3] = p[4] = 1;
else if (m == 14) p[1] = p[11] = p[12] = 1;
else if (m == 15) p[1] = 1;
else if (m == 16) p[2] = p[3] = p[5] = 1;
else if (m == 17) p[3] = 1;
else if (m == 18) p[7] = 1;
else if (m == 19) p[1] = p[5] = p[6] = 1;
else if (m == 20) p[3] = 1;
n = 1;
#ifdef BCH_DEBUG
printf("p(x) = ");
#endif
for (int i = 0; i <= m; i++) {
n *= 2;
#ifdef BCH_DEBUG
printf("%1d", p[i]);
#endif
}
#ifdef BCH_DEBUG
printf("\n");
#endif
n = n / 2 - 1;
bch->n = n;
int ninf = (n + 1) / 2 - 1;
if (length < ninf || length > n) {
return -3;
}
/*
* Generate field GF(2**m) from the irreducible polynomial p(X) with
* coefficients in p[0]..p[m].
*
* Lookup tables:
* index->polynomial form: alpha_to[] contains j=alpha^i;
* polynomial form -> index form: index_of[j=alpha^i] = i
*
* alpha=2 is the primitive element of GF(2**m)
*/
register int mask;
bch->alpha_to = malloc(n * sizeof(int));
bch->index_of = malloc(n * sizeof(int));
mask = 1;
bch->alpha_to[m] = 0;
for (int i = 0; i < m; i++) {
bch->alpha_to[i] = mask;
bch->index_of[bch->alpha_to[i]] = i;
if (p[i] != 0)
bch->alpha_to[m] ^= mask;
mask <<= 1;
}
bch->index_of[bch->alpha_to[m]] = m;
mask >>= 1;
for (int i = m + 1; i < n; i++) {
if (bch->alpha_to[i - 1] >= mask)
bch->alpha_to[i] = bch->alpha_to[m] ^ ((bch->alpha_to[i - 1] ^ mask) << 1);
else
bch->alpha_to[i] = bch->alpha_to[i - 1] << 1;
bch->index_of[bch->alpha_to[i]] = i;
}
bch->index_of[0] = -1;
/*
* Compute the generator polynomial of a binary BCH code. Fist generate the
* cycle sets modulo 2**m - 1, cycle[][] = (i, 2*i, 4*i, ..., 2^l*i). Then
* determine those cycle sets that contain integers in the set of (d-1)
* consecutive integers {1..(d-1)}. The generator polynomial is calculated
* as the product of linear factors of the form (x+alpha^i), for every i in
* the above cycle sets.
*/
register int ii, jj, ll, kaux;
register int test, aux, nocycles, root, noterms, rdncy;
int cycle[1024][21], size[1024], min[1024], zeros[1024];
/* Generate cycle sets modulo n, n = 2**m - 1 */
cycle[0][0] = 0;
size[0] = 1;
cycle[1][0] = 1;
size[1] = 1;
jj = 1; /* cycle set index */
#ifdef BCH_DEBUG
if (bch->m > 9) {
printf("Computing cycle sets modulo %d\n", bch->n);
printf("(This may take some time)...\n");
}
#endif
do {
/* Generate the jj-th cycle set */
ii = 0;
do {
ii++;
cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % bch->n;
size[jj]++;
aux = (cycle[jj][ii] * 2) % bch->n;
} while (aux != cycle[jj][0]);
/* Next cycle set representative */
ll = 0;
do {
ll++;
test = 0;
for (ii = 1; ((ii <= jj) && (!test)); ii++)
/* Examine previous cycle sets */
for (kaux = 0; ((kaux < size[ii]) && (!test)); kaux++)
if (ll == cycle[ii][kaux])
test = 1;
} while ((test) && (ll < (bch->n - 1)));
if (!(test)) {
jj++; /* next cycle set index */
cycle[jj][0] = ll;
size[jj] = 1;
}
} while (ll < (bch->n - 1));
nocycles = jj; /* number of cycle sets modulo n */
int d = 2 * t + 1;
/* Search for roots 1, 2, ..., d-1 in cycle sets */
kaux = 0;
rdncy = 0;
for (ii = 1; ii <= nocycles; ii++) {
min[kaux] = 0;
test = 0;
for (jj = 0; ((jj < size[ii]) && (!test)); jj++)
for (root = 1; ((root < d) && (!test)); root++)
if (root == cycle[ii][jj]) {
test = 1;
min[kaux] = ii;
}
if (min[kaux]) {
rdncy += size[min[kaux]];
kaux++;
}
}
noterms = kaux;
kaux = 1;
for (ii = 0; ii < noterms; ii++)
for (jj = 0; jj < size[min[ii]]; jj++) {
zeros[kaux] = cycle[min[ii]][jj];
kaux++;
}
bch-> k = length - rdncy;
if (bch->k<0)
{
return -4;
}
#ifdef BCH_DEBUG
printf("This is a (%d, %d, %d) binary BCH code\n", bch->length, bch->k, d);
#endif
/* Compute the generator polynomial */
bch->g = malloc((rdncy + 1) * sizeof(int));
bch->g[0] = bch->alpha_to[zeros[1]];
bch->g[1] = 1; /* g(x) = (X + zeros[1]) initially */
for (ii = 2; ii <= rdncy; ii++) {
bch->g[ii] = 1;
for (jj = ii - 1; jj > 0; jj--)
if (bch->g[jj] != 0)
bch->g[jj] = bch->g[jj - 1] ^ bch->alpha_to[(bch->index_of[bch->g[jj]] + zeros[ii]) % bch->n];
else
bch->g[jj] = bch->g[jj - 1];
bch->g[0] = bch->alpha_to[(bch->index_of[bch->g[0]] + zeros[ii]) % bch->n];
}
#ifdef BCH_DEBUG
printf("Generator polynomial:\ng(x) = ");
for (ii = 0; ii <= rdncy; ii++) {
printf("%d", bch->g[ii]);
}
printf("\n");
#endif
return 0;
}
void generate_bch(bch_t *bch, const int *data, int *bb) {
/*
* Compute redundacy bb[], the coefficients of b(x). The redundancy
* polynomial b(x) is the remainder after dividing x^(length-k)*data(x)
* by the generator polynomial g(x).
*/
register int feedback;
for (int i = 0; i < bch->length - bch->k; i++)
bb[i] = 0;
for (int i = bch->k - 1; i >= 0; i--) {
feedback = data[i] ^ bb[bch->length - bch->k - 1];
if (feedback != 0) {
for (int j = bch->length - bch->k - 1; j > 0; j--)
if (bch->g[j] != 0)
bb[j] = bb[j - 1] ^ feedback;
else
bb[j] = bb[j - 1];
bb[0] = bch->g[0] && feedback;
} else {
for (int j = bch->length - bch->k - 1; j > 0; j--)
bb[j] = bb[j - 1];
bb[0] = 0;
}
}
}
int apply_bch(const bch_t *bch, int *recd)
/*
* Simon Rockliff's implementation of Berlekamp's algorithm.
*
* Assume we have received bits in recd[i], i=0..(n-1).
*
* Compute the 2*t syndromes by substituting alpha^i into rec(X) and
* evaluating, storing the syndromes in s[i], i=1..2t (leave s[0] zero) .
* Then we use the Berlekamp algorithm to find the error location polynomial
* elp[i].
*
* If the degree of the elp is >t, then we cannot correct all the errors, and
* we have detected an uncorrectable error pattern. We output the information
* bits uncorrected.
*
* If the degree of elp is <=t, we substitute alpha^i , i=1..n into the elp
* to get the roots, hence the inverse roots, the error location numbers.
* This step is usually called "Chien's search".
*
* If the number of errors located is not equal the degree of the elp, then
* the decoder assumes that there are more than t errors and cannot correct
* them, only detect them. We output the information bits uncorrected.
*/
{
register int i, j, u, q, t2, count = 0, syn_error = 0;
int elp[1026][1024], d[1026], l[1026], u_lu[1026], s[1025];
int loc[200], reg[201];
t2 = 2 * bch->t;
/* first form the syndromes */
#ifdef BCH_DEBUG
printf("S(x) = ");
#endif
for (i = 1; i <= t2; i++) {
s[i] = 0;
for (j = 0; j < bch->length; j++)
if (recd[j] != 0)
s[i] ^= bch->alpha_to[(i * j) % bch->n];
if (s[i] != 0)
syn_error = 1; /* set error flag if non-zero syndrome */
/*
* Note: If the code is used only for ERROR DETECTION, then
* exit program here indicating the presence of errors.
*/
/* convert syndrome from polynomial form to index form */
s[i] = bch->index_of[s[i]];
#ifdef BCH_DEBUG
printf("%3d ", s[i]);
#endif
}
#ifdef BCH_DEBUG
printf("\n");
#endif
if (syn_error) { /* if there are errors, try to correct them */
/*
* Compute the error location polynomial via the Berlekamp
* iterative algorithm. Following the terminology of Lin and
* Costello's book : d[u] is the 'mu'th discrepancy, where
* u='mu'+1 and 'mu' (the Greek letter!) is the step number
* ranging from -1 to 2*t (see L&C), l[u] is the degree of
* the elp at that step, and u_l[u] is the difference between
* the step number and the degree of the elp.
*/
/* initialise table entries */
d[0] = 0; /* index form */
d[1] = s[1]; /* index form */
elp[0][0] = 0; /* index form */
elp[1][0] = 1; /* polynomial form */
for (i = 1; i < t2; i++) {
elp[0][i] = -1; /* index form */
elp[1][i] = 0; /* polynomial form */
}
l[0] = 0;
l[1] = 0;
u_lu[0] = -1;
u_lu[1] = 0;
u = 0;
do {
u++;
if (d[u] == -1) {
l[u + 1] = l[u];
for (i = 0; i <= l[u]; i++) {
elp[u + 1][i] = elp[u][i];
elp[u][i] = bch->index_of[elp[u][i]];
}
} else
/*
* search for words with greatest u_lu[q] for
* which d[q]!=0
*/
{
q = u - 1;
while ((d[q] == -1) && (q > 0))
q--;
/* have found first non-zero d[q] */
if (q > 0) {
j = q;
do {
j--;
if ((d[j] != -1) && (u_lu[q] < u_lu[j]))
q = j;
} while (j > 0);
}
/*
* have now found q such that d[u]!=0 and
* u_lu[q] is maximum
*/
/* store degree of new elp polynomial */
if (l[u] > l[q] + u - q)
l[u + 1] = l[u];
else
l[u + 1] = l[q] + u - q;
/* form new elp(x) */
for (i = 0; i < t2; i++)
elp[u + 1][i] = 0;
for (i = 0; i <= l[q]; i++)
if (elp[q][i] != -1)
elp[u + 1][i + u - q] =
bch->alpha_to[(d[u] + bch->n - d[q] + elp[q][i]) % bch->n];
for (i = 0; i <= l[u]; i++) {
elp[u + 1][i] ^= elp[u][i];
elp[u][i] = bch->index_of[elp[u][i]];
}
}
u_lu[u + 1] = u - l[u + 1];
/* form (u+1)th discrepancy */
if (u < t2) {
/* no discrepancy computed on last iteration */
if (s[u + 1] != -1)
d[u + 1] = bch->alpha_to[s[u + 1]];
else
d[u + 1] = 0;
for (i = 1; i <= l[u + 1]; i++)
if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0))
d[u + 1] ^= bch->alpha_to[(s[u + 1 - i]
+ bch->index_of[elp[u + 1][i]]) % bch->n];
/* put d[u+1] into index form */
d[u + 1] = bch->index_of[d[u + 1]];
}
} while ((u < t2) && (l[u + 1] <= bch->t));
u++;
if (l[u] <= bch->t) {/* Can correct errors */
/* put elp into index form */
for (i = 0; i <= l[u]; i++)
elp[u][i] = bch->index_of[elp[u][i]];
#ifdef BCH_DEBUG
printf("sigma(x) = ");
for (i = 0; i <= l[u]; i++)
printf("%3d ", elp[u][i]);
printf("\n");
printf("Roots: ");
#endif
/* Chien search: find roots of the error location polynomial */
for (i = 1; i <= l[u]; i++)
reg[i] = elp[u][i];
count = 0;
for (i = 1; i <= bch->n; i++) {
q = 1;
for (j = 1; j <= l[u]; j++)
if (reg[j] != -1) {
reg[j] = (reg[j] + j) % bch->n;
q ^= bch->alpha_to[reg[j]];
}
if (!q) { /* store root and error
* location number indices */
loc[count] = bch->n - i;
count++;
#ifdef BCH_DEBUG
printf("%3d ", bch->n - i);
#endif
}
}
#ifdef BCH_DEBUG
printf("\n");
#endif
if (count == l[u]) {
/* no. roots = degree of elp hence <= t errors */
for (i = 0; i < l[u]; i++)
recd[loc[i]] ^= 1;
return l[u];
}
else { /* elp has degree >t hence cannot solve */
#ifdef BCH_DEBUG
printf("Incomplete decoding: errors detected\n");
#endif
return -1;
}
} else {
return -1;
}
} else {
return 0; // No errors
}
}
/* LEFT justified in hex */
void bytes_to_bits(const uint8_t *bytes, int *bit_dest, int num_bits) {
for (int i = 0; i < num_bits; i++) {
int index = i / 8;
int bit_pos = 7 - (i % 8);
int bit_mask = 1 << bit_pos;
bit_dest[i] = (bytes[index] & bit_mask) != 0;
}
}
void bits_to_bytes(const int *bits, uint8_t *byte_dest, int num_bits) {
int index;
for (int i = 0; i < num_bits; i++) {
index = i / 8;
if (i % 8 == 0) {
byte_dest[index] = 0;
}
byte_dest[index] <<= 1;
byte_dest[index] |= (bits[i] & 0x01);
}
}
void swap_format(const int *bits, int *dest, int cutoff, int num_bits) {
// Do it the easy way
for (int i = 0; i < num_bits; i++) {
if (i < cutoff) {
dest[num_bits - cutoff + i] = bits[i];
} else {
dest[i - cutoff] = bits[i];
}
}
}
uint8_t rotate_byte(uint8_t x) {
uint8_t y = 0;
for (int i = 0; i < 8; i++) {
y <<= 1;
y |= (x & 0x01);
x >>= 1;
}
return y;
}
void rotate_bits(const int *in, int *out, int num_bits) {
for (int i = 0; i < num_bits; i++) {
out[i] = in[num_bits - i - 1];
}
}
void invert_bits(const int *bits, int *dest, int num_bits) {
for (int i = 0; i < num_bits; i++) {
dest[i] = (bits[i] == 0);
}
}
void dump_bch(const bch_t *bch) {
printf("m: %d length: %d t: %d n: %d k: %d\n", bch->m, bch->length, bch->t, bch->n, bch->k);
}
void print_bytes(const char *msg, const uint8_t *bytes, int num_bytes) {
printf("%s", msg);
for (int i = 0; i < num_bytes; i++) {
printf("%02x ", bytes[i]);
}
}
void print_bits(const char *msg, const int *bits, int num_bits) {
printf("%s", msg);
for (int i = 0; i < num_bits; i++) {
printf("%d ", bits[i]);
}
}

41
src/bch.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef __BCH_H
#define __BCH_H
#include <stdlib.h>
struct bch {
int m; // 2^m - 1 is max length, n
int length; // Actual packet size
int n; // 2^m - 1
int k; // Length of data portion
int t; // Number of correctable bits
int *g; // Calculated polynomial of length n - k
int *alpha_to;
int *index_of;
};
typedef struct bch bch_t;
int init_bch(bch_t *bch, int m, int length, int t);
void generate_bch(bch_t *bch, const int *data, int *bb);
int apply_bch(const bch_t *bch, int *recd);
void bytes_to_bits(const uint8_t *bytes, int *bit_dest, int num_bits);
void bits_to_bytes(const int *bits, uint8_t *byte_dest, int num_bits);
void swap_format(const int *bits, int *dest, int cutoff, int num_bits);
uint8_t rotate_byte(uint8_t x);
void rotate_bits(const int *in, int *out, int num_bits);
void print_bytes(const char *msg, const uint8_t *bytes, int num_bytes);
void print_bits(const char *msg, const int *bits, int num_bits);
void invert_bits(const int *bits, int *dest, int num_bits);
#endif

591
src/bch3.c Normal file
View File

@ -0,0 +1,591 @@
/*
* File: bch3.c
* Title: Encoder/decoder for binary BCH codes in C (Version 3.1)
* Author: Robert Morelos-Zaragoza
* Date: August 1994
* Revised: June 13, 1997
*
* =============== Encoder/Decoder for binary BCH codes in C =================
*
* Version 1: Original program. The user provides the generator polynomial
* of the code (cumbersome!).
* Version 2: Computes the generator polynomial of the code.
* Version 3: No need to input the coefficients of a primitive polynomial of
* degree m, used to construct the Galois Field GF(2**m). The
* program now works for any binary BCH code of length such that:
* 2**(m-1) - 1 < length <= 2**m - 1
*
* Note: You may have to change the size of the arrays to make it work.
*
* The encoding and decoding methods used in this program are based on the
* book "Error Control Coding: Fundamentals and Applications", by Lin and
* Costello, Prentice Hall, 1983.
*
* Thanks to Patrick Boyle (pboyle@era.com) for his observation that 'bch2.c'
* did not work for lengths other than 2**m-1 which led to this new version.
* Portions of this program are from 'rs.c', a Reed-Solomon encoder/decoder
* in C, written by Simon Rockliff (simon@augean.ua.oz.au) on 21/9/89. The
* previous version of the BCH encoder/decoder in C, 'bch2.c', was written by
* Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) on 5/19/92.
*
* NOTE:
* The author is not responsible for any malfunctioning of
* this program, nor for any damage caused by it. Please include the
* original program along with these comments in any redistribution.
*
* For more information, suggestions, or other ideas on implementing error
* correcting codes, please contact me at:
*
* Robert Morelos-Zaragoza
* 5120 Woodway, Suite 7036
* Houston, Texas 77056
*
* email: r.morelos-zaragoza@ieee.org
*
* COPYRIGHT NOTICE: This computer program is free for non-commercial purposes.
* You may implement this program for any non-commercial application. You may
* also implement this program for commercial purposes, provided that you
* obtain my written permission. Any modification of this program is covered
* by this copyright.
*
* == Copyright (c) 1994-7, Robert Morelos-Zaragoza. All rights reserved. ==
*
* m = order of the Galois field GF(2**m)
* n = 2**m - 1 = size of the multiplicative group of GF(2**m)
* length = length of the BCH code
* t = error correcting capability (max. no. of errors the code corrects)
* d = 2*t + 1 = designed min. distance = no. of consecutive roots of g(x) + 1
* k = n - deg(g(x)) = dimension (no. of information bits/codeword) of the code
* p[] = coefficients of a primitive polynomial used to generate GF(2**m)
* g[] = coefficients of the generator polynomial, g(x)
* alpha_to [] = log table of GF(2**m)
* index_of[] = antilog table of GF(2**m)
* data[] = information bits = coefficients of data polynomial, i(x)
* bb[] = coefficients of redundancy polynomial x^(length-k) i(x) modulo g(x)
* numerr = number of errors
* errpos[] = error positions
* recd[] = coefficients of the received polynomial
* decerror = number of decoding errors (in _message_ positions)
*
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
int m, n, length, k, t, d;
int p[21];
int alpha_to[1048576], index_of[1048576], g[548576];
int recd[1048576], data[1048576], bb[548576];
int seed;
int numerr, errpos[1024], decerror = 0;
void
read_p()
/*
* Read m, the degree of a primitive polynomial p(x) used to compute the
* Galois field GF(2**m). Get precomputed coefficients p[] of p(x). Read
* the code length.
*/
{
int i, ninf;
printf("bch3: An encoder/decoder for binary BCH codes\n");
printf("Copyright (c) 1994-7. Robert Morelos-Zaragoza.\n");
printf("This program is free, please read first the copyright notice.\n");
printf("\nFirst, enter a value of m such that the code length is\n");
printf("2**(m-1) - 1 < length <= 2**m - 1\n\n");
do {
printf("Enter m (between 2 and 20): ");
scanf("%d", &m);
} while ( !(m>1) || !(m<21) );
for (i=1; i<m; i++)
p[i] = 0;
p[0] = p[m] = 1;
if (m == 2) p[1] = 1;
else if (m == 3) p[1] = 1;
else if (m == 4) p[1] = 1;
else if (m == 5) p[2] = 1;
else if (m == 6) p[1] = 1;
else if (m == 7) p[1] = 1;
else if (m == 8) p[4] = p[5] = p[6] = 1;
else if (m == 9) p[4] = 1;
else if (m == 10) p[3] = 1;
else if (m == 11) p[2] = 1;
else if (m == 12) p[3] = p[4] = p[7] = 1;
else if (m == 13) p[1] = p[3] = p[4] = 1;
else if (m == 14) p[1] = p[11] = p[12] = 1;
else if (m == 15) p[1] = 1;
else if (m == 16) p[2] = p[3] = p[5] = 1;
else if (m == 17) p[3] = 1;
else if (m == 18) p[7] = 1;
else if (m == 19) p[1] = p[5] = p[6] = 1;
else if (m == 20) p[3] = 1;
printf("p(x) = ");
n = 1;
for (i = 0; i <= m; i++) {
n *= 2;
printf("%1d", p[i]);
}
printf("\n");
n = n / 2 - 1;
ninf = (n + 1) / 2 - 1;
do {
printf("Enter code length (%d < length <= %d): ", ninf, n);
scanf("%d", &length);
} while ( !((length <= n)&&(length>ninf)) );
}
void
generate_gf()
/*
* Generate field GF(2**m) from the irreducible polynomial p(X) with
* coefficients in p[0]..p[m].
*
* Lookup tables:
* index->polynomial form: alpha_to[] contains j=alpha^i;
* polynomial form -> index form: index_of[j=alpha^i] = i
*
* alpha=2 is the primitive element of GF(2**m)
*/
{
register int i, mask;
mask = 1;
alpha_to[m] = 0;
for (i = 0; i < m; i++) {
alpha_to[i] = mask;
index_of[alpha_to[i]] = i;
if (p[i] != 0)
alpha_to[m] ^= mask;
mask <<= 1;
}
index_of[alpha_to[m]] = m;
mask >>= 1;
for (i = m + 1; i < n; i++) {
if (alpha_to[i - 1] >= mask)
alpha_to[i] = alpha_to[m] ^ ((alpha_to[i - 1] ^ mask) << 1);
else
alpha_to[i] = alpha_to[i - 1] << 1;
index_of[alpha_to[i]] = i;
}
index_of[0] = -1;
}
void
gen_poly()
/*
* Compute the generator polynomial of a binary BCH code. Fist generate the
* cycle sets modulo 2**m - 1, cycle[][] = (i, 2*i, 4*i, ..., 2^l*i). Then
* determine those cycle sets that contain integers in the set of (d-1)
* consecutive integers {1..(d-1)}. The generator polynomial is calculated
* as the product of linear factors of the form (x+alpha^i), for every i in
* the above cycle sets.
*/
{
register int ii, jj, ll, kaux;
register int test, aux, nocycles, root, noterms, rdncy;
int cycle[1024][21], size[1024], min[1024], zeros[1024];
/* Generate cycle sets modulo n, n = 2**m - 1 */
cycle[0][0] = 0;
size[0] = 1;
cycle[1][0] = 1;
size[1] = 1;
jj = 1; /* cycle set index */
if (m > 9) {
printf("Computing cycle sets modulo %d\n", n);
printf("(This may take some time)...\n");
}
do {
/* Generate the jj-th cycle set */
ii = 0;
do {
ii++;
cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % n;
size[jj]++;
aux = (cycle[jj][ii] * 2) % n;
} while (aux != cycle[jj][0]);
/* Next cycle set representative */
ll = 0;
do {
ll++;
test = 0;
for (ii = 1; ((ii <= jj) && (!test)); ii++)
/* Examine previous cycle sets */
for (kaux = 0; ((kaux < size[ii]) && (!test)); kaux++)
if (ll == cycle[ii][kaux])
test = 1;
} while ((test) && (ll < (n - 1)));
if (!(test)) {
jj++; /* next cycle set index */
cycle[jj][0] = ll;
size[jj] = 1;
}
} while (ll < (n - 1));
nocycles = jj; /* number of cycle sets modulo n */
printf("Enter the error correcting capability, t: ");
scanf("%d", &t);
d = 2 * t + 1;
/* Search for roots 1, 2, ..., d-1 in cycle sets */
kaux = 0;
rdncy = 0;
for (ii = 1; ii <= nocycles; ii++) {
min[kaux] = 0;
test = 0;
for (jj = 0; ((jj < size[ii]) && (!test)); jj++)
for (root = 1; ((root < d) && (!test)); root++)
if (root == cycle[ii][jj]) {
test = 1;
min[kaux] = ii;
}
if (min[kaux]) {
rdncy += size[min[kaux]];
kaux++;
}
}
noterms = kaux;
kaux = 1;
for (ii = 0; ii < noterms; ii++)
for (jj = 0; jj < size[min[ii]]; jj++) {
zeros[kaux] = cycle[min[ii]][jj];
kaux++;
}
k = length - rdncy;
if (k<0)
{
printf("Parameters invalid!\n");
exit(0);
}
printf("This is a (%d, %d, %d) binary BCH code\n", length, k, d);
/* Compute the generator polynomial */
g[0] = alpha_to[zeros[1]];
g[1] = 1; /* g(x) = (X + zeros[1]) initially */
for (ii = 2; ii <= rdncy; ii++) {
g[ii] = 1;
for (jj = ii - 1; jj > 0; jj--)
if (g[jj] != 0)
g[jj] = g[jj - 1] ^ alpha_to[(index_of[g[jj]] + zeros[ii]) % n];
else
g[jj] = g[jj - 1];
g[0] = alpha_to[(index_of[g[0]] + zeros[ii]) % n];
}
printf("Generator polynomial:\ng(x) = ");
for (ii = 0; ii <= rdncy; ii++) {
printf("%d", g[ii]);
if (ii && ((ii % 50) == 0))
printf("\n");
}
printf("\n");
}
void
encode_bch()
/*
* Compute redundacy bb[], the coefficients of b(x). The redundancy
* polynomial b(x) is the remainder after dividing x^(length-k)*data(x)
* by the generator polynomial g(x).
*/
{
register int i, j;
register int feedback;
for (i = 0; i < length - k; i++)
bb[i] = 0;
for (i = k - 1; i >= 0; i--) {
feedback = data[i] ^ bb[length - k - 1];
if (feedback != 0) {
for (j = length - k - 1; j > 0; j--)
if (g[j] != 0)
bb[j] = bb[j - 1] ^ feedback;
else
bb[j] = bb[j - 1];
bb[0] = g[0] && feedback;
} else {
for (j = length - k - 1; j > 0; j--)
bb[j] = bb[j - 1];
bb[0] = 0;
}
}
}
void
decode_bch()
/*
* Simon Rockliff's implementation of Berlekamp's algorithm.
*
* Assume we have received bits in recd[i], i=0..(n-1).
*
* Compute the 2*t syndromes by substituting alpha^i into rec(X) and
* evaluating, storing the syndromes in s[i], i=1..2t (leave s[0] zero) .
* Then we use the Berlekamp algorithm to find the error location polynomial
* elp[i].
*
* If the degree of the elp is >t, then we cannot correct all the errors, and
* we have detected an uncorrectable error pattern. We output the information
* bits uncorrected.
*
* If the degree of elp is <=t, we substitute alpha^i , i=1..n into the elp
* to get the roots, hence the inverse roots, the error location numbers.
* This step is usually called "Chien's search".
*
* If the number of errors located is not equal the degree of the elp, then
* the decoder assumes that there are more than t errors and cannot correct
* them, only detect them. We output the information bits uncorrected.
*/
{
register int i, j, u, q, t2, count = 0, syn_error = 0;
int elp[1026][1024], d[1026], l[1026], u_lu[1026], s[1025];
int root[200], loc[200], err[1024], reg[201];
t2 = 2 * t;
/* first form the syndromes */
printf("S(x) = ");
for (i = 1; i <= t2; i++) {
s[i] = 0;
for (j = 0; j < length; j++)
if (recd[j] != 0)
s[i] ^= alpha_to[(i * j) % n];
if (s[i] != 0)
syn_error = 1; /* set error flag if non-zero syndrome */
/*
* Note: If the code is used only for ERROR DETECTION, then
* exit program here indicating the presence of errors.
*/
/* convert syndrome from polynomial form to index form */
s[i] = index_of[s[i]];
printf("%3d ", s[i]);
}
printf("\n");
if (syn_error) { /* if there are errors, try to correct them */
/*
* Compute the error location polynomial via the Berlekamp
* iterative algorithm. Following the terminology of Lin and
* Costello's book : d[u] is the 'mu'th discrepancy, where
* u='mu'+1 and 'mu' (the Greek letter!) is the step number
* ranging from -1 to 2*t (see L&C), l[u] is the degree of
* the elp at that step, and u_l[u] is the difference between
* the step number and the degree of the elp.
*/
/* initialise table entries */
d[0] = 0; /* index form */
d[1] = s[1]; /* index form */
elp[0][0] = 0; /* index form */
elp[1][0] = 1; /* polynomial form */
for (i = 1; i < t2; i++) {
elp[0][i] = -1; /* index form */
elp[1][i] = 0; /* polynomial form */
}
l[0] = 0;
l[1] = 0;
u_lu[0] = -1;
u_lu[1] = 0;
u = 0;
do {
u++;
if (d[u] == -1) {
l[u + 1] = l[u];
for (i = 0; i <= l[u]; i++) {
elp[u + 1][i] = elp[u][i];
elp[u][i] = index_of[elp[u][i]];
}
} else
/*
* search for words with greatest u_lu[q] for
* which d[q]!=0
*/
{
q = u - 1;
while ((d[q] == -1) && (q > 0))
q--;
/* have found first non-zero d[q] */
if (q > 0) {
j = q;
do {
j--;
if ((d[j] != -1) && (u_lu[q] < u_lu[j]))
q = j;
} while (j > 0);
}
/*
* have now found q such that d[u]!=0 and
* u_lu[q] is maximum
*/
/* store degree of new elp polynomial */
if (l[u] > l[q] + u - q)
l[u + 1] = l[u];
else
l[u + 1] = l[q] + u - q;
/* form new elp(x) */
for (i = 0; i < t2; i++)
elp[u + 1][i] = 0;
for (i = 0; i <= l[q]; i++)
if (elp[q][i] != -1)
elp[u + 1][i + u - q] =
alpha_to[(d[u] + n - d[q] + elp[q][i]) % n];
for (i = 0; i <= l[u]; i++) {
elp[u + 1][i] ^= elp[u][i];
elp[u][i] = index_of[elp[u][i]];
}
}
u_lu[u + 1] = u - l[u + 1];
/* form (u+1)th discrepancy */
if (u < t2) {
/* no discrepancy computed on last iteration */
if (s[u + 1] != -1)
d[u + 1] = alpha_to[s[u + 1]];
else
d[u + 1] = 0;
for (i = 1; i <= l[u + 1]; i++)
if ((s[u + 1 - i] != -1) && (elp[u + 1][i] != 0))
d[u + 1] ^= alpha_to[(s[u + 1 - i]
+ index_of[elp[u + 1][i]]) % n];
/* put d[u+1] into index form */
d[u + 1] = index_of[d[u + 1]];
}
} while ((u < t2) && (l[u + 1] <= t));
u++;
if (l[u] <= t) {/* Can correct errors */
/* put elp into index form */
for (i = 0; i <= l[u]; i++)
elp[u][i] = index_of[elp[u][i]];
printf("sigma(x) = ");
for (i = 0; i <= l[u]; i++)
printf("%3d ", elp[u][i]);
printf("\n");
printf("Roots: ");
/* Chien search: find roots of the error location polynomial */
for (i = 1; i <= l[u]; i++)
reg[i] = elp[u][i];
count = 0;
for (i = 1; i <= n; i++) {
q = 1;
for (j = 1; j <= l[u]; j++)
if (reg[j] != -1) {
reg[j] = (reg[j] + j) % n;
q ^= alpha_to[reg[j]];
}
if (!q) { /* store root and error
* location number indices */
root[count] = i;
loc[count] = n - i;
count++;
printf("%3d ", n - i);
}
}
printf("\n");
if (count == l[u])
/* no. roots = degree of elp hence <= t errors */
for (i = 0; i < l[u]; i++)
recd[loc[i]] ^= 1;
else /* elp has degree >t hence cannot solve */
printf("Incomplete decoding: errors detected\n");
}
}
}
int main()
{
int i;
read_p(); /* Read m */
generate_gf(); /* Construct the Galois Field GF(2**m) */
gen_poly(); /* Compute the generator polynomial of BCH code */
/* Randomly generate DATA */
seed = 131073;
srandom(seed);
for (i = 0; i < k; i++)
data[i] = ( random() & 65536 ) >> 16;
encode_bch(); /* encode data */
/*
* recd[] are the coefficients of c(x) = x**(length-k)*data(x) + b(x)
*/
for (i = 0; i < length - k; i++)
recd[i] = bb[i];
for (i = 0; i < k; i++)
recd[i + length - k] = data[i];
printf("Code polynomial:\nc(x) = ");
for (i = 0; i < length; i++) {
printf("%1d", recd[i]);
if (i && ((i % 50) == 0))
printf("\n");
}
printf("\n");
printf("Enter the number of errors:\n");
scanf("%d", &numerr); /* CHANNEL errors */
printf("Enter error locations (integers between");
printf(" 0 and %d): ", length-1);
/*
* recd[] are the coefficients of r(x) = c(x) + e(x)
*/
for (i = 0; i < numerr; i++)
scanf("%d", &errpos[i]);
if (numerr)
for (i = 0; i < numerr; i++)
recd[errpos[i]] ^= 1;
printf("r(x) = ");
for (i = 0; i < length; i++) {
printf("%1d", recd[i]);
if (i && ((i % 50) == 0))
printf("\n");
}
printf("\n");
decode_bch(); /* DECODE received codeword recv[] */
/*
* print out original and decoded data
*/
printf("Results:\n");
printf("original data = ");
for (i = 0; i < k; i++) {
printf("%1d", data[i]);
if (i && ((i % 50) == 0))
printf("\n");
}
printf("\nrecovered data = ");
for (i = length - k; i < length; i++) {
printf("%1d", recd[i]);
if ((i-length+k) && (((i-length+k) % 50) == 0))
printf("\n");
}
printf("\n");
/*
* DECODING ERRORS? we compare only the data portion
*/
for (i = length - k; i < length; i++)
if (data[i - length + k] != recd[i])
decerror++;
if (decerror)
printf("There were %d decoding errors in message positions\n", decerror);
else
printf("Succesful decoding\n");
}

189
src/bchapply.c Normal file
View File

@ -0,0 +1,189 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "bch.h"
#define SHOW_BYTES
#define MAX_LENGTH 64
static uint8_t r2f_mask[] = { 0x07, 0x76, 0xa0 };
int test(bch_t *bch, char *msg, int *bits, int length) {
int corrected = 0;
int temp_bits[MAX_LENGTH];
uint8_t bytes[8];
memcpy(temp_bits, bits, length * sizeof(int));
#ifdef SHOW_BYTES
bits_to_bytes(temp_bits, bytes, length);
print_bytes(msg, bytes, 8);
#else
print_bits(msg, temp_bits, length);
#endif
corrected = apply_bch(bch, temp_bits);
if (corrected >= 0) {
printf("corrected %d ", corrected);
#ifdef SHOW_BYTES
bits_to_bytes(temp_bits, bytes, length);
printf("CORR ");
print_bytes(msg, bytes, 8);
#else
print_bits(msg, temp_bits, length);
#endif
printf("\n");
} else {
printf("invalid.\n");
}
return corrected >= 0;
}
int main(int argc, char **argv) {
bch_t bch;
uint8_t bytes[8];
int m, length, t;
int data_len, crc_len;
if (argc != 4) {
fprintf(stderr, "Expecting 3 arguments: m, length, and t.\n");
return -1;
}
sscanf(argv[1], "%d", &m);
sscanf(argv[2], "%d", &length);
sscanf(argv[3], "%d", &t);
if (length > MAX_LENGTH) {
fprintf(stderr, "Max supported length is %d\n", MAX_LENGTH);
return -2;
}
int orig_bits[MAX_LENGTH+1];
init_bch(&bch, m, length, t);
data_len = bch.k;
crc_len = bch.length - bch.k;
printf("m=%d, length=%d, n=%d, k=%d, t=%d\n", bch.m, bch.length, bch.n, bch.k, bch.t);
printf("data_len=%d, crc_len=%d\n", data_len, crc_len);
//
// THIS IS THE LSB-FIRST VERSION
//
fprintf(stderr, "Enter HCB+ATAD _WITH_ the parity bit intact.\n");
fprintf(stderr, "If 't' is 3, that implies an R2F packet and the given packet will be XOR'ed with 0x0776a0.\n");
while (1) {
for (int i = 0; i < 8; i++) {
int temp;
int status = scanf("%x ", &temp);
if (status == EOF) {
return 0;
}
if (status != 1) {
fprintf(stderr, "Error: %d", status);
}
bytes[i] = temp;
if (t == 3 && i < sizeof(r2f_mask)) {
bytes[i] ^= r2f_mask[i];
}
}
int temp[MAX_LENGTH];
// HCB + ATAD
bytes_to_bits(bytes, orig_bits, length+1);
memcpy(temp, orig_bits+1, length * sizeof(int));
print_bits("atad: ", temp + crc_len, data_len);
printf("\n");
print_bits("hcb: ", temp, crc_len);
printf("\n");
test(&bch, "HCB+ATAD: ", temp, length);
// ATAD+HCB
bytes_to_bits(bytes, orig_bits, length+1);
swap_format(orig_bits+1, temp, crc_len, length);
print_bits("atad: ", temp, data_len);
printf("\n");
print_bits("hcb: ", temp+data_len, crc_len);
printf("\n");
test(&bch, "ATAD+HCB: ", temp, length);
// DATA + BCH
bytes_to_bits(bytes, orig_bits, length+1);
rotate_bits(orig_bits+1, temp, length);
print_bits("data: ", temp, data_len);
printf("\n");
print_bits("bch: ", temp+data_len, crc_len);
printf("\n");
test(&bch, "DATA+BCH: ", temp, length);
// BCH+DATA
int swap[MAX_LENGTH];
bytes_to_bits(bytes, orig_bits, length+1);
rotate_bits(orig_bits+1, temp, length);
// now DATA+BCH
swap_format(temp, swap, data_len, length);
// now BCH + DATA
print_bits("data: ", swap + crc_len, data_len);
printf("\n");
print_bits("bch: ", swap, crc_len);
printf("\n");
test(&bch, "BCH+DATA: ", swap, length);
int rot[MAX_LENGTH];
// DATA + HCB
bytes_to_bits(bytes, orig_bits, length+1);
memcpy(rot+data_len, orig_bits + 1, crc_len * sizeof(int));
rotate_bits(orig_bits+1+crc_len, temp, data_len);
memcpy(rot, temp, data_len * sizeof(int));
print_bits("data: ", rot, data_len);
printf("\n");
print_bits("hcb: ", rot+data_len, crc_len);
printf("\n");
// Now DATA+HCB
test(&bch, "DATA+HCB: ", rot, length);
// ATAD+BCH
bytes_to_bits(bytes, orig_bits, length+1);
// h+a
memcpy(rot, orig_bits+1+crc_len, data_len * sizeof(int));
rotate_bits(orig_bits+1, temp, crc_len);
memcpy(rot+data_len, temp, crc_len * sizeof(int));
// Now ATAD+BCH
print_bits("atad: ", rot, data_len);
printf("\n");
print_bits("bch: ", rot+data_len, crc_len);
printf("\n");
test(&bch, "ATAD+BCH: ", rot, length);
// HCB+DATA
bytes_to_bits(bytes, orig_bits, length+1);
memcpy(rot, orig_bits+1, crc_len * sizeof(int));
rotate_bits(orig_bits+1+crc_len, temp, data_len);
memcpy(rot+crc_len, temp, data_len * sizeof(int));
print_bits("data: ", rot+crc_len, data_len);
printf("\n");
print_bits("hcb: ", rot, crc_len);
printf("\n");
// Now HCB+DATA
test(&bch, "HCB+DATA: ", rot, length);
// BCH+ATAD
bytes_to_bits(bytes, orig_bits, length+1);
memcpy(rot+crc_len, orig_bits+1+crc_len, data_len * sizeof(int));
rotate_bits(orig_bits+1, temp, crc_len);
memcpy(rot, temp, crc_len * sizeof(int));
print_bits("atad: ", rot + crc_len, data_len);
printf("\n");
print_bits("bch: ", rot, crc_len);
printf("\n");
// Now BCH+ATAD
test(&bch, "BCH+ATAD: ", rot, length);
}
}

View File

@ -1282,12 +1282,15 @@ void config_init (char *fname, struct audio_s *p_audio_config,
else if (strcasecmp(t,"EAS") == 0) {
n = MAX_BAUD-2; // Hack - See special case later.
}
else if (strcasecmp(t,"EOTD") == 0) {
n = MAX_BAUD-3; // Hack - See special case later.
}
else {
n = atoi(t);
}
if (n >= MIN_BAUD && n <= MAX_BAUD) {
p_audio_config->achan[channel].baud = n;
if (n != 300 && n != 1200 && n != 2400 && n != 4800 && n != 9600 && n != 19200 && n != MAX_BAUD-1 && n != MAX_BAUD-2) {
if (n != 300 && n != 1200 && n != 2400 && n != 4800 && n != 9600 && n != 19200 && n != MAX_BAUD-1 && n != MAX_BAUD-2 && n != MAX_BAUD-3) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Warning: Non-standard data rate of %d bits per second. Are you sure?\n", line, n);
}
@ -1339,6 +1342,12 @@ void config_init (char *fname, struct audio_s *p_audio_config,
p_audio_config->achan[channel].space_freq = 1563; // Actually 1562.5 - logic 0.
// ? strlcpy (p_audio_config->achan[channel].profiles, "D", sizeof(p_audio_config->achan[channel].profiles));
}
else if (p_audio_config->achan[channel].baud == MAX_BAUD-3) {
p_audio_config->achan[channel].modem_type = MODEM_EOTD;
p_audio_config->achan[channel].mark_freq = 1200;
p_audio_config->achan[channel].space_freq = 1200;
p_audio_config->achan[channel].baud = 1200;
}
else {
p_audio_config->achan[channel].modem_type = MODEM_SCRAMBLE;
p_audio_config->achan[channel].mark_freq = 0;

View File

@ -2350,6 +2350,9 @@ static void aprs_user_defined (decode_aprs_t *A, char *info, int ilen)
A->g_altitude_ft = DW_METERS_TO_FEET(alt_meters);
strcpy (A->g_mfr, "");
}
else if (info[0] == '{' && info[1] == USER_DEF_USER_ID && info[2] == USER_DEF_TYPE_EOTD) {
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "End-of-Train Device CSV Data");
}
else if (strncmp(info, "{{", 2) == 0) {
snprintf (A->g_msg_type, sizeof(A->g_msg_type), "User-Defined Experimental");
}

View File

@ -134,6 +134,7 @@ int demod_init (struct audio_s *pa)
case MODEM_AFSK:
case MODEM_EAS:
case MODEM_EOTD:
if (save_audio_config_p->achan[chan].modem_type == MODEM_EAS) {
if (save_audio_config_p->achan[chan].fix_bits != RETRY_NONE) {
@ -969,6 +970,7 @@ void demod_process_sample (int chan, int subchan, int sam)
case MODEM_AFSK:
case MODEM_EAS:
case MODEM_EOTD:
if (save_audio_config_p->achan[chan].decimate > 1) {
@ -1086,7 +1088,8 @@ alevel_t demod_get_audio_level (int chan, int subchan)
alevel.rec = (int) (( D->alevel_rec_peak - D->alevel_rec_valley ) * 50.0f + 0.5f);
if (save_audio_config_p->achan[chan].modem_type == MODEM_AFSK ||
save_audio_config_p->achan[chan].modem_type == MODEM_EAS) {
save_audio_config_p->achan[chan].modem_type == MODEM_EAS ||
save_audio_config_p->achan[chan].modem_type == MODEM_EOTD) {
/* For AFSK, we have mark and space amplitudes. */

View File

@ -437,6 +437,9 @@ int main (int argc, char *argv[])
}
else if (strcasecmp(optarg, "EAS") == 0) {
B_opt = 23456; // See special case below.
}
else if (strcasecmp(optarg, "EOTD") == 0) {
B_opt = 34567; // See special case below.
}
else {
B_opt = atoi(optarg);
@ -760,7 +763,13 @@ int main (int argc, char *argv[])
audio_config.achan[0].space_freq = 1563; // Actually 1562.5 - logic 0.
strlcpy (audio_config.achan[0].profiles, "D", sizeof(audio_config.achan[0].profiles));
}
else {
else if (audio_config.achan[0].baud == 34567) {
audio_config.achan[0].modem_type = MODEM_EOTD;
audio_config.achan[0].baud = 1200;
audio_config.achan[0].mark_freq = 1200;
audio_config.achan[0].space_freq = 1800;
}
else {
audio_config.achan[0].modem_type = MODEM_SCRAMBLE;
audio_config.achan[0].mark_freq = 0;
audio_config.achan[0].space_freq = 0;
@ -1463,6 +1472,7 @@ static void usage (char **argv)
dw_printf (" 9600 bps and up uses K9NG/G3RUH standard.\n");
dw_printf (" AIS for ship Automatic Identification System.\n");
dw_printf (" EAS for Emergency Alert System (EAS) Specific Area Message Encoding (SAME).\n");
dw_printf (" EOTD for End-of-train devices.\n");
dw_printf (" -g Force G3RUH modem regardless of speed.\n");
dw_printf (" -j 2400 bps QPSK compatible with direwolf <= 1.5.\n");
dw_printf (" -J 2400 bps QPSK compatible with MFJ-2400.\n");

472
src/eotd.c Normal file
View File

@ -0,0 +1,472 @@
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2022 David Tiller, K4DET
//
// 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, see <http://www.gnu.org/licenses/>.
//
/********************************************************************************
*
* File: eotd.c
*
* Purpose: Functions for processing received EOTD transmissions and
* converting to text format.
*
*******************************************************************************/
#include "direwolf.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <inttypes.h>
#include <sys/time.h>
#include "textcolor.h"
#include "eotd_defs.h"
#include "eotd.h"
#undef EOTD_RAW
#define EOTD_TIMESTAMP
#define EOTD_APPEND_HEX
/*-------------------------------------------------------------------
*
* Convert EOTD binary block (from HDLC frame) to text.
*
* In: Pointer to EOTD binary block and number of bytes.
* Out: text.
*
*--------------------------------------------------------------------*/
void add_comma(char *text, int text_size) {
strlcat(text, ",", text_size);
}
int get_chain(uint64_t pkt, char *text, int text_size) {
uint32_t val;
val = pkt & 0x03ULL;
strlcat(text, "chain=", text_size);
switch(val) {
case 0:
strlcat(text, "MIDDLE", text_size);
break;
case 1:
strlcat(text, "LAST", text_size);
break;
case 2:
strlcat(text, "FIRST", text_size);
break;
case 3:
strlcat(text, "ONLY", text_size);
break;
}
return val;
}
void get_r2f_dev_batt_stat(uint64_t pkt, char *text, int text_size) {
uint32_t val;
pkt >>= 2;
val = pkt & 0x03ULL;
strlcat(text, "devbat=", text_size);
switch(val) {
case 3:
strlcat(text, "OK", text_size);
break;
case 2:
strlcat(text, "WEAK", text_size);
break;
case 1:
strlcat(text, "VERY_WEAK", text_size);
break;
case 0:
strlcat(text, "NOT_MONITORED", text_size);
break;
}
}
void get_r2f_msg_id_type(uint64_t pkt, char *text, int text_size) {
uint32_t val;
char temp[32];
uint64_t temp_pkt = pkt >> 4;
val = temp_pkt & 0x07ULL;
strlcat(text, "msgid=", text_size);
switch(val) {
case 0:
strlcat(text, "ONEWAY", text_size);
break;
case 7: // TEST button, maybe
// Test the CONFIRM bit
if ((pkt & 0x10000000000ULL) == 0) {
strlcat(text, "TEST/ARM_REQ", text_size);
} else {
strlcat(text, "ARM_CONFIRM", text_size);
}
break;
default:
sprintf(temp, "CUSTOM(%d)", val);
strlcat(text, temp, text_size);
break;
}
}
void get_r2f_unit_addr_code(uint64_t pkt, char *text, int text_size) {
uint32_t val;
char temp[32];
pkt >>= 7;
val = pkt & 0x1ffffULL;
strlcat(text, "unit_addr=", text_size);
sprintf(temp, "%d", val);
strlcat(text, temp, text_size);
}
void get_r2f_brake_pressure(uint64_t pkt, char *text, int text_size) {
uint32_t val;
char temp[32];
pkt >>= 24;
val = pkt & 0x7fULL;
strlcat(text, "brake_status=", text_size);
switch (val) {
case 127:
strlcat(text, "GO", text_size);
break;
case 126:
strlcat(text, "NO-GO", text_size);
break;
default:
if (val < 45) {
sprintf(temp, "NO-GO(%d psig)", val);
} else {
sprintf(temp, "GO(%d psig)", val);
}
strlcat(text, temp, text_size);
break;
}
}
void get_r2f_disc_bits(uint64_t pkt, char *text, int text_size) {
uint32_t val;
char temp[32];
pkt >>= 31;
val = pkt & 0xffULL;
strlcat(text, "disc_bits=", text_size);
sprintf(temp, "%02x", val);
strlcat(text, temp, text_size);
}
void get_r2f_valve_bit(uint64_t pkt, char *text, int text_size) {
uint32_t val;
pkt >>= 39;
val = pkt & 0x01;
strlcat(text, "valve=", text_size);
strlcat(text, val == 0 ? "FAILED" : "OPERATIONAL", text_size);
}
void get_r2f_confirm_bit(uint64_t pkt, char *text, int text_size) {
uint32_t val;
pkt >>= 40;
val = pkt & 0x01;
strlcat(text, "confirm=", text_size);
strlcat(text, val == 0 ? "UPDATE" : "RESPONSE", text_size);
}
void get_r2f_disc_bit1(uint64_t pkt, char *text, int text_size) {
uint32_t val;
char temp[32];
pkt >>= 41;
val = pkt & 0x01;
strlcat(text, "disc_bit_1=", text_size);
sprintf(temp, "%d", val);
strlcat(text, temp, text_size);
}
void get_r2f_motion_bit(uint64_t pkt, char *text, int text_size) {
uint32_t val;
pkt >>= 42;
val = pkt & 0x01;
strlcat(text, "motion=", text_size);
strlcat(text, val == 0 ? "STOPPED/NOT_MONITORED" : "IN_MOTION", text_size);
}
void get_r2f_mkr_light_batt_bit(uint64_t pkt, char *text, int text_size) {
uint32_t val;
pkt >>= 43;
val = pkt & 0x01;
strlcat(text, "light_batt=", text_size);
strlcat(text, val == 0 ? "OK/NOT_MONITORED" : "WEAK", text_size);
}
void get_r2f_mkr_light_bit(uint64_t pkt, char *text, int text_size) {
uint32_t val;
pkt >>= 44;
val = pkt & 0x01;
strlcat(text, "light=", text_size);
strlcat(text, val == 0 ? "OFF/NOT_MONITORED" : "ON", text_size);
}
void decode_basic_r2f(uint64_t pkt, char *text, int text_size) {
strlcat(text, "block=BASIC", text_size);
add_comma(text, text_size);
get_r2f_unit_addr_code(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_dev_batt_stat(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_msg_id_type(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_brake_pressure(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_disc_bits(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_valve_bit(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_confirm_bit(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_disc_bit1(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_motion_bit(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_mkr_light_batt_bit(pkt, text, text_size);
add_comma(text, text_size);
get_r2f_mkr_light_bit(pkt, text, text_size);
}
void get_f2r_msg_id_type(uint64_t pkt, char *text, int text_size) {
uint32_t val;
pkt >>= 2;
val = pkt & 0x07ULL;
strlcat(text, "msgid=", text_size);
strlcat(text, val == 0 ? "VALID" : "INVALID", text_size);
}
void get_f2r_unit_addr_code(uint64_t pkt, char *text, int text_size) {
uint32_t val;
char temp[32];
pkt >>= 5;
val = pkt & 0x1ffffULL;
strlcat(text, "unit_addr=", text_size);
sprintf(temp, "%d", val);
strlcat(text, temp, text_size);
}
void get_f2r_command(uint64_t pkt, char *text, int text_size) {
uint32_t val;
char temp[32];
pkt >>= 22;
val = pkt & 0xff;
strlcat(text, "cmd=", text_size);
switch(val) {
case 0x55:
strlcat(text, "STATUS_REQ", text_size);
break;
case 0xaa:
strlcat(text, "APPLY_BRAKES", text_size);
break;
default:
sprintf(temp, "UNKNOWN(%d)", val);
strlcat(text, temp, text_size);
break;
}
}
int get_option_format(uint64_t pkt, char *text, int text_size) {
uint32_t val;
pkt >>= 2;
val = pkt & 0x01;
return val;
}
void decode_basic_f2r(uint64_t pkt, char *text, int text_size) {
strlcat(text, "block=BASIC", text_size);
add_comma(text, text_size);
get_f2r_unit_addr_code(pkt, text, text_size);
add_comma(text, text_size);
get_f2r_msg_id_type(pkt, text, text_size);
add_comma(text, text_size);
get_f2r_command(pkt, text, text_size);
}
void decode_r2f_option_block(uint64_t pkt, char *text, int text_size) {
int indicator = get_option_format(pkt, text, text_size);
if (indicator == 0) {
// BINARY
strlcat(text, "block=OPT_BINARY", text_size);
add_comma(text, text_size);
int type, val;
char temp[32];
pkt >>= 3; // Skip chain and format bits
for (int i = 0; i < 3; i++ ) {
type = pkt & 0x7f;
pkt >>= 7;
val = pkt & 0x7f;
pkt >>= 7;
// 'No Data' indicator
if (type == 0) {
continue;
}
sprintf(temp, "TYPE_%c=%d", 'A' + i, type);
strlcat(text, temp, text_size);
add_comma(text, text_size);
sprintf(temp, "VALUE_%c=%d", 'A' + i, val);
strlcat(text, temp, text_size);
if (i != 2) add_comma(text, text_size);
}
} else {
// ASCII
strlcat(text, "block=OPT_ASCII", text_size);
add_comma(text, text_size);
char msg[8] = { 0 };
pkt >>= 3; // Skip chain and format bits
for (int i = 0; i < 6; i++) {
msg[i] = pkt & 0x7f;
pkt >>= 7;
}
strlcat(text, "message=", text_size);
strlcat(text, msg, text_size);
}
}
void decode_f2r_option_block(uint64_t pkt, char *text, int text_size) {
strlcat(text, "TODO: F2R OPTION BLOCK", text_size);
}
void eotd_to_text (unsigned char *eotd, int eotd_len, char *text, int text_size)
{
assert (eotd_len == EOTD_LENGTH + 1);
uint64_t pkt = 0ULL;
for (int i = 0; i < EOTD_LENGTH; i++) {
pkt <<= 8;
pkt |= eotd[i];
}
*text = '\0';
char eotd_type = eotd[EOTD_LENGTH];
#ifndef EOTD_RAW
if (eotd_type == EOTD_TYPE_F2R) {
//strlcat(text, "FRONT>REAR:", text_size);
strlcat(text, ":dir=f2r,", text_size);
} else {
//strlcat(text, "REAR>FRONT:", text_size);
strlcat(text, ":dir=r2f,", text_size);
}
#ifdef EOTD_TIMESTAMP
struct timeval tv;
gettimeofday(&tv, NULL);
struct tm *now = localtime(&tv.tv_sec);
char date_buffer[128];
strlcat(text, "ts=", text_size);
sprintf(date_buffer, "%4d-%02d-%02dT%02d:%02d:%02d.%03d,",
now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,
now->tm_hour, now->tm_min, now->tm_sec, (int) (tv.tv_usec / 1000));
strlcat(text, date_buffer, text_size);
#endif
int chain = get_chain(pkt, text, text_size);
add_comma(text, text_size);
// check for 'first' block - it's always the basic block
if (chain & 0x02) {
if (eotd_type == EOTD_TYPE_R2F) {
decode_basic_r2f(pkt, text, text_size);
} else {
decode_basic_f2r(pkt, text, text_size);
}
} else {
if (eotd_type == EOTD_TYPE_R2F) {
decode_r2f_option_block(pkt, text, text_size);
} else {
decode_f2r_option_block(pkt, text, text_size);
}
}
#ifdef EOTD_APPEND_HEX
char hex[64];
add_comma(text, text_size);
strlcat(text, "hex=", text_size);
for (int i = 56; i >= 0; i -= 8) {
sprintf(hex, "%02x ", (unsigned char) (pkt >> i) & 0xff);
strlcat(text, hex, text_size);
}
text[strlen(text) - 1] = '\0'; // zap trailing space
#endif
#else
char temp[8];
for (int i = 0; i < 8; i++) {
sprintf(temp, " %02x", eotd[i]);
strlcat(text, temp, text_size);
}
#endif
}

6
src/eotd.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __EOTD_H
#define __EOTD_H
void eotd_to_text (unsigned char *ais, int ais_len, char *text, int text_size);
#endif

14
src/eotd_defs.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __EOTD_DEFS
#define __EOTD_DEFS
#define EOTD_LENGTH 8
#define EOTD_PREAMBLE_AND_BARKER_CODE 0x48eaaaaa00000000ULL
#define EOTD_PREAMBLE_MASK 0xffffffff00000000ULL
#define HOTD_PREAMBLE_AND_BARKER_CODE 0x9488f1aa00000000ULL
#define HOTD_PREAMBLE_MASK 0xffffffff00000000ULL
#define EOTD_TYPE_F2R 'F'
#define EOTD_TYPE_R2F 'R'
#endif

102
src/eotd_send.c Normal file
View File

@ -0,0 +1,102 @@
#include "direwolf.h"
#include <stdio.h>
#include "eotd_send.h"
#include "audio.h"
#include "gen_tone.h"
#include "eotd_defs.h"
#define EOTD_SILENCE_SAMPLES 1000
//#define EOTD_SEND_DEBUG
static int eotd_fs[] = {1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0};
static int hotd_fs[] = {1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1 };
void my_tone_gen_put_bit (int chan, int bit) {
#ifdef EOTD_SEND_DEBUG
printf("mytone bit %d\n", bit);
#endif
tone_gen_put_bit (chan, bit);
}
void my_gen_tone_put_sample (int chan, int a, int sam) {
#ifdef EOTD_SEND_DEBUG
printf("mysilence sample %d\n", sam);
#endif
gen_tone_put_sample (chan, a, sam);
}
void send_preamble(int chan, char type) {
int bit = 0;
int preamble_count;
int fs_count;
int *fs;
if (type == EOTD_TYPE_R2F) {
bit = 0;
preamble_count = 69;
fs_count = sizeof(eotd_fs) / sizeof(int);
fs = eotd_fs;
} else {
preamble_count = 456;
fs_count = sizeof(hotd_fs) / sizeof(int);
fs = hotd_fs;
}
for (int i = 0; i < preamble_count; i++) {
my_tone_gen_put_bit (chan, bit);
bit ^= 1;
}
#ifdef EOTD_SEND_DEBUG
printf("end-of-preamble\n");
#endif
// send FS
for (int i = 0; i < fs_count; i++) {
my_tone_gen_put_bit (chan, fs[i]);
}
#ifdef EOTD_SEND_DEBUG
printf("end-of-fs\n");
#endif
}
void send_silence(int chan) {
int a = ACHAN2ADEV(chan);
for (int i = 0; i < EOTD_SILENCE_SAMPLES; i++) {
my_gen_tone_put_sample (chan, a, 0);
}
}
int eotd_send_block (int chan, char *str, char type) {
unsigned int b[EOTD_LENGTH];
int status = sscanf(str, "%x %x %x %x %x %x %x %x", b, b+1, b+2, b+3, b+4, b+5, b+6, b+7);
if (status != EOTD_LENGTH) {
fprintf(stderr, "Error: expected 8, read %d", status);
return -1;
}
send_preamble(chan, type);
for (int i = 7; i >= 0; i--) {
int byte = b[i]; // Copy this non-destructively so we can repeat it later, per spec.
for (int j = 0; j < 8; j++) {
int bit = byte & 0x01;
byte >>= 1;
my_tone_gen_put_bit (chan, bit);
}
}
#ifdef EOTD_SEND_DEBUG
printf("end-of-data\n");
#endif
send_silence(chan);
return 0;
}

6
src/eotd_send.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __EOTD_SEND_H
#define __EOTD_SEND_H
int eotd_send_block (int chan, char *str, char type);
#endif

View File

@ -76,6 +76,8 @@
#include "morse.h"
#include "dtmf.h"
#include "fx25.h"
#include "eotd_send.h"
#include "eotd_defs.h"
/* Own random number generator so we can get */
@ -97,6 +99,10 @@ static int audio_file_close (void);
static int g_add_noise = 0;
static float g_noise_level = 0;
static int g_morse_wpm = 0; /* Send morse code at this speed. */
static int g_eotd_type = 0;
static int byte_count; /* Number of data bytes written to file. */
/* Will be written to header when file is closed. */
static struct audio_s modem;
@ -115,6 +121,11 @@ static void send_packet (char *str)
morse_send (0, str, g_morse_wpm, 100, 100);
}
else if (g_eotd_type > 0) {
for (c=0; c<modem.adev[0].num_channels; c++) {
eotd_send_block (c, str, g_eotd_type);
}
}
else {
pp = ax25_from_text (str, 1);
if (pp == NULL) {
@ -227,7 +238,7 @@ int main(int argc, char **argv)
/* ':' following option character means arg is required. */
c = getopt_long(argc, argv, "gjJm:s:a:b:B:r:n:N:o:z:82M:X:",
c = getopt_long(argc, argv, "gjJm:s:a:b:B:r:n:N:o:z:82M:X:e:",
long_options, &option_index);
if (c == -1)
break;
@ -462,6 +473,20 @@ int main(int argc, char **argv)
usage (argv);
break;
case 'e':
g_eotd_type = optarg[0];
if (g_eotd_type != EOTD_TYPE_F2R && g_eotd_type != EOTD_TYPE_R2F) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("EOTD type must be %c or %c\n", EOTD_TYPE_F2R, EOTD_TYPE_R2F);
exit(EXIT_FAILURE);
}
modem.achan[0].mark_freq = 1800; // NOTE: THIS IS BACKWARDS UNTIL REV 1.7
modem.achan[0].space_freq = 1200; // backwards, too.
modem.achan[0].baud = 1200;
break;
default:
/* Should not be here. */
@ -757,9 +782,6 @@ static FILE *out_fp = NULL;
static struct wav_header header;
static int byte_count; /* Number of data bytes written to file. */
/* Will be written to header when file is closed. */
static int audio_file_open (char *fname, struct audio_s *pa)
{

View File

@ -1,5 +1,5 @@
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
////
//// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ
//
@ -33,6 +33,7 @@
#include <assert.h>
#include <string.h>
#include <stdint.h> // uint64_t
#include <stdlib.h>
//#include "tune.h"
#include "demod.h"
@ -46,14 +47,15 @@
#include "demod_9600.h" /* for descramble() */
#include "ptt.h"
#include "fx25.h"
#include "bch.h"
#include "eotd_defs.h"
//#define EOTD_DEBUG
//#define TEST 1 /* Define for unit testing. */
//#define DEBUG3 1 /* monitor the data detect signal. */
/*
* Minimum & maximum sizes of an AX.25 frame including the 2 octet FCS.
*/
@ -111,6 +113,12 @@ struct hdlc_state_s {
int eas_plus_found; /* "+" seen, indicating end of geographical area list. */
int eas_fields_after_plus; /* Number of "-" characters after the "+". */
uint64_t eotd_acc; /* Accumulate last recent 32 bits for EOTD. */
char eotd_type; /* E for End of train, H for head of train */
int eotd_gathering; /* Decoding in progress - valid frame. */
};
static struct hdlc_state_s hdlc_state[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS];
@ -224,7 +232,6 @@ static int my_rand (void) {
static void eas_rec_bit (int chan, int subchan, int slice, int raw, int future_use)
{
struct hdlc_state_s *H;
/*
* Different state information for each channel / subchannel / slice.
*/
@ -400,6 +407,190 @@ a good modem here and providing a result when it is received.
*/
int is_eotd_valid(struct hdlc_state_s *H) {
/*
The data as received in the frame buffer is in HCB+ATAD format; that is, the first
(leftmost) bit is the dummy bit or odd parity bit (which is the last bit transmitted)
followed by the BCH code (LSB first, so HCB) followed by the DATA, LSB first (ATAD).
The apply_bch funtion requires the packet in int[63] format, with the bits arranged
in either BCH+ATAD or ATAD+BCH format. Very odd. We'll use the first one.
The HCB for R2F packets must be XOR-ed with 0EED4 - since we have a leading dummy bit,
we XOR with 0EED4 >> 1.
*/
static uint8_t r2f_mask[] = {0x07, 0x76, 0xa0 };
static bch_t *bch_r2f = NULL;
static bch_t *bch_f2r = NULL;
int bits[64];
assert(H != NULL);
// +1 for 'type' byte at end
assert(H->frame_len == EOTD_LENGTH + 1);
if(bch_r2f == NULL) {
bch_r2f = malloc(sizeof(bch_t));
int status = init_bch(bch_r2f, 6, 63, 3);
if (status != 0) {
fprintf(stderr, "BCH_R2F initialization failed: %d", status);
free(bch_r2f);
bch_r2f = NULL;
return status;
}
}
if(bch_f2r == NULL) {
bch_f2r = malloc(sizeof(bch_t));
int status = init_bch(bch_f2r, 6, 63, 6);
if (status != 0) {
fprintf(stderr, "BCH_F2R initialization failed: %d", status);
free(bch_f2r);
bch_f2r = NULL;
return status;
}
}
int temp_bits[64];
bch_t *temp_bch;
if (H->eotd_type == EOTD_TYPE_F2R) {
temp_bch = bch_f2r;
} else {
temp_bch = bch_r2f;
// The HCB needs to be XOR'ed with a special constant.
for (int i = 0; i < sizeof(r2f_mask); i++) {
H->frame_buf[i] ^= r2f_mask[i];
}
}
int crc_len = temp_bch->n - temp_bch->k;
bytes_to_bits(H->frame_buf, bits, 64);
// +1 is to skip the dummy/parity bit.
rotate_bits(bits + 1 , temp_bits, crc_len);
memcpy(bits + 1, temp_bits, crc_len * sizeof(int));
// Note: bits are changed in-place.
int corrected = apply_bch(temp_bch, bits + 1);
// Put back in HCB+ATAD format
rotate_bits(bits + 1, temp_bits, crc_len);
memcpy(bits + 1, temp_bits, crc_len * sizeof(int));
bits_to_bytes(bits, H->frame_buf, 64);
// Put the XOR-ed bits back.
if (H->eotd_type == EOTD_TYPE_R2F) {
// The HCB needs to be XOR'ed with a special constant.
for (int i = 0; i < sizeof(r2f_mask); i++) {
H->frame_buf[i] ^= r2f_mask[i];
}
}
return corrected;
}
/***********************************************************************************
*
* Name: eotd_rec_bit
*
* Purpose: Extract EOTD trasmissions from a stream of bits.
*
* Inputs: chan - Channel number.
*
* subchan - This allows multiple demodulators per channel.
*
* slice - Allows multiple slicers per demodulator (subchannel).
*
* raw - One bit from the demodulator.
* should be 0 or 1.
*
* future_use - Not implemented yet. PSK already provides it.
*
*
* Description: This is called once for each received bit.
* For each valid transmission, process_rec_frame()
* is called for further processing.
*
***********************************************************************************/
static void eotd_rec_bit (int chan, int subchan, int slice, int raw, int future_use)
{
struct hdlc_state_s *H;
/*
* Different state information for each channel / subchannel / slice.
*/
H = &hdlc_state[chan][subchan][slice];
#ifdef EOTD_DEBUG
// dw_printf("chan=%d subchan=%d slice=%d raw=%d\n", chan, subchan, slice, raw);
dw_printf("%d ", raw);
#endif
//dw_printf ("slice %d = %d\n", slice, raw);
// Accumulate most recent 64 bits in LSB-first order.
H->eotd_acc >>= 1;
if (raw) {
H->eotd_acc |= 0x8000000000000000UL;
}
int done = 0;
if (!H->eotd_gathering &&
((H->eotd_acc & EOTD_PREAMBLE_MASK) == EOTD_PREAMBLE_AND_BARKER_CODE ||
(H->eotd_acc & HOTD_PREAMBLE_MASK) == HOTD_PREAMBLE_AND_BARKER_CODE)) {
#ifdef EOTD_DEBUG
dw_printf ("Barker Code Found %llx\n", H->eotd_acc);
#endif
H->eotd_type = (H->eotd_acc & EOTD_PREAMBLE_MASK) == EOTD_PREAMBLE_AND_BARKER_CODE ? EOTD_TYPE_R2F : EOTD_TYPE_F2R;
H->olen = 0;
H->eotd_gathering = 1;
H->frame_len = 0;
H->eotd_acc = 0ULL;
}
else if (H->eotd_gathering) {
H->olen++;
if (H->olen == EOTD_LENGTH * 8) {
H->frame_len = EOTD_LENGTH + 1; // appended type
for (int i = EOTD_LENGTH -1; i >=0; i--) {
H->frame_buf[i] = H->eotd_acc & 0xff;
H->eotd_acc >>= 8;
}
H->frame_buf[EOTD_LENGTH] = H->eotd_type;
done = 1;
#ifdef EOTD_DEBUG
for (int ii=0; ii < EOTD_LENGTH; ii++) {dw_printf("%02x ", H->frame_buf[ii]); } dw_printf("\n");
#endif
}
}
if (done) {
#ifdef DEBUG_E
dw_printf ("frame_buf %d = %*s\n", slice, H->frame_len, H->frame_buf);
#endif
if (is_eotd_valid(H) >= 0) {
alevel_t alevel = demod_get_audio_level (chan, subchan);
multi_modem_process_rec_frame (chan, subchan, slice, H->frame_buf, H->frame_len, alevel, 0, 0);
} else {
#ifdef EOTD_DEBUG
print_bytes("BCH failed for packet (type byte appended) ", H->frame_buf, H->frame_len);
#endif
}
H->eotd_acc = 0;
H->eotd_gathering = 0;
H->olen = 0;
H->frame_len = 0;
}
return;
} // end eotd_rec_bit
/***********************************************************************************
*
* Name: hdlc_rec_bit
@ -463,6 +654,13 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
return;
}
// EOTD does not use HDLC.
if (g_audio_p->achan[chan].modem_type == MODEM_EOTD) {
eotd_rec_bit (chan, subchan, slice, raw, not_used_remove);
return;
}
/*
* Different state information for each channel / subchannel / slice.
*/
@ -564,7 +762,7 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
if (actual_fcs == expected_fcs) {
alevel_t alevel = demod_get_audio_level (chan, subchan);
multi_modem_process_rec_frame (chan, subchan, slice, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE, 0); /* len-2 to remove FCS. */
:ulti_modem_process_rec_frame (chan, subchan, slice, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE, 0); /* len-2 to remove FCS. */
}
else {
@ -802,6 +1000,7 @@ int hdlc_rec_data_detect_any (int chan)
} /* end hdlc_rec_data_detect_any */
/* end hdlc_rec.c */

View File

@ -103,6 +103,7 @@
#include "fx25.h"
#include "version.h"
#include "ais.h"
#include "eotd.h"
@ -342,6 +343,13 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c
// alevel gets in there somehow making me question why it is passed thru here.
}
else if (save_audio_config_p->achan[chan].modem_type == MODEM_EOTD) {
char text[1024];
eotd_to_text (fbuf, flen, text, sizeof(text));
char monfmt[2048];
snprintf (monfmt, sizeof(monfmt), "EOTD>%s%1d%1d:{%c%c%s", APP_TOCALL, MAJOR_VERSION, MINOR_VERSION, USER_DEF_USER_ID, USER_DEF_TYPE_EOTD, text);
pp = ax25_from_text (monfmt, 1);
}
else {
pp = ax25_from_frame (fbuf, flen, alevel);
}

78
src/pkttest.c Normal file
View File

@ -0,0 +1,78 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "bch.h"
#include "eotd.h"
#include "eotd_defs.h"
void dump(uint8_t *bytes, char type) {
unsigned char eotd[9];
for (int i = 0; i < 8; i++) {
eotd[i] = bytes[i] & 0xff;
}
eotd[8] = type;
// Slice packet
char buffer[512];
eotd_to_text(eotd, 9, buffer, sizeof(buffer));
printf("%s\n", buffer);
};
void rotate_bytes(uint8_t *src, uint8_t *dest, int count) {
for (int i = 0; i < count; i++) {
dest[count - i - 1] = rotate_byte(src[i]);
}
}
int main(int argc, char **argv) {
bch_t bch;
uint8_t bytes[8];
int m, length, t;
int count = 0;
int rev = 0;
char type = EOTD_TYPE_R2F;
if (argc < 5) {
fprintf(stderr, "Expecting 4+ arguments - m, length, t, type (F or R) and optionally rev to reverse the input bytes.\n");
fprintf(stderr, "THE BCH CODE IS NOT VERIFIED!\n");
return -1;
}
sscanf(argv[1], "%d", &m);
sscanf(argv[2], "%d", &length);
sscanf(argv[3], "%d", &t);
sscanf(argv[4], "%c", &type);
if (argc > 5) {
if (strcasecmp(argv[5], "rev") == 0) {
rev = 1;
}
}
init_bch(&bch, m, length, t);
while (1) {
for (int i = 0; i < 8; i++) {
int t;
int status = scanf("%x ", &t);
bytes[i] = t;
if (status == EOF) {
return 0;
}
if (status != 1) {
fprintf(stderr, "Error: %d", status);
}
}
if (rev) {
uint8_t temp[8];
rotate_bytes(bytes, temp, 8);
memcpy(bytes, temp, 8);
}
printf("%04d,", count++);
dump(bytes, type);
}
return 0;
}

View File

@ -19,3 +19,6 @@
#define USER_DEF_TYPE_AIS 'A' // data type A for AIS NMEA sentence
#define USER_DEF_TYPE_EAS 'E' // data type E for EAS broadcasts
#define USER_DEF_TYPE_EOTD 'R' // data type R for 'Railroad' broadcasts
#define USER_DEF_TYPE_DTMF 'T' // 'T' is used without constant in the code.