diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
index 1923f8e..f9ad960 100644
--- a/.github/workflows/publish-release.yml
+++ b/.github/workflows/publish-release.yml
@@ -24,15 +24,11 @@ jobs:
with:
java-version: 11
- - name: Upload release
- run: ./gradlew uploadArchives --no-daemon --no-parallel
- env:
- ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
- ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
- ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.GPG_SECRET_KEYS }}
+ - uses: gradle/gradle-build-action@v2
- name: Publish release
- run: ./gradlew closeAndReleaseRepository --no-daemon --no-parallel
+ run: ./gradlew publishAllPublicationsToMavenCentralRepository
env:
- ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
- ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
+ ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USERNAME }}
+ ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }}
+ ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SECRET_KEYS }}
diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml
index 2941df7..787802d 100644
--- a/.github/workflows/publish-snapshot.yml
+++ b/.github/workflows/publish-snapshot.yml
@@ -19,12 +19,15 @@ jobs:
with:
java-version: 11
+ - uses: gradle/gradle-build-action@v2
+
- name: Retrieve version
run: |
- echo "VERSION_NAME=$(cat gradle.properties | grep -w 'VERSION_NAME' | cut -d'=' -f2)" >> $GITHUB_ENV
+ echo "VERSION_NAME=$(cat gradle.properties | grep -w "VERSION_NAME" | cut -d'=' -f2)" >> $GITHUB_ENV
+
- name: Publish snapshot
- run: ./gradlew uploadArchives --no-daemon --no-parallel
+ run: ./gradlew publishAllPublicationsToMavenCentralRepository
if: endsWith(env.VERSION_NAME, '-SNAPSHOT')
env:
- ORG_GRADLE_PROJECT_SONATYPE_NEXUS_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
- ORG_GRADLE_PROJECT_SONATYPE_NEXUS_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
+ ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_USERNAME }}
+ ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }}
diff --git a/README.md b/README.md
index 257d828..b5db560 100644
--- a/README.md
+++ b/README.md
@@ -5,19 +5,19 @@ This library decodes ADS-B Messages and creates an easy to work with track objec
Message support status
-| Downlink Format | Human Readable | Supported | Note |
-|-----------------|------------------------------------------|-----------|------|
-| DF0 | Short Air-Air Surveillance | ✅ | |
-| DF4 | Surveillance, Altitude request | ✅ | Updates Altitude information + SPI (Ident) |
-| DF5 | Surveillance, Identity request | ✅ | Updates Callsign + SPI (Ident) |
-| DF11 | MODE S only all-call | ✅ | Broadcasts ICAO Mode-S address |
-| DF16 | Long Air-Air Surveillance | ✅ | Logs warning when ACAS RA is active |
-| DF17 | Extended Squitter (Most ADS-B Data) | ⚠️ | Partial supported see DF17/18 status below |
-| DF18 | Extended Squitter/Supplementary (Ground) | ⚠️ | Partial supported see DF17/18 status below |
-| DF19 | Extended Squitter Military | ❌ | Not supported at this stage |
-| DF20 | Comm-B Altitude | ⚠️ | Partial supported see DF20/21 status below |
-| DF21 | Comm-B Identity | ⚠️ | Partial supported see DF20/21 status below |
-| DF24 | Comm-B ELM | ❌ | Not supported at this stage |
+| Downlink Format | Human Readable | Supported | Note |
+|-----------------|------------------------------------------|-----------|-----------------------------------------------|
+| DF0 | Short Air-Air Surveillance | ✅ | |
+| DF4 | Surveillance, Altitude request | ✅ | Updates Altitude information + SPI (Ident) |
+| DF5 | Surveillance, Identity request | ✅ | Updates Callsign + SPI (Ident) |
+| DF11 | MODE S only all-call | ✅ | Broadcasts ICAO Mode-S address |
+| DF16 | Long Air-Air Surveillance | ✅ | Logs warning when ACAS RA is active |
+| DF17 | Extended Squitter (Most ADS-B Data) | ⚠️ | Partially supported, see DF17/18 status below |
+| DF18 | Extended Squitter/Supplementary (Ground) | ⚠️ | Partially supported, see DF17/18 status below |
+| DF19 | Extended Squitter Military | ❌ | Not supported at this stage |
+| DF20 | Comm-B Altitude | ⚠️ | Partially supported, see DF20/21 status below |
+| DF21 | Comm-B Identity | ⚠️ | Partially supported, see DF20/21 status below |
+| DF24 | Comm-B ELM | ⚠️ | Implemented basic sequence no decoding. |
## DF17/DF18 - Extended Squitter
@@ -27,48 +27,47 @@ DF17 is used for aircraft, while DF18 is used for other vessels (ground vehicles
Most features of the DF17/18 protocol have been implemented, some message lack support for specific fields.
-| Type Code | Human Readable | Supported | Note |
-|-----------|------------------------------|-----------|------|
-| 0 | Airborne/Surface No altitude | ✅ |
-| 1 | Aircraft Identification | ✅ |
-| 2 | Aircraft Identification | ✅ |
-| 3 | Aircraft Identification | ✅ |
-| 4 | Aircraft Identification | ✅ |
-| 5 | Surface Position | ❌ | Not implemented yet
-| 6 | Surface Position | ❌ | Not implemented yet
-| 7 | Surface Position | ❌ | Not implemented yet
-| 8 | Surface Position | ❌ | Not implemented yet
-| 9 | Airborne Position | ✅ |
-| 10 | Airborne Position | ✅ |
-| 11 | Airborne Position | ✅ |
-| 12 | Airborne Position | ✅ |
-| 13 | Airborne Position | ✅ |
-| 14 | Airborne Position | ✅ |
-| 15 | Airborne Position | ✅ |
-| 16 | Airborne Position | ✅ |
-| 17 | Airborne Position | ✅ |
-| 18 | Airborne Position | ✅ |
-| 19 | Airborne Velocity | ✅ |
-| 20 | Airborne Position | ✅ |
-| 21 | Airborne Position | ✅ |
-| 22 | Airborne Position | ✅ |
-| 23 | Test Message | ✅ |
-| 24 | Surface System Status | ❌ | Not implemented yet
-| 25 | Reserved Message | ✅ |
-| 26 | Reserved Message | ✅ |
-| 27 | Reserved (Trajectory Change) | ✅ |
-| 28 | Aircraft Status Message | ✅ | Priority mode A code (emergency) + TCAS/ACAS RA Broadcast
-| 29 | Target Status Message | ✅ | Partial support
-| 30 | Reserved Message | ✅ |
-| 31 | Aircraft Operational Status | ✅ | Partial support
-
+| Type Code | Human Readable | Supported | Note |
+|-----------|------------------------------|-----------|-----------------------------------------------------------|
+| 0 | Airborne/Surface No altitude | ✅ | |
+| 1 | Aircraft Identification | ✅ | |
+| 2 | Aircraft Identification | ✅ | |
+| 3 | Aircraft Identification | ✅ | |
+| 4 | Aircraft Identification | ✅ | |
+| 5 | Surface Position | ❌ | Not implemented yet |
+| 6 | Surface Position | ❌ | Not implemented yet |
+| 7 | Surface Position | ❌ | Not implemented yet |
+| 8 | Surface Position | ❌ | Not implemented yet |
+| 9 | Airborne Position | ✅ | |
+| 10 | Airborne Position | ✅ | |
+| 11 | Airborne Position | ✅ | |
+| 12 | Airborne Position | ✅ | |
+| 13 | Airborne Position | ✅ | |
+| 14 | Airborne Position | ✅ | |
+| 15 | Airborne Position | ✅ | |
+| 16 | Airborne Position | ✅ | |
+| 17 | Airborne Position | ✅ | |
+| 18 | Airborne Position | ✅ | |
+| 19 | Airborne Velocity | ✅ | |
+| 20 | Airborne Position | ✅ | |
+| 21 | Airborne Position | ✅ | |
+| 22 | Airborne Position | ✅ | |
+| 23 | Test Message | ✅ | |
+| 24 | Surface System Status | ❌ | Not implemented yet |
+| 25 | Reserved Message | ✅ | |
+| 26 | Reserved Message | ✅ | |
+| 27 | Reserved (Trajectory Change) | ✅ | |
+| 28 | Aircraft Status Message | ✅ | Priority mode A code (emergency) + TCAS/ACAS RA Broadcast |
+| 29 | Target Status Message | ✅ | Partial support |
+| 30 | Reserved Message | ✅ | |
+| 31 | Aircraft Operational Status | ✅ | Partial support |
## DF20/21 Comm-B
DF20/21 messages are replies to data requests from a radar station, you'll only receive these messages if Mode-S radar
is actively requesting this information. You will only receive messages requested by the radar.
-Message is structure as follows
+Message is structured as follows:
```
LSB |1----|6--|9----|14----|20-----------|33------------------------------------------------------|89-----------------------|
@@ -96,57 +95,56 @@ You run through each BDS and pass the message to the decoder, if the message doe
Repeat this process until you have a match.
At this moment the coded logic has too many incorrect matches and thus decided to disable BDS guessing.
-We are actively looking for a fix, or at least ability to enable this with a experimental flag.
+We are actively looking for a fix, or at least the ability to enable this with an experimental flag.
We hope with more BDS implemented the guessing accuracy will improve.
-| BDS | Human Readable | Supported | Note |
-|-----|--------------------------------------------|-----------|------|
-| 1,0 | Data link capability report | ❌ | Detection implemented, decoding missing
-| 1,7 | Common usage GICB capability report | ✅ |
-| 1,8 | Mode S services GICB capability report | ❌ |
-| 1,9 | Mode S services GICB capability report | ❌ |
-| 1,A | Mode S services GICB capability report | ❌ |
-| 1,B | Mode S services GICB capability report | ❌ |
-| 1,C | Mode S services GICB capability report | ❌ |
-| 1,D | Mode S services GICB capability report | ❌ |
-| 1,E | Mode S services GICB capability report | ❌ |
-| 1,F | Mode S services GICB capability report | ❌ |
-| 2,0 | Aircraft Identification | ✅ |
-| 2,1 | Aircraft and Airline registration marking | ✅️ | Experimental
-| 2,2 | Antenna positions | ❌ |
-| 2,5 | Antenna type | ❌ |
-| 3,0 | ACAS Active resolution advisory | ❌ | Detection implemented, decoding missing
-| 4,0 | Selected vertical intention | ✅️ |
-| 4,1 | Next waypoint details | ❌ | 9 Characters
-| 4,2 | Next waypoint details | ❌ | Waypoint lat/lon + crossing altitude
-| 4,3 | Next waypoint details | ❌ | Bearing, time and distance to waypoint
-| 4,4 | Meteorological routine air report | ✅ |
-| 4,5 | Meteorological hazard report | ✅ |
-| 4,8 | VHF Channel report | ❌ | Info on VHF 1/2/3 (frequency + status) & Guard status
-| 5,0 | Track and turn report | ✅ |
-| 5,1 | Position report coarse | ❌ |
-| 5,2 | Position report fine | ❌ |
-| 5,3 | Air-reference state vector | ✅ |
-| 5,4 | Waypoint 1 | ❌ | 5 Chars, ETA, Estimated level, time to go
-| 5,5 | Waypoint 2 | ❌ | 5 Chars, ETA, Estimated level, time to go
-| 5,5 | Waypoint 3 | ❌ | 5 Chars, ETA, Estimated level, time to go
-| 5,F | Quasi-static parameter monitoring | ❌ |
-| 6,0 | Heading and speed report | ✅ |
-| 6,1 | Priority/emergency status | ❌ |
-| 6,5 | Aircraft operational status | ❌ |
-| E,3 | Transponder type/part number | ❌ |
-| E,4 | Transponder software revision number | ❌ |
-| E,5 | ACAS type/part number | ❌ |
-| E,6 | ACAS software revision number | ❌ |
-| E,7 | Transponder status and diagnostics | ❌ |
-| E,A | Vendor specific status and diagnostics | ❌ |
-| F,1 | Military application | ❌ |
-| F,2 | Military application | ❌ |
-
+| BDS | Human Readable | Supported | Note |
+|-----|-------------------------------------------|-----------|-------------------------------------------------------|
+| 1,0 | Data link capability report | ❌ | Detection implemented, decoding missing |
+| 1,7 | Common usage GICB capability report | ✅ | |
+| 1,8 | Mode S services GICB capability report | ❌ | |
+| 1,9 | Mode S services GICB capability report | ❌ | |
+| 1,A | Mode S services GICB capability report | ❌ | |
+| 1,B | Mode S services GICB capability report | ❌ | |
+| 1,C | Mode S services GICB capability report | ❌ | |
+| 1,D | Mode S services GICB capability report | ❌ | |
+| 1,E | Mode S services GICB capability report | ❌ | |
+| 1,F | Mode S services GICB capability report | ❌ | |
+| 2,0 | Aircraft Identification | ✅ | |
+| 2,1 | Aircraft and Airline registration marking | ✅️ | Experimental |
+| 2,2 | Antenna positions | ❌ | |
+| 2,5 | Antenna type | ❌ | |
+| 3,0 | ACAS Active resolution advisory | ❌ | Detection implemented, decoding missing |
+| 4,0 | Selected vertical intention | ✅️ | |
+| 4,1 | Next waypoint details | ❌ | 9 Characters |
+| 4,2 | Next waypoint details | ❌ | Waypoint lat/lon + crossing altitude |
+| 4,3 | Next waypoint details | ❌ | Bearing, time and distance to waypoint |
+| 4,4 | Meteorological routine air report | ✅ | |
+| 4,5 | Meteorological hazard report | ✅ | |
+| 4,8 | VHF Channel report | ❌ | Info on VHF 1/2/3 (frequency + status) & Guard status |
+| 5,0 | Track and turn report | ✅ | |
+| 5,1 | Position report coarse | ❌ | |
+| 5,2 | Position report fine | ❌ | |
+| 5,3 | Air-reference state vector | ✅ | |
+| 5,4 | Waypoint 1 | ❌ | 5 Chars, ETA, Estimated level, time to go |
+| 5,5 | Waypoint 2 | ❌ | 5 Chars, ETA, Estimated level, time to go |
+| 5,5 | Waypoint 3 | ❌ | 5 Chars, ETA, Estimated level, time to go |
+| 5,F | Quasi-static parameter monitoring | ❌ | |
+| 6,0 | Heading and speed report | ✅ | |
+| 6,1 | Priority/emergency status | ❌ | |
+| 6,5 | Aircraft operational status | ❌ | |
+| E,3 | Transponder type/part number | ❌ | |
+| E,4 | Transponder software revision number | ❌ | |
+| E,5 | ACAS type/part number | ❌ | |
+| E,6 | ACAS software revision number | ❌ | |
+| E,7 | Transponder status and diagnostics | ❌ | |
+| E,A | Vendor specific status and diagnostics | ❌ | |
+| F,1 | Military application | ❌ | |
+| F,2 | Military application | ❌ | |
# Installation
-This package is available through maven central
+This package is available through Maven Central
Pom
```xml
@@ -159,7 +157,7 @@ Pom
Gradle
```
- compile('aero.t2s:mode-s:0.2.0-SNAPSHOT')
+ compile('aero.t2s:mode-s:0.2.0-SNAPSHOT')
```
# Usage
@@ -191,9 +189,9 @@ class Main
## Using Aircraft Database
-This library is compatible with opensky dataset (https://opensky-network.org/datasets/metadata/).
+This library is compatible with [OpenSky dataset](https://opensky-network.org/datasets/metadata/).
-In order to use the database version you can start ModeS plugin as follows
+In order to use the database version you can start ModeS plugin as follows:
```java
class Main
@@ -222,9 +220,9 @@ class Main
# Contributing
-You can contribute to this project by reporting/fixing bugs or implemented a new packet.
+You can contribute to this project by reporting/fixing bugs or implement a new packet.
We are always looking for help on this project.
# License
-This library is Apache 2.0 licensed read the full license [here](LICENSE).
+This library is Apache 2.0 licensed. You can read the full license [here](LICENSE).
diff --git a/build.gradle b/build.gradle
index 6e32cbf..b6a5e66 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,7 +3,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
+ classpath 'com.vanniktech:gradle-maven-publish-plugin:0.22.0'
}
}
@@ -11,8 +11,18 @@ apply plugin: 'idea'
apply plugin: 'java-library'
apply plugin: "com.vanniktech.maven.publish"
+def javaTargetVersion = 11
+
+sourceCompatibility = javaTargetVersion
+
+subprojects {
+ tasks.withType(JavaCompile.class) {
+ options.release = javaTargetVersion
+ }
+}
+
dependencies {
- compile 'org.slf4j:slf4j-api:1.7.32'
+ api 'org.slf4j:slf4j-api:1.7.32'
// Use JUnit test framework
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
@@ -24,15 +34,20 @@ repositories {
mavenLocal()
}
-compileJava {
- sourceCompatibility = 11
- targetCompatibility = 11
+tasks.withType(JavaCompile.class) {
+ options.release = javaTargetVersion
}
test {
useJUnitPlatform()
}
+
+mavenPublishing {
+ publishToMavenCentral()
+ signAllPublications()
+}
+
signing {
if (hasProperty('SIGNING_PRIVATE_KEY')) {
useInMemoryPgpKeys(SIGNING_PRIVATE_KEY, "")
diff --git a/example/.gitignore b/example/.gitignore
deleted file mode 100644
index c379362..0000000
--- a/example/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-build/
-.gradle/
-
diff --git a/example/build/classes/java/main/example/Demo$1.class b/example/build/classes/java/main/example/Demo$1.class
deleted file mode 100644
index c222b03..0000000
Binary files a/example/build/classes/java/main/example/Demo$1.class and /dev/null differ
diff --git a/example/build/classes/java/main/example/Demo$2.class b/example/build/classes/java/main/example/Demo$2.class
deleted file mode 100644
index c1d57f1..0000000
Binary files a/example/build/classes/java/main/example/Demo$2.class and /dev/null differ
diff --git a/example/build/classes/java/main/example/Demo.class b/example/build/classes/java/main/example/Demo.class
deleted file mode 100644
index b902e3b..0000000
Binary files a/example/build/classes/java/main/example/Demo.class and /dev/null differ
diff --git a/example/build/classes/java/main/example/FlightsTable.class b/example/build/classes/java/main/example/FlightsTable.class
deleted file mode 100644
index 0c4acb6..0000000
Binary files a/example/build/classes/java/main/example/FlightsTable.class and /dev/null differ
diff --git a/example/build/classes/java/main/example/flight/FlightFrame$1.class b/example/build/classes/java/main/example/flight/FlightFrame$1.class
deleted file mode 100644
index d899d5b..0000000
Binary files a/example/build/classes/java/main/example/flight/FlightFrame$1.class and /dev/null differ
diff --git a/example/build/classes/java/main/example/flight/FlightFrame$GcibTable.class b/example/build/classes/java/main/example/flight/FlightFrame$GcibTable.class
deleted file mode 100644
index 76f0777..0000000
Binary files a/example/build/classes/java/main/example/flight/FlightFrame$GcibTable.class and /dev/null differ
diff --git a/example/build/classes/java/main/example/flight/FlightFrame.class b/example/build/classes/java/main/example/flight/FlightFrame.class
deleted file mode 100644
index 81f11cf..0000000
Binary files a/example/build/classes/java/main/example/flight/FlightFrame.class and /dev/null differ
diff --git a/example/build/tmp/compileJava/source-classes-mapping.txt b/example/build/tmp/compileJava/source-classes-mapping.txt
deleted file mode 100644
index a1ed673..0000000
--- a/example/build/tmp/compileJava/source-classes-mapping.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-example/FlightsTable.java
- example.FlightsTable
-example/flight/FlightFrame.java
- example.flight.FlightFrame
- example.flight.FlightFrame$1
- example.flight.FlightFrame$GcibTable
-example/Demo.java
- example.Demo
- example.Demo$1
- example.Demo$2
diff --git a/example/gradle/wrapper/gradle-wrapper.jar b/example/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 62d4c05..0000000
Binary files a/example/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/example/gradle/wrapper/gradle-wrapper.properties b/example/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 622ab64..0000000
--- a/example/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
diff --git a/example/gradlew b/example/gradlew
deleted file mode 100755
index fbd7c51..0000000
--- a/example/gradlew
+++ /dev/null
@@ -1,185 +0,0 @@
-#!/usr/bin/env sh
-
-#
-# Copyright 2015 the original author or authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=`expr $i + 1`
- done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-exec "$JAVACMD" "$@"
diff --git a/example/gradlew.bat b/example/gradlew.bat
deleted file mode 100644
index a9f778a..0000000
--- a/example/gradlew.bat
+++ /dev/null
@@ -1,104 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/example/build.gradle b/examples/gui/build.gradle
similarity index 75%
rename from example/build.gradle
rename to examples/gui/build.gradle
index 78563b7..89bb44a 100644
--- a/example/build.gradle
+++ b/examples/gui/build.gradle
@@ -8,5 +8,5 @@ repositories {
}
dependencies {
- compile ('aero.t2s:mode-s:0.2.0-SNAPSHOT')
+ implementation project(':')
}
diff --git a/example/src/main/java/example/Demo.java b/examples/gui/src/main/java/example/Demo.java
similarity index 100%
rename from example/src/main/java/example/Demo.java
rename to examples/gui/src/main/java/example/Demo.java
index 71f82c1..6ded03b 100644
--- a/example/src/main/java/example/Demo.java
+++ b/examples/gui/src/main/java/example/Demo.java
@@ -1,18 +1,18 @@
package example;
-import aero.t2s.modes.ModeS;
-import aero.t2s.modes.Track;
-import example.flight.FlightFrame;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.swing.*;
-
import java.awt.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
+import javax.swing.*;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import aero.t2s.modes.ModeS;
+import aero.t2s.modes.Track;
+import example.flight.FlightFrame;
/*
* ButtonDemo.java requires the following files:
diff --git a/example/src/main/java/example/FlightsTable.java b/examples/gui/src/main/java/example/FlightsTable.java
similarity index 55%
rename from example/src/main/java/example/FlightsTable.java
rename to examples/gui/src/main/java/example/FlightsTable.java
index d93da7c..7c3cabc 100644
--- a/example/src/main/java/example/FlightsTable.java
+++ b/examples/gui/src/main/java/example/FlightsTable.java
@@ -1,9 +1,9 @@
package example;
-import aero.t2s.modes.Track;
-
-import javax.swing.table.AbstractTableModel;
import java.util.List;
+import javax.swing.table.AbstractTableModel;
+
+import aero.t2s.modes.Track;
class FlightsTable extends AbstractTableModel {
private static String[] columns = {
@@ -43,15 +43,19 @@ public String getColumnName(int column) {
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
- switch (columnIndex) {
- case 0: return tracks.get(rowIndex).getIcao();
- case 1: return tracks.get(rowIndex).getCallsign();
- case 2: return String.format("%2.4f", tracks.get(rowIndex).getLat());
- case 3: return String.format("%03.4f", tracks.get(rowIndex).getLon());
- case 4: return tracks.get(rowIndex).getRocd();
- case 5: return tracks.get(rowIndex).getAltitude().getAltitude();
- case 6: return Math.round(tracks.get(rowIndex).getMagneticHeading());
- case 7: return (int)tracks.get(rowIndex).getGs();
+ try {
+ switch (columnIndex) {
+ case 0: return tracks.get(rowIndex).getIcao();
+ case 1: return tracks.get(rowIndex).getCallsign();
+ case 2: return String.format("%2.4f", tracks.get(rowIndex).getLat());
+ case 3: return String.format("%03.4f", tracks.get(rowIndex).getLon());
+ case 4: return tracks.get(rowIndex).getRocd();
+ case 5: return tracks.get(rowIndex).getAltitude().getAltitude();
+ case 6: return Math.round(tracks.get(rowIndex).getMagneticHeading());
+ case 7: return (int)tracks.get(rowIndex).getGs();
+ }
+ } catch (IndexOutOfBoundsException ex) {
+ return null;
}
return null;
diff --git a/example/src/main/java/example/flight/FlightFrame.form b/examples/gui/src/main/java/example/flight/FlightFrame.form
similarity index 63%
rename from example/src/main/java/example/flight/FlightFrame.form
rename to examples/gui/src/main/java/example/flight/FlightFrame.form
index faaf4f2..fd6ed8c 100644
--- a/example/src/main/java/example/flight/FlightFrame.form
+++ b/examples/gui/src/main/java/example/flight/FlightFrame.form
@@ -19,7 +19,7 @@
-
+
@@ -35,14 +35,6 @@
-
-
-
-
-
-
-
-
@@ -169,7 +161,7 @@
-
+
@@ -177,31 +169,47 @@
-
+
-
+
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -209,7 +217,7 @@
-
+
@@ -223,7 +231,7 @@
-
+
@@ -231,7 +239,7 @@
-
+
@@ -244,31 +252,143 @@
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/src/main/java/example/flight/FlightFrame.java b/examples/gui/src/main/java/example/flight/FlightFrame.java
similarity index 61%
rename from example/src/main/java/example/flight/FlightFrame.java
rename to examples/gui/src/main/java/example/flight/FlightFrame.java
index 8f4ff43..5723f2d 100644
--- a/example/src/main/java/example/flight/FlightFrame.java
+++ b/examples/gui/src/main/java/example/flight/FlightFrame.java
@@ -1,20 +1,17 @@
package example.flight;
-import aero.t2s.modes.Track;
-import aero.t2s.modes.constants.Hazard;
-
-import javax.swing.*;
-import javax.swing.table.AbstractTableModel;
import java.util.Timer;
import java.util.TimerTask;
+import javax.swing.*;
+
+import aero.t2s.modes.Track;
+import aero.t2s.modes.constants.Hazard;
public class FlightFrame extends JFrame {
java.util.Timer timer = new Timer();
private Track track;
-
private JTabbedPane tabbedPane1;
- private JTable gcibReportTable;
private JList meteoHazards;
private JLabel meteoHumidity;
private JLabel meteoSat;
@@ -23,11 +20,16 @@ public class FlightFrame extends JFrame {
private JLabel meteoRadioHeight;
private JPanel mainPanel;
private JTextArea flightModelLeft;
- private JTextArea flightInfoRight;
- private JTextArea infoAccuracy;
private JTextArea infoAbds;
private JTextArea acas;
+ private JTextArea register05;
+ private JTextArea register06;
+ private JTextArea register07;
+ private JTextArea register08;
+ private JTextArea register09;
+ private JTextArea register17;
+
public FlightFrame(Track track) {
this.track = track;
@@ -47,8 +49,6 @@ public void run() {
private void updateContent() {
setTitle(track.getCallsign() + " - " + track.getIcao());
- ((GcibTable)gcibReportTable.getModel()).fireTableDataChanged();
-
// Meteo Hazard
((DefaultListModel)meteoHazards.getModel()).clear();
if (track.getMeteo().getTurbulence() != Hazard.NIL) {
@@ -83,7 +83,6 @@ private void updateContent() {
"CALLSIGN: %s\n" +
"WTC: %s\n" +
"ATYP: %s\n (REG: %s)\n" +
- "Width & Length Code: %s\n" +
"OPERATOR: %s\n" +
"MODE A: %04d\n" +
"State: %s\n" +
@@ -95,9 +94,7 @@ private void updateContent() {
"Flight Status SPI: %s\n" +
"-------------------------------\n" +
"Altitude: %d%s (step size: %d)\n" +
- "Baro Altitude: %d\n" +
"Geometric offset: %d\n" +
- "GNSS Altitude: %d\n" +
"Selected Altitude Source: %s\n" +
"Selected Altitude: %d\n" +
"Selected Altitude FMS: %s\n" +
@@ -111,7 +108,6 @@ private void updateContent() {
"ROCD Baro Available: %s\n" +
"Baro ROCD: %dft/min\n" +
"------------------------------\n" +
- "Heading Source: %s\n" +
"Magnetic Heading: %d\n" +
"True Heading: %d\n" +
"Selected Heading: %d\n" +
@@ -136,7 +132,6 @@ private void updateContent() {
track.getCallsign(),
track.getWtc(),
track.getAtype(), track.getRegistration(),
- track.getLengthWidthCode().name(),
track.getOperator(),
track.getModeA(),
track.isGroundBit() ? "GROUND" : "AIRBORNE",
@@ -147,9 +142,7 @@ private void updateContent() {
track.getFlightStatus().isAlert() ? "YES" : "NO",
track.getFlightStatus().isSpi() ? "YES" : "NO",
(int)track.getAltitude().getAltitude(), track.getAltitude().isMetric() ? "M" : "FT", track.getAltitude().getStep(),
- track.getBaroAltitude(),
track.getGeometricHeightOffset(),
- track.getGnssHeight(),
track.getSelectedAltitudeSource().toString(),
track.getSelectedAltitude(),
track.getSelectedAltitudeManagedFms() ? "YES" : "NO",
@@ -160,7 +153,6 @@ private void updateContent() {
track.getRocd(),
track.getRocdSourceBaro() ? "YES" : "NO",
(int)track.getBaroRocd(),
- track.isMagneticHeading() ? "MAGNETIC" : "TRUE",
(int)track.getMagneticHeading(),
(int)track.getTrueHeading(),
(int)track.getSelectedHeading(),
@@ -216,30 +208,17 @@ private void updateContent() {
track.getAcas().getTargetRange()
));
- infoAccuracy.setText(String.format(
- "NIC: %d\n" +
- "NICa: %d\n" +
- "NICb: %d\n" +
- "NICc: %d\n" +
- "NACv: %d\n" +
- "NACp: %s\n" +
- "SIL: %d\n" +
- "----------------------------\n",
- track.getNIC(),
- track.getNICa(),
- track.getNICb(),
- track.getNICc(),
- track.getNACv(),
- track.getNACp(),
- track.getSil()
- ));
-
- infoAbds.setText(String.format(
+ infoAbds.setText(
"Version: %s\n" +
- "Single Antenna: %s\n",
- track.getVersion().name(),
- track.getSingleAntenna() ? "YES" : "NO"
- ));
+ track.getVersion().name()
+ );
+
+ register05.setText(track.register05().toString());
+ register06.setText(track.register06().toString());
+ register07.setText(track.register07().toString());
+ register08.setText(track.register08().toString());
+ register09.setText(track.register09().toString());
+ register17.setText(track.register17().toString());
}
private String formatAcasResolution() {
@@ -249,108 +228,4 @@ private String formatAcasResolution() {
return track.getAcas().getResolutionAdvisory().toString();
}
-
- private void createUIComponents() {
- gcibReportTable = new JTable(new GcibTable(track));
- gcibReportTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
- }
-
- private class GcibTable extends AbstractTableModel {
- private final String[] GCIB = {
- "0,5 Extended squitter airborne position",
- "0,6 Extended squitter surface position",
- "0,7 Extended squitter identification and category",
- "0,7 Extended squitter status",
- "0,9 Extended squitter airborne velocity information",
- "0,A Extended squitter event-driven information",
- "2,0 Aircraft identification",
- "2,1 Aircraft registration number",
- "4,0 Selected vertical intention",
- "4,1 Next waypoint identifier",
- "4,2 Next waypoint position",
- "4,3 Next waypoint information",
- "4,4 Meteorological routine report",
- "4,5 Meteorological hazard report",
- "4,8 VHF channel report",
- "5,0 Track and turn report",
- "5,1 Position coarse",
- "5,2 Position fine",
- "5,3 Air-referenced state vector",
- "5,4 Waypoint 1",
- "5,5 Waypoint 2",
- "5,6 Waypoint 3",
- "5,F Quasi-static parameter monitoring",
- "6,0 Heading and speed report",
- };
-
- public GcibTable(Track track) {
- }
-
- @Override
- public int getRowCount() {
- if (track.getCapabilityReport().isAvailable()) {
- return 24;
- }
-
- return 1;
- }
-
- @Override
- public int getColumnCount() {
- return 2;
- }
-
- @Override
- public String getColumnName(int column) {
- switch (column) {
- case 0: return "GCIB";
- case 1: return "Available";
- }
-
- return null;
- }
-
- @Override
- public Object getValueAt(int rowIndex, int columnIndex) {
- if (!track.getCapabilityReport().isAvailable()) {
- if (columnIndex == 0)
- return "Not Available";
- else
- return "";
- }
-
- if (columnIndex == 0) {
- return GCIB[rowIndex];
- }
-
- switch (rowIndex) {
- case 0: return track.getCapabilityReport().isBds05() ? "YES" : "NO";
- case 1: return track.getCapabilityReport().isBds06() ? "YES" : "NO";
- case 2: return track.getCapabilityReport().isBds07() ? "YES" : "NO";
- case 3: return track.getCapabilityReport().isBds08() ? "YES" : "NO";
- case 4: return track.getCapabilityReport().isBds09() ? "YES" : "NO";
- case 5: return track.getCapabilityReport().isBds0A() ? "YES" : "NO";
- case 6: return track.getCapabilityReport().isBds20() ? "YES" : "NO";
- case 7: return track.getCapabilityReport().isBds21() ? "YES" : "NO";
- case 8: return track.getCapabilityReport().isBds40() ? "YES" : "NO";
- case 9: return track.getCapabilityReport().isBds41() ? "YES" : "NO";
- case 10: return track.getCapabilityReport().isBds42() ? "YES" : "NO";
- case 11: return track.getCapabilityReport().isBds43() ? "YES" : "NO";
- case 12: return track.getCapabilityReport().isBds44() ? "YES" : "NO";
- case 13: return track.getCapabilityReport().isBds45() ? "YES" : "NO";
- case 14: return track.getCapabilityReport().isBds48() ? "YES" : "NO";
- case 15: return track.getCapabilityReport().isBds50() ? "YES" : "NO";
- case 16: return track.getCapabilityReport().isBds51() ? "YES" : "NO";
- case 17: return track.getCapabilityReport().isBds52() ? "YES" : "NO";
- case 18: return track.getCapabilityReport().isBds53() ? "YES" : "NO";
- case 19: return track.getCapabilityReport().isBds54() ? "YES" : "NO";
- case 20: return track.getCapabilityReport().isBds55() ? "YES" : "NO";
- case 21: return track.getCapabilityReport().isBds56() ? "YES" : "NO";
- case 22: return track.getCapabilityReport().isBds5F() ? "YES" : "NO";
- case 23: return track.getCapabilityReport().isBds60() ? "YES" : "NO";
- }
-
- return null;
- }
- }
}
diff --git a/examples/stdout/build.gradle b/examples/stdout/build.gradle
new file mode 100644
index 0000000..89bb44a
--- /dev/null
+++ b/examples/stdout/build.gradle
@@ -0,0 +1,12 @@
+apply plugin: 'idea'
+apply plugin: 'java'
+apply plugin: 'application'
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+dependencies {
+ implementation project(':')
+}
diff --git a/examples/stdout/src/main/java/aero/t2s/modes/examples/StdOutExample.java b/examples/stdout/src/main/java/aero/t2s/modes/examples/StdOutExample.java
new file mode 100644
index 0000000..336e666
--- /dev/null
+++ b/examples/stdout/src/main/java/aero/t2s/modes/examples/StdOutExample.java
@@ -0,0 +1,67 @@
+package aero.t2s.modes.examples;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import aero.t2s.modes.ModeS;
+import aero.t2s.modes.decoder.df.DF20;
+import aero.t2s.modes.decoder.df.DF21;
+
+public class StdOutExample {
+ public static void main(String[] args) {
+ ModeS modes = new ModeS(
+ "192.168.178.190", // Host IP where the Dump1090 server is running
+ 30002, // The port with raw output (default 30002)
+ 51, // Decimal latitude
+ 4 // Decimal longitude
+ );
+// modes.onTrackCreated(track -> System.out.println("CREATED " + track.toString()));
+// modes.onTrackUpdated(track -> System.out.println("UPDATED " + track.toString()));
+// modes.onTrackDeleted(track -> System.out.println("DELETED " + track.toString()));
+
+ MessageCount count = new MessageCount();
+
+ Timer timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ if (count.total > 0) {
+ System.out.print("Multi BDS messages last 60 seconds: " + count.multi + " ( " + Math.round((float)count.multi / count.total) + "%)\n");
+ count.reset();
+ }
+ }
+ }, 0, 60000);
+
+ modes.onMessage(df -> {
+ count.increment();;
+ if (df instanceof DF20) {
+ if (((DF20) df).isMultipleMatches())
+ count.incrementMulti();
+ }
+ if (df instanceof DF21) {
+ if (((DF21) df).isMultipleMatches())
+ count.incrementMulti();
+ }
+ });
+
+ modes.start();
+ }
+
+ static class MessageCount {
+ long total = 0;
+ long multi = 0;
+
+ void reset() {
+ total = 0;
+ multi = 0;
+ }
+
+ void increment() {
+ total++;
+ }
+
+ void incrementMulti() {
+ multi++;
+ }
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index a8f0d77..c47ae13 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
GROUP=aero.t2s
-VERSION_NAME=0.2.5-SNAPSHOT
+VERSION_NAME=0.2.6-SNAPSHOT
POM_ARTIFACT_ID=mode-s
POM_NAME=Mode-S/ADS-B (1090Mhz)
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 62d4c05..249e583 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 6c9a224..ae04661 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index fbd7c51..a69d9cb 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
#
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,101 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=`expr $i + 1`
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index a9f778a..53a6b23 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -54,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -64,21 +64,6 @@ echo location of your Java installation.
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
:execute
@rem Setup the command line
@@ -86,17 +71,19 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..f3f0a36
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include('examples:gui', 'examples:stdout')
diff --git a/src/main/java/aero/t2s/modes/CapabilityReport.java b/src/main/java/aero/t2s/modes/CapabilityReport.java
index df9a433..67bbf8c 100644
--- a/src/main/java/aero/t2s/modes/CapabilityReport.java
+++ b/src/main/java/aero/t2s/modes/CapabilityReport.java
@@ -214,4 +214,35 @@ public void all() {
bds5F = true;
bds60 = true;
}
+
+
+ @Override
+ public String toString() {
+ return "Bds17{" +
+ "\nbds05=" + bds05 +
+ ",\n bds06=" + bds06 +
+ ",\n bds07=" + bds07 +
+ ",\n bds08=" + bds08 +
+ ",\n bds09=" + bds09 +
+ ",\n bds0A=" + bds0A +
+ ",\n bds20=" + bds20 +
+ ",\n bds21=" + bds21 +
+ ",\n bds40=" + bds40 +
+ ",\n bds41=" + bds41 +
+ ",\n bds42=" + bds42 +
+ ",\n bds43=" + bds43 +
+ ",\n bds44=" + bds44 +
+ ",\n bds45=" + bds45 +
+ ",\n bds48=" + bds48 +
+ ",\n bds50=" + bds50 +
+ ",\n bds51=" + bds51 +
+ ",\n bds52=" + bds52 +
+ ",\n bds53=" + bds53 +
+ ",\n bds54=" + bds54 +
+ ",\n bds55=" + bds55 +
+ ",\n bds56=" + bds56 +
+ ",\n bds5F=" + bds5F +
+ ",\n bds60=" + bds60 +
+ "\n}";
+ }
}
diff --git a/src/main/java/aero/t2s/modes/CprPosition.java b/src/main/java/aero/t2s/modes/CprPosition.java
index 5326368..184710c 100644
--- a/src/main/java/aero/t2s/modes/CprPosition.java
+++ b/src/main/java/aero/t2s/modes/CprPosition.java
@@ -1,9 +1,28 @@
package aero.t2s.modes;
+import java.time.Instant;
+
public class CprPosition {
private double lat;
private double lon;
- private int time;
+ private boolean valid;
+ private long time;
+
+ public CprPosition() {
+ this.lat = 0.0;
+ this.lon = 0.0;
+ this.valid = false;
+ }
+ public CprPosition(double lat, double lon) {
+ setLatLon(lat ,lon);
+ }
+
+ public void setLatLon(double lat, double lon) {
+ this.lat = lat;
+ this.lon = lon;
+ this.time = Instant.now().toEpochMilli();
+ this.valid = true;
+ }
public void setLat(double lat) {
this.lat = lat;
@@ -21,15 +40,19 @@ public double getLon() {
return lon;
}
- public void setTime(int time) {
+ public void setTime(long time) {
this.time = time;
}
- public int getTime() {
+ public long getTime() {
return time;
}
public boolean isValid() {
- return lat != 0d && lon != 0;
+ return valid;
+ }
+
+ public boolean isExpired() {
+ return time < Instant.now().minusSeconds(10).toEpochMilli();
}
}
diff --git a/src/main/java/aero/t2s/modes/ModeSHandler.java b/src/main/java/aero/t2s/modes/ModeSHandler.java
index fc91b4f..dafdb65 100644
--- a/src/main/java/aero/t2s/modes/ModeSHandler.java
+++ b/src/main/java/aero/t2s/modes/ModeSHandler.java
@@ -1,6 +1,7 @@
package aero.t2s.modes;
import aero.t2s.modes.decoder.df.DownlinkFormat;
+import aero.t2s.modes.decoder.df.df17.AirbornePosition;
import java.util.function.Consumer;
@@ -35,9 +36,6 @@ protected short[] toData(final String input) throws EmptyMessageException, ModeA
// example mode A/C: *21D2; *0200; *0101;
throw new ModeAcMessageException();
}
- if (input.startsWith("*0000")) {
- throw new EmptyMessageException();
- }
String hex = input.replace("*", "").replace(";", "");
@@ -45,10 +43,10 @@ protected short[] toData(final String input) throws EmptyMessageException, ModeA
}
public void start() {
-
+ AirbornePosition.start();
}
public void stop() {
-
+ AirbornePosition.stop();
}
}
diff --git a/src/main/java/aero/t2s/modes/RadiusLimit.java b/src/main/java/aero/t2s/modes/RadiusLimit.java
deleted file mode 100644
index 5fb3de7..0000000
--- a/src/main/java/aero/t2s/modes/RadiusLimit.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package aero.t2s.modes;
-
-public class RadiusLimit {
- private final Track track;
-
- private double radiusLimitMetres;
- private boolean isUnknown;
-
- public RadiusLimit(Track track) {
- this.track = track;
- }
-
- public void determine() {
- radiusLimitMetres = 0;
- isUnknown = false;
-
- // Surface Position
- if (track.isGroundBit()) {
- determineSurface();
- return;
- }
-
- determineAirborne();
- }
-
- private void determineAirborne() {
- if (track.getNIC() == 11 && track.getNICa() == 0 && track.getNICb() == 0) {
- radiusLimitMetres = 7.5;
- } else if (track.getNIC() == 10 && track.getNICa() == 0 && track.getNICb() == 0) {
- radiusLimitMetres = 25;
- } else if (track.getNIC() == 9 && track.getNICa() == 0 && track.getNICb() == 0) {
- radiusLimitMetres = 42;
- } else if (track.getNIC() == 8 && track.getNICa() == 1 && track.getNICb() == 1) {
- radiusLimitMetres = 185.2;
- } else if (track.getNIC() == 7 && track.getNICa() == 0 && track.getNICb() == 0) {
- radiusLimitMetres = 370.4;
- } else if (track.getNIC() == 6 && track.getNICa() == 0 && track.getNICb() == 1) {
- radiusLimitMetres = 555.6;
- } else if (track.getNIC() == 6 && track.getNICa() == 0 && track.getNICb() == 0) {
- radiusLimitMetres = 926;
- } else if (track.getNIC() == 6 && track.getNICa() == 1 && track.getNICb() == 1) {
- radiusLimitMetres = 1111.2;
- } else if (track.getNIC() == 5 && track.getNICa() == 0 && track.getNICb() == 0) {
- radiusLimitMetres = 1852;
- } else if (track.getNIC() == 4 && track.getNICa() == 0 && track.getNICb() == 0) {
- radiusLimitMetres = 3704;
- } else if (track.getNIC() == 3 && track.getNICa() == 1 && track.getNICb() == 1) {
- radiusLimitMetres = 7408;
- } else if (track.getNIC() == 2 && track.getNICa() == 0 && track.getNICb() == 0) {
- radiusLimitMetres = 14816;
- } else if (track.getNIC() == 1 && track.getNICa() == 0 && track.getNICb() == 0) {
- radiusLimitMetres = 37040;
- } else if (track.getNIC() == 0 && track.getNICa() == 0 && track.getNICb() == 0) {
- isUnknown = true;
- } else {
- isUnknown = true;
- }
- }
-
- private void determineSurface() {
- if (track.getNIC() == 11 && track.getNICa() == 0 && track.getNICc() == 0) {
- radiusLimitMetres = 7.5;
- } else if (track.getNIC() == 10 && track.getNICa() == 0 && track.getNICc() == 0) {
- radiusLimitMetres = 25;
- } else if (track.getNIC() == 9 && track.getNICa() == 1 && track.getNICc() == 0) {
- radiusLimitMetres = 75;
- } else if (track.getNIC() == 8 && track.getNICa() == 0 && track.getNICc() == 0) {
- radiusLimitMetres = 185.2;
- } else if (track.getNIC() == 7 && track.getNICa() == 1 && track.getNICc() == 1) {
- radiusLimitMetres = 370.4;
- } else if (track.getNIC() == 6 && track.getNICa() == 1 && track.getNICc() == 0) {
- radiusLimitMetres = 555.6;
- } else if (track.getNIC() == 6 && track.getNICa() == 0 && track.getNICc() == 1) {
- radiusLimitMetres = 1111.2;
- } else if (track.getNIC() == 0 && track.getNICa() == 0 && track.getNICc() == 0) {
- isUnknown = true;
- } else {
- isUnknown = true;
- }
- }
-
- public boolean isUnknown() {
- return isUnknown;
- }
-
- public double getRadiusLimitMetres() {
- return radiusLimitMetres;
- }
-}
diff --git a/src/main/java/aero/t2s/modes/Track.java b/src/main/java/aero/t2s/modes/Track.java
index 51bed0f..f76da27 100644
--- a/src/main/java/aero/t2s/modes/Track.java
+++ b/src/main/java/aero/t2s/modes/Track.java
@@ -1,51 +1,49 @@
package aero.t2s.modes;
import aero.t2s.modes.constants.*;
+import aero.t2s.modes.registers.*;
import java.time.Instant;
public class Track {
private String icao;
private String callsign;
- private int category;
- private boolean groundBit;
- private int baroAltitude;
- private int gnssHeight;
+ private Altitude altitude = new Altitude();
private double lat;
private double lon;
- private CprPosition cprPositionEven = new CprPosition();
- private CprPosition cprPositionOdd = new CprPosition();
+ private boolean positionAvailable = false;
+ private int vx;
+ private int vy;
+ private double gs;
+ private Version version = Version.VERSION0;
+ private boolean groundBit;
Instant updated = Instant.now();
- private boolean singleAntenna;
- private int NIC;
- private int NICb;
- private int NICa;
- private int NICc;
- private RadiusLimit rc = new RadiusLimit(this);
- private int NACv;
- private NavigationAccuracyCategoryPosition NACp = NavigationAccuracyCategoryPosition.UNKNOWN;
+ private boolean wasJustCreated = true;
+
+ private Register05 register05 = new Register05V0();
+ private Register06 register06 = new Register06();
+ private Register07 register07 = new Register07();
+ private Register08 register08 = new Register08();
+ private Register09 register09 = new Register09();
+ private Register17 register17 = new Register17();
+ private Register20 register20 = new Register20();
+ private Register21 register21 = new Register21();
+
private boolean spi;
private boolean tempAlert;
private boolean emergency;
- private Version version = Version.VERSION0;
private Acas acas = new Acas();
private FlightStatus flightStatus = new FlightStatus();
- private Altitude altitude = new Altitude();
private SelectedAltitudeSource selectedAltitudeSource = SelectedAltitudeSource.UNKNOWN;
private Meteo meteo = new Meteo();
- private CapabilityReport capabilityReport = new CapabilityReport();
private int modeA;
private int geometricHeightOffset;
private int rocd;
private boolean rocdAvailable;
private boolean rocdSourceBaro;
- private int vx;
- private int vy;
- private double gs;
- private boolean headingSourceMagnetic;
+
private double magneticHeading;
private double trueHeading;
- private boolean iasAvailable;
private int ias;
private double tas;
private boolean selectedAltitudeManagedFms;
@@ -59,7 +57,6 @@ public class Track {
private boolean altitudeHold;
private boolean approachMode;
private boolean lnav;
- private LengthWidthCode lengthWidthCode = LengthWidthCode.CAT15;
private EmergencyState emergencyState = EmergencyState.NONE;
private int fmsSelectedAltitude;
private double rollAngle;
@@ -71,8 +68,6 @@ public class Track {
private String registration;
private String operator;
- private boolean wasJustCreated = true;
-
public Track(String icao) {
this.icao = icao;
}
@@ -85,143 +80,128 @@ public String getCallsign() {
return callsign;
}
- public void setCategory(int category) {
- this.category = category;
+ public Register05 register05() {
+ return register05;
}
- public String getIcao() {
- return icao;
+ public void register05(Register05 register05) {
+ this.register05 = register05;
}
- public boolean isExpired() {
- return Instant.now().minusSeconds(15).isAfter(updated);
+ public Register06 register06() {
+ return register06;
}
- public Instant getUpdatedAt() {
- return updated;
+ public void register06(Register06 register06) {
+ this.register06 = register06;
}
- public void setUpdatedAt(Instant time) {
- this.updated = time;
+ public Register07 register07() {
+ return register07;
}
- public void touch() {
- updated = Instant.now();
+ public void register07(Register07 register07) {
+ this.register07 = register07;
}
- public void setGroundBit(boolean groundBit) {
- this.groundBit = groundBit;
+ public Register08 register08() {
+ return register08;
}
- public boolean isGroundBit() {
- return groundBit;
+ public void register08(Register08 register08) {
+ this.register08 = register08;
}
- public void setBaroAltitude(int baroAltitude) {
- this.baroAltitude = baroAltitude;
+ public Register09 register09() {
+ return register09;
}
- public int getBaroAltitude() {
- return baroAltitude;
+ public void register09(Register09 register09) {
+ this.register09 = register09;
}
- public int getGnssHeight() {
- return gnssHeight;
+ public Register17 register17() {
+ return register17;
}
- public Track setGnssHeight(int gnssHeight) {
- this.gnssHeight = gnssHeight;
- return this;
+ public void register17(Register17 register17) {
+ this.register17 = register17;
}
- public CprPosition getCprPosition(boolean cprEven) {
- return cprEven ? cprPositionEven : cprPositionOdd;
+ public Register20 register20() {
+ return register20;
}
- public void setLat(double lat) {
- this.lat = lat;
+ public void register20(Register20 register20) {
+ this.register20 = register20;
}
- public double getLat() {
- return lat;
+ public Register21 register21() {
+ return register21;
}
- public void setLon(double lon) {
- this.lon = lon;
- }
-
- public double getLon() {
- return lon;
+ public void register21(Register21 register21) {
+ this.register21 = register21;
}
- public void setSingleAntenna(boolean singleAntenna) {
- this.singleAntenna = singleAntenna;
+ public String getIcao() {
+ return icao;
}
- public boolean getSingleAntenna() {
- return singleAntenna;
+ public boolean isExpired() {
+ return Instant.now().minusSeconds(15).isAfter(updated);
}
- public Version getVersion() {
- return version;
+ public Instant getUpdatedAt() {
+ return updated;
}
- public void setVersion(Version version) {
- this.version = version;
+ public Track setUpdatedAt(Instant updated) {
+ this.updated = updated;
+ return this;
}
- public int getNIC() {
- return NIC;
+ public void touch() {
+ updated = Instant.now();
}
- public int getNICa() {
- return NICa;
+ public void setGroundBit(boolean groundBit) {
+ this.groundBit = groundBit;
}
- public int getNICb() {
- return NICb;
+ public boolean isGroundBit() {
+ return groundBit;
}
- public int getNICc() {
- return NICc;
+ public void setLatLon(double lat, double lon) {
+ this.lat = lat;
+ this.lon = lon;
+ this.positionAvailable = true;
}
-
- public void setNIC(int NIC) {
- if (this.NIC != NIC) {
- this.NIC = NIC;
- rc.determine();
- }
+ public void setLat(double lat) {
+ //TODO How do we know if position really is available if we only set the lat? Can we remove this method?
+ this.lat = lat;
}
-
- public void setNICa(int NICa) {
- if (this.NICa != NICa) {
- this.NICa = NICa;
- rc.determine();
- }
+ public double getLat() {
+ return lat;
}
- public void setNICb(int niCb) {
- if (niCb != this.NICb) {
- this.NICb = niCb;
- rc.determine();
- }
+ public void setLon(double lon) {
+ //TODO How do we know if position really is available if we only set the lon? Can we remove this method?
+ this.lon = lon;
}
- public void setNICc(int NICc) {
- if (this.NICc != NICc) {
- this.NICc = NICc;
- rc.determine();
- }
+ public double getLon() {
+ return lon;
}
- public NavigationAccuracyCategoryPosition getNACp() {
- return NACp;
+ public Version getVersion() {
+ return version;
}
- public Track setNACp(NavigationAccuracyCategoryPosition NACp) {
- this.NACp = NACp;
- return this;
+ public void setVersion(Version version) {
+ this.version = version;
}
public void setSpi(boolean spi) {
@@ -273,15 +253,7 @@ public int getModeA() {
}
public boolean isPositionAvailable() {
- return lat != 0 & lon != 0;
- }
-
- public void setNACv(int naCv) {
- this.NACv = naCv;
- }
-
- public int getNACv() {
- return NACv;
+ return positionAvailable;
}
public void setGeometricHeightOffset(int geometricHeightOffset) {
@@ -340,14 +312,6 @@ public double getGs() {
return gs;
}
- public boolean isMagneticHeading() {
- return headingSourceMagnetic;
- }
-
- public void setHeadingSource(boolean magneticHeading) {
- this.headingSourceMagnetic = magneticHeading;
- }
-
public void setMagneticHeading(double magneticHeading) {
this.magneticHeading = magneticHeading;
}
@@ -364,14 +328,6 @@ public double getTrueHeading() {
return trueHeading;
}
- public void setIasAvailable(boolean iasAvailable) {
- this.iasAvailable = iasAvailable;
- }
-
- public boolean isIasAvailable() {
- return iasAvailable;
- }
-
public void setIas(int ias) {
this.ias = ias;
}
@@ -476,14 +432,6 @@ public boolean getLnav() {
return lnav;
}
- public void setLengthWidthCode(LengthWidthCode lengthWidthCode) {
- this.lengthWidthCode = lengthWidthCode;
- }
-
- public LengthWidthCode getLengthWidthCode() {
- return lengthWidthCode;
- }
-
public void setEmergencyState(EmergencyState emergencyState) {
this.emergencyState = emergencyState;
}
@@ -586,10 +534,6 @@ public Meteo getMeteo() {
return meteo;
}
- public CapabilityReport getCapabilityReport() {
- return capabilityReport;
- }
-
@Override
public String toString() {
return String.format(
diff --git a/src/main/java/aero/t2s/modes/constants/AcasReplyInformation.java b/src/main/java/aero/t2s/modes/constants/AcasReplyInformation.java
index b903797..4dffee3 100644
--- a/src/main/java/aero/t2s/modes/constants/AcasReplyInformation.java
+++ b/src/main/java/aero/t2s/modes/constants/AcasReplyInformation.java
@@ -12,11 +12,11 @@ public enum AcasReplyInformation {
/**
* 2 - reserved for ACAS
*/
- RESERVED2,
+ ACAS_RA_INHIBIT,
/**
* 3 - reserved for ACAS
*/
- RESERVED3,
+ ACAS_RA_VERTICAL_ONLY,
/**
* 4 - reserved for ACAS
*/
@@ -32,7 +32,7 @@ public enum AcasReplyInformation {
/**
* 7 - reserved for ACAS
*/
- RESERVED7,
+ ACAS_RA_FULL,
/**
* 8 - no maximum airspeed data available
*/
diff --git a/src/main/java/aero/t2s/modes/constants/AircraftCategory.java b/src/main/java/aero/t2s/modes/constants/AircraftCategory.java
new file mode 100644
index 0000000..663a8a1
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/constants/AircraftCategory.java
@@ -0,0 +1,33 @@
+package aero.t2s.modes.constants;
+
+public enum AircraftCategory {
+ // Category A
+ NO_ADS_B_EMITTER,
+ LIGHT,
+ SMALL,
+ LARGE,
+ HIGH_VORTEX_LARGE,
+ HEAVY,
+ HIGH_PERFORMANCE,
+ ROTORCRAFT,
+
+ // Category B
+ GLIDER,
+ LIGHTER_THAN_AIR,
+ SKYDIVER,
+ ULTRALIGHT,
+ UNMANNED_AERIAL_VEHICLE,
+ SPACE,
+
+ // Category C
+ SURFACE_VEHICLE_EMERGENCY,
+ SURFACE_VEHICLE_SERVICE,
+ POINT_OBSTACLE,
+ CLUSTER_OBSTACLE,
+ LINE_OBSTACLE,
+
+ //
+ RESERVED,
+ UNKNOWN,
+ ;
+}
diff --git a/src/main/java/aero/t2s/modes/constants/AltitudeSource.java b/src/main/java/aero/t2s/modes/constants/AltitudeSource.java
new file mode 100644
index 0000000..ab5ec54
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/constants/AltitudeSource.java
@@ -0,0 +1,8 @@
+package aero.t2s.modes.constants;
+
+public enum AltitudeSource {
+ UNAVAILABLE,
+ BARO,
+ GNSS_HAE,
+ BARO_GNSS_DIFF,
+}
diff --git a/src/main/java/aero/t2s/modes/constants/Angle.java b/src/main/java/aero/t2s/modes/constants/Angle.java
index df9b473..fbda00b 100644
--- a/src/main/java/aero/t2s/modes/constants/Angle.java
+++ b/src/main/java/aero/t2s/modes/constants/Angle.java
@@ -8,5 +8,6 @@ public enum Angle {
TRACK,
MAGNETIC_TRACK,
TRUE_TRACK,
- ;
+
+ UNAVAILABLE;
}
diff --git a/src/main/java/aero/t2s/modes/constants/HorizontalProtectionLimit.java b/src/main/java/aero/t2s/modes/constants/HorizontalProtectionLimit.java
new file mode 100644
index 0000000..eb515bb
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/constants/HorizontalProtectionLimit.java
@@ -0,0 +1,25 @@
+package aero.t2s.modes.constants;
+
+public enum HorizontalProtectionLimit {
+ RC_7_5(7.5),
+ RC_25(25),
+ RC_75(75),
+ RC_185(185.2),
+ RC_370(370.4),
+ RC_555(555.6),
+ RC_926(926),
+ RC_1111(1111.2),
+ RC_1852(1852),
+ RC_3704(3704),
+ RC_7408(7408),
+ RC_14816(14816),
+ RC_37040(37040),
+ RC_UNKNOWN(-1),
+ ;
+
+ private final double minAccuracyInMetres;
+
+ HorizontalProtectionLimit(double minAccuracyInMetres) {
+ this.minAccuracyInMetres = minAccuracyInMetres;
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/constants/NavigationAccuracyCategoryVelocity.java b/src/main/java/aero/t2s/modes/constants/NavigationAccuracyCategoryVelocity.java
new file mode 100644
index 0000000..37c7f82
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/constants/NavigationAccuracyCategoryVelocity.java
@@ -0,0 +1,10 @@
+package aero.t2s.modes.constants;
+
+public enum NavigationAccuracyCategoryVelocity {
+ UNKNOWN,
+ LESS_THAN_10_M_S,
+ LESS_THAN_3_M_S,
+ LESS_THAN_1_M_S,
+ LESS_THAN_0_3_M_S,
+ ;
+}
diff --git a/src/main/java/aero/t2s/modes/constants/RocdSource.java b/src/main/java/aero/t2s/modes/constants/RocdSource.java
index 4493da9..b925837 100644
--- a/src/main/java/aero/t2s/modes/constants/RocdSource.java
+++ b/src/main/java/aero/t2s/modes/constants/RocdSource.java
@@ -3,5 +3,6 @@
public enum RocdSource {
BARO,
GNSS,
- ;
+ DIFF_BARO_GNSS,
+ UNKNOWN;
}
diff --git a/src/main/java/aero/t2s/modes/constants/Speed.java b/src/main/java/aero/t2s/modes/constants/Speed.java
index 4286fe6..0730c74 100644
--- a/src/main/java/aero/t2s/modes/constants/Speed.java
+++ b/src/main/java/aero/t2s/modes/constants/Speed.java
@@ -7,5 +7,5 @@ public enum Speed {
IAS,
TAS,
GS,
- ;
+ UNKNOWN;
}
diff --git a/src/main/java/aero/t2s/modes/constants/TransmissionRate.java b/src/main/java/aero/t2s/modes/constants/TransmissionRate.java
new file mode 100644
index 0000000..2dfb611
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/constants/TransmissionRate.java
@@ -0,0 +1,9 @@
+package aero.t2s.modes.constants;
+
+public enum TransmissionRate {
+ UNKNOWN,
+ HIGH_SURFACE_RATE,
+ LOW_SURFACE_RATE,
+ RESERVED
+ ;
+}
diff --git a/src/main/java/aero/t2s/modes/decoder/AltitudeEncoding.java b/src/main/java/aero/t2s/modes/decoder/AltitudeEncoding.java
index f07b99c..0ffdb7f 100644
--- a/src/main/java/aero/t2s/modes/decoder/AltitudeEncoding.java
+++ b/src/main/java/aero/t2s/modes/decoder/AltitudeEncoding.java
@@ -23,7 +23,8 @@ public static Altitude decode(int encoded) {
}
private static Altitude decodeFeet(int encoded) {
- int n = ((encoded & 0b1111110000000) >>> 2) | (encoded & 0b11111);
+ // Remove bits 7 & 8 and stitch the binary message together.
+ int n = ((encoded & 0b1111110000000) >>> 2) + ((encoded & 0b100000) >>> 1) + (encoded & 0b1111);
int altitude = (25 * n) - 1000;
diff --git a/src/main/java/aero/t2s/modes/decoder/Decoder.java b/src/main/java/aero/t2s/modes/decoder/Decoder.java
index 5b563f6..b485453 100644
--- a/src/main/java/aero/t2s/modes/decoder/Decoder.java
+++ b/src/main/java/aero/t2s/modes/decoder/Decoder.java
@@ -50,7 +50,7 @@ public DownlinkFormat decode(short[] data) throws UnknownDownlinkFormatException
df = new DF16(data);
break;
case 17:
- df = new DF17(data, originLat, originLon);
+ df = new DF17(data);
break;
case 18:
df = new DF18(data);
@@ -71,7 +71,7 @@ public DownlinkFormat decode(short[] data) throws UnknownDownlinkFormatException
throw new UnknownDownlinkFormatException(downlinkFormat, data);
}
- return df.decode();
+ return df.decode().aircraft(modeSDatabase.find(df.getIcao()));
}
public Track getTrack(String icao) {
diff --git a/src/main/java/aero/t2s/modes/decoder/df/DF16.java b/src/main/java/aero/t2s/modes/decoder/df/DF16.java
index f5989c2..fd6f0b6 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/DF16.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/DF16.java
@@ -9,7 +9,6 @@
public class DF16 extends DownlinkFormat {
private VerticalStatus verticalStatus;
- private CrossLinkCapability crossLinkCapability;
private AcasSensitivity sensitivity;
private AcasReplyInformation replyInformation;
private Altitude altitude;
@@ -28,7 +27,6 @@ public DF16(short[] data) {
@Override
public DF16 decode() {
verticalStatus = VerticalStatus.from((data[0] >>> 2) & 0x1);
- crossLinkCapability = CrossLinkCapability.from((data[0] >>> 1) & 0x1);
sensitivity = AcasSensitivity.from(data[1] >>> 5);
replyInformation = AcasReplyInformation.from(((data[1] & 0x7) << 1) | ((data[2] >> 7) & 0x1));
altitude = AltitudeEncoding.decode((((data[2] << 8) | data[3])) & 0x1FFF);
@@ -67,7 +65,6 @@ public DF16 decode() {
public void apply(Track track) {
Acas acas = track.getAcas();
acas.setVerticalStatus(verticalStatus);
- acas.setCrossLinkCapability(crossLinkCapability);
acas.setSensitivity(sensitivity);
acas.setReplyInformation(replyInformation);
acas.setAltitude(altitude);
@@ -83,10 +80,6 @@ public VerticalStatus getVerticalStatus() {
return verticalStatus;
}
- public CrossLinkCapability getCrossLinkCapability() {
- return crossLinkCapability;
- }
-
public AcasSensitivity getSensitivity() {
return sensitivity;
}
diff --git a/src/main/java/aero/t2s/modes/decoder/df/DF17.java b/src/main/java/aero/t2s/modes/decoder/df/DF17.java
index 622caf4..9860eaa 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/DF17.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/DF17.java
@@ -4,15 +4,10 @@
import aero.t2s.modes.decoder.df.df17.*;
public class DF17 extends DownlinkFormat {
- private final double originLat;
- private final double originLon;
-
private ExtendedSquitter extendedSquitter;
- public DF17(short[] data, double originLat, double originLon) {
+ public DF17(short[] data) {
super(data, IcaoAddress.FROM_MESSAGE);
- this.originLat = originLat;
- this.originLon = originLon;
}
@Override
@@ -34,7 +29,7 @@ public DF17 decode() {
case 20:
case 21:
case 22:
- extendedSquitter = new AirbornePosition(data, originLat, originLon);
+ extendedSquitter = new AirbornePosition(data, getIcao());
break;
case 1:
case 2:
diff --git a/src/main/java/aero/t2s/modes/decoder/df/DF24.java b/src/main/java/aero/t2s/modes/decoder/df/DF24.java
index 8503e6e..b7f8085 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/DF24.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/DF24.java
@@ -1,20 +1,27 @@
package aero.t2s.modes.decoder.df;
-import aero.t2s.modes.NotImplementedException;
import aero.t2s.modes.Track;
public class DF24 extends DownlinkFormat {
+ private int sequenceNo = 0;
+
public DF24(short[] data) {
super(data, IcaoAddress.FROM_PARITY);
}
@Override
public DF24 decode() {
- throw new NotImplementedException(getClass().getSimpleName() + ": Not implemented");
+ sequenceNo = data[0] & 0b00000111;
+
+ return this;
}
@Override
public void apply(Track track) {
- //
+ // Not implemented
+ }
+
+ public int getSequenceNo() {
+ return sequenceNo;
}
}
diff --git a/src/main/java/aero/t2s/modes/decoder/df/DownlinkFormat.java b/src/main/java/aero/t2s/modes/decoder/df/DownlinkFormat.java
index 0e9bf08..6b567dd 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/DownlinkFormat.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/DownlinkFormat.java
@@ -1,6 +1,7 @@
package aero.t2s.modes.decoder.df;
import aero.t2s.modes.Track;
+import aero.t2s.modes.database.ModeSDatabase;
import aero.t2s.modes.decoder.Common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,6 +13,7 @@ public abstract class DownlinkFormat {
protected final short[] data;
private final String icao;
+ private ModeSDatabase.ModeSAircraft aircraft;
public DownlinkFormat(short[] data, IcaoAddress icaoAddressFrom) {
this.data = data;
@@ -35,6 +37,16 @@ public short[] getData() {
return data;
}
+ public DownlinkFormat aircraft(ModeSDatabase.ModeSAircraft aircraft) {
+ this.aircraft = aircraft == null ? new ModeSDatabase.ModeSAircraft(this.getIcao(), null, null, null) : aircraft;
+
+ return this;
+ }
+
+ public ModeSDatabase.ModeSAircraft getAircraft() {
+ return this.aircraft;
+ }
+
protected enum IcaoAddress {
FROM_MESSAGE,
FROM_PARITY,
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds.java b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds.java
index 4740bca..c83856c 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds.java
@@ -23,4 +23,8 @@ protected void invalidate() {
public boolean isValid() {
return valid;
}
+
+ public short[] getData() {
+ return data;
+ }
}
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds17.java b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds17.java
index d677e1c..767618c 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds17.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds17.java
@@ -73,7 +73,7 @@ public Bds17(short[] data) {
@Override
public void apply(Track track) {
- track.getCapabilityReport().update(this);
+ track.register17().update(this);
}
@Override
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds20.java b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds20.java
index c1f6205..ded30ce 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds20.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds20.java
@@ -29,6 +29,7 @@ public Bds20(short[] data) {
@Override
public void apply(Track track) {
track.setCallsign(acid);
+ track.register20().setAcid(acid);
}
@Override
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds21.java b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds21.java
index 455178a..ec805fc 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds21.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds21.java
@@ -69,6 +69,10 @@ public void apply(Track track) {
if (statusAirlineRegistration) {
track.setOperator(airline);
}
+
+ if (statusAircraftRegistration || statusAirlineRegistration) {
+ track.register21().update(registration, airline);
+ }
}
@Override
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds40.java b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds40.java
index 6767d71..2686301 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds40.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds40.java
@@ -27,10 +27,10 @@
* MCP/FCU Selected altitude
*
*
- * This information which represents the real “aircraft intent,” when available,
+ * This information which represents the real "aircraft intent," when available,
* represented by the altitude control panel selected altitude,
* the flight management system selected altitude,
- * or the current aircraft altitude according to the aircraft’s mode of flight
+ * or the current aircraft altitude according to the aircraft's mode of flight
* (the intent may not be available at all when the pilot is flying the aircraft).
*
*
@@ -39,7 +39,7 @@
* See {@link SelectedAltitudeSource} for more details
*
*
- * Note: LSB (1 bit) = 16feet with a range 0 - 65520 feet, this class considers altitudes above 50000ft as invalid / error.
+ * Note: LSB (1 bit) = 16feet with a range 0 - 65520 feet, this class considers altitudes above 52000ft as invalid / error.
*
* FMS Selected altitude
*
@@ -54,13 +54,13 @@
* The FMS selected altitude field is transmitting 32000ft, the Target Altitude Source flag is set to MCP.
*
*
- * Note: LSB (1 bit) = 16feet with a range 0 - 65520 feet, this class considers altitudes above 50000ft as invalid / error.
+ * Note: LSB (1 bit) = 16feet with a range 0 - 65520 feet, this class considers altitudes above 52000ft as invalid / error.
*
* Barometric Pressure Setting
*
- * When status flag (bit 27) is set to false indicates the information is valid and van be used
+ * When status flag (bit 27) is set to 1 indicates the information is valid and van be used
*
- * Note: LSB (1 bit) = 0.1mb with a range 0 - 410mb. You need to add 800mb to receive the real baro steting
+ * Note: LSB (1 bit) = 0.1mb with a range 0 - 410mb. You need to add 800mb to receive the real baro setting
*
* MCP/FCU Mode bits
*
@@ -113,7 +113,7 @@ public Bds40(short[] data) {
selectedAltitude = (((data[4] & 0b01111111) << 5) | (data[5] & 0b11111000) >>> 3) * 16;
if (statusMcp) {
- if (selectedAltitude > 50000) {
+ if (selectedAltitude > 52000) {
invalidate();
return;
}
@@ -126,7 +126,7 @@ public Bds40(short[] data) {
fmsAltitude = (((data[5] & 0x3) << 10) | (data[6] << 2) | ((data[7] >>> 6) & 0x3)) * 16;
if (statusFms) {
- if (fmsAltitude <= 0 || fmsAltitude > 50000) {
+ if (fmsAltitude < 0 || fmsAltitude > 52000) {
invalidate();
return;
}
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds44.java b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds44.java
index 26c6680..f202da7 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds44.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds44.java
@@ -33,6 +33,14 @@ public Bds44(short[] data) {
}
statusWindSpeed = (data[4] & 0b00001000) != 0;
+ statusAverageStaticPressure = (data[8] & 0b00100000) != 0;
+ statusTurbulence = (data[9] & 0b00000010) != 0;
+
+ if (!statusWindSpeed) {
+ invalidate();
+ return;
+ }
+
windSpeed = (data[4] & 0b00000111) << 6 | data[5] >> 2;
windDirection = ((data[5] & 0b00000011) << 7 | data[6] >> 1) * WIND_DIRECTION_ACCURACY;
if (!statusWindSpeed && windSpeed != 0) {
@@ -52,14 +60,12 @@ public Bds44(short[] data) {
return;
}
- statusAverageStaticPressure = (data[8] & 0b00100000) != 0;
averageStaticPressure = ((data[8] & 0b00011111) << 6) | data[9] >> 2;
if (!statusAverageStaticPressure && averageStaticPressure != 0) {
invalidate();
return;
}
- statusTurbulence = (data[9] & 0b00000010) != 0;
turbulence = Hazard.find(((data[9] & 0b00000001) << 1) | data[10] >>> 7);
if (!statusTurbulence && turbulence != Hazard.NIL) {
invalidate();
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds45.java b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds45.java
index 60d956d..7b6f8b4 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds45.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds45.java
@@ -1,8 +1,10 @@
package aero.t2s.modes.decoder.df.bds;
+import aero.t2s.modes.Altitude;
import aero.t2s.modes.Meteo;
import aero.t2s.modes.Track;
import aero.t2s.modes.constants.Hazard;
+import aero.t2s.modes.decoder.AltitudeEncoding;
public class Bds45 extends Bds {
private static final double SAT_ACCURACY = 0.25d;
@@ -105,6 +107,53 @@ public Bds45(short[] data) {
invalidate();
return;
}
+
+ // Windshear + turbulence + microburst => Very unlikely to be a BDS 45 valid message let's flag it as invalid
+ if (statusTurbulence && statusWindShear && statusMicroBurst) {
+ invalidate();
+ return;
+ }
+
+ if (statusMicroBurst) {
+ // If message is DF20 (altitude encoding) and altitude is above 10000ft microburst is unlikely flag as invalid
+ if (data[0] >>> 3 == 20) {
+ Altitude altitude = AltitudeEncoding.decode((data[2] & 0x1F) << 8 | data[3]);
+ if (altitude.getAltitude() > 10_000) {
+ invalidate();
+ return;
+ }
+ }
+ // DF 21 message since we do not have altitude info and message is likely to be BDS17 flag BDS45 on DF21 with microburst as invalid
+ else {
+ invalidate();
+ return;
+ }
+ }
+
+ if (statusTurbulence || statusWindShear || statusMicroBurst || statusIcing || statusWake) {
+ boolean nothing = true;
+
+ if (statusTurbulence && turbulence != Hazard.NIL) {
+ nothing = false;
+ }
+ if (statusWindShear && windShear != Hazard.NIL) {
+ nothing = false;
+ }
+ if (statusMicroBurst && microBurst != Hazard.NIL) {
+ nothing = false;
+ }
+ if (statusIcing && icing != Hazard.NIL) {
+ nothing = false;
+ }
+ if (statusWake && wake != Hazard.NIL) {
+ nothing = false;
+ }
+
+ if (nothing) {
+ invalidate();
+ return;
+ }
+ }
}
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds50.java b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds50.java
index cf23e53..3098ae8 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds50.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds50.java
@@ -1,6 +1,8 @@
package aero.t2s.modes.decoder.df.bds;
+import aero.t2s.modes.Altitude;
import aero.t2s.modes.Track;
+import aero.t2s.modes.decoder.AltitudeEncoding;
/**
* 56-bit MB Field is structured in the following format
@@ -201,6 +203,48 @@ public Bds50(short[] data) {
invalidate();
return;
}
+
+ // Check if values are way off th scale.
+ // We can only check large values
+ if ((statusTas || statusGs) && statusTrackAngle && statusRollAngle && Math.abs(trackAngleRate) > 0.25 && Math.abs(rollAngle) > 5) {
+ // We cannot have a rate one turn at 180 knots or greater at less than 30 degrees of bank
+ if ((gs > 180 || tas > 180) && rollAngle < 30 && trackAngleRate > 3) {
+ invalidate();
+ return;
+ }
+ }
+ }
+
+ public Bds compareWithBds60(Bds60 bds60) {
+ // average speed of sound between 0 - 45000ft
+ double speedOfSound = 617d;
+
+ short[] data = getData();
+ if (data[0] >>> 3 == 20) {
+ Altitude altitude = AltitudeEncoding.decode((data[2] & 0x1F) << 8 | data[3]);
+ // Estimated TAS
+ double bds60Tas = bds60.getIas() * (1 + (altitude.getAltitude() / 1000) * 0.02);
+ // Estimated Mach
+ double bds60Mach = bds60Tas / speedOfSound;
+
+ if (Math.abs(bds60Mach - bds60.getMach()) < 0.1) {
+ return bds60;
+ }
+
+ return this;
+ } else {
+ // No altitude information assume check in 2000ft increments
+ for (int altitude = 0; altitude < 45; altitude += 2) {
+ double bds60Tas = bds60.getIas() * (1 + altitude * 0.02);
+ double bds60Mach = bds60Tas / speedOfSound;
+
+ if (Math.abs(bds60Mach - bds60.getMach()) < 0.01) {
+ return bds60;
+ }
+ }
+
+ return this;
+ }
}
@Override
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds60.java b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds60.java
index 55bf427..1d1f697 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/Bds60.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/Bds60.java
@@ -79,7 +79,7 @@ public Bds60(short[] data) {
}
double baroSign = ((data[8] >>> 4) & 0x1) == 1 ? -512.0 : 0.0;
- baroRocd = ((((data[8] & 0b00001111) << 5) | (data[9] >>> 3)) + baroSign) * ROCD_ACCURCY;
+ baroRocd = (((((data[8] & 0b00001111) << 5) | (data[9] >>> 3)) + baroSign) * ROCD_ACCURCY) % 16384;
if (statusBaroRocd) {
if (baroRocd < -8000 || baroRocd > 8000) {
invalidate();
@@ -93,7 +93,7 @@ public Bds60(short[] data) {
}
double irsSign = ((data[9] >> 1) & 0x1) == 1 ? -512.0 : 0.0;
- irsRocd = (((data[9] & 0x1) << 8 | data[10]) + irsSign) * ROCD_ACCURCY;
+ irsRocd = ((((data[9] & 0x1) << 8 | data[10]) + irsSign) * ROCD_ACCURCY) % 16384;
if (statusIrsRocd) {
if (irsRocd < -8000 || irsRocd > 6000) {
invalidate();
@@ -106,6 +106,14 @@ public Bds60(short[] data) {
}
}
+ if ((irsRocd > 0 && baroRocd < 0) || (irsRocd < 0 && baroRocd > 0)) {
+ // Aircraft cannot be climbing and descending at the same time
+ if (Math.abs(irsRocd) > 1000 && Math.abs(baroRocd) > 1000) {
+ invalidate();
+ return;
+ }
+ }
+
if (statusMach && statusIas && data[0] >>> 3 == 20) {
double altitude = AltitudeEncoding.decode((data[2] & 0x1F) << 8 | data[3]).getAltitude();
if (altitude > 0) {
diff --git a/src/main/java/aero/t2s/modes/decoder/df/bds/BdsDecoder.java b/src/main/java/aero/t2s/modes/decoder/df/bds/BdsDecoder.java
index bb7209d..0c57ca5 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/bds/BdsDecoder.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/bds/BdsDecoder.java
@@ -44,6 +44,18 @@ public Bds decode() throws MultipleBdsMatchesFoundException, EmptyMessageExcepti
}
if (valid.size() > 1) {
+ // Is BDS 50 / 60 pair
+ if (valid.size() == 2 && valid.stream().anyMatch(bds -> bds.getClass().equals(Bds50.class)) && valid.stream().anyMatch(bds -> bds.getClass().equals(Bds60.class))) {
+ Bds bds50 = valid.stream().filter(bds -> bds.getClass().equals(Bds50.class)).findFirst().get();
+ Bds bds60 = valid.stream().filter(bds -> bds.getClass().equals(Bds60.class)).findFirst().get();
+
+ Bds likelyBds = ((Bds50)bds50).compareWithBds60((Bds60) bds60);
+
+ if (likelyBds != null) {
+ return likelyBds;
+ }
+ }
+
throw new MultipleBdsMatchesFoundException(valid);
}
diff --git a/src/main/java/aero/t2s/modes/decoder/df/df17/AirbornePosition.java b/src/main/java/aero/t2s/modes/decoder/df/df17/AirbornePosition.java
index 67129e2..ad59e82 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/df17/AirbornePosition.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/df17/AirbornePosition.java
@@ -1,14 +1,17 @@
package aero.t2s.modes.decoder.df.df17;
import aero.t2s.modes.Track;
-import aero.t2s.modes.constants.BarometricAltitudeIntegrityCode;
-import aero.t2s.modes.constants.SurveillanceStatus;
-import aero.t2s.modes.constants.Version;
+import aero.t2s.modes.CprPosition;
+import aero.t2s.modes.constants.*;
+import aero.t2s.modes.decoder.Common;
+import aero.t2s.modes.registers.Register05;
+import aero.t2s.modes.registers.Register05V0;
+import aero.t2s.modes.registers.Register05V2;
-public class AirbornePosition extends ExtendedSquitter {
- private final double originLat;
- private final double originLon;
+import java.util.*;
+public class AirbornePosition extends ExtendedSquitter {
+ private final String address;
private SurveillanceStatus surveillanceStatus;
private int singleAntennaFlag;
@@ -16,13 +19,15 @@ public class AirbornePosition extends ExtendedSquitter {
private int altitude;
private boolean positionAvailable;
+
private double lat;
private double lon;
+ private static Map cache = new HashMap<>();
+ private static Timer cacheCleanup;
- public AirbornePosition(short[] data, final double originLat, final double originLon) {
+ public AirbornePosition(short[] data, String address) {
super(data);
- this.originLat = originLat;
- this.originLon = originLon;
+ this.address = address;
}
@Override
@@ -42,10 +47,8 @@ public AirbornePosition decode() {
return this;
}
- positionAvailable = true;
-
int time = (data[6] >>> 3) & 0x1;
- boolean cprEven = ((data[6] >>> 2) & 0x1) == 0;
+ boolean isCprEven = ((data[6] >>> 2) & 0x1) == 0;
int cprLat = (data[6] & 0x3) << 15;
cprLat = cprLat | (data[7] << 7);
@@ -55,7 +58,38 @@ public AirbornePosition decode() {
cprLon = cprLon | (data[9] << 8);
cprLon = cprLon | data[10];
- calculatePosition(cprEven, ((double)cprLat) / ((double)(1 << 17)), ((double)cprLon) / ((double)(1 << 17)), time);
+
+ if (!cache.containsKey(address)) {
+ if (!isCprEven) {
+ return this;
+ }
+
+ synchronized (cache) {
+ cache.putIfAbsent(address, new PositionUpdate(
+ new CprPosition(cprLat / (double)(1 << 17), cprLon / (double)(1 << 17))
+ ));
+ }
+ }
+
+ PositionUpdate positionUpdate;
+ synchronized (cache) {
+ positionUpdate = cache.get(address);
+ }
+ if (isCprEven) {
+ positionUpdate.setEven(new CprPosition(cprLat / (double) (1 << 17), cprLon / (double) (1 << 17)));
+ } else {
+ positionUpdate.setOdd(new CprPosition(cprLat / (double) (1 << 17), cprLon / (double) (1 << 17)));
+ }
+
+ if (positionUpdate.isComplete()) {
+ calculateGlobal(positionUpdate.even, positionUpdate.odd);
+ } else if (positionUpdate.isPreviousPositionAvailable() && positionUpdate.isPreviousPositionAvailable()) {
+ calculateLocal(positionUpdate.odd, true, positionUpdate.previousLat, positionUpdate.previousLon);
+ }
+
+ if (positionAvailable) {
+ positionUpdate.setPreviousPosition(this.lat, this.lon);
+ }
return this;
}
@@ -66,23 +100,43 @@ public void apply(Track track) {
track.setSpi(surveillanceStatus == SurveillanceStatus.SPI);
track.setTempAlert(surveillanceStatus == SurveillanceStatus.TEMPORARY_ALERT);
track.setEmergency(surveillanceStatus == SurveillanceStatus.PERMANENT_ALERT);
+ if (positionAvailable) {
+ track.setLatLon(lat, lon);
+ }
- // Determine Antenna or Navigation Integrity Category
- if (track.getVersion().ordinal() < Version.VERSION2.ordinal()) {
- track.setSingleAntenna(singleAntennaFlag == 0);
- } else {
- track.setNICb(singleAntennaFlag);
+ if (versionChanged(track)) {
+ switch (track.getVersion()) {
+ case VERSION0:
+ case VERSION1:
+ track.register05(new Register05V0());
+ break;
+ case VERSION2:
+ track.register05(new Register05V2());
+ break;
+ }
}
+ HorizontalProtectionLimit hpl = determineHorizontalProtection(track);
+ AltitudeSource altitudeSource = determineAltitudeSource();
+
+ Register05 position = track.register05();
- if (altitudeSourceBaro) {
- track.setBaroAltitude(altitude);
+ if (position instanceof Register05V2) {
+ position.update(hpl, altitude, altitudeSource, lat, lon, surveillanceStatus);
} else {
- track.setGnssHeight(altitude);
+ ((Register05V0) position).update(hpl, altitude, altitudeSource, lat, lon, surveillanceStatus, singleAntennaFlag == 1);
}
+ }
- track.setNIC(determineNIC(track, typeCode));
- track.setLat(lat);
- track.setLon(lon);
+ private boolean versionChanged(Track track) {
+ if (track.register05() == null) {
+ return true;
+ }
+
+ if (track.getVersion() == Version.VERSION2 && track.register05().getVersion() != Version.VERSION2) {
+ return true;
+ }
+
+ return false;
}
public int getSingleAntennaFlag() {
@@ -99,14 +153,6 @@ public BarometricAltitudeIntegrityCode getNICbaro() {
}
}
- public double getOriginLat() {
- return originLat;
- }
-
- public double getOriginLon() {
- return originLon;
- }
-
public SurveillanceStatus getSurveillanceStatus() {
return surveillanceStatus;
}
@@ -131,129 +177,130 @@ public double getLon() {
return lon;
}
- private int determineNIC(Track track, int typeCode) {
+ private HorizontalProtectionLimit determineHorizontalProtection(Track track) {
switch (typeCode) {
case 9:
case 20:
- return 11;
+ return HorizontalProtectionLimit.RC_7_5;
case 10:
case 21:
- return 10;
+ return HorizontalProtectionLimit.RC_25;
case 11:
- if (track.getNICa() == 1 && track.getNICb() == 1) {
- return 9;
- } else if (track.getNICa() == 0 && track.getNICb() == 0) {
- return 8;
+ if (singleAntennaFlag == 1 && track.getVersion() == Version.VERSION2) {
+ return HorizontalProtectionLimit.RC_75;
} else {
- return 0;
+ return HorizontalProtectionLimit.RC_185;
}
case 12:
- return 7;
+ return HorizontalProtectionLimit.RC_370;
case 13:
- return 6;
+ if (singleAntennaFlag == 1 && track.getVersion() == Version.VERSION2) {
+ return HorizontalProtectionLimit.RC_555;
+ } else {
+ return HorizontalProtectionLimit.RC_926;
+ }
case 14:
- return 5;
+ return HorizontalProtectionLimit.RC_1852;
case 15:
- return 4;
+ return HorizontalProtectionLimit.RC_3704;
case 16:
- if (track.getNICa() == 0 && track.getNICb() == 0) {
- return 2;
- } else if (track.getNICa() == 1 && track.getNICb() == 1) {
- return 3;
+ if (singleAntennaFlag == 1 && track.getVersion() == Version.VERSION2) {
+ return HorizontalProtectionLimit.RC_7408;
} else {
- return 0;
+ return HorizontalProtectionLimit.RC_14816;
}
case 17:
- return 1;
+ return HorizontalProtectionLimit.RC_37040;
case 18:
case 22:
default:
- return 0;
+ return HorizontalProtectionLimit.RC_UNKNOWN;
}
}
- private void calculatePosition(boolean isEven, double lat, double lon, double time) {
-// CprPosition cprEven = track.getCprPosition(true);
-// CprPosition cprOdd = track.getCprPosition(false);
+ private AltitudeSource determineAltitudeSource() {
+ if (typeCode < 19) {
+ return AltitudeSource.BARO;
+ }
-// if (! (cprEven.isValid() && cprOdd.isValid())) {
- calculateLocal(isEven, lat, lon, time);
-// return;
-// }
+ if (typeCode == 19) {
+ return AltitudeSource.BARO_GNSS_DIFF;
+ }
-// calculateGlobal(track, cprEven, cprOdd);
+ return AltitudeSource.GNSS_HAE;
}
- private void calculateLocal(boolean isEven, double lat, double lon, double time) {
- boolean isOdd = !isEven;
-// CprPosition cpr = track.getCprPosition(isEven);
+ private void calculateLocal(CprPosition cpr, boolean isOdd, double previousLat, double previousLon) {
double dlat = isOdd ? 360.0 / 59.0 : 360.0 / 60.0;
- double j = Math.floor(originLat / dlat) + Math.floor((originLat % dlat) / dlat - lat + 0.5);
+ double j = Math.floor(previousLat / dlat) + Math.floor((previousLat % dlat) / dlat - cpr.getLat() + 0.5);
- lat = dlat * (j + lat);
+ double newLat = dlat * (j + previousLat);
- double nl = NL(lat) - (isOdd ? 1.0 : 0.0);
+ double nl = NL(newLat) - (isOdd ? 1.0 : 0.0);
double dlon = nl > 0 ? 360.0 / nl : 360;
- double m = Math.floor(originLon / dlon) + Math.floor((originLon % dlon) / dlon - lon + 0.5);
- lon = dlon * (m + lon);
+ double m = Math.floor(previousLon / dlon) + Math.floor((previousLon % dlon) / dlon - cpr.getLon() + 0.5);
+ double newLon = dlon * (m + lon);
+
+ //TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range
+ //TODO Should be a sanity-check here to see if the calculated movement since the last update is too far
+ this.lat = newLat;
+ this.lon = newLon;
+ }
+
+ private void calculateGlobal(CprPosition cprEven, CprPosition cprOdd) {
+ double dLat0 = 360.0 / 60.0;
+ double dLat1 = 360.0 / 59.0;
+
+ double j = Math.floor(59.0 * cprEven.getLat() - 60.0 * cprOdd.getLat() + 0.5);
+
+ double latEven = dLat0 * (j % 60.0 + cprEven.getLat());
+ double latOdd = dLat1 * (j % 59.0 + cprOdd.getLat());
+
+ if (latEven >= 270.0 && latEven <= 360.0) {
+ latEven -= 360.0;
+ }
+
+ if (latOdd >= 270.0 && latOdd <= 360.0) {
+ latOdd -= 360.0;
+ }
+
+ if (NL(latEven) != NL(latOdd)) {
+ return;
+ }
+
+ double lat;
+ double lon;
+ if (cprEven.getTime() > cprOdd.getTime()) {
+ double ni = cprN(latEven, 0);
+ double m = Math.floor(cprEven.getLon() * (NL(latEven) - 1) - cprOdd.getLon() * NL(latEven) + 0.5);
+ lat = latEven;
+ lon = (360d / ni) * (m % ni + cprEven.getLon());
+ } else {
+ double ni = cprN(latOdd, 1);
+ double m = Math.floor(cprEven.getLon() * (NL(latOdd) - 1) - cprOdd.getLon() * NL(latOdd) + 0.5);
+
+ lat = latOdd;
+ lon = (360d / ni) * (m % ni + cprOdd.getLon());
+ }
+
+ if (lon > 180d) {
+ lon -= 360d;
+ }
+
+ //TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range,
this.lat = lat;
this.lon = lon;
+ this.positionAvailable = true;
}
+ private double cprN(double lat, double isOdd) {
+ double nl = NL(lat) - isOdd;
-// private void calculateGlobal(Track track, CprPosition cprEven, CprPosition cprOdd) {
-// double dLat0 = 360.0 / 60.0;
-// double dLat1 = 360.0 / 59.0;
-//
-// double j = Math.floor(59.0 * cprEven.getLat() - 60.0 * cprOdd.getLat() + 0.5);
-//
-// double latEven = dLat0 * (j % 60.0 + cprEven.getLat());
-// double latOdd = dLat1 * (j % 59.0 + cprOdd.getLat());
-//
-// if (latEven >= 270.0 && latEven <= 360.0) {
-// latEven -= 360.0;
-// }
-//
-// if (latOdd >= 270.0 && latOdd <= 360.0) {
-// latOdd -= 360.0;
-// }
-//
-// if (NL(latEven) != NL(latOdd)) {
-// return;
-// }
-//
-// double lat;
-// double lon;
-// if (cprEven.getTime() > cprOdd.getTime()) {
-// double ni = cprN(latEven, 0);
-// double m = Math.floor(cprEven.getLon() * (NL(latEven) - 1) - cprOdd.getLon() * NL(latEven) + 0.5);
-//
-// lat = latEven;
-// lon = (360d / ni) * (m % ni + cprEven.getLon());
-// } else {
-// double ni = cprN(latOdd, 1);
-// double m = Math.floor(cprEven.getLon() * (NL(latOdd) - 1) - cprOdd.getLon() * NL(latOdd) + 0.5);
-//
-// lat = latOdd;
-// lon = (360d / ni) * (m % ni + cprOdd.getLon());
-// }
-//
-// if (lon > 180d) {
-// lon -= 360d;
-// }
-//
-// track.setLat(lat);
-// track.setLon(lon);
-// }
-
-// private double cprN(double lat, double isOdd) {
-// double nl = NL(lat) - isOdd;
-//
-// return nl > 1 ? nl : 1;
-// }
+ return nl > 1 ? nl : 1;
+ }
private double NL(double lat) {
if (lat == 0) return 59;
@@ -273,4 +320,66 @@ private int calculateAltitude(short[] data, int typeCode) {
return (n * qBit) - 1000;
}
+
+ public static void start() {
+ AirbornePosition.cache.clear();
+ AirbornePosition.cacheCleanup.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ List expired = new LinkedList<>();
+
+ synchronized (cache) {
+ cache.entrySet().stream().filter(entry -> entry.getValue().isExpired()).forEach(entry -> expired.add(entry.getKey()));
+ expired.forEach(cache::remove);
+ }
+ }
+ }, 0, 10_000);
+ }
+
+ public static void stop() {
+ AirbornePosition.cacheCleanup.cancel();
+ AirbornePosition.cacheCleanup = null;
+
+ AirbornePosition.cache.clear();
+ }
+
+ class PositionUpdate {
+ private CprPosition even;
+ private CprPosition odd;
+
+
+ private boolean previousPositionAvailable = false;
+ private double previousLat;
+ private double previousLon;
+
+ public PositionUpdate(CprPosition even) {
+ this.even = even;
+ }
+
+ public void setEven(CprPosition even) {
+ this.even = even;
+ this.odd = null;
+ }
+
+ public void setOdd(CprPosition odd) {
+ this.odd = odd;
+ }
+
+ public void setPreviousPosition(double lat, double lon) {
+ this.previousLat = lat;
+ this.previousLon = lon;
+ }
+
+ public boolean isPreviousPositionAvailable() {
+ return this.previousPositionAvailable;
+ }
+
+ public boolean isComplete() {
+ return even != null && odd != null;
+ }
+
+ public boolean isExpired() {
+ return even.isExpired() || odd.isExpired();
+ }
+ }
}
diff --git a/src/main/java/aero/t2s/modes/decoder/df/df17/AirborneVelocityAirspeedHeading.java b/src/main/java/aero/t2s/modes/decoder/df/df17/AirborneVelocityAirspeedHeading.java
index 4d819cf..7f67d26 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/df17/AirborneVelocityAirspeedHeading.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/df17/AirborneVelocityAirspeedHeading.java
@@ -1,8 +1,9 @@
package aero.t2s.modes.decoder.df.df17;
import aero.t2s.modes.Track;
-import aero.t2s.modes.constants.RocdSource;
-import aero.t2s.modes.constants.Speed;
+import aero.t2s.modes.constants.*;
+import aero.t2s.modes.registers.Register09;
+import aero.t2s.modes.registers.Register09V0;
public class AirborneVelocityAirspeedHeading extends AirborneVelocity {
private static final double HEADING_RESOLUTION = 360.0 / 1024.0;
@@ -35,8 +36,6 @@ public AirborneVelocityAirspeedHeading decode() {
@Override
public void apply(Track track) {
- track.setNACv(NACv.ordinal());
-
if (isGnssAltitudeDifferenceFromBaroAvailable()) {
track.setGeometricHeightOffset(getGnssAltitudeDifferenceFromBaro());
}
@@ -50,6 +49,31 @@ public void apply(Track track) {
track.setRocd(getRocd());
}
}
+
+
+ if (track.getVersion().equals(Version.VERSION2) && track.register09() instanceof Register09V0) {
+ track.register09(new Register09V0());
+ }
+ if (!track.getVersion().equals(Version.VERSION2) && !(track.register09() instanceof Register09V0)) {
+ track.register09(new Register09());
+ }
+
+ if (track.register09() instanceof Register09V0) {
+ ((Register09V0) track.register09()).setIfrCapability(isIfrCapability());
+ }
+
+ track.register09()
+ .setHeadingSource(isHeadingAvailable() ? Angle.HEADING : Angle.UNAVAILABLE)
+ .setHeading((int) Math.round(heading))
+ .setAirspeedSource(isAirspeedAvailable() ? airspeedType : Speed.UNKNOWN)
+ .setAirspeed(airspeed)
+ .setVerticalRateSource(isRocdAvailable() ? getRocdSource() : RocdSource.UNKNOWN)
+ .setVerticalRate(isRocdAvailable() ? getRocd() : 0)
+ .setGnssDifferenceFromBaro(isGnssAltitudeDifferenceFromBaroAvailable() ? getGnssAltitudeDifferenceFromBaro() : 0)
+ .setIntentChangeFlag(isIntentChange())
+ .setNACv(NavigationAccuracyCategoryVelocity.values()[NACv.ordinal()])
+ .validate();
+ ;
}
public boolean isHeadingAvailable() {
diff --git a/src/main/java/aero/t2s/modes/decoder/df/df17/AirborneVelocityGroundspeed.java b/src/main/java/aero/t2s/modes/decoder/df/df17/AirborneVelocityGroundspeed.java
index 8cc08aa..7cdeaac 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/df17/AirborneVelocityGroundspeed.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/df17/AirborneVelocityGroundspeed.java
@@ -1,7 +1,11 @@
package aero.t2s.modes.decoder.df.df17;
import aero.t2s.modes.Track;
+import aero.t2s.modes.constants.NavigationAccuracyCategoryVelocity;
import aero.t2s.modes.constants.RocdSource;
+import aero.t2s.modes.constants.Version;
+import aero.t2s.modes.registers.Register09;
+import aero.t2s.modes.registers.Register09V0;
public class AirborneVelocityGroundspeed extends AirborneVelocity {
private boolean xVelocityAvailable;
@@ -38,8 +42,6 @@ public AirborneVelocityGroundspeed decode() {
@Override
public void apply(Track track) {
- track.setNACv(NACv.ordinal());
-
if (isGnssAltitudeDifferenceFromBaroAvailable()) {
track.setGeometricHeightOffset(getGnssAltitudeDifferenceFromBaro());
}
@@ -65,5 +67,46 @@ public void apply(Track track) {
if (xVelocityAvailable && yVelocityAvailable) {
track.setGs(Math.sqrt(xVelocity * xVelocity + yVelocity * yVelocity));
}
+
+ if (track.getVersion().equals(Version.VERSION2) && track.register09() instanceof Register09V0) {
+ track.register09(new Register09V0());
+ }
+ if (!track.getVersion().equals(Version.VERSION2) && !(track.register09() instanceof Register09V0)) {
+ track.register09(new Register09());
+ }
+
+ if (track.register09() instanceof Register09V0) {
+ ((Register09V0) track.register09()).setIfrCapability(isIfrCapability());
+ }
+
+ track.register09()
+ .setVerticalRateSource(isRocdAvailable() ? getRocdSource() : RocdSource.UNKNOWN)
+ .setVerticalRate(isRocdAvailable() ? getRocd() : 0)
+ .setGnssDifferenceFromBaro(isGnssAltitudeDifferenceFromBaroAvailable() ? getGnssAltitudeDifferenceFromBaro() : 0)
+ .setIntentChangeFlag(isIntentChange())
+ .setNACv(NavigationAccuracyCategoryVelocity.values()[NACv.ordinal()])
+ .validate();
+
+ if (xVelocityAvailable && yVelocityAvailable) {
+ track.register09()
+ .setVx(xVelocity)
+ .setVy(yVelocity);
+ }
+ }
+
+ public boolean isVxAvailable() {
+ return xVelocityAvailable;
+ }
+
+ public int getVx() {
+ return xVelocity;
+ }
+
+ public boolean isVyAvailable() {
+ return yVelocityAvailable;
+ }
+
+ public int getVy() {
+ return yVelocity;
}
}
diff --git a/src/main/java/aero/t2s/modes/decoder/df/df17/AircraftIdentification.java b/src/main/java/aero/t2s/modes/decoder/df/df17/AircraftIdentification.java
index 6b04cc4..d2fc21f 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/df17/AircraftIdentification.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/df17/AircraftIdentification.java
@@ -1,10 +1,12 @@
package aero.t2s.modes.decoder.df.df17;
import aero.t2s.modes.Track;
+import aero.t2s.modes.constants.AircraftCategory;
import aero.t2s.modes.decoder.Common;
public class AircraftIdentification extends ExtendedSquitter {
private String acid;
+ private int aircraftEmitterCategory;
public AircraftIdentification(short[] data) {
super(data);
@@ -21,6 +23,7 @@ public AircraftIdentification decode() {
Common.charToString(((data[9] & 0xF) << 2) | (data[10] >>> 6)) +
Common.charToString(data[10] & 0x3F);
acid = acid.replace("_", "");
+ aircraftEmitterCategory = data[4] >> 4;
return this;
}
@@ -28,6 +31,51 @@ public AircraftIdentification decode() {
@Override
public void apply(Track track) {
track.setCallsign(acid);
+
+ track.register08().update(acid, determineAircraftCategory());
+ }
+
+ private AircraftCategory determineAircraftCategory() {
+ switch (typeCode) {
+ case 4:
+ switch (aircraftEmitterCategory) {
+ case 0: return AircraftCategory.NO_ADS_B_EMITTER;
+ case 1: return AircraftCategory.LIGHT;
+ case 2: return AircraftCategory.SMALL;
+ case 3: return AircraftCategory.LARGE;
+ case 4: return AircraftCategory.HIGH_VORTEX_LARGE;
+ case 5: return AircraftCategory.HEAVY;
+ case 6: return AircraftCategory.HIGH_PERFORMANCE;
+ case 7: return AircraftCategory.ROTORCRAFT;
+ default: return AircraftCategory.UNKNOWN;
+ }
+ case 3:
+ switch (aircraftEmitterCategory) {
+ case 0: return AircraftCategory.NO_ADS_B_EMITTER;
+ case 1: return AircraftCategory.GLIDER;
+ case 2: return AircraftCategory.LIGHTER_THAN_AIR;
+ case 3: return AircraftCategory.SKYDIVER;
+ case 4: return AircraftCategory.ULTRALIGHT;
+ case 5: return AircraftCategory.RESERVED;
+ case 6: return AircraftCategory.UNMANNED_AERIAL_VEHICLE;
+ case 7: return AircraftCategory.SPACE;
+ default: return AircraftCategory.UNKNOWN;
+ }
+ case 2:
+ switch (aircraftEmitterCategory) {
+ case 0: return AircraftCategory.NO_ADS_B_EMITTER;
+ case 1: return AircraftCategory.SURFACE_VEHICLE_EMERGENCY;
+ case 2: return AircraftCategory.SURFACE_VEHICLE_SERVICE;
+ case 3: return AircraftCategory.POINT_OBSTACLE;
+ case 4: return AircraftCategory.CLUSTER_OBSTACLE;
+ case 5: return AircraftCategory.LINE_OBSTACLE;
+ case 6: return AircraftCategory.RESERVED;
+ case 7: return AircraftCategory.RESERVED;
+ default: return AircraftCategory.UNKNOWN;
+ }
+ default:
+ return AircraftCategory.UNKNOWN;
+ }
}
public String getAcid() {
diff --git a/src/main/java/aero/t2s/modes/decoder/df/df17/TargetStatusMessageType0.java b/src/main/java/aero/t2s/modes/decoder/df/df17/TargetStatusMessageType0.java
index 03f1410..85989a9 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/df17/TargetStatusMessageType0.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/df17/TargetStatusMessageType0.java
@@ -1,6 +1,5 @@
package aero.t2s.modes.decoder.df.df17;
-import aero.t2s.modes.NotImplementedException;
import aero.t2s.modes.Track;
import aero.t2s.modes.constants.*;
@@ -61,8 +60,6 @@ public TargetStatusMessageType0 decode() {
@Override
public void apply(Track track) {
- track.setNACp(NACp);
-
if (horizontalDataAvailable != HorizontalDataAvailable.NOT_VALID)
track.setSelectedHeading(targetHeadingTrack);
diff --git a/src/main/java/aero/t2s/modes/decoder/df/df17/TargetStatusMessageType1.java b/src/main/java/aero/t2s/modes/decoder/df/df17/TargetStatusMessageType1.java
index 7a3adb5..80c4a5f 100644
--- a/src/main/java/aero/t2s/modes/decoder/df/df17/TargetStatusMessageType1.java
+++ b/src/main/java/aero/t2s/modes/decoder/df/df17/TargetStatusMessageType1.java
@@ -81,16 +81,10 @@ public void apply(Track track) {
track.setSelectedAltitudeManagedMcp(selectedAltitudeType == SelectedAltitudeSource.MCP);
track.setSelectedAltitude(selectedAltitudeType != SelectedAltitudeSource.UNKNOWN ? selectedAltitude : 0);
- if (isBaroAvailable()) {
- track.setBaroAltitude((int) Math.round(baroSetting));
- }
-
if (selectedHeadingAvailable) {
track.setSelectedHeading(selectedHeading);
}
- track.setNACp(NACp);
- track.setNICb(NICbaro.ordinal());
track.setSil(SIL.ordinal());
track.setAutopilot(autopilot);
track.setVnav(autopilotVnav);
diff --git a/src/main/java/aero/t2s/modes/examples/StdOutExample.java b/src/main/java/aero/t2s/modes/examples/StdOutExample.java
deleted file mode 100644
index 113034b..0000000
--- a/src/main/java/aero/t2s/modes/examples/StdOutExample.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package aero.t2s.modes.examples;
-
-import aero.t2s.modes.ModeS;
-
-public class StdOutExample {
- public static void main(String[] args) {
- ModeS modes = new ModeS(
- "127.0.0.1", // Host IP where the Dump1090 server is running
- 30002, // The port with raw output (default 30002)
- 51, // Decimal latitude
- 0 // Decimal longitude
- );
- modes.onTrackCreated(track -> System.out.println("CREATED " + track.toString()));
- modes.onTrackUpdated(track -> System.out.println("UPDATED " + track.toString()));
- modes.onTrackDeleted(track -> System.out.println("DELETED " + track.toString()));
-
- modes.start();
- }
-}
diff --git a/src/main/java/aero/t2s/modes/registers/Register.java b/src/main/java/aero/t2s/modes/registers/Register.java
new file mode 100644
index 0000000..0007fb5
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register.java
@@ -0,0 +1,15 @@
+package aero.t2s.modes.registers;
+
+abstract public class Register {
+ private boolean valid = false;
+
+ public void validate() {
+ valid = true;
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public abstract String toString();
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register05.java b/src/main/java/aero/t2s/modes/registers/Register05.java
new file mode 100644
index 0000000..b9ebaff
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register05.java
@@ -0,0 +1,103 @@
+package aero.t2s.modes.registers;
+
+import aero.t2s.modes.constants.AltitudeSource;
+import aero.t2s.modes.constants.HorizontalProtectionLimit;
+import aero.t2s.modes.constants.SurveillanceStatus;
+import aero.t2s.modes.constants.Version;
+
+import java.time.Instant;
+
+public abstract class Register05 extends Register {
+ private SurveillanceStatus surveillanceStatus = SurveillanceStatus.NO_CONDITION;
+ private int altitude = 0;
+ private AltitudeSource altitudeSource = AltitudeSource.BARO;
+ private double lat = 0;
+ private double lon = 0;
+ private HorizontalProtectionLimit hpl = HorizontalProtectionLimit.RC_UNKNOWN;
+ private Instant updated = Instant.MIN;
+
+ public SurveillanceStatus getSurveillanceStatus() {
+ return surveillanceStatus;
+ }
+
+ public Register05 setSurveillanceStatus(SurveillanceStatus surveillanceStatus) {
+ this.surveillanceStatus = surveillanceStatus;
+ return this;
+ }
+
+ public int getAltitude() {
+ return altitude;
+ }
+
+ public Register05 setAltitude(int altitude) {
+ this.altitude = altitude;
+ return this;
+ }
+
+ public AltitudeSource getAltitudeSource() {
+ return altitudeSource;
+ }
+
+ public Register05 setAltitudeSource(AltitudeSource altitudeSource) {
+ this.altitudeSource = altitudeSource;
+ return this;
+ }
+
+ public double getLat() {
+ return lat;
+ }
+
+ public Register05 setLat(double lat) {
+ this.lat = lat;
+ return this;
+ }
+
+ public double getLon() {
+ return lon;
+ }
+
+ public Register05 setLon(double lon) {
+ this.lon = lon;
+ return this;
+ }
+
+ public HorizontalProtectionLimit getHpl() {
+ return hpl;
+ }
+
+ public Register05 setHpl(HorizontalProtectionLimit hpl) {
+ this.hpl = hpl;
+ return this;
+ }
+
+ public Instant getUpdated() {
+ return updated;
+ }
+
+ public abstract Version getVersion();
+
+
+ public void update(HorizontalProtectionLimit hpl, int altitude, AltitudeSource source, double lat, double lon, SurveillanceStatus surveillanceStatus) {
+ this.hpl = hpl;
+ this.altitude = altitude;
+ this.lat = lat;
+ this.lon = lon;
+ this.surveillanceStatus = surveillanceStatus;
+ this.updated = Instant.now();
+ this.validate();
+ }
+
+ @Override
+ public String toString() {
+ return "Register05{\n" +
+ "valid=" + isValid() +
+ ",\n surveillanceStatus=" + surveillanceStatus +
+ ",\n altitude=" + altitude +
+ ",\n altitudeSource=" + altitudeSource.name() +
+ ",\n lat=" + lat +
+ ",\n lon=" + lon +
+ ",\n hpl=" + hpl +
+ ",\n updated=" + updated +
+ "\n}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register05V0.java b/src/main/java/aero/t2s/modes/registers/Register05V0.java
new file mode 100644
index 0000000..0f63bd2
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register05V0.java
@@ -0,0 +1,36 @@
+package aero.t2s.modes.registers;
+
+import aero.t2s.modes.constants.AltitudeSource;
+import aero.t2s.modes.constants.HorizontalProtectionLimit;
+import aero.t2s.modes.constants.SurveillanceStatus;
+import aero.t2s.modes.constants.Version;
+
+public class Register05V0 extends Register05 {
+ private boolean singleAntennaFlag = false;
+
+ public boolean isSingleAntennaFlag() {
+ return singleAntennaFlag;
+ }
+
+ public Register05V0 setSingleAntennaFlag(boolean singleAntennaFlag) {
+ this.singleAntennaFlag = singleAntennaFlag;
+ return this;
+ }
+
+ @Override
+ public Version getVersion() {
+ return Version.VERSION0;
+ }
+
+ public void update(HorizontalProtectionLimit hpl, int altitude, AltitudeSource altitudeSource, double lat, double lon, SurveillanceStatus surveillanceStatus, boolean singleAntennaFlag) {
+ super.update(hpl, altitude, altitudeSource, lat, lon, surveillanceStatus);
+ this.singleAntennaFlag = singleAntennaFlag;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "\n" + "Register05V0{" +
+ "\nsingleAntennaFlag=" + singleAntennaFlag +
+ "\n}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register05V2.java b/src/main/java/aero/t2s/modes/registers/Register05V2.java
new file mode 100644
index 0000000..60bcfde
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register05V2.java
@@ -0,0 +1,23 @@
+package aero.t2s.modes.registers;
+
+import aero.t2s.modes.constants.AltitudeSource;
+import aero.t2s.modes.constants.HorizontalProtectionLimit;
+import aero.t2s.modes.constants.SurveillanceStatus;
+import aero.t2s.modes.constants.Version;
+
+public class Register05V2 extends Register05 {
+ @Override
+ public Version getVersion() {
+ return Version.VERSION2;
+ }
+
+ @Override
+ public void update(HorizontalProtectionLimit hpl, int altitude, AltitudeSource altitudeSource, double lat, double lon, SurveillanceStatus surveillanceStatus) {
+ super.update(hpl, altitude, altitudeSource, lat, lon, surveillanceStatus);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "\n" + "Register05V2 {}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register06.java b/src/main/java/aero/t2s/modes/registers/Register06.java
new file mode 100644
index 0000000..2823235
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register06.java
@@ -0,0 +1,19 @@
+package aero.t2s.modes.registers;
+
+public class Register06 extends Register {
+ private double groundSpeed;
+ private double groundTrack;
+ private double lat;
+ private double lon;
+
+ @Override
+ public String toString() {
+ return "Register06{\n" +
+ "valid=" + isValid() +
+ ",\n groundSpeed=" + groundSpeed +
+ ",\n groundTrack=" + groundTrack +
+ ",\n lat=" + lat +
+ ",\n lon=" + lon +
+ "\n}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register07.java b/src/main/java/aero/t2s/modes/registers/Register07.java
new file mode 100644
index 0000000..e378d7a
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register07.java
@@ -0,0 +1,42 @@
+package aero.t2s.modes.registers;
+
+import aero.t2s.modes.constants.AltitudeSource;
+import aero.t2s.modes.constants.TransmissionRate;
+
+public class Register07 extends Register {
+ private TransmissionRate transmissionRate = TransmissionRate.UNKNOWN;
+ private AltitudeSource altitudeType = AltitudeSource.BARO;
+
+ public TransmissionRate getTransmissionRate() {
+ return transmissionRate;
+ }
+
+ public Register07 setTransmissionRate(TransmissionRate transmissionRate) {
+ this.transmissionRate = transmissionRate;
+ return this;
+ }
+
+ public AltitudeSource getAltitudeType() {
+ return altitudeType;
+ }
+
+ public Register07 setAltitudeType(AltitudeSource altitudeType) {
+ this.altitudeType = altitudeType;
+ return this;
+ }
+
+ public void update(TransmissionRate trs, AltitudeSource altitudeType) {
+ setTransmissionRate(trs);
+ setAltitudeType(altitudeType);
+ validate();
+ }
+
+ @Override
+ public String toString() {
+ return "Register07{\n" +
+ "valid=" + isValid() +
+ ",\ntransmissionRate=" + transmissionRate +
+ ",\n altitudeType=" + altitudeType +
+ "\n}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register08.java b/src/main/java/aero/t2s/modes/registers/Register08.java
new file mode 100644
index 0000000..8549868
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register08.java
@@ -0,0 +1,41 @@
+package aero.t2s.modes.registers;
+
+import aero.t2s.modes.constants.AircraftCategory;
+
+public class Register08 extends Register {
+ private String acid;
+ private AircraftCategory aircraftCategory = AircraftCategory.UNKNOWN;
+
+ public String getAcid() {
+ return acid;
+ }
+
+ public Register08 setAcid(String acid) {
+ this.acid = acid;
+ return this;
+ }
+
+ public AircraftCategory getAircraftCategory() {
+ return aircraftCategory;
+ }
+
+ public Register08 setAircraftCategory(AircraftCategory aircraftCategory) {
+ this.aircraftCategory = aircraftCategory;
+ return this;
+ }
+
+ public void update(String acid, AircraftCategory aircraftCategory) {
+ setAcid(acid);
+ setAircraftCategory(aircraftCategory);
+ validate();
+ }
+
+ @Override
+ public String toString() {
+ return "Register08{\n" +
+ "valid=" + isValid() +
+ "\nacid='" + acid + '\'' +
+ ",\n aircraftCategory=" + aircraftCategory +
+ "\n}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register09.java b/src/main/java/aero/t2s/modes/registers/Register09.java
new file mode 100644
index 0000000..6bf83d6
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register09.java
@@ -0,0 +1,142 @@
+package aero.t2s.modes.registers;
+
+import aero.t2s.modes.constants.Angle;
+import aero.t2s.modes.constants.NavigationAccuracyCategoryVelocity;
+import aero.t2s.modes.constants.RocdSource;
+import aero.t2s.modes.constants.Speed;
+
+public class Register09 extends Register {
+ private NavigationAccuracyCategoryVelocity NACv;
+
+ /**
+ * An intent change event shall be triggered 4 seconds after the detection of new information being inserted in registers 4016 to 4216.
+ * The code shall remain set for 18 +/-1 second following an intent change.
+ */
+ private boolean intentChangeFlag;
+ private int heading = 0;
+ private Angle headingSource = Angle.UNAVAILABLE;
+ private int airspeed = 0;
+ private Speed airspeedSource = Speed.IAS;
+ private int vx = 0;
+ private int vy = 0;
+ private int gnssDifferenceFromBaro = 0;
+ private int verticalRate;
+ private RocdSource verticalRateSource = RocdSource.GNSS;
+
+ public NavigationAccuracyCategoryVelocity getNACv() {
+ return NACv;
+ }
+
+ public Register09 setNACv(NavigationAccuracyCategoryVelocity NACv) {
+ this.NACv = NACv;
+ return this;
+ }
+
+ public boolean isIntentChangeFlag() {
+ return intentChangeFlag;
+ }
+
+ public Register09 setIntentChangeFlag(boolean intentChangeFlag) {
+ this.intentChangeFlag = intentChangeFlag;
+ return this;
+ }
+
+ public int getHeading() {
+ return heading;
+ }
+
+ public Register09 setHeading(int heading) {
+ this.heading = heading;
+ return this;
+ }
+
+ public Angle getHeadingSource() {
+ return headingSource;
+ }
+
+ public Register09 setHeadingSource(Angle headingSource) {
+ this.headingSource = headingSource;
+ return this;
+ }
+
+ public int getAirspeed() {
+ return airspeed;
+ }
+
+ public Register09 setAirspeed(int airspeed) {
+ this.airspeed = airspeed;
+ return this;
+ }
+
+ public Speed getAirspeedSource() {
+ return airspeedSource;
+ }
+
+ public Register09 setAirspeedSource(Speed airspeedSource) {
+ this.airspeedSource = airspeedSource;
+ return this;
+ }
+
+ public int getVx() {
+ return vx;
+ }
+
+ public Register09 setVx(int vx) {
+ this.vx = vx;
+ return this;
+ }
+
+ public int getVy() {
+ return vy;
+ }
+
+ public Register09 setVy(int vy) {
+ this.vy = vy;
+ return this;
+ }
+
+ public int getGnssDifferenceFromBaro() {
+ return gnssDifferenceFromBaro;
+ }
+
+ public Register09 setGnssDifferenceFromBaro(int gnssDifferenceFromBaro) {
+ this.gnssDifferenceFromBaro = gnssDifferenceFromBaro;
+ return this;
+ }
+
+ public int getVerticalRate() {
+ return verticalRate;
+ }
+
+ public Register09 setVerticalRate(int verticalRate) {
+ this.verticalRate = verticalRate;
+ return this;
+ }
+
+ public RocdSource getVerticalRateSource() {
+ return verticalRateSource;
+ }
+
+ public Register09 setVerticalRateSource(RocdSource verticalRateSource) {
+ this.verticalRateSource = verticalRateSource;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "Register09{\n" +
+ "valid=" + isValid() +
+ ",\n NACv=" + NACv +
+ ",\n intentChangeFlag=" + intentChangeFlag +
+ ",\n heading=" + heading +
+ ",\n headingSource=" + headingSource +
+ ",\n airspeed=" + airspeed +
+ ",\n airspeedSource=" + airspeedSource +
+ ",\n vx=" + vx +
+ ",\n vy=" + vy +
+ ",\n gnssDifferenceFromBaro=" + gnssDifferenceFromBaro +
+ ",\n verticalRate=" + verticalRate +
+ ",\n verticalRateSource=" + verticalRateSource +
+ "\n}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register09V0.java b/src/main/java/aero/t2s/modes/registers/Register09V0.java
new file mode 100644
index 0000000..d11552f
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register09V0.java
@@ -0,0 +1,26 @@
+package aero.t2s.modes.registers;
+
+public class Register09V0 extends Register09 {
+ /**
+ * The IFR capability flag shall be a 1-bit (bit 10) subfield in the subtypes 1, 2, 3 and 4 airborne velocity messages.
+ * IFR=1 shall signify that the transmitting aircraft has a capability for applications requiring ADS-B equipage class A1 or above.
+ * Otherwise, IFR shall be set to 0.
+ */
+ private boolean ifrCapability;
+
+ public boolean isIfrCapability() {
+ return ifrCapability;
+ }
+
+ public Register09V0 setIfrCapability(boolean ifrCapability) {
+ this.ifrCapability = ifrCapability;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "\nRegister09V0{\n" +
+ "ifrCapability=" + ifrCapability +
+ "\n}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register17.java b/src/main/java/aero/t2s/modes/registers/Register17.java
new file mode 100644
index 0000000..fc74e4e
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register17.java
@@ -0,0 +1,26 @@
+package aero.t2s.modes.registers;
+
+import aero.t2s.modes.CapabilityReport;
+import aero.t2s.modes.decoder.df.bds.Bds17;
+
+public class Register17 extends Register {
+ private CapabilityReport capabilityReport = new CapabilityReport();
+
+ public CapabilityReport getCapabilityReport() {
+ return capabilityReport;
+ }
+
+ public Register17 update(Bds17 bds) {
+ capabilityReport.update(bds);
+ this.validate();
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "Register17{\n" +
+ "valid=" + isValid() +
+ "\n" + capabilityReport.toString() +
+ "\n}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register20.java b/src/main/java/aero/t2s/modes/registers/Register20.java
new file mode 100644
index 0000000..93b46bb
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register20.java
@@ -0,0 +1,23 @@
+package aero.t2s.modes.registers;
+
+public class Register20 extends Register {
+ private String acid;
+
+ public String getAcid() {
+ return acid;
+ }
+
+ public Register20 setAcid(String acid) {
+ this.acid = acid;
+ validate();
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "Register20{\n" +
+ "valid=" + isValid() +
+ "\nacid='" + acid + '\'' +
+ "\n}";
+ }
+}
diff --git a/src/main/java/aero/t2s/modes/registers/Register21.java b/src/main/java/aero/t2s/modes/registers/Register21.java
new file mode 100644
index 0000000..00f1322
--- /dev/null
+++ b/src/main/java/aero/t2s/modes/registers/Register21.java
@@ -0,0 +1,29 @@
+package aero.t2s.modes.registers;
+
+public class Register21 extends Register {
+ private String aircraft;
+ private String airline;
+
+ public String getAircraft() {
+ return aircraft;
+ }
+
+ public String getAirline() {
+ return airline;
+ }
+
+ public void update(String aircraft, String airline) {
+ this.aircraft = airline;
+ this.airline = airline;
+ validate();
+ }
+
+ @Override
+ public String toString() {
+ return "Register21{\n" +
+ "valid=" + isValid() +
+ ",\n aircraft='" + aircraft + '\'' +
+ ",\n airline='" + airline + '\'' +
+ "\n}";
+ }
+}
diff --git a/src/test/java/aero/t2s/modes/decoder/df/DF0Test.java b/src/test/java/aero/t2s/modes/decoder/df/DF0Test.java
index 3bfbba2..c27b083 100644
--- a/src/test/java/aero/t2s/modes/decoder/df/DF0Test.java
+++ b/src/test/java/aero/t2s/modes/decoder/df/DF0Test.java
@@ -43,7 +43,7 @@ void it_decodes_sensitivity_level()
void it_decodes_reply_information()
{
df0 = new DF0(BinaryHelper.stringToByteArray("02E194979F2C4B")).decode();
- assertEquals(AcasReplyInformation.RESERVED3, df0.getReplyInformation());
+ assertEquals(AcasReplyInformation.ACAS_RA_VERTICAL_ONLY, df0.getReplyInformation());
}
@Test
@@ -51,7 +51,7 @@ void it_decodes_altitude()
{
df0 = new DF0(BinaryHelper.stringToByteArray("02E1951DE7596A")).decode();
- assertEquals(33325, df0.getAltitude().getAltitude(), 0.1);
+ assertEquals(32925, df0.getAltitude().getAltitude(), 0.1);
assertEquals(25, df0.getAltitude().getStep());
assertFalse(df0.getAltitude().isMetric());
}
diff --git a/src/test/java/aero/t2s/modes/decoder/df/DF24Test.java b/src/test/java/aero/t2s/modes/decoder/df/DF24Test.java
new file mode 100644
index 0000000..897a474
--- /dev/null
+++ b/src/test/java/aero/t2s/modes/decoder/df/DF24Test.java
@@ -0,0 +1,18 @@
+package aero.t2s.modes.decoder.df;
+
+import aero.t2s.modes.BinaryHelper;
+import aero.t2s.modes.decoder.UnknownDownlinkFormatException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class DF24Test {
+ @Test
+ public void test_df24_elm() throws UnknownDownlinkFormatException {
+ DF24 df = new DF24(BinaryHelper.stringToByteArray("C33D2901090141AE21C600180121"));
+ df.decode();
+
+ assertEquals("76CEFA", df.getIcao());
+ assertEquals(3, df.getSequenceNo());
+ }
+}
diff --git a/src/test/java/aero/t2s/modes/decoder/df/DfRealMessageTest.java b/src/test/java/aero/t2s/modes/decoder/df/DfRealMessageTest.java
new file mode 100644
index 0000000..039f009
--- /dev/null
+++ b/src/test/java/aero/t2s/modes/decoder/df/DfRealMessageTest.java
@@ -0,0 +1,438 @@
+package aero.t2s.modes.decoder.df;
+
+import aero.t2s.modes.BinaryHelper;
+import aero.t2s.modes.constants.*;
+import aero.t2s.modes.database.ModeSDatabase;
+import aero.t2s.modes.decoder.Decoder;
+import aero.t2s.modes.decoder.UnknownDownlinkFormatException;
+import aero.t2s.modes.decoder.df.bds.*;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class DfRealMessageTest {
+ @Test
+ public void test_bds60() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A0001838E71A21357F640110A153");
+
+ assertInstanceOf(DF20.class, df);
+
+ assertTrue(((DF20)df).isValid());
+ assertFalse(((DF20)df).isMultipleMatches());
+ assertInstanceOf(Bds60.class, ((DF20)df).getBds());
+ assertEquals(38000, ((DF20) df).getAltitude().getAltitude());
+
+ Bds60 bds = (Bds60) ((DF20)df).getBds();
+ assertTrue(bds.isStatusMagneticHeading());
+ assertEquals(289.863, bds.getMagneticHeading(), 0.001);
+ assertTrue(bds.isStatusIas());
+ assertEquals(272, bds.getIas());
+ assertTrue(bds.isStatusMach());
+ assertEquals(0.852, bds.getMach());
+ assertTrue(bds.isStatusBaroRocd());
+ assertEquals(-640, bds.getBaroRocd());
+ assertTrue(bds.isStatusIrsRocd());
+ assertEquals(32, bds.getIrsRocd());
+ }
+
+ @Test
+ public void test_df_20_bds_40_a48e35() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A00006A1E3A71D30AA014672C8DF");
+
+ assertInstanceOf(DF20.class, df);
+ DF20 df20 = (DF20) df;
+ assertEquals("A48E35", df.getIcao());
+ assertEquals(51000, df20.getAltitude().getAltitude());
+
+ assertTrue(df20.isValid());
+ assertInstanceOf(Bds40.class, df20.getBds());
+
+ Bds40 bds = (Bds40) df20.getBds();
+ assertTrue(bds.isStatusTargetSource());
+ assertEquals(SelectedAltitudeSource.MCP, bds.getSelectedAltitudeSource());
+ assertTrue(bds.isStatusMcp());
+ assertEquals(51008, bds.getSelectedAltitude());
+ assertTrue(bds.isStatusFms());
+ assertEquals(51008, bds.getFmsAltitude());
+ assertTrue(bds.isStatusBaro());
+ assertEquals(1013.3, bds.getBaro(), 0.1);
+ assertTrue(bds.isStatusMcpMode());
+ assertFalse(bds.isAutopilotApproach());
+ assertFalse(bds.isAutopilotVnav());
+ assertTrue(bds.isAutopilotAltitudeHold());
+ }
+
+
+ @Test
+ public void test_df20_bds60_800736() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A0001A1FA439F534BF07FFDE1ECC");
+
+ assertInstanceOf(DF20.class, df);
+ DF20 df20 = (DF20) df;
+ assertEquals("800736", df.getIcao());
+ assertEquals(40975, df20.getAltitude().getAltitude());
+
+ assertTrue(df20.isValid());
+ assertInstanceOf(Bds60.class, df20.getBds());
+
+ Bds60 bds = (Bds60) df20.getBds();
+ assertTrue(bds.isStatusIrsRocd());
+ assertEquals(-32, bds.getIrsRocd());
+ assertTrue(bds.isStatusBaroRocd());
+ assertEquals(-1024.0, bds.getBaroRocd());
+ assertTrue(bds.isStatusIas());
+ assertEquals(250, bds.getIas());
+ assertTrue(bds.isStatusMach());
+ assertEquals(0.84, bds.getMach(), 0.1);
+ assertTrue(bds.isStatusMagneticHeading());
+ assertEquals(101.7, bds.getMagneticHeading(), 0.1);
+ }
+
+ @Test
+ public void test_df21_bds60_4CA708() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A8800337D439E730BFE600C28696");
+
+ assertInstanceOf(DF21.class, df);
+ DF21 df21 = (DF21) df;
+ assertEquals("4CA708", df.getIcao());
+ assertEquals(2547, df21.getModeA());
+
+ assertTrue(df21.isValid());
+ assertInstanceOf(Bds60.class, df21.getBds());
+
+ Bds60 bds = (Bds60) df21.getBds();
+ assertTrue(bds.isStatusIrsRocd());
+ assertEquals(0, bds.getIrsRocd(), 0.1);
+ assertTrue(bds.isStatusBaroRocd());
+ assertEquals(-128, bds.getBaroRocd(), 0.1);
+ assertTrue(bds.isStatusIas());
+ assertEquals(243, bds.getIas());
+ assertTrue(bds.isStatusMach());
+ assertEquals(0.77, bds.getMach(), 0.1);
+ assertTrue(bds.isStatusMagneticHeading());
+ assertEquals(236.7, bds.getMagneticHeading(), 0.1);
+ }
+
+ @Test
+ public void test_df21_bds60_00000() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A800198EEABA2B30F0041257522A");
+
+ assertInstanceOf(DF21.class, df);
+ DF21 df21 = (DF21) df;
+ assertEquals("000000", df.getIcao()); // Military / corrupt transponder
+ assertEquals(5652, df21.getModeA());
+
+ assertTrue(df21.isValid());
+ assertInstanceOf(Bds60.class, df21.getBds());
+
+ Bds60 bds = (Bds60) df21.getBds();
+ assertTrue(bds.isStatusIrsRocd());
+ assertEquals(576, bds.getIrsRocd(), 0.1);
+ assertTrue(bds.isStatusBaroRocd());
+ assertEquals(0, bds.getBaroRocd(), 0.1);
+ assertTrue(bds.isStatusIas());
+ assertEquals(277, bds.getIas());
+ assertTrue(bds.isStatusMach());
+ assertEquals(0.77, bds.getMach(), 0.1);
+ assertTrue(bds.isStatusMagneticHeading());
+ assertEquals(300.0, bds.getMagneticHeading(), 0.1);
+ }
+
+ @Test
+ public void test_df21_bds60_406ECD() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A8000C98A549F33461E40552947D");
+
+ assertInstanceOf(DF21.class, df);
+ DF21 df21 = (DF21) df;
+ assertEquals("406ECD", df.getIcao()); // Military / corrupt transponder
+ assertEquals(5221, df21.getModeA());
+
+ assertTrue(df21.isValid());
+ assertInstanceOf(Bds60.class, df21.getBds());
+
+ Bds60 bds = (Bds60) df21.getBds();
+ assertTrue(bds.isStatusIrsRocd());
+ assertEquals(160, bds.getIrsRocd(), 0.1);
+ assertTrue(bds.isStatusBaroRocd());
+ assertEquals(1920, bds.getBaroRocd(), 0.1);
+ assertTrue(bds.isStatusIas());
+ assertEquals(249, bds.getIas());
+ assertTrue(bds.isStatusMach());
+ assertEquals(0.77, bds.getMach(), 0.1);
+ assertTrue(bds.isStatusMagneticHeading());
+ assertEquals(104.7, bds.getMagneticHeading(), 0.1);
+ }
+
+
+ @Test
+ public void test_df21_bds50_44CD73() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A0000333E17987192250004423EC");
+
+ assertInstanceOf(DF20.class, df);
+ DF20 df20 = (DF20) df;
+ assertEquals("44CD73", df.getIcao()); // Military / corrupt transponder
+ assertEquals(4275, df20.getAltitude().getAltitude());
+
+ assertTrue(df20.isValid());
+ assertInstanceOf(Bds50.class, df20.getBds());
+
+ Bds50 bds = (Bds50) df20.getBds();
+ assertTrue(bds.isStatusGs());
+ assertEquals(200, bds.getGs(), 0.1);
+ assertFalse(bds.isStatusTas());
+ assertEquals(0, bds.getTas(), 0.1);
+ assertTrue(bds.isStatusRollAngle());
+ assertEquals(-43, bds.getRollAngle(), 0.1);
+ assertTrue(bds.isStatusTrueAngleRate());
+ assertEquals(2.3, bds.getTrackAngleRate(), 0.1);
+ assertTrue(bds.isStatusTrackAngle());
+ assertEquals(214.2, bds.getTrueTrack(), 0.1);
+ }
+
+ @Test
+ public void test_df21_bds40_407776() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A000169EB2CC0030A80106C25083");
+
+ assertInstanceOf(DF20.class, df);
+ DF20 df20 = (DF20) df;
+ assertEquals("407776", df.getIcao()); // Military / corrupt transponder
+ assertEquals(35350, df20.getAltitude().getAltitude());
+
+ assertTrue(df20.isValid());
+ assertInstanceOf(Bds40.class, df20.getBds());
+
+ Bds40 bds = (Bds40) df20.getBds();
+ assertTrue(bds.isStatusMcp());
+ assertEquals(26000, bds.getSelectedAltitude(), 0.1);
+ assertTrue(bds.isStatusFms());
+ assertEquals(0, bds.getFmsAltitude(), 0.1);
+ assertTrue(bds.isStatusBaro());
+ assertEquals(1013.2, bds.getBaro(), 0.1);
+ assertTrue(bds.isStatusTargetSource());
+ assertEquals(SelectedAltitudeSource.MCP, bds.getSelectedAltitudeSource());
+ assertTrue(bds.isStatusMcpMode());
+ assertFalse(bds.isAutopilotVnav());
+ assertFalse(bds.isAutopilotAltitudeHold());
+ assertFalse(bds.isAutopilotApproach());
+ }
+
+
+ @Test
+ public void test_df21_bds10_407776() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A000042210000600B0000089B69E");
+
+ assertInstanceOf(DF20.class, df);
+ DF20 df20 = (DF20) df;
+ assertEquals("44CC63", df.getIcao()); // Military / corrupt transponder
+ assertEquals(2000, df20.getAltitude().getAltitude());
+
+ assertTrue(df20.isValid());
+ assertInstanceOf(Bds10.class, df20.getBds());
+
+ Bds10 bds = (Bds10) df20.getBds();
+ }
+
+
+ @Test
+ public void test_df21_bds40_4CA6F8() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A000069E8BBC2F30A40000528AB8");
+
+ assertInstanceOf(DF20.class, df);
+ DF20 df20 = (DF20) df;
+ assertEquals("4CA6F8", df.getIcao()); // Military / corrupt transponder
+ assertEquals(9750, df20.getAltitude().getAltitude());
+
+ assertTrue(df20.isValid());
+ assertInstanceOf(Bds40.class, df20.getBds());
+
+ Bds40 bds = (Bds40) df20.getBds();
+ assertFalse(bds.isStatusTargetSource());
+ assertNull(bds.getSelectedAltitudeSource());
+ assertTrue(bds.isStatusMcp());
+ assertEquals(6000, bds.getSelectedAltitude());
+ assertTrue(bds.isStatusFms());
+ assertEquals(3008, bds.getFmsAltitude());
+ assertTrue(bds.isStatusBaro());
+ assertEquals(1013.0, bds.getBaro(), 0.1);
+ assertFalse(bds.isStatusMcpMode());
+ assertFalse(bds.isAutopilotApproach());
+ assertFalse(bds.isAutopilotVnav());
+ assertFalse(bds.isAutopilotAltitudeHold());
+ }
+
+ @Test
+ public void test_df21_bds50_485209() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A000093BFFF16B276004997B748F");
+
+ assertInstanceOf(DF20.class, df);
+ DF20 df20 = (DF20) df;
+ assertEquals("485209", df.getIcao()); // Military / corrupt transponder
+ assertEquals(14075, df20.getAltitude().getAltitude());
+
+ assertTrue(df20.isValid());
+ assertInstanceOf(Bds50.class, df20.getBds());
+
+ Bds50 bds = (Bds50) df20.getBds();
+ assertTrue(bds.isStatusGs());
+ assertEquals(314, bds.getGs(), 0.1);
+ assertTrue(bds.isStatusTas());
+ assertEquals(306, bds.getTas(), 0.1);
+ assertTrue(bds.isStatusRollAngle());
+ assertEquals(-0.1, bds.getRollAngle(), 0.1);
+ assertTrue(bds.isStatusTrueAngleRate());
+ assertEquals(0, bds.getTrackAngleRate(), 0.1);
+ assertTrue(bds.isStatusTrackAngle());
+ assertEquals(31.8, bds.getTrueTrack(), 0.1);
+ }
+
+ @Test
+ public void test_df21_bds50_484FDF() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A800080080502D2A600CA9DF5877");
+
+ assertInstanceOf(DF21.class, df);
+ DF21 df21 = (DF21) df;
+ assertEquals("484FDF", df.getIcao()); // Military / corrupt transponder
+ assertEquals(1000, df21.getModeA());
+
+ assertTrue(df21.isValid());
+ assertInstanceOf(Bds50.class, df21.getBds());
+
+ Bds50 bds = (Bds50) df21.getBds();
+ assertTrue(bds.isStatusGs());
+ assertEquals(338, bds.getGs(), 0.1);
+ assertTrue(bds.isStatusTas());
+ assertEquals(338, bds.getTas(), 0.1);
+ assertTrue(bds.isStatusRollAngle());
+ assertEquals(0.35, bds.getRollAngle(), 0.1);
+ assertTrue(bds.isStatusTrueAngleRate());
+ assertEquals(0, bds.getTrackAngleRate(), 0.1);
+ assertTrue(bds.isStatusTrackAngle());
+ assertEquals(3.8, bds.getTrueTrack(), 0.1);
+ }
+
+ @Test
+ public void test_df20_bds50_48418A() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A0000E978F1FBD316114BDA0FFBF");
+
+ assertInstanceOf(DF20.class, df);
+ DF20 df20 = (DF20) df;
+ assertEquals("48418A", df.getIcao()); // Military / corrupt transponder
+ assertEquals(22375, df20.getAltitude().getAltitude());
+
+ assertTrue(df20.isValid());
+ assertInstanceOf(Bds50.class, df20.getBds());
+
+ Bds50 bds = (Bds50) df20.getBds();
+ assertTrue(bds.isStatusGs());
+ assertEquals(394, bds.getGs(), 0.1);
+ assertTrue(bds.isStatusTas());
+ assertEquals(378, bds.getTas(), 0.1);
+ assertTrue(bds.isStatusRollAngle());
+ assertEquals(21, bds.getRollAngle(), 0.1);
+ assertTrue(bds.isStatusTrueAngleRate());
+ assertEquals(1, bds.getTrackAngleRate(), 0.1);
+ assertTrue(bds.isStatusTrackAngle());
+ assertEquals(354, bds.getTrueTrack(), 0.1);
+ }
+
+ @Test
+ public void test_df16_02A198() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("80C18819584195384EF8505941FD");
+
+ assertInstanceOf(DF16.class, df);
+ DF16 df16 = (DF16) df;
+ assertEquals("02A198", df.getIcao()); // Military / corrupt transponder
+ assertEquals(12025, df16.getAltitude().getAltitude());
+
+ assertEquals(VerticalStatus.AIRBORNE, df16.getVerticalStatus());
+ assertEquals(AcasSensitivity.LEVEL6, df16.getSensitivity());
+ assertEquals(AcasReplyInformation.ACAS_RA_VERTICAL_ONLY, df16.getReplyInformation());
+ assertFalse(df16.getResolutionAdvisory().isActive());
+ assertFalse(df16.getResolutionAdvisory().isRequiresCorrectionUpwards());
+ assertFalse(df16.getResolutionAdvisory().isRequiresCorrectionDownwards());
+ assertFalse(df16.getResolutionAdvisory().isRequiresPositiveClimb());
+ assertFalse(df16.getResolutionAdvisory().isRequiresPositiveDescend());
+ assertFalse(df16.getResolutionAdvisory().isRequiresCrossing());
+ assertFalse(df16.getResolutionAdvisory().isSenseReversal());
+
+ assertFalse(df16.isMultipleThreats());
+ assertFalse(df16.isRANotPassAbove());
+ assertFalse(df16.isRANotPassBelow());
+ assertFalse(df16.isRANotTurnLeft());
+ assertFalse(df16.isRANotTurnRight());
+ }
+
+ @Test
+ public void test_df21_bds50_02A185() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A8001AA0F4596112BDFC569A3AEC");
+
+ assertInstanceOf(DF21.class, df);
+ DF21 df21 = (DF21) df;
+ assertEquals("02A185", df.getIcao()); // Military / corrupt transponder
+ assertEquals(7110, df21.getModeA());
+
+ assertFalse(df21.isMultipleMatches());
+ assertTrue(df21.isValid());
+ assertEquals(Bds50.class, df21.getBds().getClass());
+
+ Bds50 bds = (Bds50) df21.getBds();
+ assertTrue(bds.isStatusGs());
+ assertEquals(148, bds.getGs(), 0.1);
+ assertTrue(bds.isStatusTas());
+ assertEquals(172, bds.getTas(), 0.1);
+ assertTrue(bds.isStatusRollAngle());
+ assertEquals(-16.5, bds.getRollAngle(), 0.1);
+ assertTrue(bds.isStatusTrueAngleRate());
+ assertEquals(-2.0, bds.getTrackAngleRate(), 0.1);
+ assertTrue(bds.isStatusTrackAngle());
+ assertEquals(210.9, bds.getTrueTrack(), 0.1);
+ }
+
+ @Test
+ public void test_df20_bds17_3D2C7C() throws UnknownDownlinkFormatException {
+ DownlinkFormat df = testMessage("A0280314020100000000004E25E8");
+
+ assertInstanceOf(DF20.class, df);
+ DF20 df20 = (DF20) df;
+ assertEquals("3D2C7C", df.getIcao()); // Military / corrupt transponder
+ assertEquals(3900, df20.getAltitude().getAltitude());
+
+ assertFalse(df20.isMultipleMatches());
+ assertTrue(df20.isValid());
+ assertEquals(Bds17.class, df20.getBds().getClass());
+
+ Bds17 bds = (Bds17) df20.getBds();
+ assertFalse(bds.isBds0A());
+ assertFalse(bds.isBds05());
+ assertFalse(bds.isBds06());
+ assertFalse(bds.isBds07());
+ assertFalse(bds.isBds08());
+ assertFalse(bds.isBds09());
+ assertFalse(bds.isBds0A());
+ assertTrue(bds.isBds20());
+ assertFalse(bds.isBds21());
+ assertFalse(bds.isBds40());
+ assertFalse(bds.isBds41());
+ assertFalse(bds.isBds42());
+ assertFalse(bds.isBds43());
+ assertFalse(bds.isBds44());
+ assertFalse(bds.isBds45());
+ assertFalse(bds.isBds48());
+ assertTrue(bds.isBds50());
+ assertFalse(bds.isBds51());
+ assertFalse(bds.isBds52());
+ assertFalse(bds.isBds53());
+ assertFalse(bds.isBds54());
+ assertFalse(bds.isBds55());
+ assertFalse(bds.isBds56());
+ assertFalse(bds.isBds5F());
+ assertFalse(bds.isBds60());
+ }
+
+ private DownlinkFormat testMessage(String message) throws UnknownDownlinkFormatException {
+ Decoder decoder = new Decoder(new HashMap<>(), 50, 2, ModeSDatabase.createDatabase());
+
+ return decoder.decode(BinaryHelper.stringToByteArray(message));
+ }
+}
diff --git a/src/test/java/aero/t2s/modes/decoder/df/DfTEst.java b/src/test/java/aero/t2s/modes/decoder/df/DfTEst.java
deleted file mode 100644
index bc4f862..0000000
--- a/src/test/java/aero/t2s/modes/decoder/df/DfTEst.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package aero.t2s.modes.decoder.df;
-
-import aero.t2s.modes.BinaryHelper;
-import aero.t2s.modes.database.ModeSDatabase;
-import aero.t2s.modes.decoder.Decoder;
-import aero.t2s.modes.decoder.UnknownDownlinkFormatException;
-import org.junit.jupiter.api.Test;
-
-import java.util.HashMap;
-
-public class DfTEst {
- @Test
- public void test() throws UnknownDownlinkFormatException {
- String message = "A0001338000557F0A8000098FCDB";
-
- Decoder decoder = new Decoder(new HashMap<>(), 50, 2, ModeSDatabase.createDatabase());
-
- DownlinkFormat df = decoder.decode(BinaryHelper.stringToByteArray(message));
-
- System.out.println(df.toString());
- }
-}
diff --git a/src/test/java/aero/t2s/modes/decoder/df/bds/Bds50Test.java b/src/test/java/aero/t2s/modes/decoder/df/bds/Bds50Test.java
index 5948577..a7d10fd 100644
--- a/src/test/java/aero/t2s/modes/decoder/df/bds/Bds50Test.java
+++ b/src/test/java/aero/t2s/modes/decoder/df/bds/Bds50Test.java
@@ -29,46 +29,6 @@ public void it_does_nothing_with_all_zeros()
assertEquals(0, bds.getTas());
}
- @Test
- public void it_decodes_bds50_roll_angle()
- {
- bds = new Bds50(new short[] {
- 0x0, 0x0, 0x0, 0x0,
- 0b10100000,
- 0b00000000,
- 0b00000000,
- 0b00000000,
- 0b00000000,
- 0b00000000,
- 0b00000000,
- });
-
- assertTrue(bds.isValid());
- assertEquals(45, bds.getRollAngle());
- assertEquals(0, bds.getTrueTrack());
- assertEquals(0, bds.getGs());
- assertEquals(0, bds.getTrackAngleRate());
- assertEquals(0, bds.getTas());
-
- bds = new Bds50(new short[] {
- 0x0, 0x0, 0x0, 0x0,
- 0b11100000,
- 0b00000000,
- 0b00000000,
- 0b00000000,
- 0b00000000,
- 0b00000000,
- 0b00000000,
- });
-
- assertTrue(bds.isValid());
- assertEquals(-45, bds.getRollAngle());
- assertEquals(0, bds.getTrueTrack());
- assertEquals(0, bds.getGs());
- assertEquals(0, bds.getTrackAngleRate());
- assertEquals(0, bds.getTas());
- }
-
@Test
public void it_is_not_bds50_when_roll_angle_is_not_available_and_bits_are_set()
{