pull/4/head
Jason Milldrum 2015-10-02 08:00:53 -07:00
commit 75827c91e4
11 zmienionych plików z 1604 dodań i 0 usunięć

754
JTEncode.cpp 100644
Wyświetl plik

@ -0,0 +1,754 @@
/*
* JTEncode.cpp - JT65/JT9/WSPR encoder library for Arduino
*
* Copyright (C) 2015 Jason Milldrum <milldrum@gmail.com>
*
* Based on the algorithms presented in the WSJT software suite.
* Thanks to Andy Talbot G4JNT for the whitepaper on the WSPR encoding
* process that helped me to understand all of this.
*
* 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 3 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/>.
*/
#include <JTEncode.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdint.h>
/* Public Class Members */
JTEncode::JTEncode(void)
{
// Initialize the Reed-Solomon encoder
rs_inst = (struct rs *)(intptr_t)init_rs_int(6, 0x43, 3, 1, 51, 0);
}
/*
* jt65_encode(char * message, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
* a channel symbol table.
*
* message - Plaintext Type 6 message.
* symbols - Array of channel symbols to transmit retunred by the method.
* Ensure that you pass a uint8_t array of size JT65_SYMBOL_COUNT to the method.
*
*/
void JTEncode::jt65_encode(char * message, uint8_t * symbols)
{
// Ensure that the message text conforms to standards
// --------------------------------------------------
jt_message_prep(message);
// Bit packing
// -----------
uint8_t c[12];
jt65_bit_packing(message, c);
// Reed-Solomon encoding
// ---------------------
uint8_t s[JT65_ENCODE_COUNT];
rs_encode(c, s);
// Interleaving
// ------------
jt65_interleave(s);
// Gray Code
// ---------
jt_gray_code(s, JT65_ENCODE_COUNT);
// Merge with sync vector
// ----------------------
jt65_merge_sync_vector(s, symbols);
}
/*
* jt9_encode(char * message, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
* a channel symbol table.
*
* message - Plaintext Type 6 message.
* symbols - Array of channel symbols to transmit retunred by the method.
* Ensure that you pass a uint8_t array of size JT9_SYMBOL_COUNT to the method.
*
*/
void JTEncode::jt9_encode(char * message, uint8_t * symbols)
{
// Ensure that the message text conforms to standards
// --------------------------------------------------
jt_message_prep(message);
// Bit packing
// -----------
uint8_t c[13];
jt9_bit_packing(message, c);
// Convolutional Encoding
// ---------------------
uint8_t s[JT9_BIT_COUNT];
convolve(c, s, 13, JT9_BIT_COUNT);
// Interleaving
// ------------
jt9_interleave(s);
// Pack into 3-bit symbols
// -----------------------
uint8_t a[JT9_ENCODE_COUNT];
jt9_packbits(s, a);
// Gray Code
// ---------
jt_gray_code(a, JT9_ENCODE_COUNT);
// Merge with sync vector
// ----------------------
jt9_merge_sync_vector(a, symbols);
}
/*
* wspr_encode(char * call, char * loc, uint8_t dbm, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
*
* call - Callsign (6 characters maximum).
* loc - Maidenhead grid locator (4 charcters maximum).
* dbm - Output power in dBm.
* symbols - Array of channel symbols to transmit retunred by the method.
* Ensure that you pass a uint8_t array of size WSPR_SYMBOL_COUNT to the method.
*
*/
void JTEncode::wspr_encode(char * call, char * loc, uint8_t dbm, uint8_t * symbols)
{
// Ensure that the message text conforms to standards
// --------------------------------------------------
wspr_message_prep(call, loc, dbm);
// Bit packing
// -----------
uint8_t c[11];
wspr_bit_packing(c);
// Convolutional Encoding
// ---------------------
uint8_t s[WSPR_SYMBOL_COUNT];
convolve(c, s, 11, WSPR_BIT_COUNT);
// Interleaving
// ------------
wspr_interleave(s);
// Merge with sync vector
// ----------------------
wspr_merge_sync_vector(s, symbols);
}
/* Private Class Members */
uint8_t JTEncode::jt_code(char c)
{
// Validate the input then return the proper integer code.
// Return 255 as an error code if the char is not allowed.
if(isdigit(c))
{
return (uint8_t)(c - 48);
}
else if(c >= 'A' && c <= 'Z')
{
return (uint8_t)(c - 55);
}
else if(c == ' ')
{
return 36;
}
else if(c == '+')
{
return 37;
}
else if(c == '-')
{
return 38;
}
else if(c == '.')
{
return 39;
}
else if(c == '/')
{
return 40;
}
else if(c == '?')
{
return 41;
}
else
{
return 255;
}
}
uint8_t JTEncode::wspr_code(char c)
{
// Validate the input then return the proper integer code.
// Return 255 as an error code if the char is not allowed.
if(isdigit(c))
{
return (uint8_t)(c - 48);
}
else if(c == ' ')
{
return 36;
}
else if(c >= 'A' && c <= 'Z')
{
return (uint8_t)(c - 55);
}
else
{
return 255;
}
}
uint8_t JTEncode::gray_code(uint8_t c)
{
return (c >> 1) ^ c;
}
void JTEncode::jt_message_prep(char * message)
{
uint8_t i, j;
// Convert all chars to uppercase
for(i = 0; i < 13; i++)
{
if(islower(message[i]))
{
message[i] = toupper(message[i]);
}
}
// Pad the message with trailing spaces
uint8_t len = strlen(message);
if(len < 13)
{
for(i = len; i < 13; i++)
{
message[i] = ' ';
}
}
}
void JTEncode::wspr_message_prep(char * call, char * loc, uint8_t dbm)
{
// Callsign validation and padding
// -------------------------------
// If only the 2nd character is a digit, then pad with a space.
// If this happens, then the callsign will be truncated if it is
// longer than 5 characters.
if((call[1] >= '0' && call[1] <= '9') && (call[2] < '0' || call[2] > '9'))
{
memmove(call + 1, call, 5);
call[0] = ' ';
}
// Now the 3rd charcter in the callsign must be a digit
if(call[2] < '0' || call[2] > '9')
{
// TODO: need a better way to handle this
call[2] = '0';
}
// Ensure that the only allowed characters are digits and
// uppercase letters
uint8_t i;
for(i = 0; i < 6; i++)
{
call[i] = toupper(call[i]);
if(!(isdigit(call[i]) || isupper(call[i])))
{
call[i] = ' ';
}
}
memcpy(callsign, call, 6);
// Grid locator validation
for(i = 0; i < 4; i++)
{
loc[i] = toupper(loc[i]);
if(!(isdigit(loc[i]) || (loc[i] >= 'A' && loc[i] <= 'R')))
{
loc = "AA00";
}
}
memcpy(locator, loc, 4);
// Power level validation
// Only certain increments are allowed
if(dbm > 60)
{
dbm = 60;
}
const uint8_t valid_dbm[19] =
{0, 3, 7, 10, 13, 17, 20, 23, 27, 30, 33, 37, 40,
43, 47, 50, 53, 57, 60};
for(i = 0; i < 19; i++)
{
if(dbm == valid_dbm[i])
{
power = dbm;
}
}
// If we got this far, we have an invalid power level, so we'll round down
for(i = 1; i < 19; i++)
{
if(dbm < valid_dbm[i] && dbm >= valid_dbm[i - 1])
{
power = valid_dbm[i - 1];
}
}
}
void JTEncode::jt65_bit_packing(char * message, uint8_t * c)
{
uint32_t n1, n2, n3;
// Find the N values
n1 = jt_code(message[0]);
n1 = n1 * 42 + jt_code(message[1]);
n1 = n1 * 42 + jt_code(message[2]);
n1 = n1 * 42 + jt_code(message[3]);
n1 = n1 * 42 + jt_code(message[4]);
n2 = jt_code(message[5]);
n2 = n2 * 42 + jt_code(message[6]);
n2 = n2 * 42 + jt_code(message[7]);
n2 = n2 * 42 + jt_code(message[8]);
n2 = n2 * 42 + jt_code(message[9]);
n3 = jt_code(message[10]);
n3 = n3 * 42 + jt_code(message[11]);
n3 = n3 * 42 + jt_code(message[12]);
// Pack bits 15 and 16 of N3 into N1 and N2,
// then mask reset of N3 bits
n1 = (n1 << 1) + ((n3 >> 15) & 1);
n2 = (n2 << 1) + ((n3 >> 16) & 1);
n3 = n3 & 0x7fff;
// Set the freeform message flag
n3 += 32768;
c[0] = (n1 >> 22) & 0x003f;
c[1] = (n1 >> 16) & 0x003f;
c[2] = (n1 >> 10) & 0x003f;
c[3] = (n1 >> 4) & 0x003f;
c[4] = ((n1 & 0x000f) << 2) + ((n2 >> 26) & 0x0003);
c[5] = (n2 >> 20) & 0x003f;
c[6] = (n2 >> 14) & 0x003f;
c[7] = (n2 >> 8) & 0x003f;
c[8] = (n2 >> 2) & 0x003f;
c[9] = ((n2 & 0x0003) << 4) + ((n3 >> 12) & 0x000f);
c[10] = (n3 >> 6) & 0x003f;
c[11] = n3 & 0x003f;
}
void JTEncode::jt9_bit_packing(char * message, uint8_t * c)
{
uint32_t n1, n2, n3;
// Find the N values
n1 = jt_code(message[0]);
n1 = n1 * 42 + jt_code(message[1]);
n1 = n1 * 42 + jt_code(message[2]);
n1 = n1 * 42 + jt_code(message[3]);
n1 = n1 * 42 + jt_code(message[4]);
n2 = jt_code(message[5]);
n2 = n2 * 42 + jt_code(message[6]);
n2 = n2 * 42 + jt_code(message[7]);
n2 = n2 * 42 + jt_code(message[8]);
n2 = n2 * 42 + jt_code(message[9]);
n3 = jt_code(message[10]);
n3 = n3 * 42 + jt_code(message[11]);
n3 = n3 * 42 + jt_code(message[12]);
// Pack bits 15 and 16 of N3 into N1 and N2,
// then mask reset of N3 bits
n1 = (n1 << 1) + ((n3 >> 15) & 1);
n2 = (n2 << 1) + ((n3 >> 16) & 1);
n3 = n3 & 0x7fff;
// Set the freeform message flag
n3 += 32768;
// 71 message bits to pack, plus 1 bit flag for freeform message.
// 31 zero bits appended to end.
// N1 and N2 are 28 bits each, N3 is 16 bits
// A little less work to start with the least-significant bits
c[3] = (uint8_t)((n1 & 0x0f) << 4);
n1 = n1 >> 4;
c[2] = (uint8_t)(n1 & 0xff);
n1 = n1 >> 8;
c[1] = (uint8_t)(n1 & 0xff);
n1 = n1 >> 8;
c[0] = (uint8_t)(n1 & 0xff);
c[6] = (uint8_t)(n2 & 0xff);
n2 = n2 >> 8;
c[5] = (uint8_t)(n2 & 0xff);
n2 = n2 >> 8;
c[4] = (uint8_t)(n2 & 0xff);
n2 = n2 >> 8;
c[3] |= (uint8_t)(n2 & 0x0f);
c[8] = (uint8_t)(n3 & 0xff);
n3 = n3 >> 8;
c[7] = (uint8_t)(n3 & 0xff);
c[9] = 0;
c[10] = 0;
c[11] = 0;
c[12] = 0;
}
void JTEncode::wspr_bit_packing(uint8_t * c)
{
uint32_t n, m;
n = wspr_code(callsign[0]);
n = n * 36 + wspr_code(callsign[1]);
n = n * 10 + wspr_code(callsign[2]);
n = n * 27 + (wspr_code(callsign[3]) - 10);
n = n * 27 + (wspr_code(callsign[4]) - 10);
n = n * 27 + (wspr_code(callsign[5]) - 10);
m = ((179 - 10 * (locator[0] - 'A') - (locator[2] - '0')) * 180) +
(10 * (locator[1] - 'A')) + (locator[3] - '0');
m = (m * 128) + power + 64;
// Callsign is 28 bits, locator/power is 22 bits.
// A little less work to start with the least-significant bits
c[3] = (uint8_t)((n & 0x0f) << 4);
n = n >> 4;
c[2] = (uint8_t)(n & 0xff);
n = n >> 8;
c[1] = (uint8_t)(n & 0xff);
n = n >> 8;
c[0] = (uint8_t)(n & 0xff);
c[6] = (uint8_t)((m & 0x03) << 6);
m = m >> 2;
c[5] = (uint8_t)(m & 0xff);
m = m >> 8;
c[4] = (uint8_t)(m & 0xff);
m = m >> 8;
c[3] |= (uint8_t)(m & 0x0f);
c[7] = 0;
c[8] = 0;
c[9] = 0;
c[10] = 0;
}
void JTEncode::jt65_interleave(uint8_t * s)
{
uint8_t i, j;
uint8_t d[JT65_ENCODE_COUNT];
uint8_t d1[7][9];
// Fill temp d1 array
for(i = 0; i < 9; i++)
{
for(j = 0; j < 7; j++)
{
d1[i][j] = s[(i * 7) + j];
}
}
// Interleave and translate back to 1D destination array
for(i = 0; i < 7; i++)
{
for(j = 0; j < 9; j++)
{
d[(i * 9) + j] = d1[j][i];
}
}
memcpy(s, d, JT65_ENCODE_COUNT);
}
void JTEncode::jt9_interleave(uint8_t * s)
{
uint8_t i, j, k, n;
uint8_t d[JT9_BIT_COUNT];
uint8_t j0[JT9_BIT_COUNT];
k = 0;
// Build the interleave table
for(i = 0; i < 255; i++)
{
n = 0;
for(j = 0; j < 8; j++)
{
n = (n << 1) + ((i >> j) & 1);
}
if(n < 206)
{
j0[k] = n;
k++;
}
if(k >= 206)
{
break;
}
}
// Now do the interleave
for(i = 0; i < 206; i++)
{
d[j0[i]] = s[i];
}
memcpy(s, d, JT9_BIT_COUNT);
}
void JTEncode::wspr_interleave(uint8_t * s)
{
uint8_t d[WSPR_BIT_COUNT];
uint8_t rev, index_temp, i, j, k;
i = 0;
for(j = 0; j < 255; j++)
{
// Bit reverse the index
index_temp = j;
rev = 0;
for(k = 0; k < 8; k++)
{
if(index_temp & 0x01)
{
rev = rev | (1 << (7 - k));
}
index_temp = index_temp >> 1;
}
if(rev < WSPR_BIT_COUNT)
{
d[rev] = s[i];
i++;
}
if(i >= WSPR_BIT_COUNT)
{
break;
}
}
memcpy(s, d, WSPR_BIT_COUNT);
}
void JTEncode::jt9_packbits(uint8_t * d, uint8_t * a)
{
uint8_t i, k;
k = 0;
memset(a, 0, JT9_ENCODE_COUNT);
for(i = 0; i < JT9_ENCODE_COUNT; i++)
{
a[i] = (d[k] & 1) << 2;
k++;
a[i] |= ((d[k] & 1) << 1);
k++;
a[i] |= (d[k] & 1);
k++;
}
}
void JTEncode::jt_gray_code(uint8_t * g, uint8_t symbol_count)
{
uint8_t i;
for(i = 0; i < symbol_count; i++)
{
g[i] = gray_code(g[i]);
}
}
void JTEncode::jt65_merge_sync_vector(uint8_t * g, uint8_t * symbols)
{
uint8_t i, j = 0;
const uint8_t sync_vector[JT65_SYMBOL_COUNT] =
{1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0,
0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1,
0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1,
0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1};
for(i = 0; i < JT65_SYMBOL_COUNT; i++)
{
if(sync_vector[i])
{
symbols[i] = 0;
}
else
{
symbols[i] = g[j] + 2;
j++;
}
}
}
void JTEncode::jt9_merge_sync_vector(uint8_t * g, uint8_t * symbols)
{
uint8_t i, j = 0;
const uint8_t sync_vector[JT9_SYMBOL_COUNT] =
{1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 1};
for(i = 0; i < JT9_SYMBOL_COUNT; i++)
{
if(sync_vector[i])
{
symbols[i] = 0;
}
else
{
symbols[i] = g[j] + 1;
j++;
}
}
}
void JTEncode::wspr_merge_sync_vector(uint8_t * g, uint8_t * symbols)
{
uint8_t i;
const uint8_t sync_vector[WSPR_SYMBOL_COUNT] =
{1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0,
1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1,
0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0,
1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1,
1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0};
for(i = 0; i < WSPR_SYMBOL_COUNT; i++)
{
symbols[i] = sync_vector[i] + (2 * g[i]);
}
}
void JTEncode::convolve(uint8_t * c, uint8_t * s, uint8_t message_size, uint8_t bit_size)
{
uint32_t reg_0 = 0;
uint32_t reg_1 = 0;
uint32_t reg_temp = 0;
uint8_t input_bit, parity_bit;
uint8_t bit_count = 0;
uint8_t i, j, k;
for(i = 0; i < message_size; i++)
{
for(j = 0; j < 8; j++)
{
// Set input bit according the MSB of current element
input_bit = (((c[i] << j) & 0x80) == 0x80) ? 1 : 0;
// Shift both registers and put in the new input bit
reg_0 = reg_0 << 1;
reg_1 = reg_1 << 1;
reg_0 |= (uint32_t)input_bit;
reg_1 |= (uint32_t)input_bit;
// AND Register 0 with feedback taps, calculate parity
reg_temp = reg_0 & 0xf2d05351;
parity_bit = 0;
for(k = 0; k < 32; k++)
{
parity_bit = parity_bit ^ (reg_temp & 0x01);
reg_temp = reg_temp >> 1;
}
s[bit_count] = parity_bit;
bit_count++;
// AND Register 1 with feedback taps, calculate parity
reg_temp = reg_1 & 0xe4613c47;
parity_bit = 0;
for(k = 0; k < 32; k++)
{
parity_bit = parity_bit ^ (reg_temp & 0x01);
reg_temp = reg_temp >> 1;
}
s[bit_count] = parity_bit;
bit_count++;
if(bit_count >= bit_size)
{
break;
}
}
}
}
void JTEncode::rs_encode(uint8_t * data, uint8_t * symbols)
{
// Adapted from wrapkarn.c in the WSJT-X source code
unsigned int dat1[12];
unsigned int b[51];
unsigned int i;
// Reverse data order for the Karn codec.
for(i = 0; i < 12; i++)
{
dat1[i] = data[11 - i];
}
// Compute the parity symbols
encode_rs_int(rs_inst, dat1, b);
// Move parity symbols and data into symbols array, in reverse order.
for (i = 0; i < 51; i++)
{
symbols[50 - i] = b[i];
}
for (i = 0; i < 12; i++)
{
symbols[i + 51] = dat1[11 - i];
}
}

