1 // ---------------------------------------------------------------------
2 // cwio.cxx -- morse code modem
3 //
4 // Copyright (C) 2020
5 // Dave Freese, W1HKJ
6 //
7 // This file is part of flrig
8 //
9 // flrig 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 // flrig 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 <cstring>
27 #include <string>
28 #include <stdio.h>
29 #include <iostream>
30 #include <fstream>
31 #include <cstdlib>
32
33 #include <time.h>
34 #include <sys/time.h>
35
36 #include <FL/Fl.H>
37
38 #include "status.h"
39 #include "util.h"
40 #include "threads.h"
41 #include "debug.h"
42 #include "tod_clock.h"
43 #include "cwio.h"
44 #include "cwioUI.h"
45
46 using namespace std;
47
48 Cserial *cwio_serial = 0;
49 Cmorse *morse = 0;
50
51 static pthread_t cwio_pthread;
52 static pthread_cond_t cwio_cond;
53 static pthread_mutex_t cwio_mutex = PTHREAD_MUTEX_INITIALIZER;
54 //static pthread_mutex_t cwio_text_mutex = PTHREAD_MUTEX_INITIALIZER;
55
56 enum { NONE, START, ADD, SEND, END, TERMINATE, CALIBRATE };
57 int cwio_process = NONE;
58 bool cwio_thread_running = false;
59
send_cwkey(char c)60 void send_cwkey(char c)
61 {
62 Cserial *port = cwio_serial;
63 if (progStatus.cwioSHARED == 1) {
64 port = RigSerial; // sharing CAT port
65 bypass_serial_thread_loop = true;
66 }
67 if (progStatus.cwioSHARED == 2) port = AuxSerial; // sharing AUX port
68 if (progStatus.cwioSHARED == 3) port = SepSerial; // sharing SEP port
69 if (!port) return;
70 if (!port->IsOpen()) return;
71
72 double tc = 1200.0 / progStatus.cwioWPM;
73 double tch = 3 * tc;
74 double twd = 4 * tc;
75
76 if ((progStatus.cwio_comp > 0) && (progStatus.cwio_comp < tc)) {
77 tc = round (tc - progStatus.cwio_comp);
78 tch = round (tch - 3 * progStatus.cwio_comp);
79 twd = round (twd - 4 * progStatus.cwio_comp);
80 }
81
82 if (c == ' ' || c == 0x0a) {
83 MilliSleep(twd);
84 return;
85 }
86
87 string code = morse->tx_lookup(c);
88 for (size_t n = 0; n < code.length(); n++) {
89 if (cwio_process == END) return;
90 if (progStatus.cwioKEYLINE == 2) {
91 port->setDTR(1);
92 } else if (progStatus.cwioKEYLINE == 1) {
93 port->setRTS(1);
94 }
95 if (code[n] == '.')
96 MilliSleep(tc);
97 else
98 MilliSleep(tch);
99
100 if (progStatus.cwioKEYLINE == 2) {
101 port->setDTR(0);
102 } else if (progStatus.cwioKEYLINE == 1) {
103 port->setRTS(0);
104 }
105 if (n == code.length() -1)
106 MilliSleep(tch);
107 else
108 MilliSleep(tc);
109 }
110
111 if (progStatus.cwioSHARED == 1) {
112 bypass_serial_thread_loop = false;
113 }
114 return;
115 }
116
open_cwkey()117 int open_cwkey()
118 {
119 if (progStatus.cwioSHARED)
120 return 1;
121 if (!cwio_serial)
122 cwio_serial = new Cserial;
123
124 cwio_serial->Device(progStatus.cwioPORT);
125
126 if (cwio_serial->OpenPort() == false) {
127 LOG_ERROR("Cannot open serial port %s", cwio_serial->Device().c_str());
128 cwio_serial = 0;
129 return 0;
130 }
131 LOG_INFO("Opened %s for CW keyline control", cwio_serial->Device().c_str());
132
133 cwio_serial->RTS(false);
134 cwio_serial->DTR(false);
135
136 return 1;
137 }
138
close_cwkey()139 void close_cwkey()
140 {
141 if (cwio_serial) {
142 cwio_serial->ClosePort();
143 }
144 }
145
146 static string snd;
update_txt_to_send(void *)147 void update_txt_to_send(void *)
148 {
149 txt_to_send->value(snd.c_str());
150 txt_to_send->redraw();
151 }
152
sending_text()153 void sending_text()
154 {
155 char c;
156 if (progStatus.cwioPTT) {
157 setPTT((void *)1);
158 MilliSleep(progStatus.cwioPTT);
159 }
160 while (cwio_process == SEND) {
161 snd = txt_to_send->value();
162 if (snd.empty()) MilliSleep(10);
163 else {
164 c = snd[0];
165 snd.erase(0,1);
166 Fl::awake(update_txt_to_send);
167 send_cwkey(c);
168 }
169 }
170 if (progStatus.cwioPTT) {
171 setPTT((void *)0);
172 MilliSleep(progStatus.cwioPTT);
173 }
174 }
175
update_comp_value(void *)176 void update_comp_value(void *)
177 {
178 cnt_cwio_comp->value(progStatus.cwio_comp);
179 cnt_cwio_comp->redraw();
180 btn_cw_dtr_calibrate->value(0);
181 btn_cw_dtr_calibrate->redraw();
182 }
183
do_calibration()184 void do_calibration()
185 {
186 std::string paris = "PARIS ";
187 std::string teststr;
188 unsigned long start_time = 0;
189 unsigned long end_time = 0;
190
191 progStatus.cwio_comp = 0;
192
193 for (int i = 0; i < progStatus.cwioWPM; i++)
194 teststr.append(paris);
195
196 txt_to_send->value();
197
198 start_time = zmsec();
199 for (size_t n = 0; n < teststr.length(); n++) {
200 send_cwkey(teststr[n]);
201 }
202
203 end_time = zmsec();
204
205 progStatus.cwio_comp = 50 * (1.0 - 60000.0 / (end_time - start_time));
206
207 //std::cout << "start_time: " << start_time << ", end_time: " << end_time << std::endl;
208 //std::cout << "cwio_comp: " << progStatus.cwio_comp << std::endl;
209
210 Fl::awake(update_comp_value);
211 }
212
213 //----------------------------------------------------------------------
214 // cwio thread
215 //----------------------------------------------------------------------
216
cwio_loop(void *)217 void *cwio_loop(void *)
218 {
219 cwio_thread_running = true;
220 cwio_process = NONE;
221 while (1) {
222 pthread_mutex_lock(&cwio_mutex);
223 pthread_cond_wait(&cwio_cond, &cwio_mutex);
224 pthread_mutex_unlock(&cwio_mutex);
225
226 if (cwio_process == TERMINATE)
227 return NULL;
228
229 if (cwio_process == SEND)
230 sending_text();
231
232 if (cwio_process == CALIBRATE)
233 do_calibration();
234 }
235 return NULL;
236 }
237
start_cwio_thread()238 int start_cwio_thread()
239 {
240 if(cwio_thread_running) return 0;
241
242 if (!morse)
243 morse = new Cmorse;
244
245 memset((void *) &cwio_pthread, 0, sizeof(cwio_pthread));
246 memset((void *) &cwio_mutex, 0, sizeof(cwio_mutex));
247 memset((void *) &cwio_cond, 0, sizeof(cwio_cond));
248
249 if(pthread_cond_init(&cwio_cond, NULL)) {
250 LOG_ERROR("cwio thread create fail (pthread_cond_init)");
251 return 1;
252 }
253
254 if(pthread_mutex_init(&cwio_mutex, NULL)) {
255 LOG_ERROR("cwio thread create fail (pthread_mutex_init)");
256 return 1;
257 }
258
259 if (pthread_create(&cwio_pthread, NULL, cwio_loop, NULL) < 0) {
260 pthread_mutex_destroy(&cwio_mutex);
261 LOG_ERROR("cwio thread create fail (pthread_create)");
262 return 1;
263 }
264
265 LOG_INFO("started cwio thread");
266
267 MilliSleep(10); // Give the CPU time to set 'cwio_thread_running'
268 return 0;
269 }
270
stop_cwio_thread()271 void stop_cwio_thread()
272 {
273 if(!cwio_thread_running) return;
274 //std::cout << "stopping cwio thread" << std::endl;
275
276 cwio_process = END;
277 btn_cwioSEND->value(0);
278
279 MilliSleep(4 * 1200 / progStatus.cwioWPM);
280
281 cwio_process = TERMINATE;
282 pthread_cond_signal(&cwio_cond);
283 MilliSleep(50);
284
285 //std::cout << "joining cwio_pthread" << std::endl;
286
287 pthread_join(cwio_pthread, NULL);
288 //std::cout << "cwio thread - stopped" << std::endl;
289
290 LOG_INFO("%s", "cwio thread - stopped");
291
292 pthread_mutex_destroy(&cwio_mutex);
293 pthread_cond_destroy(&cwio_cond);
294
295 memset((void *) &cwio_pthread, 0, sizeof(cwio_pthread));
296 memset((void *) &cwio_mutex, 0, sizeof(cwio_mutex));
297
298 cwio_thread_running = false;
299 cwio_process = NONE;
300
301 close_cwkey();
302
303 if (cwio_serial) {
304 delete cwio_serial;
305 cwio_serial = 0;
306 }
307 if (morse) {
308 delete morse;
309 morse = 0;
310 }
311
312 }
313
314 static string new_text;
315
add_cwio(string txt)316 void add_cwio(string txt)
317 {
318 if (!cwio_thread_running) return;
319
320 new_text = txt_to_send->value();
321 new_text.append(txt);
322 txt_to_send->value(new_text.c_str());
323 txt_to_send->redraw();
324 }
325
send_text(bool state)326 void send_text(bool state)
327 {
328 if (!cwio_thread_running) return;
329
330 if (state && cwio_process != SEND) {
331 cwio_process = SEND;
332 pthread_cond_signal(&cwio_cond);
333 } else {
334 cwio_process = NONE;
335 }
336 }
337
cwio_clear_text()338 void cwio_clear_text()
339 {
340 txt_to_send->value("");
341 }
342
msg_cb(int n)343 void msg_cb(int n)
344 {
345 }
346
label_cb(int n)347 void label_cb(int n)
348 {
349 }
350
exec_msg(int n)351 void exec_msg(int n)
352 {
353 if ((Fl::event_state() & FL_CTRL) == FL_CTRL) {
354 for (int n = 0; n < 12; n++) {
355 edit_label[n]->value(progStatus.cwio_labels[n].c_str());
356 edit_msg[n]->value(progStatus.cwio_msgs[n].c_str());
357 }
358 cwio_editor->show();
359 return;
360 }
361 add_cwio(progStatus.cwio_msgs[n]);
362 }
363
cancel_edit()364 void cancel_edit()
365 {
366 cwio_editor->hide();
367 }
368
apply_edit()369 void apply_edit()
370 {
371 for (int n = 0; n < 12; n++) {
372 progStatus.cwio_labels[n] = edit_label[n]->value();
373 progStatus.cwio_msgs[n] = edit_msg[n]->value();
374 btn_msg[n]->label(progStatus.cwio_labels[n].c_str());
375 btn_msg[n]->redraw_label();
376 }
377 }
378
done_edit()379 void done_edit()
380 {
381 cwio_editor->hide();
382 }
383
384 // Alt-P pause transmit
385 // Alt-S start sending text
386 // F1 - F12 same as function-button mouse press
387
control_function_keys()388 void control_function_keys()
389 {
390 int key = Fl::event_key();
391 int state = Fl::event_state();
392
393 if (state & FL_ALT) {
394 if (key == 'p') {
395 btn_cwioSEND->value(0);
396 btn_cwioSEND->redraw();
397 send_text(false);
398 return;
399 }
400 if (key == 's') {
401 btn_cwioSEND->value(1);
402 btn_cwioSEND->redraw();
403 send_text(true);
404 return;
405 }
406 if (key == 'c') {
407 txt_to_send->value("");
408 return;
409 }
410 }
411 if ((key >= FL_F) && (key <= FL_F_Last)) {
412 exec_msg( key - FL_F - 1);
413 }
414 }
415
calibrate_cwio()416 void calibrate_cwio()
417 {
418 txt_to_send->value("");
419 btn_cwioSEND->value(0);
420
421 cwio_process = END;
422 MilliSleep(50);
423
424 cwio_process = CALIBRATE;
425 pthread_cond_signal(&cwio_cond);
426 }
427
428
open_cwio_config()429 void open_cwio_config()
430 {
431 cwio_configure->show();
432 }
433