Skip to content

Commit 75827c9

Browse files
committed
Initial commit
0 parents  commit 75827c9

File tree

11 files changed

+1604
-0
lines changed

11 files changed

+1604
-0
lines changed

JTEncode.cpp

Lines changed: 754 additions & 0 deletions
Large diffs are not rendered by default.

JTEncode.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* JTEncode.h - JT65/JT9/WSPR encoder library for Arduino
3+
*
4+
* Copyright (C) 2015 Jason Milldrum <[email protected]>
5+
*
6+
* Based on the algorithms presented in the WSJT software suite.
7+
* Thanks to Andy Talbot G4JNT for the whitepaper on the WSPR encoding
8+
* process that helped me to understand all of this.
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU General Public License as published by
12+
* the Free Software Foundation, either version 3 of the License, or
13+
* (at your option) any later version.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU General Public License
21+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22+
*/
23+
24+
#include "int.h"
25+
#include "rs_common.h"
26+
27+
#include <stdint.h>
28+
29+
#define JT65_SYMBOL_COUNT 126
30+
#define JT9_SYMBOL_COUNT 85
31+
#define WSPR_SYMBOL_COUNT 162
32+
33+
#define JT65_ENCODE_COUNT 63
34+
#define JT9_ENCODE_COUNT 69
35+
36+
#define JT9_BIT_COUNT 206
37+
#define WSPR_BIT_COUNT 162
38+
39+
class JTEncode
40+
{
41+
public:
42+
JTEncode(void);
43+
void jt65_encode(char *, uint8_t *);
44+
void jt9_encode(char *, uint8_t *);
45+
void wspr_encode(char *, char *, uint8_t, uint8_t *);
46+
private:
47+
uint8_t jt_code(char);
48+
uint8_t wspr_code(char);
49+
uint8_t gray_code(uint8_t);
50+
void jt_message_prep(char *);
51+
void wspr_message_prep(char *, char *, uint8_t);
52+
void jt65_bit_packing(char *, uint8_t *);
53+
void jt9_bit_packing(char *, uint8_t *);
54+
void wspr_bit_packing(uint8_t *);
55+
void jt65_interleave(uint8_t *);
56+
void jt9_interleave(uint8_t *);
57+
void wspr_interleave(uint8_t *);
58+
void jt9_packbits(uint8_t *, uint8_t *);
59+
void jt_gray_code(uint8_t *, uint8_t);
60+
void jt65_merge_sync_vector(uint8_t *, uint8_t *);
61+
void jt9_merge_sync_vector(uint8_t *, uint8_t *);
62+
void wspr_merge_sync_vector(uint8_t *, uint8_t *);
63+
void convolve(uint8_t *, uint8_t *, uint8_t, uint8_t);
64+
void rs_encode(uint8_t *, uint8_t *);
65+
void encode_rs_int(void *,data_t *, data_t *);
66+
void free_rs_int(void *);
67+
void * init_rs_int(int, int, int, int, int, int);
68+
void * rs_inst;
69+
char callsign[7];
70+
char locator[5];
71+
uint8_t power;
72+
};

