Repository navigation
Expand file tree
/
Copy pathOrionQrss.cpp
More file actions
303 lines (240 loc) · 11 KB
/
Copy pathOrionQrss.cpp
File metadata and controls
303 lines (240 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*
OrionQrss.cpp - Orion QRSS Beacon (slow speed CW)
This provides a fallback QRSS TX mode that will become active
when a GPS_LOS_TIME_EV occurs, indicating a long term GPS LOS (loss of signal).
This code is derived from the QRSS/FSKCW/DFCW Beacon Keyer by Hans Summers G0UPL(copyright 2012)
and used his permission for this derivitive work. The original source code is from here :
https://qrp-labs.com/images/qrssarduino/qrss.ino
Adapted by Michael, VE3WMB to use the Si5351a as a transmitter.
Copyright (C) 2018-2019 Michael Babineau <mbabineau.ve3wmb@gmail.com>
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 "OrionXConfig.h"
#include "OrionBoardConfig.h"
#include "OrionSi5351.h"
#include "OrionQrss.h"
#include <Chrono.h>
#include "OrionSerialMonitor.h"
const char msg[] = QRSS_MESSAGE; // Defined in OrionQrss.h
// This array is indexed by a parameter of type QrssSpeed, defined in OrionXConfig.h
const unsigned int speeds[] = {1, 30, 60, 100}; // Speeds for: s12wpm, QRSS3, QRSS6, QRSS10
byte charCode(char c) {
// This function returns the encoded CW pattern for the character passed in.
// Binary encoding is left-padded. Unused high-order bits are all ones.
// The first zero is the start bit, which is discarded.
// Processing from higher to lower order, bits we skip over ones, then discard first 0 (start bit). The next bit is the first element.
// We process each element sending a DIT or DAH, until we reach the end of the pattern.
//
// Pattern encoding is 0 = DIT, 1 = DAH.
// So 'A' = B11111001, which is 1 1 1 1 1 (padding bits) 0 (start bit) 0 1 (dit, dah)
// This excellent encoding scheme was developed by Hans, G0UPL as noted above.
switch (c)
{
case 'A': return B11111001; break; // A .-
case 'B': return B11101000; break; // B -...
case 'C': return B11101010; break; // C -.-.
case 'D': return B11110100; break; // D -..
case 'E': return B11111100; break; // E .
case 'F': return B11100010; break; // F ..-.
case 'G': return B11110110; break; // G --.
case 'H': return B11100000; break; // H ....
case 'I': return B11111000; break; // I ..
case 'J': return B11100111; break; // J .---
case 'K': return B11110101; break; // K -.-
case 'L': return B11100100; break; // L .-..
case 'M': return B11111011; break; // M --
case 'N': return B11111010; break; // N -.
case 'O': return B11110111; break; // O ---
case 'P': return B11100110; break; // P .--.
case 'Q': return B11101101; break; // Q --.-
case 'R': return B11110010; break; // R .-.
case 'S': return B11110000; break; // S ...
case 'T': return B11111101; break; // T -
case 'U': return B11110001; break; // U ..-
case 'V': return B11100001; break; // V ...-
case 'W': return B11110011; break; // W .--
case 'X': return B11101001; break; // X -..-
case 'Y': return B11101011; break; // Y -.--
case 'Z': return B11101100; break; // Z --..
case '0': return B11011111; break; // 0 -----
case '1': return B11001111; break; // 1 .----
case '2': return B11000111; break; // 2 ..---
case '3': return B11000011; break; // 3 ...--
case '4': return B11000001; break; // 4 ....-
case '5': return B11000000; break; // 5 .....
case '6': return B11010000; break; // 6 -....
case '7': return B11011000; break; // 7 --...
case '8': return B11011100; break; // 8 ---..
case '9': return B11011110; break; // 9 ----.
case ' ': return B11101111; break; // Space - equal to 4 dah lengths
case '/': return B11010010; break; // / -..-.
default: return charCode(' ');
}
}
void setRfFsk(boolean rf_on, boolean setFSK_high)
{
uint8_t fsk_value;
if (setFSK_high == true) {
fsk_value = FSK_HIGH;
}
else {
fsk_value = FSK_LOW;
}
if (rf_on == true) {
si5351bx_setfreq(SI5351A_WSPRTX_CLK_NUM, ((QRSS_BEACON_FREQ_HZ + fsk_value) * 100ULL), SI5351_CLK_ON );
}
else {
si5351bx_enable_clk(SI5351A_WSPRTX_CLK_NUM, SI5351_CLK_OFF); // Disable the TX clock
}
}
//
bool qrss_transmit(QrssMode mode, QrssSpeed ditSpeed)
{
static byte timerCounter; // Counter to get to divide by 100 to get 10Hz
static int ditCounter; // Counter to time the length of each dit
static byte pause; // Generates the pause between characters
static byte msgIndex = 255; // Index into the message
static byte character; // Bit pattern for the character being sent
static byte key; // State of the key
static byte charBit; // Which bit of the bit pattern is being sent
static boolean dah; // True when a dah is being sent
byte divisor; // Divide 1kHz by 100 normally, but by 33 when sending DFCW)
bool transmission_done = false;
// Set Divisor based on Mode
if (mode == MODE_DFCW) // Divisor is 33 for DFCW, to get the correct timing
divisor = 33; // (inter-symbol gap is 1/3 of a dit)
else
divisor = 100; // For ever other mode it is one dit length
timerCounter++; // 1000Hz at this point
if (timerCounter == divisor) // Divides by 100 (or 33 for DFCW)
{
timerCounter = 0; // 10 Hz here (30Hz for DFCW)
ditCounter++; // Generates the correct dit-length
if (ditCounter >= speeds[ditSpeed]) { // We have counted the duration of a dit
ditCounter = 0;
if (!pause) {
// Pause is set to 2 after the last element of the character has been sent
key--; // This generates the correct pause between characters (3 dits)
if ((!key) && (!charBit)) {
if (mode == MODE_DFCW)
pause = 3; // DFCW needs an extra delay to make it 4/3 dit-length
else
pause = 2;
}
} // end if (!pause)
else
pause--;
// Key becomes 255 when the last element (dit or dah) of the character has been sent
if (key == 255) {
// Done sending the last element (dit or dah) in the character
// If the last symbol of the character has been sent, get the next character
if (!charBit) {
// Increment the message character index
msgIndex++;
// If we are at the end of the message flag transmission_done
if (!msg[msgIndex]) {
msgIndex = 0;
transmission_done = true;
}
else {
// Get the encoded bit pattern for the morse character
character = charCode(msg[msgIndex]);
// Start at the 7'th (leftmost) bit of the bit pattern
charBit = 7;
// Loop through bits looking for a 0, signifying start of coding bits
while (character & (1 << charBit)) charBit--;
}
} // end if (!charBit)
charBit--; // Move to the next rightermost bit of the pattern, this is the first element
if ((transmission_done == true) || (character == charCode(' ') )) { // Special case for space
key = 0;
dah = false;
}
else {
// Get the state of the current bit in the pattern
key = character & (1 << charBit);
if (key) { // If it's a 1, set this to a dah
key = 3;
dah = true;
}
else { // otherwise it's a dit
if (mode == MODE_DFCW) // Special case for DFCW - dit's and dah's are both
key = 3; // the same length.
else
key = 1;
dah = false;
}
}
} // end if (key == 255 )
if (!key) dah = false;
//
if (mode == MODE_FSKCW)
{
//setRF(true); // in FSK/CW mode, the RF output is always ON
//setFSK(key); // and the FSK depends on the key state
setRfFsk(true, key);
}
else if (mode == MODE_QRSS)
{
//setRF(key); // in QRSS mode, the RF output is keyed
//setFSK(false); // and the FSK is always off
setRfFsk(key, false);
}
else if (mode == MODE_DFCW)
{
//setRF(key); // in DFCW mode, the RF output is keyed (ON during a dit or a dah)
//setFSK(dah); // and the FSK depends on the key state
setRfFsk(key, dah);
}
else
setRfFsk(false, false);
} // end if (ditcounter >= speeds[ditspeed];
} // end if (timercounter == divisor)
if (transmission_done == true) {
// reset the static variables for the next transmission
timerCounter = 0;
ditCounter = 0;
pause = 0;
msgIndex = 255;
character = 0;
key = 0;
charBit = 0;
dah = false;
}
return transmission_done;
} // end qrss_transmit function
void qrss_beacon() {
static unsigned long milliPrev; // Static variable stores previous millisecond count
unsigned long milliNow;
bool done_transmission = false;
// Since we are using FSKCW, turn on the clock now to let it warm up, delay one second and then turn on TX
si5351bx_setfreq(SI5351A_WSPRTX_CLK_NUM, ((QRSS_BEACON_FREQ_HZ) * 100ULL), SI5351_CLK_OFF );
delay(1000);
si5351bx_enable_clk(SI5351A_WSPRTX_CLK_NUM, SI5351_CLK_ON);
// Turn off the PARK clock
si5351bx_enable_clk(SI5351A_PARK_CLK_NUM, SI5351_CLK_OFF);
log_qrss_tx_start(MODE_FSKCW, QRSS10);
while (!done_transmission) {
milliNow = millis(); // Get millisecond counter value
if (milliNow != milliPrev) // If one millisecond has elapsed, call the beacon() function
{
milliPrev = milliNow;
done_transmission = qrss_transmit(MODE_FSKCW, QRSS10); // This gets called once per millisecond (i.e 1000 times per second)
}
} // end while (!done)
// Ensure that the Si5351a TX clock is shutdown
si5351bx_enable_clk(SI5351A_WSPRTX_CLK_NUM, SI5351_CLK_OFF);
// Re-enable the Park Clock
si5351bx_setfreq(SI5351A_PARK_CLK_NUM, (PARK_FREQ_HZ * 100ULL), SI5351_CLK_ON);
log_qrss_tx_end();
delay(POST_QRSS_TX_DELAY_MS); // Kill some time before attempting calibration
} // end qrss_beacon()