Skip to content

Commit 591a897

Browse files
committed
Improved generation of SREJ frames for more efficient AX.25 v2.2 connected mode.
1 parent c8319fc commit 591a897

12 files changed

Lines changed: 543 additions & 351 deletions

File tree

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### New Features:
66

7+
- Improved generation of SREJ frames for more efficient AX.25 v2.2 connected mode.
8+
79
- Add delay before exit so any error messages can be read before window disappears.
810

911
- New SCHANNEL feature to map a channel number to an external serial port TNC. See [APRS-LoRa-VHF-APRS-Bridge.pdf](https://raw.githubusercontent.com/wb2osz/direwolf-doc/main/APRS-LoRa-VHF-APRS-Bridge.pdf) for explanation and example.

src/ax25_link.c

Lines changed: 159 additions & 286 deletions
Large diffs are not rendered by default.

src/ax25_link.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626
#define AX25_N2_RETRY_MAX 15
2727

2828

29-
#define AX25_T1V_FRACK_MIN 1 // Number of seconds to wait before retrying.
30-
#define AX25_T1V_FRACK_DEFAULT 3 // KPC-3+ has 4. TM-D710A has 3.
29+
#define AX25_T1V_FRACK_MIN 2 // Number of seconds to wait before retrying.
30+
#define AX25_T1V_FRACK_DEFAULT 4 // KPC-3+ has 4. TM-D710A has 3.
31+
// Previous 3 seems too agressive in practice for 1200 bps.
3132
#define AX25_T1V_FRACK_MAX 15
3233

3334

@@ -37,13 +38,20 @@
3738

3839
#define AX25_K_MAXFRAME_EXTENDED_MIN 1
3940
#define AX25_K_MAXFRAME_EXTENDED_DEFAULT 32
40-
#define AX25_K_MAXFRAME_EXTENDED_MAX 63 // In theory 127 but I'm restricting as explained in SREJ handling.
41-
41+
#define AX25_K_MAXFRAME_EXTENDED_MAX 63 // It cannot be 127 because SREJ requires out‑of‑order acceptance,
42+
// which forces the window to be <= modulus/2.
43+
// With a window of 127, the sender could have 126 outstanding
44+
// unacknowledged frames. If the receiver issues an SREJ for
45+
// frame N, but the sender has already wrapped and reused sequence
46+
// numbers, the receiver cannot know:
47+
// - Is this SREJ referring to the old frame N?
48+
// - Or the new frame N after wrap-around?
49+
// This ambiguity makes SREJ unsafe with a window anywhere near the modulus.
4250

4351

4452
// Call once at startup time.
4553

46-
void ax25_link_init (struct misc_config_s *pconfig, int debug);
54+
void ax25_link_init (struct misc_config_s *pconfig, int debug, int stats);
4755

4856

4957

src/ax25_pad.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2207,6 +2207,14 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *desc, in
22072207
this_p->modulo = modulo_128;
22082208
}
22092209

2210+
// Display "n(s)" & "n(r)" as upper case to indicate modulo 128.
2211+
2212+
char ns_str[8] = "n(s)";
2213+
char nr_str[8] = "n(r)";
2214+
if (this_p->modulo == modulo_128) {
2215+
strlcpy (ns_str, "N(S)", sizeof(ns_str));
2216+
strlcpy (nr_str, "N(R)", sizeof(nr_str));
2217+
}
22102218

22112219
if (this_p->modulo == modulo_128) {
22122220
c2 = ax25_get_c2 (this_p);
@@ -2242,8 +2250,8 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *desc, in
22422250
*nr = (c >> 5) & 7;
22432251
}
22442252

