]> git.itanic.dy.fi Git - maemo-mapper/blob - src/aprs.c
Added basic APRS support - Can be disabled by removing definition of INCLUDE_APRS
[maemo-mapper] / src / aprs.c
1 /*
2  * 
3  * This file is part of Maemo Mapper.
4  *
5  * Maemo Mapper is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Maemo Mapper is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Maemo Mapper.  If not, see <http://www.gnu.org/licenses/>.
17  * 
18  * 
19  * Parts of this code have been ported from Xastir by Rob Williams (10 Aug 2008):
20  * 
21  *  * XASTIR, Amateur Station Tracking and Information Reporting
22  * Copyright (C) 1999,2000  Frank Giannandrea
23  * Copyright (C) 2000-2007  The Xastir Group
24  * 
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #    include "config.h"
29 #endif
30
31 #ifdef INCLUDE_APRS
32
33 #define _GNU_SOURCE
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <math.h>
38 #include "data.h"
39
40 #include <pthread.h>
41
42 #include <libgnomevfs/gnome-vfs.h>
43 #include <libgnomevfs/gnome-vfs-inet-connection.h>
44 #include <errno.h>
45 #include "aprs_display.h"
46
47 #ifndef LEGACY
48 #    include <hildon/hildon-note.h>
49 #    include <hildon/hildon-banner.h>
50 #else
51 #    include <hildon-widgets/hildon-note.h>
52 #    include <hildon-widgets/hildon-banner.h>
53 #endif
54
55 #include "types.h"
56 #include "data.h"
57 #include "defines.h"
58
59 #include "display.h"
60 #include "aprs.h"
61 #include "gps.h"
62 #include "gpsbt.h"
63 #include "path.h"
64 #include "util.h"
65
66 #include "aprs_decode.h"
67
68 static volatile GThread*        _aprs_inet_thread = NULL;
69 static volatile GThread*    _aprs_tty_thread = NULL;
70
71 static GMutex*                          _aprs_inet_init_mutex = NULL;
72 static GMutex*                          _aprs_tty_init_mutex = NULL;
73
74 static gint                             _aprs_rcvr_retry_count = 0;
75
76 #define VERSIONFRM      "APRS"
77 extern AprsDataRow*                     n_first;  // pointer to first element in name sorted station list
78
79
80
81 TWriteBuffer _write_buffer[APRS_PORT_COUNT];
82
83 gboolean send_line(gchar* text, gint text_len, TAprsPort port);
84
85 static gboolean aprs_handle_error_idle(gchar *error)
86 {
87     printf("%s(%s)\n", __PRETTY_FUNCTION__, error);
88
89     /* Ask for re-try. */
90     if(++_aprs_rcvr_retry_count > 2)
91     {
92         GtkWidget *confirm;
93         gchar buffer[BUFFER_SIZE];
94
95         /* Reset retry count. */
96         _aprs_rcvr_retry_count = 0;
97
98         snprintf(buffer, sizeof(buffer), "%s\nRetry?", error);
99         confirm = hildon_note_new_confirmation(GTK_WINDOW(_window), buffer);
100
101         aprs_server_disconnect();
102
103         if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
104         {
105             aprs_server_connect(); /* Try again. */
106         }
107         else
108         {
109             /* Reset Connect to APRS menu item. */
110             gtk_check_menu_item_set_active(
111                     GTK_CHECK_MENU_ITEM(_menu_enable_aprs_inet_item), FALSE);
112         }
113
114         /* Ask user to re-connect. */
115         gtk_widget_destroy(confirm);
116     }
117     else
118     {
119         aprs_server_disconnect();
120         aprs_server_connect(); /* Try again. */
121     }
122
123     g_free(error);
124
125     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
126     return FALSE;
127 }
128
129
130 /**
131  * Set the connection state.  This function controls all connection-related
132  * banners.
133  */
134 void set_aprs_tty_conn_state(ConnState new_conn_state)
135 {
136     printf("%s(%d)\n", __PRETTY_FUNCTION__, new_conn_state);
137
138     switch(_aprs_tty_state = new_conn_state)
139     {
140         case RCVR_OFF:
141         case RCVR_FIXED:
142         case RCVR_UP:  
143           if(_connect_banner)
144             {
145                 gtk_widget_destroy(_connect_banner);
146                 _connect_banner = NULL;
147             }
148             break;
149         case RCVR_DOWN:
150             if(!_connect_banner)
151                 _connect_banner = hildon_banner_show_animation(
152                         _window, NULL, _("Attempting to connect to TNC"));
153             break;
154
155         default: ; /* to quell warning. */
156     }
157
158     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
159 }
160
161 /**
162  * Set the connection state.  This function controls all connection-related
163  * banners.
164  */
165 void set_aprs_inet_conn_state(ConnState new_conn_state)
166 {
167     printf("%s(%d)\n", __PRETTY_FUNCTION__, new_conn_state);
168
169     switch(_aprs_inet_state = new_conn_state)
170     {
171         case RCVR_OFF:
172         case RCVR_FIXED:
173         case RCVR_UP:  
174           if(_connect_banner)
175             {
176                 gtk_widget_destroy(_connect_banner);
177                 _connect_banner = NULL;
178             }
179             if(_fix_banner)
180             {
181                 gtk_widget_destroy(_fix_banner);
182                 _fix_banner = NULL;
183             }
184             break;
185         case RCVR_DOWN:
186             if(_fix_banner)
187             {
188                 gtk_widget_destroy(_fix_banner);
189                 _fix_banner = NULL;
190             }
191             if(!_connect_banner)
192                 _connect_banner = hildon_banner_show_animation(
193                         _window, NULL, _("Attempting to connect to APRS server"));
194             break;
195
196         default: ; /* to quell warning. */
197     }
198
199     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
200 }
201
202
203 static gboolean aprs_parse_server_packet(gchar *packet)
204 {
205     decode_ax25_line(packet, APRS_PORT_INET);
206
207     g_free(packet);
208
209     return FALSE;
210 }
211
212
213 void update_aprs_inet_options(gboolean force)
214 {
215         // If auto filter is not or we are not connected then stop
216         if(!_aprs_server_auto_filter_on 
217                         || !_aprs_enable 
218                         || !_aprs_inet_enable 
219                         || _aprs_server_auto_filter_km <= 0) return ;
220         
221
222         // Disconnect
223         aprs_server_disconnect();
224         //Re-connect
225         aprs_server_connect();
226         
227 }
228
229 gchar *create_aprs_inet_options_string()
230 {
231         gint current_lat = (gint)round(_gps.lat);
232         gint current_lon = (gint)round(_gps.lon);
233         gchar *filter = NULL;
234         
235         filter = g_strdup_printf("user %s pass %s vers %s v%s filter r/%d/%d/%d \r\n ",
236                         _aprs_mycall, _aprs_inet_server_validation, PACKAGE, VERSION,
237                         current_lat, current_lon, _aprs_server_auto_filter_km );
238         
239         return filter;
240 }
241
242
243 static void thread_read_server()
244 {
245     gchar buf[APRS_BUFFER_SIZE];
246     gchar *buf_curr = buf;
247     gchar *buf_last = buf + sizeof(buf) - 1;
248     GnomeVFSFileSize bytes_read;
249     GnomeVFSResult vfs_result;
250     GnomeVFSInetConnection *iconn = NULL;
251     GnomeVFSSocket *socket = NULL;
252     GThread *my_thread = g_thread_self();
253     gboolean error = FALSE;
254
255     printf("%s(%s)\n", __PRETTY_FUNCTION__, _aprs_server);
256
257     
258     //fprintf(stderr, "Starting thread...\n");
259     
260     /* Lock/Unlock the mutex to ensure that _aprs_inet_thread is done being set. */
261     g_mutex_lock(_aprs_inet_init_mutex);
262     g_mutex_unlock(_aprs_inet_init_mutex);
263     
264     if(!error && my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL)
265     {
266         gint tryno;
267
268         /* Attempt to connect to APRS server. */
269         for(tryno = 0; tryno < 10; tryno++)
270         {
271             /* Create a socket to interact with server. */
272             GTimeVal timeout = { 1000, 0 };
273             gchar *filter = create_aprs_inet_options_string();
274             //fprintf(stderr, filter);              
275
276             if(GNOME_VFS_OK != (vfs_result = gnome_vfs_inet_connection_create(
277                             &iconn,
278                             _aprs_server,
279                             _aprs_server_port,
280                             NULL))
281                || NULL == ( socket = gnome_vfs_inet_connection_to_socket(iconn))
282                || GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_set_timeout(
283                            socket, &timeout, NULL))
284                || GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_write( socket,
285                                                 filter, strlen(filter), &bytes_read, NULL))
286               )
287             {
288                 g_free(filter);
289                 sleep(1);
290             }
291             else
292             {
293                 g_free(filter);
294                 break;
295             }
296         }
297
298
299         if(!iconn)
300         {
301             g_printerr("Error connecting to APRS server: (%d) %s\n",
302                     vfs_result, gnome_vfs_result_to_string(vfs_result));
303             g_idle_add((GSourceFunc)aprs_handle_error_idle,
304                     g_strdup_printf("%s",
305                     _("Error connecting to APRS server.")));
306             error = TRUE;
307         }
308     }
309     
310
311     if(!error && my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL)
312     {
313         
314         set_aprs_inet_conn_state(RCVR_UP);
315
316         while(my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL)
317         {
318             gchar *eol;
319             
320                 
321             vfs_result = gnome_vfs_socket_read( 
322                         socket,
323                     buf,
324                     buf_last - buf_curr,
325                     &bytes_read,
326                     NULL);
327             
328   
329             if(vfs_result != GNOME_VFS_OK)
330             {
331                 if(my_thread == _aprs_inet_thread)
332                 {
333                     // Error wasn't user-initiated. 
334                     g_idle_add((GSourceFunc)aprs_handle_error_idle,
335                             g_strdup_printf("%s %u",
336                                 _("Error reading APRS data."), vfs_result));
337
338                 }
339
340                 fprintf(stderr, "Read error: %s\n", gnome_vfs_result_to_string(vfs_result));
341                 error = TRUE;
342                 break;
343             }
344
345             /* Loop through the buffer and read each packet. */
346             buf_curr += bytes_read;
347             *buf_curr = '\0'; /* append a \0 so we can read as string */
348             while(!error && my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL 
349                         && (eol = strchr(buf, '\n')))
350             {
351                 /* This is the beginning of a sentence; okay to parse. */
352                 *eol = '\0'; /* overwrite \n with \0 */
353
354                 if(my_thread == _aprs_inet_thread)
355                         //g_idle_add_full(G_PRIORITY_HIGH, (GSourceFunc)aprs_parse_server_packet, g_strdup(buf), NULL );
356                     g_idle_add((GSourceFunc)aprs_parse_server_packet, g_strdup(buf));
357
358                 /* If eol is at or after (buf_curr - 1) */
359                 if(eol >= (buf_curr - 1))
360                 {
361                     /* Last read was a newline - reset read buffer */
362                     buf_curr = buf;
363                     *buf_curr = '\0';
364                 }
365                 else
366                 {
367                     /* Move the next line to the front of the buffer. */
368                     memmove(buf, eol + 1,
369                             buf_curr - eol); /* include terminating 0 */
370                     /* Subtract _curr so that it's pointing at the new \0. */
371                     buf_curr -= (eol - buf + 1);
372                 }
373             }
374             _aprs_rcvr_retry_count = 0;
375    
376             // Send any packets queued
377                         // try to get lock, otherwise try next time
378                         if(g_mutex_trylock (_write_buffer[APRS_PORT_INET].write_lock))
379                         {
380                         // Store the current end pointer as it may change
381                                 
382                 gint quantity = 0;
383                 gchar tmp_write_buffer[MAX_DEVICE_BUFFER];
384                 while (_write_buffer[APRS_PORT_INET].write_in_pos != _write_buffer[APRS_PORT_INET].write_out_pos) {
385
386                         tmp_write_buffer[quantity] = _write_buffer[APRS_PORT_INET].device_write_buffer[_write_buffer[APRS_PORT_INET].write_out_pos];
387
388                         _write_buffer[APRS_PORT_INET].write_out_pos++;
389                     if (_write_buffer[APRS_PORT_INET].write_out_pos >= MAX_DEVICE_BUFFER)
390                         _write_buffer[APRS_PORT_INET].write_out_pos = 0;
391
392                     quantity++;
393                 }
394
395                 if(quantity>0)
396                 {
397                         sleep(2);
398                         
399                         GnomeVFSFileSize bytes_read = 0;                                    
400                                         if(GNOME_VFS_OK == gnome_vfs_socket_write( socket,
401                                                         tmp_write_buffer, quantity, &bytes_read, NULL))
402                                         {
403                                                 // OK
404                                                 //fprintf(stderr, "Send packet success: %s (%u)\n", tmp_write_buffer, quantity);
405                                         }
406                                         else
407                                         {
408                                                 // Failed
409                                                 fprintf(stderr, "Failed to send packet: %s (%u)\n", tmp_write_buffer, quantity);
410                                         }
411                                         
412                         sleep(1);
413                 }
414                                 
415                     g_mutex_unlock(_write_buffer[APRS_PORT_INET].write_lock);
416                         }            
417
418         }
419         
420         //fprintf(stderr, "Exiting thread...\n");
421     }
422
423     /* Error, or we're done reading APRS data. */
424
425     /* Clean up. */
426     if(iconn)
427         gnome_vfs_inet_connection_destroy(iconn, NULL);
428         //gnome_vfs_inet_connection_free(iconn, NULL);
429     
430     iconn = NULL;
431     
432     //g_thread_exit(0);
433     
434     
435     printf("%s(): return\n", __PRETTY_FUNCTION__);
436     
437     return;
438 }
439
440 /**
441  * Disconnect from the receiver.  This method cleans up any and everything
442  * that might be associated with the receiver.
443  */
444 void aprs_server_disconnect()
445 {
446     gboolean exit_now = FALSE;
447
448     printf("%s()\n", __PRETTY_FUNCTION__);
449
450     GThread *my_thread = g_thread_self();
451
452     if(my_thread == _aprs_inet_thread)
453     {
454         exit_now = TRUE;
455     }
456
457     g_mutex_lock(_aprs_inet_init_mutex);
458     _aprs_inet_thread = NULL;
459     g_mutex_unlock(_aprs_inet_init_mutex);
460
461     
462     
463     if(_window)
464         set_aprs_inet_conn_state(RCVR_OFF);
465     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
466
467     if(exit_now) 
468     {
469         fprintf(stderr, "Stopping own thread - APRS server\n");
470         exit(0);
471         fprintf(stderr, "Stop Failed\n");
472         
473     }
474 }
475
476 /**
477  * Connect to the server.
478  * This method assumes that _fd is -1 and _channel is NULL.  If unsure, call
479  * rcvr_disconnect() first.
480  * Since this is an idle function, this function returns whether or not it
481  * should be called again, which is always FALSE.
482  */
483 gboolean aprs_server_connect()
484 {
485     printf("%s(%d)\n", __PRETTY_FUNCTION__, _aprs_inet_state);
486
487     if(_aprs_inet_enable && _aprs_inet_state == RCVR_OFF)
488     {
489         set_aprs_inet_conn_state(RCVR_DOWN);
490
491         /* Lock/Unlock the mutex to ensure that the thread doesn't
492          * start until _gps_thread is set. */
493         g_mutex_lock(_aprs_inet_init_mutex);
494
495         
496         _aprs_inet_thread = g_thread_create((GThreadFunc)thread_read_server,
497                 NULL, TRUE, NULL); /* Joinable. */
498         
499 //        g_thread_set_priority(_aprs_inet_thread, G_THREAD_PRIORITY_LOW);
500         
501         g_mutex_unlock(_aprs_inet_init_mutex);
502     }
503
504     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
505     return FALSE;
506 }
507
508 void aprs_init()
509 {
510     printf("%s()\n", __PRETTY_FUNCTION__);
511
512     _aprs_inet_init_mutex = g_mutex_new();
513     _aprs_tty_init_mutex  = g_mutex_new();
514     _write_buffer[APRS_PORT_INET].write_lock = g_mutex_new();
515     _write_buffer[APRS_PORT_TTY].write_lock = g_mutex_new();
516
517     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
518 }
519
520 void aprs_destroy(gboolean last)
521 {
522     static GThread* tmp = NULL;
523     printf("%s()\n", __PRETTY_FUNCTION__);
524
525     if(!last)
526     {
527         if(_aprs_inet_thread)
528         {
529             tmp = (GThread*)_aprs_inet_thread;
530             _aprs_inet_thread = NULL;
531         }
532     }
533     else if(tmp)
534         g_thread_join(tmp);
535
536     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
537 }
538
539 gboolean select_aprs(gint unitx, gint unity, gboolean quick)
540 {       
541     gint x, y;
542     gdouble lat1, lon1, lat2, lon2;
543     static GtkWidget *dialog = NULL;
544     static GtkWidget *list = NULL;
545     static GtkWidget *sw = NULL;
546     static GtkTreeViewColumn *column = NULL;
547     static GtkCellRenderer *renderer = NULL;
548     GtkListStore *store = NULL;
549     GtkTreeIter iter;
550     gboolean selected = FALSE;
551     gint num_stations = 0;
552     AprsStationList *first_station = NULL;
553     AprsStationList *last_station = NULL;
554
555
556     printf("%s()\n", __PRETTY_FUNCTION__);
557
558     x = unitx - pixel2unit(3 * _draw_width);
559     y = unity + pixel2unit(3 * _draw_width);
560     unit2latlon(x, y, lat1, lon1);
561
562     x = unitx + pixel2unit(3 * _draw_width);
563     y = unity - pixel2unit(3 * _draw_width);
564     unit2latlon(x, y, lat2, lon2);
565     gdouble lat, lon;
566
567     
568     AprsDataRow *p_station = (AprsDataRow *)n_first;
569
570     // Look for all stations in selected area
571     while ( (p_station) != NULL) 
572     { 
573         lat = convert_lat_l2d(p_station->coord_lat);
574         lon = convert_lon_l2d(p_station->coord_lon);
575
576         if ( ( lat2 >= lat && lat >= lat1 ) && (lon2 >= lon && lon >= lon1) )
577         {
578             // This may have been clicked on
579                 AprsStationList * p_list_item = (AprsStationList *)malloc(sizeof(AprsStationList));
580
581                 p_list_item->station = p_station;
582                 p_list_item->next = NULL;
583                 
584                 if(first_station == NULL)
585                 {
586
587                         first_station = p_list_item;
588                         last_station = p_list_item;
589                 }
590                 else
591                 {
592                         last_station->next = p_list_item;
593                         last_station = p_list_item;
594                 }
595                 
596                 num_stations++;
597         }
598
599         (p_station) = (p_station)->n_next;  // Next element in list
600     } // End of while loop
601
602     selected = FALSE;
603     
604     if(num_stations==0)
605     {
606         // No station found, maybe a POI was selected?
607     }
608     else if(num_stations == 1)
609     {
610         // Only one station was found, so display it's info
611         if(first_station->station != NULL)
612         {
613                 ShowAprsStationPopup(first_station->station);
614         }
615         selected = TRUE;
616     }
617     else
618     {
619         // Multiple possibilities, therefore ask the user which one
620         
621         // Initialize store. 
622         store = gtk_list_store_new(APRSPOI_NUM_COLUMNS,
623                                G_TYPE_BOOLEAN, //Selected 
624                                G_TYPE_STRING // Callsign
625                         );
626
627         AprsStationList * p_list_item = first_station;
628         
629         while(p_list_item != NULL)
630         {
631                 if(p_list_item->station != NULL)
632                 {
633                         gtk_list_store_append(store, &iter);
634         
635                         gtk_list_store_set(store, &iter,
636                             APRSPOI_CALLSIGN, g_strdup(p_list_item->station->call_sign),
637                             -1);
638                 }
639                 p_list_item = p_list_item->next;
640         }
641   
642         
643             if(dialog == NULL)
644             {
645                 dialog = gtk_dialog_new_with_buttons(_("Select POI"),
646                         GTK_WINDOW(_window), GTK_DIALOG_MODAL,
647                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
648                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
649                         NULL);
650         
651                 gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 300);
652         
653                 sw = gtk_scrolled_window_new (NULL, NULL);
654                 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
655                         GTK_SHADOW_ETCHED_IN);
656                 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
657                         GTK_POLICY_NEVER,
658                         GTK_POLICY_AUTOMATIC);
659                 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
660                         sw, TRUE, TRUE, 0);
661         
662                 list = gtk_tree_view_new();
663                 gtk_container_add(GTK_CONTAINER(sw), list);
664         
665                 gtk_tree_selection_set_mode(
666                         gtk_tree_view_get_selection(GTK_TREE_VIEW(list)),
667                         GTK_SELECTION_SINGLE);
668                 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), TRUE);
669
670                 renderer = gtk_cell_renderer_text_new();
671                 column = gtk_tree_view_column_new_with_attributes(
672                         _("Callsign"), renderer, "text", APRSPOI_CALLSIGN, NULL);
673                 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
674
675             }
676
677             
678             gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
679             g_object_unref(G_OBJECT(store));
680         
681             gtk_widget_show_all(dialog);
682             
683             if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
684             {
685                 if(gtk_tree_selection_get_selected(
686                     gtk_tree_view_get_selection(GTK_TREE_VIEW(list)),
687                     NULL, &iter))
688                 {
689                         // Find the callsign
690                         p_list_item = first_station;
691                         while(p_list_item != NULL)
692                         {
693                                 if(p_list_item->station != NULL)
694                                 {       
695                                         gchar * callsign = NULL;
696                                     gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
697                                         APRSPOI_CALLSIGN, &(callsign),
698                                         -1);
699         
700                                     if(strcmp(p_list_item->station->call_sign,callsign) == 0)
701                                         {
702                                         gtk_widget_hide(dialog);
703                                         
704                                                 ShowAprsStationPopup(p_list_item->station);
705                                                 selected = TRUE;
706                                                 break;
707                                                 
708                                         }
709                                 }
710                                 
711                                 p_list_item = p_list_item->next;
712                         }
713
714                 }
715
716             }
717
718         // Ensure it has been closed
719         gtk_widget_hide(dialog);
720     }
721
722
723     // Free the list, but not the stations
724     if(first_station)
725     {
726             AprsStationList * p_list_item = first_station;
727             
728             while(first_station)
729             {
730                 // Store pointer to delete contents after next pointer is stored        
731                 p_list_item = first_station;
732             
733                 // Move pointer to next
734                 first_station = p_list_item->next;
735                 
736                 free(p_list_item);
737             }
738     }
739
740
741     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, selected);
742     return selected;
743
744 }
745
746
747
748 //*****************************************************************
749 // distance_from_my_station - compute distance from my station and
750 //       course with a given call
751 //
752 // return distance and course
753 //
754 // Returns 0.0 for distance if station not found in database or the
755 // station hasn't sent out a posit yet.
756 //*****************************************************************
757
758 double distance_from_my_station(char *call_sign, gchar *course_deg, gint course_len) {
759     AprsDataRow *p_station;
760     double distance;
761     float value;
762     double d_lat, d_lon;
763
764     distance = 0.0;
765     p_station = NULL;
766     if (search_station_name(&p_station,call_sign,1)) {
767         // Check whether we have a posit yet for this station
768         if ( (p_station->coord_lat == 0l)
769                 && (p_station->coord_lon == 0l) ) {
770             distance = 0.0;
771         }
772         else {
773                 d_lat = convert_lat_l2d(p_station->coord_lat);
774                 d_lon = convert_lon_l2d(p_station->coord_lon);
775                 
776                 value = (float)calculate_distance(_gps.lat, _gps.lon, d_lat, d_lon);
777                 
778                 snprintf(course_deg,  course_len,
779                                 "%.01f°",
780                                 calculate_bearing(_gps.lat, _gps.lon, d_lat, d_lon));
781                 
782             if(_units == UNITS_KM)
783                 distance = value * 1.852;           // nautical miles to km
784             else if(_units == UNITS_MI)
785                 distance = value * 1.15078;         // nautical miles to miles
786             else if(_units == UNITS_NM)
787                 distance = value; 
788             else
789                 distance = 0.0; // Should be unreachable
790             
791                 
792         }
793     }
794     else {  // Station not found
795         distance = 0.0;
796     }
797
798
799     return(distance);
800 }
801
802
803
804 void pad_callsign(char *callsignout, char *callsignin) {
805     int i,l;
806
807     l=(int)strlen(callsignin);
808     for(i=0; i<9;i++) {
809         if(i<l) {
810             if(isalnum((int)callsignin[i]) || callsignin[i]=='-') {
811                 callsignout[i]=callsignin[i];
812             }
813             else {
814                 callsignout[i] = ' ';
815             }
816         }
817         else {
818             callsignout[i] = ' ';
819         }
820     }
821     callsignout[i] = '\0';
822 }
823 /////////// TX functionality
824
825
826
827 // This routine changes callsign chars to proper uppercase chars or
828 // numerals, fixes the callsign to six bytes, shifts the letters left by
829 // one bit, and puts the SSID number into the proper bits in the seventh
830 // byte.  The callsign as processed is ready for inclusion in an
831 // AX.25 header.
832 //
833 void fix_up_callsign(unsigned char *data, int data_size) {
834     unsigned char new_call[8] = "       ";  // Start with seven spaces
835     int ssid = 0;
836     int i;
837     int j = 0;
838     int digipeated_flag = 0;
839
840
841     // Check whether we've digipeated through this callsign yet.
842     if (strstr((const char *)data,"*") != 0) {
843          digipeated_flag++;
844     }
845
846     // Change callsign to upper-case and pad out to six places with
847     // space characters.
848     for (i = 0; i < (int)strlen((const char *)data); i++) {
849         toupper(data[i]);
850
851         if (data[i] == '-') {   // Stop at '-'
852             break;
853         }
854         else if (data[i] == '*') {
855         }
856         else {
857             new_call[j++] = data[i];
858         }
859     }
860     new_call[7] = '\0';
861
862     //fprintf(stderr,"new_call:(%s)\n",new_call);
863
864     // Handle SSID.  'i' should now be pointing at a dash or at the
865     // terminating zero character.
866     if ( (i < (int)strlen((const char *)data)) && (data[i++] == '-') ) {   // We might have an SSID
867         if (data[i] != '\0')
868             ssid = atoi((const char *)&data[i]);
869 //            ssid = data[i++] - 0x30;    // Convert from ascii to int
870 //        if (data[i] != '\0')
871 //            ssid = (ssid * 10) + (data[i] - 0x30);
872     }
873
874 //fprintf(stderr,"SSID:%d\t",ssid);
875
876     if (ssid >= 0 && ssid <= 15) {
877         new_call[6] = ssid | 0x30;  // Set 2 reserved bits
878     }
879     else {  // Whacko SSID.  Set it to zero
880         new_call[6] = 0x30;     // Set 2 reserved bits
881     }
882
883     if (digipeated_flag) {
884         new_call[6] = new_call[6] | 0x40; // Set the 'H' bit
885     }
886  
887     // Shift each byte one bit to the left
888     for (i = 0; i < 7; i++) {
889         new_call[i] = new_call[i] << 1;
890         new_call[i] = new_call[i] & 0xfe;
891     }
892
893 //fprintf(stderr,"Last:%0x\n",new_call[6]);
894
895     // Write over the top of the input string with the newly
896     // formatted callsign
897     xastir_snprintf((char *)data,
898         data_size,
899         "%s",
900         new_call);
901 }
902
903
904
905
906 // Create an AX25 frame and then turn it into a KISS packet.  Dump
907 // it into the transmit queue.
908 //
909 void send_ax25_frame(TAprsPort port, gchar *source, gchar *destination, gchar *path, gchar *data) {
910     unsigned char temp_source[15];
911     unsigned char temp_dest[15];
912     unsigned char temp[15];
913     unsigned char control[2], pid[2];
914     unsigned char transmit_txt[MAX_LINE_SIZE*2];
915     unsigned char transmit_txt2[MAX_LINE_SIZE*2];
916     unsigned char c;
917     int i, j;
918     int erd;
919     int write_in_pos_hold;
920
921
922 //fprintf(stderr,"KISS String:%s>%s,%s:%s\n",source,destination,path,data);
923
924     // Check whether transmits are disabled globally
925 //    if (transmit_disable) {
926 //        return;
927 //    }    
928
929     // Check whether transmit has been enabled for this interface.
930     // If not, get out while the gettin's good.
931 //    if (devices[port].transmit_data != 1) {
932 //        return;
933 //    }
934
935     transmit_txt[0] = '\0';
936
937     // Format the destination callsign
938     snprintf((char *)temp_dest,
939         sizeof(temp_dest),
940         "%s",
941         destination);
942     fix_up_callsign(temp_dest, sizeof(temp_dest));
943     
944     snprintf((char *)transmit_txt,
945         sizeof(transmit_txt),
946         "%s",
947         temp_dest);
948
949     // Format the source callsign
950     snprintf((char *)temp_source,
951         sizeof(temp_source),
952         "%s",
953         source);
954     fix_up_callsign(temp_source, sizeof(temp_source));
955     
956     strncat((char *)transmit_txt,
957         (char *)temp_source,
958         sizeof(transmit_txt) - strlen((char *)transmit_txt));
959
960     // Break up the path into individual callsigns and send them one
961     // by one to fix_up_callsign().  If we get passed an empty path,
962     // we merely skip this section and no path gets added to
963     // "transmit_txt".
964     j = 0;
965     temp[0] = '\0'; // Start with empty path
966     if ( (path != NULL) && (strlen(path) != 0) ) {
967         while (path[j] != '\0') {
968             i = 0;
969             while ( (path[j] != ',') && (path[j] != '\0') ) {
970                 temp[i++] = path[j++];
971             }
972             temp[i] = '\0';
973
974             if (path[j] == ',') {   // Skip over comma
975                 j++;
976             }
977
978             fix_up_callsign(temp, sizeof(temp));
979             strncat((char *)transmit_txt,
980                 (char *)temp,
981                 sizeof(transmit_txt) - strlen((char *)transmit_txt));
982         }
983     }
984
985     // Set the end-of-address bit on the last callsign in the
986     // address field
987     transmit_txt[strlen((const char *)transmit_txt) - 1] |= 0x01;
988
989     // Add the Control byte
990     control[0] = 0x03;
991     control[1] = '\0';
992     strncat((char *)transmit_txt,
993         (char *)control,
994         sizeof(transmit_txt) - strlen((char *)transmit_txt));
995
996     // Add the PID byte
997     pid[0] = 0xf0;
998     pid[1] = '\0';
999     strncat((char *)transmit_txt,
1000         (char *)pid,
1001         sizeof(transmit_txt) - strlen((char *)transmit_txt));
1002
1003     // Append the information chars
1004     strncat((char *)transmit_txt,
1005         data,
1006         sizeof(transmit_txt) - strlen((char *)transmit_txt));
1007
1008     //fprintf(stderr,"%s\n",transmit_txt);
1009
1010     // Add the KISS framing characters and do the proper escapes.
1011     j = 0;
1012     transmit_txt2[j++] = KISS_FEND;
1013
1014     // Note:  This byte is where different interfaces would be
1015     // specified:
1016     transmit_txt2[j++] = 0x00;
1017
1018     for (i = 0; i < (int)strlen((const char *)transmit_txt); i++) {
1019         c = transmit_txt[i];
1020         if (c == KISS_FEND) {
1021             transmit_txt2[j++] = KISS_FESC;
1022             transmit_txt2[j++] = KISS_TFEND;
1023         }
1024         else if (c == KISS_FESC) {
1025             transmit_txt2[j++] = KISS_FESC;
1026             transmit_txt2[j++] = KISS_TFESC;
1027         }
1028         else {
1029             transmit_txt2[j++] = c;
1030         }
1031     }
1032     transmit_txt2[j++] = KISS_FEND;
1033
1034     // Terminate the string, but don't increment the 'j' counter.
1035     // We don't want to send the NULL byte out the KISS interface,
1036     // just make sure the string is terminated in all cases.
1037     //
1038     transmit_txt2[j] = '\0';
1039
1040 //-------------------------------------------------------------------
1041 // Had to snag code from port_write_string() below because our string
1042 // needs to have 0x00 chars inside it.  port_write_string() can't
1043 // handle that case.  It's a good thing the transmit queue stuff
1044 // could handle it.
1045 //-------------------------------------------------------------------
1046
1047     erd = 0;
1048
1049
1050     
1051
1052         port_write_string(
1053                         transmit_txt2,
1054                         j/*length*/,
1055                 APRS_PORT_TTY);
1056
1057         
1058         
1059 /*
1060     g_mutex_lock (_write_buffer[port].write_lock);
1061     {
1062         
1063             write_in_pos_hold = _write_buffer[port].write_in_pos;
1064         
1065             for (i = 0; i < j && !erd; i++) {
1066                 _write_buffer[port].device_write_buffer[_write_buffer[port].write_in_pos++] = transmit_txt2[i];
1067                 if (_write_buffer[port].write_in_pos >= MAX_DEVICE_BUFFER)
1068                         _write_buffer[port].write_in_pos = 0;
1069         
1070                 if (_write_buffer[port].write_in_pos == _write_buffer[port].write_out_pos) {
1071         
1072                     // clear this restore original write_in pos and dump this string 
1073                         _write_buffer[port].write_in_pos = write_in_pos_hold;
1074                         _write_buffer[port].errors++;
1075                     erd = 1;
1076                 }
1077             }
1078             
1079             g_mutex_unlock (_write_buffer[port].write_lock);
1080     }
1081 */
1082 }
1083
1084
1085
1086
1087 // convert latitude from long to string 
1088 // Input is in Xastir coordinate system
1089 //
1090 // CONVERT_LP_NOSP      = DDMM.MMN
1091 // CONVERT_HP_NOSP      = DDMM.MMMN
1092 // CONVERT_VHP_NOSP     = DDMM.MMMMN
1093 // CONVERT_LP_NORMAL    = DD MM.MMN
1094 // CONVERT_HP_NORMAL    = DD MM.MMMN
1095 // CONVERT_UP_TRK       = NDD MM.MMMM
1096 // CONVERT_DEC_DEG      = DD.DDDDDN
1097 // CONVERT_DMS_NORMAL   = DD MM SS.SN
1098 // CONVERT_DMS_NORMAL_FORMATED   = DD'MM'SS.SN
1099 // CONVERT_HP_NORMAL_FORMATED   = DD'MM.MMMMN
1100 //
1101 void convert_lat_l2s(long lat, char *str, int str_len, int type) {
1102     char ns;
1103     float deg, min, sec;
1104     int ideg, imin;
1105     long temp;
1106
1107
1108     str[0] = '\0';
1109     deg = (float)(lat - 32400000l) / 360000.0;
1110  
1111     // Switch to integer arithmetic to avoid floating-point
1112     // rounding errors.
1113     temp = (long)(deg * 100000);
1114
1115     ns = 'S';
1116     if (temp <= 0) {
1117         ns = 'N';
1118         temp = labs(temp);
1119     }   
1120
1121     ideg = (int)temp / 100000;
1122     min = (temp % 100000) * 60.0 / 100000.0;
1123
1124     // Again switch to integer arithmetic to avoid floating-point
1125     // rounding errors.
1126     temp = (long)(min * 1000);
1127     imin = (int)(temp / 1000);
1128     sec = (temp % 1000) * 60.0 / 1000.0;
1129
1130     switch (type) {
1131
1132         case(CONVERT_LP_NOSP): /* do low P w/no space */
1133             xastir_snprintf(str,
1134                 str_len,
1135                 "%02d%05.2f%c",
1136                 ideg,
1137 //                min+0.001, // Correct possible unbiased rounding
1138                 min,
1139                 ns);
1140             break;
1141
1142         case(CONVERT_LP_NORMAL): /* do low P normal */
1143             xastir_snprintf(str,
1144                 str_len,
1145                 "%02d %05.2f%c",
1146                 ideg,
1147 //                min+0.001, // Correct possible unbiased rounding
1148                 min,
1149                 ns);
1150             break;
1151
1152         case(CONVERT_HP_NOSP): /* do HP w/no space */
1153             xastir_snprintf(str,
1154                 str_len,
1155                 "%02d%06.3f%c",
1156                 ideg,
1157 //                min+0.0001, // Correct possible unbiased rounding
1158                 min,
1159                 ns);
1160             break;
1161
1162         case(CONVERT_VHP_NOSP): /* do Very HP w/no space */
1163             xastir_snprintf(str,
1164                 str_len,
1165                 "%02d%07.4f%c",
1166                 ideg,
1167 //                min+0.00001, // Correct possible unbiased rounding
1168                 min,
1169                 ns);
1170             break;
1171
1172         case(CONVERT_UP_TRK): /* for tracklog files */
1173             xastir_snprintf(str,
1174                 str_len,
1175                 "%c%02d %07.4f",
1176                 ns,
1177                 ideg,
1178 //                min+0.00001); // Correct possible unbiased rounding
1179                 min);
1180             break;
1181
1182         case(CONVERT_DEC_DEG):
1183             xastir_snprintf(str,
1184                 str_len,
1185                 "%08.5f%c",
1186 //                (ideg+min/60.0)+0.000001, // Correct possible unbiased rounding
1187                 ideg+min/60.0,
1188                 ns);
1189             break;
1190
1191         case(CONVERT_DMS_NORMAL):
1192             xastir_snprintf(str,
1193                 str_len,
1194                 "%02d %02d %04.1f%c",
1195                 ideg,
1196                 imin,
1197 //                sec+0.01, // Correct possible unbiased rounding
1198                 sec,
1199                 ns);
1200             break;
1201         
1202         case(CONVERT_DMS_NORMAL_FORMATED):
1203             xastir_snprintf(str,
1204                 str_len,
1205                 "%02d°%02d\'%04.1f%c",
1206                 ideg,
1207                 imin,
1208 //                sec+0.01, // Correct possible unbiased rounding
1209                 sec,
1210                 ns);
1211             break;
1212
1213         case(CONVERT_HP_NORMAL_FORMATED):
1214             xastir_snprintf(str,
1215                 str_len,
1216                 "%02d°%06.3f%c",
1217                 ideg,
1218 //                min+0.0001, // Correct possible unbiased roundin
1219                 min,
1220                 ns);
1221             break;
1222         
1223         case(CONVERT_HP_NORMAL):
1224         default: /* do HP normal */
1225             xastir_snprintf(str,
1226                 str_len,
1227                 "%02d %06.3f%c",
1228                 ideg,
1229 //                min+0.0001, // Correct possible unbiased rounding
1230                 min,
1231                 ns);
1232             break;
1233     }
1234 }
1235
1236
1237
1238
1239
1240 // convert longitude from long to string
1241 // Input is in Xastir coordinate system
1242 //
1243 // CONVERT_LP_NOSP      = DDDMM.MME
1244 // CONVERT_HP_NOSP      = DDDMM.MMME
1245 // CONVERT_VHP_NOSP     = DDDMM.MMMME
1246 // CONVERT_LP_NORMAL    = DDD MM.MME
1247 // CONVERT_HP_NORMAL    = DDD MM.MMME
1248 // CONVERT_UP_TRK       = EDDD MM.MMMM
1249 // CONVERT_DEC_DEG      = DDD.DDDDDE
1250 // CONVERT_DMS_NORMAL   = DDD MM SS.SN
1251 // CONVERT_DMS_NORMAL_FORMATED   = DDD'MM'SS.SN
1252 //
1253 void convert_lon_l2s(long lon, char *str, int str_len, int type) {
1254     char ew;
1255     float deg, min, sec;
1256     int ideg, imin;
1257     long temp;
1258
1259     str[0] = '\0';
1260     deg = (float)(lon - 64800000l) / 360000.0;
1261
1262     // Switch to integer arithmetic to avoid floating-point rounding
1263     // errors.
1264     temp = (long)(deg * 100000);
1265
1266     ew = 'E';
1267     if (temp <= 0) {
1268         ew = 'W';
1269         temp = labs(temp);
1270     }
1271
1272     ideg = (int)temp / 100000;
1273     min = (temp % 100000) * 60.0 / 100000.0;
1274
1275     // Again switch to integer arithmetic to avoid floating-point
1276     // rounding errors.
1277     temp = (long)(min * 1000);
1278     imin = (int)(temp / 1000);
1279     sec = (temp % 1000) * 60.0 / 1000.0;
1280
1281     switch(type) {
1282
1283         case(CONVERT_LP_NOSP): /* do low P w/nospacel */
1284             xastir_snprintf(str,
1285                 str_len,
1286                 "%03d%05.2f%c",
1287                 ideg,
1288 //                min+0.001, // Correct possible unbiased rounding
1289                 min,
1290                 ew);
1291             break;
1292
1293         case(CONVERT_LP_NORMAL): /* do low P normal */
1294             xastir_snprintf(str,
1295                 str_len,
1296                 "%03d %05.2f%c",
1297                 ideg,
1298 //                min+0.001, // Correct possible unbiased rounding
1299                 min,
1300                 ew);
1301             break;
1302
1303         case(CONVERT_HP_NOSP): /* do HP w/nospace */
1304             xastir_snprintf(str,
1305                 str_len,
1306                 "%03d%06.3f%c",
1307                 ideg,
1308 //                min+0.0001, // Correct possible unbiased rounding
1309                 min,
1310                 ew);
1311             break;
1312
1313         case(CONVERT_VHP_NOSP): /* do Very HP w/nospace */
1314             xastir_snprintf(str,
1315                 str_len,
1316                 "%03d%07.4f%c",
1317                 ideg,
1318 //                min+0.00001, // Correct possible unbiased rounding
1319                 min,
1320                 ew);
1321             break;
1322
1323         case(CONVERT_UP_TRK): /* for tracklog files */
1324             xastir_snprintf(str,
1325                 str_len,
1326                 "%c%03d %07.4f",
1327                 ew,
1328                 ideg,
1329 //                min+0.00001); // Correct possible unbiased rounding
1330                 min);
1331             break;
1332
1333         case(CONVERT_DEC_DEG):
1334             xastir_snprintf(str,
1335                 str_len,
1336                 "%09.5f%c",
1337 //                (ideg+min/60.0)+0.000001, // Correct possible unbiased rounding
1338                 ideg+min/60.0,
1339                 ew);
1340             break;
1341
1342         case(CONVERT_DMS_NORMAL):
1343             xastir_snprintf(str,
1344                 str_len,
1345                 "%03d %02d %04.1f%c",
1346                 ideg,
1347                 imin,
1348 //                sec+0.01, // Correct possible unbiased rounding
1349                 sec,
1350                 ew);
1351             break;
1352
1353         case(CONVERT_DMS_NORMAL_FORMATED):
1354             xastir_snprintf(str,
1355                 str_len,
1356                 "%03d°%02d\'%04.1f%c",
1357                 ideg,
1358                 imin,
1359 //                sec+0.01, // Correct possible unbiased rounding
1360                 sec,
1361                 ew);
1362             break;
1363         
1364         case(CONVERT_HP_NORMAL_FORMATED):
1365             xastir_snprintf(str,
1366                 str_len,
1367                 "%03d°%06.3f%c",
1368                 ideg,
1369 //                min+0.0001, // Correct possible unbiased rounding
1370                 min,
1371                 ew);
1372             break;
1373
1374         case(CONVERT_HP_NORMAL):
1375         default: /* do HP normal */
1376             xastir_snprintf(str,
1377                 str_len,
1378                 "%03d %06.3f%c",
1379                 ideg,
1380 //                min+0.0001, // Correct possible unbiased rounding
1381                 min,
1382                 ew);
1383             break;
1384     }
1385 }
1386
1387
1388
1389
1390
1391
1392 /*************************************************************************/
1393 /* output_lat - format position with position_amb_chars for transmission */
1394 /*************************************************************************/
1395 /*
1396 char *output_lat(char *in_lat, int comp_pos) {
1397     int i,j;
1398     int position_amb_chars = 0;
1399 //fprintf(stderr,"in_lat:%s\n", in_lat);
1400
1401     if (!comp_pos) {
1402         // Don't do this as it results in truncation!
1403         //in_lat[7]=in_lat[8]; // Shift N/S down for transmission
1404     }
1405     else if (position_amb_chars>0) {
1406         in_lat[7]='0';
1407     }
1408
1409     j=0;
1410     if (position_amb_chars>0 && position_amb_chars<5) {
1411         for (i=6;i>(6-position_amb_chars-j);i--) {
1412             if (i==4) {
1413                 i--;
1414                 j=1;
1415             }
1416             if (!comp_pos) {
1417                 in_lat[i]=' ';
1418             } else
1419                 in_lat[i]='0';
1420         }
1421     }
1422
1423     if (!comp_pos) {
1424         in_lat[8] = '\0';
1425     }
1426
1427     return(in_lat);
1428 }
1429 */
1430
1431
1432
1433 /**************************************************************************/
1434 /* output_long - format position with position_amb_chars for transmission */
1435 /**************************************************************************/
1436 /*
1437 char *output_long(char *in_long, int comp_pos) {
1438     int i,j;
1439 int position_amb_chars = 0;
1440 //fprintf(stderr,"in_long:%s\n", in_long);
1441
1442     if (!comp_pos) {
1443         // Don't do this as it results in truncation!
1444         //in_long[8]=in_long[9]; // Shift e/w down for transmission
1445     }
1446     else if (position_amb_chars>0) {
1447         in_long[8]='0';
1448     }
1449
1450     j=0;
1451     if (position_amb_chars>0 && position_amb_chars<5) {
1452         for (i=7;i>(7-position_amb_chars-j);i--) {
1453             if (i==5) {
1454                 i--;
1455                 j=1;
1456             }
1457             if (!comp_pos) {
1458                 in_long[i]=' ';
1459             } else
1460                 in_long[i]='0';
1461         }
1462     }
1463
1464     if (!comp_pos)
1465         in_long[9] = '\0';
1466
1467     return(in_long);
1468 }
1469 */
1470
1471
1472
1473 //***********************************************************
1474 // output_my_aprs_data
1475 // This is the function responsible for sending out my own
1476 // posits.  The next function below this one handles objects,
1477 // messages and the like (output_my_data).
1478 //***********************************************************/
1479
1480 /*
1481 void create_output_lat_long(gchar *my_output_lat, gchar *my_output_long )
1482 {
1483     _aprs_transmit_compressed_posit = FALSE;
1484     gchar *my_lat = g_strdup_printf("%lf", _gps.lat);
1485     gchar *my_long = g_strdup_printf("%lf", _gps.lon);
1486
1487     
1488     // Format latitude string for transmit later
1489     if (_aprs_transmit_compressed_posit) {    // High res version
1490         // TODO - enable compressed beacon
1491         snprintf(my_output_lat,
1492             sizeof(my_output_lat),
1493             "%s",
1494             my_lat);
1495  
1496     }
1497     else {  // Create a low-res version of the latitude string
1498         long my_temp_lat;
1499         char temp_data[20];
1500
1501         // Convert to long
1502         my_temp_lat = convert_lat_s2l(my_lat);
1503
1504         // Convert to low-res string
1505         convert_lat_l2s(my_temp_lat,
1506             temp_data,
1507             sizeof(temp_data),
1508             CONVERT_LP_NORMAL);
1509
1510         snprintf(my_output_lat,
1511             sizeof(my_output_lat),
1512             "%c%c%c%c.%c%c%c",
1513             temp_data[0],
1514             temp_data[1],
1515             temp_data[3],
1516             temp_data[4],
1517             temp_data[6],
1518             temp_data[7],
1519             temp_data[8]);
1520     }
1521
1522     (void)output_lat(my_output_lat, _aprs_transmit_compressed_posit);
1523
1524     // Format longitude string for transmit later
1525     if (_aprs_transmit_compressed_posit) {    // High res version
1526         snprintf(my_output_long,
1527             sizeof(my_output_long),
1528             "%s",
1529             my_long);
1530     }
1531     else {  // Create a low-res version of the longitude string
1532         long my_temp_long;
1533         char temp_data[20];
1534
1535         // Convert to long
1536         my_temp_long = convert_lon_s2l(my_long);
1537
1538         // Convert to low-res string
1539         convert_lon_l2s(my_temp_long,
1540             temp_data,
1541             sizeof(temp_data),
1542             CONVERT_LP_NORMAL);
1543
1544         snprintf(my_output_long,
1545             sizeof(my_output_long),
1546             "%c%c%c%c%c.%c%c%c",
1547             temp_data[0],
1548             temp_data[1],
1549             temp_data[2],
1550             temp_data[4],
1551             temp_data[5],
1552             temp_data[7],
1553             temp_data[8],
1554             temp_data[9]);
1555     }
1556
1557     (void)output_long(my_output_long, _aprs_transmit_compressed_posit);
1558
1559 }
1560
1561
1562 void output_my_aprs_data_tty() {
1563 //TODO
1564         return ;
1565
1566         gchar my_output_lat[MAX_LAT];
1567     gchar my_output_long[MAX_LONG];
1568 //    gchar header_txt[MAX_LINE_SIZE+5];
1569 //    gchar header_txt_save[MAX_LINE_SIZE+5];
1570     gchar path_txt[MAX_LINE_SIZE+5];
1571     gchar data_txt[MAX_LINE_SIZE+5];    
1572     gchar temp[MAX_LINE_SIZE+5];
1573     gchar *unproto_path = "";
1574     gchar data_txt2[5];
1575     struct tm *day_time;
1576     gchar my_pos[256];
1577     gchar output_net[256];
1578     gchar output_phg[10];
1579     gchar output_cs[10];
1580     gchar output_alt[20];
1581     gchar output_brk[3];
1582     int ok;
1583     gchar my_comment_tx[APRS_MAX_COMMENT+1];
1584     int interfaces_ok_for_transmit = 0;
1585     gchar my_phg[10];
1586
1587     time_t sec;
1588     gchar header_txt[MAX_LINE_SIZE+5];
1589     gchar header_txt_save[MAX_LINE_SIZE+5];
1590
1591     gchar data_txt_save[MAX_LINE_SIZE+5];
1592
1593
1594     if (!(port_data.status == DEVICE_UP 
1595                 && _aprs_tty_enable && _aprs_enable && _aprs_enable_tty_tx)) return ;
1596
1597     header_txt_save[0] = '\0';
1598     data_txt_save[0] = '\0';
1599     
1600     sec = sec_now();
1601     
1602     my_phg[0] = '\0';
1603         
1604     create_output_lat_long(my_output_lat, my_output_long);
1605         
1606
1607     // First send any header/path info we might need out the port,
1608     // set up TNC's to the proper mode, etc.
1609     ok = 1;
1610
1611     // clear this for a TNC 
1612     output_net[0] = '\0';
1613
1614     // Set my call sign 
1615     // The leading \r is sent to normal serial TNCs.  The only
1616     // reason for it is that some folks' D700s are getting 
1617     // garbage in the input buffer, and the result is the mycall
1618     // line is rejected.  The \r at the beginning clears out the 
1619     // junk and lets it go through.  But data_out_ax25 tries to 
1620     // parse the MYCALL line, and the presence of a leading \r 
1621     // breaks it.
1622     snprintf(header_txt,
1623         sizeof(header_txt),
1624         "%c%s %s\r",
1625         '\3',
1626         ((port_data.device_type !=DEVICE_AX25_TNC)?
1627                     "\rMYCALL":"MYCALL"),
1628                     _aprs_mycall);
1629
1630     // Send the callsign out to the TNC only if the interface is up and tx is enabled???
1631     // We don't set it this way for KISS TNC interfaces.
1632     if ( (port_data.device_type != DEVICE_SERIAL_KISS_TNC)
1633             && (port_data.device_type != DEVICE_SERIAL_MKISS_TNC)
1634             && (port_data.status == DEVICE_UP)
1635             //&& (devices.transmit_data == 1)
1636             //&& !transmit_disable
1637             //&& !posit_tx_disable
1638             ) {
1639         port_write_string(header_txt, APRS_PORT_TTY);
1640 //send_line(gchar* text, gint text_len, TAprsPort port)
1641     }
1642
1643     // Set unproto path:  Get next unproto path in
1644     // sequence.
1645     
1646     snprintf(header_txt,
1647             sizeof(header_txt),
1648             "%c%s %s VIA %s\r",
1649             '\3',
1650             "UNPROTO",
1651             VERSIONFRM,
1652             _aprs_unproto_path);
1653
1654     snprintf(header_txt_save,
1655             sizeof(header_txt_save),
1656             "%s>%s,%s:",
1657             _aprs_mycall,
1658             VERSIONFRM,
1659             _aprs_unproto_path);
1660
1661     snprintf(path_txt,
1662             sizeof(path_txt),
1663             "%s",
1664             _aprs_unproto_path);
1665
1666
1667     // Send the header data to the TNC.  This sets the
1668     // unproto path that'll be used by the next packet.
1669     // We don't set it this way for KISS TNC interfaces.
1670     if ( (port_data.device_type != DEVICE_SERIAL_KISS_TNC)
1671             && (port_data.device_type != DEVICE_SERIAL_MKISS_TNC)
1672             && (port_data.status == DEVICE_UP)
1673             //&& (devices.transmit_data == 1)
1674             //&& !transmit_disable
1675             //&& !posit_tx_disable
1676             ) {
1677         port_write_string(header_txt, APRS_PORT_TTY);
1678     }
1679
1680
1681     // Set converse mode.  We don't need to do this for
1682     // KISS TNC interfaces.  One european TNC (tnc2-ui)
1683     // doesn't accept "conv" but does accept the 'k'
1684     // command.  A Kantronics KPC-2 v2.71 TNC accepts
1685     // the "conv" command but not the 'k' command.
1686     // Figures!
1687     // 
1688     snprintf(header_txt, sizeof(header_txt), "%c%s\r", '\3', APRS_CONVERSE_MODE);
1689  
1690         if ( (port_data.device_type != DEVICE_SERIAL_KISS_TNC)
1691                 && (port_data.device_type != DEVICE_SERIAL_MKISS_TNC)
1692                 && (port_data.status == DEVICE_UP)
1693                 //&& (devices.transmit_data == 1)
1694                 //&& !transmit_disable
1695             //&& !posit_tx_disable
1696         ) {
1697         port_write_string(header_txt, APRS_PORT_TTY);
1698     }
1699     // sleep(1);
1700
1701
1702
1703     // Set up some more strings for later transmission
1704
1705     // send station info 
1706     output_cs[0] = '\0';
1707     output_phg[0] = '\0';
1708     output_alt[0] = '\0';
1709     output_brk[0] = '\0';
1710
1711
1712     if (_aprs_transmit_compressed_posit)
1713     {
1714         // TOOD - enable compressed beacon support
1715
1716 //      snprintf(my_pos,
1717 //            sizeof(my_pos),
1718 //            "%s",
1719 //            compress_posit(my_output_lat,
1720 //                my_group,
1721 //                my_output_long,
1722 //                my_symbol,
1723 //                my_last_course,
1724 //                my_last_speed,  // In knots
1725 //                my_phg));
1726
1727     }
1728     else { // standard non compressed mode 
1729         snprintf(my_pos,
1730             sizeof(my_pos),
1731             "%s%c%s%c",
1732             my_output_lat,
1733             _aprs_beacon_group,
1734             my_output_long,
1735             _aprs_beacon_symbol);
1736         // get PHG, if used for output 
1737         if (strlen(my_phg) >= 6)
1738             snprintf(output_phg,
1739                 sizeof(output_phg),
1740                 "%s",
1741                 my_phg);
1742
1743         // get CSE/SPD, Always needed for output even if 0 
1744         snprintf(output_cs,
1745             sizeof(output_cs),
1746             "%03d/%03d/",
1747             _gps.heading,
1748             _gps.speed);    // Speed in knots
1749
1750         // get altitude 
1751 // TODO
1752 //        if (my_last_altitude_time > 0)
1753 //            snprintf(output_alt,
1754 //                sizeof(output_alt),
1755 //                "A=%06ld/",
1756 //                 my_last_altitude);
1757     }
1758
1759
1760     // APRS_MOBILE LOCAL TIME 
1761
1762 // TODO
1763 //    if((strlen(output_cs) < 8) && (my_last_altitude_time > 0)) {
1764 //        xastir_snprintf(output_brk,
1765 //            sizeof(output_brk),
1766 //            "/");
1767 //    }
1768
1769     day_time = localtime(&sec);
1770
1771     snprintf(data_txt_save,
1772         sizeof(data_txt_save),
1773         "@%02d%02d%02d/%s%s%s%s%s",
1774         day_time->tm_mday,
1775         day_time->tm_hour,
1776         day_time->tm_min,
1777         my_pos,
1778         output_cs,
1779         output_brk,
1780         output_alt,
1781         my_comment_tx);
1782
1783 //WE7U2:
1784     // Truncate at max length for this type of APRS
1785     // packet.
1786     if (_aprs_transmit_compressed_posit) {
1787         if (strlen(data_txt_save) > 61) {
1788             data_txt_save[61] = '\0';
1789         }
1790     }
1791     else { // Uncompressed lat/long
1792         if (strlen(data_txt_save) > 70) {
1793             data_txt_save[70] = '\0';
1794         }
1795     }
1796
1797     // Add '\r' onto end.
1798     strncat(data_txt_save, "\r", 1);
1799
1800     snprintf(data_txt,
1801         sizeof(data_txt),
1802         "%s%s",
1803         output_net,
1804         data_txt_save);
1805
1806
1807     if (ok) {
1808         // Here's where the actual transmit of the posit occurs.  The
1809         // transmit string has been set up in "data_txt" by this point.
1810
1811         // If transmit or posits have been turned off, don't transmit posit
1812         if ( (port_data.status == DEVICE_UP)
1813 //                && (devices.transmit_data == 1)
1814 //                    && !transmit_disable
1815 //                    && !posit_tx_disable
1816                 ) {
1817
1818             interfaces_ok_for_transmit++;
1819
1820 // WE7U:  Change so that path is passed as well for KISS TNC
1821 // interfaces:  header_txt_save would probably be the one to pass,
1822 // or create a new string just for KISS TNC's.
1823
1824             if ( (port_data.device_type == DEVICE_SERIAL_KISS_TNC)
1825                     || (port_data.device_type == DEVICE_SERIAL_MKISS_TNC) ) {
1826
1827                 // Note:  This one has callsign & destination in the string
1828
1829                 // Transmit the posit out the KISS interface
1830                 send_ax25_frame(APRS_PORT_TTY, 
1831                                                 _aprs_mycall,    // source
1832                                 VERSIONFRM,     // destination
1833                                 path_txt,       // path
1834                                 data_txt);      // data
1835             }
1836             else {  // Not a Serial KISS TNC interface
1837
1838
1839                 port_write_string(data_txt, APRS_PORT_TTY);  // Transmit the posit
1840             }
1841
1842
1843             // Put our transmitted packet into the Incoming Data
1844             // window as well.  This way we can see both sides of a
1845             // conversation.  data_port == -1 for x_spider port,
1846             // normal interface number otherwise.  -99 to get a "**"
1847             // display meaning all ports.
1848             //
1849             // For packets that we're igating we end up with a CR or
1850             // LF on the end of them.  Remove that so the display
1851             // looks nice.
1852             //snprintf(temp,
1853             //    sizeof(temp),
1854             //    "%s>%s,%s:%s",
1855             //    my_callsign,
1856             //    VERSIONFRM,
1857             //    unproto_path,
1858             //    data_txt);
1859             //makePrintable(temp);
1860             //packet_data_add("TX ", temp, port);
1861
1862         }
1863     } // End of posit transmit: "if (ok)"
1864 }
1865 */
1866
1867 void create_output_pos_packet(TAprsPort port, gchar **packet, int *length)
1868 {
1869         
1870 //      gchar encodedPos[MAX_LINE_SIZE];
1871         
1872         if(_aprs_transmit_compressed_posit)
1873         {
1874                 // TODO
1875         }
1876         else
1877         {
1878                 //!5122.09N/00008.42W&APRS4R IGATE RUNNING ON NSLU2
1879                 
1880                 // For now just use a simple packet
1881                 
1882                 gchar slat[10];
1883                 gchar slon[10];
1884                 
1885                 gdouble pos = (_gps.lat > 0 ? _gps.lat : _gps.lat*-1);
1886                 
1887                 gdouble min = (pos - (int)pos)*60.0;
1888                 sprintf(slat, "%02d%02d.%02.0f", (int)pos, (int)min,
1889                                     ((min - (int)min)*100.0) );
1890                             
1891                 pos = (_gps.lon > 0 ? _gps.lon : _gps.lon*-1);
1892                 
1893                 min = (pos - (int)pos)*60.0;
1894                 sprintf(slon, "%03d%02d.%02.0f", (int)pos, (int)min,
1895                                                     ((min - (int)min)*100.0) );
1896                 
1897                 *packet = g_strdup_printf(
1898                 //snprintf(encodedPos, MAX_LINE_SIZE,
1899                         "%c%s%c%c%s%c%c%s%c",
1900                         '=',
1901                         slat, 
1902                         (_gps.lat > 0 ? 'N' : 'S'),
1903                         _aprs_beacon_group,
1904                         slon,
1905                         (_gps.lon > 0 ? 'E' : 'W'),
1906                         _aprs_beacon_symbol,
1907                         (port == APRS_PORT_INET ? _aprs_inet_beacon_comment : _aprs_beacon_comment),
1908                         (char)0
1909                         );
1910         }
1911         
1912 /*      *packet = g_strdup_printf(
1913         "%s>%s,%s:%s\r\n",
1914         _aprs_mycall,
1915         VERSIONFRM,
1916         _aprs_unproto_path,
1917         encodedPos);
1918 */
1919         *length = strlen(*packet);
1920 }
1921
1922
1923 void send_packet(TAprsPort port, gchar* to_call, gchar* path, gchar* packet, gint packet_length)
1924 {
1925         if(port == APRS_PORT_INET 
1926                         || !(port_data.device_type == DEVICE_SERIAL_KISS_TNC
1927                 || port_data.device_type == DEVICE_SERIAL_MKISS_TNC) )
1928         {
1929                 gchar *packet_header
1930                         =  g_strdup_printf(
1931                 "%s>%s,%s:",
1932                 _aprs_mycall,
1933                 to_call,
1934                 path);
1935                 
1936                 gchar *full_packet = g_strdup_printf("%s%s\r\n", packet_header, packet);
1937                 
1938                 
1939                 send_line(full_packet, strlen(packet_header)+packet_length+2,  port);
1940         }
1941         else
1942         {
1943                 send_ax25_frame(port, _aprs_mycall, to_call, path, packet);
1944         }
1945         
1946 }
1947
1948 void output_my_aprs_data(TAprsPort port) {
1949         
1950
1951         gchar *packet;
1952         int length = 0;
1953         
1954         
1955     //create_output_lat_long(my_output_lat, my_output_long);
1956         create_output_pos_packet(port, &packet, &length);
1957
1958         
1959         send_packet(port, VERSIONFRM, _aprs_unproto_path, packet, length);
1960         
1961 }
1962
1963 /////////// TTY functionality
1964
1965 int serial_init();
1966
1967 static void thread_read_tty()
1968 {
1969         set_aprs_tty_conn_state(RCVR_UP);
1970         
1971         serial_init ();
1972         
1973     port_read();
1974 }
1975
1976 gboolean aprs_tty_connect()
1977 {
1978     printf("%s(%d)\n", __PRETTY_FUNCTION__, _aprs_tty_state);
1979
1980     if(_aprs_tty_enable && _aprs_tty_state == RCVR_OFF)
1981     {
1982         set_aprs_tty_conn_state(RCVR_DOWN);
1983
1984         // Lock/Unlock the mutex to ensure that the thread doesn't
1985         // start until _gps_thread is set. 
1986         g_mutex_lock(_aprs_tty_init_mutex);
1987
1988         _aprs_tty_thread = g_thread_create((GThreadFunc)thread_read_tty,
1989                 NULL, TRUE, NULL); // Joinable. 
1990
1991         g_mutex_unlock(_aprs_tty_init_mutex);
1992     }
1993
1994     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1995     return FALSE;
1996
1997 }
1998
1999 void aprs_tty_disconnect()
2000 {
2001     gboolean exit_now = FALSE;
2002
2003     printf("%s()\n", __PRETTY_FUNCTION__);
2004
2005     GThread *my_thread = g_thread_self();
2006
2007     if(my_thread == _aprs_tty_thread)
2008     {
2009         exit_now = TRUE;
2010     }
2011
2012     _aprs_tty_thread = NULL;
2013
2014     if(_window)
2015         set_aprs_tty_conn_state(RCVR_OFF);
2016     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
2017
2018     if(exit_now) exit(0);
2019
2020 }
2021
2022
2023 //***********************************************************
2024 // port_write_string()
2025 //
2026 // port is port# used
2027 // data is the string to write
2028 //***********************************************************
2029
2030 void port_write_string(gchar *data, gint len, TAprsPort port) {
2031     int i,erd, retval;
2032     int write_in_pos_hold;
2033
2034     if (data == NULL)
2035         return;
2036
2037     if (data[0] == '\0')
2038         return;
2039     
2040     
2041     
2042     if(port == APRS_PORT_TTY)
2043     {
2044     
2045         if(g_mutex_trylock (_write_buffer[port].write_lock))
2046         {
2047                 //fprintf(stderr, "TTY Write... ");
2048                     retval = (int)write(port_data.channel,
2049                         data,
2050                         len);
2051                     //fprintf(stderr, "done... ");
2052                     g_mutex_unlock (_write_buffer[port].write_lock);
2053                     //fprintf(stderr, "Unlocked\n");
2054         }
2055         else
2056                 fprintf(stderr, "Failed to get lock\n");
2057     }
2058     else
2059     {
2060         send_line(data, len, port);
2061     }
2062 }
2063
2064
2065 gboolean send_line(gchar* text, gint text_len, TAprsPort port)
2066 {
2067         if(APRS_PORT_INET == port && !_aprs_enable_inet_tx) return FALSE;
2068         else if (APRS_PORT_TTY == port && !_aprs_enable_tty_tx) return FALSE;
2069         
2070         if(APRS_PORT_TTY == port)
2071         {
2072         
2073         }
2074         gboolean error = FALSE;
2075         gint i;
2076     gint write_in_pos_hold = _write_buffer[port].write_in_pos;
2077     
2078     // Lock the mutex 
2079     g_mutex_lock(_write_buffer[port].write_lock);
2080     
2081     for (i = 0; i < text_len && !error; i++) {
2082         _write_buffer[port].device_write_buffer[_write_buffer[port].write_in_pos++] 
2083                                                 = text[i];
2084         
2085         if (_write_buffer[port].write_in_pos >= MAX_DEVICE_BUFFER)
2086                 _write_buffer[port].write_in_pos = 0;
2087
2088         if (_write_buffer[port].write_in_pos == _write_buffer[port].write_out_pos) {
2089             fprintf(stderr,"Port %d Buffer overrun\n",port);
2090
2091             /* clear this restore original write_in pos and dump this string */
2092             _write_buffer[port].write_in_pos = write_in_pos_hold;
2093             _write_buffer[port].errors++;
2094             error = TRUE;
2095         }
2096     }
2097         
2098     g_mutex_unlock(_write_buffer[port].write_lock);
2099     
2100     return error;
2101 }
2102
2103
2104 gboolean aprs_send_beacon_inet()
2105 {
2106         aprs_send_beacon(APRS_PORT_INET);
2107
2108 }
2109 gboolean aprs_send_beacon(TAprsPort port)
2110 {
2111         if(_aprs_enable)
2112         {
2113                 output_my_aprs_data(port);
2114                 
2115                 //fprintf(stderr, "Beacon sent\n" );
2116         }
2117         
2118         return TRUE;
2119 }
2120
2121 #endif //INCLUDE_APRS