-
Notifications
You must be signed in to change notification settings - Fork 314
/
Copy pathmheard.c
331 lines (272 loc) · 9.83 KB
/
mheard.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
//
// 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 */