2245-
//snprintf (desc, DESC_SIZ, "I %s, n(s)=%d, n(r)=%d, %s=%d", cr_text, *ns, *nr, pf_text, *pf);
2246-
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));
2253+
snprintf (desc, DESC_SIZ, "I %s, %s=%d, %s=%d, %s=%d, pid=0x%02x",
2254+
cr_text, ns_str, *ns, nr_str, *nr, pf_text, *pf, ax25_get_pid(this_p));
22472255
return (frame_type_I);
22482256
}
22492257
else if ((c & 2) == 0) {
@@ -2259,12 +2267,11 @@ ax25_frame_type_t ax25_frame_type (packet_t this_p, cmdres_t *cr, char *desc, in
22592267
*nr = (c >> 5) & 7;
22602268
}
22612269

2262-
22632270
switch ((c >> 2) & 3) {
2264-
case 0: snprintf (desc, DESC_SIZ, "RR %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_RR); break;
2265-
case 1: snprintf (desc, DESC_SIZ, "RNR %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_RNR); break;
2266-
case 2: snprintf (desc, DESC_SIZ, "REJ %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_REJ); break;
2267-
case 3: snprintf (desc, DESC_SIZ, "SREJ %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_SREJ); break;
2271+
case 0: snprintf (desc, DESC_SIZ, "RR %s, %s=%d, %s=%d", cr_text, nr_str, *nr, pf_text, *pf); return (frame_type_S_RR); break;
2272+
case 1: snprintf (desc, DESC_SIZ, "RNR %s, %s=%d, %s=%d", cr_text, nr_str, *nr, pf_text, *pf); return (frame_type_S_RNR); break;
2273+
case 2: snprintf (desc, DESC_SIZ, "REJ %s, %s=%d, %s=%d", cr_text, nr_str, *nr, pf_text, *pf); return (frame_type_S_REJ); break;
2274+
case 3: snprintf (desc, DESC_SIZ, "SREJ %s, %s=%d, %s=%d", cr_text, nr_str, *nr, pf_text, *pf); return (frame_type_S_SREJ); break;
22682275
}
22692276
}
22702277
else {

src/config.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4785,6 +4785,10 @@ void config_init (char *fname, struct audio_s *p_audio_config,
47854785
* IGTXVIA channel [ path ]
47864786
*/
47874787

4788+
// FIXME: Should allow multiple transmit channels.
4789+
// Should probably have multiple IGTXVIA, rather than a list of channels,
4790+
// because they might want different paths.
4791+
47884792
else if (strcasecmp(t, "IGTXVIA") == 0) {
47894793
int n;
47904794

src/direwolf.c

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,10 @@ int main (int argc, char *argv[])
215215
char input_file[80];
216216
char T_opt_timestamp[40];
217217

218-
int t_opt = 1; /* Text color option. */
218+
int t_opt = 1; /* Text color option. */
219+
char o_opt[80] = ""; // Console capture received raw packets.
220+
char O_opt[80] = ""; // Console capture all output.
221+
219222
int a_opt = 0; /* "-a n" interval, in seconds, for audio statistics report. 0 for none. */
220223
int g_opt = 0; /* G3RUH mode, ignoring default for speed. */
221224
int j_opt = 0; /* 2400 bps PSK compatible with direwolf <= 1.5 */
@@ -236,6 +239,7 @@ int main (int argc, char *argv[])
236239
int d_2_opt = 0; /* "-d 2" option for IL2P. Default minimal. Repeat for more detail. */
237240
int d_c_opt = 0; /* "-d c" option for connected mode data link state machine. */
238241
int d_q_opt = 0; /* "-d q" option for data link state machine queue. */
242+
int d_s_opt = 0; /* "-d s" print statistics when link ends. */
239243

240244
int aprstt_debug = 0; /* "-d d" option for APRStt (think Dtmf) debug. */
241245

@@ -295,11 +299,19 @@ int main (int argc, char *argv[])
295299

296300
// FIXME: consider case of no space between t and number.
297301

302+
// Prescan for options that are needed before ant text goes to console.
303+
298304
for (j=1; j<argc-1; j++) {
299305
if (strcmp(argv[j], "-t") == 0) {
300306
t_opt = atoi (argv[j+1]);
301307
//dw_printf ("DEBUG: text color option = %d.\n", t_opt);
302308
}
309+
if (strcmp(argv[j], "-o") == 0) {
310+
strlcpy (o_opt, argv[j+1], sizeof(o_opt));
311+
}
312+
if (strcmp(argv[j], "-O") == 0) {
313+
strlcpy (O_opt, argv[j+1], sizeof(O_opt));
314+
}
303315
}
304316

305317
// TODO: control development/beta/release by version.h instead of changing here.
@@ -310,6 +322,10 @@ int main (int argc, char *argv[])
310322
// https://www.dennisbabkin.com/blog/?t=how-to-tell-the-real-version-of-windows-your-app-is-running-on
311323

312324
text_color_init(t_opt);
325+
dw_printf_capture_init (o_opt, O_opt);
326+
327+
// Print application version.
328+
313329
text_color_set(DW_COLOR_INFO);
314330
//dw_printf ("Dire Wolf version %d.%d (%s) BETA TEST 1\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
315331
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "B", __DATE__);
@@ -453,7 +469,7 @@ int main (int argc, char *argv[])
453469

454470
/* ':' following option character means arg is required. */
455471

456-
c = getopt_long(argc, argv, "hP:B:gjJD:U:c:px:r:b:n:d:q:t:ul:L:Sa:E:T:e:X:AI:i:",
472+
c = getopt_long(argc, argv, "hP:B:gjJD:U:c:px:r:b:n:d:q:t:ul:L:Sa:E:T:e:X:AI:i:o:O:",
457473
long_options, &option_index);
458474
if (c == -1)
459475
break;
@@ -666,6 +682,8 @@ int main (int argc, char *argv[])
666682
case 'h': d_h_opt++; break; // Hamlib verbose level.
667683
#endif
668684
case 'c': d_c_opt++; break; // Connected mode data link state machine
685+
// Repeat for more detail.
686+
case 's': d_s_opt++; break; // Print statistics when link ends
669687
case 'x': d_x_opt++; break; // FX.25
670688
case '2': d_2_opt++; break; // IL2P
671689
case 'd': aprstt_debug++; break; // APRStt (mnemonic Dtmf)
@@ -691,6 +709,8 @@ int main (int argc, char *argv[])
691709
break;
692710

693711
case 't': /* Was handled earlier. */
712+
case 'o':
713+
case 'O':
694714
break;
695715

696716

@@ -791,7 +811,6 @@ int main (int argc, char *argv[])
791811
}
792812

793813
strlcpy (input_file, argv[optind], sizeof(input_file));
794-
795814
}
796815

797816
/*
@@ -1148,7 +1167,7 @@ int main (int argc, char *argv[])
11481167
igate_init (&audio_config, &igate_config, &digi_config, d_i_opt);
11491168
cdigipeater_init (&audio_config, &cdigi_config);
11501169
pfilter_init (&igate_config, d_f_opt);
1151-
ax25_link_init (&misc_config, d_c_opt);
1170+
ax25_link_init (&misc_config, d_c_opt, d_s_opt);
11521171

11531172
/*
11541173
* Provide the AGW & KISS socket interfaces for use by a client application.
@@ -1355,7 +1374,7 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
13551374
// TODO: suppress this message if not using soundcard input.
13561375
// i.e. we have no control over the situation when using SDR.
13571376

1358-
if (alevel.rec > 110) {
1377+
if (!q_h_opt && alevel.rec > 110) {
13591378

13601379
text_color_set(DW_COLOR_ERROR);
13611380
dw_printf ("Audio input level is too high. This may cause distortion and reduced decode performance.\n");
@@ -1451,6 +1470,14 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev
14511470
xid_parse (pinfo, info_len, &param, info2text, sizeof(info2text));
14521471
dw_printf (" %s\n", info2text);
14531472
}
1473+
else if (ftype == frame_type_S_SREJ) {
1474+
// Additional sequence numbers can be in the info part.
1475+
// This does not handle the range case, which we don't generate.
1476+
for (int j = 0; j < info_len; j++) {
1477+
dw_printf (" +%d", (unsigned int)(pinfo[j]) >> 1);
1478+
}
1479+
dw_printf ("\n");
1480+
}
14541481
else {
14551482
ax25_safe_print ((char *)pinfo, info_len, ( ! ax25_is_aprs(pp)) && ( ! d_u_opt) );
14561483
dw_printf ("\n");
@@ -1742,7 +1769,8 @@ static void usage (void)
17421769
dw_printf ("Usage: direwolf [options] [ - | stdin | UDP:nnnn ]\n");
17431770
dw_printf ("Options:\n");
17441771
dw_printf (" -c fname Configuration file name.\n");
1745-
dw_printf (" -l logdir Directory name for log files. Use . for current.\n");
1772+
dw_printf (" -l logdir Directory name for daily log files. Use . for current.\n");
1773+
dw_printf (" -L logname Generate single log file with fixed name.\n");
17461774
dw_printf (" -r n Audio sample rate, per sec.\n");
17471775
dw_printf (" -n n Number of audio channels, 1 or 2.\n");
17481776
dw_printf (" -b n Bits per audio sample, 8 or 16.\n");
@@ -1780,6 +1808,7 @@ static void usage (void)
17801808
dw_printf (" h h = hamlib increase verbose level.\n");
17811809
#endif
17821810
dw_printf (" c c = Connected mode data link state machine.\n");
1811+
dw_printf (" s s = Print statistics when link ends.\n");
17831812
dw_printf (" x x = FX.25 increase verbose level.\n");
17841813
dw_printf (" 2 2 = IL2P.\n");
17851814
dw_printf (" d d = APRStt (DTMF to APRS object translation).\n");

src/textcolor.c

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This file is part of Dire Wolf, an amateur radio packet TNC.
44
//
5-
// Copyright (C) 2011, 2012, 2013, 2014, 2019 John Langner, WB2OSZ
5+
// Copyright (C) 2011, 2012, 2013, 2014, 2019, 2025 John Langner, WB2OSZ
66
//
77
// This program is free software: you can redistribute it and/or modify
88
// it under the terms of the GNU General Public License as published by
@@ -186,17 +186,21 @@ static const char clear_eos[] = "\e[0J";
186186
* 9 (more accurately any invalid value) = try all of them and exit.
187187
*/
188188

189+
// TODO: Should we have an exit handler to restore initial condition?
190+
189191
static int g_enable_color = 1;
192+
static FILE *o_fp = NULL;
193+
static FILE *O_fp = NULL;
194+
static int current_color = 0;
190195

191196

192197
void text_color_init (int enable_color)
193198
{
194199

200+
g_enable_color = enable_color;
195201

196202
#if __WIN32__
197203

198-
g_enable_color = enable_color;
199-
200204
if (g_enable_color != 0) {
201205

202206
HANDLE h;
@@ -226,7 +230,7 @@ g_enable_color = enable_color;
226230

227231
// Run a test if outside of acceptable range.
228232

229-
if (enable_color < 0 || enable_color > MAX_T) {
233+
if (g_enable_color < 0 || g_enable_color > MAX_T) {
230234
int t;
231235
for (t = 0; t <= MAX_T; t++) {
232236
text_color_init (t);
@@ -245,8 +249,6 @@ g_enable_color = enable_color;
245249
exit (EXIT_SUCCESS);
246250
}
247251

248-
g_enable_color = enable_color;
249-
250252
if (g_enable_color != 0) {
251253
int t = g_enable_color;
252254

@@ -258,7 +260,36 @@ g_enable_color = enable_color;
258260
printf ("%s", t_black[t]);
259261
}
260262
#endif
261-
}
263+
264+
} //end text_color_init
265+
266+
267+
// Creat screen output capture files.
268+
// "-o" is for only raw received packets.
269+
// "-O" is for everything.
270+
271+
void dw_printf_capture_init (char *o_opt, char *O_opt)
272+
{
273+
if (strlen(o_opt) > 0) {
274+
o_fp = fopen (o_opt, "w");
275+
if (o_fp == NULL) {
276+
text_color_set (DW_COLOR_ERROR);
277+
dw_printf ("Failed to open -o %s for write.\n", o_opt);
278+
exit (EXIT_FAILURE);
279+
}
280+
}
281+
printf ("DEBUG O_opt=%s, strlen=%d\n", O_opt, (int)strlen(O_opt));
282+
if (strlen(O_opt) > 0) {
283+
O_fp = fopen (o_opt, "w+");
284+
if (O_fp == NULL) {
285+
text_color_set (DW_COLOR_ERROR);
286+
dw_printf ("Failed to open -O %s for write.\n", O_opt);
287+
dw_printf ("errno=%d\n", errno);
288+
exit (EXIT_FAILURE);
289+
}
290+
}
291+
292+
} // end dw_printf_capture_init
262293

263294

264295
#if __WIN32__
@@ -335,6 +366,7 @@ void text_color_set ( enum dw_color_e c )
335366

336367
#else
337368

369+
338370
void text_color_set ( enum dw_color_e c )
339371
{
340372

@@ -377,6 +409,8 @@ void text_color_set ( enum dw_color_e c )
377409
printf ("%s", t_dark_green[t]);
378410
break;
379411
}
412+
413+
current_color = c;
380414
}
381415

382416
#endif
@@ -416,7 +450,14 @@ int dw_printf (const char *fmt, ...)
416450
len = vsnprintf (buffer, BSIZE, fmt, args);
417451
va_end (args);
418452

419-
// TODO: other possible destinations...
453+
// other possible destinations...
454+
455+
if (o_fp != NULL && current_color == DW_COLOR_REC) {
456+
fputs (buffer, o_fp);
457+
}
458+
if (O_fp != NULL) {
459+
fputs (buffer, O_fp);
460+
}
420461

421462
fputs (buffer, stdout);
422463
return (len);

src/textcolor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ typedef enum dw_color_e dw_color_t;
2323

2424

2525
void text_color_init (int enable_color);
26+
void dw_printf_capture_init (char *o_opt, char *O_opt);
2627
void text_color_set (dw_color_t c);
2728
void text_color_term (void);
2829

0 commit comments

Comments
 (0)