New client side packet filter to select "messages" only to stations that have been heard nearby recently. This is now the default if no IS to RF filter is specified.

Expanded debug options so you can understand what is going on with packet filtering.

Added new document Successful-APRS-IGate-Operation.pdf with IGate background, configuration, and troubleshooting tips.
This commit is contained in:
WB2OSZ 2017-01-01 11:49:55 -05:00
parent 3516de7f6a
commit 74ac4812d5
23 changed files with 1566 additions and 143 deletions

View File

@ -2,6 +2,36 @@
# Revision History # # Revision History #
----------
## Version 1.4 -- Development snapshot G -- January 2017 ##
This is a snapshot at some semi-stable point in the development of the next version. It is not well tested. New features might be incomplete, poorly documented, and subject to change.
### New Features: ###
- New client side packet filter to select "messages" only to stations that have been heard nearby recently. This is now the default if no IS to RF filter is specified.
- Expanded debug options so you can understand what is going on with packet filtering.
- Added new document ***Successful-APRS-IGate-Operation.pdf*** with IGate background, configuration, and troubleshooting tips.
----------
## Version 1.4 -- Development snapshot F -- December 2016 ##
This is a snapshot at some semi-stable point in the development of the next version. It is not well tested. New features might be incomplete, poorly documented, and subject to change.
### Bugs Fixed: ###
- -p command line option caused segmentation fault with glibc >= 2.24.
---------- ----------

View File

@ -13,13 +13,25 @@ all : $(APPS) direwolf.desktop direwolf.conf
CC := gcc CC := gcc
# Just for fun, let's see how clang compares to gcc. First install like this:
# sudo apt-get update
# apt-cache search clang
# sudo apt-get install clang-3.5
#
# CC := clang-3.5
# _XOPEN_SOURCE=600 and _DEFAULT_SOURCE=1 are needed for glibc >= 2.24. # _XOPEN_SOURCE=600 and _DEFAULT_SOURCE=1 are needed for glibc >= 2.24.
# Explanation here: https://github.com/wb2osz/direwolf/issues/62 # Explanation here: https://github.com/wb2osz/direwolf/issues/62
# There are a few source files where it had been necessary to define __USE_XOPEN2KXSI, # There are a few source files where it had been necessary to define __USE_XOPEN2KXSI,
# __USE_XOPEN, or _POSIX_C_SOURCE. Doesn't seem to be needed after adding this. # __USE_XOPEN, or _POSIX_C_SOURCE. Doesn't seem to be needed after adding this.
CFLAGS := -O3 -pthread -Igeotranz -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE=1 CFLAGS := -O3 -pthread -Igeotranz -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE=1 -Wall
# That was fine for a recent Ubuntu and Raspbian Jessie.
# However, Raspbian wheezy was then missing declaration for strsep and definition of fd_set.
CFLAGS += -D_BSD_SOURCE
LDFLAGS := -lm -lpthread -lrt LDFLAGS := -lm -lpthread -lrt
@ -113,7 +125,26 @@ endif
# One article said it was added with gcc 4.2 but I haven't verified that. # One article said it was added with gcc 4.2 but I haven't verified that.
# #
# Add -ffastmath in only if compiler version recognizes it. # ---------- How does clang compare? - Ubuntu x86_64 ----------
#
# I keep hearing a lot about "clang." Let's see how it compares with gcc.
# Simply use different compiler and keep all options the same.
#
# test case: atest 02_Track_2.wav
#
# gcc 4.8.4: 988 packets decoded in 40.503 seconds. 38.3 x realtime
# 988 packets decoded in 40.403 seconds. 38.4 x realtime
#
# clang 3.8.0-2: 988 packets decoded in 77.454 seconds. 20.0 x realtime
# 988 packets decoded in 77.232 seconds. 20.1 x realtime
#
# I'm not impressed. Almost twice as long. Maybe we need to try some other compiler options.
# -march=native did not help.
# Makefile.macosx, which uses clang, has these:
# -fvectorize -fslp-vectorize -fslp-vectorize-aggressive
# Those did not help.
#
useffast := $(shell gcc --help -v 2>/dev/null | grep ffast-math) useffast := $(shell gcc --help -v 2>/dev/null | grep ffast-math)
ifneq ($(useffast),) ifneq ($(useffast),)
@ -146,7 +177,7 @@ endif
# #
# #
# ---------- ARM - Raspberry Pi 2 ---------- # ---------- ARM - Raspberry Pi 2 ----------
# #
# Besides the higher clock speed, the Raspberry Pi 2 has the NEON instruction set # Besides the higher clock speed, the Raspberry Pi 2 has the NEON instruction set
# which which should make things considerably faster. # which which should make things considerably faster.
@ -200,10 +231,27 @@ endif
# cause compatibility issues for those with older computers. # cause compatibility issues for those with older computers.
# #
# ---------- How does clang compare? - ARM - Raspberry Pi 2 ----------
#
# I keep hearing a lot about "clang." Let's see how it compares with gcc.
# Simply use different compiler and keep all options the same.
#
# test case: atest 02_Track_2.wav
#
# gcc 4.9.2-10: 988 packets decoded in 353.025 seconds. 4.4 x realtime
# 988 packets decoded in 352.752 seconds. 4.4 x realtime
#
# clang 3.5.0-10: 988 packets decoded in 825.879 seconds. 1.9 x realtime
# 988 packets decoded in 831.469 seconds. 1.9 x realtime
#
# There might be a different set of options which produce faster code
# but the initial test doesn't look good. About 2.3 times as slow.
# If you want to use OSS (for FreeBSD, OpenBSD) instead of # If you want to use OSS (for FreeBSD, OpenBSD) instead of
# ALSA (for Linux), comment out (or remove) the two lines below. # ALSA (for Linux), comment out (or remove) the two lines below.
# TODO: Can we automate this somehow?
CFLAGS += -DUSE_ALSA CFLAGS += -DUSE_ALSA
LDFLAGS += -lasound LDFLAGS += -lasound
@ -220,6 +268,8 @@ endif
# Uncomment following lines to enable hamlib support. # Uncomment following lines to enable hamlib support.
# TODO: automate this too. See if hamlib has been installed.
#CFLAGS += -DUSE_HAMLIB #CFLAGS += -DUSE_HAMLIB
#LDFLAGS += -lhamlib #LDFLAGS += -lhamlib

View File

@ -77,6 +77,12 @@ CC := $(DARWIN_CC) -m32 $(SYS_LIBS) $(SYS_MIN)
CFLAGS := -Os -pthread -Igeotranz -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE=1 $(EXTRA_CFLAGS) CFLAGS := -Os -pthread -Igeotranz -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE=1 $(EXTRA_CFLAGS)
# That was fine for a recent Ubuntu and Raspbian Jessie.
# However, Raspbian wheezy was then missing declaration for strsep and definition of fd_set.
CFLAGS += -D_BSD_SOURCE
# $(info $$CC is [${CC}]) # $(info $$CC is [${CC}])
# #

View File

