Compare commits

..

111 Commits

Author SHA1 Message Date
eric1tran 849a3b7329 Release 7.2 2022-03-10 02:53:19 +00:00
eric1tran b842f4a8ed skyaware lighttpd module replaced dump1090-fa. We can disable dump1090-fa module now 2022-03-07 21:09:42 +00:00
eric1tran 6fc48a8cef Bump version to 7.2 2022-03-02 18:10:43 +00:00
eric1tran 2e83305229 Version bump back to 7.2 2022-02-25 15:39:07 +00:00
eric1tran d0e87cc219 Remove OSM Black & White layer which is no longer in service 2022-02-16 19:56:02 +00:00
Eric Tran ac7b5759f7
SkyAware HTML/CSS cleanup (#171)
* Remove deprecated dump1090-fa/ html files and update dump1090-fa.install accordingly

* Remove deprecated lighttpd files and update paths/readmes

* SkyAware readme update

* More SkyAware readme updates

* Bump html/css script cache bust versions
2022-02-15 08:58:22 -06:00
Oliver Jowett 350664a13a
Merge pull request #169 from ffontaine/master
Makefile: set _POSIX_C_SOURCE
2022-01-15 20:44:22 +08:00
Fabrice Fontaine f24c932cf8 Makefile: set _POSIX_C_SOURCE
Set -D_POSIX_C_SOURCE=200112L to avoid the following build failure with
uclibc-ng:

anet.c: In function ‘anetTcpGenericConnect’:
anet.c:153:21: error: storage size of ‘gai_hints’ isn’t known
  153 |     struct addrinfo gai_hints;
      |                     ^~~~~~~~~

In file included from dump1090.h:83,
                 from dump1090.c:50:
compat/compat.h:46:3: error: conflicting types for ‘clockid_t’
   46 | } clockid_t;
      |   ^~~~~~~~~
In file included from /home/fabrice/buildroot/output/host/or1k-buildroot-linux-uclibc/sysroot/usr/include/sys/types.h:132,
                 from /home/fabrice/buildroot/output/host/or1k-buildroot-linux-uclibc/sysroot/usr/include/stdlib.h:198,
                 from dump1090.h:66,
                 from dump1090.c:50:
/home/fabrice/buildroot/output/host/or1k-buildroot-linux-uclibc/sysroot/usr/include/time.h:91:21: note: previous declaration of ‘clockid_t’ was here
   91 | typedef __clockid_t clockid_t;
      |                     ^~~~~~~~~

net_io.c: In function ‘writeJsonToFile’:
net_io.c:2122:18: error: ‘PATH_MAX’ undeclared (first use in this function)
 2122 |     char pathbuf[PATH_MAX];
      |                  ^~~~~~~~

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2022-01-15 10:54:32 +01:00
Oliver Jowett c35135cabf
Merge pull request #170 from CodyCodeman/fix-make-syntax-error-darwin
Darwin: Fix Makefile syntax error
2022-01-15 12:58:44 +08:00
Oliver Jowett 86885c2480 Version bump to 8.0~dev 2022-01-15 11:26:16 +08:00
Cody 3be128877d
Fix syntax error 2022-01-14 12:23:30 -06:00
eric1tran a13356d801 Release 7.1 2022-01-12 17:10:52 +00:00
eric1tran 855f71918e Revert "Revert the debian/rules changes"
This reverts commit f5185cbebe.
2022-01-12 15:00:08 +00:00
eric1tran f5185cbebe Revert the debian/rules changes 2022-01-12 13:37:55 +00:00
eric1tran 765b295479 Don't forget Stretch 2022-01-11 19:42:36 +00:00
eric1tran 778d85e75f Start dump1090-fa after installing due to behavioral change in compat 10 2022-01-11 19:19:33 +00:00
eric1tran fb959f0fc7 No change 7.1 release for PiAware 7.1 2022-01-11 13:40:48 +00:00
eric1tran fec0da4f0b Version bump to 7.1~dev 2022-01-11 13:37:42 +00:00
eric1tran 52cdd32d0f Mark as stable in changelog 2021-12-21 14:55:38 +00:00
eric1tran 4601566886 Update changelogs 2021-12-20 17:06:29 +00:00
eric1tran 23f5dfef2c Merge branch 'dev' into staging 2021-12-13 17:49:25 +00:00
eric1tran 42a5c2a978 Update aircraft db to 20211210 2021-12-10 12:57:59 +00:00
Oliver Jowett 65cd5fe441 Clean up AVR parsing a bit. Add length checks. Decode timestamps. 2021-12-09 18:37:50 +08:00
Oliver Jowett f932baa5fa Adaptive gain: more aggressively re-probe for a higher gain
following a decrease in gain due to an increased noise floor.

This is controlled by --adaptive-range-scan-delay and defaults
to 5 minutes (vs. the default rescan delay of 1 hour)
2021-12-07 17:29:50 +08:00
Oliver Jowett fa8a066b4c Disable cpufeatures on darwin/arm64 2021-12-07 15:18:58 +08:00
Oliver Jowett b645f7d4f2 Try to respect CFLAGS/CPPFLAGS as far as possible; move required extra flags into a separate var.
This means you can, in theory, completely override CFLAGS/CPPFLAGS without also having to
provide all the internal flags that the Makefile usually detects.

$ make CFLAGS=-qwerty
[...]
cc  -I. -DMODES_DUMP1090_VERSION=\"unknown\" -DMODES_DUMP1090_VARIANT=\"dump1090-fa\" -D_DEFAULT_SOURCE -DENABLE_CPUFEATURES -Icpu_features/include -DENABLE_RTLSDR -DENABLE_BLADERF -DENABLE_HACKRF -DENABLE_LIMESDR -DSTARCH_MIX_X86 -qwerty -std=c11 -fno-common -Wall -Wmissing-declarations -Werror -W -I/usr/include/ -I/usr/include/libusb-1.0 -I/usr/include/ -I/usr/include/ -I/usr/include/libusb-1.0  -c dump1090.c -o dump1090.o

Should fix #161
2021-12-07 15:12:49 +08:00
Oliver Jowett bc72177c8b Treat ARCH=arm64 like ARCH=aarch64 2021-12-07 13:17:02 +08:00
eric1tran 24c0248fc0 Openlayers cleanup. Remove v4.4.2 js/css files and remove source map files as minified files are not being used to begin with 2021-11-22 18:27:12 +00:00
Oliver Jowett cdbd6c77a9 Updates for bullseye, drop jessie support.
* dh-compat 10
 * no dh-systemd dependency
 * backport version for buster
 * teach prepare-build about bullseye
 * update stretch maintainer address
 * update Jenkinsfile (this will be broken until the builder machine is upgraded)
2021-11-22 19:29:30 +08:00
eric1tran 9b2cd0a5ba 7.0 Version bump 2021-11-01 19:29:34 +00:00
Oliver Jowett 059e48b82b Allow env vars (not only command line overrides) to set CPUFEATURES_{ARCH,UNAME}.
Change CPUFEATURES_{ARCH,UNAME} defaults to use {ARCH,UNAME} values.

Fixes #158
2021-09-23 13:24:59 +08:00
Oliver Jowett 5f7e7cf8e0 Version bump to 7.0~dev 2021-09-23 13:14:03 +08:00
Oliver Jowett 752a7aeac0 No-change 6.1 release for PiAware 6.1
Fix 6.0 changelog date format
2021-09-06 16:07:03 +08:00
Oliver Jowett 08c7ccbc8e Adaptive gain docs.
(These are not included in the build so don't need to re-tag 6.0)
2021-09-01 19:45:01 +08:00
eric1tran 9788588aba Release 6.0 & changelog 2021-08-31 21:26:24 +00:00
eric1tran 8b9dd42676 Merge branch 'dev' into staging 2021-08-24 16:52:07 +00:00
Oliver Jowett 14e6d5c3fa Rearrange config upgrade so upgrading a v6 config file
disables adaptive gain.

This is needed for the case where a v5 package with an unmodified
config file is upgraded. dpkg will install the v6 default config
file automatically so it will already be a v6 config by the time
that upgrade-config runs, but we do still need to go in and
disable adaptive gain.
2021-08-23 13:07:05 +08:00
eric1tran e8eeac4654 Merge branch 'dev' into staging 2021-08-18 18:40:20 +00:00
Eric Tran abf8fa2b00
Revert "CSS updates (including dark mode)" (#149) 2021-08-18 13:37:50 -05:00
eric1tran 06bc3c34cf Version bump to 6.0 2021-08-17 20:41:14 +00:00
Oliver Jowett 88fec00f17 Add VH- registration rule to public_html_merged, too 2021-08-17 21:12:23 +08:00
Oliver Jowett ae042eeb27 Update aircraft db to 20210817 (Australian registrations & types)
Update public_html db so it's in sync with public_html_merged
2021-08-17 15:03:34 +08:00
Oliver Jowett f45b2c6475 Add a deterministic registration rule for VH- registrations 2021-08-17 14:34:10 +08:00
eric1tran 2ba2f4da71 Update aircraft db to 20210816 2021-08-16 17:32:24 +00:00
eric1tran 29a6ae5102 Merge branch 'dev' of github.com:flightaware/dump1090 into dev 2021-08-16 17:16:37 +00:00
Oliver Jowett 0aac23e049 Enable rtlsdr bounce buffers on aarch64, too (thanks to @wiedehopf for the suggestion) 2021-08-11 17:28:47 +08:00
Oliver Jowett ef70374126 Report json write errors, with some rate limiting.
Fixes #129
2021-08-04 17:33:28 +08:00
Oliver Jowett 7d552a2ede Remove old testfile (no longer useful due to sampling rate changes)
fixes #135 (well, wallpapers over the cracks at least)
2021-08-04 17:22:10 +08:00
Oliver Jowett 8b0e8ce6e3 Merge just one commit (21d1fe0f11) from PR #143 2021-08-04 17:11:44 +08:00
Oliver Jowett e02d0d5ffa Regenerate starch-generated files 2021-08-04 17:08:00 +08:00
Oliver Jowett 871a7e685f Update starch to upstream commit 0c8249fa4bc523345c156885542e9192e8bf9420 2021-08-04 17:07:08 +08:00
Oliver Jowett dd53b36bdf
Merge pull request #142 from flightaware/mrar
MRAR Comm-B support
2021-08-03 21:28:58 +08:00
Oliver Jowett 26924e4efe
Merge pull request #144 from b3nn0/stratux-gain
Stratux output: include current receiver gain
2021-08-03 17:22:54 +08:00
Adrian Batzill 6785c56348 Stratux output: include current receiver gain 2021-07-30 12:32:45 +01:00
VirusPilot 21d1fe0f11
Update dump1090.c
corrected an obvious typo
2021-07-30 08:36:45 +02:00
Oliver Jowett 3367cc5a82 faup1090: bump TSV_VERSION to 9E for MRAR, Comm-B changes 2021-07-29 19:04:54 +08:00
Oliver Jowett fe36349083 faup1090: upload MRAR data 2021-07-29 19:04:54 +08:00
Oliver Jowett e0f794b1a5 Track MRAR data per-aircraft and emit in aircraft.json 2021-07-29 19:04:54 +08:00
Oliver Jowett 95ab1c0faa faup1090: upload unknown-format Comm-B messages on demand 2021-07-29 19:04:53 +08:00
Oliver Jowett fab8081322 Upload Comm-B GICB messages 2021-07-29 19:04:53 +08:00
Oliver Jowett 9d4e2230d2 Add a few more Comm-B EMPTY_RESPONSE cases 2021-07-29 19:04:52 +08:00
Oliver Jowett 825f959e4d Add skeletal BDS0,5 decoding.
These messages duplicate ADS-B messages so it's not clear why
they are being interrogated for, and we don't really trust them
enough to do anything with the position information. But
recognizing/decoding them where possible does let us exclude other
Comm-B message types, avoiding some false positives.
2021-07-29 19:04:52 +08:00
Oliver Jowett c148fdca84 Distinguish "unknown format" from "didn't even try to decode"
when decoding Comm-B messages
2021-07-29 19:04:51 +08:00
Oliver Jowett 9f146fcb71 Set mrar_source_valid correctly 2021-07-29 19:04:51 +08:00
Oliver Jowett cdc818a9f3 MRAR source=5 is bogus, don't decode that. 2021-07-29 19:04:50 +08:00
Oliver Jowett e3a8e00412 WIP on MRAR decoding 2021-07-29 19:04:49 +08:00
Oliver Jowett 6b8cac3922 Increase default adaptive gain rescan interval to 1 hour 2021-07-26 22:08:57 +08:00
Oliver Jowett 899ee2530c Add CONFIG_STYLE to default config for unambiguous detection of
new-style config. Try to handle un-upgraded config files in
start-dump1090-fa. Always pass --quiet to dump1090-fa.
2021-07-26 20:26:17 +08:00
Oliver Jowett d6405ddefd Ignore bogus -I/ from librtlsdr pkgconfig 2021-07-26 20:25:35 +08:00
Oliver Jowett e058929977 stretch compile fixes? round two 2021-07-21 20:51:57 +08:00
Oliver Jowett 7b1771cdad Fix limesdr build 2021-07-21 20:03:06 +08:00
Oliver Jowett 017e5d391b Add ADAPTIVE_DYNAMIC_RANGE_TARGET config var 2021-07-21 19:42:09 +08:00
Oliver Jowett 5bd1718bb2 try non-static const to make stretch build happy? 2021-07-21 19:41:42 +08:00
Oliver Jowett f3e498a62a Treat the magic agc setting as just another gain step.
The legacy -10 value is still supported for compatbility with old
configs, but new configs should just select an appropriate gain e.g.
60dB.

rtlsdr default gain continues to be the highest regular gain step (~ 49.6dB)
2021-07-19 17:34:45 +08:00
Oliver Jowett d4b3b03fe0 Try to rewrite old-style config files to new-style config files
on upgrade to 6.0. (Not extensively tested yet)

We do this for two reasons:

1) the config file layout is completely different, it's error-prone
and tedious to force the user to manually transfer their customizations
on upgrade;

2) if there are user modifications to the config file, an upgrade triggered
remotely via piaware will prefer to keep the original config file intact
rather than installing the new version, and the old version just won't
work correctly with the new infrastructure.
2021-07-19 15:50:55 +08:00
Oliver Jowett ec69b94544 Overhaul of /etc/default/dump1090-fa
Now we have specific config options for different features rather
than just a big list of command-line options. You can still provide
custom options via EXTRA_OPTIONS or OVERRIDE_OPTIONS.

Also adds adaptive gain settings, and a "slow cpu" setting which
can turn on cpu-reducing options automatically when a slower (armv6)
CPU is in use.
2021-07-19 15:50:55 +08:00
Oliver Jowett 3069f3d99f Set some sensible net flush defaults rather than relying on the default config file 2021-07-19 15:50:55 +08:00
Oliver Jowett 4cf8eb254e Report actual duty cycle on startup 2021-07-19 15:50:55 +08:00
Oliver Jowett be3c9930f1 Add --adaptive-duty-cycle option.
This makes the adaptive gain code only process some fraction of all samples,
rather than every sample. This helps a lot with CPU on slower machines.
We default to a 50% duty cycle to save some CPU without really affecting
the behavior much.

We split the one-second adaptive gain blocks into 20* 50ms "subblocks"
Each subblock is either entirely processed or not processed at all,
based on the configured duty cycle. (This means that the duty cycle
can only be specified in approx 5% increments, but that should be
fine)

For adaptive dynamic range, there's no further complications - we
just look at fewer samples to compute the noise floor.

For adaptive burst, we need to scale up the measured burst rate by
1/duty cycle to account for the samples that we didn't process. It's
also more likely to cause functional changes as it's quite possible
for a relatively small number of loud messages being missed to
affect the computed rate substantially. Not many options there other
than increasing the duty cycle when adaptive burst handling is wanted.
2021-07-19 15:50:55 +08:00
Oliver Jowett e0f7a33df4 Fix some case where adaptive_range_gain_limit would not be correctly decreased 2021-07-19 15:50:55 +08:00
wiedehopf 9e671a14e4
restrict overlay rendering to covered areas (reduce source load) (#137) 2021-07-15 15:46:30 -05:00
eric1tran 4e21610c50 Merge commit 'refs/pull/127/head' of github.com:flightaware/dump1090 into dev 2021-07-14 13:59:46 +00:00
Matthias Wirth 603da245bd restrict overlay rendering to covered areas (reduce source load) 2021-07-14 09:39:37 +02:00
Oliver Jowett 6e5f2595e0 Update starch to upstream commit f4b84e62d5747ff44cd468c4d8ca1626434378c0 2021-07-13 13:16:02 +08:00
Oliver Jowett 2480e5169c Use a starch implementation for the burst-detection sample counting loop. 2021-07-08 18:53:02 +08:00
Oliver Jowett 1cb4284e6c Move the --wisdom help somewhere more sensible 2021-07-08 18:46:48 +08:00
Oliver Jowett ac97423249 Misc adaptive gain changes:
Update adaptive gain control loop to handle interactions between burst detection and dynamic range control.

Internal doc updates & naming cleanups.

Stats tweaks.
2021-07-07 20:57:34 +08:00
Oliver Jowett fd8f2d77e1 Fix adaptive gain stats not getting set on stats_current -> zeros
appearing in stats output. Now we have a validity flag for the stats
members where we have to pick one value and not simply sum data.
2021-07-07 20:18:54 +08:00
Oliver Jowett b3172181d5 Use compat/compat.h for endian-swapping functions in DSP code.
Might fix #128
2021-07-07 11:26:41 +08:00
Oliver Jowett 56625449e8
Adaptive gain, first pass (#134)
This PR adds basic adaptive gain support, which adjusts SDR gain on the fly based on the noise & signal levels seen.

There are two control mechanisms:

Dynamic range control is enabled by the --adaptive-range option. This adjusts SDR gain to try to achieve a minimum dynamic range, regardless of the exact hardware in the RF path.

Burst (loud message) control is enabled by the --adaptive-burst option. This decreases SDR gain when undecodable loud messages are heard, allowing for better reception of nearby aircraft at the expense of range.

This is only the basic implementation - see the PR for remaining work to do.
2021-06-29 20:11:13 +08:00
Eric Gideon b775f2b326 tweaks to group, mostly 2021-05-22 11:48:05 -07:00
Eric Gideon 8d877eeed8 further color cleanup, fixing links and headers in the settings box 2021-05-22 11:48:05 -07:00
Eric Gideon 88741d097b ol-layerswitcher pinned to global vars, adjusted margin/padding for consistent spacing 2021-05-22 11:48:05 -07:00
Eric Gideon 24c425c968 initial global vars added, BG color changed 2021-05-22 11:48:05 -07:00
Eric Gideon acde5b4a91 Prettier default-settings pass using 4 spaces for tabs to clean up CSS and enforce consistency 2021-05-22 11:48:05 -07:00
eric1tran f7b6f7aefc Merge branch 'dev' of github.com:flightaware/dump1090 into dev 2021-05-20 21:25:56 +00:00
Chris Carini a4cd5bb42d
Fixing spelling mistake in deprecated (#125) 2021-05-20 16:25:46 -05:00
eric1tran 465d436174 Fix ignored DisplayUnits in config.js PR #76 2021-05-20 17:29:17 +00:00
eric1tran 223283863f 6.0~dev 2021-05-18 20:41:27 +00:00
Oliver Jowett b4acf08738 Fix overflow in get_deadline for large (>2000ms) timeouts 2021-04-29 14:52:45 +08:00
Oliver Jowett 265055106b
Merge pull request #123 from VasiliyTurchenko/hackrf_ant_power
add "--enable-antenna-power" command line option for HackRF config
2021-03-22 18:52:18 +08:00
Турченко Василий Владимирович 80acb91dbc add "--enable-antenna-power" command line option for HackRF configuration
to be able to feed antenna's connector with +3.3v HackRF's internal DC
voltage.
2021-03-22 00:31:47 +03:00
Oliver Jowett 3d013c4251 Note that build profiles only work with buster 2021-03-22 00:36:11 +08:00
Oliver Jowett 86bb40a31c Update install docs to mention use of prepare-build.sh for package builds.
Soft removal of jessie support (just remove the build docs)
2021-03-22 00:33:03 +08:00
Oliver Jowett 8dd83d2e7e Don't hang on exit if rtlsdr hardware stops sending samples.
Give up and exit after 30 seconds of no sample data, rather than just warning and continuing.

background & discussion: https://discussions.flightaware.com/t/cpu-hikes-crash-dump1090-fa/74759

The scenario this addresses is:

 * Hardware wedges, USB bulk endpoint stops providing data
 * librtlsdr remains in rtlsdr_read_async() waiting for either USB data which never arrives,
   or a cancellation via _a different thread_ calling rtlsdr_cancel_async().
 * main thread notices the lack of SDR data and complains
 * something external e.g. piaware tries a restart and sends SIGTERM
 * the signal handler sets Modes.exit = 1; the main thread starts waiting for receive thread termination
 * because we're never getting callbacks from rtlsdr_read_async(), we never call rtlsdr_cancel_async
 * dump1090 hangs waiting on receive thread termination

To fix this, add a sdrStop() handler function where the general contract is "make the receive thread terminate".
In the rtlsdr case, this calls rtlsdr_cancel_async directly, which will make rtlsdr_read_async() return even
if the hardware is stuck.

The main thread then calls sdrStop() before waiting for receive thread termination.

Also, as discussed in the thread above, there's not really much point in continuing to run if the SDR
has wedged, so bail out after 30 seconds of no sample data.

Also, if pthread_timedjoin_np is available, use it in preference to pthread_join so that we do not wait
indefinitely for the receive thread on shutdown. If the join times out, give up and abort() as we can't
safely continue a clean shutdown while the receive thread is running.
2021-03-21 02:03:08 +08:00
Oliver Jowett c433463392 Reduce CPU further in --no-fix-df mode. Add --enable-df24 option.
This reinstates the fastpath in the 2.4MHz demodulator that
aborts message demodulation early if the DF bits don't match a message
type that we know how to decode, or stops early if the DF bits can
only correspond to a short 56-bit message.

Do this in a more general form where we test against a set of valid
DF values, rather than a switch/case. Then populate the sets based on
the current error-correction settings (e.g. if we are allowed to repair
DF damage with 1 bit error correction, then a DF17 message could appear
with any of DF=17, DF=16, DF=19, DF=21, DF=25, or DF=1 and still be
correctable to a valid DF17 message)

In the default case this produces a small CPU improvement as short
messages might be able to stop earlier and a few DF values can be ignored
early. With --no-fix-df it produces a larger improvement as relatively few
DF values are valid in that mode.

To further reduce CPU, the default behaviour has changed to _not_
decode DF24 messages (Comm-D ELM messages). These are rarely seen in
the wild and dump1090 can't do anything useful with them. Disabling
them allows us to further reduce the set of valid DF values as they
effectively use all DF values from 24..31, 25% of all possible values.
To re-enable DF24 decoding, use the new `--enable-df24` option.

Finally, avoid doing some CRC calculations twice. This was only happening
once per valid decoded message, not per demodulation attempt, so it's not
such a large win.
2021-03-19 17:03:15 +08:00
Oliver Jowett 455896e86d Fix broken 32-bit x86 test that broke builds on non-x86 2021-03-17 02:21:18 +08:00
eric1tran 037fe4f37f Remove extra <td> tag 2021-03-15 12:35:12 +00:00
Oliver Jowett c97b83d3ed Maybe fix alignment warnings in arm neon code with recent gcc 2021-03-13 20:24:01 +08:00
Oliver Jowett 462dee56f3 Fix builds on 32-bit x86 2021-03-13 12:49:40 +08:00
Oliver Jowett d3c692f630 5.1~dev for the dev branch 2021-03-13 12:41:21 +08:00
917 changed files with 5175 additions and 14255 deletions

2
Jenkinsfile vendored
View File

@ -8,7 +8,7 @@ node(label: 'raspberrypi') {
durabilityHint(hint: 'PERFORMANCE_OPTIMIZED')
])
def dists = ["buster", "stretch", "jessie"]
def dists = ["bullseye", "buster", "stretch"]
def srcdir = "${WORKSPACE}/src"
stage('Checkout') {

View File

@ -2,10 +2,10 @@ PROGNAME=dump1090
DUMP1090_VERSION ?= unknown
CPPFLAGS += -I. -DMODES_DUMP1090_VERSION=\"$(DUMP1090_VERSION)\" -DMODES_DUMP1090_VARIANT=\"dump1090-fa\"
CFLAGS ?= -O3 -g
DUMP1090_CFLAGS := -std=c11 -fno-common -Wall -Wmissing-declarations -Werror -W
DUMP1090_CPPFLAGS := -I. -D_POSIX_C_SOURCE=200112L -DMODES_DUMP1090_VERSION=\"$(DUMP1090_VERSION)\" -DMODES_DUMP1090_VARIANT=\"dump1090-fa\"
DIALECT = -std=c11
CFLAGS += $(DIALECT) -O3 -g -Wall -Wmissing-declarations -Werror -W -D_DEFAULT_SOURCE -fno-common
LIBS = -lpthread -lm
SDR_OBJ = cpu.o sdr.o fifo.o sdr_ifile.o dsp/helpers/tables.o
@ -39,10 +39,14 @@ else
LIMESDR ?= no
endif
UNAME := $(shell uname)
HOST_UNAME := $(shell uname)
HOST_ARCH := $(shell uname -m)
UNAME ?= $(HOST_UNAME)
ARCH ?= $(HOST_ARCH)
ifeq ($(UNAME), Linux)
CPPFLAGS += -D_DEFAULT_SOURCE
DUMP1090_CPPFLAGS += -D_DEFAULT_SOURCE
LIBS += -lrt
LIBS_USB += -lusb-1.0
LIBS_CURSES := -lncurses
@ -51,32 +55,35 @@ endif
ifeq ($(UNAME), Darwin)
ifneq ($(shell sw_vers -productVersion | egrep '^10\.([0-9]|1[01])\.'),) # Mac OS X ver <= 10.11
CPPFLAGS += -DMISSING_GETTIME
DUMP1090_CPPFLAGS += -DMISSING_GETTIME
COMPAT += compat/clock_gettime/clock_gettime.o
endif
CPPFLAGS += -DMISSING_NANOSLEEP
DUMP1090_CPPFLAGS += -DMISSING_NANOSLEEP
COMPAT += compat/clock_nanosleep/clock_nanosleep.o
LIBS_USB += -lusb-1.0
LIBS_CURSES := -lncurses
CPUFEATURES ?= yes
# cpufeatures reportedly does not work (yet) on darwin arm64
ifneq ($(ARCH),arm64)
CPUFEATURES ?= yes
endif
endif
ifeq ($(UNAME), OpenBSD)
CPPFLAGS += -DMISSING_NANOSLEEP
DUMP1090_CPPFLAGS += -DMISSING_NANOSLEEP
COMPAT += compat/clock_nanosleep/clock_nanosleep.o
LIBS_USB += -lusb-1.0
LIBS_CURSES := -lncurses
endif
ifeq ($(UNAME), FreeBSD)
CPPFLAGS += -D_DEFAULT_SOURCE
DUMP1090_CPPFLAGS += -D_DEFAULT_SOURCE
LIBS += -lrt
LIBS_USB += -lusb
LIBS_CURSES := -lncurses
endif
ifeq ($(UNAME), NetBSD)
CFLAGS += -D_DEFAULT_SOURCE
DUMP1090_CPPFLAGS += -D_DEFAULT_SOURCE
LIBS += -lrt
LIBS_USB += -lusb-1.0
LIBS_CURSES := -lcurses
@ -86,7 +93,7 @@ CPUFEATURES ?= no
ifeq ($(CPUFEATURES),yes)
include Makefile.cpufeatures
CPPFLAGS += -DENABLE_CPUFEATURES -Icpu_features/include
DUMP1090_CPPFLAGS += -DENABLE_CPUFEATURES -Icpu_features/include
endif
RTLSDR ?= yes
@ -94,10 +101,10 @@ BLADERF ?= yes
ifeq ($(RTLSDR), yes)
SDR_OBJ += sdr_rtlsdr.o
CPPFLAGS += -DENABLE_RTLSDR
DUMP1090_CPPFLAGS += -DENABLE_RTLSDR
ifdef RTLSDR_PREFIX
CPPFLAGS += -I$(RTLSDR_PREFIX)/include
DUMP1090_CPPFLAGS += -I$(RTLSDR_PREFIX)/include
ifeq ($(STATIC), yes)
LIBS_SDR += -L$(RTLSDR_PREFIX)/lib -Wl,-Bstatic -lrtlsdr -Wl,-Bdynamic $(LIBS_USB)
else
@ -107,8 +114,11 @@ ifeq ($(RTLSDR), yes)
# some packaged .pc files are massively broken, try to handle it
# FreeBSD's librtlsdr.pc includes -std=gnu89 in cflags
# some linux librtlsdr packages return a bare -I/ with no path in --cflags
RTLSDR_CFLAGS := $(shell pkg-config --cflags librtlsdr)
CFLAGS += $(filter-out -std=%,$(RTLSDR_CFLAGS))
RTLSDR_CFLAGS := $(filter-out -std=%,$(RTLSDR_CFLAGS))
RTLSDR_CFLAGS := $(filter-out -I/,$(RTLSDR_CFLAGS))
DUMP1090_CFLAGS += $(RTLSDR_CFLAGS)
# some linux librtlsdr packages return a bare -L with no path in --libs
# which horribly confuses things because it eats the next option on the command line
@ -123,22 +133,22 @@ endif
ifeq ($(BLADERF), yes)
SDR_OBJ += sdr_bladerf.o
CPPFLAGS += -DENABLE_BLADERF
CFLAGS += $(shell pkg-config --cflags libbladeRF)
DUMP1090_CPPFLAGS += -DENABLE_BLADERF
DUMP1090_CFLAGS += $(shell pkg-config --cflags libbladeRF)
LIBS_SDR += $(shell pkg-config --libs libbladeRF)
endif
ifeq ($(HACKRF), yes)
SDR_OBJ += sdr_hackrf.o
CPPFLAGS += -DENABLE_HACKRF
CFLAGS += $(shell pkg-config --cflags libhackrf)
DUMP1090_CPPFLAGS += -DENABLE_HACKRF
DUMP1090_CFLAGS += $(shell pkg-config --cflags libhackrf)
LIBS_SDR += $(shell pkg-config --libs libhackrf)
endif
ifeq ($(LIMESDR), yes)
SDR_OBJ += sdr_limesdr.o
CPPFLAGS += -DENABLE_LIMESDR
CFLAGS += $(shell pkg-config --cflags LimeSuite)
DUMP1090_CPPFLAGS += -DENABLE_LIMESDR
DUMP1090_CFLAGS += $(shell pkg-config --cflags LimeSuite)
LIBS_SDR += $(shell pkg-config --libs LimeSuite)
endif
@ -147,36 +157,42 @@ endif
## starch (runtime DSP code selection) mix, architecture-specific
##
ARCH ?= $(shell uname -m)
ifneq ($(CPUFEATURES),yes)
# need to be able to detect CPU features at runtime to enable any non-standard compiler flags
STARCH_MIX := generic
CPPFLAGS += -DSTARCH_MIX_GENERIC
DUMP1090_CPPFLAGS += -DSTARCH_MIX_GENERIC
else
ifeq ($(ARCH),x86_64)
# AVX, AVX2
STARCH_MIX := x86
CPPFLAGS += -DSTARCH_MIX_X86
DUMP1090_CPPFLAGS += -DSTARCH_MIX_X86
else ifeq ($(findstring aarch,$(ARCH)),aarch)
STARCH_MIX := aarch64
DUMP1090_CPPFLAGS += -DSTARCH_MIX_AARCH64
else ifeq ($(findstring arm64,$(ARCH)),arm64)
# Apple calls this arm64, not aarch64
STARCH_MIX := aarch64
DUMP1090_CPPFLAGS += -DSTARCH_MIX_AARCH64
else ifeq ($(findstring arm,$(ARCH)),arm)
# ARMv7 NEON
STARCH_MIX := arm
CPPFLAGS += -DSTARCH_MIX_ARM
else ifeq ($(findstring aarch,$(ARCH)),aarch)
STARCH_MIX := aarch64
CPPFLAGS += -DSTARCH_MIX_AARCH64
DUMP1090_CPPFLAGS += -DSTARCH_MIX_ARM
else
STARCH_MIX := generic
CPPFLAGS += -DSTARCH_MIX_GENERIC
DUMP1090_CPPFLAGS += -DSTARCH_MIX_GENERIC
endif
endif
all: showconfig dump1090 view1090 starch-benchmark
STARCH_COMPILE := $(CC) $(CPPFLAGS) $(CFLAGS) -c
ALL_CCFLAGS := $(CPPFLAGS) $(DUMP1090_CPPFLAGS) $(CFLAGS) $(DUMP1090_CFLAGS)
STARCH_COMPILE := $(CC) $(ALL_CCFLAGS) -c
include dsp/generated/makefile.$(STARCH_MIX)
showconfig:
@echo "Building with:" >&2
@echo " Version string: $(DUMP1090_VERSION)" >&2
@echo " Architecture: $(ARCH)" >&2
@echo " DSP mix: $(STARCH_MIX)" >&2
@echo " RTLSDR support: $(RTLSDR)" >&2
@echo " BladeRF support: $(BLADERF)" >&2
@ -184,15 +200,15 @@ showconfig:
@echo " LimeSDR support: $(LIMESDR)" >&2
%.o: %.c *.h
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
$(CC) $(ALL_CCFLAGS) -c $< -o $@
dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o comm_b.o net_io.o crc.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o convert.o ais_charset.o $(SDR_OBJ) $(COMPAT) $(CPUFEATURES_OBJS) $(STARCH_OBJS)
dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o comm_b.o net_io.o crc.o demod_2400.o stats.o cpr.o icao_filter.o track.o util.o convert.o ais_charset.o adaptive.o $(SDR_OBJ) $(COMPAT) $(CPUFEATURES_OBJS) $(STARCH_OBJS)
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_SDR) $(LIBS_CURSES)
view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o comm_b.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o ais_charset.o $(COMPAT)
view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o comm_b.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o ais_charset.o sdr_stub.o $(COMPAT)
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBS_CURSES)
faup1090: faup1090.o anet.o mode_ac.o mode_s.o comm_b.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o ais_charset.o $(COMPAT)
faup1090: faup1090.o anet.o mode_ac.o mode_s.o comm_b.o net_io.o crc.o stats.o cpr.o icao_filter.o track.o util.o ais_charset.o sdr_stub.o $(COMPAT)
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS)
starch-benchmark: cpu.o dsp/helpers/tables.o $(CPUFEATURES_OBJS) $(STARCH_OBJS) $(STARCH_BENCHMARK_OBJ)
@ -205,25 +221,25 @@ test: cprtests
./cprtests
cprtests: cpr.o cprtests.o
$(CC) $(CPPFLAGS) $(CFLAGS) -g -o $@ $^ -lm
$(CC) $(ALL_CCFLAGS) -g -o $@ $^ -lm
crctests: crc.c crc.h
$(CC) $(CPPFLAGS) $(CFLAGS) -g -DCRCDEBUG -o $@ $<
$(CC) $(ALL_CCFLAGS) -g -DCRCDEBUG -o $@ $<
benchmarks: oneoff/convert_benchmark
oneoff/convert_benchmark
oneoff/convert_benchmark: oneoff/convert_benchmark.o convert.o util.o dsp/helpers/tables.o cpu.o $(CPUFEATURES_OBJS) $(STARCH_OBJS)
$(CC) $(CPPFLAGS) $(CFLAGS) -g -o $@ $^ -lm -lpthread
$(CC) $(ALL_CCFLAGS) -g -o $@ $^ -lm -lpthread
oneoff/decode_comm_b: oneoff/decode_comm_b.o comm_b.o ais_charset.o
$(CC) $(CPPFLAGS) $(CFLAGS) -g -o $@ $^ -lm
$(CC) $(ALL_CCFLAGS) -g -o $@ $^ -lm
oneoff/dsp_error_measurement: oneoff/dsp_error_measurement.o dsp/helpers/tables.o cpu.o $(CPUFEATURES_OBJS) $(STARCH_OBJS)
$(CC) $(CPPFLAGS) $(CFLAGS) -g -o $@ $^ -lm
$(CC) $(ALL_CCFLAGS) -g -o $@ $^ -lm
oneoff/uc8_capture_stats: oneoff/uc8_capture_stats.o
$(CC) $(CPPFLAGS) $(CFLAGS) -g -o $@ $^ -lm
$(CC) $(ALL_CCFLAGS) -g -o $@ $^ -lm
starchgen:
dsp/starchgen.py .

