1 /*
2 * Tlf - contest logging program for amateur radio operators
3 * Copyright (C) 2011 Thomas Beierlein <tb@forth-ev.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20
21 #include <ctype.h>
22 #include <pthread.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <sys/time.h>
27 #include <math.h>
28
29 #include "bandmap.h"
30 #include "qtcutil.h"
31 #include "qtcvars.h" // Includes globalvars.h
32 #include "searchcallarray.h"
33 #include "searchlog.h"
34 #include "tlf_curses.h"
35 #include "ui_utils.h"
36 #include "getctydata.h"
37 #include "dxcc.h"
38 #include "initial_exchange.h"
39 #include "bands.h"
40
41 #define TOLERANCE 100 /* spots with a QRG +/-TOLERANCE
42 will be counted as the same QRG */
43
44 #define SPOT_COLUMN_WIDTH 22
45 #define SPOT_FREQ_WIDTH 7
46 #define SPOT_CALL_WIDTH SPOT_COLUMN_WIDTH-SPOT_FREQ_WIDTH-4 // 3 spaces before and 1 after call
47
48 #define DISTANCE(x, y) \
49 ( x < y ? y - x : x -y )
50
51 #define TOPLINE 14
52 #define LASTLINE (LINES - 2)
53
54 #define LINELENGTH 80
55 #define COLUMNS ((LINELENGTH - 14) / SPOT_COLUMN_WIDTH)
56 #define NR_SPOTS ((LASTLINE - TOPLINE + 1) * COLUMNS)
57
58 pthread_mutex_t bm_mutex = PTHREAD_MUTEX_INITIALIZER;
59
60 /** \brief sorted list of all recent DX spots
61 */
62 GList *allspots = NULL;
63
64 /** \brief sorted list of filtered spots
65 */
66 GPtrArray *spots;
67
68
69 bm_config_t bm_config = {
70 1, /* show all bands */
71 1, /* show all mode */
72 1, /* show dupes */
73 1, /* skip dupes during grab */
74 900,/* default livetime */
75 0 /* DO NOT show ONLY multipliers */
76 };
77 short bm_initialized = 0;
78
79 extern freq_t freq;
80 extern int trx_control;
81 extern int bandinx;
82 extern int trxmode;
83 extern char thisnode;
84 extern struct worked_t worked[];
85 extern int contest;
86 extern char whichcontest[];
87
88 char *qtc_format(char *call);
89
90 gint cmp_freq(spot *a, spot *b);
91
92 /*
93 * write bandmap spots to a file
94 */
bmdata_write_file()95 void bmdata_write_file() {
96
97 FILE *fp;
98 spot *sp;
99 GList *found;
100 struct timeval tv;
101
102 if ((fp = fopen(".bmdata.dat", "w")) == NULL) {
103 attron(modify_attr(COLOR_PAIR(CB_DUPE) | A_BOLD));
104 mvprintw(13, 29, "can't open bandmap data file!");
105 refreshp();
106 return;
107 }
108
109 gettimeofday(&tv, NULL);
110
111 pthread_mutex_lock(&bm_mutex);
112
113 found = allspots;
114 fprintf(fp, "%d\n", (int)tv.tv_sec);
115 while (found != NULL) {
116 sp = found->data;
117 fprintf(fp, "%s;%d;%d;%d;%c;%u;%d;%d;%d;%s\n",
118 sp->call, sp->freq, sp->mode, sp->band,
119 sp->node, (int)sp->timeout, sp->dupe, sp->cqzone,
120 sp->ctynr, g_strchomp(sp->pfx));
121 found = found->next;
122 }
123
124 pthread_mutex_unlock(&bm_mutex);
125
126 fclose(fp);
127 }
128
129 /*
130 * read bandmap spots from file, put them to allspots list
131 */
bmdata_read_file()132 void bmdata_read_file() {
133 FILE *fp;
134 struct timeval tv;
135 int timediff, last_bm_save_time, fc;
136 char line[50], *token;
137 static int bmdata_parsed = 0;
138
139 if ((fp = fopen(".bmdata.dat", "r")) != NULL && bmdata_parsed == 0) {
140 bmdata_parsed = 1;
141 if (fgets(line, 50, fp)) {
142 sscanf(line, "%d", &last_bm_save_time);
143 gettimeofday(&tv, NULL);
144 timediff = (int)tv.tv_sec - last_bm_save_time;
145 if (timediff < 0)
146 timediff = 0;
147
148 while (fgets(line, 50, fp)) {
149 spot *entry = g_new0(spot, 1);
150 fc = 0;
151 token = strtok(line, ";");
152 while (token != NULL) {
153 switch (fc) {
154 case 0: entry -> call = g_strdup(token);
155 break;
156 case 1: sscanf(token, "%d", &entry->freq);
157 break;
158 case 2: sscanf(token, "%hhd", &entry->mode);
159 break;
160 case 3: sscanf(token, "%hd", &entry->band);
161 break;
162 case 4: sscanf(token, "%c", &entry->node);
163 break;
164 case 5: sscanf(token, "%u", &entry->timeout);
165 break;
166 case 6: sscanf(token, "%hhd", &entry->dupe);
167 break;
168 case 7: sscanf(token, "%u", &entry->cqzone);
169 break;
170 case 8: sscanf(token, "%u", &entry->ctynr);
171 break;
172 case 9: entry->pfx = g_strdup(token);
173 break;
174 }
175 fc++;
176 token = strtok(NULL, ";");
177 }
178 if (entry->timeout > timediff) {
179 entry->timeout -= timediff; /* remaining time */
180 allspots = g_list_insert_sorted(allspots, entry, (GCompareFunc)cmp_freq);
181 } else {
182 g_free(entry);
183 }
184 }
185 }
186 fclose(fp);
187 }
188 }
189
190 /** \brief initialize bandmap
191 *
192 * initalize colors and data structures for bandmap operation
193 */
bm_init()194 void bm_init() {
195
196 pthread_mutex_lock(&bm_mutex);
197
198 init_pair(CB_NEW, COLOR_CYAN, COLOR_WHITE);
199 init_pair(CB_NORMAL, COLOR_BLUE, COLOR_WHITE);
200 init_pair(CB_DUPE, COLOR_BLACK, COLOR_WHITE);
201 init_pair(CB_OLD, COLOR_YELLOW, COLOR_WHITE);
202 init_pair(CB_MULTI, COLOR_WHITE, COLOR_BLUE);
203
204 spots = g_ptr_array_sized_new(128);
205
206 bmdata_read_file();
207
208 pthread_mutex_unlock(&bm_mutex);
209 }
210
211
212 /** \brief guess mode based on frequency
213 *
214 * \return CWMODE, DIGIMODE or SSBMODE
215 */
freq2mode(freq_t freq,int band)216 int freq2mode(freq_t freq, int band) {
217 if (freq <= cwcorner[band])
218 return CWMODE;
219 else if (freq < ssbcorner[band])
220 return DIGIMODE;
221 else
222 return SSBMODE;
223 }
224
225
226
227 /** \brief add DX spot message to bandmap
228 *
229 * check if cluster message is a dx spot,
230 * if so split it into pieces and insert in spot list */
bm_add(char * s)231 void bm_add(char *s) {
232 char *line;
233 char *call;
234 char node = ' ';
235
236 line = g_strdup(s);
237 if (strncmp(line, "DX de ", 6) != 0) {
238 g_free(line);
239 return;
240 }
241
242 if ((call = strtok(line + 26, " \t")) == NULL) {
243 g_free(line);
244 return;
245 }
246
247 if (strncmp(line + 6, "TLF-", 4) == 0)
248 node = line[10]; /* get sending node id */
249
250 bandmap_addspot(call, atof(line + 16) * 1000, node);
251 g_free(line);
252 }
253
254
255 /* compare functions to search in list */
cmp_call(spot * ldata,char * call)256 gint cmp_call(spot *ldata, char *call) {
257
258 return g_strcmp0(ldata->call, call);
259 }
260
cmp_freq(spot * a,spot * b)261 gint cmp_freq(spot *a, spot *b) {
262 unsigned int af = a->freq;
263 unsigned int bf = b->freq;
264
265 if (af < bf) return -1;
266 if (af > bf) return 1;
267 return 0;
268 }
269
270 /** add a new spot to bandmap data
271 * \param call the call to add
272 * \param freq on which frequency heard
273 * \param node reporting node
274 */
bandmap_addspot(char * call,freq_t freq,char node)275 void bandmap_addspot(char *call, freq_t freq, char node) {
276 /* - if a spot on that band and mode is already in list replace old entry
277 * with new one and set timeout to SPOT_NEW,
278 * otherwise add it to the list as new
279 * - if other call on same frequency (with some TOLERANCE) replace it and set
280 * timeout to SPOT_NEW
281 * - all frequencies from cluster are rounded to 100 Hz,
282 * remember all other frequencies exactly
283 * but display only rounded to 100 Hz - sort exact
284 */
285 GList *found;
286 int band;
287 char mode;
288 dxcc_data *dxccdata;
289 int dxccindex;
290 int wi;
291 char *lastexch;
292 extern struct ie_list *main_ie_list;
293 struct ie_list *current_ie;
294
295 /* add only HF spots */
296 if (freq > 30000000)
297 return;
298
299 band = freq2band(freq);
300 if (band == BANDINDEX_OOB) /* no ham band */
301 return;
302
303 mode = freq2mode(freq, band);
304
305 /* acquire bandmap mutex */
306 pthread_mutex_lock(&bm_mutex);
307
308 /* look if call is already on list in that mode and band */
309 /* each call is allowed in every combination of band and mode
310 * but only once */
311 found = g_list_find_custom(allspots, call, (GCompareFunc)cmp_call);
312
313 while (found != NULL) {
314
315 /* if same band and mode -> found spot already in list */
316 if (((spot *)found->data)->band == band &&
317 ((spot *)found->data)->mode == mode)
318 break;
319
320 found = g_list_find_custom(found->next, call, (GCompareFunc)cmp_call);
321 }
322
323 /* if already in list on that band and mode
324 * -> set timeout to SPOT_NEW, and set new freq and reporting node
325 * if freq has changed enough sort list anew by freq
326 */
327 if (found) {
328 ((spot *)found->data)->timeout = SPOT_NEW;
329 ((spot *)found->data)->node = node;
330 if (DISTANCE(((spot *)found->data)->freq, freq) > TOLERANCE) {
331 ((spot *)found->data)->freq = freq;
332 allspots = g_list_sort(allspots, (GCompareFunc)cmp_freq);
333 }
334 } else {
335 /* if not in list already -> prepare new entry and
336 * insert in list at correct freq */
337 spot *entry = g_new(spot, 1);
338 entry -> call = g_strdup(call);
339 entry -> freq = freq;
340 entry -> mode = mode;
341 entry -> band = band;
342 entry -> node = node;
343 entry -> timeout = SPOT_NEW;
344 entry -> dupe = 0; /* Dupe will be determined later. */
345
346 lastexch = NULL;
347 dxccindex = getctynr(entry->call);
348 if (cqww == 1) {
349 // check if the callsign exists in worked list
350 wi = searchcallarray(call);
351 if (wi >= 0) {
352 lastexch = g_strdup(worked[wi].exchange);
353 }
354
355 if (lastexch == NULL && main_ie_list != NULL) {
356 current_ie = main_ie_list;
357
358 while (current_ie) {
359 if (strcmp(call, current_ie->call) == 0) {
360 lastexch = g_strdup(current_ie->exchange);
361 break;
362 }
363 current_ie = current_ie->next;
364 }
365 }
366 }
367 if (dxccindex > 0) {
368 dxccdata = dxcc_by_index(dxccindex);
369 entry -> cqzone = dxccdata->cq;
370 if (lastexch != NULL) {
371 entry -> cqzone = atoi(lastexch);
372 g_free(lastexch);
373 }
374 entry -> ctynr = dxccindex;
375 entry -> pfx = g_strdup(dxccdata->pfx);
376 } else {
377 entry -> cqzone = 0;
378 entry -> ctynr = 0;
379 entry -> pfx = g_strdup("");
380 }
381 allspots = g_list_insert_sorted(allspots, entry, (GCompareFunc)cmp_freq);
382 /* lookup where it is */
383 found = g_list_find(allspots, entry);
384 }
385
386 /* check that spot is unique on freq +/- TOLERANCE Hz,
387 * drop other entries if needed */
388 if (found->prev &&
389 (DISTANCE(((spot *)(found->prev)->data)->freq, freq) < TOLERANCE)) {
390 spot *olddata;
391 olddata = found->prev->data;
392 allspots = g_list_remove_link(allspots, found->prev);
393 g_free(olddata->call);
394 g_free(olddata->pfx);
395 g_free(olddata);
396 }
397 if (found->next &&
398 (DISTANCE(((spot *)(found->next)->data)->freq, freq) < TOLERANCE)) {
399 spot *olddata;
400 olddata = found->next->data;
401 allspots = g_list_remove_link(allspots, found->next);
402 g_free(olddata->call);
403 g_free(olddata->pfx);
404 g_free(olddata);
405 }
406
407
408 pthread_mutex_unlock(&bm_mutex);
409 }
410
411
bandmap_age()412 void bandmap_age() {
413 /*
414 * go through all entries
415 * + decrement timeout
416 * + set state to new, normal, aged or dead
417 * + if dead -> drop it from collection
418 */
419
420 pthread_mutex_lock(&bm_mutex);
421
422 GList *list = allspots;
423
424 while (list) {
425 spot *data = list->data;
426 GList *temp = list;
427 list = list->next;
428 if (data->timeout) {
429 data->timeout--;
430 }
431 if (data->timeout == 0) {
432 allspots = g_list_remove_link(allspots, temp);
433 g_free(data->call);
434 g_free(data->pfx);
435 g_free(data);
436 }
437 }
438
439 pthread_mutex_unlock(&bm_mutex);
440 }
441
442
443 /** check if call is new multi
444 *
445 * \return true if new multi
446 */
bm_ismulti(char * call,spot * data,int band)447 bool bm_ismulti(char *call, spot *data, int band) {
448
449 if (data == NULL || data->cqzone <= 0 || data->ctynr <= 0) {
450 return false; // no data
451 }
452
453 if (cqww == 1) {
454 if ((zones[data->cqzone] & inxes[band]) == 0
455 || (countries[data->ctynr] & inxes[band]) == 0) {
456 return true;
457 }
458 }
459
460 return false;
461 }
462
463
464 /** check if call is a dupe
465 *
466 * \return true if is dupe
467 */
468 /** \todo should check band AND mode if already worked.... */
469
bm_isdupe(char * call,int band)470 bool bm_isdupe(char *call, int band) {
471
472 /* spots for warc bands are never dupes */
473 if (IsWarcIndex(band))
474 return false;
475
476 int found = searchcallarray(call);
477
478 if (found == -1) /* new call */
479 return false;
480
481 if (qtcdirection > 0) {
482 struct t_qtc_store_obj *qtc_obj = qtc_get(call);
483 if (qtc_obj->total > 0 && qtc_obj->total < 10) {
484 return false;
485 }
486 if (qtc_obj->total == 0 && qtc_obj->capable > 0) {
487 return false;
488 }
489 }
490
491 if (worked[found].band & inxes[band]) {
492 return worked_in_current_minitest_period(found);
493 }
494
495 return false;
496 }
497
498
bm_show_info()499 void bm_show_info() {
500
501 int curx, cury;
502
503 getyx(stdscr, cury, curx); /* remember cursor */
504
505 /* show info field on the right */
506 attrset(COLOR_PAIR(CB_DUPE) | A_BOLD);
507 move(TOPLINE, 66);
508 vline(ACS_VLINE, LINES - TOPLINE - 1);
509
510 mvprintw(LASTLINE - 5, 67, " bands: %s", bm_config.allband ? "all" : "own");
511 mvprintw(LASTLINE - 4, 67, " modes: %s", bm_config.allmode ? "all" : "own");
512 mvprintw(LASTLINE - 3, 67, " dupes: %s", bm_config.showdupes ? "yes" : "no");
513 mvprintw(LASTLINE - 2, 67, " onl.ml: %s", bm_config.onlymults ? "yes" : "no");
514
515 attrset(COLOR_PAIR(CB_NEW) | A_STANDOUT);
516 mvprintw(LASTLINE - 1, 67, " MULTI");
517
518 attrset(COLOR_PAIR(CB_NEW) | A_BOLD);
519 printw(" NEW");
520
521 attrset(COLOR_PAIR(CB_NORMAL));
522 mvprintw(LASTLINE, 67, "SPOT");
523
524 attrset(COLOR_PAIR(CB_OLD));
525 printw(" OLD");
526
527 attrset(COLOR_PAIR(CB_DUPE) | A_BOLD);
528 printw(" dupe");
529
530 attroff(A_BOLD | A_STANDOUT);
531
532 move(cury, curx); /* reset cursor */
533 }
534
535
536 /* helper function for bandmap display
537 * mark entries according to age, source and worked state. Mark new multis
538 * - new brigth blue
539 * - normal blue
540 * - aged brown
541 * - worked small caps */
colorize_spot(spot * data)542 void colorize_spot(spot *data) {
543 if (data -> timeout > SPOT_NORMAL)
544 attrset(COLOR_PAIR(CB_NEW) | A_BOLD);
545
546 else if (data -> timeout > SPOT_OLD)
547 attrset(COLOR_PAIR(CB_NORMAL));
548
549 else
550 attrset(COLOR_PAIR(CB_OLD));
551
552 if (bm_ismulti(NULL, data, data->band)) {
553 attrset(COLOR_PAIR(CB_NEW) | A_STANDOUT);
554 attron(A_STANDOUT);
555 }
556
557 else if (data->dupe && bm_config.showdupes) {
558 attrset(COLOR_PAIR(CB_DUPE) | A_BOLD);
559 attroff(A_STANDOUT);
560 }
561 }
562
563 /* helper function for bandmap display
564 * convert dupes to lower case
565 * add QTC flags for WAE contest
566 */
format_spot(spot * data)567 char *format_spot(spot *data) {
568 char *temp;
569 char *temp2;
570
571 if (qtcdirection > 0) {
572 temp = qtc_format(data->call);
573 } else
574 temp = g_strdup(data->call);
575
576 if (data->dupe && bm_config.showdupes) {
577 temp2 = temp;
578 temp = g_ascii_strdown(temp2, -1);
579 g_free(temp2);
580 }
581 return temp;
582 }
583
584
585 /* helper function for bandmap display
586 * shows formatted spot on actual cursor position
587 */
show_spot(spot * data)588 void show_spot(spot *data) {
589 attrset(COLOR_PAIR(CB_DUPE) | A_BOLD);
590 printw("%7.1f %c ", data->freq / 1000.,
591 (data->node == thisnode ? '*' : data->node));
592
593 char *temp = format_spot(data);
594 colorize_spot(data);
595 printw("%-12s", temp);
596 g_free(temp);
597 }
598
599
600 /* helper function for bandmap display
601 * shows spot on actual working frequency
602 */
show_spot_on_qrg(spot * data)603 void show_spot_on_qrg(spot *data) {
604
605 printw("%7.1f %c ", (data->freq / 1000.),
606 (data->node == thisnode ? '*' : data->node));
607
608 char *temp = format_spot(data);
609 printw("%-12s", temp);
610 g_free(temp);
611 }
612
613
614 /* helper function for bandmap display
615 * advance to next spot position
616 */
next_spot_position(int * y,int * x)617 void next_spot_position(int *y, int *x) {
618 *y += 1;
619 if (*y == LASTLINE + 1) {
620 *y = TOPLINE;
621 *x += SPOT_COLUMN_WIDTH;
622 }
623 }
624
625 /* helper function for bandmap display
626 * provide center frequency for display
627 *
628 * If we have a rig online, read the frequency from it.
629 * Otherwise calculate center frequency from band and mode
630 * as middle value of the band/mode corners.
631 */
bm_get_center(int band,int mode)632 freq_t bm_get_center(int band, int mode) {
633 freq_t centerfrequency;
634
635 if (trx_control)
636 return freq; /* return freq from rig */
637
638 /* calculate center frequency for current band and mode */
639 if (CWMODE == mode) {
640 centerfrequency = (bandcorner[band][0] + cwcorner[band]) / 2.;
641 } else if (SSBMODE == mode) {
642 centerfrequency = (ssbcorner[band] + bandcorner[band][1]) / 2.;
643 } else {
644 centerfrequency = (cwcorner[band] + ssbcorner[band]) / 2.;
645 }
646 return centerfrequency;
647 }
648
649
bandmap_show()650 void bandmap_show() {
651 /*
652 * display depending on filter state
653 * - all bands on/off
654 * - all mode on/off
655 * - dupes on/off
656 *
657 * If more entries to show than room in window, show around current frequency
658 *
659 * mark entries according to age, source and worked state. Mark new multis
660 * - new brigth blue
661 * - normal blue
662 * - aged black
663 * - worked small caps
664 * - new multi underlined
665 * - self announced stations
666 * small preceeding letter for reporting station
667 *
668 * maybe show own frequency as dashline in other color
669 * (maybee green highlighted)
670 * - highligth actual spot if near its frequency
671 *
672 * Allow selection of one of the spots (switches to S&P)
673 * - Ctrl-G as known
674 * - '.' and cursor plus 'Enter' \Todo
675 * - Test mouseclick.. \Todo
676 *
677 * '.' goes into map, shows help line above and supports
678 * - cursormovement
679 * - 'ESC' leaves mode
680 * - 'Enter' selects spot
681 * - 'B', 'D', 'M', 'O' switches filtering for band, dupes, mode and multiPlier on or off.
682 */
683
684 GList *list;
685 spot *data;
686 int curx, cury;
687 int bm_x, bm_y;
688 int i, j;
689 bool dupe, multi;
690
691 if (!bm_initialized) {
692 bm_init();
693 bm_initialized = 1;
694 }
695
696 /* acquire mutex
697 * do not add new spots to allspots during
698 * - aging and
699 * - filtering
700 * furthermore do not allow call lookup as long as
701 * filtered spot array is build anew */
702
703 pthread_mutex_lock(&bm_mutex);
704
705 /* make array of spots to display
706 * filter spotlist according to settings */
707
708 if (spots)
709 g_ptr_array_free(spots, TRUE); /* free array */
710
711 spots = g_ptr_array_sized_new(128); /* allocate new one */
712
713 list = allspots;
714
715 while (list) {
716 data = list->data;
717
718 /* if spot is allband or allmode is set or band or mode matches
719 * actual one than add it to the filtered 'spot' array
720 * drop spots on WARC bands if in contest mode
721 */
722 multi = bm_ismulti(NULL, data, data->band);
723 dupe = bm_isdupe(data->call, data->band);
724
725 if ((!contest || !IsWarcIndex(data->band)) &&
726 (bm_config.allband || (data->band == bandinx)) &&
727 (bm_config.allmode || (data->mode == trxmode)) &&
728 (bm_config.showdupes || !dupe) &&
729 (! bm_config.onlymults || multi)) {
730
731 data -> dupe = dupe;
732 g_ptr_array_add(spots, data);
733 }
734
735 list = list->next;
736 }
737
738 pthread_mutex_unlock(&bm_mutex);
739
740
741 /* afterwards display filtered list around own QRG +/- some offest
742 * (offset gets reset if we change frequency */
743
744 getyx(stdscr, cury, curx); /* remember cursor */
745
746 /* start in TOPLINE, column 0 */
747 bm_y = TOPLINE;
748 bm_x = 0;
749
750 /* clear space for bandmap */
751 attrset(COLOR_PAIR(CB_DUPE) | A_BOLD);
752
753 move(bm_y, 0); /* do not overwrite # frequency */
754 for (j = 0; j < 67; j++)
755 addch(' ');
756
757 for (i = bm_y + 1; i < LASTLINE + 1; i++) {
758 move(i, 0);
759 for (j = 0; j < 80; j++)
760 addch(' ');
761 }
762
763 /* show info text */
764 bm_show_info();
765
766 /* split bandmap into two parts below and above current QRG.
767 * Give both both parts equal size.
768 * If there are less spots then reserved in the half
769 * give the remaining room to the other half.
770 *
771 * These results in maximized usage of the bandmap display while
772 * trying to keep the actual frequency in the center.
773 */
774 unsigned int below_qrg = 0;
775 unsigned int on_qrg = 0;
776 unsigned int startindex, stopindex;
777
778 const freq_t centerfrequency = bm_get_center(bandinx, trxmode);
779
780 /* calc number of spots below your current QRG */
781 for (i = 0; i < spots->len; i++) {
782 data = g_ptr_array_index(spots, i);
783
784 if (data->freq <= centerfrequency - TOLERANCE)
785 below_qrg++;
786 else
787 break;
788 }
789
790 /* check if current QRG is on a spot */
791 if (below_qrg < spots->len) {
792 data = g_ptr_array_index(spots, below_qrg);
793
794 if (!(data->freq > centerfrequency + TOLERANCE))
795 on_qrg = 1;
796 }
797
798 /* calc the index into the spot array of the first spot to show */
799 {
800 unsigned int max_below;
801 unsigned int above_qrg = spots->len - below_qrg - on_qrg;
802
803 if (above_qrg < ((NR_SPOTS - 1) / 2)) {
804 max_below = NR_SPOTS - above_qrg - 1;
805 } else
806 max_below = NR_SPOTS / 2;
807
808 startindex = (below_qrg < max_below) ? 0 : (below_qrg - max_below);
809 }
810
811 /* calculate the index+1 of the last spot to show */
812 stopindex = (spots->len < startindex + NR_SPOTS - (1 - on_qrg))
813 ? spots->len
814 : (startindex + NR_SPOTS - (1 - on_qrg));
815
816 /* correct calculations if we have no rig frequency to show */
817 if (trx_control == 0) {
818 if (on_qrg) {
819 on_qrg = 0;
820 } else {
821 stopindex += 1;
822 }
823 if (spots->len < stopindex)
824 stopindex = spots->len;
825 }
826
827 /* show spots below QRG */
828 for (i = startindex; i < below_qrg; i++) {
829 move(bm_y, bm_x);
830 show_spot(g_ptr_array_index(spots, i));
831 next_spot_position(&bm_y, &bm_x);
832 }
833
834 /* show highlighted frequency marker or spot on QRG if rig control
835 * is active */
836 if (trx_control != 0) {
837 move(bm_y, bm_x);
838 attrset(COLOR_PAIR(C_HEADER) | A_STANDOUT);
839 if (!on_qrg) {
840 printw("%7.1f %s", centerfrequency / 1000.0, "============");
841 } else {
842 show_spot_on_qrg(g_ptr_array_index(spots, below_qrg));
843 }
844 next_spot_position(&bm_y, &bm_x);
845 }
846
847 /* show spots above QRG */
848 for (i = below_qrg + on_qrg; i < stopindex; i++) {
849 move(bm_y, bm_x);
850 show_spot(g_ptr_array_index(spots, i));
851 next_spot_position(&bm_y, &bm_x);
852 }
853
854 attroff(A_BOLD);
855 move(cury, curx); /* reset cursor */
856
857 refreshp();
858 }
859
860
861 /** allow control of bandmap features
862 */
bm_menu()863 void bm_menu() {
864 int curx, cury;
865 char c = -1;
866 int j;
867
868 getyx(stdscr, cury, curx); /* remember cursor */
869
870 attrset(COLOR_PAIR(C_LOG) | A_STANDOUT);
871 mvprintw(13, 0, " Toggle <B>and, <M>ode, <D>upes or <O>nly multi filter");
872 printw(" | any other - leave ");
873
874 c = toupper(key_get());
875 switch (c) {
876 case 'B':
877 bm_config.allband = 1 - bm_config.allband;
878 break;
879
880 case 'M':
881 bm_config.allmode = 1 - bm_config.allmode;
882 break;
883
884 case 'D':
885 bm_config.showdupes = 1 - bm_config.showdupes;
886 break;
887
888 case 'O':
889 bm_config.onlymults = 1 - bm_config.onlymults;
890 break;
891 }
892 bandmap_show(); /* refresh display */
893
894 move(13, 0);
895 for (j = 0; j < 80; j++)
896 addch(' ');
897
898 move(cury, curx);
899 refreshp();
900 }
901
copy_spot(spot * data)902 spot *copy_spot(spot *data) {
903 spot *result = NULL;
904
905 result = g_new(spot, 1);
906 result -> call = g_strdup(data -> call);
907 result -> freq = data -> freq;
908 result -> mode = data -> mode;
909 result -> band = data -> band;
910 result -> node = data -> node;
911 result -> timeout = data -> timeout;
912 result -> dupe = data -> dupe;
913 result -> cqzone = data -> cqzone;
914 result -> ctynr = data -> ctynr;
915 result -> pfx = g_strdup(data -> pfx);
916
917 return result;
918 }
919
920 /** Search partialcall in filtered bandmap
921 *
922 * Lookup given partial call in the list of filtered bandmap spots.
923 * Return a copy of the first entry found (means with teh lowest frequency).
924 *
925 * \param partialcall - part of call to look up
926 * \return spot * structure with a copy of the found spot
927 * or NULL if not found (You have to free the structure
928 * after use).
929 */
bandmap_lookup(char * partialcall)930 spot *bandmap_lookup(char *partialcall) {
931 spot *result = NULL;
932
933 if ((*partialcall != '\0') && (spots->len > 0)) {
934 int i;
935
936 pthread_mutex_lock(&bm_mutex);
937
938 for (i = 0; i < spots->len; i++) {
939 spot *data;
940 data = g_ptr_array_index(spots, i);
941
942 if (strstr(data->call, partialcall) != NULL) {
943
944 /* copy data into a new Spot structure */
945 result = copy_spot(data);
946
947 break;
948 }
949 }
950
951 pthread_mutex_unlock(&bm_mutex);
952
953 }
954 return result;
955 }
956
957 /** Lookup next call in filtered spotlist
958 *
959 * Starting at given frequency lookup the array of filtered spots for
960 * the next call up- or downwards.
961 * Apply some headroom for frequency comparison (see problem with ORION rig
962 * (Dec2011).
963 * Returns a copy of the spot data or NULL if no such entry.
964 *
965 * \param upwards - lookup upwards if not 0
966 * \param freq - frequency to start from
967 *
968 * \return spot * structure with a copy of the found spot
969 * or NULL if not found (You have to free the structure
970 * after use).
971 */
972
bandmap_next(unsigned int upwards,freq_t freq)973 spot *bandmap_next(unsigned int upwards, freq_t freq) {
974 spot *result = NULL;
975
976 if (spots->len > 0) {
977 int i;
978
979 pthread_mutex_lock(&bm_mutex);
980
981 if (upwards) {
982
983 for (i = 0; i < spots->len; i++) {
984 spot *data;
985 data = g_ptr_array_index(spots, i);
986
987 if ((data->freq > freq + TOLERANCE / 2) &&
988 (!bm_config.skipdupes || data->dupe == 0)) {
989 /* copy data into a new Spot structure */
990 result = copy_spot(data);
991
992 break;
993 }
994 }
995 } else {
996 for (i = spots->len - 1; i >= 0; i--) {
997 spot *data;
998 data = g_ptr_array_index(spots, i);
999
1000 if ((data->freq < freq - TOLERANCE / 2) &&
1001 (!bm_config.skipdupes || data->dupe == 0)) {
1002 /* copy data into a new Spot structure */
1003 result = copy_spot(data);
1004
1005 break;
1006 }
1007 }
1008 }
1009 pthread_mutex_unlock(&bm_mutex);
1010
1011 }
1012 return result;
1013 }
1014
1015 /*
1016 * copy string to buffer but truncate it to n characters
1017 * If truncated show it by replacing last two chars by '..'
1018 * The buffer has to be at least n+1 chars long.
1019 */
str_truncate(char * buffer,char * string,int n)1020 void str_truncate(char *buffer, char *string, int n) {
1021 if (strlen(string) > n) {
1022 g_strlcpy(buffer, string, n - 1); /* truncate to n-2 chars */
1023 strcat(buffer, "..");
1024 } else {
1025 g_strlcpy(buffer, string, n + 1); /* copy up to n chars */
1026 }
1027 }
1028
1029 /*
1030 * format bandmap call output for WAE
1031 * - prepare and return a temporary string from call and number of QTC's
1032 * (if any)
1033 */
qtc_format(char * call)1034 char *qtc_format(char *call) {
1035 char tcall[15];
1036 char qtcflag;
1037 struct t_qtc_store_obj *qtc_temp_ptr;
1038
1039 qtc_temp_ptr = qtc_get(call);
1040 qtcflag = qtc_get_value(qtc_temp_ptr);
1041
1042 if (qtc_temp_ptr->total <= 0 && qtcflag == '\0') {
1043 str_truncate(tcall, call, SPOT_CALL_WIDTH);
1044 } else {
1045 str_truncate(tcall, call, SPOT_CALL_WIDTH - 2);
1046 sprintf(tcall + strlen(tcall), " %c", qtcflag);
1047 }
1048 return g_strdup(tcall);
1049 }
1050
1051
1052 /** Search filtered bandmap for a spot near the given frequency
1053 *
1054 * Return the call found at that frequency or NULL if no spot found
1055 *
1056 * \param dest - place to put the call in
1057 * \param freq - the frequency where to look for a spot
1058 */
get_spot_on_qrg(char * dest,freq_t freq)1059 void get_spot_on_qrg(char *dest, freq_t freq) {
1060
1061 *dest = '\0';
1062
1063 if (spots->len > 0) {
1064 int i;
1065
1066 pthread_mutex_lock(&bm_mutex);
1067
1068 for (i = 0; i < spots->len; i++) {
1069 spot *data;
1070 data = g_ptr_array_index(spots, i);
1071
1072 if ((fabs(data->freq - freq) < TOLERANCE) &&
1073 (!bm_config.skipdupes || data->dupe == 0)) {
1074 strcpy(dest, data->call);
1075 break;
1076 }
1077 }
1078
1079 pthread_mutex_unlock(&bm_mutex);
1080
1081 }
1082 }
1083