1 /*
2     Gpredict: Real-time satellite tracking and orbit prediction program
3 
4     Copyright (C)  2001-2017  Alexandru Csete, OZ9AEC.
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, visit http://www.fsf.org/
18 */
19 #ifdef HAVE_CONFIG_H
20 #include <build-config.h>
21 #endif
22 #include <gtk/gtk.h>
23 
24 #include "gtk-sat-data.h"
25 #include "locator.h"
26 #include "pass-to-txt.h"
27 #include "predict-tools.h"
28 #include "sat-cfg.h"
29 #include "sat-vis.h"
30 #include "sat-pass-dialogs.h"
31 #include "sgpsdp/sgp4sdp4.h"
32 #include "time-tools.h"
33 
34 
35 #define NUMCOL 19
36 #define COL_WIDTH 8
37 
38 
39 const gchar    *SPCT[] = {
40     N_(" Time"),
41     N_("  Az  "),               /* 6 */
42     N_("   El "),
43     N_("  Ra  "),
44     N_("  Dec "),
45     N_("Range"),
46     N_(" Rate "),
47     N_("  Lat "),
48     N_("  Lon  "),
49     N_(" SSP  "),
50     N_("Footp"),
51     N_(" Alt "),
52     N_(" Vel "),
53     N_(" Dop "),
54     N_(" Loss "),
55     N_(" Del "),
56     N_("  MA  "),
57     N_(" Pha  "),
58     N_("Vis")
59 };
60 
61 
62 const guint     COLW[] = {
63     0,
64     6,
65     6,
66     6,
67     6,
68     5,
69     6,
70     6,
71     7,
72     6,
73     5,
74     5,
75     5,
76     5,
77     6,
78     5,
79     6,
80     6,
81     3
82 };
83 
84 
85 const gchar    *MPCT[] = {
86     N_(" AOS"),
87     N_("  TCA"),                /* 6 */
88     N_("  LOS"),
89     N_("Duration"),
90     N_("Max El"),
91     N_("AOS Az"),
92     N_("Max El Az"),
93     N_("LOS Az"),
94     N_("Orbit"),
95     N_("Vis")
96 };
97 
98 
99 const guint     MCW[] = {
100     0,
101     0,
102     0,
103     8,
104     6,
105     6,
106     9,
107     6,
108     5,
109     3
110 };
111 
112 
113 static void     Calc_RADec(gdouble jul_utc, gdouble saz, gdouble sel,
114                            qth_t * qth, obs_astro_t * obs_set);
115 
pass_to_txt_pgheader(pass_t * pass,qth_t * qth,gint fields)116 gchar          *pass_to_txt_pgheader(pass_t * pass, qth_t * qth, gint fields)
117 {
118     gboolean        loc;
119     gchar          *utc;
120     gchar          *header;
121     gchar           aosbuff[TIME_FORMAT_MAX_LENGTH];
122     gchar           losbuff[TIME_FORMAT_MAX_LENGTH];
123     gchar          *fmtstr;
124     time_t          aos, los;
125     guint           size;
126 
127     (void)fields;               /* avoid unused parameter compiler warning */
128 
129     fmtstr = sat_cfg_get_str(SAT_CFG_STR_TIME_FORMAT);
130     loc = sat_cfg_get_bool(SAT_CFG_BOOL_USE_LOCAL_TIME);
131     aos = (pass->aos - 2440587.5) * 86400.;
132     los = (pass->los - 2440587.5) * 86400.;
133 
134     if (loc)
135     {
136         utc = g_strdup(_("Local"));
137 
138         /* AOS */
139         size =
140             strftime(aosbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, localtime(&aos));
141         if (size == 0)
142             /* size > MAX_LENGTH */
143             aosbuff[TIME_FORMAT_MAX_LENGTH - 1] = '\0';
144 
145         /* LOS */
146         size =
147             strftime(losbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, localtime(&los));
148         if (size == 0)
149             /* size > MAX_LENGTH */
150             losbuff[TIME_FORMAT_MAX_LENGTH - 1] = '\0';
151 
152     }
153     else
154     {
155         utc = g_strdup(_("UTC"));
156 
157         /* AOS */
158         size = strftime(aosbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, gmtime(&aos));
159         if (size == 0)
160             /* size > MAX_LENGTH */
161             aosbuff[TIME_FORMAT_MAX_LENGTH - 1] = '\0';
162 
163         /* LOS */
164         size = strftime(losbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, gmtime(&los));
165         if (size == 0)
166             /* size > MAX_LENGTH */
167             losbuff[TIME_FORMAT_MAX_LENGTH - 1] = '\0';
168     }
169 
170     header = g_strdup_printf(_("Pass details for %s (orbit %d)\n"
171                                "Observer: %s, %s\n"
172                                "LAT:%.2f LON:%.2f\n"
173                                "AOS: %s %s\n"
174                                "LOS: %s %s\n"),
175                              pass->satname, pass->orbit,
176                              qth->name, qth->loc, qth->lat, qth->lon,
177                              aosbuff, utc, losbuff, utc);
178 
179     g_free(utc);
180 
181     return header;
182 }
183 
pass_to_txt_tblheader(pass_t * pass,qth_t * qth,gint fields)184 gchar          *pass_to_txt_tblheader(pass_t * pass, qth_t * qth, gint fields)
185 {
186     gchar          *fmtstr;
187     guint           size;
188     gchar           tbuff[TIME_FORMAT_MAX_LENGTH];
189     guint           i;
190     guint           linelength = 0;
191     gchar          *line;
192     gchar          *sep;
193     gchar          *buff;
194 
195     (void)qth;                  /* avoid unused parameter compiler warning */
196 
197     /* first, get the length of the time field */
198     fmtstr = sat_cfg_get_str(SAT_CFG_STR_TIME_FORMAT);
199     size = daynum_to_str(tbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, pass->aos);
200 
201     g_free(fmtstr);
202 
203     /* add time column */
204     buff = g_strnfill(size - 4, ' ');
205     line = g_strconcat(_(SPCT[0]), buff, NULL);
206     linelength = size + 1;
207     g_free(buff);
208 
209     for (i = 1; i < NUMCOL; i++)
210     {
211 
212         if (fields & (1 << i))
213         {
214 
215             /* add column to line */
216             buff = g_strconcat(line, " ", _(SPCT[i]), NULL);
217             g_free(line);
218             line = g_strdup(buff);
219             g_free(buff);
220 
221             /* update line length */
222             linelength += COLW[i] + 1;
223         }
224     }
225 
226     /* add separator line */
227     sep = g_strnfill(linelength, '-');
228     buff = g_strdup_printf("%s\n%s\n%s\n", sep, line, sep);
229     g_free(line);
230     g_free(sep);
231 
232     return buff;
233 }
234 
pass_to_txt_tblcontents(pass_t * pass,qth_t * qth,gint fields)235 gchar          *pass_to_txt_tblcontents(pass_t * pass, qth_t * qth,
236                                         gint fields)
237 {
238     gchar          *fmtstr;
239     gchar           tbuff[TIME_FORMAT_MAX_LENGTH];
240     guint           i, num;
241     gchar          *line;
242     gchar          *data = NULL;
243     gchar          *buff;
244     pass_detail_t  *detail;
245     obs_astro_t     astro;
246     gdouble         ra, dec, numf;
247     gchar          *ssp;
248 
249     /* first, get the length of the time field */
250     fmtstr = sat_cfg_get_str(SAT_CFG_STR_TIME_FORMAT);
251     daynum_to_str(tbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, pass->aos);
252 
253     /* get number of rows */
254     num = g_slist_length(pass->details);
255 
256     for (i = 0; i < num; i++)
257     {
258 
259         /* get detail */
260         detail = PASS_DETAIL(g_slist_nth_data(pass->details, i));
261 
262         /* time */
263         daynum_to_str(tbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, detail->time);
264 
265         line = g_strdup_printf(" %s", tbuff);
266 
267         /* Az */
268         if (fields & SINGLE_PASS_FLAG_AZ)
269         {
270             buff = g_strdup_printf("%s %6.2f", line, detail->az);
271             g_free(line);
272             line = g_strdup(buff);
273             g_free(buff);
274         }
275 
276         /* El */
277         if (fields & SINGLE_PASS_FLAG_EL)
278         {
279             buff = g_strdup_printf("%s %6.2f", line, detail->el);
280             g_free(line);
281             line = g_strdup(buff);
282             g_free(buff);
283         }
284 
285         /* Ra */
286         if (fields & SINGLE_PASS_FLAG_RA)
287         {
288             Calc_RADec(detail->time, detail->az, detail->el, qth, &astro);
289             ra = Degrees(astro.ra);
290             buff = g_strdup_printf("%s %6.2f", line, ra);
291             g_free(line);
292             line = g_strdup(buff);
293             g_free(buff);
294         }
295 
296         /* Dec */
297         if (fields & SINGLE_PASS_FLAG_DEC)
298         {
299             Calc_RADec(detail->time, detail->az, detail->el, qth, &astro);
300             dec = Degrees(astro.dec);
301             buff = g_strdup_printf("%s %6.2f", line, dec);
302             g_free(line);
303             line = g_strdup(buff);
304             g_free(buff);
305         }
306 
307         /* Range */
308         if (fields & SINGLE_PASS_FLAG_RANGE)
309         {
310             buff = g_strdup_printf("%s %5.0f", line, detail->range);
311             g_free(line);
312             line = g_strdup(buff);
313             g_free(buff);
314         }
315 
316         /* Range Rate */
317         if (fields & SINGLE_PASS_FLAG_RANGE_RATE)
318         {
319             buff = g_strdup_printf("%s %6.3f", line, detail->range_rate);
320             g_free(line);
321             line = g_strdup(buff);
322             g_free(buff);
323         }
324 
325         /* Lat */
326         if (fields & SINGLE_PASS_FLAG_LAT)
327         {
328             buff = g_strdup_printf("%s %6.2f", line, detail->lat);
329             g_free(line);
330             line = g_strdup(buff);
331             g_free(buff);
332         }
333 
334         /* Lon */
335         if (fields & SINGLE_PASS_FLAG_LON)
336         {
337             buff = g_strdup_printf("%s %7.2f", line, detail->lon);
338             g_free(line);
339             line = g_strdup(buff);
340             g_free(buff);
341         }
342 
343         /* SSP */
344         if (fields & SINGLE_PASS_FLAG_SSP)
345         {
346             ssp = g_try_malloc(7);
347             if (longlat2locator(detail->lon, detail->lat, ssp, 3) == RIG_OK)
348             {
349                 buff = g_strdup_printf("%s %s", line, ssp);
350                 g_free(line);
351                 line = g_strdup(buff);
352                 g_free(buff);
353             }
354             g_free(ssp);
355         }
356 
357         /* Footprint */
358         if (fields & SINGLE_PASS_FLAG_FOOTPRINT)
359         {
360             buff = g_strdup_printf("%s %5.0f", line, detail->footprint);
361             g_free(line);
362             line = g_strdup(buff);
363             g_free(buff);
364         }
365 
366         /* Alt */
367         if (fields & SINGLE_PASS_FLAG_ALT)
368         {
369             buff = g_strdup_printf("%s %5.0f", line, detail->alt);
370             g_free(line);
371             line = g_strdup(buff);
372             g_free(buff);
373         }
374 
375         /* Vel */
376         if (fields & SINGLE_PASS_FLAG_VEL)
377         {
378             buff = g_strdup_printf("%s %5.3f", line, detail->velo);
379             g_free(line);
380             line = g_strdup(buff);
381             g_free(buff);
382         }
383 
384         /* Doppler */
385         if (fields & SINGLE_PASS_FLAG_DOPPLER)
386         {
387             numf = -100.0e06 * (detail->range_rate / 299792.4580);
388             buff = g_strdup_printf("%s %5.0f", line, numf);
389             g_free(line);
390             line = g_strdup(buff);
391             g_free(buff);
392         }
393 
394         /* Loss */
395         if (fields & SINGLE_PASS_FLAG_LOSS)
396         {
397             numf = 72.4 + 20.0 * log10(detail->range);  // dB
398             buff = g_strdup_printf("%s %6.2f", line, numf);
399             g_free(line);
400             line = g_strdup(buff);
401             g_free(buff);
402         }
403 
404         /* Delay */
405         if (fields & SINGLE_PASS_FLAG_DELAY)
406         {
407             numf = detail->range / 299.7924580; // msec
408             buff = g_strdup_printf("%s %5.2f", line, numf);
409             g_free(line);
410             line = g_strdup(buff);
411             g_free(buff);
412         }
413 
414         /* MA */
415         if (fields & SINGLE_PASS_FLAG_MA)
416         {
417             buff = g_strdup_printf("%s %6.2f", line, detail->ma);
418             g_free(line);
419             line = g_strdup(buff);
420             g_free(buff);
421         }
422 
423         /* Phase */
424         if (fields & SINGLE_PASS_FLAG_PHASE)
425         {
426             buff = g_strdup_printf("%s %6.2f", line, detail->phase);
427             g_free(line);
428             line = g_strdup(buff);
429             g_free(buff);
430         }
431 
432         /* Visibility */
433         if (fields & SINGLE_PASS_FLAG_VIS)
434         {
435             buff = g_strdup_printf("%s  %c", line, vis_to_chr(detail->vis));
436             g_free(line);
437             line = g_strdup(buff);
438             g_free(buff);
439         }
440 
441         /* append line to return string */
442         if (i == 0)
443         {
444             data = g_strdup_printf("%s\n", line);
445             g_free(line);
446         }
447         else
448         {
449             buff = g_strconcat(data, line, "\n", NULL);
450             g_free(data);
451             data = g_strdup(buff);
452             g_free(buff);
453         }
454     }
455 
456     g_free(fmtstr);
457 
458     return data;
459 }
460 
passes_to_txt_pgheader(GSList * passes,qth_t * qth,gint fields)461 gchar          *passes_to_txt_pgheader(GSList * passes, qth_t * qth,
462                                        gint fields)
463 {
464     gchar          *header;
465     pass_t         *pass;
466 
467     (void)fields;
468 
469     pass = PASS(g_slist_nth_data(passes, 0));
470 
471     header = g_strdup_printf(_("Upcoming passes for %s\n"
472                                "Observer: %s, %s\n"
473                                "LAT:%.2f LON:%.2f\n"),
474                              pass->satname, qth->name, qth->loc,
475                              qth->lat, qth->lon);
476 
477     return header;
478 }
479 
passes_to_txt_tblheader(GSList * passes,qth_t * qth,gint fields)480 gchar          *passes_to_txt_tblheader(GSList * passes, qth_t * qth,
481                                         gint fields)
482 {
483     gchar          *fmtstr;
484     guint           size;
485     gchar           tbuff[TIME_FORMAT_MAX_LENGTH];
486     guint           i;
487     guint           linelength = 0;
488     gchar          *line;
489     gchar          *sep;
490     gchar          *buff;
491     pass_t         *pass;
492 
493     (void)qth;
494 
495     /* first, get the length of the time field */
496     pass = PASS(g_slist_nth_data(passes, 0));
497     fmtstr = sat_cfg_get_str(SAT_CFG_STR_TIME_FORMAT);
498     size = daynum_to_str(tbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, pass->aos);
499 
500     g_free(fmtstr);
501 
502     /* add AOS, TCA, and LOS columns */
503     buff = g_strnfill(size - 3, ' ');
504     line =
505         g_strconcat(_(MPCT[0]), buff, _(MPCT[1]), buff, _(MPCT[2]), buff,
506                     NULL);
507     linelength = 3 * (size + 2);
508     g_free(buff);
509 
510     for (i = 3; i < 10; i++)
511     {
512         if (fields & (1 << i))
513         {
514 
515             /* add column to line */
516             buff = g_strconcat(line, "  ", _(MPCT[i]), NULL);
517             g_free(line);
518             line = g_strdup(buff);
519             g_free(buff);
520 
521             /* update line length */
522             linelength += MCW[i] + 2;
523         }
524     }
525 
526     /* add separator line */
527     sep = g_strnfill(linelength, '-');
528     buff = g_strdup_printf("%s\n%s\n%s\n", sep, line, sep);
529     g_free(line);
530     g_free(sep);
531 
532     return buff;
533 }
534 
passes_to_txt_tblcontents(GSList * passes,qth_t * qth,gint fields)535 gchar          *passes_to_txt_tblcontents(GSList * passes, qth_t * qth,
536                                           gint fields)
537 {
538     gchar          *fmtstr;
539     gchar           tbuff[TIME_FORMAT_MAX_LENGTH];
540     guint           i, num;
541     gchar          *line = NULL;
542     gchar          *data = NULL;
543     gchar          *buff;
544     pass_t         *pass;
545 
546     (void)qth;                  /* avoid unused parameter compiler warning */
547 
548     pass = PASS(g_slist_nth_data(passes, 0));
549 
550     /* first, get the length of the time field */
551     fmtstr = sat_cfg_get_str(SAT_CFG_STR_TIME_FORMAT);
552     daynum_to_str(tbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, pass->aos);
553 
554     /* get number of rows */
555     num = g_slist_length(passes);
556 
557     for (i = 0; i < num; i++)
558     {
559 
560         pass = PASS(g_slist_nth_data(passes, i));
561 
562         /* AOS */
563         daynum_to_str(tbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, pass->aos);
564 
565         line = g_strdup_printf(" %s", tbuff);
566 
567         /* TCA */
568         daynum_to_str(tbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, pass->tca);
569 
570         buff = g_strdup(line);
571         g_free(line);
572         line = g_strdup_printf("%s  %s", buff, tbuff);
573         g_free(buff);
574 
575         /* LOS */
576         daynum_to_str(tbuff, TIME_FORMAT_MAX_LENGTH, fmtstr, pass->los);
577 
578         buff = g_strdup(line);
579         g_free(line);
580         line = g_strdup_printf("%s  %s", buff, tbuff);
581         g_free(buff);
582 
583         /* Duration */
584         if (fields & (1 << MULTI_PASS_COL_DURATION))
585         {
586             guint           h, m, s;
587 
588             /* convert julian date to seconds */
589             s = (guint) ((pass->los - pass->aos) * 86400);
590 
591             /* extract hours */
592             h = (guint) floor(s / 3600);
593             s -= 3600 * h;
594 
595             /* extract minutes */
596             m = (guint) floor(s / 60);
597             s -= 60 * m;
598 
599             buff = g_strdup_printf("%s  %02d:%02d:%02d", line, h, m, s);
600             g_free(line);
601             line = g_strdup(buff);
602             g_free(buff);
603         }
604 
605         /* Max El */
606         if (fields & (1 << MULTI_PASS_COL_MAX_EL))
607         {
608             buff = g_strdup_printf("%s  %6.2f", line, pass->max_el);
609             g_free(line);
610             line = g_strdup(buff);
611             g_free(buff);
612         }
613 
614         /* AOS Az */
615         if (fields & (1 << MULTI_PASS_COL_AOS_AZ))
616         {
617             buff = g_strdup_printf("%s  %6.2f", line, pass->aos_az);
618             g_free(line);
619             line = g_strdup(buff);
620             g_free(buff);
621         }
622 
623         /* Max El Az */
624         if (fields & (1 << MULTI_PASS_COL_MAX_EL_AZ))
625         {
626             buff = g_strdup_printf("%s  %9.2f", line, pass->maxel_az);
627             g_free(line);
628             line = g_strdup(buff);
629             g_free(buff);
630         }
631 
632         /* LOS Az */
633         if (fields & (1 << MULTI_PASS_COL_LOS_AZ))
634         {
635             buff = g_strdup_printf("%s  %6.2f", line, pass->los_az);
636             g_free(line);
637             line = g_strdup(buff);
638             g_free(buff);
639         }
640 
641         /* Orbit */
642         if (fields & (1 << MULTI_PASS_COL_ORBIT))
643         {
644             buff = g_strdup_printf("%s  %5d", line, pass->orbit);
645             g_free(line);
646             line = g_strdup(buff);
647             g_free(buff);
648         }
649 
650         /* Visibility */
651         if (fields & (1 << MULTI_PASS_COL_VIS))
652         {
653             buff = g_strdup_printf("%s  %s", line, pass->vis);
654             g_free(line);
655             line = g_strdup(buff);
656             g_free(buff);
657         }
658 
659         /* append line to return string */
660         if (i == 0)
661         {
662             data = g_strdup_printf("%s\n", line);
663             g_free(line);
664         }
665         else
666         {
667             buff = g_strconcat(data, line, "\n", NULL);
668             g_free(data);
669             data = g_strdup(buff);
670             g_free(buff);
671         }
672 
673     }
674 
675     g_free(fmtstr);
676 
677     return data;
678 }
679 
Calc_RADec(gdouble jul_utc,gdouble saz,gdouble sel,qth_t * qth,obs_astro_t * obs_set)680 static void Calc_RADec(gdouble jul_utc, gdouble saz, gdouble sel,
681                        qth_t * qth, obs_astro_t * obs_set)
682 {
683     double          phi, theta, sin_theta, cos_theta, sin_phi, cos_phi,
684         az, el, Lxh, Lyh, Lzh, Sx, Ex, Zx, Sy, Ey, Zy, Sz, Ez, Zz,
685         Lx, Ly, Lz, cos_delta, sin_alpha, cos_alpha;
686     geodetic_t      geodetic;
687 
688 
689     geodetic.lon = qth->lon * de2ra;
690     geodetic.lat = qth->lat * de2ra;
691     geodetic.alt = qth->alt / 1000.0;
692     geodetic.theta = 0;
693 
694     az = saz * de2ra;
695     el = sel * de2ra;
696     phi = geodetic.lat;
697     theta = FMod2p(ThetaG_JD(jul_utc) + geodetic.lon);
698     sin_theta = sin(theta);
699     cos_theta = cos(theta);
700     sin_phi = sin(phi);
701     cos_phi = cos(phi);
702     Lxh = -cos(az) * cos(el);
703     Lyh = sin(az) * cos(el);
704     Lzh = sin(el);
705     Sx = sin_phi * cos_theta;
706     Ex = -sin_theta;
707     Zx = cos_theta * cos_phi;
708     Sy = sin_phi * sin_theta;
709     Ey = cos_theta;
710     Zy = sin_theta * cos_phi;
711     Sz = -cos_phi;
712     Ez = 0;
713     Zz = sin_phi;
714     Lx = Sx * Lxh + Ex * Lyh + Zx * Lzh;
715     Ly = Sy * Lxh + Ey * Lyh + Zy * Lzh;
716     Lz = Sz * Lxh + Ez * Lyh + Zz * Lzh;
717     obs_set->dec = ArcSin(Lz);  /* Declination (radians) */
718     cos_delta = sqrt(1 - Sqr(Lz));
719     sin_alpha = Ly / cos_delta;
720     cos_alpha = Lx / cos_delta;
721     obs_set->ra = AcTan(sin_alpha, cos_alpha);  /* Right Ascension (radians) */
722     obs_set->ra = FMod2p(obs_set->ra);
723 }
724