View File

@ -2,22 +2,28 @@
# cmake integration is a little tricky, so let's do this by hand for now
CPUFEATURES_UNAME := $(shell uname)
CPUFEATURES_ARCH := $(shell uname -m)
CPUFEATURES_UNAME ?= $(UNAME)
CPUFEATURES_ARCH ?= $(ARCH)
CPUFEATURES_CFLAGS ?= $(CFLAGS)
CPUFEATURES_OBJS := cpu_features/src/filesystem.o cpu_features/src/stack_line_reader.o cpu_features/src/string_view.o
CPUFEATURES_CFLAGS := -std=c99 -O -g -DSTACK_LINE_READER_BUFFER_SIZE=1024 -DNDEBUG
CPUFEATURES_EXTRA_CFLAGS := -std=c99
CPUFEATURES_EXTRA_CPPFLAGS := -DSTACK_LINE_READER_BUFFER_SIZE=1024 -DNDEBUG -Icpu_features/include
ifeq ($(CPUFEATURES_UNAME),Linux)
CPUFEATURES_OBJS += cpu_features/src/hwcaps.o
CPUFEATURES_CFLAGS += -DHAVE_STRONG_GETAUXVAL
CPUFEATURES_EXTRA_CPPFLAGS += -DHAVE_STRONG_GETAUXVAL
endif
ifeq ($(CPUFEATURES_UNAME),Darwin)
CPUFEATURES_CFLAGS += -DHAVE_SYSCTLBYNAME
CPUFEATURES_EXTRA_CPPFLAGS += -DHAVE_SYSCTLBYNAME
endif
ifeq ($(CPUFEATURES_ARCH), x86_64)
ifeq ($(CPUFEATURES_ARCH),x86_64)
CPUFEATURES_OBJS += cpu_features/src/cpuinfo_x86.o
endif
ifneq (,$(filter i%86,$(CPUFEATURES_ARCH)))
CPUFEATURES_OBJS += cpu_features/src/cpuinfo_x86.o
endif
@ -29,5 +35,4 @@ ifneq (,$(findstring aarch64,$(CPUFEATURES_ARCH)))
CPUFEATURES_OBJS += cpu_features/src/cpuinfo_aarch64.o
endif
$(CPUFEATURES_OBJS): override CFLAGS := $(CPUFEATURES_CFLAGS)
$(CPUFEATURES_OBJS): override CPPFLAGS := -Icpu_features/include
$(CPUFEATURES_OBJS): override ALL_CCFLAGS := $(CPUFEATURES_CPPFLAGS) $(CPUFEATURES_EXTRA_CPPFLAGS) $(CPUFEATURES_CFLAGS) $(CPUFEATURES_EXTRA_CFLAGS)

191
README.adaptive-gain.md Normal file
View File

@ -0,0 +1,191 @@
# Adaptive gain configuration
dump1090-fa can optionally tune the receiver gain automatically to try to
pick a gain value for the particular hardware and RF environment without
manual tuning. This README covers some of the background for why this is
useful, and how to configure dump1090 to enable this feature.
## Background
In general, more receiver gain means better reception. Most ADS-B transmitters
within line of sight transmit with enough power that their messages can
potentially be decoded, but if the receiver gain setting is too low then very
weak signals may still be too weak to be decoded even after amplification.
Adding extra receiver gain helps in this case.
However, there are two problems with just adding more gain to a wideband SDR.
First, _everything_ is amplified, not only the signals of interest. Noise and
RF interference is also amplified. At high gain settings or in noisy RF
environments, this can interfere with receiving the ADS-B signals themselves.
Second, there is a wide range of possible ADS-B signal strengths. There can be
a 60dB or more difference between the weakest signals (a distant aircraft at
the limit of receiver range) and the strongest signals (a nearby aircraft on
the ramp 100m from the receiver). Increasing the receiver gain to handle the
weakest signals can mean that the strongest signals overload the receiver.
A rtlsdr receiver only has about 30-35dB of dynamic range available at a
particular gain setting, so there is no single gain setting that can
simultaneously handle both the weakest and strongest signals.
Adaptive gain tries to deal with these cases by changing the receiver gain
on the fly to handle the signal and noise levels that are currently being seen
without human intervention. It is not perfect and it's not a substitute for
hand-tuning of gain settings, but it aims at picking a reasonable setting for
cases where individual hand-tuning isn't possible.
## Where to configure adaptive gain options
How to configure adaptive gain varies depending on how you have installed
dump1090.
If you are using a PiAware sdcard image, adaptive gain can be configured by
editing `/boot/piaware-config.txt` or by using the `piaware-config` command.
If you are using the Debian package, adaptive gain can be configured by editing
`/etc/default/dump1090-fa`.
If running dump1090 directly, adaptive gain options are set directly by
command-line options.
## Default settings
For new PiAware or Debian package installations, adaptive dynamic range mode
is enabled by default and adaptive burst mode is disabled by default.
For _upgrades_ of PiAware or the Debian package from versions older than 6.0,
both adaptive gain modes are disabled by default.
These defaults can be overridden as described below.
## Adaptive gain in dynamic range mode
The dynamic range adaptive gain mode attempts to set the receiver gain to
maintain a given dynamic range - that is, it tries to set the gain so that
general noise is at or below a given level. This takes into account different
or changing RF environments and different receiver hardware (antenna,
preamplifiers, etc) that affects the overall gain of the system, and usually
will pick a reasonable gain setting without intervention.
To enable this mode:
* Set `adaptive-dynamic-range yes` in piaware-config; or
* Set `ADAPTIVE_DYNAMIC_RANGE=yes` in `/etc/default/dump1090-fa`; or
* Pass the `--adaptive-range` option on the command line.
The default settings for dynamic range will use a dynamic range target chosen
based on SDR type (e.g. 30dB for rtlsdr receivers). This is usually a good
default. To override this target:
* Set `adaptive-dynamic-range-target` in piaware-config; or
* Set `ADAPTIVE_DYNAMIC_RANGE_TARGET` in `/etc/default/dump1090-fa`; or
* Pass the `--adaptive-range-target` option on the command line.
## Adaptive gain in "burst" / loud signal mode
The "burst" adaptive gain mode listens for loud bursts of signal that were
_not_ successfully decoded as ADS-B messages, but which have approximately
the right timing to be possible messages that were lost due to receiver
overloading. When enough overly-loud signals are heard in a short period of
time, dump1090 will _reduce_ the receiver gain to try to allow them to be
received.
This is a more situational setting. It may allow reception of loud nearby
aircraft (e.g. if you are close to an airport). The tradeoff is that when
there are nearby aircraft, overall receiver range may be reduced. Whether
this is a good tradeoff depends on the aircraft you're interested in.
By default, adaptive gain burst mode is disabled.
To enable burst mode:
* Set `adaptive-burst yes` in piaware-config; or
* Set `ADAPTIVE_BURST=yes` in `/etc/default/dump1090-fa`; or
* Pass the `--adaptive-burst` option on the command line.
This mode is more experimental than the dynamic range mode and tweaking of
the advanced burst options may be needed depending on your local installation.
In particular, `--adaptive-burst-loud-rate` and `adaptive-burst-quiet-rate`
may need adjusting. Feedback on what works for you and what doesn't would
be appreciated!
Burst mode and dynamic range mode can be enabled at the same time.
## Limiting the gain range
If you know in advance approximately what the gain setting should be, so
you want to allow adaptive gain to change the gain only within a certain range,
you can set minimum and maximum gain settings in dB. Adaptive gain will only
adjust the gain within this range. To set this:
* Set `adaptive-min-gain` and `adaptive-max-gain` in piaware-config; or
* Set `ADAPTIVE_MIN_GAIN` and `ADAPTIVE_MAX_GAIN` in `/etc/default/dump1090-fa`; or
* Pass the `--adaptive-min-gain` and `--adaptive-max-gain` options on the command line.
If you know approximately where the gain should be, then a good starting point would be
to set the max and min adaptive gain to +/- 10dB around your gain setting.
## Reducing the CPU cost of adaptive gain
The measurements needed to adjust gain have a CPU cost, and on slower
devices it may be useful to reduce the amount of work that adaptive gain does.
This can be done by adjusting the adaptive gain duty cycle. This is a
percentage that controls what fraction of incoming data adaptive gain inspects.
100% means that every sample is inspected. Lower values reduce CPU use, with
a tradeoff that adaptive gain has a less accurate picture of the RF
environment. The default duty cycle is 50% on "fast" CPUs and 10% on "slow"
CPUs (where currently "slow" means "armv6 architecture", for example the
Pi Zero or Pi 1). To reduce the duty cycle further:
* Set `slow-cpu yes` in piaware-config; or
* Set `SLOW_CPU=yes` in `/etc/default/dump1090-fa`; or
* Pass the `--adaptive-duty-cycle` option on the command line
## Advanced options
There are a number of advanced options that are only supported as
command-line options or via the EXTRA_OPTIONS setting in
`/etc/default/dump1090-fa`. They tweak settings that require some knowledge of
dump1090 internals to make sense of, so YMMV.
For a complete list of options, run `dump1090-fa --help` and look at the
adaptive gain section.
## Device support
Currently, adaptive gain is only supported on rtlsdr devices. Support for other
SDRs is planned for the future.
If you're a developer and want to add support for your SDR, you'll need
to implement the gain control API used in `sdr.[ch]`. See `sdr_rtlsdr.c`
(`rtlsdrGetGain`, `rtlsdrSetGain`, etc) for examples.
## Comparison with wiedehopf's auto-gain scripts
There is an [auto-gain script](https://github.com/wiedehopf/adsb-scripts/wiki/Automatic-gain-optimization-for-readsb-and-dump1090-fa)
written by [wiedehopf](https://github.com/wiedehopf) that aims to solve similar
problems. The implementation approaches are quite different, and which one works best
for you will depend on the problem you're trying to solve.
The major differences between adaptive gain and the auto-gain script are:
* Adaptive gain works on short-term data (seconds or minutes) and can react to
changes in a similar timeframe. It tries to set an appropriate gain for the
_current_ environment without much regard for longer-term trends. The auto-gain
script looks at data over longer timeframes (1+ days) and reacts more slowly,
but takes into account data across that whole period.
* Adaptive gain in dynamic range mode looks at an estimate of the noise floor
to decide whether to increase gain. Adaptive gain in burst mode looks at the
signal strength of samples that were _not_ successfully decoded to decide when
to reduce gain. The auto-gain scripts look at the signal strength of _successfully_
decoded messages to decide when to increase or decrease gain. These are each measuring
something different, and which is most effective will depend on the exact RF
environment.
* Adaptive gain burst mode and the auto-gain script superfically measure similar things,
but the difference in measurement timeframe mean they react quite differently. Burst mode
tries to react to transient loud signals i.e. temporarily nearby aircraft. The auto-gain
script uses the loud-messages fraction as an estimate for messages lost to excessive
gain over the long term, not only the transient case.
* Adaptive gain has a few less moving parts (i.e. no external scripts, no config changes over
time) as it's built directly into dump1090 itself.

View File

@ -1,15 +1,3 @@
# EOSS Specific Changes
The `eoss` branch adds the following changes/features so that the dump1090-fa release can work relatively seamlessly on the EOSS SDR system:
- Updates to the web display and the underlying Javascript to Redirect the map source to the locally running OSM map server. This way map tiles are fetched from the local system and removes the need for a dedicated Internet connection.
- Apache is used as the web server of choice instead of lighttpd. The Apache configuration is modified to use an alias (/dump1090-fa) pointing to the /user/share/dump1090-fa/html directory.
- Parameter changes for the dump1090-fa daemon so that it looks to use only those RTL-SDR USB dongles that have been labeled with "ADSB" as their serial number. This allows
multiple RTL-SDR dongles to be used on the SDR system (dump1090-fa uses one while the SDR system can use another).
For detailed instructions and build info for the `eoss` branch, please see the [EOSS-Install-dump1090](https://github.com/TheKoola/eosstracker/blob/master/doc/EOSS-Install-dump1090.md) under the eosstracker project.
# dump1090-fa Debian/Raspbian packages
dump1090-fa is a ADS-B, Mode S, and Mode 3A/3C demodulator and decoder that
@ -28,49 +16,19 @@ it can be used to contribute crowd-sourced flight tracking data to FlightAware.
It is designed to build as a Debian package, but should also be buildable on
many other Linux or Unix-like systems.
## Building under buster
## Building under bullseye, buster, or stretch
```bash
$ sudo apt-get install build-essential fakeroot debhelper librtlsdr-dev pkg-config dh-systemd libncurses5-dev libbladerf-dev libhackrf-dev liblimesuite-dev
$ sudo apt-get install build-essential fakeroot debhelper librtlsdr-dev pkg-config libncurses5-dev libbladerf-dev libhackrf-dev liblimesuite-dev
$ ./prepare-build.sh bullseye # or buster, or stretch
$ cd package-bullseye # or buster, or stretch
$ dpkg-buildpackage -b --no-sign
```
## Building under stretch
```bash
$ sudo apt-get install build-essential debhelper librtlsdr-dev pkg-config dh-systemd libncurses5-dev libbladerf-dev
$ dpkg-buildpackage -b --no-sign
```
## Building under jessie
### Dependencies - bladeRF
You will need a build of libbladeRF. You can build packages from source:
```bash
$ git clone https://github.com/Nuand/bladeRF.git
$ cd bladeRF
$ git checkout 2017.12-rc1
$ dpkg-buildpackage -b
```
Or Nuand has some build/install instructions including an Ubuntu PPA
at https://github.com/Nuand/bladeRF/wiki/Getting-Started:-Linux
Or FlightAware provides armhf packages as part of the piaware repository;
see https://flightaware.com/adsb/piaware/install
### Dependencies - rtlsdr
This is packaged with jessie. `sudo apt-get install librtlsdr-dev`
### Actually building it
Nothing special, just build it (`dpkg-buildpackage -b`)
## Building with limited dependencies
(Supported for bullseye and buster builds only)
The package supports some build profiles to allow building without all
required SDR libraries being present. This will produce a package with
limited SDR support only.

645
adaptive.c Normal file
View File

@ -0,0 +1,645 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// adaptive.c: adaptive gain control
//
// Copyright (c) 2021 FlightAware, LLC
//
// This file is free software: you may copy, redistribute 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 file 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/>.
#include "dump1090.h"
#include "adaptive.h"
//
// gain limits
//
static int adaptive_gain_min;
static int adaptive_gain_max;
// gain steps relative to current gain
static float adaptive_gain_up_db;
static float adaptive_gain_down_db;
//
// block handling
//
// 1 block = approx 1 second of samples. Control updates are done at the end of each block only.
// Each block is made up of an integer number of subblocks (currently 20)
//
// 1 subblock = approx 50ms of samples. Duty cycle decisions are made at the subblock level;
// either the whole subblock is processed, or the whole subblock is skipped.
// Each subblock is made up of an integer number of windows (currently 1250)
//
// 1 window = approx 40us of samples. Burst measurements are made by counting samples within each window.
//
// All three levels are aligned, i.e. every block boundary is also a subblock boundary;
// every subblock boundary is also a window boundary.
static const unsigned adaptive_subblocks_per_block = 20; // subblocks per block
static unsigned adaptive_subblocks_remaining; // subblocks remaining in the current block
// Duty cycle is expressed as N/D
// where N = adaptive_subblbock_dutycycle_N = adaptive_subblocks_per_block * Modes.adaptive_duty_cycle
// and D = adaptive_subblocks_dutycycle_D = adaptive_subblocks_per_block
//
// i.e. within each block, there are exactly N active subblocks out of D total subblocks
//
// The active subblocks are distributed evenly across the block by increasing a counter by N on each
// subblock, modulo D, and marking the subblock as active each time the counter rolls over.
static unsigned adaptive_subblock_dutycycle_N; // subblock duty cycle numerator N
// stretch gcc doesn't like this as a separate const
#define adaptive_subblock_dutycycle_D adaptive_subblocks_per_block
static unsigned adaptive_subblock_dutycycle_counter; // subblock duty cycle counter (modulo D)
static bool adaptive_subblock_active; // is the current subblock active i.e. samples should be processed, not skipped?
static unsigned adaptive_samples_per_subblock; // samples per subblock
static unsigned adaptive_subblock_samples_remaining; // samples remaining in the current subblock
static unsigned adaptive_samples_per_window; // samples per window
void adaptive_init();
void adaptive_update(uint16_t *buf, unsigned length, struct modesMessage *decoded);
static void adaptive_update_subblock(uint16_t *buf, unsigned length, struct modesMessage *decoded);
static void adaptive_end_of_block();
static void adaptive_control_update();
//
// burst handling
//
static unsigned adaptive_burst_window_remaining; // samples remaining in the current burst window
static unsigned adaptive_burst_window_counter; // loud samples seen in current burst window
static unsigned adaptive_burst_runlength; // consecutive loud burst windows seen
static unsigned adaptive_burst_block_loud_undecoded; // loud undecoded bursts seen in this block so far
static unsigned adaptive_burst_block_loud_decoded; // loud decoded messages seen in this block so far
static double adaptive_burst_loud_undecoded_smoothed; // smoothed rate of loud misdecodes per block
static double adaptive_burst_loud_decoded_smoothed; // smoothed rate of loud successful decodes per block
static unsigned adaptive_burst_change_timer; // countdown inhibiting control after changing gain
static double adaptive_burst_loud_threshold; // current signal level threshold for a "loud decode"
static unsigned adaptive_burst_loud_blocks = 0; // consecutive blocks with loud rate
static unsigned adaptive_burst_quiet_blocks = 0; // consecutive blocks with quiet rate
static void adaptive_burst_update(uint16_t *buf, unsigned length);
static void adaptive_burst_skip(unsigned length);
static unsigned adaptive_burst_count_samples(uint16_t *buf, unsigned n);
static void adaptive_burst_scan_windows(uint16_t *buf, unsigned windows);
static void adaptive_burst_end_of_window(unsigned counter);
static void adaptive_burst_end_of_block();
//
// noise floor measurement (adaptive dynamic range)
//
static unsigned *adaptive_range_radix; // radix-sort buckets for current block
static unsigned adaptive_range_radix_counter; // sum of all radix-sort buckets (= number of samples sorted)
static double adaptive_range_smoothed; // smoothed noise floor estimate, dBFS
static enum { RANGE_SCAN_IDLE, RANGE_SCAN_UP, RANGE_SCAN_DOWN, RANGE_RESCAN_UP, RANGE_RESCAN_DOWN } adaptive_range_state = RANGE_SCAN_UP;
static unsigned adaptive_range_change_timer; // countdown inhibiting control after changing gain
static unsigned adaptive_range_rescan_timer; // countdown to next upwards gain reprobe
static int adaptive_range_gain_limit; // probed maximum gain step with acceptable dynamic range
static void adaptive_range_update(uint16_t *buf, unsigned length);
static void adaptive_range_end_of_block();
// Try to change the SDR gain to 'step' and tell the user about it,
// with 'why' as the reason to show. Return true if the gain actually changed.
static bool adaptive_set_gain(int step, const char *why)
{
if (step < adaptive_gain_min)
step = adaptive_gain_min;
if (step > adaptive_gain_max)
step = adaptive_gain_max;
int current_gain = sdrGetGain();
if (current_gain == step)
return false;
fprintf(stderr, "adaptive: changing gain from %.1fdB (step %d) to %.1fdB (step %d) because: %s\n",
sdrGetGainDb(current_gain), current_gain, sdrGetGainDb(step), step, why);
int new_gain = sdrSetGain(step);
bool changed = (current_gain != new_gain);
if (changed)
++Modes.stats_current.adaptive_gain_changes;
return changed;
}
// Update internal state to reflect a gain change
// (usually after adaptive_set_gain returns true, but also called during init)
static void adaptive_gain_changed()
{
int new_gain = sdrGetGain();
adaptive_gain_up_db = sdrGetGainDb(new_gain + 1) - sdrGetGainDb(new_gain);
adaptive_gain_down_db = sdrGetGainDb(new_gain) - sdrGetGainDb(new_gain - 1);
double loud_threshold_dbfs = 0 - adaptive_gain_up_db - 3.0;
adaptive_burst_loud_threshold = pow(10, loud_threshold_dbfs / 10.0);
adaptive_range_change_timer = Modes.adaptive_range_change_delay;
adaptive_burst_change_timer = Modes.adaptive_burst_change_delay;
adaptive_burst_loud_blocks = 0;
adaptive_burst_quiet_blocks = 0;
}
// External init entry point
void adaptive_init()
{
int maxgain = sdrGetMaxGain();
// If the SDR doesn't support gain control, disable ourselves
if (maxgain < 0) {
if (Modes.adaptive_burst_control || Modes.adaptive_range_control) {
fprintf(stderr, "warning: adaptive gain control requested, but SDR gain control not available, ignored.\n");
}
Modes.adaptive_burst_control = false;
Modes.adaptive_range_control = false;
}
// If we're disabled, do nothing
if (!Modes.adaptive_burst_control && !Modes.adaptive_range_control)
return;
// Set up window, subblock, and block sizes
// Look for 40us bursts
adaptive_samples_per_window = Modes.sample_rate / 25000;
// Use ~50ms subblocks; ensure it's an exact multiple of window size
adaptive_samples_per_subblock = adaptive_samples_per_window * 1250;
adaptive_subblocks_remaining = adaptive_subblocks_per_block;
adaptive_subblock_samples_remaining = adaptive_samples_per_subblock;
adaptive_subblock_active = false;
float N = roundf(adaptive_subblock_dutycycle_D * Modes.adaptive_duty_cycle);
if (N <= 0)
N = 1;
if (N > adaptive_subblock_dutycycle_D)
N = adaptive_subblock_dutycycle_D;
fprintf(stderr, "adaptive: using %.0f%% duty cycle\n", 100.0 * N / adaptive_subblock_dutycycle_D);
adaptive_subblock_dutycycle_N = (unsigned)N;
adaptive_burst_window_remaining = adaptive_samples_per_window;
adaptive_burst_window_counter = 0;
adaptive_range_radix = calloc(sizeof(unsigned), 65536);
adaptive_range_state = RANGE_RESCAN_UP;
// select and enforce gain limits
for (adaptive_gain_min = 0; adaptive_gain_min < maxgain; ++adaptive_gain_min) {
if (sdrGetGainDb(adaptive_gain_min) >= Modes.adaptive_min_gain_db)
break;
}
for (adaptive_gain_max = maxgain; adaptive_gain_max > adaptive_gain_min; --adaptive_gain_max) {
if (sdrGetGainDb(adaptive_gain_max) <= Modes.adaptive_max_gain_db)
break;
}
fprintf(stderr, "adaptive: enabled adaptive gain control with gain limits %.1fdB (step %d) .. %.1fdB (step %d)\n",
sdrGetGainDb(adaptive_gain_min), adaptive_gain_min, sdrGetGainDb(adaptive_gain_max), adaptive_gain_max);
if (Modes.adaptive_range_control)
fprintf(stderr, "adaptive: enabled dynamic range control, target dynamic range %.1fdB\n", Modes.adaptive_range_target);
if (Modes.adaptive_burst_control)
fprintf(stderr, "adaptive: enabled burst control\n");
adaptive_set_gain(sdrGetGain(), "constraining gain to adaptive gain limits");
adaptive_gain_changed();
adaptive_range_gain_limit = sdrGetGain();
}
// Feed some samples into the adaptive system. Any number of samples might be passed in.
void adaptive_update(uint16_t *buf, unsigned length, struct modesMessage *decoded)
{
if (!Modes.adaptive_burst_control && !Modes.adaptive_range_control)
return;
// process complete subblocks
while (length >= adaptive_subblock_samples_remaining) {
if (adaptive_subblock_active)
adaptive_update_subblock(buf, adaptive_subblock_samples_remaining, decoded);
buf += adaptive_subblock_samples_remaining;
length -= adaptive_subblock_samples_remaining;
adaptive_subblock_samples_remaining = adaptive_samples_per_subblock;
adaptive_subblock_dutycycle_counter += adaptive_subblock_dutycycle_N;
if (adaptive_subblock_dutycycle_counter >= adaptive_subblock_dutycycle_D) {
adaptive_subblock_dutycycle_counter -= adaptive_subblock_dutycycle_D;
adaptive_subblock_active = true;
} else {
adaptive_subblock_active = false;
// fake a quiet window to reset any existing run
adaptive_burst_end_of_window(0);
}
if (!--adaptive_subblocks_remaining) {
// Block completed, do a control update
adaptive_subblocks_remaining = adaptive_subblocks_per_block;
adaptive_end_of_block();
}
}
// process final samples that don't complete a subblock
if (length > 0) {
if (adaptive_subblock_active)
adaptive_update_subblock(buf, length, decoded);
adaptive_subblock_samples_remaining -= length;
}
}
// Feed some samples into the adaptive system. The samples are guaranteed to not cross a subblock boundary.
// The samples should be processsed (i.e. duty cycle is in the active part)
static void adaptive_update_subblock(uint16_t *buf, unsigned length, struct modesMessage *decoded)
{
if (decoded) {
if (/* decoded->msgbits == 112 && */ decoded->signalLevel >= adaptive_burst_loud_threshold)
++adaptive_burst_block_loud_decoded;
adaptive_burst_skip(length);
} else {
adaptive_burst_update(buf, length);
adaptive_range_update(buf, length);
}
}
// Burst measurement: ignore the next 'length' samples (they are a successfully decoded message)
static void adaptive_burst_skip(unsigned length)
{
if (!Modes.adaptive_burst_control)
return;
// first window
if (length < adaptive_burst_window_remaining) {
// partial fill
adaptive_burst_window_remaining -= length;
return;
}
// skip remainder of first window, dispatch it
adaptive_burst_end_of_window(adaptive_burst_window_counter);
length -= adaptive_burst_window_remaining;
// skip remaining windows, dispatch them
unsigned windows = length / adaptive_samples_per_window;
unsigned samples = windows * adaptive_samples_per_window;
while (windows--)
adaptive_burst_end_of_window(0);
length -= samples;
// final partial window
adaptive_burst_window_counter = 0;
adaptive_burst_window_remaining = adaptive_samples_per_window - length;
}
// Burst measurement: process 'length' samples from 'buf', look for loud bursts;
// the samples might cross burst window boundaries;
// the samples will not cross a block boundary.
static void adaptive_burst_update(uint16_t *buf, unsigned length)
{
if (!Modes.adaptive_burst_control)
return;
// first window
if (length < adaptive_burst_window_remaining) {
// partial fill
adaptive_burst_window_counter += adaptive_burst_count_samples(buf, length);
adaptive_burst_window_remaining -= length;
return;
}
// complete fill of first partial window
unsigned n = adaptive_burst_window_remaining;
unsigned counter = adaptive_burst_window_counter + adaptive_burst_count_samples(buf, n);
adaptive_burst_end_of_window(counter);
buf += n;
length -= n;
// remaining windows
unsigned windows = length / adaptive_samples_per_window;
unsigned samples = windows * adaptive_samples_per_window;
adaptive_burst_scan_windows(buf, windows);
buf += samples;
length -= samples;
// final partial window
adaptive_burst_window_counter = adaptive_burst_count_samples(buf, length);
adaptive_burst_window_remaining = adaptive_samples_per_window - length;
}
// Burst measurement: process 'windows' complete burst windows starting at 'buf';
// 'buf' is aligned to the start of a burst window
static void adaptive_burst_scan_windows(uint16_t *buf, unsigned windows)
{
while (windows--) {
unsigned counter = adaptive_burst_count_samples(buf, adaptive_samples_per_window);
buf += adaptive_samples_per_window;
adaptive_burst_end_of_window(counter);
}
}
// Burst measurement: process 'n' samples from 'buf', look for loud samples;
// the samples are guaranteed not to cross window boundaries;
// return the number of loud samples seen
static inline unsigned adaptive_burst_count_samples(uint16_t *buf, unsigned n)
{
unsigned counter;
starch_count_above_u16(buf, n, 46395 /* -3dBFS */, &counter);
return counter;
}
// Burst measurement: we reached the end of a burst window with 'counter'
// loud samples seen, handle that window.
static void adaptive_burst_end_of_window(unsigned counter)
{
if (counter > adaptive_samples_per_window / 4) {
// This window is loud, extend any existing run of loud windows
++adaptive_burst_runlength;
} else {
// Quiet window. If we saw a run of loud windows >= 80us long, count
// that as a candidate for an over-amplified message that was
// not decoded.
if (adaptive_burst_runlength >= 2 && adaptive_burst_runlength <= 5)
++adaptive_burst_block_loud_undecoded;
adaptive_burst_runlength = 0;
}
}
// Noise measurement: process 'length' samples from 'buf'.
// The samples will not cross a block boundary.
static void adaptive_range_update(uint16_t *buf, unsigned length)
{
if (!Modes.adaptive_range_control)
return;
adaptive_range_radix_counter += length;
while (length--) {
// do a very simple radix sort of sample magnitudes
// so we can later find the Nth percentile value
++adaptive_range_radix[buf[0]];
++buf;
}
}
// Noise measurement: we reached the end of a block, update
// our noise estimate
static void adaptive_range_end_of_block()
{
if (!Modes.adaptive_range_control)
return;
unsigned n = 0, i = 0;
// measure Nth percentile magnitude
unsigned count_n = adaptive_range_radix_counter * Modes.adaptive_range_percentile / 100;
while (i < 65536 && n <= count_n)
n += adaptive_range_radix[i++];
uint16_t percentile_n = i - 1;
// maintain an EMA of the Nth percentile
adaptive_range_smoothed = adaptive_range_smoothed * (1 - Modes.adaptive_range_alpha) + percentile_n * Modes.adaptive_range_alpha;
// .. report to stats in dBFS
if (adaptive_range_smoothed > 0) {
Modes.stats_current.adaptive_noise_dbfs = 20 * log10(adaptive_range_smoothed / 65536.0);
} else {
Modes.stats_current.adaptive_noise_dbfs = 0;
}
// reset radix sort for the next block
memset(adaptive_range_radix, 0, 65536 * sizeof(unsigned));
adaptive_range_radix_counter = 0;
}
// Burst measurement: we reached the end of a block, update our burst rate estimate
static void adaptive_burst_end_of_block()
{
if (!Modes.adaptive_burst_control)
return;
// scale rates based on the actual duty cycle fraction
// (e.g. if we are only inspecting 2/5 of samples, then scale the rate by 5/2)
double scale = (double)adaptive_subblock_dutycycle_D / adaptive_subblock_dutycycle_N;
// maintain an EMA of the number of undecoded loud bursts seen per block
Modes.stats_current.adaptive_loud_undecoded += adaptive_burst_block_loud_undecoded;
adaptive_burst_loud_undecoded_smoothed = adaptive_burst_loud_undecoded_smoothed * (1 - Modes.adaptive_burst_alpha) + scale * adaptive_burst_block_loud_undecoded * Modes.adaptive_burst_alpha;
adaptive_burst_block_loud_undecoded = 0;
// maintain an EMA of the number of decoded, but loud, messages seen per block
Modes.stats_current.adaptive_loud_decoded += adaptive_burst_block_loud_decoded;
adaptive_burst_loud_decoded_smoothed = adaptive_burst_loud_decoded_smoothed * (1 - Modes.adaptive_burst_alpha) + scale * adaptive_burst_block_loud_decoded * Modes.adaptive_burst_alpha;
adaptive_burst_block_loud_decoded = 0;
}
void flush_stats(uint64_t now);
static void adaptive_increase_gain(const char *why)
{
if (adaptive_set_gain(sdrGetGain() + 1, why))
adaptive_gain_changed();
}
static void adaptive_decrease_gain(const char *why)
{
if (adaptive_set_gain(sdrGetGain() - 1, why))
adaptive_gain_changed();
}
// Adaptive gain: we reached a block boundary. Update measurements and act on them.
static void adaptive_end_of_block()
{
adaptive_range_end_of_block();
adaptive_burst_end_of_block();
adaptive_control_update();
Modes.stats_current.adaptive_valid = true;
unsigned current = Modes.stats_current.adaptive_gain = sdrGetGain();
Modes.stats_current.adaptive_range_gain_limit = adaptive_range_gain_limit;
++Modes.stats_current.adaptive_gain_seconds[current < STATS_GAIN_COUNT ? current : STATS_GAIN_COUNT-1];
}
static void adaptive_control_update()
{
// votes for what to do with the gain
// "gain_not_up" overlaps somewhat with "gain_down", but they are not identical;
// burst control may want to prevent gain from increasing, but not necessarily
// decrease gain.
bool gain_up = false;
const char *gain_up_reason = NULL;
bool gain_down = false;
const char *gain_down_reason = NULL;
bool gain_not_up = false;
int current_gain = sdrGetGain();
if (adaptive_burst_change_timer)
--adaptive_burst_change_timer;
if (adaptive_range_change_timer > 0)
--adaptive_range_change_timer;
if (adaptive_range_rescan_timer > 0)
--adaptive_range_rescan_timer;
if (Modes.adaptive_burst_control && !adaptive_burst_change_timer) {
if (adaptive_burst_loud_undecoded_smoothed > Modes.adaptive_burst_loud_rate) {
adaptive_burst_quiet_blocks = 0;
++adaptive_burst_loud_blocks;
} else if (adaptive_burst_loud_decoded_smoothed < Modes.adaptive_burst_quiet_rate) {
adaptive_burst_loud_blocks = 0;
++adaptive_burst_quiet_blocks;
} else {
adaptive_burst_loud_blocks = 0;
adaptive_burst_quiet_blocks = 0;
}
if (adaptive_burst_loud_blocks >= Modes.adaptive_burst_loud_runlength) {
// we need to reduce gain (further)
gain_down = gain_not_up = true;
gain_down_reason = "high rate of loud undecoded messages";
// if we're currently doing a downward scan, reducing gain further may confuse it;
// stop that scan and restart it once we are no longer in a reduced-gain state
if (adaptive_range_state == RANGE_SCAN_DOWN || adaptive_range_state == RANGE_RESCAN_DOWN) {
adaptive_range_state = RANGE_SCAN_IDLE;
adaptive_range_rescan_timer = 0;
}
} else if (adaptive_burst_quiet_blocks < Modes.adaptive_burst_quiet_runlength) {
// we're OK at the current gain, but should not increase it
gain_not_up = true;
} else if (current_gain < adaptive_range_gain_limit) {
// we're OK at the current gain, and can increase gain to the previously discovered
// dynamic range limit
gain_up = true;
gain_up_reason = "low loud message rate and gain below dynamic range limit";
}
}
if (Modes.adaptive_range_control && !adaptive_range_change_timer) {
float available_range = -20 * log10(adaptive_range_smoothed / 65536.0);
// allow the gain limit to increase if this gain setting is acceptable
// (decreasing the limit is done separately depending on the current state as we make slightly different decisions in IDLE
// to provide hysteresis)
if (available_range >= Modes.adaptive_range_target && current_gain > adaptive_range_gain_limit) {
adaptive_range_gain_limit = current_gain;
}
switch (adaptive_range_state) {
case RANGE_SCAN_UP:
case RANGE_RESCAN_UP:
if (available_range < Modes.adaptive_range_target) {
// Current gain fails to meet our target. Switch to downward scanning.
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) < required dynamic range (%.1fdB), switching to downward scan\n", available_range, Modes.adaptive_range_target);
gain_down = gain_not_up = true;
gain_down_reason = "probing dynamic range gain lower bound";
adaptive_range_state = (adaptive_range_state == RANGE_RESCAN_UP ? RANGE_RESCAN_DOWN : RANGE_SCAN_DOWN);
if (adaptive_range_gain_limit >= current_gain) {
adaptive_range_gain_limit = current_gain - 1;
}
break;
}
if (sdrGetGain() >= adaptive_gain_max) {
// We have reached our upper gain limit
fprintf(stderr, "adaptive: reached upper gain limit, halting dynamic range scan here\n");
adaptive_range_state = RANGE_SCAN_IDLE;
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
break;
}
// This gain step is OK and we have more to try, try the next gain step up.
// (But if burst detection has inhibited increasing gain, don't do anything yet, just try again next block)
if (!gain_not_up) {
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) >= required dynamic range (%.1fdB), continuing upward scan\n", available_range, Modes.adaptive_range_target);
gain_up = true;
gain_up_reason = "probing dynamic range gain upper bound";
}
break;
case RANGE_SCAN_DOWN:
case RANGE_RESCAN_DOWN:
if (available_range >= Modes.adaptive_range_target) {
// Current gain meets our target; we are done with the scan.
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) >= required dynamic range (%.1fdB), stopping downwards scan here\n", available_range, Modes.adaptive_range_target);
adaptive_range_state = RANGE_SCAN_IDLE;
adaptive_range_rescan_timer = (adaptive_range_state == RANGE_SCAN_DOWN ? Modes.adaptive_range_scan_delay : Modes.adaptive_range_rescan_delay);
break;
}
if (adaptive_range_gain_limit >= current_gain) {
adaptive_range_gain_limit = current_gain - 1;
}
if (sdrGetGain() <= adaptive_gain_min) {
fprintf(stderr, "adaptive: reached lower gain limit, halting dynamic range scan here\n");
adaptive_range_state = RANGE_SCAN_IDLE;
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
break;
}
// This gain step is too loud and we have more to try, try the next gain step down
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) < required dynamic range (%.1fdB), continuing downwards scan\n", available_range, Modes.adaptive_range_target);
gain_down = gain_not_up = true;
gain_down_reason = "probing dynamic range gain lower bound";
break;
case RANGE_SCAN_IDLE:
// Look for increased noise that could be compensated for by decreasing gain.
// Do this even if we're waiting to rescan or if burst control is also active
if (available_range + adaptive_gain_down_db / 2 < Modes.adaptive_range_target && sdrGetGain() > adaptive_gain_min) {
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) + half gain step down (%.1fdB) < required dynamic range (%.1fdB), starting downward scan\n",
available_range, Modes.adaptive_range_target, adaptive_gain_down_db);
if (adaptive_range_gain_limit >= current_gain) {
adaptive_range_gain_limit = current_gain - 1;
}
adaptive_range_state = RANGE_SCAN_DOWN;
gain_down = gain_not_up = true;
gain_down_reason = "dynamic range fell below target value";
break;
}
// Infrequently consider increasing gain to handle the case where we've selected a too-low gain where the noise floor is dominated by noise unrelated to the gain setting.
// But don't do this while burst control is preventing gain increases.
if (!adaptive_range_rescan_timer && !gain_not_up) {
if (available_range >= Modes.adaptive_range_target && sdrGetGain() < adaptive_gain_max) {
fprintf(stderr, "adaptive: start periodic scan for acceptable dynamic range at increased gain\n");
gain_up = true;
gain_up_reason = "periodic re-probing of dynamic range gain upper bound";
adaptive_range_state = RANGE_RESCAN_UP;
break;
}
// Nothing to do for a while.
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
}
break;
default:
fprintf(stderr, "adaptive: in a weird state (%d), trying to fix it\n", adaptive_range_state);
adaptive_range_state = RANGE_SCAN_IDLE;
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
break;
}
}
// now actually perform any gain changes
if (gain_down)
adaptive_decrease_gain(gain_down_reason);
else if (gain_up && !gain_not_up)
adaptive_increase_gain(gain_up_reason);
}