README.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
JT65/JT9/WSPR Encoder Library for Arduino
2+
=========================================
3+
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.
4+
5+
Please feel free to use the issues feature of GitHub if you run into problems or have suggestions for important features to implement.
6+
7+
Hardware Requirements and Setup
8+
-------------------------------
9+
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.
10+
11+
How To Install
12+
--------------
13+
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.
14+
15+
(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.)
16+
17+
Example
18+
-------
19+
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.
20+
21+
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.
22+
23+
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.
24+
25+
An instance of the JTEncode object is created:
26+
27+
JTEncode jtencode;
28+
29+
On sketch startup, the mode parameters are set based on which mode is currently selected (by the DEFAULT_MODE define):
30+
31+
// Set the proper frequency, tone spacing, symbol count, and
32+
// timer CTC depending on mode
33+
switch(cur_mode)
34+
{
35+
case MODE_JT9:
36+
freq = JT9_DEFAULT_FREQ;
37+
ctc = JT9_CTC;
38+
symbol_count = JT9_SYMBOL_COUNT; // From the library defines
39+
tone_spacing = JT9_TONE_SPACING;
40+
break;
41+
case MODE_JT65:
42+
freq = JT65_DEFAULT_FREQ;
43+
ctc = JT65_CTC;
44+
symbol_count = JT65_SYMBOL_COUNT; // From the library defines
45+
tone_spacing = JT65_TONE_SPACING;
46+
break;
47+
case MODE_WSPR:
48+
freq = WSPR_DEFAULT_FREQ;
49+
ctc = WSPR_CTC;
50+
symbol_count = WSPR_SYMBOL_COUNT; // From the library defines
51+
tone_spacing = WSPR_TONE_SPACING;
52+
break;
53+
}
54+
55+
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.
56+
57+
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:
58+
59+
// Set the proper frequency and timer CTC depending on mode
60+
switch(cur_mode)
61+
{
62+
case MODE_JT9:
63+
jtencode.jt9_encode(message, tx_buffer);
64+
break;
65+
case MODE_JT65:
66+
jtencode.jt65_encode(message, tx_buffer);
67+
break;
68+
case MODE_WSPR:
69+
jtencode.wspr_encode(call, loc, dbm, tx_buffer);
70+
break;
71+
}
72+
73+
Once the channel symbols have been generate, it is a simple matter of transmitting them in sequence, each of the correct amount of time:
74+
75+
// Now transmit the channel symbols
76+
for(i = 0; i < symbol_count; i++)
77+
{
78+
si5351.set_freq((freq * 100) + (tx_buffer[i] * tone_spacing), 0, SI5351_CLK0);
79+
proceed = false;
80+
while(!proceed);
81+
}
82+
83+
Public Methods
84+
------------------
85+
###jt65_encode()
86+
```
87+
/*
88+
* jt65_encode(char * message, uint8_t * symbols)
89+
*
90+
* Takes an arbitrary message of up to 13 allowable characters and returns
91+
* a channel symbol table.
92+
*
93+
* message - Plaintext Type 6 message.
94+
* symbols - Array of channel symbols to transmit retunred by the method.
95+
* Ensure that you pass a uint8_t array of size JT65_SYMBOL_COUNT to the method.
96+
*
97+
*/
98+
```
99+
###jt9_encode()
100+
```
101+
/*
102+
* jt9_encode(char * message, uint8_t * symbols)
103+
*
104+
* Takes an arbitrary message of up to 13 allowable characters and returns
105+
* a channel symbol table.
106+
*
107+
* message - Plaintext Type 6 message.
108+
* symbols - Array of channel symbols to transmit retunred by the method.
109+
* Ensure that you pass a uint8_t array of size JT9_SYMBOL_COUNT to the method.
110+
*
111+
*/
112+
```
113+
114+
###wspr_encode()
115+
```
116+
/*
117+
* wspr_encode(char * call, char * loc, uint8_t dbm, uint8_t * symbols)
118+
*
119+
* Takes an arbitrary message of up to 13 allowable characters and returns
120+
*
121+
* call - Callsign (6 characters maximum).
122+
* loc - Maidenhead grid locator (4 charcters maximum).
123+
* dbm - Output power in dBm.
124+
* symbols - Array of channel symbols to transmit retunred by the method.
125+
* Ensure that you pass a uint8_t array of size WSPR_SYMBOL_COUNT to the method.
126+
*
127+
*/
128+
```
129+
Tokens
130+
------
131+
Here are the defines, structs, and enumerations you will find handy to use with the library.
132+
133+
Defines:
134+
135+
JT65_SYMBOL_COUNT, JT9_SYMBOL_COUNT, WSPR_SYMBOL_COUNT
136+
137+
Acknowledgements
138+
----------------
139+
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.

