mirror of https://github.com/wb2osz/direwolf.git
AX.25 v2.2 connected mode.
modified: CHANGES.md modified: Makefile.linux modified: Makefile.macosx modified: Makefile.win modified: README.md modified: atest.c modified: audio.h new file: ax25_link.c new file: ax25_link.h modified: ax25_pad.c modified: ax25_pad.h modified: ax25_pad2.c new file: cdigipeater.c new file: cdigipeater.h modified: config.c modified: config.h modified: digipeater.c modified: direwolf.c modified: direwolf.h modified: dlq.c modified: dlq.h modified: doc/Going-beyond-9600-baud.pdf modified: doc/Raspberry-Pi-APRS.pdf modified: doc/Raspberry-Pi-SDR-IGate.pdf modified: doc/User-Guide.pdf modified: gen_packets.c modified: hdlc_rec.c modified: hdlc_send.c modified: hdlc_send.h modified: igate.c modified: log.c modified: log.h modified: multi_modem.c modified: pfilter.c modified: pfilter.h modified: ptt.c modified: recv.c modified: serial_port.c modified: server.c modified: server.h modified: symbols-new.txt modified: tocalls.txt modified: tq.c modified: tq.h modified: waypoint.c modified: xid.c new file: xid.h modified: xmit.c
This commit is contained in:
parent
2a08f33966
commit
d85abe214f
14
CHANGES.md
14
CHANGES.md
|
@ -1,6 +1,20 @@
|
|||
|
||||
# Revision History #
|
||||
|
||||
|
||||
|
||||
----------
|
||||
|
||||
## Version 1.4 -- Development snapshot D -- November 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.
|
||||
|
||||
|
||||
### New Features: ###
|
||||
|
||||
- AX.25 v2.2 connected mode. See chapter 10 of User Guide for details.
|
||||
|
||||
|
||||
----------
|
||||
|
||||
## Version 1.4 -- Development snapshot C -- June 2016 ##
|
||||
|
|
|
@ -230,12 +230,12 @@ z := $(notdir ${CURDIR})
|
|||
|
||||
direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_psk.o demod_9600.o hdlc_rec.o \
|
||||
hdlc_rec2.o multi_modem.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o \
|
||||
fcs_calc.o ax25_pad.o ax25_pad2.o xid.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
gen_tone.o audio.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \
|
||||
gen_tone.o audio.o audio_stats.o digipeater.o cdigipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \
|
||||
ptt.o beacon.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o waypoint.o serial_port.o log.o telemetry.o \
|
||||
dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o mheard.o \
|
||||
dwgps.o dwgpsnmea.o dwgpsd.o dtime_now.o mheard.o ax25_link.o \
|
||||
misc.a geotranz.a
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
ifneq ($(enable_gpsd),)
|
||||
|
@ -588,7 +588,7 @@ install-rpi : dw-start.sh
|
|||
# Combine some unit tests into a single regression sanity check.
|
||||
|
||||
|
||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
|
||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
|
||||
|
||||
# Can we encode and decode at popular data rates?
|
||||
|
||||
|
@ -708,6 +708,15 @@ pad2test : ax25_pad2.c ax25_pad.c fcs_calc.o textcolor.o misc.a
|
|||
rm pad2test
|
||||
|
||||
|
||||
# Unit Test for XID frame encode/decode.
|
||||
|
||||
.PHONY: xidtest
|
||||
xidtest : xid.c textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -DXIDTEST -o $@ $^ $(LDFLAGS)
|
||||
./xidtest
|
||||
rm xidtest
|
||||
|
||||
|
||||
|
||||
# ----------------------------- Manual tests and experiments ---------------------------
|
||||
|
||||
|
|
|
@ -221,14 +221,14 @@ z := $(notdir ${CURDIR})
|
|||
|
||||
# Main application.
|
||||
|
||||
direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_pad.o beacon.o \
|
||||
direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_link.o ax25_pad.o ax25_pad2.o beacon.o \
|
||||
config.o decode_aprs.o dedupe.o demod_9600.o demod_afsk.o demod_psk.o \
|
||||
demod.o digipeater.o dlq.o dsp.o dtime_now.o dtmf.o dwgps.o \
|
||||
demod.o digipeater.o cdigipeater.o dlq.o dsp.o dtime_now.o dtmf.o dwgps.o \
|
||||
encode_aprs.o encode_aprs.o fcs_calc.o fcs_calc.o gen_tone.o \
|
||||
geotranz.a hdlc_rec.o hdlc_rec2.o hdlc_send.o igate.o kiss_frame.o \
|
||||
kiss.o kissnet.o latlong.o latlong.o log.o morse.o multi_modem.o \
|
||||
waypoint.o serial_port.o pfilter.o ptt.o rdq.o recv.o rrbb.o server.o \
|
||||
symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xmit.o \
|
||||
symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xid.o xmit.o \
|
||||
dwgps.o dwgpsnmea.o mheard.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lpthread $(LDLIBS) -lm
|
||||
|
||||
|
|
28
Makefile.win
28
Makefile.win
|
@ -16,7 +16,7 @@
|
|||
#
|
||||
|
||||
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc
|
||||
all : direwolf decode_aprs text2tt tt2text ll2utm utm2ll aclients log2gpx gen_packets atest ttcalc tnctest
|
||||
|
||||
|
||||
# People say we need -mthreads option for threads to work properly.
|
||||
|
@ -39,8 +39,6 @@ CFLAGS += -g
|
|||
#CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||
|
||||
|
||||
# TODO: Development in progress. Don't try using yet.
|
||||
#CFLAGS += -DNEW14
|
||||
|
||||
#
|
||||
# Let's see impact of various optimization levels.
|
||||
|
@ -86,16 +84,14 @@ demod_afsk.o : fsk_demod_state.h
|
|||
demod_psk.o : fsk_demod_state.h
|
||||
|
||||
|
||||
# later ax25_link.o
|
||||
|
||||
direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_psk.o demod_9600.o hdlc_rec.o \
|
||||
hdlc_rec2.o multi_modem.o rdq.o rrbb.o dlq.o \
|
||||
fcs_calc.o ax25_pad.o ax25_pad2.o \
|
||||
fcs_calc.o ax25_pad.o ax25_pad2.o xid.o \
|
||||
decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \
|
||||
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
||||
gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o cdigipeater.o pfilter.o dedupe.o tq.o xmit.o \
|
||||
ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \
|
||||
dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o waypoint.o serial_port.o log.o telemetry.o \
|
||||
dwgps.o dwgpsnmea.o dtime_now.o mheard.o \
|
||||
dwgps.o dwgpsnmea.o dtime_now.o mheard.o ax25_link.o \
|
||||
dw-icon.o regex.a misc.a geotranz.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
@ -260,7 +256,7 @@ strlcat.o : misc/strlcat.c
|
|||
# Combine some unit tests into a single regression sanity check.
|
||||
|
||||
|
||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
|
||||
check : dtest ttest tttexttest pftest tlmtest lltest enctest kisstest pad2test xidtest check-modem1200 check-modem300 check-modem9600 check-modem19200 check-modem2400 check-modem4800
|
||||
|
||||
# Can we encode and decode at popular data rates?
|
||||
# Verify that single bit fixup increases the count.
|
||||
|
@ -317,6 +313,7 @@ atest : atest.c fsk_fast_filter.h demod.c demod_afsk.c demod_psk.c demod_9600.c
|
|||
rrbb.o fcs_calc.o ax25_pad.o decode_aprs.o \
|
||||
dwgpsnmea.o dwgps.o serial_port.o latlong.c \
|
||||
symbols.c tt_text.c textcolor.c telemetry.c dtime_now.o \
|
||||
decode_aprs.o log.o \
|
||||
misc.a regex.a
|
||||
echo " " > tune.h
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
@ -412,10 +409,23 @@ pad2test : ax25_pad2.c ax25_pad.c fcs_calc.o textcolor.o regex.a misc.a
|
|||
./pad2test
|
||||
rm pad2test.exe
|
||||
|
||||
# Unit Test for XID frame encode/decode.
|
||||
|
||||
.PHONY: xidtest
|
||||
xidtest : xid.c textcolor.o misc.a
|
||||
$(CC) $(CFLAGS) -DXIDTEST -o $@ $^
|
||||
./xidtest
|
||||
rm xidtest.exe
|
||||
|
||||
|
||||
|
||||
# ------------------------------ Other manual testing & experimenting -------------------------------
|
||||
|
||||
|
||||
tnctest : tnctest.c textcolor.o dtime_now.o serial_port.o misc.a
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32
|
||||
|
||||
|
||||
# For tweaking the demodulator.
|
||||
|
||||
demod.o : tune.h
|
||||
|
|
|
@ -42,6 +42,8 @@ Decodes more than 1000 error-free frames from [WA8LMF TNC Test CD](http://wa8lmf
|
|||
|
||||
- Includes separate raw packet decoder, decode_aprs.
|
||||
|
||||
- AX.25 v2.2 connected mode. (New in version 1.4.)
|
||||
|
||||
- Open source so you can see how it works and make your own modifications.
|
||||
|
||||
- Runs in 3 different environments:
|
||||
|
@ -95,4 +97,4 @@ Here are some good places to share information:
|
|||
- [TAPR aprssig](http://www.tapr.org/pipermail/aprssig/)
|
||||
|
||||
|
||||
|
||||
The github "issues" section is for reporting software defects and enhancement requests. It is NOT a place to ask questions or have general discussions. Please use one of the locations above.
|
||||
|
|
18
atest.c
18
atest.c
|
@ -743,6 +743,24 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
|
|||
ax25_safe_print ((char *)pinfo, info_len, 0);
|
||||
dw_printf ("\n");
|
||||
|
||||
#if 1 // temp experiment TODO: remove this.
|
||||
|
||||
#include "decode_aprs.h"
|
||||
#include "log.h"
|
||||
|
||||
if (ax25_is_aprs(pp)) {
|
||||
|
||||
decode_aprs_t A;
|
||||
|
||||
decode_aprs (&A, pp, 0);
|
||||
|
||||
// Temp experiment to see how different systems set the RR bits in the source and destination.
|
||||
// log_rr_bits (&A, pp);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
ax25_delete (pp);
|
||||
|
||||
} /* end fake dlq_append */
|
||||
|
|
14
audio.h
14
audio.h
|
@ -89,6 +89,14 @@ struct audio_s {
|
|||
/* statistics reports. This is set by */
|
||||
/* the "-a" option. 0 to disable feature. */
|
||||
|
||||
int xmit_error_rate; /* For testing purposes, we can generate frames with an invalid CRC */
|
||||
/* to simulate corruption while going over the air. */
|
||||
/* This is the probability, in per cent, of randomly corrupting it. */
|
||||
/* Normally this is 0. 25 would mean corrupt it 25% of the time. */
|
||||
|
||||
int recv_error_rate; /* Similar but the % probablity of dropping a received frame. */
|
||||
|
||||
|
||||
/* Properties for each audio channel, common to receive and transmit. */
|
||||
/* Can be different for each radio channel. */
|
||||
|
||||
|
@ -166,18 +174,20 @@ struct audio_s {
|
|||
int passall; /* Allow thru even with bad CRC. */
|
||||
|
||||
|
||||
|
||||
/* Additional properties for transmit. */
|
||||
|
||||
/* Originally we had control outputs only for PTT. */
|
||||
/* In version 1.2, we generalize this to allow others such as DCD. */
|
||||
/* In version 1.4 we add CON for connected to another station. */
|
||||
/* Index following structure by one of these: */
|
||||
|
||||
|
||||
#define OCTYPE_PTT 0
|
||||
#define OCTYPE_DCD 1
|
||||
#define OCTYPE_FUTURE 2
|
||||
#define OCTYPE_CON 2
|
||||
|
||||
#define NUM_OCTYPES 4 /* number of values above */
|
||||
#define NUM_OCTYPES 4 /* number of values above. WHY IS THIS NOT 3? */
|
||||
|
||||
struct {
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,82 @@
|
|||
|
||||
/* ax25_link.h */
|
||||
|
||||
|
||||
#ifndef AX25_LINK_H
|
||||
#define AX25_LINK_H 1
|
||||
|
||||
#include "ax25_pad.h" // for AX25_MAX_INFO_LEN
|
||||
|
||||
#include "dlq.h" // for dlq_item_t
|
||||
|
||||
#include "config.h" // for struct misc_config_s
|
||||
|
||||
|
||||
|
||||
// Limits and defaults for parameters.
|
||||
|
||||
|
||||
#define AX25_N1_PACLEN_MIN 1 // Max bytes in Information part of frame.
|
||||
#define AX25_N1_PACLEN_DEFAULT 256 // some v2.0 implementations have 128
|
||||
#define AX25_N1_PACLEN_MAX AX25_MAX_INFO_LEN // from ax25_pad.h
|
||||
|
||||
|
||||
#define AX25_N2_RETRY_MIN 1 // Number of times to retry before giving up.
|
||||
#define AX25_N2_RETRY_DEFAULT 10
|
||||
#define AX25_N2_RETRY_MAX 15
|
||||
|
||||
|
||||
#define AX25_T1V_FRACK_MIN 1 // Number of seconds to wait before retrying.
|
||||
#define AX25_T1V_FRACK_DEFAULT 3 // KPC-3+ has 4. TM-D710A has 3.
|
||||
#define AX25_T1V_FRACK_MAX 15
|
||||
|
||||
|
||||
#define AX25_K_MAXFRAME_BASIC_MIN 1 // Window size - number of I frames to send before waiting for ack.
|
||||
#define AX25_K_MAXFRAME_BASIC_DEFAULT 4
|
||||
#define AX25_K_MAXFRAME_BASIC_MAX 7
|
||||
|
||||
#define AX25_K_MAXFRAME_EXTENDED_MIN 1
|
||||
#define AX25_K_MAXFRAME_EXTENDED_DEFAULT 32
|
||||
#define AX25_K_MAXFRAME_EXTENDED_MAX 63 // In theory 127 but I'm restricting as explained in SREJ handling.
|
||||
|
||||
|
||||
|
||||
// Call once at startup time.
|
||||
|
||||
void ax25_link_init (struct misc_config_s *pconfig);
|
||||
|
||||
|
||||
|
||||
// IMPORTANT:
|
||||
|
||||
// These functions must be called on a single thread, one at a time.
|
||||
// The Data Link Queue (DLQ) is used to serialize events from multiple sources.
|
||||
|
||||
|
||||
void dl_connect_request (dlq_item_t *E);
|
||||
|
||||
void dl_disconnect_request (dlq_item_t *E);
|
||||
|
||||
void dl_data_request (dlq_item_t *E);
|
||||
|
||||
void dl_register_callsign (dlq_item_t *E);
|
||||
|
||||
void dl_unregister_callsign (dlq_item_t *E);
|
||||
|
||||
void dl_client_cleanup (dlq_item_t *E);
|
||||
|
||||
|
||||
void lm_data_indication (dlq_item_t *E);
|
||||
|
||||
void lm_channel_busy (dlq_item_t *E);
|
||||
|
||||
|
||||
void dl_timer_expiry (void);
|
||||
|
||||
|
||||
double ax25_link_get_next_timer_expiry (void);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/* end ax25_link.h */
|
192
ax25_pad.c
192
ax25_pad.c
|
@ -24,6 +24,10 @@
|
|||
*
|
||||
* Purpose: Packet assembler and disasembler.
|
||||
*
|
||||
* This was written when I was only concerned about APRS which
|
||||
* uses only UI frames. ax25_pad2.c, added years later, has
|
||||
* functions for dealing with other types of frames.
|
||||
*
|
||||
* We can obtain AX.25 packets from different sources:
|
||||
*
|
||||
* (a) from an HDLC frame.
|
||||
|
@ -77,11 +81,24 @@
|
|||
* SSID = substation ID
|
||||
* 0 = zero
|
||||
*
|
||||
* The AX.25 spec states that the RR bits should be 11 if not used.
|
||||
* There are a couple documents talking about possible uses for APRS.
|
||||
* I'm ignoring them for now.
|
||||
* http://www.aprs.org/aprs12/preemptive-digipeating.txt
|
||||
* http://www.aprs.org/aprs12/RR-bits.txt
|
||||
*
|
||||
* I don't recall why I originally intended to set the source/destination C bits both to 1.
|
||||
* Reviewing this 5 years later, after spending more time delving into the
|
||||
* AX.25 spec, I think it should be 1 for destination and 0 for source.
|
||||
* In practice you see all four combinations being used by APRS stations
|
||||
* and no one really cares about these two bits.
|
||||
*
|
||||
* The final octet of the Source has the form:
|
||||
*
|
||||
* C R R SSID 0, where,
|
||||
*
|
||||
* C = command/response = 1
|
||||
* C = command/response = 1 (originally, now I think it should be 0 for source.)
|
||||
* (Haven't gone back to check to see what code actually does.)
|
||||
* R R = Reserved = 1 1
|
||||
* SSID = substation ID
|
||||
* 0 = zero (or 1 if no repeaters)
|
||||
|
@ -165,8 +182,8 @@ char *strtok_r(char *str, const char *delim, char **saveptr);
|
|||
#endif
|
||||
|
||||
|
||||
#include "ax25_pad.h"
|
||||
#include "textcolor.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "fcs_calc.h"
|
||||
|
||||
/*
|
||||
|
@ -236,7 +253,13 @@ packet_t ax25_new (void)
|
|||
/*
|
||||
* check for memory leak.
|
||||
*/
|
||||
if (new_count > delete_count + 100) {
|
||||
|
||||
// version 1.4 push up the threshold. We could have considerably more with connected mode.
|
||||
|
||||
//if (new_count > delete_count + 100) {
|
||||
if (new_count > delete_count + 256) {
|
||||
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Report to WB2OSZ - Memory leak for packet objects. new=%d, delete=%d\n", new_count, delete_count);
|
||||
#if AX25MEMDEBUG
|
||||
|
@ -434,7 +457,7 @@ packet_t ax25_from_text (char *monitor, int strict)
|
|||
this_p->frame_data[AX25_SOURCE*7+6] = SSID_H_MASK | SSID_RR_MASK | SSID_LAST_MASK;
|
||||
|
||||
this_p->frame_data[14] = AX25_UI_FRAME;
|
||||
this_p->frame_data[15] = AX25_NO_LAYER_3;
|
||||
this_p->frame_data[15] = AX25_PID_NO_LAYER_3;
|
||||
|
||||
this_p->frame_len = 7 + 7 + 1 + 1;
|
||||
this_p->num_addr = (-1);
|
||||
|
@ -1517,6 +1540,39 @@ int ax25_get_first_not_repeated(packet_t this_p)
|
|||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_get_rr
|
||||
*
|
||||
* Purpose: Return the two reserved "RR" bits in the specified address field.
|
||||
*
|
||||
* Inputs: pp - Packet object.
|
||||
*
|
||||
* n - Index of address. Use the symbols
|
||||
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
|
||||
*
|
||||
* Returns: 0, 1, 2, or 3.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
int ax25_get_rr (packet_t this_p, int n)
|
||||
{
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
assert (n >= 0 && n < this_p->num_addr);
|
||||
|
||||
if (n >= 0 && n < this_p->num_addr) {
|
||||
return ((this_p->frame_data[n*7+6] & SSID_RR_MASK) >> SSID_RR_SHIFT);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error: ax25_get_rr(%d), num_addr=%d\n", n, this_p->num_addr);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_get_info
|
||||
|
@ -1678,6 +1734,25 @@ double ax25_get_release_time (packet_t this_p)
|
|||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: ax25_set_modulo
|
||||
*
|
||||
* Purpose: Set modulo value for I and S frame sequence numbers.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
void ax25_set_modulo (packet_t this_p, int modulo)
|
||||
{
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
this_p->modulo = modulo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
|
@ -1793,12 +1868,9 @@ int ax25_pack (packet_t this_p, unsigned char result[AX25_MAX_PACKET_LEN])
|
|||
*
|
||||
* Inputs: this_p - pointer to packet object.
|
||||
*
|
||||
* modulo - We often need to know this because context is // TODO: remove this - return cr instead.
|
||||
* required to know if control is 1 or 2 bytes.
|
||||
*
|
||||
* Outputs: desc - Text description such as "I frame" or
|
||||
* "U frame SABME".
|
||||
* Supply 16 bytes to be safe.
|
||||
* Supply 40 bytes to be safe.
|
||||
*
|
||||
* cr - Command or response?
|
||||
*
|
||||
|
@ -1813,13 +1885,14 @@ int ax25_pack (packet_t this_p, unsigned char result[AX25_MAX_PACKET_LEN])
|
|||
*------------------------------------------------------------------*/
|
||||
|
||||
// TODO: need someway to ensure caller allocated enough space.
|
||||
#define DESC_SIZ 32
|
||||
// Should pass in as parameter.
|
||||
#define DESC_SIZ 40
|
||||
|
||||
|
||||
ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns)
|
||||
{
|
||||
int c; // U frames are always one control byte.
|
||||
int c2; // I & S frames can have second Control byte.
|
||||
int c2 = 0; // I & S frames can have second Control byte.
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
@ -1835,6 +1908,41 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *desc, in
|
|||
strlcpy (desc, "Not AX.25", DESC_SIZ);
|
||||
return (frame_not_AX25);
|
||||
}
|
||||
|
||||
/*
|
||||
* TERRIBLE HACK :-( for display purposes.
|
||||
*
|
||||
* I and S frames can have 1 or 2 control bytes but there is
|
||||
* no good way to determine this without dipping into the data
|
||||
* link state machine. Can we guess?
|
||||
*
|
||||
* S frames have no protocol id or information so if there is one
|
||||
* more byte beyond the control field, we could assume there are
|
||||
* two control bytes.
|
||||
*
|
||||
* For I frames, the protocol id will usually be 0xf0. If we find
|
||||
* that as the first byte of the information field, it is probably
|
||||
* the pid and not part of the information. Ditto for segments 0x08.
|
||||
* Not fool proof but good enough for troubleshooting text out.
|
||||
*
|
||||
* If we have a link to the peer station, this will be set properly
|
||||
* before it needs to be used for other reasons.
|
||||
*
|
||||
* Setting one of the RR bits (find reference!) is sounding better and better.
|
||||
* It's in common usage so I should lobby to get that in the official protocol spec.
|
||||
*/
|
||||
|
||||
if (this_p->modulo == 0 && (c & 3) == 1 && ax25_get_c2(this_p) != -1) {
|
||||
this_p->modulo = modulo_128;
|
||||
}
|
||||
else if (this_p->modulo == 0 && (c & 1) == 0 && this_p->frame_data[ax25_get_info_offset(this_p)] == 0xF0) {
|
||||
this_p->modulo = modulo_128;
|
||||
}
|
||||
else if (this_p->modulo == 0 && (c & 1) == 0 && this_p->frame_data[ax25_get_info_offset(this_p)] == 0x08) { // same for segments
|
||||
this_p->modulo = modulo_128;
|
||||
}
|
||||
|
||||
|
||||
if (this_p->modulo == modulo_128) {
|
||||
c2 = ax25_get_c2 (this_p);
|
||||
}
|
||||
|
@ -1868,7 +1976,9 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *desc, in
|
|||
*pf = (c >> 4) & 1;
|
||||
*nr = (c >> 5) & 7;
|
||||
}
|
||||
snprintf (desc, DESC_SIZ, "I %s, n(s)= %d, n(r)=%d, %s=%d", cr_text, *ns, *nr, pf_text, *pf);
|
||||
|
||||
//snprintf (desc, DESC_SIZ, "I %s, n(s)=%d, n(r)=%d, %s=%d", cr_text, *ns, *nr, pf_text, *pf);
|
||||
snprintf (desc, DESC_SIZ, "I %s, n(s)=%d, n(r)=%d, %s=%d, pid=0x%02x", cr_text, *ns, *nr, pf_text, *pf, ax25_get_pid(this_p));
|
||||
return (frame_type_I);
|
||||
}
|
||||
else if ((c & 2) == 0) {
|
||||
|
@ -1960,6 +2070,7 @@ static void hex_dump (unsigned char *p, int len)
|
|||
}
|
||||
|
||||
/* Text description of control octet. */
|
||||
// FIXME: this is wrong. It doesn't handle modulo 128.
|
||||
|
||||
// TODO: use ax25_frame_type() instead.
|
||||
|
||||
|
@ -2108,10 +2219,12 @@ int ax25_is_aprs (packet_t this_p)
|
|||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
if (this_p->frame_len == 0) return(0);
|
||||
|
||||
ctrl = ax25_get_control(this_p);
|
||||
pid = ax25_get_pid(this_p);
|
||||
|
||||
is_aprs = this_p->num_addr >= 2 && ctrl == AX25_UI_FRAME && pid == AX25_NO_LAYER_3;
|
||||
is_aprs = this_p->num_addr >= 2 && ctrl == AX25_UI_FRAME && pid == AX25_PID_NO_LAYER_3;
|
||||
|
||||
#if 0
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
|
@ -2120,6 +2233,41 @@ int ax25_is_aprs (packet_t this_p)
|
|||
return (is_aprs);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ax25_is_null_frame
|
||||
*
|
||||
* Purpose: Is this packet structure empty?
|
||||
*
|
||||
* Inputs: this_p - pointer to packet object.
|
||||
*
|
||||
* Returns: True if frame data length is 0.
|
||||
*
|
||||
* Description: This is used when we want to wake up the
|
||||
* transmit queue processing thread but don't
|
||||
* want to transmit a frame.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
|
||||
int ax25_is_null_frame (packet_t this_p)
|
||||
{
|
||||
int is_null;
|
||||
|
||||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
is_null = this_p->frame_len == 0;
|
||||
|
||||
#if 0
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ax25_is_null_frame(): is_null=%d\n", is_null);
|
||||
#endif
|
||||
return (is_null);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: ax25_get_control
|
||||
|
@ -2140,6 +2288,8 @@ int ax25_get_control (packet_t this_p)
|
|||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
if (this_p->frame_len == 0) return(-1);
|
||||
|
||||
if (this_p->num_addr >= 2) {
|
||||
return (this_p->frame_data[ax25_get_control_offset(this_p)]);
|
||||
}
|
||||
|
@ -2151,10 +2301,19 @@ int ax25_get_c2 (packet_t this_p)
|
|||
assert (this_p->magic1 == MAGIC);
|
||||
assert (this_p->magic2 == MAGIC);
|
||||
|
||||
if (this_p->frame_len == 0) return(-1);
|
||||
|
||||
if (this_p->num_addr >= 2) {
|
||||
return (this_p->frame_data[ax25_get_control_offset(this_p)+1]);
|
||||
int offset2 = ax25_get_control_offset(this_p)+1;
|
||||
|
||||
if (offset2 < this_p->frame_len) {
|
||||
return (this_p->frame_data[offset2]);
|
||||
}
|
||||
else {
|
||||
return (-1); /* attempt to go beyond the end of frame. */
|
||||
}
|
||||
}
|
||||
return (-1);
|
||||
return (-1); /* not AX.25 */
|
||||
}
|
||||
|
||||
|
||||
|
@ -2184,6 +2343,8 @@ int ax25_get_pid (packet_t this_p)
|
|||
// TODO: handle 2 control byte case.
|
||||
// TODO: sanity check: is it I or UI frame?
|
||||
|
||||
if (this_p->frame_len == 0) return(-1);
|
||||
|
||||
if (this_p->num_addr >= 2) {
|
||||
return (this_p->frame_data[ax25_get_pid_offset(this_p)]);
|
||||
}
|
||||
|
@ -2372,7 +2533,8 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
|
|||
if (len > MAXSAFE)
|
||||
len = MAXSAFE;
|
||||
|
||||
while (len > 0 && *pstr != '\0')
|
||||
//while (len > 0 && *pstr != '\0')
|
||||
while (len > 0)
|
||||
{
|
||||
ch = *((unsigned char *)pstr);
|
||||
|
||||
|
|
57
ax25_pad.h
57
ax25_pad.h
|
@ -64,8 +64,10 @@
|
|||
*/
|
||||
|
||||
#define AX25_UI_FRAME 3 /* Control field value. */
|
||||
#define AX25_NO_LAYER_3 0xf0 /* protocol ID */
|
||||
|
||||
#define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */
|
||||
#define AX25_PID_SEGMENTATION_FRAGMENT 0x08
|
||||
#define AX25_PID_ESCAPE_CHARACTER 0xff
|
||||
|
||||
|
||||
#ifdef AX25_PAD_C /* Keep this hidden - implementation could change. */
|
||||
|
@ -159,9 +161,11 @@ typedef struct packet_s *packet_t;
|
|||
typedef enum cmdres_e { cr_00 = 2, cr_cmd = 1, cr_res = 0, cr_11 = 3 } cmdres_t;
|
||||
|
||||
|
||||
extern packet_t ax25_new (void);
|
||||
|
||||
|
||||
#ifdef AX25_PAD_C /* Keep this hidden - implementation could change. */
|
||||
|
||||
extern packet_t ax25_new (void);
|
||||
|
||||
/*
|
||||
* APRS always has one control octet of 0x03 but the more
|
||||
|
@ -169,6 +173,8 @@ extern packet_t ax25_new (void);
|
|||
* whether "modulo 128 operation" is in effect.
|
||||
*/
|
||||
|
||||
//#define DEBUGX 1
|
||||
|
||||
static inline int ax25_get_control_offset (packet_t this_p)
|
||||
{
|
||||
return (this_p->num_addr*7);
|
||||
|
@ -176,7 +182,29 @@ static inline int ax25_get_control_offset (packet_t this_p)
|
|||
|
||||
static inline int ax25_get_num_control (packet_t this_p)
|
||||
{
|
||||
return (this_p->modulo == 128 ? 2 : 1);
|
||||
int c;
|
||||
|
||||
c = this_p->frame_data[ax25_get_control_offset(this_p)];
|
||||
|
||||
if ( (c & 0x01) == 0 ) { /* I xxxx xxx0 */
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_control, %02x is I frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1);
|
||||
#endif
|
||||
return ((this_p->modulo == 128) ? 2 : 1);
|
||||
}
|
||||
|
||||
if ( (c & 0x03) == 1 ) { /* S xxxx xx01 */
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_control, %02x is S frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1);
|
||||
#endif
|
||||
return ((this_p->modulo == 128) ? 2 : 1);
|
||||
}
|
||||
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_control, %02x is U frame, always returns 1.\n", c);
|
||||
#endif
|
||||
|
||||
return (1); /* U xxxx xx11 */
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,11 +230,17 @@ static int ax25_get_num_pid (packet_t this_p)
|
|||
c == 0x03 || c == 0x13) { /* UI 000x 0011 */
|
||||
|
||||
pid = this_p->frame_data[ax25_get_pid_offset(this_p)];
|
||||
if (pid == 0xff) {
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_pid, %02x is I or UI frame, pid = %02x, returns %d\n", c, pid, (pid==AX25_PID_ESCAPE_CHARACTER) ? 2 : 1);
|
||||
#endif
|
||||
if (pid == AX25_PID_ESCAPE_CHARACTER) {
|
||||
return (2); /* pid 1111 1111 means another follows. */
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_num_pid, %02x is neither I nor UI frame, returns 0\n", c);
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -225,7 +259,11 @@ static int ax25_get_num_pid (packet_t this_p)
|
|||
|
||||
static inline int ax25_get_info_offset (packet_t this_p)
|
||||
{
|
||||
return (ax25_get_control_offset (this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p));
|
||||
int offset = ax25_get_control_offset (this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p);
|
||||
#if DEBUGX
|
||||
dw_printf ("ax25_get_info_offset, returns %d\n", offset);
|
||||
#endif
|
||||
return (offset);
|
||||
}
|
||||
|
||||
static inline int ax25_get_num_info (packet_t this_p)
|
||||
|
@ -249,7 +287,7 @@ typedef enum ax25_modulo_e { modulo_8 = 8, modulo_128 = 128 } ax25_modulo_t;
|
|||
|
||||
typedef enum ax25_frame_type_e {
|
||||
|
||||
frame_type_I, // Information
|
||||
frame_type_I = 0, // Information
|
||||
|
||||
frame_type_S_RR, // Receive Ready - System Ready To Receive
|
||||
frame_type_S_RNR, // Receive Not Ready - TNC Buffer Full
|
||||
|
@ -268,6 +306,8 @@ typedef enum ax25_frame_type_e {
|
|||
frame_type_U, // other Unnumbered, not used by AX.25.
|
||||
|
||||
frame_not_AX25 // Could not get control byte from frame.
|
||||
// This must be last because value plus 1 is
|
||||
// for the size of an array.
|
||||
|
||||
} ax25_frame_type_t;
|
||||
|
||||
|
@ -354,6 +394,8 @@ extern int ax25_get_heard(packet_t this_p);
|
|||
|
||||
extern int ax25_get_first_not_repeated(packet_t pp);
|
||||
|
||||
extern int ax25_get_rr (packet_t this_p, int n);
|
||||
|
||||
extern int ax25_get_info (packet_t pp, unsigned char **paddr);
|
||||
|
||||
extern void ax25_set_nextp (packet_t this_p, packet_t next_p);
|
||||
|
@ -365,6 +407,8 @@ extern packet_t ax25_get_nextp (packet_t this_p);
|
|||
extern void ax25_set_release_time (packet_t this_p, double release_time);
|
||||
extern double ax25_get_release_time (packet_t this_p);
|
||||
|
||||
extern void ax25_set_modulo (packet_t this_p, int modulo);
|
||||
|
||||
extern void ax25_format_addrs (packet_t pp, char *);
|
||||
|
||||
extern int ax25_pack (packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]);
|
||||
|
@ -374,6 +418,7 @@ extern ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *d
|
|||
extern void ax25_hex_dump (packet_t this_p);
|
||||
|
||||
extern int ax25_is_aprs (packet_t pp);
|
||||
extern int ax25_is_null_frame (packet_t this_p);
|
||||
|
||||
extern int ax25_get_control (packet_t this_p);
|
||||
extern int ax25_get_c2 (packet_t this_p);
|
||||
|
|
16
ax25_pad2.c
16
ax25_pad2.c
|
@ -254,7 +254,7 @@ packet_t ax25_u_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
if (t != 2) {
|
||||
if (cr != t) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: U frame, cr is %d but must be %d.\n", __func__, cr, t);
|
||||
dw_printf ("Internal error in %s: U frame, cr is %d but must be %d. ftype=%d\n", __func__, cr, t, ftype);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,7 +270,7 @@ packet_t ax25_u_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
if (pid < 0 || pid == 0 || pid == 0xff) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: U frame, Invalid pid value 0x%02x.\n", __func__, pid);
|
||||
pid = AX25_NO_LAYER_3;
|
||||
pid = AX25_PID_NO_LAYER_3;
|
||||
}
|
||||
*p++ = pid;
|
||||
this_p->frame_len++;
|
||||
|
@ -303,7 +303,7 @@ packet_t ax25_u_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
#if PAD2TEST
|
||||
ax25_frame_type_t check_ftype;
|
||||
cmdres_t check_cr;
|
||||
char check_desc[32];
|
||||
char check_desc[80];
|
||||
int check_pf;
|
||||
int check_nr;
|
||||
int check_ns;
|
||||
|
@ -444,7 +444,7 @@ packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
|
||||
ax25_frame_type_t check_ftype;
|
||||
cmdres_t check_cr;
|
||||
char check_desc[32];
|
||||
char check_desc[80];
|
||||
int check_pf;
|
||||
int check_nr;
|
||||
int check_ns;
|
||||
|
@ -573,9 +573,9 @@ packet_t ax25_i_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
// or 0xff (which means more bytes follow).
|
||||
|
||||
if (pid < 0 || pid == 0 || pid == 0xff) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal error in %s: I frame, Invalid pid value 0x%02x.\n", __func__, pid);
|
||||
pid = AX25_NO_LAYER_3;
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("Warning: Client application provided invalid PID value, 0x%02x, for I frame.\n", pid);
|
||||
pid = AX25_PID_NO_LAYER_3;
|
||||
}
|
||||
*p++ = pid;
|
||||
this_p->frame_len++;
|
||||
|
@ -601,7 +601,7 @@ packet_t ax25_i_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_ad
|
|||
|
||||
ax25_frame_type_t check_ftype;
|
||||
cmdres_t check_cr;
|
||||
char check_desc[32];
|
||||
char check_desc[80];
|
||||
int check_pf;
|
||||
int check_nr;
|
||||
int check_ns;
|
||||
|
|
|
@ -0,0 +1,325 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2016 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdigipeater.c
|
||||
*
|
||||
* Purpose: Act as an digital repeater for connected AX.25 mode.
|
||||
* Similar digipeater.c is for APRS.
|
||||
*
|
||||
*
|
||||
* Description: Decide whether the specified packet should
|
||||
* be digipeated. Put my callsign in the digipeater field used.
|
||||
*
|
||||
* APRS and connected mode were two split into two
|
||||
* separate files. Yes, there is duplicate code but they
|
||||
* are significantly different and I thought it would be
|
||||
* too confusing to munge them together.
|
||||
*
|
||||
* References: The Ax.25 protcol barely mentions digipeaters and
|
||||
* and doesn't describe how they should work.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#define CDIGIPEATER_C
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h> /* for isdigit, isupper */
|
||||
#include "regex.h"
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include "ax25_pad.h"
|
||||
#include "cdigipeater.h"
|
||||
#include "textcolor.h"
|
||||
#include "tq.h"
|
||||
#include "pfilter.h"
|
||||
|
||||
|
||||
static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
||||
regex_t *alias, int to_chan, char *filter_str);
|
||||
|
||||
|
||||
/*
|
||||
* Keep pointer to configuration options.
|
||||
* Set by cdigipeater_init and used later.
|
||||
*/
|
||||
|
||||
|
||||
static struct audio_s *save_audio_config_p;
|
||||
static struct cdigi_config_s *save_cdigi_config_p;
|
||||
|
||||
|
||||
/*
|
||||
* Maintain count of packets digipeated for each combination of from/to channel.
|
||||
*/
|
||||
|
||||
static int cdigi_count[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
int cdigipeater_get_count (int from_chan, int to_chan) {
|
||||
return (cdigi_count[from_chan][to_chan]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdigipeater_init
|
||||
*
|
||||
* Purpose: Initialize with stuff from configuration file.
|
||||
*
|
||||
* Inputs: p_audio_config - Configuration for audio channels.
|
||||
*
|
||||
* p_cdigi_config - Connected Digipeater configuration details.
|
||||
*
|
||||
* Outputs: Save pointers to configuration for later use.
|
||||
*
|
||||
* Description: Called once at application startup time.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
void cdigipeater_init (struct audio_s *p_audio_config, struct cdigi_config_s *p_cdigi_config)
|
||||
{
|
||||
save_audio_config_p = p_audio_config;
|
||||
save_cdigi_config_p = p_cdigi_config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdigipeater
|
||||
*
|
||||
* Purpose: Re-transmit packet if it matches the rules.
|
||||
*
|
||||
* Inputs: chan - Radio channel where it was received.
|
||||
*
|
||||
* pp - Packet object.
|
||||
*
|
||||
* Returns: None.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
void cdigipeater (int from_chan, packet_t pp)
|
||||
{
|
||||
int to_chan;
|
||||
|
||||
|
||||
if ( from_chan < 0 || from_chan >= MAX_CHANS || ( ! save_audio_config_p->achan[from_chan].valid) ) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("cdigipeater: Did not expect to receive on invalid channel %d.\n", from_chan);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* First pass: Look at packets being digipeated to same channel.
|
||||
*
|
||||
* There was a reason for two passes for APRS.
|
||||
* Might not have a benefit here.
|
||||
*/
|
||||
|
||||
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
|
||||
if (save_cdigi_config_p->enabled[from_chan][to_chan]) {
|
||||
if (to_chan == from_chan) {
|
||||
packet_t result;
|
||||
|
||||
result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
||||
save_audio_config_p->achan[to_chan].mycall,
|
||||
&save_cdigi_config_p->alias[from_chan][to_chan], to_chan,
|
||||
save_cdigi_config_p->filter_str[from_chan][to_chan]);
|
||||
if (result != NULL) {
|
||||
tq_append (to_chan, TQ_PRIO_0_HI, result);
|
||||
cdigi_count[from_chan][to_chan]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Second pass: Look at packets being digipeated to different channel.
|
||||
*/
|
||||
|
||||
for (to_chan=0; to_chan<MAX_CHANS; to_chan++) {
|
||||
if (save_cdigi_config_p->enabled[from_chan][to_chan]) {
|
||||
if (to_chan != from_chan) {
|
||||
packet_t result;
|
||||
|
||||
result = cdigipeat_match (from_chan, pp, save_audio_config_p->achan[from_chan].mycall,
|
||||
save_audio_config_p->achan[to_chan].mycall,
|
||||
&save_cdigi_config_p->alias[from_chan][to_chan], to_chan,
|
||||
save_cdigi_config_p->filter_str[from_chan][to_chan]);
|
||||
if (result != NULL) {
|
||||
tq_append (to_chan, TQ_PRIO_0_HI, result);
|
||||
cdigi_count[from_chan][to_chan]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* end cdigipeater */
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdigipeat_match
|
||||
*
|
||||
* Purpose: A simple digipeater for connected mode AX.25.
|
||||
*
|
||||
* Input: pp - Pointer to a packet object.
|
||||
*
|
||||
* mycall_rec - Call of my station, with optional SSID,
|
||||
* associated with the radio channel where the
|
||||
* packet was received.
|
||||
*
|
||||
* mycall_xmit - Call of my station, with optional SSID,
|
||||
* associated with the radio channel where the
|
||||
* packet is to be transmitted. Could be the same as
|
||||
* mycall_rec or different.
|
||||
*
|
||||
* alias - Compiled pattern for my station aliases.
|
||||
* Could be NULL if no aliases.
|
||||
*
|
||||
* to_chan - Channel number that we are transmitting to.
|
||||
*
|
||||
* filter_str - Filter expression string or NULL.
|
||||
*
|
||||
* Returns: Packet object for transmission or NULL.
|
||||
* The original packet is not modified. The caller is responsible for freeing it.
|
||||
* We make a copy and return that modified copy!
|
||||
* This is very important because we could digipeat from one channel to many.
|
||||
*
|
||||
* Description: The packet will be digipeated if the next unused digipeater
|
||||
* field matches one of the following:
|
||||
*
|
||||
* - mycall_rec
|
||||
* - alias list
|
||||
*
|
||||
* APRS digipeating drops duplicates within 30 seconds but we don't do that here.
|
||||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static packet_t cdigipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
||||
regex_t *alias, int to_chan, char *filter_str)
|
||||
{
|
||||
int r;
|
||||
char repeater[AX25_MAX_ADDR_LEN];
|
||||
int err;
|
||||
char err_msg[100];
|
||||
|
||||
/*
|
||||
* First check if filtering has been configured.
|
||||
*/
|
||||
|
||||
if (filter_str != NULL) {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Find the first repeater station which doesn't have "has been repeated" set.
|
||||
*
|
||||
* r = index of the address position in the frame.
|
||||
*/
|
||||
r = ax25_get_first_not_repeated(pp);
|
||||
|
||||
if (r < AX25_REPEATER_1) {
|
||||
return (NULL); // Nothing to do.
|
||||
}
|
||||
|
||||
ax25_get_addr_with_ssid(pp, r, repeater);
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("First unused digipeater is %s\n", repeater);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* First check for explicit use of my call.
|
||||
* Note that receive and transmit channels could have different callsigns.
|
||||
*/
|
||||
|
||||
if (strcmp(repeater, mycall_rec) == 0) {
|
||||
packet_t result;
|
||||
|
||||
result = ax25_dup (pp);
|
||||
assert (result != NULL);
|
||||
|
||||
/* If using multiple radio channels, they could have different calls. */
|
||||
|
||||
ax25_set_addr (result, r, mycall_xmit);
|
||||
ax25_set_h (result, r);
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have an alias match, substitute MYCALL.
|
||||
*/
|
||||
if (alias != NULL) {
|
||||
err = regexec(alias,repeater,0,NULL,0);
|
||||
if (err == 0) {
|
||||
packet_t result;
|
||||
|
||||
result = ax25_dup (pp);
|
||||
assert (result != NULL);
|
||||
|
||||
ax25_set_addr (result, r, mycall_xmit);
|
||||
ax25_set_h (result, r);
|
||||
return (result);
|
||||
}
|
||||
else if (err != REG_NOMATCH) {
|
||||
regerror(err, alias, err_msg, sizeof(err_msg));
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("%s\n", err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't repeat it if we get here.
|
||||
*/
|
||||
return (NULL);
|
||||
|
||||
} /* end cdigipeat_match */
|
||||
|
||||
|
||||
|
||||
/* end cdigipeater.c */
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
|
||||
#ifndef CDIGIPEATER_H
|
||||
#define CDIGIPEATER_H 1
|
||||
|
||||
#include "regex.h"
|
||||
|
||||
#include "direwolf.h" /* for MAX_CHANS */
|
||||
#include "ax25_pad.h" /* for packet_t */
|
||||
#include "audio.h" /* for radio channel properties */
|
||||
|
||||
|
||||
/*
|
||||
* Information required for Connected mode digipeating.
|
||||
*
|
||||
* The configuration file reader fills in this information
|
||||
* and it is passed to cdigipeater_init at application start up time.
|
||||
*/
|
||||
|
||||
|
||||
struct cdigi_config_s {
|
||||
|
||||
/*
|
||||
* Rules for each of the [from_chan][to_chan] combinations.
|
||||
*/
|
||||
|
||||
regex_t alias[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
int enabled[MAX_CHANS][MAX_CHANS];
|
||||
|
||||
char *filter_str[MAX_CHANS+1][MAX_CHANS+1];
|
||||
// NULL or optional Packet Filter strings such as "t/m".
|
||||
// Notice the size of arrays is one larger than normal.
|
||||
// That extra position is for the IGate.
|
||||
};
|
||||
|
||||
/*
|
||||
* Call once at application start up time.
|
||||
*/
|
||||
|
||||
extern void cdigipeater_init (struct audio_s *p_audio_config, struct cdigi_config_s *p_cdigi_config);
|
||||
|
||||
/*
|
||||
* Call this for each packet received.
|
||||
* Suitable packets will be queued for transmission.
|
||||
*/
|
||||
|
||||
extern void cdigipeater (int from_chan, packet_t pp);
|
||||
|
||||
|
||||
/* Make statistics available. */
|
||||
|
||||
int cdigipeater_get_count (int from_chan, int to_chan);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/* end cdigipeater.h */
|
||||
|
366
config.c
366
config.c
|
@ -53,6 +53,7 @@
|
|||
#include "textcolor.h"
|
||||
#include "audio.h"
|
||||
#include "digipeater.h"
|
||||
#include "cdigipeater.h"
|
||||
#include "config.h"
|
||||
#include "aprs_tt.h"
|
||||
#include "igate.h"
|
||||
|
@ -60,6 +61,7 @@
|
|||
#include "symbols.h"
|
||||
#include "xmit.h"
|
||||
#include "tt_text.h"
|
||||
#include "ax25_link.h"
|
||||
|
||||
// geotranz
|
||||
|
||||
|
@ -680,7 +682,9 @@ static char *split (char *string, int rest_of_line)
|
|||
*
|
||||
* Outputs: p_audio_config - Radio channel parameters stored here.
|
||||
*
|
||||
* p_digi_config - Digipeater configuration stored here.
|
||||
* p_digi_config - APRS Digipeater configuration stored here.
|
||||
*
|
||||
* p_cdigi_config - Connected Digipeater configuration stored here.
|
||||
*
|
||||
* p_tt_config - APRStt stuff.
|
||||
*
|
||||
|
@ -703,6 +707,7 @@ static char *split (char *string, int rest_of_line)
|
|||
|
||||
void config_init (char *fname, struct audio_s *p_audio_config,
|
||||
struct digi_config_s *p_digi_config,
|
||||
struct cdigi_config_s *p_cdigi_config,
|
||||
struct tt_config_s *p_tt_config,
|
||||
struct igate_config_s *p_igate_config,
|
||||
struct misc_config_s *p_misc_config)
|
||||
|
@ -789,6 +794,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
|
||||
memset (p_digi_config, 0, sizeof(struct digi_config_s));
|
||||
p_digi_config->dedupe_time = DEFAULT_DEDUPE;
|
||||
memset (p_cdigi_config, 0, sizeof(struct cdigi_config_s));
|
||||
|
||||
memset (p_tt_config, 0, sizeof(struct tt_config_s));
|
||||
p_tt_config->gateway_enabled = 0;
|
||||
|
@ -865,6 +871,19 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
|
||||
strlcpy (p_misc_config->logdir, "", sizeof(p_misc_config->logdir));
|
||||
|
||||
/* connected mode. */
|
||||
|
||||
p_misc_config->frack = AX25_T1V_FRACK_DEFAULT; /* Number of seconds to wait for ack to transmission. */
|
||||
|
||||
p_misc_config->retry = AX25_N2_RETRY_DEFAULT; /* Number of times to retry before giving up. */
|
||||
|
||||
p_misc_config->paclen = AX25_N1_PACLEN_DEFAULT; /* Max number of bytes in information part of frame. */
|
||||
|
||||
p_misc_config->maxframe_basic = AX25_K_MAXFRAME_BASIC_DEFAULT; /* Max frames to send before ACK. mod 8 "Window" size. */
|
||||
|
||||
p_misc_config->maxframe_extended = AX25_K_MAXFRAME_EXTENDED_DEFAULT; /* Max frames to send before ACK. mod 128 "Window" size. */
|
||||
|
||||
p_misc_config->maxv22 = AX25_N2_RETRY_DEFAULT / 2; /* Max SABME before falling back to SABM. */
|
||||
|
||||
/*
|
||||
* Try to extract options from a file.
|
||||
|
@ -1568,6 +1587,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
/*
|
||||
* PTT - Push To Talk signal line.
|
||||
* DCD - Data Carrier Detect indicator.
|
||||
* CON - Connected to another station indicator.
|
||||
*
|
||||
* xxx serial-port [-]rts-or-dtr [ [-]rts-or-dtr ]
|
||||
* xxx GPIO [-]gpio-num
|
||||
|
@ -1582,7 +1602,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
* Applies to most recent CHANNEL command.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "PTT") == 0 || strcasecmp(t, "DCD") == 0) {
|
||||
else if (strcasecmp(t, "PTT") == 0 || strcasecmp(t, "DCD") == 0 || strcasecmp(t, "CON") == 0) {
|
||||
int ot;
|
||||
char otname[8];
|
||||
|
||||
|
@ -1595,8 +1615,8 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
strlcpy (otname, "DCD", sizeof(otname));
|
||||
}
|
||||
else {
|
||||
ot = OCTYPE_FUTURE;
|
||||
strlcpy (otname, "FUTURE", sizeof(otname));
|
||||
ot = OCTYPE_CON;
|
||||
strlcpy (otname, "CON", sizeof(otname));
|
||||
}
|
||||
|
||||
t = split(NULL,0);
|
||||
|
@ -1784,7 +1804,7 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
} /* end of second serial port control line. */
|
||||
} /* end of serial port case. */
|
||||
|
||||
} /* end of PTT */
|
||||
} /* end of PTT, DCD, CON */
|
||||
|
||||
/*
|
||||
* INPUTS
|
||||
|
@ -1984,14 +2004,14 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
|
||||
/*
|
||||
* ==================== Digipeater parameters ====================
|
||||
* ==================== APRS Digipeater parameters ====================
|
||||
*/
|
||||
|
||||
/*
|
||||
* DIGIPEAT from-chan to-chan alias-pattern wide-pattern [ OFF|DROP|MARK|TRACE ]
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "digipeat") == 0) {
|
||||
else if (strcasecmp(t, "DIGIPEAT") == 0 || strcasecmp(t, "DIGIPEATER") == 0) {
|
||||
int from_chan, to_chan;
|
||||
int e;
|
||||
char message[100];
|
||||
|
@ -2175,7 +2195,82 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
|
||||
|
||||
/*
|
||||
* ==================== Packet Filtering for digipeater or IGate ====================
|
||||
* ==================== Connected Digipeater parameters ====================
|
||||
*/
|
||||
|
||||
/*
|
||||
* CDIGIPEAT from-chan to-chan [ alias-pattern ]
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "CDIGIPEAT") == 0 || strcasecmp(t, "CDIGIPEATER") == 0) {
|
||||
int from_chan, to_chan;
|
||||
int e;
|
||||
char message[100];
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Missing FROM-channel on line %d.\n", line);
|
||||
continue;
|
||||
}
|
||||
from_chan = atoi(t);
|
||||
if (from_chan < 0 || from_chan >= MAX_CHANS) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: FROM-channel must be in range of 0 to %d on line %d.\n",
|
||||
MAX_CHANS-1, line);
|
||||
continue;
|
||||
}
|
||||
if ( ! p_audio_config->achan[from_chan].valid) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: FROM-channel %d is not valid.\n",
|
||||
line, from_chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Missing TO-channel on line %d.\n", line);
|
||||
continue;
|
||||
}
|
||||
to_chan = atoi(t);
|
||||
if (to_chan < 0 || to_chan >= MAX_CHANS) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: TO-channel must be in range of 0 to %d on line %d.\n",
|
||||
MAX_CHANS-1, line);
|
||||
continue;
|
||||
}
|
||||
if ( ! p_audio_config->achan[to_chan].valid) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: TO-channel %d is not valid.\n",
|
||||
line, to_chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t != NULL) {
|
||||
e = regcomp (&(p_cdigi_config->alias[from_chan][to_chan]), t, REG_EXTENDED|REG_NOSUB);
|
||||
if (e != 0) {
|
||||
regerror (e, &(p_cdigi_config->alias[from_chan][to_chan]), message, sizeof(message));
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Invalid alias matching pattern on line %d:\n%s\n",
|
||||
line, message);
|
||||
continue;
|
||||
}
|
||||
t = split(NULL,0);
|
||||
}
|
||||
|
||||
p_cdigi_config->enabled[from_chan][to_chan] = 1;
|
||||
|
||||
if (t != NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: Found \"%s\" where end of line was expected.\n", line, t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ==================== Packet Filtering for APRS digipeater or IGate ====================
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -2252,6 +2347,74 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* ==================== Packet Filtering for connected digipeater ====================
|
||||
*/
|
||||
|
||||
/*
|
||||
* CFILTER from-chan to-chan filter_specification_expression
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "CFILTER") == 0) {
|
||||
int from_chan, to_chan;
|
||||
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Missing FROM-channel on line %d.\n", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
from_chan = isdigit(*t) ? atoi(t) : -999;
|
||||
if (from_chan < 0 || from_chan >= MAX_CHANS) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Filter FROM-channel must be in range of 0 to %d on line %d.\n",
|
||||
MAX_CHANS-1, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! p_audio_config->achan[from_chan].valid) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: FROM-channel %d is not valid.\n",
|
||||
line, from_chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Missing TO-channel on line %d.\n", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
to_chan = isdigit(*t) ? atoi(t) : -999;
|
||||
if (to_chan < 0 || to_chan >= MAX_CHANS) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Filter TO-channel must be in range of 0 to %d on line %d.\n",
|
||||
MAX_CHANS-1, line);
|
||||
continue;
|
||||
}
|
||||
if ( ! p_audio_config->achan[to_chan].valid) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file, line %d: TO-channel %d is not valid.\n",
|
||||
line, to_chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
t = split(NULL,1); /* Take rest of line including spaces. */
|
||||
|
||||
if (t == NULL) {
|
||||
t = " "; /* Empty means permit nothing. */
|
||||
}
|
||||
|
||||
p_cdigi_config->filter_str[from_chan][to_chan] = strdup(t);
|
||||
|
||||
//TODO1.2: Do a test run to see errors now instead of waiting.
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ==================== APRStt gateway ====================
|
||||
*/
|
||||
|
@ -4163,6 +4326,154 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
p_misc_config->sb_configured = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ==================== AX.25 connected mode ====================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* FRACK n - Number of seconds to wait for ack to transmission.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "FRACK") == 0) {
|
||||
int n;
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing value for FRACK.\n", line);
|
||||
continue;
|
||||
}
|
||||
n = atoi(t);
|
||||
if (n >= AX25_T1V_FRACK_MIN && n <= AX25_T1V_FRACK_MAX) {
|
||||
p_misc_config->frack = n;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Invalid FRACK time. Using default %d.\n", line, p_misc_config->frack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RETRY n - Number of times to retry before giving up.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "RETRY") == 0) {
|
||||
int n;
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing value for RETRY.\n", line);
|
||||
continue;
|
||||
}
|
||||
n = atoi(t);
|
||||
if (n >= AX25_N2_RETRY_MIN && n <= AX25_N2_RETRY_MAX) {
|
||||
p_misc_config->retry = n;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Invalid RETRY number. Using default %d.\n", line, p_misc_config->retry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PACLEN n - Maximum number of bytes in information part.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "PACLEN") == 0) {
|
||||
int n;
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing value for PACLEN.\n", line);
|
||||
continue;
|
||||
}
|
||||
n = atoi(t);
|
||||
if (n >= AX25_N1_PACLEN_MIN && n <= AX25_N1_PACLEN_MAX) {
|
||||
p_misc_config->paclen = n;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Invalid PACLEN value. Using default %d.\n", line, p_misc_config->paclen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MAXFRAME n - Max frames to send before ACK. mod 8 "Window" size.
|
||||
*
|
||||
* Window size would make more sense but everyone else calls it MAXFRAME.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "MAXFRAME") == 0) {
|
||||
int n;
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing value for MAXFRAME.\n", line);
|
||||
continue;
|
||||
}
|
||||
n = atoi(t);
|
||||
if (n >= AX25_K_MAXFRAME_BASIC_MIN && n <= AX25_K_MAXFRAME_BASIC_MAX) {
|
||||
p_misc_config->maxframe_basic = n;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Invalid MAXFRAME value outside range of %d to %d. Using default %d.\n",
|
||||
line, AX25_K_MAXFRAME_BASIC_MIN, AX25_K_MAXFRAME_BASIC_MAX, p_misc_config->maxframe_basic);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* EMAXFRAME n - Max frames to send before ACK. mod 128 "Window" size.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "EMAXFRAME") == 0) {
|
||||
int n;
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing value for EMAXFRAME.\n", line);
|
||||
continue;
|
||||
}
|
||||
n = atoi(t);
|
||||
if (n >= AX25_K_MAXFRAME_EXTENDED_MIN && n <= AX25_K_MAXFRAME_EXTENDED_MAX) {
|
||||
p_misc_config->maxframe_extended = n;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Invalid EMAXFRAME value outside of range %d to %d. Using default %d.\n",
|
||||
line, AX25_K_MAXFRAME_EXTENDED_MIN, AX25_K_MAXFRAME_EXTENDED_MAX, p_misc_config->maxframe_extended);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MAXV22 n - Max number of SABME sent before trying SABM.
|
||||
*/
|
||||
|
||||
else if (strcasecmp(t, "MAXV22") == 0) {
|
||||
int n;
|
||||
t = split(NULL,0);
|
||||
if (t == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Missing value for MAXV22.\n", line);
|
||||
continue;
|
||||
}
|
||||
n = atoi(t);
|
||||
if (n >= 0 && n <= AX25_N2_RETRY_MAX) {
|
||||
p_misc_config->maxv22 = n;
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Line %d: Invalid MAXV22 number. Will use half of RETRY.\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Invalid command.
|
||||
*/
|
||||
|
@ -4189,6 +4500,8 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
for (i=0; i<MAX_CHANS; i++) {
|
||||
for (j=0; j<MAX_CHANS; j++) {
|
||||
|
||||
/* APRS digipeating. */
|
||||
|
||||
if (p_digi_config->enabled[i][j]) {
|
||||
|
||||
if ( strcmp(p_audio_config->achan[i].mycall, "") == 0 ||
|
||||
|
@ -4221,6 +4534,39 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
//p_digi_config->enabled[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Connected mode digipeating. */
|
||||
|
||||
if (p_cdigi_config->enabled[i][j]) {
|
||||
|
||||
if ( strcmp(p_audio_config->achan[i].mycall, "") == 0 ||
|
||||
strcmp(p_audio_config->achan[i].mycall, "NOCALL") == 0 ||
|
||||
strcmp(p_audio_config->achan[i].mycall, "N0CALL") == 0) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: MYCALL must be set for receive channel %d before digipeating is allowed.\n", i);
|
||||
p_cdigi_config->enabled[i][j] = 0;
|
||||
}
|
||||
|
||||
if ( strcmp(p_audio_config->achan[j].mycall, "") == 0 ||
|
||||
strcmp(p_audio_config->achan[j].mycall, "NOCALL") == 0 ||
|
||||
strcmp(p_audio_config->achan[j].mycall, "N0CALL") == 0) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: MYCALL must be set for transmit channel %d before digipeating is allowed.\n", i);
|
||||
p_cdigi_config->enabled[i][j] = 0;
|
||||
}
|
||||
|
||||
b = 0;
|
||||
for (k=0; k<p_misc_config->num_beacons; k++) {
|
||||
if (p_misc_config->beacon[k].sendto_chan == j) b++;
|
||||
}
|
||||
if (b == 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Config file: Beaconing should be configured for channel %d when digipeating is enabled.\n", j);
|
||||
// It's a recommendation, not a requirement.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p_audio_config->achan[i].valid && strlen(p_igate_config->t2_login) > 0) {
|
||||
|
@ -4243,6 +4589,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
|||
|
||||
}
|
||||
|
||||
if (p_misc_config->maxv22 < 0) {
|
||||
p_misc_config->maxv22 = p_misc_config->retry / 2;
|
||||
}
|
||||
|
||||
} /* end config_init */
|
||||
|
||||
|
||||
|
|
21
config.h
21
config.h
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "audio.h" /* for struct audio_s */
|
||||
#include "digipeater.h" /* for struct digi_config_s */
|
||||
#include "cdigipeater.h" /* for struct cdigi_config_s */
|
||||
#include "aprs_tt.h" /* for struct tt_config_s */
|
||||
#include "igate.h" /* for struct igate_config_s */
|
||||
|
||||
|
@ -76,6 +77,25 @@ struct misc_config_s {
|
|||
int sb_turn_angle; /* degrees */
|
||||
int sb_turn_slope; /* degrees * MPH */
|
||||
|
||||
// AX.25 connected mode.
|
||||
|
||||
int frack; /* Number of seconds to wait for ack to transmission. */
|
||||
|
||||
int retry; /* Number of times to retry before giving up. */
|
||||
|
||||
int paclen; /* Max number of bytes in information part of frame. */
|
||||
|
||||
int maxframe_basic; /* Max frames to send before ACK. mod 8 "Window" size. */
|
||||
|
||||
int maxframe_extended; /* Max frames to send before ACK. mod 128 "Window" size. */
|
||||
|
||||
int maxv22; /* Maximum number of unanswered SABME frames sent before */
|
||||
/* switching to SABM. This is to handle the case of an old */
|
||||
/* TNC which simply ignores SABME rather than replying with FRMR. */
|
||||
|
||||
|
||||
|
||||
// Beacons.
|
||||
|
||||
int num_beacons; /* Number of beacons defined. */
|
||||
|
||||
|
@ -166,6 +186,7 @@ struct misc_config_s {
|
|||
|
||||
extern void config_init (char *fname, struct audio_s *p_modem,
|
||||
struct digi_config_s *digi_config,
|
||||
struct cdigi_config_s *cdigi_config,
|
||||
struct tt_config_s *p_tt_config,
|
||||
struct igate_config_s *p_igate_config,
|
||||
struct misc_config_s *misc_config);
|
||||
|
|
22
digipeater.c
22
digipeater.c
|
@ -23,6 +23,7 @@
|
|||
* Name: digipeater.c
|
||||
*
|
||||
* Purpose: Act as an APRS digital repeater.
|
||||
* Similar cdigipeater.c is for connected mode.
|
||||
*
|
||||
*
|
||||
* Description: Decide whether the specified packet should
|
||||
|
@ -255,6 +256,10 @@ void digipeater (int from_chan, packet_t pp)
|
|||
*
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
#define OBSOLETE14 1
|
||||
|
||||
|
||||
#ifndef OBSOLETE14
|
||||
static char *dest_ssid_path[16] = {
|
||||
"", /* Use VIA path */
|
||||
"WIDE1-1",
|
||||
|
@ -272,6 +277,7 @@ static char *dest_ssid_path[16] = {
|
|||
"WIDE2-2", /* South */
|
||||
"WIDE2-2", /* East */
|
||||
"WIDE2-2" }; /* West */
|
||||
#endif
|
||||
|
||||
|
||||
static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, char *mycall_xmit,
|
||||
|
@ -290,7 +296,7 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
|
||||
if (filter_str != NULL) {
|
||||
|
||||
if (pfilter(from_chan, to_chan, filter_str, pp) != 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.
|
||||
|
@ -322,11 +328,15 @@ static packet_t digipeat_match (int from_chan, packet_t pp, char *mycall_rec, ch
|
|||
* Otherwise we don't want to modify the input because this could be called multiple times.
|
||||
*/
|
||||
|
||||
#ifndef OBSOLETE14 // Took it out in 1.4
|
||||
|
||||
if (ax25_get_num_repeaters(pp) == 0 && (ssid = ax25_get_ssid(pp, AX25_DESTINATION)) > 0) {
|
||||
ax25_set_addr(pp, AX25_REPEATER_1, dest_ssid_path[ssid]);
|
||||
ax25_set_ssid(pp, AX25_DESTINATION, 0);
|
||||
/* Continue with general case, below. */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Find the first repeater station which doesn't have "has been repeated" set.
|
||||
|
@ -795,11 +805,15 @@ int main (int argc, char *argv[])
|
|||
|
||||
|
||||
/*
|
||||
* Change destination SSID to normal digipeater if none specified.
|
||||
* Change destination SSID to normal digipeater if none specified. (Obsolete, removed.)
|
||||
*/
|
||||
test ( "W1ABC>TEST-3:",
|
||||
"W1ABC>TEST,WB2OSZ-9*,WIDE3-2:");
|
||||
|
||||
test ( "W1ABC>TEST-3:",
|
||||
#ifndef OBSOLETE14
|
||||
"W1ABC>TEST,WB2OSZ-9*,WIDE3-2:");
|
||||
#else
|
||||
"");
|
||||
#endif
|
||||
test ( "W1DEF>TEST-3,WIDE2-2:",
|
||||
"W1DEF>TEST-3,WB2OSZ-9*,WIDE2-1:");
|
||||
|
||||
|
|
105
direwolf.c
105
direwolf.c
|
@ -88,6 +88,7 @@
|
|||
#include "hdlc_rec.h"
|
||||
#include "hdlc_rec2.h"
|
||||
#include "ax25_pad.h"
|
||||
#include "xid.h"
|
||||
#include "decode_aprs.h"
|
||||
#include "textcolor.h"
|
||||
#include "server.h"
|
||||
|
@ -97,6 +98,7 @@
|
|||
#include "waypoint.h"
|
||||
#include "gen_tone.h"
|
||||
#include "digipeater.h"
|
||||
#include "cdigipeater.h"
|
||||
#include "tq.h"
|
||||
#include "xmit.h"
|
||||
#include "ptt.h"
|
||||
|
@ -112,6 +114,7 @@
|
|||
#include "recv.h"
|
||||
#include "morse.h"
|
||||
#include "mheard.h"
|
||||
#include "ax25_link.h"
|
||||
|
||||
|
||||
//static int idx_decoded = 0;
|
||||
|
@ -158,7 +161,8 @@ static void __cpuid(int cpuinfo[4], int infotype){
|
|||
|
||||
static struct audio_s audio_config;
|
||||
static struct tt_config_s tt_config;
|
||||
struct digi_config_s digi_config;
|
||||
//struct digi_config_s digi_config;
|
||||
//struct cdigi_config_s cdigi_config;
|
||||
|
||||
|
||||
static int d_u_opt = 0; /* "-d u" command line option to print UTF-8 also in hexadecimal. */
|
||||
|
@ -181,6 +185,7 @@ int main (int argc, char *argv[])
|
|||
int xmit_calibrate_option = 0;
|
||||
int enable_pseudo_terminal = 0;
|
||||
struct digi_config_s digi_config;
|
||||
struct cdigi_config_s cdigi_config;
|
||||
struct igate_config_s igate_config;
|
||||
int r_opt = 0, n_opt = 0, b_opt = 0, B_opt = 0, D_opt = 0; /* Command line options. */
|
||||
char P_opt[16];
|
||||
|
@ -199,6 +204,8 @@ int main (int argc, char *argv[])
|
|||
#if USE_HAMLIB
|
||||
int d_h_opt = 0; /* "-d h" option for hamlib debugging. Repeat for more detail */
|
||||
#endif
|
||||
int E_tx_opt = 0; /* "-E n" Error rate % for clobbering trasmit frames. */
|
||||
int E_rx_opt = 0; /* "-E Rn" Error rate % for clobbering receive frames. */
|
||||
|
||||
strlcpy(l_opt, "", sizeof(l_opt));
|
||||
strlcpy(P_opt, "", sizeof(P_opt));
|
||||
|
@ -247,7 +254,7 @@ int main (int argc, char *argv[])
|
|||
text_color_init(t_opt);
|
||||
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 DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "C", __DATE__);
|
||||
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "D", __DATE__);
|
||||
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
|
||||
|
||||
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB)
|
||||
|
@ -332,7 +339,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
/* ':' following option character means arg is required. */
|
||||
|
||||
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:Sa:",
|
||||
c = getopt_long(argc, argv, "P:B:D:c:pxr:b:n:d:q:t:Ul:Sa:E:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -518,6 +525,27 @@ int main (int argc, char *argv[])
|
|||
exit (0);
|
||||
break;
|
||||
|
||||
case 'E': /* -E Error rate (%) for corrupting frames. */
|
||||
/* Just a number is transmit. Precede by R for receive. */
|
||||
|
||||
if (*optarg == 'r' || *optarg == 'R') {
|
||||
E_rx_opt = atoi(optarg+1);
|
||||
if (E_rx_opt < 1 || E_rx_opt > 99) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("-ER must be in range of 1 to 99.\n");
|
||||
E_rx_opt = 10;
|
||||
}
|
||||
}
|
||||
else {
|
||||
E_tx_opt = atoi(optarg);
|
||||
if (E_tx_opt < 1 || E_tx_opt > 99) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf("-E must be in range of 1 to 99.\n");
|
||||
E_tx_opt = 10;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/* Should not be here. */
|
||||
|
@ -552,7 +580,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
symbols_init ();
|
||||
|
||||
config_init (config_file, &audio_config, &digi_config, &tt_config, &igate_config, &misc_config);
|
||||
config_init (config_file, &audio_config, &digi_config, &cdigi_config, &tt_config, &igate_config, &misc_config);
|
||||
|
||||
if (r_opt != 0) {
|
||||
audio_config.adev[0].samples_per_sec = r_opt;
|
||||
|
@ -621,6 +649,12 @@ int main (int argc, char *argv[])
|
|||
audio_config.achan[0].decimate = D_opt;
|
||||
}
|
||||
|
||||
// temp - only xmit errors.
|
||||
|
||||
audio_config.xmit_error_rate = E_tx_opt;
|
||||
audio_config.recv_error_rate = E_rx_opt;
|
||||
|
||||
|
||||
if (strlen(l_opt) > 0) {
|
||||
strlcpy (misc_config.logdir, l_opt, sizeof(misc_config.logdir));
|
||||
}
|
||||
|
@ -711,6 +745,8 @@ int main (int argc, char *argv[])
|
|||
*/
|
||||
digipeater_init (&audio_config, &digi_config);
|
||||
igate_init (&audio_config, &igate_config, &digi_config, d_i_opt);
|
||||
cdigipeater_init (&audio_config, &cdigi_config);
|
||||
ax25_link_init (&misc_config);
|
||||
|
||||
/*
|
||||
* Provide the AGW & KISS socket interfaces for use by a client application.
|
||||
|
@ -890,7 +926,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
text_color_set(DW_COLOR_REC);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
text_color_set(DW_COLOR_DECODED);
|
||||
}
|
||||
|
||||
if (audio_config.achan[chan].num_subchan > 1 && audio_config.achan[chan].num_slicers == 1) {
|
||||
|
@ -914,26 +950,41 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
if ( ! ax25_is_aprs(pp)) {
|
||||
ax25_frame_type_t ftype;
|
||||
cmdres_t cr;
|
||||
char desc[32];
|
||||
char desc[80];
|
||||
int pf;
|
||||
int nr;
|
||||
int ns;
|
||||
|
||||
ftype = ax25_frame_type (pp, &cr, desc, &pf, &nr, &ns);
|
||||
(void)ftype;
|
||||
|
||||
/* Could change by 1, since earlier call, if we guess at modulo 128. */
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
|
||||
dw_printf ("(%s)", desc);
|
||||
if (ftype == frame_type_U_XID) {
|
||||
struct xid_param_s param;
|
||||
char info2text[100];
|
||||
|
||||
xid_parse (pinfo, info_len, ¶m, info2text, sizeof(info2text));
|
||||
dw_printf (" %s\n", info2text);
|
||||
}
|
||||
else {
|
||||
ax25_safe_print ((char *)pinfo, info_len, ( ! ax25_is_aprs(pp)) && ( ! d_u_opt) );
|
||||
dw_printf ("\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// for APRS we generally want to display non-ASCII to see UTF-8.
|
||||
// for other, probably want to restrict to ASCII only because we are
|
||||
// more likely to have compressed data than UTF-8 text.
|
||||
|
||||
// TODO: Might want to use d_u_opt for transmitted frames too.
|
||||
|
||||
ax25_safe_print ((char *)pinfo, info_len, ( ! ax25_is_aprs(pp)) && ( ! d_u_opt) );
|
||||
dw_printf ("\n");
|
||||
}
|
||||
|
||||
// for APRS we generally want to display non-ASCII to see UTF-8.
|
||||
// for other, probably want to restrict to ASCII only because we are
|
||||
// more likely to have compressed data than UTF-8 text.
|
||||
|
||||
// TODO: Might want to use d_u_opt for transmitted frames too.
|
||||
|
||||
|
||||
ax25_safe_print ((char *)pinfo, info_len, ( ! ax25_is_aprs(pp)) && ( ! d_u_opt) );
|
||||
dw_printf ("\n");
|
||||
|
||||
// Also display in pure ASCII if non-ASCII characters and "-d u" option specified.
|
||||
|
||||
|
@ -992,6 +1043,9 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
log_write (chan, &A, pp, alevel, retries);
|
||||
|
||||
// temp experiment.
|
||||
//log_rr_bits (&A, pp);
|
||||
|
||||
// Add to list of stations heard.
|
||||
|
||||
mheard_save (chan, &A, pp, alevel, retries);
|
||||
|
@ -1052,23 +1106,30 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
|
|||
|
||||
|
||||
/*
|
||||
* Note that the digipeater function can modify the packet in place so
|
||||
* this is the last thing we should do with it.
|
||||
* Again, use only those with correct CRC; We don't want to spread corrupted data!
|
||||
* Single bit change appears to be safe from observations so far but be cautious.
|
||||
* APRS digipeater.
|
||||
* Use only those with correct CRC; We don't want to spread corrupted data!
|
||||
*/
|
||||
|
||||
if (ax25_is_aprs(pp) && retries == RETRY_NONE) {
|
||||
|
||||
digipeater (chan, pp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Connected mode digipeater.
|
||||
* Use only those with correct CRC.
|
||||
*/
|
||||
|
||||
if (retries == RETRY_NONE) {
|
||||
|
||||
cdigipeater (chan, pp);
|
||||
}
|
||||
}
|
||||
|
||||
ax25_delete (pp);
|
||||
|
||||
} /* end app_process_rec_packet */
|
||||
|
||||
|
||||
|
||||
/* Process control C and window close events. */
|
||||
|
||||
#if __WIN32__
|
||||
|
|
|
@ -233,6 +233,11 @@ char *strcasestr(const char *S, const char *FIND);
|
|||
#else // Use our own copy
|
||||
|
||||
|
||||
// These prevent /usr/include/gps.h from providing its own definition.
|
||||
#define HAVE_STRLCAT 1
|
||||
#define HAVE_STRLCPY 1
|
||||
|
||||
|
||||
#define DEBUG_STRL 1
|
||||
|
||||
#if DEBUG_STRL
|
||||
|
|
351
dlq.c
351
dlq.c
|
@ -24,7 +24,7 @@
|
|||
*
|
||||
* Purpose: Received frame queue.
|
||||
*
|
||||
* Description: In earlierversions, the main thread read from the
|
||||
* Description: In earlier versions, the main thread read from the
|
||||
* audio device and performed the receive demodulation/decoding.
|
||||
*
|
||||
* Since version 1.2 we have a separate receive thread
|
||||
|
@ -32,7 +32,7 @@
|
|||
* received frames from all channels and process them
|
||||
* serially.
|
||||
*
|
||||
* In version 1.4, other types of events go into this
|
||||
* In version 1.4, other types of events also go into this
|
||||
* queue and we use it to drive the data link state machine.
|
||||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
@ -85,10 +85,13 @@ static int was_init = 0; /* was initialization performed? */
|
|||
|
||||
static void append_to_queue (struct dlq_item_s *pnew);
|
||||
|
||||
static volatile int s_new_count = 0; /* To detect memory leak. */
|
||||
static volatile int s_delete_count = 0; // TODO: need to test.
|
||||
static volatile int s_new_count = 0; /* To detect memory leak for queue items. */
|
||||
static volatile int s_delete_count = 0; // TODO: need to test.
|
||||
|
||||
|
||||
static volatile int s_cdata_new_count = 0; /* To detect memory leak for connected mode data. */
|
||||
static volatile int s_cdata_delete_count = 0; // TODO: need to test.
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
@ -109,8 +112,6 @@ static volatile int s_delete_count = 0; // TODO: need to test.
|
|||
|
||||
void dlq_init (void)
|
||||
{
|
||||
//int c, p;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("dlq_init ( )\n");
|
||||
|
@ -189,6 +190,7 @@ void dlq_init (void)
|
|||
* Normally this was received over the radio but we can create
|
||||
* our own from APRStt or beaconing.
|
||||
*
|
||||
* This would correspond to PH-DATA Indication in the AX.25 protocol spec.
|
||||
*
|
||||
* Inputs: chan - Channel, 0 is first.
|
||||
*
|
||||
|
@ -244,11 +246,17 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Allocate a new queue item. */
|
||||
|
||||
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
|
||||
s_new_count++;
|
||||
|
||||
if (s_new_count > s_delete_count + 50) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("INTERNAL ERROR: DLQ memory leak, new=%d, delete=%d\n", s_new_count, s_delete_count);
|
||||
}
|
||||
|
||||
pnew->nextp = NULL;
|
||||
pnew->type = DLQ_REC_FRAME;
|
||||
pnew->chan = chan;
|
||||
|
@ -458,6 +466,7 @@ static void append_to_queue (struct dlq_item_s *pnew)
|
|||
* pid - Protocol ID for data. Normally 0xf0 but the API
|
||||
* allows the client app to use something non-standard
|
||||
* for special situations.
|
||||
* TODO: remove this. PID is only for I and UI frames.
|
||||
*
|
||||
* Outputs: Request is appended to queue for processing by
|
||||
* the data link state machine.
|
||||
|
@ -486,7 +495,6 @@ void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num
|
|||
memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
|
||||
pnew->num_addr = num_addr;
|
||||
pnew->client = client;
|
||||
pnew->pid = pid;
|
||||
|
||||
/* Put it into queue. */
|
||||
|
||||
|
@ -603,9 +611,10 @@ void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int n
|
|||
memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
|
||||
pnew->num_addr = num_addr;
|
||||
pnew->client = client;
|
||||
pnew->pid = pid;
|
||||
|
||||
/* TODO: haven't thought about user data yet. */
|
||||
/* Attach the transmit data. */
|
||||
|
||||
pnew->txdata = cdata_new(pid,xdata_ptr,xdata_len);
|
||||
|
||||
/* Put it into queue. */
|
||||
|
||||
|
@ -614,6 +623,195 @@ void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int n
|
|||
} /* end dlq_xmit_data_request */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: dlq_register_callsign
|
||||
* dlq_unregister_callsign
|
||||
*
|
||||
* Purpose: Register callsigns that we will recognize for incoming connection requests.
|
||||
*
|
||||
* Inputs: addrs - Source (owncall), destination (peercall),
|
||||
* and possibly digipeaters.
|
||||
*
|
||||
* chan - Channel, 0 is first.
|
||||
*
|
||||
* client - Client application instance.
|
||||
*
|
||||
* Outputs: Request is appended to queue for processing by
|
||||
* the data link state machine.
|
||||
*
|
||||
* Description: The data link state machine does not use MYCALL from the APRS configuration.
|
||||
* For outgoing frames, the client supplies the source callsign.
|
||||
* For incoming connection requests, we need to know what address(es) to respond to.
|
||||
*
|
||||
* Note that one client application can register multiple callsigns for
|
||||
* multiple channels.
|
||||
* Different clients can register different different addresses on the same channel.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client)
|
||||
{
|
||||
struct dlq_item_s *pnew;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("dlq_register_callsign (%s, chan=%d, client=%d)\n", addr, chan, client);
|
||||
#endif
|
||||
|
||||
assert (chan >= 0 && chan < MAX_CHANS);
|
||||
|
||||
/* Allocate a new queue item. */
|
||||
|
||||
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
|
||||
s_new_count++;
|
||||
|
||||
pnew->type = DLQ_REGISTER_CALLSIGN;
|
||||
pnew->chan = chan;
|
||||
strlcpy (pnew->addrs[0], addr, AX25_MAX_ADDR_LEN);
|
||||
pnew->num_addr = 1;
|
||||
pnew->client = client;
|
||||
|
||||
/* Put it into queue. */
|
||||
|
||||
append_to_queue (pnew);
|
||||
|
||||
} /* end dlq_register_callsign */
|
||||
|
||||
|
||||
void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client)
|
||||
{
|
||||
struct dlq_item_s *pnew;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("dlq_unregister_callsign (%s, chan=%d, client=%d)\n", addr, chan, client);
|
||||
#endif
|
||||
|
||||
assert (chan >= 0 && chan < MAX_CHANS);
|
||||
|
||||
/* Allocate a new queue item. */
|
||||
|
||||
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
|
||||
s_new_count++;
|
||||
|
||||
pnew->type = DLQ_UNREGISTER_CALLSIGN;
|
||||
pnew->chan = chan;
|
||||
strlcpy (pnew->addrs[0], addr, AX25_MAX_ADDR_LEN);
|
||||
pnew->num_addr = 1;
|
||||
pnew->client = client;
|
||||
|
||||
/* Put it into queue. */
|
||||
|
||||
append_to_queue (pnew);
|
||||
|
||||
} /* end dlq_unregister_callsign */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: dlq_channel_busy
|
||||
*
|
||||
* Purpose: Inform data link state machine about activity on the radio channel.
|
||||
*
|
||||
* Inputs: chan - Radio channel number.
|
||||
*
|
||||
* activity - OCTYPE_PTT or OCTYPE_DCD, as defined in audio.h.
|
||||
* Other values will be discarded.
|
||||
*
|
||||
* status - 1 for active or 0 for quiet.
|
||||
*
|
||||
* Outputs: Request is appended to queue for processing by
|
||||
* the data link state machine.
|
||||
*
|
||||
* Description: Notify the link state machine about changes in carrier detect
|
||||
* and our transmitter.
|
||||
* This is needed for pausing some of our timers. For example,
|
||||
* if we transmit a frame and expect a response in 3 seconds, that
|
||||
* might be delayed because someone else is using the channel.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
void dlq_channel_busy (int chan, int activity, int status)
|
||||
{
|
||||
struct dlq_item_s *pnew;
|
||||
|
||||
if (activity == OCTYPE_PTT || activity == OCTYPE_DCD) {
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("dlq_channel_busy (...)\n");
|
||||
#endif
|
||||
|
||||
|
||||
/* Allocate a new queue item. */
|
||||
|
||||
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
|
||||
s_new_count++;
|
||||
|
||||
pnew->type = DLQ_CHANNEL_BUSY;
|
||||
pnew->chan = chan;
|
||||
pnew->activity = activity;
|
||||
pnew->status = status;
|
||||
|
||||
/* Put it into queue. */
|
||||
|
||||
append_to_queue (pnew);
|
||||
}
|
||||
|
||||
} /* end dlq_channel_busy */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: dlq_client_cleanup
|
||||
*
|
||||
* Purpose: Client application has disappeared.
|
||||
* i.e. The TCP connection has been broken.
|
||||
*
|
||||
* Inputs: client - Client application instance.
|
||||
*
|
||||
* Outputs: Request is appended to queue for processing by
|
||||
* the data link state machine.
|
||||
*
|
||||
* Description: Notify the link state machine that given client has gone away.
|
||||
* Clean up all information related to that client application.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
void dlq_client_cleanup (int client)
|
||||
{
|
||||
struct dlq_item_s *pnew;
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("dlq_client_cleanup (...)\n");
|
||||
#endif
|
||||
|
||||
// assert (client >= 0 && client < MAX_NET_CLIENTS);
|
||||
|
||||
/* Allocate a new queue item. */
|
||||
|
||||
pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
|
||||
s_new_count++;
|
||||
|
||||
// All we care about is the client number.
|
||||
|
||||
pnew->type = DLQ_CLIENT_CLEANUP;
|
||||
pnew->client = client;
|
||||
|
||||
/* Put it into queue. */
|
||||
|
||||
append_to_queue (pnew);
|
||||
|
||||
} /* end dlq_client_cleanup */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: dlq_wait_while_empty
|
||||
|
@ -817,12 +1015,145 @@ struct dlq_item_s *dlq_remove (void)
|
|||
|
||||
void dlq_delete (struct dlq_item_s *pitem)
|
||||
{
|
||||
if (pitem == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("INTERNAL ERROR: dlq_delete() given NULL pointer.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
s_delete_count++;
|
||||
if (pitem->pp != NULL) ax25_delete (pitem->pp)
|
||||
|
||||
if (pitem->pp != NULL) {
|
||||
ax25_delete (pitem->pp);
|
||||
pitem->pp = NULL;
|
||||
}
|
||||
|
||||
if (pitem->txdata != NULL) {
|
||||
cdata_delete (pitem->txdata);
|
||||
pitem->txdata = NULL;
|
||||
}
|
||||
|
||||
free (pitem);
|
||||
|
||||
} /* end dlq_delete */
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdata_new
|
||||
*
|
||||
* Purpose: Allocate blocks of data for sending and receiving connected data.
|
||||
*
|
||||
* Inputs: pid - protocol id.
|
||||
* data - pointer to data. Can be NULL for segment reassembler.
|
||||
* len - length of data.
|
||||
*
|
||||
* Returns: Structure with a copy of the data.
|
||||
*
|
||||
* Description: The flow goes like this:
|
||||
*
|
||||
* Client application extablishes a connection with another station.
|
||||
* Client application calls "dlq_xmit_data_request."
|
||||
* A copy of the data is made with this function and attached to the queue item.
|
||||
* The txdata block is attached to the appropriate link state machine.
|
||||
* At the proper time, it is transmitted in an I frame.
|
||||
* It needs to be kept around in case it needs to be retransmitted.
|
||||
* When no longer needed, it is freed with cdata_delete.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
cdata_t *cdata_new (int pid, char *data, int len)
|
||||
{
|
||||
int size;
|
||||
cdata_t *cdata;
|
||||
|
||||
s_cdata_new_count++;
|
||||
|
||||
/* Round up the size to the next 128 bytes. */
|
||||
/* The theory is that a smaller number of unique sizes might be */
|
||||
/* beneficial for memory fragmentation and garbage collection. */
|
||||
|
||||
size = ( len + 127 ) & ~0x7f;
|
||||
|
||||
cdata = malloc ( sizeof(cdata_t) + size );
|
||||
|
||||
cdata->magic = TXDATA_MAGIC;
|
||||
cdata->next = NULL;
|
||||
cdata->pid = pid;
|
||||
cdata->size = size;
|
||||
cdata->len = len;
|
||||
|
||||
assert (len >= 0 && len <= size);
|
||||
if (data == NULL) {
|
||||
memset (cdata->data, '?', size);
|
||||
}
|
||||
else {
|
||||
memcpy (cdata->data, data, len);
|
||||
}
|
||||
return (cdata);
|
||||
|
||||
} /* end cdata_new */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdata_delete
|
||||
*
|
||||
* Purpose: Release storage used by a connected data block.
|
||||
*
|
||||
* Inputs: cdata - Pointer to a data block.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void cdata_delete (cdata_t *cdata)
|
||||
{
|
||||
if (cdata == NULL) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("INTERNAL ERROR: cdata_delete() given NULL pointer.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cdata->magic != TXDATA_MAGIC) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("INTERNAL ERROR: cdata_delete() given corrupted data.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
s_cdata_delete_count++;
|
||||
|
||||
cdata->magic = 0;
|
||||
|
||||
free (cdata);
|
||||
|
||||
} /* end cdata_delete */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: cdata_check_leak
|
||||
*
|
||||
* Purpose: Check for memory leak of cdata items.
|
||||
*
|
||||
* Description: This is called when we expect no outstanding allocations.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void cdata_check_leak (void)
|
||||
{
|
||||
if (s_cdata_delete_count != s_cdata_new_count) {
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Internal Error, %s, new=%d, delete=%d\n", __func__, s_cdata_new_count, s_cdata_delete_count);
|
||||
}
|
||||
|
||||
} /* end cdata_check_leak */
|
||||
|
||||
|
||||
|
||||
/* end dlq.c */
|
||||
|
|
63
dlq.h
63
dlq.h
|
@ -12,10 +12,30 @@
|
|||
#include "audio.h"
|
||||
|
||||
|
||||
/* A transmit or receive data block for connected mode. */
|
||||
|
||||
typedef struct cdata_s {
|
||||
int magic; /* For integrity checking. */
|
||||
|
||||
#define TXDATA_MAGIC 0x09110911
|
||||
|
||||
struct cdata_s *next; /* Pointer to next when part of a list. */
|
||||
|
||||
int pid; /* Protocol id. */
|
||||
|
||||
int size; /* Number of bytes allocated. */
|
||||
|
||||
int len; /* Number of bytes actually used. */
|
||||
|
||||
char data[]; /* Variable length data. */
|
||||
|
||||
} cdata_t;
|
||||
|
||||
|
||||
|
||||
/* Types of things that can be in queue. */
|
||||
|
||||
typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST} dlq_type_t;
|
||||
typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_CHANNEL_BUSY, DLQ_CLIENT_CLEANUP} dlq_type_t;
|
||||
|
||||
|
||||
/* A queue item. */
|
||||
|
@ -28,10 +48,13 @@ typedef struct dlq_item_s {
|
|||
struct dlq_item_s *nextp; /* Next item in queue. */
|
||||
|
||||
dlq_type_t type; /* Type of item. */
|
||||
/* DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST */
|
||||
/* See enum definition above. */
|
||||
|
||||
int chan; /* Radio channel of origin. */
|
||||
|
||||
// I'm not worried about amount of memory used but this might be a
|
||||
// little clearer if a union was used for the different event types.
|
||||
|
||||
// Used for received frame.
|
||||
|
||||
int subchan; /* Winning "subchannel" when using multiple */
|
||||
|
@ -49,7 +72,7 @@ typedef struct dlq_item_s {
|
|||
|
||||
char spectrum[MAX_SUBCHANS*MAX_SLICERS+1]; /* "Spectrum" display for multi-decoders. */
|
||||
|
||||
// Used by requests from a client application.
|
||||
// Used by requests from a client application, connect, etc.
|
||||
|
||||
char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
|
||||
|
||||
|
@ -57,9 +80,19 @@ typedef struct dlq_item_s {
|
|||
|
||||
int client;
|
||||
|
||||
int pid;
|
||||
|
||||
/* TODO: xmit data */
|
||||
// Used only by client request to transmit connected data.
|
||||
|
||||
cdata_t *txdata;
|
||||
|
||||
// Used for channel activity change.
|
||||
// It is useful to know when the channel is busy either for carrier detect
|
||||
// or when we are transmitting.
|
||||
|
||||
int activity; /* OCTYPE_PTT for my transmission start/end. */
|
||||
/* OCTYPE_DCD if we hear someone else. */
|
||||
|
||||
int status; /* 1 for active or 0 for quiet. */
|
||||
|
||||
} dlq_item_t;
|
||||
|
||||
|
@ -75,7 +108,16 @@ void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num
|
|||
|
||||
void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client);
|
||||
|
||||
void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int clienti, int pid, char *xdata_ptr, int xdata_len);
|
||||
void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len);
|
||||
|
||||
void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client);
|
||||
|
||||
void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client);
|
||||
|
||||
void dlq_channel_busy (int chan, int activity, int status);
|
||||
|
||||
void dlq_client_cleanup (int client);
|
||||
|
||||
|
||||
|
||||
int dlq_wait_while_empty (double timeout_val);
|
||||
|
@ -84,6 +126,15 @@ struct dlq_item_s *dlq_remove (void);
|
|||
|
||||
void dlq_delete (struct dlq_item_s *pitem);
|
||||
|
||||
|
||||
|
||||
cdata_t *cdata_new (int pid, char *data, int len);
|
||||
|
||||
void cdata_delete (cdata_t *txdata);
|
||||
|
||||
void cdata_check_leak (void);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/* end dlq.h */
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -141,7 +141,7 @@ static void send_packet (char *str)
|
|||
}
|
||||
#endif
|
||||
hdlc_send_flags (c, 8, 0);
|
||||
hdlc_send_frame (c, fbuf, flen);
|
||||
hdlc_send_frame (c, fbuf, flen, 0);
|
||||
hdlc_send_flags (c, 2, 1);
|
||||
}
|
||||
ax25_delete (pp);
|
||||
|
|
20
hdlc_rec.c
20
hdlc_rec.c
|
@ -260,8 +260,15 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
|
|||
* with the special "flag" characters.
|
||||
*
|
||||
* Idle time of all zero bits (alternating tones at maximum rate)
|
||||
* has also been observed rarely.
|
||||
* Recognize zero(s) followed by a flag even though it vilolates the spec.
|
||||
* has also been observed rarely. It is easy to understand the reasoning.
|
||||
* The tones alternate at the maximum rate, making it symmetrical and providing
|
||||
* the most opportunity for the PLL to lock on to the edges.
|
||||
* It also violates the published protocol spec.
|
||||
*
|
||||
* Recognize zero(s) followed by a single flag even though it violates the spec.
|
||||
*
|
||||
* It has been reported that the TinyTrak4 does this.
|
||||
* https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/1207
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -304,6 +311,15 @@ void hdlc_rec_bit (int chan, int subchan, int slice, int raw, int is_scrambled,
|
|||
/*
|
||||
* Loss of signal should result in lack of transitions.
|
||||
* (all '1' bits) for at least a little while.
|
||||
*
|
||||
* When this was written, I was only concerned about 1200 baud.
|
||||
* For 9600, added later, there is a (de)scrambling function.
|
||||
* So if there is no change in the signal, we would get pseudo random bits here.
|
||||
* Maybe we need to put in another check earlier so DCD is not held on too long
|
||||
* after loss of signal for 9600.
|
||||
* No, that would not be a good idea. part of a valid frame, when scrambled,
|
||||
* could have seven or more "1" bits in a row.
|
||||
* Needs more study.
|
||||
*/
|
||||
|
||||
|
||||
|
|
18
hdlc_send.c
18
hdlc_send.c
|
@ -52,6 +52,8 @@ static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "hdl
|
|||
*
|
||||
* flen - Frame length, not including the FCS.
|
||||
*
|
||||
* bad_fcs - Append an invalid FCS for testing purposes.
|
||||
*
|
||||
* Outputs: Bits are shipped out by calling tone_gen_put_bit().
|
||||
*
|
||||
* Returns: Number of bits sent including "flags" and the
|
||||
|
@ -73,8 +75,7 @@ static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "hdl
|
|||
*
|
||||
*--------------------------------------------------------------*/
|
||||
|
||||
|
||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen)
|
||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs)
|
||||
{
|
||||
int j, fcs;
|
||||
|
||||
|
@ -84,7 +85,7 @@ int hdlc_send_frame (int chan, unsigned char *fbuf, int flen)
|
|||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("hdlc_send_frame ( chan = %d, fbuf = %p, flen = %d )\n", chan, fbuf, flen);
|
||||
dw_printf ("hdlc_send_frame ( chan = %d, fbuf = %p, flen = %d, bad_fcs = %d)\n", chan, fbuf, flen, bad_fcs);
|
||||
fflush (stdout);
|
||||
#endif
|
||||
|
||||
|
@ -97,8 +98,15 @@ int hdlc_send_frame (int chan, unsigned char *fbuf, int flen)
|
|||
|
||||
fcs = fcs_calc (fbuf, flen);
|
||||
|
||||
send_data (chan, fcs & 0xff);
|
||||
send_data (chan, (fcs >> 8) & 0xff);
|
||||
if (bad_fcs) {
|
||||
/* For testing only - Simulate a frame getting corrupted along the way. */
|
||||
send_data (chan, (~fcs) & 0xff);
|
||||
send_data (chan, ((~fcs) >> 8) & 0xff);
|
||||
}
|
||||
else {
|
||||
send_data (chan, fcs & 0xff);
|
||||
send_data (chan, (fcs >> 8) & 0xff);
|
||||
}
|
||||
|
||||
send_control (chan, 0x7e); /* End frame */
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
/* hdlc_send.h */
|
||||
|
||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen);
|
||||
int hdlc_send_frame (int chan, unsigned char *fbuf, int flen, int bad_fcs);
|
||||
|
||||
int hdlc_send_flags (int chan, int flags, int finish);
|
||||
|
||||
|
|
4
igate.c
4
igate.c
|
@ -883,7 +883,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
|
|||
|
||||
if (save_digi_config_p->filter_str[chan][MAX_CHANS] != NULL) {
|
||||
|
||||
if (pfilter(chan, MAX_CHANS, save_digi_config_p->filter_str[chan][MAX_CHANS], recv_pp) != 1) {
|
||||
if (pfilter(chan, MAX_CHANS, save_digi_config_p->filter_str[chan][MAX_CHANS], recv_pp, 1) != 1) {
|
||||
|
||||
// Is this useful troubleshooting information or just distracting noise?
|
||||
// Originally this was always printed but there was a request to add a "quiet" option to suppress this.
|
||||
|
@ -1542,7 +1542,7 @@ static void xmit_packet (char *message, int to_chan)
|
|||
|
||||
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) {
|
||||
if (pfilter(MAX_CHANS, to_chan, save_digi_config_p->filter_str[MAX_CHANS][to_chan], pp3, 1) != 1) {
|
||||
|
||||
// Originally this was always printed but it's probably too much noise.
|
||||
// Version 1.4, print only if debug option is specified.
|
||||
|
|
83
log.c
83
log.c
|
@ -341,6 +341,89 @@ void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_
|
|||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: log_rr_bits
|
||||
*
|
||||
* Purpose: Quick hack to look at the C and RR bits just to see what is there.
|
||||
* This seems like a good place because it is a small subset of the function above.
|
||||
*
|
||||
* Inputs: A - Explode information from APRS packet.
|
||||
*
|
||||
* pp - Received packet object.
|
||||
*
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void log_rr_bits (decode_aprs_t *A, packet_t pp)
|
||||
{
|
||||
|
||||
if (1) {
|
||||
|
||||
char heard[AX25_MAX_ADDR_LEN+1];
|
||||
char smfr[60];
|
||||
char *p;
|
||||
int src_c, dst_c;
|
||||
int src_rr, dst_rr;
|
||||
|
||||
// Sanitize system type (manufacturer) changing any comma to period.
|
||||
|
||||
strlcpy (smfr, A->g_mfr, sizeof(smfr));
|
||||
for (p=smfr; *p!='\0'; p++) {
|
||||
if (*p == ',') *p = '.';
|
||||
}
|
||||
|
||||
/* Who are we hearing? Original station or digipeater? */
|
||||
/* Similar code in direwolf.c. Combine into one function? */
|
||||
|
||||
strlcpy(heard, "", sizeof(heard));
|
||||
|
||||
if (pp != NULL) {
|
||||
int h;
|
||||
|
||||
if (ax25_get_num_addr(pp) == 0) {
|
||||
/* Not AX.25. No station to display below. */
|
||||
h = -1;
|
||||
strlcpy (heard, "", sizeof(heard));
|
||||
}
|
||||
else {
|
||||
h = ax25_get_heard(pp);
|
||||
ax25_get_addr_with_ssid(pp, h, heard);
|
||||
}
|
||||
|
||||
if (h >= AX25_REPEATER_2 &&
|
||||
strncmp(heard, "WIDE", 4) == 0 &&
|
||||
isdigit(heard[4]) &&
|
||||
heard[5] == '\0') {
|
||||
|
||||
ax25_get_addr_with_ssid(pp, h-1, heard);
|
||||
strlcat (heard, "?", sizeof(heard));
|
||||
}
|
||||
|
||||
src_c = ax25_get_h (pp, AX25_SOURCE);
|
||||
dst_c = ax25_get_h (pp, AX25_DESTINATION);
|
||||
src_rr = ax25_get_rr (pp, AX25_SOURCE);
|
||||
dst_rr = ax25_get_rr (pp, AX25_DESTINATION);
|
||||
|
||||
// C RR for source
|
||||
// C RR for destination
|
||||
// system type
|
||||
// source
|
||||
// station heard
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
|
||||
dw_printf ("%d %d%d %d %d%d,%s,%s,%s\n",
|
||||
src_c, (src_rr >> 1) & 1, src_rr & 1,
|
||||
dst_c, (dst_rr >> 1) & 1, dst_rr & 1,
|
||||
smfr, A->g_src, heard);
|
||||
}
|
||||
}
|
||||
|
||||
} /* end log_rr_bits */
|
||||
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
*
|
||||
* Function: log_term
|
||||
|
|
2
log.h
2
log.h
|
@ -14,4 +14,6 @@ void log_init (char *path);
|
|||
|
||||
void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries);
|
||||
|
||||
void log_rr_bits (decode_aprs_t *A, packet_t pp);
|
||||
|
||||
void log_term (void);
|
|
@ -470,7 +470,27 @@ void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned c
|
|||
if (save_audio_config_p->achan[chan].num_subchan == 1 &&
|
||||
save_audio_config_p->achan[chan].num_slicers == 1) {
|
||||
|
||||
dlq_rec_frame (chan, subchan, slice, pp, alevel, retries, "");
|
||||
|
||||
int drop_it = 0;
|
||||
if (save_audio_config_p->recv_error_rate != 0) {
|
||||
float r = (float)(rand()) / (float)RAND_MAX; // Random, 0.0 to 1.0
|
||||
|
||||
//text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf ("TEMP DEBUG. recv error rate = %d\n", save_audio_config_p->recv_error_rate);
|
||||
|
||||
if (save_audio_config_p->recv_error_rate / 100.0 > r) {
|
||||
drop_it = 1;
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Intentionally dropping incoming frame. Recv Error rate = %d per cent.\n", save_audio_config_p->recv_error_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if (drop_it ) {
|
||||
ax25_delete (pp);
|
||||
}
|
||||
else {
|
||||
dlq_rec_frame (chan, subchan, slice, pp, alevel, retries, "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -649,14 +669,34 @@ static void pick_best_candidate (int chan)
|
|||
j = subchan_from_n(best_n);
|
||||
k = slice_from_n(best_n);
|
||||
|
||||
dlq_rec_frame (chan, j, k,
|
||||
int drop_it = 0;
|
||||
if (save_audio_config_p->recv_error_rate != 0) {
|
||||
float r = (float)(rand()) / (float)RAND_MAX; // Random, 0.0 to 1.0
|
||||
|
||||
//text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf ("TEMP DEBUG. recv error rate = %d\n", save_audio_config_p->recv_error_rate);
|
||||
|
||||
if (save_audio_config_p->recv_error_rate / 100.0 > r) {
|
||||
drop_it = 1;
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Intentionally dropping incoming frame. Recv Error rate = %d per cent.\n", save_audio_config_p->recv_error_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if ( drop_it ) {
|
||||
ax25_delete (candidate[chan][j][k].packet_p);
|
||||
candidate[chan][j][k].packet_p = NULL;
|
||||
}
|
||||
else {
|
||||
dlq_rec_frame (chan, j, k,
|
||||
candidate[chan][j][k].packet_p,
|
||||
candidate[chan][j][k].alevel,
|
||||
(int)(candidate[chan][j][k].retries),
|
||||
spectrum);
|
||||
|
||||
/* Someone else owns it now and will delete it later. */
|
||||
candidate[chan][j][k].packet_p = NULL;
|
||||
/* Someone else owns it now and will delete it later. */
|
||||
candidate[chan][j][k].packet_p = NULL;
|
||||
}
|
||||
|
||||
/* Clear in preparation for next time. */
|
||||
|
||||
|
|
54
pfilter.c
54
pfilter.c
|
@ -81,9 +81,15 @@ typedef struct pfstate_s {
|
|||
packet_t pp;
|
||||
|
||||
/*
|
||||
* Packet split into separate parts.
|
||||
* Are we processing APRS or connected mode?
|
||||
* This determines whch types of filters are available.
|
||||
*/
|
||||
int is_aprs;
|
||||
|
||||
/*
|
||||
* Packet split into separate parts if APRS.
|
||||
* Most interesting fields are:
|
||||
* g_src - source address
|
||||
*
|
||||
* g_symbol_table - / \ or overlay
|
||||
* g_symbol_code
|
||||
* g_lat, g_lon - Location
|
||||
|
@ -133,6 +139,10 @@ static int filt_s (pfstate_t *pf);
|
|||
*
|
||||
* pp - Packet object handle.
|
||||
*
|
||||
* is_aprs - True for APRS, false for connected mode digipeater.
|
||||
* Connected mode allows a subset of the filter types, only
|
||||
* looking at the addresses, not information part contents.
|
||||
*
|
||||
* Returns: 1 = yes
|
||||
* 0 = no
|
||||
* -1 = error detected
|
||||
|
@ -142,7 +152,7 @@ static int filt_s (pfstate_t *pf);
|
|||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
||||
int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs)
|
||||
{
|
||||
pfstate_t pfstate;
|
||||
char *p;
|
||||
|
@ -151,6 +161,8 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
|||
assert (from_chan >= 0 && from_chan <= MAX_CHANS);
|
||||
assert (to_chan >= 0 && to_chan <= MAX_CHANS);
|
||||
|
||||
memset (&pfstate, 0, sizeof(pfstate));
|
||||
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("INTERNAL ERROR in pfilter: NULL packet pointer. Please report this!\n");
|
||||
|
@ -165,10 +177,9 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
|||
pfstate.from_chan = from_chan;
|
||||
pfstate.to_chan = to_chan;
|
||||
|
||||
/* Copy filter string, removing any control characters. */
|
||||
/* Copy filter string, changing any control characers to spaces. */
|
||||
|
||||
memset (pfstate.filter_str, 0, sizeof(pfstate.filter_str));
|
||||
strncpy (pfstate.filter_str, filter, MAX_FILTER_LEN-1);
|
||||
strlcpy (pfstate.filter_str, filter, sizeof(pfstate.filter_str));
|
||||
|
||||
pfstate.nexti = 0;
|
||||
for (p = pfstate.filter_str; *p != '\0'; p++) {
|
||||
|
@ -178,7 +189,11 @@ int pfilter (int from_chan, int to_chan, char *filter, packet_t pp)
|
|||
}
|
||||
|
||||
pfstate.pp = pp;
|
||||
decode_aprs (&pfstate.decoded, pp, 1);
|
||||
pfstate.is_aprs = is_aprs;
|
||||
|
||||
if (is_aprs) {
|
||||
decode_aprs (&pfstate.decoded, pp, 1);
|
||||
}
|
||||
|
||||
next_token(&pfstate);
|
||||
|
||||
|
@ -415,6 +430,14 @@ static int parse_primary (pfstate_t *pf)
|
|||
* 0 = no
|
||||
* -1 = error detected
|
||||
*
|
||||
* Description: All filter specifications are allowed for APRS.
|
||||
* Only those dealing with addresses are allowed for connected digipeater.
|
||||
*
|
||||
* b - budlist (source)
|
||||
* d - digipeaters used
|
||||
* v - digipeaters not used
|
||||
* u - unproto (destination)
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
@ -424,6 +447,16 @@ static int parse_filter_spec (pfstate_t *pf)
|
|||
int result = -1;
|
||||
char addr[AX25_MAX_ADDR_LEN];
|
||||
|
||||
|
||||
if ( ( ! pf->is_aprs) && strchr ("01bdvu", pf->token_str[0]) == NULL) {
|
||||
|
||||
print_error (pf, "Only b, d, v, and u specifications are allowed for connected mode digipeater filtering.");
|
||||
result = -1;
|
||||
next_token (pf);
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
/* undocumented: can use 0 or 1 for testing. */
|
||||
|
||||
if (strcmp(pf->token_str, "0") == 0) {
|
||||
|
@ -437,7 +470,8 @@ static int parse_filter_spec (pfstate_t *pf)
|
|||
|
||||
else if (pf->token_str[0] == 'b' && ispunct(pf->token_str[1])) {
|
||||
/* Budlist - source address */
|
||||
result = filt_bodgu (pf, pf->decoded.g_src);
|
||||
ax25_get_addr_with_ssid (pf->pp, AX25_SOURCE, addr);
|
||||
result = filt_bodgu (pf, addr);
|
||||
}
|
||||
else if (pf->token_str[0] == 'o' && ispunct(pf->token_str[1])) {
|
||||
/* Object or item name */
|
||||
|
@ -479,7 +513,7 @@ static int parse_filter_spec (pfstate_t *pf)
|
|||
}
|
||||
else if (pf->token_str[0] == 'u' && ispunct(pf->token_str[1])) {
|
||||
/* Unproto (destination) - probably want to exclude mic-e types */
|
||||
/* because destintation 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) != '`') {
|
||||
ax25_get_addr_with_ssid (pf->pp, AX25_DESTINATION, addr);
|
||||
|
@ -1120,7 +1154,7 @@ static void pftest (int test_num, char *filter, char *monitor, int expected)
|
|||
pp = ax25_from_text (monitor, 1);
|
||||
assert (pp != NULL);
|
||||
|
||||
result = pfilter (0, 0, filter, pp);
|
||||
result = pfilter (0, 0, filter, pp, 1);
|
||||
if (result != expected) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("Unexpected result for test number %d\n", test_num);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
/* pfilter.h */
|
||||
|
||||
int pfilter (int from_chan, int to_chan, char *filter, packet_t pp);
|
||||
int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs);
|
||||
|
||||
int is_telem_metadata (char *infop);
|
14
ptt.c
14
ptt.c
|
@ -46,6 +46,8 @@
|
|||
*
|
||||
* Version 1.3: HAMLIB support.
|
||||
*
|
||||
* Version 1.4: The spare "future" indicator is now used when connected to another station.
|
||||
*
|
||||
* References: http://www.robbayer.com/files/serial-win.pdf
|
||||
*
|
||||
* https://www.kernel.org/doc/Documentation/gpio.txt
|
||||
|
@ -127,6 +129,7 @@ typedef int HANDLE;
|
|||
#include "textcolor.h"
|
||||
#include "audio.h"
|
||||
#include "ptt.h"
|
||||
#include "dlq.h"
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -372,7 +375,7 @@ void ptt_init (struct audio_s *audio_config_p)
|
|||
|
||||
strlcpy (otnames[OCTYPE_PTT], "PTT", sizeof(otnames[OCTYPE_PTT]));
|
||||
strlcpy (otnames[OCTYPE_DCD], "DCD", sizeof(otnames[OCTYPE_DCD]));
|
||||
strlcpy (otnames[OCTYPE_FUTURE], "FUTURE", sizeof(otnames[OCTYPE_FUTURE]));
|
||||
strlcpy (otnames[OCTYPE_CON], "CON", sizeof(otnames[OCTYPE_CON]));
|
||||
|
||||
|
||||
for (ch = 0; ch < MAX_CHANS; ch++) {
|
||||
|
@ -773,7 +776,7 @@ void ptt_init (struct audio_s *audio_config_p)
|
|||
* probably be renamed something like octrl_set.
|
||||
*
|
||||
* Inputs: ot - Output control type:
|
||||
* OCTYPE_PTT, OCTYPE_DCD, OCTYPE_HAMLIB, OCTYPE_FUTURE
|
||||
* OCTYPE_PTT, OCTYPE_DCD, OCTYPE_FUTURE
|
||||
*
|
||||
* chan - channel, 0 .. (number of channels)-1
|
||||
*
|
||||
|
@ -810,6 +813,13 @@ void ptt_set (int ot, int chan, int ptt_signal)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The data link state machine has an interest in activity on the radio channel.
|
||||
* This is a very convenient place to get that information.
|
||||
*/
|
||||
|
||||
dlq_channel_busy (chan, ot, ptt_signal);
|
||||
|
||||
/*
|
||||
* Inverted output?
|
||||
*/
|
||||
|
|
46
recv.c
46
recv.c
|
@ -68,7 +68,7 @@
|
|||
*
|
||||
* The difference is that app_process_rec_frame
|
||||
* is no longer called directly. Instead
|
||||
* the frame is appended to a queue with dlq_append.
|
||||
* the frame is appended to a queue with dlq_rec_frame.
|
||||
*
|
||||
* Received frames can now be processed one at
|
||||
* a time and we don't need to worry about later
|
||||
|
@ -107,9 +107,8 @@
|
|||
#include "dtmf.h"
|
||||
#include "aprs_tt.h"
|
||||
#include "dtime_now.h"
|
||||
#if NEW14
|
||||
#include "ax25_link.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if __WIN32__
|
||||
static unsigned __stdcall recv_adev_thread (void *arg);
|
||||
|
@ -266,7 +265,7 @@ static void * recv_adev_thread (void *arg)
|
|||
}
|
||||
|
||||
/* When a complete frame is accumulated, */
|
||||
/* dlq_append, is called. */
|
||||
/* dlq_rec_frame, is called. */
|
||||
|
||||
/* recv_process, below, drains the queue. */
|
||||
|
||||
|
@ -293,15 +292,11 @@ void recv_process (void)
|
|||
while (1) {
|
||||
|
||||
int timed_out;
|
||||
#if NEW14
|
||||
|
||||
double timeout_value = ax25_link_get_next_timer_expiry();
|
||||
|
||||
|
||||
timed_out = dlq_wait_while_empty (timeout_value);
|
||||
#else
|
||||
dlq_wait_while_empty (0.0);
|
||||
timed_out = 0;
|
||||
#endif
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
|
@ -315,9 +310,7 @@ void recv_process (void)
|
|||
dw_printf ("recv_process: time waiting on dlq. call dl_timer_expiry.\n");
|
||||
#endif
|
||||
|
||||
#if NEW14
|
||||
dl_timer_expiry ();
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
||||
|
@ -345,17 +338,16 @@ void recv_process (void)
|
|||
*/
|
||||
|
||||
app_process_rec_packet (pitem->chan, pitem->subchan, pitem->slice, pitem->pp, pitem->alevel, pitem->retries, pitem->spectrum);
|
||||
pitem->pp = NULL; // Was consumed by above. Don't try to use or free again.
|
||||
|
||||
|
||||
/*
|
||||
* Link processing - Can ignore UI frames.
|
||||
* Link processing.
|
||||
*/
|
||||
// TODO - ax25_link_rec_frame & delete & remove delete from above.
|
||||
lm_data_indication(pitem);
|
||||
|
||||
break;
|
||||
|
||||
#if NEW14
|
||||
|
||||
case DLQ_CONNECT_REQUEST:
|
||||
|
||||
dl_connect_request (pitem);
|
||||
|
@ -370,7 +362,27 @@ void recv_process (void)
|
|||
|
||||
dl_data_request (pitem);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case DLQ_REGISTER_CALLSIGN:
|
||||
|
||||
dl_register_callsign (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_UNREGISTER_CALLSIGN:
|
||||
|
||||
dl_unregister_callsign (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_CHANNEL_BUSY:
|
||||
|
||||
lm_channel_busy (pitem);
|
||||
break;
|
||||
|
||||
case DLQ_CLIENT_CLEANUP:
|
||||
|
||||
dl_client_cleanup (pitem);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
dlq_delete (pitem);
|
||||
|
|
|
@ -184,6 +184,10 @@ MYFDTYPE serial_port_open (char *devicename, int baud)
|
|||
//text_color_set(DW_COLOR_INFO);
|
||||
//dw_printf("Successful serial port open on %s.\n", devicename);
|
||||
|
||||
// Some devices, e.g. KPC-3+, can't turn off hardware flow control and need RTS.
|
||||
|
||||
EscapeCommFunction(fd,SETRTS);
|
||||
EscapeCommFunction(fd,SETDTR);
|
||||
#else
|
||||
|
||||
/* Linux version. */
|
||||
|
|
211
server.c
211
server.c
|
@ -179,23 +179,6 @@ static int enable_send_monitor_to_client[MAX_NET_CLIENTS];
|
|||
/* the client app must send a command to enable this. */
|
||||
|
||||
|
||||
/*
|
||||
* Registered callsigns from 'X' command.
|
||||
* For simplicity just use a fixed size array until there
|
||||
* is evidence that a larger number would be needed.
|
||||
*
|
||||
* Also keep track of which client did the registration.
|
||||
* For example client 0 might register the callsign ABC
|
||||
* and client 1 register DEF. If something comes addressed
|
||||
* to DEF, we would want it going only to client 1.
|
||||
*/
|
||||
|
||||
#define MAX_REG_CALLSIGNS 20
|
||||
|
||||
static char registered_callsigns[MAX_REG_CALLSIGNS][AX25_MAX_ADDR_LEN];
|
||||
static int registered_by_client[MAX_REG_CALLSIGNS];
|
||||
|
||||
|
||||
// TODO: define in one place, use everywhere.
|
||||
// TODO: Macro to terminate thread when no point to go on.
|
||||
|
||||
|
@ -464,8 +447,6 @@ void server_init (struct audio_s *audio_config_p, struct misc_config_s *mc)
|
|||
enable_send_monitor_to_client[client] = 0;
|
||||
}
|
||||
|
||||
memset (registered_callsigns, 0, sizeof(registered_callsigns));
|
||||
|
||||
if (server_port == 0) {
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Disabled AGW network client port.\n");
|
||||
|
@ -653,6 +634,7 @@ static THREAD_F connect_listen_thread (void *arg)
|
|||
}
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
// TODO: "attached" or some other term would be better because "connected" has a different meaning for AX.25.
|
||||
dw_printf("\nConnected to AGW client application %d ...\n\n", client);
|
||||
|
||||
/*
|
||||
|
@ -836,6 +818,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
closesocket (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
WSACleanup();
|
||||
dlq_client_cleanup (client);
|
||||
}
|
||||
#else
|
||||
err = write (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
|
@ -845,6 +828,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
dw_printf ("\nError sending message to AGW client application. Closing connection.\n\n");
|
||||
close (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -939,6 +923,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
closesocket (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
WSACleanup();
|
||||
dlq_client_cleanup (client);
|
||||
}
|
||||
#else
|
||||
err = write (client_sock[client], &agwpe_msg, sizeof(agwpe_msg.hdr) + netle2host(agwpe_msg.hdr.data_len_NETLE));
|
||||
|
@ -948,6 +933,7 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
dw_printf ("\nError sending message to AGW client application %d. Closing connection.\n\n", client);
|
||||
close (client_sock[client]);
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -964,6 +950,8 @@ void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int fl
|
|||
* Purpose: Send notification to client app when a link has
|
||||
* been established with another station.
|
||||
*
|
||||
* DL-CONNECT Confirm or DL-CONNECT Indication in the protocol spec.
|
||||
*
|
||||
* Inputs: chan - Which radio channel.
|
||||
*
|
||||
* client - Which one of potentially several clients.
|
||||
|
@ -1017,6 +1005,8 @@ void server_link_established (int chan, int client, char *remote_call, char *own
|
|||
* another station has been terminated or a connection
|
||||
* attempt failed.
|
||||
*
|
||||
* DL-DISCONNECT Confirm or DL-DISCONNECT Indication in the protocol spec.
|
||||
*
|
||||
* Inputs: chan - Which radio channel.
|
||||
*
|
||||
* client - Which one of potentially several clients.
|
||||
|
@ -1027,7 +1017,7 @@ void server_link_established (int chan, int client, char *remote_call, char *own
|
|||
*
|
||||
* timeout - true when no answer from other station.
|
||||
* How do we distinguish who asked for the
|
||||
* termination of an existing linkn?
|
||||
* termination of an existing link?
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
@ -1060,6 +1050,66 @@ void server_link_terminated (int chan, int client, char *remote_call, char *own_
|
|||
} /* end server_link_terminated */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: server_rec_conn_data
|
||||
*
|
||||
* Purpose: Send received connected data to the application.
|
||||
*
|
||||
* DL-DATA Indication in the protocol spec.
|
||||
*
|
||||
* Inputs: chan - Which radio channel.
|
||||
*
|
||||
* client - Which one of potentially several clients.
|
||||
*
|
||||
* remote_call - Callsign[-ssid] of remote station.
|
||||
*
|
||||
* own_call - Callsign[-ssid] of my end.
|
||||
*
|
||||
* pid - Protocol ID from I frame.
|
||||
*
|
||||
* data_ptr - Pointer to a block of bytes.
|
||||
*
|
||||
* data_len - Number of bytes. Could be zero.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
void server_rec_conn_data (int chan, int client, char *remote_call, char *own_call, int pid, char *data_ptr, int data_len)
|
||||
{
|
||||
|
||||
struct {
|
||||
struct agwpe_s hdr;
|
||||
char info[AX25_MAX_INFO_LEN]; // I suppose there is potential for something larger.
|
||||
// We'll cross that bridge if we ever come to it.
|
||||
} reply;
|
||||
|
||||
|
||||
memset (&reply.hdr, 0, sizeof(reply.hdr));
|
||||
reply.hdr.portx = chan;
|
||||
reply.hdr.datakind = 'D';
|
||||
reply.hdr.pid = pid;
|
||||
|
||||
strlcpy (reply.hdr.call_from, remote_call, sizeof(reply.hdr.call_from));
|
||||
strlcpy (reply.hdr.call_to, own_call, sizeof(reply.hdr.call_to));
|
||||
|
||||
if (data_len < 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Invalid length %d for connected data to client %d.\n", data_len, client);
|
||||
data_len = 0;
|
||||
}
|
||||
else if (data_len > AX25_MAX_INFO_LEN) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Invalid length %d for connected data to client %d.\n", data_len, client);
|
||||
data_len = AX25_MAX_INFO_LEN;
|
||||
}
|
||||
|
||||
memcpy (reply.info, data_ptr, data_len);
|
||||
reply.hdr.data_len_NETLE = host2netle(data_len);
|
||||
|
||||
send_to_client (client, &reply);
|
||||
|
||||
} /* end server_rec_conn_data */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
|
@ -1199,6 +1249,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
close (client_sock[client]);
|
||||
#endif
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1240,6 +1291,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
close (client_sock[client]);
|
||||
#endif
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -1258,6 +1310,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
close (client_sock[client]);
|
||||
#endif
|
||||
client_sock[client] = -1;
|
||||
dlq_client_cleanup (client);
|
||||
return (0);
|
||||
}
|
||||
if (n >= 0) {
|
||||
|
@ -1581,38 +1634,34 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
{
|
||||
struct {
|
||||
struct agwpe_s hdr;
|
||||
char data;
|
||||
char data; /* 1 = success, 0 = failure */
|
||||
} reply;
|
||||
|
||||
int j, ok;
|
||||
int ok = 1;
|
||||
|
||||
// The protocol spec says it is an error to register the same one more than once.
|
||||
// Too much trouble. Report success if the channel is valid.
|
||||
|
||||
|
||||
int chan = cmd.hdr.portx;
|
||||
|
||||
if (chan >= 0 && chan < MAX_CHANS && save_audio_config_p->achan[chan].valid) {
|
||||
ok = 1;
|
||||
dlq_register_callsign (cmd.hdr.call_from, chan, client);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("AGW protocol error. Register callsign for invalid channel %d.\n", chan);
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
|
||||
memset (&reply, 0, sizeof(reply));
|
||||
reply.hdr.datakind = 'X';
|
||||
reply.hdr.portx = cmd.hdr.portx;
|
||||
memcpy (reply.hdr.call_from, cmd.hdr.call_from, sizeof(reply.hdr.call_from));
|
||||
reply.hdr.data_len_NETLE = host2netle(1);
|
||||
|
||||
// Version 1.0.
|
||||
// Previously used sizeof(reply) but compiler rounded it up to next byte boundary.
|
||||
// That's why more cumbersome size expression is used.
|
||||
|
||||
// The protocol spec says it is an error to register the same one more than once.
|
||||
// First make sure is it not already in there. Add if space available.
|
||||
|
||||
if (server_callsign_registered_by_client(cmd.hdr.call_from) >= 0) {
|
||||
ok = 0;
|
||||
}
|
||||
else {
|
||||
ok = 0;
|
||||
for (j = 0; j < MAX_REG_CALLSIGNS && ok == 0; j++) {
|
||||
if (registered_callsigns[j][0] == '\0') {
|
||||
strlcpy (registered_callsigns[j], cmd.hdr.call_from, sizeof(registered_callsigns[j]));
|
||||
registered_by_client[j] = client;
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reply.data = ok; /* 1 = success, 0 = failure */
|
||||
reply.data = ok;
|
||||
send_to_client (client, &reply);
|
||||
}
|
||||
break;
|
||||
|
@ -1620,13 +1669,15 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
case 'x': /* Unregister CallSign */
|
||||
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < MAX_REG_CALLSIGNS; j++) {
|
||||
if (strcmp(registered_callsigns[j], cmd.hdr.call_from) == 0) {
|
||||
registered_callsigns[j][0] = '\0';
|
||||
registered_by_client[j] = -1;
|
||||
}
|
||||
int chan = cmd.hdr.portx;
|
||||
|
||||
if (chan >= 0 && chan < MAX_CHANS && save_audio_config_p->achan[chan].valid) {
|
||||
dlq_unregister_callsign (cmd.hdr.call_from, chan, client);
|
||||
}
|
||||
else {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("AGW protocol error. Unregister callsign for invalid channel %d.\n", chan);
|
||||
}
|
||||
}
|
||||
/* No reponse is expected. */
|
||||
|
@ -1648,7 +1699,7 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
int j;
|
||||
|
||||
strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
|
||||
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
|
||||
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_DESTINATION]));
|
||||
|
||||
if (cmd.hdr.datakind == 'c') {
|
||||
pid = cmd.hdr.pid; /* non standard for NETROM, TCP/IP, etc. */
|
||||
|
@ -1675,15 +1726,9 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
}
|
||||
}
|
||||
|
||||
#if NEW14
|
||||
|
||||
dlq_connect_request (callsigns, num_calls, cmd.hdr.portx, client, pid);
|
||||
#else
|
||||
(void)pid; // suppress unused variable message.
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Can't process command '%c' from AGW client app %d.\n", cmd.hdr.datakind, client);
|
||||
dw_printf ("Connected packet mode is not implemented.\n");
|
||||
#endif
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1696,15 +1741,9 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
|
||||
strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
|
||||
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
|
||||
#if NEW14
|
||||
|
||||
dlq_xmit_data_request (callsigns, num_calls, cmd.hdr.portx, client, cmd.hdr.pid, cmd.data, netle2host(cmd.hdr.data_len_NETLE));
|
||||
#else
|
||||
(void)num_calls; // suppress unused variable warning.
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Can't process command '%c' from AGW client app %d.\n", cmd.hdr.datakind, client);
|
||||
dw_printf ("Connected packet mode is not implemented.\n");
|
||||
#endif
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1716,15 +1755,9 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
|
||||
strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE]));
|
||||
strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE]));
|
||||
#if NEW14
|
||||
|
||||
dlq_disconnect_request (callsigns, num_calls, cmd.hdr.portx, client);
|
||||
#else
|
||||
(void)num_calls; // suppress unused variable warning.
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("\n");
|
||||
dw_printf ("Can't process command '%c' from AGW client app %d.\n", cmd.hdr.datakind, client);
|
||||
dw_printf ("Connected packet mode is not implemented.\n");
|
||||
#endif
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1839,30 +1872,4 @@ static THREAD_F cmd_listen_thread (void *arg)
|
|||
} /* end send_to_client */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: server_callsign_registered_by_client
|
||||
*
|
||||
* Purpose: See if given callsign was registered.
|
||||
*
|
||||
* Inputs: callsign
|
||||
*
|
||||
* Returns: >= 0 for the client number.
|
||||
* -1 for not found.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
int server_callsign_registered_by_client (char *callsign)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < MAX_REG_CALLSIGNS; j++) {
|
||||
if (strcmp(registered_callsigns[j], callsign) == 0) {
|
||||
return (registered_by_client[j]);
|
||||
}
|
||||
}
|
||||
return (-1);
|
||||
|
||||
} /* end server_callsign_registered_by_client */
|
||||
|
||||
/* end server.c */
|
||||
|
|
2
server.h
2
server.h
|
@ -22,5 +22,7 @@ void server_link_established (int chan, int client, char *remote_call, char *own
|
|||
|
||||
void server_link_terminated (int chan, int client, char *remote_call, char *own_call, int timeout);
|
||||
|
||||
void server_rec_conn_data (int chan, int client, char *remote_call, char *own_call, int pid, char *data_ptr, int data_len);
|
||||
|
||||
|
||||
/* end server.h */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
APRS SYMBOL OVERLAY and EXTENSION TABLES in APRS 1.2 22 Mar 2016
|
||||
APRS SYMBOL OVERLAY and EXTENSION TABLES in APRS 1.2 18 Oct 2016
|
||||
---------------------------------------------------------------------
|
||||
|
||||
BACKGROUND: This file addresses new additions proposals (OVERLAYS)
|
||||
|
@ -7,6 +7,9 @@ document remains on the www.aprs.org/symbols/symbolsX.txt page, but
|
|||
only has one line per symbol character. Since each of the symbols
|
||||
can have up to 36 overlays, this gives us thousands of symbols codes.
|
||||
|
||||
18 Oct 2016: Added Green, Yellow, Red for Flood gaures using G,Y,R
|
||||
overlays + N for Normal. (on existing H2O symbol)
|
||||
Added DIGIPEATERS
|
||||
22 Mar 2016: Added A0 overlay circle for ALSTAR nodes
|
||||
and V0 for VOIP combined echolink and IRLP
|
||||
and P& for PSKmail node
|
||||
|
@ -226,6 +229,19 @@ BD = Bus Depot (new Aug 2014)
|
|||
LD = LIght Rail or Subway (new Aug 2014)
|
||||
SD = Seaport Depot (new Aug 2014)
|
||||
|
||||
DIGIPEATERS
|
||||
/# - Generic digipeater
|
||||
1# - WIDE1-1 digipeater
|
||||
A# - Alternate input (i.e. 144.990MHz) digipeater
|
||||
E# - Emergency powered (assumed full normal digi)
|
||||
I# - I-gate equipped digipeater
|
||||
L# - WIDEn-N with path length trapping
|
||||
P# - PacComm
|
||||
S# - SSn-N digipeater (includes WIDEn-N)
|
||||
X# - eXperimental digipeater
|
||||
V# - Viscous https://github.com/PhirePhly/aprx/blob/master/ViscousDigipeater.README
|
||||
W# - WIDEn-N, SSn-N and Trapping
|
||||
|
||||
EMERGENCY: #!
|
||||
/! = Police/Sheriff, etc
|
||||
\! = Emergency!
|
||||
|
@ -389,6 +405,18 @@ Tu = Tanker
|
|||
Cu = Chlorine Tanker
|
||||
Hu = Hazardous
|
||||
|
||||
WATER #w
|
||||
/w = Water Station or other H2O
|
||||
\w = flooding (or Avalanche/slides)
|
||||
Aw = Avalanche
|
||||
Gw = Green Flood Gauge
|
||||
Mw = Mud slide
|
||||
Nw = Normal flood gauge (blue)
|
||||
Rw = Red flood gauge
|
||||
Sw = Snow Blockage
|
||||
Yw = Yellow flood gauge
|
||||
|
||||
|
||||
|
||||
Anyone can use any overlay on any of the overlayable symbols for any
|
||||
special purpose. We are not trying to document all possible such
|
||||
|
|
27
tocalls.txt
27
tocalls.txt
|
@ -1,6 +1,10 @@
|
|||
APRS TO-CALL VERSION NUMBERS 29 Apr 2016
|
||||
APRS TO-CALL VERSION NUMBERS 14 Nov 2016
|
||||
-------------------------------------------------------------------
|
||||
WB4APR
|
||||
14 Nov 16 Added APINxx for PinPoint by AB0WV
|
||||
09 Nov 16 added APNICx for SQ5EKU http://sq5eku.blogspot.com/
|
||||
24 Oct 16 added APTKPT TrackPoint N0LP, removed APZTKP
|
||||
24 Aug 16 added APK004 for Kenwood THD-74
|
||||
29 Apr 16 added APFPRS for FreeDV by Jeroen PE1RXQ
|
||||
25 Feb 16 Added APCDS0 for Leon Lessing ZS6LMG's cell tracker
|
||||
21 Jan 16 added APDNOx for APRSduino by DO3SWW
|
||||
|
@ -12,12 +16,7 @@ APRS TO-CALL VERSION NUMBERS 29 Apr 2016
|
|||
27 Apr 15 added APZMAJ for Martyn M1MAJ DeLorme inReach Tracker
|
||||
21 Apr 15 added APB2MF & APR2MF DL2MF - MF2APRS Radiosonde
|
||||
06 Apr 15 added APAVT5 SainSonic AP510 - a 1watt tracker
|
||||
13 Mar 14 added APECAN Pecan Pico APRS Balloon Tracker
|
||||
02 Sep 14 added APSTMx W7QO's Balloon trackers
|
||||
21 Aug 14 added APSMSx Paul Defrusne's SMS gateway
|
||||
11 Aug 14 added APCWP8 John GM7HHB, WinphoneAPRS
|
||||
18 Dec 13 added APZWKR GM1WKR NetSked application
|
||||
|
||||
. . . . . ...
|
||||
11 Jan 12 added APYTxx for YagTracker and updated Yaesu APY008/350
|
||||
|
||||
In APRS, the AX.25 Destination address is not used for packet
|
||||
|
@ -86,6 +85,7 @@ a TOCALL number series:
|
|||
APHTxx HMTracker by IU0AAC
|
||||
API APICQx for ICQ
|
||||
APICxx for HA9MCQ Pic IGate
|
||||
APINxx for PinPoint by AB0WV
|
||||
APJ APJAxx JavAPRS
|
||||
APJExx JeAPRS
|
||||
APJIxx jAPRSIgate
|
||||
|
@ -93,6 +93,7 @@ a TOCALL number series:
|
|||
APJYnn KA2DDO Yet another APRS system
|
||||
APK APK0xx Kenwood TH-D7's
|
||||
APK003 Kenwood TH-D72
|
||||
APK004 Kenwood TH-D74
|
||||
APK1xx Kenwood D700's
|
||||
APK102 Kenwood D710
|
||||
APKRAM KRAMstuff.com - Mark. G7LEU
|
||||
|
@ -107,6 +108,7 @@ a TOCALL number series:
|
|||
APN9xx Kantronics KPC-9612 Roms
|
||||
APNAxx WB6ZSU's APRServe
|
||||
APNDxx DIGI_NED
|
||||
APNICx SQ5EKU http://sq5eku.blogspot.com/
|
||||
APNK01 Kenwood D700 (APK101) type
|
||||
APNK80 KAM version 8.0
|
||||
APNKMP KAM+
|
||||
|
@ -144,14 +146,15 @@ a TOCALL number series:
|
|||
APSCxx aprsc APRS-IS core server (OH7LZB, OH2MQK)
|
||||
APSK63 APRS Messenger -over-PSK63
|
||||
APSK25 APRS Messenger GMSK-250
|
||||
APSMSx Paul Defrusne's SMS gateway
|
||||
APSMSx Paul Dufresne's SMSGTE - SMS Gateway
|
||||
APSTMx for W7QO's Balloon trackers
|
||||
APSTPO for N0AGI Satellite Tracking and Operations
|
||||
APT APTIGR TigerTrack
|
||||
APTTxx Tiny Track
|
||||
APT2xx Tiny Track II
|
||||
APT APT2xx Tiny Track II
|
||||
APT3xx Tiny Track III
|
||||
APTAxx K4ATM's tiny track
|
||||
APTIGR TigerTrack
|
||||
APTKPT TrackPoint N0LP
|
||||
APTTxx Tiny Track
|
||||
APTWxx Byons WXTrac
|
||||
APTVxx for ATV/APRN and SSTV applications
|
||||
APU APU1xx UIview 16 bit applications
|
||||
|
@ -179,7 +182,7 @@ a TOCALL number series:
|
|||
APZMAJ Martyn M1MAJ DeLorme inReach Tracker
|
||||
APZMDR for HaMDR trackers - hessu * hes.iki.fi]
|
||||
APZPAD Smart Palm
|
||||
APZTKP TrackPoint, Nick N0LP (Balloon tracking)
|
||||
APZTKP TrackPoint, Nick N0LP (Balloon tracking)(depricated)
|
||||
APZWIT MAP27 radio (Mountain Rescue) EI7IG
|
||||
APZWKR GM1WKR NetSked application
|
||||
|
||||
|
|
419
tq.c
419
tq.c
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2012, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2012, 2014, 2015, 2016 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
@ -35,6 +35,8 @@
|
|||
*
|
||||
*---------------------------------------------------------------*/
|
||||
|
||||
#define TQ_C 1
|
||||
|
||||
#include "direwolf.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
@ -120,7 +122,7 @@ void tq_init (struct audio_s *audio_config_p)
|
|||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("tq_init ( %d )\n", nchan);
|
||||
dw_printf ("tq_init ( )\n");
|
||||
#endif
|
||||
|
||||
save_audio_config_p = audio_config_p;
|
||||
|
@ -187,11 +189,14 @@ void tq_init (struct audio_s *audio_config_p)
|
|||
*
|
||||
* Name: tq_append
|
||||
*
|
||||
* Purpose: Add a packet to the end of the specified transmit queue.
|
||||
* Purpose: Add an APRS packet to the end of the specified transmit queue.
|
||||
*
|
||||
* Connected mode is a little different. Use lm_data_request instead.
|
||||
*
|
||||
* Inputs: chan - Channel, 0 is first.
|
||||
*
|
||||
* prio - Priority, use TQ_PRIO_0_HI or TQ_PRIO_1_LO.
|
||||
* prio - Priority, use TQ_PRIO_0_HI for digipeated or
|
||||
* TQ_PRIO_1_LO for normal.
|
||||
*
|
||||
* pp - Address of packet object.
|
||||
* Caller should NOT make any references to
|
||||
|
@ -218,8 +223,11 @@ void tq_append (int chan, int prio, packet_t pp)
|
|||
|
||||
|
||||
#if DEBUG
|
||||
unsigned char *pinfo;
|
||||
int info_len = ax25_get_info (pp, &pinfo);
|
||||
if (info_len > 10) info_len = 10;
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("tq_append (chan=%d, prio=%d, pp=%p)\n", chan, prio, pp);
|
||||
dw_printf ("tq_append (chan=%d, prio=%d, pp=%p) \"%*s\"\n", chan, prio, pp, info_len, (char*)pinfo);
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -242,6 +250,8 @@ void tq_append (int chan, int prio, packet_t pp)
|
|||
if (chan < 0 || chan >= MAX_CHANS || ! save_audio_config_p->achan[chan].valid) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan);
|
||||
dw_printf ("This is probably a client application error, not a problem with direwolf.\n");
|
||||
dw_printf ("AX.25 for Linux is known to transmit on channels 2 & 8 sometimes when it shouldn't.\n");
|
||||
ax25_delete(pp);
|
||||
return;
|
||||
}
|
||||
|
@ -286,14 +296,6 @@ void tq_append (int chan, int prio, packet_t pp)
|
|||
|
||||
dw_mutex_lock (&tq_mutex);
|
||||
|
||||
// was_empty = 1;
|
||||
// for (c=0; c<tq_num_channels; c++) {
|
||||
// for (p=0; p<TQ_NUM_PRIO; p++) {
|
||||
// if (queue_head[c][p] != NULL)
|
||||
// was_empty = 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (queue_head[chan][prio] == NULL) {
|
||||
queue_head[chan][prio] = pp;
|
||||
}
|
||||
|
@ -334,7 +336,340 @@ void tq_append (int chan, int prio, packet_t pp)
|
|||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
} /* end tq_append */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: lm_data_request
|
||||
*
|
||||
* Purpose: Add an AX.25 frame to the end of the specified transmit queue.
|
||||
*
|
||||
* Use tq_append instead for APRS.
|
||||
*
|
||||
* Inputs: chan - Channel, 0 is first.
|
||||
*
|
||||
* prio - Priority, use TQ_PRIO_0_HI for priority (expedited)
|
||||
* or TQ_PRIO_1_LO for normal.
|
||||
*
|
||||
* pp - Address of packet object.
|
||||
* Caller should NOT make any references to
|
||||
* it after this point because it could
|
||||
* be deleted at any time.
|
||||
*
|
||||
* Outputs: A packet object is added to transmit queue.
|
||||
*
|
||||
* Description: 5.4.
|
||||
*
|
||||
* LM-DATA Request. The Data-link State Machine uses this primitive to pass
|
||||
* frames of any type (SABM, RR, UI, etc.) to the Link Multiplexer State Machine.
|
||||
*
|
||||
* LM-EXPEDITED-DATA Request. The data-link machine uses this primitive to
|
||||
* request transmission of each digipeat or expedite data frame.
|
||||
*
|
||||
* C2a.1
|
||||
*
|
||||
* PH-DATA Request. This primitive from the Link Multiplexer State Machine
|
||||
* provides an AX.25 frame of any type (UI, SABM, I, etc.) that is to be transmitted. An
|
||||
* unlimited number of frames may be provided. If the transmission exceeds the 10-
|
||||
* minute limit or the anti-hogging time limit, the half-duplex Physical State Machine
|
||||
* automatically relinquishes the channel for use by the other stations. The
|
||||
* transmission is automatically resumed at the next transmission opportunity
|
||||
* indicated by the CSMA/p-persistence contention algorithm.
|
||||
*
|
||||
* PH-EXPEDITED-DATA Request. This primitive from the Link Multiplexer State
|
||||
* Machine provides the AX.25 frame that is to be transmitted immediately. The
|
||||
* simplex Physical State Machine gives preference to priority frames over normal
|
||||
* frames, and will take advantage of the PRIACK window. Priority frames can be
|
||||
* provided by the link multiplexer at any time; a PH-SEIZE Request and subsequent
|
||||
* PH Release Request are not employed for priority frames.
|
||||
*
|
||||
* C3.1
|
||||
*
|
||||
* LM-DATA Request. This primitive from the Data-link State Machine provides a
|
||||
* AX.25 frame of any type (UI, SABM, I, etc.) that is to be transmitted. An unlimited
|
||||
* number of frames may be provided. The Link Multiplexer State Machine
|
||||
* accumulates the frames in a first-in, first-out queue until it is time to transmit them.
|
||||
*
|
||||
* C4.2
|
||||
*
|
||||
* LM-DATA Request. This primitive is used by the Data link State Machines to pass
|
||||
* frames of any type (SABM, RR, UI, etc.) to the Link Multiplexer State Machine.
|
||||
*
|
||||
* LM-EXPEDITED-DATA Request. This primitive is used by the Data link State
|
||||
* Machine to pass expedited data to the link multiplexer.
|
||||
*
|
||||
*
|
||||
* Implementation: Add packet to end of linked list.
|
||||
* Signal the transmit thread if the queue was formerly empty.
|
||||
*
|
||||
* Note that we have a transmit thread each audio channel.
|
||||
* Two channels can share one audio output device.
|
||||
*
|
||||
* IMPORTANT! Don't make an further references to the packet object after
|
||||
* giving it to lm_data_request.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
// TODO: FIXME: this is a copy of tq_append. Need to fine tune and explain why.
|
||||
|
||||
|
||||
void lm_data_request (int chan, int prio, packet_t pp)
|
||||
{
|
||||
packet_t plast;
|
||||
packet_t pnext;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
unsigned char *pinfo;
|
||||
int info_len = ax25_get_info (pp, &pinfo);
|
||||
if (info_len > 10) info_len = 10;
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("lm_data_request (chan=%d, prio=%d, pp=%p) \"%*s\"\n", chan, prio, pp, info_len, (char*)pinfo);
|
||||
#endif
|
||||
|
||||
|
||||
assert (prio >= 0 && prio < TQ_NUM_PRIO);
|
||||
|
||||
if (pp == NULL) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("INTERNAL ERROR: lm_data_request NULL packet pointer. Please report this!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
|
||||
if (ax25memdebug_get()) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("lm_data_request (chan=%d, prio=%d, seq=%d)\n", chan, prio, ax25memdebug_seq(pp));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (chan < 0 || chan >= MAX_CHANS || ! save_audio_config_p->achan[chan].valid) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan);
|
||||
ax25_delete(pp);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is transmit queue out of control?
|
||||
*/
|
||||
|
||||
if (tq_count(chan,prio) > 250) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Warning: Transmit packet queue for channel %d is extremely long.\n", chan);
|
||||
dw_printf ("Perhaps the channel is so busy there is no opportunity to send.\n");
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("lm_data_request: enter critical section\n");
|
||||
#endif
|
||||
|
||||
dw_mutex_lock (&tq_mutex);
|
||||
|
||||
|
||||
if (queue_head[chan][prio] == NULL) {
|
||||
queue_head[chan][prio] = pp;
|
||||
}
|
||||
else {
|
||||
plast = queue_head[chan][prio];
|
||||
while ((pnext = ax25_get_nextp(plast)) != NULL) {
|
||||
plast = pnext;
|
||||
}
|
||||
ax25_set_nextp (plast, pp);
|
||||
}
|
||||
|
||||
dw_mutex_unlock (&tq_mutex);
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("lm_data_request: left critical section\n");
|
||||
#endif
|
||||
|
||||
// Appendix C2a, from the Ax.25 protocol spec, says that a priority frame
|
||||
// will start transmission. If not already transmitting, normal frames
|
||||
// will pile up until LM-SEIZE Request starts transmission.
|
||||
|
||||
|
||||
// Erratum: It doesn't take long for that to fail.
|
||||
// We send SABM(e) frames to the transmit queue and the transmitter doesn't get activated.
|
||||
|
||||
|
||||
//NO! if (prio == TQ_PRIO_0_HI) {
|
||||
|
||||
#if DEBUG
|
||||
dw_printf ("lm_data_request (): about to wake up xmit thread.\n");
|
||||
#endif
|
||||
#if __WIN32__
|
||||
SetEvent (wake_up_event[chan]);
|
||||
#else
|
||||
if (xmit_thread_is_waiting[chan]) {
|
||||
int err;
|
||||
|
||||
dw_mutex_lock (&(wake_up_mutex[chan]));
|
||||
|
||||
err = pthread_cond_signal (&(wake_up_cond[chan]));
|
||||
if (err != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("lm_data_request: pthread_cond_signal err=%d", err);
|
||||
perror ("");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
dw_mutex_unlock (&(wake_up_mutex[chan]));
|
||||
}
|
||||
#endif
|
||||
//NO! }
|
||||
|
||||
} /* end lm_data_request */
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: lm_seize_request
|
||||
*
|
||||
* Purpose: Force start of transmit even if transmit queue is empty.
|
||||
*
|
||||
* Inputs: chan - Channel, 0 is first.
|
||||
*
|
||||
* Description: 5.4.
|
||||
*
|
||||
* LM-SEIZE Request. The Data-link State Machine uses this primitive to request the
|
||||
* Link Multiplexer State Machine to arrange for transmission at the next available
|
||||
* opportunity. The Data-link State Machine uses this primitive when an
|
||||
* acknowledgement must be made; the exact frame in which the acknowledgement
|
||||
* is sent will be chosen when the actual time for transmission arrives.
|
||||
*
|
||||
* C2a.1
|
||||
*
|
||||
* PH-SEIZE Request. This primitive requests the simplex state machine to begin
|
||||
* transmitting at the next available opportunity. When that opportunity has been
|
||||
* identified (according to the CSMA/p-persistence algorithm included within), the
|
||||
* transmitter started, a parameterized window provided for the startup of a
|
||||
* conventional repeater (if required), and a parameterized time allowed for the
|
||||
* synchronization of the remote station's receiver (known as TXDELAY in most
|
||||
* implementations), then a PH-SEIZE Confirm primitive is returned to the link
|
||||
* multiplexer.
|
||||
*
|
||||
* C3.1
|
||||
*
|
||||
* LM-SEIZE Request. This primitive requests the Link Multiplexer State Machine to
|
||||
* arrange for transmission at the next available opportunity. The Data-link State
|
||||
* Machine uses this primitive when an acknowledgment must be made, but the exact
|
||||
* frame in which the acknowledgment will be sent will be chosen when the actual
|
||||
* time for transmission arrives. The Link Multiplexer State Machine uses the LMSEIZE
|
||||
* Confirm primitive to indicate that the transmission opportunity has arrived.
|
||||
* After the Data-link State Machine has provided the acknowledgment, the Data-link
|
||||
* State Machine gives permission to stop transmission with the LM Release Request
|
||||
* primitive.
|
||||
*
|
||||
* C4.2
|
||||
*
|
||||
* LM-SEIZE Request. This primitive is used by the Data link State Machine to
|
||||
* request the Link Multiplexer State Machine to arrange for transmission at the next
|
||||
* available opportunity. The Data link State Machine uses this primitive when an
|
||||
* acknowledgment must be made, but the exact frame in which the acknowledgment
|
||||
* is sent will be chosen when the actual time for transmission arrives.
|
||||
*
|
||||
*
|
||||
* Implementation: Add a null frame (i.e. length of 0) to give the process a kick.
|
||||
* xmit.c needs to be smart enough to discard it.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
void lm_seize_request (int chan)
|
||||
{
|
||||
packet_t pp;
|
||||
int prio = TQ_PRIO_1_LO;
|
||||
|
||||
packet_t plast;
|
||||
packet_t pnext;
|
||||
|
||||
|
||||
#if DEBUG
|
||||
unsigned char *pinfo;
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("lm_seize_request (chan=%d)\n", chan);
|
||||
#endif
|
||||
|
||||
if (chan < 0 || chan >= MAX_CHANS || ! save_audio_config_p->achan[chan].valid) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("ERROR - Request to transmit on invalid radio channel %d.\n", chan);
|
||||
return;
|
||||
}
|
||||
|
||||
pp = ax25_new();
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
|
||||
if (ax25memdebug_get()) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("lm_seize_request (chan=%d, seq=%d)\n", chan, ax25memdebug_seq(pp));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("lm_seize_request: enter critical section\n");
|
||||
#endif
|
||||
|
||||
dw_mutex_lock (&tq_mutex);
|
||||
|
||||
|
||||
if (queue_head[chan][prio] == NULL) {
|
||||
queue_head[chan][prio] = pp;
|
||||
}
|
||||
else {
|
||||
plast = queue_head[chan][prio];
|
||||
while ((pnext = ax25_get_nextp(plast)) != NULL) {
|
||||
plast = pnext;
|
||||
}
|
||||
ax25_set_nextp (plast, pp);
|
||||
}
|
||||
|
||||
dw_mutex_unlock (&tq_mutex);
|
||||
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("lm_seize_request: left critical section\n");
|
||||
#endif
|
||||
|
||||
|
||||
#if DEBUG
|
||||
dw_printf ("lm_seize_request (): about to wake up xmit thread.\n");
|
||||
#endif
|
||||
#if __WIN32__
|
||||
SetEvent (wake_up_event[chan]);
|
||||
#else
|
||||
if (xmit_thread_is_waiting[chan]) {
|
||||
int err;
|
||||
|
||||
dw_mutex_lock (&(wake_up_mutex[chan]));
|
||||
|
||||
err = pthread_cond_signal (&(wake_up_cond[chan]));
|
||||
if (err != 0) {
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("lm_seize_request: pthread_cond_signal err=%d", err);
|
||||
perror ("");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
dw_mutex_unlock (&(wake_up_mutex[chan]));
|
||||
}
|
||||
#endif
|
||||
|
||||
} /* end lm_seize_request */
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
@ -484,7 +819,61 @@ packet_t tq_remove (int chan, int prio)
|
|||
}
|
||||
#endif
|
||||
return (result_p);
|
||||
}
|
||||
|
||||
} /* end tq_remove */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: tq_peek
|
||||
*
|
||||
* Purpose: Take a peek at the next frame in the queue but don't remove it.
|
||||
*
|
||||
* Inputs: chan - Channel, 0 is first.
|
||||
*
|
||||
* prio - Priority, use TQ_PRIO_0_HI or TQ_PRIO_1_LO.
|
||||
*
|
||||
* Returns: Pointer to packet object or NULL.
|
||||
*
|
||||
* Caller should NOT destroy it because it is still in the queue.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
packet_t tq_peek (int chan, int prio)
|
||||
{
|
||||
|
||||
packet_t result_p;
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("tq_peek(%d,%d) enter critical section\n", chan, prio);
|
||||
#endif
|
||||
|
||||
// I don't think we need critical region here.
|
||||
//dw_mutex_lock (&tq_mutex);
|
||||
|
||||
result_p = queue_head[chan][prio];
|
||||
// Just take a peek at the head. Don't remove it.
|
||||
|
||||
//dw_mutex_unlock (&tq_mutex);
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("tq_remove(%d,%d) leave critical section, returns %p\n", chan, prio, result_p);
|
||||
#endif
|
||||
|
||||
#if AX25MEMDEBUG
|
||||
|
||||
if (ax25memdebug_get() && result_p != NULL) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("tq_remove (chan=%d, prio=%d) seq=%d\n", chan, prio, ax25memdebug_seq(result_p));
|
||||
}
|
||||
#endif
|
||||
return (result_p);
|
||||
|
||||
} /* end tq_peek */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
|
|
6
tq.h
6
tq.h
|
@ -24,10 +24,16 @@ void tq_init (struct audio_s *audio_config_p);
|
|||
|
||||
void tq_append (int chan, int prio, packet_t pp);
|
||||
|
||||
void lm_data_request (int chan, int prio, packet_t pp);
|
||||
|
||||
void lm_seize_request (int chan);
|
||||
|
||||
void tq_wait_while_empty (int chan);
|
||||
|
||||
packet_t tq_remove (int chan, int prio);
|
||||
|
||||
packet_t tq_peek (int chan, int prio);
|
||||
|
||||
int tq_count (int chan, int prio);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -502,7 +502,7 @@ void waypoint_send_sentence (char *name_in, double dlat, double dlong, char symt
|
|||
* These would not be parsed properly:
|
||||
*
|
||||
* $PKWDWPL,150753,V,4237.14,N,07120.83,W,,,190316,,test3,/,*1B
|
||||
* $PKWDWPL,150758,V,4237.14,N,07120.83,W,,,190316,,test4,/**3B
|
||||
* $PKWDWPL,150758,V,4237.14,N,07120.83,W,,,190316,,test4,/ **3B
|
||||
*
|
||||
* We perform the usual substitution and the other end would
|
||||
* need to change them back after extracting from NMEA sentence.
|
||||
|
|
262
xid.c
262
xid.c
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2014 John Langner, WB2OSZ
|
||||
// Copyright (C) 2014, 2016 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
@ -49,28 +49,10 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "textcolor.h"
|
||||
//#include "xid.h"
|
||||
|
||||
|
||||
struct ax25_param_s {
|
||||
|
||||
int full_duplex;
|
||||
|
||||
// Order is important because negotiation keeps the lower.
|
||||
enum rej_e {implicit_reject=1, selective_reject=2, selective_reject_reject=3 } rej;
|
||||
|
||||
enum modulo_e {modulo_8 = 8, modulo_128 = 128} modulo;
|
||||
|
||||
int i_field_length_rx; /* In bytes. XID has it in bits. */
|
||||
|
||||
int window_size_rx;
|
||||
|
||||
int ack_timer; /* "T1" in mSec. */
|
||||
|
||||
int retries; /* "N1" */
|
||||
};
|
||||
#include "xid.h"
|
||||
|
||||
|
||||
|
||||
|
@ -102,13 +84,20 @@ struct ax25_param_s {
|
|||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: ...
|
||||
* Name: xid_parse
|
||||
*
|
||||
* Purpose: ...
|
||||
* Purpose: Decode information part of XID frame into individual values.
|
||||
*
|
||||
* Inputs: ...
|
||||
* Inputs: info - pointer to information part of frame.
|
||||
*
|
||||
* Outputs: ...
|
||||
* info_len - Number of bytes in information part of frame.
|
||||
* Could be 0.
|
||||
*
|
||||
* desc_size - Size of desc. 100 is good.
|
||||
*
|
||||
* Outputs: result - Structure with extracted values.
|
||||
*
|
||||
* desc - Text description for troubleshooting.
|
||||
*
|
||||
* Returns: 1 for mostly successful (with possible error messages), 0 for failure.
|
||||
*
|
||||
|
@ -120,37 +109,44 @@ struct ax25_param_s {
|
|||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
||||
int xid_parse (unsigned char *info, int info_len, struct xid_param_s *result, char *desc, int desc_size)
|
||||
{
|
||||
unsigned char *p;
|
||||
int group_len;
|
||||
char stemp[64];
|
||||
char debug_msg[256];
|
||||
|
||||
|
||||
result->full_duplex = 0;
|
||||
strlcpy (desc, "", desc_size);
|
||||
|
||||
// Default is implicit reject for pre version 2.2 but we wouldn't be here in that case.
|
||||
result->rej = selective_reject;
|
||||
// What should we do when some fields are missing?
|
||||
|
||||
result->modulo = modulo_8;
|
||||
// The AX.25 v2.2 protocol spec says, for most of these,
|
||||
// "If this field is not present, the current values are retained."
|
||||
|
||||
result->i_field_length_rx = 256; // bytes here but converted to bits during encoding.
|
||||
// We set the values to our usual G_UNKNOWN to mean undefined and let the caller deal with it.
|
||||
|
||||
// Default is 4 for pre version 2.2 but we wouldn't be here in that case.
|
||||
result->window_size_rx = result->modulo == modulo_128 ? 32 : 7;
|
||||
|
||||
result->ack_timer = 3000;
|
||||
result->retries = 10;
|
||||
result->full_duplex = G_UNKNOWN;
|
||||
result->rej = G_UNKNOWN;
|
||||
result->modulo = G_UNKNOWN;
|
||||
result->i_field_length_rx = G_UNKNOWN;
|
||||
result->window_size_rx = G_UNKNOWN;
|
||||
result->ack_timer = G_UNKNOWN;
|
||||
result->retries = G_UNKNOWN;
|
||||
|
||||
|
||||
/* Information field is optional but that seems pretty lame. */
|
||||
|
||||
if (info_len == 0) {
|
||||
return (1);
|
||||
}
|
||||
|
||||
p = info;
|
||||
|
||||
if (*p != FI_Format_Indicator) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("XID error: First byte of info field should be Format Indicator, %d.\n", FI_Format_Indicator);
|
||||
dw_printf ("XID error: First byte of info field should be Format Indicator, %02x.\n", FI_Format_Indicator);
|
||||
dw_printf ("XID info part: %02x %02x %02x %02x %02x ... length=%d\n", info[0], info[1], info[2], info[3], info[4], info_len);
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
|
@ -192,38 +188,46 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
|||
|
||||
if (pval & PV_Classes_Procedures_Half_Duplex && ! (pval & PV_Classes_Procedures_Full_Duplex)) {
|
||||
result->full_duplex = 0;
|
||||
strlcat (desc, "Half-Duplex ", desc_size);
|
||||
}
|
||||
else if (pval & PV_Classes_Procedures_Full_Duplex && ! (pval & PV_Classes_Procedures_Half_Duplex)) {
|
||||
result->full_duplex = 1;
|
||||
strlcat (desc, "Full-Duplex ", desc_size);
|
||||
}
|
||||
else {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("XID error: Expected one of Half or Full Duplex be set.\n");
|
||||
result->full_duplex = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PI_HDLC_Optional_Functions:
|
||||
|
||||
if (pval & PV_HDLC_Optional_Functions_REJ_cmd_resp && pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp) {
|
||||
if ((pval & PV_HDLC_Optional_Functions_REJ_cmd_resp) && (pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp)) {
|
||||
result->rej = selective_reject_reject; /* Both bits set */
|
||||
strlcat (desc, "SREJ-REJ ", desc_size);
|
||||
}
|
||||
else if (pval & PV_HDLC_Optional_Functions_REJ_cmd_resp && ! (pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp)) {
|
||||
else if ((pval & PV_HDLC_Optional_Functions_REJ_cmd_resp) && ! (pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp)) {
|
||||
result->rej = implicit_reject; /* Only REJ is set */
|
||||
strlcat (desc, "REJ ", desc_size);
|
||||
}
|
||||
else if ( ! (pval & PV_HDLC_Optional_Functions_REJ_cmd_resp) && pval & PV_HDLC_Optional_Functions_SREJ_cmd_resp) {
|
||||
result->rej = selective_reject; /* Only SREJ is set */
|
||||
strlcat (desc, "SREJ ", desc_size);
|
||||
}
|
||||
else {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("XID error: Expected one or both of REJ, SREJ to be set.\n");
|
||||
}
|
||||
|
||||
if (pval & PV_HDLC_Optional_Functions_Modulo_8 && ! (pval & PV_HDLC_Optional_Functions_Modulo_128)) {
|
||||
if ((pval & PV_HDLC_Optional_Functions_Modulo_8) && ! (pval & PV_HDLC_Optional_Functions_Modulo_128)) {
|
||||
result->modulo = modulo_8;
|
||||
strlcat (desc, "modulo-8 ", desc_size);
|
||||
}
|
||||
else if (pval & PV_HDLC_Optional_Functions_Modulo_128 && ! (pval & PV_HDLC_Optional_Functions_Modulo_8)) {
|
||||
else if ((pval & PV_HDLC_Optional_Functions_Modulo_128) && ! (pval & PV_HDLC_Optional_Functions_Modulo_8)) {
|
||||
result->modulo = modulo_128;
|
||||
strlcat (desc, "modulo-128 ", desc_size);
|
||||
}
|
||||
else {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
@ -256,6 +260,9 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
|||
|
||||
result->i_field_length_rx = pval / 8;
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "I-Field-Length-Rx=%d ", result->i_field_length_rx);
|
||||
strlcat (desc, stemp, desc_size);
|
||||
|
||||
if (pval & 0x7) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("XID error: I Field Length Rx, %d, is not a whole number of bytes.\n", pval);
|
||||
|
@ -267,22 +274,34 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
|||
|
||||
result->window_size_rx = pval;
|
||||
|
||||
if (pval < 1 || pval >= result->modulo) {
|
||||
snprintf (stemp, sizeof(stemp), "Window-Size-Rx=%d ", result->window_size_rx);
|
||||
strlcat (desc, stemp, desc_size);
|
||||
|
||||
if (pval < 1 || pval > 127) {
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
dw_printf ("XID error: Window Size Rx, %d, is not in range of 1 thru %d.\n", pval, result->modulo-1);
|
||||
result->window_size_rx = result->modulo == modulo_128 ? 32 : 7;
|
||||
dw_printf ("XID error: Window Size Rx, %d, is not in range of 1 thru 127.\n", pval);
|
||||
result->window_size_rx = 127;
|
||||
// Let the caller deal with modulo 8 consideration.
|
||||
}
|
||||
|
||||
//continue here.
|
||||
//continue here with more error checking.
|
||||
|
||||
break;
|
||||
|
||||
case PI_Ack_Timer:
|
||||
result->ack_timer = pval;
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "Ack-Timer=%d ", result->ack_timer);
|
||||
strlcat (desc, stemp, desc_size);
|
||||
|
||||
break;
|
||||
|
||||
case PI_Retries:
|
||||
case PI_Retries: // Is it retrys or retries?
|
||||
result->retries = pval;
|
||||
|
||||
snprintf (stemp, sizeof(stemp), "Retries=%d ", result->retries);
|
||||
strlcat (desc, stemp, desc_size);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -338,9 +357,10 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
|||
*
|
||||
* Outputs: info - Information part of XID frame.
|
||||
* Does not include the control byte.
|
||||
* Supply 32 bytes to be safe.
|
||||
* Use buffer of 40 bytes just to be safe.
|
||||
*
|
||||
* Returns: Number of bytes in the info part. Should be at most 27.
|
||||
* Again, provide a larger space just to be safe in case this ever changes.
|
||||
*
|
||||
* Description: 6.3.2 "Parameter negotiation occurs at any time. It is accomplished by sending
|
||||
* the XID command frame and receiving the XID response frame. Implementations of
|
||||
|
@ -359,35 +379,57 @@ int xid_parse (unsigned char *info, int info_len, struct ax25_param_s *result)
|
|||
* Both TNCs set themselves up based on the values used in the XID response. Negotiation
|
||||
* is used by Classes of Procedures, HDLC Optional Functions, Acknowledge Timer and Retries."
|
||||
*
|
||||
* Comment: I have a problem with "... occurs at any time." What if we were in the middle
|
||||
* of transferring a large file with k=32 then along comes XID which says switch to modulo 8?
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
int xid_encode (struct ax25_param_s *param, unsigned char *info)
|
||||
int xid_encode (struct xid_param_s *param, unsigned char *info)
|
||||
{
|
||||
unsigned char *p;
|
||||
int len;
|
||||
int x;
|
||||
int m = 0;
|
||||
|
||||
|
||||
p = info;
|
||||
|
||||
*p++ = FI_Format_Indicator;
|
||||
*p++ = GI_Group_Identifier;
|
||||
*p++ = 0;
|
||||
*p++ = 0x17;
|
||||
|
||||
m = 4; // classes of procedures
|
||||
m += 5; // HDLC optional features
|
||||
if (param->i_field_length_rx != G_UNKNOWN) m += 4;
|
||||
if (param->window_size_rx != G_UNKNOWN) m += 3;
|
||||
if (param->ack_timer != G_UNKNOWN) m += 4;
|
||||
if (param->retries != G_UNKNOWN) m += 3;
|
||||
|
||||
*p++ = m; // 0x17 if all present.
|
||||
|
||||
// "Classes of Procedures" has half / full duplex.
|
||||
|
||||
// We always send this.
|
||||
|
||||
*p++ = PI_Classes_of_Procedures;
|
||||
*p++ = 2;
|
||||
|
||||
x = PV_Classes_Procedures_Balanced_ABM;
|
||||
|
||||
if (param->full_duplex)
|
||||
if (param->full_duplex == 1)
|
||||
x |= PV_Classes_Procedures_Full_Duplex;
|
||||
else
|
||||
else // includes G_UNKNOWN
|
||||
x |= PV_Classes_Procedures_Half_Duplex;
|
||||
|
||||
*p++ = (x >> 8) & 0xff;
|
||||
*p++ = x & 0xff;
|
||||
|
||||
// "HDLC Optional Functions" contains REJ/SREJ & modulo 8/128.
|
||||
|
||||
// We always send this.
|
||||
// Watch out for unknown values and do something reasonable.
|
||||
|
||||
*p++ = PI_HDLC_Optional_Functions;
|
||||
*p++ = 3;
|
||||
|
||||
|
@ -396,7 +438,7 @@ int xid_encode (struct ax25_param_s *param, unsigned char *info)
|
|||
PV_HDLC_Optional_Functions_16_bit_FCS |
|
||||
PV_HDLC_Optional_Functions_Synchronous_Tx;
|
||||
|
||||
if (param->rej == implicit_reject || param->rej == selective_reject_reject)
|
||||
if (param->rej == implicit_reject || param->rej == selective_reject_reject || param->rej == G_UNKNOWN)
|
||||
x |= PV_HDLC_Optional_Functions_REJ_cmd_resp;
|
||||
|
||||
if (param->rej == selective_reject || param->rej == selective_reject_reject)
|
||||
|
@ -404,13 +446,18 @@ int xid_encode (struct ax25_param_s *param, unsigned char *info)
|
|||
|
||||
if (param->modulo == modulo_128)
|
||||
x |= PV_HDLC_Optional_Functions_Modulo_128;
|
||||
else
|
||||
else // includes G_UNKNOWN
|
||||
x |= PV_HDLC_Optional_Functions_Modulo_8;
|
||||
|
||||
*p++ = (x >> 16) & 0xff;
|
||||
*p++ = (x >> 8) & 0xff;
|
||||
*p++ = x & 0xff;
|
||||
|
||||
// The rest are skipped if undefined values.
|
||||
|
||||
// "I Field Length Rx" - max I field length acceptable to me.
|
||||
// This is in bits. 8191 would be max number of bytes to fit in field.
|
||||
|
||||
if (param->i_field_length_rx != G_UNKNOWN) {
|
||||
*p++ = PI_I_Field_Length_Rx;
|
||||
*p++ = 2;
|
||||
|
@ -419,12 +466,16 @@ int xid_encode (struct ax25_param_s *param, unsigned char *info)
|
|||
*p++ = x & 0xff;
|
||||
}
|
||||
|
||||
// "Window Size Rx"
|
||||
|
||||
if (param->window_size_rx != G_UNKNOWN) {
|
||||
*p++ = PI_Window_Size_Rx;
|
||||
*p++ = 1;
|
||||
*p++ = param->window_size_rx;
|
||||
}
|
||||
|
||||
// "Ack Timer" milliseconds. We could handle up to 65535 here.
|
||||
|
||||
if (param->ack_timer != G_UNKNOWN) {
|
||||
*p++ = PI_Ack_Timer;
|
||||
*p++ = 2;
|
||||
|
@ -432,6 +483,8 @@ int xid_encode (struct ax25_param_s *param, unsigned char *info)
|
|||
*p++ = param->ack_timer & 0xff;
|
||||
}
|
||||
|
||||
// "Retries."
|
||||
|
||||
if (param->retries != G_UNKNOWN) {
|
||||
*p++ = PI_Retries;
|
||||
*p++ = 1;
|
||||
|
@ -439,7 +492,7 @@ int xid_encode (struct ax25_param_s *param, unsigned char *info)
|
|||
}
|
||||
|
||||
len = p - info;
|
||||
assert (len <= 27);
|
||||
|
||||
return (len);
|
||||
|
||||
} /* end xid_encode */
|
||||
|
@ -508,14 +561,19 @@ static unsigned char example[27] = {
|
|||
|
||||
int main (int argc, char *argv[]) {
|
||||
|
||||
struct ax25_param_s param;
|
||||
struct ax25_param_s param2;
|
||||
struct xid_param_s param;
|
||||
struct xid_param_s param2;
|
||||
int n;
|
||||
unsigned char info[40];
|
||||
unsigned char info[40]; // Currently max of 27 but things can change.
|
||||
char desc[100]; // 80 is not adequate.
|
||||
|
||||
|
||||
/* parse example. */
|
||||
|
||||
n = xid_parse (example, sizeof(example), ¶m);
|
||||
n = xid_parse (example, sizeof(example), ¶m, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
|
@ -548,40 +606,96 @@ int main (int argc, char *argv[]) {
|
|||
param.modulo = modulo_8;
|
||||
param.i_field_length_rx = 2048;
|
||||
param.window_size_rx = 3;
|
||||
param.ack_timer = 3000;
|
||||
param.retries = 10;
|
||||
param.ack_timer = 1234;
|
||||
param.retries = 12;
|
||||
|
||||
n = xid_encode (¶m, info);
|
||||
n = xid_parse (info, n, ¶m2);
|
||||
n = xid_parse (info, n, ¶m2, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (param2.full_duplex == 1);
|
||||
assert (param2.rej == implicit_reject);
|
||||
assert (param2.modulo == modulo_8);
|
||||
assert (param2.i_field_length_rx == 2048);
|
||||
assert (param2.window_size_rx == 3);
|
||||
assert (param2.ack_timer == 3000);
|
||||
assert (param2.retries == 10);
|
||||
assert (param2.ack_timer == 1234);
|
||||
assert (param2.retries == 12);
|
||||
|
||||
/* Finally the third possbility for rej. */
|
||||
/* The third possbility for rej. We don't use this. */
|
||||
|
||||
param.full_duplex = 0;
|
||||
param.rej = selective_reject;
|
||||
param.modulo = modulo_8;
|
||||
param.i_field_length_rx = 256;
|
||||
param.i_field_length_rx = 61;
|
||||
param.window_size_rx = 4;
|
||||
param.ack_timer = 3000;
|
||||
param.retries = 10;
|
||||
param.ack_timer = 5555;
|
||||
param.retries = 9;
|
||||
|
||||
n = xid_encode (¶m, info);
|
||||
n = xid_parse (info, n, ¶m2);
|
||||
n = xid_parse (info, n, ¶m2, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (param2.full_duplex == 0);
|
||||
assert (param2.rej == selective_reject);
|
||||
assert (param2.modulo == modulo_8);
|
||||
assert (param2.i_field_length_rx == 256);
|
||||
assert (param2.i_field_length_rx == 61);
|
||||
assert (param2.window_size_rx == 4);
|
||||
assert (param2.ack_timer == 3000);
|
||||
assert (param2.retries == 10);
|
||||
assert (param2.ack_timer == 5555);
|
||||
assert (param2.retries == 9);
|
||||
|
||||
|
||||
/* Specify some and not others. */
|
||||
|
||||
param.full_duplex = 0;
|
||||
param.rej = selective_reject;
|
||||
param.modulo = modulo_8;
|
||||
param.i_field_length_rx = G_UNKNOWN;
|
||||
param.window_size_rx = G_UNKNOWN;
|
||||
param.ack_timer = 999;
|
||||
param.retries = G_UNKNOWN;
|
||||
|
||||
n = xid_encode (¶m, info);
|
||||
n = xid_parse (info, n, ¶m2, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (param2.full_duplex == 0);
|
||||
assert (param2.rej == selective_reject);
|
||||
assert (param2.modulo == modulo_8);
|
||||
assert (param2.i_field_length_rx == G_UNKNOWN);
|
||||
assert (param2.window_size_rx == G_UNKNOWN);
|
||||
assert (param2.ack_timer == 999);
|
||||
assert (param2.retries == G_UNKNOWN);
|
||||
|
||||
/* Default values for empty info field. */
|
||||
|
||||
n = 0;
|
||||
n = xid_parse (info, n, ¶m2, desc, sizeof(desc));
|
||||
|
||||
text_color_set (DW_COLOR_DEBUG);
|
||||
dw_printf ("%s\n", desc);
|
||||
|
||||
text_color_set (DW_COLOR_ERROR);
|
||||
|
||||
assert (param2.full_duplex == G_UNKNOWN);
|
||||
assert (param2.rej == G_UNKNOWN);
|
||||
assert (param2.modulo == G_UNKNOWN);
|
||||
assert (param2.i_field_length_rx == G_UNKNOWN);
|
||||
assert (param2.window_size_rx == G_UNKNOWN);
|
||||
assert (param2.ack_timer == G_UNKNOWN);
|
||||
assert (param2.retries == G_UNKNOWN);
|
||||
|
||||
|
||||
text_color_set (DW_COLOR_REC);
|
||||
dw_printf ("XID test: Success.\n");
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
|
||||
/* xid.h */
|
||||
|
||||
#include "ax25_pad.h" // for enum ax25_modulo_e
|
||||
|
||||
|
||||
struct xid_param_s {
|
||||
|
||||
int full_duplex;
|
||||
|
||||
// Order is important because negotiation keeps the lower value.
|
||||
// We will support only 1 & 2.
|
||||
|
||||
enum rej_e {implicit_reject=1, selective_reject=2, selective_reject_reject=3 } rej;
|
||||
|
||||
enum ax25_modulo_e modulo;
|
||||
|
||||
int i_field_length_rx; /* In bytes. XID has it in bits. */
|
||||
|
||||
int window_size_rx;
|
||||
|
||||
int ack_timer; /* "T1" in mSec. */
|
||||
|
||||
int retries; /* "N1" */
|
||||
};
|
||||
|
||||
|
||||
int xid_parse (unsigned char *info, int info_len, struct xid_param_s *result, char *desc, int desc_size);
|
||||
|
||||
int xid_encode (struct xid_param_s *param, unsigned char *info);
|
585
xmit.c
585
xmit.c
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
||||
//
|
||||
// Copyright (C) 2011, 2013, 2014, 2015 John Langner, WB2OSZ
|
||||
// Copyright (C) 2011, 2013, 2014, 2015, 2016 John Langner, WB2OSZ
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
@ -72,6 +72,7 @@
|
|||
#include "ptt.h"
|
||||
#include "dtime_now.h"
|
||||
#include "morse.h"
|
||||
#include "xid.h"
|
||||
|
||||
|
||||
|
||||
|
@ -107,6 +108,9 @@ static int xmit_bits_per_sec[MAX_CHANS]; /* Data transmission rate. */
|
|||
static int g_debug_xmit_packet; /* print packet in hexadecimal form for debugging. */
|
||||
|
||||
|
||||
// TODO: When this was first written, bits/sec was same as baud.
|
||||
// Need to revisit this for PSK modes where they are not the same.
|
||||
|
||||
|
||||
#define BITS_TO_MS(b,ch) (((b)*1000)/xmit_bits_per_sec[(ch)])
|
||||
|
||||
|
@ -130,8 +134,9 @@ static dw_mutex_t audio_out_dev_mutex[MAX_ADEVS];
|
|||
|
||||
|
||||
|
||||
static int wait_for_clear_channel (int channel, int nowait, int slotttime, int persist);
|
||||
static void xmit_ax25_frames (int c, int p, packet_t pp);
|
||||
static int wait_for_clear_channel (int channel, int slotttime, int persist);
|
||||
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 void xmit_speech (int c, packet_t pp);
|
||||
static void xmit_morse (int c, packet_t pp, int wpm);
|
||||
|
||||
|
@ -346,6 +351,62 @@ void xmit_set_txtail (int channel, int value)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: frame_flavor
|
||||
*
|
||||
* Purpose: Separate frames into different flavors so we can decide
|
||||
* which can be bundled into a single transmission and which should
|
||||
* be sent separately.
|
||||
*
|
||||
* Inputs: pp - Packet object.
|
||||
*
|
||||
* Returns: Flavor, one of:
|
||||
*
|
||||
* FLAVOR_SPEECH - Destination address is SPEECH.
|
||||
* FLAVOR_MORSE - Destination address is MORSE.
|
||||
* FLAVOR_APRS_NEW - APRS original, i.e. not digipeating.
|
||||
* FLAVOR_APRS_DIGI - APRS digipeating.
|
||||
* 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;
|
||||
|
||||
static flavor_t frame_flavor (packet_t pp)
|
||||
{
|
||||
|
||||
if (ax25_is_aprs (pp)) { // UI frame, PID 0xF0.
|
||||
// It's unfortunate APRS did not use its own special PID.
|
||||
|
||||
char dest[AX25_MAX_ADDR_LEN];
|
||||
|
||||
ax25_get_addr_no_ssid(pp, AX25_DESTINATION, dest);
|
||||
|
||||
if (strcmp(dest, "SPEECH") == 0) {
|
||||
return (FLAVOR_SPEECH);
|
||||
}
|
||||
|
||||
if (strcmp(dest, "MORSE") == 0) {
|
||||
return (FLAVOR_MORSE);
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
|
||||
if (ax25_get_num_repeaters(pp) >= 1 && ax25_get_h(pp,AX25_REPEATER_1)) {
|
||||
return (FLAVOR_APRS_DIGI);
|
||||
}
|
||||
|
||||
return (FLAVOR_APRS_NEW);
|
||||
}
|
||||
|
||||
return (FLAVOR_OTHER);
|
||||
|
||||
} /* end frame_flavor */
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: xmit_thread
|
||||
|
@ -367,6 +428,9 @@ void xmit_set_txtail (int channel, int value)
|
|||
* rather than waiting random times to avoid collisions.
|
||||
* The KPC-3 configuration option for this is "UIDWAIT OFF". (?)
|
||||
*
|
||||
* AX.25 connected mode also has a couple cases
|
||||
* where "expedited" frames are sent.
|
||||
*
|
||||
* Low Priority -
|
||||
*
|
||||
* Other packets are sent after a random wait time
|
||||
|
@ -382,6 +446,11 @@ void xmit_set_txtail (int channel, int value)
|
|||
* each channel has its own thread.
|
||||
* Add speech capability.
|
||||
*
|
||||
* Version 1.4: Rearranged logic for bundling multiple frames into a single transmission.
|
||||
*
|
||||
* The rule is that Speech, Morse Code, and APRS digipeated frames
|
||||
* are all sent separately. The rest can be bundled.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
#if __WIN32__
|
||||
|
@ -390,119 +459,126 @@ static unsigned __stdcall xmit_thread (void *arg)
|
|||
static void * xmit_thread (void *arg)
|
||||
#endif
|
||||
{
|
||||
int c = (int)(long)arg; // channel number.
|
||||
int chan = (int)(long)arg; // channel number.
|
||||
packet_t pp;
|
||||
int p;
|
||||
int prio;
|
||||
int ok;
|
||||
|
||||
/*
|
||||
* These are for timing of a transmission.
|
||||
* All are in usual unix time (seconds since 1/1/1970) but higher resolution
|
||||
*/
|
||||
|
||||
|
||||
while (1) {
|
||||
|
||||
tq_wait_while_empty (c);
|
||||
tq_wait_while_empty (chan);
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread, channel %d: woke up\n", c);
|
||||
#endif
|
||||
|
||||
for (p=0; p<TQ_NUM_PRIO; p++) {
|
||||
|
||||
pp = tq_remove (c, p);
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: tq_remove(chan=%d, prio=%d) returned %p\n", c, p, pp);
|
||||
#endif
|
||||
if (pp != NULL) {
|
||||
// Does this extra loop offer any benefit?
|
||||
while (tq_peek(chan, TQ_PRIO_0_HI) != NULL || tq_peek(chan, TQ_PRIO_1_LO) != NULL) {
|
||||
|
||||
/*
|
||||
* Wait for the channel to be clear.
|
||||
* For the high priority queue, begin transmitting immediately.
|
||||
* For the low priority queue, wait a random amount of time, in hopes
|
||||
* of minimizing collisions.
|
||||
* If there is something in the high priority queue, begin transmitting immediately.
|
||||
* Otherwise, wait a random amount of time, in hopes of minimizing collisions.
|
||||
*/
|
||||
ok = wait_for_clear_channel (c, (p==TQ_PRIO_0_HI), xmit_slottime[c], xmit_persist[c]);
|
||||
ok = wait_for_clear_channel (chan, xmit_slottime[chan], xmit_persist[chan]);
|
||||
|
||||
if (ok) {
|
||||
prio = TQ_PRIO_1_LO;
|
||||
pp = tq_remove (chan, TQ_PRIO_0_HI);
|
||||
if (pp != NULL) {
|
||||
prio = TQ_PRIO_0_HI;
|
||||
}
|
||||
else {
|
||||
pp = tq_remove (chan, TQ_PRIO_1_LO);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: tq_remove(chan=%d, prio=%d) returned %p\n", chan, prio, pp);
|
||||
#endif
|
||||
// Shouldn't have NULL here but be careful.
|
||||
|
||||
if (pp != NULL) {
|
||||
|
||||
|
||||
if (ok) {
|
||||
/*
|
||||
* Channel is clear and we have lock on output device.
|
||||
*
|
||||
* If destination is "SPEECH" send info part to speech synthesizer.
|
||||
* If destination is "MORSE" send as morse code.
|
||||
*/
|
||||
char dest[AX25_MAX_ADDR_LEN];
|
||||
int ssid = 0;
|
||||
|
||||
int ssid, wpm;
|
||||
|
||||
if (ax25_is_aprs (pp)) {
|
||||
switch (frame_flavor(pp)) {
|
||||
|
||||
ax25_get_addr_no_ssid(pp, AX25_DESTINATION, dest);
|
||||
case FLAVOR_SPEECH:
|
||||
xmit_speech (chan, pp);
|
||||
break;
|
||||
|
||||
case FLAVOR_MORSE:
|
||||
ssid = ax25_get_ssid(pp, AX25_DESTINATION);
|
||||
}
|
||||
else {
|
||||
strlcpy (dest, "", sizeof(dest));
|
||||
}
|
||||
|
||||
if (strcmp(dest, "SPEECH") == 0) {
|
||||
xmit_speech (c, pp);
|
||||
}
|
||||
else if (strcmp(dest, "MORSE") == 0) {
|
||||
|
||||
int wpm = ssid * 2;
|
||||
if (wpm == 0) wpm = MORSE_DEFAULT_WPM;
|
||||
wpm = (ssid > 0) ? (ssid * 2) : MORSE_DEFAULT_WPM;
|
||||
|
||||
// This is a bit of a hack so we don't respond too quickly for APRStt.
|
||||
// It will be sent in high priority queue while a beacon wouldn't.
|
||||
// Add a little delay so user has time release PTT after sending #.
|
||||
// This and default txdelay would give us a second.
|
||||
|
||||
if (p == TQ_PRIO_0_HI) {
|
||||
if (prio == TQ_PRIO_0_HI) {
|
||||
//text_color_set(DW_COLOR_DEBUG);
|
||||
//dw_printf ("APRStt morse xmit delay hack...\n");
|
||||
SLEEP_MS (700);
|
||||
}
|
||||
xmit_morse (chan, pp, wpm);
|
||||
break;
|
||||
|
||||
xmit_morse (c, pp, wpm);
|
||||
}
|
||||
else {
|
||||
xmit_ax25_frames (c, p, pp);
|
||||
}
|
||||
case FLAVOR_APRS_DIGI:
|
||||
xmit_ax25_frames (chan, prio, pp, 1); /* 1 means don't bundle */
|
||||
break;
|
||||
|
||||
dw_mutex_unlock (&(audio_out_dev_mutex[ACHAN2ADEV(c)]));
|
||||
case FLAVOR_APRS_NEW:
|
||||
case FLAVOR_OTHER:
|
||||
default:
|
||||
xmit_ax25_frames (chan, prio, pp, 256);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
||||
// Corresponding lock is in wait_for_clear_channel.
|
||||
|
||||
dw_mutex_unlock (&(audio_out_dev_mutex[ACHAN2ADEV(chan)]));
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Timeout waiting for clear channel.
|
||||
* Discard the packet.
|
||||
* Display with ERROR color rather than XMIT color.
|
||||
*/
|
||||
char stemp[1024]; /* max size needed? */
|
||||
int info_len;
|
||||
unsigned char *pinfo;
|
||||
char stemp[1024]; /* max size needed? */
|
||||
int info_len;
|
||||
unsigned char *pinfo;
|
||||
|
||||
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Waited too long for clear channel. Discarding packet below.\n");
|
||||
text_color_set(DW_COLOR_ERROR);
|
||||
dw_printf ("Waited too long for clear channel. Discarding packet below.\n");
|
||||
|
||||
ax25_format_addrs (pp, stemp);
|
||||
ax25_format_addrs (pp, stemp);
|
||||
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("[%d%c] ", chan, (prio==TQ_PRIO_0_HI) ? 'H' : 'L');
|
||||
|
||||
dw_printf ("%s", stemp); /* stations followed by : */
|
||||
ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
|
||||
dw_printf ("\n");
|
||||
ax25_delete (pp);
|
||||
dw_printf ("%s", stemp); /* stations followed by : */
|
||||
ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
|
||||
dw_printf ("\n");
|
||||
ax25_delete (pp);
|
||||
|
||||
} /* wait for clear channel. */
|
||||
} /* for high priority then low priority */
|
||||
}
|
||||
}
|
||||
} /* wait for clear channel error. */
|
||||
} /* Have pp */
|
||||
} /* while queue not empty */
|
||||
} /* while 1 */
|
||||
|
||||
return 0; /* unreachable but quiet the warning. */
|
||||
|
||||
|
@ -517,28 +593,31 @@ static void * xmit_thread (void *arg)
|
|||
* Purpose: After we have a clear channel, and possibly waited a random time,
|
||||
* we transmit one or more frames.
|
||||
*
|
||||
* Inputs: c - Channel number.
|
||||
* Inputs: chan - Channel number.
|
||||
*
|
||||
* p - Priority of the queue.
|
||||
* prio - Priority of the first frame.
|
||||
* Subsequent frames could be different.
|
||||
*
|
||||
* pp - Packet object pointer.
|
||||
* It will be deleted so caller should not try
|
||||
* to reference it after this.
|
||||
*
|
||||
* max_bundle - Max number of frames to bundle into one transmission.
|
||||
*
|
||||
* Description: Turn on transmitter.
|
||||
* Send flags for TXDELAY time.
|
||||
* Send the first packet, given by pp.
|
||||
* Possibly send more packets from the same queue.
|
||||
* Possibly send more packets from either queue.
|
||||
* Send flags for TXTAIL time.
|
||||
* Turn off transmitter.
|
||||
*
|
||||
*
|
||||
* How many frames in one transmission?
|
||||
* How many frames in one transmission? (for APRS)
|
||||
*
|
||||
* Should we send multiple frames in one transmission if we
|
||||
* have more than one sitting in the queue? At first I was thinking
|
||||
* this would help reduce channel congestion. I don't recall seeing
|
||||
* anything in the specifications allowing or disallowing multiple
|
||||
* anything in the APRS specifications allowing or disallowing multiple
|
||||
* frames in one transmission. I can think of some scenarios
|
||||
* where it might help. I can think of some where it would
|
||||
* definitely be counter productive.
|
||||
|
@ -558,21 +637,27 @@ static void * xmit_thread (void *arg)
|
|||
* Version 0.9: Earlier versions always sent one frame per transmission.
|
||||
* This was fine for APRS but more and more people are now
|
||||
* using this as a KISS TNC for connected protocols.
|
||||
* Rather than having a MAXFRAME configuration file item,
|
||||
* Rather than having a configuration file item,
|
||||
* we try setting the maximum number automatically.
|
||||
* 1 for digipeated frames, 7 for others.
|
||||
*
|
||||
* Version 1.4: Lift the limit. We could theoretically have a window size up to 127.
|
||||
* If another section pumps out that many quickly we shouldn't
|
||||
* break it up here. Empty out both queues with some exceptions.
|
||||
*
|
||||
* Digipeated APRS, Speech, and Morse code should have
|
||||
* their own separate transmissions.
|
||||
* Everything else can be bundled together.
|
||||
* Different priorities can share a single transmission.
|
||||
* Once we have control of the channel, we might as well keep going.
|
||||
* [High] Priority frames will always go to head of the line,
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static void xmit_ax25_frames (int c, int p, packet_t pp)
|
||||
static void xmit_ax25_frames (int chan, int prio, packet_t pp, int max_bundle)
|
||||
{
|
||||
|
||||
unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
|
||||
int flen;
|
||||
char stemp[1024]; /* max size needed? */
|
||||
int info_len;
|
||||
unsigned char *pinfo;
|
||||
int pre_flags, post_flags;
|
||||
int num_bits; /* Total number of bits in transmission */
|
||||
/* including all flags and bit stuffing. */
|
||||
|
@ -580,8 +665,7 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
|||
int already;
|
||||
int wait_more;
|
||||
|
||||
int maxframe; /* Maximum number of frames for one transmission. */
|
||||
int numframe; /* Number of frames sent during this transmission. */
|
||||
int numframe = 0; /* Number of frames sent during this transmission. */
|
||||
|
||||
/*
|
||||
* These are for timing of a transmission.
|
||||
|
@ -593,49 +677,6 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
|||
|
||||
int nb;
|
||||
|
||||
maxframe = (p == TQ_PRIO_0_HI) ? 1 : 7;
|
||||
|
||||
|
||||
/*
|
||||
* Print trasmitted packet. Prefix by channel and priority.
|
||||
* Do this before we get into the time critical part.
|
||||
*/
|
||||
ax25_format_addrs (pp, stemp);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
|
||||
dw_printf ("%s", stemp); /* stations followed by : */
|
||||
|
||||
/* Demystify non-APRS. Use same format for received frames in direwolf.c. */
|
||||
|
||||
if ( ! ax25_is_aprs(pp)) {
|
||||
ax25_frame_type_t ftype;
|
||||
cmdres_t cr;
|
||||
char desc[32];
|
||||
int pf;
|
||||
int nr;
|
||||
int ns;
|
||||
|
||||
ftype = ax25_frame_type (pp, &cr, desc, &pf, &nr, &ns);
|
||||
(void)ftype;
|
||||
|
||||
dw_printf ("(%s)", desc);
|
||||
}
|
||||
|
||||
ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
|
||||
dw_printf ("\n");
|
||||
(void)ax25_check_addresses (pp);
|
||||
|
||||
/* Optional hex dump of packet. */
|
||||
|
||||
if (g_debug_xmit_packet) {
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("------\n");
|
||||
ax25_hex_dump (pp);
|
||||
dw_printf ("------\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn on transmitter.
|
||||
* Start sending leading flag bytes.
|
||||
|
@ -647,26 +688,26 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
|||
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: Turn on PTT now for channel %d. speed = %d\n", c, xmit_bits_per_sec[c]);
|
||||
dw_printf ("xmit_thread: Turn on PTT now for channel %d. speed = %d\n", chan, xmit_bits_per_sec[chan]);
|
||||
#endif
|
||||
ptt_set (OCTYPE_PTT, c, 1);
|
||||
ptt_set (OCTYPE_PTT, chan, 1);
|
||||
|
||||
pre_flags = MS_TO_BITS(xmit_txdelay[c] * 10, c) / 8;
|
||||
num_bits = hdlc_send_flags (c, pre_flags, 0);
|
||||
pre_flags = MS_TO_BITS(xmit_txdelay[chan] * 10, chan) / 8;
|
||||
num_bits = hdlc_send_flags (chan, pre_flags, 0);
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: txdelay=%d [*10], pre_flags=%d, num_bits=%d\n", xmit_txdelay[c], pre_flags, num_bits);
|
||||
dw_printf ("xmit_thread: txdelay=%d [*10], pre_flags=%d, num_bits=%d\n", xmit_txdelay[chan], pre_flags, num_bits);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Transmit the frame.
|
||||
*/
|
||||
flen = ax25_pack (pp, fbuf);
|
||||
assert (flen >= 1 && flen <= sizeof(fbuf));
|
||||
nb = hdlc_send_frame (c, fbuf, flen);
|
||||
|
||||
nb = send_one_frame (chan, prio, pp);
|
||||
|
||||
num_bits += nb;
|
||||
numframe = 1;
|
||||
if (nb > 0) numframe++;
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: flen=%d, nb=%d, num_bits=%d, numframe=%d\n", flen, nb, num_bits, numframe);
|
||||
|
@ -674,68 +715,84 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
|||
ax25_delete (pp);
|
||||
|
||||
/*
|
||||
* Additional packets if available and not exceeding max.
|
||||
* See if we can bundle additional frames into this transmission.
|
||||
*/
|
||||
|
||||
while (numframe < maxframe && tq_count (c,p) > 0) {
|
||||
|
||||
pp = tq_remove (c, p);
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: tq_remove(chan=%d, prio=%d) returned %p\n", c, p, pp);
|
||||
#endif
|
||||
ax25_format_addrs (pp, stemp);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
|
||||
dw_printf ("%s", stemp); /* stations followed by : */
|
||||
ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
|
||||
dw_printf ("\n");
|
||||
(void)ax25_check_addresses (pp);
|
||||
|
||||
if (g_debug_xmit_packet) {
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("------\n");
|
||||
ax25_hex_dump (pp);
|
||||
dw_printf ("------\n");
|
||||
}
|
||||
int done = 0;
|
||||
while (numframe < max_bundle && ! done) {
|
||||
|
||||
/*
|
||||
* Transmit the frame.
|
||||
* Peek at what is available.
|
||||
* Don't remove from queue yet because it might not be eligible.
|
||||
*/
|
||||
flen = ax25_pack (pp, fbuf);
|
||||
assert (flen >= 1 && flen <= sizeof(fbuf));
|
||||
nb = hdlc_send_frame (c, fbuf, flen);
|
||||
num_bits += nb;
|
||||
numframe++;
|
||||
prio = TQ_PRIO_1_LO;
|
||||
pp = tq_peek (chan, TQ_PRIO_0_HI);
|
||||
if (pp != NULL) {
|
||||
prio = TQ_PRIO_0_HI;
|
||||
}
|
||||
else {
|
||||
pp = tq_peek (chan, TQ_PRIO_1_LO);
|
||||
}
|
||||
|
||||
if (pp != NULL) {
|
||||
|
||||
switch (frame_flavor(pp)) {
|
||||
|
||||
case FLAVOR_SPEECH:
|
||||
case FLAVOR_MORSE:
|
||||
case FLAVOR_APRS_DIGI:
|
||||
default:
|
||||
done = 1; // not eligible for bundling.
|
||||
break;
|
||||
|
||||
case FLAVOR_APRS_NEW:
|
||||
case FLAVOR_OTHER:
|
||||
|
||||
pp = tq_remove (chan, prio);
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: flen=%d, nb=%d, num_bits=%d, numframe=%d\n", flen, nb, num_bits, numframe);
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: tq_remove(chan=%d, prio=%d) returned %p\n", chan, prio, pp);
|
||||
#endif
|
||||
ax25_delete (pp);
|
||||
|
||||
nb = send_one_frame (chan, prio, pp);
|
||||
|
||||
num_bits += nb;
|
||||
if (nb > 0) numframe++;
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: flen=%d, nb=%d, num_bits=%d, numframe=%d\n", flen, nb, num_bits, numframe);
|
||||
#endif
|
||||
ax25_delete (pp);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Need TXTAIL because we don't know exactly when the sound is done.
|
||||
*/
|
||||
|
||||
post_flags = MS_TO_BITS(xmit_txtail[c] * 10, c) / 8;
|
||||
nb = hdlc_send_flags (c, post_flags, 1);
|
||||
post_flags = MS_TO_BITS(xmit_txtail[chan] * 10, chan) / 8;
|
||||
nb = hdlc_send_flags (chan, post_flags, 1);
|
||||
num_bits += nb;
|
||||
#if DEBUG
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("xmit_thread: txtail=%d [*10], post_flags=%d, nb=%d, num_bits=%d\n", xmit_txtail[c], post_flags, nb, num_bits);
|
||||
dw_printf ("xmit_thread: txtail=%d [*10], post_flags=%d, nb=%d, num_bits=%d\n", xmit_txtail[chan], post_flags, nb, num_bits);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* While demodulating is CPU intensive, generating the tones is not.
|
||||
* Example: on the RPi, with 50% of the CPU taken with two receive
|
||||
* Example: on the RPi model 1, with 50% of the CPU taken with two receive
|
||||
* channels, a transmission of more than a second is generated in
|
||||
* about 40 mS of elapsed real time.
|
||||
*/
|
||||
|
||||
audio_wait(ACHAN2ADEV(c));
|
||||
audio_wait(ACHAN2ADEV(chan));
|
||||
|
||||
/*
|
||||
* Ideally we should be here just about the time when the audio is ending.
|
||||
|
@ -744,7 +801,7 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
|||
* Calculate how long the frame(s) should take in milliseconds.
|
||||
*/
|
||||
|
||||
duration = BITS_TO_MS(num_bits, c);
|
||||
duration = BITS_TO_MS(num_bits, chan);
|
||||
|
||||
/*
|
||||
* See how long it has been since PTT was turned on.
|
||||
|
@ -786,12 +843,123 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
|||
dw_printf ("xmit_thread: Turn off PTT now. Actual time on was %d mS, vs. %d desired\n", (int) ((time_now - time_ptt) * 1000.), duration);
|
||||
#endif
|
||||
|
||||
ptt_set (OCTYPE_PTT, c, 0);
|
||||
ptt_set (OCTYPE_PTT, chan, 0);
|
||||
|
||||
} /* end xmit_ax25_frames */
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: send_one_frame
|
||||
*
|
||||
* Purpose: Send one AX.25 frame.
|
||||
*
|
||||
* Inputs: c - Channel number.
|
||||
*
|
||||
* p - Priority.
|
||||
*
|
||||
* pp - Packet object pointer. Caller will delete it.
|
||||
*
|
||||
* Returns: Number of bits transmitted.
|
||||
*
|
||||
* Description: Caller is responsible for activiating PTT, TXDELAY,
|
||||
* deciding how many frames can be in one transmission,
|
||||
* deactivating PTT.
|
||||
*
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static int send_one_frame (int c, int p, packet_t pp)
|
||||
{
|
||||
unsigned char fbuf[AX25_MAX_PACKET_LEN+2];
|
||||
int flen;
|
||||
char stemp[1024]; /* max size needed? */
|
||||
int info_len;
|
||||
unsigned char *pinfo;
|
||||
int nb;
|
||||
|
||||
|
||||
if (ax25_is_null_frame(pp)) {
|
||||
return(0);
|
||||
}
|
||||
|
||||
ax25_format_addrs (pp, stemp);
|
||||
info_len = ax25_get_info (pp, &pinfo);
|
||||
text_color_set(DW_COLOR_XMIT);
|
||||
dw_printf ("[%d%c] ", c, p==TQ_PRIO_0_HI ? 'H' : 'L');
|
||||
dw_printf ("%s", stemp); /* stations followed by : */
|
||||
|
||||
/* Demystify non-APRS. Use same format for received frames in direwolf.c. */
|
||||
|
||||
if ( ! ax25_is_aprs(pp)) {
|
||||
ax25_frame_type_t ftype;
|
||||
cmdres_t cr;
|
||||
char desc[80];
|
||||
int pf;
|
||||
int nr;
|
||||
int ns;
|
||||
|
||||
ftype = ax25_frame_type (pp, &cr, desc, &pf, &nr, &ns);
|
||||
|
||||
dw_printf ("(%s)", desc);
|
||||
|
||||
if (ftype == frame_type_U_XID) {
|
||||
struct xid_param_s param;
|
||||
char info2text[100];
|
||||
|
||||
xid_parse (pinfo, info_len, ¶m, info2text, sizeof(info2text));
|
||||
dw_printf (" %s\n", info2text);
|
||||
}
|
||||
else {
|
||||
ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
|
||||
dw_printf ("\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
ax25_safe_print ((char *)pinfo, info_len, ! ax25_is_aprs(pp));
|
||||
dw_printf ("\n");
|
||||
}
|
||||
|
||||
(void)ax25_check_addresses (pp);
|
||||
|
||||
/* Optional hex dump of packet. */
|
||||
|
||||
if (g_debug_xmit_packet) {
|
||||
|
||||
text_color_set(DW_COLOR_DEBUG);
|
||||
dw_printf ("------\n");
|
||||
ax25_hex_dump (pp);
|
||||
dw_printf ("------\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transmit the frame.
|
||||
*/
|
||||
flen = ax25_pack (pp, fbuf);
|
||||
assert (flen >= 1 && flen <= sizeof(fbuf));
|
||||
|
||||
int send_invalid_fcs2 = 0;
|
||||
|
||||
if (save_audio_config_p->xmit_error_rate != 0) {
|
||||
float r = (float)(rand()) / (float)RAND_MAX; // Random, 0.0 to 1.0
|
||||
|
||||
if (save_audio_config_p->xmit_error_rate / 100.0 > r) {
|
||||
send_invalid_fcs2 = 1;
|
||||
text_color_set(DW_COLOR_INFO);
|
||||
dw_printf ("Intentionally sending invalid CRC for frame above. Xmit Error rate = %d per cent.\n", save_audio_config_p->xmit_error_rate);
|
||||
}
|
||||
}
|
||||
|
||||
nb = hdlc_send_frame (c, fbuf, flen, send_invalid_fcs2);
|
||||
return (nb);
|
||||
|
||||
} /* end send_one_frame */
|
||||
|
||||
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------
|
||||
*
|
||||
* Name: xmit_speech
|
||||
|
@ -814,8 +982,6 @@ static void xmit_ax25_frames (int c, int p, packet_t pp)
|
|||
|
||||
static void xmit_speech (int c, packet_t pp)
|
||||
{
|
||||
|
||||
|
||||
int info_len;
|
||||
unsigned char *pinfo;
|
||||
|
||||
|
@ -932,8 +1098,6 @@ int xmit_speak_it (char *script, int c, char *orig_msg)
|
|||
|
||||
static void xmit_morse (int c, packet_t pp, int wpm)
|
||||
{
|
||||
|
||||
|
||||
int info_len;
|
||||
unsigned char *pinfo;
|
||||
|
||||
|
@ -960,15 +1124,7 @@ static void xmit_morse (int c, packet_t pp, int wpm)
|
|||
* Purpose: Wait for the radio channel to be clear and any
|
||||
* additional time for collision avoidance.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Inputs: channel - Radio channel number.
|
||||
*
|
||||
* nowait - Should be true for the high priority queue
|
||||
* (packets being digipeated). This will
|
||||
* allow transmission immediately when the
|
||||
* channel is clear rather than waiting a
|
||||
* random amount of time.
|
||||
* Inputs: chan - Radio channel number.
|
||||
*
|
||||
* slottime - Amount of time to wait for each iteration
|
||||
* of the waiting algorithm. 10 mSec units.
|
||||
|
@ -977,14 +1133,12 @@ static void xmit_morse (int c, packet_t pp, int wpm)
|
|||
*
|
||||
* Returns: 1 for OK. 0 for timeout.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* New in version 1.2: also obtain a lock on audio out device.
|
||||
* Description: New in version 1.2: also obtain a lock on audio out device.
|
||||
*
|
||||
* Transmit delay algorithm:
|
||||
*
|
||||
* Wait for channel to be clear.
|
||||
* Return if nowait is true.
|
||||
* If anything in high priority queue, bail out of the following.
|
||||
*
|
||||
* Wait slottime * 10 milliseconds.
|
||||
* Generate an 8 bit random number in range of 0 - 255.
|
||||
|
@ -1015,17 +1169,13 @@ static void xmit_morse (int c, packet_t pp, int wpm)
|
|||
#define WAIT_TIMEOUT_MS (60 * 1000)
|
||||
#define WAIT_CHECK_EVERY_MS 10
|
||||
|
||||
static int wait_for_clear_channel (int channel, int nowait, int slottime, int persist)
|
||||
static int wait_for_clear_channel (int chan, int slottime, int persist)
|
||||
{
|
||||
int r;
|
||||
int n;
|
||||
|
||||
|
||||
n = 0;
|
||||
int n = 0;
|
||||
|
||||
start_over_again:
|
||||
|
||||
while (hdlc_rec_data_detect_any(channel)) {
|
||||
while (hdlc_rec_data_detect_any(chan)) {
|
||||
SLEEP_MS(WAIT_CHECK_EVERY_MS);
|
||||
n++;
|
||||
if (n > (WAIT_TIMEOUT_MS / WAIT_CHECK_EVERY_MS)) {
|
||||
|
@ -1033,41 +1183,52 @@ start_over_again:
|
|||
}
|
||||
}
|
||||
|
||||
//TODO1.2: rethink dwait.
|
||||
//TODO: rethink dwait.
|
||||
|
||||
/*
|
||||
* Added in version 1.2 - for transceivers that can't
|
||||
* turn around fast enough when using squelch and VOX.
|
||||
*/
|
||||
|
||||
if (save_audio_config_p->achan[channel].dwait > 0) {
|
||||
SLEEP_MS (save_audio_config_p->achan[channel].dwait * 10);
|
||||
if (save_audio_config_p->achan[chan].dwait > 0) {
|
||||
SLEEP_MS (save_audio_config_p->achan[chan].dwait * 10);
|
||||
}
|
||||
|
||||
if (hdlc_rec_data_detect_any(channel)) {
|
||||
if (hdlc_rec_data_detect_any(chan)) {
|
||||
goto start_over_again;
|
||||
}
|
||||
|
||||
if ( ! nowait) {
|
||||
/*
|
||||
* Wait random time.
|
||||
* Proceed to transmit sooner if anything shows up in high priority queue.
|
||||
*/
|
||||
while (tq_peek(chan, TQ_PRIO_0_HI) == NULL) {
|
||||
int r;
|
||||
|
||||
while (1) {
|
||||
SLEEP_MS (slottime * 10);
|
||||
|
||||
SLEEP_MS (slottime * 10);
|
||||
|
||||
if (hdlc_rec_data_detect_any(channel)) {
|
||||
goto start_over_again;
|
||||
}
|
||||
|
||||
r = rand() & 0xff;
|
||||
if (r <= persist) {
|
||||
break;
|
||||
}
|
||||
if (hdlc_rec_data_detect_any(chan)) {
|
||||
goto start_over_again;
|
||||
}
|
||||
|
||||
r = rand() & 0xff;
|
||||
if (r <= persist) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO1.2
|
||||
/*
|
||||
* This is to prevent two channels from transmitting at the same time
|
||||
* thru a stereo audio device.
|
||||
* We are not clever enough to combine two audio streams.
|
||||
* They must go out one at a time.
|
||||
* Documentation recommends using separate audio device for each channel rather than stereo.
|
||||
* That also allows better use of multiple cores for receiving.
|
||||
*/
|
||||
|
||||
while ( ! dw_mutex_try_lock(&(audio_out_dev_mutex[ACHAN2ADEV(channel)]))) {
|
||||
// TODO: review this.
|
||||
|
||||
while ( ! dw_mutex_try_lock(&(audio_out_dev_mutex[ACHAN2ADEV(chan)]))) {
|
||||
SLEEP_MS(WAIT_CHECK_EVERY_MS);
|
||||
n++;
|
||||
if (n > (WAIT_TIMEOUT_MS / WAIT_CHECK_EVERY_MS)) {
|
||||
|
|
Loading…
Reference in New Issue