// // 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 . // /*------------------------------------------------------------------ * * 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 #include #include #include #include #include #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: * * 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 */