@ -26,19 +26,30 @@ all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_pa
# -Ofast was added in gcc 4.6 which was the MinGW version back in 2012. # -Ofast was added in gcc 4.6 which was the MinGW version back in 2012.
CC := gcc CC := gcc
CFLAGS := -Wall -Ofast -march=pentium3 -msse -Iregex -Iutm -Igeotranz -mthreads -DUSE_REGEX_STATIC CFLAGS := -Ofast -march=pentium3 -msse -Iregex -Iutm -Igeotranz -mthreads -DUSE_REGEX_STATIC -Wall -Wlogical-op
#CFLAGS := -Wall -march=pentium3 -msse -Iregex -Iutm -Igeotranz -mthreads -DUSE_REGEX_STATIC
AR := ar AR := ar
CFLAGS += -g CFLAGS += -g
# For version 1.4, we upgrade from 4.6.2 to 4.9.3. # For version 1.4, we upgrade from gcc 4.6.2 to 4.9.3.
# gcc 4.8 adds these. Try them just for fun. # gcc 4.8 adds these. Try them just for fun.
# No, it needs libasan which is not on Windows. # No, it needs libasan which is not on Windows.
#CFLAGS += -fsanitize=address -fno-omit-frame-pointer #CFLAGS += -fsanitize=address -fno-omit-frame-pointer
# Helpful for the demodulators. Overkill for non-hot spots.
#CFLAGS += -Wdouble-promotion
# Don't have the patience for this right now.
#CFLAGS += -Wextra
# Continue working on these.
CFLAGS += -Wsign-compare
CFLAGS += -Wuninitialized
CFLAGS += -Wold-style-declaration
# CFLAGS += -fdelete-null-pointer-checks -Wnull-dereference ---not recognized
#CFLAGS += -Wold-style-definition
#-Wmissing-prototypes
# #
# Let's see impact of various optimization levels. # Let's see impact of various optimization levels.
@ -178,7 +189,7 @@ log2gpx : log2gpx.c textcolor.o misc.a
# Test application to generate sound. # Test application to generate sound.
gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.o textcolor.o dsp.o misc.a regex.a gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse.o dtmf.o textcolor.o dsp.o misc.a regex.a
$(CC) $(CFLAGS) -o $@ $^ $(CC) $(CFLAGS) -o $@ $^
@ -219,7 +230,7 @@ utm.o : geotranz/utm.c
# functions supplied by the gnu C library. # functions supplied by the gnu C library.
# For the native WIN32 version, we need to use our own copy. # For the native WIN32 version, we need to use our own copy.
# These were copied from http://gnuwin32.sourceforge.net/packages/regex.htm # These were copied from http://gnuwin32.sourceforge.net/packages/regex.htm
# # Consider upgrading from https://www.gnu.org/software/libc/sources.html
regex.a : regex.o regex.a : regex.o
ar -cr $@ $^ ar -cr $@ $^
@ -228,7 +239,8 @@ regex.o : regex/regex.c
$(CC) $(CFLAGS) -Dbool=int -Dtrue=1 -Dfalse=0 -c -o $@ $^ $(CC) $(CFLAGS) -Dbool=int -Dtrue=1 -Dfalse=0 -c -o $@ $^
# There are several string functios found in Linux
# There are several string functions found in Linux
# but not on Windows. Need to provide our own copy. # but not on Windows. Need to provide our own copy.
misc.a : strsep.o strtok_r.o strcasestr.o strlcpy.o strlcat.o misc.a : strsep.o strtok_r.o strcasestr.o strlcpy.o strlcat.o
@ -256,7 +268,7 @@ strlcat.o : misc/strlcat.c
# Combine some unit tests into a single regression sanity check. # Combine some unit tests into a single regression sanity check.
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800 check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest dtmftest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
# Can we encode and decode at popular data rates? # Can we encode and decode at popular data rates?
# Verify that single bit fixup increases the count. # Verify that single bit fixup increases the count.
@ -273,7 +285,15 @@ check-modem300 : gen_packets atest
atest -B300 -F1 -L71 -G75 test3.wav atest -B300 -F1 -L71 -G75 test3.wav
rm test3.wav rm test3.wav
#FIXME: test full amplitude.
check-modem9600 : gen_packets atest check-modem9600 : gen_packets atest
gen_packets -B9600 -a 170 -o test96.wav
sleep 1
atest -B9600 -F0 -L4 -G4 test96.wav
sleep 1
rm test96.wav
sleep 1
gen_packets -B9600 -n 100 -o test96.wav gen_packets -B9600 -n 100 -o test96.wav
sleep 1 sleep 1
atest -B9600 -F0 -L50 -G54 test96.wav atest -B9600 -F0 -L50 -G54 test96.wav
@ -282,6 +302,12 @@ check-modem9600 : gen_packets atest
rm test96.wav rm test96.wav
check-modem19200 : gen_packets atest check-modem19200 : gen_packets atest
gen_packets -r 96000 -B19200 -a 170 -o test19.wav
sleep 1
atest -B19200 -F0 -L4 test19.wav
sleep 1
rm test19.wav
sleep 1
gen_packets -r 96000 -B19200 -n 100 -o test19.wav gen_packets -r 96000 -B19200 -n 100 -o test19.wav
sleep 1 sleep 1
atest -B19200 -F0 -L55 -G59 test19.wav atest -B19200 -F0 -L55 -G59 test19.wav
@ -333,8 +359,8 @@ atest9 : atest.c demod.c dsp.c demod_afsk.c demod_psk.c demod_9600.c hdlc_rec.c
# Unit test for inner digipeater algorithm # Unit test for inner digipeater algorithm
.PHONY: dtest .PHONY: dtest
dtest : digipeater.c dedupe.c \ dtest : digipeater.c dedupe.c pfilter.c \
pfilter.o ax25_pad.o fcs_calc.o tq.o textcolor.o \ ax25_pad.o fcs_calc.o tq.o textcolor.o \
decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o misc.a regex.a decode_aprs.o dwgpsnmea.o dwgps.o serial_port.o latlong.o telemetry.o symbols.o tt_text.o misc.a regex.a
$(CC) $(CFLAGS) -DDIGITEST -o $@ $^ $(CC) $(CFLAGS) -DDIGITEST -o $@ $^
./dtest ./dtest
@ -417,6 +443,13 @@ xidtest : xid.c textcolor.o misc.a
./xidtest ./xidtest
rm xidtest.exe rm xidtest.exe
# Unit Test for DTMF encode/decode.
.PHONY: dtmftest
dtmftest : dtmf.c textcolor.o
$(CC) $(CFLAGS) -DDTMF_TEST -o $@ $^
./dtmftest
rm dtmftest.exe
# ------------------------------ Other manual testing & experimenting ------------------------------- # ------------------------------ Other manual testing & experimenting -------------------------------
@ -529,7 +562,7 @@ walk96 : walk96.c dwgps.o dwgpsnmea.o kiss_frame.o \
xmit.o hdlc_send.o gen_tone.o ptt.o tq.o \ xmit.o hdlc_send.o gen_tone.o ptt.o tq.o \
hdlc_rec.o hdlc_rec2.o rrbb.o dsp.o audio_win.o \ hdlc_rec.o hdlc_rec2.o rrbb.o dsp.o audio_win.o \
multi_modem.o demod.o demod_afsk.o demod_psk.c demod_9600.o rdq.o \ multi_modem.o demod.o demod_afsk.o demod_psk.c demod_9600.o rdq.o \
server.o morse.o audio_stats.o dtime_now.o dlq.o \ server.o morse.o dtmf.o audio_stats.o dtime_now.o dlq.o \
regex.a misc.a regex.a misc.a
$(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32 $(CC) $(CFLAGS) -DWALK96 -o $@ $^ -lwinmm -lws2_32

View File

@ -1820,6 +1820,64 @@ void ax25_format_addrs (packet_t this_p, char *result)
} }
/*------------------------------------------------------------------
*
* Function: ax25_format_via_path
*
* Purpose: Format via path addresses suitable for printing.
*
* Inputs: Current packet.
*
* result_size - Number of bytes available for result.
* We can have up to 8 addresses x 9 characters
* plus 7 commas, possible *, and nul = 81 minimum.
*
* Outputs: result - Digipeater field addresses combined into a single string of the form:
*
* "repeater, repeater ..."
*
* An asterisk is displayed after the last digipeater
* with the "H" bit set. e.g. If we hear RPT2,
*
* RPT1,RPT2*,RPT3
*
* No asterisk means the source is being heard directly.
*
*------------------------------------------------------------------*/
void ax25_format_via_path (packet_t this_p, char *result, size_t result_size)
{
int i;
int heard;
char stemp[AX25_MAX_ADDR_LEN];
assert (this_p->magic1 == MAGIC);
assert (this_p->magic2 == MAGIC);
*result = '\0';
/* Don't get upset if no addresses. */
/* This will allow packets that do not comply to AX.25 format. */
if (this_p->num_addr == 0) {
return;
}
heard = ax25_get_heard(this_p);
for (i=(int)AX25_REPEATER_1; i<this_p->num_addr; i++) {
if (i > (int)AX25_REPEATER_1) {
strlcat (result, ",", result_size);
}
ax25_get_addr_with_ssid (this_p, i, stemp);
strlcat (result, stemp, result_size);
if (i == heard) {
strlcat (result, "*", result_size);
}
}
} /* end ax25_format_via_path */
/*------------------------------------------------------------------ /*------------------------------------------------------------------
* *
* Function: ax25_pack * Function: ax25_pack

View File

@ -410,6 +410,7 @@ extern double ax25_get_release_time (packet_t this_p);
extern void ax25_set_modulo (packet_t this_p, int modulo); extern void ax25_set_modulo (packet_t this_p, int modulo);
extern void ax25_format_addrs (packet_t pp, char *); extern void ax25_format_addrs (packet_t pp, char *);
extern void ax25_format_via_path (packet_t this_p, char *result, size_t result_size);
extern int ax25_pack (packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]); extern int ax25_pack (packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]);

View File

@ -954,7 +954,7 @@ static void beacon_send (int j, dwgps_info_t *gpsinfo)
case SENDTO_RECV: case SENDTO_RECV:
/* Simulated reception. */ /* Simulated reception from radio. */
memset (&alevel, 0xff, sizeof(alevel)); memset (&alevel, 0xff, sizeof(alevel));
dlq_rec_frame (g_misc_config_p->beacon[j].sendto_chan, 0, 0, pp, alevel, 0, ""); dlq_rec_frame (g_misc_config_p->beacon[j].sendto_chan, 0, 0, pp, alevel, 0, "");

View File

@ -241,14 +241,6 @@ static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, c
if (filter_str != NULL) { if (filter_str != NULL) {
if (pfilter(from_chan, to_chan, filter_str, pp, 0) != 1) { if (pfilter(from_chan, to_chan, filter_str, pp, 0) != 1) {
// Take out debug message?
// Actually it turns out to be useful.
// Maybe add a quiet option to suppress it.
//#if DEBUG
text_color_set(DW_COLOR_INFO);
dw_printf ("Packet was rejected for digipeating from channel %d to %d by cfilter: %s\n", from_chan, to_chan, filter_str);
//#endif
return(NULL); return(NULL);
} }
} }

View File

