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