30
adaptive.h Normal file
View File

@ -0,0 +1,30 @@
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// adaptive.h: adaptive gain control prototypes
//
// Copyright (c) 2021 FlightAware, LLC
//
// This file is free software: you may copy, redistribute 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 file 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/>.
#ifndef ADAPTIVE_H
#define ADAPTIVE_H
#include <inttypes.h>
struct modesMessage;
void adaptive_init();
void adaptive_update(uint16_t *buf, unsigned length, struct modesMessage *decoded);
#endif

257
comm_b.c
View File

@ -29,8 +29,10 @@ static int decodeBDS17(struct modesMessage *mm, bool store);
static int decodeBDS20(struct modesMessage *mm, bool store);
static int decodeBDS30(struct modesMessage *mm, bool store);
static int decodeBDS40(struct modesMessage *mm, bool store);
static int decodeBDS44(struct modesMessage *mm, bool store);
static int decodeBDS50(struct modesMessage *mm, bool store);
static int decodeBDS60(struct modesMessage *mm, bool store);
static int decodeBDS05(struct modesMessage *mm, bool store);
static CommBDecoderFn comm_b_decoders[] = {
&decodeEmptyResponse,
@ -40,17 +42,18 @@ static CommBDecoderFn comm_b_decoders[] = {
&decodeBDS17,
&decodeBDS40,
&decodeBDS50,
&decodeBDS60
&decodeBDS60,
&decodeBDS44,
&decodeBDS05
};
void decodeCommB(struct modesMessage *mm)
{
mm->commb_format = COMMB_UNKNOWN;
// If DR or UM are set, this message is _probably_ noise
// as nothing really seems to use the multisite broadcast stuff?
// Also skip anything that had errors corrected
if (mm->DR != 0 || mm->UM != 0 || mm->correctedbits > 0) {
mm->commb_format = COMMB_NOT_DECODED;
return;
}
@ -77,12 +80,39 @@ void decodeCommB(struct modesMessage *mm)
// decode it
bestDecoder(mm, true);
}
} else {
mm->commb_format = COMMB_UNKNOWN;
}
}
static int decodeEmptyResponse(struct modesMessage *mm, bool store)
{
for (unsigned i = 0; i < 7; ++i) {
// 00000000000000 is a common response. Ignore it.
//
// Also, it's common to see responses that look like this:
// 40000000000000
// 50000000000000
// 60000000000000
// typically in grouped bursts (one of each message) from
// the same aircraft.
//
// I speculate that these are response to interrogations for
// BDS 4,0 5,0 and 6,0 respectively where the transponder
// doesn't support the register or has no data loaded for it.
// Treat them like empty responses.
switch (mm->MB[0]) {
case 0x00:
case 0x40:
case 0x50:
case 0x60:
break;
default:
return 0;
}
for (unsigned i = 1; i < 7; ++i) {
if (mm->MB[i] != 0) {
return 0;
}
@ -148,10 +178,10 @@ static int decodeBDS17(struct modesMessage *mm, bool store)
score -= 2;
}
if (getbit(msg, 13)) { // 4,4 meterological routine report
score -= 2;
score -= 1;
}
if (getbit(msg, 14)) { // 4,4 meterological hazard report
score -= 2;
score -= 1;
}
if (getbit(msg, 20)) { // 5,4 waypoint 1
score -= 2;
@ -173,8 +203,11 @@ static int decodeBDS17(struct modesMessage *mm, bool store)
} else if (!getbit(msg, 1) && !getbit(msg, 2) && !getbit(msg, 3) && !getbit(msg, 4) && !getbit(msg, 5) && !getbit(msg, 6)) {
// not ES capable
score += 1;
} else if (!getbit(msg, 1) && !getbit(msg, 2) && getbit(msg, 3) && getbit(msg, 4) && getbit(msg, 5)) {
// ES with no position data
score += 3;
} else {
// partial ES support, unlikely
// other combinations, unlikely
score -= 12;
}
@ -740,3 +773,213 @@ static int decodeBDS60(struct modesMessage *mm, bool store)
return score;
}
// BDS4,4 Meterological routine air report
static int decodeBDS44(struct modesMessage *mm, bool store)
{
unsigned char *msg = mm->MB;
unsigned source = getbits(msg, 1, 4);
unsigned wind_valid = getbit(msg, 5);
unsigned windspeed_raw = getbits(msg, 6, 14);
unsigned winddir_raw = getbits(msg, 15, 23);
// ICAO 9871 is inconsistent, it claims:
// bit 24 sign
// bits 25..34 static air temperature, MSB = 64C, LSB=0.25C, range -128C..+128C
//
// .. but this does not actually work, there is one too many bits in the bitfield
// for the claimed values.
//
// Based on observed data, the most plausible actual layout is:
//
// bit 24 status
// bit 25 sign
// bits 26..34 static air temperature, MSB=64C, LSB=0.25C, range -128C..+128C
unsigned sat_valid = getbit(msg, 24);
unsigned sat_sign = getbit(msg, 25);
unsigned sat_raw = getbits(msg, 26, 34);
unsigned asp_valid = getbit(msg, 35);
unsigned asp_raw = getbits(msg, 36, 46);
unsigned turbulence_valid = getbit(msg, 47);
unsigned turbulence_raw = getbits(msg, 48, 49);
unsigned humidity_valid = getbit(msg, 50);
unsigned humidity_raw = getbits(msg, 51, 56);
if (source == MRAR_SOURCE_INVALID || source >= MRAR_SOURCE_RESERVED)
return 0; // invalid or reserved source
if (!wind_valid || !sat_valid)
return 0; // all valid messages seen in the wild have at least temp + wind
if (!asp_valid && asp_raw != 0)
return 0; // ASP not valid, but non-zero values in the ASP field
if (!turbulence_valid && turbulence_raw != 0)
return 0; // turbulence not valid, but non-zero values in the turbulence field
if (!humidity_valid && humidity_raw != 0)
return 0; // humidity not valid, but non-zero values in the humidity field
int score = 0;
float wind_speed = 0;
float wind_dir = 0;
if (wind_valid) {
wind_dir = winddir_raw * (180.0 / 256.0);
wind_speed = windspeed_raw;
if (windspeed_raw == 0) // possible but uncommon
score += 2;
else if (wind_speed <= 250)
score += 19;
else
return 0;
} else {
score += 1;
}
float sat = 0;
if (sat_valid) {
sat = sat_raw * 0.25;
if (sat_sign)
sat -= 128;
if (sat == 0) // possible but uncommon
score += 2;
else if (sat >= -80 && sat <= 60)
score += 11;
else
return 0;
} else {
score += 1;
}
float asp = 0;
if (asp_valid) {
asp = asp_raw;
if (asp >= 25 && asp <= 1100)
score += 12;
else
return 0;
} else {
score += 1;
}
hazard_t turbulence = HAZARD_NIL;
if (turbulence_valid) {
turbulence = (hazard_t) turbulence_raw;
score += 3;
} else {
score += 1;
}
float humidity = 0;
if (humidity_valid) {
humidity = humidity_raw * (100.0 / 64.0);
score += 7;
} else {
score += 1;
}
if (source == MRAR_SOURCE_DMEDME && wind_valid && sat_valid && score > 0) {
// Some GICB messages can be easily mistaken for a MRAR:
//
// GICB bit 1: BDS 0,5 ES airborne position = 0 ]
// GICB bit 2: BDS 0,6 ES surface position = 0 ]
// GICB bit 3: BDS 0,7 ES status = 1 ]
// GICB bit 4: BDS 0,8 ES type & identification = 1 ] -> MRAR source = 3
// GICB bit 5: BDS 0,9 ES airborne velocity = 1 -> MRAR wind valid bit
// GICB bit 24: BDS 6,0 heading and speed report = 1 -> MRAR temp valid bit
// most trailing bits = 0
//
// so only treat this as MRAR as a last resort
score = 1;
}
if (store) {
mm->commb_format = COMMB_MRAR;
mm->mrar_source_valid = 1;
mm->mrar_source = (mrar_source_t) source;
if (wind_valid) {
mm->wind_valid = 1;
mm->wind_speed = wind_speed;
mm->wind_dir = wind_dir;
}
if (sat_valid) {
mm->temperature_valid = 1;
mm->temperature = sat;
}
if (asp_valid) {
mm->pressure_valid = 1;
mm->pressure = asp;
}
if (turbulence_valid) {
mm->turbulence_valid = 1;
mm->turbulence = turbulence;
}
if (humidity_valid) {
mm->humidity_valid = 1;
mm->humidity = humidity;
}
}
return score;
}
// BDS0,5 extended squitter airborne position
// (apparently this gets queried via comm-b sometimes??)
// We don't try to _use_ this as a position, but we can
// at least try to recognize it, to exclude other
// comm-b types (in particular they can be mistaken for MRAR)
static int decodeBDS05(struct modesMessage *mm, bool store)
{
// We recognize these by matching the position altitude against
// the altitude in the surrounding message, so we need a
// DF20 not a DF21
if (mm->msgtype != 20)
return 0;
unsigned char *msg = mm->MB;
unsigned typecode = getbits(msg, 1, 5);
if (typecode < 9 || typecode > 18)
return 0; // only consider typecodes that could be an airborne position with baro altitude
unsigned t_bit = getbit(msg, 21);
if (t_bit) // unlikely
return 0;
unsigned ac12 = getbits(msg, 9, 20);
if (!ac12)
return 0;
// Insert M=0 to make an AC13 value, match against the
// AC13 value in the surrounding message
unsigned ac13 = ((ac12 & 0x0FC0) << 1) | (ac12 & 0x003F);
if (mm->AC != ac13)
return 0; // no altitude match
unsigned lat = getbits(msg, 23, 39);
unsigned lon = getbits(msg, 40, 56);
if (lat == 0 || lon == 0) // unlikely position
return 0;
if (store) {
mm->commb_format = COMMB_AIRBORNE_POSITION;
// No further decoding done, we don't really trust this
// enough to use as real input to CPR
}
// Score this high enough to override everything else
return 100;
}

View File

@ -1,30 +0,0 @@
Source: dump1090-fa
Section: embedded
Priority: extra
Maintainer: Oliver Jowett <oliver@mutability.co.uk>
Build-Depends: debhelper(>=9), librtlsdr-dev, libusb-1.0-0-dev, pkg-config, dh-systemd, libncurses5-dev, libbladerf-dev
Standards-Version: 3.9.3
Homepage: http://www.flightaware.com/
Vcs-Git: https://github.com/flightaware/dump1090.git
Package: dump1090
Architecture: all
Depends: dump1090-fa, ${misc:Depends}
Priority: extra
Section: oldlibs
Description: transitional dummy package for dump1090
This is a transitional dummy package to handle upgrades from
the old package name of "dump1090" to the new package name of
"dump1090-fa". It can safely be removed.
Package: dump1090-fa
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libbladerf1 (>= 0.2016.06), adduser, lighttpd
Replaces: dump1090 (<< 3.0)
Breaks: dump1090 (<< 3.0)
Description: ADS-B Ground Station System for RTL-SDR
Networked Aviation Mode S / ADS-B decoder/translator with RTL-SDR software
defined radio USB device support.
.
This is FlightAware's fork of dump1090-mutability, customized for use
in the PiAware sdcard images.

View File

@ -1,29 +0,0 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/default.mk
override_dh_auto_build:
# jessie's gcc doesn't support the compiler flags needed for ARM-specific starch flavors;
# turn off runtime CPU detection
dh_auto_build -- RTLSDR=yes BLADERF=yes HACKRF=no LIMESDR=no DUMP1090_VERSION=$(DEB_VERSION) CPUFEATURES=no
override_dh_install:
dh_install
install -d debian/dump1090-fa/usr/bin
cp -a dump1090 debian/dump1090-fa/usr/bin/dump1090-fa
cp -a view1090 debian/dump1090-fa/usr/bin/view1090-fa
%:
dh $@ --with=systemd

View File

@ -1,8 +1,8 @@
Source: dump1090-fa
Section: embedded
Priority: extra
Maintainer: Oliver Jowett <oliver@mutability.co.uk>
Build-Depends: debhelper(>=9), librtlsdr-dev, libusb-1.0-0-dev, pkg-config, dh-systemd, libncurses5-dev, libbladerf-dev
Maintainer: Oliver Jowett <oliver.jowett@flightaware.com>
Build-Depends: debhelper(>=10), librtlsdr-dev, libusb-1.0-0-dev, pkg-config, libncurses5-dev, libbladerf-dev
Standards-Version: 3.9.3
Homepage: http://www.flightaware.com/
Vcs-Git: https://github.com/flightaware/dump1090.git

View File

@ -25,5 +25,11 @@ override_dh_install:
cp -a dump1090 debian/dump1090-fa/usr/bin/dump1090-fa
cp -a view1090 debian/dump1090-fa/usr/bin/view1090-fa
override_dh_installinit:
dh_installinit --no-stop-on-upgrade --no-restart-after-upgrade
override_dh_systemd_start:
dh_systemd_start --no-stop-on-upgrade --no-restart-after-upgrade --name=dump1090-fa.service
%:
dh $@ --with=systemd

49
debian/changelog vendored
View File

@ -1,3 +1,52 @@
dump1090-fa (7.2) stable; urgency=medium
* dump1090: Fix Makefile syntax error for Darwin OS build (courtesy @CodyCodeman, PR #170)
* dump1090: set _POSIX_C_SOURCE to fix build failure with uclibc-ng (courtesy @ffontaine, PR #169)
* SkyAware: Remove obselete map interface at /dump1090-fa
* SkyAware: Remove OSM Black & White layer which is no longer in service
-- Eric Tran <eric.tran@flightaware.com> Wed, 09 Mar 2022 20:09:55 -0600
dump1090-fa (7.1) stable; urgency=medium
* Preserve start/upgrade postinst logic for pre 7.1 updates until we support compat 10 behavior
-- Eric Tran <eric.tran@flightaware.com> Wed, 12 Jan 2022 11:37:20 -0600
dump1090-fa (7.0) stable; urgency=medium
* dump1090: Allow env vars (not only command line overrdies) to set CPU_FEATURES_{ARCH,UNAME}
* dump1090: Treat ARCH=arm64 like ARCH=aarch64
* dump1090: Try to respect CFLAGS/CPPFLAGS as far as possible; move required extra flags into a separate var
* dump1090: Adaptive gain - more aggressively re-probe for higher gain after a decrease in gain due to increased noise floor
* dump1090: Cleanup AVR parsing
* SkyAware: Update aircraft db to 20211210
* SkyAware: Cleanup unused Openlayers files
-- Eric Tran <eric.tran@flightaware.com> Mon, 20 Dec 2021 11:00:00 -0600
dump1090-fa (6.1) stable; urgency=medium
* No-change 6.1 release for PiAware 6.1
-- Oliver Jowett <oliver.jowett@flightaware.com> Mon, 06 Sep 2021 16:06:00 +0800
dump1090-fa (6.0) stable; urgency=medium
* dump1090: Adaptive gain feature with associated dump1090-fa config parameters
* dump1090: Add support for decoding BDS 4,4 - Meteorological Routine Air Report (MRAR) Comm-B messages
* dump1090: Stratux output: include current receiver gain (courtesy @b3nn0, PR #144)
* dump1090: Enable rtlsdr bounce buffers on aarch64 (courtesy @wiedehopf for suggestion)
* dump1090: New default dump1090-fa config file format
* dump1090: Use compat/compat.h for endian-swapping functions in DSP code
* dump1090: Report json write errors, with some rate limiting (fixes issue #129)
* starch: Update starch to upstream commit 0c8249fa4bc523345c156885542e9192e8bf68fd
* SkyAware: restrict overlay rendering to covered areas (reduce source load) (courtesy @wiedehopf, PR #137)
* SkyAware: Fix ignored DisplayUnits in config.js (courtesy @paulyc, PR #76)
* SkyAware: Update aircraft db to 20210817 with better Australian aircraft & types
-- Eric Tran <eric.tran@flightaware.com> Tue, 31 Aug 2021 12:41:06 -0600
dump1090-fa (5.0) stable; urgency=medium
* SkyAware: New Altitude, Speed, Aircraft Ident, and Aircraft Type filters to fine tune the select aircraft you want see

2
debian/compat vendored
View File

@ -1 +1 @@
9
10

18
debian/control vendored
View File

@ -2,30 +2,20 @@ Source: dump1090-fa
Section: embedded
Priority: extra
Maintainer: Oliver Jowett <oliver.jowett@flightaware.com>
Build-Depends: debhelper(>=9),
Build-Depends: debhelper(>=10),
librtlsdr-dev <!custom> <rtlsdr>,
libbladerf-dev <!custom> <bladerf>,
libhackrf-dev <!custom> <hackrf>,
liblimesuite-dev <!custom> <limesdr>,
libusb-1.0-0-dev <!custom> <rtlsdr> <bladerf> <hackrf> <limesdr>,
pkg-config, dh-systemd, libncurses5-dev
pkg-config, libncurses5-dev
Standards-Version: 3.9.3
Homepage: http://www.flightaware.com/
Vcs-Git: https://github.com/edgeofspace/dump1090-fa.git
Package: dump1090
Architecture: all
Depends: dump1090-fa, ${misc:Depends}
Priority: extra
Section: oldlibs
Description: transitional dummy package for dump1090
This is a transitional dummy package to handle upgrades from
the old package name of "dump1090" to the new package name of
"dump1090-fa". It can safely be removed.
Vcs-Git: https://github.com/flightaware/dump1090.git
Package: dump1090-fa
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, adduser
Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, lighttpd
Description: FlightAware ADS-B Ground Station System for SDRs
Networked Aviation Mode S / ADS-B decoder/translator with support
for RTL-SDR, BladeRF, HackRF, and LimeSDR software defined radio USB

View File

@ -2,20 +2,61 @@
# This is sourced by /usr/share/dump1090-fa/start-dump1090-fa as a
# shellscript fragment.
# If you are using a PiAware sdcard image, this config file is regenerated
# on boot based on the contents of piaware-config.txt; any changes made to this
# file will be lost.
# dump1090-fa won't automatically start unless ENABLED=yes
ENABLED=yes
RECEIVER_OPTIONS="--device ADSB --gain -10 --ppm 0"
DECODER_OPTIONS="--max-range 800 --fix"
NET_OPTIONS="--net --net-heartbeat 60 --net-ro-size 1300 --net-ro-interval 0.2 --net-ri-port 0 --net-ro-port 30002 --net-sbs-port 30003 --net-bi-port 30004,30104 --net-bo-port 30005"
JSON_OPTIONS="--json-location-accuracy 1"
# SDR device type. Use "none" for a net-only configuration
RECEIVER=rtlsdr
# serial number or device index of device to use (only needed if there is more than one SDR connected)
RECEIVER_SERIAL=
# Initial receiver gain, in dB. If adaptive gain is enabled (see below) the actual gain
# may change over time
RECEIVER_GAIN=60
# Use a machine-specific wisdom file if it exists
if [ -f /etc/dump1090-fa/wisdom.local ]
then
RECEIVER_OPTIONS="${RECEIVER_OPTIONS} --wisdom /etc/dump1090-fa/wisdom.local"
fi
# Adjust gain to try to achieve optimal dynamic range / noise floor?
ADAPTIVE_DYNAMIC_RANGE=yes
# Target dynamic range in dB (leave blank to autoselect based on SDR type)
ADAPTIVE_DYNAMIC_RANGE_TARGET=
# Reduce gain when loud message bursts from nearby aircraft are seen?
ADAPTIVE_BURST=no
# Gain range to allow when changing gain, in dB (empty = no limit)
ADAPTIVE_MIN_GAIN=
ADAPTIVE_MAX_GAIN=
# Turn on options to reduce load on slower CPUs, at the expense of slightly worse decoder performance.
# Setting "auto" will enable these options only if the CPU appears to be a slow CPU (currently this
# means armv6 only, e.g. Pi Zero)
SLOW_CPU=auto
# Local wisdom file used to select DSP implementations; uses built-in ranking if the file is missing
WISDOM=/etc/dump1090-fa/wisdom.local
# Correct CRC errors where possible
ERROR_CORRECTION=yes
# Receiver location, used for some types of position decoding. Provide the location as
# signed decimal degrees. If not given here, dump1090 will also try to read a receiver
# location from /var/cache/piaware/location.env (written automatically by PiAware, if installed)
RECEIVER_LAT=
RECEIVER_LON=
# Maximum range, in NM. Positions more distant than this are ignored. No limit if not set.
MAX_RANGE=360
# Network ports to listen on for connections
NET_RAW_INPUT_PORTS=
NET_RAW_OUTPUT_PORTS=30002
NET_SBS_OUTPUT_PORTS=30003
NET_BEAST_INPUT_PORTS=30004,30104
NET_BEAST_OUTPUT_PORTS=30005
# Accuracy of location written to JSON output
JSON_LOCATION_ACCURACY=1
# Additional options can be added here:
EXTRA_OPTIONS=""
# If OVERRIDE_OPTIONS is set, only those options are used; all other options
# in this config file are ignored.
OVERRIDE_OPTIONS=""
# This is a marker to make it easier for scripts to identify a v6-style config file
CONFIG_STYLE=6

View File

@ -1,6 +1,8 @@
public_html/* usr/share/dump1090-fa/html
public_html_merged/* usr/share/skyaware/html
public_html/* usr/share/skyaware/html
debian/lighttpd/* etc/lighttpd/conf-available
bladerf/* usr/share/dump1090-fa/bladerf
debian/start-dump1090-fa usr/share/dump1090-fa/
debian/generate-wisdom usr/share/dump1090-fa/
debian/upgrade-config usr/share/dump1090-fa/
debian/dump1090-fa.default usr/share/dump1090-fa/
starch-benchmark /usr/lib/dump1090-fa/

View File

@ -34,44 +34,21 @@ case "$1" in
# plugdev required for bladeRF USB access
adduser "$RUNAS" plugdev
# Make sure the "data" directory is owned by dump1090 so the dump1090-fa process has write access
jsondir="/usr/share/dump1090-fa/html/data"
if [ ! -d ${jsondir} ]; then
echo "Creating directory ${jsondir}..." >&2
mkdir ${jsondir}
fi
if [ -d ${jsondir} ]; then
echo "Changing permissions on ${jsondir}..." >&2
chown ${RUNAS}:nogroup ${jsondir}
chmod 755 ${jsondir}
fi
# Update apache
changed=false
for apachefile in /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/default-ssl.conf
do
if [ -f ${apachefile} ]; then
echo "Modifying ${apachefile}..." >&2
sed -i '/<\/VirtualHost>/i \
####dump1090-start###\
Alias "/data" "/dump1090-fa/data"\
Alias "/dump1090-fa" "/usr/share/dump1090-fa/html"\
Alias "^/dump1090-fa$" "/dump1090-fa"\
SetEnvIf Request_URI "/dump1090-fa/data/.*\.json$" Header set "Access-Control-Allow-Origin" "*"\
####dump1090-end###' ${apachefile}
changed=true
fi
done
# Restart apache
if $changed
# set up lighttpd
if dpkg --compare-versions "$2" lt "3.1.0"
then
echo "Restarting Apache..." >&2
invoke-rc.d apache2 restart || echo "Warning: apache2 failed to restart." >&2
echo "Enabling lighttpd integration.." >&2
lighty-enable-mod dump1090-fa || true
# only enable the statcache config if there is nothing else around that already
# configures it, because lighttpd fails if it's configured twice
if ! grep -q -E '^\S*server.stat-cache-engine' /etc/lighttpd/conf-enabled/*.conf
then
echo "Enabling lighttpd integration (stat cache).." >&2
lighty-enable-mod dump1090-fa-statcache || true
fi
fi
# on upgrade, add an ENABLED line if it's not already present
if dpkg --compare-versions "$2" lt-nl "3.7.0"
then
@ -86,14 +63,36 @@ case "$1" in
fi
fi
# enabling and starting the dump1090-fa service
echo "Unmasking the dump1090-fa service..." >&2
systemctl unmask dump1090-fa
echo "Enabling the dump1090-fa service..." >&2
systemctl enable dump1090-fa
echo "Starting the dump1090-fa service..." >&2
systemctl restart dump1090-fa
# on upgrade from pre-6.0, update the defaults file to the new syntax
if dpkg --compare-versions "$2" lt-nl "6.0"
then
if [ -f /etc/default/dump1090-fa ]
then
echo "Trying to upgrade existing config to new syntax.." >&2
/usr/share/dump1090-fa/upgrade-config /etc/default/dump1090-fa /usr/share/dump1090-fa/dump1090-fa.default \
|| echo "Something went wrong upgrading the config; your config may be broken. Sorry!" >&1
fi
fi
if dpkg --compare-versions "$2" le "5.0"
then
echo "Enabling lighttpd skyaware module.." >&2
lighty-enable-mod skyaware || true
fi
# dump1090-fa lighttpd module deprecated in 7.2
if dpkg --compare-versions "$2" lt "7.2"
then
if [ -e /etc/lighttpd/conf-enabled/89-dump1090-fa.conf ]
then
echo "Disabling deprecated lighttpd dump1090-fa module..." >&2
lighty-disable-mod dump1090-fa || true
fi
fi
echo "Restarting lighttpd.." >&2
invoke-rc.d lighttpd restart || true
;;
abort-upgrade|abort-remove|abort-deconfigure)

View File

@ -26,21 +26,24 @@ case "$1" in
remove)
changed=false
if [ -e /etc/lighttpd/conf-enabled/89-dump1090-fa.conf ]
then
echo "Disabling lighttpd integration.." >&2
lighty-disable-mod dump1090-fa || true
changed=true
fi
for apachefile in /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/default-ssl.conf
do
if [ -f ${apachefile} ]; then
echo "Modifying apache file ${apachefile}..." >&2
sed -i '/^[ \t]*#*dump1090-start#*/,/^[ \t]*#*dump1090-end#*/{/^[ \t]*#*dump1090-start#*/!{/^[ \t]*#*dump1090-end#*/!d}}' ${apachefile}
sed -i '/[ \t]*#*dump1090-start#*/,/^[ \t]*#*dump1090-end#*/d' ${apachefile}
changed=true
fi
done
if [ -e /etc/lighttpd/conf-enabled/88-dump1090-fa-statcache.conf ]
then
echo "Disabling lighttpd integration (stat cache).." >&2
lighty-disable-mod dump1090-fa-statcache || true
changed=true
fi
if $changed
then
echo "Restarting Apache..." >&2
invoke-rc.d apache2 restart || echo "Warning: apache2 failed to restart." >&2
echo "Restarting lighttpd.." >&2
invoke-rc.d lighttpd restart || echo "Warning: lighttpd failed to restart." >&2
fi
;;

View File

@ -10,7 +10,7 @@ After=network.target
User=dump1090
RuntimeDirectory=dump1090-fa
RuntimeDirectoryMode=0755
ExecStart=/usr/share/dump1090-fa/start-dump1090-fa --write-json /usr/share/dump1090-fa/html/data --quiet
ExecStart=/usr/share/dump1090-fa/start-dump1090-fa --write-json %t/dump1090-fa
SyslogIdentifier=dump1090-fa
Type=simple
Restart=on-failure

View File

@ -0,0 +1,4 @@
# The stat cache must be disabled, as aircraft.json changes
# frequently and lighttpd's stat cache often ends up with the
# wrong content length.
server.stat-cache-engine = "disable"

75
debian/lighttpd/89-skyaware.conf vendored Normal file
View File

@ -0,0 +1,75 @@
# Allows access to the static files that provide the dump1090 map view,
# and also to the dynamically-generated json parts that contain aircraft
# data and are periodically written by the dump1090 daemon.
# Enable alias module
#
## This module is normally already enabled in lighttpd, so you should not
## need to uncommment this line.
## There are some cases (e.g. when installing this on a Raspberry Pi
## that runs PiHole) in which the module has been removed from the
## default configuration, and the dump1090-fa web interface no longer
## loads properly.
## If this is what you are experiencing, or if you see messages in your
## error log like:
## (server.c.1493) WARNING: unknown config-key: alias.url (ignored)
## then uncommenting this line and then restarting lighttpd could fix
## the issue.
## This is not enabled by default as standard lighttpd will not start if
## modules are loaded multiple times.
#
# server.modules += ( "mod_alias" )
alias.url += (
"/skyaware/data/" => "/run/dump1090-fa/",
"/skyaware/data-978/" => "/run/skyaware978/",
"/skyaware/" => "/usr/share/skyaware/html/"
)
# redirect the slash-less URL
url.redirect += (
"^/skyaware$" => "/skyaware/"
)
# Listen on port 8080 and serve the map there, too.
$SERVER["socket"] == ":8080" {
alias.url += (
"/data/" => "/run/dump1090-fa/",
"/data-978/" => "/run/skyaware978/",
"/" => "/usr/share/skyaware/html/"
)
}
# Add CORS header
server.modules += ( "mod_setenv" )
$HTTP["url"] =~ "^/skyaware/data/.*\.json$" {
setenv.set-response-header = ( "Access-Control-Allow-Origin" => "*" )
}
# Uncomment this section to enable SSL traffic (HTTPS) - especially useful
# for .dev domains
## Listen on 8443 for SSL connections
#server.modules += ( "mod_openssl" )
#$HTTP["host"] == "piaware.example.com" {
# $SERVER["socket"] == ":8443" {
# ssl.engine = "enable"
# ssl.pemfile = "/etc/ssl/certs/combined.pem"
# ssl.ca-file = "/etc/ssl/certs/fullchain.cer"
# ssl.honor-cipher-order = "enable"
# ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
# ssl.use-sslv2 = "disable"
# ssl.use-sslv3 = "disable"
#
# alias.url += (
# "/data/" => "/run/dump1090-fa/",
# "/" => "/usr/share/skyware/html/"
# )
# }
#}
#
## Redirect HTTP to HTTPS
#$HTTP["scheme"] == "http" {
# $HTTP["host"] =~ ".*" {
# url.redirect = (".*" => "https://%0$0")
# }
#}

6
debian/rules vendored
View File

@ -53,5 +53,11 @@ override_dh_install:
cp -a dump1090 debian/dump1090-fa/usr/bin/dump1090-fa
cp -a view1090 debian/dump1090-fa/usr/bin/view1090-fa
override_dh_installinit:
dh_installinit --no-stop-on-upgrade --no-restart-after-upgrade
override_dh_systemd_start:
dh_systemd_start --no-stop-on-upgrade --no-restart-after-upgrade --name=dump1090-fa.service
%:
dh $@ --with=systemd

View File

@ -15,19 +15,93 @@ then
. /var/cache/piaware/location.env
fi
if [ "x$ENABLED" != "xyes" ]
if [ "$ENABLED" != "yes" ]
then
echo "dump1090-fa not enabled in /etc/default/dump1090-fa" >&2
exit 64
fi
if [ -n "$PIAWARE_LAT" -a -n "$PIAWARE_LON" ]
# process options
# if there's no CONFIG_STYLE, infer a version
if [ -z "$CONFIG_STYLE" ]
then
POSITION="--lat $PIAWARE_LAT --lon $PIAWARE_LON"
if [ -n "$RECEIVER_OPTIONS" -o -n "$DECODER_OPTIONS" -o -n "$NET_OPTIONS" -o -n "$JSON_OPTIONS" ]
then
CONFIG_STYLE=5
else
CONFIG_STYLE=6
fi
fi
exec /usr/bin/dump1090-fa \
$RECEIVER_OPTIONS $DECODER_OPTIONS $NET_OPTIONS $JSON_OPTIONS $POSITION \
"$@"
is_slow_cpu() {
case "$SLOW_CPU" in
yes) return 0 ;;
auto)
case $(uname -m) in
armv6*) return 0 ;;
*) return 1 ;;
esac
;;
*) return 1 ;;
esac
}
if [ "$CONFIG_STYLE" = "5" ]
then
# old style config file
echo "/etc/default/dump1090-fa is using the old config style, please consider updating it" >&2
OPTS="$RECEIVER_OPTIONS $DECODER_OPTIONS $NET_OPTIONS $JSON_OPTIONS"
elif [ -n "$OVERRIDE_OPTIONS" ]
then
# ignore all other settings, use only provided options
OPTS="$OVERRIDE_OPTIONS"
else
# build a list of options based on config settings
OPTS=""
if [ "${RECEIVER:-none}" = "none" ]
then
OPTS="$OPTS --device-type none"
else
if [ -n "$RECEIVER" ]; then OPTS="$OPTS --device-type $RECEIVER"; fi
if [ -n "$RECEIVER_SERIAL" ]; then OPTS="$OPTS --device-index $RECEIVER_SERIAL"; fi
if [ -n "$RECEIVER_GAIN" ]; then OPTS="$OPTS --gain $RECEIVER_GAIN"; fi
if [ -n "$WISDOM" -a -f "$WISDOM" ]; then OPTS="$OPTS --wisdom $WISDOM"; fi
if [ "$ADAPTIVE_DYNAMIC_RANGE" = "yes" ]; then OPTS="$OPTS --adaptive-range"; fi
if [ -n "$ADAPTIVE_DYNAMIC_RANGE_TARGET" ]; then OPTS="$OPTS --adaptive-range-target $ADAPTIVE_DYNAMIC_RANGE_TARGET"; fi
if [ "$ADAPTIVE_BURST" = "yes" ]; then OPTS="$OPTS --adaptive-burst"; fi
if [ -n "$ADAPTIVE_MIN_GAIN" ]; then OPTS="$OPTS --adaptive-min-gain $ADAPTIVE_MIN_GAIN"; fi
if [ -n "$ADAPTIVE_MAX_GAIN" ]; then OPTS="$OPTS --adaptive-max-gain $ADAPTIVE_MAX_GAIN"; fi
if is_slow_cpu
then
OPTS="$OPTS --adaptive-duty-cycle 10 --no-fix-df"
fi
fi
if [ "$ERROR_CORRECTION" = "yes" ]; then OPTS="$OPTS --fix"; fi
if [ -n "$RECEIVER_LAT" -a -n "$RECEIVER_LON" ]; then
OPTS="$OPTS --lat $RECEIVER_LAT --lon $RECEIVER_LON"
elif [ -n "$PIAWARE_LAT" -a -n "$PIAWARE_LON" ]; then
OPTS="$OPTS --lat $PIAWARE_LAT --lon $PIAWARE_LON"
fi
if [ -n "$MAX_RANGE" ]; then OPTS="$OPTS --max-range $MAX_RANGE"; fi
if [ -n "$NET_RAW_INPUT_PORTS" ]; then OPTS="$OPTS --net-ri-port $NET_RAW_INPUT_PORTS"; fi
if [ -n "$NET_RAW_OUTPUT_PORTS" ]; then OPTS="$OPTS --net-ro-port $NET_RAW_OUTPUT_PORTS"; fi
if [ -n "$NET_SBS_OUTPUT_PORTS" ]; then OPTS="$OPTS --net-sbs-port $NET_SBS_OUTPUT_PORTS"; fi
if [ -n "$NET_BEAST_INPUT_PORTS" ]; then OPTS="$OPTS --net-bi-port $NET_BEAST_INPUT_PORTS"; fi
if [ -n "$NET_BEAST_OUTPUT_PORTS" ]; then OPTS="$OPTS --net-bo-port $NET_BEAST_OUTPUT_PORTS"; fi
if [ -n "$JSON_LOCATION_ACCURACY" ]; then OPTS="$OPTS --json-location-accuracy $JSON_LOCATION_ACCURACY"; fi
if [ -n "$EXTRA_OPTIONS" ]; then OPTS="$OPTS $EXTRA_OPTIONS"; fi
fi
exec /usr/bin/dump1090-fa --quiet $OPTS "$@"
# exec failed, do not restart
exit 64

187
debian/upgrade-config vendored Executable file
View File

@ -0,0 +1,187 @@
#!/bin/sh
# This script runs during upgrade from pre-6.0 to try to
# update /etc/default/dump1090-fa to the new-style
# config.
if [ $# -lt 2 ]
then
echo "syntax: $0 path-to-config path-to-package-defaults" >&2
exit 2
fi
OLDCONFIG="$1"
NEWCONFIG="${OLDCONFIG}.v6-upgrade"
BACKUPCONFIG="${OLDCONFIG}.pre-v6-upgrade"
PACKAGECONFIG="$2"
if [ ! -f $OLDCONFIG ]
then
echo "$OLDCONFIG does not exist, nothing to upgrade" >&2
exit 0
fi
if [ ! -f $PACKAGECONFIG ]
then
echo "$PACKAGECONFIG does not exist, cannot continue" >&2
exit 1
fi
if [ -f $BACKUPCONFIG ]
then
echo "$BACKUPCONFIG already exists, I'm not going to clobber it" >&2
exit 1
fi
# set some assumed defaults (5.0 dump1090 binary defaults if no command-line options are given)
ENABLED="no"
RECEIVER="rtlsdr"
RECEIVER_SERIAL=""
RECEIVER_GAIN="50"
WISDOM=""
ERROR_CORRECTION="no"
MAX_RANGE=""
NET_RAW_INPUT_PORTS="30001"
NET_RAW_OUTPUT_PORTS="30002"
NET_SBS_OUTPUT_PORTS="30003"
NET_BEAST_INPUT_PORTS="30004,30104"
NET_BEAST_OUTPUT_PORTS="30005"
EXTRAS_NET_RO_SIZE="0"
EXTRAS_NET_RO_INTERVAL="0"
EXTRAS_NET_HEARTBEAT="60"
# read the old config
. "$OLDCONFIG"
# if there's no CONFIG_STYLE, infer a version
if [ -z "$CONFIG_STYLE" ]
then
if [ -n "$RECEIVER_OPTIONS" -o -n "$DECODER_OPTIONS" -o -n "$NET_OPTIONS" -o -n "$JSON_OPTIONS" ]
then
CONFIG_STYLE=5
else
CONFIG_STYLE=6
fi
fi
# process all the options from the old config file and accumulate settings in env vars
process_v5_options() {
while [ $# -gt 0 ]
do
opt="$1"
shift
case "$opt" in
--device-type) RECEIVER="$1"; shift ;;
--device-index) RECEIVER_SERIAL="$1"; shift ;;
--gain) RECEIVER_GAIN="$1"; shift ;;
--max-range) MAX_RANGE="$1"; shift ;;
--fix) ERROR_CORRECTION="yes" ;;
--net) ;;
--net-ri-port) NET_RAW_INPUT_PORTS="$1"; shift ;;
--net-ro-port) NET_RAW_OUTPUT_PORTS="$1"; shift ;;
--net-sbs-port) NET_SBS_OUTPUT_PORTS="$1"; shift ;;
--net-bi-port) NET_BEAST_INPUT_PORTS="$1"; shift ;;
--net-bo-port) NET_BEAST_OUTPUT_PORTS="$1"; shift ;;
--json-location-accuracy) JSON_LOCATION_ACCURACY="$1"; shift ;;
--wisdom) WISDOM="$1"; shift ;;
--ppm) EXTRAS_PPM="$1"; shift ;;
--net-heartbeat) EXTRAS_NET_HEARTBEAT="$1"; shift ;;
--net-ro-size) EXTRAS_NET_RO_SIZE="$1"; shift ;;
--net-ro-interval) EXTRAS_NET_RO_INTERVAL="$1"; shift ;;
*) EXTRA_OPTIONS="$EXTRA_OPTIONS ${opt}" ;;
esac
done
# update EXTRA_OPTIONS for any non-default special settings
if [ "${EXTRAS_PPM:-0}" != "0" ]; then EXTRA_OPTIONS="$EXTRA_OPTIONS --ppm $EXTRAS_PPM"; fi
if [ "${EXTRAS_NET_HEARTBEAT:-60}" != "60" ]; then EXTRA_OPTIONS="$EXTRA_OPTIONS --net-heartbeat $EXTRAS_NET_HEARTBEAT"; fi
if [ "${EXTRAS_NET_RO_SIZE:-1300}" != "1300" ]; then EXTRA_OPTIONS="$EXTRA_OPTIONS --net-ro-size $EXTRAS_NET_RO_SIZE"; fi
if [ "${EXTRAS_NET_RO_INTERVAL:-1300}" != "0.2" ]; then EXTRA_OPTIONS="$EXTRA_OPTIONS --net-ro-interval $EXTRAS_NET_RO_INTERVAL"; fi
# special case for device index 0 (the default)
if [ "${RECEIVER_SERIAL}" = "0" ]; then RECEIVER_SERIAL=""; fi
# special case for gain -10 -> gain 60
if [ "${RECEIVER_GAIN}" = "-10" ]; then RECEIVER_GAIN="60"; fi
# special case for ports set to zero (new config uses a blank entry for that)
if [ "${NET_RAW_INPUT_PORTS}" = "0" ]; then NET_RAW_INPUT_PORTS=""; fi
if [ "${NET_RAW_OUTPUT_PORTS}" = "0" ]; then NET_RAW_OUTPUT_PORTS=""; fi
if [ "${NET_SBS_INPUT_PORTS}" = "0" ]; then NET_SBS_OUTPUT_PORTS=""; fi
if [ "${NET_BEAST_INPUT_PORTS}" = "0" ]; then NET_BEAST_INPUT_PORTS=""; fi
if [ "${NET_BEAST_OUTPUT_PORTS}" = "0" ]; then NET_BEAST_OUTPUT_PORTS=""; fi
}
SEDSCRIPT=$(mktemp -t dump1090XXX.sed)
if [ "$CONFIG_STYLE" = "5" ]
then
echo "Generating a v6-style config from the v5-style config in $OLDCONFIG" >&2
# nb: all values either derived from env vars or the package defaults
process_v5_options $RECEIVER_OPTIONS $DECODER_OPTIONS $NET_OPTIONS $JSON_OPTIONS
FROMCONFIG=$PACKAGECONFIG
cat >>$SEDSCRIPT <<EOF
s@^ENABLED=.*@ENABLED=${ENABLED}@
s@^RECEIVER=.*@RECEIVER=${RECEIVER}@
s@^RECEIVER_SERIAL=.*@RECEIVER_SERIAL="${RECEIVER_SERIAL}"@
s@^RECEIVER_GAIN=.*@RECEIVER_GAIN=${RECEIVER_GAIN}@
s@^WISDOM=.*@WISDOM=${WISDOM}@
s@^ERROR_CORRECTION=.*@ERROR_CORRECTION=${ERROR_CORRECTION}@
s@^MAX_RANGE=.*@MAX_RANGE=${MAX_RANGE}@
s@^NET_RAW_INPUT_PORTS=.*@NET_RAW_INPUT_PORTS=${NET_RAW_INPUT_PORTS}@
s@^NET_RAW_OUTPUT_PORTS=.*@NET_RAW_OUTPUT_PORTS=${NET_RAW_OUTPUT_PORTS}@
s@^NET_SBS_OUTPUT_PORTS=.*@NET_SBS_OUTPUT_PORTS=${NET_SBS_OUTPUT_PORTS}@
s@^NET_BEAST_INPUT_PORTS=.*@NET_BEAST_INPUT_PORTS=${NET_BEAST_INPUT_PORTS}@
s@^NET_BEAST_OUTPUT_PORTS=.*@NET_BEAST_OUTPUT_PORTS=${NET_BEAST_OUTPUT_PORTS}@
s@^JSON_LOCATION_ACCURACY=.*@JSON_LOCATION_ACCURACY=${JSON_LOCATION_ACCURACY}@
s@^EXTRA_OPTIONS=.*@EXTRA_OPTIONS="$EXTRA_OPTIONS"@
EOF
else
# Existing config file doesn't seem to be a v5, just turn off adaptive gain settings
# as this is an upgrade but otherwise leave it unchanged. (This happens when
# there were no changes made by the user to the config file before upgrading; dpkg
# will install the new v6 default config file automatically in that case)
FROMCONFIG=$OLDCONFIG
fi
# All upgrades get adaptive gain defaulting to off, to preserve the behaviour of
# existing installs. New installs using the package defaults will default to on.
echo "Disabling adaptive gain in $OLDCONFIG as this is an upgrade from pre-v6" >&2
cat >>$SEDSCRIPT <<EOF
s@^ADAPTIVE_DYNAMIC_RANGE=.*@ADAPTIVE_DYNAMIC_RANGE=no@
s@^ADAPTIVE_BURST=.*@ADAPTIVE_BURST=no@
EOF
# substitute into the config file template to generate our customized config
if ! sed -f $SEDSCRIPT <$FROMCONFIG >$NEWCONFIG
then
echo "Something went wrong trying to upgrade $OLDCONFIG, giving up.." >&2
exit 1
fi
rm $SEDSCRIPT
# back up the old config and install the new config
if ! cp -p $OLDCONFIG $BACKUPCONFIG
then
echo "Something went wrong trying to back up $OLDCONFIG, giving up.." >&2
exit 1
fi
if ! mv $NEWCONFIG $OLDCONFIG
then
echo "Something went wrong trying to install the new version of $NEWCONFIG, giving up.." >&2
exit 1
fi
# all good
cat >&2 <<EOF
Upgraded existing config $OLDCONFIG to the new config syntax.
Please doublecheck that it looks ok!
The old config file has been preserved at $BACKUPCONFIG
EOF
exit 0

