1 // ----------------------------------------------------------------------------
2 // winkeyer.cxx  --  Interface to k1el WinKeyer hardware
3 //
4 // Copyright (C) 2017
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 
26 #include <math.h>
27 #include <string>
28 #include <cstring>
29 #include <stdio.h>
30 #include <iostream>
31 #include <fstream>
32 #include <sstream>
33 #include <cstdlib>
34 
35 #include <FL/Fl.H>
36 
37 #include "debug.h"
38 
39 #include "fl_digi.h"
40 #include "confdialog.h"
41 
42 #include "winkeyer.h"
43 #include "status.h"
44 #include "serial.h"
45 #include "threads.h"
46 #include "modem.h"
47 #include "morse.h"
48 
49 #include "icons.h"
50 
51 #define LOG_WKEY  LOG_INFO
52 
53 using namespace std;
54 
55 //----------------------------------------------------------------------
56 // WinKeyer general support
57 //----------------------------------------------------------------------
58 Cserial WK_serial;
59 
60 pthread_t WK_serial_thread;
61 pthread_mutex_t WK_mutex_serial = PTHREAD_MUTEX_INITIALIZER;
62 pthread_mutex_t WK_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
63 
64 //======================================================================
65 // WinKey command sequences
66 //======================================================================
67 
68 // ADMIN MODE
69 static const char ADMIN				= '\x00';
70 //static const char CALIBRATE			= '\x00';
71 static const char RESET				= '\x01';
72 static const char HOST_OPEN			= '\x02';
73 static const char HOST_CLOSE		= '\x03';
74 static const char ECHO_TEST			= '\x04';
75 //static const char PADDLE_A2D		= '\x05';
76 //static const char SPEED_A2D			= '\x06';
77 //static const char GET_VALUES		= '\x07';
78 //static const char GET_CAL			= '\x09';
79 //static const char WK1_MODE			= '\x0A';
80 static const char WK2_MODE			= '\x0B';
81 //static const char DUMP_EEPROM		= '\x0C';
82 //static const char LOAD_EEPROM		= '\x0D';
83 static const char FSK_MODE			= '\x13';
84 
85 //static const char *SEND_MSG_NBR		= "\x0E";
86 
87 // HOST MODE
88 //static const char *SIDETONE			= "\x01";	// N see table page 6 Interface Manual
89 static const char *SET_WPM			= "\x02";	// 5 .. N .. 99 in WPM
90 //static const char *SET_WEIGHT		= "\x03";	// 10 .. N .. 90 %
91 //static const char *SET_PTT_LT		= "\x04";	// A - lead time, B - tail time
92 												// both 0..250 in 10 msec steps
93 												// "0x04<01><A0> = 10 msec lead, 1.6 sec tail
94 static const char *SET_SPEED_POT	= "\x05";	// A = min, B = range, C anything
95 //static const char *PAUSE			= "\x06";	// 1 = pause, 0 = release
96 static const char *GET_SPEED_POT 	= "\x07"; 	// return values as per page 7/8
97 //static const char *BACKSPACE		= "\x08";
98 //static const char *SET_PIN_CONFIG	= "\x09";	// N as per tables page 8
99 static const char *CLEAR_BUFFER		= "\x0A";
100 static const char *KEY_IMMEDIATE	= "\x0B";	// 0 = keyup, 1 = keydown
101 //static const char *HSCW				= "\x0C";	// N = lpm / 100
102 //static const char *FARNS_WPM		= "\x0D";	// 10 .. N .. 99
103 //static const char *SET_WK2_MODE		= "\x0E";	// N as per table page 9
104 static const char *LOAD_DEFAULTS	= "\x0F";
105 	// A = MODE REGISTER	B = SPEED IN WPM		C = SIDETONE FREQ
106 	// D = WEIGHT			E = LEAD-IN TIME		F = TAIL TIME
107 	// G = MIN_WPM			H = WPM RANGE			I = 1ST EXTENSION
108 	// J = KEY COMPENSATION	K = FARNSWORTH WPM		L = PADDLE SETPOINT
109 	// M = DIT/DAH RATIO	N = PIN_CONFIGURATION	O = DONT CARE ==> zero
110 //static const char *FIRST_EXT		= "\x10";		// see page 10/11
111 //static const char *SET_KEY_COMP		= "\x11";	// see page 11
112 static const char *NULL_CMD			= "\x13\x13\x13";
113 //static const char *PADDLE_SW_PNT	= "\x12";	// 10 .. N .. 90%
114 //static const char *SOFT_PADDLE		= "\x14";	// 0 - up, 1 - dit, 2 - dah, 3 - both
115 //static const char *GET_STATUS		= "\x15";	// request status byte, see page 12
116 //static const char *POINTER_CMD		= "\x16";	// see page 12
117 //static const char *SET_DIT_DAH		= "\x17";	// 33 .. N .. 66 N = ratio * 50 / 3
118 
119 // BUFFERED COMMANDS
120 //static const char *PTT_ON_OFF		= "\x18";	// 1 = on 0 = off
121 //static const char *KEY_BUFFERED		= "\x19";	// 0 .. N .. 99 seconds
122 //static const char *WAIT				= "\x1A";	// 0 .. N .. 99 seconds
123 //static const char *MERGE			= "\x1B";	// merger CD into prosign, ie AR, SK etc
124 //static const char *CHANGE_BFR_SPD	= "\x1C";	// 5 .. N .. 99
125 //static const char *CHANGE_HSCW_SPD	= "\x1D";	// N = lpm / 100
126 //static const char *CANCEL_BFR_SPD	= "\x1E";
127 //static const char *BUFFER_NOP		= "\x1F";
128 
129 //======================================================================
130 // loop for serial i/o thread
131 // runs continuously until program is closed
132 // only accesses the serial port if it has been successfully opened
133 //======================================================================
134 
135 void WK_version_(int);
136 void WK_echo_test(int);
137 void WK_status_(int);
138 void WK_speed_(int);
139 void WK_echo_(int);
140 void WK_eeprom_(int);
141 bool WK_readByte(unsigned char &byte);
142 int WK_readString();
143 int WK_sendString (string &s);
144 void WK_clearSerialPort();
145 void WK_display_byte(int);
146 
147 bool WK_bypass_serial_thread_loop = true;
148 bool WK_run_serial_thread = true;
149 
150 bool WK_PTT = false;
151 int  WK_powerlevel = 0;
152 
153 static string WK_str_out = "";
154 static bool WK_get_version = false;
155 static bool WK_test_echo = false;
156 static bool WK_host_is_up = false;
157 static bool wk2_version = false;
158 
159 static bool read_EEPROM = false;
160 static char eeprom_image[256];
161 static int  eeprom_ptr = 0;
162 
163 static int  wkeyer_ready = true;
164 
165 static cMorse *wkmorse = 0;
166 
hexstr(std::string & s)167 static std::string hexstr(std::string &s)
168 {
169 	static std::string hex;
170 	static char val[3];
171 	hex.clear();
172 	for (size_t i = 0; i < s.length(); i++) {
173 		snprintf(val, sizeof(val), "%02x", s[i] & 0xFF);
174 		hex.append(" 0x").append(val);
175 	}
176 	return hex;
177 }
178 
upcase(string & s)179 static void upcase(string &s)
180 {
181 	for (size_t n = 0; n < s.length(); n++)
182 		if (s[n] > ' ') s[n] = toupper(s[n]);
183 }
184 
185 enum {NOTHING, WAIT_ECHO, WAIT_VERSION};
186 
WK_send_command(string & cmd,int what=NOTHING)187 static void WK_send_command(string &cmd, int what = NOTHING)
188 {
189 	int cnt = 101;
190 	while (cnt-- && !WK_str_out.empty()) MilliSleep(1);
191 	if (!WK_str_out.empty())
192 		return;
193 
194 	guard_lock wklock(&WK_mutex_serial);
195 
196 	upcase(cmd);
197 	cnt = 101;
198 	WK_str_out = cmd;
199 
200 	while (cnt-- && !WK_str_out.empty()) MilliSleep(1);
201 
202 	switch (what) {
203 		case WAIT_ECHO :
204 			WK_test_echo = true;
205 			break;
206 		case WAIT_VERSION :
207 			WK_get_version = true;
208 			break;
209 		default: ;
210 	}
211 }
212 
213 static bool WK_wait_for_char = false;
214 static char lastchar = ' ';
215 
WK_send_char(int c)216 int WK_send_char(int c)
217 {
218 	if (!wkmorse) wkmorse = new cMorse;
219 
220 	c = toupper(c);
221 	if (c <= 0) {
222 		MilliSleep(10);
223 		Fl::awake();
224 		return 0;
225 	}
226 
227 	struct timeval t;
228 	long msec_start;
229 	long msec_end;
230 
231 	if (c < ' ') c = ' ';
232 
233 	if (c == '0' && progStatus.WK_cut_zeronine) c = 'T';
234 	if (c == '9' && progStatus.WK_cut_zeronine) c = 'N';
235 	int n = 0;
236 
237 	if (lastchar == ' ' && c == ' ') n = 7;
238 	else if (lastchar != ' ' && c == ' ') n = 4;
239 	else {
240 		if (c != ' ') {
241 			std::string code = wkmorse->tx_lookup(c);
242 			for (size_t i = 0; i < code.length(); i++) {
243 				n += 2;
244 				if (code[i] == '-') n += 2;
245 			}
246 			n += 2;
247 		}
248 	}
249 	n *= 1200 / cntCW_WPM->value();
250 
251 	lastchar = c;
252 
253 	if (c != ' ') {
254 		guard_lock wklock(&WK_buffer_mutex);
255 		WK_str_out += c;
256 		if (btn_WK_serial_echo->value())
257 			WK_wait_for_char = true;
258 	}
259 
260 	gettimeofday(&t, NULL);
261 	msec_start = (t.tv_sec - (t.tv_sec / 10000) * 10000) * 1000;
262 	msec_start += t.tv_usec / 1000;
263 	msec_start += n;
264 
265 	if (WK_wait_for_char) {
266 		n += 100;
267 		while (WK_wait_for_char) {
268 			MilliSleep(1);
269 			if (n % 50 == 0) Fl::awake();
270 			if (--n == 0) {
271 				WK_wait_for_char = false;
272 				LOG_ERROR("Winkeyer did not echo character!");
273 				return 1;
274 			}
275 			if (active_modem->get_stopflag()) {
276 				WK_wait_for_char = false;
277 				LOG_INFO("Aborted transmission");
278 				return 1;
279 			}
280 		}
281 	} else {
282 		while (!active_modem->get_stopflag()) {
283 			MilliSleep(1);
284 			gettimeofday(&t, NULL);
285 			msec_end = (t.tv_sec - (t.tv_sec / 10000) * 10000) * 1000;
286 			msec_end += t.tv_usec / 1000;
287 			if (msec_end >= msec_start) {
288 				break;
289 			}
290 			n--;
291 			if (n == 0) {
292 				break;
293 			}
294 			if (n % 50 == 0) Fl::awake();
295 		}
296 		REQ(WK_display_byte, c);
297 	}
298 
299 	return 0;
300 }
301 
WK_serial_thread_loop(void * d)302 void * WK_serial_thread_loop(void *d)
303 {
304 	SET_THREAD_ID(WKEY_TID);
305 
306 unsigned char byte;
307 	for(;;) {
308 		if (!WK_run_serial_thread) break;
309 
310 		MilliSleep(1);//5);
311 
312 		if (WK_bypass_serial_thread_loop ||
313 			!WK_serial.IsOpen()) goto WK_serial_bypass_loop;
314 
315 // process outgoing
316 		{
317 			guard_lock wklock(&WK_buffer_mutex);
318 			if (!WK_str_out.empty()) {
319 				WK_sendString(WK_str_out);
320 				WK_str_out.clear();
321 			}
322 		}
323 
324 // receive WinKeyer response
325 		{
326 			guard_lock wklock(&WK_mutex_serial);
327 			if (WK_serial.ReadBuffer(&byte, 1) == 1) {
328 
329 				if ((byte == 0xA5 || read_EEPROM))
330 					WK_eeprom_(byte);
331 				else if ((byte & 0xC0) == 0xC0)
332 					WK_status_(byte);
333 				else if ((byte & 0xC0) == 0x80)
334 					WK_speed_(byte);
335 				else if (WK_test_echo)
336 					WK_echo_test(byte);
337 				else if (WK_get_version)
338 					WK_version_(byte);
339 				else if (WK_wait_for_char)
340 					WK_echo_(byte);
341 			}
342 		}
343 
344 WK_serial_bypass_loop: ;
345 	}
346 	return NULL;
347 }
348 
WK_display_byte(int ch)349 void WK_display_byte(int ch)
350 {
351 	ReceiveText->add(ch, FTextBase::XMIT);
352 }
353 
WK_display_chars(std::string s)354 void WK_display_chars(std::string s)
355 {
356 	ReceiveText->add(s.c_str());
357 }
358 
WK_echo_(int byte)359 void WK_echo_(int byte)
360 {
361 	if (WK_wait_for_char) {
362 		if (byte == ' ' && lastchar == '\n')
363 			byte = lastchar;
364 	}
365 	REQ(WK_display_byte, byte);
366 	lastchar = byte;
367 	WK_wait_for_char = false;
368 }
369 
WK_echo_test(int byte)370 void WK_echo_test(int byte)
371 {
372 	if (byte != 'U') return;
373 	LOG_WKEY("passed echo test");
374 	WK_test_echo = false;
375 }
376 
WK_version_(int byte)377 void WK_version_(int byte)
378 {
379 	static char ver[200];
380 	snprintf(ver, sizeof(ver), "\nConnected to Winkeyer h/w version %d\n",
381 		byte & 0xFF);
382 	WK_host_is_up = true;
383 	WK_get_version = false;
384 	REQ(WK_display_chars, ver);
385 	progStatus.WK_version = byte;
386 }
387 
WK_show_status_change(int byte)388 void WK_show_status_change(int byte)
389 {
390 	box_WK_wait->color((byte & 0x10) == 0x10 ? FL_DARK_GREEN : FL_BACKGROUND2_COLOR);
391 	box_WK_wait->redraw();
392 
393 	box_WK_keydown->color((byte & 0x08) == 0x08 ? FL_DARK_GREEN : FL_BACKGROUND2_COLOR);
394 	box_WK_keydown->redraw();
395 
396 	box_WK_busy->color((byte & 0x04) == 0x04 ? FL_DARK_GREEN : FL_BACKGROUND2_COLOR);
397 	box_WK_busy->redraw();
398 
399 	box_WK_break_in->color((byte & 0x02) == 0x02 ? FL_DARK_GREEN : FL_BACKGROUND2_COLOR);
400 	box_WK_break_in->redraw();
401 
402 	box_WK_xoff->color((byte & 0x01) == 0x01 ? FL_DARK_GREEN : FL_BACKGROUND2_COLOR);
403 	box_WK_xoff->redraw();
404 }
405 
WK_status_(int byte)406 void WK_status_(int byte)
407 {
408 	if ((byte & 0x04)== 0x04) wkeyer_ready = false;
409 	else wkeyer_ready = true;
410 	LOG_DEBUG("Wait %c, Keydown %c, Busy %c, Breakin %c, Xoff %c",
411 		byte & 0x10 ? 'T' : 'F',
412 		byte & 0x08 ? 'T' : 'F',
413 		byte & 0x04 ? 'T' : 'F',
414 		byte & 0x02 ? 'T' : 'F',
415 		byte & 0x01 ? 'T' : 'F');
416 	REQ(WK_show_status_change, byte);
417 }
418 
WK_show_speed_change(int wpm)419 void WK_show_speed_change(int wpm)
420 {
421 	if (!progStatus.WK_use_pot) {
422 		return;
423 	}
424 	static char szwpm[8];
425 	snprintf(szwpm, sizeof(szwpm), "%3d", wpm);
426 
427 	txt_WK_wpm->value(szwpm);
428 	txt_WK_wpm->redraw();
429 
430 	cntCW_WPM->value(wpm);
431 	cntCW_WPM->redraw();
432 
433 	progdefaults.CWspeed = wpm;
434 
435 	sync_cw_parameters();
436 
437 	string cmd = SET_WPM;
438 	cmd += progdefaults.CWspeed;
439 
440 LOG_WKEY("SET_WPM %.1f : %s", progdefaults.CWspeed, hexstr(cmd).c_str());
441 
442 	WK_send_command(cmd);
443 }
444 
WK_speed_(int byte)445 void WK_speed_(int byte)
446 {
447 	int val = (byte & 0x3F) + progStatus.WK_min_wpm;
448 	REQ(WK_show_speed_change, val);
449 }
450 
WK_set_wpm()451 void WK_set_wpm()
452 {
453 	string cmd = SET_WPM;
454 	cmd += progdefaults.CWspeed;
455 
456 LOG_WKEY("SET_WPM %.1f : %s", progdefaults.CWspeed, hexstr(cmd).c_str());
457 
458 	WK_send_command(cmd);
459 }
460 
WK_use_pot_changed()461 void WK_use_pot_changed()
462 {
463 	progStatus.WK_use_pot = btn_WK_use_pot->value();
464 	if (progStatus.WK_use_pot) {
465 		string cmd = GET_SPEED_POT;
466 
467 LOG_WKEY("GET_SPEED_POT : %s", hexstr(cmd).c_str());
468 
469 	} else {
470 		string cmd = SET_WPM;
471 		if (cntCW_WPM->value() > 55) cntCW_WPM->value(55);
472 		if (cntCW_WPM->value() < 5) cntCW_WPM->value(5);
473 		progdefaults.CWspeed = cntCW_WPM->value();
474 		cmd += progdefaults.CWspeed;
475 
476 LOG_WKEY("SET_WPM %.1f : %s", progdefaults.CWspeed, hexstr(cmd).c_str());
477 
478 		WK_send_command(cmd);
479 	}
480 }
481 
WK_eeprom_(int byte)482 void WK_eeprom_(int byte)
483 {
484 	if (byte == 0xA5) {
485 		memset( eeprom_image, 0, 256);
486 		eeprom_ptr = 0;
487 		read_EEPROM = true;
488 	}
489 	if (eeprom_ptr < 256)
490 		eeprom_image[eeprom_ptr++] = byte;
491 	if (eeprom_ptr == 256) {
492 		read_EEPROM = false;
493 		LOG_WKEY("\n%s", str2hex(eeprom_image, 256));
494 		eeprom_ptr = 0;
495 	}
496 }
497 
load_defaults()498 void load_defaults()
499 {
500 	string cmd = LOAD_DEFAULTS;
501 
502 	cmd += progStatus.WK_mode_register;
503 	cmd += progdefaults.CWspeed;
504 	cmd += progStatus.WK_sidetone;
505 	cmd += progStatus.WK_weight;
506 	cmd += progStatus.WK_lead_in_time;
507 	cmd += progStatus.WK_tail_time;
508 	cmd += progStatus.WK_min_wpm;
509 	cmd += progStatus.WK_rng_wpm;
510 	cmd += progStatus.WK_first_extension;
511 	cmd += progStatus.WK_key_compensation;
512 	cmd += progStatus.WK_farnsworth_wpm;
513 	cmd += progStatus.WK_paddle_setpoint;
514 	cmd += progStatus.WK_dit_dah_ratio;
515 	cmd += progStatus.WK_pin_configuration;
516 	cmd += progStatus.WK_dont_care;
517 
518 LOG_WKEY("\n\
519       mode register .... %0x\n\
520       CW speed ......... %.1f\n\
521       side tone ........ %d\n\
522       weight ........... %d\n\
523       lead in time ..... %d\n\
524       tail time ........ %d\n\
525       min wpm .......... %d\n\
526       rng wpm .......... %d\n\
527       first ext ........ %d\n\
528       key comp ......... %d\n\
529       farnsworth wpm ... %d\n\
530       paddle setpoint .. %d\n\
531       dit dah ratio .... %d\n\
532       pin config ....... %d\n\
533       don't care ....... %d\n\
534       hex string ....... %s",
535 	progStatus.WK_mode_register,
536 	progdefaults.CWspeed,
537 	progStatus.WK_sidetone,
538 	progStatus.WK_weight,
539 	progStatus.WK_lead_in_time,
540 	progStatus.WK_tail_time,
541 	progStatus.WK_min_wpm,
542 	progStatus.WK_rng_wpm,
543 	progStatus.WK_first_extension,
544 	progStatus.WK_key_compensation,
545 	progStatus.WK_farnsworth_wpm,
546 	progStatus.WK_paddle_setpoint,
547 	progStatus.WK_dit_dah_ratio,
548 	progStatus.WK_pin_configuration,
549 	progStatus.WK_dont_care,
550 	hexstr(cmd).c_str());
551 
552 	WK_send_command(cmd);
553 
554 	cmd = SET_SPEED_POT;
555 	cmd += progStatus.WK_min_wpm;
556 	cmd += progStatus.WK_rng_wpm;
557 	cmd += 0xFF;
558 LOG_WKEY("SET_SPEED_POT : %s", hexstr(cmd).c_str());
559 	WK_send_command(cmd);
560 
561 	if (progStatus.WK_use_pot) {
562 		string cmd = GET_SPEED_POT;
563 LOG_WKEY("GET_SPEED_POT : %s", hexstr(cmd).c_str());
564 		WK_send_command(cmd);
565 
566 	} else {
567 		string cmd = SET_WPM;
568 		cmd += progdefaults.CWspeed;
569 LOG_WKEY("SETWPM %.1f : %s", progdefaults.CWspeed, hexstr(cmd).c_str());
570 		WK_send_command(cmd);
571 
572 	}
573 }
574 
WKCW_init()575 void WKCW_init()
576 {
577 	string cmd;
578 
579 	if (wk2_version) {
580 		cmd = "  ";
581 		cmd[0] = ADMIN;
582 		cmd[1] = WK2_MODE;
583 LOG_WKEY("WK2_MODE %s", hexstr(cmd).c_str());
584 		WK_send_command(cmd);
585 	}
586 
587 	btn_WK_use_pot->value(progStatus.WK_use_pot);
588 
589 	load_defaults();
590 
591 	cmd = GET_SPEED_POT;
592 LOG_WKEY("GET_SPEED_POT : %s", hexstr(cmd).c_str());
593 	WK_send_command(cmd);
594 
595 	cmd = SET_WPM;
596 	cmd += progdefaults.CWspeed;
597 LOG_WKEY("SET_WPM %.1f : %s", progdefaults.CWspeed, hexstr(cmd).c_str());
598 	WK_send_command(cmd);
599 
600 	cmd = SET_SPEED_POT;
601 	cmd += progStatus.WK_min_wpm;
602 	cmd += progStatus.WK_rng_wpm;
603 	cmd += 0xFF;
604 LOG_WKEY("SET_SPEED_POT : %s", hexstr(cmd).c_str());
605 	WK_send_command(cmd);
606 
607 	cmd = GET_SPEED_POT;
608 LOG_WKEY("GET_SPEED_POT : %s", hexstr(cmd).c_str());
609 	WK_send_command(cmd);
610 }
611 
open_wkeyer()612 void open_wkeyer()
613 {
614 	int cnt = 0;
615 	string cmd = NULL_CMD;
616 
617 LOG_WKEY("NULL_CMD : %s", hexstr(cmd).c_str());
618 
619 	WK_send_command(cmd);
620 
621 	WK_clearSerialPort();
622 
623 // This code only works for a real WinKeyer
624 // fails for the K3NG Arduino sketch code as written to MORTTY
625 
626 	if (btn_WK_serial_echo->value() && !btnK3NG->value()) {
627 		cmd = "  ";
628 		cmd[0] = ADMIN;
629 		cmd[1] = ECHO_TEST;
630 		cmd += 'U';
631 
632 LOG_WKEY("ECHO_TEST : %s", hexstr(cmd).c_str());
633 
634 		WK_send_command(cmd, WAIT_ECHO);
635 
636 		cnt = 5000;
637 		while (WK_test_echo == true && cnt) {
638 			MilliSleep(1);
639 			cnt--;
640 		}
641 
642 		if (WK_test_echo) {
643 			debug::show();
644 			LOG_ERROR("%s", "Winkeyer not responding");
645 			WK_test_echo = false;
646 			pthread_mutex_lock(&WK_mutex_serial);
647 				WK_bypass_serial_thread_loop = true;
648 			pthread_mutex_unlock(&WK_mutex_serial);
649 			WK_serial.ClosePort();
650 			progStatus.WK_serial_port_name = "NONE";
651 			select_WK_CommPort->value(progStatus.WK_serial_port_name.c_str());
652 			return;
653 		}
654 
655 		LOG_WKEY("Echo response in %d msec", 5000 - cnt);
656 
657 	}
658 
659 	cmd = "  ";
660 	cmd[0] = ADMIN;
661 	cmd[1] = HOST_OPEN;
662 LOG_WKEY("HOST_OPEN : %s", hexstr(cmd).c_str());
663 
664 	WK_send_command(cmd, WAIT_VERSION);
665 
666 	cnt = 1000;
667 	while (WK_get_version == true && cnt) {
668 		MilliSleep(1);
669 		cnt--;
670 	}
671 
672 
673 }
674 
close_wkeyer()675 void close_wkeyer()
676 {
677 	string cmd = "  ";
678 
679 	cmd[0] = ADMIN;
680 	cmd[1] = RESET;
681 LOG_WKEY("WKEY RESET : %s", hexstr(cmd).c_str());
682 	WK_send_command(cmd);
683 
684 	cmd[0] = ADMIN;
685 	cmd[1] = HOST_CLOSE;
686 LOG_WKEY("HOST CLOSE : %s", hexstr(cmd).c_str());
687 	WK_send_command(cmd);
688 
689 }
690 
WK_cancel_transmit()691 void WK_cancel_transmit()
692 {
693 	string cmd = CLEAR_BUFFER;
694 
695 LOG_WKEY("CLEAR_BUFFER : %s", hexstr(cmd).c_str());
696 
697 	WK_send_command(cmd);
698 }
699 
WK_tune(bool on)700 void WK_tune(bool on)
701 {
702 	string cmd = KEY_IMMEDIATE;
703 	if (on) cmd += '\1';
704 	else cmd += '\0';
705 
706 LOG_WKEY("KEY_IMMEDIATE : %s", hexstr(cmd).c_str());
707 
708 	WK_send_command(cmd);
709 }
710 
711 //----------------------------------------------------------------------
712 // WinKeyer setup support
713 //----------------------------------------------------------------------
714 
WK_change_btn_swap()715 void WK_change_btn_swap()
716 {
717 	progStatus.WK_mode_register &=0xF7;
718 	progStatus.WK_mode_register |= btn_WK_swap->value() ? 0x08 : 0x00;
719 	LOG_WKEY("mode reg: %2X", progStatus.WK_mode_register);
720 	if (progStatus.WK_online)
721 		load_defaults();
722 }
723 
WK_change_btn_auto_space()724 void WK_change_btn_auto_space()
725 {
726 	progStatus.WK_mode_register &=0xFD;
727 	progStatus.WK_mode_register |= btn_WK_auto_space->value() ? 0x02 : 0x00;
728 	LOG_WKEY("mode reg: %2X", progStatus.WK_mode_register);
729 	if (progStatus.WK_online)
730 		load_defaults();
731 }
732 
733 
WK_change_btn_ct_space()734 void WK_change_btn_ct_space()
735 {
736 	progStatus.WK_mode_register &= 0xFE;
737 	progStatus.WK_mode_register |= btn_WK_ct_space->value();
738 	LOG_WKEY("mode reg: %2X", progStatus.WK_mode_register);
739 	if (progStatus.WK_online)
740 		load_defaults();
741 }
742 
743 
WK_change_btn_paddledog()744 void WK_change_btn_paddledog()
745 {
746 	progStatus.WK_mode_register &=0x7F;
747 	progStatus.WK_mode_register |= btn_WK_paddledog->value() ? 0x80 : 0x00;
748 	LOG_WKEY("mode reg: %2X", progStatus.WK_mode_register);
749 	if (progStatus.WK_online)
750 		load_defaults();
751 }
752 
753 
WK_change_btn_cut_zeronine()754 void WK_change_btn_cut_zeronine()
755 {
756 	progStatus.WK_cut_zeronine = btn_WK_cut_zeronine->value();
757 }
758 
759 
WK_change_btn_paddle_echo()760 void WK_change_btn_paddle_echo()
761 {
762 	progStatus.WK_mode_register &=0xBF;
763 	progStatus.WK_mode_register |= btn_WK_paddle_echo->value() ? 0x40 : 0x00;
764 	LOG_WKEY("mode reg: %2X", progStatus.WK_mode_register);
765 	if (progStatus.WK_online)
766 		load_defaults();
767 }
768 
769 
WK_change_btn_serial_echo()770 void WK_change_btn_serial_echo()
771 {
772 	progStatus.WK_mode_register &=0xFB;
773 	progStatus.WK_mode_register |= btn_WK_serial_echo->value() ? 0x04 : 0x00;
774 	LOG_WKEY("mode reg: %2X", progStatus.WK_mode_register);
775 	if (progStatus.WK_online)
776 		load_defaults();
777 }
778 
779 
WK_change_btn_sidetone_on()780 void WK_change_btn_sidetone_on()
781 {
782 	progStatus.WK_sidetone = choice_WK_sidetone->index() + 1;
783 	progStatus.WK_sidetone |= (btn_WK_sidetone_on->value() ? 0x80 : 0x00);
784 	if (progStatus.WK_online)
785 		load_defaults();
786 }
787 
WK_change_btn_tone_on()788 void WK_change_btn_tone_on()
789 {
790 	progStatus.WK_pin_configuration = (progStatus.WK_pin_configuration & 0xFD) | (btn_WK_tone_on->value() ? 2 : 0);
791 	if (progStatus.WK_online)
792 		load_defaults();
793 }
794 
795 
WK_change_btn_ptt_on()796 void WK_change_btn_ptt_on()
797 {
798 	progStatus.WK_pin_configuration = (progStatus.WK_pin_configuration & 0xFE) | (btn_WK_ptt_on->value() ? 1 : 0);
799 	if (progStatus.WK_online)
800 		load_defaults();
801 }
802 
WK_change_cntr_min_wpm()803 void WK_change_cntr_min_wpm()
804 {
805 	progStatus.WK_min_wpm = cntr_WK_min_wpm->value();
806 	if (progStatus.WK_speed_wpm < progStatus.WK_min_wpm) {
807 		progStatus.WK_speed_wpm = progStatus.WK_min_wpm;
808 		cntCW_WPM->value(progStatus.WK_speed_wpm);
809 	}
810 	if (progStatus.WK_online)
811 		load_defaults();
812 }
813 
814 
WK_change_cntr_rng_wpm()815 void WK_change_cntr_rng_wpm()
816 {
817 	progStatus.WK_rng_wpm = cntr_WK_rng_wpm->value();
818 	if (progStatus.WK_speed_wpm > progStatus.WK_min_wpm + progStatus.WK_rng_wpm) {
819 		progStatus.WK_speed_wpm = progStatus.WK_min_wpm + progStatus.WK_rng_wpm;
820 		cntCW_WPM->value(progStatus.WK_speed_wpm);
821 	}
822 	if (progStatus.WK_online)
823 		load_defaults();
824 }
825 
826 
WK_change_cntr_farnsworth()827 void WK_change_cntr_farnsworth()
828 {
829 	progStatus.WK_farnsworth_wpm = cntr_WK_farnsworth->value();
830 	if (progStatus.WK_online)
831 		load_defaults();
832 }
833 
834 
WK_change_cntr_cmd_wpm()835 void WK_change_cntr_cmd_wpm()
836 {
837 	progStatus.WK_cmd_wpm = cntr_WK_cmd_wpm->value();
838 }
839 
840 
WK_change_cntr_ratio()841 void WK_change_cntr_ratio()
842 {
843 	progStatus.WK_dit_dah_ratio = (unsigned char)(cntr_WK_ratio->value() * 50 / 3);
844 	if (progStatus.WK_online)
845 		load_defaults();
846 }
847 
848 
WK_change_cntr_comp()849 void WK_change_cntr_comp()
850 {
851 	progStatus.WK_key_compensation = cntr_WK_comp->value();
852 	if (progStatus.WK_online)
853 		load_defaults();
854 }
855 
856 
WK_change_cntr_first_ext()857 void WK_change_cntr_first_ext()
858 {
859 	progStatus.WK_first_extension = cntr_WK_first_ext->value();
860 	if (progStatus.WK_online)
861 		load_defaults();
862 }
863 
864 
WK_change_cntr_sample()865 void WK_change_cntr_sample()
866 {
867 	progStatus.WK_paddle_setpoint = cntr_WK_sample->value();
868 	if (progStatus.WK_online)
869 		load_defaults();
870 }
871 
872 
WK_change_cntr_weight()873 void WK_change_cntr_weight()
874 {
875 	progStatus.WK_weight = cntr_WK_weight->value();
876 	if (progStatus.WK_online)
877 		load_defaults();
878 }
879 
880 
WK_change_cntr_leadin()881 void WK_change_cntr_leadin()
882 {
883 	progStatus.WK_lead_in_time = cntr_WK_leadin->value();
884 	if (progStatus.WK_online)
885 		load_defaults();
886 }
887 
888 
WK_change_cntr_tail()889 void WK_change_cntr_tail()
890 {
891 	progStatus.WK_tail_time = cntr_WK_tail->value();
892 	if (progStatus.WK_online)
893 		load_defaults();
894 }
895 
896 
WK_change_choice_keyer_mode()897 void WK_change_choice_keyer_mode()
898 {
899 	int modebits = choice_WK_keyer_mode->index() << 4;
900 	progStatus.WK_mode_register = (progStatus.WK_mode_register & 0xCF) | modebits;
901 	LOG_WKEY("mode reg: %02X", progStatus.WK_mode_register);
902 	if (progStatus.WK_online)
903 		load_defaults();
904 }
905 
906 
WK_change_choice_hang()907 void WK_change_choice_hang()
908 {
909 	int hangbits = choice_WK_hang->index() << 4;
910 	progStatus.WK_pin_configuration = (progStatus.WK_pin_configuration & 0xCF) | hangbits;
911 	if (progStatus.WK_online)
912 		load_defaults();
913 }
914 
915 
WK_change_choice_sidetone()916 void WK_change_choice_sidetone()
917 {
918 	progStatus.WK_sidetone = choice_WK_sidetone->index() + 1;
919 	progStatus.WK_sidetone |= (btn_WK_sidetone_on->value() ? 0x80 : 0x00);
920 	if (progStatus.WK_online)
921 		load_defaults();
922 }
923 
924 
WK_change_choice_output_pins()925 void WK_change_choice_output_pins()
926 {
927 	int pinbits = (choice_WK_output_pins->index() + 1) << 2;
928 	progStatus.WK_pin_configuration = (progStatus.WK_pin_configuration & 0xF3) | pinbits;
929 	if (progStatus.WK_online)
930 		load_defaults();
931 }
932 
933 //----------------------------------------------------------------------
934 // serial support
935 //----------------------------------------------------------------------
936 
937 extern bool test;
938 
WK_start_wkey_serial()939 bool WK_start_wkey_serial()
940 {
941 	WK_bypass_serial_thread_loop = true;
942 
943 	WK_serial.ClosePort();
944 
945 	if (progStatus.WK_serial_port_name == "NONE") return false;
946 
947 	WK_serial.Device(progStatus.WK_serial_port_name);
948 	WK_serial.Baud(1200);
949 	WK_serial.Stopbits(2);
950 	WK_serial.Retries(1);
951 	WK_serial.Timeout(1);//50);
952 	WK_serial.RTSptt(false);
953 	WK_serial.DTRptt(false);
954 	WK_serial.RTSCTS(false);
955 	WK_serial.RTS(false);
956 	WK_serial.DTR(true);
957 
958 	if (!WK_serial.OpenPort()) {
959 		LOG_ERROR("Cannot access %s", progStatus.WK_serial_port_name.c_str());
960 		return false;
961 	} else  {
962 		LOG_WKEY("\n\
963 Serial port:\n\
964   Port     : %s\n\
965   Baud     : %d\n\
966   Stopbits : %d\n\
967   Timeout  : %d\n\
968   DTR      : %s\n\
969   RTS/CTS  : %s",
970 	progStatus.WK_serial_port_name.c_str(),
971 	WK_serial.Baud(),
972 	WK_serial.Stopbits(),
973 	WK_serial.Timeout(),
974 	WK_serial.DTR() ? "true" : "false",
975 	WK_serial.RTSCTS() ? "true" : "false");
976 	}
977 
978 	MilliSleep(400);	// to allow WK1 to wake up
979 
980 	return true;
981 }
982 
983 #define WK_RXBUFFSIZE 512
984 static unsigned char WK_replybuff[WK_RXBUFFSIZE+1];
985 static string WK_replystr;
986 
WK_readByte(unsigned char & byte)987 bool WK_readByte(unsigned char &byte)
988 {
989 	unsigned char c;
990 	int ret = WK_serial.ReadBuffer(&c, 1);
991 	byte = c;
992 	return ret;
993 }
994 
WK_readString()995 int WK_readString()
996 {
997 	int numread = 0;
998 	size_t n;
999 	memset(WK_replybuff, 0, WK_RXBUFFSIZE + 1);
1000 	while (numread < WK_RXBUFFSIZE) {
1001 		if ((n = WK_serial.ReadBuffer(&WK_replybuff[numread], WK_RXBUFFSIZE - numread)) == 0) break;
1002 		numread += n;
1003 	}
1004 	WK_replystr.append((const char *)WK_replybuff);
1005 	return numread;
1006 }
1007 
WK_sendString(string & s)1008 int WK_sendString (string &s)
1009 {
1010 	if (WK_serial.IsOpen() == false) {
1011 		LOG_WKEY("command: %s", str2hex(s.data(), s.length()));
1012 		return 0;
1013 	}
1014 	int numwrite = (int)s.length();
1015 
1016 	WK_serial.WriteBuffer((unsigned char *)s.c_str(), numwrite);
1017 
1018 	if (isprint(s[0]))
1019 		LOG_WKEY("Sent %d: '%s' %s", numwrite, s.c_str(), str2hex(s.data(), s.length()));
1020 	else
1021 		LOG_WKEY("Sent %d: %s", numwrite, str2hex(s.data(), s.length()));
1022 	return numwrite;
1023 }
1024 
WK_clearSerialPort()1025 void WK_clearSerialPort()
1026 {
1027 	if (WK_serial.IsOpen() == false) return;
1028 	WK_serial.FlushBuffer();
1029 }
1030 
1031 static bool WK_thread_activated = false;
1032 
WK_exit()1033 void WK_exit()
1034 {
1035 	if (!WK_thread_activated) return;
1036 
1037 	if (progStatus.WK_online)
1038 		WKCW_connect(false);
1039 
1040 	pthread_mutex_lock(&WK_mutex_serial);
1041 		WK_run_serial_thread = false;
1042 	pthread_mutex_unlock(&WK_mutex_serial);
1043 	pthread_join(WK_serial_thread, NULL);
1044 
1045 }
1046 
1047 // =====================================================================
1048 // Winkeyer 3.0 FSK interface support
1049 // =====================================================================
1050 
1051 // progStatus configuration parameters:
1052 //
1053 //	WKFSK_baud
1054 //	WKFSK_stopbits
1055 //	WKFSK_ptt
1056 //	WKFSK_polarity
1057 //	WKFSK_sidetone
1058 //	WKFSK_auto_crlf
1059 //	WKFSK_diddle
1060 //	WKFSK_diddle_char
1061 //	WKFSK_usos
1062 //	WKFSK_monitor
1063 
1064 #define wait_one_char(baud, stopbits) (int)((6 + (stopbits))*1000.0 / (baud))
1065 
WKFSK_send_char(int ch)1066 void WKFSK_send_char(int ch)
1067 {
1068 	unsigned char c = toupper(ch);
1069 	int n = (int)(5 * wait_one_char(45.45, 2));
1070 
1071 	if (c == 0 || c == '\n') {
1072 		MilliSleep(10);
1073 		Fl::awake();
1074 		return;
1075 	}
1076 	if (c == '\r') c = '}';
1077 
1078 	{
1079 		guard_lock wklock(&WK_buffer_mutex);
1080 		if (c == '[' || c == ']' || c == '}' || c == '{' || c < ' ') {
1081 			LOG_WKEY("%s",
1082 				(c == '[' ? "[ - ptt ON" :
1083 				 c == ']' ? "] - ptt OFF" :
1084 				 c == '}' ? "} - CR/LF" :
1085 				 c == '{' ? "{ - left brace" : "Control code"));
1086 		} else
1087 			LOG_WKEY("Sending %c, %x", c, c);
1088 		if (progStatus.WKFSK_monitor)
1089 			WK_wait_for_char = true;
1090 		WK_str_out += c;
1091 	}
1092 
1093 	while (WK_wait_for_char) {
1094 		MilliSleep(10);
1095 		Fl::awake();
1096 		n -= 10;
1097 		if (n <= 0 || active_modem->get_stopflag()) {
1098 			WK_wait_for_char = false;
1099 			LOG_WKEY("%s",
1100 				(n <= 0 ? "echo: NIL" : "xmt aborted") );
1101 			return;
1102 		}
1103 	}
1104 	return;
1105 }
1106 
WKFSK_init()1107 void WKFSK_init()
1108 {
1109 	std::string cmd = "    ";
1110 
1111 LOG_WKEY("mode        %d", progStatus.WKFSK_mode);
1112 LOG_WKEY("diddle      %d", progStatus.WKFSK_diddle);
1113 LOG_WKEY("ptt         %d", progStatus.WKFSK_ptt);
1114 LOG_WKEY("auto crlf   %d", progStatus.WKFSK_auto_crlf);
1115 LOG_WKEY("monitor     %d", progStatus.WKFSK_monitor);
1116 LOG_WKEY("polarity    %d", progStatus.WKFSK_polarity);
1117 LOG_WKEY("baud        %d", progStatus.WKFSK_baud);
1118 LOG_WKEY("stopbits    %d", progStatus.WKFSK_stopbits);
1119 LOG_WKEY("sidetone    %d", progStatus.WKFSK_sidetone);
1120 LOG_WKEY("diddle_char %d", progStatus.WKFSK_diddle_char);
1121 LOG_WKEY("usos        %d", progStatus.WKFSK_usos);
1122 
1123 	cmd[0] = ADMIN;
1124 	cmd[1] = FSK_MODE;
1125 	cmd[2] = (progStatus.WKFSK_mode ? 0x80 : 0x00);
1126 	cmd[2] |= (progStatus.WKFSK_diddle ? 0x40 : 0x00);
1127 	cmd[2] |= (progStatus.WKFSK_ptt ? 0x20 : 0x00);
1128 	cmd[2] |= (progStatus.WKFSK_auto_crlf ? 0x10 : 0x00);
1129 	cmd[2] |= (progStatus.WKFSK_monitor ? 0x08 : 0x00);
1130 	cmd[2] |= (progStatus.WKFSK_polarity ? 0x04 : 0x00);
1131 	cmd[2] |= progStatus.WKFSK_baud;
1132 
1133 	cmd[3] = (progStatus.WKFSK_sidetone ? 0x10 : 0x00);
1134 	cmd[3] |= (progStatus.WKFSK_stopbits ? 0x08 : 0x00);
1135 	cmd[3] |= (progStatus.WKFSK_diddle_char ? 0x04 : 0x00);
1136 	cmd[3] |= progStatus.WKFSK_usos;
1137 
1138 	WK_send_command(cmd);
1139 
1140 }
1141 
WKCW_connect(bool start)1142 void WKCW_connect(bool start)
1143 {
1144 	LOG_WKEY("WKCW_connect(%s)", (start ? "ON" : "OFF"));
1145 
1146 	progStatus.WKFSK_mode = false;
1147 	btn_WKFSK_connect->value(0);
1148 	btn_WKCW_connect->value(0);
1149 
1150 	if (!WK_thread_activated) {
1151 		if (pthread_create(&WK_serial_thread, NULL, WK_serial_thread_loop, NULL)) {
1152 			perror("pthread_create");
1153 			exit(EXIT_FAILURE);
1154 		}
1155 		WK_thread_activated = true;
1156 	}
1157 
1158 	if (!start) {
1159 		close_wkeyer();
1160 		MilliSleep(100);
1161 		WK_bypass_serial_thread_loop = true;
1162 		MilliSleep(50);
1163 		WK_serial.ClosePort();
1164 		ReceiveText->add("\nWinKeyer disconnected\n");
1165 		progStatus.WK_online = false;
1166 		return;
1167 	}
1168 
1169 // shutdown and then reconnect if currently in FSK mode
1170 	if (progStatus.WK_online) {
1171 		close_wkeyer();
1172 		MilliSleep(100);
1173 		WK_bypass_serial_thread_loop = true;
1174 		MilliSleep(50);
1175 		WK_serial.ClosePort();
1176 	}
1177 
1178 	if (WK_start_wkey_serial()) {
1179 		WK_bypass_serial_thread_loop = false;
1180 		open_wkeyer();
1181 		if (!WK_serial.IsOpen()) {
1182 			progStatus.WK_online = false;
1183 			return;
1184 		} else {
1185 			progStatus.WK_online = true;
1186 			btn_WKCW_connect->value(1);
1187 		}
1188 	} else
1189 		progStatus.WK_online = false;
1190 
1191 	WKCW_init();
1192 }
1193 
WKFSK_connect(bool start)1194 void WKFSK_connect(bool start)
1195 {
1196 	LOG_WKEY("WKFSK_connect(%s)", (start ? "ON" : "OFF"));
1197 
1198 	progStatus.WKFSK_mode = false;
1199 	btn_WKFSK_connect->value(0);
1200 	btn_WKCW_connect->value(0);
1201 
1202 	if (!WK_thread_activated) {
1203 		if (pthread_create(&WK_serial_thread, NULL, WK_serial_thread_loop, NULL)) {
1204 			perror("pthread_create");
1205 			exit(EXIT_FAILURE);
1206 		}
1207 		WK_thread_activated = true;
1208 	}
1209 
1210 	if (!start) {
1211 		close_wkeyer();
1212 		MilliSleep(100);
1213 		WK_bypass_serial_thread_loop = true;
1214 		MilliSleep(50);
1215 		WK_serial.ClosePort();
1216 		ReceiveText->add("\nWinKeyer disconnected\n");
1217 		progStatus.WK_online = false;
1218 		return;
1219 	}
1220 
1221 // shutdown and then reconnect if currently in CW mode
1222 	if (progStatus.WK_online) {
1223 		close_wkeyer();
1224 		MilliSleep(100);
1225 		WK_bypass_serial_thread_loop = true;
1226 		MilliSleep(50);
1227 		WK_serial.ClosePort();
1228 	}
1229 
1230 	if (WK_start_wkey_serial()) {
1231 		WK_bypass_serial_thread_loop = false;
1232 		open_wkeyer();
1233 		if (!WK_serial.IsOpen()) {
1234 			progStatus.WK_online = false;
1235 			return;
1236 		} else {
1237 			progStatus.WK_online = true;
1238 		}
1239 	} else
1240 		progStatus.WK_online = false;
1241 
1242 	if (progStatus.WK_version < 31) {
1243 		fl_alert2("Winkeyer version must be 31 or greater");
1244 
1245 		close_wkeyer();
1246 		MilliSleep(100);
1247 		WK_bypass_serial_thread_loop = true;
1248 		MilliSleep(50);
1249 		WK_serial.ClosePort();
1250 
1251 		ReceiveText->add("\nWinKeyer disconnected\n");
1252 
1253 		progStatus.WK_online = false;
1254 		progStatus.WKFSK_mode = false;
1255 		return;
1256 	}
1257 
1258 	progStatus.WKFSK_mode = true;
1259 
1260 	WKFSK_init();
1261 
1262 	btn_WKFSK_connect->value(1);
1263 
1264 }
1265