1 // ----------------------------------------------------------------------------
2 // cw.cxx -- morse code modem
3 //
4 // Copyright (C) 2006-2010
5 // Dave Freese, W1HKJ
6 // (C) Mauri Niininen, AG1LE
7 //
8 // This file is part of fldigi. Adapted from code contained in gmfsk source code
9 // distribution.
10 // gmfsk Copyright (C) 2001, 2002, 2003
11 // Tomi Manninen (oh2bns@sral.fi)
12 // Copyright (C) 2004
13 // Lawrence Glaister (ve7it@shaw.ca)
14 //
15 // Fldigi is free software: you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation, either version 3 of the License, or
18 // (at your option) any later version.
19 //
20 // Fldigi is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
27 // ----------------------------------------------------------------------------
28
29
30 #include <config.h>
31
32 #include <cstring>
33 #include <string>
34 #include <stdio.h>
35 #include <iostream>
36 #include <fstream>
37 #include <cstdlib>
38
39 #include "digiscope.h"
40 #include "waterfall.h"
41 #include "fl_digi.h"
42 #include "fftfilt.h"
43 #include "serial.h"
44 #include "ptt.h"
45 #include "main.h"
46
47 #include "cw.h"
48 #include "misc.h"
49 #include "configuration.h"
50 #include "confdialog.h"
51 #include "status.h"
52 #include "debug.h"
53 #include "FTextRXTX.h"
54 #include "modem.h"
55
56 #include "qrunner.h"
57
58 #include "winkeyer.h"
59 #include "nanoIO.h"
60 #include "KYkeying.h"
61 #include "ICOMkeying.h"
62 #include "YAESUkeying.h"
63
64 #include "audio_alert.h"
65
66 using namespace std;
67
68 void start_cwio_thread();
69 void stop_cwio_thread();
70
71 #define XMT_FILT_LEN 256
72 #define QSK_DELAY_LEN 4*XMT_FILT_LEN
73 #define CW_FFT_SIZE 2048 // must be a factor of 2
74
75 static double nano_d2d = 0;
76 static int nano_wpm = 0;
77
78 const cw::SOM_TABLE cw::som_table[] = {
79 /* Prosigns */
80 {"-...-", {1.0, 0.33, 0.33, 0.33, 1.0, 0, 0} },
81 {".-.-", { 0.33, 1.0, 0.33, 1.0, 0, 0, 0} },
82 {".-...", { 0.33, 1.0, 0.33, 0.33, 0.33, 0, 0} },
83 {".-.-.", { 0.33, 1.0, 0.33, 1.0, 0.33, 0, 0} },
84 {"...-.-", { 0.33, 0.33, 0.33, 1.0, 0.33, 1.0, 0} },
85 {"-.--.", {1.0, 0.33, 1.0, 1.0, 0.33, 0, 0} },
86 {"..-.-", { 0.33, 0.33, 1.0, 0.33, 1.0, 0, 0} },
87 {"....--", { 0.33, 0.33, 0.33, 0.33, 1.0, 1.0, 0} },
88 {"...-.", { 0.33, 0.33, 0.33, 1.0, 0.33, 0, 0} },
89 /* ASCII 7bit letters */
90 {".-", { 0.33, 1.0, 0, 0, 0, 0, 0} },
91 {"-...", {1.0, 0.33, 0.33, 0.33, 0, 0, 0} },
92 {"-.-.", {1.0, 0.33, 1.0, 0.33, 0, 0, 0} },
93 {"-..", {1.0, 0.33, 0.33, 0, 0, 0, 0} },
94 {".", { 0.33, 0, 0, 0, 0, 0, 0} },
95 {"..-.", { 0.33, 0.33, 1.0, 0.33, 0, 0, 0} },
96 {"--.", {1.0, 1.0, 0.33, 0, 0, 0, 0} },
97 {"....", { 0.33, 0.33, 0.33, 0.33, 0, 0, 0} },
98 {"..", { 0.33, 0.33, 0, 0, 0, 0, 0} },
99 {".---", { 0.33, 1.0, 1.0, 1.0, 0, 0, 0} },
100 {"-.-", {1.0, 0.33, 1.0, 0, 0, 0, 0} },
101 {".-..", { 0.33, 1.0, 0.33, 0.33, 0, 0, 0} },
102 {"--", {1.0, 1.0, 0, 0, 0, 0, 0} },
103 {"-.", {1.0, 0.33, 0, 0, 0, 0, 0} },
104 {"---", {1.0, 1.0, 1.0, 0, 0, 0, 0} },
105 {".--.", { 0.33, 1.0, 1.0, 0.33, 0, 0, 0} },
106 {"--.-", {1.0, 1.0, 0.33, 1.0, 0, 0, 0} },
107 {".-.", { 0.33, 1.0, 0.33, 0, 0, 0, 0} },
108 {"...", { 0.33, 0.33, 0.33, 0, 0, 0, 0} },
109 {"-", {1.0, 0, 0, 0, 0, 0, 0} },
110 {"..-", { 0.33, 0.33, 1.0, 0, 0, 0, 0} },
111 {"...-", { 0.33, 0.33, 0.33, 1.0, 0, 0, 0} },
112 {".--", { 0.33, 1.0, 1.0, 0, 0, 0, 0} },
113 {"-..-", {1.0, 0.33, 0.33, 1.0, 0, 0, 0} },
114 {"-.--", {1.0, 0.33, 1.0, 1.0, 0, 0, 0} },
115 {"--..", {1.0, 1.0, 0.33, 0.33, 0, 0, 0} },
116 /* Numerals */
117 {"-----", {1.0, 1.0, 1.0, 1.0, 1.0, 0, 0} },
118 {".----", { 0.33, 1.0, 1.0, 1.0, 1.0, 0, 0} },
119 {"..---", { 0.33, 0.33, 1.0, 1.0, 1.0, 0, 0} },
120 {"...--", { 0.33, 0.33, 0.33, 1.0, 1.0, 0, 0} },
121 {"....-", { 0.33, 0.33, 0.33, 0.33, 1.0, 0, 0} },
122 {".....", { 0.33, 0.33, 0.33, 0.33, 0.33, 0, 0} },
123 {"-....", {1.0, 0.33, 0.33, 0.33, 0.33, 0, 0} },
124 {"--...", {1.0, 1.0, 0.33, 0.33, 0.33, 0, 0} },
125 {"---..", {1.0, 1.0, 1.0, 0.33, 0.33, 0, 0} },
126 {"----.", {1.0, 1.0, 1.0, 1.0, 0.33, 0, 0} },
127 /* Punctuation */
128 {".-..-.", { 0.33, 1.0, 0.33, 0.33, 1.0, 0.33, 0} },
129 {".----.", { 0.33, 1.0, 1.0, 1.0, 1.0, 0.33, 0} },
130 {"...-..-", { 0.33, 0.33, 0.33, 1.0, 0.33, 0.33, 1.0} },
131 {"-.---.", {1.0, 0.33, 1.0, 1.0, 0.33, 0, 0} },
132 {"-.--.-", {1.0, 0.33, 1.0, 1.0, 0.33, 1.0, 0} },
133 {"--..--", {1.0, 1.0, 0.33, 0.33, 1.0, 1.0, 0} },
134 {"-....-", {1.0, 0.33, 0.33, 0.33, 0.33, 1.0, 0} },
135 {".-.-.-", { 0.33, 1.0, 0.33, 1.0, 0.33, 1.0, 0} },
136 {"-..-.", {1.0, 0.33, 0.33, 1.0, 0.33, 0, 0} },
137 {"---...", {1.0, 1.0, 1.0, 0.33, 0.33, 0.33, 0} },
138 {"-.-.-.", {1.0, 0.33, 1.0, 0.33, 1.0, 0.33, 0} },
139 {"..--..", { 0.33, 0.33, 1.0, 1.0, 0.33, 0.33, 0} },
140 {"..--.-", { 0.33, 0.33, 1.0, 1.0, 0.33, 1.0, 0} },
141 {".--.-.", { 0.33, 1.0, 1.0, 0.33, 1.0, 0.33, 0} },
142 {"-.-.--", {1.0, 0.33, 1.0, 0.33, 1.0, 1.0, 0} },
143
144 {".-.-", {0.33, 1.0, 0.33, 1.0, 0, 0 , 0} }, // A umlaut, A aelig
145 {".--.-", {0.33, 1.0, 1.0, 0.33, 1.0, 0, 0 } }, // A ring
146 {"-.-..", {1.0, 0.33, 1.0, 0.33, 0.33, 0, 0} }, // C cedilla
147 {".-..-", {0.33, 1.0, 0.33, 0.33, 1.0, 0, 0} }, // E grave
148 {"..-..", {0.33, 0.33, 1.0, 0.33, 0.33, 0, 0} }, // E acute
149 {"---.", {1.0, 1.0, 1.0, 0.33, 0, 0, 0} }, // O acute, O umlat, O slash
150 {"--.--", {1.0, 1.0, 0.33, 1.0, 1.0, 0, 0} }, // N tilde
151 {"..--", {0.33, 0.33, 1.0, 1.0, 0, 0, 0} }, // U umlaut, U circ
152
153 {"", {0.0}}
154 };
155
normalize(float * v,int n,int twodots)156 int cw::normalize(float *v, int n, int twodots)
157 {
158 if( n == 0 ) return 0 ;
159
160 float max = v[0];
161 float min = v[0];
162 int j;
163
164 /* find max and min values */
165 for (j=1; j<n; j++) {
166 float vj = v[j];
167 if (vj > max) max = vj;
168 else if (vj < min) min = vj;
169 }
170 /* all values 0 - no need to normalize or decode */
171 if (max == 0.0) return 0;
172
173 /* scale values between [0,1] -- if Max longer than 2 dots it was "dah" and should be 1.0, otherwise it was "dit" and should be 0.33 */
174 float ratio = (max > twodots) ? 1.0 : 0.33 ;
175 ratio /= max ;
176 for (j=0; j<n; j++) v[j] *= ratio;
177 return (1);
178 }
179
180
find_winner(float * inbuf,int twodots)181 std::string cw::find_winner (float *inbuf, int twodots)
182 {
183 float diffsf = 999999999999.0;
184
185 if ( normalize (inbuf, WGT_SIZE, twodots) == 0) return " ";
186
187 int winner = -1;
188 for ( int n = 0; som_table[n].rpr.length(); n++) {
189 /* Compute the distance between codebook and input entry */
190 float difference = 0.0;
191 for (int i = 0; i < WGT_SIZE; i++) {
192 float diff = (inbuf[i] - som_table[n].wgt[i]);
193 difference += diff * diff;
194 if (difference > diffsf) break;
195 }
196
197 /* If distance is smaller than previous distances */
198 if (difference < diffsf) {
199 winner = n;
200 diffsf = difference;
201 }
202 }
203
204 std::string sc;
205 if (!som_table[winner].rpr.empty()) {
206 sc = morse.rx_lookup(som_table[winner].rpr);
207 if (sc.empty())
208 sc = (progdefaults.CW_noise == '*' ? "*" :
209 progdefaults.CW_noise == '_' ? "_" :
210 progdefaults.CW_noise == ' ' ? " " : "");
211 } else
212 sc = (progdefaults.CW_noise == '*' ? "*" :
213 progdefaults.CW_noise == '_' ? "_" :
214 progdefaults.CW_noise == ' ' ? " " : "");
215 return sc;
216 }
217
tx_init()218 void cw::tx_init()
219 {
220 phaseacc = 0;
221 lastsym = 0;
222 qskphase = 0;
223 if (progdefaults.pretone) pretone();
224
225 symbols = 0;
226 acc_symbols = 0;
227 ovhd_symbols = 0;
228
229 maxval = 0.0;
230 }
231
rx_init()232 void cw::rx_init()
233 {
234 cw_receive_state = RS_IDLE;
235 smpl_ctr = 0;
236 cw_rr_current = 0;
237 cw_ptr = 0;
238 agc_peak = 0;
239 set_scope_mode(Digiscope::SCOPE);
240
241 update_Status();
242 usedefaultWPM = false;
243 scope_clear = true;
244
245 viewcw.restart();
246 }
247
init()248 void cw::init()
249 {
250 bool wfrev = wf->Reverse();
251 bool wfsb = wf->USB();
252 reverse = wfrev ^ !wfsb;
253
254 if (progdefaults.StartAtSweetSpot)
255 set_freq(progdefaults.CWsweetspot);
256 else if (progStatus.carrier != 0) {
257 set_freq(progStatus.carrier);
258 #if !BENCHMARK_MODE
259 progStatus.carrier = 0;
260 #endif
261 } else
262 set_freq(wf->Carrier());
263
264 trackingfilter->reset();
265 two_dots = (long int)trackingfilter->run(2 * cw_send_dot_length);
266 put_cwRcvWPM(cw_send_speed);
267
268 memset(outbuf, 0, OUTBUFSIZE*sizeof(*outbuf));
269 memset(qskbuf, 0, OUTBUFSIZE*sizeof(*qskbuf));
270
271 morse.init();
272 use_paren = progdefaults.CW_use_paren;
273 prosigns = progdefaults.CW_prosigns;
274
275 rx_init();
276
277 stopflag = false;
278 maxval = 0;
279
280 if (use_nanoIO) set_nanoCW();
281
282 }
283
~cw()284 cw::~cw() {
285 if (cw_FFT_filter) delete cw_FFT_filter;
286 if (bitfilter) delete bitfilter;
287 if (trackingfilter) delete trackingfilter;
288 stop_cwio_thread();
289 }
290
cw()291 cw::cw() : modem()
292 {
293 cap |= CAP_BW;
294
295 mode = MODE_CW;
296 freqlock = false;
297 usedefaultWPM = false;
298 frequency = progdefaults.CWsweetspot;
299 tx_frequency = get_txfreq_woffset();
300 risetime = progdefaults.CWrisetime;
301 QSKshape = progdefaults.QSKshape;
302
303 cw_ptr = 0;
304 clrcount = CLRCOUNT;
305
306 samplerate = CW_SAMPLERATE;
307 fragmentsize = CWMaxSymLen;
308
309 wpm = cw_speed = progdefaults.CWspeed;
310 bandwidth = progdefaults.CWbandwidth;
311
312 cw_send_speed = cw_speed;
313 cw_receive_speed = cw_speed;
314 two_dots = 2 * KWPM / cw_speed;
315 cw_noise_spike_threshold = two_dots / 4;
316 cw_send_dot_length = KWPM / cw_send_speed;
317 cw_send_dash_length = 3 * cw_send_dot_length;
318 symbollen = (int)round(samplerate * 1.2 / progdefaults.CWspeed); // transmit char rate
319 fsymlen = (int)round(samplerate * 1.2 / progdefaults.CWfarnsworth); // transmit word rate
320
321 rx_rep_buf.clear();
322
323 // block of variables that get updated each time speed changes
324 pipesize = (22 * samplerate * 12) / (progdefaults.CWspeed * 160);
325 if (pipesize < 0) pipesize = 512;
326 if (pipesize > MAX_PIPE_SIZE) pipesize = MAX_PIPE_SIZE;
327
328 cwTrack = true;
329 phaseacc = 0.0;
330 FFTphase = 0.0;
331 FFTvalue = 0.0;
332 pipeptr = 0;
333 clrcount = 0;
334
335 upper_threshold = progdefaults.CWupper;
336 lower_threshold = progdefaults.CWlower;
337 for (int i = 0; i < MAX_PIPE_SIZE; clearpipe[i++] = 0.0);
338
339 agc_peak = 1.0;
340 in_replay = 0;
341
342 use_matched_filter = progdefaults.CWmfilt;
343
344 bandwidth = progdefaults.CWbandwidth;
345 if (use_matched_filter)
346 progdefaults.CWbandwidth = bandwidth = 5.0 * progdefaults.CWspeed / 1.2;
347
348 cw_FFT_filter = new fftfilt(1.0 * progdefaults.CWbandwidth / samplerate, CW_FFT_SIZE);
349
350 int bfv = symbollen / ( 2 * DEC_RATIO);
351 if (bfv < 1) bfv = 1;
352
353 bitfilter = new Cmovavg(bfv);
354
355 trackingfilter = new Cmovavg(TRACKING_FILTER_SIZE);
356
357 create_edges();
358
359 nano_wpm = progdefaults.CWspeed;
360 nano_d2d = progdefaults.CWdash2dot;
361
362 sync_parameters();
363 REQ(static_cast<void (waterfall::*)(int)>(&waterfall::Bandwidth), wf, (int)bandwidth);
364 REQ(static_cast<int (Fl_Value_Slider2::*)(double)>(&Fl_Value_Slider2::value), sldrCWbandwidth, (int)bandwidth);
365 update_Status();
366
367 synchscope = 50;
368 noise_floor = 1.0;
369 sig_avg = 0.0;
370
371 start_cwio_thread();
372
373 }
374
375 // SHOULD ONLY BE CALLED FROM THE rx_processing loop
reset_rx_filter()376 void cw::reset_rx_filter()
377 {
378 if (use_matched_filter != progdefaults.CWmfilt ||
379 cw_speed != progdefaults.CWspeed ||
380 (bandwidth != progdefaults.CWbandwidth && !use_matched_filter)) {
381
382 use_matched_filter = progdefaults.CWmfilt;
383 cw_send_speed = cw_speed = progdefaults.CWspeed;
384
385 if (use_matched_filter)
386 progdefaults.CWbandwidth = bandwidth = 5.0 * progdefaults.CWspeed / 1.2;
387 else
388 bandwidth = progdefaults.CWbandwidth;
389
390 cw_FFT_filter->create_lpf(1.0 * bandwidth / samplerate);
391 FFTphase = 0;
392
393 REQ(static_cast<void (waterfall::*)(int)>(&waterfall::Bandwidth),
394 wf, (int)bandwidth);
395 REQ(static_cast<int (Fl_Value_Slider2::*)(double)>(&Fl_Value_Slider2::value),
396 sldrCWbandwidth, (int)bandwidth);
397
398 pipesize = (22 * samplerate * 12) / (progdefaults.CWspeed * 160);
399 if (pipesize < 0) pipesize = 512;
400 if (pipesize > MAX_PIPE_SIZE) pipesize = MAX_PIPE_SIZE;
401
402 two_dots = 2 * KWPM / cw_speed;
403 cw_noise_spike_threshold = two_dots / 4;
404 cw_send_dot_length = KWPM / cw_send_speed;
405 cw_send_dash_length = 3 * cw_send_dot_length;
406 symbollen = (int)round(samplerate * 1.2 / progdefaults.CWspeed);
407 fsymlen = (int)round(samplerate * 1.2 / progdefaults.CWfarnsworth);
408
409 phaseacc = 0.0;
410 FFTphase = 0.0;
411 FFTvalue = 0.0;
412 pipeptr = 0;
413 clrcount = 0;
414 smpl_ctr = 0;
415
416 rx_rep_buf.clear();
417
418 int bfv = symbollen / ( 2 * DEC_RATIO);
419 if (bfv < 1) bfv = 1;
420
421 bitfilter->setLength(bfv);
422
423 siglevel = 0;
424
425 }
426
427 }
428
429 // sync_parameters()
430 // Synchronize the dot, dash, end of element, end of character, and end
431 // of word timings and ranges to new values of Morse speed, or receive tolerance.
432
sync_transmit_parameters()433 void cw::sync_transmit_parameters()
434 {
435 // wpm = usedefaultWPM ? progdefaults.defCWspeed : progdefaults.CWspeed;
436 fwpm = progdefaults.CWfarnsworth;
437
438 cw_send_dot_length = KWPM / progdefaults.CWspeed;
439 cw_send_dash_length = 3 * cw_send_dot_length;
440
441 nusymbollen = (int)round(samplerate * 1.2 / progdefaults.CWspeed);
442 nufsymlen = (int)round(samplerate * 1.2 / fwpm);
443
444 if (symbollen != nusymbollen ||
445 nufsymlen != fsymlen ||
446 risetime != progdefaults.CWrisetime ||
447 QSKshape != progdefaults.QSKshape) {
448 risetime = progdefaults.CWrisetime;
449 QSKshape = progdefaults.QSKshape;
450 symbollen = nusymbollen;
451 fsymlen = nufsymlen;
452 create_edges();
453 }
454 }
455
sync_parameters()456 void cw::sync_parameters()
457 {
458 sync_transmit_parameters();
459
460 if (use_nanoIO) {
461 if (nano_wpm != progdefaults.CWspeed) {
462 nano_wpm = progdefaults.CWspeed;
463 set_nanoWPM(progdefaults.CWspeed);
464 }
465 if (nano_d2d != progdefaults.CWdash2dot) {
466 nano_d2d = progdefaults.CWdash2dot;
467 set_nano_dash2dot(progdefaults.CWdash2dot);
468 }
469 }
470
471 // check if user changed the tracking or the cw default speed
472 if ((cwTrack != progdefaults.CWtrack) ||
473 (cw_send_speed != progdefaults.CWspeed)) {
474 trackingfilter->reset();
475 two_dots = 2 * cw_send_dot_length;
476 put_cwRcvWPM(cw_send_speed);
477 }
478 cwTrack = progdefaults.CWtrack;
479 cw_send_speed = progdefaults.CWspeed;
480
481 // Receive parameters:
482 lowerwpm = cw_send_speed - progdefaults.CWrange;
483 upperwpm = cw_send_speed + progdefaults.CWrange;
484 if (lowerwpm < progdefaults.CWlowerlimit)
485 lowerwpm = progdefaults.CWlowerlimit;
486 if (upperwpm > progdefaults.CWupperlimit)
487 upperwpm = progdefaults.CWupperlimit;
488 cw_lower_limit = 2 * KWPM / upperwpm;
489 cw_upper_limit = 2 * KWPM / lowerwpm;
490
491 if (cwTrack)
492 cw_receive_speed = KWPM / (two_dots / 2);
493 else {
494 cw_receive_speed = cw_send_speed;
495 two_dots = 2 * cw_send_dot_length;
496 }
497
498 if (cw_receive_speed > 0)
499 cw_receive_dot_length = KWPM / cw_receive_speed;
500 else
501 cw_receive_dot_length = KWPM / 5;
502
503 cw_receive_dash_length = 3 * cw_receive_dot_length;
504
505 cw_noise_spike_threshold = cw_receive_dot_length / 2;
506
507 }
508
509
510 //=======================================================================
511 // cw_update_tracking()
512 //=======================================================================
513
update_tracking(int dur_1,int dur_2)514 inline void cw::update_tracking(int dur_1, int dur_2)
515 {
516 static int min_dot = KWPM / 200;
517 static int max_dash = 3 * KWPM / 5;
518 if ((dur_1 > dur_2) && (dur_1 > 4 * dur_2)) return;
519 if ((dur_2 > dur_1) && (dur_2 > 4 * dur_1)) return;
520 if (dur_1 < min_dot || dur_2 < min_dot) return;
521 if (dur_2 > max_dash || dur_2 > max_dash) return;
522
523 two_dots = trackingfilter->run((dur_1 + dur_2) / 2);
524
525 sync_parameters();
526 }
527
update_Status()528 void cw::update_Status()
529 {
530 put_MODEstatus("CW %s Rx %d", usedefaultWPM ? "*" : " ", cw_receive_speed);
531 REQ(set_CWwpm);
532 }
533
534 //=======================================================================
535 //update_syncscope()
536 //Routine called to update the display on the sync scope display.
537 //For CW this is an o scope pattern that shows the cw data stream.
538 //=======================================================================
539 //
540
update_syncscope()541 void cw::update_syncscope()
542 {
543 if (pipesize < 0 || pipesize > MAX_PIPE_SIZE)
544 return;
545
546 for (int i = 0; i < pipesize; i++)
547 scopedata[i] = 0.96*pipe[i]+0.02;
548
549 set_scope_xaxis_1(siglevel);
550
551 set_scope(scopedata, pipesize, true);
552 scopedata.next(); // change buffers
553
554 clrcount = CLRCOUNT;
555 put_cwRcvWPM(cw_receive_speed);
556 update_Status();
557 }
558
clear_syncscope()559 void cw::clear_syncscope()
560 {
561 set_scope_xaxis_1(siglevel);
562
563 set_scope(clearpipe, pipesize, false);
564 clrcount = CLRCOUNT;
565 }
566
mixer(cmplx in)567 cmplx cw::mixer(cmplx in)
568 {
569 cmplx z (cos(phaseacc), sin(phaseacc));
570 z = z * in;
571
572 phaseacc += TWOPI * frequency / samplerate;
573 if (phaseacc > TWOPI) phaseacc -= TWOPI;
574
575 return z;
576 }
577
578 //=====================================================================
579 // cw_rxprocess()
580 // Called with a block (size SCBLOCKSIZE samples) of audio.
581 //
582 //======================================================================
583
decode_stream(double value)584 void cw::decode_stream(double value)
585 {
586 std::string sc;
587 std::string somc;
588 int attack = 0;
589 int decay = 0;
590 switch (progdefaults.cwrx_attack) {
591 case 0: attack = 400; break;//100; break;
592 case 1: default: attack = 200; break;//50; break;
593 case 2: attack = 100;//25;
594 }
595 switch (progdefaults.cwrx_decay) {
596 case 0: decay = 2000; break;//1000; break;
597 case 1: default : decay = 1000; break;//500; break;
598 case 2: decay = 500;//250;
599 }
600
601 sig_avg = decayavg(sig_avg, value, decay);
602
603 if (value < sig_avg) {
604 if (value < noise_floor)
605 noise_floor = decayavg(noise_floor, value, attack);
606 else
607 noise_floor = decayavg(noise_floor, value, decay);
608 }
609 if (value > sig_avg) {
610 if (value > agc_peak)
611 agc_peak = decayavg(agc_peak, value, attack);
612 else
613 agc_peak = decayavg(agc_peak, value, decay);
614 }
615
616 float norm_noise = noise_floor / agc_peak;
617 float norm_sig = sig_avg / agc_peak;
618 siglevel = norm_sig;
619
620 if (agc_peak)
621 value /= agc_peak;
622 else
623 value = 0;
624
625 metric = 0.8 * metric;
626 if ((noise_floor > 1e-4) && (noise_floor < sig_avg))
627 metric += 0.2 * clamp(2.5 * (20*log10(sig_avg / noise_floor)) , 0, 100);
628
629 float diff = (norm_sig - norm_noise);
630
631 progdefaults.CWupper = norm_sig - 0.2 * diff;
632 progdefaults.CWlower = norm_noise + 0.7 * diff;
633
634 pipe[pipeptr] = value;
635 if (++pipeptr == pipesize) pipeptr = 0;
636
637 if (!progStatus.sqlonoff || metric > progStatus.sldrSquelchValue ) {
638 // Power detection using hysterisis detector
639 // upward trend means tone starting
640 if ((value > progdefaults.CWupper) && (cw_receive_state != RS_IN_TONE)) {
641 handle_event(CW_KEYDOWN_EVENT, sc);
642 }
643 // downward trend means tone stopping
644 if ((value < progdefaults.CWlower) && (cw_receive_state == RS_IN_TONE)) {
645 handle_event(CW_KEYUP_EVENT, sc);
646 }
647 }
648
649 if (handle_event(CW_QUERY_EVENT, sc) == CW_SUCCESS) {
650 update_syncscope();
651 synchscope = 100;
652 if (progdefaults.CWuseSOMdecoding) {
653 somc = find_winner(cw_buffer, two_dots);
654 if (!somc.empty())
655 for (size_t n = 0; n < somc.length(); n++)
656 put_rx_char(
657 somc[n],
658 somc[0] == '<' ? FTextBase::CTRL : FTextBase::RECV);
659 cw_ptr = 0;
660 memset(cw_buffer, 0, sizeof(cw_buffer));
661 } else {
662 for (size_t n = 0; n < sc.length(); n++)
663 put_rx_char(
664 sc[n],
665 sc[0] == '<' ? FTextBase::CTRL : FTextBase::RECV);
666 }
667 } else if (--synchscope == 0) {
668 synchscope = 25;
669 update_syncscope();
670 }
671
672 }
673
rx_FFTprocess(const double * buf,int len)674 void cw::rx_FFTprocess(const double *buf, int len)
675 {
676 cmplx z, *zp;
677 int n;
678
679 while (len-- > 0) {
680
681 z = cmplx ( *buf * cos(FFTphase), *buf * sin(FFTphase) );
682 FFTphase += TWOPI * frequency / samplerate;
683 if (FFTphase > TWOPI) FFTphase -= TWOPI;
684
685 buf++;
686
687 n = cw_FFT_filter->run(z, &zp); // n = 0 or filterlen/2
688
689 if (!n) continue;
690
691 for (int i = 0; i < n; i++) {
692 // update the basic sample counter used for morse timing
693 ++smpl_ctr;
694
695 if (smpl_ctr % DEC_RATIO) continue; // decimate by DEC_RATIO
696
697 // demodulate
698 FFTvalue = abs(zp[i]);
699 FFTvalue = bitfilter->run(FFTvalue);
700
701 decode_stream(FFTvalue);
702
703 } // for (i =0; i < n ...
704
705 } //while (len-- > 0)
706 }
707
708 static bool cwprocessing = false;
709
rx_process(const double * buf,int len)710 int cw::rx_process(const double *buf, int len)
711 {
712 if (use_paren != progdefaults.CW_use_paren ||
713 prosigns != progdefaults.CW_prosigns) {
714 use_paren = progdefaults.CW_use_paren;
715 prosigns = progdefaults.CW_prosigns;
716 morse.init();
717 }
718
719 if (cwprocessing)
720 return 0;
721
722 cwprocessing = true;
723
724 reset_rx_filter();
725
726 rx_FFTprocess(buf, len);
727
728 if (!clrcount--) clear_syncscope();
729
730 display_metric(metric);
731
732 if ( (dlgViewer->visible() || progStatus.show_channels )
733 && !bHighSpeed && !bHistory )
734 viewcw.rx_process(buf, len);
735
736 cwprocessing = false;
737
738 return 0;
739 }
740
741 // ----------------------------------------------------------------------
742
743 // Compare two timestamps, and return the difference between them in usecs.
744
usec_diff(unsigned int earlier,unsigned int later)745 inline int cw::usec_diff(unsigned int earlier, unsigned int later)
746 {
747 return (earlier >= later) ? 0 : (later - earlier);
748 }
749
750
751 //=======================================================================
752 // handle_event()
753 // high level cw decoder... gets called with keyup, keydown, reset and
754 // query commands.
755 // Keyup/down influences decoding logic.
756 // Reset starts everything out fresh.
757 // The query command returns CW_SUCCESS and the character that has
758 // been decoded (may be '*',' ' or [a-z,0-9] or a few others)
759 // If there is no data ready, CW_ERROR is returned.
760 //=======================================================================
761
handle_event(int cw_event,string & sc)762 int cw::handle_event(int cw_event, string &sc)
763 {
764 static int space_sent = true; // for word space logic
765 static int last_element = 0; // length of last dot/dash
766 int element_usec; // Time difference in usecs
767
768 switch (cw_event) {
769 case CW_RESET_EVENT:
770 sync_parameters();
771 cw_receive_state = RS_IDLE;
772 cw_rr_current = 0; // reset decoding pointer
773 cw_ptr = 0;
774 memset(cw_buffer, 0, sizeof(cw_buffer));
775 smpl_ctr = 0; // reset audio sample counter
776 rx_rep_buf.clear();
777 break;
778 case CW_KEYDOWN_EVENT:
779 // A receive tone start can only happen while we
780 // are idle, or in the middle of a character.
781 if (cw_receive_state == RS_IN_TONE)
782 return CW_ERROR;
783 // first tone in idle state reset audio sample counter
784 if (cw_receive_state == RS_IDLE) {
785 smpl_ctr = 0;
786 rx_rep_buf.clear();
787 cw_rr_current = 0;
788 cw_ptr = 0;
789 }
790 // save the timestamp
791 cw_rr_start_timestamp = smpl_ctr;
792 // Set state to indicate we are inside a tone.
793 old_cw_receive_state = cw_receive_state;
794 cw_receive_state = RS_IN_TONE;
795 return CW_ERROR;
796 break;
797 case CW_KEYUP_EVENT:
798 // The receive state is expected to be inside a tone.
799 if (cw_receive_state != RS_IN_TONE)
800 return CW_ERROR;
801 // Save the current timestamp
802 cw_rr_end_timestamp = smpl_ctr;
803 element_usec = usec_diff(cw_rr_start_timestamp, cw_rr_end_timestamp);
804
805 // make sure our timing values are up to date
806 sync_parameters();
807 // If the tone length is shorter than any noise cancelling
808 // threshold that has been set, then ignore this tone.
809 if (cw_noise_spike_threshold > 0
810 && element_usec < cw_noise_spike_threshold) {
811 cw_receive_state = RS_IDLE;
812 return CW_ERROR;
813 }
814
815 // Set up to track speed on dot-dash or dash-dot pairs for this test to work, we need a dot dash pair or a
816 // dash dot pair to validate timing from and force the speed tracking in the right direction. This method
817 // is fundamentally different than the method in the unix cw project. Great ideas come from staring at the
818 // screen long enough!. Its kind of simple really ... when you have no idea how fast or slow the cw is...
819 // the only way to get a threshold is by having both code elements and setting the threshold between them
820 // knowing that one is supposed to be 3 times longer than the other. with straight key code... this gets
821 // quite variable, but with most faster cw sent with electronic keyers, this is one relationship that is
822 // quite reliable. Lawrence Glaister (ve7it@shaw.ca)
823 if (last_element > 0) {
824 // check for dot dash sequence (current should be 3 x last)
825 if ((element_usec > 2 * last_element) &&
826 (element_usec < 4 * last_element)) {
827 update_tracking(last_element, element_usec);
828 }
829 // check for dash dot sequence (last should be 3 x current)
830 if ((last_element > 2 * element_usec) &&
831 (last_element < 4 * element_usec)) {
832 update_tracking(element_usec, last_element);
833 }
834 }
835 last_element = element_usec;
836 // ok... do we have a dit or a dah?
837 // a dot is anything shorter than 2 dot times
838 if (element_usec <= two_dots) {
839 rx_rep_buf += CW_DOT_REPRESENTATION;
840 // printf("%d dit ", last_element/1000); // print dot length
841 cw_buffer[cw_ptr++] = (float)last_element;
842 } else {
843 // a dash is anything longer than 2 dot times
844 rx_rep_buf += CW_DASH_REPRESENTATION;
845 cw_buffer[cw_ptr++] = (float)last_element;
846 }
847 // We just added a representation to the receive buffer.
848 // If it's full, then reset everything as it probably noise
849 if (rx_rep_buf.length() > MAX_MORSE_ELEMENTS) {
850 cw_receive_state = RS_IDLE;
851 cw_rr_current = 0; // reset decoding pointer
852 cw_ptr = 0;
853 smpl_ctr = 0; // reset audio sample counter
854 return CW_ERROR;
855 } else {
856 // zero terminate representation
857 // rx_rep_buf.clear();
858 cw_buffer[cw_ptr] = 0.0;
859 }
860 // All is well. Move to the more normal after-tone state.
861 cw_receive_state = RS_AFTER_TONE;
862 return CW_ERROR;
863 break;
864 case CW_QUERY_EVENT:
865 // this should be called quite often (faster than inter-character gap) It looks after timing
866 // key up intervals and determining when a character, a word space, or an error char '*' should be returned.
867 // CW_SUCCESS is returned when there is a printable character. Nothing to do if we are in a tone
868 if (cw_receive_state == RS_IN_TONE)
869 return CW_ERROR;
870 // compute length of silence so far
871 sync_parameters();
872 element_usec = usec_diff(cw_rr_end_timestamp, smpl_ctr);
873 // SHORT time since keyup... nothing to do yet
874 if (element_usec < (2 * cw_receive_dot_length))
875 return CW_ERROR;
876 // MEDIUM time since keyup... check for character space
877 // one shot through this code via receive state logic
878 // FARNSWOTH MOD HERE -->
879 if (element_usec >= (2 * cw_receive_dot_length) &&
880 element_usec <= (4 * cw_receive_dot_length) &&
881 cw_receive_state == RS_AFTER_TONE) {
882 // Look up the representation
883 sc = morse.rx_lookup(rx_rep_buf);
884 if (sc.empty()) {
885 // invalid decode... let user see error
886 sc = (progdefaults.CW_noise == '*' ? "*" :
887 progdefaults.CW_noise == '_' ? "_" :
888 progdefaults.CW_noise == ' ' ? " " : "");
889
890 }
891 rx_rep_buf.clear();
892 cw_receive_state = RS_IDLE;
893 cw_rr_current = 0; // reset decoding pointer
894 space_sent = false;
895 cw_ptr = 0;
896
897 return CW_SUCCESS;
898 }
899 // LONG time since keyup... check for a word space
900 // FARNSWOTH MOD HERE -->
901 if ((element_usec > (4 * cw_receive_dot_length)) && !space_sent) {
902 sc = " ";
903 space_sent = true;
904 return CW_SUCCESS;
905 }
906 // should never get here... catch all
907 return CW_ERROR;
908 break;
909 }
910 // should never get here... catch all
911 return CW_ERROR;
912 }
913
914 //===========================================================================
915 // cw transmit routines
916 // Define the amplitude envelop for key down events (32 samples long)
917 // this is 1/2 cycle of a raised cosine
918 //===========================================================================
919
920 double keyshape[CWKNUM];
921 double QSKkeyshape[CWKNUM];
922
create_edges()923 void cw::create_edges()
924 {
925 for (int i = 0; i < CWKNUM; i++) keyshape[i] = 1.0;
926
927 switch (QSKshape) {
928 case 1: // blackman
929 knum = (int)(risetime * CW_SAMPLERATE / 1000);
930 if (knum >= symbollen) knum = symbollen;
931 for (int i = 0; i < knum; i++)
932 keyshape[i] = (0.42 - 0.50 * cos(M_PI * i/ knum) + 0.08 * cos(2 * M_PI * i / knum));
933 break;
934 case 0: // hanning
935 default:
936 knum = (int)(risetime * CW_SAMPLERATE / 1000);
937 if (knum >= symbollen) knum = symbollen;
938 for (int i = 0; i < knum; i++)
939 keyshape[i] = 0.5 * (1.0 - cos (M_PI * i / knum));
940 }
941
942 for (int i = 0; i < CWKNUM; i++) QSKkeyshape[i] = 1.0;
943
944 switch (QSKshape) {
945 case 1: // blackman
946 qnum = (int)(progdefaults.QSKrisetime * CW_SAMPLERATE / 1000);
947 if (qnum >= symbollen) qnum = symbollen;
948 for (int i = 0; i < qnum; i++)
949 QSKkeyshape[i] = (0.42 - 0.50 * cos(M_PI * i/ qnum) + 0.08 * cos(2 * M_PI * i / qnum));
950 break;
951 case 0: // hanning
952 default:
953 qnum = (int)(progdefaults.QSKrisetime * CW_SAMPLERATE / 1000);
954 if (qnum >= symbollen) qnum = symbollen;
955 for (int i = 0; i < qnum; i++)
956 QSKkeyshape[i] = 0.5 * (1.0 - cos (M_PI * i / qnum));
957 }
958 }
959
nco(double freq)960 inline double cw::nco(double freq)
961 {
962 phaseacc += 2.0 * M_PI * freq / samplerate;
963 if (phaseacc > TWOPI) phaseacc -= TWOPI;
964 return sin(phaseacc);
965 }
966
qsknco()967 inline double cw::qsknco()
968 {
969 double amp;
970 amp = sin(qskphase);
971 qskphase += TWOPI * progdefaults.QSKfrequency / samplerate;
972 if (qskphase > TWOPI) qskphase -= TWOPI;
973 return amp;
974 }
975
976 //=====================================================================
977 // send_symbol()
978 // Sends a part of a morse character (one dot duration) of either
979 // sound at the correct freq or silence. Rise and fall time is controlled
980 // with a raised cosine shape.
981 //
982 // Left channel contains the shaped A2 CW waveform
983 // Right channel contains a square wave signal that is used
984 // to trigger a qsk switch. Right channel has pre and post timings for
985 // proper switching of the qsk switch before and after the A2 element.
986 // If the Pre + Post timing exceeds the interelement spacing then the
987 // Pre and / or Post is only applied at the beginning and end of the
988 // character.
989 //=======================================================================
990
991 bool first_char = true;
992
993 enum {START, FIRST, MID, LAST, SPACE};
994
send_symbol(int bit,int len,int state)995 void cw::send_symbol(int bit, int len, int state)
996 {
997 double qsk_amp = progdefaults.QSK ? progdefaults.QSKamp : 0.0;
998
999 sync_transmit_parameters();
1000 acc_symbols += len;
1001
1002 memset(outbuf, 0, OUTBUFSIZE*sizeof(*outbuf));
1003 memset(qskbuf, 0, OUTBUFSIZE*sizeof(*qskbuf));
1004
1005 if (bit == 1) { // keydown
1006 tx_frequency = get_txfreq_woffset();
1007 if (CW_KEYLINE_isopen ||
1008 progdefaults.CW_KEYLINE_on_cat_port ||
1009 progdefaults.CW_KEYLINE_on_ptt_port)
1010 tx_frequency = progdefaults.CWsweetspot;
1011 for (int n = 0; n < len; n++) {
1012 outbuf[n] = nco(tx_frequency);
1013 if (n < knum) outbuf[n] *= keyshape[n];
1014 if (len - n < knum) outbuf[n] *= keyshape[len - n];
1015 qskbuf[n] = qsk_amp * qsknco();
1016 }
1017 } else { // keyup
1018 for (int n = 0; n < len; n++) {
1019 outbuf[n] = 0;
1020 if (progdefaults.QSK) {
1021 qskbuf[n] = 0;
1022 if (state == START || state == FIRST) {
1023 qskbuf[n] = 0;
1024 if (n > len - kpre) {
1025 qskbuf[n] = qsk_amp * qsknco();
1026 if (n < len - kpre + qnum)
1027 qskbuf[n] *= QSKkeyshape[n - (len - kpre)];
1028 }
1029 } else if (state == MID) {
1030 qskbuf[n] = qsk_amp * qsknco();
1031 if (len > kpre + kpost) {
1032 if (n < kpost)
1033 qskbuf[n] *= QSKkeyshape[kpost - n];
1034 else if (n > len - kpre)
1035 qskbuf[n] *= QSKkeyshape[n - (len - kpre)];
1036 else qskbuf[n] = 0;
1037 }
1038 } else if (state == LAST) {
1039 qskbuf[n] = qsk_amp * qsknco();
1040 if (n > kpost - qnum)
1041 qskbuf[n] *= QSKkeyshape[kpost - n];
1042 if (n >= kpost) qskbuf[n] = 0;
1043 } else { // state == SPACE
1044 qskbuf[n] = 0;
1045 }
1046 }
1047 }
1048 }
1049
1050 if (progdefaults.QSK)
1051 ModulateStereo(outbuf, qskbuf, len);
1052 else
1053 ModulateXmtr(outbuf, len);
1054
1055 }
1056
1057 //=====================================================================
1058 // send_ch()
1059 // sends a morse character and the space afterwards
1060 //=======================================================================
1061
send_ch(int ch)1062 void cw::send_ch(int ch)
1063 {
1064 string code;
1065
1066 float kfactor = CW_SAMPLERATE / 1000.0;
1067 float tc = 1200.0 / progdefaults.CWspeed;
1068 float ta = 0.0;
1069 float tch = 3 * tc, twd = 4 * tc;
1070
1071 if (progdefaults.CWusefarnsworth && (progdefaults.CWspeed > progdefaults.CWfarnsworth)) {
1072 ta = 60000.0 / progdefaults.CWfarnsworth - 37200.0 / progdefaults.CWspeed;
1073 tch = 3 * ta / 19;
1074 twd = 4 * ta / 19;
1075 }
1076 tc *= kfactor;
1077 tch *= kfactor;
1078 twd *= kfactor;
1079
1080 sync_parameters();
1081
1082 if (progdefaults.CWpre < progdefaults.QSKrisetime)
1083 kpre = progdefaults.QSKrisetime * kfactor;
1084 else
1085 kpre = progdefaults.CWpre * kfactor;
1086
1087 if (progdefaults.CWpost < progdefaults.QSKrisetime)
1088 kpost = progdefaults.QSKrisetime * kfactor;
1089 else
1090 kpost = progdefaults.CWpost * kfactor;
1091
1092 if ((ch == ' ') || (ch == '\n')) {
1093 send_symbol(0,
1094 twd,
1095 SPACE);
1096 put_echo_char(progdefaults.rx_lowercase ? tolower(ch) : ch);
1097 return;
1098 }
1099
1100 code = morse.tx_lookup(ch);
1101 if (!code.length()) {
1102 return;
1103 }
1104
1105 float w = (progdefaults.CWdash2dot + 1) / (progdefaults.CWdash2dot -1);
1106
1107 int elements = code.length();
1108
1109 if (kpre)
1110 send_symbol(
1111 0,
1112 (first_char ? kpre :
1113 (kpre < 3 * tc - kpost) ? kpre :
1114 3 * tc ),
1115 (first_char ? START : FIRST));
1116
1117 for (int n = 0; n < elements; n++) {
1118 send_symbol(1,
1119 (code[n] == '-' ? (w + 1) : (w - 1)) * symbollen,
1120 MID);
1121 send_symbol(0,
1122 ((n < elements - 1) ? tc :
1123 (kpost + kpre < 3 * tc) ? tch - kpre:
1124 tch),
1125 (n < elements - 1 ? MID : LAST) );
1126 }
1127
1128 if (ch != -1) {
1129 string prtstr = morse.tx_print();
1130 for (size_t n = 0; n < prtstr.length(); n++)
1131 put_echo_char(
1132 prtstr[n],
1133 prtstr[0] == '<' ? FTextBase::CTRL : FTextBase::XMIT);
1134 }
1135 }
1136
1137 //=====================================================================
1138 // cw_txprocess()
1139 // Read characters from screen and send them out the sound card.
1140 // This is called repeatedly from a thread during tx.
1141 //=======================================================================
tx_process()1142 int cw::tx_process()
1143 {
1144 int c = get_tx_char();
1145
1146 if (c == GET_TX_CHAR_NODATA) {
1147 if (stopflag) {
1148 stopflag = false;
1149 put_echo_char('\n');
1150 first_char = true;
1151 return -1;
1152 }
1153 Fl::awake();
1154 MilliSleep(50);
1155 return 0;
1156 }
1157
1158 if (progdefaults.use_FLRIGkeying) {
1159 if (c == GET_TX_CHAR_ETX || stopflag) {
1160 stopflag = false;
1161 put_echo_char('\n');
1162 return -1;
1163 }
1164 flrig_cwio_send(c);
1165 put_echo_char(c);
1166 return 0;
1167 }
1168
1169 if (progStatus.WK_online) {
1170 if (c == GET_TX_CHAR_ETX || stopflag) {
1171 stopflag = false;
1172 put_echo_char('\n');
1173 return -1;
1174 }
1175 if (WK_send_char(c)){
1176 put_echo_char('\n');
1177 return -1; // WinKeyer problem
1178 }
1179 return 0;
1180 }
1181
1182 if (use_nanoIO) {
1183 if (c == GET_TX_CHAR_ETX || stopflag) {
1184 stopflag = false;
1185 put_echo_char('\n');
1186 return -1;
1187 }
1188 nano_send_char(c);
1189 put_echo_char(c);
1190 return 0;
1191 }
1192
1193 if (progdefaults.use_ELCTkeying || progdefaults.use_KNWDkeying) {
1194 if (c == GET_TX_CHAR_ETX || stopflag) {
1195 stopflag = false;
1196 put_echo_char('\n');
1197 return -1;
1198 }
1199 KYkeyer_send_char(c);
1200 put_echo_char(c);
1201 return 0;
1202 }
1203
1204 if (progdefaults.use_ICOMkeying) {
1205 if (c == GET_TX_CHAR_ETX || stopflag) {
1206 stopflag = false;
1207 put_echo_char('\n');
1208 return -1;
1209 }
1210 ICOMkeyer_send_char(c);
1211 put_echo_char(c);
1212 return 0;
1213 }
1214
1215 if (progdefaults.use_YAESUkeying) {
1216 if (c == GET_TX_CHAR_ETX || stopflag) {
1217 stopflag = false;
1218 put_echo_char('\n');
1219 return -1;
1220 }
1221 FTkeyer_send_char(c);
1222 put_echo_char(c);
1223 return 0;
1224 }
1225
1226 if (c == GET_TX_CHAR_ETX || stopflag) {
1227 stopflag = false;
1228 put_echo_char('\n');
1229 first_char = true;
1230 return -1;
1231 }
1232
1233 acc_symbols = 0;
1234
1235 if (CW_KEYLINE_isopen ||
1236 progdefaults.CW_KEYLINE_on_cat_port ||
1237 progdefaults.CW_KEYLINE_on_ptt_port)
1238 send_CW(c);
1239 // else {
1240 send_ch(c);
1241 first_char = false;
1242 // }
1243 char_samples = acc_symbols;
1244
1245 return 0;
1246 }
1247
incWPM()1248 void cw::incWPM()
1249 {
1250
1251 if (usedefaultWPM) return;
1252 if (progdefaults.CWspeed < progdefaults.CWupperlimit) {
1253 progdefaults.CWspeed++;
1254 sync_parameters();
1255 set_CWwpm();
1256 update_Status();
1257 }
1258 }
1259
decWPM()1260 void cw::decWPM()
1261 {
1262
1263 if (usedefaultWPM) return;
1264 if (progdefaults.CWspeed > progdefaults.CWlowerlimit) {
1265 progdefaults.CWspeed--;
1266 set_CWwpm();
1267 sync_parameters();
1268 update_Status();
1269 }
1270 }
1271
toggleWPM()1272 void cw::toggleWPM()
1273 {
1274 usedefaultWPM = !usedefaultWPM;
1275 if (usedefaultWPM) {
1276 wpm = progdefaults.CWspeed;
1277 progdefaults.CWspeed = progdefaults.defCWspeed;
1278 } else {
1279 progdefaults.CWspeed = wpm;
1280 }
1281 sync_parameters();
1282 update_Status();
1283 }
1284
1285 // ---------------------------------------------------------------------
1286 // CW output on DTR/RTS signal lines
1287 //----------------------------------------------------------------------
1288
1289 Cserial CW_KEYLINE_serial;
1290 bool CW_KEYLINE_isopen = false;
1291
open_CW_KEYLINE()1292 int open_CW_KEYLINE()
1293 {
1294 CW_KEYLINE_serial.Device(progdefaults.CW_KEYLINE_serial_port_name);
1295 CW_KEYLINE_serial.Baud(progdefaults.BaudRate(9));
1296 CW_KEYLINE_serial.RTS(false);
1297 CW_KEYLINE_serial.DTR(false);
1298 CW_KEYLINE_serial.RTSptt(false);
1299 CW_KEYLINE_serial.DTRptt(false);
1300 CW_KEYLINE_serial.RestoreTIO(true);
1301 CW_KEYLINE_serial.RTSCTS(false);
1302 CW_KEYLINE_serial.Stopbits(1);
1303
1304 LOG_VERBOSE("\n\
1305 CW Keyline Serial port parameters:\n\
1306 device : %s\n\
1307 baudrate : %d\n\
1308 stopbits : %d\n\
1309 initial rts: %+d\n\
1310 initial dtr: %+d\n\
1311 restore tio: %c\n\
1312 flowcontrol: %c\n",
1313 CW_KEYLINE_serial.Device().c_str(),
1314 CW_KEYLINE_serial.Baud(),
1315 CW_KEYLINE_serial.Stopbits(),
1316 (CW_KEYLINE_serial.RTS() ? +12 : -12),
1317 (CW_KEYLINE_serial.DTR() ? +12 : -12),
1318 (CW_KEYLINE_serial.RestoreTIO() ? 'T' : 'F'),
1319 (CW_KEYLINE_serial.RTSCTS() ? 'T' : 'F')
1320 );
1321
1322 if (CW_KEYLINE_serial.OpenPort() == false) {
1323 LOG_ERROR("Cannot open serial port %s", CW_KEYLINE_serial.Device().c_str());
1324 CW_KEYLINE_isopen = false;
1325 return 0;
1326 }
1327 CW_KEYLINE_isopen = true;
1328 return 1;
1329 }
1330
close_CW_KEYLINE()1331 void close_CW_KEYLINE()
1332 {
1333 CW_KEYLINE_serial.ClosePort();
1334 CW_KEYLINE_isopen = false;
1335 }
1336
1337 //----------------------------------------------------------------------
1338 #include <queue>
1339
1340 static pthread_t cwio_pthread;
1341 static pthread_cond_t cwio_cond;
1342 static pthread_mutex_t cwio_mutex = PTHREAD_MUTEX_INITIALIZER;
1343 static pthread_mutex_t fifo_mutex = PTHREAD_MUTEX_INITIALIZER;
1344 pthread_mutex_t cwio_ptt_mutex = PTHREAD_MUTEX_INITIALIZER;
1345
1346 static bool cwio_thread_running = false;
1347 static bool cwio_terminate_flag = false;
1348 static bool cwio_calibrate_flag = false;
1349
1350 //----------------------------------------------------------------------
1351
1352 static int cwio_ch;
1353 static cMorse *cwio_morse = 0;
1354 static queue<int> fifo;
1355 static std::string cwio_prosigns;
1356
1357 //----------------------------------------------------------------------
1358 // CW output using flrig cwio calls
1359 //----------------------------------------------------------------------
1360 static char lastcwiochar = 0;
flrig_cwio_send(char c)1361 void flrig_cwio_send(char c)
1362 {
1363 if (cwio_morse == 0) {
1364 cwio_morse = new cMorse;
1365 cwio_morse->init();
1366 }
1367
1368 if (c == '[') {
1369 flrig_cwio_ptt(1);
1370 return;
1371 }
1372 if (c == ']') {
1373 flrig_cwio_ptt(0);
1374 return;
1375 }
1376 std::string s = " ";
1377 s[0] = c;
1378 flrig_cwio_send_text(s);
1379
1380 int tc = 1200 / progdefaults.CWspeed;
1381 if (progdefaults.CWusefarnsworth && (progdefaults.CWspeed > progdefaults.CWfarnsworth))
1382 tc = 1200 / progdefaults.CWfarnsworth;
1383
1384 if (c == ' ') {
1385 if (lastcwiochar == ' ')
1386 tc *= 7;
1387 else
1388 tc *= 5;
1389 } else
1390 tc *= (cwio_morse->tx_length(c));
1391 lastcwiochar = c;
1392 MilliSleep(tc);
1393 }
1394
1395 //----------------------------------------------------------------------
1396
cwio_key(int on)1397 void cwio_key(int on)
1398 {
1399 if (CW_KEYLINE_isopen ||
1400 progdefaults.CW_KEYLINE_on_cat_port ||
1401 progdefaults.CW_KEYLINE_on_ptt_port) {
1402 Cserial *ser = &CW_KEYLINE_serial;
1403 if (progdefaults.CW_KEYLINE_on_cat_port)
1404 ser = &rigio;
1405 else if (progdefaults.CW_KEYLINE_on_ptt_port)
1406 ser = &push2talk->serPort;
1407 switch (progdefaults.CW_KEYLINE) {
1408 case 0: break;
1409 case 1: ser->SetRTS(on); break;
1410 case 2: ser->SetDTR(on); break;
1411 }
1412 }
1413 }
1414
cwio_ptt(int on)1415 void cwio_ptt(int on)
1416 {
1417 if (CW_KEYLINE_isopen ||
1418 progdefaults.CW_KEYLINE_on_cat_port ||
1419 progdefaults.CW_KEYLINE_on_ptt_port) {
1420 Cserial *ser = &CW_KEYLINE_serial;
1421 if (progdefaults.CW_KEYLINE_on_cat_port)
1422 ser = &rigio;
1423 else if (progdefaults.CW_KEYLINE_on_ptt_port)
1424 ser = &push2talk->serPort;
1425 switch (progdefaults.PTT_KEYLINE) {
1426 case 0: break;
1427 case 1: ser->SetRTS(on); break;
1428 case 2: ser->SetDTR(on); break;
1429 }
1430 }
1431 }
1432
1433 #define cwio_bit(bit, len) {\
1434 switch (progdefaults.CW_KEYLINE) {\
1435 case 0: break;\
1436 case 1: ser->SetRTS(bit); break;\
1437 case 2: ser->SetDTR(bit); break;\
1438 }\
1439 MilliSleep(len);}
1440
send_cwio(int c)1441 void send_cwio(int c)
1442 {
1443 if (c == GET_TX_CHAR_NODATA || c == 0x0d) {
1444 return;
1445 }
1446
1447 float tc = 1200.0 / progdefaults.CWspeed;
1448 if (tc <= 0) tc = 1;
1449 float ta = 0.0;
1450 float tch = 3 * tc, twd = 4 * tc;
1451
1452 Cserial *ser = &CW_KEYLINE_serial;
1453 if (progdefaults.CW_KEYLINE_on_cat_port)
1454 ser = &rigio;
1455 else if (progdefaults.CW_KEYLINE_on_ptt_port)
1456 ser = &push2talk->serPort;
1457
1458 if (progdefaults.CWusefarnsworth && (progdefaults.CWspeed > progdefaults.CWfarnsworth)) {
1459 ta = 60000.0 / progdefaults.CWfarnsworth - 37200 / progdefaults.CWspeed;
1460 tch = 3 * ta / 19;
1461 twd = 4 * ta / 19;
1462 }
1463
1464 if (progdefaults.cwio_comp && progdefaults.cwio_comp < tc) {
1465 tc -= progdefaults.cwio_comp;
1466 tch -= progdefaults.cwio_comp;
1467 twd -= progdefaults.cwio_comp;
1468 }
1469
1470 if (c == 0x0a) c = ' ';
1471
1472 if (c == ' ') {
1473 cwio_bit(0, twd);
1474 return;
1475 }
1476
1477 string code;
1478 code = cwio_morse->tx_lookup(c);
1479 if (!code.length()) {
1480 return;
1481 }
1482
1483 guard_lock lk(&cwio_ptt_mutex);
1484
1485 for (size_t n = 0; n < code.length(); n++) {
1486 if (code[n] == '.') {
1487 cwio_bit(1, tc);
1488 } else {
1489 cwio_bit(1, 3*tc);
1490 }
1491 if (n < code.length() -1) {
1492 cwio_bit(0, tc);
1493 } else {
1494 cwio_bit(0, tch);
1495 }
1496 }
1497
1498 }
1499
1500 unsigned long start_time = 0L;
1501 unsigned long end_time = 0L;
1502 int testwpm = 20;
1503 int testwords = 10;
1504
cwio_calibrate_finished(void *)1505 void cwio_calibrate_finished(void *)
1506 {
1507 double ratio = (1200.0 * 50.0 * testwords / progdefaults.CWspeed) / (end_time - start_time);
1508 int comp = round(testwpm * (1.0 - ratio));
1509 progdefaults.cwio_comp = comp;
1510 cnt_cwio_comp->value(comp);
1511 btn_cw_dtr_calibrate->value(0);
1512
1513 LOG_INFO("\n\
1514 xmt %d words at %.0f wpm : %0.3f secs\n\
1515 compensation ratio: %f\n\
1516 compensation (msec): %d",
1517 testwords,
1518 progdefaults.CWspeed,
1519 (end_time - start_time) / 1000.0,
1520 ratio,
1521 comp);
1522 }
1523
cwio_calibrate()1524 void cwio_calibrate()
1525 {
1526 std::string paris = "PARIS ";
1527 bool farnsworth = progdefaults.CWusefarnsworth;
1528 progdefaults.CWusefarnsworth = false;
1529 int comp = progdefaults.cwio_comp;
1530 progdefaults.cwio_comp = 0;
1531
1532 guard_lock lk(&fifo_mutex);
1533
1534 start_time = zmsec();
1535 for (int i = 0; i < testwords; i++)
1536 for (size_t n = 0; n < paris.length(); n++)
1537 send_cwio(paris[n]);
1538 end_time = zmsec();
1539
1540 progdefaults.CWusefarnsworth = farnsworth;
1541 progdefaults.cwio_comp = comp;
1542
1543 Fl::awake(cwio_calibrate_finished);
1544 }
1545
cwio_loop(void * args)1546 static void * cwio_loop(void *args)
1547 {
1548 SET_THREAD_ID(CWIO_TID);
1549
1550 cwio_thread_running = true;
1551 cwio_terminate_flag = false;
1552
1553 while(1) {
1554 pthread_mutex_lock(&cwio_mutex);
1555 pthread_cond_wait(&cwio_cond, &cwio_mutex);
1556 pthread_mutex_unlock(&cwio_mutex);
1557
1558 if (cwio_terminate_flag)
1559 break;
1560 if (cwio_calibrate_flag) {
1561 cwio_calibrate();
1562 cwio_calibrate_flag = false;
1563 }
1564 while (!fifo.empty()) {
1565 {
1566 guard_lock lk(&fifo_mutex);
1567 cwio_ch = fifo.front();
1568 fifo.pop();
1569 }
1570 send_cwio(cwio_ch);
1571 }
1572 }
1573 return (void *)0;
1574 }
1575
calibrate_cwio()1576 void calibrate_cwio()
1577 {
1578 if (!cwio_thread_running)
1579 start_cwio_thread();
1580
1581 if (cwio_morse == 0) {
1582 cwio_morse = new cMorse;
1583 cwio_morse->init();
1584 }
1585
1586 cwio_calibrate_flag = true;
1587 pthread_cond_signal(&cwio_cond);
1588 }
1589
stop_cwio_thread(void)1590 void stop_cwio_thread(void)
1591 {
1592 if(!cwio_thread_running) return;
1593
1594 cwio_terminate_flag = true;
1595 pthread_cond_signal(&cwio_cond);
1596
1597 MilliSleep(10);
1598
1599 pthread_join(cwio_pthread, NULL);
1600
1601 pthread_mutex_destroy(&cwio_mutex);
1602 pthread_cond_destroy(&cwio_cond);
1603
1604 memset((void *) &cwio_pthread, 0, sizeof(cwio_pthread));
1605 memset((void *) &cwio_mutex, 0, sizeof(cwio_mutex));
1606
1607 cwio_thread_running = false;
1608 cwio_terminate_flag = false;
1609
1610 delete cwio_morse;
1611 cwio_morse = 0;
1612 }
1613
start_cwio_thread(void)1614 void start_cwio_thread(void)
1615 {
1616 if (cwio_thread_running) return;
1617
1618 memset((void *) &cwio_pthread, 0, sizeof(cwio_pthread));
1619 memset((void *) &cwio_mutex, 0, sizeof(cwio_mutex));
1620 memset((void *) &cwio_cond, 0, sizeof(cwio_cond));
1621
1622 if(pthread_cond_init(&cwio_cond, NULL)) {
1623 LOG_ERROR("Alert thread create fail (pthread_cond_init)");
1624 return;
1625 }
1626
1627 if(pthread_mutex_init(&cwio_mutex, NULL)) {
1628 LOG_ERROR("AUDIO_ALERT thread create fail (pthread_mutex_init)");
1629 return;
1630 }
1631
1632 if (pthread_create(&cwio_pthread, NULL, cwio_loop, NULL) < 0) {
1633 pthread_mutex_destroy(&cwio_mutex);
1634 LOG_ERROR("AUDIO_ALERT thread create fail (pthread_create)");
1635 }
1636
1637 LOG_VERBOSE("started audio cwio thread");
1638
1639 MilliSleep(10); // Give the CPU time to set 'cwio_thread_running'
1640 }
1641
send_CW(int c)1642 void cw::send_CW(int c)
1643 {
1644 if (!cwio_thread_running)
1645 start_cwio_thread();
1646
1647 if (cwio_morse == 0) {
1648 cwio_morse = new cMorse;
1649 cwio_morse->init();
1650 }
1651
1652 if (cwio_prosigns != progdefaults.CW_prosigns) {
1653 cwio_prosigns = progdefaults.CW_prosigns;
1654 cwio_morse->init();
1655 }
1656
1657 guard_lock lk(&fifo_mutex);
1658 fifo.push(c);
1659
1660 pthread_cond_signal(&cwio_cond);
1661
1662 }
1663
1664 unsigned long CAT_start_time = 0L;
1665 unsigned long CAT_end_time = 0L;
1666
CAT_keying_calibrate_finished(void *)1667 void CAT_keying_calibrate_finished(void *)
1668 {
1669 int comp = (CAT_end_time - CAT_start_time - 60000);
1670
1671 progdefaults.CATkeying_compensation = comp;
1672
1673 out_CATkeying_compensation->value(comp / 1000.0);
1674
1675 char info[1000];
1676 snprintf(info, sizeof(info),
1677 "Speed test: %.0f wpm : %0.2f secs",
1678 progdefaults.CWspeed,
1679 (CAT_end_time - CAT_start_time) / 1000.0);
1680 LOG_INFO("\n%s", info);
1681
1682 }
1683
1684 static pthread_t CW_keying_pthread;
1685 bool CW_CAT_thread_running = false;
1686
do_CAT_keying_calibrate(void * args)1687 void *do_CAT_keying_calibrate(void *args)
1688 {
1689 CW_CAT_thread_running = true;
1690
1691 if (progdefaults.use_KNWDkeying || progdefaults.use_ELCTkeying)
1692 set_KYkeyer();
1693 else if (progdefaults.use_ICOMkeying)
1694 set_ICOMkeyer();
1695 else if (progdefaults.use_YAESUkeying)
1696 set_FTkeyer();
1697
1698 std::string paris = "PARIS ";
1699 bool farnsworth = progdefaults.CWusefarnsworth;
1700 progdefaults.CWusefarnsworth = false;
1701 progdefaults.CATkeying_compensation = 0;
1702
1703 CAT_start_time = zmsec();
1704 for (int i = 0; i < progdefaults.CWspeed; i++) {
1705 for (size_t n = 0; n < paris.length(); n++) {
1706 if (progdefaults.use_KNWDkeying || progdefaults.use_ELCTkeying)
1707 KYkeyer_send_char(paris[n]);
1708 else if (progdefaults.use_ICOMkeying)
1709 ICOMkeyer_send_char(paris[n]);
1710 else if (progdefaults.use_YAESUkeying)
1711 FTkeyer_send_char(paris[n]);
1712 }
1713 }
1714 CAT_end_time = zmsec();
1715
1716 progdefaults.CWusefarnsworth = farnsworth;
1717
1718 Fl::awake(CAT_keying_calibrate_finished);
1719 CW_CAT_thread_running = false;
1720 return NULL;
1721 }
1722
CAT_keying_calibrate()1723 void CAT_keying_calibrate()
1724 {
1725 if (CW_CAT_thread_running) return;
1726
1727 if (pthread_create(&CW_keying_pthread, NULL, do_CAT_keying_calibrate, NULL) < 0) {
1728 LOG_ERROR("CW CAT calibration thread create failed");
1729 return;
1730 }
1731
1732 LOG_VERBOSE("started CW CAT calibration thread");
1733
1734 MilliSleep(10);
1735
1736 }
1737
CAT_keying_test_finished(void *)1738 void CAT_keying_test_finished(void *)
1739 {
1740 int comp = (CAT_end_time - CAT_start_time - 60000);
1741 out_CATkeying_test_result->value(comp / 1000.0);
1742 }
1743
do_CAT_keying_test(void * args)1744 void *do_CAT_keying_test(void *args)
1745 {
1746 CW_CAT_thread_running = true;
1747
1748 if (progdefaults.use_KNWDkeying || progdefaults.use_ELCTkeying)
1749 set_KYkeyer();
1750 else if (progdefaults.use_ICOMkeying)
1751 set_ICOMkeyer();
1752 else if (progdefaults.use_YAESUkeying)
1753 set_FTkeyer();
1754
1755 std::string paris = "PARIS ";
1756 bool farnsworth = progdefaults.CWusefarnsworth;
1757 progdefaults.CWusefarnsworth = false;
1758
1759 CAT_start_time = zmsec();
1760 for (int i = 0; i < progdefaults.CWspeed; i++) {
1761 for (size_t n = 0; n < paris.length(); n++) {
1762 if (progdefaults.use_KNWDkeying || progdefaults.use_ELCTkeying)
1763 KYkeyer_send_char(paris[n]);
1764 else if (progdefaults.use_ICOMkeying)
1765 ICOMkeyer_send_char(paris[n]);
1766 else if (progdefaults.use_YAESUkeying)
1767 FTkeyer_send_char(paris[n]);
1768 }
1769 }
1770 CAT_end_time = zmsec();
1771
1772 progdefaults.CWusefarnsworth = farnsworth;
1773
1774 Fl::awake(CAT_keying_test_finished);
1775 CW_CAT_thread_running = false;
1776 return NULL;
1777 }
1778
CAT_keying_test()1779 void CAT_keying_test()
1780 {
1781 if (CW_CAT_thread_running) return;
1782
1783 if (pthread_create(&CW_keying_pthread, NULL, do_CAT_keying_test, NULL) < 0) {
1784 LOG_ERROR("CW CAT calibration thread create failed");
1785 return;
1786 }
1787
1788 LOG_VERBOSE("started CW CAT calibration thread");
1789
1790 MilliSleep(10);
1791
1792 }
1793