// // This file is part of Dire Wolf, an amateur radio packet TNC. // // Copyright (C) 2013 John Langner, WB2OSZ // // This program 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 2 of the License, or // (at your option) any later version. // // This program 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 this program. If not, see . // /*------------------------------------------------------------------ * * Module: tt-user.c * * Purpose: Keep track of the APRStt users. * * Description: This maintains a list of recently heard APRStt users * and prepares "object" format packets for transmission. * * References: This is based upon APRStt (TM) documents but not 100% * compliant due to ambiguities and inconsistencies in * the specifications. * * http://www.aprs.org/aprstt.html * *---------------------------------------------------------------*/ #include #include #include #include #include #include #include #include "direwolf.h" #include "ax25_pad.h" #include "textcolor.h" #include "aprs_tt.h" #include "tt_text.h" #include "dedupe.h" #include "tq.h" #include "igate.h" #include "tt_user.h" #include "encode_aprs.h" #include "latlong.h" /* * Information kept about local APRStt users. * * For now, just use a fixed size array for simplicity. */ #if TT_MAIN #define MAX_TT_USERS 3 #else #define MAX_TT_USERS 100 #endif #define MAX_CALLSIGN_LEN 9 /* "Object Report" names can be up to 9 characters. */ #define MAX_COMMENT_LEN 43 /* Max length of comment in "Object Report." */ //#define G_UNKNOWN -999999 /* Should be in one place. */ #define NUM_XMITS 3 #define XMIT_DELAY_1 5 #define XMIT_DELAY_2 8 #define XMIT_DELAY_3 13 static struct tt_user_s { char callsign[MAX_CALLSIGN_LEN+1]; /* Callsign of station heard. */ /* Does not include the "-12" SSID added later. */ /* Possibly other tactical call / object label. */ /* Null string indicates table position is not used. */ int ssid; /* SSID to add. */ /* Default of 12 but not always. */ char overlay; /* Overlay character. Should be 0-9, A-Z. */ /* Could be / or \ for general object. */ char symbol; /* 'A' for traditional. */ /* Can be any symbol for extended objects. */ char digit_suffix[3+1]; /* Suffix abbreviation as 3 digits. */ time_t last_heard; /* Timestamp when last heard. */ /* User information will be deleted at some */ /* point after last time being heard. */ int xmits; /* Number of remaining times to transmit info */ /* about the user. This is set to 3 when */ /* a station is heard and decremented each time */ /* an object packet is sent. The idea is to send */ /* 3 within 30 seconds to improve chances of */ /* being heard while using digipeater duplicate */ /* removal. */ time_t next_xmit; /* Time for next transmit. Meaningful only */ /* if xmits > 0. */ int corral_slot; /* If location is known, set this to 0. */ /* Otherwise, this is a display offset position */ /* from the gateway. */ double latitude, longitude; /* Location either from user or generated */ /* position in the corral. */ char freq[12]; /* Frequency in format 999.999MHz */ char comment[MAX_COMMENT_LEN+1]; /* Free form comment. */ char mic_e; /* Position status. */ char dao[8]; /* Enhanced position information. */ } tt_user[MAX_TT_USERS]; static void clear_user(int i); static void xmit_object_report (int i, double c_lat, double c_long, int ambiguity, double c_offs); /*------------------------------------------------------------------ * * Name: tt_user_init * * Purpose: Initialize the APRStt gateway at system startup time. * * Inputs: Configuration options gathered by config.c. * * Global out: Make our own local copy of the structure here. * * Returns: None * * Description: The main program needs to call this at application * start up time after reading the configuration file. * * TT_MAIN is defined for unit testing. * *----------------------------------------------------------------*/ static struct tt_config_s tt_config; void tt_user_init (struct tt_config_s *p) { int i; #if TT_MAIN /* For unit testing. */ memset (&tt_config, 0, sizeof(struct tt_config_s)); /* Don't care about the location translation here. */ tt_config.retain_time = 20; /* Normally 80 minutes. */ tt_config.num_xmits = 3; assert (tt_config.num_xmits <= TT_MAX_XMITS); tt_config.xmit_delay[0] = 3; /* Before initial transmission. */ tt_config.xmit_delay[1] = 5; tt_config.xmit_delay[2] = 5; tt_config.corral_lat = 42.61900; tt_config.corral_lon = -71.34717; tt_config.corral_offset = 0.02 / 60; tt_config.corral_ambiguity = 0; #else memcpy (&tt_config, p, sizeof(struct tt_config_s)); #endif for (i=0; i= 0 && i < MAX_TT_USERS); memset (&tt_user[i], 0, sizeof (struct tt_user_s)); } /* end clear_user */ /*------------------------------------------------------------------ * * Name: find_avail * * Purpose: Find an available user table location. * * Inputs: none * * Returns: Handle for refering to table position. * * Description: If table is already full, this should delete the * least recently heard user to make room. * *----------------------------------------------------------------*/ static int find_avail (void) { int i; int i_oldest; for (i=0; i= 1 not already in use. * *----------------------------------------------------------------*/ static int corral_slot (void) { int slot, i, used; for (slot=1; ; slot++) { used = 0;; for (i=0; i= 0 && i < MAX_TT_USERS); strncpy (tt_user[i].callsign, callsign, MAX_CALLSIGN_LEN); tt_user[i].callsign[MAX_CALLSIGN_LEN] = '\0'; tt_user[i].ssid = ssid; tt_user[i].overlay = overlay; tt_user[i].symbol = symbol; digit_suffix(tt_user[i].callsign, tt_user[i].digit_suffix); if (latitude != G_UNKNOWN && longitude != G_UNKNOWN) { /* We have specific location. */ tt_user[i].corral_slot = 0; tt_user[i].latitude = latitude; tt_user[i].longitude = longitude; } else { /* Unknown location, put it in the corral. */ tt_user[i].corral_slot = corral_slot(); } strcpy (tt_user[i].freq, freq); strncpy (tt_user[i].comment, comment, MAX_COMMENT_LEN); tt_user[i].comment[MAX_COMMENT_LEN] = '\0'; tt_user[i].mic_e = mic_e; strncpy(tt_user[i].dao, dao, 6); } else { /* * Known user. Update with any new information. */ assert (i >= 0 && i < MAX_TT_USERS); /* Any reason to look at ssid here? */ if (latitude != G_UNKNOWN && longitude != G_UNKNOWN) { /* We have specific location. */ tt_user[i].corral_slot = 0; tt_user[i].latitude = latitude; tt_user[i].longitude = longitude; } if (freq[0] != '\0') { strcpy (tt_user[i].freq, freq); } if (comment[0] != '\0') { strncpy (tt_user[i].comment, comment, MAX_COMMENT_LEN); tt_user[i].comment[MAX_COMMENT_LEN] = '\0'; } if (mic_e != ' ') { tt_user[i].mic_e = mic_e; } if (strlen(dao) > 0) { strncpy(tt_user[i].dao, dao, 6); tt_user[i].dao[5] = '\0'; } } /* * In both cases, note last time heard and schedule object report transmission. */ tt_user[i].last_heard = time(NULL); tt_user[i].xmits = 0; tt_user[i].next_xmit = tt_user[i].last_heard + tt_config.xmit_delay[0]; return (0); /* Success! */ } /* end tt_user_heard */ /*------------------------------------------------------------------ * * Name: tt_user_background * * Purpose: * * Inputs: * * Outputs: Append to transmit queue. * * Returns: None * * Description: ...... TBD * *----------------------------------------------------------------*/ void tt_user_background (void) { time_t now = time(NULL); int i; for (i=0; i= 0 && i < MAX_TT_USERS); /* * Prepare the object name. * Tack on "-12" if it is a callsign. */ strcpy (object_name, tt_user[i].callsign); if (strlen(object_name) <= 6 && tt_user[i].ssid != 0) { char stemp8[8]; sprintf (stemp8, "-%d", tt_user[i].ssid); strcat (object_name, stemp8); } if (tt_user[i].corral_slot == 0) { /* * Known location. */ olat = tt_user[i].latitude; olong = tt_user[i].longitude; } else { /* * Use made up position in the corral. */ olat = c_lat - (tt_user[i].corral_slot - 1) * c_offs; olong = c_long; } /* * Build comment field from various information. */ strcpy (info_comment, ""); if (strlen(tt_user[i].comment) != 0) { strcat (info_comment, tt_user[i].comment); } if (tt_user[i].mic_e >= '1' && tt_user[i].mic_e <= '9') { strcat (info_comment, mic_e_position_comment[tt_user[i].mic_e - '0']); } if (strlen(tt_user[i].dao) > 0) { strcat (info_comment, tt_user[i].dao); } /* Official limit is 43 characters. */ info_comment[MAX_COMMENT_LEN] = '\0'; /* * Combine with header from configuration file. * * (If APRStt gateway has been configured.) */ if (tt_config.obj_xmit_header[0] != '\0') { // TODO: Should take the call from radio channel configuration. // Application version is compiled in. // Config should have only optional via path. strcpy (stemp, tt_config.obj_xmit_header); strcat (stemp, ":"); encode_object (object_name, 0, tt_user[i].last_heard, olat, olong, tt_user[i].overlay, tt_user[i].symbol, 0,0,0,NULL, 0,0, /* PHGD, C/S */ atof(tt_user[i].freq), 0, 0, info_comment, object_info); strcat (stemp, object_info); //text_color_set(DW_COLOR_ERROR); //printf ("\nDEBUG: %s\n\n", stemp); #if TT_MAIN printf ("---> %s\n\n", stemp); #else /* * Convert to packet and append to transmit queue. */ pp = ax25_from_text (stemp, 1); flen = ax25_pack (pp, fbuf); /* * Process as if we heard ourself. */ // TODO: We need radio channel where this came from. // It would make a difference if running two radios // and they have different station identifiers. int chan = 0; igate_send_rec_packet (chan, pp); /* Remember it so we don't digipeat our own. */ dedupe_remember (pp, tt_config.obj_xmit_chan); tq_append (tt_config.obj_xmit_chan, TQ_PRIO_1_LO, pp); #endif } } /*------------------------------------------------------------------ * * Name: tt_user_dump * * Purpose: Print information about known users for debugging. * * Inputs: None. * * Description: Timestamps displayed relative to current time. * *----------------------------------------------------------------*/ void tt_user_dump (void) { int i; time_t now = time(NULL); printf ("call ov suf lsthrd xmit nxt cor lat long freq m comment\n"); for (i=0; i