3 * This file is part of Maemo Mapper.
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.
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.
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/>.
19 * Parts of this code have been ported from Xastir by Rob Williams (10 Aug 2008):
21 * * XASTIR, Amateur Station Tracking and Information Reporting
22 * Copyright (C) 1999,2000 Frank Giannandrea
23 * Copyright (C) 2000-2007 The Xastir Group
42 #include <libgnomevfs/gnome-vfs.h>
43 #include <libgnomevfs/gnome-vfs-inet-connection.h>
45 #include "aprs_display.h"
48 # include <hildon/hildon-note.h>
49 # include <hildon/hildon-banner.h>
51 # include <hildon-widgets/hildon-note.h>
52 # include <hildon-widgets/hildon-banner.h>
66 #include "aprs_decode.h"
68 static volatile GThread* _aprs_inet_thread = NULL;
69 static volatile GThread* _aprs_tty_thread = NULL;
71 static GMutex* _aprs_inet_init_mutex = NULL;
72 static GMutex* _aprs_tty_init_mutex = NULL;
74 static gint _aprs_rcvr_retry_count = 0;
76 #define VERSIONFRM "APRS"
77 extern AprsDataRow* n_first; // pointer to first element in name sorted station list
81 TWriteBuffer _write_buffer[APRS_PORT_COUNT];
83 gboolean send_line(gchar* text, gint text_len, TAprsPort port);
85 static gboolean aprs_handle_error_idle(gchar *error)
87 printf("%s(%s)\n", __PRETTY_FUNCTION__, error);
90 if(++_aprs_rcvr_retry_count > 2)
93 gchar buffer[BUFFER_SIZE];
95 /* Reset retry count. */
96 _aprs_rcvr_retry_count = 0;
98 snprintf(buffer, sizeof(buffer), "%s\nRetry?", error);
99 confirm = hildon_note_new_confirmation(GTK_WINDOW(_window), buffer);
101 aprs_server_disconnect();
103 if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
105 aprs_server_connect(); /* Try again. */
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);
114 /* Ask user to re-connect. */
115 gtk_widget_destroy(confirm);
119 aprs_server_disconnect();
120 aprs_server_connect(); /* Try again. */
125 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
131 * Set the connection state. This function controls all connection-related
134 void set_aprs_tty_conn_state(ConnState new_conn_state)
136 printf("%s(%d)\n", __PRETTY_FUNCTION__, new_conn_state);
138 switch(_aprs_tty_state = new_conn_state)
145 gtk_widget_destroy(_connect_banner);
146 _connect_banner = NULL;
151 _connect_banner = hildon_banner_show_animation(
152 _window, NULL, _("Attempting to connect to TNC"));
155 default: ; /* to quell warning. */
158 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
162 * Set the connection state. This function controls all connection-related
165 void set_aprs_inet_conn_state(ConnState new_conn_state)
167 printf("%s(%d)\n", __PRETTY_FUNCTION__, new_conn_state);
169 switch(_aprs_inet_state = new_conn_state)
176 gtk_widget_destroy(_connect_banner);
177 _connect_banner = NULL;
181 gtk_widget_destroy(_fix_banner);
188 gtk_widget_destroy(_fix_banner);
192 _connect_banner = hildon_banner_show_animation(
193 _window, NULL, _("Attempting to connect to APRS server"));
196 default: ; /* to quell warning. */
199 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
203 static gboolean aprs_parse_server_packet(gchar *packet)
205 decode_ax25_line(packet, APRS_PORT_INET);
213 void update_aprs_inet_options(gboolean force)
215 // If auto filter is not or we are not connected then stop
216 if(!_aprs_server_auto_filter_on
218 || !_aprs_inet_enable
219 || _aprs_server_auto_filter_km <= 0) return ;
223 aprs_server_disconnect();
225 aprs_server_connect();
229 gchar *create_aprs_inet_options_string()
231 gint current_lat = (gint)round(_gps.lat);
232 gint current_lon = (gint)round(_gps.lon);
233 gchar *filter = NULL;
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 );
243 static void thread_read_server()
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;
255 printf("%s(%s)\n", __PRETTY_FUNCTION__, _aprs_server);
258 //fprintf(stderr, "Starting thread...\n");
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);
264 if(!error && my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL)
268 /* Attempt to connect to APRS server. */
269 for(tryno = 0; tryno < 10; tryno++)
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);
276 if(GNOME_VFS_OK != (vfs_result = gnome_vfs_inet_connection_create(
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))
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.")));
311 if(!error && my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL)
314 set_aprs_inet_conn_state(RCVR_UP);
316 while(my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL)
321 vfs_result = gnome_vfs_socket_read(
329 if(vfs_result != GNOME_VFS_OK)
331 if(my_thread == _aprs_inet_thread)
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));
340 fprintf(stderr, "Read error: %s\n", gnome_vfs_result_to_string(vfs_result));
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')))
351 /* This is the beginning of a sentence; okay to parse. */
352 *eol = '\0'; /* overwrite \n with \0 */
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));
358 /* If eol is at or after (buf_curr - 1) */
359 if(eol >= (buf_curr - 1))
361 /* Last read was a newline - reset read buffer */
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);
374 _aprs_rcvr_retry_count = 0;
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))
380 // Store the current end pointer as it may change
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) {
386 tmp_write_buffer[quantity] = _write_buffer[APRS_PORT_INET].device_write_buffer[_write_buffer[APRS_PORT_INET].write_out_pos];
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;
399 GnomeVFSFileSize bytes_read = 0;
400 if(GNOME_VFS_OK == gnome_vfs_socket_write( socket,
401 tmp_write_buffer, quantity, &bytes_read, NULL))
404 //fprintf(stderr, "Send packet success: %s (%u)\n", tmp_write_buffer, quantity);
409 fprintf(stderr, "Failed to send packet: %s (%u)\n", tmp_write_buffer, quantity);
415 g_mutex_unlock(_write_buffer[APRS_PORT_INET].write_lock);
420 //fprintf(stderr, "Exiting thread...\n");
423 /* Error, or we're done reading APRS data. */
427 gnome_vfs_inet_connection_destroy(iconn, NULL);
428 //gnome_vfs_inet_connection_free(iconn, NULL);
435 printf("%s(): return\n", __PRETTY_FUNCTION__);
441 * Disconnect from the receiver. This method cleans up any and everything
442 * that might be associated with the receiver.
444 void aprs_server_disconnect()
446 gboolean exit_now = FALSE;
448 printf("%s()\n", __PRETTY_FUNCTION__);
450 GThread *my_thread = g_thread_self();
452 if(my_thread == _aprs_inet_thread)
457 g_mutex_lock(_aprs_inet_init_mutex);
458 _aprs_inet_thread = NULL;
459 g_mutex_unlock(_aprs_inet_init_mutex);
464 set_aprs_inet_conn_state(RCVR_OFF);
465 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
469 fprintf(stderr, "Stopping own thread - APRS server\n");
471 fprintf(stderr, "Stop Failed\n");
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.
483 gboolean aprs_server_connect()
485 printf("%s(%d)\n", __PRETTY_FUNCTION__, _aprs_inet_state);
487 if(_aprs_inet_enable && _aprs_inet_state == RCVR_OFF)
489 set_aprs_inet_conn_state(RCVR_DOWN);
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);
496 _aprs_inet_thread = g_thread_create((GThreadFunc)thread_read_server,
497 NULL, TRUE, NULL); /* Joinable. */
499 // g_thread_set_priority(_aprs_inet_thread, G_THREAD_PRIORITY_LOW);
501 g_mutex_unlock(_aprs_inet_init_mutex);
504 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
510 printf("%s()\n", __PRETTY_FUNCTION__);
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();
517 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
520 void aprs_destroy(gboolean last)
522 static GThread* tmp = NULL;
523 printf("%s()\n", __PRETTY_FUNCTION__);
527 if(_aprs_inet_thread)
529 tmp = (GThread*)_aprs_inet_thread;
530 _aprs_inet_thread = NULL;
536 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
539 gboolean select_aprs(gint unitx, gint unity, gboolean quick)
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;
550 gboolean selected = FALSE;
551 gint num_stations = 0;
552 AprsStationList *first_station = NULL;
553 AprsStationList *last_station = NULL;
556 printf("%s()\n", __PRETTY_FUNCTION__);
558 x = unitx - pixel2unit(3 * _draw_width);
559 y = unity + pixel2unit(3 * _draw_width);
560 unit2latlon(x, y, lat1, lon1);
562 x = unitx + pixel2unit(3 * _draw_width);
563 y = unity - pixel2unit(3 * _draw_width);
564 unit2latlon(x, y, lat2, lon2);
568 AprsDataRow *p_station = (AprsDataRow *)n_first;
570 // Look for all stations in selected area
571 while ( (p_station) != NULL)
573 lat = convert_lat_l2d(p_station->coord_lat);
574 lon = convert_lon_l2d(p_station->coord_lon);
576 if ( ( lat2 >= lat && lat >= lat1 ) && (lon2 >= lon && lon >= lon1) )
578 // This may have been clicked on
579 AprsStationList * p_list_item = (AprsStationList *)malloc(sizeof(AprsStationList));
581 p_list_item->station = p_station;
582 p_list_item->next = NULL;
584 if(first_station == NULL)
587 first_station = p_list_item;
588 last_station = p_list_item;
592 last_station->next = p_list_item;
593 last_station = p_list_item;
599 (p_station) = (p_station)->n_next; // Next element in list
600 } // End of while loop
606 // No station found, maybe a POI was selected?
608 else if(num_stations == 1)
610 // Only one station was found, so display it's info
611 if(first_station->station != NULL)
613 ShowAprsStationPopup(first_station->station);
619 // Multiple possibilities, therefore ask the user which one
622 store = gtk_list_store_new(APRSPOI_NUM_COLUMNS,
623 G_TYPE_BOOLEAN, //Selected
624 G_TYPE_STRING // Callsign
627 AprsStationList * p_list_item = first_station;
629 while(p_list_item != NULL)
631 if(p_list_item->station != NULL)
633 gtk_list_store_append(store, &iter);
635 gtk_list_store_set(store, &iter,
636 APRSPOI_CALLSIGN, g_strdup(p_list_item->station->call_sign),
639 p_list_item = p_list_item->next;
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,
651 gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 300);
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),
658 GTK_POLICY_AUTOMATIC);
659 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
662 list = gtk_tree_view_new();
663 gtk_container_add(GTK_CONTAINER(sw), list);
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);
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);
678 gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
679 g_object_unref(G_OBJECT(store));
681 gtk_widget_show_all(dialog);
683 if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
685 if(gtk_tree_selection_get_selected(
686 gtk_tree_view_get_selection(GTK_TREE_VIEW(list)),
690 p_list_item = first_station;
691 while(p_list_item != NULL)
693 if(p_list_item->station != NULL)
695 gchar * callsign = NULL;
696 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
697 APRSPOI_CALLSIGN, &(callsign),
700 if(strcmp(p_list_item->station->call_sign,callsign) == 0)
702 gtk_widget_hide(dialog);
704 ShowAprsStationPopup(p_list_item->station);
711 p_list_item = p_list_item->next;
718 // Ensure it has been closed
719 gtk_widget_hide(dialog);
723 // Free the list, but not the stations
726 AprsStationList * p_list_item = first_station;
730 // Store pointer to delete contents after next pointer is stored
731 p_list_item = first_station;
733 // Move pointer to next
734 first_station = p_list_item->next;
741 vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, selected);
748 //*****************************************************************
749 // distance_from_my_station - compute distance from my station and
750 // course with a given call
752 // return distance and course
754 // Returns 0.0 for distance if station not found in database or the
755 // station hasn't sent out a posit yet.
756 //*****************************************************************
758 double distance_from_my_station(char *call_sign, gchar *course_deg, gint course_len) {
759 AprsDataRow *p_station;
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) ) {
773 d_lat = convert_lat_l2d(p_station->coord_lat);
774 d_lon = convert_lon_l2d(p_station->coord_lon);
776 value = (float)calculate_distance(_gps.lat, _gps.lon, d_lat, d_lon);
778 snprintf(course_deg, course_len,
780 calculate_bearing(_gps.lat, _gps.lon, d_lat, d_lon));
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)
789 distance = 0.0; // Should be unreachable
794 else { // Station not found
804 void pad_callsign(char *callsignout, char *callsignin) {
807 l=(int)strlen(callsignin);
810 if(isalnum((int)callsignin[i]) || callsignin[i]=='-') {
811 callsignout[i]=callsignin[i];
814 callsignout[i] = ' ';
818 callsignout[i] = ' ';
821 callsignout[i] = '\0';
823 /////////// TX functionality
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
833 void fix_up_callsign(unsigned char *data, int data_size) {
834 unsigned char new_call[8] = " "; // Start with seven spaces
838 int digipeated_flag = 0;
841 // Check whether we've digipeated through this callsign yet.
842 if (strstr((const char *)data,"*") != 0) {
846 // Change callsign to upper-case and pad out to six places with
848 for (i = 0; i < (int)strlen((const char *)data); i++) {
851 if (data[i] == '-') { // Stop at '-'
854 else if (data[i] == '*') {
857 new_call[j++] = data[i];
862 //fprintf(stderr,"new_call:(%s)\n",new_call);
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
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);
874 //fprintf(stderr,"SSID:%d\t",ssid);
876 if (ssid >= 0 && ssid <= 15) {
877 new_call[6] = ssid | 0x30; // Set 2 reserved bits
879 else { // Whacko SSID. Set it to zero
880 new_call[6] = 0x30; // Set 2 reserved bits
883 if (digipeated_flag) {
884 new_call[6] = new_call[6] | 0x40; // Set the 'H' bit
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;
893 //fprintf(stderr,"Last:%0x\n",new_call[6]);
895 // Write over the top of the input string with the newly
896 // formatted callsign
897 xastir_snprintf((char *)data,
906 // Create an AX25 frame and then turn it into a KISS packet. Dump
907 // it into the transmit queue.
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];
919 int write_in_pos_hold;
922 //fprintf(stderr,"KISS String:%s>%s,%s:%s\n",source,destination,path,data);
924 // Check whether transmits are disabled globally
925 // if (transmit_disable) {
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) {
935 transmit_txt[0] = '\0';
937 // Format the destination callsign
938 snprintf((char *)temp_dest,
942 fix_up_callsign(temp_dest, sizeof(temp_dest));
944 snprintf((char *)transmit_txt,
945 sizeof(transmit_txt),
949 // Format the source callsign
950 snprintf((char *)temp_source,
954 fix_up_callsign(temp_source, sizeof(temp_source));
956 strncat((char *)transmit_txt,
958 sizeof(transmit_txt) - strlen((char *)transmit_txt));
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
965 temp[0] = '\0'; // Start with empty path
966 if ( (path != NULL) && (strlen(path) != 0) ) {
967 while (path[j] != '\0') {
969 while ( (path[j] != ',') && (path[j] != '\0') ) {
970 temp[i++] = path[j++];
974 if (path[j] == ',') { // Skip over comma
978 fix_up_callsign(temp, sizeof(temp));
979 strncat((char *)transmit_txt,
981 sizeof(transmit_txt) - strlen((char *)transmit_txt));
985 // Set the end-of-address bit on the last callsign in the
987 transmit_txt[strlen((const char *)transmit_txt) - 1] |= 0x01;
989 // Add the Control byte
992 strncat((char *)transmit_txt,
994 sizeof(transmit_txt) - strlen((char *)transmit_txt));
999 strncat((char *)transmit_txt,
1001 sizeof(transmit_txt) - strlen((char *)transmit_txt));
1003 // Append the information chars
1004 strncat((char *)transmit_txt,
1006 sizeof(transmit_txt) - strlen((char *)transmit_txt));
1008 //fprintf(stderr,"%s\n",transmit_txt);
1010 // Add the KISS framing characters and do the proper escapes.
1012 transmit_txt2[j++] = KISS_FEND;
1014 // Note: This byte is where different interfaces would be
1016 transmit_txt2[j++] = 0x00;
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;
1024 else if (c == KISS_FESC) {
1025 transmit_txt2[j++] = KISS_FESC;
1026 transmit_txt2[j++] = KISS_TFESC;
1029 transmit_txt2[j++] = c;
1032 transmit_txt2[j++] = KISS_FEND;
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.
1038 transmit_txt2[j] = '\0';
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
1045 //-------------------------------------------------------------------
1060 g_mutex_lock (_write_buffer[port].write_lock);
1063 write_in_pos_hold = _write_buffer[port].write_in_pos;
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;
1070 if (_write_buffer[port].write_in_pos == _write_buffer[port].write_out_pos) {
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++;
1079 g_mutex_unlock (_write_buffer[port].write_lock);
1087 // convert latitude from long to string
1088 // Input is in Xastir coordinate system
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
1101 void convert_lat_l2s(long lat, char *str, int str_len, int type) {
1103 float deg, min, sec;
1109 deg = (float)(lat - 32400000l) / 360000.0;
1111 // Switch to integer arithmetic to avoid floating-point
1113 temp = (long)(deg * 100000);
1121 ideg = (int)temp / 100000;
1122 min = (temp % 100000) * 60.0 / 100000.0;
1124 // Again switch to integer arithmetic to avoid floating-point
1126 temp = (long)(min * 1000);
1127 imin = (int)(temp / 1000);
1128 sec = (temp % 1000) * 60.0 / 1000.0;
1132 case(CONVERT_LP_NOSP): /* do low P w/no space */
1133 xastir_snprintf(str,
1137 // min+0.001, // Correct possible unbiased rounding
1142 case(CONVERT_LP_NORMAL): /* do low P normal */
1143 xastir_snprintf(str,
1147 // min+0.001, // Correct possible unbiased rounding
1152 case(CONVERT_HP_NOSP): /* do HP w/no space */
1153 xastir_snprintf(str,
1157 // min+0.0001, // Correct possible unbiased rounding
1162 case(CONVERT_VHP_NOSP): /* do Very HP w/no space */
1163 xastir_snprintf(str,
1167 // min+0.00001, // Correct possible unbiased rounding
1172 case(CONVERT_UP_TRK): /* for tracklog files */
1173 xastir_snprintf(str,
1178 // min+0.00001); // Correct possible unbiased rounding
1182 case(CONVERT_DEC_DEG):
1183 xastir_snprintf(str,
1186 // (ideg+min/60.0)+0.000001, // Correct possible unbiased rounding
1191 case(CONVERT_DMS_NORMAL):
1192 xastir_snprintf(str,
1194 "%02d %02d %04.1f%c",
1197 // sec+0.01, // Correct possible unbiased rounding
1202 case(CONVERT_DMS_NORMAL_FORMATED):
1203 xastir_snprintf(str,
1205 "%02d°%02d\'%04.1f%c",
1208 // sec+0.01, // Correct possible unbiased rounding
1213 case(CONVERT_HP_NORMAL_FORMATED):
1214 xastir_snprintf(str,
1218 // min+0.0001, // Correct possible unbiased roundin
1223 case(CONVERT_HP_NORMAL):
1224 default: /* do HP normal */
1225 xastir_snprintf(str,
1229 // min+0.0001, // Correct possible unbiased rounding
1240 // convert longitude from long to string
1241 // Input is in Xastir coordinate system
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
1253 void convert_lon_l2s(long lon, char *str, int str_len, int type) {
1255 float deg, min, sec;
1260 deg = (float)(lon - 64800000l) / 360000.0;
1262 // Switch to integer arithmetic to avoid floating-point rounding
1264 temp = (long)(deg * 100000);
1272 ideg = (int)temp / 100000;
1273 min = (temp % 100000) * 60.0 / 100000.0;
1275 // Again switch to integer arithmetic to avoid floating-point
1277 temp = (long)(min * 1000);
1278 imin = (int)(temp / 1000);
1279 sec = (temp % 1000) * 60.0 / 1000.0;
1283 case(CONVERT_LP_NOSP): /* do low P w/nospacel */
1284 xastir_snprintf(str,
1288 // min+0.001, // Correct possible unbiased rounding
1293 case(CONVERT_LP_NORMAL): /* do low P normal */
1294 xastir_snprintf(str,
1298 // min+0.001, // Correct possible unbiased rounding
1303 case(CONVERT_HP_NOSP): /* do HP w/nospace */
1304 xastir_snprintf(str,
1308 // min+0.0001, // Correct possible unbiased rounding
1313 case(CONVERT_VHP_NOSP): /* do Very HP w/nospace */
1314 xastir_snprintf(str,
1318 // min+0.00001, // Correct possible unbiased rounding
1323 case(CONVERT_UP_TRK): /* for tracklog files */
1324 xastir_snprintf(str,
1329 // min+0.00001); // Correct possible unbiased rounding
1333 case(CONVERT_DEC_DEG):
1334 xastir_snprintf(str,
1337 // (ideg+min/60.0)+0.000001, // Correct possible unbiased rounding
1342 case(CONVERT_DMS_NORMAL):
1343 xastir_snprintf(str,
1345 "%03d %02d %04.1f%c",
1348 // sec+0.01, // Correct possible unbiased rounding
1353 case(CONVERT_DMS_NORMAL_FORMATED):
1354 xastir_snprintf(str,
1356 "%03d°%02d\'%04.1f%c",
1359 // sec+0.01, // Correct possible unbiased rounding
1364 case(CONVERT_HP_NORMAL_FORMATED):
1365 xastir_snprintf(str,
1369 // min+0.0001, // Correct possible unbiased rounding
1374 case(CONVERT_HP_NORMAL):
1375 default: /* do HP normal */
1376 xastir_snprintf(str,
1380 // min+0.0001, // Correct possible unbiased rounding
1392 /*************************************************************************/
1393 /* output_lat - format position with position_amb_chars for transmission */
1394 /*************************************************************************/
1396 char *output_lat(char *in_lat, int comp_pos) {
1398 int position_amb_chars = 0;
1399 //fprintf(stderr,"in_lat:%s\n", in_lat);
1402 // Don't do this as it results in truncation!
1403 //in_lat[7]=in_lat[8]; // Shift N/S down for transmission
1405 else if (position_amb_chars>0) {
1410 if (position_amb_chars>0 && position_amb_chars<5) {
1411 for (i=6;i>(6-position_amb_chars-j);i--) {
1433 /**************************************************************************/
1434 /* output_long - format position with position_amb_chars for transmission */
1435 /**************************************************************************/
1437 char *output_long(char *in_long, int comp_pos) {
1439 int position_amb_chars = 0;
1440 //fprintf(stderr,"in_long:%s\n", in_long);
1443 // Don't do this as it results in truncation!
1444 //in_long[8]=in_long[9]; // Shift e/w down for transmission
1446 else if (position_amb_chars>0) {
1451 if (position_amb_chars>0 && position_amb_chars<5) {
1452 for (i=7;i>(7-position_amb_chars-j);i--) {
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 //***********************************************************/
1481 void create_output_lat_long(gchar *my_output_lat, gchar *my_output_long )
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);
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),
1497 else { // Create a low-res version of the latitude string
1502 my_temp_lat = convert_lat_s2l(my_lat);
1504 // Convert to low-res string
1505 convert_lat_l2s(my_temp_lat,
1510 snprintf(my_output_lat,
1511 sizeof(my_output_lat),
1522 (void)output_lat(my_output_lat, _aprs_transmit_compressed_posit);
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),
1531 else { // Create a low-res version of the longitude string
1536 my_temp_long = convert_lon_s2l(my_long);
1538 // Convert to low-res string
1539 convert_lon_l2s(my_temp_long,
1544 snprintf(my_output_long,
1545 sizeof(my_output_long),
1546 "%c%c%c%c%c.%c%c%c",
1557 (void)output_long(my_output_long, _aprs_transmit_compressed_posit);
1562 void output_my_aprs_data_tty() {
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 = "";
1575 struct tm *day_time;
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];
1583 gchar my_comment_tx[APRS_MAX_COMMENT+1];
1584 int interfaces_ok_for_transmit = 0;
1588 gchar header_txt[MAX_LINE_SIZE+5];
1589 gchar header_txt_save[MAX_LINE_SIZE+5];
1591 gchar data_txt_save[MAX_LINE_SIZE+5];
1594 if (!(port_data.status == DEVICE_UP
1595 && _aprs_tty_enable && _aprs_enable && _aprs_enable_tty_tx)) return ;
1597 header_txt_save[0] = '\0';
1598 data_txt_save[0] = '\0';
1604 create_output_lat_long(my_output_lat, my_output_long);
1607 // First send any header/path info we might need out the port,
1608 // set up TNC's to the proper mode, etc.
1611 // clear this for a TNC
1612 output_net[0] = '\0';
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
1622 snprintf(header_txt,
1626 ((port_data.device_type !=DEVICE_AX25_TNC)?
1627 "\rMYCALL":"MYCALL"),
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
1639 port_write_string(header_txt, APRS_PORT_TTY);
1640 //send_line(gchar* text, gint text_len, TAprsPort port)
1643 // Set unproto path: Get next unproto path in
1646 snprintf(header_txt,
1652 _aprs_unproto_path);
1654 snprintf(header_txt_save,
1655 sizeof(header_txt_save),
1659 _aprs_unproto_path);
1664 _aprs_unproto_path);
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
1677 port_write_string(header_txt, APRS_PORT_TTY);
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.
1688 snprintf(header_txt, sizeof(header_txt), "%c%s\r", '\3', APRS_CONVERSE_MODE);
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
1697 port_write_string(header_txt, APRS_PORT_TTY);
1703 // Set up some more strings for later transmission
1705 // send station info
1706 output_cs[0] = '\0';
1707 output_phg[0] = '\0';
1708 output_alt[0] = '\0';
1709 output_brk[0] = '\0';
1712 if (_aprs_transmit_compressed_posit)
1714 // TOOD - enable compressed beacon support
1719 // compress_posit(my_output_lat,
1724 // my_last_speed, // In knots
1728 else { // standard non compressed mode
1735 _aprs_beacon_symbol);
1736 // get PHG, if used for output
1737 if (strlen(my_phg) >= 6)
1738 snprintf(output_phg,
1743 // get CSE/SPD, Always needed for output even if 0
1748 _gps.speed); // Speed in knots
1752 // if (my_last_altitude_time > 0)
1753 // snprintf(output_alt,
1754 // sizeof(output_alt),
1756 // my_last_altitude);
1760 // APRS_MOBILE LOCAL TIME
1763 // if((strlen(output_cs) < 8) && (my_last_altitude_time > 0)) {
1764 // xastir_snprintf(output_brk,
1765 // sizeof(output_brk),
1769 day_time = localtime(&sec);
1771 snprintf(data_txt_save,
1772 sizeof(data_txt_save),
1773 "@%02d%02d%02d/%s%s%s%s%s",
1784 // Truncate at max length for this type of APRS
1786 if (_aprs_transmit_compressed_posit) {
1787 if (strlen(data_txt_save) > 61) {
1788 data_txt_save[61] = '\0';
1791 else { // Uncompressed lat/long
1792 if (strlen(data_txt_save) > 70) {
1793 data_txt_save[70] = '\0';
1797 // Add '\r' onto end.
1798 strncat(data_txt_save, "\r", 1);
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.
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
1818 interfaces_ok_for_transmit++;
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.
1824 if ( (port_data.device_type == DEVICE_SERIAL_KISS_TNC)
1825 || (port_data.device_type == DEVICE_SERIAL_MKISS_TNC) ) {
1827 // Note: This one has callsign & destination in the string
1829 // Transmit the posit out the KISS interface
1830 send_ax25_frame(APRS_PORT_TTY,
1831 _aprs_mycall, // source
1832 VERSIONFRM, // destination
1836 else { // Not a Serial KISS TNC interface
1839 port_write_string(data_txt, APRS_PORT_TTY); // Transmit the posit
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.
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
1859 //makePrintable(temp);
1860 //packet_data_add("TX ", temp, port);
1863 } // End of posit transmit: "if (ok)"
1867 void create_output_pos_packet(TAprsPort port, gchar **packet, int *length)
1870 // gchar encodedPos[MAX_LINE_SIZE];
1872 if(_aprs_transmit_compressed_posit)
1878 //!5122.09N/00008.42W&APRS4R IGATE RUNNING ON NSLU2
1880 // For now just use a simple packet
1885 gdouble pos = (_gps.lat > 0 ? _gps.lat : _gps.lat*-1);
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) );
1891 pos = (_gps.lon > 0 ? _gps.lon : _gps.lon*-1);
1893 min = (pos - (int)pos)*60.0;
1894 sprintf(slon, "%03d%02d.%02.0f", (int)pos, (int)min,
1895 ((min - (int)min)*100.0) );
1897 *packet = g_strdup_printf(
1898 //snprintf(encodedPos, MAX_LINE_SIZE,
1899 "%c%s%c%c%s%c%c%s%c",
1902 (_gps.lat > 0 ? 'N' : 'S'),
1905 (_gps.lon > 0 ? 'E' : 'W'),
1906 _aprs_beacon_symbol,
1907 (port == APRS_PORT_INET ? _aprs_inet_beacon_comment : _aprs_beacon_comment),
1912 /* *packet = g_strdup_printf(
1919 *length = strlen(*packet);
1923 void send_packet(TAprsPort port, gchar* to_call, gchar* path, gchar* packet, gint packet_length)
1925 if(port == APRS_PORT_INET
1926 || !(port_data.device_type == DEVICE_SERIAL_KISS_TNC
1927 || port_data.device_type == DEVICE_SERIAL_MKISS_TNC) )
1929 gchar *packet_header
1936 gchar *full_packet = g_strdup_printf("%s%s\r\n", packet_header, packet);
1939 send_line(full_packet, strlen(packet_header)+packet_length+2, port);
1943 send_ax25_frame(port, _aprs_mycall, to_call, path, packet);
1948 void output_my_aprs_data(TAprsPort port) {
1955 //create_output_lat_long(my_output_lat, my_output_long);
1956 create_output_pos_packet(port, &packet, &length);
1959 send_packet(port, VERSIONFRM, _aprs_unproto_path, packet, length);
1963 /////////// TTY functionality
1967 static void thread_read_tty()
1969 set_aprs_tty_conn_state(RCVR_UP);
1976 gboolean aprs_tty_connect()
1978 printf("%s(%d)\n", __PRETTY_FUNCTION__, _aprs_tty_state);
1980 if(_aprs_tty_enable && _aprs_tty_state == RCVR_OFF)
1982 set_aprs_tty_conn_state(RCVR_DOWN);
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);
1988 _aprs_tty_thread = g_thread_create((GThreadFunc)thread_read_tty,
1989 NULL, TRUE, NULL); // Joinable.
1991 g_mutex_unlock(_aprs_tty_init_mutex);
1994 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
1999 void aprs_tty_disconnect()
2001 gboolean exit_now = FALSE;
2003 printf("%s()\n", __PRETTY_FUNCTION__);
2005 GThread *my_thread = g_thread_self();
2007 if(my_thread == _aprs_tty_thread)
2012 _aprs_tty_thread = NULL;
2015 set_aprs_tty_conn_state(RCVR_OFF);
2016 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
2018 if(exit_now) exit(0);
2023 //***********************************************************
2024 // port_write_string()
2026 // port is port# used
2027 // data is the string to write
2028 //***********************************************************
2030 void port_write_string(gchar *data, gint len, TAprsPort port) {
2032 int write_in_pos_hold;
2037 if (data[0] == '\0')
2042 if(port == APRS_PORT_TTY)
2045 if(g_mutex_trylock (_write_buffer[port].write_lock))
2047 //fprintf(stderr, "TTY Write... ");
2048 retval = (int)write(port_data.channel,
2051 //fprintf(stderr, "done... ");
2052 g_mutex_unlock (_write_buffer[port].write_lock);
2053 //fprintf(stderr, "Unlocked\n");
2056 fprintf(stderr, "Failed to get lock\n");
2060 send_line(data, len, port);
2065 gboolean send_line(gchar* text, gint text_len, TAprsPort port)
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;
2070 if(APRS_PORT_TTY == port)
2074 gboolean error = FALSE;
2076 gint write_in_pos_hold = _write_buffer[port].write_in_pos;
2079 g_mutex_lock(_write_buffer[port].write_lock);
2081 for (i = 0; i < text_len && !error; i++) {
2082 _write_buffer[port].device_write_buffer[_write_buffer[port].write_in_pos++]
2085 if (_write_buffer[port].write_in_pos >= MAX_DEVICE_BUFFER)
2086 _write_buffer[port].write_in_pos = 0;
2088 if (_write_buffer[port].write_in_pos == _write_buffer[port].write_out_pos) {
2089 fprintf(stderr,"Port %d Buffer overrun\n",port);
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++;
2098 g_mutex_unlock(_write_buffer[port].write_lock);
2104 gboolean aprs_send_beacon_inet()
2106 aprs_send_beacon(APRS_PORT_INET);
2109 gboolean aprs_send_beacon(TAprsPort port)
2113 output_my_aprs_data(port);
2115 //fprintf(stderr, "Beacon sent\n" );
2121 #endif //INCLUDE_APRS