@ -138,7 +138,7 @@ static const struct units_s {
{ "league", 4828.032 }, { "league", 4828.032 },
{ "lea", 4828.032 } }; { "lea", 4828.032 } };
#define NUM_UNITS (sizeof(units) / sizeof(struct units_s)) #define NUM_UNITS ((int)((sizeof(units) / sizeof(struct units_s))))
static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_s *p_audio_config); static int beacon_options(char *cmd, struct beacon_s *b, int line, struct audio_s *p_audio_config);
@ -857,6 +857,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
p_igate_config->tx_chan = -1; /* IS->RF not enabled */ p_igate_config->tx_chan = -1; /* IS->RF not enabled */
p_igate_config->tx_limit_1 = IGATE_TX_LIMIT_1_DEFAULT; p_igate_config->tx_limit_1 = IGATE_TX_LIMIT_1_DEFAULT;
p_igate_config->tx_limit_5 = IGATE_TX_LIMIT_5_DEFAULT; p_igate_config->tx_limit_5 = IGATE_TX_LIMIT_5_DEFAULT;
p_igate_config->igmsp = 1;
/* People find this confusing. */ /* People find this confusing. */
@ -2503,7 +2504,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTPOINT pattern must begin with upper case 'B'.\n", line); dw_printf ("Line %d: TTPOINT pattern must begin with upper case 'B'.\n", line);
} }
for (j=1; j<strlen(t); j++) { for (j=1; j<(int)(strlen(t)); j++) {
if ( ! isdigit(t[j])) { if ( ! isdigit(t[j])) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTPOINT pattern must be B and digits only.\n", line); dw_printf ("Line %d: TTPOINT pattern must be B and digits only.\n", line);
@ -2586,7 +2587,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTVECTOR pattern would normally contain \"5bbb\".\n", line); dw_printf ("Line %d: TTVECTOR pattern would normally contain \"5bbb\".\n", line);
} }
for (j=1; j<strlen(t); j++) { for (j=1; j<(int)(strlen(t)); j++) {
if ( ! isdigit(t[j]) && t[j] != 'b' && t[j] != 'd') { if ( ! isdigit(t[j]) && t[j] != 'b' && t[j] != 'd') {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTVECTOR pattern must contain only B, digits, b, and d.\n", line); dw_printf ("Line %d: TTVECTOR pattern must contain only B, digits, b, and d.\n", line);
@ -2697,7 +2698,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTGRID pattern must begin with upper case 'B'.\n", line); dw_printf ("Line %d: TTGRID pattern must begin with upper case 'B'.\n", line);
} }
for (j=1; j<strlen(t); j++) { for (j=1; j<(int)(strlen(t)); j++) {
if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') { if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTGRID pattern must be B, optional digit, xxx, yyy.\n", line); dw_printf ("Line %d: TTGRID pattern must be B, optional digit, xxx, yyy.\n", line);
@ -2801,7 +2802,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
p_tt_config->ttloc_len--; p_tt_config->ttloc_len--;
continue; continue;
} }
for (j=1; j<strlen(t); j++) { for (j=1; j < (int)(strlen(t)); j++) {
if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') { if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTUTM pattern must be B, optional digit, xxx, yyy.\n", line); dw_printf ("Line %d: TTUTM pattern must be B, optional digit, xxx, yyy.\n", line);
@ -2920,7 +2921,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
} }
num_x = 0; num_x = 0;
num_y = 0; num_y = 0;
for (j=1; j<strlen(t); j++) { for (j=1; j<(int)(strlen(t)); j++) {
if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') { if ( ! isdigit(t[j]) && t[j] != 'x' && t[j] != 'y') {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTUSNG/TTMGRS pattern must be B, optional digit, xxx, yyy.\n", line); dw_printf ("Line %d: TTUSNG/TTMGRS pattern must be B, optional digit, xxx, yyy.\n", line);
@ -3039,7 +3040,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
count_x = 0; count_x = 0;
count_other = 0; count_other = 0;
for (k = j ; k < strlen(t); k++) { for (k = j ; k < (int)(strlen(t)); k++) {
if (t[k] == 'x') count_x++; if (t[k] == 'x') count_x++;
else count_other++; else count_other++;
} }
@ -3296,7 +3297,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
p_count[0] = p_count[1] = p_count[2] = 0; p_count[0] = p_count[1] = p_count[2] = 0;
for (j=0; j<strlen(t); j++) { for (j=0; j<(int)(strlen(t)); j++) {
if ( strchr ("0123456789ABCDxyz", t[j]) == NULL) { if ( strchr ("0123456789ABCDxyz", t[j]) == NULL) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: TTMACRO pattern can contain only digits, A, B, C, D, and lower case x, y, or z.\n", line); dw_printf ("Line %d: TTMACRO pattern can contain only digits, A, B, C, D, and lower case x, y, or z.\n", line);
@ -3493,7 +3494,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
d_count[0] = d_count[1] = d_count[2] = 0; d_count[0] = d_count[1] = d_count[2] = 0;
for (j=0; j<strlen(otemp); j++) { for (j=0; j<(int)(strlen(otemp)); j++) {
if (otemp[j] >= 'x' && otemp[j] <= 'z') { if (otemp[j] >= 'x' && otemp[j] <= 'z') {
d_count[otemp[j]-'x']++; d_count[otemp[j]-'x']++;
} }
@ -3903,7 +3904,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
} }
/* /*
* IGFILTER - Filter for messages from IGate server * IGFILTER - IGate Server side filters.
* *
* IGFILTER filter-spec ... * IGFILTER filter-spec ...
*/ */
@ -3973,6 +3974,37 @@ void config_init (char *fname, struct audio_s *p_audio_config,
} }
} }
/*
* IGMSP - Number of times to send position of message sender.
*
* IGMSP n
*/
else if (strcasecmp(t, "IGMSP") == 0) {
t = split(NULL,0);
if (t != NULL) {
int n = atoi(t);
if (n >= 0 && n <= 10) {
p_igate_config->igmsp = n;
}
else {
p_igate_config->satgate_delay = 1;
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Unreasonable number of times for message sender position. Using default 1.\n", line);
}
}
else {
p_igate_config->satgate_delay = 1;
text_color_set(DW_COLOR_ERROR);
dw_printf ("Line %d: Missing number of times for message sender position. Using default 1.\n", line);
}
}
/* /*
* SATGATE - Special SATgate mode to delay packets heard directly. * SATGATE - Special SATgate mode to delay packets heard directly.
* *
@ -4576,6 +4608,8 @@ void config_init (char *fname, struct audio_s *p_audio_config,
dw_printf ("Config file: MYCALL must be set for receive channel %d before Rx IGate is allowed.\n", i); dw_printf ("Config file: MYCALL must be set for receive channel %d before Rx IGate is allowed.\n", i);
strlcpy (p_igate_config->t2_login, "", sizeof(p_igate_config->t2_login)); strlcpy (p_igate_config->t2_login, "", sizeof(p_igate_config->t2_login));
} }
// Currently we can have only one transmit channel.
// This might be generalized someday to allow more.
if (p_igate_config->tx_chan >= 0 && if (p_igate_config->tx_chan >= 0 &&
( strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "") == 0 || ( strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "") == 0 ||
strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "NOCALL") == 0 || strcmp(p_audio_config->achan[p_igate_config->tx_chan].mycall, "NOCALL") == 0 ||
@ -4586,11 +4620,23 @@ void config_init (char *fname, struct audio_s *p_audio_config,
p_igate_config->tx_chan = -1; p_igate_config->tx_chan = -1;
} }
} }
} }
// Apply default IS>RF IGate filter if none specified. New in 1.4.
// This will handle eventual case of multiple transmit channels.
for (j=0; j<MAX_CHANS; j++) {
if (p_audio_config->achan[j].valid && strlen(p_igate_config->t2_login) > 0) {
if (p_digi_config->filter_str[MAX_CHANS][j] == NULL) {
p_digi_config->filter_str[MAX_CHANS][j] = strdup("i/30");
}
}
}
// Terrible hack. But what can we do?
if (p_misc_config->maxv22 < 0) { if (p_misc_config->maxv22 < 0) {
p_misc_config->maxv22 = p_misc_config->retry / 2; p_misc_config->maxv22 = p_misc_config->retry / 3;
} }
} /* end config_init */ } /* end config_init */

View File

@ -1725,25 +1725,27 @@ static void aprs_item (decode_aprs_t *A, unsigned char *info, int ilen)
{ {
struct aprs_item_s { struct aprs_item_s {
char dti; /* ) */ char dti; /* ')' */
char name[9]; /* Actually variable length 3 - 9 bytes. */ char name[10]; /* Actually variable length 3 - 9 bytes. */
/* DON'T refer to the rest of this structure; */ /* DON'T refer to the rest of this structure; */
/* the offsets will be wrong! */ /* the offsets will be wrong! */
/* We make it 10 here so we don't get subscript out of bounds */
/* warning when looking for following '!' or '_' character. */
char live_killed; /* ! for live or _ for killed */ char live_killed__; /* ! for live or _ for killed */
position_t pos; position_t pos__;
char comment[43]; /* First 7 bytes could be data extension. */ char comment__[43]; /* First 7 bytes could be data extension. */
} *p; } *p;
struct aprs_compressed_item_s { struct aprs_compressed_item_s {
char dti; /* ) */ char dti; /* ')' */
char name[9]; /* Actually variable length 3 - 9 bytes. */ char name[10]; /* Actually variable length 3 - 9 bytes. */
/* DON'T refer to the rest of this structure; */ /* DON'T refer to the rest of this structure; */
/* the offsets will be wrong! */ /* the offsets will be wrong! */
char live_killed; /* ! for live or _ for killed */ char live_killed__; /* ! for live or _ for killed */
compressed_position_t cpos; compressed_position_t cpos__;
char comment[40]; /* No data extension in this case. */ char comment__[40]; /* No data extension in this case. */
} *q; } *q;

View File

@ -297,14 +297,6 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
if (filter_str != NULL) { if (filter_str != NULL) {
if (pfilter(from_chan, to_chan, filter_str, pp, 1) != 1) { if (pfilter(from_chan, to_chan, filter_str, pp, 1) != 1) {
// TODO1.2: take out debug message
// Actually it turns out to be useful.
// Maybe add a quiet option to suppress it although no one has complained about it yet.
//#if DEBUG
text_color_set(DW_COLOR_DEBUG);
dw_printf ("Packet was rejected for digipeating from channel %d to %d by filter: %s\n", from_chan, to_chan, filter_str);
//#endif
return(NULL); return(NULL);
} }
} }

View File

@ -167,6 +167,7 @@ static struct tt_config_s tt_config;
static const int audio_amplitude = 100; /* % of audio sample range. */ static const int audio_amplitude = 100; /* % of audio sample range. */
/* This translates to +-32k for 16 bit samples. */ /* This translates to +-32k for 16 bit samples. */
/* Currently no option to change this. */
static int d_u_opt = 0; /* "-d u" command line option to print UTF-8 also in hexadecimal. */ static int d_u_opt = 0; /* "-d u" command line option to print UTF-8 also in hexadecimal. */
static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */ static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */
@ -204,6 +205,8 @@ int main (int argc, char *argv[])
int d_g_opt = 0; /* "-d g" option for GPS. Can be repeated for more detail. */ int d_g_opt = 0; /* "-d g" option for GPS. Can be repeated for more detail. */
int d_o_opt = 0; /* "-d o" option for output control such as PTT and DCD. */ int d_o_opt = 0; /* "-d o" option for output control such as PTT and DCD. */
int d_i_opt = 0; /* "-d i" option for IGate. Repeat for more detail */ int d_i_opt = 0; /* "-d i" option for IGate. Repeat for more detail */
int d_m_opt = 0; /* "-d m" option for mheard list. */
int d_f_opt = 0; /* "-d f" option for filtering. Repeat for more detail. */
#if USE_HAMLIB #if USE_HAMLIB
int d_h_opt = 0; /* "-d h" option for hamlib debugging. Repeat for more detail */ int d_h_opt = 0; /* "-d h" option for hamlib debugging. Repeat for more detail */
#endif #endif
@ -257,7 +260,7 @@ int main (int argc, char *argv[])
text_color_init(t_opt); text_color_init(t_opt);
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); //dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "E", __DATE__); dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__);
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB) #if defined(ENABLE_GPSD) || defined(USE_HAMLIB)
@ -477,9 +480,11 @@ int main (int argc, char *argv[])
case 'p': d_p_opt = 1; break; // TODO: packet dump for xmit side. case 'p': d_p_opt = 1; break; // TODO: packet dump for xmit side.
case 'o': d_o_opt++; ptt_set_debug(d_o_opt); break; case 'o': d_o_opt++; ptt_set_debug(d_o_opt); break;
case 'i': d_i_opt++; break; case 'i': d_i_opt++; break;
case 'm': d_m_opt++; break;
case 'f': d_f_opt++; break;
#if AX25MEMDEBUG #if AX25MEMDEBUG
case 'm': ax25memdebug_set(); break; // Track down memory leak. Not documented. case 'l': ax25memdebug_set(); break; // Track down memory Leak. Not documented.
#endif #endif // Previously 'm' but that is now used for mheard.
#if USE_HAMLIB #if USE_HAMLIB
case 'h': d_h_opt++; break; // Hamlib verbose level. case 'h': d_h_opt++; break; // Hamlib verbose level.
#endif #endif
@ -749,7 +754,7 @@ int main (int argc, char *argv[])
digipeater_init (&audio_config, &digi_config); digipeater_init (&audio_config, &digi_config);
igate_init (&audio_config, &igate_config, &digi_config, d_i_opt); igate_init (&audio_config, &igate_config, &digi_config, d_i_opt);
cdigipeater_init (&audio_config, &cdigi_config); cdigipeater_init (&audio_config, &cdigi_config);
//FIXME//pfilter_init (&igate_config, 0); pfilter_init (&igate_config, d_f_opt);
ax25_link_init (&misc_config); ax25_link_init (&misc_config);
/* /*
@ -778,7 +783,7 @@ int main (int argc, char *argv[])
*/ */
log_init(misc_config.logdir); log_init(misc_config.logdir);
mheard_init (0); // might add debug option someday. mheard_init (d_m_opt);
beacon_init (&audio_config, &misc_config, &igate_config); beacon_init (&audio_config, &misc_config, &igate_config);
@ -1057,8 +1062,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
// Add to list of stations heard over the radio. // Add to list of stations heard over the radio.
mheard_save (chan, &A, pp, alevel, retries); mheard_save_rf (chan, &A, pp, alevel, retries);
//FIXME//mheard_save_rf (chan, &A, pp, alevel, retries);
// Convert to NMEA waypoint sentence if we have a location. // Convert to NMEA waypoint sentence if we have a location.
@ -1209,6 +1213,7 @@ static void usage (char **argv)
dw_printf (" t t = Tracker beacon.\n"); dw_printf (" t t = Tracker beacon.\n");
dw_printf (" o o = output controls such as PTT and DCD.\n"); dw_printf (" o o = output controls such as PTT and DCD.\n");
dw_printf (" i i = IGate.\n"); dw_printf (" i i = IGate.\n");
dw_printf (" m m = Monitor heard station list.\n");
#if USE_HAMLIB #if USE_HAMLIB
dw_printf (" h h = hamlib increase verbose level.\n"); dw_printf (" h h = hamlib increase verbose level.\n");
#endif #endif

View File

@ -18,6 +18,11 @@
These dive into more detail for specialized topics or typical usage scenarios. These dive into more detail for specialized topics or typical usage scenarios.
- [Successful APRS IGate Operation](Successful-APRS-IGate-Operation.pdf)
Dire Wolf can serve as a gateway between the APRS radio network and APRS-IS servers on the Internet.
This explains how it all works, proper configuration, and troubleshooting.
- [APRStt Implementation Notes](APRStt-Implementation-Notes.pdf) - [APRStt Implementation Notes](APRStt-Implementation-Notes.pdf)

Binary file not shown.

Binary file not shown.

244
igate.c
View File

@ -99,6 +99,7 @@
#include "latlong.h" #include "latlong.h"
#include "pfilter.h" #include "pfilter.h"
#include "dtime_now.h" #include "dtime_now.h"
#include "mheard.h"
#if __WIN32__ #if __WIN32__
@ -118,7 +119,7 @@ static packet_t dp_queue_head;
static void satgate_delay_packet (packet_t pp, int chan); static void satgate_delay_packet (packet_t pp, int chan);
static void send_packet_to_server (packet_t pp, int chan); static void send_packet_to_server (packet_t pp, int chan);
static void send_msg_to_server (const char *msg); static void send_msg_to_server (const char *msg);
static void xmit_packet (char *message, int chan); static void maybe_xmit_packet_from_igate (char *message, int chan);
static void rx_to_ig_init (void); static void rx_to_ig_init (void);
static void rx_to_ig_remember (packet_t pp); static void rx_to_ig_remember (packet_t pp);
@ -691,8 +692,11 @@ static void * connnect_thread (void *arg)
// Try each address until we find one that is successful. // Try each address until we find one that is successful.
for (n=0; n<num_hosts; n++) { for (n=0; n<num_hosts; n++) {
#if __WIN32__
SOCKET is;
#else
int is; int is;
#endif
ai = hosts[n]; ai = hosts[n];
ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str)); ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
@ -827,6 +831,9 @@ static void * connnect_thread (void *arg)
} }
} }
exit(0); // Unreachable but stops compiler from complaining
// about function not returning a value.
} /* end connnect_thread */ } /* end connnect_thread */
@ -879,6 +886,10 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
/* /*
* Check for filtering from specified channel to the IGate server. * Check for filtering from specified channel to the IGate server.
*
* Should we do this after unwrapping the payload from a third party packet?
* In my experience, third party packets have only been seen coming from IGates.
* In that case, the payload will have TCPIP in the path and it will be dropped.
*/ */
if (save_digi_config_p->filter_str[chan][MAX_CHANS] != NULL) { if (save_digi_config_p->filter_str[chan][MAX_CHANS] != NULL) {
@ -1066,7 +1077,12 @@ static void send_packet_to_server (packet_t pp, int chan)
(void)(info_len); (void)(info_len);
/* /*
* Do not relay if a duplicate of something sent recently. * We will often see the same packet multiple times close together due to digipeating.
* The consensus seems to be that we should just send the first and drop the later duplicates.
* There is some dissent on this issue. http://www.tapr.org/pipermail/aprssig/2016-July/045907.html
* There could be some value to sending them all to provide information about digipeater paths.
* If you feel strongly about this issue, you could remove the following section.
* Currently rx_to_ig_allow only checks for recent duplicates.
*/ */
if ( ! rx_to_ig_allow(pp)) { if ( ! rx_to_ig_allow(pp)) {
@ -1080,11 +1096,40 @@ static void send_packet_to_server (packet_t pp, int chan)
/* /*
* Finally, append ",qAR," and my call to the path. * Finally, append ",qAR," and my call to the path.
*/
/*
* It seems that the specification has changed recently.
* http://www.tapr.org/pipermail/aprssig/2016-December/046456.html
*
* We can see the history at the Internet Archive Wayback Machine.
*
* http://www.aprs-is.net/Connecting.aspx
* captured Oct 19, 2016:
* ... Only the qAR construct may be generated by a client (IGate) on APRS-IS.
* Captured Dec 1, 2016:
* ... Only the qAR and qAO constructs may be generated by a client (IGate) on APRS-IS.
*
* http://www.aprs-is.net/q.aspx
* Captured April 23, 2016:
* (no mention of client generating qAO.)
* Captured July 19, 2016:
* qAO - (letter O) Packet is placed on APRS-IS by a receive-only IGate from RF.
* The callSSID following the qAO is the callSSID of the IGate. Note that receive-only
* IGates are discouraged on standard APRS frequencies. Please consider a bidirectional
* IGate that only gates to RF messages for stations heard directly.
*/ */
ax25_format_addrs (pp, msg); ax25_format_addrs (pp, msg);
msg[strlen(msg)-1] = '\0'; /* Remove trailing ":" */ msg[strlen(msg)-1] = '\0'; /* Remove trailing ":" */
strlcat (msg, ",qAR,", sizeof(msg));
if (save_igate_config_p->tx_chan >= 0) {
strlcat (msg, ",qAR,", sizeof(msg));
}
else {
strlcat (msg, ",qAO,", sizeof(msg)); // new for version 1.4.
}
strlcat (msg, save_audio_config_p->achan[chan].mycall, sizeof(msg)); strlcat (msg, save_audio_config_p->achan[chan].mycall, sizeof(msg));
strlcat (msg, ":", sizeof(msg)); strlcat (msg, ":", sizeof(msg));
strlcat (msg, (char*)pinfo, sizeof(msg)); strlcat (msg, (char*)pinfo, sizeof(msg));
@ -1268,7 +1313,7 @@ static void * igate_recv_thread (void *arg)
ch = get1ch(); ch = get1ch();
stats_downlink_bytes++; stats_downlink_bytes++;
if (len < sizeof(message)) if (len < (int)(sizeof(message)))
{ {
message[len] = ch; message[len] = ch;
} }
@ -1339,12 +1384,20 @@ static void * igate_recv_thread (void *arg)
ax25_safe_print ((char *)message, len, 0); ax25_safe_print ((char *)message, len, 0);
dw_printf ("\n"); dw_printf ("\n");
/*
* Record that we heard from the source address.
*/
mheard_save_is ((char *)message);
stats_downlink_packets++; stats_downlink_packets++;
/*
* Possibly transmit if so configured.
*/
int to_chan = save_igate_config_p->tx_chan; int to_chan = save_igate_config_p->tx_chan;
if (to_chan >= 0) { if (to_chan >= 0) {
xmit_packet ((char*)message, to_chan); maybe_xmit_packet_from_igate ((char*)message, to_chan);
} }
} }
@ -1476,10 +1529,10 @@ static void * satgate_delay_thread (void *arg)
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
* *
* Name: xmit_packet * Name: maybe_xmit_packet_from_igate
* *
* Purpose: Convert text string, from IGate server, to third party * Purpose: Convert text string, from IGate server, to third party
* packet and send to transmit queue. * packet and send to transmit queue if appropriate.
* *
* Inputs: message - As sent by the server. * Inputs: message - As sent by the server.
* Any trailing CRLF should have been removed. * Any trailing CRLF should have been removed.
@ -1497,18 +1550,23 @@ static void * satgate_delay_thread (void *arg)
* repackaging to go over the radio. * repackaging to go over the radio.
* *
* The "q construct" ( http://www.aprs-is.net/q.aspx ) provides * 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. * a clue about the journey taken. "qAX" means that the station sending
* the packet to the server did not login properly as a ham radio
* operator so we don't want to put this on to RF.
* *
* to_chan - Radio channel for transmitting. * to_chan - Radio channel for transmitting.
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
static void xmit_packet (char *message, int to_chan) static void maybe_xmit_packet_from_igate (char *message, int to_chan)
{ {
packet_t pp3; packet_t pp3;
char payload[AX25_MAX_PACKET_LEN]; /* what is max len? */ char payload[AX25_MAX_PACKET_LEN]; /* what is max len? */
char src[AX25_MAX_ADDR_LEN]; /* Source address. */
char *pinfo = NULL; char *pinfo = NULL;
int info_len; int info_len;
int n;
assert (to_chan >= 0 && to_chan < MAX_CHANS); assert (to_chan >= 0 && to_chan < MAX_CHANS);
@ -1530,6 +1588,32 @@ static void xmit_packet (char *message, int to_chan)
return; return;
} }
ax25_get_addr_with_ssid (pp3, AX25_SOURCE, src);
/*
* Drop if path contains:
* NOGATE or RFONLY - means IGate should not pass them.
* TCPXX or qAX - means it came from somewhere that did not identify itself correctly.
*/
for (n = 0; n < ax25_get_num_repeaters(pp3); n++) {
char via[AX25_MAX_ADDR_LEN]; /* includes ssid. Do we want to ignore it? */
ax25_get_addr_with_ssid (pp3, n + AX25_REPEATER_1, via);
if (strcmp(via, "qAX") == 0 ||
strcmp(via, "TCPXX") == 0 ||
strcmp(via, "RFONLY") == 0 ||
strcmp(via, "NOGATE") == 0) {
if (s_debug >= 1) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("Tx IGate: Do not transmit with %s in path.\n", via);
}
ax25_delete (pp3);
return;
}
}
/* /*
* Apply our own packet filtering if configured. * Apply our own packet filtering if configured.
@ -1540,20 +1624,58 @@ static void xmit_packet (char *message, int to_chan)
assert (to_chan >= 0 && to_chan < MAX_CHANS); assert (to_chan >= 0 && to_chan < MAX_CHANS);
if (save_digi_config_p->filter_str[MAX_CHANS][to_chan] != NULL) {
if (pfilter(MAX_CHANS, to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan], pp3, 1) != 1) { /*
* We have a rather strange special case here.
* If we recently transmitted a 'message' from some station,
* send the position of the message sender when it comes along later.
*
* If we have a position report, look up the sender and see if we should
* bypass the normal filtering.
*/
// Originally this was always printed but it's probably too much noise. // TODO: Not quite this simple. Should have a function to check for position.
// Version 1.4, print only if debug option is specified. // $ raw gps could be a position. @ could be weather data depending on symbol.
info_len = ax25_get_info (pp3, (unsigned char **)(&pinfo));
int msp_special_case = 0;
if (info_len >= 1 && strchr("!=/@'`", *pinfo) != NULL) {
int n = mheard_get_msp(src);
if (n > 0) {
msp_special_case = 1;
if (s_debug >= 1) { if (s_debug >= 1) {
text_color_set(DW_COLOR_INFO); text_color_set(DW_COLOR_INFO);
dw_printf ("Packet from IGate to channel %d was rejected by filter: %s\n", to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan]); dw_printf ("Special case, allow position from message sender %s, %d remaining.\n", src, n - 1);
} }
ax25_delete (pp3); mheard_set_msp (src, n - 1);
return; }
}
if ( ! msp_special_case) {
if (save_digi_config_p->filter_str[MAX_CHANS][to_chan] != NULL) {
if (pfilter(MAX_CHANS, to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan], pp3, 1) != 1) {
// Previously there was a debug message here about the packet being dropped by filtering.
// This is now handled better by the "-df" command line option for filtering details.
// TODO: clean up - remove these lines.
//if (s_debug >= 1) {
// text_color_set(DW_COLOR_INFO);
// dw_printf ("Packet from IGate to channel %d was rejected by filter: %s\n", to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan]);
//}
ax25_delete (pp3);
return;
}
} }
} }
@ -1566,6 +1688,34 @@ static void xmit_packet (char *message, int to_chan)
* *
* We want to reduce it to this before wrapping it as third party traffic. * We want to reduce it to this before wrapping it as third party traffic.
* K1USN-1>APWW10:T#479,100,048,002,500,000,10000000<0x0d><0x0a> * K1USN-1>APWW10:T#479,100,048,002,500,000,10000000<0x0d><0x0a>
*/
/*
* These are typical examples where we see TCPIP*,qAC,<server>
*
* N3LLO-4>APRX28,TCPIP*,qAC,T2NUENGLD:T#474,21.4,0.3,114.0,4.0,0.0,00000000
* N1WJO>APWW10,TCPIP*,qAC,T2MAINE:)147.120!4412.27N/07033.27WrW1OCA repeater136.5 Tone Norway Me
* AB1OC-10>APWW10,TCPIP*,qAC,T2IAD2:=4242.70N/07135.41W#(Time 0:00:00)!INSERVICE!!W60!
*
* But sometimes we get a different form:
*
* N1YG-1>T1SY9P,WIDE1-1,WIDE2-2,qAR,W2DAN-15:'c&<0x7f>l <0x1c>-/>
* W1HS-8>TSSP9T,WIDE1-1,WIDE2-1,qAR,N3LLO-2:`d^Vl"W>/'"85}|*&%_'[|!wLK!|3
* N1RCW-1>APU25N,MA2-2,qAR,KA1VCQ-1:=4140.41N/07030.21W-Home Station/Fill-in Digi {UIV32N}
* N1IEJ>T4PY3U,W1EMA-1,WIDE1*,WIDE2-2,qAR,KD1KE:`a5"l!<0x7f>-/]"4f}Retired & Busy=
*
* Oh! They have qAR rather than qAC. What does that mean?
* From http://www.aprs-is.net/q.aspx
*
* qAC - Packet was received from the client directly via a verified connection (FROMCALL=login).
* The callSSID following the qAC is the server's callsign-SSID.
*
* qAR - Packet was received directly (via a verified connection) from an IGate using the ,I construct.
* The callSSID following the qAR it the callSSID of the IGate.
*
* What is the ",I" construct?
* Do we care here?
* Is is something new and improved that we should be using in the other direction?
*/ */
while (ax25_get_num_repeaters(pp3) > 0) { while (ax25_get_num_repeaters(pp3) > 0) {
@ -1598,6 +1748,16 @@ static void xmit_packet (char *message, int to_chan)
/* /*
* Encapsulate for sending over radio if no reason to drop it. * Encapsulate for sending over radio if no reason to drop it.
*/
/*
* We don't want to suppress duplicate "messages" within a short time period.
* Suppose we transmitted a "message" for some station and it did not respond with an ack.
* 25 seconds later the sender retries. Wouldn't we want to pass along that retry?
*
* "Messages" get preferential treatment because they are high value and very rare.
* -> Bypass the duplicate suppression.
* -> Raise the rate limiting value.
*/ */
if (ig_to_tx_allow (pp3, to_chan)) { if (ig_to_tx_allow (pp3, to_chan)) {
char radio [500]; char radio [500];
@ -1626,8 +1786,17 @@ static void xmit_packet (char *message, int to_chan)
#endif #endif
stats_rf_xmit_packets++; // Any type of packet. stats_rf_xmit_packets++; // Any type of packet.
// TEMP TEST: metadata temporarily allowed during testing.
if (*pinfo == ':' && ! is_telem_metadata(pinfo)) { if (*pinfo == ':' && ! is_telem_metadata(pinfo)) {
stats_msg_cnt++; // "message" be sure to exclude telemetry metadata. // temp test // if (*pinfo == ':') {
// We transmitted a "message." Telemetry metadata is excluded.
// Remember to pass along address of the sender later.
stats_msg_cnt++; // Update statistics.
mheard_set_msp (src, save_igate_config_p->igmsp);
} }
ig_to_tx_remember (pp3, save_igate_config_p->tx_chan, 0); // correct. version before encapsulating it. ig_to_tx_remember (pp3, save_igate_config_p->tx_chan, 0); // correct. version before encapsulating it.
@ -1644,7 +1813,7 @@ static void xmit_packet (char *message, int to_chan)
ax25_delete (pp3); ax25_delete (pp3);
} /* end xmit_packet */ } /* end maybe_xmit_packet_from_igate */
@ -1820,7 +1989,7 @@ static int rx_to_ig_allow (packet_t pp)
* duplicate of another sent recently. * duplicate of another sent recently.
* *
* This is the essentially the same as the pair of functions * This is the essentially the same as the pair of functions
* above with one addition restriction. * above, for RF to IS, with one additional restriction.
* *
* The typical residential Internet connection is around 10,000 * The typical residential Internet connection is around 10,000
* to 50,000 times faster than the radio links we are using. It would * to 50,000 times faster than the radio links we are using. It would
@ -2058,14 +2227,35 @@ static int ig_to_tx_allow (packet_t pp, int chan)
for (j=0; j<IG2TX_HISTORY_MAX; j++) { for (j=0; j<IG2TX_HISTORY_MAX; j++) {
if (ig2tx_checksum[j] == crc && ig2tx_chan[j] == chan && ig2tx_time_stamp[j] >= now - IG2TX_DEDUPE_TIME) { if (ig2tx_checksum[j] == crc && ig2tx_chan[j] == chan && ig2tx_time_stamp[j] >= now - IG2TX_DEDUPE_TIME) {
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG); /* We have a duplicate within some time period. */
// could be multiple entries and this might not be the most recent.
dw_printf ("ig_to_tx_allow? NO. Sent %d seconds ago. bydigi=%d\n", (int)(now - ig2tx_time_stamp[j]), ig2tx_bydigi[j]); if (*pinfo == ':' && ! is_telem_metadata((char*)pinfo)) {
/* I think I want to avoid the duplicate suppression for "messages." */
/* Suppose we transmit a message from station X and it doesn't get an ack back. */
/* Station X then sends exactly the same thing 20 seconds later. */
/* We don't want to suppress the retry. */
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf ("ig_to_tx_allow? Yes for duplicate message sent %d seconds ago. bydigi=%d\n", (int)(now - ig2tx_time_stamp[j]), ig2tx_bydigi[j]);
}
}
else {
/* Normal (non-message) case. */
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
// could be multiple entries and this might not be the most recent.
dw_printf ("ig_to_tx_allow? NO. Duplicate sent %d seconds ago. bydigi=%d\n", (int)(now - ig2tx_time_stamp[j]), ig2tx_bydigi[j]);
}
text_color_set(DW_COLOR_INFO);
dw_printf ("Tx IGate: Drop duplicate packet transmitted recently.\n");
return 0;
} }
text_color_set(DW_COLOR_INFO);
dw_printf ("Tx IGate: Drop duplicate packet transmitted recently.\n");
return 0;
} }
} }