View File

@ -58,6 +58,40 @@ static inline int slice_phase4(uint16_t *m) {
return m[0] + 5 * m[1] - 5 * m[2] - m[3];
}
static uint32_t valid_df_short_bitset; // set of acceptable DF values for short messages
static uint32_t valid_df_long_bitset; // set of acceptable DF values for long messages
static uint32_t generate_damage_set(uint8_t df, unsigned damage_bits)
{
uint32_t result = (1 << df);
if (!damage_bits)
return result;
for (unsigned bit = 0; bit < 5; ++bit) {
unsigned damaged_df = df ^ (1 << bit);
result |= generate_damage_set(damaged_df, damage_bits - 1);
}
return result;
}
static void init_bitsets()
{
// DFs that we directly understand without correction
valid_df_short_bitset = (1 << 0) | (1 << 4) | (1 << 5) | (1 << 11);
valid_df_long_bitset = (1 << 16) | (1 << 17) | (1 << 18) | (1 << 20) | (1 << 21);
if (Modes.enable_df24)
valid_df_long_bitset |= (1 << 24) | (1 << 25) | (1 << 26) | (1 << 27) | (1 << 28) | (1 << 29) | (1 << 30) | (1 << 31);
// if we can also repair DF damage, include those corrections
if (Modes.fix_df && Modes.nfix_crc) {
valid_df_short_bitset |= generate_damage_set(11, 1);
valid_df_long_bitset |= generate_damage_set(17, Modes.nfix_crc);
valid_df_long_bitset |= generate_damage_set(18, Modes.nfix_crc);
}
}
//
// Given 'mlen' magnitude samples in 'm', sampled at 2.4MHz,
// try to demodulate some Mode S messages.
@ -69,6 +103,17 @@ void demodulate2400(struct mag_buf *mag)
unsigned char msg1[MODES_LONG_MSG_BYTES], msg2[MODES_LONG_MSG_BYTES], *msg;
uint32_t j;
static unsigned last_message_end = 0;
// initialize bitsets on first call
if (!valid_df_short_bitset)
init_bitsets();
if (mag->flags & MAGBUF_DISCONTINUOUS) {
// gap, start from the very beginning
last_message_end = 0;
}
unsigned char *bestmsg;
int bestscore, bestphase;
@ -82,7 +127,11 @@ void demodulate2400(struct mag_buf *mag)
msg = msg1;
for (j = 0; j < mlen; j++) {
// sanity check
if (last_message_end > mlen)
last_message_end = mlen;
for (j = last_message_end; j < mlen; j++) {
uint16_t *preamble = &m[j];
int high;
uint32_t base_signal, base_noise;
@ -173,7 +222,7 @@ void demodulate2400(struct mag_buf *mag)
bestmsg = NULL; bestscore = SR_NOT_SET; bestphase = -1;
for (try_phase = 4; try_phase <= 8; ++try_phase) {
uint16_t *pPtr;
int phase, i, score;
int phase, score;
// Decode all the next 112 bits, regardless of the actual message
// size. We'll check the actual message type later
@ -181,7 +230,8 @@ void demodulate2400(struct mag_buf *mag)
pPtr = &m[j+19] + (try_phase/5);
phase = try_phase % 5;
for (i = 0; i < MODES_LONG_MSG_BYTES; ++i) {
unsigned bytelen = 1;
for (unsigned i = 0; i < bytelen; ++i) {
uint8_t theByte = 0;
switch (phase) {
@ -263,6 +313,22 @@ void demodulate2400(struct mag_buf *mag)
}
msg[i] = theByte;
if (i == 0) {
// inspect DF field early, only continue processing
// messages where the DF appears valid
unsigned df = theByte >> 3;
if (valid_df_long_bitset & (1 << df))
bytelen = MODES_LONG_MSG_BYTES;
else if (valid_df_short_bitset & (1 << df))
bytelen = MODES_SHORT_MSG_BYTES;
}
}
if (bytelen == 1) {
// rejected early by the DF filter
Modes.stats_current.demod_rejected_bad++;
continue;
}
// Score the mode S message and see if it's any good.
@ -336,13 +402,21 @@ void demodulate2400(struct mag_buf *mag)
Modes.stats_current.strong_signal_count++; // signal power above -3dBFS
}
// Feed "empty" sample to adaptive gain logic
if (j > last_message_end)
adaptive_update(&m[last_message_end], j - last_message_end, NULL);
// Feed message samples to adaptive gain logic, update end pointer
last_message_end = j + (msglen + 8) * 12/5;
adaptive_update(&m[j], last_message_end - j, &mm);
// Skip over the message:
// (we actually skip to 8 bits before the end of the message,
// because we can often decode two messages that *almost* collide,
// where the preamble of the second message clobbered the last
// few bits of the first message, but the message bits didn't
// overlap)
j += msglen*12/5;
j = last_message_end - 8*12/5;
// Pass data to the next layer
useModesMessage(&mm);
@ -354,8 +428,20 @@ void demodulate2400(struct mag_buf *mag)
Modes.stats_current.noise_power_sum += (mag->mean_power * mlen - sum_signal_power);
Modes.stats_current.noise_power_count += mlen;
}
}
// feed trailing empty samples to adaptive gain logic
if (last_message_end < mlen) {
// trailing data from end of last message to start of overlap;
// on the next pass, start from the start of the overlap
adaptive_update(&m[last_message_end], mlen - last_message_end, NULL);
last_message_end = 0;
} else {
// last decoded message runs into the overlap region;
// on the next pass, start at the right place in the overlap;
// no trailing data to pass this time
last_message_end -= mlen;
}
}
#ifdef MODEAC_DEBUG

