1 /*
2  * Tlf - contest logging program for amateur radio operators
3  * Copyright (C) 2001-2002-2003 Rein Couperus <pa0rct@amsat.org>
4  *                    2010-2011 Thomas Beierlein <tb@forth-ev.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #include <glib.h>
22 #include <math.h>
23 #include <string.h>
24 #include <time.h>
25 
26 #include "dxcc.h"
27 #include "get_time.h"
28 #include "globalvars.h"
29 #include "getwwv.h"
30 #include "sunup.h"
31 #include "qrb.h"
32 #include "tlf_panel.h"
33 #include "ui_utils.h"
34 
35 
36 // message splitters:
37 // line[0] - original line, content can be modified in-place
38 // line[1],line[2] - pointers to subsequent lines within line[0], initially NULL
39 
split_condx(const int len,char ** line)40 static void split_condx(const int len, char **line) {
41     if (len < 40) {
42 	return;
43     }
44     char *p = line[0] + 39;
45     while (*p != ' ' && p > line[0]) {    // find a space before index 39
46 	--p;
47     }
48     if (p > line[0]) {
49 	*p = 0;
50 	line[1] = p + 1;
51     } else {    // no space found, truncate (should not happen)
52 	line[0][40] = 0;
53     }
54 }
55 
56 
57 /*
58 WCY de DK0WCY-1 <12> : K=2 expK=0 A=19 R=11 SFI=74 SA=qui GMF=qui Au=no
59                       ^                           ^
60 
61 WCY de DK0WCY-1 <12> :
62 K=2 expK=0 A=19 R=11 SFI=74
63 SA=qui GMF=qui Au=no
64 */
split_wcy(const int len,char ** line)65 static void split_wcy(const int len, char **line) {
66 
67     char *p = strchr(line[0], ':'); // first colon
68     if (p != NULL && p + 2 < line[0] + len)  {
69 	p[1] = 0;
70 	line[1] = p + 2;
71 	p = strstr(line[1], "SFI");
72 	if (p != NULL) {
73 	    p = strchr(p, ' ');     // the space after SFI
74 	}
75 	if (p != NULL) {
76 	    p[0] = 0;
77 	    line[2] = p + 1;
78 	}
79     }
80 }
81 
82 /*
83 WWV de VE7CC <21>:   SFI=74, A=9, K=1, No Storms -> No Storms
84                     ^                 ^
85 
86 WWV de VE7CC <21>:
87 SFI=74, A=9, K=1,
88 No Storms -> No Storms
89 */
split_wwv(const int len,char ** line)90 static void split_wwv(const int len, char **line) {
91     char *p = strstr(line[0], "SFI");
92     if (p != NULL && p - 1 >= line[0])  {
93 	p[-1] = 0;  // the space before SFI
94 	line[1] = p;
95 	p = strstr(line[1], "K=");
96 	if (p != NULL) {
97 	    p = strchr(p, ' '); // the space after K
98 	}
99 	if (p != NULL) {
100 	    p[0] = 0;
101 	    line[2] = p + 1;
102 	}
103     }
104 }
105 
show_condx(WINDOW * win)106 static void show_condx(WINDOW *win) {
107     if (lastwwv[0] == 0) {
108 	mvwaddstr(win, 10, 40, "Condx: n/a");  // no data
109 	return;
110     }
111 
112     static GRegex *squash_regex = NULL;
113     if (squash_regex == NULL) {
114 	squash_regex = g_regex_new(" {2,}", 0, 0, NULL);
115     }
116 
117     // squash spaces and show condx
118     gchar *condx = g_regex_replace_literal(squash_regex, lastwwv, -1, 0, "  ", 0,
119 					   NULL);
120     char *line[3] = {condx, NULL, NULL};
121     split_condx(strlen(condx), line);
122 
123     mvwaddstr(win, 10, 40, line[0]);
124     if (line[1] != NULL) {
125 	mvwaddstr(win, 11, 40, line[1]);
126     }
127 
128     g_free(condx);
129 
130     // show original WWV message, split into up to 3 lines if needed
131     line[0] = g_strdup(lastwwv_raw);
132     line[1] = line[2] = NULL;
133     const int len = strlen(lastwwv_raw);
134     if (strncmp(lastwwv_raw, "WCY", 3) == 0) {
135 	split_wcy(len, line);
136     } else {
137 	split_wwv(len, line);
138     }
139 
140     for (int i = 0; i < 3; ++i) {
141 	if (line[i] != NULL) {
142 	    mvwaddstr(win, 14 + i, 40, line[i]);
143 	}
144     }
145 
146     g_free(line[0]);
147 }
148 
149 /*********************************************************************
150  Code below is based on MICROMUF.PAS
151 
152 http://www.classiccmp.org/cpmarchives/cpm/Software/WalnutCD/cpm/hamradio/micromuf.pas
153 
154  Original attribution:
155 ---------------------------------------------------------------------
156   This program uses 'MINI-F2' devised by R. Fricker (BBC external
157   services) for FO-F calculations and L.M. Muggleton's formula for
158   FO-E calculations.
159 
160 
161   For the L.U.F. a minimum useable fieldstrength of 30 DBUV at the
162   receiver and 250 KW of transmitter power (aerial gain: 18 DBI) are
163   assumed.  The L.U.F. is derived from absorption calculations based
164   on the work of Piggot, George, Samuel, and Bradley.  In spite of the
165   program's simplicity it gives a good impression of the ionosphere's
166   behaviour and can be used for propagation predictions.
167 
168             Hans Bakhuizen
169             Propagation Unit; Frequency Bureau
170             Radio Netherlands
171             P.O. Box 222
172             1200 JG Hilversum Holland
173 
174 (C) Copyright Media Network June 1984
175 
176 Translation from Basic into TURBO Pascal by Jonathan D Ogden on
177 September 26, 1986.
178 
179        Jonathan D Ogden
180        402 e Daniel
181        Champaign, Il 61820 USA
182 ---------------------------------------------------------------------
183 *********************************************************************/
184 
185 int month;
186 
187 extern struct tm *time_ptr;
188 
189 double yt;
190 double xt;
191 double yr;
192 double xr;
193 
194 int t;
195 
196 double xn, xs, ls, h, ff, x, yn_, k, lm, u, a;
197 
198 
power(man,ex)199 static double power(man, ex)
200 double man, ex;
201 {
202     return exp(ex * log(man));
203 }
204 
interlat()205 static void interlat() {
206     double yi, q;
207     /* Intermediate Latitude & Longitude calculations */
208     q = cos(u / RADIAN) * cos(xt / RADIAN) * sin(k * lm / RADIAN);
209     x = q + sin(xt / RADIAN) * cos(k * lm / RADIAN);
210     xn = atan(x / sqrt(1 - x * x + 1e-12)) * RADIAN;
211     q = cos(k * lm / RADIAN) - sin(xt / RADIAN) * sin(xn / RADIAN);
212     yi = (M_PI_2 - atan(x / sqrt(1 - x * x + 1e-12))) * RADIAN;
213 
214     if (u < 180.0)
215 	yi = -yi;
216     yn_ = yt + yi;
217     if (yn_ > 180.0)
218 	yn_ -= 360.0;
219     if (yn_ < -180.0)
220 	yn_ += 360.0;
221 }
222 
mini_f2()223 static void mini_f2() {
224     double temp, tl, yf, ex, yz, yg, zo, z, mh, xh, wx, sx, ty, fo, sf;
225 
226     yz = yn_;
227     if (yn_ < -160.0)
228 	yz = yn_ + 360.0;
229     yg = (20.0 - yz) / 50;
230     temp = 1 - (yg / 7.0);
231     zo = 20.0 * yg / (1 + yg + yg * yg) + 5.0 * (temp * temp);
232     z = xn - zo;
233     tl = t - yn_ / 15.0;
234     if (tl > 24.0)
235 	tl -= 24.0;
236     if (tl < 0.0)
237 	tl += 24.0;
238 
239     mh = month;
240     if (z <= 0.0) {
241 	z = -z;
242 	mh += 6;
243     }
244 
245     xh = cos(30.0 * (mh - 6.5) / RADIAN);	/* 1 week delay on equinoxes */
246     sx = (fabs(xh) + xh) / 2.0;	/* F-layer local summer variance */
247     wx = (fabs(xh) - xh) / 2.0;	/* F-layer local winter variance */
248 
249     if (z > 77.5)
250 	z = 77.5;
251     ty = tl;
252     if (ty < 5.0)
253 	ty = tl + 24.0;
254     yf = (ty - 14.0 - sx * 2.0 + wx * 2.0 - ssn_r / 175.0) *
255 	 (7.0 - sx * 3.0 + wx * 4.0 - ssn_r / (150.0 - wx * 75.0));
256 
257     if (fabs(yf) > 60.0)
258 	yf = 60.0;
259     x = 1 + ssn_r / (175.0 + sx * 175.0);
260     fo = 6.5 * x * cos(yf / RADIAN) *
261 	 sqrt(cos((z - sx * 5.0 + wx * 5.0) / RADIAN));
262     ex = -0.5;
263     temp = cos(a / RADIAN) * 6367.0 / (6367.0 + h);
264     sf = power(1.0 - temp * temp, ex);
265     ff = fo * sf;
266     ff *= 1.15;
267 
268 }
269 
e_layer()270 static void e_layer() {
271     double temp, fe, se, ex, xz, q;
272 
273     q = sin(xn / RADIAN) * sin(xs / RADIAN);
274     x = q +
275 	cos(xn / RADIAN) * cos(xs / RADIAN) * cos((yn_ - 15.0 * (t - 12.0)) / RADIAN);
276     xz = (M_PI_2 - atan(x / sqrt(1 - x * x + 1e-12))) * RADIAN;
277 
278     if (xz <= 85.0) {
279 	ex = 1.0 / 3.0;
280 	fe = 3.4 * (1.0 + 0.0016 * ssn_r) * power(cos(xz / RADIAN), ex);
281     } else {
282 	ex = -0.5;
283 	fe = 3.4 * (1.0 + 0.0016 * ssn_r) * power(xz - 80.0, ex);
284     }
285 
286     temp = cos(a / RADIAN);
287     se = power(1.0 - (0.965 * temp * temp), ex);
288 //se /= 4;
289 //se += 1;
290     ls = 0.028 * fe * fe * se;
291 // ls *= 15;
292 
293 }
294 
muf(void)295 void muf(void) {
296     extern double QTH_Lat;
297     extern double QTH_Long;
298     extern double DEST_Lat;
299     extern double DEST_Long;
300     double sunrise;
301     double sundown;
302 
303     int row;
304     double la, l, mf, lh;
305     dxcc_data *dx;
306     char country[40];
307     int i;
308     char time_buf[25];
309     int su, sd, su_min, sd_min;
310     double ab;
311     double n;
312     double td;
313     double q;
314 
315     n = 0.0;
316 
317     xt = QTH_Lat;
318     yt = QTH_Long;
319     xr = DEST_Lat;
320     yr = DEST_Long;
321 
322     dx = dxcc_by_index(countrynr);
323     strncpy(country, dx->countryname, 25);
324 
325 
326     get_time();
327     strftime(time_buf, sizeof(time_buf), "    %d-%b-%Y %H:%M ", time_ptr);
328 
329     q = sin(xt / RADIAN) * sin(xr / RADIAN);
330     x = q + cos(xt / RADIAN) * cos(xr / RADIAN) * cos(yt / RADIAN - yr / RADIAN);
331     la = (M_PI_2 - atan(x / sqrt(1 - x * x + 1e-12))) * RADIAN;
332     l = ARC_IN_KM * la;
333     q = sin(xr / RADIAN) - sin(xt / RADIAN) * cos(la / RADIAN);
334     x = q / cos(xt / RADIAN) / sin(la / RADIAN);
335     u = (M_PI_2 - atan(x / sqrt(1 - x * x + 1e-12))) * RADIAN;
336     if (yt - yr <= 0)
337 	u = 360 - u;
338     h = 275 + ssn_r / 2;
339     xs = 23.4 * cos(30 * (month - 6.25) / RADIAN);
340     n++;
341     lh = l / n;
342     while (lh > 4000.0) {
343 	n++;
344 	lh = l / n;
345     }
346 
347     lm = la / n;
348     a = atan((cos(0.5 * lm / RADIAN) -
349 	      6367.0 / (h + 6367.0)) / sin(0.5 * lm / RADIAN)) * RADIAN;
350 
351     while (a < 1.5) {
352 	n++;
353 	lh /= n;
354 	while (lh > 4000.0) {
355 	    n++;
356 	    lh = l / n;
357 	}
358 	lm = la / n;
359 	a = atan((cos(0.5 * lm / RADIAN) -
360 		  6367.0 / (h + 6367.0)) / sin(0.5 * lm / RADIAN)) * RADIAN;
361     }
362 
363     WINDOW *win = newwin(LINES, 80, 0, 0);
364     PANEL *pan = new_panel(win);
365 
366     wclear(win);
367     wattron(win, modify_attr(COLOR_PAIR(C_WINDOW) | A_STANDOUT));
368 
369     for (i = 0; i < LINES; i++)
370 	mvwprintw(win, i, 0, backgrnd_str);
371 
372     mvwprintw(win, 1, 0, "        SSN: %3.0f ", ssn_r);
373 
374     if (countrynr != 0) {
375 	mvwprintw(win, 1, 40, "%s", country);
376 	mvwprintw(win, 3, 40, "Dist  : %5.0f km", l);
377 	mvwprintw(win, 4, 40, "Azim  :   %3.0f degrees", u);
378 	mvwprintw(win, 5, 40, "F-hops:    %2.0f", n);
379 
380 	sunup(xr, &sunrise, &sundown);	/* calculate local sunup and down
381                                                at destination lattitude */
382 	/* transform to UTC based on longitude from country description */
383 	td = (yr * 4) / 60 ; 	/* 4 degree/min */
384 	sunrise += td;
385 	sundown += td;
386 
387 	if (sunrise >= 24.0)
388 	    sunrise -= 24.0;
389 	else if (sunrise <= 0.0)
390 	    sunrise += 24.0;
391 
392 	if (sundown >= 24.0)
393 	    sundown -= 24.0;
394 	else if (sundown <= 0.0)
395 	    sundown += 24.0;
396 
397 	su = (int)(sunrise);
398 	sd = (int)(sundown);
399 
400 	su_min = (int)((sunrise - su) * 60);
401 	sd_min = (int)((sundown - sd) * 60);
402 
403 	mvwprintw(win, 3, 0, time_buf);
404 	mvwprintw(win, 7, 40, "Sun   : %02d:%02d-%02d:%02d UTC", su, su_min, sd,
405 		  sd_min);
406     }
407 
408 
409     show_condx(win);
410 
411     // show frequency chart
412     q = 34.0;
413     row = 4;
414     while (q >= 2.0) {
415 	if ((row == 7) || (row == 10) || (row == 14) || (row == 17)) {
416 	    mvwprintw(win, row, 0, "|_________________________|%2.0f", q);
417 
418 	} else
419 	    mvwprintw(win, row, 0, "|                         |%2.0f", q);	/* 25 spaces */
420 	q -= 2.0;
421 	row++;
422     }
423     mvwprintw(win, 20, 0, "---------------------------");	/* 27 dashes */
424     mvwprintw(win, 21, 0, " 0 2 4 6 8 10  14  18  22 H (UTC)");
425     mvwprintw(win, 4, 30, "MHz");
426 
427     if (countrynr != 0) {
428 	for (t = 1; t <= 24; t++) {
429 	    ab = 0.0;
430 	    k = 0.5;
431 	    interlat();
432 	    mini_f2();
433 	    mf = ff;
434 
435 	    k = n - 0.5;
436 	    interlat();
437 	    mini_f2();
438 
439 	    if (ff < mf)
440 		mf = ff;
441 	    double ve = 21 - (long) floor(mf / 2.0 + 0.5);
442 
443 	    double ho = t + 1;
444 	    if (ve < 4)
445 		ve = 4;
446 	    mvwprintw(win, (int) ve, (int) ho, "+");
447 
448 	    while (k <= n - 0.25) {
449 		interlat();
450 		e_layer();
451 		ab += ls;
452 		k += 0.5;
453 	    }
454 	    ve = 20 - (long) floor(ab + 0.5);
455 	    if (ve < 4)
456 		ve = 4;
457 	    if (ve > 20)
458 		ve = 20;
459 
460 	    mvwprintw(win, (int) ve, (int) ho, "-");
461 	}
462     }
463 
464     mvwprintw(win, 23, 0, " --- Press a key to continue --- ");
465     refreshp();
466     key_get();
467 
468     hide_panel(pan);
469     del_panel(pan);
470     delwin(win);
471 }
472