12
igate.h
View File

@ -45,6 +45,9 @@ struct igate_config_s {
*/ */
int tx_chan; /* Radio channel for transmitting. */ int tx_chan; /* Radio channel for transmitting. */
/* 0=first, etc. -1 for none. */ /* 0=first, etc. -1 for none. */
/* Presently IGate can transmit on only a single channel. */
/* A future version might generalize this. */
/* Each transmit channel would have its own client side filtering. */
char tx_via[80]; /* VIA path for transmitting third party packets. */ char tx_via[80]; /* VIA path for transmitting third party packets. */
/* Usual text representation. */ /* Usual text representation. */
@ -52,13 +55,20 @@ struct igate_config_s {
/* simply be inserted after the destination address. */ /* simply be inserted after the destination address. */
int max_digi_hops; /* Maximum number of digipeater hops possible for via path. */ int max_digi_hops; /* Maximum number of digipeater hops possible for via path. */
/* e.g. "WIDE1-1,WDIE2-2" would be 3. */ /* Derived from the SSID when last character of address is a digit. */
/* e.g. "WIDE1-1,WIDE5-2" would be 3. */
/* This is useful to know so we can determine how many */ /* This is useful to know so we can determine how many */
/* stations we might be able to reach. */ /* stations we might be able to reach. */
int tx_limit_1; /* Max. packets to transmit in 1 minute. */ int tx_limit_1; /* Max. packets to transmit in 1 minute. */
int tx_limit_5; /* Max. packets to transmit in 5 minutes. */ int tx_limit_5; /* Max. packets to transmit in 5 minutes. */
int igmsp; /* Number of message sender position reports to allow. */
/* Common practice is to default to 1. */
/* We allow additional flexibility of 0 to disable feature */
/* or a small number to allow more. */
/* /*
* Special SATgate mode to delay packets heard directly. * Special SATgate mode to delay packets heard directly.
*/ */

1
kiss.c
View File

@ -124,6 +124,7 @@
#include <ctype.h> #include <ctype.h>
#include <fcntl.h> #include <fcntl.h>
#include <termios.h> #include <termios.h>
#include <sys/select.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#ifdef __OpenBSD__ #ifdef __OpenBSD__

516
mheard.c
View File

@ -24,12 +24,19 @@
* *
* Purpose: Maintain a list of all stations heard. * Purpose: Maintain a list of all stations heard.
* *
* Description: This was added for IGate statistics but would also be * Description: This was added for IGate statistics and checking if a user is local
* useful for the AGW network protocol 'H' request. * but would also be useful for the AGW network protocol 'H' request.
* *
* This application has no GUI and is not interactive so * This application has no GUI and is not interactive so
* I'm not sure what else we might do with the information. * I'm not sure what else we might do with the information.
* *
* 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.
*
* Future Ideas: Someone suggested using SQLite to store the information * Future Ideas: Someone suggested using SQLite to store the information
* so other applications could access it. * so other applications could access it.
* *
@ -49,21 +56,29 @@
#include "ax25_pad.h" #include "ax25_pad.h"
#include "hdlc_rec2.h" // for retry_t #include "hdlc_rec2.h" // for retry_t
#include "mheard.h" #include "mheard.h"
#include "latlong.h"
// I think we can get away without a critical region if we follow certain rules. // This is getting updated from two different threads so we need a critical region
// for adding new nodes.
static dw_mutex_t mheard_mutex;
// I think we can get away without a critical region for reading if we follow these
// rules:
// //
// (1) All updates are from a single thread. Although there are multiple receive // (1) When adding a new node, make sure it is complete, including next ptr,
// 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. // before adding it to the list.
// (3) Nothing gets deleted. // (2) Update the start of list pointer last.
// // (2) Nothing gets deleted.
// It shouldn't be a problem if the data readers are from other threads.
// If we ever decide to start cleaning out very old data, all access would then
// need to use the mutex.
/* /*
* Information for each station heard over the radio. * Information for each station heard over the radio or from Internet Server.
*/ */
typedef struct mheard_s { typedef struct mheard_s {
@ -72,16 +87,36 @@ typedef struct mheard_s {
char callsign[AX25_MAX_ADDR_LEN]; // Callsign from the AX.25 source field. 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. int count; // Number of times heard.
// Zero when heard directly. // We don't use this for anything.
// Just something potentially interesting when looking at data dump.
time_t last_heard; // Timestamp when last heard. int chan; // Most recent channel where heard.
int num_digi_hops; // Number of digipeater hops before we heard it.
// over radio. Zero when heard directly.
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.
// What else would be useful? // What else would be useful?
// The AGW protocol is by channel and returns // The AGW protocol is by channel and returns
// first heard in addition to last heard. // first heard in addition to last heard.
} mheard_t; } mheard_t;
/* /*
* The list could be quite long and we hit this a lot so use a hash table. * The list could be quite long and we hit this a lot so use a hash table.
*/ */
@ -112,7 +147,7 @@ static mheard_t *mheard_ptr(char *callsign) {
} }
static int mheard_debug; static int mheard_debug = 0;
/*------------------------------------------------------------------ /*------------------------------------------------------------------
@ -139,15 +174,130 @@ void mheard_init (int debug)
mheard_hash[i] = NULL; mheard_hash[i] = NULL;
} }
/*
* Mutex to coordinate adding new nodes.
*/
dw_mutex_init(&mheard_mutex);
} /* end mheard_init */ } /* end mheard_init */
/*------------------------------------------------------------------ /*------------------------------------------------------------------
* *
* Function: mheard_save * Function: mheard_dump
* *
* Purpose: Save information about station heard. * 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.
* *
* Inputs: chan - Radio channel where heard. * Inputs: chan - Radio channel where heard.
* *
@ -166,7 +316,7 @@ void mheard_init (int debug)
* *
*------------------------------------------------------------------*/ *------------------------------------------------------------------*/
void mheard_save (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries) void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries)
{ {
time_t now = time(NULL); time_t now = time(NULL);
char source[AX25_MAX_ADDR_LEN]; char source[AX25_MAX_ADDR_LEN];
@ -196,18 +346,24 @@ void mheard_save (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retr
if (mheard_debug) { if (mheard_debug) {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_DEBUG);
dw_printf ("mheard_save: %s %d - added new\n", source, hops); dw_printf ("mheard_save_rf: %s %d - added new\n", source, hops);
} }
mptr = calloc(sizeof(mheard_t),1); mptr = calloc(sizeof(mheard_t),1);
strlcpy (mptr->callsign, source, sizeof(mptr->callsign)); strlcpy (mptr->callsign, source, sizeof(mptr->callsign));
mptr->count = 1;
mptr->chan = chan;
mptr->num_digi_hops = hops; mptr->num_digi_hops = hops;
mptr->last_heard = now; mptr->last_heard_rf = now;
mptr->dlat = G_UNKNOWN;
mptr->dlon = G_UNKNOWN;
i = hash_index(source); i = hash_index(source);
dw_mutex_lock (&mheard_mutex);
mptr->pnext = mheard_hash[i]; // before inserting into list. mptr->pnext = mheard_hash[i]; // before inserting into list.
mheard_hash[i] = mptr; mheard_hash[i] = mptr;
dw_mutex_unlock (&mheard_mutex);
} }
else { else {
@ -218,32 +374,162 @@ void mheard_save (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retr
* We are interested in the shortest path if heard very recently. * We are interested in the shortest path if heard very recently.
*/ */
if (hops > mptr->num_digi_hops && (int)(now - mptr->last_heard) < 15) { if (hops > mptr->num_digi_hops && (int)(now - mptr->last_heard_rf) < 15) {
if (mheard_debug) { if (mheard_debug) {
text_color_set(DW_COLOR_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) ); 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) );
} }
} }
else { else {
if (mheard_debug) { if (mheard_debug) {
text_color_set(DW_COLOR_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)); 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));
} }
mptr->count++;
mptr->chan = chan;
mptr->num_digi_hops = hops; mptr->num_digi_hops = hops;
mptr->last_heard = now; 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.62N/07040.68W$/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:/221700h/9AmA<Ct3_ sT010/002g005t045r000p023P020h97b10148
* 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);
}
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);
}
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.
if (mheard_debug >= 2) { if (mheard_debug >= 2) {
int limit = 10; // normally 30 or 60 int limit = 10; // normally 30 or 60
text_color_set(DW_COLOR_DEBUG); 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)); 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 */ if (mheard_debug) {
mheard_dump ();
}
ax25_delete (pp);
} /* end mheard_save_is */
/*------------------------------------------------------------------ /*------------------------------------------------------------------
@ -312,7 +598,7 @@ int mheard_count (int max_hops, int time_limit)
for (i = 0; i < MHEARD_HASH_SIZE; i++) { for (i = 0; i < MHEARD_HASH_SIZE; i++) {
for (p = mheard_hash[i]; p != NULL; p = p->pnext) { for (p = mheard_hash[i]; p != NULL; p = p->pnext) {
if (p->last_heard >= since && p->num_digi_hops <= max_hops) { if (p->last_heard_rf >= since && p->num_digi_hops <= max_hops) {
count++; count++;
} }
} }
@ -328,4 +614,184 @@ int mheard_count (int max_hops, int time_limit)
} /* end mheard_count */ } /* end mheard_count */
/*------------------------------------------------------------------
*
* 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 */
/* end mheard.c */ /* end mheard.c */

