/* * * This file is part of Maemo Mapper. * * Maemo Mapper is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Maemo Mapper is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Maemo Mapper. If not, see . * * * Parts of this code have been ported from Xastir by Rob Williams (10 Aug 2008): * * * XASTIR, Amateur Station Tracking and Information Reporting * Copyright (C) 1999,2000 Frank Giannandrea * Copyright (C) 2000-2007 The Xastir Group * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef INCLUDE_APRS #define _GNU_SOURCE #include #include #include #include "data.h" #include #include #include #include #include "aprs_display.h" #ifndef LEGACY # include # include #else # include # include #endif #include "types.h" #include "data.h" #include "defines.h" #include "display.h" #include "aprs.h" #include "gps.h" #ifdef HAVE_LIBGPSBT # include "gpsbt.h" #endif #include "path.h" #include "util.h" #include "aprs_decode.h" static volatile GThread* _aprs_inet_thread = NULL; static volatile GThread* _aprs_tty_thread = NULL; static GMutex* _aprs_inet_init_mutex = NULL; static GMutex* _aprs_tty_init_mutex = NULL; static gint _aprs_rcvr_retry_count = 0; static guint _aprs_inet_beacon_timer; static guint _aprs_tty_beacon_timer; #define VERSIONFRM "APRS" extern AprsDataRow* n_first; // pointer to first element in name sorted station list TWriteBuffer _write_buffer[APRS_PORT_COUNT]; gboolean send_line(gchar* text, gint text_len, TAprsPort port); void port_read() ; void aprs_tty_disconnect(); static gboolean aprs_handle_error_idle(gchar *error) { printf("%s(%s)\n", __PRETTY_FUNCTION__, error); /* Ask for re-try. */ if(++_aprs_rcvr_retry_count > 2) { GtkWidget *confirm; gchar buffer[BUFFER_SIZE]; /* Reset retry count. */ _aprs_rcvr_retry_count = 0; snprintf(buffer, sizeof(buffer), "%s\nRetry?", error); confirm = hildon_note_new_confirmation(GTK_WINDOW(_window), buffer); aprs_server_disconnect(); if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm))) { aprs_server_connect(); /* Try again. */ } else { /* Reset Connect to APRS menu item. */ gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_enable_aprs_inet_item), FALSE); } /* Ask user to re-connect. */ gtk_widget_destroy(confirm); } else { aprs_server_disconnect(); aprs_server_connect(); /* Try again. */ } g_free(error); vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } /** * Set the connection state. This function controls all connection-related * banners. */ void set_aprs_tty_conn_state(ConnState new_conn_state) { printf("%s(%d)\n", __PRETTY_FUNCTION__, new_conn_state); switch(_aprs_tty_state = new_conn_state) { case RCVR_OFF: case RCVR_FIXED: case RCVR_UP: if(_connect_banner) { gtk_widget_destroy(_connect_banner); _connect_banner = NULL; } if(_aprs_tty_beacon_timer>0) g_source_remove(_aprs_tty_beacon_timer); if(_aprs_enable && _aprs_tty_enable && _aprs_enable_tty_tx && _aprs_tty_beacon_interval>0) _aprs_tty_beacon_timer = g_timeout_add(_aprs_tty_beacon_interval*1000 , timer_callback_aprs_tty, NULL); _aprs_rcvr_retry_count = 0; break; case RCVR_DOWN: if(!_connect_banner) _connect_banner = hildon_banner_show_animation( _window, NULL, _("Attempting to connect to TNC")); if(_aprs_tty_beacon_timer>0) g_source_remove(_aprs_tty_beacon_timer); break; default: ; /* to quell warning. */ } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Set the connection state. This function controls all connection-related * banners. */ void set_aprs_inet_conn_state(ConnState new_conn_state) { printf("%s(%d)\n", __PRETTY_FUNCTION__, new_conn_state); switch(_aprs_inet_state = new_conn_state) { case RCVR_OFF: case RCVR_FIXED: case RCVR_UP: if(_connect_banner) { gtk_widget_destroy(_connect_banner); _connect_banner = NULL; } if(_fix_banner) { gtk_widget_destroy(_fix_banner); _fix_banner = NULL; } if(_aprs_inet_beacon_timer>0) g_source_remove(_aprs_inet_beacon_timer); if(_aprs_enable && _aprs_inet_enable && _aprs_enable_inet_tx && _aprs_inet_beacon_interval>0) _aprs_inet_beacon_timer = g_timeout_add(_aprs_inet_beacon_interval*1000 , timer_callback_aprs_inet, NULL); break; case RCVR_DOWN: if(_fix_banner) { gtk_widget_destroy(_fix_banner); _fix_banner = NULL; } if(!_connect_banner) _connect_banner = hildon_banner_show_animation( _window, NULL, _("Attempting to connect to APRS server")); if(_aprs_inet_beacon_timer>0) g_source_remove(_aprs_inet_beacon_timer); break; default: ; /* to quell warning. */ } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static gboolean aprs_parse_server_packet(gchar *packet) { decode_ax25_line(packet, APRS_PORT_INET); g_free(packet); return FALSE; } void update_aprs_inet_options(gboolean force) { // If auto filter is not or we are not connected then stop if(!_aprs_server_auto_filter_on || !_aprs_enable || !_aprs_inet_enable || _aprs_server_auto_filter_km <= 0) return ; // Disconnect aprs_server_disconnect(); //Re-connect aprs_server_connect(); } gchar *create_aprs_inet_options_string() { gint current_lat = (gint)round(_gps.lat); gint current_lon = (gint)round(_gps.lon); gchar *filter = NULL; filter = g_strdup_printf("user %s pass %s vers %s v%s filter r/%d/%d/%d \r\n ", _aprs_mycall, _aprs_inet_server_validation, PACKAGE, VERSION, current_lat, current_lon, _aprs_server_auto_filter_km ); return filter; } static void thread_read_server() { gchar buf[APRS_BUFFER_SIZE]; gchar *buf_curr = buf; gchar *buf_last = buf + sizeof(buf) - 1; GnomeVFSFileSize bytes_read; GnomeVFSResult vfs_result; GnomeVFSInetConnection *iconn = NULL; GnomeVFSSocket *socket = NULL; GThread *my_thread = g_thread_self(); gboolean error = FALSE; printf("%s(%s)\n", __PRETTY_FUNCTION__, _aprs_server); //fprintf(stderr, "Starting thread...\n"); /* Lock/Unlock the mutex to ensure that _aprs_inet_thread is done being set. */ g_mutex_lock(_aprs_inet_init_mutex); g_mutex_unlock(_aprs_inet_init_mutex); if(!error && my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL) { gint tryno; /* Attempt to connect to APRS server. */ for(tryno = 0; tryno < 10; tryno++) { /* Create a socket to interact with server. */ GTimeVal timeout = { 1000, 0 }; gchar *filter = create_aprs_inet_options_string(); //fprintf(stderr, filter); if(GNOME_VFS_OK != (vfs_result = gnome_vfs_inet_connection_create( &iconn, _aprs_server, _aprs_server_port, NULL)) || NULL == ( socket = gnome_vfs_inet_connection_to_socket(iconn)) || GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_set_timeout( socket, &timeout, NULL)) || GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_write( socket, filter, strlen(filter), &bytes_read, NULL)) ) { g_free(filter); sleep(1); } else { g_free(filter); break; } } if(!iconn) { g_printerr("Error connecting to APRS server: (%d) %s\n", vfs_result, gnome_vfs_result_to_string(vfs_result)); g_idle_add((GSourceFunc)aprs_handle_error_idle, g_strdup_printf("%s", _("Error connecting to APRS server."))); error = TRUE; } } if(!error && my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL) { set_aprs_inet_conn_state(RCVR_UP); while(my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL) { gchar *eol; vfs_result = gnome_vfs_socket_read( socket, buf, buf_last - buf_curr, &bytes_read, NULL); if(vfs_result != GNOME_VFS_OK) { if(my_thread == _aprs_inet_thread) { // Error wasn't user-initiated. g_idle_add((GSourceFunc)aprs_handle_error_idle, g_strdup_printf("%s %u", _("Error reading APRS data."), vfs_result)); } fprintf(stderr, "Read error: %s\n", gnome_vfs_result_to_string(vfs_result)); error = TRUE; break; } /* Loop through the buffer and read each packet. */ buf_curr += bytes_read; *buf_curr = '\0'; /* append a \0 so we can read as string */ while(!error && my_thread == _aprs_inet_thread && _aprs_inet_thread != NULL && (eol = strchr(buf, '\n'))) { /* This is the beginning of a sentence; okay to parse. */ *eol = '\0'; /* overwrite \n with \0 */ if(my_thread == _aprs_inet_thread) //g_idle_add_full(G_PRIORITY_HIGH, (GSourceFunc)aprs_parse_server_packet, g_strdup(buf), NULL ); g_idle_add((GSourceFunc)aprs_parse_server_packet, g_strdup(buf)); /* If eol is at or after (buf_curr - 1) */ if(eol >= (buf_curr - 1)) { /* Last read was a newline - reset read buffer */ buf_curr = buf; *buf_curr = '\0'; } else { /* Move the next line to the front of the buffer. */ memmove(buf, eol + 1, buf_curr - eol); /* include terminating 0 */ /* Subtract _curr so that it's pointing at the new \0. */ buf_curr -= (eol - buf + 1); } } _aprs_rcvr_retry_count = 0; // Send any packets queued // try to get lock, otherwise try next time if(g_mutex_trylock (_write_buffer[APRS_PORT_INET].write_lock)) { // Store the current end pointer as it may change gint quantity = 0; gchar tmp_write_buffer[MAX_DEVICE_BUFFER]; while (_write_buffer[APRS_PORT_INET].write_in_pos != _write_buffer[APRS_PORT_INET].write_out_pos) { tmp_write_buffer[quantity] = _write_buffer[APRS_PORT_INET].device_write_buffer[_write_buffer[APRS_PORT_INET].write_out_pos]; _write_buffer[APRS_PORT_INET].write_out_pos++; if (_write_buffer[APRS_PORT_INET].write_out_pos >= MAX_DEVICE_BUFFER) _write_buffer[APRS_PORT_INET].write_out_pos = 0; quantity++; } if(quantity>0) { sleep(2); GnomeVFSFileSize bytes_read = 0; if(GNOME_VFS_OK == gnome_vfs_socket_write( socket, tmp_write_buffer, quantity, &bytes_read, NULL)) { // OK //fprintf(stderr, "Send packet success: %s (%u)\n", tmp_write_buffer, quantity); } else { // Failed fprintf(stderr, "Failed to send packet: %s (%u)\n", tmp_write_buffer, quantity); } sleep(1); } g_mutex_unlock(_write_buffer[APRS_PORT_INET].write_lock); } } //fprintf(stderr, "Exiting thread...\n"); } /* Error, or we're done reading APRS data. */ /* Clean up. */ if(iconn) gnome_vfs_inet_connection_destroy(iconn, NULL); //gnome_vfs_inet_connection_free(iconn, NULL); iconn = NULL; //g_thread_exit(0); printf("%s(): return\n", __PRETTY_FUNCTION__); return; } /** * Disconnect from the receiver. This method cleans up any and everything * that might be associated with the receiver. */ void aprs_server_disconnect() { gboolean exit_now = FALSE; printf("%s()\n", __PRETTY_FUNCTION__); GThread *my_thread = g_thread_self(); if(my_thread == _aprs_inet_thread) { exit_now = TRUE; } g_mutex_lock(_aprs_inet_init_mutex); _aprs_inet_thread = NULL; g_mutex_unlock(_aprs_inet_init_mutex); if(_window) set_aprs_inet_conn_state(RCVR_OFF); vprintf("%s(): return\n", __PRETTY_FUNCTION__); if(exit_now) { fprintf(stderr, "Stopping own thread - APRS server\n"); exit(0); fprintf(stderr, "Stop Failed\n"); } } /** * Connect to the server. * This method assumes that _fd is -1 and _channel is NULL. If unsure, call * rcvr_disconnect() first. * Since this is an idle function, this function returns whether or not it * should be called again, which is always FALSE. */ gboolean aprs_server_connect() { printf("%s(%d)\n", __PRETTY_FUNCTION__, _aprs_inet_state); if(_aprs_inet_enable && _aprs_inet_state == RCVR_OFF) { set_aprs_inet_conn_state(RCVR_DOWN); /* Lock/Unlock the mutex to ensure that the thread doesn't * start until _gps_thread is set. */ g_mutex_lock(_aprs_inet_init_mutex); _aprs_inet_thread = g_thread_create((GThreadFunc)thread_read_server, NULL, TRUE, NULL); /* Joinable. */ // g_thread_set_priority(_aprs_inet_thread, G_THREAD_PRIORITY_LOW); g_mutex_unlock(_aprs_inet_init_mutex); } vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } void aprs_init() { printf("%s()\n", __PRETTY_FUNCTION__); _aprs_inet_init_mutex = g_mutex_new(); _aprs_tty_init_mutex = g_mutex_new(); _write_buffer[APRS_PORT_INET].write_lock = g_mutex_new(); _write_buffer[APRS_PORT_TTY].write_lock = g_mutex_new(); vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); } void aprs_destroy(gboolean last) { static GThread* tmp = NULL; printf("%s()\n", __PRETTY_FUNCTION__); if(!last) { if(_aprs_inet_thread) { tmp = (GThread*)_aprs_inet_thread; _aprs_inet_thread = NULL; } } else if(tmp) g_thread_join(tmp); vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); } gboolean select_aprs(gint unitx, gint unity, gboolean quick) { gint x, y; gdouble lat1, lon1, lat2, lon2; static GtkWidget *dialog = NULL; static GtkWidget *list = NULL; static GtkWidget *sw = NULL; static GtkTreeViewColumn *column = NULL; static GtkCellRenderer *renderer = NULL; GtkListStore *store = NULL; GtkTreeIter iter; gboolean selected = FALSE; gint num_stations = 0; AprsStationList *first_station = NULL; AprsStationList *last_station = NULL; printf("%s()\n", __PRETTY_FUNCTION__); x = unitx - pixel2unit(3 * _draw_width); y = unity + pixel2unit(3 * _draw_width); unit2latlon(x, y, lat1, lon1); x = unitx + pixel2unit(3 * _draw_width); y = unity - pixel2unit(3 * _draw_width); unit2latlon(x, y, lat2, lon2); gdouble lat, lon; AprsDataRow *p_station = (AprsDataRow *)n_first; // Look for all stations in selected area while ( (p_station) != NULL) { lat = convert_lat_l2d(p_station->coord_lat); lon = convert_lon_l2d(p_station->coord_lon); if ( ( lat2 >= lat && lat >= lat1 ) && (lon2 >= lon && lon >= lon1) ) { // This may have been clicked on AprsStationList * p_list_item = (AprsStationList *)malloc(sizeof(AprsStationList)); p_list_item->station = p_station; p_list_item->next = NULL; if(first_station == NULL) { first_station = p_list_item; last_station = p_list_item; } else { last_station->next = p_list_item; last_station = p_list_item; } num_stations++; } (p_station) = (p_station)->n_next; // Next element in list } // End of while loop selected = FALSE; if(num_stations==0) { // No station found, maybe a POI was selected? } else if(num_stations == 1) { // Only one station was found, so display it's info if(first_station->station != NULL) { ShowAprsStationPopup(first_station->station); } selected = TRUE; } else { // Multiple possibilities, therefore ask the user which one // Initialize store. store = gtk_list_store_new(APRSPOI_NUM_COLUMNS, G_TYPE_BOOLEAN, //Selected G_TYPE_STRING // Callsign ); AprsStationList * p_list_item = first_station; while(p_list_item != NULL) { if(p_list_item->station != NULL) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, APRSPOI_CALLSIGN, g_strdup(p_list_item->station->call_sign), -1); } p_list_item = p_list_item->next; } if(dialog == NULL) { dialog = gtk_dialog_new_with_buttons(_("Select APRS Station"), GTK_WINDOW(_window), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 300); sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), sw, TRUE, TRUE, 0); list = gtk_tree_view_new(); gtk_container_add(GTK_CONTAINER(sw), list); gtk_tree_selection_set_mode( gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), GTK_SELECTION_SINGLE); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), TRUE); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("Callsign"), renderer, "text", APRSPOI_CALLSIGN, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(list), column); } gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store)); g_object_unref(G_OBJECT(store)); gtk_widget_show_all(dialog); if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) { if(gtk_tree_selection_get_selected( gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), NULL, &iter)) { // Find the callsign p_list_item = first_station; while(p_list_item != NULL) { if(p_list_item->station != NULL) { gchar * callsign = NULL; gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, APRSPOI_CALLSIGN, &(callsign), -1); if(strcmp(p_list_item->station->call_sign,callsign) == 0) { gtk_widget_hide(dialog); ShowAprsStationPopup(p_list_item->station); selected = TRUE; break; } } p_list_item = p_list_item->next; } } } // Ensure it has been closed gtk_widget_hide(dialog); } // Free the list, but not the stations if(first_station) { AprsStationList * p_list_item = first_station; while(first_station) { // Store pointer to delete contents after next pointer is stored p_list_item = first_station; // Move pointer to next first_station = p_list_item->next; free(p_list_item); } } vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, selected); return selected; } //***************************************************************** // distance_from_my_station - compute distance from my station and // course with a given call // // return distance and course // // Returns 0.0 for distance if station not found in database or the // station hasn't sent out a posit yet. //***************************************************************** double distance_from_my_station(char *call_sign, gchar *course_deg, gint course_len) { AprsDataRow *p_station; double distance; float value; double d_lat, d_lon; distance = 0.0; p_station = NULL; if (search_station_name(&p_station,call_sign,1)) { // Check whether we have a posit yet for this station if ( (p_station->coord_lat == 0l) && (p_station->coord_lon == 0l) ) { distance = 0.0; } else { d_lat = convert_lat_l2d(p_station->coord_lat); d_lon = convert_lon_l2d(p_station->coord_lon); value = (float)calculate_distance(_gps.lat, _gps.lon, d_lat, d_lon); snprintf(course_deg, course_len, "%.01f°", calculate_bearing(_gps.lat, _gps.lon, d_lat, d_lon)); if(_units == UNITS_KM) distance = value * 1.852; // nautical miles to km else if(_units == UNITS_MI) distance = value * 1.15078; // nautical miles to miles else if(_units == UNITS_NM) distance = value; else distance = 0.0; // Should be unreachable } } else { // Station not found distance = 0.0; } return(distance); } void pad_callsign(char *callsignout, char *callsignin) { int i,l; l=(int)strlen(callsignin); for(i=0; i<9;i++) { if(i= 0 && ssid <= 15) { new_call[6] = ssid | 0x30; // Set 2 reserved bits } else { // Whacko SSID. Set it to zero new_call[6] = 0x30; // Set 2 reserved bits } if (digipeated_flag) { new_call[6] = new_call[6] | 0x40; // Set the 'H' bit } // Shift each byte one bit to the left for (i = 0; i < 7; i++) { new_call[i] = new_call[i] << 1; new_call[i] = new_call[i] & 0xfe; } //fprintf(stderr,"Last:%0x\n",new_call[6]); // Write over the top of the input string with the newly // formatted callsign xastir_snprintf((char *)data, data_size, "%s", new_call); } // Create an AX25 frame and then turn it into a KISS packet. Dump // it into the transmit queue. // void send_ax25_frame(TAprsPort port, gchar *source, gchar *destination, gchar *path, gchar *data) { unsigned char temp_source[15]; unsigned char temp_dest[15]; unsigned char temp[15]; unsigned char control[2], pid[2]; unsigned char transmit_txt[MAX_LINE_SIZE*2]; unsigned char transmit_txt2[MAX_LINE_SIZE*2]; unsigned char c; int i, j; int erd; int write_in_pos_hold; //fprintf(stderr,"KISS String:%s>%s,%s:%s\n",source,destination,path,data); // Check whether transmits are disabled globally // if (transmit_disable) { // return; // } // Check whether transmit has been enabled for this interface. // If not, get out while the gettin's good. // if (devices[port].transmit_data != 1) { // return; // } transmit_txt[0] = '\0'; // Format the destination callsign snprintf((char *)temp_dest, sizeof(temp_dest), "%s", destination); fix_up_callsign(temp_dest, sizeof(temp_dest)); snprintf((char *)transmit_txt, sizeof(transmit_txt), "%s", temp_dest); // Format the source callsign snprintf((char *)temp_source, sizeof(temp_source), "%s", source); fix_up_callsign(temp_source, sizeof(temp_source)); strncat((char *)transmit_txt, (char *)temp_source, sizeof(transmit_txt) - strlen((char *)transmit_txt)); // Break up the path into individual callsigns and send them one // by one to fix_up_callsign(). If we get passed an empty path, // we merely skip this section and no path gets added to // "transmit_txt". j = 0; temp[0] = '\0'; // Start with empty path if ( (path != NULL) && (strlen(path) != 0) ) { while (path[j] != '\0') { i = 0; while ( (path[j] != ',') && (path[j] != '\0') ) { temp[i++] = path[j++]; } temp[i] = '\0'; if (path[j] == ',') { // Skip over comma j++; } fix_up_callsign(temp, sizeof(temp)); strncat((char *)transmit_txt, (char *)temp, sizeof(transmit_txt) - strlen((char *)transmit_txt)); } } // Set the end-of-address bit on the last callsign in the // address field transmit_txt[strlen((const char *)transmit_txt) - 1] |= 0x01; // Add the Control byte control[0] = 0x03; control[1] = '\0'; strncat((char *)transmit_txt, (char *)control, sizeof(transmit_txt) - strlen((char *)transmit_txt)); // Add the PID byte pid[0] = 0xf0; pid[1] = '\0'; strncat((char *)transmit_txt, (char *)pid, sizeof(transmit_txt) - strlen((char *)transmit_txt)); // Append the information chars strncat((char *)transmit_txt, data, sizeof(transmit_txt) - strlen((char *)transmit_txt)); //fprintf(stderr,"%s\n",transmit_txt); // Add the KISS framing characters and do the proper escapes. j = 0; transmit_txt2[j++] = KISS_FEND; // Note: This byte is where different interfaces would be // specified: transmit_txt2[j++] = 0x00; for (i = 0; i < (int)strlen((const char *)transmit_txt); i++) { c = transmit_txt[i]; if (c == KISS_FEND) { transmit_txt2[j++] = KISS_FESC; transmit_txt2[j++] = KISS_TFEND; } else if (c == KISS_FESC) { transmit_txt2[j++] = KISS_FESC; transmit_txt2[j++] = KISS_TFESC; } else { transmit_txt2[j++] = c; } } transmit_txt2[j++] = KISS_FEND; // Terminate the string, but don't increment the 'j' counter. // We don't want to send the NULL byte out the KISS interface, // just make sure the string is terminated in all cases. // transmit_txt2[j] = '\0'; //------------------------------------------------------------------- // Had to snag code from port_write_string() below because our string // needs to have 0x00 chars inside it. port_write_string() can't // handle that case. It's a good thing the transmit queue stuff // could handle it. //------------------------------------------------------------------- erd = 0; port_write_string( transmit_txt2, j/*length*/, APRS_PORT_TTY); /* g_mutex_lock (_write_buffer[port].write_lock); { write_in_pos_hold = _write_buffer[port].write_in_pos; for (i = 0; i < j && !erd; i++) { _write_buffer[port].device_write_buffer[_write_buffer[port].write_in_pos++] = transmit_txt2[i]; if (_write_buffer[port].write_in_pos >= MAX_DEVICE_BUFFER) _write_buffer[port].write_in_pos = 0; if (_write_buffer[port].write_in_pos == _write_buffer[port].write_out_pos) { // clear this restore original write_in pos and dump this string _write_buffer[port].write_in_pos = write_in_pos_hold; _write_buffer[port].errors++; erd = 1; } } g_mutex_unlock (_write_buffer[port].write_lock); } */ } // convert latitude from long to string // Input is in Xastir coordinate system // // CONVERT_LP_NOSP = DDMM.MMN // CONVERT_HP_NOSP = DDMM.MMMN // CONVERT_VHP_NOSP = DDMM.MMMMN // CONVERT_LP_NORMAL = DD MM.MMN // CONVERT_HP_NORMAL = DD MM.MMMN // CONVERT_UP_TRK = NDD MM.MMMM // CONVERT_DEC_DEG = DD.DDDDDN // CONVERT_DMS_NORMAL = DD MM SS.SN // CONVERT_DMS_NORMAL_FORMATED = DD'MM'SS.SN // CONVERT_HP_NORMAL_FORMATED = DD'MM.MMMMN // void convert_lat_l2s(long lat, char *str, int str_len, int type) { char ns; float deg, min, sec; int ideg, imin; long temp; str[0] = '\0'; deg = (float)(lat - 32400000l) / 360000.0; // Switch to integer arithmetic to avoid floating-point // rounding errors. temp = (long)(deg * 100000); ns = 'S'; if (temp <= 0) { ns = 'N'; temp = labs(temp); } ideg = (int)temp / 100000; min = (temp % 100000) * 60.0 / 100000.0; // Again switch to integer arithmetic to avoid floating-point // rounding errors. temp = (long)(min * 1000); imin = (int)(temp / 1000); sec = (temp % 1000) * 60.0 / 1000.0; switch (type) { case(CONVERT_LP_NOSP): /* do low P w/no space */ xastir_snprintf(str, str_len, "%02d%05.2f%c", ideg, // min+0.001, // Correct possible unbiased rounding min, ns); break; case(CONVERT_LP_NORMAL): /* do low P normal */ xastir_snprintf(str, str_len, "%02d %05.2f%c", ideg, // min+0.001, // Correct possible unbiased rounding min, ns); break; case(CONVERT_HP_NOSP): /* do HP w/no space */ xastir_snprintf(str, str_len, "%02d%06.3f%c", ideg, // min+0.0001, // Correct possible unbiased rounding min, ns); break; case(CONVERT_VHP_NOSP): /* do Very HP w/no space */ xastir_snprintf(str, str_len, "%02d%07.4f%c", ideg, // min+0.00001, // Correct possible unbiased rounding min, ns); break; case(CONVERT_UP_TRK): /* for tracklog files */ xastir_snprintf(str, str_len, "%c%02d %07.4f", ns, ideg, // min+0.00001); // Correct possible unbiased rounding min); break; case(CONVERT_DEC_DEG): xastir_snprintf(str, str_len, "%08.5f%c", // (ideg+min/60.0)+0.000001, // Correct possible unbiased rounding ideg+min/60.0, ns); break; case(CONVERT_DMS_NORMAL): xastir_snprintf(str, str_len, "%02d %02d %04.1f%c", ideg, imin, // sec+0.01, // Correct possible unbiased rounding sec, ns); break; case(CONVERT_DMS_NORMAL_FORMATED): xastir_snprintf(str, str_len, "%02d°%02d\'%04.1f%c", ideg, imin, // sec+0.01, // Correct possible unbiased rounding sec, ns); break; case(CONVERT_HP_NORMAL_FORMATED): xastir_snprintf(str, str_len, "%02d°%06.3f%c", ideg, // min+0.0001, // Correct possible unbiased roundin min, ns); break; case(CONVERT_HP_NORMAL): default: /* do HP normal */ xastir_snprintf(str, str_len, "%02d %06.3f%c", ideg, // min+0.0001, // Correct possible unbiased rounding min, ns); break; } } // convert longitude from long to string // Input is in Xastir coordinate system // // CONVERT_LP_NOSP = DDDMM.MME // CONVERT_HP_NOSP = DDDMM.MMME // CONVERT_VHP_NOSP = DDDMM.MMMME // CONVERT_LP_NORMAL = DDD MM.MME // CONVERT_HP_NORMAL = DDD MM.MMME // CONVERT_UP_TRK = EDDD MM.MMMM // CONVERT_DEC_DEG = DDD.DDDDDE // CONVERT_DMS_NORMAL = DDD MM SS.SN // CONVERT_DMS_NORMAL_FORMATED = DDD'MM'SS.SN // void convert_lon_l2s(long lon, char *str, int str_len, int type) { char ew; float deg, min, sec; int ideg, imin; long temp; str[0] = '\0'; deg = (float)(lon - 64800000l) / 360000.0; // Switch to integer arithmetic to avoid floating-point rounding // errors. temp = (long)(deg * 100000); ew = 'E'; if (temp <= 0) { ew = 'W'; temp = labs(temp); } ideg = (int)temp / 100000; min = (temp % 100000) * 60.0 / 100000.0; // Again switch to integer arithmetic to avoid floating-point // rounding errors. temp = (long)(min * 1000); imin = (int)(temp / 1000); sec = (temp % 1000) * 60.0 / 1000.0; switch(type) { case(CONVERT_LP_NOSP): /* do low P w/nospacel */ xastir_snprintf(str, str_len, "%03d%05.2f%c", ideg, // min+0.001, // Correct possible unbiased rounding min, ew); break; case(CONVERT_LP_NORMAL): /* do low P normal */ xastir_snprintf(str, str_len, "%03d %05.2f%c", ideg, // min+0.001, // Correct possible unbiased rounding min, ew); break; case(CONVERT_HP_NOSP): /* do HP w/nospace */ xastir_snprintf(str, str_len, "%03d%06.3f%c", ideg, // min+0.0001, // Correct possible unbiased rounding min, ew); break; case(CONVERT_VHP_NOSP): /* do Very HP w/nospace */ xastir_snprintf(str, str_len, "%03d%07.4f%c", ideg, // min+0.00001, // Correct possible unbiased rounding min, ew); break; case(CONVERT_UP_TRK): /* for tracklog files */ xastir_snprintf(str, str_len, "%c%03d %07.4f", ew, ideg, // min+0.00001); // Correct possible unbiased rounding min); break; case(CONVERT_DEC_DEG): xastir_snprintf(str, str_len, "%09.5f%c", // (ideg+min/60.0)+0.000001, // Correct possible unbiased rounding ideg+min/60.0, ew); break; case(CONVERT_DMS_NORMAL): xastir_snprintf(str, str_len, "%03d %02d %04.1f%c", ideg, imin, // sec+0.01, // Correct possible unbiased rounding sec, ew); break; case(CONVERT_DMS_NORMAL_FORMATED): xastir_snprintf(str, str_len, "%03d°%02d\'%04.1f%c", ideg, imin, // sec+0.01, // Correct possible unbiased rounding sec, ew); break; case(CONVERT_HP_NORMAL_FORMATED): xastir_snprintf(str, str_len, "%03d°%06.3f%c", ideg, // min+0.0001, // Correct possible unbiased rounding min, ew); break; case(CONVERT_HP_NORMAL): default: /* do HP normal */ xastir_snprintf(str, str_len, "%03d %06.3f%c", ideg, // min+0.0001, // Correct possible unbiased rounding min, ew); break; } } /*************************************************************************/ /* output_lat - format position with position_amb_chars for transmission */ /*************************************************************************/ /* char *output_lat(char *in_lat, int comp_pos) { int i,j; int position_amb_chars = 0; //fprintf(stderr,"in_lat:%s\n", in_lat); if (!comp_pos) { // Don't do this as it results in truncation! //in_lat[7]=in_lat[8]; // Shift N/S down for transmission } else if (position_amb_chars>0) { in_lat[7]='0'; } j=0; if (position_amb_chars>0 && position_amb_chars<5) { for (i=6;i>(6-position_amb_chars-j);i--) { if (i==4) { i--; j=1; } if (!comp_pos) { in_lat[i]=' '; } else in_lat[i]='0'; } } if (!comp_pos) { in_lat[8] = '\0'; } return(in_lat); } */ /**************************************************************************/ /* output_long - format position with position_amb_chars for transmission */ /**************************************************************************/ /* char *output_long(char *in_long, int comp_pos) { int i,j; int position_amb_chars = 0; //fprintf(stderr,"in_long:%s\n", in_long); if (!comp_pos) { // Don't do this as it results in truncation! //in_long[8]=in_long[9]; // Shift e/w down for transmission } else if (position_amb_chars>0) { in_long[8]='0'; } j=0; if (position_amb_chars>0 && position_amb_chars<5) { for (i=7;i>(7-position_amb_chars-j);i--) { if (i==5) { i--; j=1; } if (!comp_pos) { in_long[i]=' '; } else in_long[i]='0'; } } if (!comp_pos) in_long[9] = '\0'; return(in_long); } */ //*********************************************************** // output_my_aprs_data // This is the function responsible for sending out my own // posits. The next function below this one handles objects, // messages and the like (output_my_data). //***********************************************************/ /* void create_output_lat_long(gchar *my_output_lat, gchar *my_output_long ) { _aprs_transmit_compressed_posit = FALSE; gchar *my_lat = g_strdup_printf("%lf", _gps.lat); gchar *my_long = g_strdup_printf("%lf", _gps.lon); // Format latitude string for transmit later if (_aprs_transmit_compressed_posit) { // High res version // TODO - enable compressed beacon snprintf(my_output_lat, sizeof(my_output_lat), "%s", my_lat); } else { // Create a low-res version of the latitude string long my_temp_lat; char temp_data[20]; // Convert to long my_temp_lat = convert_lat_s2l(my_lat); // Convert to low-res string convert_lat_l2s(my_temp_lat, temp_data, sizeof(temp_data), CONVERT_LP_NORMAL); snprintf(my_output_lat, sizeof(my_output_lat), "%c%c%c%c.%c%c%c", temp_data[0], temp_data[1], temp_data[3], temp_data[4], temp_data[6], temp_data[7], temp_data[8]); } (void)output_lat(my_output_lat, _aprs_transmit_compressed_posit); // Format longitude string for transmit later if (_aprs_transmit_compressed_posit) { // High res version snprintf(my_output_long, sizeof(my_output_long), "%s", my_long); } else { // Create a low-res version of the longitude string long my_temp_long; char temp_data[20]; // Convert to long my_temp_long = convert_lon_s2l(my_long); // Convert to low-res string convert_lon_l2s(my_temp_long, temp_data, sizeof(temp_data), CONVERT_LP_NORMAL); snprintf(my_output_long, sizeof(my_output_long), "%c%c%c%c%c.%c%c%c", temp_data[0], temp_data[1], temp_data[2], temp_data[4], temp_data[5], temp_data[7], temp_data[8], temp_data[9]); } (void)output_long(my_output_long, _aprs_transmit_compressed_posit); } void output_my_aprs_data_tty() { //TODO return ; gchar my_output_lat[MAX_LAT]; gchar my_output_long[MAX_LONG]; // gchar header_txt[MAX_LINE_SIZE+5]; // gchar header_txt_save[MAX_LINE_SIZE+5]; gchar path_txt[MAX_LINE_SIZE+5]; gchar data_txt[MAX_LINE_SIZE+5]; gchar temp[MAX_LINE_SIZE+5]; gchar *unproto_path = ""; gchar data_txt2[5]; struct tm *day_time; gchar my_pos[256]; gchar output_net[256]; gchar output_phg[10]; gchar output_cs[10]; gchar output_alt[20]; gchar output_brk[3]; int ok; gchar my_comment_tx[APRS_MAX_COMMENT+1]; int interfaces_ok_for_transmit = 0; gchar my_phg[10]; time_t sec; gchar header_txt[MAX_LINE_SIZE+5]; gchar header_txt_save[MAX_LINE_SIZE+5]; gchar data_txt_save[MAX_LINE_SIZE+5]; if (!(port_data.status == DEVICE_UP && _aprs_tty_enable && _aprs_enable && _aprs_enable_tty_tx)) return ; header_txt_save[0] = '\0'; data_txt_save[0] = '\0'; sec = sec_now(); my_phg[0] = '\0'; create_output_lat_long(my_output_lat, my_output_long); // First send any header/path info we might need out the port, // set up TNC's to the proper mode, etc. ok = 1; // clear this for a TNC output_net[0] = '\0'; // Set my call sign // The leading \r is sent to normal serial TNCs. The only // reason for it is that some folks' D700s are getting // garbage in the input buffer, and the result is the mycall // line is rejected. The \r at the beginning clears out the // junk and lets it go through. But data_out_ax25 tries to // parse the MYCALL line, and the presence of a leading \r // breaks it. snprintf(header_txt, sizeof(header_txt), "%c%s %s\r", '\3', ((port_data.device_type !=DEVICE_AX25_TNC)? "\rMYCALL":"MYCALL"), _aprs_mycall); // Send the callsign out to the TNC only if the interface is up and tx is enabled??? // We don't set it this way for KISS TNC interfaces. if ( (port_data.device_type != DEVICE_SERIAL_KISS_TNC) && (port_data.device_type != DEVICE_SERIAL_MKISS_TNC) && (port_data.status == DEVICE_UP) //&& (devices.transmit_data == 1) //&& !transmit_disable //&& !posit_tx_disable ) { port_write_string(header_txt, APRS_PORT_TTY); //send_line(gchar* text, gint text_len, TAprsPort port) } // Set unproto path: Get next unproto path in // sequence. snprintf(header_txt, sizeof(header_txt), "%c%s %s VIA %s\r", '\3', "UNPROTO", VERSIONFRM, _aprs_unproto_path); snprintf(header_txt_save, sizeof(header_txt_save), "%s>%s,%s:", _aprs_mycall, VERSIONFRM, _aprs_unproto_path); snprintf(path_txt, sizeof(path_txt), "%s", _aprs_unproto_path); // Send the header data to the TNC. This sets the // unproto path that'll be used by the next packet. // We don't set it this way for KISS TNC interfaces. if ( (port_data.device_type != DEVICE_SERIAL_KISS_TNC) && (port_data.device_type != DEVICE_SERIAL_MKISS_TNC) && (port_data.status == DEVICE_UP) //&& (devices.transmit_data == 1) //&& !transmit_disable //&& !posit_tx_disable ) { port_write_string(header_txt, APRS_PORT_TTY); } // Set converse mode. We don't need to do this for // KISS TNC interfaces. One european TNC (tnc2-ui) // doesn't accept "conv" but does accept the 'k' // command. A Kantronics KPC-2 v2.71 TNC accepts // the "conv" command but not the 'k' command. // Figures! // snprintf(header_txt, sizeof(header_txt), "%c%s\r", '\3', APRS_CONVERSE_MODE); if ( (port_data.device_type != DEVICE_SERIAL_KISS_TNC) && (port_data.device_type != DEVICE_SERIAL_MKISS_TNC) && (port_data.status == DEVICE_UP) //&& (devices.transmit_data == 1) //&& !transmit_disable //&& !posit_tx_disable ) { port_write_string(header_txt, APRS_PORT_TTY); } // sleep(1); // Set up some more strings for later transmission // send station info output_cs[0] = '\0'; output_phg[0] = '\0'; output_alt[0] = '\0'; output_brk[0] = '\0'; if (_aprs_transmit_compressed_posit) { // TOOD - enable compressed beacon support // snprintf(my_pos, // sizeof(my_pos), // "%s", // compress_posit(my_output_lat, // my_group, // my_output_long, // my_symbol, // my_last_course, // my_last_speed, // In knots // my_phg)); } else { // standard non compressed mode snprintf(my_pos, sizeof(my_pos), "%s%c%s%c", my_output_lat, _aprs_beacon_group, my_output_long, _aprs_beacon_symbol); // get PHG, if used for output if (strlen(my_phg) >= 6) snprintf(output_phg, sizeof(output_phg), "%s", my_phg); // get CSE/SPD, Always needed for output even if 0 snprintf(output_cs, sizeof(output_cs), "%03d/%03d/", _gps.heading, _gps.speed); // Speed in knots // get altitude // TODO // if (my_last_altitude_time > 0) // snprintf(output_alt, // sizeof(output_alt), // "A=%06ld/", // my_last_altitude); } // APRS_MOBILE LOCAL TIME // TODO // if((strlen(output_cs) < 8) && (my_last_altitude_time > 0)) { // xastir_snprintf(output_brk, // sizeof(output_brk), // "/"); // } day_time = localtime(&sec); snprintf(data_txt_save, sizeof(data_txt_save), "@%02d%02d%02d/%s%s%s%s%s", day_time->tm_mday, day_time->tm_hour, day_time->tm_min, my_pos, output_cs, output_brk, output_alt, my_comment_tx); //WE7U2: // Truncate at max length for this type of APRS // packet. if (_aprs_transmit_compressed_posit) { if (strlen(data_txt_save) > 61) { data_txt_save[61] = '\0'; } } else { // Uncompressed lat/long if (strlen(data_txt_save) > 70) { data_txt_save[70] = '\0'; } } // Add '\r' onto end. strncat(data_txt_save, "\r", 1); snprintf(data_txt, sizeof(data_txt), "%s%s", output_net, data_txt_save); if (ok) { // Here's where the actual transmit of the posit occurs. The // transmit string has been set up in "data_txt" by this point. // If transmit or posits have been turned off, don't transmit posit if ( (port_data.status == DEVICE_UP) // && (devices.transmit_data == 1) // && !transmit_disable // && !posit_tx_disable ) { interfaces_ok_for_transmit++; // WE7U: Change so that path is passed as well for KISS TNC // interfaces: header_txt_save would probably be the one to pass, // or create a new string just for KISS TNC's. if ( (port_data.device_type == DEVICE_SERIAL_KISS_TNC) || (port_data.device_type == DEVICE_SERIAL_MKISS_TNC) ) { // Note: This one has callsign & destination in the string // Transmit the posit out the KISS interface send_ax25_frame(APRS_PORT_TTY, _aprs_mycall, // source VERSIONFRM, // destination path_txt, // path data_txt); // data } else { // Not a Serial KISS TNC interface port_write_string(data_txt, APRS_PORT_TTY); // Transmit the posit } // Put our transmitted packet into the Incoming Data // window as well. This way we can see both sides of a // conversation. data_port == -1 for x_spider port, // normal interface number otherwise. -99 to get a "**" // display meaning all ports. // // For packets that we're igating we end up with a CR or // LF on the end of them. Remove that so the display // looks nice. //snprintf(temp, // sizeof(temp), // "%s>%s,%s:%s", // my_callsign, // VERSIONFRM, // unproto_path, // data_txt); //makePrintable(temp); //packet_data_add("TX ", temp, port); } } // End of posit transmit: "if (ok)" } */ void create_output_pos_packet(TAprsPort port, gchar **packet, int *length) { // gchar encodedPos[MAX_LINE_SIZE]; if(_aprs_transmit_compressed_posit) { // TODO } else { //!5122.09N/00008.42W&APRS4R IGATE RUNNING ON NSLU2 // For now just use a simple packet gchar slat[10]; gchar slon[10]; gdouble pos = (_gps.lat > 0 ? _gps.lat : 0-_gps.lat); gdouble min = (pos - (int)pos)*60.0; sprintf(slat, "%02d%02d.%02.0f", (int)pos, (int)min, ((min - (int)min)*100.0) ); pos = (_gps.lon > 0 ? _gps.lon : 0-_gps.lon); min = (pos - (int)pos)*60.0; sprintf(slon, "%03d%02d.%02.0f", (int)pos, (int)min, ((min - (int)min)*100.0) ); *packet = g_strdup_printf( "%c%s%c%c%s%c%c%s%c", '=', slat, (_gps.lat > 0 ? 'N' : 'S'), _aprs_beacon_group, slon, (_gps.lon > 0 ? 'E' : 'W'), _aprs_beacon_symbol, (port == APRS_PORT_INET ? _aprs_inet_beacon_comment : _aprs_beacon_comment), (char)0 ); } *length = strlen(*packet); } void send_packet(TAprsPort port, gchar* to_call, gchar* path, gchar* packet, gint packet_length) { if(port == APRS_PORT_INET || !(port_data.device_type == DEVICE_SERIAL_KISS_TNC || port_data.device_type == DEVICE_SERIAL_MKISS_TNC) ) { gchar *packet_header = g_strdup_printf( "%s>%s,%s:", _aprs_mycall, to_call, path); gchar *full_packet = g_strdup_printf("%s%s\r\n", packet_header, packet); send_line(full_packet, strlen(packet_header)+packet_length+2, port); } else { send_ax25_frame(port, _aprs_mycall, to_call, path, packet); } } void output_my_aprs_data(TAprsPort port) { gchar *packet; int length = 0; //create_output_lat_long(my_output_lat, my_output_long); create_output_pos_packet(port, &packet, &length); send_packet(port, VERSIONFRM, _aprs_unproto_path, packet, length); if(packet != NULL) g_free(packet); } /////////// TTY functionality int serial_init(); int _aprs_tnc_retry_count = 0; static gboolean aprs_tnc_handle_error_idle (gchar *error) { printf("%s(%s)\n", __PRETTY_FUNCTION__, error); /* Ask for re-try. */ if(++_aprs_tnc_retry_count > 2) { GtkWidget *confirm; gchar buffer[BUFFER_SIZE]; /* Reset retry count. */ _aprs_tnc_retry_count = 0; snprintf(buffer, sizeof(buffer), "%s\nRetry?", error); confirm = hildon_note_new_confirmation(GTK_WINDOW(_window), buffer); aprs_tty_disconnect(); if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm))) aprs_tty_connect(); /* Try again. */ else { /* Disable GPS. */ gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_enable_aprs_tty_item), FALSE); } /* Ask user to re-connect. */ gtk_widget_destroy(confirm); } else { aprs_tty_disconnect(); aprs_tty_connect(); /* Try again. */ } g_free(error); vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } void close_tnc_port(); static void thread_read_tty() { // gint max_retries = 5; GThread *my_thread = g_thread_self(); //fprintf(stderr, "in thread_read_tty\n"); if(/*max_retries>0 &&*/ _aprs_tty_thread == my_thread ) { if( serial_init() >= 0 ) { // Success //fprintf(stderr, "TTY port open \n"); port_read(); if(_aprs_tty_thread == my_thread) { g_idle_add((GSourceFunc)aprs_tnc_handle_error_idle, g_strdup_printf("%s", _("Error reading data from TNC Port."))); } } else { // Failed fprintf(stderr, "Failed to init serial port\n"); g_idle_add((GSourceFunc)aprs_tnc_handle_error_idle, g_strdup_printf("%s", _("Error connecting to TNC Port."))); } close_tnc_port(); // max_retries--; // sleep(50); } // if(max_retries==0) // { // Failed // set_aprs_tty_conn_state(RCVR_OFF); // MACRO_BANNER_SHOW_INFO(_window, _("Failed to connect to TNC!")); \ // } } gboolean aprs_tty_connect() { printf("%s(%d)\n", __PRETTY_FUNCTION__, _aprs_tty_state); if(_aprs_tty_enable && _aprs_tty_state == RCVR_OFF) { set_aprs_tty_conn_state(RCVR_DOWN); // Lock/Unlock the mutex to ensure that the thread doesn't // start until _gps_thread is set. g_mutex_lock(_aprs_tty_init_mutex); _aprs_tty_thread = g_thread_create((GThreadFunc)thread_read_tty, NULL, TRUE, NULL); // Joinable. g_mutex_unlock(_aprs_tty_init_mutex); } vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } void aprs_tty_disconnect() { gboolean exit_now = FALSE; printf("%s()\n", __PRETTY_FUNCTION__); GThread *my_thread = g_thread_self(); if(my_thread == _aprs_tty_thread) { exit_now = TRUE; close_tnc_port(); } _aprs_tty_thread = NULL; if(_window) set_aprs_tty_conn_state(RCVR_OFF); vprintf("%s(): return\n", __PRETTY_FUNCTION__); if(exit_now) exit(0); } //*********************************************************** // port_write_string() // // port is port# used // data is the string to write //*********************************************************** void port_write_string(gchar *data, gint len, TAprsPort port) { // int i,erd, int retval; // int write_in_pos_hold; if (data == NULL) return; if (data[0] == '\0') return; if(port == APRS_PORT_TTY) { if(g_mutex_trylock (_write_buffer[port].write_lock)) { //fprintf(stderr, "TTY Write... "); retval = (int)write(port_data.channel, data, len); //fprintf(stderr, "done... "); g_mutex_unlock (_write_buffer[port].write_lock); //fprintf(stderr, "Unlocked\n"); } else fprintf(stderr, "Failed to get lock\n"); } else { send_line(data, len, port); } } gboolean send_line(gchar* text, gint text_len, TAprsPort port) { if(APRS_PORT_INET == port && !_aprs_enable_inet_tx) return FALSE; else if (APRS_PORT_TTY == port && !_aprs_enable_tty_tx) return FALSE; if(APRS_PORT_TTY == port) { } gboolean error = FALSE; gint i; gint write_in_pos_hold = _write_buffer[port].write_in_pos; // Lock the mutex g_mutex_lock(_write_buffer[port].write_lock); for (i = 0; i < text_len && !error; i++) { _write_buffer[port].device_write_buffer[_write_buffer[port].write_in_pos++] = text[i]; if (_write_buffer[port].write_in_pos >= MAX_DEVICE_BUFFER) _write_buffer[port].write_in_pos = 0; if (_write_buffer[port].write_in_pos == _write_buffer[port].write_out_pos) { fprintf(stderr,"Port %d Buffer overrun\n",port); /* clear this restore original write_in pos and dump this string */ _write_buffer[port].write_in_pos = write_in_pos_hold; _write_buffer[port].errors++; error = TRUE; } } g_mutex_unlock(_write_buffer[port].write_lock); return error; } //*********************************************************** // port_read() // // // This function becomes the long-running thread that snags // characters from an interface and passes them off to the // decoding routines. One copy of this is run for each read // thread for each interface. //*********************************************************** gboolean read_port_data(); void port_read() { // unsigned char cin, last; // gint i; struct timeval tmv; fd_set rd; // cin = (unsigned char)0; // last = (unsigned char)0; gboolean success = TRUE; GThread *my_thread = g_thread_self(); fprintf(stderr, "Enter port_read\n"); // We stay in this read loop until the port is shut down while(port_data.active == DEVICE_IN_USE && _aprs_tty_thread == my_thread && RCVR_UP == _aprs_tty_state && success == TRUE){ if (port_data.status == DEVICE_UP){ port_data.read_in_pos = 0; port_data.scan = 1; while (port_data.scan >= 0 && success == TRUE //&& RCVR_UP == _aprs_tty_state && (port_data.read_in_pos < (MAX_DEVICE_BUFFER - 1) ) && (port_data.status == DEVICE_UP) && (_aprs_tty_thread == my_thread) ) { success = read_port_data(); } } if (port_data.active == DEVICE_IN_USE) { // We need to delay here so that the thread doesn't use // high amounts of CPU doing nothing. // This select that waits on data and a timeout, so that if data // doesn't come in within a certain period of time, we wake up to // check whether the socket has gone down. Else, we go back into // the select to wait for more data or a timeout. FreeBSD has a // problem if this is less than 1ms. Linux works ok down to 100us. // We don't need it anywhere near that short though. We just need // to check whether the main thread has requested the interface be // closed, and so need to have this short enough to have reasonable // response time to the user. //sched_yield(); // Yield to other threads // Set up the select to block until data ready or 100ms // timeout, whichever occurs first. FD_ZERO(&rd); FD_SET(port_data.channel, &rd); tmv.tv_sec = 0; tmv.tv_usec = 100000; // 100 ms (void)select(0,&rd,NULL,NULL,&tmv); } } fprintf(stderr, "End of port_read\n"); } gboolean aprs_send_beacon_inet() { aprs_send_beacon(APRS_PORT_INET); return TRUE; } gboolean aprs_send_beacon(TAprsPort port) { if(_aprs_enable) { output_my_aprs_data(port); //fprintf(stderr, "Beacon sent\n" ); } return TRUE; } gboolean timer_callback_aprs_inet (gpointer data) { if(_aprs_inet_enable && _aprs_enable_inet_tx && _aprs_inet_beacon_interval>0) { aprs_send_beacon(APRS_PORT_INET); return TRUE; // Continue timer } return FALSE; // Stop timer } gboolean timer_callback_aprs_tty (gpointer data) { if(_aprs_tty_enable && _aprs_enable_tty_tx && _aprs_tty_beacon_interval>0) { fprintf(stderr, "Sending beacon for TNC...\n"); aprs_send_beacon(APRS_PORT_TTY); return TRUE; // Continue timer } return FALSE; // Stop timer } void aprs_timer_init() { // disable timer if exists if(_aprs_inet_beacon_timer>0) g_source_remove(_aprs_inet_beacon_timer); if(_aprs_tty_beacon_timer>0) g_source_remove(_aprs_tty_beacon_timer); if(_aprs_enable) { if(_aprs_inet_enable && _aprs_enable_inet_tx && _aprs_inet_beacon_interval>0) _aprs_inet_beacon_timer = g_timeout_add(_aprs_inet_beacon_interval*1000 , timer_callback_aprs_inet, NULL); if(_aprs_tty_enable && _aprs_enable_tty_tx && _aprs_tty_beacon_interval>0) _aprs_tty_beacon_timer = g_timeout_add(_aprs_tty_beacon_interval*1000 , timer_callback_aprs_tty, NULL); } } #endif //INCLUDE_APRS