mirror of https://github.com/wb2osz/direwolf.git
				
				
				
			
		
			
				
	
	
		
			1195 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1195 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
//
 | 
						|
//    This file is part of Dire Wolf, an amateur radio packet TNC.
 | 
						|
//
 | 
						|
//    Copyright (C) 2013, 2014, 2015  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 <http://www.gnu.org/licenses/>.
 | 
						|
//
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * 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 "direwolf.h"
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <time.h>
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
#include "version.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"
 | 
						|
 | 
						|
#include "server.h"
 | 
						|
#include "kiss.h"
 | 
						|
#include "kissserial.h"
 | 
						|
#include "kissnet.h"
 | 
						|
#include "kiss_frame.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 count;				/* Number of times we received information for this object. */
 | 
						|
						/* Value 1 means first time and could be used to send */
 | 
						|
						/* a welcome greeting. */
 | 
						|
 | 
						|
	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. */
 | 
						|
						// TODO:  I think implementation is different.
 | 
						|
 | 
						|
	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. */
 | 
						|
 | 
						|
	char loc_text[24];			/* Text representation of location when a single */
 | 
						|
						/* lat/lon point would be deceptive.  e.g.  */
 | 
						|
						/* 32TPP8049 */
 | 
						|
						/* 32TPP8179549363 */
 | 
						|
						/* 32T 681795 4849363 */
 | 
						|
						/* EM29QE78 */
 | 
						|
 | 
						|
	double latitude, longitude;		/* Location either from user or generated */		
 | 
						|
						/* position in the corral. */
 | 
						|
 | 
						|
	int ambiguity;				/* Number of digits to omit from location. */
 | 
						|
						/* Default 0, max. 4. */
 | 
						|
 | 
						|
	char freq[12];				/* Frequency in format 999.999MHz */
 | 
						|
 | 
						|
	char ctcss[5];				/* CTCSS tone.  Exactly 3 digits for integer part. */
 | 
						|
						/* For example 74.4 Hz becomes "074". */
 | 
						|
 | 
						|
	char comment[MAX_COMMENT_LEN+1];	/* Free form comment from user. */
 | 
						|
						/* Comment sent in final object report includes */
 | 
						|
						/* other information besides this. */
 | 
						|
 | 
						|
	char mic_e;				/* Position status. */
 | 
						|
						/* Should be a character in range of '1' to '9' for */
 | 
						|
						/* the predefined status strings or '0' for none. */
 | 
						|
 | 
						|
	char dao[8];				/* Enhanced position information. */
 | 
						|
						
 | 
						|
 | 
						|
} tt_user[MAX_TT_USERS];
 | 
						|
 | 
						|
 | 
						|
static void clear_user(int i);
 | 
						|
 | 
						|
static void xmit_object_report (int i, int first_time);
 | 
						|
 | 
						|
static void tt_setenv (int i);
 | 
						|
 | 
						|
 | 
						|
#if __WIN32__
 | 
						|
 | 
						|
// setenv is missing on Windows!
 | 
						|
 | 
						|
