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