72
JTEncode.h 100644
Wyświetl plik

@ -0,0 +1,72 @@
/*
* JTEncode.h - JT65/JT9/WSPR encoder library for Arduino
*
* Copyright (C) 2015 Jason Milldrum <milldrum@gmail.com>
*
* Based on the algorithms presented in the WSJT software suite.
* Thanks to Andy Talbot G4JNT for the whitepaper on the WSPR encoding
* process that helped me to understand all of this.
*
* 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 3 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/>.
*/
#include "int.h"
#include "rs_common.h"
#include <stdint.h>
#define JT65_SYMBOL_COUNT 126
#define JT9_SYMBOL_COUNT 85
#define WSPR_SYMBOL_COUNT 162
#define JT65_ENCODE_COUNT 63
#define JT9_ENCODE_COUNT 69
#define JT9_BIT_COUNT 206
#define WSPR_BIT_COUNT 162
class JTEncode
{
public:
JTEncode(void);
void jt65_encode(char *, uint8_t *);
void jt9_encode(char *, uint8_t *);
void wspr_encode(char *, char *, uint8_t, uint8_t *);
private:
uint8_t jt_code(char);
uint8_t wspr_code(char);
uint8_t gray_code(uint8_t);
void jt_message_prep(char *);
void wspr_message_prep(char *, char *, uint8_t);
void jt65_bit_packing(char *, uint8_t *);
void jt9_bit_packing(char *, uint8_t *);
void wspr_bit_packing(uint8_t *);
void jt65_interleave(uint8_t *);
void jt9_interleave(uint8_t *);
void wspr_interleave(uint8_t *);
void jt9_packbits(uint8_t *, uint8_t *);
void jt_gray_code(uint8_t *, uint8_t);
void jt65_merge_sync_vector(uint8_t *, uint8_t *);
void jt9_merge_sync_vector(uint8_t *, uint8_t *);
void wspr_merge_sync_vector(uint8_t *, uint8_t *);
void convolve(uint8_t *, uint8_t *, uint8_t, uint8_t);
void rs_encode(uint8_t *, uint8_t *);
void encode_rs_int(void *,data_t *, data_t *);
void free_rs_int(void *);
void * init_rs_int(int, int, int, int, int, int);
void * rs_inst;
char callsign[7];
char locator[5];
uint8_t power;
};

