Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package acarsdec for openSUSE:Factory 
checked in at 2026-11-04 19:46:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/acarsdec (Old)
 and      /work/SRC/openSUSE:Factory/.acarsdec.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "acarsdec"

Wed Nov  4 19:46:07 2026 rev:9 rq:1394841 version:4.7

Changes:
--------
--- /work/SRC/openSUSE:Factory/acarsdec/acarsdec.changes        2026-08-26 
19:57:50.271460210 +0200
+++ /work/SRC/openSUSE:Factory/.acarsdec.new.3759290/acarsdec.changes   
2026-11-04 19:49:52.004472368 +0100
@@ -1,0 +2,15 @@
+Tue Nov  3 18:28:26 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 4.7:
+  * IQ file input support (U8 and S16 sample formats)
+  * sdrplay support improvements: antenna naming aligned with
+    dumpvdl2, automatic bandwidth adjustment and multiplier
+    selection, cleanup and better error handling
+  * fix unsafe processing of input in label decoding
+  * enable resampling routines to fall back to faster
+    non-resampling ones when possible
+  * allow timestamping of audio and IQ input via -T
+  * speed up output processing by reusing formatted data
+  * add -V for version output
+
+-------------------------------------------------------------------

Old:
----
  acarsdec-4.6.tar.gz

New:
----
  acarsdec-4.7.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ acarsdec.spec ++++++
--- /var/tmp/diff_new_pack.jZhYWv/_old  2026-11-04 19:49:53.253524692 +0100
+++ /var/tmp/diff_new_pack.jZhYWv/_new  2026-11-04 19:49:53.258524901 +0100
@@ -18,7 +18,7 @@
 
 
 Name:           acarsdec
-Version:        4.6
+Version:        4.7
 Release:        0
 Summary:        ACARS SDR decoder
 # Legal-Review-Notice: every source file carries a boilerplate naming the "GNU

++++++ acarsdec-4.6.tar.gz -> acarsdec-4.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/CMakeLists.txt 
new/acarsdec-4.7/CMakeLists.txt
--- old/acarsdec-4.6/CMakeLists.txt     2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/CMakeLists.txt     2026-11-03 12:42:53.000000000 +0100
@@ -7,7 +7,7 @@
 
 include(CMakeDependentOption)
 
