-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathwsp-beacon.ino
334 lines (281 loc) · 9.98 KB
/
wsp-beacon.ino
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
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#include <JTEncode.h>
#include <si5351.h>
#include <TimeLib.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#define FIRMWARE_VERSION 1.0
//******************************************************************
// WSPR configuration
//******************************************************************
// WSPR protocol configuration
#define WSPR_TONE_SPACING 146
#define WSPR_DELAY 683
#define WSPR_MESSAGE_BUFFER_SIZE 255
// WSPR center frequency in Hz
// #define WSPR_DEFAULT_FREQ 137500ULL // 0.1375 MHz - 2200m
// #define WSPR_DEFAULT_FREQ 475700ULL // 0.4757 MHz - 600m
// #define WSPR_DEFAULT_FREQ 1838100ULL // 1.8381 MHz - 160m
// #define WSPR_DEFAULT_FREQ 3570100ULL // 3.5701 MHz - 80m
// #define WSPR_DEFAULT_FREQ 5288700ULL // 5.2887 MHz - 60m
// #define WSPR_DEFAULT_FREQ 7040100ULL // 7.0401 MHz - 40m
// #define WSPR_DEFAULT_FREQ 10140200ULL // 10.1402 MHz - 30m
// #define WSPR_DEFAULT_FREQ 14097100ULL // 14.0971 MHz - 20m
// #define WSPR_DEFAULT_FREQ 18106100ULL // 18.1061 MHz - 17m
// #define WSPR_DEFAULT_FREQ 21096100ULL // 21.0961 MHz - 15m
// #define WSPR_DEFAULT_FREQ 24926100ULL // 24.9261 MHz - 12m
#define WSPR_DEFAULT_FREQ 28126100ULL // 28.1261 MHz - 10m
// #define WSPR_DEFAULT_FREQ 50294500ULL // 50.2945 MHz - 6m
// #define WSPR_DEFAULT_FREQ 144490000ULL // 144.4900 MHz - 2m
// WSPR message parameters
#define WSPR_CALL "XX0YYY"
#define WSPR_DBM 23
char WSPR_QTH_LOCATOR[5];
//******************************************************************
// Hardware defines
//******************************************************************
#define TX_LED_PIN 8
#define POWER_ON_LED_PIN 10
#define SERIAL_PORT_BAUDRATE 115200
#define RESET_DELAY 1000
#define SI5351_CAL_FACTOR 2000
#define SI5351_I2C_ADDRESS 0x60
#define GPS_RX_PIN 4
#define GPS_TX_PIN 3
#define GPS_BAUDRATE 9600
#define GPS_SERIAL_READ_DURATION 1200
#define GPS_STATUS_LED_PIN 9
#define GPS_INIT_MAX_TIME 5000
#define GPS_INIT_DELAY 500
#define GPS_SYNC_ATTEMPTS 10
#define GPS_SYNC_DELAY 10000
//******************************************************************
// Global variables
//******************************************************************
uint8_t tx_buffer[WSPR_MESSAGE_BUFFER_SIZE];
Si5351 si5351(SI5351_I2C_ADDRESS);
void(* resetHardware) (void) = 0;
//******************************************************************
// Function Prototypes
//******************************************************************
void initializeLEDs();
void initializeGPSSerialConnection(SoftwareSerial& gpsSerial);
void initializeSI5351();
void synchronizeGPSData();
bool trySyncGPSData(SoftwareSerial& gpsSerial, TinyGPSPlus& gps);
void setQTHLocator(const TinyGPSPlus& gps);
void encodeWSPRMessage();
void transmitWSPRMessage();
void printCurrentDateTime();
void printCurrentLocation(const TinyGPSPlus& gps);
void printDelimiter();
void printTransmissionDetails();
void printWSPRConfiguration();
//******************************************************************
// Function Definitions
//******************************************************************
void initializeLEDs()
{
pinMode(TX_LED_PIN, OUTPUT);
digitalWrite(TX_LED_PIN, LOW);
pinMode(POWER_ON_LED_PIN, OUTPUT);
digitalWrite(POWER_ON_LED_PIN, HIGH);
pinMode(GPS_STATUS_LED_PIN, OUTPUT);
digitalWrite(GPS_STATUS_LED_PIN, LOW);
}
void initializeGPSSerialConnection(SoftwareSerial& gpsSerial)
{
gpsSerial.begin(GPS_BAUDRATE);
const unsigned long startTime{millis()};
while (gpsSerial.available() == false && millis() <= startTime + GPS_INIT_MAX_TIME)
{
delay(GPS_INIT_DELAY);
}
if (gpsSerial.available() == false)
{
Serial.println(F("- Unable to get data from GPS module! -"));
delay(RESET_DELAY);
resetHardware();
}
}
void initializeSI5351()
{
if (si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, SI5351_CAL_FACTOR))
{
Serial.println(F("- SI5351 successfully initialized! -"));
// Set CLK0 as TX OUT
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_6MA);
si5351.output_enable(SI5351_CLK0, 0);
}
else
{
Serial.println(F("- SI5351 initialization error! -"));
Serial.print(F("- Ensure that the SI5351 has an I2C address 0x"));
Serial.print(SI5351_I2C_ADDRESS, HEX);
Serial.println(F(" -"));
delay(RESET_DELAY);
resetHardware();
}
}
void synchronizeGPSData()
{
SoftwareSerial gpsSerial{GPS_RX_PIN, GPS_TX_PIN};
initializeGPSSerialConnection(gpsSerial);
Serial.println(F("- GPS data sync -"));
TinyGPSPlus gps;
uint8_t syncAttemps{1};
bool dataSynchronized{trySyncGPSData(gpsSerial, gps)};
while (dataSynchronized == false && syncAttemps < GPS_SYNC_ATTEMPTS)
{
Serial.print(F("- Sync attempt "));
Serial.print(syncAttemps);
Serial.println(F(" failed! -"));
Serial.println(F("- Waiting for the next sync attempt... -"));
delay(GPS_SYNC_DELAY);
dataSynchronized = trySyncGPSData(gpsSerial, gps);
++syncAttemps;
}
if (dataSynchronized)
{
Serial.print(F("- Date & time (GMT) synchronized by GPS: "));
printCurrentDateTime();
Serial.println(F(" -"));
Serial.print(F("- Location synchronized by GPS: "));
printCurrentLocation(gps);
Serial.println(F(" -"));
Serial.print(F("- QTH locator: "));
Serial.print(WSPR_QTH_LOCATOR);
Serial.println(F(" -"));
}
else
{
Serial.println(F("- GPS data sync not available! -"));
Serial.println(F("- Transmitting a WSPR message without time & location sync is impossible! -"));
Serial.println(F("- Check your GPS antenna and try again! -"));
delay(RESET_DELAY);
resetHardware();
}
}
bool trySyncGPSData(SoftwareSerial& gpsSerial, TinyGPSPlus& gps)
{
const unsigned long startTime{millis()};
while (millis() - startTime < GPS_SERIAL_READ_DURATION)
{
while (gpsSerial.available())
gps.encode(gpsSerial.read());
}
if (gps.time.isValid() && gps.date.isValid() && gps.location.isValid()){
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(), gps.date.day(), gps.date.month(), gps.date.year());
setQTHLocator(gps);
digitalWrite(GPS_STATUS_LED_PIN, HIGH);
return true;
}
digitalWrite(GPS_STATUS_LED_PIN, LOW);
return false;
}
void setQTHLocator(const TinyGPSPlus& gps) {
float latitude{gps.location.lat() + 90.0};
float longitude{gps.location.lng() + 180.0};
WSPR_QTH_LOCATOR[0] = 'A' + (longitude / 20);
WSPR_QTH_LOCATOR[1] = 'A' + (latitude / 10);
WSPR_QTH_LOCATOR[2] = '0' + (uint8_t)(longitude / 2) % 10;
WSPR_QTH_LOCATOR[3] = '0' + (uint8_t)(latitude) % 10;
WSPR_QTH_LOCATOR[4] = '\0';
}
void encodeWSPRMessage()
{
memset(tx_buffer, 0, WSPR_MESSAGE_BUFFER_SIZE);
JTEncode jtencode;
jtencode.wspr_encode(WSPR_CALL, WSPR_QTH_LOCATOR, WSPR_DBM, tx_buffer);
}
void transmittWsprMessage()
{
Serial.println(F("- WSPR TX ON -"));
digitalWrite(TX_LED_PIN, HIGH);
// WSPR message transmission at each function call is performed on a randomly selected
// frequency within the range of +/- 100 Hz from the center frequency.
const unsigned long transmissionFrequency{WSPR_DEFAULT_FREQ + random(-100, 101)};
Serial.print(F("- Transmisson frequency: "));
Serial.print(transmissionFrequency / 1000000.0, 6);
Serial.println(F(" MHz -"));
si5351.output_enable(SI5351_CLK0, 1);
for(uint8_t i{0}; i < WSPR_SYMBOL_COUNT; ++i)
{
si5351.set_freq(transmissionFrequency * 100ULL + (tx_buffer[i] * WSPR_TONE_SPACING), SI5351_CLK0);
delay(WSPR_DELAY);
}
si5351.output_enable(SI5351_CLK0, 0);
Serial.println(F("- WSPR TX OFF -"));
digitalWrite(TX_LED_PIN, LOW);
}
void printCurrentDateTime()
{
Serial.print(day());
Serial.print(F("/"));
Serial.print(month());
Serial.print(F("/"));
Serial.print(year());
Serial.print(F(" "));
Serial.print(hour());
Serial.print(F(":"));
Serial.print(minute());
Serial.print(F(":"));
Serial.print(second());
}
void printCurrentLocation(const TinyGPSPlus& gps)
{
Serial.print(gps.location.lat(), 4);
Serial.print(F(", "));
Serial.print(gps.location.lng(), 4);
}
void printDelimiter()
{
Serial.println(F("********************************************"));
}
void printTransmissionDetails() {
Serial.print(F("- Start of transmission time (GMT): "));
printCurrentDateTime();
Serial.println(F(" -"));
Serial.print(F("- WSPR message: "));
Serial.print(WSPR_CALL);
Serial.print(F(" "));
Serial.print(WSPR_QTH_LOCATOR);
Serial.print(F(" "));
Serial.print(WSPR_DBM);
Serial.println(F(" -"));
}
void printWSPRConfiguration() {
printDelimiter();
Serial.println(F("[ WSPR BEACON ]"));
Serial.print(F("- Firmware version: "));
Serial.print(FIRMWARE_VERSION);
Serial.println(F(" -"));
Serial.print(F("- Working frequency: "));
Serial.print(WSPR_DEFAULT_FREQ / 1000000.0, 6);
Serial.println(F(" MHz -"));
printDelimiter();
}
void setup()
{
initializeLEDs();
Serial.begin(SERIAL_PORT_BAUDRATE);
while (!Serial);
printWSPRConfiguration();
initializeSI5351();
synchronizeGPSData();
printDelimiter();
Serial.println(F("- Entering WSPR TX loop..."));
printDelimiter();
encodeWSPRMessage();
randomSeed(millis());
}
void loop()
{
if(second() == 0 && minute() % 2 == 0)
{
printTransmissionDetails();
transmittWsprMessage();
printDelimiter();
synchronizeGPSData();
printDelimiter();
}
}