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