diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 46d3ac7..96409a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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() diff --git a/src/atest.c b/src/atest.c index 5c19775..79c20fd 100644 --- a/src/atest.c +++ b/src/atest.c @@ -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; diff --git a/src/audio.h b/src/audio.h index 61dec9d..786d27a 100644 --- a/src/audio.h +++ b/src/audio.h @@ -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. */ diff --git a/src/bch.c b/src/bch.c new file mode 100644 index 0000000..4207ca6 --- /dev/null +++ b/src/bch.c @@ -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 +#include +#include +#include +#include +#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; in = 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]); + } +} diff --git a/src/bch.h b/src/bch.h new file mode 100644 index 0000000..2b22af7 --- /dev/null +++ b/src/bch.h @@ -0,0 +1,41 @@ +#ifndef __BCH_H +#define __BCH_H +#include + +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 diff --git a/src/bch3.c b/src/bch3.c new file mode 100644 index 0000000..2f13e6e --- /dev/null +++ b/src/bch3.c @@ -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 +#include +#include + +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; ininf)) ); +} + + +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"); +} diff --git a/src/bchapply.c b/src/bchapply.c new file mode 100644 index 0000000..a66d7f1 --- /dev/null +++ b/src/bchapply.c @@ -0,0 +1,189 @@ +#include +#include +#include +#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); + } +} diff --git a/src/config.c b/src/config.c index 8588a8c..407650c 100644 --- a/src/config.c +++ b/src/config.c @@ -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; diff --git a/src/decode_aprs.c b/src/decode_aprs.c index 3afa377..5df125c 100644 --- a/src/decode_aprs.c +++ b/src/decode_aprs.c @@ -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"); } diff --git a/src/demod.c b/src/demod.c index 281367b..0340096 100644 --- a/src/demod.c +++ b/src/demod.c @@ -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. */ diff --git a/src/direwolf.c b/src/direwolf.c index 456b16f..589307c 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -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"); diff --git a/src/eotd.c b/src/eotd.c new file mode 100644 index 0000000..9167072 --- /dev/null +++ b/src/eotd.c @@ -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 . +// + + +/******************************************************************************** + * + * File: eotd.c + * + * Purpose: Functions for processing received EOTD transmissions and + * converting to text format. + * + *******************************************************************************/ + +#include "direwolf.h" + +#include +#include +#include +#include +#include +#include +#include + +#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 +} diff --git a/src/eotd.h b/src/eotd.h new file mode 100644 index 0000000..78e12f7 --- /dev/null +++ b/src/eotd.h @@ -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 diff --git a/src/eotd_defs.h b/src/eotd_defs.h new file mode 100644 index 0000000..1ed2622 --- /dev/null +++ b/src/eotd_defs.h @@ -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 diff --git a/src/eotd_send.c b/src/eotd_send.c new file mode 100644 index 0000000..635bb23 --- /dev/null +++ b/src/eotd_send.c @@ -0,0 +1,102 @@ +#include "direwolf.h" + +#include + +#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; +} diff --git a/src/eotd_send.h b/src/eotd_send.h new file mode 100644 index 0000000..c331265 --- /dev/null +++ b/src/eotd_send.h @@ -0,0 +1,6 @@ +#ifndef __EOTD_SEND_H +#define __EOTD_SEND_H + +int eotd_send_block (int chan, char *str, char type); + +#endif diff --git a/src/gen_packets.c b/src/gen_packets.c index b097790..bd5762e 100644 --- a/src/gen_packets.c +++ b/src/gen_packets.c @@ -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 #include #include // uint64_t +#include //#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 */ diff --git a/src/multi_modem.c b/src/multi_modem.c index c59af07..abc4edd 100644 --- a/src/multi_modem.c +++ b/src/multi_modem.c @@ -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); } diff --git a/src/pkttest.c b/src/pkttest.c new file mode 100644 index 0000000..5696d95 --- /dev/null +++ b/src/pkttest.c @@ -0,0 +1,78 @@ +#include +#include +#include +#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; +} diff --git a/src/version.h b/src/version.h index a09490c..2b58115 100644 --- a/src/version.h +++ b/src/version.h @@ -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. +