kopia lustrzana https://github.com/mikaelnousiainen/RS41ng
1391 wiersze
32 KiB
C++
1391 wiersze
32 KiB
C++
/*
|
|
* JTEncode.cpp - JT65/JT9/WSPR/FSQ encoder library for Arduino
|
|
*
|
|
* Copyright (C) 2015-2018 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 "crc14.h"
|
|
#include "generator.h"
|
|
|
|
#include <cstring>
|
|
#include <cctype>
|
|
#include <cstdlib>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
|
|
// Define an upper bound on the number of glyphs. Defining it this
|
|
// way allows adding characters without having to update a hard-coded
|
|
// upper bound.
|
|
#define NGLYPHS (sizeof(fsq_code_table)/sizeof(fsq_code_table[0]))
|
|
|
|
/* Public Class Members */
|
|
|
|
JTEncode::JTEncode(void)
|
|
{
|
|
// Initialize the Reed-Solomon encoder
|
|
rs_inst = (struct rs *) init_rs_int(6, 0x43, 3, 1, 51, 0);
|
|
}
|
|
|
|
JTEncode::~JTEncode()
|
|
{
|
|
free_rs_int(rs_inst);
|
|
}
|
|
|
|
/*
|
|
* jt65_encode(const 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 returned by the method.
|
|
* Ensure that you pass a uint8_t array of at least size JT65_SYMBOL_COUNT to the method.
|
|
*
|
|
*/
|
|
uint8_t jt65_buffer[JT65_ENCODE_COUNT];
|
|
|
|
void JTEncode::jt65_encode(const char * msg, uint8_t * symbols)
|
|
{
|
|
char message[14];
|
|
memset(message, 0, 14);
|
|
strcpy(message, msg);
|
|
|
|
// 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
|
|
// ---------------------
|
|
rs_encode(c, jt65_buffer);
|
|
|
|
// Interleaving
|
|
// ------------
|
|
jt65_interleave(jt65_buffer);
|
|
|
|
// Gray Code
|
|
// ---------
|
|
jt_gray_code(jt65_buffer, JT65_ENCODE_COUNT);
|
|
|
|
// Merge with sync vector
|
|
// ----------------------
|
|
jt65_merge_sync_vector(jt65_buffer, symbols);
|
|
}
|
|
|
|
/*
|
|
* jt9_encode(const 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 returned by the method.
|
|
* Ensure that you pass a uint8_t array of at least size JT9_SYMBOL_COUNT to the method.
|
|
*
|
|
*/
|
|
uint8_t jt9_buffer_s[JT9_BIT_COUNT];
|
|
uint8_t jt9_buffer_a[JT9_ENCODE_COUNT];
|
|
void JTEncode::jt9_encode(const char * msg, uint8_t * symbols)
|
|
{
|
|
char message[14];
|
|
memset(message, 0, 14);
|
|
strcpy(message, msg);
|
|
|
|
// 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
|
|
// ---------------------
|
|
convolve(c, jt9_buffer_s, 13, JT9_BIT_COUNT);
|
|
|
|
// Interleaving
|
|
// ------------
|
|
jt9_interleave(jt9_buffer_s);
|
|
|
|
// Pack into 3-bit symbols
|
|
// -----------------------
|
|
jt9_packbits(jt9_buffer_s, jt9_buffer_a);
|
|
|
|
// Gray Code
|
|
// ---------
|
|
jt_gray_code(jt9_buffer_a, JT9_ENCODE_COUNT);
|
|
|
|
// Merge with sync vector
|
|
// ----------------------
|
|
jt9_merge_sync_vector(jt9_buffer_a, symbols);
|
|
}
|
|
|
|
/*
|
|
* jt4_encode(const 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 returned by the method.
|
|
* Ensure that you pass a uint8_t array of at least size JT9_SYMBOL_COUNT to the method.
|
|
*
|
|
*/
|
|
uint8_t jt4_buffer_s[JT4_SYMBOL_COUNT];
|
|
void JTEncode::jt4_encode(const char * msg, uint8_t * symbols)
|
|
{
|
|
char message[14];
|
|
memset(message, 0, 14);
|
|
strcpy(message, msg);
|
|
|
|
// 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
|
|
// ---------------------
|
|
convolve(c, jt4_buffer_s, 13, JT4_BIT_COUNT);
|
|
|
|
// Interleaving
|
|
// ------------
|
|
jt9_interleave(jt4_buffer_s);
|
|
memmove(jt4_buffer_s + 1, jt4_buffer_s, JT4_BIT_COUNT);
|
|
jt4_buffer_s[0] = 0; // Append a 0 bit to start of sequence
|
|
|
|
// Merge with sync vector
|
|
// ----------------------
|
|
jt4_merge_sync_vector(jt4_buffer_s, symbols);
|
|
}
|
|
|
|
/*
|
|
* wspr_encode(const char * call, const char * loc, const 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 characters maximum).
|
|
* dbm - Output power in dBm.
|
|
* symbols - Array of channel symbols to transmit returned by the method.
|
|
* Ensure that you pass a uint8_t array of at least size WSPR_SYMBOL_COUNT to the method.
|
|
*
|
|
*/
|
|
uint8_t wspr_buffer_s[WSPR_SYMBOL_COUNT];
|
|
void JTEncode::wspr_encode(const char * call, const char * loc, const uint8_t dbm, uint8_t * symbols)
|
|
{
|
|
char call_[7];
|
|
char loc_[5];
|
|
uint8_t dbm_ = dbm;
|
|
strcpy(call_, call);
|
|
strcpy(loc_, loc);
|
|
|
|
// 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
|
|
// ---------------------
|
|
convolve(c, wspr_buffer_s, 11, WSPR_BIT_COUNT);
|
|
|
|
// Interleaving
|
|
// ------------
|
|
wspr_interleave(wspr_buffer_s);
|
|
|
|
// Merge with sync vector
|
|
// ----------------------
|
|
wspr_merge_sync_vector(wspr_buffer_s, symbols);
|
|
}
|
|
|
|
/*
|
|
* fsq_encode(const char * from_call, const char * message, uint8_t * symbols)
|
|
*
|
|
* Takes an arbitrary message and returns a FSQ channel symbol table.
|
|
*
|
|
* from_call - Callsign of issuing station (maximum size: 20)
|
|
* message - Null-terminated message string, no greater than 130 chars in length
|
|
* symbols - Array of channel symbols to transmit returned by the method.
|
|
* Ensure that you pass a uint8_t array of at least the size of the message
|
|
* plus 5 characters to the method. Terminated in 0xFF.
|
|
*
|
|
*/
|
|
char fsq_tx_buffer[155];
|
|
void JTEncode::fsq_encode(const char * from_call, const char * message, uint8_t * symbols)
|
|
{
|
|
char * tx_message;
|
|
uint16_t symbol_pos = 0;
|
|
uint8_t i, fch, vcode1, vcode2, tone;
|
|
uint8_t cur_tone = 0;
|
|
|
|
// Clear out the transmit buffer
|
|
// -----------------------------
|
|
memset(fsq_tx_buffer, 0, 155);
|
|
|
|
// Create the message to be transmitted
|
|
// ------------------------------------
|
|
sprintf(fsq_tx_buffer, " \n%s: %s", from_call, message);
|
|
|
|
tx_message = fsq_tx_buffer;
|
|
|
|
// Iterate through the message and encode
|
|
// --------------------------------------
|
|
while(*tx_message != '\0')
|
|
{
|
|
for(i = 0; i < NGLYPHS; i++)
|
|
{
|
|
uint8_t ch = (uint8_t)*tx_message;
|
|
|
|
// Check each element of the varicode table to see if we've found the
|
|
// character we're trying to send.
|
|
fch = fsq_code_table[i].ch;
|
|
|
|
if(fch == ch)
|
|
{
|
|
// Found the character, now fetch the varicode chars
|
|
vcode1 = fsq_code_table[i].var[0];
|
|
vcode2 = fsq_code_table[i].var[1];
|
|
|
|
// Transmit the appropriate tone per a varicode char
|
|
if(vcode2 == 0)
|
|
{
|
|
// If the 2nd varicode char is a 0 in the table,
|
|
// we are transmitting a lowercase character, and thus
|
|
// only transmit one tone for this character.
|
|
|
|
// Generate tone
|
|
cur_tone = ((cur_tone + vcode1 + 1) % 33);
|
|
symbols[symbol_pos++] = cur_tone;
|
|
}
|
|
else
|
|
{
|
|
// If the 2nd varicode char is anything other than 0 in
|
|
// the table, then we need to transmit both
|
|
|
|
// Generate 1st tone
|
|
cur_tone = ((cur_tone + vcode1 + 1) % 33);
|
|
symbols[symbol_pos++] = cur_tone;
|
|
|
|
// Generate 2nd tone
|
|
cur_tone = ((cur_tone + vcode2 + 1) % 33);
|
|
symbols[symbol_pos++] = cur_tone;
|
|
}
|
|
break; // We've found and transmitted the char,
|
|
// so exit the for loop
|
|
}
|
|
}
|
|
|
|
tx_message++;
|
|
}
|
|
|
|
// Message termination
|
|
// ----------------
|
|
symbols[symbol_pos] = 0xff;
|
|
}
|
|
|
|
/*
|
|
* fsq_dir_encode(const char * from_call, const char * to_call, const char cmd, const char * message, uint8_t * symbols)
|
|
*
|
|
* Takes an arbitrary message and returns a FSQ channel symbol table.
|
|
*
|
|
* from_call - Callsign from which message is directed (maximum size: 20)
|
|
* to_call - Callsign to which message is directed (maximum size: 20)
|
|
* cmd - Directed command
|
|
* message - Null-terminated message string, no greater than 100 chars in length
|
|
* symbols - Array of channel symbols to transmit returned by the method.
|
|
* Ensure that you pass a uint8_t array of at least the size of the message
|
|
* plus 5 characters to the method. Terminated in 0xFF.
|
|
*
|
|
*/
|
|
char fsq_dir_tx_buffer[155];
|
|
void JTEncode::fsq_dir_encode(const char * from_call, const char * to_call, const char cmd, const char * message, uint8_t * symbols)
|
|
{
|
|
char * tx_message;
|
|
uint16_t symbol_pos = 0;
|
|
uint8_t i, fch, vcode1, vcode2, tone, from_call_crc;
|
|
uint8_t cur_tone = 0;
|
|
|
|
// Generate a CRC on from_call
|
|
// ---------------------------
|
|
from_call_crc = crc8(from_call);
|
|
|
|
// Clear out the transmit buffer
|
|
// -----------------------------
|
|
memset(fsq_dir_tx_buffer, 0, 155);
|
|
|
|
// Create the message to be transmitted
|
|
// We are building a directed message here.
|
|
// FSQ very specifically needs " \b " in
|
|
// directed mode to indicate EOT. A single backspace won't do it.
|
|
sprintf(fsq_dir_tx_buffer, " \n%s:%02x%s%c%s%s", from_call, from_call_crc, to_call, cmd, message, " \b ");
|
|
|
|
tx_message = fsq_dir_tx_buffer;
|
|
|
|
// Iterate through the message and encode
|
|
// --------------------------------------
|
|
while(*tx_message != '\0')
|
|
{
|
|
for(i = 0; i < NGLYPHS; i++)
|
|
{
|
|
uint8_t ch = (uint8_t)*tx_message;
|
|
|
|
// Check each element of the varicode table to see if we've found the
|
|
// character we're trying to send.
|
|
fch = fsq_code_table[i].ch;
|
|
|
|
if(fch == ch)
|
|
{
|
|
// Found the character, now fetch the varicode chars
|
|
vcode1 = fsq_code_table[i].var[0];
|
|
vcode2 = fsq_code_table[i].var[1];
|
|
|
|
// Transmit the appropriate tone per a varicode char
|
|
if(vcode2 == 0)
|
|
{
|
|
// If the 2nd varicode char is a 0 in the table,
|
|
// we are transmitting a lowercase character, and thus
|
|
// only transmit one tone for this character.
|
|
|
|
// Generate tone
|
|
cur_tone = ((cur_tone + vcode1 + 1) % 33);
|
|
symbols[symbol_pos++] = cur_tone;
|
|
}
|
|
else
|
|
{
|
|
// If the 2nd varicode char is anything other than 0 in
|
|
// the table, then we need to transmit both
|
|
|
|
// Generate 1st tone
|
|
cur_tone = ((cur_tone + vcode1 + 1) % 33);
|
|
symbols[symbol_pos++] = cur_tone;
|
|
|
|
// Generate 2nd tone
|
|
cur_tone = ((cur_tone + vcode2 + 1) % 33);
|
|
symbols[symbol_pos++] = cur_tone;
|
|
}
|
|
break; // We've found and transmitted the char,
|
|
// so exit the for loop
|
|
}
|
|
}
|
|
|
|
tx_message++;
|
|
}
|
|
|
|
// Message termination
|
|
// ----------------
|
|
symbols[symbol_pos] = 0xff;
|
|
}
|
|
|
|
/*
|
|
* ft8_encode(const char * message, uint8_t * symbols)
|
|
*
|
|
* Takes an arbitrary message of up to 13 allowable characters or a telemetry message
|
|
* of up to 18 hexadecimal digit (in string format) and returns a channel symbol table.
|
|
* Encoded for the FT8 protocol used in WSJT-X v2.0 and beyond (79 channel symbols).
|
|
*
|
|
* message - Type 0.0 free text message or Type 0.5 telemetry message.
|
|
* symbols - Array of channel symbols to transmit returned by the method.
|
|
* Ensure that you pass a uint8_t array of at least size FT8_SYMBOL_COUNT to the method.
|
|
*
|
|
*/
|
|
uint8_t ft8_buffer_s[FT8_BIT_COUNT];
|
|
void JTEncode::ft8_encode(const char * msg, uint8_t * symbols)
|
|
{
|
|
uint8_t i;
|
|
|
|
char message[19];
|
|
memset(message, 0, 19);
|
|
strcpy(message, msg);
|
|
|
|
// Bit packing
|
|
// -----------
|
|
uint8_t c[77];
|
|
memset(c, 0, 77);
|
|
ft8_bit_packing(message, c);
|
|
|
|
// Message Encoding
|
|
// ----------------
|
|
ft8_encode(c, ft8_buffer_s);
|
|
|
|
// Merge with sync vector
|
|
// ----------------------
|
|
ft8_merge_sync_vector(ft8_buffer_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::ft_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) - 47;
|
|
}
|
|
else if(c >= 'A' && c <= 'Z')
|
|
{
|
|
return (uint8_t)(c) - 54;
|
|
}
|
|
else if(c == ' ')
|
|
{
|
|
return 0;
|
|
}
|
|
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;
|
|
}
|
|
|
|
int8_t JTEncode::hex2int(char ch)
|
|
{
|
|
if (ch >= '0' && ch <= '9')
|
|
return ch - '0';
|
|
if (ch >= 'A' && ch <= 'F')
|
|
return ch - 'A' + 10;
|
|
if (ch >= 'a' && ch <= 'f')
|
|
return ch - 'a' + 10;
|
|
return -1;
|
|
}
|
|
|
|
void JTEncode::jt_message_prep(char * message)
|
|
{
|
|
uint8_t i;
|
|
|
|
// Pad the message with trailing spaces
|
|
uint8_t len = strlen(message);
|
|
if(len < 13)
|
|
{
|
|
for(i = len; i <= 13; i++)
|
|
{
|
|
message[i] = ' ';
|
|
}
|
|
}
|
|
|
|
// Convert all chars to uppercase
|
|
for(i = 0; i < 13; i++)
|
|
{
|
|
if(islower(message[i]))
|
|
{
|
|
message[i] = toupper(message[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void JTEncode::ft_message_prep(char * message)
|
|
{
|
|
uint8_t i;
|
|
char temp_msg[14];
|
|
|
|
snprintf(temp_msg, 14, "%13s", message);
|
|
|
|
// Convert all chars to uppercase
|
|
for(i = 0; i < 13; i++)
|
|
{
|
|
if(islower(temp_msg[i]))
|
|
{
|
|
temp_msg[i] = toupper(temp_msg[i]);
|
|
}
|
|
}
|
|
|
|
strcpy(message, temp_msg);
|
|
}
|
|
|
|
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')))
|
|
{
|
|
memcpy(loc, "AA00", 5);
|
|
//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::ft8_bit_packing(char* message, uint8_t* codeword)
|
|
{
|
|
// Just encoding type 0 free text and type 0.5 telemetry for now
|
|
|
|
// The bit packing algorithm is:
|
|
// sum(message(pos) * 42^pos)
|
|
|
|
uint8_t i3 = 0;
|
|
uint8_t n3 = 0;
|
|
uint8_t qa[10];
|
|
uint8_t qb[10];
|
|
char c18[19];
|
|
bool telem = false;
|
|
char temp_msg[19];
|
|
memset(qa, 0, 10);
|
|
memset(qb, 0, 10);
|
|
|
|
uint8_t i, j, x, i0;
|
|
uint32_t ireg = 0;
|
|
|
|
// See if this is a telemetry message
|
|
// Has to be hex digits, can be no more than 18
|
|
for(i = 0; i < 19; ++i)
|
|
{
|
|
if(message[i] == 0 || message[i] == ' ')
|
|
{
|
|
break;
|
|
}
|
|
else if(hex2int(message[i]) == -1)
|
|
{
|
|
telem = false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
c18[i] = message[i];
|
|
telem = true;
|
|
}
|
|
}
|
|
|
|
// If telemetry
|
|
if(telem)
|
|
{
|
|
// Get the first 18 hex digits
|
|
for(i = 0; i < strlen(message); ++i)
|
|
{
|
|
i0 = i;
|
|
if(message[i] == ' ')
|
|
{
|
|
--i0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
memset(c18, 0, 19);
|
|
memmove(c18, message, i0 + 1);
|
|
snprintf(temp_msg, 19, "%*s", 18, c18);
|
|
|
|
// Convert all chars to uppercase
|
|
for(i = 0; i < strlen(temp_msg); i++)
|
|
{
|
|
if(islower(temp_msg[i]))
|
|
{
|
|
temp_msg[i] = toupper(temp_msg[i]);
|
|
}
|
|
}
|
|
strcpy(message, temp_msg);
|
|
|
|
|
|
uint8_t temp_int;
|
|
temp_int = message[0] == ' ' ? 0 : hex2int(message[0]);
|
|
for(i = 1; i < 4; ++i)
|
|
{
|
|
codeword[i - 1] = (((temp_int << i) & 0x8) >> 3) & 1;
|
|
}
|
|
temp_int = message[1] == ' ' ? 0 : hex2int(message[1]);
|
|
for(i = 0; i < 4; ++i)
|
|
{
|
|
codeword[i + 3] = (((temp_int << i) & 0x8) >> 3) & 1;
|
|
}
|
|
for(i = 0; i < 8; ++i)
|
|
{
|
|
if(message[2 * i + 2] == ' ')
|
|
{
|
|
temp_int = 0;
|
|
}
|
|
else
|
|
{
|
|
temp_int = hex2int(message[2 * i + 2]);
|
|
}
|
|
for(j = 0; j < 4; ++j)
|
|
{
|
|
codeword[(i + 1) * 8 + j - 1] = (((temp_int << j) & 0x8) >> 3) & 1;
|
|
}
|
|
if(message[2 * i + 3] == ' ')
|
|
{
|
|
temp_int = 0;
|
|
}
|
|
else
|
|
{
|
|
temp_int = hex2int(message[2 * i + 3]);
|
|
}
|
|
for(j = 0; j < 4; ++j)
|
|
{
|
|
codeword[(i + 1) * 8 + j + 3] = (((temp_int << j) & 0x8) >> 3) & 1;
|
|
}
|
|
}
|
|
|
|
i3 = 0;
|
|
n3 = 5;
|
|
}
|
|
else
|
|
{
|
|
ft_message_prep(message);
|
|
|
|
for(i = 0; i < 13; ++i)
|
|
{
|
|
x = ft_code(message[i]);
|
|
|
|
// mult
|
|
ireg = 0;
|
|
for(j = 0; j < 9; ++j)
|
|
{
|
|
ireg = (uint8_t)qa[j] * 42 + (uint8_t)((ireg >> 8) & 0xff);
|
|
qb[j] = (uint8_t)(ireg & 0xff);
|
|
}
|
|
qb[9] = (uint8_t)((ireg >> 8) & 0xff);
|
|
|
|
// add
|
|
ireg = x << 8;
|
|
for(j = 0; j < 9; ++j)
|
|
{
|
|
ireg = (uint8_t)qb[j] + (uint8_t)((ireg >> 8) & 0xff);
|
|
qa[j] = (uint8_t)(ireg & 0xff);
|
|
}
|
|
qa[9] = (uint8_t)((ireg >> 8) & 0xff);
|
|
}
|
|
|
|
// Format bits to output array
|
|
for(i = 1; i < 8; ++i)
|
|
{
|
|
codeword[i - 1] = (((qa[8] << i) & 0x80) >> 7) & 1;
|
|
}
|
|
for(i = 0; i < 8; ++i)
|
|
{
|
|
for(j = 0; j < 8; ++j)
|
|
{
|
|
codeword[(i + 1) * 8 + j - 1] = (((qa[7 - i] << j) & 0x80) >> 7) & 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write the message type bits at the end of the array
|
|
for(i = 0; i < 3; ++i)
|
|
{
|
|
codeword[i + 71] = (n3 >> i) & 1;
|
|
}
|
|
for(i = 0; i < 3; ++i)
|
|
{
|
|
codeword[i + 74] = (i3 >> i) & 1;
|
|
}
|
|
}
|
|
|
|
uint8_t jt65_buffer_d[JT65_ENCODE_COUNT];
|
|
void JTEncode::jt65_interleave(uint8_t * s)
|
|
{
|
|
uint8_t i, j;
|
|
|
|
// Interleave
|
|
for(i = 0; i < 9; i++)
|
|
{
|
|
for(j = 0; j < 7; j++)
|
|
{
|
|
jt65_buffer_d[(j * 9) + i] = s[(i * 7) + j];
|
|
}
|
|
}
|
|
|
|
memcpy(s, jt65_buffer_d, JT65_ENCODE_COUNT);
|
|
}
|
|
|
|
uint8_t jt9_buffer_d[JT9_BIT_COUNT];
|
|
void JTEncode::jt9_interleave(uint8_t * s)
|
|
{
|
|
uint8_t i, j;
|
|
|
|
// Do the interleave
|
|
for(i = 0; i < JT9_BIT_COUNT; i++)
|
|
{
|
|
//#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
|
|
#if defined(__arm__)
|
|
jt9_buffer_d[jt9i[i]] = s[i];
|
|
#else
|
|
j = pgm_read_byte(&jt9i[i]);
|
|
jt9_buffer_d[j] = s[i];
|
|
#endif
|
|
}
|
|
|
|
memcpy(s, jt9_buffer_d, JT9_BIT_COUNT);
|
|
}
|
|
|
|
uint8_t wspr_buffer_d[WSPR_BIT_COUNT];
|
|
void JTEncode::wspr_interleave(uint8_t * s)
|
|
{
|
|
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)
|
|
{
|
|
wspr_buffer_d[rev] = s[i];
|
|
i++;
|
|
}
|
|
|
|
if(i >= WSPR_BIT_COUNT)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
memcpy(s, wspr_buffer_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::ft8_encode(uint8_t* codeword, uint8_t* symbols)
|
|
{
|
|
const uint8_t FT8_N = 174;
|
|
const uint8_t FT8_K = 91;
|
|
const uint8_t FT8_M = FT8_N - FT8_K;
|
|
|
|
uint8_t tempchar[FT8_K];
|
|
uint8_t message91[FT8_K];
|
|
uint8_t pchecks[FT8_M];
|
|
uint8_t i1_msg_bytes[12];
|
|
uint8_t i, j;
|
|
uint16_t ncrc14;
|
|
|
|
crc_t crc;
|
|
crc_cfg_t crc_cfg;
|
|
crc_cfg.reflect_in = 0;
|
|
crc_cfg.xor_in = 0;
|
|
crc_cfg.reflect_out = 0;
|
|
crc_cfg.xor_out = 0;
|
|
crc = crc_init(&crc_cfg);
|
|
|
|
// Add 14-bit CRC to form 91-bit message
|
|
memset(tempchar, 0, 91);
|
|
memcpy(tempchar, codeword, 77);
|
|
tempchar[77] = 0;
|
|
tempchar[78] = 0;
|
|
tempchar[79] = 0;
|
|
memset(i1_msg_bytes, 0, 12);
|
|
for(i = 0; i < 10; ++i)
|
|
{
|
|
for(j = 0; j < 8; ++j)
|
|
{
|
|
i1_msg_bytes[i] <<= 1;
|
|
i1_msg_bytes[i] |= tempchar[i * 8 + j];
|
|
}
|
|
}
|
|
|
|
ncrc14 = crc_update(&crc_cfg, crc, (unsigned char *)i1_msg_bytes, 12);
|
|
crc = crc_finalize(&crc_cfg, crc);
|
|
|
|
for(i = 0; i < 14; ++i)
|
|
{
|
|
if((((ncrc14 << (i + 2)) & 0x8000) >> 15) & 1)
|
|
{
|
|
tempchar[i + 77] = 1;
|
|
}
|
|
else
|
|
{
|
|
tempchar[i + 77] = 0;
|
|
}
|
|
}
|
|
memcpy(message91, tempchar, 91);
|
|
|
|
for(i = 0; i < FT8_M; ++i)
|
|
{
|
|
uint32_t nsum = 0;
|
|
for(j = 0; j < FT8_K; ++j)
|
|
{
|
|
#if defined(__arm__)
|
|
uint8_t bits = generator_bits[i][j / 8];
|
|
#else
|
|
uint8_t bits = pgm_read_byte(&(generator_bits[i][j / 8]));
|
|
#endif
|
|
bits <<= (j % 8);
|
|
bits &= 0x80;
|
|
bits >>= 7;
|
|
bits &= 1;
|
|
nsum += (message91[j] * bits);
|
|
}
|
|
pchecks[i] = nsum % 2;
|
|
}
|
|
|
|
memcpy(symbols, message91, FT8_K);
|
|
memcpy(symbols + FT8_K, pchecks, FT8_M);
|
|
}
|
|
|
|
const uint8_t jt65_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};
|
|
|
|
void JTEncode::jt65_merge_sync_vector(uint8_t * g, uint8_t * symbols)
|
|
{
|
|
uint8_t i, j = 0;
|
|
|
|
for(i = 0; i < JT65_SYMBOL_COUNT; i++)
|
|
{
|
|
if(jt65_sync_vector[i])
|
|
{
|
|
symbols[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
symbols[i] = g[j] + 2;
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
const uint8_t jt9_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};
|
|
void JTEncode::jt9_merge_sync_vector(uint8_t * g, uint8_t * symbols)
|
|
{
|
|
uint8_t i, j = 0;
|
|
|
|
for(i = 0; i < JT9_SYMBOL_COUNT; i++)
|
|
{
|
|
if(jt9_sync_vector[i])
|
|
{
|
|
symbols[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
symbols[i] = g[j] + 1;
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void JTEncode::jt4_merge_sync_vector(uint8_t * g, uint8_t * symbols)
|
|
{
|
|
uint8_t i;
|
|
const uint8_t sync_vector[JT4_SYMBOL_COUNT] =
|
|
{0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0,
|
|
0, 0, 0, 0, 1, 1, 0, 0, 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ,1,
|
|
0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0,
|
|
1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
|
|
0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0,
|
|
1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1,
|
|
1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
|
|
0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
|
|
1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1,
|
|
0, 1, 1, 1, 1, 0, 1, 0, 1};
|
|
|
|
for(i = 0; i < JT4_SYMBOL_COUNT; i++)
|
|
{
|
|
symbols[i] = sync_vector[i] + (2 * g[i]);
|
|
}
|
|
}
|
|
|
|
const uint8_t wspr_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};
|
|
void JTEncode::wspr_merge_sync_vector(uint8_t * g, uint8_t * symbols)
|
|
{
|
|
uint8_t i;
|
|
|
|
for(i = 0; i < WSPR_SYMBOL_COUNT; i++)
|
|
{
|
|
symbols[i] = wspr_sync_vector[i] + (2 * g[i]);
|
|
}
|
|
}
|
|
|
|
void JTEncode::ft8_merge_sync_vector(uint8_t* symbols, uint8_t* output)
|
|
{
|
|
const uint8_t costas7x7[7] = {3, 1, 4, 0, 6, 5, 2};
|
|
const uint8_t graymap[8] = {0, 1, 3, 2, 5, 6, 4, 7};
|
|
uint8_t i, j, k, idx;
|
|
|
|
// Insert Costas sync arrays
|
|
memcpy(output, costas7x7, 7);
|
|
memcpy(output + 36, costas7x7, 7);
|
|
memcpy(output + FT8_SYMBOL_COUNT - 7, costas7x7, 7);
|
|
|
|
k = 6;
|
|
for(j = 0; j < 58; ++j) // 58 data symbols
|
|
{
|
|
i = 3 * j;
|
|
++k;
|
|
if(j == 29)
|
|
{
|
|
k += 7;
|
|
}
|
|
idx = symbols[i] * 4 + symbols[i + 1] * 2 + symbols[i + 2];
|
|
output[k] = graymap[idx];
|
|
}
|
|
}
|
|
|
|
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
|
|
uint8_t dat1[12];
|
|
uint8_t b[51];
|
|
uint8_t sym[JT65_ENCODE_COUNT];
|
|
uint8_t 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++)
|
|
{
|
|
sym[50 - i] = b[i];
|
|
}
|
|
|
|
for (i = 0; i < 12; i++)
|
|
{
|
|
sym[i + 51] = dat1[11 - i];
|
|
}
|
|
|
|
memcpy(symbols, sym, JT65_ENCODE_COUNT);
|
|
}
|
|
|
|
uint8_t JTEncode::crc8(const char * text)
|
|
{
|
|
uint8_t crc = '\0';
|
|
uint8_t ch;
|
|
|
|
int i;
|
|
for(i = 0; i < strlen(text); i++)
|
|
{
|
|
ch = text[i];
|
|
//#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
|
|
#if defined(__arm__)
|
|
crc = crc8_table[(crc) ^ ch];
|
|
#else
|
|
crc = pgm_read_byte(&(crc8_table[(crc) ^ ch]));
|
|
#endif
|
|
crc &= 0xFF;
|
|
}
|
|
|
|
return crc;
|
|
}
|