int setenv(const char *name, const char *value, int overwrite)
 | 
						|
{
 | 
						|
	char etemp[1000];
 | 
						|
 | 
						|
	snprintf (etemp, sizeof(etemp), "%s=%s", name, value);
 | 
						|
	putenv (etemp);
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * 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 audio_s *save_audio_config_p;
 | 
						|
 | 
						|
static struct tt_config_s *save_tt_config_p;
 | 
						|
 | 
						|
 | 
						|
void tt_user_init (struct audio_s *p_audio_config, struct tt_config_s *p_tt_config)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	save_audio_config_p = p_audio_config;
 | 
						|
 | 
						|
	save_tt_config_p = p_tt_config;
 | 
						|
 | 
						|
	for (i=0; i<MAX_TT_USERS; i++) {
 | 
						|
	  clear_user (i);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * Name:        tt_user_search
 | 
						|
 *
 | 
						|
 * Purpose:     Search for user in recent history.
 | 
						|
 *
 | 
						|
 * Inputs:      callsign	- full or a old style 3 DIGIT suffix abbreviation
 | 
						|
 *		overlay
 | 
						|
 *
 | 
						|
 * Returns:     Handle for refering to table position or -1 if not found.
 | 
						|
 *		This happens to be an index into an array but
 | 
						|
 *		the implementation could change so the caller should
 | 
						|
 *		not make any assumptions.
 | 
						|
 *
 | 
						|
 *----------------------------------------------------------------*/
 | 
						|
 | 
						|
int tt_user_search (char *callsign, char overlay)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
/*
 | 
						|
 * First, look for exact match to full call and overlay.
 | 
						|
 */
 | 
						|
	for (i=0; i<MAX_TT_USERS; i++) {
 | 
						|
	  if (strcmp(callsign, tt_user[i].callsign) == 0 && 
 | 
						|
		overlay == tt_user[i].overlay) {
 | 
						|
	    return (i);
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
/*
 | 
						|
 * Look for digits only suffix plus overlay.
 | 
						|
 */
 | 
						|
	for (i=0; i<MAX_TT_USERS; i++) {
 | 
						|
	  if (strcmp(callsign, tt_user[i].digit_suffix) == 0 && 
 | 
						|
		overlay != ' ' &&
 | 
						|
		overlay == tt_user[i].overlay) {
 | 
						|
	    return (i);
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
/*
 | 
						|
 * Look for digits only suffix if no overlay was specified.
 | 
						|
 */
 | 
						|
	for (i=0; i<MAX_TT_USERS; i++) {
 | 
						|
	  if (strcmp(callsign, tt_user[i].digit_suffix) == 0 && 
 | 
						|
		overlay == ' ') {
 | 
						|
	    return (i);
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
/*
 | 
						|
 * Not sure about the new spelled suffix yet...
 | 
						|
 */
 | 
						|
	return (-1);
 | 
						|
 | 
						|
}  /* end tt_user_search */
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * Name:        tt_3char_suffix_search
 | 
						|
 *
 | 
						|
 * Purpose:     Search for new style 3 CHARACTER (vs. 3 digit) suffix in recent history.
 | 
						|
 *
 | 
						|
 * Inputs:      suffix	- full or a old style 3 DIGIT suffix abbreviation
 | 
						|
 *
 | 
						|
 * Outputs:	callsign - corresponding full callsign or empty string.
 | 
						|
 *
 | 
						|
 * Returns:     Handle for refering to table position (>= 0) or -1 if not found.
 | 
						|
 *		This happens to be an index into an array but
 | 
						|
 *		the implementation could change so the caller should
 | 
						|
 *		not make any assumptions.
 | 
						|
 *
 | 
						|
 *----------------------------------------------------------------*/
 | 
						|
 | 
						|
int tt_3char_suffix_search (char *suffix, char *callsign)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * Look for suffix in list of known calls.
 | 
						|
 */
 | 
						|
	for (i=0; i<MAX_TT_USERS; i++) {
 | 
						|
	  int len = strlen(tt_user[i].callsign);
 | 
						|
 | 
						|
	  if (len >= 3 && len <= 6 && strcmp(tt_user[i].callsign + len - 3, suffix) == 0) {
 | 
						|
	    strlcpy (callsign, tt_user[i].callsign, MAX_CALLSIGN_LEN+1);
 | 
						|
	    return (i);
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
/*
 | 
						|
 * Not found.
 | 
						|
 */
 | 
						|
	strlcpy (callsign, "", MAX_CALLSIGN_LEN+1);
 | 
						|
	return (-1);
 | 
						|
 | 
						|
}  /* end tt_3char_suffix_search */
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * Name:        clear_user
 | 
						|
 *
 | 
						|
 * Purpose:     Clear specified user table entry.
 | 
						|
 *
 | 
						|
 * Inputs:      handle for user table entry.
 | 
						|
 *
 | 
						|
 *----------------------------------------------------------------*/
 | 
						|
 | 
						|
static void clear_user(int i)
 | 
						|
{
 | 
						|
	assert (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<MAX_TT_USERS; i++) {
 | 
						|
	  if (tt_user[i].callsign[0] == '\0') {
 | 
						|
	    clear_user (i);
 | 
						|
	    return (i);
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
/* Remove least recently heard. */
 | 
						|
 | 
						|
	i_oldest = 0;
 | 
						|
 | 
						|
	for (i=1; i<MAX_TT_USERS; i++) {
 | 
						|
	  if (tt_user[i].last_heard < tt_user[i_oldest].last_heard) {
 | 
						|
	    i_oldest = i;
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
	clear_user (i_oldest);
 | 
						|
	return (i_oldest);
 | 
						|
 | 
						|
} /* end find_avail */
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * Name:        corral_slot
 | 
						|
 *
 | 
						|
 * Purpose:     Find an available position in the corral.
 | 
						|
 *
 | 
						|
 * Inputs:      none
 | 
						|
 *
 | 
						|
 * Returns:     Small integer >= 1 not already in use.
 | 
						|
 *
 | 
						|
 *----------------------------------------------------------------*/
 | 
						|
 | 
						|
static int corral_slot (void)
 | 
						|
{
 | 
						|
	int slot, i, used;
 | 
						|
 | 
						|
	for (slot=1; ; slot++) {
 | 
						|
	  used = 0;;
 | 
						|
	  for (i=0; i<MAX_TT_USERS && ! used; i++) {
 | 
						|
	    if (tt_user[i].callsign[0] != '\0' && tt_user[i].corral_slot == slot) {
 | 
						|
	      used = 1;
 | 
						|
	    }
 | 
						|
	  }
 | 
						|
	  if (!used) {
 | 
						|
	    return (slot);
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
} /* end corral_slot */
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * Name:        digit_suffix
 | 
						|
 *
 | 
						|
 * Purpose:     Find 3 digit only suffix code for given call.
 | 
						|
 *
 | 
						|
 * Inputs:      callsign
 | 
						|
 *
 | 
						|
 * Outputs:	3 digit suffix
 | 
						|
 *
 | 
						|
 *----------------------------------------------------------------*/
 | 
						|
 | 
						|
static void digit_suffix (char *callsign, char *suffix)
 | 
						|
{
 | 
						|
	char two_key[50];
 | 
						|
	char *t;
 | 
						|
 | 
						|
 | 
						|
	strlcpy (suffix, "000", 5);			// TODO: should have proper size
 | 
						|
	tt_text_to_two_key (callsign, 0, two_key);
 | 
						|
	for (t = two_key; *t != '\0'; t++) {
 | 
						|
	  if (isdigit(*t)) {
 | 
						|
	    suffix[0] = suffix[1];
 | 
						|
	    suffix[1] = suffix[2];
 | 
						|
	    suffix[2] = *t;
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
} /* end digit_suffix */
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * Name:        tt_user_heard
 | 
						|
 *
 | 
						|
 * Purpose:     Record information from an APRStt trasmission.
 | 
						|
 *
 | 
						|
 * Inputs:      callsign	- full or an abbreviation
 | 
						|
 *		ssid
 | 
						|
 *		overlay		- or symbol table identifier
 | 
						|
 *		symbol
 | 
						|
 *		loc_text	- Original text for non lat/lon location
 | 
						|
 *		latitude
 | 
						|
 *		longitude
 | 
						|
 *		ambiguity
 | 
						|
 *		freq
 | 
						|
 *		ctcss
 | 
						|
 *		comment
 | 
						|
 *		mic_e
 | 
						|
 *		dao
 | 
						|
 *
 | 
						|
 * Outputs:	Information is stored in table above.
 | 
						|
 *		Last heard time is updated.
 | 
						|
 *		Object Report transmission is scheduled.
 | 
						|
 *
 | 
						|
 * Returns:	0 for success or one of the TT_ERROR_... codes.
 | 
						|
 *
 | 
						|
 *----------------------------------------------------------------*/
 | 
						|
 | 
						|
int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude, 
 | 
						|
		double longitude, int ambiguity, char *freq, char *ctcss, char *comment, char mic_e, char *dao)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
	//text_color_set(DW_COLOR_DEBUG);
 | 
						|
	//dw_printf ("tt_user_heard (%s, %d, %c, %c, %s, ...)\n", callsign, ssid, overlay, symbol, loc_text);
 | 
						|
 | 
						|
/*
 | 
						|
 * At this time all messages are expected to contain a callsign.
 | 
						|
 * Other types of messages, not related to a particular person/object
 | 
						|
 * are a future possibility. 
 | 
						|
 */
 | 
						|
	if (callsign[0] == '\0') {
 | 
						|
	  text_color_set(DW_COLOR_ERROR);
 | 
						|
	  printf ("APRStt tone sequence did not include callsign / object name.\n");
 | 
						|
	  return (TT_ERROR_NO_CALL);
 | 
						|
	}
 | 
						|
 | 
						|
/*
 | 
						|
 * Is it someone new or a returning user?
 | 
						|
 */
 | 
						|
	i = tt_user_search (callsign, overlay);
 | 
						|
	if (i == -1) {
 | 
						|
 | 
						|
/*
 | 
						|
 * New person.  Create new table entry with all available information.
 | 
						|
 */
 | 
						|
	  i = find_avail ();
 | 
						|
 | 
						|
	  assert (i >= 0 && i < MAX_TT_USERS);
 | 
						|
	  strlcpy (tt_user[i].callsign, callsign, sizeof(tt_user[i].callsign));
 | 
						|
	  tt_user[i].count = 1;
 | 
						|
	  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);
 | 
						|
	  strlcpy (tt_user[i].loc_text, loc_text, sizeof(tt_user[i].loc_text));
 | 
						|
 | 
						|
	  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();
 | 
						|
	  }
 | 
						|
 | 
						|
	  tt_user[i].ambiguity = ambiguity;
 | 
						|
 | 
						|
	  strlcpy (tt_user[i].freq, freq, sizeof(tt_user[i].freq));
 | 
						|
	  strlcpy (tt_user[i].ctcss, ctcss, sizeof(tt_user[i].ctcss));
 | 
						|
	  strlcpy (tt_user[i].comment, comment, sizeof(tt_user[i].comment));
 | 
						|
	  tt_user[i].mic_e = mic_e;
 | 
						|
	  strlcpy(tt_user[i].dao, dao, sizeof(tt_user[i].dao));
 | 
						|
	}
 | 
						|
	else {
 | 
						|
/*
 | 
						|
 * Known user.  Update with any new information.
 | 
						|
 * Keep any old values where not being updated.
 | 
						|
 */
 | 
						|
	  assert (i >= 0 && i < MAX_TT_USERS);
 | 
						|
 | 
						|
	  tt_user[i].count++;
 | 
						|
 | 
						|
	  /* Any reason to look at ssid here? */
 | 
						|
 | 
						|
	  /* Update the symbol if not the default. */
 | 
						|
 | 
						|
	  if (overlay != APRSTT_DEFAULT_SYMTAB || symbol != APRSTT_DEFAULT_SYMBOL) {
 | 
						|
	    tt_user[i].overlay = overlay;
 | 
						|
	    tt_user[i].symbol = symbol;
 | 
						|
	  }
 | 
						|
 | 
						|
	  if (strlen(loc_text) > 0) {
 | 
						|
	    strlcpy (tt_user[i].loc_text, loc_text, sizeof(tt_user[i].loc_text));
 | 
						|
	  }
 | 
						|
 | 
						|
	  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 (ambiguity != G_UNKNOWN) {
 | 
						|
	    tt_user[i].ambiguity = ambiguity;
 | 
						|
	  }
 | 
						|
 | 
						|
	  if (freq[0] != '\0') {
 | 
						|
	    strlcpy (tt_user[i].freq, freq, sizeof(tt_user[i].freq));
 | 
						|
	  }
 | 
						|
 | 
						|
	  if (ctcss[0] != '\0') {
 | 
						|
	    strlcpy (tt_user[i].ctcss, ctcss, sizeof(tt_user[i].ctcss));
 | 
						|
	  }
 | 
						|
 | 
						|
	  if (comment[0] != '\0') {
 | 
						|
	    strlcpy (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) {
 | 
						|
	    strlcpy(tt_user[i].dao, dao, sizeof(tt_user[i].dao));
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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 + save_tt_config_p->xmit_delay[0];
 | 
						|
 | 
						|
/*
 | 
						|
 * Send to applications and IGate immediately.
 | 
						|
 */
 | 
						|
 | 
						|
	xmit_object_report (i, 1);	
 | 
						|
 | 
						|
/*
 | 
						|
 * Put properties into environment variables in preparation
 | 
						|
 * for calling a user-specified script.
 | 
						|
 */
 | 
						|
 | 
						|
	tt_setenv (i);
 | 
						|
 | 
						|
	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;
 | 
						|
 | 
						|
	//text_color_set(DW_COLOR_DEBUG);
 | 
						|
	//dw_printf ("tt_user_background()  now = %d\n", (int)now);
 | 
						|
 | 
						|
 | 
						|
	for (i=0; i<MAX_TT_USERS; i++) {
 | 
						|
 | 
						|
	  assert (i >= 0 && i < MAX_TT_USERS);
 | 
						|
 | 
						|
	  if (tt_user[i].callsign[0] != '\0') {
 | 
						|
	    if (tt_user[i].xmits < save_tt_config_p->num_xmits && tt_user[i].next_xmit <= now) {
 | 
						|
 | 
						|
 | 
						|
	      //text_color_set(DW_COLOR_DEBUG);
 | 
						|
	      //dw_printf ("tt_user_background()  now = %d\n", (int)now);
 | 
						|
	      //tt_user_dump ();
 | 
						|
 | 
						|
	      xmit_object_report (i, 0);	
 | 
						|
 
 | 
						|
	      /* Increase count of number times this one was sent. */
 | 
						|
	      tt_user[i].xmits++;
 | 
						|
	      if (tt_user[i].xmits < save_tt_config_p->num_xmits) {
 | 
						|
	        /* Schedule next one. */
 | 
						|
	        tt_user[i].next_xmit += save_tt_config_p->xmit_delay[tt_user[i].xmits];    
 | 
						|
	      }
 | 
						|
 | 
						|
	      //tt_user_dump ();
 | 
						|
	    }
 | 
						|
	  }
 | 
						|
	}
 | 
						|
 | 
						|
/*
 | 
						|
 * Purge if too old.
 | 
						|
 */
 | 
						|
	for (i=0; i<MAX_TT_USERS; i++) {
 | 
						|
	  if (tt_user[i].callsign[0] != '\0') {
 | 
						|
	    if (tt_user[i].last_heard + save_tt_config_p->retain_time < now) {
 | 
						|
 | 
						|
	     //dw_printf ("debug: purging expired user %d\n", i);
 | 
						|
 | 
						|
	      clear_user (i);
 | 
						|
	    }
 | 
						|
	  }
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * Name:        xmit_object_report
 | 
						|
 *
 | 
						|
 * Purpose:     Create object report packet and put into transmit queue.
 | 
						|
 *
 | 
						|
 * Inputs:      i	   - Index into user table.
 | 
						|
 *
 | 
						|
 *		first_time - Is this being called immediately after the tone sequence
 | 
						|
 *			 	was received or after some delay?
 | 
						|
 *				For the former, we send to any attached applications
 | 
						|
 *				and the IGate.
 | 
						|
 *				For the latter, we transmit over radio.
 | 
						|
 *
 | 
						|
 * Outputs:	Append to transmit queue.
 | 
						|
 *
 | 
						|
 * Returns:     None
 | 
						|
 *
 | 
						|
 * Description:	Details for specified user are converted to
 | 
						|
 *		"Object Report Format" and added to the transmit queue.
 | 
						|
 *
 | 
						|
 *		If the user did not report a position, we have to make 
 | 
						|
 *		up something so the corresponding object will show up on
 | 
						|
 *		the map or other list of nearby stations.
 | 
						|
 *
 | 
						|
 *		The traditional approach is to put them in different 
 | 
						|
 *		positions in the "corral" by applying increments of an
 | 
						|
 *		offset from the starting position.  This has two 
 | 
						|
 *		unfortunate properties.  It gives the illusion we know
 | 
						|
 *		where the person is located.   Being in the ,,,
 | 
						|
 *
 | 
						|
 *----------------------------------------------------------------*/
 | 
						|
 | 
						|
static void xmit_object_report (int i, int first_time)
 | 
						|
{
 | 
						|
	char object_name[20];		// xxxxxxxxx or xxxxxx-nn
 | 
						|
	char info_comment[200];		// usercomment [locationtext] /status !DAO!
 | 
						|
	char object_info[250];		// info part of Object Report packet
 | 
						|
	char stemp[300];		// src>dest,path:object_info
 | 
						|
 | 
						|
	double olat, olong;
 | 
						|
	int oambig;			// Position ambiguity.
 | 
						|
	packet_t pp;
 | 
						|
	char c4[4];
 | 
						|
 | 
						|
	//text_color_set(DW_COLOR_DEBUG);
 | 
						|
	//printf ("xmit_object_report (index = %d, first_time = %d) rx = %d, tx = %d\n", i, first_time, 
 | 
						|
	//			save_tt_config_p->obj_recv_chan, save_tt_config_p->obj_xmit_chan);
 | 
						|
 | 
						|
	assert (i >= 0 && i < MAX_TT_USERS);
 | 
						|
 | 
						|
/*
 | 
						|
 * Prepare the object name.  
 | 
						|
 * Tack on "-12" if it is a callsign.
 | 
						|
 */
 | 
						|
	strlcpy (object_name, tt_user[i].callsign, sizeof(object_name));
 | 
						|
 | 
						|
	if (strlen(object_name) <= 6 && tt_user[i].ssid != 0) {
 | 
						|
	  char stemp8[8];
 | 
						|
	  snprintf (stemp8, sizeof(stemp8), "-%d", tt_user[i].ssid);
 | 
						|
	  strlcat (object_name, stemp8, sizeof(object_name));
 | 
						|
	}
 | 
						|
 | 
						|
	if (tt_user[i].corral_slot == 0) {
 | 
						|
/* 
 | 
						|
 * Known location.
 | 
						|
 */
 | 
						|
	  olat = tt_user[i].latitude;
 | 
						|
	  olong = tt_user[i].longitude;
 | 
						|
	  oambig = tt_user[i].ambiguity;
 | 
						|
	  if (oambig == G_UNKNOWN) oambig = 0;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
/*
 | 
						|
 * Use made up position in the corral.
 | 
						|
 */
 | 
						|
	  double c_lat = save_tt_config_p->corral_lat;		// Corral latitude.
 | 
						|
	  double c_long = save_tt_config_p->corral_lon;		// Corral longitude.
 | 
						|
	  double c_offs =  save_tt_config_p->corral_offset;	// Corral (latitude) offset.
 | 
						|
 | 
						|
	  olat = c_lat - (tt_user[i].corral_slot - 1) * c_offs;
 | 
						|
	  olong = c_long;
 | 
						|
	  oambig = 0;
 | 
						|
	}
 | 
						|
 | 
						|
/*
 | 
						|
 * Build comment field from various information.
 | 
						|
 *
 | 
						|
 * 	usercomment [locationtext] /status !DAO!
 | 
						|
 *
 | 
						|
 * Any frequency is inserted at beginning later.
 | 
						|
 */
 | 
						|
	strlcpy (info_comment, "", sizeof(info_comment));
 | 
						|
 | 
						|
	if (strlen(tt_user[i].comment) != 0) {
 | 
						|
	  strlcat (info_comment, tt_user[i].comment, sizeof(info_comment));
 | 
						|
	}
 | 
						|
 | 
						|
	if (strlen(tt_user[i].loc_text) > 0) {
 | 
						|
	  if (strlen(info_comment) > 0) {
 | 
						|
	    strlcat (info_comment, " ", sizeof(info_comment));
 | 
						|
	  }
 | 
						|
	  strlcat (info_comment, "[", sizeof(info_comment));
 | 
						|
	  strlcat (info_comment, tt_user[i].loc_text, sizeof(info_comment));
 | 
						|
	  strlcat (info_comment, "]", sizeof(info_comment));
 | 
						|
	}
 | 
						|
 | 
						|
	if (tt_user[i].mic_e >= '1' && tt_user[i].mic_e <= '9') {
 | 
						|
	  
 | 
						|
	  if (strlen(info_comment) > 0) {
 | 
						|
	    strlcat (info_comment, " ", sizeof(info_comment));
 | 
						|
	  }
 | 
						|
 | 
						|
	  // Insert "/" if status does not already begin with it.
 | 
						|
	  if (save_tt_config_p->status[tt_user[i].mic_e - '0'][0] != '/') {
 | 
						|
	    strlcat (info_comment, "/", sizeof(info_comment));
 | 
						|
	  }
 | 
						|
	  strlcat (info_comment, save_tt_config_p->status[tt_user[i].mic_e - '0'], sizeof(info_comment));
 | 
						|
	}
 | 
						|
 | 
						|
	if (strlen(tt_user[i].dao) > 0) {
 | 
						|
	  if (strlen(info_comment) > 0) {
 | 
						|
	    strlcat (info_comment, " ", sizeof(info_comment));
 | 
						|
	  }
 | 
						|
	  strlcat (info_comment, tt_user[i].dao, sizeof(info_comment));
 | 
						|
	}
 | 
						|
 | 
						|
	/* Official limit is 43 characters. */
 | 
						|
	//info_comment[MAX_COMMENT_LEN] = '\0';
 | 
						|
	
 | 
						|
/*
 | 
						|
 * Packet header is built from mycall (of transmit channel) and software version.
 | 
						|
 */
 | 
						|
 | 
						|
	if (save_tt_config_p->obj_xmit_chan >= 0) {
 | 
						|
	  strlcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_xmit_chan].mycall, sizeof(stemp));
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	  strlcpy (stemp, save_audio_config_p->achan[save_tt_config_p->obj_recv_chan].mycall, sizeof(stemp));
 | 
						|
	}
 | 
						|
	strlcat (stemp, ">", sizeof(stemp));
 | 
						|
	strlcat (stemp, APP_TOCALL, sizeof(stemp));
 | 
						|
	c4[0] = '0' + MAJOR_VERSION;
 | 
						|
	c4[1] = '0' + MINOR_VERSION;
 | 
						|
	c4[2] = '\0';
 | 
						|
	strlcat (stemp, c4, sizeof(stemp));
 | 
						|
 | 
						|
/*
 | 
						|
 * Append via path, for transmission, if specified. 
 | 
						|
 */
 | 
						|
 | 
						|
	if ( ! first_time && save_tt_config_p->obj_xmit_via[0] != '\0') {
 | 
						|
	  strlcat (stemp, ",", sizeof(stemp));
 | 
						|
	  strlcat (stemp, save_tt_config_p->obj_xmit_via, sizeof(stemp));
 | 
						|
	}
 | 
						|
 | 
						|
	strlcat (stemp, ":", sizeof(stemp));
 | 
						|
 | 
						|
	encode_object (object_name, 0, tt_user[i].last_heard, olat, olong, oambig,
 | 
						|
		tt_user[i].overlay, tt_user[i].symbol, 
 | 
						|
		0,0,0,NULL, G_UNKNOWN, G_UNKNOWN,	/* PHGD, Course/Speed */
 | 
						|
		strlen(tt_user[i].freq) > 0 ? atof(tt_user[i].freq) : G_UNKNOWN,
 | 
						|
		strlen(tt_user[i].ctcss) > 0 ? atof(tt_user[i].ctcss) : G_UNKNOWN,
 | 
						|
		G_UNKNOWN,	/* CTCSS */
 | 
						|
		info_comment, object_info, sizeof(object_info));
 | 
						|
 | 
						|
	strlcat (stemp, object_info, sizeof(stemp));
 | 
						|
 | 
						|
#if TT_MAIN
 | 
						|
 | 
						|
	printf ("---> %s\n\n", stemp);
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
	if (first_time) {
 | 
						|
	  text_color_set(DW_COLOR_DEBUG);
 | 
						|
	  dw_printf ("[APRStt] %s\n", stemp);
 | 
						|
	}
 | 
						|
 | 
						|
/*
 | 
						|
 * Convert text to packet.
 | 
						|
 */
 | 
						|
	pp = ax25_from_text (stemp, 1);
 | 
						|
 | 
						|
	if (pp == NULL) {
 | 
						|
	  text_color_set(DW_COLOR_ERROR);
 | 
						|
	  dw_printf ("\"%s\"\n", stemp);
 | 
						|
	  return;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
/* 
 | 
						|
 * Send to one or more of the following depending on configuration:
 | 
						|
 *	Transmit queue.
 | 
						|
 *	Any attached application(s).
 | 
						|
 * 	IGate.
 | 
						|
 *
 | 
						|
 * When transmitting over the radio, it gets sent multipe times, to help
 | 
						|
 * probablity of being heard, with increasing delays between.
 | 
						|
 *
 | 
						|
 * The other methods are reliable so we only want to send it once.
 | 
						|
 */
 | 
						|
 | 
						|
	if (first_time && save_tt_config_p->obj_send_to_app)  {
 | 
						|
	  unsigned char fbuf[AX25_MAX_PACKET_LEN];
 | 
						|
	  int flen;
 | 
						|
 | 
						|
 	  // TODO1.3:  Put a wrapper around this so we only call one function to send by all methods.
 | 
						|
	  // We see the same sequence in direwolf.c.
 | 
						|
 | 
						|
	  flen = ax25_pack(pp, fbuf);
 | 
						|
 | 
						|
	  server_send_rec_packet (save_tt_config_p->obj_recv_chan, pp, fbuf, flen);
 | 
						|
	  kissnet_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, NULL, -1);
 | 
						|
	  kissserial_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, NULL, -1);
 | 
						|
	  kisspt_send_rec_packet (save_tt_config_p->obj_recv_chan, KISS_CMD_DATA_FRAME, fbuf, flen, NULL, -1);
 | 
						|
	}
 | 
						|
 | 
						|
	if (first_time && save_tt_config_p->obj_send_to_ig)  {
 | 
						|
 | 
						|
	  //text_color_set(DW_COLOR_DEBUG);
 | 
						|
	  //dw_printf ("xmit_object_report (): send to IGate\n");
 | 
						|
 | 
						|
          igate_send_rec_packet (save_tt_config_p->obj_recv_chan, pp);
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	if ( ! first_time && save_tt_config_p->obj_xmit_chan >= 0) {
 | 
						|
 | 
						|
	  /* Remember it so we don't digipeat our own. */
 | 
						|
 | 
						|
	  dedupe_remember (pp, save_tt_config_p->obj_xmit_chan);
 | 
						|
 | 
						|
	  tq_append (save_tt_config_p->obj_xmit_chan, TQ_PRIO_1_LO, pp);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	  ax25_delete (pp);
 | 
						|
	}
 | 
						|
 | 
						|
#endif 
 | 
						|
	
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static const char *letters[26] = {
 | 
						|
        "Alpha",
 | 
						|
        "Bravo",
 | 
						|
        "Charlie",
 | 
						|
        "Delta",
 | 
						|
        "Echo",
 | 
						|
        "Foxtrot",
 | 
						|
        "Golf",
 | 
						|
        "Hotel",
 | 
						|
        "India",
 | 
						|
        "Juliet",
 | 
						|
        "Kilo",
 | 
						|
        "Lima",
 | 
						|
        "Mike",
 | 
						|
        "November",
 | 
						|
        "Oscar",
 | 
						|
        "Papa",
 | 
						|
        "Quebec",
 | 
						|
        "Romeo",
 | 
						|
        "Sierra",
 | 
						|
        "Tango",
 | 
						|
        "Uniform",
 | 
						|
        "Victor",
 | 
						|
        "Whiskey",
 | 
						|
        "X-ray",
 | 
						|
        "Yankee",
 | 
						|
        "Zulu"
 | 
						|
};
 | 
						|
 | 
						|
static const char *digits[10] = {
 | 
						|
	"Zero",
 | 
						|
	"One",
 | 
						|
	"Two",
 | 
						|
	"Three",
 | 
						|
	"Four",
 | 
						|
	"Five",
 | 
						|
	"Six",
 | 
						|
	"Seven",
 | 
						|
	"Eight",
 | 
						|
	"Nine"		
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * Name:        tt_setenv
 | 
						|
 *
 | 
						|
 * Purpose:     Put information in environment variables in preparation
 | 
						|
 *		for calling a user-supplied script for custom processing.
 | 
						|
 *
 | 
						|
 * Inputs:      i	- Index into tt_user table.
 | 
						|
 *
 | 
						|
 * Description:	Timestamps displayed relative to current time.
 | 
						|
 *
 | 
						|
 *----------------------------------------------------------------*/
 | 
						|
 | 
						|
 | 
						|
static void tt_setenv (int i)
 | 
						|
{
 | 
						|
	char stemp[256];
 | 
						|
	char t2[2];
 | 
						|
	char *p;
 | 
						|
 | 
						|
	assert (i >= 0 && i < MAX_TT_USERS);
 | 
						|
 | 
						|
	setenv ("TTCALL", tt_user[i].callsign, 1);
 | 
						|
 | 
						|
	strlcpy (stemp, "", sizeof(stemp));
 | 
						|
	t2[1] = '\0';
 | 
						|
	for (p = tt_user[i].callsign; *p != '\0'; p++) {
 | 
						|
	  t2[0] = *p;
 | 
						|
	  strlcat (stemp, t2, sizeof(stemp));
 | 
						|
	  if (p[1] != '\0') strlcat (stemp, " ", sizeof(stemp));
 | 
						|
	}
 | 
						|
	setenv ("TTCALLSP", stemp, 1);
 | 
						|
 | 
						|
	strlcpy (stemp, "", sizeof(stemp));
 | 
						|
	for (p = tt_user[i].callsign; *p != '\0'; p++) {
 | 
						|
	  if (isupper(*p)) {
 | 
						|
	    strlcat (stemp, letters[*p - 'A'], sizeof(stemp));
 | 
						|
	  }
 | 
						|
	  else if (islower(*p)) {
 | 
						|
	    strlcat (stemp, letters[*p - 'a'], sizeof(stemp));
 | 
						|
	  }
 | 
						|
	  else if (isdigit(*p)) {
 | 
						|
	    strlcat (stemp, digits[*p - '0'], sizeof(stemp));
 | 
						|
	  }
 | 
						|
	  else {
 | 
						|
	    t2[0] = *p;
 | 
						|
	    strlcat (stemp, t2, sizeof(stemp));
 | 
						|
	  }
 | 
						|
	  if (p[1] != '\0') strlcat (stemp, " ", sizeof(stemp));
 | 
						|
	}
 | 
						|
	setenv ("TTCALLPH", stemp, 1);
 | 
						|
 | 
						|
	snprintf (stemp, sizeof(stemp), "%d", tt_user[i].ssid);
 | 
						|
	setenv ("TTSSID",stemp , 1);
 | 
						|
 | 
						|
	snprintf (stemp, sizeof(stemp), "%d", tt_user[i].count);
 | 
						|
	setenv ("TTCOUNT",stemp , 1);
 | 
						|
 | 
						|
	snprintf (stemp, sizeof(stemp), "%c%c", tt_user[i].overlay, tt_user[i].symbol);
 | 
						|
	setenv ("TTSYMBOL",stemp , 1);
 | 
						|
 | 
						|
	snprintf (stemp, sizeof(stemp), "%.6f", tt_user[i].latitude);
 | 
						|
	setenv ("TTLAT",stemp , 1);
 | 
						|
 | 
						|
	snprintf (stemp, sizeof(stemp), "%.6f", tt_user[i].longitude);
 | 
						|
	setenv ("TTLON",stemp , 1);
 | 
						|
 | 
						|
	setenv ("TTFREQ", tt_user[i].freq, 1);
 | 
						|
 | 
						|
	// TODO: Should convert to actual frequency. e.g.  074 becomes 74.4
 | 
						|
	// There is some code for this in decode_aprs.c but not broken out
 | 
						|
	// into a function that we could use from here.
 | 
						|
	// TODO: Document this environment variable after converting.
 | 
						|
 | 
						|
	setenv ("TTCTCSS", tt_user[i].ctcss, 1);
 | 
						|
 | 
						|
	setenv ("TTCOMMENT", tt_user[i].comment, 1);
 | 
						|
 | 
						|
	setenv ("TTLOC", tt_user[i].loc_text, 1);
 | 
						|
 | 
						|
	if (tt_user[i].mic_e >= '1' && tt_user[i].mic_e <= '9') {
 | 
						|
	  setenv ("TTSTATUS", save_tt_config_p->status[tt_user[i].mic_e - '0'], 1);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	  setenv ("TTSTATUS", "", 1);
 | 
						|
	}
 | 
						|
 | 
						|
	setenv ("TTDAO", tt_user[i].dao, 1);
 | 
						|
 | 
						|
} /* end tt_setenv */
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * 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     ctcss m comment\n");
 | 
						|
	for (i=0; i<MAX_TT_USERS; i++) {
 | 
						|
	  if (tt_user[i].callsign[0] != '\0') {
 | 
						|
	    printf ("%-6s %c%c %-3s %6d %d %+6d %d %6.2f %7.2f %-10s %-3s %c %s\n",
 | 
						|
	    	tt_user[i].callsign,
 | 
						|
	    	tt_user[i].overlay,
 | 
						|
	    	tt_user[i].symbol,
 | 
						|
	    	tt_user[i].digit_suffix,
 | 
						|
	    	(int)(tt_user[i].last_heard - now),
 | 
						|
	    	tt_user[i].xmits,
 | 
						|
	    	(int)(tt_user[i].next_xmit - now),
 | 
						|
	    	tt_user[i].corral_slot,
 | 
						|
	    	tt_user[i].latitude,
 | 
						|
	    	tt_user[i].longitude,
 | 
						|
	    	tt_user[i].freq,
 | 
						|
		tt_user[i].ctcss,
 | 
						|
	    	tt_user[i].mic_e,
 | 
						|
	    	tt_user[i].comment);
 | 
						|
	  }
 | 
						|
	}
 | 
						|
			
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * Name:        main
 | 
						|
 *
 | 
						|
 * Purpose:     Quick test for some functions in this file.
 | 
						|
 *
 | 
						|
 * Description:	Just a smattering, not an organized test.
 | 
						|
 *
 | 
						|
 * 		$ rm a.exe ; gcc -DTT_MAIN -Iregex tt_user.c tt_text.c encode_aprs.c latlong.c textcolor.c misc.a ; ./a.exe
 | 
						|
 *
 | 
						|
 *----------------------------------------------------------------*/
 | 
						|
 | 
						|
 | 
						|
#if TT_MAIN
 | 
						|
 | 
						|
 | 
						|
static struct audio_s my_audio_config;
 | 
						|
 | 
						|
static struct tt_config_s my_tt_config;
 | 
						|
 | 
						|
 | 
						|
int main (int argc, char *argv[])
 | 
						|
{
 | 
						|
	int n;
 | 
						|
 | 
						|
/* Fake audio config - All we care about is mycall for constructing object report packet. */
 | 
						|
 | 
						|
	memset (&my_audio_config, 0, sizeof(my_audio_config));
 | 
						|
 | 
						|
	strlcpy (my_audio_config.achan[0].mycall, "WB2OSZ-15", sizeof(my_audio_config.achan[0].mycall));
 | 
						|
 | 
						|
/* Fake TT gateway config. */
 | 
						|
 | 
						|
	memset (&my_tt_config, 0, sizeof(my_tt_config));	
 | 
						|
 | 
						|
	/* Don't care about the location translation here. */
 | 
						|
 | 
						|
	my_tt_config.retain_time = 20;		/* Normally 80 minutes. */
 | 
						|
	my_tt_config.num_xmits = 3;
 | 
						|
	assert (my_tt_config.num_xmits <= TT_MAX_XMITS);
 | 
						|
	my_tt_config.xmit_delay[0] = 3;		/* Before initial transmission. */
 | 
						|
	my_tt_config.xmit_delay[1] = 5;
 | 
						|
	my_tt_config.xmit_delay[2] = 5;
 | 
						|
 | 
						|
	my_tt_config.corral_lat = 42.61900;
 | 
						|
	my_tt_config.corral_lon = -71.34717;
 | 
						|
	my_tt_config.corral_offset = 0.02 / 60;
 | 
						|
	my_tt_config.corral_ambiguity = 0;
 | 
						|
 | 
						|
 | 
						|
	tt_user_init(&my_audio_config, &my_tt_config);
 | 
						|
 | 
						|
// tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude,
 | 
						|
//              double longitude, int ambiguity, char *freq, char *ctcss, char *comment, char mic_e, char *dao);
 | 
						|
 | 
						|
	tt_user_heard ("TEST1",  12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
 | 
						|
	SLEEP_SEC (1);
 | 
						|
	tt_user_heard ("TEST2",  12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
 | 
						|
	SLEEP_SEC (1);
 | 
						|
	tt_user_heard ("TEST3",  12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
 | 
						|
	SLEEP_SEC (1);
 | 
						|
	tt_user_heard ("TEST4",  12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
 | 
						|
	SLEEP_SEC (1);
 | 
						|
	tt_user_heard ("WB2OSZ", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
 | 
						|
	tt_user_heard ("K2H",    12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "", ' ', "!T99!");
 | 
						|
	tt_user_dump ();
 | 
						|
 | 
						|
	tt_user_heard ("679",    12, 'J', 'A', "", 37.25,     -71.75,    0, "", " ", " ", ' ', "!T99!");
 | 
						|
	tt_user_heard ("WB2OSZ", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "146.520MHz", "", "", ' ', "!T99!");
 | 
						|
	tt_user_heard ("WB1GOF", 12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "146.955MHz", "074", "", ' ', "!T99!");
 | 
						|
	tt_user_heard ("679",    12, 'J', 'A', "", G_UNKNOWN, G_UNKNOWN, 0, "", "", "Hello, world", '9', "!T99!");
 | 
						|
	tt_user_dump ();
 | 
						|
	
 | 
						|
	for (n=0; n<30; n++) {
 | 
						|
	  SLEEP_SEC(1);
 | 
						|
	  tt_user_background ();
 | 
						|
	}
 | 
						|
 | 
						|
	return(0);
 | 
						|
 | 
						|
}  /* end main */
 | 
						|
 | 
						|
#endif		/* unit test */
 | 
						|
 | 
						|
 | 
						|
/* end tt-user.c */
 | 
						|
 |