1 // ----------------------------------------------------------------------------
2 // Nav.cxx -- Interface to Arduino Nano Nav keyer
3 //
4 // Copyright (C) 2018
5 // Dave Freese, W1HKJ
6 //
7 // This file is part of fldigi.
8 //
9 // Fldigi is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // Fldigi is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
21 // ----------------------------------------------------------------------------
22
23
24 #include <config.h>
25 #include <iostream>
26 #include <fstream>
27
28 #include "main.h"
29 #include "fl_digi.h"
30 #include "gettext.h"
31 #include "rtty.h"
32 #include "serial.h"
33 #include "Nav.h"
34
35 bool use_Nav = false;
36 static int tty_mode = LETTERS;
37
38 static Cserial Nav_serial;
39 static Cserial Nav_config;
40
41 //#define NAVDEBUG 1
42
print_string(std::string s)43 static void print_string(std::string s)
44 {
45 #ifdef NAVDEBUG
46 std::string fname = HomeDir;
47 fname.append("nav_debug.txt");
48 std::ofstream dbgfile(fname.c_str(), std::ios::app);
49 dbgfile << s;
50 dbgfile.close();
51 #else
52 LOG_INFO("%s", s.c_str());
53 #endif
54 ReceiveText->add(s.c_str());
55 }
56
57 static std::string Nav_config_string = "\
58 11101111012";
59 static std::string config_params = "\
60 ||||||||||+-10 FSK stop bits - 1:1, 2:1.5, 3: 2 bits\n\
61 |||||||||+---9 FSK BAUD rate - 1: 45.45, 2: 75, 3: 100 baud\n\
62 ||||||||+----8 FSK PTT - 0: PTT disabled, 1: PTT enabled\n\
63 |||||||+-----7 FSK side tone - 0: no tone, 1: tone\n\
64 ||||||+------6 FSK polarity - 0: reverse, 1: normal polarity\n\
65 |||||+-------5 CAT LED behavior - 0: steady, 1: on-access poll\n\
66 ||||+--------4 LED brightness - 0: DIM, 1: normal brightness\n\
67 |||+---------3 WinKey PTT - 0: no PTT, 1: WinKey PTT\n\
68 ||+----------2 RF attenuator - 0: 20db atten', 1: no atten'\n\
69 |+-----------1 CH.2 attenuator - 0: 15db atten', 1: no atten'\n\
70 +------------0 CH.1 attenuator - 0: 15db atten', 1: no atten'\n";
71
Nav_update_config_controls()72 void Nav_update_config_controls()
73 {
74 switch (Nav_config_string[0]) { // channel 1 attenuator
75 default:
76 case '1': sel_Nav_ch1->index(1); break;
77 case '0': sel_Nav_ch1->index(0); break;
78 }
79 switch (Nav_config_string[1]) { // channel 2 attenuator
80 default:
81 case '1': sel_Nav_ch2->index(1); break;
82 case '0': sel_Nav_ch2->index(0); break;
83 }
84 switch (Nav_config_string[2]) { // rf attenuator
85 default:
86 case '1': sel_Nav_rf_att->index(1); break;
87 case '0': sel_Nav_rf_att->index(0); break;
88 }
89 switch (Nav_config_string[3]) { // Winkey PTT
90 default:
91 case '1': sel_Nav_wk_ptt->index(0); break;
92 case '0': sel_Nav_wk_ptt->index(1); break;
93 }
94 switch (Nav_config_string[4]) { // LED
95 default:
96 case '1': sel_Nav_LED->index(1); break;
97 case '0': sel_Nav_LED->index(0); break;
98 }
99 switch (Nav_config_string[5]) { // CAT LED
100 default:
101 case '1': sel_Nav_CAT_LED->index(1); break;
102 case '0': sel_Nav_CAT_LED->index(0); break;
103 }
104 switch (Nav_config_string[6]) { // polarity
105 default:
106 case '0': sel_Nav_FSK_polarity->index(1); break;
107 case '1': sel_Nav_FSK_polarity->index(0); break;
108 }
109 switch (Nav_config_string[7]) { // sidetone
110 default:
111 case '1': sel_Nav_FSK_sidetone->index(0); break;
112 case '0': sel_Nav_FSK_sidetone->index(1); break;
113 }
114 switch (Nav_config_string[8]) { // PTT
115 default:
116 case '1': sel_Nav_FSK_ptt->index(0); break;
117 case '0': sel_Nav_FSK_ptt->index(1); break;
118 }
119 switch (Nav_config_string[9]) { // baud
120 default:
121 case '1' : sel_Nav_FSK_baud->index(0); break;
122 case '2' : sel_Nav_FSK_baud->index(1); break;
123 case '3' : sel_Nav_FSK_baud->index(2); break;
124 }
125 switch (Nav_config_string[10]) { // stop bits
126 default:
127 case '1': sel_Nav_FSK_stopbits->index(0); break;
128 case '2': sel_Nav_FSK_stopbits->index(1); break;
129 case '3': sel_Nav_FSK_stopbits->index(2); break;
130 }
131
132 print_string(std::string(Nav_config_string).append("\n").append(config_params));
133
134 }
135
Nav_set_configuration()136 void Nav_set_configuration()
137 {
138 std::string cmd = "KL";
139 cmd.append(Nav_config_string).append("\r\n");
140 Nav_config.WriteBuffer((unsigned char *)cmd.c_str(), cmd.length());
141
142 std::string resp = Nav_read_string(Nav_config, 2000, "\n");
143 if (resp.find("kl") != std::string::npos) {
144 print_string(_("Config change accepted\n"));
145 Nav_write_eeprom();
146 } else
147 print_string(_("Config change NOT accepted\n"));
148 }
149
Nav_get_configuration()150 void Nav_get_configuration()
151 {
152 std::string cmd = "KM\r\n";
153 Nav_config.WriteBuffer((unsigned char *)cmd.c_str(), cmd.length());
154
155 std::string resp = Nav_read_string(Nav_config, 2000, "\n");
156 size_t p = resp.find("km");
157 if (p >= 11) {
158 p -= 11;
159 Nav_config_string = resp.substr(0, 11);
160 Nav_update_config_controls();
161 } else {
162 std::string err;
163 err.assign(_("Nav_get_configuration: ")).append(resp).append("\n");
164 print_string(err);
165 }
166 }
167
Nav_write_eeprom()168 void Nav_write_eeprom()
169 {
170 std::string cmd = "KS\r\n";
171 Nav_config.WriteBuffer((unsigned char *)cmd.c_str(), cmd.length());
172
173 std::string resp = Nav_read_string(Nav_config, 2000, "\n");
174 if (resp.find("ks") == std::string::npos) {
175 print_string(_("\nNavigator write to EEPROM failed!\n"));
176 } else
177 print_string(_("\nEEPROM write reported success\n"));
178 }
179
Nav_restore_eeprom()180 void Nav_restore_eeprom()
181 {
182 std::string cmd = "KR\r\n";
183 Nav_config.WriteBuffer((unsigned char *)cmd.c_str(), cmd.length());
184
185 std::string resp = Nav_read_string(Nav_config, 2000, "\n");
186 if (resp.find("kr") == std::string::npos) {
187 print_string(_("\nEEPROM data failed!\n"));
188 } else {
189 print_string(_("\nEEPROM data restored\n"));
190 Nav_get_configuration();
191 }
192 }
193
Nav_set_channel_1_att(int v)194 void Nav_set_channel_1_att(int v)
195 {
196 Nav_config_string[0] = (v ? '1' : '0');
197 Nav_set_configuration();
198 }
199
Nav_set_channel_2_att(int v)200 void Nav_set_channel_2_att(int v)
201 {
202 Nav_config_string[1] = (v ? '1' : '0');
203 Nav_set_configuration();
204 }
205
Nav_set_rf_att(int v)206 void Nav_set_rf_att(int v)
207 {
208 Nav_config_string[2] = (v ? '1' : '0');
209 Nav_set_configuration();
210 }
211
Nav_set_wk_ptt(int v)212 extern void Nav_set_wk_ptt(int v)
213 {
214 Nav_config_string[3] = (v ? '1' : '0');
215 Nav_set_configuration();
216 }
217
218 // "1" normal, "0" dim
Nav_set_led(int v)219 void Nav_set_led(int v)
220 {
221 Nav_config_string[4] = (v ? '1' : '0');
222 Nav_set_configuration();
223 }
224
225 /*
226 void led_dim(void *)
227 {
228 Nav_set_led(0);
229 }
230
231 void led_normal(void *)
232 {
233 Nav_set_led(1);
234 }
235
236 void blink_led()
237 {
238 char led = Nav_config_string[4];
239 if (led == '1') {
240 led_dim(NULL);
241 Fl::add_timeout(1.0, led_normal);
242 } else {
243 led_normal(NULL);
244 Fl::add_timeout(1.0, led_dim);
245 }
246 }
247 */
248
Nav_set_cat_led(int v)249 void Nav_set_cat_led(int v)
250 {
251 Nav_config_string[5] = (v ? '1' : '0');
252 Nav_set_configuration();
253 }
254
Nav_set_polarity(int v)255 void Nav_set_polarity(int v)
256 {
257 Nav_config_string[6] = (v ? '0' : '1');
258 Nav_set_configuration();
259 }
260
Nav_set_sidetone(int v)261 void Nav_set_sidetone(int v)
262 {
263 Nav_config_string[7] = (v ? '0' : '1');
264 Nav_set_configuration();
265 }
266
Nav_set_ptt(int v)267 void Nav_set_ptt(int v)
268 {
269 Nav_config_string[8] = (v ? '0' : '1');
270 Nav_set_configuration();
271 }
272
Nav_set_baud(int v)273 void Nav_set_baud(int v)
274 {
275 switch (v) {
276 case 0: Nav_config_string[9] = '1'; break; // 45.45 baud
277 case 1: Nav_config_string[9] = '2'; break; // 75.0 baud
278 case 2: Nav_config_string[9] = '3'; break; // 100.0 baud
279 }
280 Nav_set_configuration();
281 }
282
Nav_set_stopbits(int v)283 void Nav_set_stopbits(int v)
284 {
285 switch (v) {
286 case 0: Nav_config_string[10] = '1'; break;
287 case 1: Nav_config_string[10] = '2'; break;
288 case 2: Nav_config_string[10] = '3'; break;
289 }
290 Nav_set_configuration();
291 }
292
Nav_PTT(int val)293 void Nav_PTT(int val)
294 {
295 }
296
Nav_read_byte(Cserial & serial,int msec)297 char Nav_read_byte(Cserial &serial, int msec)
298 {
299 std::string resp;
300 resp.clear();
301
302 resp = Nav_serial_read(serial);
303 int numtries = msec/100;
304 while (resp.empty() && numtries) {
305 MilliSleep(100);
306 resp = Nav_serial_read(serial);
307 numtries--;
308 }
309 if (resp.empty())
310 return 0;
311 return resp[0];
312 }
313
Nav_read_string(Cserial & serial,int msec_wait,std::string find)314 std::string Nav_read_string(Cserial &serial, int msec_wait, std::string find)
315 {
316 std::string resp;
317
318 resp = Nav_serial_read(serial);
319
320 int timer = msec_wait;
321 if (timer < 100) msec_wait = timer = 100;
322
323 if (!find.empty()) {
324 while ((timer > 0) && (resp.find(find) == std::string::npos)) {
325 MilliSleep(100);
326 Fl::awake(); Fl::flush();
327 resp.append(Nav_serial_read(serial));
328 timer -= 100;
329 }
330 } else {
331 while (timer) {
332 MilliSleep(100);
333 Fl::awake(); Fl::flush();
334 resp.append(Nav_serial_read(serial));
335 timer -= 100;
336 }
337 }
338 if (resp.find("KR") != std::string::npos) {
339 print_string(_("\nNavigator returned error code!\n"));
340 }
341 return resp;
342 }
343
xmt_delay(int n)344 void xmt_delay(int n)
345 {
346 double baudrate = 45.45;
347 if (progdefaults.Nav_FSK_baud == 1) baudrate = 75.0;
348 if (progdefaults.Nav_FSK_baud == 2) baudrate = 100.0;
349 MilliSleep(n * 7500.0 / baudrate); // start + 5 data + 1.5 stop bits
350 }
351
send_char(int c)352 static void send_char(int c)
353 {
354 if (c == LETTERS) {
355 tty_mode = LETTERS;
356 c = 0x1F;
357 } else if (c == FIGURES) {
358 tty_mode = FIGURES;
359 c = 0x1B;
360 } else
361 c &= 0x1F;
362
363 unsigned char cmd[2] = " ";
364 cmd[0] = c;
365 Nav_serial.WriteBuffer(cmd, 1);
366
367 xmt_delay(1);
368 }
369
370 static std::string letters = "E\nA SIU\rDRJNFCKTZLWHYPQOBG MXV ";
371 static std::string figures = "3\n- \a87\r$4\',!:(5\")2#6019?& ./; ";
372
baudot_enc(char data)373 int baudot_enc(char data)
374 {
375 size_t c = std::string::npos;
376
377 data = toupper(data);
378
379 c = letters.find(data);
380 if (c != std::string::npos)
381 return (c + 1) | LETTERS;
382
383 c = figures.find(data);
384 if (c != std::string::npos)
385 return (c + 1) | FIGURES;
386
387 return -1;
388 }
389
Nav_send_char(int c)390 void Nav_send_char(int c)
391 {
392 if (c == GET_TX_CHAR_NODATA) {
393 send_char(LETTERS);
394 return;
395 }
396
397 if (c == ' ' && tty_mode == FIGURES)
398 send_char(LETTERS);
399
400 c = baudot_enc(c & 0xFF);
401
402 if ((tty_mode == LETTERS) && ((c & FIGURES) == FIGURES))
403 send_char(FIGURES);
404 if ((tty_mode == FIGURES) && ((c & LETTERS) == LETTERS))
405 send_char(LETTERS);
406
407 send_char(c);
408
409 }
410
Nav_serial_read(Cserial & serial)411 std::string Nav_serial_read(Cserial &serial) {
412 static char buffer[1025];
413
414 memset(buffer, '\0',1025);
415
416 int rb = serial.ReadBuffer((unsigned char *)buffer, 1024);
417 if (rb)
418 return buffer;
419 return "";
420 }
421
422 // One time use of separate thread to read initial values
423
close_NavFSK()424 void close_NavFSK()
425 {
426 Nav_serial.ClosePort();
427
428 use_Nav = false;
429
430 print_string(_("Disconnected from Navigator FSK port\n"));
431
432 progStatus.Nav_online = false;
433 enable_rtty_quickchange();
434 }
435 /* =====================================================================
436
437 Received from Clint, designer of Navigator
438
439 The B channel of the 2nd FT2232C device is configured as an 8 bit FIFO,
440 only the lower 5 bits are used.
441
442 Actual FSK parameters are sent to the Navigator Configuration Port,
443 consisting of output
444
445 Polarity (Normal or Reverse),
446 Sidetone (On or Off),
447 FSK PTT (must be set to ON)
448 Baud Rate (45.45, 75, 100)
449 Stop Bits (1, 1.5, 2)
450
451 From these settings (Baud rate, Stop bits, Data length (5) and Start bit (1) )
452 the character rate can be computed to determine when to send the next
453 character to the FIFO (below).
454
455 Data Channel - Baudot encoded FSK 5 bit characters are sent to the FSK
456 FIFO device. The FIFO is 374 characters deep. The first two characters
457 sent to the FIFO should be sent with no delay between the two characters.
458 Subsequent characters should be sent at a rate determined by the parameters
459 in the previous paragraph. The rate should be about 0.5% faster than the
460 calculated rate to ensure that the buffer is not under run. When the
461 last character is sent to the FIFO, the PTT signal should be dropped.
462
463 The FSK Controller will maintain the PTT signal to the radio until the
464 last bit of the last character has been transmitted. Output PTT will be
465 dropped at that time. When the PTT drops to the radio, it will return
466 to RX state.
467
468 Baudot LTRS and FIGS bytes and USOS LTRS bytes are not handled by the Navigator
469
470 The Navigator does not provide any state feedback to the controlling PC,
471 i.e. state of the FIFO or the PTT signal lines, timing must be approximated
472 in the controlling PC. Note: The FTDI part must provide a Busy Status to
473 indicate when the FIFO is full.
474
475 In the Windows implementation, there were two configuration items that were used:
476
477 Open Port: SetCommState() function- the DCB was able to be configured as a
478 45 baud, 1.5 stop bit, no parity, 5 bit bytesize device.
479
480 WriteFile() function: Windows buffer size set to 1 - this allowed the flow
481 control to be done without having to time the data rate to avoid buffer under run.
482
483 ======================================================================*/
open_NavFSK()484 bool open_NavFSK()
485 {
486 progStatus.Nav_online = false;
487
488 Nav_serial.Device(progdefaults.Nav_FSK_port);
489 Nav_serial.Baud(1200);
490 Nav_serial.Timeout(200);
491 Nav_serial.Retries(10);
492 Nav_serial.Stopbits(1);
493
494 if (!Nav_serial.OpenPort()) {
495 std::string err = std::string(_("Could not open ")).append(progdefaults.Nav_FSK_port).append("\n");
496 print_string(err);
497 return false;
498 }
499
500 use_Nav = true;
501
502 progStatus.Nav_online = true;
503 disable_rtty_quickchange();
504
505 print_string(_("Connected to Navigator FSK port\n"));
506
507 return true;
508 }
509
close_NavConfig()510 void close_NavConfig()
511 {
512 Nav_config.ClosePort();
513
514 print_string(_("Disconnected from Navigator config port\n"));
515
516 progStatus.Nav_config_online = false;
517 }
518
open_NavConfig()519 bool open_NavConfig()
520 {
521 #ifdef NAVDEBUG
522 std::string info = "Navigator debug file: ";
523 info.append(zdate()).append(" ").append(ztime()).append("\n");
524 print_string("========================================================\n");
525 print_string(info);
526 #endif
527 Nav_config.Device(progdefaults.Nav_config_port);
528 Nav_config.Baud(1200);
529 Nav_config.Timeout(200);
530 Nav_config.Retries(10);
531 Nav_config.Stopbits(1);
532
533 if (!Nav_config.OpenPort()) {
534 std::string err;
535 err.assign(_("Could not open ")).append(progdefaults.Nav_config_port);
536 print_string(err);
537 return false;
538 }
539
540 std::string cmd = "KT\r\n";
541 std::string resp = "";
542
543 Nav_config.WriteBuffer((unsigned char *)(cmd.c_str()), cmd.length());
544 resp = Nav_read_string(Nav_config, 2000, "\n");
545 if (resp.find("kt") == std::string::npos) {
546 Nav_config.WriteBuffer((unsigned char *)(cmd.c_str()), cmd.length());
547 resp = Nav_read_string(Nav_config, 2000, "\n");
548 if (resp.find("kt") == std::string::npos) {
549 print_string(_("Navigator failed to respond to NOOP.\n"));
550 close_NavConfig();
551 return false;
552 }
553 }
554
555 cmd = "KQ\r\n";
556 Nav_config.WriteBuffer((unsigned char *)(cmd.c_str()), cmd.length());
557 resp = Nav_read_string(Nav_config, 2000, "\n");
558
559 if (resp.find("\n") == std::string::npos) {
560 print_string(_("Navigator did not send Version string.\n"));
561 close_NavConfig();
562 return false;
563 }
564
565 print_string(std::string("Navigator ").append(resp));
566
567 Nav_get_configuration();
568
569 // blink_led();
570
571 print_string(_("Connected to Navigator configuration port\n"));
572
573 progStatus.Nav_config_online = true;
574
575 return true;
576 }
577