139
README.md 100644
Wyświetl plik

@ -0,0 +1,139 @@
JT65/JT9/WSPR Encoder Library for Arduino
=========================================
This library very simply generates a set of channel symbols for JT65, JT9, or WSPR based on the user providing a properly formatted Type 6 message for JT65 or JT9 (which is 13 valid characters) or a callsign, Maidenhead grid locator, and power output for WSPR. When paired with a synthesizer that can output frequencies in fine, phase-continuous tuning steps (such as the Si5351), then a beacon or telemetry transmitter can be created which can change the transmitted characters as needed from the Arduino.
Please feel free to use the issues feature of GitHub if you run into problems or have suggestions for important features to implement.
Hardware Requirements and Setup
-------------------------------
This library has been written for the Arduino platform and has been successfully tested on the Arduino Uno and an Uno clone. Since the library itself does not access the hardware, there is no reason it should not run on any Arduino model of recent vintage.
How To Install
--------------
Include the JTEncode library into your instance of the Arduino IDE. Download a ZIP file of the library from the GitHub repository by using the "Download ZIP" button at the right of the main repository page. Extract the ZIP file, then rename the unzipped folder as "JTEncode". Finally, open the Arduino IDE, select menu Sketch > Import Library... > Add Library..., and select the renamed folder that you just downloaded. Restart the IDE and you should have access to the new library.
(Yes, the Arduino IDE can import a ZIP file, but it doesn't like filenames with characters such as dashes, as GitHub does when it appends the branch name with a dash. Perhaps there's an elegant way around this, we'll see.)
Example
-------
There is a simple example that is placed in your examples menu under JTEncode. Open this to see how to incorporate this library with your code. The example provided with with the library is meant to be used in conjuction with the [Etherkit Si5351A Breakout Board](https://www.etherkit.com/rf-modules/si5351a-breakout-board.html), although it could be modified to use with other synthesizers which meet the technical requirements of the JT65/JT9/WSPR modes.
To run this example, be sure to download the [Si5351Arduino](https://github.com/etherkit/Si5351Arduino) library and follow the instructions there to connect the Si5351A Breakout Board to your Arduino. In order to trigger transmissions, you will also need to connect a momentary pushbutton from pin 12 of the Arduino to ground.
The example sketch itself is fairly straightforward. JT65, JT9, and WSPR modes are modulated in same way: phase-continuous multiple-frequency shift keying (MFSK). The message to be transmitted is passed to the JTEncode method corresponding to the desired mode, along with a pointer to an array which holds the returned channel symbols. When the pushbutton is pushed, the sketch then transmits each channel symbol sequentially as an offset from the base frequency given in the sketch define section.
An instance of the JTEncode object is created:
JTEncode jtencode;
On sketch startup, the mode parameters are set based on which mode is currently selected (by the DEFAULT_MODE define):
// Set the proper frequency, tone spacing, symbol count, and
// timer CTC depending on mode
switch(cur_mode)
{
case MODE_JT9:
freq = JT9_DEFAULT_FREQ;
ctc = JT9_CTC;
symbol_count = JT9_SYMBOL_COUNT; // From the library defines
tone_spacing = JT9_TONE_SPACING;
break;
case MODE_JT65:
freq = JT65_DEFAULT_FREQ;
ctc = JT65_CTC;
symbol_count = JT65_SYMBOL_COUNT; // From the library defines
tone_spacing = JT65_TONE_SPACING;
break;
case MODE_WSPR:
freq = WSPR_DEFAULT_FREQ;
ctc = WSPR_CTC;
symbol_count = WSPR_SYMBOL_COUNT; // From the library defines
tone_spacing = WSPR_TONE_SPACING;
break;
}
Note that the number of channel symbols for each mode is defined in the library, so you can use those defines to initialize your own symbol array sizes.
During transmit, the proper class method is chosen based on the desired mode, then the transmit symbol buffer and the other mode information is set:
// Set the proper frequency and timer CTC depending on mode
switch(cur_mode)
{
case MODE_JT9:
jtencode.jt9_encode(message, tx_buffer);
break;
case MODE_JT65:
jtencode.jt65_encode(message, tx_buffer);
break;
case MODE_WSPR:
jtencode.wspr_encode(call, loc, dbm, tx_buffer);
break;
}
Once the channel symbols have been generate, it is a simple matter of transmitting them in sequence, each of the correct amount of time:
// Now transmit the channel symbols
for(i = 0; i < symbol_count; i++)
{
si5351.set_freq((freq * 100) + (tx_buffer[i] * tone_spacing), 0, SI5351_CLK0);
proceed = false;
while(!proceed);
}
Public Methods
------------------
###jt65_encode()
```
/*
* jt65_encode(char * message, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
* a channel symbol table.
*
* message - Plaintext Type 6 message.
* symbols - Array of channel symbols to transmit retunred by the method.
* Ensure that you pass a uint8_t array of size JT65_SYMBOL_COUNT to the method.
*
*/
```
###jt9_encode()
```
/*
* jt9_encode(char * message, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
* a channel symbol table.
*
* message - Plaintext Type 6 message.
* symbols - Array of channel symbols to transmit retunred by the method.
* Ensure that you pass a uint8_t array of size JT9_SYMBOL_COUNT to the method.
*
*/
```
###wspr_encode()
```
/*
* wspr_encode(char * call, char * loc, uint8_t dbm, uint8_t * symbols)
*
* Takes an arbitrary message of up to 13 allowable characters and returns
*
* call - Callsign (6 characters maximum).
* loc - Maidenhead grid locator (4 charcters maximum).
* dbm - Output power in dBm.
* symbols - Array of channel symbols to transmit retunred by the method.
* Ensure that you pass a uint8_t array of size WSPR_SYMBOL_COUNT to the method.
*
*/
```
Tokens
------
Here are the defines, structs, and enumerations you will find handy to use with the library.
Defines:
JT65_SYMBOL_COUNT, JT9_SYMBOL_COUNT, WSPR_SYMBOL_COUNT
Acknowledgements
----------------
Many thanks to Joe Taylor K1JT for his innovative work in amateur radio. We are lucky to have him. The algorithms in this program were derived from the source code in the [WSJT](http://sourceforge.net/projects/wsjt/) suite of applications. Also, many thanks for Andy Talbot G4JNT for [his paper](http://www.g4jnt.com/JTModesBcns.htm) on the WSPR coding protocol, which helped me to understand the WSPR encoding process, which in turn helped me to understand the related JT protocols.

58
encode_rs.h 100644
Wyświetl plik

@ -0,0 +1,58 @@
/* The guts of the Reed-Solomon encoder, meant to be #included
* into a function body with the following typedefs, macros and variables supplied
* according to the code parameters:
* data_t - a typedef for the data symbol
* data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
* data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
* NROOTS - the number of roots in the RS code generator polynomial,
* which is the same as the number of parity symbols in a block.
Integer variable or literal.
*
* NN - the total number of symbols in a RS block. Integer variable or literal.
* PAD - the number of pad symbols in a block. Integer variable or literal.
* ALPHA_TO - The address of an array of NN elements to convert Galois field
* elements in index (log) form to polynomial form. Read only.
* INDEX_OF - The address of an array of NN elements to convert Galois field
* elements in polynomial form to index (log) form. Read only.
* MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
* GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
* The memset() and memmove() functions are used. The appropriate header
* file declaring these functions (usually <string.h>) must be included by the calling
* program.
* Copyright 2004, Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#undef A0
#define A0 (NN) /* Special reserved value encoding zero in index form */
{
int i, j;
data_t feedback;
memset(parity,0,NROOTS*sizeof(data_t));
for(i=0;i<NN-NROOTS-PAD;i++){
feedback = INDEX_OF[data[i] ^ parity[0]];
if(feedback != A0){ /* feedback term is non-zero */
#ifdef UNNORMALIZED
/* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
* always be for the polynomials constructed by init_rs()
*/
feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
#endif
for(j=1;j<NROOTS;j++)
parity[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
}
/* Shift */
memmove(&parity[0],&parity[1],sizeof(data_t)*(NROOTS-1));
if(feedback != A0)
parity[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
else
parity[NROOTS-1] = 0;
}
}

70
encode_rs_int.cpp 100644
Wyświetl plik

@ -0,0 +1,70 @@
/* Reed-Solomon encoder
* Copyright 2003, Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*
* Slightly modified by Jason Milldrum NT7S, 2015 to fit into the Arduino framework
*
* The guts of the Reed-Solomon encoder, meant to be #included
* into a function body with the following typedefs, macros and variables supplied
* according to the code parameters:
* data_t - a typedef for the data symbol
* data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
* data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
* NROOTS - the number of roots in the RS code generator polynomial,
* which is the same as the number of parity symbols in a block.
Integer variable or literal.
*
* NN - the total number of symbols in a RS block. Integer variable or literal.
* PAD - the number of pad symbols in a block. Integer variable or literal.
* ALPHA_TO - The address of an array of NN elements to convert Galois field
* elements in index (log) form to polynomial form. Read only.
* INDEX_OF - The address of an array of NN elements to convert Galois field
* elements in polynomial form to index (log) form. Read only.
* MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
* GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
* The memset() and memmove() functions are used. The appropriate header
* file declaring these functions (usually <string.h>) must be included by the calling
* program.
*/
#include <string.h>
#include <JTEncode.h>
#include "int.h"
#include "rs_common.h"
void JTEncode::encode_rs_int(void *p, data_t *data, data_t *parity)
{
struct rs *rs = (struct rs *)p;
#undef A0
#define A0 (NN) /* Special reserved value encoding zero in index form */
{
int i, j;
data_t feedback;
memset(parity,0,NROOTS*sizeof(data_t));
for(i=0;i<NN-NROOTS-PAD;i++){
feedback = INDEX_OF[data[i] ^ parity[0]];
if(feedback != A0){ /* feedback term is non-zero */
#ifdef UNNORMALIZED
/* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
* always be for the polynomials constructed by init_rs()
*/
feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
#endif
for(j=1;j<NROOTS;j++)
parity[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
}
/* Shift */
memmove(&parity[0],&parity[1],sizeof(data_t)*(NROOTS-1));
if(feedback != A0)
parity[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
else
parity[NROOTS-1] = 0;
}
}
}

Wyświetl plik

@ -0,0 +1,207 @@
//
// Simple JT65/JT9/WSPR beacon for Arduino, with the Etherkit
// Si5351A Breakout Board, by Jason Milldrum NT7S.
//
// Transmit an abritrary message of up to 13 valid characters
// (a Type 6 message) in JT65 and JT9, or a standard Type 1
// message in WSPR.
//
// Connect a momentary push button to pin 12 to use as the
// transmit trigger. Get fancy by adding your own code to trigger
// off of the time from a GPS or your PC via virtual serial.
//
// Original code based on Feld Hell beacon for Arduino by Mark
// Vandewettering K6HX, adapted for the Si5351A by Robert
// Liesenfeld AK6L <ak6l@ak6l.org>.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject
// to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#include <si5351.h>
#include <JTEncode.h>
#include <rs_common.h>
#include <int.h>
#include <string.h>
#include "Wire.h"
// Mode defines
#define JT9_TONE_SPACING 174 // ~1.74 Hz
#define JT65_TONE_SPACING 269 // ~2.69 Hz
#define WSPR_TONE_SPACING 146 // ~1.46 Hz
#define JT9_CTC 9000 // CTC value for JT9-1
#define JT65_CTC 5812 // CTC value for JT65A
#define WSPR_CTC 10672 // CTC value for WSPR
#define JT9_DEFAULT_FREQ 14078600UL
#define JT65_DEFAULT_FREQ 14077500UL
#define WSPR_DEFAULT_FREQ 14097100UL
#define DEFAULT_MODE MODE_JT65
// Hardware defines
#define BUTTON 12
#define LED_PIN 13
// Enumerations
enum mode {MODE_JT9, MODE_JT65, MODE_WSPR};
// Class instantiation
Si5351 si5351;
JTEncode jtencode;
// Global variables
unsigned long freq;
char message[14] = "N0CALL AA00";
char call[7] = "N0CALL";
char loc[5] = "AA00";
uint8_t dbm = 27;
uint8_t tx_buffer[255];
enum mode cur_mode = DEFAULT_MODE;
uint8_t symbol_count;
uint16_t ctc, tone_spacing;
// Global variables used in ISRs
volatile bool proceed = false;
// Timer interrupt vector. This toggles the variable we use to gate
// each column of output to ensure accurate timing. Called whenever
// Timer1 hits the count set below in setup().
ISR(TIMER1_COMPA_vect)
{
proceed = true;
}
// Loop through the string, transmitting one character at a time.
void encode()
{
uint8_t i;
// Clear out the old transmit buffer
memset(tx_buffer, 0, 255);
// Set the proper frequency and timer CTC depending on mode
switch(cur_mode)
{
case MODE_JT9:
jtencode.jt9_encode(message, tx_buffer);
break;
case MODE_JT65:
jtencode.jt65_encode(message, tx_buffer);
break;
case MODE_WSPR:
jtencode.wspr_encode(call, loc, dbm, tx_buffer);
break;
}
// Reset the tone to the base frequency and turn on the output
si5351.output_enable(SI5351_CLK0, 1);
digitalWrite(LED_PIN, HIGH);
// Now transmit the channel symbols
for(i = 0; i < symbol_count; i++)
{
si5351.set_freq((freq * 100) + (tx_buffer[i] * tone_spacing), 0, SI5351_CLK0);
proceed = false;
while(!proceed);
}
// Turn off the output
si5351.output_enable(SI5351_CLK0, 0);
digitalWrite(LED_PIN, LOW);
}
void setup()
{
// Use the Arduino's on-board LED as a keying indicator.
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Use a button connected to pin 12 as a transmit trigger
pinMode(BUTTON, INPUT_PULLUP);
//Serial.begin(57600);
// Set the proper frequency, tone spacing, symbol count, and
// timer CTC depending on mode
switch(cur_mode)
{
case MODE_JT9:
freq = JT9_DEFAULT_FREQ;
ctc = JT9_CTC;
symbol_count = JT9_SYMBOL_COUNT; // From the library defines
tone_spacing = JT9_TONE_SPACING;
break;
case MODE_JT65:
freq = JT65_DEFAULT_FREQ;
ctc = JT65_CTC;
symbol_count = JT65_SYMBOL_COUNT; // From the library defines
tone_spacing = JT65_TONE_SPACING;
break;
case MODE_WSPR:
freq = WSPR_DEFAULT_FREQ;
ctc = WSPR_CTC;
symbol_count = WSPR_SYMBOL_COUNT; // From the library defines
tone_spacing = WSPR_TONE_SPACING;
break;
}
// Initialize the Si5351
// Change the 2nd parameter in init if using a ref osc other
// than 25 MHz
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
// Set CLK0 output
si5351.set_correction(0);
si5351.set_freq(freq * 100, 0, SI5351_CLK0);
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); // Set for max power if desired
si5351.output_enable(SI5351_CLK0, 0); // Disable the clock initially
// Set up Timer1 for interrupts every symbol period.
noInterrupts(); // Turn off interrupts.
TCCR1A = 0; // Set entire TCCR1A register to 0; disconnects
// interrupt output pins, sets normal waveform
// mode. We're just using Timer1 as a counter.
TCNT1 = 0; // Initialize counter value to 0.
TCCR1B = (1 << CS12) | // Set CS12 and CS10 bit to set prescale
(1 << CS10) | // to /1024
(1 << WGM12); // turn on CTC
// which gives, 64 us ticks
TIMSK1 = (1 << OCIE1A); // Enable timer compare interrupt.
OCR1A = ctc; // Set up interrupt trigger count;
interrupts(); // Re-enable interrupts.
}
void loop()
{
// Debounce the button and trigger TX on push
if(digitalRead(BUTTON) == LOW)
{
delay(50); // delay to debounce
if (digitalRead(BUTTON) == LOW)
{
encode();
delay(50); //delay to avoid extra triggers
}
}
}

106
init_rs.h 100644
Wyświetl plik

@ -0,0 +1,106 @@
/* Common code for intializing a Reed-Solomon control block (char or int symbols)
* Copyright 2004 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#undef NULL
#define NULL ((void *)0)
{
int i, j, sr,root,iprim;
rs = NULL;
/* Check parameter ranges */
if(symsize < 0 || symsize > 8*sizeof(data_t)){
goto done;
}
if(fcr < 0 || fcr >= (1<<symsize))
goto done;
if(prim <= 0 || prim >= (1<<symsize))
goto done;
if(nroots < 0 || nroots >= (1<<symsize))
goto done; /* Can't have more roots than symbol values! */
if(pad < 0 || pad >= ((1<<symsize) -1 - nroots))
goto done; /* Too much padding */
rs = (struct rs *)calloc(1,sizeof(struct rs));
if(rs == NULL)
goto done;
rs->mm = symsize;
rs->nn = (1<<symsize)-1;
rs->pad = pad;
rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->alpha_to == NULL){
free(rs);
rs = NULL;
goto done;
}
rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->index_of == NULL){
free(rs->alpha_to);
free(rs);
rs = NULL;
goto done;
}
/* Generate Galois field lookup tables */
rs->index_of[0] = A0; /* log(zero) = -inf */
rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */
sr = 1;
for(i=0;i<rs->nn;i++){
rs->index_of[sr] = i;
rs->alpha_to[i] = sr;
sr <<= 1;
if(sr & (1<<symsize))
sr ^= gfpoly;
sr &= rs->nn;
}
if(sr != 1){
/* field generator polynomial is not primitive! */
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = NULL;
goto done;
}
/* Form RS code generator polynomial from its roots */
rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1));
if(rs->genpoly == NULL){
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = NULL;
goto done;
}
rs->fcr = fcr;
rs->prim = prim;
rs->nroots = nroots;
/* Find prim-th root of 1, used in decoding */
for(iprim=1;(iprim % prim) != 0;iprim += rs->nn)
;
rs->iprim = iprim / prim;
rs->genpoly[0] = 1;
for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) {
rs->genpoly[i+1] = 1;
/* Multiply rs->genpoly[] by @**(root + x) */
for (j = i; j > 0; j--){
if (rs->genpoly[j] != 0)
rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)];
else
rs->genpoly[j] = rs->genpoly[j-1];
}
/* rs->genpoly[0] can never be zero */
rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)];
}
/* convert rs->genpoly[] to index form for quicker encoding */
for (i = 0; i <= nroots; i++)
rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
done:;
}

