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