2016-07-03 22:09:34 +00:00
//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2016 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/>.
//
/*------------------------------------------------------------------
*
* File : mheard . c
*
* Purpose : Maintain a list of all stations heard .
*
2017-01-01 16:49:55 +00:00
* Description : This was added for IGate statistics and checking if a user is local
* but would also be useful for the AGW network protocol ' H ' request .
2016-07-03 22:09:34 +00:00
*
* This application has no GUI and is not interactive so
* I ' m not sure what else we might do with the information .
*
2017-01-01 16:49:55 +00:00
* Why mheard instead of just heard ? The KPC - 3 + has an MHEARD command
* to list stations heard . I guess that stuck in my mind .
* It should be noted that here " heard " refers to the AX .25 source station .
* Before printing the received packet , the " heard " line refers to who
* we heard over the radio . This would be the digipeater with " * " after
* its name .
*
2016-07-03 22:09:34 +00:00
* Future Ideas : Someone suggested using SQLite to store the information
* so other applications could access it .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# include "direwolf.h"
# include <stdio.h>
# include <time.h>
# include <assert.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include "textcolor.h"
# include "decode_aprs.h"
# include "ax25_pad.h"
# include "hdlc_rec2.h" // for retry_t
# include "mheard.h"
2017-01-01 16:49:55 +00:00
# include "latlong.h"
// This is getting updated from two different threads so we need a critical region
// for adding new nodes.
static dw_mutex_t mheard_mutex ;
2016-07-03 22:09:34 +00:00
2017-01-01 16:49:55 +00:00
// I think we can get away without a critical region for reading if we follow these
// rules:
2016-07-03 22:09:34 +00:00
//
2017-01-01 16:49:55 +00:00
// (1) When adding a new node, make sure it is complete, including next ptr,
2016-07-03 22:09:34 +00:00
// before adding it to the list.
2017-01-01 16:49:55 +00:00
// (2) Update the start of list pointer last.
// (2) Nothing gets deleted.
// If we ever decide to start cleaning out very old data, all access would then
// need to use the mutex.
2016-07-03 22:09:34 +00:00
/*
2017-01-01 16:49:55 +00:00
* Information for each station heard over the radio or from Internet Server .
2016-07-03 22:09:34 +00:00
*/
typedef struct mheard_s {
struct mheard_s * pnext ; // Pointer to next in list.
char callsign [ AX25_MAX_ADDR_LEN ] ; // Callsign from the AX.25 source field.
2017-01-01 16:49:55 +00:00
int count ; // Number of times heard.
// We don't use this for anything.
// Just something potentially interesting when looking at data dump.
int chan ; // Most recent channel where heard.
2016-07-03 22:09:34 +00:00
int num_digi_hops ; // Number of digipeater hops before we heard it.
2017-01-01 16:49:55 +00:00
// over radio. Zero when heard directly.
2016-07-03 22:09:34 +00:00
2017-01-01 16:49:55 +00:00
time_t last_heard_rf ; // Timestamp when last heard over the radio.
time_t last_heard_is ; // Timestamp when last heard from Internet Server.
double dlat , dlon ; // Last position. G_UNKNOWN for unknown.
int msp ; // Allow message sender positon report.
// When non zero, an IS>RF position report is allowed.
// Then decremented.
2016-07-03 22:09:34 +00:00
// What else would be useful?
// The AGW protocol is by channel and returns
// first heard in addition to last heard.
} mheard_t ;
2017-01-01 16:49:55 +00:00
2016-07-03 22:09:34 +00:00
/*
* The list could be quite long and we hit this a lot so use a hash table .
*/
# define MHEARD_HASH_SIZE 73 // Best if prime number.
static mheard_t * mheard_hash [ MHEARD_HASH_SIZE ] ;
static inline int hash_index ( char * callsign ) {
int n = 0 ;
char * p = callsign ;
while ( * p ! = ' \0 ' ) {
n + = * p + + ;
}
return ( n % MHEARD_HASH_SIZE ) ;
}
static mheard_t * mheard_ptr ( char * callsign ) {
int n = hash_index ( callsign ) ;
mheard_t * p = mheard_hash [ n ] ;
while ( p ! = NULL ) {
if ( strcmp ( callsign , p - > callsign ) = = 0 ) return ( p ) ;
p = p - > pnext ;
}
return ( NULL ) ;
}
2017-01-01 16:49:55 +00:00
static int mheard_debug = 0 ;
2016-07-03 22:09:34 +00:00
/*------------------------------------------------------------------
*
* Function : mheard_init
*
* Purpose : Initialization at start of application .
*
* Inputs : debug - Debug level .
*
* Description : Clear pointer table .
* Save debug level for later use .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void mheard_init ( int debug )
{
int i ;
mheard_debug = debug ;
for ( i = 0 ; i < MHEARD_HASH_SIZE ; i + + ) {
mheard_hash [ i ] = NULL ;
}
2017-01-01 16:49:55 +00:00
/*
* Mutex to coordinate adding new nodes .
*/
dw_mutex_init ( & mheard_mutex ) ;
2016-07-03 22:09:34 +00:00
} /* end mheard_init */
/*------------------------------------------------------------------
*
2017-01-01 16:49:55 +00:00
* Function : mheard_dump
2016-07-03 22:09:34 +00:00
*
2017-01-01 16:49:55 +00:00
* Purpose : Print list of stations heard for debugging .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* convert some time in past to hours:minutes text format. */
static void age ( char * result , time_t now , time_t t )
{
int s , h , m ;
if ( t = = 0 ) {
strcpy ( result , " - " ) ;
return ;
}
s = ( int ) ( now - t ) ;
m = s / 60 ;
h = m / 60 ;
m - = h * 60 ;
sprintf ( result , " %4d:%02d " , h , m ) ;
}
/* Convert latitude, longitude to text or - if not defined. */
static void latlon ( char * result , double dlat , double dlon )
{
if ( dlat ! = G_UNKNOWN & & dlon ! = G_UNKNOWN ) {
sprintf ( result , " %6.2f %7.2f " , dlat , dlon ) ;
}
else {
strcpy ( result , " - - " ) ;
}
}
/* Compare last heard time for use with qsort. */
# define MAXX(x,y) (((x)>(y))?(x):(y))
static int compar ( const void * a , const void * b )
{
mheard_t * ma = * ( ( mheard_t * * ) a ) ;
mheard_t * mb = * ( ( mheard_t * * ) b ) ;
time_t ta = MAXX ( ma - > last_heard_rf , ma - > last_heard_is ) ;
time_t tb = MAXX ( mb - > last_heard_rf , mb - > last_heard_is ) ;
return ( tb - ta ) ;
}
# define MAXDUMP 1000
static void mheard_dump ( void )
{
int i ;
mheard_t * mptr ;
time_t now = time ( NULL ) ;
char stuff [ 80 ] ;
char rf [ 16 ] ; // hours:minutes
char is [ 16 ] ;
char position [ 40 ] ;
mheard_t * station [ MAXDUMP ] ;
int num_stations = 0 ;
/* Get linear array of node pointers so they can be sorted easily. */
num_stations = 0 ;
for ( i = 0 ; i < MHEARD_HASH_SIZE ; i + + ) {
for ( mptr = mheard_hash [ i ] ; mptr ! = NULL ; mptr = mptr - > pnext ) {
if ( num_stations < MAXDUMP ) {
station [ num_stations ] = mptr ;
num_stations + + ;
}
else {
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " mheard_dump - max number of stations exceeded. \n " ) ;
}
}
}
/* Sort most recently heard to the top then print. */
qsort ( station , num_stations , sizeof ( mheard_t * ) , compar ) ;
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " callsign cnt chan hops RF IS lat long msp \n " ) ;
for ( i = 0 ; i < num_stations ; i + + ) {
mptr = station [ i ] ;
age ( rf , now , mptr - > last_heard_rf ) ;
age ( is , now , mptr - > last_heard_is ) ;
latlon ( position , mptr - > dlat , mptr - > dlon ) ;
snprintf ( stuff , sizeof ( stuff ) , " %-9s %3d %d %d %7s %7s %s %d \n " ,
mptr - > callsign , mptr - > count , mptr - > chan , mptr - > num_digi_hops , rf , is , position , mptr - > msp ) ;
dw_printf ( " %s " , stuff ) ;
}
} /* end mheard_dump */
/*------------------------------------------------------------------
*
* Function : mheard_save_rf
*
* Purpose : Save information about station heard over the radio .
2016-07-03 22:09:34 +00:00
*
* Inputs : chan - Radio channel where heard .
*
* A - Exploded information from APRS packet .
*
* pp - Received packet object .
*
* alevel - audio level .
*
* retries - Amount of effort to get a good CRC .
*
* Description : Calling sequence was copied from " log_write. "
* It has a lot more than what we currently keep but the
* hooks are there so it will be easy to capture additional
* information when the need arises .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2017-01-01 16:49:55 +00:00
void mheard_save_rf ( int chan , decode_aprs_t * A , packet_t pp , alevel_t alevel , retry_t retries )
2016-07-03 22:09:34 +00:00
{
time_t now = time ( NULL ) ;
char source [ AX25_MAX_ADDR_LEN ] ;
int hops ;
mheard_t * mptr ;
ax25_get_addr_with_ssid ( pp , AX25_SOURCE , source ) ;
/*
* How many digipeaters has it gone thru before we hear it ?
* We can count the number of digi addresses that are marked as " has been used. "
* This is not always accurate because there is inconsistency in digipeater behavior .
* The base AX .25 spec seems clear in this regard . The used digipeaters should
* should accurately reflict the path taken by the packet . Sometimes we see excess
* stuff in there . Even when you understand what is going on , it is still an ambiguous
* situation . Look for my rant in the User Guide .
*/
hops = ax25_get_heard ( pp ) - AX25_SOURCE ;
mptr = mheard_ptr ( source ) ;
if ( mptr = = NULL ) {
int i ;
/*
* Not heard before . Add it .
*/
if ( mheard_debug ) {
text_color_set ( DW_COLOR_DEBUG ) ;
2017-01-01 16:49:55 +00:00
dw_printf ( " mheard_save_rf: %s %d - added new \n " , source , hops ) ;
2016-07-03 22:09:34 +00:00
}
mptr = calloc ( sizeof ( mheard_t ) , 1 ) ;
strlcpy ( mptr - > callsign , source , sizeof ( mptr - > callsign ) ) ;
2017-01-01 16:49:55 +00:00
mptr - > count = 1 ;
mptr - > chan = chan ;
2016-07-03 22:09:34 +00:00
mptr - > num_digi_hops = hops ;
2017-01-01 16:49:55 +00:00
mptr - > last_heard_rf = now ;
mptr - > dlat = G_UNKNOWN ;
mptr - > dlon = G_UNKNOWN ;
2016-07-03 22:09:34 +00:00
i = hash_index ( source ) ;
2017-01-01 16:49:55 +00:00
dw_mutex_lock ( & mheard_mutex ) ;
2016-07-03 22:09:34 +00:00
mptr - > pnext = mheard_hash [ i ] ; // before inserting into list.
mheard_hash [ i ] = mptr ;
2017-01-01 16:49:55 +00:00
dw_mutex_unlock ( & mheard_mutex ) ;
2016-07-03 22:09:34 +00:00
}
else {
/*
* Update existing entry .
* The only tricky part here is that we might hear the same transmission
* several times . First direct , then thru various digipeater paths .
* We are interested in the shortest path if heard very recently .
*/
2017-01-01 16:49:55 +00:00
if ( hops > mptr - > num_digi_hops & & ( int ) ( now - mptr - > last_heard_rf ) < 15 ) {
2016-07-03 22:09:34 +00:00
if ( mheard_debug ) {
text_color_set ( DW_COLOR_DEBUG ) ;
2017-01-01 16:49:55 +00:00
dw_printf ( " mheard_save_rf: %s %d - skip because hops was %d %d seconds ago. \n " , source , hops , mptr - > num_digi_hops , ( int ) ( now - mptr - > last_heard_rf ) ) ;
2016-07-03 22:09:34 +00:00
}
}
else {
if ( mheard_debug ) {
text_color_set ( DW_COLOR_DEBUG ) ;
2017-01-01 16:49:55 +00:00
dw_printf ( " mheard_save_rf: %s %d - update time, was %d hops %d seconds ago. \n " , source , hops , mptr - > num_digi_hops , ( int ) ( now - mptr - > last_heard_rf ) ) ;
2016-07-03 22:09:34 +00:00
}
2017-01-01 16:49:55 +00:00
mptr - > count + + ;
mptr - > chan = chan ;
2016-07-03 22:09:34 +00:00
mptr - > num_digi_hops = hops ;
2017-01-01 16:49:55 +00:00
mptr - > last_heard_rf = now ;
}
}
if ( A - > g_lat ! = G_UNKNOWN & & A - > g_lon ! = G_UNKNOWN ) {
mptr - > dlat = A - > g_lat ;
mptr - > dlon = A - > g_lon ;
}
if ( mheard_debug > = 2 ) {
int limit = 10 ; // normally 30 or 60. more frequent when debugging.
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " mheard debug, %d min, DIR_CNT=%d,LOC_CNT=%d,RF_CNT=%d \n " , limit , mheard_count ( 0 , limit ) , mheard_count ( 2 , limit ) , mheard_count ( 8 , limit ) ) ;
}
if ( mheard_debug ) {
mheard_dump ( ) ;
}
} /* end mheard_save_rf */
/*------------------------------------------------------------------
*
* Function : mheard_save_is
*
* Purpose : Save information about station heard via Internet Server .
*
* Inputs : ptext - Packet in monitoring text form as sent by the Internet server .
*
* Any trailing CRLF should have been removed .
* Typical examples :
*
* KA1BTK - 5 > APDR13 , TCPIP * , qAC , T2IRELAND : = 4237.62 N / 07040.68 W $ / A = - 00054 http : //aprsdroid.org/
* N1HKO - 10 > APJI40 , TCPIP * , qAC , N1HKO - JS : < IGATE , MSG_CNT = 0 , LOC_CNT = 0
* K1RI - 2 > APWW10 , WIDE1 - 1 , WIDE2 - 1 , qAS , K1RI : / 221700 h / 9 AmA < Ct3_ sT010 / 002 g005t045r000p023P020h97b10148
* KC1BOS - 2 > T3PQ3S , WIDE1 - 1 , WIDE2 - 1 , qAR , W1TG - 1 : ` c ) @ qh \ > / " 50}TinyTrak4 Mobile
*
* Notice how the final address in the header might not
* be a valid AX .25 address . We see a 9 character address
* ( with no ssid ) and an ssid of two letters .
*
* The " q construct " ( http : //www.aprs-is.net/q.aspx ) provides
* a clue about the journey taken but I don ' t think we care here .
*
* All we should care about here is the the source address .
*
* Description :
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void mheard_save_is ( char * ptext )
{
packet_t pp ;
time_t now = time ( NULL ) ;
char source [ AX25_MAX_ADDR_LEN ] ;
mheard_t * mptr ;
/*
* Try to parse it into a packet object .
* This will contain " q constructs " and we might see an address
* with two alphnumeric characters in the SSID so we must use
* the non - strict parsing .
*
* Bug : Up to 8 digipeaters are allowed in radio format .
* There is a potential of finding a larger number here .
*/
pp = ax25_from_text ( ptext , 0 ) ;
if ( pp = = NULL ) {
if ( mheard_debug ) {
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " mheard_save_is: Could not parse message from server. \n " ) ;
dw_printf ( " %s \n " , ptext ) ;
}
return ;
}
ax25_get_addr_with_ssid ( pp , AX25_SOURCE , source ) ;
mptr = mheard_ptr ( source ) ;
if ( mptr = = NULL ) {
int i ;
/*
* Not heard before . Add it .
*/
if ( mheard_debug ) {
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " mheard_save_is: %s - added new \n " , source ) ;
2016-07-03 22:09:34 +00:00
}
2017-01-01 16:49:55 +00:00
mptr = calloc ( sizeof ( mheard_t ) , 1 ) ;
strlcpy ( mptr - > callsign , source , sizeof ( mptr - > callsign ) ) ;
mptr - > count = 1 ;
mptr - > last_heard_is = now ;
mptr - > dlat = G_UNKNOWN ;
mptr - > dlon = G_UNKNOWN ;
i = hash_index ( source ) ;
dw_mutex_lock ( & mheard_mutex ) ;
mptr - > pnext = mheard_hash [ i ] ; // before inserting into list.
mheard_hash [ i ] = mptr ;
dw_mutex_unlock ( & mheard_mutex ) ;
2016-07-03 22:09:34 +00:00
}
2017-01-01 16:49:55 +00:00
else {
/* Already there. UPdate last heard from IS time. */
if ( mheard_debug ) {
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " mheard_save_is: %s - update time, was %d seconds ago. \n " , source , ( int ) ( now - mptr - > last_heard_rf ) ) ;
}
mptr - > count + + ;
mptr - > last_heard_is = now ;
}
// Is is desirable to save any location in this case?
// I don't think it would help.
// The whole purpose of keeping the location is for message sending filter.
// We wouldn't want to try sending a message to the station if we didn't hear it over the radio.
// On the other hand, I don't think it would hurt.
// The filter always includes a time since last heard over the radi.
2016-07-03 22:09:34 +00:00
if ( mheard_debug > = 2 ) {
int limit = 10 ; // normally 30 or 60
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " mheard debug, %d min, DIR_CNT=%d,LOC_CNT=%d,RF_CNT=%d \n " , limit , mheard_count ( 0 , limit ) , mheard_count ( 2 , limit ) , mheard_count ( 8 , limit ) ) ;
}
2017-01-01 16:49:55 +00:00
if ( mheard_debug ) {
mheard_dump ( ) ;
}
ax25_delete ( pp ) ;
} /* end mheard_save_is */
2016-07-03 22:09:34 +00:00
/*------------------------------------------------------------------
*
* Function : mheard_count
*
* Purpose : Count local stations for IGate statistics report like this :
*
* < IGATE , MSG_CNT = 1 , LOC_CNT = 25
*
* Inputs : max_hops - Include only stations heard with this number of
* digipeater hops or less . For reporting , we might use :
*
* 0 for DIR_CNT ( heard directly )
* IGate transmit path for LOC_CNT .
* e . g . 3 for WIDE1 - 1 , WIDE2 - 2
* 8 for RF_CNT .
*
* time_limit - Include only stations heard within this many minutes .
* Typically 30 or 60.
*
* Returns : Number to be used in the statistics report .
*
* Description : Look for discussion here : http : //www.tapr.org/pipermail/aprssig/2016-June/045837.html
*
* Lynn KJ4ERJ :
*
* For APRSISCE / 32 , " Local " is defined as those stations to which messages
* would be gated if any are received from the APRS - IS . This currently
* means unique stations heard within the past 30 minutes with at most two
* used path hops .
*
* I added DIR_CNT and RF_CNT with comma delimiters to APRSISCE / 32 ' s IGate
* status . DIR_CNT is the count of unique stations received on RF in the
* past 30 minutes with no used hops . RF_CNT is the total count of unique
* stations received on RF in the past 30 minutes .
*
* Steve K4HG :
*
* The number of hops defining local should match the number of hops of the
* outgoing packets from the IGate . So if the path is only WIDE , then local
* should only be stations heard direct or through one hop . From the beginning
* I was very much against on a standardization of the outgoing IGate path ,
* hams should be free to manage their local RF network in a way that works
* for them . Busy areas one hop may be best , I lived in an area where three was
* a much better choice . I avoided as much as possible prescribing anything
* that might change between locations .
*
* The intent was how many stations are there for which messages could be IGated .
* IGate software keeps an internal list of the ' local ' stations so it knows
* when to IGate a message , and this number should be the length of that list .
* Some IGates have a parameter for local timeout , 1 hour was the original default ,
* so if in an hour the IGate has not heard another local packet the station is
* dropped from the local list . Messages will no longer be IGated to that station
* and the station count would drop by one . The number should not just continue to rise .
*
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int mheard_count ( int max_hops , int time_limit )
{
time_t since = time ( NULL ) - time_limit * 60 ;
int count = 0 ;
int i ;
mheard_t * p ;
for ( i = 0 ; i < MHEARD_HASH_SIZE ; i + + ) {
for ( p = mheard_hash [ i ] ; p ! = NULL ; p = p - > pnext ) {
2017-01-01 16:49:55 +00:00
if ( p - > last_heard_rf > = since & & p - > num_digi_hops < = max_hops ) {
2016-07-03 22:09:34 +00:00
count + + ;
}
}
}
if ( mheard_debug = = 1 ) {
text_color_set ( DW_COLOR_DEBUG ) ;
dw_printf ( " mheard_count(<= %d digi hops, last %d minutes) returns %d \n " , max_hops , time_limit , count ) ;
}
return ( count ) ;
} /* end mheard_count */
2017-01-01 16:49:55 +00:00
/*------------------------------------------------------------------
*
* Function : mheard_was_recently_nearby
*
* Purpose : Determine whether given station was heard recently on the radio .
*
* Inputs : role - " addressee " or " source " if debug out is desired .
* Otherwise empty string .
*
* callsign - Callsign for station .
*
* time_limit - Include only stations heard within this many minutes .
* Typically 30 or 60.
*
* max_hops - Include only stations heard with this number of
* digipeater hops or less . For reporting , we might use :
*
* dlat , dlon , km - Include only stations within distance of location .
* Not used if G_UNKNOWN is supplied .
*
* Returns : 1 for true , 0 for false .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int mheard_was_recently_nearby ( char * role , char * callsign , int time_limit , int max_hops , double dlat , double dlon , double km )
{
mheard_t * mptr ;
time_t now ;
int heard_ago ;
if ( role ! = NULL & & strlen ( role ) > 0 ) {
text_color_set ( DW_COLOR_INFO ) ;
if ( dlat ! = G_UNKNOWN & & dlon ! = G_UNKNOWN & & km ! = G_UNKNOWN ) {
dw_printf ( " Was message %s %s heard in the past %d minutes, with %d or fewer digipeater hops, and within %.1f km of %.2f %.2f? \n " , role , callsign , time_limit , max_hops , km , dlat , dlon ) ;
}
else {
dw_printf ( " Was message %s %s heard in the past %d minutes, with %d or fewer digipeater hops? \n " , role , callsign , time_limit , max_hops ) ;
}
}
mptr = mheard_ptr ( callsign ) ;
if ( mptr = = NULL | | mptr - > last_heard_rf = = 0 ) {
if ( role ! = NULL & & strlen ( role ) > 0 ) {
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " No, we have not heard %s over the radio. \n " , callsign ) ;
}
return ( 0 ) ;
}
now = time ( NULL ) ;
heard_ago = ( int ) ( now - mptr - > last_heard_rf ) / 60 ;
if ( heard_ago > time_limit ) {
if ( role ! = NULL & & strlen ( role ) > 0 ) {
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " No, %s was last heard over the radio %d minutes ago with %d digipeater hops. \n " , callsign , heard_ago , mptr - > num_digi_hops ) ;
}
return ( 0 ) ;
}
if ( mptr - > num_digi_hops > max_hops ) {
if ( role ! = NULL & & strlen ( role ) > 0 ) {
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " No, %s was last heard over the radio with %d digipeater hops %d minutes ago. \n " , callsign , mptr - > num_digi_hops , heard_ago ) ;
}
return ( 0 ) ;
}
// Apply physical distance check?
if ( dlat ! = G_UNKNOWN & & dlon ! = G_UNKNOWN & & km ! = G_UNKNOWN & & mptr - > dlat ! = G_UNKNOWN & & mptr - > dlon ! = G_UNKNOWN ) {
double dist = ll_distance_km ( mptr - > dlat , mptr - > dlon , dlat , dlon ) ;
if ( dist > km ) {
if ( role ! = NULL & & strlen ( role ) > 0 ) {
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " No, %s was %.1f km away although it was %d digipeater hops %d minutes ago. \n " , callsign , dist , mptr - > num_digi_hops , heard_ago ) ;
}
return ( 0 ) ;
}
else {
if ( role ! = NULL & & strlen ( role ) > 0 ) {
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " Yes, %s last heard over radio %d minutes ago, %d digipeater hops. Last location %.1f km away. \n " , callsign , heard_ago , mptr - > num_digi_hops , dist ) ;
}
return ( 1 ) ;
}
}
// Passed all the tests.
if ( role ! = NULL & & strlen ( role ) > 0 ) {
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " Yes, %s last heard over radio %d minutes ago, %d digipeater hops. \n " , callsign , heard_ago , mptr - > num_digi_hops ) ;
}
return ( 1 ) ;
} /* end mheard_was_recently_nearby */
/*------------------------------------------------------------------
*
* Function : mheard_set_msp
*
* Purpose : Set the " message sender position " count for specified station .
*
* Inputs : callsign - Callsign for station which sent the " message. "
*
* num - Number of position reports to allow . Typically 1.
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void mheard_set_msp ( char * callsign , int num )
{
mheard_t * mptr ;
mptr = mheard_ptr ( callsign ) ;
if ( mptr ! = NULL ) {
mptr - > msp = num ;
if ( mheard_debug ) {
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " MSP for %s set to %d \n " , callsign , num ) ;
}
}
else {
text_color_set ( DW_COLOR_ERROR ) ;
dw_printf ( " Internal error: Can't find %s to set MSP. \n " , callsign ) ;
}
} /* end mheard_set_msp */
/*------------------------------------------------------------------
*
* Function : mheard_get_msp
*
* Purpose : Get the " message sender position " count for specified station .
*
* Inputs : callsign - Callsign for station which sent the " message. "
*
* Returns : The cound for the specified station .
* 0 if not found .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int mheard_get_msp ( char * callsign )
{
mheard_t * mptr ;
mptr = mheard_ptr ( callsign ) ;
if ( mptr ! = NULL ) {
if ( mheard_debug ) {
text_color_set ( DW_COLOR_INFO ) ;
dw_printf ( " MSP for %s is %d \n " , callsign , mptr - > msp ) ;
}
return ( mptr - > msp ) ; // Should we have a time limit?
}
return ( 0 ) ;
} /* end mheard_get_msp */
2016-07-03 22:09:34 +00:00
/* end mheard.c */