encode_rs.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/* The guts of the Reed-Solomon encoder, meant to be #included
2+
* into a function body with the following typedefs, macros and variables supplied
3+
* according to the code parameters:
4+
5+
* data_t - a typedef for the data symbol
6+
* data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
7+
* data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
8+
* NROOTS - the number of roots in the RS code generator polynomial,
9+
* which is the same as the number of parity symbols in a block.
10+
Integer variable or literal.
11+
*
12+
* NN - the total number of symbols in a RS block. Integer variable or literal.
13+
* PAD - the number of pad symbols in a block. Integer variable or literal.
14+
* ALPHA_TO - The address of an array of NN elements to convert Galois field
15+
* elements in index (log) form to polynomial form. Read only.
16+
* INDEX_OF - The address of an array of NN elements to convert Galois field
17+
* elements in polynomial form to index (log) form. Read only.
18+
* MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
19+
* GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
20+
21+
* The memset() and memmove() functions are used. The appropriate header
22+
* file declaring these functions (usually <string.h>) must be included by the calling
23+
* program.
24+
25+
* Copyright 2004, Phil Karn, KA9Q
26+
* May be used under the terms of the GNU Lesser General Public License (LGPL)
27+
*/
28+
29+
30+
#undef A0
31+
#define A0 (NN) /* Special reserved value encoding zero in index form */
32+
33+
{
34+
int i, j;
35+
data_t feedback;
36+
37+
memset(parity,0,NROOTS*sizeof(data_t));
38+
39+
for(i=0;i<NN-NROOTS-PAD;i++){
40+
feedback = INDEX_OF[data[i] ^ parity[0]];
41+
if(feedback != A0){ /* feedback term is non-zero */
42+
#ifdef UNNORMALIZED
43+
/* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
44+
* always be for the polynomials constructed by init_rs()
45+
*/
46+
feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
47+
#endif
48+
for(j=1;j<NROOTS;j++)
49+
parity[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
50+
}
51+
/* Shift */
52+
memmove(&parity[0],&parity[1],sizeof(data_t)*(NROOTS-1));
53+
if(feedback != A0)
54+
parity[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
55+
else
56+
parity[NROOTS-1] = 0;
57+
}
58+
}

encode_rs_int.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* Reed-Solomon encoder
2+
* Copyright 2003, Phil Karn, KA9Q
3+
* May be used under the terms of the GNU Lesser General Public License (LGPL)
4+
*
5+
* Slightly modified by Jason Milldrum NT7S, 2015 to fit into the Arduino framework
6+
*
7+
* The guts of the Reed-Solomon encoder, meant to be #included
8+
* into a function body with the following typedefs, macros and variables supplied
9+
* according to the code parameters:
10+
11+
* data_t - a typedef for the data symbol
12+
* data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded
13+
* data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols
14+
* NROOTS - the number of roots in the RS code generator polynomial,
15+
* which is the same as the number of parity symbols in a block.
16+
Integer variable or literal.
17+
*
18+
* NN - the total number of symbols in a RS block. Integer variable or literal.
19+
* PAD - the number of pad symbols in a block. Integer variable or literal.
20+
* ALPHA_TO - The address of an array of NN elements to convert Galois field
21+
* elements in index (log) form to polynomial form. Read only.
22+
* INDEX_OF - The address of an array of NN elements to convert Galois field
23+
* elements in polynomial form to index (log) form. Read only.
24+
* MODNN - a function to reduce its argument modulo NN. May be inline or a macro.
25+
* GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form
26+
27+
* The memset() and memmove() functions are used. The appropriate header
28+
* file declaring these functions (usually <string.h>) must be included by the calling
29+
* program.
30+
*/
31+
32+
#include <string.h>
33+
#include <JTEncode.h>
34+
#include "int.h"
35+
#include "rs_common.h"
36+
37+
void JTEncode::encode_rs_int(void *p, data_t *data, data_t *parity)
38+
{
39+
struct rs *rs = (struct rs *)p;
40+
41+
#undef A0
42+
#define A0 (NN) /* Special reserved value encoding zero in index form */
43+
44+
{
45+
int i, j;
46+
data_t feedback;
47+
48+
memset(parity,0,NROOTS*sizeof(data_t));
49+
50+
for(i=0;i<NN-NROOTS-PAD;i++){
51+
feedback = INDEX_OF[data[i] ^ parity[0]];
52+
if(feedback != A0){ /* feedback term is non-zero */
53+
#ifdef UNNORMALIZED
54+
/* This line is unnecessary when GENPOLY[NROOTS] is unity, as it must
55+
* always be for the polynomials constructed by init_rs()
56+
*/
57+
feedback = MODNN(NN - GENPOLY[NROOTS] + feedback);
58+
#endif
59+
for(j=1;j<NROOTS;j++)
60+
parity[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS-j])];
61+
}
62+
/* Shift */
63+
memmove(&parity[0],&parity[1],sizeof(data_t)*(NROOTS-1));
64+
if(feedback != A0)
65+
parity[NROOTS-1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])];
66+
else
67+
parity[NROOTS-1] = 0;
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)