127
init_rs_int.cpp 100644
Wyświetl plik

@ -0,0 +1,127 @@
/* Initialize a RS codec
*
* Copyright 2002 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*
* Slightly modified by Jason Milldrum NT7S, 2015 to fit into the Arduino framework
*/
#include <string.h>
#include <stdlib.h>
#include <JTEncode.h>
#include "rs_common.h"
void JTEncode::free_rs_int(void * p)
{
struct rs *rs = (struct rs *)p;
free(rs->alpha_to);
free(rs->index_of);
free(rs->genpoly);
free(rs);
}
void * JTEncode::init_rs_int(int symsize, int gfpoly, int fcr, int prim,
int nroots, int pad)
{
struct rs *rs;
int i, j, sr,root,iprim;
rs = ((struct rs *)0);
/* Check parameter ranges */
if(symsize < 0 || symsize > 8*sizeof(data_t)){
goto done;
}
if(fcr < 0 || fcr >= (1<<symsize))
goto done;
if(prim <= 0 || prim >= (1<<symsize))
goto done;
if(nroots < 0 || nroots >= (1<<symsize))
goto done; /* Can't have more roots than symbol values! */
if(pad < 0 || pad >= ((1<<symsize) -1 - nroots))
goto done; /* Too much padding */
rs = (struct rs *)calloc(1,sizeof(struct rs));
if(rs == ((struct rs *)0))
goto done;
rs->mm = symsize;
rs->nn = (1<<symsize)-1;
rs->pad = pad;
rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->alpha_to == NULL){
free(rs);
rs = ((struct rs *)0);
goto done;
}
rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1));
if(rs->index_of == NULL){
free(rs->alpha_to);
free(rs);
rs = ((struct rs *)0);
goto done;
}
/* Generate Galois field lookup tables */
rs->index_of[0] = A0; /* log(zero) = -inf */
rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */
sr = 1;
for(i=0;i<rs->nn;i++){
rs->index_of[sr] = i;
rs->alpha_to[i] = sr;
sr <<= 1;
if(sr & (1<<symsize))
sr ^= gfpoly;
sr &= rs->nn;
}
if(sr != 1){
/* field generator polynomial is not primitive! */
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = ((struct rs *)0);
goto done;
}
/* Form RS code generator polynomial from its roots */
rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1));
if(rs->genpoly == NULL){
free(rs->alpha_to);
free(rs->index_of);
free(rs);
rs = ((struct rs *)0);
goto done;
}
rs->fcr = fcr;
rs->prim = prim;
rs->nroots = nroots;
/* Find prim-th root of 1, used in decoding */
for(iprim=1;(iprim % prim) != 0;iprim += rs->nn)
;
rs->iprim = iprim / prim;
rs->genpoly[0] = 1;
for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) {
rs->genpoly[i+1] = 1;
/* Multiply rs->genpoly[] by @**(root + x) */
for (j = i; j > 0; j--){
if (rs->genpoly[j] != 0)
rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)];
else
rs->genpoly[j] = rs->genpoly[j-1];
}
/* rs->genpoly[0] can never be zero */
rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)];
}
/* convert rs->genpoly[] to index form for quicker encoding */
for (i = 0; i <= nroots; i++)
rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
done:;
return rs;
}

