1 /*
2 * Tlf - contest logging program for amateur radio operators
3 * Copyright (C) 2001-2005 Rein Couperus <pa0r@eudxf.org>
4 * 2009-2016 Thomas Beierlein <tb@forth-ev.de>
5 * 2013 Ervin Hegedüs - HA2OS <airween@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 /* ------------------------------------------------------------
23 * Callinput handles the call input field
24 *
25 *--------------------------------------------------------------*/
26
27
28 #include <ctype.h>
29 #include <fcntl.h>
30 #include <math.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include "addspot.h"
36 #include "autocq.h"
37 #include "bandmap.h"
38 #include "calledit.h"
39 #include "callinput.h"
40 #include "changefreq.h"
41 #include "changepars.h"
42 #include "clear_display.h"
43 #include "cleanup.h"
44 #include "cw_utils.h"
45 #include "edit_last.h"
46 #include "err_utils.h"
47 #include "deleteqso.h"
48 #include "getctydata.h"
49 #include "gettxinfo.h"
50 #include "grabspot.h"
51 #include "ignore_unused.h"
52 #include "keystroke_names.h"
53 #include "lancode.h"
54 #include "muf.h"
55 #include "netkeyer.h"
56 #include "nicebox.h" // Includes curses.h
57 #include "note.h"
58 #include "prevqso.h"
59 #include "printcall.h"
60 #include "qtcvars.h" // Includes globalvars.h
61 #include "qtcwin.h"
62 #include "rtty.h"
63 #include "searchlog.h" // Includes glib.h
64 #include "sendbuf.h"
65 #include "sendspcall.h"
66 #include "show_help.h"
67 #include "showinfo.h"
68 #include "showpxmap.h"
69 #include "speedupndown.h"
70 #include "splitscreen.h"
71 #include "stoptx.h"
72 #include "time_update.h"
73 #include "trx_memory.h"
74 #include "ui_utils.h"
75 #include "writeparas.h"
76 #include "showzones.h"
77 #include "bands.h"
78 #include "fldigixmlrpc.h"
79
80 #define TUNE_UP 6 /* tune up for 6 s (no more than 10) */
81
82 typedef enum { STORE_OR_POP, POP, SWAP } memory_op_t;
83
84 void send_bandswitch(freq_t freq);
85 int autosend(void);
86 int plain_number(char *str);
87 void handle_bandswitch(int direction);
88 void handle_memory_operation(memory_op_t op);
89
90 extern int no_arrows;
91 extern char hiscall[];
92 extern int bandinx;
93 extern char band[NBANDS][4];
94 extern int contest;
95 extern int dxped;
96 extern freq_t freq;
97 extern int trx_control;
98 extern freq_t bandfrequency[];
99
100
101 /** callsign input loop
102 *
103 * \return code of last typed character */
callinput(void)104 int callinput(void) {
105
106 extern int itumult;
107 extern int wazmult;
108 extern int isdupe; // LZ3NY auto-b4 patch
109 extern int cwstart;
110 extern int early_started;
111 extern char hiscall_sent[];
112 extern char comment[];
113 extern cqmode_t cqmode;
114 extern int trxmode;
115 extern char lastcall[];
116 extern int cqdelay;
117 extern char his_rst[];
118 extern int cluster;
119 extern int announcefilter;
120 extern char ph_message[14][80];
121 extern SCREEN *mainscreen;
122 extern int simulator;
123 extern int simulator_mode;
124 extern char talkarray[5][62];
125 extern int lan_active;
126 extern int zonedisplay;
127 extern int showscore_flag;
128 extern int searchflg;
129 extern int cqww;
130 extern char cqzone[];
131 extern char ituzone[];
132 extern int ctcomp;
133 extern int nob4;
134 extern int change_rst;
135 extern int weight;
136 extern int k_pin14;
137 extern int k_ptt;
138 extern int noautocq;
139 extern int miniterm;
140 extern int no_rst;
141
142 extern int bmautoadd;
143 extern int bmautograb;
144 extern int digikeyer;
145
146 static freq_t freqstore; /* qrg during last callsign input
147 character, 0 if grabbed,
148 used to decide if a call in input
149 field was entered by hand or
150 grabbed from spot list */
151
152 enum grabstate_t { NONE, IN_PROGRESS, REACHED };
153
154 struct grab_t {
155 enum grabstate_t state;
156 freq_t spotfreq;
157 char call[15];
158 };
159
160 static struct grab_t grab = { .state = NONE };
161
162
163 int cury, curx;
164 int j, t, x = 0;
165 char instring[2] = { '\0', '\0' };
166 static int lastwindow;
167
168
169 attron(modify_attr(COLOR_PAIR(NORMCOLOR)));
170
171 printcall(); /* print call input field */
172 searchlog();
173
174 while (strlen(hiscall) <= 13) {
175
176 show_zones(bandinx);
177 printcall();
178
179 /* wait for next char pressed, but update time, cluster and TRX qrg */
180 /* main loop waiting for input */
181 x = -1;
182 while (x < 1) {
183
184 usleep(10000);
185
186 time_update();
187
188 if (trxmode == DIGIMODE) {
189 show_rtty();
190 }
191
192 if (digikeyer == FLDIGI && fldigi_set_callfield == 1 && hiscall[0] != '\0') {
193 freqstore = freq;
194 fldigi_set_callfield = 0;
195 }
196
197 /* if BMAUTOADD is active and user has input a call sign
198 * (indicated by non-zero freqstore) check if he turns away
199 * from frequency and if so add call to spot list */
200 if (bmautoadd != 0 && freqstore != 0) {
201 if (strlen(hiscall) >= 3) {
202 if (fabs(freq - freqstore) > 500) {
203 add_to_spots(hiscall, freqstore);
204 hiscall[0] = '\0';
205 HideSearchPanel();
206 freqstore = 0;
207 }
208 }
209 }
210
211 /* if BMAUTOGRAB is active in S&P mode and input field is empty and a spot has
212 * not already been grabbed here check if a spot is on freq
213 * and pick it up if one found */
214 if (bmautograb && cqmode == S_P && *hiscall == '\0' && grab.state == NONE) {
215 get_spot_on_qrg(grab.call, freq);
216 if (strlen(grab.call) >= 3) {
217 strncpy(hiscall, grab.call, sizeof(hiscall));
218 grab.state = REACHED;
219 grab.spotfreq = freq;
220
221 showinfo(getctydata_pfx(hiscall));
222 printcall();
223 searchlog();
224 freqstore = 0;
225 }
226 }
227
228 /* wait till freq of grabbed spot is reported back from rig.
229 * Then go to 'reached' state' */
230 if (grab.state == IN_PROGRESS && fabs(freq - grab.spotfreq) <= 100)
231 grab.state = REACHED;
232
233 /* Check if we tune away from old freq before a grabbed spot is
234 * reached. If so stop grab process */
235
236 /* if we have grabbed a call from spot list and tune away
237 * then forget about it */
238 if (fabs(freq - grab.spotfreq) > 500 && grab.state == REACHED) {
239 grab.state = NONE;
240 hiscall[0] = '\0';
241 printcall();
242 HideSearchPanel();
243 showinfo(SHOWINFO_DUMMY);
244 }
245
246
247 /* make sure that the wrefresh() inside getch() shows the cursor
248 * in the input field */
249 wmove(stdscr, 12, 29 + strlen(hiscall));
250 x = key_poll();
251
252 }
253
254 /* special handling of some keycodes if call field is empty */
255 if (*hiscall == '\0') {
256 // <Enter>, sends CQ message (F1), starts autoCQ, or sends S&P message.
257 if ((x == '\n' || x == KEY_ENTER) && *hiscall == '\0') {
258 if (cqmode == CQ) {
259 if (noautocq != 1)
260 x = auto_cq();
261 } else {
262 sendspcall();
263 break;
264 }
265 }
266
267 // Up Arrow or Alt-e, edit last QSO
268 if (x == KEY_UP || x == ALT_E) {
269 edit_last();
270 break;
271 }
272
273 // Equals, confirms last callsign already logged if call field is empty.
274 if (x == '=' && *hiscall == '\0') {
275 char *str = g_strdup_printf("%s TU ", lastcall);
276 sendmessage(str);
277 g_free(str);
278 break;
279 }
280 }
281
282 // Shift-F1: switch back (swap) to CQ frequency if it's in memory
283 // and start a CQ
284 if (x == SHIFT_F(1)) {
285 if (cqmode == S_P && memory_get_cqmode() == CQ) {
286 handle_memory_operation(SWAP);
287 }
288
289 x = KEY_F(1); // continue as F1
290 }
291
292 switch (x) {
293
294 // Plus (+)
295 // - in non-CT mode switch to other mode (CQ <-> S&P)
296 // - in CT mode send TU and log QSO.
297 case '+': {
298 if (!ctcomp) {
299
300 /* switch to other mode */
301 if (cqmode == CQ) {
302 cqmode = S_P;
303 } else
304 cqmode = CQ;
305
306 /* and show new mode */
307 show_header_line();
308
309 } else {
310
311 if (strlen(hiscall) > 2) {
312 if (((cqww == 1) || (wazmult == 1))
313 && (*comment == '\0'))
314 strcpy(comment, cqzone);
315
316 if ((itumult == 1) && (*comment == '\0'))
317 strcpy(comment, ituzone);
318
319 if (*comment == '\0') {
320 x = -1;
321 } else {
322 /* F4 (TU macro) */
323 send_standard_message(3);
324
325 x = '\\'; // key for logging QSO without message
326 }
327 }
328 }
329 break;
330 }
331
332 // Ctrl-Q (^Q), open QTC window for receiving or sending QTCs.
333 case CTRL_Q: {
334 if (qtcdirection == 1 || qtcdirection == 3) { // in case of QTC=RECV or QTC=BOTH
335 qtc_main_panel(RECV);
336 }
337 if (qtcdirection == 2) { // in case of QTC=SEND
338 qtc_main_panel(SEND);
339 }
340 x = KEY_LEFT;
341 continue;
342 }
343
344 // Ctrl-S (^S), open QTC window for sending QTCs.
345 case CTRL_S: {
346 if (qtcdirection == 2 || qtcdirection == 3) { // in case of QTC=SEND ot QTC=BOTH
347 qtc_main_panel(SEND);
348 }
349 x = KEY_LEFT;
350 continue;
351 }
352
353 // <Home>, enter call edit when call field is not empty.
354 case KEY_HOME: {
355 if ((*hiscall != '\0') && (ungetch(x) == OK)) {
356 calledit();
357 }
358
359 break;
360 }
361
362 // Left Arrow, enter call edit when call field is not empty, or band down.
363 case KEY_LEFT: {
364 if (*hiscall != '\0') {
365 calledit();
366 } else {
367 handle_bandswitch(BAND_DOWN);
368 }
369
370 break;
371 }
372
373 // Right Arrow, band up when call field is empty.
374 case KEY_RIGHT: {
375 handle_bandswitch(BAND_UP);
376 break;
377 }
378
379 // Alt-w (M-w), set Morse weight.
380 case ALT_W: {
381 char weightbuf[5] = "";
382 char *end;
383
384 mvprintw(12, 29, "Wght: -50..50");
385
386 nicebox(1, 1, 2, 12, "Cw");
387 attron(COLOR_PAIR(C_LOG) | A_STANDOUT);
388 mvprintw(2, 2, "Speed: %2u ", GetCWSpeed());
389 mvprintw(3, 2, "Weight: %3d ", weight);
390 refreshp();
391
392 usleep(800000);
393 mvprintw(3, 10, " ");
394
395 echo();
396 mvgetnstr(3, 10, weightbuf, 3);
397 noecho();
398
399 g_strchomp(weightbuf);
400
401 int tmp = strtol(weightbuf, &end, 10);
402
403 if ((weightbuf[0] != '\0') && (*end == '\0')) {
404 /* successful conversion */
405
406 if (tmp > -51 && tmp < 51) {
407 weight = tmp;
408 netkeyer(K_WEIGHT, weightbuf);
409 }
410 }
411 clear_display();
412 printcall();
413
414 break;
415 }
416
417 // Alt-v (M-v), change Morse speed in CW mode, else band down.
418 case ALT_V: {
419 if (ctcomp == 1) {
420 while (x != ESCAPE) {
421 nicebox(1, 1, 2, 12, "Cw");
422 attron(COLOR_PAIR(C_LOG) | A_STANDOUT);
423 mvprintw(2, 2, "Speed: %2u ", GetCWSpeed());
424 mvprintw(3, 2, "Weight: %3d ", weight);
425 printcall();
426 refreshp();
427
428 x = key_get();
429 if (x == KEY_UP) {
430 speedup();
431 attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
432
433 mvprintw(0, 14, "%2u", GetCWSpeed());
434
435 } else if (x == KEY_DOWN) {
436 speeddown();
437 attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
438 mvprintw(0, 14, "%2u", GetCWSpeed());
439
440 } else
441 x = ESCAPE;
442
443 clear_display();
444 }
445 } else { // trlog compatible, band switch
446 handle_bandswitch(BAND_DOWN);
447 }
448 x = -1;
449
450 break;
451 }
452
453 // <Page-Up>, change RST if call field not empty, else increase CW speed.
454 case KEY_PPAGE: {
455 if ((change_rst == 1) && (strlen(hiscall) != 0)) { // change RST
456
457 if (his_rst[1] <= 56) {
458
459 his_rst[1]++;
460
461 if (!no_rst)
462 mvprintw(12, 44, his_rst);
463 mvprintw(12, 29, hiscall);
464 }
465
466 } else { // change cw speed
467 speedup();
468
469 attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
470 mvprintw(0, 14, "%2u", GetCWSpeed());
471 }
472
473 break;
474 }
475
476
477 // <Page-Down>, change RST if call field not empty, else decrease CW speed.
478 case KEY_NPAGE: {
479 if ((change_rst == 1) && (strlen(hiscall) != 0)) {
480
481 if (his_rst[1] > 49) {
482 his_rst[1]--;
483
484 if (!no_rst)
485 mvprintw(12, 44, his_rst);
486 mvprintw(12, 29, hiscall);
487 }
488
489 } else {
490
491 speeddown();
492
493 attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
494 mvprintw(0, 14, "%2u", GetCWSpeed());
495 }
496 break;
497 }
498
499 // <Enter>, log QSO in CT mode, else test if B4 message should be sent.
500 case '\n':
501 case KEY_ENTER: {
502 if (strlen(hiscall) > 2 && ctcomp == 1) {
503 /* There seems to be a call, log it in CT mode but only if
504 * the exchange field is not empty.
505 */
506 if (comment[0] == '\0') {
507 x = -1;
508 break;
509 } else {
510 /* Log without sending message. */
511 x = BACKSLASH;
512 break;
513 }
514 }
515
516 if (strlen(hiscall) < 3 || nob4 == 1)
517 break;
518
519 /* check b4 QSO if call is long enough and 'nob4' off */
520 isdupe = 0; // LZ3NY auto-b4 patch
521
522 searchlog();
523
524 if (isdupe != 0) {
525 // XXX: Before digi_message, SSB mode sent CW here. - W8BSD
526 send_standard_message(6); /* as with F7 */
527 cleanup();
528 clear_display();
529 }
530 break;
531 }
532
533 /* <Insert>, send exchange in CT mode */
534 case KEY_IC: {
535 if (ctcomp != 0) {
536 /* F3 (RST macro) */
537 send_standard_message(2);
538 /* Set to space to move cursor to exchange field
539 * which will trigger autofill if available.
540 */
541 x = ' ';
542 }
543 break;
544 }
545
546 // Colon, prefix for entering commands or changing parameters.
547 case ':': {
548 changepars();
549 hiscall[0] = '\0';
550 x = 0;
551 clear_display();
552 attron(COLOR_PAIR(C_LOG) | A_STANDOUT);
553
554 for (j = 13; j <= 23; j++) {
555 mvprintw(j, 0, backgrnd_str);
556 }
557
558 attron(modify_attr(COLOR_PAIR(NORMCOLOR)));
559
560 mvprintw(12, 29, " ");
561 mvprintw(12, 29, "");
562 refreshp();
563 break;
564 }
565
566 // Hash, save xcvr freq to mem or restore mem to xcvr.
567 case '#': {
568 handle_memory_operation(STORE_OR_POP);
569 break;
570 }
571 // Dollar, pop memory (MEM -> TRX)
572 case '$': {
573 handle_memory_operation(POP);
574 break;
575 }
576 // Percent, swap memory (MEM <-> TRX)
577 case '%': {
578 handle_memory_operation(SWAP);
579 break;
580 }
581
582 // Minus, delete previous QSO from log.
583 case '-': {
584 delete_qso();
585 break;
586 }
587
588 // Semicolon or Alt-n (M-n), insert note in log.
589 case ';':
590 case ALT_N: {
591 include_note();
592 x = -1;
593 break;
594 }
595
596 // Alt-0 to Alt-9 (M-0...M-9), send CW/Digimode messages 15-24.
597 case 176 ... 185: {
598 send_standard_message(x - 162); /* alt-0 to alt-9 */
599
600 break;
601 }
602
603 // F1, send CQ or S&P call message.
604 case KEY_F(1): {
605 if (trxmode == CWMODE || trxmode == DIGIMODE) {
606
607 if (cqmode == S_P) {
608 sendspcall();
609 } else {
610 send_standard_message(0); /* CQ */
611 }
612
613
614 if (simulator != 0) {
615 simulator_mode = 1;
616 }
617 } else {
618
619 if (cqmode == S_P)
620 play_file(ph_message[5]); /* S&P */
621 else
622 play_file(ph_message[0]);
623 }
624 break;
625 }
626
627 // F2-F11, send messages 2 through 11.
628 case KEY_F(2) ... KEY_F(11): {
629 send_standard_message(x - KEY_F(1)); // F2...F11 - F1 = 1...10
630
631 break;
632 }
633
634 // F12, activate autocq timing and message.
635 case KEY_F(12): {
636 x = auto_cq();
637 break;
638 }
639
640 // Query, send call with " ?" appended or F5 message in voice mode.
641 case '?': {
642 if (*hiscall != '\0') {
643 strcat(hiscall, " ?");
644 send_standard_message(4);
645 hiscall[strlen(hiscall) - 2] = '\0';
646 }
647 x = -1;
648 break;
649 }
650
651 // <Backspace>, remove chracter left of cursor, move cursor left one position.
652 case KEY_BACKSPACE: {
653 if (*hiscall != '\0') {
654 getyx(stdscr, cury, curx);
655 mvprintw(cury, curx - 1, " ");
656 mvprintw(cury, curx - 1, "");
657 hiscall[strlen(hiscall) - 1] = '\0';
658
659 if (atoi(hiscall) < 1800) { /* no frequency */
660 showinfo(getctydata_pfx(hiscall));
661 searchlog();
662 refreshp();
663 }
664
665 x = -1;
666 break;
667 }
668 break;
669 }
670
671 // Alt-r (M-r) or Alt-s (M-s), toggle score window.
672 case ALT_R:
673 case ALT_S: {
674 if (showscore_flag == 0)
675 showscore_flag = 1;
676 else {
677 showscore_flag = 0;
678 }
679 clear_display();
680 break;
681 }
682
683 // Alt-k (M-k), synonym for Ctrl-K (^K).
684 case ALT_K: {
685 x = CTRL_K; // Ctrl-K
686 break;
687 }
688
689 // Alt-a (M-a), cycle cluster window.
690 case ALT_A: {
691 if (cluster == NOCLUSTER) {
692 cluster = CLUSTER; // alt-A
693 announcefilter = FILTER_ALL;
694 } else if (cluster == CLUSTER) {
695 cluster = MAP;
696 } else if (cluster == MAP) {
697 cluster = NOCLUSTER;
698 }
699
700 break;
701 }
702
703 // Alt-b (M-b), band-up for TR-Log mode.
704 case ALT_B: {
705 if (ctcomp == 0) {
706 handle_bandswitch(BAND_UP);
707 }
708 break;
709 }
710
711 // Alt-j (M-j), show station frequencies.
712 case ALT_J: {
713 if (cluster != FREQWINDOW) {
714 lastwindow = cluster;
715 cluster = FREQWINDOW;
716 } else
717 cluster = lastwindow;
718
719 break;
720 }
721
722 // Alt-h (M-h), show help.
723 case ALT_H: {
724 show_help();
725 break;
726 }
727
728 // Alt-, (M-,) or period, change bandmap filter config.
729 case 172:
730 case '.': {
731 bm_menu();
732 break;
733 }
734
735 // Alt-c (M-c), toggle check window.
736 case ALT_C: {
737 if (searchflg != SEARCHWINDOW)
738 searchflg = SEARCHWINDOW;
739 else
740 searchflg = 0;
741 break;
742 }
743
744 // Alt-m (M-m), show multipliers.
745 case ALT_M: {
746 show_mults();
747 refreshp();
748 break;
749 }
750
751 // Alt-p (M-p), toggle PTT via cwdaemon
752 case ALT_P: {
753 if (k_ptt == 0) {
754 k_ptt = 1;
755 attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
756 mvprintw(0, 2, "PTT on ");
757 mvprintw(12, 29, "");
758 refreshp();
759 netkeyer(K_PTT, "1"); // ptt on
760 x = key_get(); // any character to stop tuning
761 if (x == ALT_P) // Alt-P (M-p)
762 netkeyer(K_PTT, "0"); // ptt off
763 k_ptt = 0;
764 show_header_line();
765 refreshp();
766 } else
767 netkeyer(K_PTT, "0"); // ptt off in any case.
768
769 break;
770 }
771
772 // Alt-t (M-t), tune xcvr via cwdaemon.
773 case ALT_T: {
774 int count;
775 gchar *buff;
776
777 attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
778 mvprintw(0, 2, "Tune ");
779 mvprintw(12, 29, "");
780 refreshp();
781
782 buff = g_strdup_printf("%d", TUNE_UP);
783 netkeyer(K_TUNE, buff); // cw on
784 g_free(buff);
785
786 count = (int)(TUNE_UP / 0.25);
787
788 while (count != 0) {
789 usleep(250000);
790 if (key_poll() != -1) // any key pressed ?
791 break;
792 count--;
793 }
794
795 netkeyer(K_ABORT, ""); // cw abort
796
797 show_header_line();
798 refreshp();
799
800 break;
801 }
802
803 // Alt-z (M-z), show zones worked.
804 case ALT_Z: {
805 if (cqww == 1) {
806 if (zonedisplay == 0)
807 zonedisplay = 1;
808 else {
809 zonedisplay = 0;
810 clear_display();
811 }
812 } else {
813 multiplierinfo();
814 }
815
816 break;
817 }
818
819 // Alt-q (M-q) or Alt-x (M-x), Exit
820 case ALT_Q:
821 case ALT_X: {
822 mvprintw(13, 29, "You want to leave tlf? (y/n): ");
823 while (x != 'n' && x != 'N') {
824
825 x = key_get();
826
827 if (x == 'y' || x == 'Y') {
828 writeparas();
829 cleanup_telnet();
830 endwin();
831
832 puts("\n\nThanks for using TLF.. 73\n");
833 exit(0);
834 }
835 }
836 x = ESCAPE;
837 break;
838 }
839
840 // <Escape>, clear call input or stop sending.
841 case ESCAPE: {
842 if (early_started == 0) {
843 /* if CW not started early drop call and start anew */
844 cleanup();
845 clear_display();
846 } else {
847 /* otherwise just stop sending */
848 stoptx();
849 *hiscall_sent = '\0';
850 early_started = 0;
851 }
852
853 freqstore = 0;
854 break;
855 }
856
857 // Underscore, confirm last exchange.
858 case '_': {
859 prev_qso();
860
861 break;
862 }
863
864 // Exclamation, open a new shell.
865 case '!': {
866 const char *shell = getenv("SHELL");
867 if (shell == NULL) {
868 shell = "sh";
869 }
870 endwin();
871 IGNORE(system("clear"));;
872 IGNORE(system(shell));;
873 IGNORE(system("clear"));;
874 set_term(mainscreen);
875 clear_display();
876
877 break;
878 }
879
880 // Ctrl-L (^L), resets screen.
881 case CTRL_L: {
882 endwin();
883 set_term(mainscreen);
884 clear_display();
885
886 break;
887 }
888
889 // Ctrl-P (^P), show MUF display.
890 case CTRL_P: {
891 int currentterm = miniterm;
892 miniterm = 0;
893 muf();
894 miniterm = currentterm;
895 clear_display();
896
897 break;
898 }
899
900 // Ctrl-A (^A), add a spot and share on LAN.
901 case CTRL_A: {
902 addspot();
903 HideSearchPanel();
904 showinfo(SHOWINFO_DUMMY);
905
906 grab.state = REACHED;
907 grab.spotfreq = freq;
908 break;
909 }
910
911 // Ctrl-B (^B), send spot to DX cluster.
912 case CTRL_B: {
913 announcefilter = 0;
914 cluster = CLUSTER;
915 send_cluster();
916
917 break;
918 }
919
920 // Ctrl-F (^F), change frequency dialog.
921 case CTRL_F: {
922 change_freq();
923
924 break;
925 }
926
927 // Ctrl-G (^G), grab next DX spot from bandmap.
928 case CTRL_G: {
929 freq_t f = grab_next();
930 if (f > 0.0) {
931 grab.state = IN_PROGRESS;
932 grab.spotfreq = f;
933 show_header_line();
934 freqstore = 0;
935 }
936
937 break;
938 }
939
940 // Alt-g (M-g), grab first spot matching call field chars.
941 case ALT_G: {
942 double f = grabspot();
943 if (f > 0.0) {
944 grab.state = IN_PROGRESS;
945 grab.spotfreq = f;
946 show_header_line();
947 freqstore = 0;
948 }
949
950 break;
951 }
952
953 // Double quote, send talk message to other nodes.
954 case '\"': {
955 if (lan_active != 0)
956 talk();
957
958 break;
959 }
960
961 // Ctrl-R (^R), toogle trx1, trx2 via lp0 pin 14.
962 case CTRL_R: {
963 if (k_pin14 == 0) {
964 k_pin14 = 1;
965 netkeyer(K_SET14, "1");
966 } else {
967 k_pin14 = 0;
968 netkeyer(K_SET14, "0");
969 }
970 break;
971 }
972
973 // Ctrl-T (^T) or Alt-i (M-i), show talk messages.
974 case CTRL_T:
975 case ALT_I: {
976 if (lan_active != 0) {
977
978 for (t = 0; t <= 5; t++)
979 mvprintw(14 + t, 1,
980 " ");
981 for (t = 0; t <= 4; t++)
982 mvprintw(15 + t, 1, talkarray[t]);
983 nicebox(14, 0, 5, 59, "Messages");
984
985 refreshp();
986 key_get();
987 attron(COLOR_PAIR(C_LOG) | A_STANDOUT);
988 for (t = 0; t <= 6; t++)
989 mvprintw(14 + t, 0,
990 " ");
991
992 clear_display();
993 }
994 break;
995 }
996
997 } /* end switch */
998
999
1000 // Ctrl-<Page-Up>, increase cqdelay by 1/2 second.
1001 // Alt-<Page-Up>, same for terminals that consume Ctrl-<Page-Up>.
1002 if ((key_kPRV3 && x == key_kPRV3)
1003 || (key_kPRV5 && x == key_kPRV5)) {
1004
1005 if (cqdelay <= 60) {
1006 cqdelay++;
1007
1008 attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
1009 mvprintw(0, 19, " ");
1010 mvprintw(0, 19, "%i", cqdelay);
1011 }
1012
1013 break;
1014 }
1015
1016
1017 // Ctrl-<Page-Down>, decrease cqdelay by 1/2 Second.
1018 // Alt-<Page-Down>, same for terminals that consume Ctrl-<Page-Down>.
1019 if ((key_kNXT3 && x == key_kNXT3)
1020 || (key_kNXT5 && x == key_kNXT5)) {
1021
1022 if (cqdelay >= 4) {
1023 cqdelay--;
1024
1025 attron(COLOR_PAIR(C_HEADER) | A_STANDOUT);
1026 mvprintw(0, 19, " ");
1027 mvprintw(0, 19, "%i", cqdelay);
1028 }
1029
1030 break;
1031 }
1032
1033
1034 /* Convert to upper case */
1035 if (x >= 'a' && x <= 'z')
1036 x = x - 32;
1037
1038 /* Add character to call input field. */
1039 if (x >= '/' && x <= 'Z') {
1040
1041 if (strlen(hiscall) < 13) {
1042 instring[0] = x;
1043 instring[1] = '\0';
1044 addch(x);
1045 strcat(hiscall, instring);
1046 if (cqmode == CQ && cwstart > 0 &&
1047 trxmode == CWMODE && contest == 1) {
1048 /* early start keying after 'cwstart' characters but only
1049 * if input field contains at least one nondigit */
1050 if (strlen(hiscall) == cwstart && !plain_number(hiscall)) {
1051 x = autosend();
1052 }
1053 }
1054 }
1055
1056 if (atoi(hiscall) < 1800) { /* no frequency */
1057
1058 showinfo(getctydata_pfx(hiscall));
1059 searchlog();
1060 }
1061
1062 refreshp();
1063
1064 freqstore = freq;
1065
1066 }
1067
1068 if (cqmode == CQ && cwstart < 0 && trxmode == CWMODE &&
1069 contest == 1) {
1070 if (x == '\n' || x == KEY_ENTER) {
1071 /* early start keying after 'Enter' but only if input field
1072 * contains at least two chars, one or more of it nondigit */
1073 if (strlen(hiscall) >= 2 && !plain_number(hiscall)) {
1074 x = autosend();
1075 }
1076 }
1077 }
1078
1079 if ((x == '\n' || x == KEY_ENTER) || x == SPACE || x == TAB
1080 || x == CTRL_K || x == 44 || x == BACKSLASH) {
1081 break;
1082 }
1083
1084 }
1085
1086 return (x);
1087 }
1088
1089 /** check if string is plain number
1090 *
1091 * Check if string contains only digits
1092 * \param str the string to check
1093 * \return true if only digits inside
1094 * false at least one none digit
1095 */
plain_number(char * str)1096 int plain_number(char *str) {
1097 int i;
1098
1099 for (i = 0; i < strlen(str); i++) {
1100 if (!isdigit(str[i])) {
1101 return false;
1102 }
1103 }
1104
1105 return true;
1106 }
1107
1108
1109 /** autosend function
1110 *
1111 * autosend allow an operator in RUN mode to just enter the call of the
1112 * other station. TLF will start sending the call and switch automatically
1113 * to sending the exchange when typing stops.
1114 * - starts after 2..5 characters
1115 * - shorter calls have to be finished with ENTER key
1116 * - as soon as autosend starts only alfanumerical keys are accepted
1117 * - no edit after input possible
1118 * - calculates expected time to send call from cw speed and
1119 * - switches to sending exchange after that time is reached
1120 *
1121 * \return last typed key, ESC or \n
1122 * ESC - transmission has stopped
1123 * \n - timeout or CR pressed -> send exchange
1124 */
autosend()1125 int autosend() {
1126
1127 extern int early_started;
1128 extern int sending_call;
1129 extern char hiscall_sent[];
1130 extern char hiscall[];
1131
1132 GTimer *timer;
1133 double timeout, timeout_sent;
1134 int x;
1135 int char_sent;
1136
1137 early_started = 1;
1138 sending_call = 1;
1139 sendmessage(hiscall);
1140 sending_call = 0;
1141 strcpy(hiscall_sent, hiscall);
1142
1143 char_sent = 0; /* no char sent so far */
1144 timeout_sent = (1.2 / GetCWSpeed()) * getCWdots(hiscall[char_sent]);
1145
1146 timer = g_timer_new();
1147 timeout = (1.2 / GetCWSpeed()) * cw_message_length(hiscall);
1148
1149 x = -1;
1150 while ((x != ESCAPE) && (x != '\n' && x != KEY_ENTER)) {
1151 x = -1;
1152 while ((x == -1) && (g_timer_elapsed(timer, NULL) < timeout)) {
1153
1154 highlightCall(char_sent + 1);
1155
1156 usleep(10000);
1157
1158 if (g_timer_elapsed(timer, NULL) > timeout_sent) {
1159 /* one char sent - display and set new timeout */
1160 char_sent ++;
1161 timeout_sent +=
1162 (1.2 / GetCWSpeed()) * getCWdots(hiscall[char_sent]);
1163
1164 }
1165
1166 /* make sure that the wrefresh() inside getch() shows the cursor
1167 * in the input field */
1168 wmove(stdscr, 12, 29 + strlen(hiscall));
1169 x = key_poll();
1170
1171 }
1172
1173 if (x == -1) { /* timeout */
1174 x = '\n';
1175 continue;
1176 }
1177
1178 // <Escape>
1179 if (x == ESCAPE) {
1180 stoptx();
1181 *hiscall_sent = '\0';
1182 early_started = 0;
1183 continue;
1184 }
1185
1186 /* convert to upper case */
1187 if (x >= 'a' && x <= 'z')
1188 x = x - 32;
1189
1190 int len = strlen(hiscall);
1191 if (len < 13 && x >= '/' && x <= 'Z') {
1192 char append[2];
1193
1194 /* insert into hiscall */
1195 hiscall[len] = x;
1196 hiscall[len + 1] = '\0';
1197
1198 /* display it */
1199 printcall();
1200
1201 /* send it to cw */
1202 append[0] = x;
1203 append[1] = '\0';
1204 sendmessage(append);
1205
1206 /* add char length to timeout */
1207 timeout += (1.2 / GetCWSpeed()) * getCWdots((char) x);
1208
1209 len = strlen(hiscall_sent);
1210 hiscall_sent[len] = x;
1211 hiscall_sent[len + 1] = '\0';
1212 }
1213 }
1214
1215 g_timer_destroy(timer);
1216 return x;
1217 }
1218
1219
play_file(char * audiofile)1220 int play_file(char *audiofile) {
1221
1222 extern int txdelay;
1223 extern unsigned char rigptt;
1224
1225 int fd;
1226 char playcommand[120];
1227
1228 if (*audiofile == '\0')
1229 return (0);
1230
1231 if ((fd = open(audiofile, O_RDONLY, 0664)) < 0) {
1232 TLF_LOG_INFO("cannot open sound file %s!", audiofile);
1233 } else {
1234 close(fd);
1235 if (access("./play_vk", X_OK) == 0) {
1236 sprintf(playcommand, "./play_vk %s", audiofile);
1237 } else {
1238 sprintf(playcommand, "play_vk %s", audiofile);
1239 }
1240 /* CAT PTT wanted and available, use it. */
1241 if (rigptt == 0x03) {
1242 /* Request PTT On */
1243 rigptt |= (1 << 3); /* 0x0b */
1244 } else { /* Fall back to netkeyer interface */
1245 netkeyer(K_PTT, "1"); // ptt on
1246 }
1247
1248 usleep(txdelay * 1000);
1249 IGNORE(system(playcommand));;
1250 printcall();
1251
1252 /* CAT PTT wanted, available, and active. */
1253 if (rigptt == 0x07) {
1254
1255 /* Request PTT Off */
1256 rigptt |= (1 << 4); /* 0x17 */
1257 } else { /* Fall back to netkeyer interface */
1258 netkeyer(K_PTT, "0"); // ptt off
1259 }
1260 }
1261
1262 return 0;
1263 }
1264
1265
send_bandswitch(freq_t freq)1266 void send_bandswitch(freq_t freq) {
1267
1268 extern int use_bandoutput;
1269 extern int bandinx;
1270 extern int bandindexarray[];
1271
1272 char outnibble[3];
1273 int bandswitch = 0;
1274
1275 if (!use_bandoutput) {
1276 return;
1277 }
1278
1279 const int khz = (int)(freq / 1000.0);
1280 if (khz > 15) { // looks like a freq...
1281 switch (khz) {
1282 case 1800 ... 2000:
1283 bandswitch = 1;
1284 break;
1285 case 3500 ... 4000:
1286 bandswitch = 2;
1287 break;
1288 case 7000 ... 7300:
1289 bandswitch = 3;
1290 break;
1291 case 10100 ... 10150:
1292 bandswitch = 4;
1293 break;
1294 case 14000 ... 14350:
1295 bandswitch = 5;
1296 break;
1297 case 18068 ... 18168:
1298 bandswitch = 6;
1299 break;
1300 case 21000 ... 21450:
1301 bandswitch = 7;
1302 break;
1303 case 24890 ... 24990:
1304 bandswitch = 8;
1305 break;
1306 case 28000 ... 29700:
1307 bandswitch = 9;
1308 }
1309 } else // use the bandinx
1310 bandswitch = bandinx + 1;
1311
1312 bandswitch = bandindexarray[bandswitch];
1313
1314 sprintf(outnibble, "%d", bandswitch);
1315 netkeyer(K_SWITCH, outnibble);
1316 }
1317
1318
1319 /** handle bandswitch from keyboard
1320 *
1321 **/
handle_bandswitch(int direction)1322 void handle_bandswitch(int direction) {
1323 // make sure call field is empty and arrows are enabled
1324 if (*hiscall != '\0' || no_arrows) {
1325 return;
1326 }
1327
1328 next_band(direction);
1329
1330 if (contest == 1 && dxped == 0) {
1331 while (IsWarcIndex(bandinx)) { /* loop till next contest band */
1332 next_band(direction);
1333 }
1334 }
1335
1336 attron(COLOR_PAIR(C_WINDOW) | A_STANDOUT);
1337 mvprintw(12, 0, band[bandinx]);
1338
1339 if (trx_control) {
1340 freq = bandfrequency[bandinx]; // TODO: is this needed?
1341 set_outfreq(bandfrequency[bandinx]);
1342 }
1343
1344 send_bandswitch(bandinx);
1345 }
1346
1347 /** handle TRX memory operation and update screen
1348 *
1349 **/
handle_memory_operation(memory_op_t op)1350 void handle_memory_operation(memory_op_t op) {
1351 const freq_t newfreq = memory_get_freq();
1352
1353 switch (op) {
1354 case STORE_OR_POP:
1355 memory_store_or_pop();
1356 break;
1357 case POP:
1358 memory_pop();
1359 break;
1360 case SWAP:
1361 memory_swap();
1362 break;
1363 }
1364
1365 show_header_line();
1366 showinfo(getctydata_pfx(hiscall));
1367 searchlog();
1368 move(12, 29 + strlen(hiscall));
1369
1370 if (newfreq <= 0) {
1371 return; // freq not changed
1372 }
1373
1374 // wait until change is reported back from the rig (timeout: 1 sec)
1375 for (int i = 0; i < 20; i++) {
1376 if (fabs(freq - newfreq) < 100) {
1377 break;
1378 }
1379 usleep(50000);
1380 }
1381 }
1382