mirror of https://github.com/wb2osz/direwolf.git
332 lines
9.8 KiB
C
332 lines
9.8 KiB
C
//
|
|
// 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.
|
|
*
|
|
* Description: This was added for IGate statistics but would also be
|
|
* useful for the AGW network protocol 'H' request.
|
|
*
|
|
* This application has no GUI and is not interactive so
|
|
* I'm not sure what else we might do with the information.
|
|
*
|
|
* 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"
|
|
|
|
|
|
// I think we can get away without a critical region if we follow certain rules.
|
|
//
|
|
// (1) All updates are from a single thread. Although there are multiple receive
|
|
// threads, all received packets go into a single queue for serial processing.
|
|
// (2) When adding a new node, make sure it is complete, including next ptr,
|
|
// before adding it to the list.
|
|
// (3) Nothing gets deleted.
|
|
//
|
|
// It shouldn't be a problem if the data readers are from other threads.
|
|
|
|
|
|
/*
|
|
* Information for each station heard over the radio.
|
|
*/
|
|
|
|
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.
|
|
|
|
int num_digi_hops; // Number of digipeater hops before we heard it.
|
|
// Zero when heard directly.
|
|
|
|
time_t last_heard; // Timestamp when last heard.
|
|
|
|
// What else would be useful?
|
|
// The AGW protocol is by channel and returns
|
|
// first heard in addition to last heard.
|
|
} mheard_t;
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
|
|
static int mheard_debug;
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
} /* end mheard_init */
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: mheard_save
|
|
*
|
|
* Purpose: Save information about station heard.
|
|
*
|
|
* 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.
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
void mheard_save (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries)
|
|
{
|
|
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);
|
|
dw_printf ("mheard_save: %s %d - added new\n", source, hops);
|
|
}
|
|
|
|
mptr = calloc(sizeof(mheard_t),1);
|
|
strlcpy (mptr->callsign, source, sizeof(mptr->callsign));
|
|
mptr->num_digi_hops = hops;
|
|
mptr->last_heard = now;
|
|
|
|
i = hash_index(source);
|
|
|
|
mptr->pnext = mheard_hash[i]; // before inserting into list.
|
|
mheard_hash[i] = mptr;
|
|
}
|
|
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.
|
|
*/
|
|
|
|
if (hops > mptr->num_digi_hops && (int)(now - mptr->last_heard) < 15) {
|
|
|
|
if (mheard_debug) {
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("mheard_save: %s %d - skip because hops was %d %d seconds ago.\n", source, hops, mptr->num_digi_hops, (int)(now - mptr->last_heard) );
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (mheard_debug) {
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("mheard_save: %s %d - update time, was %d hops %d seconds ago.\n", source, hops, mptr->num_digi_hops, (int)(now - mptr->last_heard));
|
|
}
|
|
|
|
mptr->num_digi_hops = hops;
|
|
mptr->last_heard = now;
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
} /* end mheard_save */
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* 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) {
|
|
if (p->last_heard >= since && p->num_digi_hops <= max_hops) {
|
|
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 */
|
|
|
|
|
|
/* end mheard.c */
|