25
int.h 100644
Wyświetl plik

@ -0,0 +1,25 @@
/* Stuff specific to the general (integer) version of the Reed-Solomon codecs
*
* Copyright 2003, Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#ifndef INT_H_
#define INT_H_
typedef unsigned int data_t;
#define MODNN(x) modnn(rs,x)
#define MM (rs->mm)
#define NN (rs->nn)
#define ALPHA_TO (rs->alpha_to)
#define INDEX_OF (rs->index_of)
#define GENPOLY (rs->genpoly)
#define NROOTS (rs->nroots)
#define FCR (rs->fcr)
#define PRIM (rs->prim)
#define IPRIM (rs->iprim)
#define PAD (rs->pad)
#define A0 (NN)
#endif

13
keywords.txt 100644
Wyświetl plik

@ -0,0 +1,13 @@
JTEncode KEYWORD1
jt65_encode KEYWORD2
jt9_encode KEYWORD2
wspr_encode KEYWORD2
JT65_SYMBOL_COUNT LITERAL1
JT9_SYMBOL_COUNT LITERAL1
WSPR_SYMBOL_COUNT LITERAL1
JT65_ENCODE_COUNT LITERAL1
JT9_ENCODE_COUNT LITERAL1
JT9_BIT_COUNT LITERAL1
WSPR_BIT_COUNT LITERAL1

33
rs_common.h 100644
Wyświetl plik

@ -0,0 +1,33 @@
/* Stuff common to all the general-purpose Reed-Solomon codecs
* Copyright 2004 Phil Karn, KA9Q
* May be used under the terms of the GNU Lesser General Public License (LGPL)
*/
#ifndef RS_COMMON_H_
#define RS_COMMON_H_
#include "int.h"
/* Reed-Solomon codec control block */
struct rs {
int mm; /* Bits per symbol */
int nn; /* Symbols per block (= (1<<mm)-1) */
data_t *alpha_to; /* log lookup table */
data_t *index_of; /* Antilog lookup table */
data_t *genpoly; /* Generator polynomial */
int nroots; /* Number of generator roots = number of parity symbols */
int fcr; /* First consecutive root, index form */
int prim; /* Primitive element, index form */
int iprim; /* prim-th root of 1, index form */
int pad; /* Padding bytes in shortened block */
};
static inline int modnn(struct rs *rs,int x){
while (x >= rs->nn) {
x -= rs->nn;
x = (x >> rs->mm) + (x & rs->nn);
}
return x;
}
#endif