View File

@ -16,6 +16,6 @@ typedef union {
typedef struct {
int16_t I;
int16_t Q;
} __attribute__((__packed__)) sc16_t;
} __attribute__((__packed__, __aligned__(2))) sc16_t;
#endif

View File

@ -0,0 +1,39 @@
#include <stdlib.h>
void STARCH_BENCHMARK(count_above_u16) (void)
{
uint16_t *in = NULL;
const unsigned len = 96; /* Typical use is with short burst windows (40us) */
const unsigned threshold = 46395; /* -3dBFS */
if (!(in = STARCH_BENCHMARK_ALLOC(len, uint16_t))) {
goto done;
}
srand(1);
for (unsigned i = 0; i < len; ++i) {
in[i] = rand() % 65536;
}
unsigned count;
STARCH_BENCHMARK_RUN( count_above_u16, in, len, threshold, &count );
done:
STARCH_BENCHMARK_FREE(in);
}
bool STARCH_BENCHMARK_VERIFY(count_above_u16) (const uint16_t *in, unsigned len, uint16_t threshold, unsigned *out_count)
{
unsigned expected = 0;
for (unsigned i = 0; i < len; ++i) {
if (in[i] >= threshold)
++expected;
}
if (expected != *out_count) {
fprintf(stderr, "verification failed: expected count %u, got count %u\n", expected, *out_count);
return false;
}
return true;
}

View File

