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