View File

@ -2,8 +2,19 @@
/* mheard.h */ /* mheard.h */
#include "decode_aprs.h" // for decode_aprs_t
void mheard_init (int debug); void mheard_init (int debug);
void mheard_save (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries); void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries);
int mheard_count (int max_hops, int time_limit); void mheard_save_is (char *ptext);
int mheard_count (int max_hops, int time_limit);
int mheard_was_recently_nearby (char *role, char *callsign, int time_limit, int max_hops, double dlat, double dlon, double km);
void mheard_set_msp (char *callsign, int num);
int mheard_get_msp (char *callsign);

464
pfilter.c
View File

@ -51,6 +51,43 @@
#include "decode_aprs.h" #include "decode_aprs.h"
#include "latlong.h" #include "latlong.h"
#include "pfilter.h" #include "pfilter.h"
#include "mheard.h"
/*
* Global stuff (to this file)
*
* These are set by init function.
*/
static struct igate_config_s *save_igate_config_p;
static int s_debug = 0;
/*-------------------------------------------------------------------
*
* Name: pfilter_init
*
* Purpose: One time initialization when main application starts up.
*
* Inputs: p_igate_config - IGate configuration.
*
* debug_level - 0 no debug output.
* 1 single summary line with final result. Indent by 1.
* 2 details from each filter specification. Indent by 3.
* 3 Logical operators. Indent by 2.
*
*--------------------------------------------------------------------*/
void pfilter_init (struct igate_config_s *p_igate_config, int debug_level)
{
s_debug = debug_level;
save_igate_config_p = p_igate_config;
}
@ -65,9 +102,6 @@ typedef struct pfstate_s {
int from_chan; /* From and to channels. MAX_CHANS is used for IGate. */ int from_chan; /* From and to channels. MAX_CHANS is used for IGate. */
int to_chan; /* Used only for debug and error messages. */ int to_chan; /* Used only for debug and error messages. */
// TODO: might want to put channels and packet here so we only pass one thing around.
/* /*
* Original filter string from config file. * Original filter string from config file.
* All control characters should be replaced by spaces. * All control characters should be replaced by spaces.
@ -120,8 +154,17 @@ static void print_error (pfstate_t *pf, char *msg);
static int filt_bodgu (pfstate_t *pf, char *pattern); static int filt_bodgu (pfstate_t *pf, char *pattern);
static int filt_t (pfstate_t *pf); static int filt_t (pfstate_t *pf);
static int filt_r (pfstate_t *pf); static int filt_r (pfstate_t *pf, char *sdist);
static int filt_s (pfstate_t *pf); static int filt_s (pfstate_t *pf);
static int filt_i (pfstate_t *pf);
static char *bool2text (int val)
{
if (val == 1) return "TRUE";
if (val == 0) return "FALSE";
if (val == -1) return "ERROR";
return "OOPS!";
}
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
@ -164,12 +207,12 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs)
memset (&pfstate, 0, sizeof(pfstate)); memset (&pfstate, 0, sizeof(pfstate));
if (pp == NULL) { if (pp == NULL) {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_ERROR);
dw_printf ("INTERNAL ERROR in pfilter: NULL packet pointer. Please report this!\n"); dw_printf ("INTERNAL ERROR in pfilter: NULL packet pointer. Please report this!\n");
return (-1); return (-1);
} }
if (filter == NULL) { if (filter == NULL) {
text_color_set(DW_COLOR_DEBUG); text_color_set(DW_COLOR_ERROR);
dw_printf ("INTERNAL ERROR in pfilter: NULL filter string pointer. Please report this!\n"); dw_printf ("INTERNAL ERROR in pfilter: NULL filter string pointer. Please report this!\n");
return (-1); return (-1);
} }
@ -212,6 +255,23 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs)
result = -1; result = -1;
} }
} }
if (s_debug >= 1) {
text_color_set(DW_COLOR_DEBUG);
if (from_chan == MAX_CHANS) {
dw_printf (" Packet filter from IGate to radio channel %d returns %s\n", to_chan, bool2text(result));
}
else if (to_chan == MAX_CHANS) {
dw_printf (" Packet filter from radio channel %d to IGate returns %s\n", from_chan, bool2text(result));
}
else if (is_aprs) {
dw_printf (" Packet filter for APRS digipeater from radio channel %d to %d returns %s\n", from_chan, to_chan, bool2text(result));
}
else {
dw_printf (" Packet filter for traditional digipeater from radio channel %d to %d returns %s\n", from_chan, to_chan, bool2text(result));
}
}
return (result); return (result);
} /* end pfilter */ } /* end pfilter */
@ -350,6 +410,12 @@ static int parse_or_expr (pfstate_t *pf)
next_token (pf); next_token (pf);
e = parse_and_expr (pf); e = parse_and_expr (pf);
if (s_debug >= 3) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s | %s\n", bool2text(result), bool2text(e));
}
if (e < 0) return (-1); if (e < 0) return (-1);
result |= e; result |= e;
} }
@ -371,6 +437,12 @@ static int parse_and_expr (pfstate_t *pf)
next_token (pf); next_token (pf);
e = parse_primary (pf); e = parse_primary (pf);
if (s_debug >= 3) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s & %s\n", bool2text(result), bool2text(e));
}
if (e < 0) return (-1); if (e < 0) return (-1);
result &= e; result &= e;
} }
@ -404,6 +476,12 @@ static int parse_primary (pfstate_t *pf)
next_token (pf); next_token (pf);
e = parse_primary (pf); e = parse_primary (pf);
if (s_debug >= 3) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" ! %s\n", bool2text(e));
}
if (e < 0) result = -1; if (e < 0) result = -1;
else result = ! e; else result = ! e;
} }
@ -440,12 +518,9 @@ static int parse_primary (pfstate_t *pf)
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
static int parse_filter_spec (pfstate_t *pf) static int parse_filter_spec (pfstate_t *pf)
{ {
int result = -1; int result = -1;
char addr[AX25_MAX_ADDR_LEN];
if ( ( ! pf->is_aprs) && strchr ("01bdvu", pf->token_str[0]) == NULL) { if ( ( ! pf->is_aprs) && strchr ("01bdvu", pf->token_str[0]) == NULL) {
@ -468,15 +543,33 @@ static int parse_filter_spec (pfstate_t *pf)
/* simple string matching */ /* simple string matching */
/* b - budlist */
else if (pf->token_str[0] == 'b' && ispunct(pf->token_str[1])) { else if (pf->token_str[0] == 'b' && ispunct(pf->token_str[1])) {
/* Budlist - source address */ /* Budlist - source address */
char addr[AX25_MAX_ADDR_LEN];
ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, addr); ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, addr);
result = filt_bodgu (pf, addr); result = filt_bodgu (pf, addr);
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), addr);
}
} }
/* o - object or item name */
else if (pf->token_str[0] == 'o' && ispunct(pf->token_str[1])) { else if (pf->token_str[0] == 'o' && ispunct(pf->token_str[1])) {
/* Object or item name */
result = filt_bodgu (pf, pf->decoded.g_name); result = filt_bodgu (pf, pf->decoded.g_name);
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), pf->decoded.g_name);
}
} }
/* d - was digipeated by */
else if (pf->token_str[0] == 'd' && ispunct(pf->token_str[1])) { else if (pf->token_str[0] == 'd' && ispunct(pf->token_str[1])) {
int n; int n;
// loop on all digipeaters // loop on all digipeaters
@ -484,11 +577,26 @@ static int parse_filter_spec (pfstate_t *pf)
for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) { for (n = AX25_REPEATER_1; result == 0 && n < ax25_get_num_addr (pf->pp); n++) {
// Consider only those with the H (has-been-used) bit set. // Consider only those with the H (has-been-used) bit set.
if (ax25_get_h (pf->pp, n)) { if (ax25_get_h (pf->pp, n)) {
char addr[AX25_MAX_ADDR_LEN];
ax25_get_addr_with_ssid (pf->pp, n, addr); ax25_get_addr_with_ssid (pf->pp, n, addr);
result = filt_bodgu (pf, addr); result = filt_bodgu (pf, addr);
} }
} }
if (s_debug >= 2) {
char path[100];
ax25_format_via_path (pf->pp, path, sizeof(path));
if (strlen(path) == 0) {
strcpy (path, "no digipeater path");
}
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), path);
}
} }
/* v - via not used */
else if (pf->token_str[0] == 'v' && ispunct(pf->token_str[1])) { else if (pf->token_str[0] == 'v' && ispunct(pf->token_str[1])) {
int n; int n;
// loop on all digipeaters (mnemonic Via) // loop on all digipeaters (mnemonic Via)
@ -497,55 +605,140 @@ static int parse_filter_spec (pfstate_t *pf)
// This is different than the previous "d" filter. // This is different than the previous "d" filter.
// Consider only those where the the H (has-been-used) bit is NOT set. // Consider only those where the the H (has-been-used) bit is NOT set.
if ( ! ax25_get_h (pf->pp, n)) { if ( ! ax25_get_h (pf->pp, n)) {
char addr[AX25_MAX_ADDR_LEN];
ax25_get_addr_with_ssid (pf->pp, n, addr); ax25_get_addr_with_ssid (pf->pp, n, addr);
result = filt_bodgu (pf, addr); result = filt_bodgu (pf, addr);
} }
} }
if (s_debug >= 2) {
char path[100];
ax25_format_via_path (pf->pp, path, sizeof(path));
if (strlen(path) == 0) {
strcpy (path, "no digipeater path");
}
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), path);
}
} }
/* g - Addressee of message. */
else if (pf->token_str[0] == 'g' && ispunct(pf->token_str[1])) { else if (pf->token_str[0] == 'g' && ispunct(pf->token_str[1])) {
/* Addressee of message. */
if (ax25_get_dti(pf->pp) == ':') { if (ax25_get_dti(pf->pp) == ':') {
result = filt_bodgu (pf, pf->decoded.g_addressee); result = filt_bodgu (pf, pf->decoded.g_addressee);
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), pf->decoded.g_addressee);
}
} }
else { else {
result = 0; result = 0;
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), "not a message");
}
} }
} }
/* u - unproto (destination) */
else if (pf->token_str[0] == 'u' && ispunct(pf->token_str[1])) { else if (pf->token_str[0] == 'u' && ispunct(pf->token_str[1])) {
/* Unproto (destination) - probably want to exclude mic-e types */ /* Probably want to exclude mic-e types */
/* because destination is used for part of location. */ /* because destination is used for part of location. */
if (ax25_get_dti(pf->pp) != '\'' && ax25_get_dti(pf->pp) != '`') { if (ax25_get_dti(pf->pp) != '\'' && ax25_get_dti(pf->pp) != '`') {
char addr[AX25_MAX_ADDR_LEN];
ax25_get_addr_with_ssid (pf->pp, AX25_DESTINATION, addr); ax25_get_addr_with_ssid (pf->pp, AX25_DESTINATION, addr);
result = filt_bodgu (pf, addr); result = filt_bodgu (pf, addr);
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), addr);
}
} }
else { else {
result = 0; result = 0;
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), "MIC-E packet type");
}
} }
} }
/* type: position, weather, etc. */ /* t - type: position, weather, etc. */
else if (pf->token_str[0] == 't' && ispunct(pf->token_str[1])) { else if (pf->token_str[0] == 't' && ispunct(pf->token_str[1])) {
ax25_get_addr_with_ssid (pf->pp, AX25_DESTINATION, addr);
result = filt_t (pf); result = filt_t (pf);
if (s_debug >= 2) {
char *infop = NULL;
(void) ax25_get_info (pf->pp, (unsigned char **)(&infop));
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %c data type indicator\n", pf->token_str, bool2text(result), *infop);
}
} }
/* range */ /* r - range */
else if (pf->token_str[0] == 'r' && ispunct(pf->token_str[1])) { else if (pf->token_str[0] == 'r' && ispunct(pf->token_str[1])) {
/* range */ /* range */
result = filt_r (pf); char sdist[30];
strcpy (sdist, "unknown distance");
result = filt_r (pf, sdist);
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
dw_printf (" %s returns %s for %s\n", pf->token_str, bool2text(result), sdist);
}
} }
/* symbol */ /* s - symbol */
else if (pf->token_str[0] == 's' && ispunct(pf->token_str[1])) { else if (pf->token_str[0] == 's' && ispunct(pf->token_str[1])) {
/* symbol */ /* symbol */
result = filt_s (pf); result = filt_s (pf);
if (s_debug >= 2) {
text_color_set(DW_COLOR_DEBUG);
if (pf->decoded.g_symbol_table == '/') {
dw_printf (" %s returns %s for symbol %c in primary table\n", pf->token_str, bool2text(result), pf->decoded.g_symbol_code);
}
else if (pf->decoded.g_symbol_table == '\\') {
dw_printf (" %s returns %s for symbol %c in alternate table\n", pf->token_str, bool2text(result), pf->decoded.g_symbol_code);
}
else {
dw_printf (" %s returns %s for symbol %c with overlay %c\n", pf->token_str, bool2text(result), pf->decoded.g_symbol_code, pf->decoded.g_symbol_table);
}
}
} }
/* i - IGate messaging default */
else if (pf->token_str[0] == 'i' && ispunct(pf->token_str[1])) {
/* IGatge messaging */
result = filt_i (pf);
if (s_debug >= 2) {
char *infop = NULL;
(void) ax25_get_info (pf->pp, (unsigned char **)(&infop));
text_color_set(DW_COLOR_DEBUG);
if (*infop == ':' && ! is_telem_metadata(infop)) {
dw_printf (" %s returns %s for message to %s\n", pf->token_str, bool2text(result), pf->decoded.g_addressee);
}
else {
dw_printf (" %s returns %s for not an APRS 'message'\n", pf->token_str, bool2text(result));
}
}
}
/* unrecognized filter type */
else { else {
char stemp[80]; char stemp[80];
snprintf (stemp, sizeof(stemp), "Unrecognized filter type '%c'", pf->token_str[0]); snprintf (stemp, sizeof(stemp), "Unrecognized filter type '%c'", pf->token_str[0]);
@ -573,7 +766,7 @@ static int parse_filter_spec (pfstate_t *pf)
* Digipeater d/digi1/digi2... * Digipeater d/digi1/digi2...
* Group Msg g/call1/call2... * Group Msg g/call1/call2...
* Unproto u/unproto1/unproto2... * Unproto u/unproto1/unproto2...
* Via-not-yet v/digi1/digi2... * Via-not-yet v/digi1/digi2...noteapd
* *
* arg - Value to match from source addr, destination, * arg - Value to match from source addr, destination,
* used digipeater, object name, etc. * used digipeater, object name, etc.
@ -610,7 +803,7 @@ static int filt_bodgu (pfstate_t *pf, char *arg)
/* Wildcarding. Should have single * on end. */ /* Wildcarding. Should have single * on end. */
mlen = w - v; mlen = w - v;
if (mlen != strlen(v) - 1) { if (mlen != (int)(strlen(v) - 1)) {
print_error (pf, "Any wildcard * must be at the end of pattern.\n"); print_error (pf, "Any wildcard * must be at the end of pattern.\n");
return (-1); return (-1);
} }
@ -787,6 +980,8 @@ static int filt_t (pfstate_t *pf)
* *
* decoded.g_lat & decoded.g_lon * decoded.g_lat & decoded.g_lon
* *
* Outputs: sdist - Distance as a string for troubleshooting.
*
* Returns: 1 = yes * Returns: 1 = yes
* 0 = no * 0 = no
* -1 = error detected * -1 = error detected
@ -795,7 +990,7 @@ static int filt_t (pfstate_t *pf)
* *
*------------------------------------------------------------------------------*/ *------------------------------------------------------------------------------*/
static int filt_r (pfstate_t *pf) static int filt_r (pfstate_t *pf, char *sdist)
{ {
char str[MAX_TOKEN_LEN]; char str[MAX_TOKEN_LEN];
char *cp; char *cp;
@ -836,10 +1031,7 @@ static int filt_r (pfstate_t *pf)
km = ll_distance_km (dlat, dlon, pf->decoded.g_lat, pf->decoded.g_lon); km = ll_distance_km (dlat, dlon, pf->decoded.g_lat, pf->decoded.g_lon);
sprintf (sdist, "%.2f km", km);
text_color_set (DW_COLOR_DEBUG);
dw_printf ("Calculated distance = %.3f km\n", km);
if (km <= ddist) { if (km <= ddist) {
return (1); return (1);
@ -892,7 +1084,10 @@ static int filt_s (pfstate_t *pf)
sep[1] = '\0'; sep[1] = '\0';
cp = str + 2; cp = str + 2;
// TODO: check here.
pri = strsep (&cp, sep); pri = strsep (&cp, sep);
if (pri == NULL) { if (pri == NULL) {
print_error (pf, "Missing arguments for Symbol filter."); print_error (pf, "Missing arguments for Symbol filter.");
return (-1); return (-1);
@ -936,6 +1131,212 @@ static int filt_s (pfstate_t *pf)
} }
/*------------------------------------------------------------------------------
*
* Name: filt_i
*
* Purpose: IGate messaging default behavior.
*
* Inputs: pf - Pointer to current state information.
* token_str should contain something of format:
*
* i/time/hops/lat/lon/km
*
* Returns: 1 = yes
* 0 = no
* -1 = error detected
*
* Description: Selection is based on time since last heard on RF, and distance
* in terms of digipeater hops and/or phyiscal location.
*
* i/time
* i/time/hops
* i/time/hops/lat/lon/km
*
*
* "time" is maximum number of minutes since message addressee was last heard.
* This is required.
*
* "hops" is maximum number of digpeater hops. (i.e. 0 for heard directly).
* If hops is not specified, the maximum transmit digipeater hop count,
* from the IGTXVIA configuration will be used.
* The rest is distanced, in kilometers, from given point.
*
* Examples:
* i/60/0 Heard in past 60 minutes directly.
* i/45 Past 45 minutes, default max digi hops.
* i/30/3 Default time, max 3 digi hops.
* i/30/8/42.6/-71.3/50.
*
*
* It only makes sense to use this for the IS>RF direction.
* The basic idea is that we want to transmit a "message" only if the
* addressee has been heard recently and is not too far away.
*
* After passing along a "message" we will also allow the next
* position report from the sender of the "message."
* That is done somewhere else. We are not concerned with it here.
*
*------------------------------------------------------------------------------*/
static int filt_i (pfstate_t *pf)
{
char str[MAX_TOKEN_LEN];
char *cp;
char sep[2];
char *v;
int heardtime = 30;
#if PFTEST
int maxhops = 2;
#else
int maxhops = save_igate_config_p->max_digi_hops; // from IGTXVIA config.
#endif
double dlat = G_UNKNOWN;
double dlon = G_UNKNOWN;
double km = G_UNKNOWN;
char src[AX25_MAX_ADDR_LEN];
char *infop = NULL;
int info_len;
//char *f;
//char addressee[AX25_MAX_ADDR_LEN];
strlcpy (str, pf->token_str, sizeof(str));
sep[0] = str[1];
sep[1] = '\0';
cp = str + 2;
// Get parameters or defaults.
v = strsep (&cp, sep);
if (v != NULL && strlen(v) > 0) {
heardtime = atoi(v);
}
else {
print_error (pf, "Missing time limit for IGate message filter.");
return (-1);
}
v = strsep (&cp, sep);
if (v != NULL) {
if (strlen(v) > 0) {
maxhops = atoi(v);
}
else {
print_error (pf, "Missing max digipeater hops for IGate message filter.");
return (-1);
}
v = strsep (&cp, sep);
if (v != NULL && strlen(v) > 0) {
dlat = atof(v);
v = strsep (&cp, sep);
if (v != NULL && strlen(v) > 0) {
dlon = atof(v);
}
else {
print_error (pf, "Missing longitude for IGate message filter.");
return (-1);
}
v = strsep (&cp, sep);
if (v != NULL && strlen(v) > 0) {
km = atof(v);
}
else {
print_error (pf, "Missing distance, in km, for IGate message filter.");
return (-1);
}
}
v = strsep (&cp, sep);
if (v != NULL) {
print_error (pf, "Something unexpected after distance for IGate message filter.");
return (-1);
}
}
#if PFTEST
text_color_set(DW_COLOR_DEBUG);
dw_printf ("debug: IGate message filter, %d minutes, %d hops, %.2f %.2f %.2f km\n",
heardtime, maxhops, dlat, dlon, km);
#endif
/*
* Get source address and info part.
* Addressee has already been extracted into pf->decoded.g_addressee.
*/
memset (src, 0, sizeof(src));
ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, src);
info_len = ax25_get_info (pf->pp, (unsigned char **)(&infop));
if (infop == NULL) return (0);
if (info_len < 1) return (0);
// Determine packet type. We are interested only in "message."
// Telemetry metadata is not considered a message.
if (*infop != ':') return (0);
if (is_telem_metadata(infop)) return (0);
#if defined(PFTEST) || defined(DIGITEST) // TODO: test functionality too, not just syntax.
(void)dlat; // Suppress set and not used warning.
(void)dlon;
(void)km;
(void)maxhops;
(void)heardtime;
return (1);
#else
/*
* Condition 1:
* "the receiving station has been heard within range within a predefined time
* period (range defined as digi hops, distance, or both)."
*/
int was_heard = mheard_was_recently_nearby ("addressee", pf->decoded.g_addressee, heardtime, maxhops, dlat, dlon, km);
if ( ! was_heard) return (0);
/*
* Condition 2:
* "the sending station has not been heard via RF within a predefined time period
* (packets gated from the Internet by other stations are excluded from this test)."
*
* This is the part I'm not so sure about.
* I guess the intention is that if the sender can be heard over RF, then the addressee
* might hear the sender without the help of Igate stations.
* Suppose the sender was 1 digipeater hop to the west and the addressee was 1 digipeater hop to the east.
* I can communicate with each of them with 1 digipeater hop but for them to reach each other, they
* might need 3 hops and using that many is generally frowned upon and rare.
*
* Maybe we could compromise here and say the sender must have been heard directly.
* It sent the message currently being processed so we must have heard it very recently, i.e. in
* the past minute, rather than the usual 30 or 60 minutes for the addressee.
*/
was_heard = mheard_was_recently_nearby ("source", src, 1, 0, G_UNKNOWN, G_UNKNOWN, G_UNKNOWN);
if (was_heard) return (0);
return (1);
#endif
} /* end filt_i */
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
* *
* Name: print_error * Name: print_error
@ -1131,6 +1532,21 @@ int main ()
pftest (203, "t/w t/w", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", -1); pftest (203, "t/w t/w", "CWAPID>APRS:;CWAttttz *DDHHMMzLATLONICONADVISETYPE{seq#", -1);
pftest (204, "r/42.6/-71.3", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", -1); pftest (204, "r/42.6/-71.3", "WA1PLE-5>APWW10,W1MHL,N8VIM,WIDE2*:@022301h4208.75N/07115.16WoAPRS-IS for Win32", -1);
pftest (220, "i/30/8/42.6/-71.3/50", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (222, "i/30/8/42.6/-71.3/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (223, "i/30/8/42.6/-71.3", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (224, "i/30/8/42.6/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (225, "i/30/8/42.6", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
pftest (226, "i/30/8/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (227, "i/30/8", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
// FIXME: behaves differently on Windows and Linux
//pftest (228, "i/30/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (229, "i/30", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", 1);
pftest (230, "i/", "WB2OSZ-5>APDW14::W2UB :Happy Birthday{001", -1);
// TODO: to be continued...
if (error_count > 0) { if (error_count > 0) {
text_color_set (DW_COLOR_ERROR); text_color_set (DW_COLOR_ERROR);

View File

@ -1,6 +1,13 @@
/* pfilter.h */ /* pfilter.h */
#include "igate.h" // for igate_config_s
void pfilter_init (struct igate_config_s *p_igate_config, int debug_level);
int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs); int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs);
int is_telem_metadata (char *infop); int is_telem_metadata (char *infop);

112
xmit.c
View File

@ -72,6 +72,7 @@
#include "ptt.h" #include "ptt.h"
#include "dtime_now.h" #include "dtime_now.h"
#include "morse.h" #include "morse.h"
#include "dtmf.h"
#include "xid.h" #include "xid.h"
@ -116,6 +117,8 @@ static int g_debug_xmit_packet; /* print packet in hexadecimal form for debuggi
#define MS_TO_BITS(ms,ch) (((ms)*xmit_bits_per_sec[(ch)])/1000) #define MS_TO_BITS(ms,ch) (((ms)*xmit_bits_per_sec[(ch)])/1000)
#define MAXX(a,b) (((a)>(b)) ? (a) : (b))
#if __WIN32__ #if __WIN32__
static unsigned __stdcall xmit_thread (void *arg); static unsigned __stdcall xmit_thread (void *arg);
@ -139,6 +142,7 @@ static void xmit_ax25_frames (int c, int p, packet_t pp, int max_bundle);
static int send_one_frame (int c, int p, packet_t pp); static int send_one_frame (int c, int p, packet_t pp);
static void xmit_speech (int c, packet_t pp); static void xmit_speech (int c, packet_t pp);
static void xmit_morse (int c, packet_t pp, int wpm); static void xmit_morse (int c, packet_t pp, int wpm);
static void xmit_dtmf (int c, packet_t pp, int speed);
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
@ -366,13 +370,14 @@ void xmit_set_txtail (int channel, int value)
* *
* FLAVOR_SPEECH - Destination address is SPEECH. * FLAVOR_SPEECH - Destination address is SPEECH.
* FLAVOR_MORSE - Destination address is MORSE. * FLAVOR_MORSE - Destination address is MORSE.
* FLAVOR_DTMF - Destination address is DTMF.
* FLAVOR_APRS_NEW - APRS original, i.e. not digipeating. * FLAVOR_APRS_NEW - APRS original, i.e. not digipeating.
* FLAVOR_APRS_DIGI - APRS digipeating. * FLAVOR_APRS_DIGI - APRS digipeating.
* FLAVOR_OTHER - Anything left over, i.e. connected mode. * FLAVOR_OTHER - Anything left over, i.e. connected mode.
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
typedef enum flavor_e { FLAVOR_APRS_NEW, FLAVOR_APRS_DIGI, FLAVOR_SPEECH, FLAVOR_MORSE, FLAVOR_OTHER } flavor_t; typedef enum flavor_e { FLAVOR_APRS_NEW, FLAVOR_APRS_DIGI, FLAVOR_SPEECH, FLAVOR_MORSE, FLAVOR_DTMF, FLAVOR_OTHER } flavor_t;
static flavor_t frame_flavor (packet_t pp) static flavor_t frame_flavor (packet_t pp)
{ {
@ -392,6 +397,10 @@ static flavor_t frame_flavor (packet_t pp)
return (FLAVOR_MORSE); return (FLAVOR_MORSE);
} }
if (strcmp(dest, "DTMF") == 0) {
return (FLAVOR_DTMF);
}
/* Is there at least one digipeater AND has first one been used? */ /* Is there at least one digipeater AND has first one been used? */
/* I could be the first in the list or later. Doesn't matter. */ /* I could be the first in the list or later. Doesn't matter. */
@ -448,7 +457,7 @@ static flavor_t frame_flavor (packet_t pp)
* *
* Version 1.4: Rearranged logic for bundling multiple frames into a single transmission. * Version 1.4: Rearranged logic for bundling multiple frames into a single transmission.
* *
* The rule is that Speech, Morse Code, and APRS digipeated frames * The rule is that Speech, Morse Code, DTMF, and APRS digipeated frames
* are all sent separately. The rest can be bundled. * are all sent separately. The rest can be bundled.
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
@ -507,9 +516,10 @@ static void * xmit_thread (void *arg)
* *
* If destination is "SPEECH" send info part to speech synthesizer. * If destination is "SPEECH" send info part to speech synthesizer.
* If destination is "MORSE" send as morse code. * If destination is "MORSE" send as morse code.
* If destination is "DTMF" send as Touch Tones.
*/ */
int ssid, wpm; int ssid, wpm, speed;
switch (frame_flavor(pp)) { switch (frame_flavor(pp)) {
@ -534,6 +544,14 @@ static void * xmit_thread (void *arg)
xmit_morse (chan, pp, wpm); xmit_morse (chan, pp, wpm);
break; break;
case FLAVOR_DTMF:
speed = ax25_get_ssid(pp, AX25_DESTINATION);
if (speed == 0) speed = 5; // default half of maximum
if (speed > 10) speed = 10;
xmit_dtmf (chan, pp, speed);
break;
case FLAVOR_APRS_DIGI: case FLAVOR_APRS_DIGI:
xmit_ax25_frames (chan, prio, pp, 1); /* 1 means don't bundle */ xmit_ax25_frames (chan, prio, pp, 1); /* 1 means don't bundle */
break; break;
@ -740,6 +758,7 @@ static void xmit_ax25_frames (int chan, int prio, packet_t pp, int max_bundle)
case FLAVOR_SPEECH: case FLAVOR_SPEECH:
case FLAVOR_MORSE: case FLAVOR_MORSE:
case FLAVOR_DTMF:
case FLAVOR_APRS_DIGI: case FLAVOR_APRS_DIGI:
default: default:
done = 1; // not eligible for bundling. done = 1; // not eligible for bundling.
@ -938,7 +957,7 @@ static int send_one_frame (int c, int p, packet_t pp)
* Transmit the frame. * Transmit the frame.
*/ */
flen = ax25_pack (pp, fbuf); flen = ax25_pack (pp, fbuf);
assert (flen >= 1 && flen <= sizeof(fbuf)); assert (flen >= 1 && flen <= (int)(sizeof(fbuf)));
int send_invalid_fcs2 = 0; int send_invalid_fcs2 = 0;
@ -1091,6 +1110,7 @@ int xmit_speak_it (char *script, int c, char *orig_msg)
* *
* Description: Turn on transmitter. * Description: Turn on transmitter.
* Send text as Morse code. * Send text as Morse code.
* A small amount of quiet padding will appear at start and end.
* Turn off transmitter. * Turn off transmitter.
* *
*--------------------------------------------------------------------*/ *--------------------------------------------------------------------*/
@ -1100,6 +1120,8 @@ static void xmit_morse (int c, packet_t pp, int wpm)
{ {
int info_len; int info_len;
unsigned char *pinfo; unsigned char *pinfo;
int length_ms, wait_ms;
double start_ptt, wait_until, now;
info_len = ax25_get_info (pp, &pinfo); info_len = ax25_get_info (pp, &pinfo);
@ -1108,8 +1130,22 @@ static void xmit_morse (int c, packet_t pp, int wpm)
dw_printf ("[%d.morse] \"%s\"\n", c, pinfo); dw_printf ("[%d.morse] \"%s\"\n", c, pinfo);
ptt_set (OCTYPE_PTT, c, 1); ptt_set (OCTYPE_PTT, c, 1);
start_ptt = dtime_now();
morse_send (c, (char*)pinfo, wpm, xmit_txdelay[c] * 10, xmit_txtail[c] * 10); // make txdelay at least 300 and txtail at least 250 ms.
length_ms = morse_send (c, (char*)pinfo, wpm, MAXX(xmit_txdelay[c] * 10, 300), MAXX(xmit_txtail[c] * 10, 250));
// there is probably still sound queued up in the output buffers.
wait_until = start_ptt + length_ms * 0.001;
now = dtime_now();
wait_ms = (int) ( ( wait_until - now ) * 1000 );
if (wait_ms > 0) {
SLEEP_MS(wait_ms);
}
ptt_set (OCTYPE_PTT, c, 0); ptt_set (OCTYPE_PTT, c, 0);
ax25_delete (pp); ax25_delete (pp);
@ -1117,6 +1153,72 @@ static void xmit_morse (int c, packet_t pp, int wpm)
} /* end xmit_morse */ } /* end xmit_morse */
/*-------------------------------------------------------------------
*
* Name: xmit_dtmf
*
* Purpose: After we have a clear channel, and possibly waited a random time,
* we transmit information part of frame as DTMF tones.
*
* Inputs: c - Channel number.
*
* pp - Packet object pointer.
* It will be deleted so caller should not try
* to reference it after this.
*
* speed - Button presses per second.
*
* Description: Turn on transmitter.
* Send text as touch tones.
* A small amount of quiet padding will appear at start and end.
* Turn off transmitter.
*
*--------------------------------------------------------------------*/
static void xmit_dtmf (int c, packet_t pp, int speed)
{
int info_len;
unsigned char *pinfo;
int length_ms, wait_ms;
double start_ptt, wait_until, now;
info_len = ax25_get_info (pp, &pinfo);
(void)info_len;
text_color_set(DW_COLOR_XMIT);
dw_printf ("[%d.dtmf] \"%s\"\n", c, pinfo);
ptt_set (OCTYPE_PTT, c, 1);
start_ptt = dtime_now();
// make txdelay at least 300 and txtail at least 250 ms.
length_ms = dtmf_send (c, (char*)pinfo, speed, MAXX(xmit_txdelay[c] * 10, 300), MAXX(xmit_txtail[c] * 10, 250));
// there is probably still sound queued up in the output buffers.
wait_until = start_ptt + length_ms * 0.001;
now = dtime_now();
wait_ms = (int) ( ( wait_until - now ) * 1000 );
if (wait_ms > 0) {
SLEEP_MS(wait_ms);
}
else {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Oops. CPU too slow to keep up with DTMF generation.\n");
}
ptt_set (OCTYPE_PTT, c, 0);
ax25_delete (pp);
} /* end xmit_dtmf */
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
* *
* Name: wait_for_clear_channel * Name: wait_for_clear_channel