@ -12,6 +12,16 @@
#include <unistd.h>
#include <errno.h>
//On Windows platform, separate functions for allocating and freeing aligned memory must be used
#ifdef _WIN32
# include <malloc.h>
# define util_aligned_alloc(alignment,size) (_aligned_malloc(size, alignment))
# define util_aligned_free(ptr) (_aligned_free(ptr))
#else
# define util_aligned_alloc(alignment,size) (aligned_alloc(alignment,size))
# define util_aligned_free(ptr) (free(ptr))
#endif
#include "starch.h"
typedef struct timespec starch_benchmark_time;
@ -76,7 +86,7 @@ void *starch_benchmark_aligned_alloc(size_t alignment, size_t type_alignment, si
* of a more restrictive larger alignment)
*/
size_t header_size = (use_alignment < sizeof(void*) ? sizeof(void*) : use_alignment);
char *block_ptr = aligned_alloc(use_alignment, header_size + size + use_alignment);
char *block_ptr = util_aligned_alloc(use_alignment, header_size + size + use_alignment);
if (!block_ptr) {
fprintf(stderr, "STARCH_BENCHMARK_ALLOC of %zu bytes failed: %s\n", size, strerror(errno));
return NULL;
@ -99,7 +109,7 @@ void starch_benchmark_aligned_free(void *user_ptr)
if (!user_ptr)
return;
void **stash = (void**)user_ptr - 1;
free(*stash);
util_aligned_free(*stash);
}
static bool starch_benchmark_flavor_in_list(const char *flavor, const starch_benchmark_flavor_list *list)
@ -112,6 +122,230 @@ static bool starch_benchmark_flavor_in_list(const char *flavor, const starch_ben
}
/* prototypes for benchmark helpers provided by user code */
void starch_count_above_u16_benchmark (void);
bool starch_count_above_u16_benchmark_verify ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
/* prototype the benchmarking function so that we can build with -Wmissing-declarations */
void starch_count_above_u16_benchmark(void);
static void starch_benchmark_one_count_above_u16( starch_count_above_u16_regentry * _entry, const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 )
{
fprintf(stderr, " %-40s ", _entry->name);
/* test for support */
if (_entry->flavor_supported && !(_entry->flavor_supported())) {
fprintf(stderr, "unsupported\n");
return;
}
if (starch_benchmark_flavor_whitelist && !starch_benchmark_flavor_in_list(_entry->flavor, starch_benchmark_flavor_whitelist)) {
fprintf(stderr, "skipped (not whitelisted)\n");
return;
}
if (starch_benchmark_flavor_blacklist && starch_benchmark_flavor_in_list(_entry->flavor, starch_benchmark_flavor_blacklist)) {
fprintf(stderr, "skipped (blacklisted)\n");
return;
}
if (starch_benchmark_list_only) {
fprintf(stderr, "supported\n");
return;
}
/* initial warmup */
for (unsigned _loop = 0; _loop < starch_benchmark_warmup_loops; ++_loop)
_entry->callable ( arg0, arg1, arg2, arg3 );
/* verify correctness of the output */
if (! starch_count_above_u16_benchmark_verify ( arg0, arg1, arg2, arg3 )) {
fprintf(stderr, "skipped (verification failed)\n");
starch_benchmark_validation_failed = true;
return;
}
if (starch_benchmark_validate_only) {
fprintf(stderr, "validation ok\n");
return;
}
/* pre-benchmark, find a loop count that takes at least 100ms */
starch_benchmark_time _start, _end;
uint64_t _elapsed = 0;
uint64_t _loops = 127;
while (_elapsed < 100000000) {
_loops *= 2;
starch_benchmark_get_time(&_start);
for (uint64_t _loop = 0; _loop < _loops; ++_loop)
_entry->callable ( arg0, arg1, arg2, arg3 );
starch_benchmark_get_time(&_end);
_elapsed = starch_benchmark_elapsed(&_start, &_end);
}
/* real benchmark, run for approx 1 second */
_loops = _loops * 1000000000 / _elapsed;
_elapsed = 0;
uint64_t _elapsed_min = UINT64_MAX;
uint64_t _elapsed_max = 0;
for (unsigned _iter = 0; _iter < starch_benchmark_iterations; ++_iter) {
starch_benchmark_get_time(&_start);
for (uint64_t _loop = 0; _loop < _loops; ++_loop)
_entry->callable ( arg0, arg1, arg2, arg3 );
starch_benchmark_get_time(&_end);
uint64_t _elapsed_one = starch_benchmark_elapsed(&_start, &_end);
if (_elapsed_one < _elapsed_min)
_elapsed_min = _elapsed_one;
if (_elapsed_one > _elapsed_max)
_elapsed_max = _elapsed_one;
_elapsed += _elapsed_one;
}
uint64_t _per_loop;
if (starch_benchmark_iterations > 2)
_per_loop = (_elapsed - _elapsed_min - _elapsed_max) / _loops / (starch_benchmark_iterations - 2);
else
_per_loop = _elapsed / _loops / starch_benchmark_iterations;
fprintf(stderr, "%" PRIu64 " ns/call\n", _per_loop);
if (starch_benchmark_result_count >= starch_benchmark_result_size) {
if (!starch_benchmark_result_size)
starch_benchmark_result_size = 64;
else
starch_benchmark_result_size *= 2;
starch_benchmark_results = realloc(starch_benchmark_results, starch_benchmark_result_size * sizeof(*starch_benchmark_results));
if (!starch_benchmark_results) {
fprintf(stderr, "realloc: %s\n", strerror(errno));
exit(1);
}
}
starch_benchmark_results[starch_benchmark_result_count].name = "count_above_u16";
starch_benchmark_results[starch_benchmark_result_count].impl = _entry->name;
starch_benchmark_results[starch_benchmark_result_count].ns = _per_loop;
++starch_benchmark_result_count;
}
static void starch_benchmark_run_count_above_u16( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 )
{
for (starch_count_above_u16_regentry *_entry = starch_count_above_u16_registry; _entry->name; ++_entry) {
starch_benchmark_one_count_above_u16( _entry, arg0, arg1, arg2, arg3 );
}
}
/* prototypes for benchmark helpers provided by user code */
void starch_count_above_u16_aligned_benchmark (void);
bool starch_count_above_u16_aligned_benchmark_verify ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
/* prototype the benchmarking function so that we can build with -Wmissing-declarations */
void starch_count_above_u16_aligned_benchmark(void);
static void starch_benchmark_one_count_above_u16_aligned( starch_count_above_u16_aligned_regentry * _entry, const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 )
{
fprintf(stderr, " %-40s ", _entry->name);
/* test for support */
if (_entry->flavor_supported && !(_entry->flavor_supported())) {
fprintf(stderr, "unsupported\n");
return;
}
if (starch_benchmark_flavor_whitelist && !starch_benchmark_flavor_in_list(_entry->flavor, starch_benchmark_flavor_whitelist)) {
fprintf(stderr, "skipped (not whitelisted)\n");
return;
}
if (starch_benchmark_flavor_blacklist && starch_benchmark_flavor_in_list(_entry->flavor, starch_benchmark_flavor_blacklist)) {
fprintf(stderr, "skipped (blacklisted)\n");
return;
}
if (starch_benchmark_list_only) {
fprintf(stderr, "supported\n");
return;
}
/* initial warmup */
for (unsigned _loop = 0; _loop < starch_benchmark_warmup_loops; ++_loop)
_entry->callable ( arg0, arg1, arg2, arg3 );
/* verify correctness of the output */
if (! starch_count_above_u16_aligned_benchmark_verify ( arg0, arg1, arg2, arg3 )) {
fprintf(stderr, "skipped (verification failed)\n");
starch_benchmark_validation_failed = true;
return;
}
if (starch_benchmark_validate_only) {
fprintf(stderr, "validation ok\n");
return;
}
/* pre-benchmark, find a loop count that takes at least 100ms */
starch_benchmark_time _start, _end;
uint64_t _elapsed = 0;
uint64_t _loops = 127;
while (_elapsed < 100000000) {
_loops *= 2;
starch_benchmark_get_time(&_start);
for (uint64_t _loop = 0; _loop < _loops; ++_loop)
_entry->callable ( arg0, arg1, arg2, arg3 );
starch_benchmark_get_time(&_end);
_elapsed = starch_benchmark_elapsed(&_start, &_end);
}
/* real benchmark, run for approx 1 second */
_loops = _loops * 1000000000 / _elapsed;
_elapsed = 0;
uint64_t _elapsed_min = UINT64_MAX;
uint64_t _elapsed_max = 0;
for (unsigned _iter = 0; _iter < starch_benchmark_iterations; ++_iter) {
starch_benchmark_get_time(&_start);
for (uint64_t _loop = 0; _loop < _loops; ++_loop)
_entry->callable ( arg0, arg1, arg2, arg3 );
starch_benchmark_get_time(&_end);
uint64_t _elapsed_one = starch_benchmark_elapsed(&_start, &_end);
if (_elapsed_one < _elapsed_min)
_elapsed_min = _elapsed_one;
if (_elapsed_one > _elapsed_max)
_elapsed_max = _elapsed_one;
_elapsed += _elapsed_one;
}
uint64_t _per_loop;
if (starch_benchmark_iterations > 2)
_per_loop = (_elapsed - _elapsed_min - _elapsed_max) / _loops / (starch_benchmark_iterations - 2);
else
_per_loop = _elapsed / _loops / starch_benchmark_iterations;
fprintf(stderr, "%" PRIu64 " ns/call\n", _per_loop);
if (starch_benchmark_result_count >= starch_benchmark_result_size) {
if (!starch_benchmark_result_size)
starch_benchmark_result_size = 64;
else
starch_benchmark_result_size *= 2;
starch_benchmark_results = realloc(starch_benchmark_results, starch_benchmark_result_size * sizeof(*starch_benchmark_results));
if (!starch_benchmark_results) {
fprintf(stderr, "realloc: %s\n", strerror(errno));
exit(1);
}
}
starch_benchmark_results[starch_benchmark_result_count].name = "count_above_u16_aligned";
starch_benchmark_results[starch_benchmark_result_count].impl = _entry->name;
starch_benchmark_results[starch_benchmark_result_count].ns = _per_loop;
++starch_benchmark_result_count;
}
static void starch_benchmark_run_count_above_u16_aligned( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 )
{
for (starch_count_above_u16_aligned_regentry *_entry = starch_count_above_u16_aligned_registry; _entry->name; ++_entry) {
starch_benchmark_one_count_above_u16_aligned( _entry, arg0, arg1, arg2, arg3 );
}
}
/* prototypes for benchmark helpers provided by user code */
void starch_magnitude_power_uc8_benchmark (void);
bool starch_magnitude_power_uc8_benchmark_verify ( const uc8_t * arg0, uint16_t * arg1, unsigned arg2, double * arg3, double * arg4 );
@ -1246,6 +1480,7 @@ static void starch_benchmark_run_mean_power_u16_aligned( const uint16_t * arg0,
#define STARCH_BENCHMARK_ALLOC(_count, _type) ((_type *) starch_benchmark_aligned_alloc(1, alignof(_type), (_count) * sizeof(_type)))
#define STARCH_BENCHMARK_FREE(_ptr) starch_benchmark_aligned_free(_ptr)
#include "../benchmark/count_above_u16_benchmark.c"
#include "../benchmark/magnitude_power_uc8_benchmark.c"
#include "../benchmark/magnitude_sc16_benchmark.c"
#include "../benchmark/magnitude_sc16q11_benchmark.c"
@ -1274,12 +1509,23 @@ static void starch_benchmark_run_mean_power_u16_aligned( const uint16_t * arg0,
#define STARCH_BENCHMARK_ALLOC(_count, _type) ((_type *) starch_benchmark_aligned_alloc(STARCH_MIX_ALIGNMENT, alignof(_type), (_count) * sizeof(_type)))
#define STARCH_BENCHMARK_FREE(_ptr) starch_benchmark_aligned_free(_ptr)
#include "../benchmark/count_above_u16_benchmark.c"
#include "../benchmark/magnitude_power_uc8_benchmark.c"
#include "../benchmark/magnitude_sc16_benchmark.c"
#include "../benchmark/magnitude_sc16q11_benchmark.c"
#include "../benchmark/magnitude_uc8_benchmark.c"
#include "../benchmark/mean_power_u16_benchmark.c"
static void starch_benchmark_all_count_above_u16(void)
{
fprintf(stderr, "==== count_above_u16 ===\n");
starch_count_above_u16_benchmark ();
}
static void starch_benchmark_all_count_above_u16_aligned(void)
{
fprintf(stderr, "==== count_above_u16_aligned ===\n");
starch_count_above_u16_aligned_benchmark ();
}
static void starch_benchmark_all_magnitude_power_uc8(void)
{
fprintf(stderr, "==== magnitude_power_uc8 ===\n");
@ -1383,6 +1629,8 @@ static void starch_benchmark_usage(const char *argv0)
#endif
"\n"
"Supported functions: "
"count_above_u16 "
"count_above_u16_aligned "
"magnitude_power_uc8 "
"magnitude_power_uc8_aligned "
"magnitude_sc16 "
@ -1478,6 +1726,16 @@ int main(int argc, char **argv)
}
for (int i = optind; i < argc; ++i) {
if (!strcmp(argv[i], "count_above_u16")) {
specific = 1;
starch_benchmark_all_count_above_u16();
continue;
}
if (!strcmp(argv[i], "count_above_u16_aligned")) {
specific = 1;
starch_benchmark_all_count_above_u16_aligned();
continue;
}
if (!strcmp(argv[i], "magnitude_power_uc8")) {
specific = 1;
starch_benchmark_all_magnitude_power_uc8();
@ -1534,6 +1792,8 @@ int main(int argc, char **argv)
}
if (!specific) {
starch_benchmark_all_count_above_u16();
starch_benchmark_all_count_above_u16_aligned();
starch_benchmark_all_magnitude_power_uc8();
starch_benchmark_all_magnitude_power_uc8_aligned();
starch_benchmark_all_magnitude_sc16();

View File

@ -19,6 +19,165 @@ static int starch_regentry_rank_compare (const void *l, const void *r)
return left->rank - right->rank;
}
/* dispatcher / registry for count_above_u16 */
starch_count_above_u16_regentry * starch_count_above_u16_select() {
for (starch_count_above_u16_regentry *entry = starch_count_above_u16_registry;
entry->name;
++entry)
{
if (entry->flavor_supported && !(entry->flavor_supported()))
continue;
return entry;
}
return NULL;
}
static void starch_count_above_u16_dispatch ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 ) {
starch_count_above_u16_regentry *entry = starch_count_above_u16_select();
if (!entry)
abort();
starch_count_above_u16 = entry->callable;
starch_count_above_u16 ( arg0, arg1, arg2, arg3 );
}
starch_count_above_u16_ptr starch_count_above_u16 = starch_count_above_u16_dispatch;
void starch_count_above_u16_set_wisdom (const char * const * received_wisdom)
{
/* re-rank the registry based on received wisdom */
starch_count_above_u16_regentry *entry;
for (entry = starch_count_above_u16_registry; entry->name; ++entry) {
const char * const *search;
for (search = received_wisdom; *search; ++search) {
if (!strcmp(*search, entry->name)) {
break;
}
}
if (*search) {
/* matches an entry in the wisdom list, order by position in the list */
entry->rank = search - received_wisdom;
} else {
/* no match, rank after all possible matches, retaining existing order */
entry->rank = (search - received_wisdom) + (entry - starch_count_above_u16_registry);
}
}
/* re-sort based on the new ranking */
qsort(starch_count_above_u16_registry, entry - starch_count_above_u16_registry, sizeof(starch_count_above_u16_regentry), starch_regentry_rank_compare);
/* reset the implementation pointer so the next call will re-select */
starch_count_above_u16 = starch_count_above_u16_dispatch;
}
starch_count_above_u16_regentry starch_count_above_u16_registry[] = {
#ifdef STARCH_MIX_AARCH64
{ 0, "generic_armv8_neon_simd", "armv8_neon_simd", starch_count_above_u16_generic_armv8_neon_simd, cpu_supports_armv8_simd },
{ 1, "neon_armv8_neon_simd", "armv8_neon_simd", starch_count_above_u16_neon_armv8_neon_simd, cpu_supports_armv8_simd },
{ 2, "generic_generic", "generic", starch_count_above_u16_generic_generic, NULL },
#endif /* STARCH_MIX_AARCH64 */
#ifdef STARCH_MIX_ARM
{ 0, "neon_armv7a_neon_vfpv4", "armv7a_neon_vfpv4", starch_count_above_u16_neon_armv7a_neon_vfpv4, cpu_supports_armv7_neon_vfpv4 },
{ 1, "generic_generic", "generic", starch_count_above_u16_generic_generic, NULL },
{ 2, "generic_armv7a_neon_vfpv4", "armv7a_neon_vfpv4", starch_count_above_u16_generic_armv7a_neon_vfpv4, cpu_supports_armv7_neon_vfpv4 },
#endif /* STARCH_MIX_ARM */
#ifdef STARCH_MIX_GENERIC
{ 0, "generic_generic", "generic", starch_count_above_u16_generic_generic, NULL },
#endif /* STARCH_MIX_GENERIC */
#ifdef STARCH_MIX_X86
{ 0, "generic_x86_avx2", "x86_avx2", starch_count_above_u16_generic_x86_avx2, cpu_supports_avx2 },
{ 1, "generic_generic", "generic", starch_count_above_u16_generic_generic, NULL },
#endif /* STARCH_MIX_X86 */
{ 0, NULL, NULL, NULL, NULL }
};
/* dispatcher / registry for count_above_u16_aligned */
starch_count_above_u16_aligned_regentry * starch_count_above_u16_aligned_select() {
for (starch_count_above_u16_aligned_regentry *entry = starch_count_above_u16_aligned_registry;
entry->name;
++entry)
{
if (entry->flavor_supported && !(entry->flavor_supported()))
continue;
return entry;
}
return NULL;
}
static void starch_count_above_u16_aligned_dispatch ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 ) {
starch_count_above_u16_aligned_regentry *entry = starch_count_above_u16_aligned_select();
if (!entry)
abort();
starch_count_above_u16_aligned = entry->callable;
starch_count_above_u16_aligned ( arg0, arg1, arg2, arg3 );
}
starch_count_above_u16_aligned_ptr starch_count_above_u16_aligned = starch_count_above_u16_aligned_dispatch;
void starch_count_above_u16_aligned_set_wisdom (const char * const * received_wisdom)
{
/* re-rank the registry based on received wisdom */
starch_count_above_u16_aligned_regentry *entry;
for (entry = starch_count_above_u16_aligned_registry; entry->name; ++entry) {
const char * const *search;
for (search = received_wisdom; *search; ++search) {
if (!strcmp(*search, entry->name)) {
break;
}
}
if (*search) {
/* matches an entry in the wisdom list, order by position in the list */
entry->rank = search - received_wisdom;
} else {
/* no match, rank after all possible matches, retaining existing order */
entry->rank = (search - received_wisdom) + (entry - starch_count_above_u16_aligned_registry);
}
}
/* re-sort based on the new ranking */
qsort(starch_count_above_u16_aligned_registry, entry - starch_count_above_u16_aligned_registry, sizeof(starch_count_above_u16_aligned_regentry), starch_regentry_rank_compare);
/* reset the implementation pointer so the next call will re-select */
starch_count_above_u16_aligned = starch_count_above_u16_aligned_dispatch;
}
starch_count_above_u16_aligned_regentry starch_count_above_u16_aligned_registry[] = {
#ifdef STARCH_MIX_AARCH64
{ 0, "generic_armv8_neon_simd_aligned", "armv8_neon_simd", starch_count_above_u16_aligned_generic_armv8_neon_simd, cpu_supports_armv8_simd },
{ 1, "neon_armv8_neon_simd_aligned", "armv8_neon_simd", starch_count_above_u16_aligned_neon_armv8_neon_simd, cpu_supports_armv8_simd },
{ 2, "generic_armv8_neon_simd", "armv8_neon_simd", starch_count_above_u16_generic_armv8_neon_simd, cpu_supports_armv8_simd },
{ 3, "neon_armv8_neon_simd", "armv8_neon_simd", starch_count_above_u16_neon_armv8_neon_simd, cpu_supports_armv8_simd },
{ 4, "generic_generic", "generic", starch_count_above_u16_generic_generic, NULL },
#endif /* STARCH_MIX_AARCH64 */
#ifdef STARCH_MIX_ARM
{ 0, "neon_armv7a_neon_vfpv4", "armv7a_neon_vfpv4", starch_count_above_u16_neon_armv7a_neon_vfpv4, cpu_supports_armv7_neon_vfpv4 },
{ 1, "generic_generic", "generic", starch_count_above_u16_generic_generic, NULL },
{ 2, "generic_armv7a_neon_vfpv4_aligned", "armv7a_neon_vfpv4", starch_count_above_u16_aligned_generic_armv7a_neon_vfpv4, cpu_supports_armv7_neon_vfpv4 },
{ 3, "neon_armv7a_neon_vfpv4_aligned", "armv7a_neon_vfpv4", starch_count_above_u16_aligned_neon_armv7a_neon_vfpv4, cpu_supports_armv7_neon_vfpv4 },
{ 4, "generic_armv7a_neon_vfpv4", "armv7a_neon_vfpv4", starch_count_above_u16_generic_armv7a_neon_vfpv4, cpu_supports_armv7_neon_vfpv4 },
#endif /* STARCH_MIX_ARM */
#ifdef STARCH_MIX_GENERIC
{ 0, "generic_generic", "generic", starch_count_above_u16_generic_generic, NULL },
#endif /* STARCH_MIX_GENERIC */
#ifdef STARCH_MIX_X86
{ 0, "generic_x86_avx2_aligned", "x86_avx2", starch_count_above_u16_aligned_generic_x86_avx2, cpu_supports_avx2 },
{ 1, "generic_generic", "generic", starch_count_above_u16_generic_generic, NULL },
{ 2, "generic_x86_avx2", "x86_avx2", starch_count_above_u16_generic_x86_avx2, cpu_supports_avx2 },
#endif /* STARCH_MIX_X86 */
{ 0, NULL, NULL, NULL, NULL }
};
/* dispatcher / registry for magnitude_power_uc8 */
starch_magnitude_power_uc8_regentry * starch_magnitude_power_uc8_select() {
@ -992,6 +1151,14 @@ int starch_read_wisdom (const char * path)
return -1;
/* reset all ranks to identify entries not listed in the wisdom file; we'll assign ranks at the end to produce a stable sort */
int rank_count_above_u16 = 0;
for (starch_count_above_u16_regentry *entry = starch_count_above_u16_registry; entry->name; ++entry) {
entry->rank = 0;
}
int rank_count_above_u16_aligned = 0;
for (starch_count_above_u16_aligned_regentry *entry = starch_count_above_u16_aligned_registry; entry->name; ++entry) {
entry->rank = 0;
}
int rank_magnitude_power_uc8 = 0;
for (starch_magnitude_power_uc8_regentry *entry = starch_magnitude_power_uc8_registry; entry->name; ++entry) {
entry->rank = 0;
@ -1065,6 +1232,24 @@ int starch_read_wisdom (const char * path)
*end = 0;
/* try to find a matching registry entry */
if (!strcmp(name, "count_above_u16")) {
for (starch_count_above_u16_regentry *entry = starch_count_above_u16_registry; entry->name; ++entry) {
if (!strcmp(impl, entry->name)) {
entry->rank = ++rank_count_above_u16;
break;
}
}
continue;
}
if (!strcmp(name, "count_above_u16_aligned")) {
for (starch_count_above_u16_aligned_regentry *entry = starch_count_above_u16_aligned_registry; entry->name; ++entry) {
if (!strcmp(impl, entry->name)) {
entry->rank = ++rank_count_above_u16_aligned;
break;
}
}
continue;
}
if (!strcmp(name, "magnitude_power_uc8")) {
for (starch_magnitude_power_uc8_regentry *entry = starch_magnitude_power_uc8_registry; entry->name; ++entry) {
if (!strcmp(impl, entry->name)) {
@ -1165,6 +1350,28 @@ int starch_read_wisdom (const char * path)
fclose(fp);
/* assign ranks to unmatched items to (stable) sort them last; re-sort everything */
{
starch_count_above_u16_regentry *entry;
for (entry = starch_count_above_u16_registry; entry->name; ++entry) {
if (!entry->rank)
entry->rank = ++rank_count_above_u16;
}
qsort(starch_count_above_u16_registry, entry - starch_count_above_u16_registry, sizeof(starch_count_above_u16_regentry), starch_regentry_rank_compare);
/* reset the implementation pointer so the next call will re-select */
starch_count_above_u16 = starch_count_above_u16_dispatch;
}
{
starch_count_above_u16_aligned_regentry *entry;
for (entry = starch_count_above_u16_aligned_registry; entry->name; ++entry) {
if (!entry->rank)
entry->rank = ++rank_count_above_u16_aligned;
}
qsort(starch_count_above_u16_aligned_registry, entry - starch_count_above_u16_aligned_registry, sizeof(starch_count_above_u16_aligned_regentry), starch_regentry_rank_compare);
/* reset the implementation pointer so the next call will re-select */
starch_count_above_u16_aligned = starch_count_above_u16_aligned_dispatch;
}
{
starch_magnitude_power_uc8_regentry *entry;
for (entry = starch_magnitude_power_uc8_registry; entry->name; ++entry) {

View File

@ -14,6 +14,7 @@
#define STARCH_IMPL(_function,_impl) starch_ ## _function ## _ ## _impl ## _ ## armv7a_neon_vfpv4
#define STARCH_IMPL_REQUIRES(_function,_impl,_feature) STARCH_IMPL(_function,_impl)
#include "../impl/count_above_u16.c"
#include "../impl/magnitude_power_uc8.c"
#include "../impl/magnitude_sc16.c"
#include "../impl/magnitude_sc16q11.c"
@ -33,6 +34,7 @@
#define STARCH_IMPL(_function,_impl) starch_ ## _function ## _aligned_ ## _impl ## _ ## armv7a_neon_vfpv4
#define STARCH_IMPL_REQUIRES(_function,_impl,_feature) STARCH_IMPL(_function,_impl)
#include "../impl/count_above_u16.c"
#include "../impl/magnitude_power_uc8.c"
#include "../impl/magnitude_sc16.c"
#include "../impl/magnitude_sc16q11.c"

View File

@ -14,6 +14,7 @@
#define STARCH_IMPL(_function,_impl) starch_ ## _function ## _ ## _impl ## _ ## armv8_neon_simd
#define STARCH_IMPL_REQUIRES(_function,_impl,_feature) STARCH_IMPL(_function,_impl)
#include "../impl/count_above_u16.c"
#include "../impl/magnitude_power_uc8.c"
#include "../impl/magnitude_sc16.c"
#include "../impl/magnitude_sc16q11.c"
@ -33,6 +34,7 @@
#define STARCH_IMPL(_function,_impl) starch_ ## _function ## _aligned_ ## _impl ## _ ## armv8_neon_simd
#define STARCH_IMPL_REQUIRES(_function,_impl,_feature) STARCH_IMPL(_function,_impl)
#include "../impl/count_above_u16.c"
#include "../impl/magnitude_power_uc8.c"
#include "../impl/magnitude_sc16.c"
#include "../impl/magnitude_sc16q11.c"

View File

@ -13,6 +13,7 @@
#define STARCH_IMPL(_function,_impl) starch_ ## _function ## _ ## _impl ## _ ## generic
#define STARCH_IMPL_REQUIRES(_function,_impl,_feature) STARCH_IMPL(_function,_impl)
#include "../impl/count_above_u16.c"
#include "../impl/magnitude_power_uc8.c"
#include "../impl/magnitude_sc16.c"
#include "../impl/magnitude_sc16q11.c"

View File

@ -13,6 +13,7 @@
#define STARCH_IMPL(_function,_impl) starch_ ## _function ## _ ## _impl ## _ ## x86_avx2
#define STARCH_IMPL_REQUIRES(_function,_impl,_feature) STARCH_IMPL(_function,_impl)
#include "../impl/count_above_u16.c"
#include "../impl/magnitude_power_uc8.c"
#include "../impl/magnitude_sc16.c"
#include "../impl/magnitude_sc16q11.c"
@ -32,6 +33,7 @@
#define STARCH_IMPL(_function,_impl) starch_ ## _function ## _aligned_ ## _impl ## _ ## x86_avx2
#define STARCH_IMPL_REQUIRES(_function,_impl,_feature) STARCH_IMPL(_function,_impl)
#include "../impl/count_above_u16.c"
#include "../impl/magnitude_power_uc8.c"
#include "../impl/magnitude_sc16.c"
#include "../impl/magnitude_sc16q11.c"

View File

@ -18,22 +18,27 @@
# $(STARCH_BENCHMARK_OBJ): object files providing a standalone benchmarking app (link all of $(STARCH_OBJS) too)
# explicit build rules for each object file listed in $(STARCH_OBJS)
MKDIR_P = mkdir -p
STARCH_CFLAGS := -DSTARCH_MIX_AARCH64
dsp/generated/flavor.armv8_neon_simd.o: dsp/generated/flavor.armv8_neon_simd.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) -march=armv8-a+simd -ffast-math dsp/generated/flavor.armv8_neon_simd.c -o dsp/generated/flavor.armv8_neon_simd.o
dsp/generated/flavor.armv8_neon_simd.o: dsp/generated/flavor.armv8_neon_simd.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/flavor.armv8_neon_simd.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) -march=armv8-a+simd -ffast-math dsp/generated/flavor.armv8_neon_simd.c -o $(STARCH_OBJ_PATH)dsp/generated/flavor.armv8_neon_simd.o
dsp/generated/flavor.generic.o: dsp/generated/flavor.generic.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/flavor.generic.c -o dsp/generated/flavor.generic.o
dsp/generated/flavor.generic.o: dsp/generated/flavor.generic.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/flavor.generic.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/flavor.generic.c -o $(STARCH_OBJ_PATH)dsp/generated/flavor.generic.o
dsp/generated/dispatcher.o: dsp/generated/dispatcher.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/dispatcher.c -o dsp/generated/dispatcher.o
dsp/generated/dispatcher.o: dsp/generated/dispatcher.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/dispatcher.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/dispatcher.c -o $(STARCH_OBJ_PATH)dsp/generated/dispatcher.o
STARCH_OBJS := dsp/generated/flavor.armv8_neon_simd.o dsp/generated/flavor.generic.o dsp/generated/dispatcher.o
dsp/generated/benchmark.o: dsp/generated/benchmark.c dsp/benchmark/magnitude_sc16_benchmark.c dsp/benchmark/magnitude_uc8_benchmark.c dsp/benchmark/magnitude_power_uc8_benchmark.c dsp/benchmark/mean_power_u16_benchmark.c dsp/benchmark/magnitude_sc16q11_benchmark.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/benchmark.c -o dsp/generated/benchmark.o
dsp/generated/benchmark.o: dsp/generated/benchmark.c dsp/benchmark/magnitude_sc16_benchmark.c dsp/benchmark/magnitude_uc8_benchmark.c dsp/benchmark/magnitude_power_uc8_benchmark.c dsp/benchmark/mean_power_u16_benchmark.c dsp/benchmark/count_above_u16_benchmark.c dsp/benchmark/magnitude_sc16q11_benchmark.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/benchmark.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/benchmark.c -o $(STARCH_OBJ_PATH)dsp/generated/benchmark.o
STARCH_BENCHMARK_OBJ := dsp/generated/benchmark.o

View File

@ -18,22 +18,27 @@
# $(STARCH_BENCHMARK_OBJ): object files providing a standalone benchmarking app (link all of $(STARCH_OBJS) too)
# explicit build rules for each object file listed in $(STARCH_OBJS)
MKDIR_P = mkdir -p
STARCH_CFLAGS := -DSTARCH_MIX_ARM
dsp/generated/flavor.armv7a_neon_vfpv4.o: dsp/generated/flavor.armv7a_neon_vfpv4.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) -march=armv7-a+neon-vfpv4 -mfpu=neon-vfpv4 -ffast-math dsp/generated/flavor.armv7a_neon_vfpv4.c -o dsp/generated/flavor.armv7a_neon_vfpv4.o
dsp/generated/flavor.armv7a_neon_vfpv4.o: dsp/generated/flavor.armv7a_neon_vfpv4.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/flavor.armv7a_neon_vfpv4.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) -march=armv7-a+neon-vfpv4 -mfpu=neon-vfpv4 -ffast-math dsp/generated/flavor.armv7a_neon_vfpv4.c -o $(STARCH_OBJ_PATH)dsp/generated/flavor.armv7a_neon_vfpv4.o
dsp/generated/flavor.generic.o: dsp/generated/flavor.generic.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/flavor.generic.c -o dsp/generated/flavor.generic.o
dsp/generated/flavor.generic.o: dsp/generated/flavor.generic.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/flavor.generic.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/flavor.generic.c -o $(STARCH_OBJ_PATH)dsp/generated/flavor.generic.o
dsp/generated/dispatcher.o: dsp/generated/dispatcher.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/dispatcher.c -o dsp/generated/dispatcher.o
dsp/generated/dispatcher.o: dsp/generated/dispatcher.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/dispatcher.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/dispatcher.c -o $(STARCH_OBJ_PATH)dsp/generated/dispatcher.o
STARCH_OBJS := dsp/generated/flavor.armv7a_neon_vfpv4.o dsp/generated/flavor.generic.o dsp/generated/dispatcher.o
dsp/generated/benchmark.o: dsp/generated/benchmark.c dsp/benchmark/magnitude_sc16_benchmark.c dsp/benchmark/magnitude_uc8_benchmark.c dsp/benchmark/magnitude_power_uc8_benchmark.c dsp/benchmark/mean_power_u16_benchmark.c dsp/benchmark/magnitude_sc16q11_benchmark.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/benchmark.c -o dsp/generated/benchmark.o
dsp/generated/benchmark.o: dsp/generated/benchmark.c dsp/benchmark/magnitude_sc16_benchmark.c dsp/benchmark/magnitude_uc8_benchmark.c dsp/benchmark/magnitude_power_uc8_benchmark.c dsp/benchmark/mean_power_u16_benchmark.c dsp/benchmark/count_above_u16_benchmark.c dsp/benchmark/magnitude_sc16q11_benchmark.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/benchmark.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/benchmark.c -o $(STARCH_OBJ_PATH)dsp/generated/benchmark.o
STARCH_BENCHMARK_OBJ := dsp/generated/benchmark.o

View File

@ -18,19 +18,23 @@
# $(STARCH_BENCHMARK_OBJ): object files providing a standalone benchmarking app (link all of $(STARCH_OBJS) too)
# explicit build rules for each object file listed in $(STARCH_OBJS)
MKDIR_P = mkdir -p
STARCH_CFLAGS := -DSTARCH_MIX_GENERIC
dsp/generated/flavor.generic.o: dsp/generated/flavor.generic.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/flavor.generic.c -o dsp/generated/flavor.generic.o
dsp/generated/flavor.generic.o: dsp/generated/flavor.generic.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/flavor.generic.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/flavor.generic.c -o $(STARCH_OBJ_PATH)dsp/generated/flavor.generic.o
dsp/generated/dispatcher.o: dsp/generated/dispatcher.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/dispatcher.c -o dsp/generated/dispatcher.o
dsp/generated/dispatcher.o: dsp/generated/dispatcher.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/dispatcher.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/dispatcher.c -o $(STARCH_OBJ_PATH)dsp/generated/dispatcher.o
STARCH_OBJS := dsp/generated/flavor.generic.o dsp/generated/dispatcher.o
dsp/generated/benchmark.o: dsp/generated/benchmark.c dsp/benchmark/magnitude_sc16_benchmark.c dsp/benchmark/magnitude_uc8_benchmark.c dsp/benchmark/magnitude_power_uc8_benchmark.c dsp/benchmark/mean_power_u16_benchmark.c dsp/benchmark/magnitude_sc16q11_benchmark.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/benchmark.c -o dsp/generated/benchmark.o
dsp/generated/benchmark.o: dsp/generated/benchmark.c dsp/benchmark/magnitude_sc16_benchmark.c dsp/benchmark/magnitude_uc8_benchmark.c dsp/benchmark/magnitude_power_uc8_benchmark.c dsp/benchmark/mean_power_u16_benchmark.c dsp/benchmark/count_above_u16_benchmark.c dsp/benchmark/magnitude_sc16q11_benchmark.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/benchmark.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/benchmark.c -o $(STARCH_OBJ_PATH)dsp/generated/benchmark.o
STARCH_BENCHMARK_OBJ := dsp/generated/benchmark.o

View File

@ -18,22 +18,27 @@
# $(STARCH_BENCHMARK_OBJ): object files providing a standalone benchmarking app (link all of $(STARCH_OBJS) too)
# explicit build rules for each object file listed in $(STARCH_OBJS)
MKDIR_P = mkdir -p
STARCH_CFLAGS := -DSTARCH_MIX_X86
dsp/generated/flavor.x86_avx2.o: dsp/generated/flavor.x86_avx2.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) -mavx2 -ffast-math dsp/generated/flavor.x86_avx2.c -o dsp/generated/flavor.x86_avx2.o
dsp/generated/flavor.x86_avx2.o: dsp/generated/flavor.x86_avx2.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/flavor.x86_avx2.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) -mavx2 -ffast-math dsp/generated/flavor.x86_avx2.c -o $(STARCH_OBJ_PATH)dsp/generated/flavor.x86_avx2.o
dsp/generated/flavor.generic.o: dsp/generated/flavor.generic.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/flavor.generic.c -o dsp/generated/flavor.generic.o
dsp/generated/flavor.generic.o: dsp/generated/flavor.generic.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/flavor.generic.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/flavor.generic.c -o $(STARCH_OBJ_PATH)dsp/generated/flavor.generic.o
dsp/generated/dispatcher.o: dsp/generated/dispatcher.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/magnitude_sc16.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/dispatcher.c -o dsp/generated/dispatcher.o
dsp/generated/dispatcher.o: dsp/generated/dispatcher.c dsp/impl/mean_power_u16.c dsp/impl/magnitude_power_uc8.c dsp/impl/magnitude_uc8.c dsp/impl/magnitude_sc16q11.c dsp/impl/count_above_u16.c dsp/impl/magnitude_sc16.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/dispatcher.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/dispatcher.c -o $(STARCH_OBJ_PATH)dsp/generated/dispatcher.o
STARCH_OBJS := dsp/generated/flavor.x86_avx2.o dsp/generated/flavor.generic.o dsp/generated/dispatcher.o
dsp/generated/benchmark.o: dsp/generated/benchmark.c dsp/benchmark/magnitude_sc16_benchmark.c dsp/benchmark/magnitude_uc8_benchmark.c dsp/benchmark/magnitude_power_uc8_benchmark.c dsp/benchmark/mean_power_u16_benchmark.c dsp/benchmark/magnitude_sc16q11_benchmark.c
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/benchmark.c -o dsp/generated/benchmark.o
dsp/generated/benchmark.o: dsp/generated/benchmark.c dsp/benchmark/magnitude_sc16_benchmark.c dsp/benchmark/magnitude_uc8_benchmark.c dsp/benchmark/magnitude_power_uc8_benchmark.c dsp/benchmark/mean_power_u16_benchmark.c dsp/benchmark/count_above_u16_benchmark.c dsp/benchmark/magnitude_sc16q11_benchmark.c
@$(MKDIR_P) $(dir $(STARCH_OBJ_PATH)dsp/generated/benchmark.o)
$(STARCH_COMPILE) $(STARCH_CFLAGS) dsp/generated/benchmark.c -o $(STARCH_OBJ_PATH)dsp/generated/benchmark.o
STARCH_BENCHMARK_OBJ := dsp/generated/benchmark.o

41
dsp/generated/starch.h generated
View File

@ -195,6 +195,36 @@ extern starch_mean_power_u16_aligned_regentry starch_mean_power_u16_aligned_regi
starch_mean_power_u16_aligned_regentry * starch_mean_power_u16_aligned_select();
void starch_mean_power_u16_aligned_set_wisdom( const char * const * received_wisdom );
typedef void (* starch_count_above_u16_ptr) ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
extern starch_count_above_u16_ptr starch_count_above_u16;
typedef struct {
int rank;
const char *name;
const char *flavor;
starch_count_above_u16_ptr callable;
int (*flavor_supported)();
} starch_count_above_u16_regentry;
extern starch_count_above_u16_regentry starch_count_above_u16_registry[];
starch_count_above_u16_regentry * starch_count_above_u16_select();
void starch_count_above_u16_set_wisdom( const char * const * received_wisdom );
typedef void (* starch_count_above_u16_aligned_ptr) ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
extern starch_count_above_u16_aligned_ptr starch_count_above_u16_aligned;
typedef struct {
int rank;
const char *name;
const char *flavor;
starch_count_above_u16_aligned_ptr callable;
int (*flavor_supported)();
} starch_count_above_u16_aligned_regentry;
extern starch_count_above_u16_aligned_regentry starch_count_above_u16_aligned_registry[];
starch_count_above_u16_aligned_regentry * starch_count_above_u16_aligned_select();
void starch_count_above_u16_aligned_set_wisdom( const char * const * received_wisdom );
/* flavors and prototypes */
#ifdef STARCH_FLAVOR_ARMV7A_NEON_VFPV4
@ -233,6 +263,10 @@ void starch_magnitude_sc16q11_12bit_table_armv7a_neon_vfpv4 ( const sc16_t * arg
void starch_magnitude_sc16q11_aligned_12bit_table_armv7a_neon_vfpv4 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16q11_neon_vrsqrte_armv7a_neon_vfpv4 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16q11_aligned_neon_vrsqrte_armv7a_neon_vfpv4 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_count_above_u16_generic_armv7a_neon_vfpv4 ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_count_above_u16_aligned_generic_armv7a_neon_vfpv4 ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_count_above_u16_neon_armv7a_neon_vfpv4 ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_count_above_u16_aligned_neon_armv7a_neon_vfpv4 ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_magnitude_sc16_exact_u32_armv7a_neon_vfpv4 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16_aligned_exact_u32_armv7a_neon_vfpv4 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16_exact_float_armv7a_neon_vfpv4 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
@ -279,6 +313,10 @@ void starch_magnitude_sc16q11_12bit_table_armv8_neon_simd ( const sc16_t * arg0,
void starch_magnitude_sc16q11_aligned_12bit_table_armv8_neon_simd ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16q11_neon_vrsqrte_armv8_neon_simd ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16q11_aligned_neon_vrsqrte_armv8_neon_simd ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_count_above_u16_generic_armv8_neon_simd ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_count_above_u16_aligned_generic_armv8_neon_simd ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_count_above_u16_neon_armv8_neon_simd ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_count_above_u16_aligned_neon_armv8_neon_simd ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_magnitude_sc16_exact_u32_armv8_neon_simd ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16_aligned_exact_u32_armv8_neon_simd ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16_exact_float_armv8_neon_simd ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
@ -303,6 +341,7 @@ void starch_magnitude_sc16q11_exact_u32_generic ( const sc16_t * arg0, uint16_t
void starch_magnitude_sc16q11_exact_float_generic ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16q11_11bit_table_generic ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16q11_12bit_table_generic ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_count_above_u16_generic_generic ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_magnitude_sc16_exact_u32_generic ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16_exact_float_generic ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
#endif /* STARCH_FLAVOR_GENERIC */
@ -337,6 +376,8 @@ void starch_magnitude_sc16q11_11bit_table_x86_avx2 ( const sc16_t * arg0, uint16
void starch_magnitude_sc16q11_aligned_11bit_table_x86_avx2 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16q11_12bit_table_x86_avx2 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16q11_aligned_12bit_table_x86_avx2 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_count_above_u16_generic_x86_avx2 ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_count_above_u16_aligned_generic_x86_avx2 ( const uint16_t * arg0, unsigned arg1, uint16_t arg2, unsigned * arg3 );
void starch_magnitude_sc16_exact_u32_x86_avx2 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16_aligned_exact_u32_x86_avx2 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );
void starch_magnitude_sc16_exact_float_x86_avx2 ( const sc16_t * arg0, uint16_t * arg1, unsigned arg2 );

View File

@ -0,0 +1,58 @@
/*
* Count the number of samples in a uint16_t buffer that are >= a threshold.
*/
void STARCH_IMPL(count_above_u16, generic) (const uint16_t *in, unsigned len, uint16_t threshold, unsigned *out_count)
{
const uint16_t * restrict in_align = STARCH_ALIGNED(in);
unsigned count = 0;
while (len--) {
if (in_align[0] >= threshold)
++count;
++in_align;
}
*out_count = count;
}
#ifdef STARCH_FEATURE_NEON
#include <arm_neon.h>
void STARCH_IMPL_REQUIRES(count_above_u16, neon, STARCH_FEATURE_NEON) (const uint16_t *in, unsigned len, uint16_t threshold, unsigned *out_count)
{
const uint16_t * restrict in_align = STARCH_ALIGNED(in);
const uint16x8_t threshold_x8 = vdupq_n_u16(threshold);
int32x4_t accumulator0 = vdupq_n_s32(0);
int32x4_t accumulator1 = vdupq_n_s32(0);
unsigned len8 = len >> 3;
while (len8--) {
uint16x8_t mag = vld1q_u16(in_align);
int16x8_t compare = vreinterpretq_s16_u16(vcgeq_u16(mag, threshold_x8));
accumulator0 = vsubw_s16(accumulator0, vget_low_s16(compare));
accumulator1 = vsubw_s16(accumulator1, vget_high_s16(compare));
in_align += 8;
}
// sum accumulators across all lanes
int32x4_t sum2 = vaddq_s32(accumulator0, accumulator1);
int32x2_t sum4 = vadd_s32(vget_low_s32(sum2), vget_high_s32(sum2));
int32x2_t sum8 = vpadd_s32(sum4, sum4);
int32x4_t sum8_x2 = vcombine_s32(sum8, sum8);
unsigned len1 = len & 7;
while (len1--) {
uint16x4_t mag = vld1_dup_u16(in_align);
int16x4_t compare = vreinterpret_s16_u16(vcge_u16(mag, vget_low_u16(threshold_x8)));
sum8_x2 = vsubw_s16(sum8_x2, compare);
in_align += 1;
}
*out_count = vgetq_lane_s32(sum8_x2, 0);
}
#endif

View File

@ -1,10 +1,11 @@
#include <math.h>
#include <endian.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdalign.h>
#include <inttypes.h>
#include "compat/compat.h"
#include "dsp/helpers/tables.h"
/* Convert UC8 values to unsigned 16-bit magnitudes */

View File

@ -1,5 +1,6 @@
#include <math.h>
#include <endian.h>
#include "compat/compat.h"
/* Convert (little-endian) SC16 values to unsigned 16-bit magnitudes */

View File

@ -1,5 +1,6 @@
#include <math.h>
#include <endian.h>
#include "compat/compat.h"
#include "dsp/helpers/tables.h"

View File

@ -1,10 +1,11 @@
#include <math.h>
#include <endian.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdalign.h>
#include <inttypes.h>
#include "compat/compat.h"
#include "dsp/helpers/tables.h"
/* Convert UC8 values to unsigned 16-bit magnitudes */

View File

@ -20,6 +20,7 @@ gen.add_function(name = 'magnitude_power_uc8', argtypes = ['const uc8_t *', 'uin
gen.add_function(name = 'magnitude_sc16', argtypes = ['const sc16_t *', 'uint16_t *', 'unsigned'], aligned = True)
gen.add_function(name = 'magnitude_sc16q11', argtypes = ['const sc16_t *', 'uint16_t *', 'unsigned'], aligned = True)
gen.add_function(name = 'mean_power_u16', argtypes = ['const uint16_t *', 'unsigned', 'double *', 'double *'], aligned = True)
gen.add_function(name = 'count_above_u16', argtypes = ['const uint16_t *', 'unsigned', 'uint16_t', 'unsigned *'], aligned = True)
gen.add_feature(name='neon', description='ARM NEON')

View File

@ -110,11 +110,10 @@ static void modesInitConfig(void) {
memset(&Modes, 0, sizeof(Modes));
// Now initialise things that should not be 0/NULL to their defaults
Modes.gain = MODES_MAX_GAIN;
Modes.gain = MODES_DEFAULT_GAIN;
Modes.freq = MODES_DEFAULT_FREQ;
Modes.check_crc = 1;
Modes.fix_df = 1;
Modes.net_heartbeat_interval = MODES_NET_HEARTBEAT_INTERVAL;
Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL;
Modes.json_interval = 1000;
Modes.json_stats_interval = 60000;
@ -122,6 +121,31 @@ static void modesInitConfig(void) {
Modes.maxRange = 1852 * 300; // 300NM default max range
Modes.mode_ac_auto = 1;
Modes.net_heartbeat_interval = MODES_NET_HEARTBEAT_INTERVAL;
Modes.net_output_flush_size = 1300;
Modes.net_output_flush_interval = 500;
// adaptive
Modes.adaptive_min_gain_db = 0;
Modes.adaptive_max_gain_db = 99999;
Modes.adaptive_duty_cycle = 0.5;
Modes.adaptive_burst_control = false;
Modes.adaptive_burst_alpha = 2.0 / (5 + 1);
Modes.adaptive_burst_change_delay = 5;
Modes.adaptive_burst_loud_runlength = 10;
Modes.adaptive_burst_loud_rate = 5.0;
Modes.adaptive_burst_quiet_runlength = 10;
Modes.adaptive_burst_quiet_rate = 5.0;
Modes.adaptive_range_control = false;
Modes.adaptive_range_alpha = 2.0 / (5 + 1);
Modes.adaptive_range_percentile = 40;
Modes.adaptive_range_change_delay = 10;
Modes.adaptive_range_scan_delay = 300;
Modes.adaptive_range_rescan_delay = 3600;
sdrInitConfig();
}
//
@ -287,6 +311,7 @@ static void showDSP()
SHOW(magnitude_sc16);
SHOW(magnitude_sc16q11);
SHOW(mean_power_u16);
SHOW(count_above_u16);
#undef SHOW
@ -300,60 +325,111 @@ static void showHelp(void)
sdrShowHelp();
printf(
" Common options\n"
" Output modes\n"
"\n"
"--gain <db> Set gain (default: max gain. Use -10 for auto-gain)\n"
"--freq <hz> Set frequency (default: 1090 Mhz)\n"
"--interactive Interactive mode refreshing data on screen. Implies --throttle\n"
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60)\n"
"--interactive-show-distance Show aircraft distance and bearing instead of lat/lon\n"
" (requires --lat and --lon)\n"
"--interactive-distance-units Distance units ('km', 'sm', 'nm') (default: 'nm')\n"
"--interactive-callsign-filter Only callsigns that match the prefix or regex will be displayed\n"
// ------ 80 char limit ----------------------------------------------------------|
"--raw Show only messages hex values\n"
"--net Enable networking with default ports unless overridden\n"
"--modeac Enable decoding of SSR Modes 3/A & 3/C\n"
"--no-modeac-auto Don't enable Mode A/C if requested by a Beast connection\n"
"--mlat display raw messages in Beast ascii mode\n"
"--onlyaddr Show only ICAO addresses (testing purposes)\n"
"--metric Use metric units (meters, km/h, ...)\n"
"--gnss Show altitudes as HAE/GNSS when available\n"
"--quiet Disable output to stdout. Use for daemon applications\n"
"--show-only <addr> Show only messages from the given ICAO on stdout\n"
"--snip <level> Strip IQ file removing samples < level\n"
"\n"
" Decoder settings\n"
"\n"
// ------ 80 char limit ----------------------------------------------------------|
"--gain <db> Set gain in dB (default: varies by SDR type)\n"
"--freq <hz> Set frequency (default: 1090 Mhz)\n"
"--fix Enable single-bit error correction using CRC\n"
"--fix-2bit Enable two-bit error correction using CRC\n"
" (use with caution!)\n"
"--no-fix Disable error correction using CRC\n"
"--no-fix-df Disable error correction of the DF message field\n"
" (reduces CPU requirements)\n"
"--no-crc-check Disable messages with broken CRC (discouraged)\n"
"--enable-df24 Enable decoding of DF24 Comm-D ELM messages\n"
"--lat <latitude> Reference/receiver latitude for surface positions\n"
"--lon <longitude> Reference/receiver longitude for surface positions\n"
"--max-range <distance> Absolute maximum range for position decoding (in NM)\n"
"\n"
// ------ 80 char limit ----------------------------------------------------------|
" Adaptive gain\n"
"\n"
"--adaptive-burst Adjust gain for too-loud message bursts\n"
"--adaptive-burst-change-delay <s> Set delay after changing gain before\n"
" resuming burst control (seconds)\n"
"--adaptive-burst-alpha <a> Set burst rate smoothing factor\n"
" (0..1, smaller=more smoothing)\n"
"--adaptive-burst-loud-rate <r> Set burst rate for gain decrease\n"
"--adaptive-burst-loud-runlength <l> Set burst runlength for gain decrease\n"
"--adaptive-burst-quiet-rate <r> Set burst rate for gain increase\n"
"--adaptive-burst-quiet-runlength <l> Set burst runlength for gain increase\n"
"--adaptive-range Adjust gain for target dynamic range\n"
"--adaptive-range-target <db> Set target dynamic range in dB\n"
"--adaptive-range-alpha <a> Set dynamic range noise smoothing factor\n"
" (0..1, smaller=more smoothing)\n"
"--adaptive-range-percentile <p> Set dynamic range noise percentile\n"
"--adaptive-range-change-delay <s> Set delay after changing gain before\n"
" resuming dynamic range control (seconds)\n"
"--adaptive-range-scan-delay <s> Set scan interval for dynamic range\n"
" gain scanning following a gain decrease\n"
" due to an increase in noise (seconds)\n"
"--adaptive-range-rescan-delay <s> Set periodic rescan interval for dynamic\n"
" range gain scanning (seconds)\n"
"--adaptive-min-gain <g> Set gain adjustment range lower limit (dB)\n"
"--adaptive-max-gain <g> Set gain adjustment range upper limit (dB)\n"
"--adaptive-duty-cycle <p> Set adaptive gain duty cycle %% (1..100)\n"
"\n"
// ------ 80 char limit ----------------------------------------------------------|
" Network connections\n"
"\n"
"--net Enable networking with default ports unless overridden\n"
"--no-modeac-auto Don't enable Mode A/C if requested by a net connection\n"
"--net-only Enable just networking, no RTL device or file used\n"
"--net-bind-address <ip> IP address to bind to (default: Any; Use 127.0.0.1 for private)\n"
"--net-bind-address <ip> IP address to bind to (use 127.0.0.1 for private)\n"
"--net-ri-port <ports> TCP raw input listen ports (default: 30001)\n"
"--net-ro-port <ports> TCP raw output listen ports (default: 30002)\n"
"--net-sbs-port <ports> TCP BaseStation output listen ports (default: 30003)\n"
"--net-bi-port <ports> TCP Beast input listen ports (default: 30004,30104)\n"
"--net-bo-port <ports> TCP Beast output listen ports (default: 30005)\n"
"--net-stratux-port <ports> TCP Stratux output listen ports (default: disabled)\n"
"--net-stratux-port <ports> TCP Stratux output listen ports (default: disabled)\n"
"--net-ro-size <size> TCP output minimum size (default: 0)\n"
"--net-ro-interval <rate> TCP output memory flush rate in seconds (default: 0)\n"
"--net-heartbeat <rate> TCP heartbeat rate in seconds (default: 60 sec; 0 to disable)\n"
"--net-heartbeat <rate> TCP heartbeat rate in seconds\n"
" (default: 60 sec; 0 to disable)\n"
"--net-buffer <n> TCP buffer size 64Kb * (2^n) (default: n=0, 64Kb)\n"
"--net-verbatim Make Beast-format output connections default to verbatim mode\n"
" (forward all messages, without applying CRC corrections)\n"
"--forward-mlat Allow forwarding of received mlat results to output ports\n"
"--lat <latitude> Reference/receiver latitude for surface posn (opt)\n"
"--lon <longitude> Reference/receiver longitude for surface posn (opt)\n"
"--max-range <distance> Absolute maximum range for position decoding (in nm, default: 300)\n"
"--fix Enable single-bit error correction using CRC\n"
"--fix-2bit Enable two-bit error correction using CRC (use with caution)\n"
"--no-fix Disable error correction using CRC\n"
"--no-fix-df Disable error correction of the DF message field (reduces CPU requirements)\n"
"--no-crc-check Disable messages with broken CRC (discouraged)\n"
"--mlat display raw messages in Beast ascii mode\n"
"--stats With --ifile print stats at exit. No other output\n"
"--stats-range Collect/show range histogram\n"
"--net-verbatim Make output connections default to verbatim mode\n"
" (forward all messages without correction)\n"
"--forward-mlat Allow forwarding of received mlat results\n"
"\n"
// ------ 80 char limit ----------------------------------------------------------|
" Stats and json output\n"
"\n"
"--stats Show stats summary at exit.\n"
"--stats-every <seconds> Show and reset stats every <seconds> seconds\n"
"--onlyaddr Show only ICAO addresses (testing purposes)\n"
"--metric Use metric units (meters, km/h, ...)\n"
"--gnss Show altitudes as HAE/GNSS (with H suffix) when available\n"
"--snip <level> Strip IQ file removing samples < level\n"
"--quiet Disable output to stdout. Use for daemon applications\n"
"--show-only <addr> Show only messages from the given ICAO on stdout\n"
"--write-json <dir> Periodically write json output to <dir> (for serving by a separate webserver)\n"
"--stats-range Collect/show range histogram\n"
"--write-json <dir> Periodically write json output to <dir>\n"
" (for serving by a separate webserver)\n"
"--write-json-every <t> Write json aircraft output every t seconds (default 1)\n"
"--json-stats-every <t> Write json stats output every t seconds (default 60)\n"
"--json-location-accuracy <n> Accuracy of receiver location in json metadata: 0=no location, 1=approximate, 2=exact\n"
#if 0
"--dcfilter Apply a 1Hz DC filter to input data (requires more CPU)\n"
#endif
"--json-location-accuracy <n> Accuracy of receiver location in json metadata\n"
" (0=no location, 1=approximate, 2=exact)\n"
"\n"
" Interactive mode\n"
"\n"
"--interactive Interactive mode refreshing data on screen.\n"
" Implies --throttle\n"
"--interactive-ttl <sec> Remove from list if idle for <sec>\n"
"--interactive-show-distance Show aircraft distance and bearing\n"
" (requires --lat and --lon)\n"
"--interactive-distance-units <u> Distance units ('km', 'sm', 'nm')\n"
"--interactive-callsign-filter <r> Filter rows by callsign against regex\n"
"\n"
" Misc\n"
"\n"
"--wisdom <path> Read DSP wisdom from given path\n"
"--version Show version, build and DSP options\n"
"--help Show this help\n"
@ -362,7 +438,8 @@ static void showHelp(void)
// Accumulate stats data from stats_current to stats_periodic, stats_alltime and stats_latest;
// reset stats_current
static void flush_stats(uint64_t now)
void flush_stats(uint64_t now);
void flush_stats(uint64_t now)
{
add_stats(&Modes.stats_current, &Modes.stats_periodic, &Modes.stats_periodic);
add_stats(&Modes.stats_current, &Modes.stats_alltime, &Modes.stats_alltime);
@ -533,7 +610,7 @@ int main(int argc, char **argv) {
} else if ( (!strcmp(argv[j], "--device") || !strcmp(argv[j], "--device-index")) && more) {
Modes.dev_name = strdup(argv[++j]);
} else if (!strcmp(argv[j],"--gain") && more) {
Modes.gain = (int) (atof(argv[++j])*10); // Gain is in tens of DBs
Modes.gain = atof(argv[++j]);
} else if (!strcmp(argv[j],"--dcfilter")) {
#if 0
Modes.dc_filter = 1;
@ -547,6 +624,8 @@ int main(int argc, char **argv) {
Modes.nfix_crc = 1;
} else if (!strcmp(argv[j],"--fix-2bit")) {
Modes.nfix_crc = 2;
} else if (!strcmp(argv[j],"--enable-df24")) {
Modes.enable_df24 = 1;
} else if (!strcmp(argv[j],"--no-fix")) {
Modes.nfix_crc = 0;
} else if (!strcmp(argv[j],"--no-fix-df")) {
@ -690,7 +769,41 @@ int main(int argc, char **argv) {
fprintf(stderr,
"Failed to read wisdom file %s: %s\n", argv[j], strerror(errno));
exit(1);
}
}
} else if (!strcmp(argv[j], "--adaptive-min-gain") && more) {
Modes.adaptive_min_gain_db = atof(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-max-gain") && more) {
Modes.adaptive_max_gain_db = atof(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-duty-cycle") && more) {
Modes.adaptive_duty_cycle = atof(argv[++j]) / 100.0;
} else if (!strcmp(argv[j], "--adaptive-burst")) {
Modes.adaptive_burst_control = true;
} else if (!strcmp(argv[j], "--adaptive-burst-alpha") && more) {
Modes.adaptive_burst_alpha = atof(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-burst-change-delay") && more) {
Modes.adaptive_burst_change_delay = atoi(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-burst-loud-rate") && more) {
Modes.adaptive_burst_loud_rate = atof(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-burst-loud-runlength") && more) {
Modes.adaptive_burst_loud_runlength = atoi(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-burst-quiet-rate") && more) {
Modes.adaptive_burst_quiet_rate = atof(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-burst-quiet-runlength") && more) {
Modes.adaptive_burst_quiet_runlength = atoi(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-range")) {
Modes.adaptive_range_control = true;
} else if (!strcmp(argv[j], "--adaptive-range-alpha") && more) {
Modes.adaptive_range_alpha = atof(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-range-percentile") && more) {
Modes.adaptive_range_percentile = atoi(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-range-target") && more) {
Modes.adaptive_range_target = atof(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-range-change-delay") && more) {
Modes.adaptive_range_change_delay = atoi(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-range-scan-delay") && more) {
Modes.adaptive_range_scan_delay = atoi(argv[++j]);
} else if (!strcmp(argv[j], "--adaptive-range-rescan-delay") && more) {
Modes.adaptive_range_rescan_delay = atoi(argv[++j]);
} else if (sdrHandleOption(argc, argv, &j)) {
/* handled */
} else {
@ -721,7 +834,7 @@ int main(int argc, char **argv) {
if (!sdrOpen()) {
exit(1);
}
if (Modes.net) {
modesInitNet();
}
@ -737,6 +850,8 @@ int main(int argc, char **argv) {
for (j = 0; j < 15; ++j)
Modes.stats_1min[j].start = Modes.stats_1min[j].end = Modes.stats_current.start;
adaptive_init();
// write initial json files so they're not missing
writeJsonToFile("receiver.json", generateReceiverJson);
writeJsonToFile("stats.json", generateStatsJson);
@ -758,7 +873,7 @@ int main(int argc, char **argv) {
nanosleep(&slp, NULL);
}
} else {
int watchdogCounter = 10; // about 1 second
int watchdogCounter = 300; // about 30 seconds
// Create the thread that will read the data from the device.
pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL);
@ -786,12 +901,12 @@ int main(int argc, char **argv) {
fifo_release(buf);
// We got something so reset the watchdog
watchdogCounter = 10;
watchdogCounter = 300;
} else {
// Nothing to process this time around.
if (--watchdogCounter <= 0) {
log_with_timestamp("No data received from the SDR for a long time, it may have wedged");
watchdogCounter = 600;
log_with_timestamp("No samples received from the SDR for a long time. Maybe the hardware is wedged? Giving up.");
Modes.exit = 2; // abnormal exit
}
}
@ -801,8 +916,14 @@ int main(int argc, char **argv) {
}
log_with_timestamp("Waiting for receive thread termination");
sdrStop(); // tell reader thread to wake up and exit
fifo_halt(); // Reader thread should do this anyway, but just in case..
pthread_join(Modes.reader_thread,NULL); // Wait on reader thread exit
// Wait on reader thread exit
if (join_thread(Modes.reader_thread, NULL, 30000) == ETIMEDOUT) {
log_with_timestamp("Receive thread did not shut down cleanly in 30 seconds, aborting.");
abort(); // Can't complete cleanup while the receive thread is active; bail out.
}
}
interactiveCleanup();

View File

@ -92,8 +92,8 @@
#define MODES_RTL_BUF_SIZE (16*16384) // 256k
#define MODES_MAG_BUF_SAMPLES (MODES_RTL_BUF_SIZE / 2) // Each sample is 2 bytes
#define MODES_MAG_BUFFERS 12 // Number of magnitude buffers (should be smaller than RTL_BUFFERS for flowcontrol to work)
#define MODES_AUTO_GAIN -100 // Use automatic gain
#define MODES_MAX_GAIN 999999 // Use max available gain
#define MODES_LEGACY_AUTO_GAIN -10 // old gain value for "use automatic gain"
#define MODES_DEFAULT_GAIN 999999 // Use default SDR gain
#define MODES_MSG_SQUELCH_DB 4.0 // Minimum SNR, in dB
#define MODES_MSG_ENCODER_ERRS 3 // Maximum number of encoding errors
@ -202,6 +202,7 @@ typedef enum {
typedef enum {
COMMB_UNKNOWN,
COMMB_AMBIGUOUS,
COMMB_NOT_DECODED,
COMMB_EMPTY_RESPONSE,
COMMB_DATALINK_CAPS,
COMMB_GICB_CAPS,
@ -209,7 +210,9 @@ typedef enum {
COMMB_ACAS_RA,
COMMB_VERTICAL_INTENT,
COMMB_TRACK_TURN,
COMMB_HEADING_SPEED
COMMB_HEADING_SPEED,
COMMB_MRAR,
COMMB_AIRBORNE_POSITION
} commb_format_t;
typedef enum {
@ -237,6 +240,24 @@ typedef enum {
NAV_ALT_INVALID, NAV_ALT_UNKNOWN, NAV_ALT_AIRCRAFT, NAV_ALT_MCP, NAV_ALT_FMS
} nav_altitude_source_t;
// BDS4,4 MRAR - FOM/Source values
typedef enum {
MRAR_SOURCE_INVALID = 0,
MRAR_SOURCE_INS = 1,
MRAR_SOURCE_GNSS = 2,
MRAR_SOURCE_DMEDME = 3,
MRAR_SOURCE_VORDME = 4,
MRAR_SOURCE_RESERVED = 5
} mrar_source_t;
// BDS4,4 and BDS4,5 hazard reports
typedef enum {
HAZARD_NIL = 0,
HAZARD_LIGHT = 1,
HAZARD_MODERATE = 2,
HAZARD_SEVERE = 3
} hazard_t;
#define MODES_NON_ICAO_ADDRESS (1<<24) // Set on addresses to indicate they are not ICAO addresses
#define MODES_INTERACTIVE_REFRESH_TIME 250 // Milliseconds
@ -272,6 +293,7 @@ typedef enum {
#include "convert.h"
#include "sdr.h"
#include "fifo.h"
#include "adaptive.h"
//======================== structure declarations =========================
@ -296,9 +318,9 @@ struct _Modes { // Internal state
// Sample conversion
int dc_filter; // should we apply a DC filter?
// RTLSDR
// RTLSDR and some other SDRs
char * dev_name;
int gain;
float gain; // value in dB, or MODES_AUTO_GAIN, or MODES_MAX_GAIN
int freq;
// Networking
@ -325,6 +347,7 @@ struct _Modes { // Internal state
int nfix_crc; // Number of crc bit error(s) to correct
int check_crc; // Only display messages with good CRC
int fix_df; // Try to correct damage to the DF field, as well as the main message body
int enable_df24; // Enable decoding of DF24..DF31 (Comm-D ELM)
int raw; // Raw output format
int mode_ac; // Enable decoding of SSR Modes A & C
int mode_ac_auto; // allow toggling of A/C by Beast commands
@ -362,6 +385,7 @@ struct _Modes { // Internal state
uint64_t json_stats_interval; // Interval between rewriting the json stats file, in milliseconds
int json_location_accuracy; // Accuracy of location metadata: 0=none, 1=approx, 2=exact
double faup_rate_multiplier; // Multiplier to adjust rate of faup1090 messages emitted
bool faup_upload_unknown_commb; // faup1090: should we upload Comm-B messages that weren't in a recognized format?
int json_aircraft_history_next;
struct {
@ -387,6 +411,28 @@ struct _Modes { // Internal state
int stats_newest_1min; // Index into stats_1min of the most recent 1-minute window
struct stats stats_5min; // Accumulated stats from the last 5 complete 1-minute windows
struct stats stats_15min; // Accumulated stats from the last 15 complete 1-minute windows
// Adaptive gain config
float adaptive_min_gain_db;
float adaptive_max_gain_db;
float adaptive_duty_cycle;
bool adaptive_burst_control;
float adaptive_burst_alpha;
unsigned adaptive_burst_change_delay;
float adaptive_burst_loud_rate;
unsigned adaptive_burst_loud_runlength;
float adaptive_burst_quiet_rate;
unsigned adaptive_burst_quiet_runlength;
bool adaptive_range_control;
float adaptive_range_alpha;
unsigned adaptive_range_percentile;
float adaptive_range_target;
unsigned adaptive_range_change_delay;
unsigned adaptive_range_scan_delay;
unsigned adaptive_range_rescan_delay;
};
extern struct _Modes Modes;
@ -588,6 +634,22 @@ struct modesMessage {
nav_modes_t modes;
} nav;
// BDS 4,4 MRAR
unsigned mrar_source_valid : 1;
unsigned wind_valid : 1;
unsigned temperature_valid : 1;
unsigned pressure_valid : 1;
unsigned turbulence_valid : 1;
unsigned humidity_valid : 1;
mrar_source_t mrar_source;
float wind_speed; // kts
float wind_dir; // degrees
float temperature; // degrees C
float pressure; // hPa
hazard_t turbulence; // NIL/LIGHT/MODERATE/SEVERE
float humidity; // 0-100 %
};
// This one needs modesMessage:

111
mode_s.c
View File

@ -247,7 +247,9 @@ static bool isShortPIMessage(const unsigned char *msg)
return (df == 11); // assume IID==0
}
static int correctMessage(const unsigned char *in, unsigned char *out)
#define UNCHECKED_SYNDROME 0xFFFFFFFFU
static int correctMessage(const unsigned char *in, unsigned char *out, uint32_t *short_syndrome, uint32_t *long_syndrome)
{
// Possible DF values of the first byte of a message that could be a valid DF11/17/18
// message after correction. See tools/df-correction-arrays.py for generator code.
@ -262,6 +264,9 @@ static int correctMessage(const unsigned char *in, unsigned char *out)
0x00060000, 0x066f0006, 0x6fff066f
};
*short_syndrome = UNCHECKED_SYNDROME;
*long_syndrome = UNCHECKED_SYNDROME;
// Try to correct, including corrections to the initial 5 bit DF field
// that determines message format
@ -275,27 +280,27 @@ static int correctMessage(const unsigned char *in, unsigned char *out)
struct errorinfo *long_ei = NULL;
if (df_correctable_long[fix_df_bits] & df_bit) {
uint32_t long_syndrome = modesChecksum(in, MODES_LONG_MSG_BITS);
if (isLongPIMessage(in) && long_syndrome == 0) {
*long_syndrome = modesChecksum(in, MODES_LONG_MSG_BITS);
if (isLongPIMessage(in) && *long_syndrome == 0) {
// DF17/18 message with correct checksum
memcpy(out, in, MODES_LONG_MSG_BYTES);
return 0;
}
long_ei = modesChecksumDiagnose(long_syndrome, MODES_LONG_MSG_BITS);
long_ei = modesChecksumDiagnose(*long_syndrome, MODES_LONG_MSG_BITS);
}
struct errorinfo *short_ei = NULL;
if (df_correctable_short[fix_df_bits] & df_bit) {
uint32_t short_syndrome = modesChecksum(in, MODES_SHORT_MSG_BITS);
if (isShortPIMessage(in) && (short_syndrome & 0xFFFF80) == 0) {
*short_syndrome = modesChecksum(in, MODES_SHORT_MSG_BITS);
if (isShortPIMessage(in) && (*short_syndrome & 0xFFFF80) == 0) {
// DF11 message with correct checksum
// (low 7 bits may be IID)
memcpy(out, in, MODES_SHORT_MSG_BYTES);
return 0;
}
short_ei = modesChecksumDiagnose(short_syndrome, MODES_SHORT_MSG_BITS); // assume IID == 0
short_ei = modesChecksumDiagnose(*short_syndrome, MODES_SHORT_MSG_BITS); // assume IID == 0
}
// Might be a damaged DF11/17/18, or might be another message type that doesn't have a full CRC
@ -344,29 +349,38 @@ static int correctMessage(const unsigned char *in, unsigned char *out)
// The more positive, the more reliable the message is.
score_rank scoreModesMessage(const unsigned char *uncorrected)
{
// try to produce a corrected DF11/17/18, including correcting the DF bits
unsigned char corrected[14];
int corrections = correctMessage(uncorrected, corrected);
// This is a "valid" DF0 message, but it's not useful; we discard these messages
static const unsigned char all_zeros[MODES_SHORT_MSG_BYTES] = { 0, 0, 0, 0, 0, 0, 0 };
if (!memcmp(all_zeros, corrected, sizeof(all_zeros)))
if (!memcmp(all_zeros, uncorrected, sizeof(all_zeros)))
return SR_ALL_ZEROS;
// try to produce a corrected DF11/17/18, including correcting the DF bits
unsigned char corrected[14];
uint32_t short_syndrome, long_syndrome;
int corrections = correctMessage(uncorrected, corrected, &short_syndrome, &long_syndrome);
unsigned df = getbits(corrected, 1, 5); // Downlink Format
switch (df) {
case 0: // short air-air surveillance
case 4: // surveillance, altitude reply
case 5: // surveillance, altitude reply
{
uint32_t addr = modesChecksum(corrected, MODES_SHORT_MSG_BITS);
bool recent = icaoFilterTest(addr);
if (short_syndrome == UNCHECKED_SYNDROME)
short_syndrome = modesChecksum(corrected, MODES_SHORT_MSG_BITS);
bool recent = icaoFilterTest(short_syndrome);
return recent ? SR_UNRELIABLE_KNOWN : SR_UNRELIABLE_UNKNOWN;
}
case 16: // long air-air surveillance
case 20: // Comm-B, altitude reply
case 21: // Comm-B, identity reply
{
if (long_syndrome == UNCHECKED_SYNDROME)
long_syndrome = modesChecksum(corrected, MODES_LONG_MSG_BITS);
bool recent = icaoFilterTest(long_syndrome);
return recent ? SR_UNRELIABLE_KNOWN : SR_UNRELIABLE_UNKNOWN;
}
case 24: // Comm-D (ELM)
case 25: // Comm-D (ELM)
case 26: // Comm-D (ELM)
@ -376,8 +390,11 @@ score_rank scoreModesMessage(const unsigned char *uncorrected)
case 30: // Comm-D (ELM)
case 31: // Comm-D (ELM)
{
uint32_t addr = modesChecksum(corrected, MODES_LONG_MSG_BITS);
bool recent = icaoFilterTest(addr);
if (!Modes.enable_df24)
return SR_UNCORRECTABLE;
if (long_syndrome == UNCHECKED_SYNDROME)
long_syndrome = modesChecksum(corrected, MODES_LONG_MSG_BITS);
bool recent = icaoFilterTest(long_syndrome);
return recent ? SR_UNRELIABLE_KNOWN : SR_UNRELIABLE_UNKNOWN;
}
@ -385,7 +402,9 @@ score_rank scoreModesMessage(const unsigned char *uncorrected)
{
// DF11 All-call reply
uint32_t addr = getbits(corrected, 9, 32);
uint32_t iid = modesChecksum(corrected, MODES_SHORT_MSG_BITS) & 0x7F;
if (short_syndrome == UNCHECKED_SYNDROME)
short_syndrome = modesChecksum(corrected, MODES_SHORT_MSG_BITS);
uint32_t iid = short_syndrome & 0x7F;
bool recent = icaoFilterTest(addr);
switch (corrections) {
@ -513,13 +532,23 @@ int decodeModesMessage(struct modesMessage *mm, const unsigned char *in)
memcpy(mm->verbatim, in, MODES_LONG_MSG_BYTES);
// Apply corrections to our local copy
int corrections = correctMessage(in, mm->msg);
uint32_t short_syndrome, long_syndrome;
int corrections = correctMessage(in, mm->msg, &short_syndrome, &long_syndrome);
const unsigned char *msg = mm->msg;
// Get the message type ASAP as other operations depend on this
mm->msgtype = getbits(msg, 1, 5); // Downlink Format
mm->msgbits = modesMessageLenByType(mm->msgtype);
mm->crc = modesChecksum(msg, mm->msgbits);
if (mm->msgtype & 16) {
if (long_syndrome == UNCHECKED_SYNDROME)
long_syndrome = modesChecksum(mm->msg, MODES_LONG_MSG_BITS);
mm->crc = long_syndrome;
} else {
if (short_syndrome == UNCHECKED_SYNDROME)
short_syndrome = modesChecksum(mm->msg, MODES_SHORT_MSG_BITS);
mm->crc = short_syndrome;
}
mm->correctedbits = corrections > 0 ? corrections : 0;
mm->addr = 0;
@ -1652,6 +1681,8 @@ static const char *commb_format_to_string(commb_format_t format) {
return "empty response";
case COMMB_AMBIGUOUS:
return "ambiguous format";
case COMMB_NOT_DECODED:
return "not decoded";
case COMMB_DATALINK_CAPS:
return "BDS1,0 Datalink capabilities";
case COMMB_GICB_CAPS:
@ -1666,6 +1697,10 @@ static const char *commb_format_to_string(commb_format_t format) {
return "BDS5,0 Track and turn report";
case COMMB_HEADING_SPEED:
return "BDS6,0 Heading and speed report";
case COMMB_MRAR:
return "BDS4,4 Meterological routine air report";
case COMMB_AIRBORNE_POSITION:
return "BDS0,5 Extended squitter airborne position";
default:
return "unknown format";
}
@ -1719,6 +1754,29 @@ static const char *emergency_to_string(emergency_t emergency)
}
}
static const char *mrar_source_to_string(mrar_source_t source)
{
switch (source) {
case MRAR_SOURCE_INVALID: return "invalid";
case MRAR_SOURCE_INS: return "INS";
case MRAR_SOURCE_GNSS: return "GNSS";
case MRAR_SOURCE_DMEDME: return "DME/DME";
case MRAR_SOURCE_VORDME: return "VOR/DME";
default: return "reserved";
}
}
static const char *hazard_to_string(hazard_t hazard)
{
switch (hazard) {
case HAZARD_NIL: return "nil";
case HAZARD_LIGHT: return "light";
case HAZARD_MODERATE: return "moderate";
case HAZARD_SEVERE: return "severe";
default: return "invalid hazard severity";
}
}
static void print_hex_bytes(unsigned char *data, size_t len) {
size_t i;
for (i = 0; i < len; ++i) {
@ -2184,6 +2242,21 @@ void displayModesMessage(struct modesMessage *mm) {
printf(" Emergency/priority: %s\n", emergency_to_string(mm->emergency));
}
if (mm->mrar_source_valid)
printf(" MRAR FOM/Source: %s\n", mrar_source_to_string(mm->mrar_source));
if (mm->wind_valid) {
printf(" Wind speed: %.0f kt\n", mm->wind_speed);
printf(" Wind direction: %.1f degrees\n", mm->wind_dir);
}
if (mm->temperature_valid)
printf(" Air temperature: %.1f degrees C\n", mm->temperature);
if (mm->pressure_valid)
printf(" Static pressure: %.0f hPa\n", mm->pressure);
if (mm->turbulence_valid)
printf(" Turbulence: %s\n", hazard_to_string(mm->turbulence));
if (mm->humidity_valid)
printf(" Humidity: %.0f%%\n", mm->humidity);
printf("\n");
fflush(stdout);
}

348
net_io.c
View File

@ -839,12 +839,14 @@ static void modesSendStratuxOutput(struct modesMessage *mm, struct aircraft *a)
"\"TypeCode\":%d,"
"\"SubtypeCode\":%d,"
"\"SignalLevel\":%f,"
"\"Gain\":%f,"
"\"IsMlat\":%s,",
mm->addr,
mm->msgtype, cacf,
mm->metype,
mm->mesub,
mm->signalLevel, // what precision and range is needed for RSSI?
sdrGetGainDb(sdrGetGain()),
is_mlat_str);
//// callsign
@ -1126,7 +1128,7 @@ static int handleFaupCommand(struct client *c, char *p) {
// Traverse through message for commands
while (msg_field != NULL) {
if (strcmp(msg_field, "upload_rate_multiplier") == 0) {
if (!strcmp(msg_field, "upload_rate_multiplier")) {
msg_field = strtok (NULL, "\t");
multiplier = atof(msg_field);
@ -1140,6 +1142,14 @@ static int handleFaupCommand(struct client *c, char *p) {
Modes.faup_rate_multiplier = multiplier;
break;
}
if (!strcmp(msg_field, "upload_unknown_commb")) {
msg_field = strtok (NULL, "\t");
unsigned enable = atoi(msg_field);
fprintf(stderr, "handleFaupCommand(): %s upload of unknown Comm-B messages\n", enable ? "Enabling" : "Disabling");
Modes.faup_upload_unknown_commb = enable;
break;
}
msg_field = strtok (NULL, "\t");
}
@ -1290,6 +1300,36 @@ static int hexDigitVal(int c) {
else if (c >= 'a' && c <= 'f') return c-'a'+10;
else return -1;
}
// decode 12 hex digits as a 48-bit timestamp
static bool timestampFromHex(const char *hex, uint64_t *timestamp)
{
uint64_t ts = 0;
for (unsigned i = 0; i < 12; ++i) {
int v = hexDigitVal(hex[i]);
if (v < 0)
return false;
ts = (ts << 4) | v;
}
*timestamp = ts;
return true;
}
// decode 2 hex digits as a signal level
static bool signalFromHex(const char *hex, double *signal)
{
int d1 = hexDigitVal(hex[0]);
int d2 = hexDigitVal(hex[1]);
if (d1 < 0 || d2 < 0)
return false;
double sig = ((d1 << 4) | d2) / 255.0;
*signal = sig * sig;
return true;
}
//
//=========================================================================
//
@ -1331,26 +1371,50 @@ static int decodeHexMessage(struct client *c, char *hex) {
// and some AVR records that we can understand
if (hex[l-1] != ';') {return (0);} // not complete - abort
switch(hex[0]) {
case '<': {
mm.signalLevel = ((hexDigitVal(hex[13])<<4) | hexDigitVal(hex[14])) / 255.0;
mm.signalLevel = mm.signalLevel * mm.signalLevel;
hex += 15; l -= 16; // Skip <, timestamp and siglevel, and ;
break;}
switch (hex[0]) {
case '<':
// [0] '<'
// [1..12] timestamp
// [13..14] signal level
// [15..l-2] data
// [l-1] ';'
if (l < 18)
return 0; // truncated
if (!timestampFromHex(hex + 1, &mm.timestampMsg))
return 0; // malformed timestamp
if (!signalFromHex(hex + 13, &mm.signalLevel))
return 0; // malformed signal level
hex += 15;
l -= 16;
break;
case '@': // No CRC check
case '%': { // CRC is OK
hex += 13; l -= 14; // Skip @,%, and timestamp, and ;
break;}
case '%': // CRC is OK
// [0] '@' or '%'
// [1..12] timestamp
// [13..l-2] data
// [l-1] ';'
if (l < 16)
return 0; // truncated
if (!timestampFromHex(hex + 1, &mm.timestampMsg))
return 0; // malformed timestamp
hex += 13;
l -= 14;
break;
case '*':
case ':': {
hex++; l-=2; // Skip * and ;
break;}
case ':':
// [0] '*' or ':'
// [1..l-2] data
// [l-1] ';'
if (l < 4)
return 0; // truncated
hex++;
l -= 2;
break;
default: {
return (0); // We don't know what this is, so abort
break;}
default:
return 0;
}
if ( (l != (MODEAC_MSG_BYTES * 2))
@ -1604,6 +1668,29 @@ static const char *nav_altitude_source_enum_string(nav_altitude_source_t src)
}
}
static const char *mrar_source_enum_string(mrar_source_t src)
{
switch (src) {
case MRAR_SOURCE_INVALID: return "invalid";
case MRAR_SOURCE_INS: return "ins";
case MRAR_SOURCE_GNSS: return "gnss";
case MRAR_SOURCE_DMEDME: return "dmedme";
case MRAR_SOURCE_VORDME: return "vordme";
default: return "reserved";
}
}
static const char *hazard_enum_string(hazard_t hazard)
{
switch (hazard) {
case HAZARD_NIL: return "nil";
case HAZARD_LIGHT: return "light";
case HAZARD_MODERATE: return "moderate";
case HAZARD_SEVERE: return "severe";
default: return "invalid";
}
}
char *generateAircraftJson(const char *url_path, int *len) {
uint64_t now = mstime();
struct aircraft *a;
@ -1707,12 +1794,23 @@ char *generateAircraftJson(const char *url_path, int *len) {
p = safe_snprintf(p, end, ",\"gva\":%u", a->gva);
if (trackDataValid(&a->sda_valid))
p = safe_snprintf(p, end, ",\"sda\":%u", a->sda);
if (trackDataValid(&a->mrar_source_valid))
p = safe_snprintf(p, end, ",\"mrar_source\":\"%s\"", mrar_source_enum_string(a->mrar_source));
if (trackDataValid(&a->wind_valid))
p = safe_snprintf(p, end, ",\"wind_speed\":%.0f,\"wind_dir\":%.1f", a->wind_speed, a->wind_dir);
if (trackDataValid(&a->temperature_valid))
p = safe_snprintf(p, end, ",\"temperature\":%.2f", a->temperature);
if (trackDataValid(&a->pressure_valid))
p = safe_snprintf(p, end, ",\"pressure\":%.0f", a->pressure);
if (trackDataValid(&a->turbulence_valid))
p = safe_snprintf(p, end, ",\"turbulence\":\"%s\"", hazard_enum_string(a->turbulence));
if (trackDataValid(&a->humidity_valid))
p = safe_snprintf(p, end, ",\"humidity\":%.1f", a->humidity);
if (a->modeA_hit)
p = safe_snprintf(p, end, ",\"modea\":true");
if (a->modeC_hit)
p = safe_snprintf(p, end, ",\"modec\":true");
p = safe_snprintf(p, end, ",\"mlat\":");
p = append_flags(p, end, a, SOURCE_MLAT);
p = safe_snprintf(p, end, ",\"tisb\":");
@ -1803,64 +1901,90 @@ static char * appendStatsJson(char *p,
p = safe_snprintf(p, end, "]}");
}
{
uint64_t demod_cpu_millis = (uint64_t)st->demod_cpu.tv_sec*1000UL + st->demod_cpu.tv_nsec/1000000UL;
uint64_t reader_cpu_millis = (uint64_t)st->reader_cpu.tv_sec*1000UL + st->reader_cpu.tv_nsec/1000000UL;
uint64_t background_cpu_millis = (uint64_t)st->background_cpu.tv_sec*1000UL + st->background_cpu.tv_nsec/1000000UL;
uint64_t demod_cpu_millis = (uint64_t)st->demod_cpu.tv_sec*1000UL + st->demod_cpu.tv_nsec/1000000UL;
uint64_t reader_cpu_millis = (uint64_t)st->reader_cpu.tv_sec*1000UL + st->reader_cpu.tv_nsec/1000000UL;
uint64_t background_cpu_millis = (uint64_t)st->background_cpu.tv_sec*1000UL + st->background_cpu.tv_nsec/1000000UL;
p = safe_snprintf(p, end,
",\"cpr\":{\"surface\":%u"
",\"airborne\":%u"
",\"global_ok\":%u"
",\"global_bad\":%u"
",\"global_range\":%u"
",\"global_speed\":%u"
",\"global_skipped\":%u"
",\"local_ok\":%u"
",\"local_aircraft_relative\":%u"
",\"local_receiver_relative\":%u"
",\"local_skipped\":%u"
",\"local_range\":%u"
",\"local_speed\":%u"
",\"filtered\":%u}"
",\"altitude_suppressed\":%u"
",\"cpu\":{\"demod\":%llu,\"reader\":%llu,\"background\":%llu}"
",\"tracks\":{\"all\":%u"
",\"single_message\":%u"
",\"unreliable\":%u}"
",\"messages\":%u",
st->cpr_surface,
st->cpr_airborne,
st->cpr_global_ok,
st->cpr_global_bad,
st->cpr_global_range_checks,
st->cpr_global_speed_checks,
st->cpr_global_skipped,
st->cpr_local_ok,
st->cpr_local_aircraft_relative,
st->cpr_local_receiver_relative,
st->cpr_local_skipped,
st->cpr_local_range_checks,
st->cpr_local_speed_checks,
st->cpr_filtered,
st->suppressed_altitude_messages,
(unsigned long long)demod_cpu_millis,
(unsigned long long)reader_cpu_millis,
(unsigned long long)background_cpu_millis,
st->unique_aircraft,
st->single_message_aircraft,
st->unreliable_aircraft,
st->messages_total);
for (i = 0; i < 32; ++i) {
if (i == 0)
p = safe_snprintf(p, end, ",\"messages_by_df\":[%u", st->messages_by_df[i]);
else
p = safe_snprintf(p, end, ",%u", st->messages_by_df[i]);
}
p = safe_snprintf(p, end, "]");
if (st->adaptive_valid) {
p = safe_snprintf(p, end,
",\"cpr\":{\"surface\":%u"
",\"airborne\":%u"
",\"global_ok\":%u"
",\"global_bad\":%u"
",\"global_range\":%u"
",\"global_speed\":%u"
",\"global_skipped\":%u"
",\"local_ok\":%u"
",\"local_aircraft_relative\":%u"
",\"local_receiver_relative\":%u"
",\"local_skipped\":%u"
",\"local_range\":%u"
",\"local_speed\":%u"
",\"filtered\":%u}"
",\"altitude_suppressed\":%u"
",\"cpu\":{\"demod\":%llu,\"reader\":%llu,\"background\":%llu}"
",\"tracks\":{\"all\":%u"
",\"single_message\":%u"
",\"unreliable\":%u}"
",\"messages\":%u",
st->cpr_surface,
st->cpr_airborne,
st->cpr_global_ok,
st->cpr_global_bad,
st->cpr_global_range_checks,
st->cpr_global_speed_checks,
st->cpr_global_skipped,
st->cpr_local_ok,
st->cpr_local_aircraft_relative,
st->cpr_local_receiver_relative,
st->cpr_local_skipped,
st->cpr_local_range_checks,
st->cpr_local_speed_checks,
st->cpr_filtered,
st->suppressed_altitude_messages,
(unsigned long long)demod_cpu_millis,
(unsigned long long)reader_cpu_millis,
(unsigned long long)background_cpu_millis,
st->unique_aircraft,
st->single_message_aircraft,
st->unreliable_aircraft,
st->messages_total);
for (i = 0; i < 32; ++i) {
if (i == 0)
p = safe_snprintf(p, end, ",\"messages_by_df\":[%u", st->messages_by_df[i]);
else
p = safe_snprintf(p, end, ",%u", st->messages_by_df[i]);
",\"adaptive\":"
"{\"gain_db\":%.1f"
",\"dynamic_range_limit_db\":%.1f"
",\"gain_changes\":%u"
",\"loud_undecoded\":%u"
",\"loud_decoded\":%u"
",\"noise_dbfs\":%.1f"
",\"gain_seconds\":[",
sdrGetGainDb(st->adaptive_gain),
sdrGetGainDb(st->adaptive_range_gain_limit),
st->adaptive_gain_changes,
st->adaptive_loud_undecoded,
st->adaptive_loud_decoded,
st->adaptive_noise_dbfs);
bool first = true;
for (unsigned i = 0; i < STATS_GAIN_COUNT; ++i) {
if (st->adaptive_gain_seconds[i] > 0) {
p = safe_snprintf(p, end, "%s[%.1f,%u]",
first ? "" : ",",
sdrGetGainDb(i), st->adaptive_gain_seconds[i]);
first = false;
}
}
p = safe_snprintf(p, end, "]}");
}
p = safe_snprintf(p, end, "}");
return p;
}
@ -1967,6 +2091,30 @@ char *generateHistoryJson(const char *url_path, int *len)
return strdup(Modes.json_aircraft_history[history_index].content);
}
static void ratelimitWriteError(const char *format, ...)
{
static uint64_t lastError = 0;
static unsigned suppressed = 0;
uint64_t now = mstime();
if (now - lastError < 60000) {
++suppressed;
return;
}
lastError = now;
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
if (suppressed) {
fprintf(stderr, " (%u more error messages suppressed)", suppressed);
suppressed = 0;
}
fprintf(stderr, "\n");
va_end(ap);
}
// Write JSON to file
void writeJsonToFile(const char *file, char * (*generator) (const char *,int*))
{
@ -1984,8 +2132,10 @@ void writeJsonToFile(const char *file, char * (*generator) (const char *,int*))
snprintf(tmppath, PATH_MAX, "%s/%s.XXXXXX", Modes.json_dir, file);
tmppath[PATH_MAX-1] = 0;
fd = mkstemp(tmppath);
if (fd < 0)
if (fd < 0) {
ratelimitWriteError("failed to create %s (while updating %s/%s): %s", tmppath, Modes.json_dir, file, strerror(errno));
return;
}
mask = umask(0);
umask(mask);
@ -1995,15 +2145,23 @@ void writeJsonToFile(const char *file, char * (*generator) (const char *,int*))
pathbuf[PATH_MAX-1] = 0;
content = generator(pathbuf, &len);
if (write(fd, content, len) != len)
if (write(fd, content, len) != len) {
ratelimitWriteError("failed to write to %s (while updating %s/%s): %s", tmppath, Modes.json_dir, file, strerror(errno));
goto error_1;
}
if (close(fd) < 0)
if (close(fd) < 0) {
ratelimitWriteError("failed to write to %s (while updating %s/%s): %s", tmppath, Modes.json_dir, file, strerror(errno));
goto error_2;
}
snprintf(pathbuf, PATH_MAX, "%s/%s", Modes.json_dir, file);
pathbuf[PATH_MAX-1] = 0;
rename(tmppath, pathbuf);
if (rename(tmppath, pathbuf) < 0) {
ratelimitWriteError("failed to rename %s to %s: %s", tmppath, pathbuf, strerror(errno));
goto error_2;
}
free(content);
return;
@ -2231,7 +2389,7 @@ __attribute__ ((format (printf,4,5))) static char *appendFATSV(char *p, char *en
}
#define TSV_MAX_PACKET_SIZE 800
#define TSV_VERSION "8E"
#define TSV_VERSION "9E"
static void writeFATSVPositionUpdate(float lat, float lon, float alt)
{
@ -2326,6 +2484,23 @@ static void writeFATSVEvent(struct modesMessage *mm, struct aircraft *a)
}
break;
case COMMB_GICB_CAPS:
// BDS 1,7: common usage GICB capability report
if (memcmp(mm->MB, a->fatsv_emitted_bds_17, 7) != 0) {
memcpy(a->fatsv_emitted_bds_17, mm->MB, 7);
writeFATSVEventMessage(mm, "gicb_caps", mm->MB, 7);
}
break;
case COMMB_UNKNOWN:
// If enabled, upload raw unrecognized Comm-B messages
// for server-side analysis
if (Modes.faup_upload_unknown_commb && memcmp(mm->MB, a->fatsv_emitted_unknown_commb, 7) != 0) {
memcpy(a->fatsv_emitted_unknown_commb, mm->MB, 7);
writeFATSVEventMessage(mm, "unknown_commb", mm->MB, 7);
}
break;
default:
// nothing
break;
@ -2500,7 +2675,13 @@ static void writeFATSV()
(airgroundValid && a->airground == AG_AIRBORNE && a->fatsv_emitted_airground == AG_GROUND) ||
(airgroundValid && a->airground == AG_GROUND && a->fatsv_emitted_airground == AG_AIRBORNE) ||
(squawkValid && a->squawk != a->fatsv_emitted_squawk) ||
(trackDataValid(&a->emergency_valid) && a->emergency != a->fatsv_emitted_emergency);
(trackDataValid(&a->emergency_valid) && a->emergency != a->fatsv_emitted_emergency) ||
(trackDataValid(&a->mrar_source_valid) && a->mrar_source_valid.updated > a->fatsv_last_emitted) ||
(trackDataValid(&a->wind_valid) && a->wind_valid.updated > a->fatsv_last_emitted) ||
(trackDataValid(&a->pressure_valid) && a->pressure_valid.updated > a->fatsv_last_emitted) ||
(trackDataValid(&a->temperature_valid) && a->temperature_valid.updated > a->fatsv_last_emitted) ||
(trackDataValid(&a->turbulence_valid) && a->turbulence_valid.updated > a->fatsv_last_emitted) ||
(trackDataValid(&a->humidity_valid) && a->humidity_valid.updated > a->fatsv_last_emitted);
uint64_t minAge;
double adjustedMinAge;
@ -2602,10 +2783,17 @@ static void writeFATSV()
p = appendFATSVMeta(p, end, "nav_alt_mcp", a, &a->nav_altitude_mcp_valid, "%u", a->nav_altitude_mcp);
p = appendFATSVMeta(p, end, "nav_alt_fms", a, &a->nav_altitude_fms_valid, "%u", a->nav_altitude_fms);
p = appendFATSVMeta(p, end, "nav_alt_src", a, &a->nav_altitude_src_valid, "%s", nav_altitude_source_enum_string(a->nav_altitude_src));
p = appendFATSVMeta(p, end, "nav_heading", a, &a->nav_heading_valid, "%.1f", a->nav_heading);
p = appendFATSVMeta(p, end, "nav_modes", a, &a->nav_modes_valid, "{%s}", nav_modes_flags_string(a->nav_modes));
p = appendFATSVMeta(p, end, "nav_qnh", a, &a->nav_qnh_valid, "%.1f", a->nav_qnh);
p = appendFATSVMeta(p, end, "emergency", a, &a->emergency_valid, "%s", emergency_enum_string(a->emergency));
p = appendFATSVMeta(p, end, "nav_heading", a, &a->nav_heading_valid, "%.1f", a->nav_heading);
p = appendFATSVMeta(p, end, "nav_modes", a, &a->nav_modes_valid, "{%s}", nav_modes_flags_string(a->nav_modes));
p = appendFATSVMeta(p, end, "nav_qnh", a, &a->nav_qnh_valid, "%.1f", a->nav_qnh);
p = appendFATSVMeta(p, end, "emergency", a, &a->emergency_valid, "%s", emergency_enum_string(a->emergency));
p = appendFATSVMeta(p, end, "mrar_source", a, &a->mrar_source_valid, "%s", mrar_source_enum_string(a->mrar_source));
p = appendFATSVMeta(p, end, "wind_speed", a, &a->wind_valid, "%.0f", a->wind_speed);
p = appendFATSVMeta(p, end, "wind_dir", a, &a->wind_valid, "%.1f", a->wind_dir);
p = appendFATSVMeta(p, end, "temperature", a, &a->temperature_valid, "%.2f", a->temperature);
p = appendFATSVMeta(p, end, "pressure", a, &a->pressure_valid, "%.0f", a->pressure);
p = appendFATSVMeta(p, end, "turbulence", a, &a->turbulence_valid, "%s", hazard_enum_string(a->turbulence));
p = appendFATSVMeta(p, end, "humidity", a, &a->humidity_valid, "%.0f", a->humidity);
// if we didn't get anything interesting, bail out.
// We don't need to do anything special to unwind prepareWrite().

View File

@ -35,21 +35,20 @@ FILES=$(find $TOP -mindepth 1 -maxdepth 1 -name .git -prune -o -name 'debian*' -
mkdir -p $OUT
cp -a $FILES $OUT
cp -a $TOP/debian $OUT
[ -d $TOP/debian-$DIST ] && cp -a $TOP/debian-$DIST/* $OUT/debian/
case "$DIST" in
jessie)
cp -a $TOP/debian-jessie/* $OUT/debian/
echo "Updating changelog for jessie backport build" >&2
dch --changelog $OUT/debian/changelog --local ~bpo8+ --force-distribution --distribution jessie-backports "Automated backport build for jessie"
;;
stretch)
cp -a $TOP/debian-stretch/* $OUT/debian/
echo "Updating changelog for stretch backport build" >&2
dch --changelog $OUT/debian/changelog --local ~bpo9+ --force-distribution --distribution stretch-backports "Automated backport build for jessie"
;;
buster)
echo "Updating changelog for buster backport build" >&2
dch --changelog $OUT/debian/changelog --local ~bpo10+ --force-distribution --distribution buster-backports "Automated backport build for buster"
;;
bullseye)
;;
*)

View File

@ -1,18 +1,18 @@
# SkyAware 4.0 Features
# SkyAware Features
## Customize display via URL query strings
Syntax: <ip_address>/dump1090-fa/?parameter=value
Syntax: <ip_address>/skyaware/?parameter=value
Examples:
http://192.0.0.1/dump1090-fa/?sidebar=hide
http://<ip_address>/skyaware/?sidebar=hide
http://192.0.0.1/dump1090-fa/?altitudeChart=hide
http://<ip_address>/skyaware/?altitudeChart=hide
http://192.0.0.1/dump1090-fa/?rangeRings=hide
http://<ip_address>/skyaware/?rangeRings=hide
http://192.0.0.1/dump1090-fa/?ringCount=3?ringBaseDistance=100?ringInterval=50
http://<ip_address>/skyaware/?ringCount=3&ringBaseDistance=100&ringInterval=50
| Parameter | Possible Values |
| :---------: | :---------: |
@ -31,7 +31,7 @@ Examples:
| rangeRings | show/hide |
| ringCount | integer |
| ringBaseDistance | integer |
| ringInteval | integer |
| ringInterval | integer |

View File

@ -23,9 +23,8 @@ DisplayUnits = "nautical";
// degrees.
// Default center of the map.
// 39.234 | -103.697 - Limon, CO
DefaultCenterLat = 39.234;
DefaultCenterLon = -103.697;
DefaultCenterLat = 45.0;
DefaultCenterLon = 9.0;
// The google maps zoom level, 0 - 16, lower is further out
DefaultZoomLvl = 7;
@ -126,3 +125,8 @@ BingMapsAPIKey = null;
// This is not polished yet (and so is disabled by default),
// currently it's just a data dump of the new fields with no UX work.
ExtendedData = false;
DefaultMaxAltitudeFilter = 65000
DefaultMinAltitudeFilter = 0
DefaultMaxSpeedFilter = 1000
DefaultMinSpeedFilter = 0

View File

@ -1 +0,0 @@
Placeholder for the JSON output data directory.

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"01001":{"r":"V5-NAM","t":"F900"},"01028":{"r":"V5-NMF","t":"A343"},"01029":{"r":"V5-NME","t":"A343"},"02031":{"r":"E3-AAQ","t":"B762"}}
{"01001":{"r":"V5-NAM","t":"F900"},"01028":{"r":"V5-NMF","t":"A343"},"01029":{"r":"V5-NME","t":"A343"},"0103F":{"r":"V5-GON"},"02031":{"r":"E3-AAQ","t":"B762"}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"00007":{"r":"EK-32011","t":"A319"},"0000A":{"r":"EK-32002","t":"A320"},"0000B":{"r":"EK-32003","t":"A320"},"0000C":{"r":"EK-32008","t":"A320"},"00010":{"r":"EK-73736","t":"B735"},"00013":{"r":"EK-11001","t":"AN12"},"00023":{"r":"EK-73786","t":"B737"},"0002F":{"r":"EK-12104","t":"AN12"},"0003C":{"r":"EK-RA01","t":"A319"},"0004A":{"r":"EK-32007","t":"A319"},"00059":{"r":"EK-95015","t":"SU95"},"0005A":{"r":"EK-95016","t":"SU95"},"0005F":{"r":"EW-412TH","t":"IL76"},"00076":{"r":"EK-74723","t":"B742"},"00079":{"r":"EK-74798","t":"B742"},"00087":{"r":"EK-74799","t":"B742"},"0009D":{"r":"EK-73775","t":"B735"},"0009F":{"r":"EK-73772","t":"B735"},"00801":{"r":"4K8888","t":"A319"},"00803":{"r":"4KAZ03","t":"A319"},"00804":{"r":"4KAZ04","t":"A319"},"00805":{"r":"4KAZ05","t":"A319"},"00806":{"r":"4KAI06","t":"GLF5"},"00807":{"r":"4KAI07","t":"A320"},"00808":{"r":"4K-MEK8","t":"GLF5"},"0080C":{"r":"4KAZ12","t":"B752"},"00826":{"r":"4K-AZ38","t":"B752"},"00828":{"r":"4KAZ40","t":"IL76"},"00829":{"r":"4KAZ41","t":"IL76"},"0082B":{"r":"4K-AZ43","t":"B752"},"00836":{"r":"4K-AZ54","t":"A320"},"00838":{"r":"4K-AZ56","t":"AN12"},"0083C":{"r":"4KAZ60","t":"IL76"},"00840":{"r":"4KAZ64","t":"E190"},"00841":{"r":"4KAZ65","t":"E190"},"00842":{"r":"4KAZ66","t":"E190"},"00843":{"r":"4KAZ67","t":"E190"},"0084D":{"r":"4KAZ77","t":"A320"},"0084E":{"r":"4KAZ78","t":"A320"},"0084F":{"r":"4KAZ79","t":"A320"},"00850":{"r":"4KAZ80","t":"A320"},"00851":{"r":"4KAZ81","t":"B763"},"00852":{"r":"4KAZ82","t":"B763"},"00853":{"r":"4KAZ83","t":"A320"},"00854":{"r":"4KAZ84","t":"A320"},"00855":{"r":"4KAZ85","t":"A345"},"00856":{"r":"4KAZ86","t":"A345"},"00858":{"r":"4KAZ88","t":"GALX"},"00864":{"r":"4KAZ100","t":"IL76"},"00865":{"r":"4KAZ101","t":"IL76"},"008D0":{"r":"4K-AZ208","t":"G280"},"00918":{"r":"4K-AZ280","t":"G280"},"00B20":{"r":"4KSW800","t":"B744"},"00B21":{"r":"4KSW888","t":"B744"},"00B70":{"r":"4K-SW880","t":"B763"},"00B78":{"r":"4KAZ888","t":"GLF4"},"00BB9":{"r":"4K-SW808","t":"B763"},"00BBE":{"r":"4KSW008","t":"B744"},"00BBF":{"r":"4KAZ11","t":"B752"},"00BC0":{"r":"4KAI08","t":"A346"},"00BC4":{"r":"4KAI88","t":"GLF6"},"00BC6":{"r":"4K-LAR","t":"GLF4"},"00BC7":{"r":"4KJJ888","t":"GLF5"},"00BE8":{"r":"4KAI01","t":"B763"},"00BE9":{"r":"4KAI001","t":"B77L"},"01097":{"r":"EX-37008"},"010F2":{"r":"EX-32001","t":"A320"},"010F4":{"r":"EX-37001","t":"B733"},"010F9":{"r":"EX-73401","t":"B734"},"01839":{"r":"EZ-A779","t":"B77L"},"0183A":{"r":"EZ-A778","t":"B77L"},"0183B":{"r":"EZ-A017","t":"B738"},"0183C":{"r":"EZ-A016","t":"B738"},"0183D":{"r":"EZ-A015","t":"B738"},"01842":{"r":"EZ-A010","t":"B752"},"01846":{"r":"EZ-A011","t":"B752"},"01847":{"r":"EZ-A012","t":"B752"},"01849":{"r":"EZ-A014","t":"B752"},"01854":{"r":"EZ-A106","t":"B712"},"01855":{"r":"EZ-A107","t":"B712"},"01858":{"r":"EZ-A004","t":"B738"},"01859":{"r":"EZ-A005","t":"B738"},"0185A":{"r":"EZ-A007","t":"B737"},"0185E":{"r":"EZ-A006","t":"B737"},"0185F":{"r":"EZ-A008","t":"B737"},"01860":{"r":"EZ-A009","t":"B737"},"01861":{"r":"EZ-A777","t":"B772"},"80001":{"r":"A5-RGF","t":"A319"},"80002":{"r":"A5-RGG","t":"A319"},"82209":{"r":"JU-1011","t":"B763"},"8220A":{"r":"JU-1012","t":"B763"},"83037":{"r":"UP-B5701","t":"B752"},"83065":{"r":"UP-Y4204","t":"YK42"},"83088":{"r":"UP-I7620","t":"IL76"},"830A5":{"r":"UP-A2001","t":"A320"},"830BD":{"r":"UP-CS302","t":"C25B"},"830C2":{"r":"UP-K3501","t":"B350"},"830ED":{"r":"UP-A3001","t":"A332"},"830FE":{"r":"UP-K3502","t":"B350"},"831E8":{"r":"UP-T5409","t":"T154"}}
{"00005":{"r":"EK-73792","t":"B738"},"00007":{"r":"EK-32011","t":"A319"},"0000C":{"r":"EK32008","t":"A320"},"00010":{"r":"EK-73736","t":"B735"},"00013":{"r":"EK-11001","t":"AN12"},"00024":{"r":"EK-73705","t":"B738"},"0002F":{"r":"EK-12104","t":"AN12"},"0003C":{"r":"EK-RA01","t":"A319"},"0004A":{"r":"EK-32007","t":"A319"},"00059":{"r":"EK-95015","t":"SU95"},"0005A":{"r":"EK-95016","t":"SU95"},"0005F":{"r":"EW-412TH","t":"IL76"},"00076":{"r":"EK-74723","t":"B742"},"00079":{"r":"EK-74798","t":"B742"},"00087":{"r":"EK-74799","t":"B742"},"0009D":{"r":"EK-73775","t":"B735"},"0009F":{"r":"EK-73772","t":"B735"},"000A2":{"r":"EK-SHA","t":"B735"},"00801":{"r":"4K8888","t":"A319"},"00803":{"r":"4KAZ03","t":"A319"},"00804":{"r":"4KAZ04","t":"A319"},"00805":{"r":"4KAZ05","t":"A319"},"00806":{"r":"4KAI06","t":"GLF5"},"00807":{"r":"4KAI07","t":"A320"},"00808":{"r":"4KJJ8","t":"G280"},"0080C":{"r":"4KAZ12","t":"B752"},"00826":{"r":"4K-AZ38","t":"B752"},"00828":{"r":"4KAZ40","t":"IL76"},"00829":{"r":"4KAZ41","t":"IL76"},"0082B":{"r":"4K-AZ43","t":"B752"},"00836":{"r":"4K-AZ54","t":"A320"},"00838":{"r":"4K-AZ56","t":"AN12"},"0083C":{"r":"4KAZ60","t":"IL76"},"00840":{"r":"4KAZ64","t":"E190"},"00841":{"r":"4KAZ65","t":"E190"},"00842":{"r":"4KAZ66","t":"E190"},"00843":{"r":"4KAZ67","t":"E190"},"0084D":{"r":"4KAZ77","t":"A320"},"0084E":{"r":"4KAZ78","t":"A320"},"0084F":{"r":"4KAZ79","t":"A320"},"00850":{"r":"4KAZ80","t":"A320"},"00851":{"r":"4KAZ81","t":"B763"},"00852":{"r":"4KAZ82","t":"B763"},"00853":{"r":"4KAZ83","t":"A320"},"00854":{"r":"4KAZ84","t":"A320"},"00855":{"r":"4KAZ85","t":"A345"},"00856":{"r":"4KAZ86","t":"A345"},"00858":{"r":"4K-AZ88","t":"GALX"},"00864":{"r":"4KAZ100","t":"IL76"},"00865":{"r":"4KAZ101","t":"IL76"},"008D0":{"r":"4K-AZ208","t":"G280"},"00918":{"r":"4K-AZ280","t":"G280"},"00B20":{"r":"4KSW800","t":"B744"},"00B21":{"r":"4KSW888","t":"B744"},"00B70":{"r":"4K-SW880","t":"B763"},"00B78":{"r":"4KAZ888","t":"GLF4"},"00BB9":{"r":"4K-SW808","t":"B763"},"00BBE":{"r":"4KSW008","t":"B744"},"00BBF":{"r":"4KAZ11","t":"B752"},"00BC0":{"r":"4KAI08","t":"A346"},"00BC4":{"r":"4KAI88","t":"GLF6"},"00BC6":{"r":"4K-LAR","t":"GLF4"},"00BC7":{"r":"4KJJ888","t":"GLF5"},"00BC8":{"r":"4K-ASG","t":"GLF6"},"00BE8":{"r":"4KAI01","t":"B763"},"00BE9":{"r":"4KAI001","t":"B77L"},"01097":{"r":"EX-37008"},"010F2":{"r":"EX-32001","t":"A320"},"010F4":{"r":"EX-37001","t":"B733"},"010F9":{"r":"EX-73401","t":"B734"},"01839":{"r":"EZ-A779","t":"B77L"},"0183A":{"r":"EZ-A778","t":"B77L"},"0183B":{"r":"EZ-A017","t":"B738"},"0183C":{"r":"EZ-A016","t":"B738"},"0183D":{"r":"EZ-A015","t":"B738"},"01842":{"r":"EZ-A010","t":"B752"},"01846":{"r":"EZ-A011","t":"B752"},"01847":{"r":"EZ-A012","t":"B752"},"01849":{"r":"EZ-A014","t":"B752"},"01854":{"r":"EZ-A106","t":"B712"},"01855":{"r":"EZ-A107","t":"B712"},"01858":{"r":"EZ-A004","t":"B738"},"01859":{"r":"EZ-A005","t":"B738"},"0185A":{"r":"EZ-A007","t":"B737"},"0185E":{"r":"EZ-A006","t":"B737"},"0185F":{"r":"EZ-A008","t":"B737"},"01860":{"r":"EZ-A009","t":"B737"},"01861":{"r":"EZ-A777","t":"B772"},"80001":{"r":"A5-RGF","t":"A319"},"80002":{"r":"A5-RGG","t":"A319"},"82209":{"r":"JU-1011","t":"B763"},"8220A":{"r":"JU-1012","t":"B763"},"83037":{"r":"UP-B5701","t":"B752"},"83065":{"r":"UP-Y4204","t":"YK42"},"83088":{"r":"UP-I7620","t":"IL76"},"830A5":{"r":"UP-A2001","t":"A320"},"830BD":{"r":"UP-CS302","t":"C25B"},"830C2":{"r":"UP-K3501","t":"B350"},"830ED":{"r":"UP-A3001","t":"A332"},"830FE":{"r":"UP-K3502","t":"B350"},"831E8":{"r":"UP-T5409","t":"T154"}}

File diff suppressed because one or more lines are too long

1
public_html/db/7C.json Normal file

File diff suppressed because one or more lines are too long

1
public_html/db/7C0.json Normal file

File diff suppressed because one or more lines are too long

1
public_html/db/7C1.json Normal file

File diff suppressed because one or more lines are too long

1
public_html/db/7C2.json Normal file

File diff suppressed because one or more lines are too long

1
public_html/db/7C3.json Normal file

File diff suppressed because one or more lines are too long

1
public_html/db/7C4.json Normal file

File diff suppressed because one or more lines are too long

1
public_html/db/7C6.json Normal file

File diff suppressed because one or more lines are too long

1
public_html/db/7C7.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More