1 /*
2  * gsat - a realtime satellite tracking graphical frontend to predict
3  *
4  * Copyright (C) 2001 by Xavier Crehueras, EB3CZS
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (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  * Look at the README for more information on the program.
21  */
22 
23 /* Main processing loop and network functions */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <netdb.h>
35 #include <signal.h>
36 #include <errno.h>
37 #include <arpa/inet.h>
38 #include <math.h>
39 #include <dlfcn.h>
40 #include <dirent.h>
41 
42 #include "support.h"
43 #include "comms.h"
44 
45 #include "globals.h"
46 
47 #ifndef PI
48 #define PI 3.141592653589793
49 #endif
50 
51 void on_cb_disconnect_clicked(void);
52 
arccos(x,y)53 double arccos(x,y)
54 double x, y;
55 {
56   /* This function implements the arccosine function,
57      returning a value between 0 and two pi. */
58 
59   double result=0.0, fraction;
60 
61   fraction=x/y;
62 
63   if ((x>0.0) && (y>0.0))
64     result=acos(fraction);
65 
66   if ((x<0.0) && (y>0.0))
67     result=acos(fraction);
68 
69   if ((x<0.0) && (y<0.0))
70     result=PI+acos(fraction);
71 
72   if ((x>0.0) && (y<0.0))
73     result=PI+acos(fraction);
74 
75   return result;
76 }
77 
78 /* Function to open a dialog box displaying error messages. */
79 
error_dialog(gchar * message)80 void error_dialog(gchar *message)
81 {
82 
83   GtkWidget *dialog, *frame, *box, *vbox1, *vbox2, *vbox3, *label, *okay_button;
84 
85   /* Create the widgets */
86 
87   dialog = gtk_dialog_new();
88   frame = gtk_frame_new(NULL);
89   box = gtk_vbox_new( TRUE, 2 );
90   vbox1 = gtk_vbox_new( FALSE, 2 );
91   vbox2 = gtk_vbox_new( TRUE, 2 );
92   vbox3 = gtk_vbox_new( FALSE, 2 );
93   label = gtk_label_new (message);
94   okay_button = gtk_button_new_with_label("Okay");
95 
96   /* Ensure that the dialog box is destroyed when the user clicks ok. */
97 
98   gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
99 			     GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT(dialog));
100   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
101 		     okay_button);
102 
103   /* Add the label, and show everything we've added to the dialog. */
104 
105   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), frame);
106   gtk_container_add (GTK_CONTAINER (frame), box);
107   gtk_container_set_border_width (GTK_CONTAINER (frame), 4);
108   gtk_box_pack_start (GTK_BOX(box), vbox1, FALSE, FALSE, 2);
109   gtk_box_pack_start (GTK_BOX(box), vbox2, TRUE, TRUE, 2);
110   gtk_box_pack_start (GTK_BOX(box), vbox3, FALSE, FALSE, 2);
111   gtk_container_add (GTK_CONTAINER (vbox2), label);
112   gtk_window_set_title( GTK_WINDOW(dialog),"gsat - ERROR");
113   gtk_widget_show_all (dialog);
114 }
115 
show_status(const gchar * statusmsg)116 void show_status( const gchar * statusmsg )
117 {
118   GtkWidget *widget;
119   gint ctxid;
120 
121   widget = lookup_widget( mainwindow, "statusbar" );
122   ctxid = gtk_statusbar_get_context_id(GTK_STATUSBAR(widget),"Status");
123   gtk_statusbar_pop(GTK_STATUSBAR(widget),ctxid);
124   gtk_statusbar_push(GTK_STATUSBAR(widget),ctxid,statusmsg);
125 }
126 
127 /* Establish network conection */
128 
connectsock(char * host,char * service,char * protocol)129 int connectsock(char *host, char *service, char *protocol)
130 {
131   /* This function is used to connect to the server.  "host" is the
132      name host is the name of the computer the server program resides
133      on.  "service" is the name of the socket port.  "protocol" is the
134      socket protocol.  It should be set to UDP. */
135 
136   struct hostent *phe;
137   struct servent *pse;
138   struct protoent *ppe;
139   struct sockaddr_in sin;
140 
141   int s, type;
142 
143   bzero((char *)&sin,sizeof(struct sockaddr_in));
144   sin.sin_family=AF_INET;
145 
146   if ((pse=getservbyname(service,protocol)))
147     sin.sin_port=pse->s_port;
148   else if ((sin.sin_port=htons((unsigned short)atoi(service)))==0) {
149     fprintf(stderr,"WARNING: Can't get predict services. Check /etc/services. Trying with default port.\n");
150     sin.sin_port=htons(1210);
151   }
152 
153   if ((phe=gethostbyname(host)))
154     bcopy(phe->h_addr,(char *)&sin.sin_addr,phe->h_length);
155 
156   else if ((sin.sin_addr.s_addr=inet_addr(host))==INADDR_NONE) {
157     fprintf(stderr,"ERROR: Can't get host address.\n");
158     error_dialog("ERROR: Can't get host address.\n");
159     return -1;
160   }
161 
162   if ((ppe=getprotobyname(protocol))==0)
163     return -1;
164 
165   if (strcmp(protocol,"udp")==0)
166     type=SOCK_DGRAM;
167   else
168     type=SOCK_STREAM;
169 
170   s=socket(PF_INET,type,ppe->p_proto);
171 
172   if (s<0) {
173     fprintf(stderr,"ERROR: Can't get socket.\n");
174     return -1;
175   }
176 
177   if (connect(s,(struct sockaddr *)&sin,sizeof(sin))<0) {
178     fprintf(stderr,"ERROR: Can't connect to socket.\n");
179     return -1;
180   }
181 
182   return s;
183 }
184 
185 /* Send the command to the server */
186 
send_command(int sock,char * command)187 int send_command(int sock, char *command)
188 {
189   int len;
190 
191   len=write(sock,command,strlen(command));
192 
193   return len;
194 }
195 
196 /* Read from network socket */
197 
read_socket(int sock,char * buf)198 int read_socket(int sock, char *buf)
199 {
200   int n;
201   struct timeval tv;
202   fd_set rfds;
203   time_t now;
204 
205   FD_ZERO( &rfds );
206   FD_SET( sock, &rfds );
207   tv.tv_sec=NETTIMEOUT;
208   tv.tv_usec=0;
209 
210   if( select(sock+1,&rfds,NULL,NULL,&tv))
211     n=read(sock,buf,NETBUFSIZE);
212 /*      n=recvfrom(sock,buf,strlen(buf),0,NULL,NULL); */
213   else {
214     now=time(NULL);
215     fprintf(stderr,"Timeout reading from socket.\n");
216     return -1;
217   }
218 
219   buf[n]='\0';
220 
221   return n;
222 }
223 
224 /* Get response to command from server */
225 
get_response(int sock,char * buf)226 int get_response(int sock, char *buf)
227 {
228   int len;
229   int timeouts;
230 
231   len=0;
232   timeouts=0;
233   while(timeouts<MAXTIMEOUTS) {
234     if((len=read_socket(sock,buf))<0) {
235       timeouts++;
236       if(timeouts==MAXTIMEOUTS) {
237 	fprintf(stderr,"ERROR: predict server %s is not responding to commands.\n", predicthost);
238 	return -1;
239       }
240     }
241     else
242       break;
243   }
244 
245   return len;
246 }
247 
248 
249 /* Get prediction data */
250 
251 #define BUFSIZE 60*MAXDOTS
252 
get_orbitdata(char * satname)253 void get_orbitdata(char *satname)
254 {
255   int i,cnt,sx,sy;
256   long int begin,now;
257   float slat,slong,saz,sel,phase;
258   char buf[BUFSIZE],weekday[4],adate[8],atime[9];
259 
260   /* Get satellite data */
261   /* Build a command buffer */
262   now=time(NULL);
263   begin=now-MAXDOTS*60;
264   cnt=0;
265 
266   fprintf(stderr,"Now: %ld From: %ld\n",now,now-MAXDOTS);
267 
268   sprintf(buf,"GET_SAT_POS %s %ld +90m\n",satname,begin);
269   fprintf(stderr,"Command: %s",buf);
270 
271   /* Send the command to the server */
272   send_command(netsocket,buf);
273 
274   /* Calculate sub-satellite points */
275 
276   for( i=0; i<MAXDOTS+1; i++ ) {
277     /* Get the response */
278     if(get_response(netsocket,buf)==-1) {
279       error_dialog("Too many errors receiving response from server, disconnecting");
280       on_cb_disconnect_clicked();
281     }
282 
283     if(buf[0]==26)
284       break;
285     fprintf(stderr,"Index: %d Data: %s",i,buf);
286 
287     sscanf(buf,"%ld %s %s %s %f %f %f %f %f",&now,weekday,adate,atime,
288 	   &slat,&slong,&phase,&saz,&sel);
289 
290     if(slong>180.0) {
291       slong = slong - 180.0;
292       sx = (int)(MAPSIZEX - (slong * MAPSIZEX / 360.0));
293     }
294     else {
295       sx = (int)((MAPSIZEX/2.0) - (slong * MAPSIZEX / 360.0));
296     }
297     sy = (int)((MAPSIZEY/2.0) - (slat * MAPSIZEY / 180.0));
298 
299     /* do not draw repeated dots in the orbit */
300     if( dots[cnt-1].x!=sx || dots[cnt-1].y!=sy ) {
301       dots[cnt].x=sx;
302       dots[cnt].y=sy;
303       cnt++;
304     }
305   }
306 
307   fprintf(stderr,"Count: %d\n",cnt);
308   ndots=cnt;
309 }
310 
311 
312 /* Main processing loop */
313 
314 gint
timeout_callback(gpointer data)315 timeout_callback( gpointer data )
316 {
317   int i, j, azi, ctxid;
318   char buf[NETBUFSIZE], buf2[NETBUFSIZE], buf3[NETBUFSIZE],
319     satname[26], *sat, callsign[16], param[30], *dopplerptr;
320   long aostime,orbitnumber;
321   float az, el, slong, slat, footprint, range, doppler, altitude,
322     velocity, qthlat, qthlong, qthalt, phase, eclipse, squint;
323   time_t t, now;
324   GtkWidget *widget;
325   double 	uplink, downlink, beacon, updoppler, downdoppler, beacondoppler,
326     dopplershiftup, dopplershiftdown, dopplershiftbeacon,
327     rangelat, rangelong, azimuth, ssplat,
328     ssplong, TWOPI, HALFPI, deg2rad=1.74532925199e-02,
329     R0=6378.16, beta, num, dem;
330   GdkRectangle updatewin;
331   gint sx, sy;
332   GdkPoint footprintdots[360], qthfootprintdots[360];
333   char visible;
334   static int flash, azflash;
335 
336   TWOPI=2.0*PI;
337   HALFPI=PI/2.0;
338 
339   /* Show date and time */
340   now = time(NULL);
341   if( timeUTC )
342     sprintf(param,"%s",asctime(gmtime(&now)));
343   else
344     sprintf(param,"%s",asctime(localtime(&now)));
345   param[strlen(param)-1]='\0';
346   widget = lookup_widget( mainwindow, "tx_date" );
347   gtk_entry_set_text(GTK_ENTRY(widget),param);
348 
349   /* if we are not connected, don't do anything more */
350   if( connected == FALSE )
351     return TRUE; /* allow more timeouts to ocur */
352 
353   /* Get Satellite name from combo entry */
354   widget = lookup_widget( mainwindow, "combo" );
355   sat = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(widget)->entry));
356   strncpy( satname, sat, 25 );
357   satname[ 26 ] = '\0';
358   if( azelgraph==TRUE) {
359     widget = lookup_widget( dialog_azel_graph, "tx_azel_sat" );
360     gtk_entry_set_text(GTK_ENTRY(widget),satname);
361   }
362 
363 /*    if(doprediction==TRUE) { */
364 /*      get_orbitdata(satname); */
365 /*      doprediction=FALSE; */
366 /*    } */
367 
368   /* Get satellite data */
369   /* Build a command buffer */
370   sprintf(buf,"GET_SAT %s\n",satname);
371 
372   /* Send the command to the server */
373   send_command(netsocket,buf);
374 
375   /* Get the response */
376   if(get_response(netsocket,buf)==-1) {
377     error_dialog("Too many errors receiving response from server, disconnecting");
378     on_cb_disconnect_clicked();
379     return TRUE;
380   }
381 
382   /* Get doppler data */
383   /* Build a command buffer */
384   sprintf(buf2,"GET_DOPPLER %s\n",satname);
385 
386   /* Send the command to the server */
387   send_command(netsocket,buf2);
388 
389   /* Get the response */
390   if(get_response(netsocket,buf2)==-1) {
391     error_dialog("Too many errors receiving response from server, disconnecting");
392     on_cb_disconnect_clicked();
393     return TRUE;
394   }
395 
396   /* Parse the satellite data */
397 
398   /* The first element of the response is the satellite name.
399      It is ended with a '\n' character and may contain spaces. */
400 
401   for (i=0; buf[i]!='\n'; i++)
402     satname[i]=buf[i];
403 
404   satname[i]=0;
405   i++;
406 
407   /* The rest of the data can be parsed using the sscanf()
408      function.  First, the satellite name is removed from
409      "buf", and then "buf" is parsed for numerical data
410      using an sscanf(). */
411 
412   for (j=0; buf[i+j]!=0; j++)
413     buf[j]=buf[i+j];
414 
415   buf[j]=0;
416 
417   sscanf(buf,"%f %f %f %f %ld %f %f %f %f %ld %c %f %f %f",
418 	 &slong, &slat, &az, &el, &aostime, &footprint, &range,
419 	 &altitude, &velocity, &orbitnumber, &visible, &phase, &eclipse, &squint);
420 
421   /* Get doppler shift */
422   sscanf(buf2,"%f",&doppler );
423 
424   /* Get qth data */
425   /* Build a command buffer */
426   sprintf(buf3,"GET_QTH\n");
427 
428   /* Send the command to the server */
429   send_command(netsocket,buf3);
430 
431   /* Get the response */
432   if(get_response(netsocket,buf3)==-1) {
433     error_dialog("Too many errors receiving response from server, disconnecting");
434     on_cb_disconnect_clicked();
435     return TRUE;
436   }
437 
438   /* Parse the satellite data */
439 
440   /* The first element of the response is the callsign.
441      It is ended with a '\n' character and may contain spaces. */
442 
443   for (i=0; buf3[i]!='\n'; i++)
444     callsign[i]=buf3[i];
445 
446   callsign[i]=0;
447   i++;
448 
449   /* The rest of the data can be parsed using the sscanf()
450      function.  First, the callsign is removed from
451      "buf3", and then "buf3" is parsed for numerical data
452      using an sscanf(). */
453 
454   for (j=0; buf3[i+j]!=0; j++)
455     buf3[j]=buf3[i+j];
456 
457   buf3[j]=0;
458 
459   sscanf(buf3,"%f %f %f",&qthlat,&qthlong,&qthalt);
460 
461   /* Check if satellite has decayed */
462   if(aostime==0 && altitude==0) {
463     clear_widgets();
464     clear_map();
465     plot_QTH(qthlat,qthlong,callsign);
466     widget = lookup_widget( data, "tx_aos" );
467     gtk_entry_set_text(GTK_ENTRY(widget),"Never");
468     widget = lookup_widget( data, "satbar" );
469     ctxid = gtk_statusbar_get_context_id(GTK_STATUSBAR(widget),"Status");
470     gtk_statusbar_pop(GTK_STATUSBAR(widget),ctxid);
471     gtk_statusbar_push(GTK_STATUSBAR(widget),ctxid,
472 		       "Satellite has decayed");
473     return TRUE;
474   }
475 
476   /* Now display all the satellite data on the widgets */
477   sprintf(param,"%7.2f %c",(slong>180?360.0-slong:slong),(slong>180?'E':'W'));
478   widget = lookup_widget( data, "tx_longitude" );
479   gtk_entry_set_text(GTK_ENTRY(widget),param);
480 
481   sprintf(param,"%7.2f %c",fabs((double)slat),(slat<0?'S':'N'));
482   widget = lookup_widget( data, "tx_latitude" );
483   gtk_entry_set_text(GTK_ENTRY(widget),param);
484 
485   sprintf(param,"%7.2f deg",az);
486   widget = lookup_widget( data, "tx_azimuth" );
487   gtk_entry_set_text(GTK_ENTRY(widget),param);
488   if( azelgraph==TRUE) {
489     sprintf(param,"%7.2f",az);
490     widget = lookup_widget( dialog_azel_graph, "tx_azel_azimuth" );
491     gtk_entry_set_text(GTK_ENTRY(widget),param);
492   }
493 
494   sprintf(param,"%+-6.2f deg",el);
495   widget = lookup_widget( data, "tx_elevation" );
496   gtk_entry_set_text(GTK_ENTRY(widget),param);
497   if( azelgraph==TRUE) {
498     sprintf(param,"%+-6.2f",el);
499     widget = lookup_widget( dialog_azel_graph, "tx_azel_elevation" );
500     gtk_entry_set_text(GTK_ENTRY(widget),param);
501   }
502 
503   if(enablerotor==TRUE)
504     (*plugin_set_rotor)(az, el);
505 
506   sprintf(param,"%7.2f km",footprint);
507   widget = lookup_widget( data, "tx_footprint" );
508   gtk_entry_set_text(GTK_ENTRY(widget),param);
509 
510   sprintf(param,"%7.2f km",range);
511   widget = lookup_widget( data, "tx_range" );
512   gtk_entry_set_text(GTK_ENTRY(widget),param);
513 
514   sprintf(param,"%7.2f km",altitude);
515   widget = lookup_widget( data, "tx_altitude" );
516   gtk_entry_set_text(GTK_ENTRY(widget),param);
517 
518   if(strncmp(predictversion,"2.1",3)==0)
519     sprintf(param,"%7.2f km/h",velocity);
520   else
521     sprintf(param,"%7.2f km/s",velocity);
522 
523   widget = lookup_widget( data, "tx_velocity" );
524   gtk_entry_set_text(GTK_ENTRY(widget),param);
525 
526   sprintf(param,"%ld",orbitnumber);
527   widget = lookup_widget( data, "tx_orbit" );
528   gtk_entry_set_text(GTK_ENTRY(widget),param);
529 
530   sprintf(param,"%3.1f deg",phase);
531   widget = lookup_widget( data, "tx_ma" );
532   gtk_entry_set_text(GTK_ENTRY(widget),param);
533 
534   /* Check if we have a valid squint angle (360=no squint) */
535   if(squint==360.0) {
536     sprintf(param,"N/A");
537   } else {
538     sprintf(param,"%3.1f deg",squint);
539   }
540   widget = lookup_widget( data, "tx_squint" );
541   gtk_entry_set_text(GTK_ENTRY(widget),param);
542 
543   /* change label of AOS widget to AOS/LOS */
544   widget = lookup_widget( data, "lb_aos" );
545   if( el>=0 )
546     gtk_label_set_text(GTK_LABEL(widget),"    LOS at");
547   else
548     gtk_label_set_text(GTK_LABEL(widget),"Next AOS");
549 
550   t=(time_t)aostime;
551   if( timeUTC )
552     sprintf(param,"%s",asctime(gmtime(&t)));
553   else
554     sprintf(param,"%s",asctime(localtime(&t)));
555   param[strlen(param)-1]='\0';
556   widget = lookup_widget( data, "tx_aos" );
557   gtk_entry_set_text(GTK_ENTRY(widget),param);
558 
559   /* execute AOS and LOS commands */
560   if( newsat==0 ) {
561     if( (lastel < 0 && el >= 0)||(lastel >=0 && el < 0) ) {
562       if( el >= 0 ) {
563 	if(strlen(prefs_aos_command)>0)
564 	  system(prefs_aos_command);
565       }
566       else {
567 	if(strlen(prefs_los_command)>0)
568 	  system(prefs_los_command);
569       }
570     }
571   }
572   newsat=0;
573 
574   /* Show doppler correction if elevation > 0 */
575   widget = lookup_widget( data, "tx_uplink" );
576   dopplerptr=(gchar *)gtk_entry_get_text(GTK_ENTRY(widget));
577   uplink=atof(dopplerptr);
578   if(el >= 0 && uplink > 0.0) {
579     dopplershiftup=(-uplink)/100000.0*doppler;
580     sprintf(param,"%7.2f Hz",dopplershiftup);
581     widget = lookup_widget( data, "tx_upshift" );
582     gtk_entry_set_text(GTK_ENTRY(widget),param);
583     updoppler=uplink+(dopplershiftup/1000.0);
584     sprintf(param,"%7.2f kHz",updoppler);
585     widget = lookup_widget( data, "tx_updoppler" );
586     gtk_entry_set_text(GTK_ENTRY(widget),param);
587 
588     /* Set Uplink frequency on the radio */
589     if( enableupdoppler==TRUE )
590       (*plugin_set_uplink_frequency)(updoppler);
591   }
592   else {
593     widget = lookup_widget( data, "tx_upshift" );
594     gtk_entry_set_text(GTK_ENTRY(widget),"--------");
595     widget = lookup_widget( data, "tx_updoppler" );
596     gtk_entry_set_text(GTK_ENTRY(widget),"--------");
597   }
598 
599   widget = lookup_widget( data, "tx_downlink" );
600   dopplerptr=(gchar *)gtk_entry_get_text(GTK_ENTRY(widget));
601   downlink=atof(dopplerptr);
602   if(el >= 0 && downlink > 0.0) {
603     dopplershiftdown=downlink/100000.0*doppler;
604     sprintf(param,"%7.2f Hz",dopplershiftdown);
605     widget = lookup_widget( data, "tx_downshift" );
606     gtk_entry_set_text(GTK_ENTRY(widget),param);
607     downdoppler=downlink+(dopplershiftdown/1000.0);
608     sprintf(param,"%7.2f kHz",downdoppler);
609     widget = lookup_widget( data, "tx_dwdoppler" );
610     gtk_entry_set_text(GTK_ENTRY(widget),param);
611 
612     /* Set Downlink frequency on the radio */
613     if( enabledowndoppler==TRUE )
614       (*plugin_set_downlink_frequency)(downdoppler);
615   }
616   else {
617     widget = lookup_widget( data, "tx_downshift" );
618     gtk_entry_set_text(GTK_ENTRY(widget),"--------");
619     widget = lookup_widget( data, "tx_dwdoppler" );
620     gtk_entry_set_text(GTK_ENTRY(widget),"--------");
621   }
622 
623   widget = lookup_widget( data, "tx_beacon" );
624   dopplerptr=(gchar *)gtk_entry_get_text(GTK_ENTRY(widget));
625   beacon=atof(dopplerptr);
626   if(el >= 0 && beacon > 0.0) {
627     dopplershiftbeacon=beacon/100000.0*doppler;
628     sprintf(param,"%7.2f Hz",dopplershiftbeacon);
629     widget = lookup_widget( data, "tx_beaconshift" );
630     gtk_entry_set_text(GTK_ENTRY(widget),param);
631     beacondoppler=beacon+(dopplershiftbeacon/1000.0);
632     sprintf(param,"%7.2f kHz",beacondoppler);
633     widget = lookup_widget( data, "tx_beacondoppler" );
634     gtk_entry_set_text(GTK_ENTRY(widget),param);
635 
636     /* Set Beacon frequency on the radio */
637     if( enablebeacondoppler==TRUE )
638       (*plugin_set_beacon_frequency)(beacondoppler);
639   }
640   else {
641     widget = lookup_widget( data, "tx_beaconshift" );
642     gtk_entry_set_text(GTK_ENTRY(widget),"--------");
643     widget = lookup_widget( data, "tx_beacondoppler" );
644     gtk_entry_set_text(GTK_ENTRY(widget),"--------");
645   }
646 
647   /* Show satellite visibility in 2nd status bar */
648   widget = lookup_widget( data, "satbar" );
649   ctxid = gtk_statusbar_get_context_id(GTK_STATUSBAR(widget),"Status");
650   switch( visible ) {
651   case 'D':
652     gtk_statusbar_pop(GTK_STATUSBAR(widget),ctxid);
653     gtk_statusbar_push(GTK_STATUSBAR(widget),ctxid,
654 		       "Satellite is in daylight");
655     break;
656   case 'N':
657     gtk_statusbar_pop(GTK_STATUSBAR(widget),ctxid);
658     gtk_statusbar_push(GTK_STATUSBAR(widget),ctxid,
659 		       "Satellite is in darkness");
660     break;
661   case 'V':
662     gtk_statusbar_pop(GTK_STATUSBAR(widget),ctxid);
663     gtk_statusbar_push(GTK_STATUSBAR(widget),ctxid,
664 		       "Satellite is visible");
665     break;
666   default:
667     gtk_statusbar_pop(GTK_STATUSBAR(widget),ctxid);
668     gtk_statusbar_push(GTK_STATUSBAR(widget),ctxid,
669 		       "No visibility information available");
670   }
671 
672   /* Graphical representation */
673 
674   /* Range Circle Calculations by KD2BD */
675 
676   if( satfootprint ) {
677     ssplat=slat*deg2rad;
678     ssplong=slong*deg2rad;
679     beta=(0.5*footprint)/R0;
680 
681     for (azi=0; azi<360; azi++)
682       {
683 	azimuth=deg2rad*(double)azi;
684 	rangelat=asin(sin(ssplat)*cos(beta)+cos(azimuth)*sin(beta)*cos(ssplat));
685 	num=cos(beta)-(sin(ssplat)*sin(rangelat));
686 	dem=cos(ssplat)*cos(rangelat);
687 
688 	if (azi==0 && (beta > HALFPI-ssplat))
689 	  rangelong=ssplong+PI;
690 
691 	else if (azi==180 && (beta > HALFPI+ssplat))
692 	  rangelong=ssplong+PI;
693 
694 	else if (fabs(num/dem)>1.0)
695 	  rangelong=ssplong;
696 
697 	else
698 	  {
699 	    if ((180-azi)>=0)
700 	      rangelong=ssplong-arccos(num,dem);
701 	    else
702 	      rangelong=ssplong+arccos(num,dem);
703 	  }
704 
705 	while (rangelong<0.0)
706 	  rangelong+=TWOPI;
707 
708 	while (rangelong>(2.0*PI))
709 	  rangelong-=TWOPI;
710 
711 	rangelat=rangelat/deg2rad;
712 	rangelong=rangelong/deg2rad;
713 
714 	/* Convert range circle data to map-based
715 	   coordinates and draw on map */
716 
717 	if (rangelong>180.0)
718 	  {
719 	    rangelong=rangelong-180.0;
720 	    sx=(int)(MAPSIZEX-(rangelong*MAPSIZEX/360.0));
721 	  }
722 
723 	else
724 	  sx=(int)((MAPSIZEX/2.0)-(rangelong*MAPSIZEX/360.0));
725 
726 	sy=(int)((MAPSIZEY/2.0)-(rangelat*MAPSIZEY/180.0));
727 
728 	/* store position for Plot Range Circle */
729 	footprintdots[azi].x=sx;
730 	footprintdots[azi].y=sy;
731       }
732   }
733 
734   if( qthfootprint ) {
735     ssplat=qthlat*deg2rad;
736     ssplong=qthlong*deg2rad;
737     beta=(0.5*footprint)/R0;
738 
739     for (azi=0; azi<360; azi++)
740       {
741 	azimuth=deg2rad*(double)azi;
742 	rangelat=asin(sin(ssplat)*cos(beta)+cos(azimuth)*sin(beta)*cos(ssplat));
743 	num=cos(beta)-(sin(ssplat)*sin(rangelat));
744 	dem=cos(ssplat)*cos(rangelat);
745 
746 	if (azi==0 && (beta > HALFPI-ssplat))
747 	  rangelong=ssplong+PI;
748 
749 	else if (azi==180 && (beta > HALFPI+ssplat))
750 	  rangelong=ssplong+PI;
751 
752 	else if (fabs(num/dem)>1.0)
753 	  rangelong=ssplong;
754 
755 	else
756 	  {
757 	    if ((180-azi)>=0)
758 	      rangelong=ssplong-arccos(num,dem);
759 	    else
760 	      rangelong=ssplong+arccos(num,dem);
761 	  }
762 
763 	while (rangelong<0.0)
764 	  rangelong+=TWOPI;
765 
766 	while (rangelong>(2.0*PI))
767 	  rangelong-=TWOPI;
768 
769 	rangelat=rangelat/deg2rad;
770 	rangelong=rangelong/deg2rad;
771 
772 	/* Convert range circle data to map-based
773 	   coordinates and draw on map */
774 
775 	if (rangelong>180.0)
776 	  {
777 	    rangelong=rangelong-180.0;
778 	    sx=(int)(MAPSIZEX-(rangelong*MAPSIZEX/360.0));
779 	  }
780 
781 	else
782 	  sx=(int)((MAPSIZEX/2.0)-(rangelong*MAPSIZEX/360.0));
783 
784 	sy=(int)((MAPSIZEY/2.0)-(rangelat*MAPSIZEY/180.0));
785 
786 	/* store position for Plot Range Circle */
787 	qthfootprintdots[azi].x=sx;
788 	qthfootprintdots[azi].y=sy;
789       }
790   }
791 
792   /* AZ/EL dots */
793   if( lastel < 0 && el >= 0 )    /* if sat is beginning a new pass ... */
794     nazeldots=0;                 /* reset az/el dots counter */
795 
796   /* save last elevation value */
797   lastel=el;
798 
799   if(el>=0) {
800     azimuth=(double)(az-90.0)*deg2rad;
801     sx=(AZELSIZEX/2)+cos(azimuth)*((double)(AZELSIZEX/2)-el*(double)(AZELSIZEX/2)/90.0);
802     sy=(AZELSIZEY/2)+sin(azimuth)*((double)(AZELSIZEY/2)-el*(double)(AZELSIZEY/2)/90.0);
803 
804     if( nazeldots > 0 ) {
805       if( azeldots[nazeldots-1].x!=sx || azeldots[nazeldots-1].y!=sy) {
806 	azeldots[nazeldots].x=sx;
807 	azeldots[nazeldots].y=sy;
808 	nazeldots++;
809       }
810     }
811     else {
812       azeldots[0].x=sx;
813       azeldots[0].y=sy;
814       nazeldots=1;
815     }
816   }
817 
818   /* First draw the empty map */
819   gdk_draw_pixmap( drawmap, yellow_gc, sourcemap, 0, 0, 0, 0, MAPSIZEX, MAPSIZEY );
820 
821   /* Draw grid if needed */
822   if(drawgrid==TRUE) {
823     draw_grid();
824   }
825 
826   /* Draw orbit */
827   if( drawtrack==TRUE )
828     if( ndots>0 )
829       gdk_draw_points(drawmap,red_gc,dots,ndots);
830 
831   /* Draw footprints */
832   if( satfootprint==TRUE) {
833     for(i=0; i<359; i++) {
834       if( abs( footprintdots[i].x - footprintdots[i+1].x ) < MAPSIZEX/2 )
835 	gdk_draw_line(drawmap, yellow_gc, footprintdots[i].x,footprintdots[i].y,
836 		      footprintdots[i+1].x,footprintdots[i+1].y);
837     }
838     if( abs( footprintdots[0].x - footprintdots[i].x ) < MAPSIZEX/2 )
839       gdk_draw_line(drawmap, yellow_gc, footprintdots[0].x,footprintdots[0].y,
840 		    footprintdots[i].x,footprintdots[i].y);
841   }
842 
843   if( qthfootprint==TRUE) {
844     for(i=0; i<359; i++) {
845       if( abs( qthfootprintdots[i].x - qthfootprintdots[i+1].x ) < MAPSIZEX/2 )
846 	gdk_draw_line(drawmap, cyan_gc, qthfootprintdots[i].x,qthfootprintdots[i].y,
847 		      qthfootprintdots[i+1].x,qthfootprintdots[i+1].y);
848     }
849     if( abs( qthfootprintdots[0].x - qthfootprintdots[i].x ) < MAPSIZEX/2 )
850       gdk_draw_line(drawmap, cyan_gc, qthfootprintdots[0].x,qthfootprintdots[0].y,
851 		    qthfootprintdots[i].x,qthfootprintdots[i].y);
852   }
853 
854   /* Plot sub-satellite point */
855 
856   if(slong>180.0) {
857     slong = slong - 180.0;
858     sx = (int)(MAPSIZEX - (slong * MAPSIZEX / 360.0));
859   }
860   else {
861     sx = (int)((MAPSIZEX/2.0) - (slong * MAPSIZEX / 360.0));
862   }
863   sy = (int)((MAPSIZEY/2.0) - (slat * MAPSIZEY / 180.0));
864 
865   /* do not draw repeated dots in the orbit */
866   if( dots[ndots-1].x!=sx || dots[ndots-1].y!=sy ) {
867     if( ndots<MAXDOTS ) {
868       dots[ndots].x=sx;
869       dots[ndots].y=sy;
870       ndots++;
871     }
872     else {
873       for( i=0;i<MAXDOTS-1;i++ ) {
874 	dots[i].x=dots[i+1].x;
875 	dots[i].y=dots[i+1].y;
876       }
877       dots[MAXDOTS-1].x=sx;
878       dots[MAXDOTS-1].y=sy;
879     }
880   }
881 
882   /* Plot qth point */
883   plot_QTH(qthlat,qthlong,callsign);
884 
885   /* the flashing square */
886   if( flash==0 ) {
887     gdk_draw_rectangle( drawmap, purple_gc, TRUE, sx-1, sy-1, 3, 3 );
888     flash=1;
889   }
890   else {
891     gdk_draw_rectangle( drawmap, yellow_gc, TRUE, sx-1, sy-1, 3, 3 );
892     flash=0;
893   }
894 
895   /* Force map redraw */
896   widget = lookup_widget( data, "maparea" );
897   updatewin.x = 0;
898   updatewin.y = 0;
899   updatewin.width = widget->allocation.width;
900   updatewin.height = widget->allocation.height;
901   gtk_widget_draw ( widget, &updatewin);
902 
903   /* draw AZ/EL graphic */
904   if(azelgraph==TRUE) {
905     /* First draw the empty az/el graphic */
906     gdk_draw_pixmap( drawazel, blue_gc, sourceazel,
907 		       0, 0, 0, 0, AZELSIZEX, AZELSIZEY );
908     /* Then draw the track */
909     if( nazeldots>0 )
910       gdk_draw_lines(drawazel,blue_gc,azeldots,nazeldots);
911 
912     /* and a flashing square */
913     if(el>=0) {
914       sx=azeldots[nazeldots-1].x;
915       sy=azeldots[nazeldots-1].y;
916       if( azflash==0 ) {
917 	gdk_draw_rectangle( drawazel, cyan_gc, TRUE, sx-1, sy-1, 3, 3 );
918 	azflash=1;
919       }
920       else {
921 	gdk_draw_rectangle( drawazel, blue_gc, TRUE, sx-1, sy-1, 3, 3 );
922 	azflash=0;
923       }
924     }
925 
926     /* Force AZ/EL redraw */
927     widget = lookup_widget( dialog_azel_graph, "azelgraph" );
928     updatewin.x = 0;
929     updatewin.y = 0;
930     updatewin.width = widget->allocation.width;
931     updatewin.height = widget->allocation.height;
932     gtk_widget_draw ( widget, &updatewin);
933   }
934 
935   return TRUE;
936 }
937 
938 /* Connect to predict server */
939 
940 #define LISTBUFSIZE 2048
941 
connect_server(void)942 int connect_server( void )
943 {
944   GtkWidget * widget;
945   int i;
946   char bufr[LISTBUFSIZE];
947   char *sname;
948   char *bufp;
949   gpointer *satname;
950 
951   /* Get satellite list from server */
952   netsocket=connectsock(predicthost,predictport,"udp");
953 
954   if (netsocket<0) {
955     fprintf(stderr, "ERROR: Can't connect to the predict server on %s.\n", predicthost);
956     error_dialog("Can't connect to the predict server.");
957     return 1;
958   }
959 
960   send_command(netsocket,"GET_LIST");
961 
962   if(get_response(netsocket,bufr)==-1) {
963     error_dialog("Too many errors receiving response from server, disconnecting");
964     on_cb_disconnect_clicked();
965     return 1;
966   }
967 
968   /* Parse the response and place each name
969      in the GList satlist. */
970 
971   bufp=bufr;
972   while( (sname=strsep(&bufp,"\n")) != NULL) {
973     if(strlen(sname)>0) {
974       satname=g_malloc0(strlen(sname)+1);
975       strncpy((char *)satname,(const char*)sname,strlen(sname));
976       satlist=g_list_append(satlist,satname);
977     }
978   }
979 
980   /* Attach satellite list */
981   widget=lookup_widget( mainwindow, "combo" );
982   gtk_combo_set_popdown_strings( GTK_COMBO(widget), satlist);
983 
984   /* Get predict version from server */
985   send_command(netsocket,"GET_VERSION");
986 
987   if(get_response(netsocket,predictversion)==-1) {
988     error_dialog("Too many errors receiving response from server, disconnecting");
989     on_cb_disconnect_clicked();
990     return TRUE;
991   }
992 
993   for(i=0;i<strlen(predictversion);i++)
994     if(predictversion[i]=='\n') {
995       predictversion[i]='\0';
996       break;
997     }
998 
999   /* Setup status bar */
1000   sprintf(statusmsg,"Connected to predict server version %s on %s", \
1001 	  predictversion, predicthost);
1002   show_status( statusmsg );
1003 
1004   /* Enable main loop processing */
1005   connected=TRUE;
1006   lastel=0;
1007 
1008   return 0;
1009 }
1010 
clear_map(void)1011 void clear_map( void )
1012 {
1013   GtkWidget *widget;
1014   GdkRectangle updatewin;
1015 
1016   /* copy original map to draw map */
1017   gdk_draw_pixmap( drawmap, yellow_gc, sourcemap, 0, 0, 0, 0, MAPSIZEX, MAPSIZEY );
1018 
1019   /* draw grid if needed */
1020   if(drawgrid==TRUE) {
1021     draw_grid();
1022   }
1023 
1024   /* Force map redraw */
1025   widget = lookup_widget( mainwindow, "maparea" );
1026   updatewin.x = 0;
1027   updatewin.y = 0;
1028   updatewin.width = widget->allocation.width;
1029   updatewin.height = widget->allocation.height;
1030   gtk_widget_draw ( widget, &updatewin);
1031 }
1032 
draw_grid(void)1033 void draw_grid( void )
1034 {
1035   GtkWidget *widget;
1036   gint sx, sy, i;
1037 
1038   widget=lookup_widget(mainwindow, "maparea");
1039   for(i=-90; i<90; i=i+30) {
1040     sy = (int)((MAPSIZEY/2.0) - (i * MAPSIZEY / 180.0));
1041     gdk_draw_line(drawmap,widget->style->black_gc,0,sy,MAPSIZEX,sy);
1042   }
1043   for(i=-180; i<180; i=i+30) {
1044     sx = (int)((MAPSIZEX/2.0) - (i * MAPSIZEX / 360.0));
1045     gdk_draw_line(drawmap,widget->style->black_gc,sx,0,sx,MAPSIZEY);
1046   }
1047 }
1048 
plot_QTH(float qthlat,float qthlong,char * callsign)1049 void plot_QTH( float qthlat, float qthlong, char *callsign )
1050 {
1051   gint qthx, qthy;
1052 
1053   if(qthlong>180.0) {
1054     qthlong = qthlong - 180.0;
1055     qthx = (int)(MAPSIZEX - (qthlong * MAPSIZEX / 360.0));
1056   }
1057   else {
1058     qthx = (int)((MAPSIZEX/2.0) - (qthlong * MAPSIZEX / 360.0));
1059   }
1060   qthy = (int)((MAPSIZEY/2.0) - (qthlat * MAPSIZEY / 180.0));
1061 
1062   gdk_draw_rectangle( drawmap, cyan_gc, TRUE, qthx-1, qthy-1, 3, 3 );
1063   gdk_draw_string( drawmap, drawfont, cyan_gc, qthx+4, qthy+4, callsign );
1064 }
1065 
clear_widgets(void)1066 void clear_widgets( void )
1067 {
1068   GtkWidget *widget;
1069 
1070   widget = lookup_widget( mainwindow, "tx_longitude" );
1071   gtk_entry_set_text(GTK_ENTRY(widget),"");
1072 
1073   widget = lookup_widget( mainwindow, "tx_latitude" );
1074   gtk_entry_set_text(GTK_ENTRY(widget),"");
1075 
1076   widget = lookup_widget( mainwindow, "tx_azimuth" );
1077   gtk_entry_set_text(GTK_ENTRY(widget),"");
1078 
1079   widget = lookup_widget( mainwindow, "tx_elevation" );
1080   gtk_entry_set_text(GTK_ENTRY(widget),"");
1081 
1082   widget = lookup_widget( mainwindow, "tx_footprint" );
1083   gtk_entry_set_text(GTK_ENTRY(widget),"");
1084 
1085   widget = lookup_widget( mainwindow, "tx_range" );
1086   gtk_entry_set_text(GTK_ENTRY(widget),"");
1087 
1088   widget = lookup_widget( mainwindow, "tx_altitude" );
1089   gtk_entry_set_text(GTK_ENTRY(widget),"");
1090 
1091   widget = lookup_widget( mainwindow, "tx_velocity" );
1092   gtk_entry_set_text(GTK_ENTRY(widget),"");
1093 
1094   widget = lookup_widget( mainwindow, "tx_ma" );
1095   gtk_entry_set_text(GTK_ENTRY(widget),"");
1096 
1097   widget = lookup_widget( mainwindow, "tx_squint" );
1098   gtk_entry_set_text(GTK_ENTRY(widget),"");
1099 
1100   widget = lookup_widget( mainwindow, "tx_orbit" );
1101   gtk_entry_set_text(GTK_ENTRY(widget),"");
1102 
1103   widget = lookup_widget( mainwindow, "lb_aos" );
1104   gtk_label_set_text(GTK_LABEL(widget),"Next AOS");
1105 
1106   widget = lookup_widget( mainwindow, "tx_aos" );
1107   gtk_entry_set_text(GTK_ENTRY(widget),"");
1108 
1109   widget = lookup_widget( mainwindow, "tx_upshift" );
1110   gtk_entry_set_text(GTK_ENTRY(widget),"");
1111 
1112   widget = lookup_widget( mainwindow, "tx_updoppler" );
1113   gtk_entry_set_text(GTK_ENTRY(widget),"");
1114 
1115   widget = lookup_widget( mainwindow, "tx_downshift" );
1116   gtk_entry_set_text(GTK_ENTRY(widget),"");
1117   widget = lookup_widget( mainwindow, "tx_dwdoppler" );
1118   gtk_entry_set_text(GTK_ENTRY(widget),"");
1119 
1120   widget = lookup_widget( mainwindow, "tx_beaconshift" );
1121   gtk_entry_set_text(GTK_ENTRY(widget),"");
1122   widget = lookup_widget( mainwindow, "tx_beacondoppler" );
1123   gtk_entry_set_text(GTK_ENTRY(widget),"");
1124 
1125   widget = lookup_widget( dialog_azel_graph, "tx_azel_sat" );
1126   gtk_entry_set_text(GTK_ENTRY(widget),"");
1127 
1128   widget = lookup_widget( dialog_azel_graph, "tx_azel_azimuth" );
1129   gtk_entry_set_text(GTK_ENTRY(widget),"");
1130 
1131   widget = lookup_widget( dialog_azel_graph, "tx_azel_elevation" );
1132   gtk_entry_set_text(GTK_ENTRY(widget),"");
1133 }
1134 
disconnect_server(void)1135 void disconnect_server( void )
1136 {
1137   GtkWidget *widget;
1138   gint ctxid;
1139   gpointer *satname;
1140 
1141   /* if we are not connected, don't do anything more */
1142   if( connected == FALSE )
1143     return;
1144 
1145   /* Disable main loop processing */
1146   connected=FALSE;
1147 
1148   /* Close network socket */
1149   close(netsocket);
1150 
1151   /* Setup status bar */
1152   show_status("Not connected");
1153 
1154   /* Clear all data from widgets */
1155   widget=lookup_widget( mainwindow, "combo" );
1156   gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(widget)->entry), "");
1157   gtk_list_clear_items(GTK_LIST(GTK_COMBO(widget)->list), 0, -1);
1158 
1159   clear_map();
1160   clear_widgets();
1161 
1162   /* Empty the satellite list and free memory */
1163   satlist=g_list_first( satlist );
1164   while( g_list_length( satlist ) > 0 ) {
1165     satname=satlist->data;
1166     satlist=g_list_remove( satlist, satname );
1167     g_free( satname );
1168   }
1169 
1170   widget = lookup_widget( mainwindow, "satbar" );
1171   ctxid = gtk_statusbar_get_context_id(GTK_STATUSBAR(widget),"Status");
1172   gtk_statusbar_pop(GTK_STATUSBAR(widget),ctxid);
1173   gtk_statusbar_push(GTK_STATUSBAR(widget),ctxid,
1174 			     "No visibility information available");
1175 }
1176