-add_executable(acarsdec acars.c  acarsdec.c  label.c  msk.c  output.c netout.c 
fileout.c lib.c statsd.c)
+add_executable(acarsdec acars.c  acarsdec.c  label.c  msk.c iqfile.c output.c 
netout.c fileout.c lib.c statsd.c)
 execute_process(COMMAND git describe --tags --always --dirty OUTPUT_VARIABLE 
VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
 if(VERSION)
 add_compile_definitions(VERSION="${VERSION}")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/README.md new/acarsdec-4.7/README.md
--- old/acarsdec-4.6/README.md  2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/README.md  2026-11-03 12:42:53.000000000 +0100
@@ -1,15 +1,15 @@
 # ACARSDEC
 
-acarsdec is a multi-channels ACARS decoder with built-in rtl_sdr, soapysdr, 
airspy or sdrplay device support.
+acarsdec is a multi-channels ACARS decoder with built-in SDR, audio and IQ 
input support.
 Since 3.0, it can work with a database backend: 
[acarsserv](https://github.com/TLeconte/acarsserv) to store received ACARS 
messages.
 
 ## Features
 
  * arbitrary number of channels decoded simultaneously (limited by bandwidth 
and CPU power)
  * error detection AND correction
- * input via either audio file (including raw audio), ALSA soundcard, 
[rtl_sdr](https://sdr.osmocom.org/trac/wiki/rtl-sdr),
+ * input via either multichannel audio file/stream (including raw audio), ALSA 
soundcard, SDR devices ([rtl_sdr](https://sdr.osmocom.org/trac/wiki/rtl-sdr),
    [SoapySDR](https://github.com/pothosware/SoapySDR/wiki), 
[airspy](https://airspy.com/)
-   or [sdrplay](https://www.sdrplay.com) software defined radios (SDR)
+   [sdrplay](https://www.sdrplay.com)) or raw IQ file/stream
  * multiple simultaneous outputs via file, UDP or MQTT
  * multiple output formats: one line, full text, planeplotter, acarsserv, JSON 
and live monitoring
  * decoding of ARINC-622 ATS applications (ADS-C, CPDLC) via 
[libacars](https://github.com/szpajder/libacars) library
@@ -72,7 +72,8 @@
 - `-DMQTT=OFF`
 
 The `-DCMAKE_C_FLAGS="-march=native"` argument to cmake will produce an 
`acarsdec` executable
-that is optimized for the machine it was built on (and may not run correctly 
on other devices).
+that is optimized for the machine it was built on (and may not run correctly 
on other devices),
+improving its performance (i.e. reduced CPU load).
 It can be ommitted for a platform-generic build (with possibly reduced 
performance).
  
 #### Raspberry Pi builds
@@ -102,8 +103,11 @@
  -A            don't output uplink messages (ie : only aircraft messages)
  -b <filter>   filter output by label (ex: -b "H1:Q0" : only output messages  
with label H1 or Q0)
  -e            don't output empty messages (ie : _d,Q0, etc ...)
+ -h            show the full list of available options and exit
  -i <stationid>        station id used in statsd reports and network 
destinations (default: hostname)
  -t <seconds>  set forget time (TTL) in seconds for monitor mode (default: 600)
+ -T            enable timestamping for non-live inputs (enabled by default for 
SDR/ALSA, disabled for others)
+ -V            show version number and exit
 
  --statsd host=HOST,port=1234          enable statsd reporting to host "HOST" 
(name or IP) on port "1234"
  --output FORMAT:DESTINATION:DESTPARAMS (see below for supported output 
formats and destinations. DESTPARAMS are coma-separated: ',')
@@ -118,6 +122,9 @@
 Multiple instances of `--output` can be used. At least one is required.
 See `--output help` for a list of supported formats and destinations.
 Not all combinations of formats and destinations are valid, acarsdec will 
complain if an invalid combination is chosen.
+When using several outputs with the same formats, grouping them by same output 
format on the commandline will reduce acarsdec CPU usage.
+(e.g. prefer `--output json:DEST1:PARAMS1 --output json:DEST2:PARAM2 --output 
full:file --output full:DEST3:PARAM3` instead of
+`--output json:DEST1:PARAMS1 --output full:file --output json:DEST2:PARAM2 
--output full:DEST3:PARAM3`)
 
 One (and **only one**) input source must also be selected, see below.
 Options (including frequencies) can be provided in any order.
@@ -196,32 +203,47 @@
  -g <linearity_gain>   set linearity gain [0-21] (default: 18)
 ```
 
-#### SDRplay (API version 3.15 and above - untested/unmaintained, help wanted)
+#### SDRplay (API version 3.15 and above - barely tested, help wanted)
 
 ```
  --sdrplay <device>    decode from sdrplay with sequence number <device> or 
hex serial <device>
- -a <antenna>          set antenna port to use (default: first antenna)
+ -a <antenna>          set antenna port to use, A B or C (default: first 
antenna)
  -B <bias>             enable (1) or disable (0) the bias tee (default is 0)
  -c <freq>             set center frequency to tune to in MHz, e.g. 131.800 
(default: automatic)
  -G <gRdB>             gain reduction in dB's, range 20 .. 59 (default: -100 
is autogain)
- -L <lnaState>         set the lnaState (depends on the device)
- -m <rateMult>         set sample rate multiplier: sample rate is <rateMult> * 
12000 S/s (default: 252)
+ -L <lnaState>         set the lnaState, works together with gRdB (depends on 
the device)
+ -m <rateMult>         set sample rate multiplier: sample rate is <rateMult> * 
12000 S/s (default: automatic)
  -p <ppm>              set ppm frequency correction (default: 0)
 ```
 
-All SDR sources described above expect a list of frequencies `<f1> [<f2> 
[...]]` to decode from, expressed in decimal MHz
-e.g. `131.525 131.725 131.825`.
+All SDR sources described above as well as IQ file input below expect a list 
of frequencies `<f1> [<f2> [...]]`
+to decode from, expressed in decimal MHz e.g. `131.525 131.725 131.825`.
 
 **NOTE: all the SDR sources set the input gain to "automatic", which generally 
means "maximum" and is a bad idea for
 good ACARS message reception. It is best to tune the gain to maximize 
Signal-to-Noise Ratio and reduce the number of
 saturated messages (where the received level is over 0dB).**
 
+#### IQ file
+
+```
+ --iqfile file=<file>,sr=<N>,format=<F>        
+                       decode raw IQ input from <file>, with samplerate <N> 
S/s and format <F>: 'U8' (default) or 'S16'
+                       Best performance is achieved if <N> is an integer 
multiple of 12000
+ -c <freq>             set the center frequency of the IQ data in MHz, e.g. 
131.800 (required)
+ -T                    enable timestamping
+```
+
+See `--iqfile help` for more details.
+
+NB: `<file>` can be `/dev/stdin` to process piped-in data.
+
 #### audio file
 
 All formats supported by libsndfile can be processed.
 
 ```
  --sndfile <file>      decode from <file>, which must be a libsndfile 
supported format and sampled at a multiple of 12kHz
+ -T                    enable timestamping
 ```
 
 To decode raw audio, extra parameters must be provided. Example:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/acars.c new/acarsdec-4.7/acars.c
--- old/acarsdec-4.6/acars.c    2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/acars.c    2026-11-03 12:42:53.000000000 +0100
@@ -345,7 +345,7 @@
        case SOH1:
                if (likely(r == SOH)) {
                        if (ch->blk == NULL) {
-                               ch->blk = malloc(sizeof(*ch->blk));
+                               ch->blk = calloc(1, sizeof(*ch->blk));
                                if (unlikely(ch->blk == NULL)) {
                                        perror(NULL);
                                        break;  // fail
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/acarsdec.c new/acarsdec-4.7/acarsdec.c
--- old/acarsdec-4.6/acarsdec.c 2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/acarsdec.c 2026-11-03 12:42:53.000000000 +0100
@@ -1,6 +1,6 @@
 /*
  *  Copyright (c) 2007-2022,2025 Thierry Leconte
- *  Copyright (c) 2024 Thibaut VARENE
+ *  Copyright (c) 2024-2026 Thibaut VARENE
  *
  *   
  *   This code is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <limits.h>
 #include <err.h>
+#include <math.h>
 #ifdef HAVE_LIBACARS
 #include <libacars/version.h>
 #endif
@@ -56,6 +57,7 @@
 #ifdef WITH_SNDFILE
  #include "soundfile.h"
 #endif
+#include "iqfile.h"
 
 runtime_t R = {
        .mdly = 600,
@@ -72,6 +74,7 @@
 #ifdef WITH_SNDFILE
                "[--sndfile <file.wav>]",
 #endif
+               "[--iqfile <params>]",
        };
 
        const char *const sdropts[] = {
@@ -114,28 +117,40 @@
                                fprintf(stderr, " | ");
                }
                fprintf(stderr, " <f1> [<f2> [...]] ]\n\n");
-               fprintf(stderr, " <f1> [<f2> [...]] are given in decimal MHz, 
e.g. 131.525");
+               fprintf(stderr, " <f1> [<f2> [...]] are given in decimal MHz, 
e.g. 131.525\n");
        }
 }
 
-static void usage(void)
+static void versstr(void)
 {
        fprintf(stderr, "Acarsdec %s Copyright (c) 2007-2022,2025 Thierry 
Leconte, (c) 2024-2026 Thibaut VARENE\n", ACARSDEC_VERSION);
 #ifdef HAVE_LIBACARS
        fprintf(stderr, "(libacars %s)\n", LA_VERSION);
 #endif
-       fprintf(stderr, "\nUsage: acarsdec [-A] [-b 'labels,..'] [-e] [-i 
station_id] [-t secs] [--statsd host=ip,port=1234] --output 
FORMAT:DESTINATION:PARAMS [--output ...]");
+}
+
+static void usage(void)
+{
+       fprintf(stderr, "\nUsage: acarsdec [-AehTV] [-b 'labels,..'] [-i 
station_id] [-t secs] [--statsd host=ip,port=1234] --output 
FORMAT:DESTINATION:PARAMS [--output ...]");
 #ifdef HAVE_LIBACARS
        fprintf(stderr, " [--skip-reassembly]");
 #endif
        print_available_ins();
+}
+
+static void long_usage(void) {
+       versstr();
+       usage();
        fprintf(stderr,
-               "\n\n"
+               "\n"
                " -A\t\t\t: don't output uplink messages (ie : only aircraft 
messages)\n"
                " -b <filter>\t\t: filter output by label (ex: -b \"H1:Q0\" : 
only output messages  with label H1 or Q0)\n"
                " -e\t\t\t: don't output empty messages (ie : _d,Q0, etc ...)\n"
+               " -h \t\t\t: show the full list of available options and exit\n"
                " -i <stationid>\t\t: station id used in acarsdec network 
format (default: hostname)\n"
                " -t <seconds>\t\t: set forget time (TTL) to <seconds> for 
flight routes (affects monitor and routejson, default: 600)\n"
+               " -T \t\t\t: enable timestamping for non-live inputs (enabled 
by default for SDR/ALSA, disabled for others)\n"
+               " -V \t\t\t: show version number and exit\n"
 #ifdef HAVE_LIBACARS
                " --skip-reassembly\t: disable reassembling fragmented ACARS 
messages\n"
 #endif
@@ -148,7 +163,7 @@
 #endif
 #ifdef WITH_SNDFILE
        fprintf(stderr, "\n --sndfile <file>\t: decode from <file> sampled at a 
multiple of %u Hz\n", INTRATE);
-       fprintf(stderr, " see \"--sndfile help\" for details\n");
+       fprintf(stderr, "\t\t\t  see \"--sndfile help\" for details\n");
 #endif
 #ifdef WITH_RTL
        fprintf(stderr,
@@ -172,12 +187,12 @@
        fprintf(stderr,
                "\n sdrplayopts:\n"
                " --sdrplay <device>\t: decode from sdrplay with sequence 
number <device> or hex serial <device>\n"
-               " -a <antenna>\t\t: set antenna port to use (default: first 
antenna)\n"
+               " -a <antenna>\t\t: set antenna port to use, A B or C (default: 
first antenna)\n"
                " -B <bias>\t\t: enable (1) or disable (0) the bias tee 
(default is 0)\n"
                " -c <freq>\t\t: set center frequency to tune to in MHz, e.g. 
131.800 (default: automatic)\n"
                " -G <gRdB>\t\t: gain reduction in dB's, range 20 .. 59 
(default: -100 is autogain)\n"
-               " -L <lnaState>\t\t: set the lnaState (depends on the device)\n"
-               " -m <rateMult>\t\t: set sample rate multiplier: sample rate is 
<rateMult> * 12000 S/s (default: 252)\n"
+               " -L <lnaState>\t\t: set the lnaState, works together with gRdB 
(depends on the device)\n"
+               " -m <rateMult>\t\t: set sample rate multiplier: sample rate is 
<rateMult> * 12000 S/s (default: automatic)\n"
                " -p <ppm>\t\t: set ppm frequency correction (default: 0)\n");
 #endif
 #ifdef WITH_SOAPY
@@ -190,6 +205,7 @@
                " -m <rateMult>\t\t: set sample rate multiplier: sample rate is 
<rateMult> * 12000 S/s (default: automatic)\n"
                " -p <ppm>\t\t: set ppm frequency correction (default: 0)\n");
 #endif
+       fprintf(stderr, "\n --iqfile <params>\t: decode from raw IQ input, see 
\"--iqfile help\" for details\n");
        exit(1);
 }
 
@@ -286,6 +302,7 @@
 #ifdef WITH_SOAPY
                { "soapysdr", required_argument, NULL, IN_SOAPY },
 #endif
+               { "iqfile", required_argument, NULL, IN_IQFILE },
                { "verbose", no_argument, NULL, 'v' },
                { "output", required_argument, NULL, -2 },      // -1 is EOF
                { "statsd", required_argument, NULL, -3 },
@@ -295,7 +312,6 @@
                { NULL, 0, NULL, 0 }
        };
        char sys_hostname[HOST_NAME_MAX + 1];
-       char *lblf = NULL;
        char *inarg = NULL, *statsdarg = NULL;
 
        gethostname(sys_hostname, HOST_NAME_MAX);
@@ -303,7 +319,7 @@
        R.idstation = strdup(sys_hostname);
 
        res = 0;
-       while ((c = getopt_long(argc, argv, "hvt:g:m:a:Aep:c:i:L:G:b:B:", 
long_opts, NULL)) != EOF) {
+       while ((c = getopt_long(argc, argv, "hvt:g:m:a:Aep:c:i:L:G:b:B:TV", 
long_opts, NULL)) != EOF) {
                switch (c) {
                case -2:
                        res = setup_output(optarg);
@@ -325,7 +341,13 @@
                        R.mdly = atoi(optarg);
                        break;
                case 'b':
-                       lblf = optarg;
+                       build_label_filter(optarg);
+                       break;
+               case IN_IQFILE:
+                       if (R.inmode)
+                               errx(-1, "Only 1 input allowed");
+                       R.inmode = IN_IQFILE;
+                       inarg = optarg;
                        break;
 #ifdef WITH_ALSA
                case IN_ALSA:
@@ -355,6 +377,9 @@
                case 'B':
                        R.bias = atoi(optarg);
                        break;
+               case 'T':
+                       R.ts = true;
+                       break;
 #ifdef WITH_RTL
                case IN_RTL:
                        if (R.inmode)
@@ -399,7 +424,7 @@
                        break;
 #endif
                case 'c':
-                       R.Fc = (unsigned int)(1000000 * atof(optarg));
+                       R.Fc = (unsigned int)lround(1000000 * atof(optarg));
                        break;
                case 'A':
                        R.airflt = 1;
@@ -411,14 +436,22 @@
                        free(R.idstation);
                        R.idstation = strdup(optarg);
                        break;
+               case 'V':
+                       versstr();
+                       exit(0);
+                       break;
+               case '?':
+                       usage();
+                       exit(1);
+                       break;
                case 'h':
                default:
-                       usage();
+                       long_usage();
                }
        }
 
        if (R.inmode == IN_NONE) {
-               fprintf(stderr, "Missing input\n");
+               fprintf(stderr, "Missing input\n\n");
                usage();
        }
 
@@ -460,6 +493,9 @@
                res = initSoapy(inarg);
                break;
 #endif
+       case IN_IQFILE:
+               res = initIqfile(inarg);
+               break;
        default:
                res = -1;
        }
@@ -467,8 +503,6 @@
        if (res)
                errx(res, "Unable to init input");
 
-       build_label_filter(lblf);
-
        res = initOutputs();
        if (res)
                errx(res, "Unable to init outputs");
@@ -548,6 +582,9 @@
                res = runSoapySample();
                break;
 #endif
+       case IN_IQFILE:
+               res = runIqfileSample();
+               break;
        default:
                res = -1;
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/acarsdec.h new/acarsdec-4.7/acarsdec.h
--- old/acarsdec-4.6/acarsdec.h 2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/acarsdec.h 2026-11-03 12:42:53.000000000 +0100
@@ -31,7 +31,7 @@
 #endif
 
 #ifndef VERSION
-#define ACARSDEC_VERSION "4.6"
+#define ACARSDEC_VERSION "4.7"
 #else
 #define ACARSDEC_VERSION VERSION
 #endif
@@ -107,6 +107,7 @@
        float complex *restrict inb;            // oversampled bit circular 
buffer
        float complex mix_phase;                // arbitrary-rate SDR NCO state
        float complex mix_step;                 // arbitrary-rate SDR NCO 
increment
+       float mix_scale;                        // arbitrary-rate SDR NCO scale 
factor (for renormalization)
        float MskPhi;                           // MSK oscillator phase
        float MskDphi;                          // MSK oscillator phase offset
        float MskDf;                            // MSK oscillator phase offset 
integral
@@ -116,7 +117,6 @@
        float MskClk;                           // MSK bit clock
        unsigned int MskS;                      // MSK sample index
        unsigned int idx;                       // `inb` buffer index
-       unsigned int mix_count;                 // arbitrary-rate SDR NCO 
renormalization counter
 
        unsigned int Fr;                        // channel frequency (in Hz)
 
@@ -140,6 +140,7 @@
        unsigned int nbch;
        volatile bool running;
        bool verbose;
+       bool ts;                // timestamp output
 
        // used only in output
        bool airflt;
@@ -152,7 +153,7 @@
        output_t *outputs;
        char *idstation;
 
-       enum { IN_NONE = 0, IN_ALSA, IN_SNDFILE, IN_RTL, IN_AIR, IN_SDRPLAY, 
IN_SOAPY } inmode;
+       enum { IN_NONE = 0, IN_ALSA, IN_SNDFILE, IN_RTL, IN_AIR, IN_SDRPLAY, 
IN_SOAPY, IN_IQFILE } inmode;
 
        // used only during setup
        float gain;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/air.c new/acarsdec-4.7/air.c
--- old/acarsdec-4.6/air.c      2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/air.c      2026-11-03 12:42:53.000000000 +0100
@@ -88,6 +88,8 @@
        uint64_t *airspy_device_list = NULL;
        uint32_t required_rate;
 
+       R.ts = true;
+
        if (!R.gain)
                R.gain = 18;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/alsa.c new/acarsdec-4.7/alsa.c
--- old/acarsdec-4.6/alsa.c     2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/alsa.c     2026-11-03 12:42:53.000000000 +0100
@@ -111,6 +111,8 @@
                return 1;
        }
 
+       R.ts = true;
+       
        return (0);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/iqfile.c new/acarsdec-4.7/iqfile.c
--- old/acarsdec-4.6/iqfile.c   1970-01-01 01:00:00.000000000 +0100
+++ new/acarsdec-4.7/iqfile.c   2026-11-03 12:42:53.000000000 +0100
@@ -0,0 +1,203 @@
+/*
+ *  Copyright (c) 2026 Thibaut VARENE
+ *
+ *
+ *   This code is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License version 2
+ *   published by the Free Software Foundation.
+ *
+ *   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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "acarsdec.h"
+#include "lib.h"
+
+#define ERRPFX "ERROR: IQFILE: "
+#define MAXNSAMPLES 4096
+
+
+static FILE *iqfp;
+static enum iqfile_format { IQ_U8, IQ_S16 } iqfmt;
+static unsigned int iqrate;
+
+static void usage(void)
+{
+       fprintf(stderr,
+               "iqfile input accepts as parameters the following 
coma-separated elements (in any order):\n"
+               " - the 'file=' followed by a filename (mandatory),\n"
+               " - the 'sr=' argument specifying the source samplerate which 
must be at least %d Sps (mandatory),\n"
+               " - and the optional 'format=' argument, which is one of 'U8' 
(default), 'S16'.\n"
+               "The center and target frequencies must also be provided on the 
command line.\n"
+               "Best performance is obtained when using a sample rate which is 
a multiple of %d Sps.\n"
+               "examples:\n"
+               "   'file=data.raw,sr=1200000' to process unsigned 8-bit raw IQ 
data sampled at 1.2MSps\n"
+               "   'format=S16,sr=2000000,file=data.raw' to process signed 
16-bit raw IQ data sample at 2MSps\n"
+               "\n",
+               4*INTRATE, INTRATE);
+
+       exit(1);
+}
+
+int initIqfile(char *optarg)
+{
+       char *format = NULL, *fname = NULL, *sr = NULL;
+       struct params_s iqp[] = {
+               { .name = "sr", .valp = &sr, },
+               { .name = "format", .valp = &format, },
+               { .name = "file", .valp = &fname, },
+       };
+       char *retp;
+       float scale = 127.5F;   // U8 default
+
+       if (!strcmp("help", optarg))
+               usage();
+
+       retp = parse_params(&optarg, iqp, ARRAY_SIZE(iqp));
+       if (retp) {
+               fprintf(stderr, ERRPFX "invalid parameter '%s'\n", retp);
+               return -1;
+       }
+
+       if (!fname) {
+               fprintf(stderr, ERRPFX "missing filename\n");
+               return -1;
+       }
+
+       if (!sr) {
+               fprintf(stderr, ERRPFX "missing samplerate\n");
+               return -1;
+       }
+       else {
+               iqrate = strtoul(sr, &sr, 0);
+               if ('\0' != *sr) {
+                       fprintf(stderr, ERRPFX "invalid sr value '%s'\n", sr);
+                       return -1;
+               }
+               if (iqrate < 4*INTRATE) {
+                       fprintf(stderr, ERRPFX "invalid sr value '%d'\n", 
iqrate);
+                       return -1;
+               }
+       }
+
+       if (format) {
+               if (!strcmp("U8", format)) {
+                       iqfmt = IQ_U8;
+                       scale = 127.5F;
+               }
+               else if (!strcmp("S16", format)) {
+                       iqfmt = IQ_S16;
+                       scale = 32768.0F;
+               }
+               else {
+                       fprintf(stderr, ERRPFX "invalid format value '%s'\n", 
format);
+                       return -1;
+               }
+       }
+
+       if (!R.Fc) {
+               fprintf(stderr, ERRPFX "missing center frequency\n");
+               return -1;
+       }
+
+       if (!R.minFc) {
+               fprintf(stderr, ERRPFX "missing input frequencies\n");
+               return -1;
+       }
+
+       iqfp = fopen(fname, "r");
+       if (!iqfp) {
+               fprintf(stderr, ERRPFX "could not open %s: %s\n", fname, 
strerror(errno));
+               return 1;
+       }
+
+       return channels_init_sdr_resample(R.Fc, iqrate, scale);
+}
+
+
+static void process_samples(const void *buf, size_t nread)
+{
+       const unsigned int mult = iqrate / INTRATE;
+       float complex phasors[mult];
+       const uint8_t *pu8 = buf;
+       const int16_t *ps16 = buf;
+
+       if (unlikely(nread % 2)) {
+               fprintf(stderr, ERRPFX "incomplete read\n");
+               return;
+       }
+
+       while (nread) {
+               unsigned int ind, lim = unlikely(nread / 2 < mult) ? nread / 2 
: mult;
+               float i, q;
+
+               switch (iqfmt) {
+                       case IQ_U8:
+                               for (ind = 0; ind < lim; ind++) {
+                                       i = (float)(*pu8++) - 127.5F;
+                                       q = (float)(*pu8++) - 127.5F;
+                                       phasors[ind] = i + q * I;
+                               }
+                               break;
+                       case IQ_S16:
+                               for (ind = 0; ind < lim; ind++) {
+                                       i = (float)(*ps16++);
+                                       q = (float)(*ps16++);
+                                       phasors[ind] = i + q * I;
+                               }
+                               break;
+                       default:
+                               return;
+               }
+
+               channels_mix_phasors_resample(phasors, lim, iqrate);
+               nread -= lim * 2;
+       }
+}
+
+int runIqfileSample(void)
+{
+       size_t nread, ssize, bufsize = MAXNSAMPLES * 2;
+       void *buf;
+
+       switch (iqfmt) {
+               case IQ_U8:
+                       ssize = sizeof(uint8_t);
+                       break;
+               case IQ_S16:
+                       ssize = sizeof(int16_t);
+                       break;
+               default:
+                       return -1;
+       }
+
+       buf = malloc(bufsize * ssize);
+       if (!buf) {
+               perror(ERRPFX "runIqfileSample()");
+               return -1;
+       }
+
+       do {
+               nread = fread(buf, ssize, bufsize, iqfp);
+               if (!nread)
+                       goto out;
+
+               process_samples(buf, nread);
+       } while (R.running);
+
+out:
+       free(buf);
+       fclose(iqfp);
+       return 0;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/iqfile.h new/acarsdec-4.7/iqfile.h
--- old/acarsdec-4.6/iqfile.h   1970-01-01 01:00:00.000000000 +0100
+++ new/acarsdec-4.7/iqfile.h   2026-11-03 12:42:53.000000000 +0100
@@ -0,0 +1,7 @@
+#ifndef iqfile_h
+#define iqfile_h
+
+int initIqfile(char *optarg);
+int runIqfileSample(void);
+
+#endif /* iqfile_h */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/label.c new/acarsdec-4.7/label.c
--- old/acarsdec-4.6/label.c    2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/label.c    2026-11-03 12:42:53.000000000 +0100
@@ -4,24 +4,13 @@
 #include <string.h>
 #include "label.h"
 
-static char *lblfilter[1024];
+#define MAX_FILTERS    1024
+static char * lblfilter[MAX_FILTERS];
 
 void build_label_filter(char *arg)
 {
-       int i = 0;
-       char *aptr;
-
-       lblfilter[0] = NULL;
-       if (arg == NULL)
-               return;
-
-       aptr = strtok(strdup(arg), ":");
-       while (aptr) {
-               lblfilter[i] = aptr;
-               i++;
-               aptr = strtok(NULL, ":");
-       }
-       lblfilter[i] = NULL;
+       char **ap;
+       for (ap = lblfilter; ap < &lblfilter[MAX_FILTERS-2] && (*ap = 
strsep(&arg, ":")); ap++);
 }
 
 int label_filter(char *lbl)
@@ -31,12 +20,9 @@
        if (lblfilter[0] == NULL)
                return 1;
 
-       i = 0;
-       while (lblfilter[i]) {
+       for (i=0; lblfilter[i]; i++)
                if (strcmp(lbl, lblfilter[i]) == 0)
                        return 1;
-               i++;
-       }
 
        return 0;
 }
@@ -387,6 +373,9 @@
 
 int DecodeLabel(acarsmsg_t *msg, oooi_t *oooi)
 {
+       if (!msg->txt)
+               return 0;
+
        switch (msg->label[0]) {
        case '1':
                switch (msg->label[1]) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/lib.c new/acarsdec-4.7/lib.c
--- old/acarsdec-4.6/lib.c      2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/lib.c      2026-11-03 12:42:53.000000000 +0100
@@ -65,14 +65,19 @@
 
 unsigned int find_centerfreq_rate(unsigned int minFc, unsigned int maxFc, 
unsigned int input_rate)
 {
-       if (R.Fc)
-               return R.Fc;
+       if (!minFc) {
+               fprintf(stderr, "ERROR: need a least one input frequency\n");
+               return 0;
+       }
 
        if ((maxFc - minFc) > input_rate - 4 * INTRATE) {
-               fprintf(stderr, "Frequencies too far apart\n");
+               fprintf(stderr, "ERROR: input frequencies too far apart\n");
                return 0;
        }
 
+       if (R.Fc)
+               return R.Fc;
+
        return (maxFc + minFc) / 2;
 }
 
@@ -120,20 +125,23 @@
  * @param Fc the chosen center frequency
  * @param input_rate the chosen sampling rate
  * @param scale the phasor scale (e.g. 1.0F for complex float32 phasors; 
32768.0F for int16 phasors, ...)
+ * @note falls back to channels_init_sdr() if input_rate is an integer 
multiple of INTRATE
  */
 int channels_init_sdr_resample(unsigned int Fc, unsigned int input_rate, float 
scale)
 {
        unsigned int n;
-       float scale_factor;
+       float correctionPhase, scale_factor;
 
        if (!input_rate)
                return 1;
 
+       if (!(input_rate % INTRATE))
+               return channels_init_sdr(Fc, input_rate / INTRATE, scale);
+
        scale_factor = ((float)INTRATE / (float)input_rate) / scale;
 
        for (n = 0; n < R.nbch; n++) {
                channel_t *ch = &R.channels[n];
-               float correctionPhase;
 
                ch->dm_buffer = malloc(DMBUFSZ * sizeof(*ch->dm_buffer));
                if (ch->dm_buffer == NULL) {
@@ -145,9 +153,8 @@
                vprerr("#%d: Fc = %uHz, Fr = %uHz, phase = % f (%+dHz)\n",
                       n + 1, Fc, ch->Fr, correctionPhase, (signed)(ch->Fr - 
Fc));
 
-               ch->mix_phase = scale_factor;
+               ch->mix_scale = ch->mix_phase = scale_factor;
                ch->mix_step = cexpf(correctionPhase * -I);
-               ch->mix_count = 0;
        }
 
        return 0;
@@ -271,17 +278,22 @@
  * @note this implementation is an order of magnitude more CPU-intensive than
  * the regular `channels_mix_phasors()` implementation and should only be used
  * for inputs that do not support sample rates which are multiples of INTRATE.
+ * @note this function, like its init() counterpart, falls back to 
channels_mix_phasors()
+ * if the provided samplerate is an integer multiple of INTRATE.
  */
 void channels_mix_phasors_resample(const float complex *restrict phasors, 
unsigned int len, unsigned int input_rate)
 {
        static float complex *restrict D = NULL;
-       static unsigned int phase_acc = 0;
+       static unsigned int mix_count = 0, phase_acc = 0;
        const unsigned int nbch = R.nbch;
        unsigned int i, n;
 
-       if (unlikely(!len || !input_rate))
+       if (unlikely(!len))
                return;
 
+       if (!(input_rate % INTRATE))
+               return channels_mix_phasors(phasors, len, input_rate / INTRATE);
+
        if (unlikely(!D)) {
                // Allocate one accumlator per channel
                D = calloc(nbch, sizeof(*D));
@@ -295,24 +307,11 @@
 
                for (n = 0; n < nbch; n++) {
                        channel_t *ch = &R.channels[n];
-                       float complex mixed;
                        float phase_mag;
 
                        // Shift this channel from its RF offset down to 
baseband
                        // and add the result into the current output 
accumulator.
-                       mixed = sample * ch->mix_phase;
-
-#if defined(DEBUG)
-                       // The below can be used to debug if the 
renormalization isn't working
-                       // correctly or if the data is bad (the sample is NaN 
or inf).
-                       if (!isfinite(crealf(mixed)) || 
!isfinite(cimagf(mixed))) {
-                               vprerr("WARNING: MIX: resetting non-finite 
mixed sample on channel #%u\n", n + 1);
-                               ch->mix_phase = cabsf(ch->mix_phase) > 0.0F ? 
ch->mix_phase / cabsf(ch->mix_phase) * ((float)INTRATE / (float)input_rate) : 
((float)INTRATE / (float)input_rate);
-                               ch->mix_count = 0;
-                               mixed = 0.0F;
-                       }
-#endif
-                       D[n] += mixed;
+                       D[n] += sample * ch->mix_phase;
 
                        // Advance the oscillator so the next sample uses the 
correct
                        // phase for this channels frequency offset.
@@ -320,15 +319,14 @@
 
                        // Every MIX_RENORM_INTERVAL, renormalize the phase 
magnitude, preventing
                        // drift towards zero/infinity. Floating point roundoff 
can slowly change
-                       // the magnitude of mix_phase. This keeps the 
oscillator stable. Prior to
-                       // implementing this check, after several minutes of 
runtime, the mix_phase
-                       // could/would trend towards zero/inf.
-                       if ((++ch->mix_count & (MIX_RENORM_INTERVAL - 1U)) == 
0) {
+                       // the magnitude of mix_phase. This keeps the 
oscillator stable.
+                       if ((mix_count & (MIX_RENORM_INTERVAL - 1U)) == 0) {
                                phase_mag = cabsf(ch->mix_phase);
                                ch->mix_phase /= phase_mag;
-                               ch->mix_phase *= ((float)INTRATE / 
(float)input_rate);
+                               ch->mix_phase *= ch->mix_scale;
                        }
                }
+               mix_count++;
 
                // Convert the arbitrary rate into a steady 12kHz output,
                // and when enough has accumulated, emit a decoder-rate
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/output.c new/acarsdec-4.7/output.c
--- old/acarsdec-4.6/output.c   2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/output.c   2026-11-03 12:42:53.000000000 +0100
@@ -397,7 +397,7 @@
        else
                len += snprintf(buf + len, bufsz - len, "[#%1d (L:%+5.1f/%.1f 
E:%1d) ", chn + 1, msg->lvl, msg->nf, msg->err);
 
-       if (R.inmode != IN_SNDFILE)
+       if (R.ts)
                len += fmt_date(tv, buf + len, bufsz - len);
 
        len += snprintf(buf + len, bufsz - len, " 
--------------------------------\n");
@@ -473,7 +473,7 @@
 
        len = snprintf(buf, bufsz, "#%1d (L:%+5.1f/%.1f E:%1d) ", chn + 1, 
msg->lvl, msg->nf, msg->err);
 
-       if (R.inmode != IN_SNDFILE)
+       if (R.ts)
                len += fmt_date(tv, buf + len, bufsz - len);
 
        len += snprintf(buf + len, bufsz - len, " %7s %6s %1c %2s %4s %s", 
msg->addr, msg->fid, msg->mode, msg->label, msg->no, txt);
@@ -565,8 +565,10 @@
        if (json_obj == NULL)
                return ok;
 
-       double t = (double)tv.tv_sec + ((double)tv.tv_usec) / 1e6;
-       cJSON_AddNumberToObject(json_obj, "timestamp", t);
+       if (R.ts) {
+               double t = (double)tv.tv_sec + ((double)tv.tv_usec) / 1e6;
+               cJSON_AddNumberToObject(json_obj, "timestamp", t);
+       }
        if (R.idstation[0])
                cJSON_AddStringToObject(json_obj, "station_id", R.idstation);
        cJSON_AddNumberToObject(json_obj, "channel", chn);
@@ -660,8 +662,10 @@
                if (json_obj == NULL)
                        return 0;
 
-               double t = (double)tv.tv_sec + ((double)tv.tv_usec) / 1e6;
-               cJSON_AddNumberToObject(json_obj, "timestamp", t);
+               if (R.ts) {
+                       double t = (double)tv.tv_sec + ((double)tv.tv_usec) / 
1e6;
+                       cJSON_AddNumberToObject(json_obj, "timestamp", t);
+               }
                if (R.idstation[0])
                        cJSON_AddStringToObject(json_obj, "station_id", 
R.idstation);
                cJSON_AddStringToObject(json_obj, "flight", fl->fid);
@@ -871,42 +875,44 @@
        if (R.emptymsg && (msg.txt == NULL || msg.txt[0] == '\0'))
                return;
 
+       int len = 0, prevfmt = 0;
        // for now and until we see contention, we don't bother using a 
separate thread per output. KISS.
        for (out = R.outputs; out; out = out->next) {
-               int len = 0;
-               switch (out->fmt) {
-               case FMT_MONITOR:
-                       len = fmt_monitor(&msg, blk->chn, blk->tv, fmtbuf, 
FMTBUFLEN);
-                       break;
-               case FMT_ONELINE:
-                       len = fmt_oneline(&msg, blk->chn, blk->tv, fmtbuf, 
FMTBUFLEN);
-                       break;
-               case FMT_FULL:
-                       len = fmt_msg(&msg, blk->chn, blk->tv, fmtbuf, 
FMTBUFLEN);
-                       break;
-               case FMT_NATIVE:
-                       len = fmt_sv(&msg, blk->chn, blk->tv, fmtbuf, 
FMTBUFLEN);
-                       break;
-               case FMT_PP:
-                       len = fmt_pp(&msg, fmtbuf, FMTBUFLEN);
-                       break;
+               if (prevfmt != out->fmt) {      // avoid reformatting if same 
format is used consecutively
+                       switch (out->fmt) {
+                       case FMT_MONITOR:
+                               len = fmt_monitor(&msg, blk->chn, blk->tv, 
fmtbuf, FMTBUFLEN);
+                               break;
+                       case FMT_ONELINE:
+                               len = fmt_oneline(&msg, blk->chn, blk->tv, 
fmtbuf, FMTBUFLEN);
+                               break;
+                       case FMT_FULL:
+                               len = fmt_msg(&msg, blk->chn, blk->tv, fmtbuf, 
FMTBUFLEN);
+                               break;
+                       case FMT_NATIVE:
+                               len = fmt_sv(&msg, blk->chn, blk->tv, fmtbuf, 
FMTBUFLEN);
+                               break;
+                       case FMT_PP:
+                               len = fmt_pp(&msg, fmtbuf, FMTBUFLEN);
+                               break;
 #ifdef HAVE_CJSON
-               case FMT_ROUTEJSON:
-                       len = fl ? fmt_routejson(fl, blk->tv, fmtbuf, 
FMTBUFLEN) : -1;
-                       break;;
-               case FMT_JSON:
-                       len = fmt_json(&msg, blk->chn, blk->tv, fmtbuf, 
FMTBUFLEN);
-                       break;
+                       case FMT_ROUTEJSON:
+                               len = fl ? fmt_routejson(fl, blk->tv, fmtbuf, 
FMTBUFLEN) : -1;
+                               break;;
+                       case FMT_JSON:
+                               len = fmt_json(&msg, blk->chn, blk->tv, fmtbuf, 
FMTBUFLEN);
+                               break;
 #endif /* HAVE_CJSON */
-               default:
-                       continue;
+                       default:
+                               continue;
+                       }
                }
 
-               // NB if the same format is used for multiple outputs, the 
buffer will be recomputed each time. Deemed acceptable
-
                if (len <= 0)
                        continue;
 
+               prevfmt = out->fmt;
+
                switch (out->dst) {
                case DST_FILE:
                        Filewrite(fmtbuf, len, out->priv);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/rtl.c new/acarsdec-4.7/rtl.c
--- old/acarsdec-4.6/rtl.c      2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/rtl.c      2026-11-03 12:42:53.000000000 +0100
@@ -139,6 +139,8 @@
        if (!optarg)
                return 1;       // cannot happen with getopt()
 
+       R.ts = true;
+       
        if (!R.rateMult) {
                m = min_multiplier(R.minFc, R.maxFc);
                R.rateMult = (m > 80U) ? m : 80U;       // rtl has a hole in 
available sr between 300kHz and 900kHZ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/sdrplay.c new/acarsdec-4.7/sdrplay.c
--- old/acarsdec-4.6/sdrplay.c  2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/sdrplay.c  2026-11-03 12:42:53.000000000 +0100
@@ -1,6 +1,7 @@
 /*
  *  Copyright (c) 2015 Thierry Leconte
  *  Copyright (c) 2025 Franco Venturi
+ *  Copyright (c) 2026 Thibaut VARENE
  *
  *
  *   This code is free software; you can redistribute it and/or modify
@@ -28,13 +29,6 @@
 #include "lib.h"
 #include <sdrplay_api.h>
 
-// choose 252 as the 12k (INTRATE) multiplier because 12k * 252 = 3024Msps
-// (which is reasonable)
-// the specific reason for 252 is that the SDRplay API RX callback size
-// (numSamples) is 1008, which is an integer multiple of 252, and that is
-// supposed to improve performance with acarsdec
-#define SDRPLAY_MULT 252U
-
 #define ERRPFX "ERROR: SDRplay: "
 #define WARNPFX        "WARNING: SDRplay: "
 
@@ -50,6 +44,8 @@
        if (!optarg)
                return 1;       // cannot happen with getopt()
 
+       R.ts = true;
+       
        char *serialNumber = NULL;
        int rspSequenceNumber = -1;
        if (strlen(optarg) == 10 && strspn(optarg, "0123456789ABCDEF") == 10) {
@@ -62,7 +58,9 @@
        }
 
        if (!R.rateMult)
-               R.rateMult = SDRPLAY_MULT;
+               R.rateMult = min_multiplier(R.minFc, R.maxFc);
+
+       // SDRplay hardware sampling rate starts at 2MSps, lower rates are 
achieved via decimation
        unsigned int sr = INTRATE * R.rateMult;
        int decimation = 1;
        while (sr < 2000000 && decimation <= 32) {
@@ -73,9 +71,6 @@
        if (!R.gRdB)
                R.gRdB = -100;  // AGC
 
-       if (!R.lnaState)
-               R.lnaState = 2;
-
        Fc = find_centerfreq(R.minFc, R.maxFc, R.rateMult);
        if (Fc == 0)
                return 5;
@@ -94,29 +89,24 @@
        err = sdrplay_api_ApiVersion(&ver);
        if (err != sdrplay_api_Success) {
                fprintf(stderr, ERRPFX "ApiVersion failed: %s\n", 
sdrplay_api_GetErrorString(err));
-               sdrplay_api_Close();
-               return -2;
+               goto fail;
        }          
        if (ver != SDRPLAY_API_VERSION) {
                fprintf(stderr, ERRPFX "API version mismatch - expected=%.2f 
found=%.2f\n", SDRPLAY_API_VERSION, ver);
-               sdrplay_api_Close();
-               return -3;
+               goto fail;
        }
 
        /* select device */
        err = sdrplay_api_LockDeviceApi();
        if (err != sdrplay_api_Success) {
                fprintf(stderr, ERRPFX "LockDeviceApi failed: %s\n", 
sdrplay_api_GetErrorString(err));
-               sdrplay_api_Close();
-               return -4;
+               goto fail;
        }
        unsigned int ndevices = sizeof(devices) / sizeof(devices[0]);
        err = sdrplay_api_GetDevices(devices, &ndevices, ndevices);
        if (err != sdrplay_api_Success) {
                fprintf(stderr, ERRPFX "GetDevices failed: %s\n", 
sdrplay_api_GetErrorString(err));
-               sdrplay_api_UnlockDeviceApi();
-               sdrplay_api_Close();
-               return -5;
+               goto unlockfail;
        }
        int deviceIndex = -1;
        if (serialNumber != NULL) {
@@ -133,9 +123,7 @@
        }
        if (deviceIndex == -1) {
                fprintf(stderr, ERRPFX "RSP not found or not available\n");
-               sdrplay_api_UnlockDeviceApi();
-               sdrplay_api_Close();
-               return -6;
+               goto unlockfail;
        }
        device = devices[deviceIndex];
 
@@ -143,37 +131,22 @@
        if (device.hwVer == SDRPLAY_RSPduo_ID) {
                if ((device.rspDuoMode & sdrplay_api_RspDuoMode_Single_Tuner) 
!= sdrplay_api_RspDuoMode_Single_Tuner) {
                        fprintf(stderr, ERRPFX "RSPduo single tuner mode not 
available\n");
-                       sdrplay_api_UnlockDeviceApi();
-                       sdrplay_api_Close();
-                       return -7;
+                       goto unlockfail;
                }
                device.rspDuoMode = sdrplay_api_RspDuoMode_Single_Tuner;
-               if (R.antenna != NULL) {
-                       if (strcmp(R.antenna, "Tuner 1 50 ohm") == 0 || 
strcmp(R.antenna, "High Z") == 0) {
-                               device.tuner = sdrplay_api_Tuner_A;
-                       } else if (strcmp(R.antenna, "Tuner 2 50 ohm") == 0) {
-                               device.tuner = sdrplay_api_Tuner_B;
-                       } else {
-                               device.tuner = sdrplay_api_Tuner_A;
-                       }
-               }
-               device.rspDuoSampleFreq = 0;
+               device.tuner = sdrplay_api_Tuner_A;
        }
 
        err = sdrplay_api_SelectDevice(&device);
        if (err != sdrplay_api_Success) {
                fprintf(stderr, ERRPFX "SelectDevice failed: %s\n", 
sdrplay_api_GetErrorString(err));
-               sdrplay_api_UnlockDeviceApi();
-               sdrplay_api_Close();
-               return -8;
+               goto unlockfail;
        }
 
        err = sdrplay_api_UnlockDeviceApi();
        if (err != sdrplay_api_Success) {
                fprintf(stderr, ERRPFX "UnlockDeviceApi failed: %s\n", 
sdrplay_api_GetErrorString(err));
-               sdrplay_api_ReleaseDevice(&device);
-               sdrplay_api_Close();
-               return -9;
+               goto unlockfail;
        }
 
        /* select device settings */
@@ -181,9 +154,7 @@
        err = sdrplay_api_GetDeviceParams(device.dev, &device_params);
        if (err != sdrplay_api_Success) {
                fprintf(stderr, ERRPFX "GetDeviceParams failed: %s\n", 
sdrplay_api_GetErrorString(err));
-               sdrplay_api_ReleaseDevice(&device);
-               sdrplay_api_Close();
-               return -10;
+               goto fail;
        }
 
        device_params->devParams->mode = sdrplay_api_BULK;
@@ -193,78 +164,98 @@
        rx_channel_params->ctrlParams.decimation.enable = decimation > 1;
        rx_channel_params->ctrlParams.decimation.decimationFactor = decimation;
        rx_channel_params->tunerParams.ifType = sdrplay_api_IF_Zero;
+       rx_channel_params->tunerParams.rfFreq.rfHz = Fc;
+
+       uint32_t bw = ((R.maxFc - R.minFc) + 2 * INTRATE) / 1000U;
+       sdrplay_api_Bw_MHzT bwt = sdrplay_api_BW_Undefined;
+       if (bw <= 200U) bwt = sdrplay_api_BW_0_200;
+       else if (bw <= 300U) bwt = sdrplay_api_BW_0_300;
+       else if (bw <= 600U) bwt = sdrplay_api_BW_0_600;
+       else if (bw <= 1536U) bwt = sdrplay_api_BW_1_536;
+       else if (bw <= 5000U) bwt = sdrplay_api_BW_5_000;
+       else if (bw <= 6000U) bwt = sdrplay_api_BW_6_000;
+       else if (bw <= 7000U) bwt = sdrplay_api_BW_7_000;
+       else if (bw <= 8000U) bwt = sdrplay_api_BW_8_000;
+       rx_channel_params->tunerParams.bwType = bwt;
+
+       vprerr("Setting center freq: %.4f MHz, sample rate: %.4f MS/s, 
decimation factor: %d, bandwidth: %d kHZ\n",
+              Fc / 1e6, sr / 1e6, decimation, bwt);
+
        if (R.gRdB == -100) {
-               rx_channel_params->ctrlParams.agc.enable = 
sdrplay_api_AGC_100HZ;
+               rx_channel_params->ctrlParams.agc.enable = sdrplay_api_AGC_5HZ;
+               rx_channel_params->ctrlParams.agc.setPoint_dBfs = -30;  // from 
dumpvdl2
+               vprerr("AGC enabled, setpoint at -30 dBFS\n");
        } else {
                rx_channel_params->ctrlParams.agc.enable = 
sdrplay_api_AGC_DISABLE;
                rx_channel_params->tunerParams.gain.gRdB = R.gRdB;
+               rx_channel_params->tunerParams.gain.LNAstate = R.lnaState;
+               vprerr("IF gain reduction: %d, LNA state: %d\n", R.gRdB, 
R.lnaState);
        }
-       rx_channel_params->tunerParams.gain.LNAstate = R.lnaState;
        device_params->devParams->ppm = (double)(R.ppm);
-       rx_channel_params->tunerParams.rfFreq.rfHz = Fc;
+
        if (R.antenna != NULL) {
                int antennaOK = 0;
-               if (device.hwVer == SDRPLAY_RSP2_ID) {
-                       if (strcmp(R.antenna, "Antenna A") == 0) {
-                               antennaOK = 1;
-                               rx_channel_params->rsp2TunerParams.antennaSel = 
sdrplay_api_Rsp2_ANTENNA_A;
-                               rx_channel_params->rsp2TunerParams.amPortSel = 
sdrplay_api_Rsp2_AMPORT_2;
-                       } else if (strcmp(R.antenna, "Antenna B") == 0) {
-                               antennaOK = 1;
-                               rx_channel_params->rsp2TunerParams.antennaSel = 
sdrplay_api_Rsp2_ANTENNA_B;
-                               rx_channel_params->rsp2TunerParams.amPortSel = 
sdrplay_api_Rsp2_AMPORT_2;
-                       } else if (strcmp(R.antenna, "Hi-Z") == 0) {
-                               antennaOK = 1;
-                               rx_channel_params->rsp2TunerParams.antennaSel = 
sdrplay_api_Rsp2_ANTENNA_A;
-                               rx_channel_params->rsp2TunerParams.amPortSel = 
sdrplay_api_Rsp2_AMPORT_1;
-                       }
-               } else if (device.hwVer == SDRPLAY_RSPduo_ID) {
-                       if (strcmp(R.antenna, "High Z") == 0) {
-                               antennaOK = 1;
-                               
rx_channel_params->rspDuoTunerParams.tuner1AmPortSel = 
sdrplay_api_RspDuo_AMPORT_1;
-                       } else {
-                               antennaOK = 1;
-                               
rx_channel_params->rspDuoTunerParams.tuner1AmPortSel = 
sdrplay_api_RspDuo_AMPORT_2;
-                       }
-               } else if (device.hwVer == SDRPLAY_RSPdx_ID || device.hwVer == 
SDRPLAY_RSPdxR2_ID) {
-                       if (strcmp(R.antenna, "Antenna A") == 0) {
-                               antennaOK = 1;
-                               
device_params->devParams->rspDxParams.antennaSel = sdrplay_api_RspDx_ANTENNA_A;
-                       } else if (strcmp(R.antenna, "Antenna B") == 0) {
-                               antennaOK = 1;
-                               
device_params->devParams->rspDxParams.antennaSel = sdrplay_api_RspDx_ANTENNA_B;
-                       } else if (strcmp(R.antenna, "Antenna C") == 0) {
-                               antennaOK = 1;
-                               
device_params->devParams->rspDxParams.antennaSel = sdrplay_api_RspDx_ANTENNA_C;
-                       }
+               switch (device.hwVer) {
+                       case SDRPLAY_RSP2_ID:
+                               if (strcmp(R.antenna, "A") == 0) {
+                                       antennaOK = 1;
+                                       
rx_channel_params->rsp2TunerParams.antennaSel = sdrplay_api_Rsp2_ANTENNA_A;
+                               } else if (strcmp(R.antenna, "B") == 0) {
+                                       antennaOK = 1;
+                                       
rx_channel_params->rsp2TunerParams.antennaSel = sdrplay_api_Rsp2_ANTENNA_B;
+                               }
+                               break;
+                       case SDRPLAY_RSPdx_ID:
+                       case SDRPLAY_RSPdxR2_ID:
+                               if (strcmp(R.antenna, "A") == 0) {
+                                       antennaOK = 1;
+                                       
device_params->devParams->rspDxParams.antennaSel = sdrplay_api_RspDx_ANTENNA_A;
+                               } else if (strcmp(R.antenna, "B") == 0) {
+                                       antennaOK = 1;
+                                       
device_params->devParams->rspDxParams.antennaSel = sdrplay_api_RspDx_ANTENNA_B;
+                               } else if (strcmp(R.antenna, "C") == 0) {
+                                       antennaOK = 1;
+                                       
device_params->devParams->rspDxParams.antennaSel = sdrplay_api_RspDx_ANTENNA_C;
+                               }
+                               break;
+                       default:
+                               fprintf(stderr, ERRPFX "cannot select antenna 
port: not supported\n");
+                               goto fail;
                }
                if (!antennaOK) {
                        fprintf(stderr, ERRPFX "invalid antenna: %s\n", 
R.antenna);
-                       sdrplay_api_ReleaseDevice(&device);
-                       sdrplay_api_Close();
-                       return -11;
+                       goto fail;
                }
        }
 
        if (R.bias) {
-               if (device.hwVer == SDRPLAY_RSP1A_ID || SDRPLAY_RSP1B_ID) {
-                       rx_channel_params->rsp1aTunerParams.biasTEnable = 
R.bias;
-               } else if (device.hwVer == SDRPLAY_RSP2_ID) {
-                       rx_channel_params->rsp2TunerParams.biasTEnable = R.bias;
-               } else if (device.hwVer == SDRPLAY_RSPduo_ID) {
-                       rx_channel_params->rspDuoTunerParams.biasTEnable = 
R.bias;
-               } else if (device.hwVer == SDRPLAY_RSPdx_ID || device.hwVer == 
SDRPLAY_RSPdxR2_ID) {
-                       device_params->devParams->rspDxParams.biasTEnable = 
R.bias;
+               switch (device.hwVer) {
+                       case SDRPLAY_RSP1A_ID:
+                       case SDRPLAY_RSP1B_ID:
+                               rx_channel_params->rsp1aTunerParams.biasTEnable 
= R.bias;
+                               break;
+                       case SDRPLAY_RSP2_ID:
+                               rx_channel_params->rsp2TunerParams.biasTEnable 
= R.bias;
+                               break;
+                       case SDRPLAY_RSPduo_ID:
+                               
rx_channel_params->rspDuoTunerParams.biasTEnable = R.bias;
+                               break;
+                       case SDRPLAY_RSPdx_ID:
+                       case SDRPLAY_RSPdxR2_ID:
+                               
device_params->devParams->rspDxParams.biasTEnable = R.bias;
+                               break;
+                       default:
+                               fprintf(stderr, WARNPFX "not enabling Bias-T: 
not supported\n");
                }
        }
 
-       if (R.gRdB == -100)
-               fprintf(stderr, "SDRplay device selects freq %d and sets 
autogain and LNA state %d\n", Fc, R.lnaState);
-       else
-               fprintf(stderr, "SDRplay device selects freq %d and sets IF 
gain reduction %d and LNA state %d\n",
-                       Fc, R.gRdB, R.lnaState);
-
        return 0;
+
+unlockfail:
+       sdrplay_api_UnlockDeviceApi();
+fail:
+       sdrplay_api_Close();
+       return -1;
 }
 
 static void sdrplayRXCallback(short *xi,
@@ -291,9 +282,7 @@
        err = sdrplay_api_Init(device.dev, &callbackFns, NULL);
        if (err != sdrplay_api_Success) {
                fprintf(stderr, ERRPFX "Init failed: %s\n", 
sdrplay_api_GetErrorString(err));
-               sdrplay_api_ReleaseDevice(&device);
-               sdrplay_api_Close();
-               return -1;
+               goto fail;
        }
 
        while (R.running)
@@ -302,12 +291,14 @@
        err = sdrplay_api_Uninit(device.dev);
        if (err != sdrplay_api_Success) {
                fprintf(stderr, ERRPFX "Uninit failed: %s\n", 
sdrplay_api_GetErrorString(err));
-               sdrplay_api_ReleaseDevice(&device);
-               sdrplay_api_Close();
-               return -2;
+               goto fail;
        }
 
        return 0;
+
+fail:
+       sdrplay_api_Close();
+       return -1;
 }
 
 static unsigned int nextSampleNum = 0xffffffff;
@@ -359,14 +350,7 @@
        (void)cbContext;
 
        if (eventId == sdrplay_api_PowerOverloadChange) {
-               switch (params->powerOverloadParams.powerOverloadChangeType) {
-               case sdrplay_api_Overload_Detected:
-                       fprintf(stderr, WARNPFX "power overload detected 
event\n");
-                       break;
-               case sdrplay_api_Overload_Corrected:
-                       fprintf(stderr, WARNPFX "power overload correct 
event\n");
-                       break;
-               }
+               vprerr("power overload %s event\n", 
(params->powerOverloadParams.powerOverloadChangeType == 
sdrplay_api_Overload_Detected) ? "detected" : "corrected");
                sdrplay_api_Update(device.dev, tuner, 
sdrplay_api_Update_Ctrl_OverloadMsgAck, sdrplay_api_Update_Ext1_None);
        }
        return;
@@ -377,11 +361,9 @@
        sdrplay_api_ErrT err;
 
        err = sdrplay_api_ReleaseDevice(&device);
-       if (err != sdrplay_api_Success) {
-               fprintf(stderr, ERRPFX "ReleaseDevice failed: %s\n", 
sdrplay_api_GetErrorString(err));
-               sdrplay_api_Close();
-               return -1;
-       }
+       if (err != sdrplay_api_Success)
+               fprintf(stderr, WARNPFX "ReleaseDevice failed: %s\n", 
sdrplay_api_GetErrorString(err));
+
        err = sdrplay_api_Close();
        if (err != sdrplay_api_Success) {
                fprintf(stderr, ERRPFX "Close failed: %s\n", 
sdrplay_api_GetErrorString(err));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/soapy.c new/acarsdec-4.7/soapy.c
--- old/acarsdec-4.6/soapy.c    2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/soapy.c    2026-11-03 12:42:53.000000000 +0100
@@ -83,6 +83,8 @@
        if (!optarg)
                return 1;       // cannot happen after getopt()
 
+       R.ts = true;
+       
        dev = SoapySDRDevice_makeStrArgs(optarg);
        if (dev == NULL) {
                fprintf(stderr, ERRPFX "opening SoapySDR device using string 
\"%s\": %s\n", optarg, SoapySDRDevice_lastError());
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/acarsdec-4.6/soundfile.c new/acarsdec-4.7/soundfile.c
--- old/acarsdec-4.6/soundfile.c        2026-06-18 08:44:42.000000000 +0200
+++ new/acarsdec-4.7/soundfile.c        2026-11-03 12:42:53.000000000 +0100
@@ -32,7 +32,7 @@
 
 static SNDFILE *insnd;
 
-static int usage(void)
+static void usage(void)
 {
        fprintf(stderr,
                "sndfile input accepts as parameter:\n"
@@ -48,10 +48,11 @@
                "   'data.raw,subtype=2,channels=2,endian=little' to process a 
2-channel, 16-bit little-endian PCM data\n"
                "\n"
                "NOTE: audio sample rate must be a multiple of %d Hz.\n"
-               "For raw audio, the rate multiplier can be specified via '-m' 
(default: 1)\n",
+               "For raw audio, the rate multiplier can be specified via '-m' 
(default: 1)\n"
+               "\n",
                INTRATE);
 
-       return 1;
+       exit(1);
 }
 
 int initSoundfile(char *optarg)
@@ -91,7 +92,7 @@
        }
 
        if (!strcmp("help", fname))
-               return usage();
+               usage();
 
        // if we have a subtype, assume we're dealing with raw input
        if (stype) {

Reply via email to