diff --git a/src/main/java/de/serosystems/example/ExampleDecoder.java b/src/main/java/de/serosystems/example/ExampleDecoder.java
index 8022654..3be9fe4 100644
--- a/src/main/java/de/serosystems/example/ExampleDecoder.java
+++ b/src/main/java/de/serosystems/example/ExampleDecoder.java
@@ -298,7 +298,8 @@ public void decodeMsg(long timestamp, String raw, Position receiver) {
System.out.println(" Has IFR capability: " + veloc.hasIFRCapability());
break;
- case ADSB_TARGET_STATE_AND_STATUS:
+ case ADSB_TARGET_STATE_AND_STATUS_V1:
+ case ADSB_TARGET_STATE_AND_STATUS_V2:
System.out.println("["+icao24+"]: Target State and Status reported");
if (msg instanceof TargetStateAndStatusV1Msg) {
TargetStateAndStatusV1Msg tStatus = (TargetStateAndStatusV1Msg) msg;
diff --git a/src/main/java/de/serosystems/lib1090/msgs/ModeSDownlinkMsg.java b/src/main/java/de/serosystems/lib1090/msgs/ModeSDownlinkMsg.java
index 24b27f6..f222b77 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/ModeSDownlinkMsg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/ModeSDownlinkMsg.java
@@ -79,7 +79,8 @@ public enum subtype {
ADSB_SURFACE_STATUS_V1,
ADSB_AIRBORN_STATUS_V2,
ADSB_SURFACE_STATUS_V2,
- ADSB_TARGET_STATE_AND_STATUS,
+ ADSB_TARGET_STATE_AND_STATUS_V1,
+ ADSB_TARGET_STATE_AND_STATUS_V2,
SURFACE_SYSTEM_STATUS,
// TIS-B subtypes
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV1Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV1Msg.java
index c762c48..e85c82a 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV1Msg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV1Msg.java
@@ -32,11 +32,11 @@
*
* @author Matthias Schäfer (schaefer@sero-systems.de)
*/
-public class AirborneOperationalStatusV1Msg extends ExtendedSquitter implements Serializable {
+public class AirborneOperationalStatusV1Msg extends ExtendedSquitter implements Serializable, OperationalStatusV1Msg {
private static final long serialVersionUID = -4371842571556132611L;
+ private static final byte SUBTYPE_CODE = 0;
- private byte subtype_code;
protected int capability_class_code; // actually 16 bit unsigned
protected int operational_mode_code; // actually 16 bit unsigned
private byte version;
@@ -90,10 +90,10 @@ public AirborneOperationalStatusV1Msg(ExtendedSquitter squitter) throws BadForma
BitReader b = BitReader.forBigEndian(msg);
- subtype_code = b.readByte(6, 8);
- if (subtype_code > 1) { // currently only 0 and 1 specified, 2-7 are reserved
- throw new UnspecifiedFormatError("Operational status message subtype " + subtype_code + " reserved.");
- } else if (subtype_code != 0) {
+ byte subtypeCode = b.readByte(6, 8);
+ if (subtypeCode > 1) { // currently only 0 and 1 specified, 2-7 are reserved
+ throw new UnspecifiedFormatError("Operational status message subtype " + subtypeCode + " reserved.");
+ } else if (subtypeCode != SUBTYPE_CODE) {
throw new BadFormatException("Not an airborne operational status message");
}
@@ -115,6 +115,14 @@ public AirborneOperationalStatusV1Msg(ExtendedSquitter squitter) throws BadForma
hrd = b.readByte(54, 54) == 1;
}
+ /**
+ * @return the subtype code, 0 for airborne operational status messages
+ */
+ @Override
+ public byte getSubtypeCode() {
+ return SUBTYPE_CODE;
+ }
+
/**
* @return true if TCAS is operational or unknown, false if TCAS is not operational.
*/
@@ -258,7 +266,6 @@ public boolean getHorizontalReferenceDirection() {
@Override
public String toString() {
return "AirborneOperationalStatusV1Msg{" +
- "subtype_code=" + subtype_code +
", capability_class_code=" + capability_class_code +
", operational_mode_code=" + operational_mode_code +
", version=" + version +
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV2Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV2Msg.java
index c772141..e2499b0 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV2Msg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV2Msg.java
@@ -27,7 +27,7 @@
/**
* @author Markus Fuchs (fuchs@opensky-network.org)
*/
-public class AirborneOperationalStatusV2Msg extends AirborneOperationalStatusV1Msg implements Serializable {
+public class AirborneOperationalStatusV2Msg extends AirborneOperationalStatusV1Msg implements Serializable, OperationalStatusV2Msg {
private static final long serialVersionUID = -2032348919695227545L;
@@ -87,6 +87,7 @@ public boolean hasOperationalTCAS() {
/**
* @return whether aircraft has an UAT receiver
*/
+ @Override
public boolean hasUATIn() {
return (capability_class_code & 0x20) != 0;
}
@@ -94,6 +95,7 @@ public boolean hasUATIn() {
/**
* @return whether aircraft uses a single antenna or two
*/
+ @Override
public boolean hasSingleAntenna() {
return (operational_mode_code & 0x400) != 0;
}
@@ -121,6 +123,7 @@ else if (geometric_vertical_accuracy == 2)
*
* @return system design assurance (see A.1.4.10.14 in RTCA DO-260B)
*/
+ @Override
public byte getSystemDesignAssurance() {
return (byte) ((operational_mode_code & 0x300) >>> 8);
}
@@ -131,6 +134,7 @@ public byte getSystemDesignAssurance() {
* @return true if SIL (Source Integrity Level) is based on "per sample" probability, otherwise
* it's based on "per hour".
*/
+ @Override
public boolean hasSILSupplement() {
return sil_supplement;
}
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/AirspeedHeadingMsg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/AirspeedHeadingMsg.java
index a611b7c..4253981 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/AirspeedHeadingMsg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/AirspeedHeadingMsg.java
@@ -37,7 +37,7 @@ public class AirspeedHeadingMsg extends ExtendedSquitter implements Serializable
private boolean ifr_capability;
private byte navigation_accuracy_category;
private boolean heading_status_bit;
- private double heading; // in degrees
+ private short heading; // raw value
private boolean true_airspeed; // 0 = indicated AS, 1 = true AS
private short airspeed; // in knots
private boolean airspeed_available;
@@ -98,7 +98,7 @@ public AirspeedHeadingMsg(ExtendedSquitter squitter) throws BadFormatException {
// heading available
heading_status_bit = (msg[1]&0x4)>0;
- heading = ((msg[1]&0x3)<<8 | msg[2]&0xFF) * 360./1024.;
+ heading = (short) (((msg[1]&0x3)<<8) | (msg[2]&0xFF));
true_airspeed = (msg[3]&0x80)>0;
airspeed = (short) (((msg[3]&0x7F)<<3 | msg[4]>>>5&0x07)-1);
@@ -205,13 +205,21 @@ public Integer getGeoMinusBaro() {
return geo_minus_baro;
}
+ /**
+ * @return raw heading field value (10 bit). Check {@link #hasHeadingStatusFlag()} to determine whether this value
+ * is valid.
+ */
+ public int getHeadingRaw() {
+ return heading;
+ }
+
/**
* @return heading in decimal degrees ([0, 360]). 0° = geographic north or null if no information is available.
* The latter can also be checked using {@link #hasHeadingStatusFlag()}.
*/
public Double getHeading() {
if (!heading_status_bit) return null;
- return heading;
+ return heading * 360. / 1024.;
}
/**
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusMsg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusMsg.java
new file mode 100644
index 0000000..d32d616
--- /dev/null
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusMsg.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of lib1090.
+ * Copyright (C) 2026 SeRo Systems GmbH
+ *
+ * lib1090 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * lib1090 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with de.serosystems.lib1090. If not, see .
+ */
+
+package de.serosystems.lib1090.msgs.adsb;
+
+/**
+ * Common API for ADS-B operational status messages.
+ */
+public interface OperationalStatusMsg {
+
+ /**
+ * @return whether 1090ES IN is available
+ */
+ boolean has1090ESIn();
+
+ /**
+ * @return the version number of the formats and protocols in use on the aircraft installation
+ */
+ byte getVersion();
+}
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV0Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV0Msg.java
index 2e15801..a56a868 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV0Msg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV0Msg.java
@@ -29,7 +29,7 @@
*
* @author Markus Fuchs (fuchs@opensky-network.org)
*/
-public class OperationalStatusV0Msg extends ExtendedSquitter implements Serializable {
+public class OperationalStatusV0Msg extends ExtendedSquitter implements Serializable, OperationalStatusMsg {
private static final long serialVersionUID = -8925123066831152922L;
@@ -106,6 +106,15 @@ public boolean hasOperationalCDTI() {
return (enroute_capabilities & 0x10) != 0;
}
+ /**
+ * @return whether 1090ES IN is available
+ * @see #hasOperationalCDTI() alias: the field has been renamed in V1
+ */
+ @Override
+ public boolean has1090ESIn() {
+ return hasOperationalCDTI();
+ }
+
/**
* the version number of the formats and protocols in use on the aircraft installation.
* 0: Conformant to DO-260/ED-102 and DO-242
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV1Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV1Msg.java
new file mode 100644
index 0000000..c8ba72d
--- /dev/null
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV1Msg.java
@@ -0,0 +1,76 @@
+/*
+ * This file is part of lib1090.
+ * Copyright (C) 2026 SeRo Systems GmbH
+ *
+ * lib1090 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * lib1090 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with de.serosystems.lib1090. If not, see .
+ */
+
+package de.serosystems.lib1090.msgs.adsb;
+
+import de.serosystems.lib1090.decoding.OperationalStatus;
+
+/**
+ * Common API for ADS-B operational status version 1 messages.
+ */
+public interface OperationalStatusV1Msg extends OperationalStatusMsg {
+
+ /**
+ * @return the subtype code, 0 for airborne operational status messages and 1 for surface operational status messages
+ */
+ byte getSubtypeCode();
+
+ /**
+ * @return the NIC supplement A to the format type code of position messages
+ */
+ boolean hasNICSupplementA();
+
+ /**
+ * @return the navigation accuracy for position messages; rather use getPositionUncertainty
+ */
+ byte getNACp();
+
+ /**
+ * Get the 95% horizontal accuracy bounds (EPU) derived from NACp value.
+ *
+ * @return the estimated position uncertainty according to the position NAC in meters (-1 for unknown)
+ */
+ default double getPositionUncertainty() {
+ return OperationalStatus.nacPtoEPU(getNACp());
+ }
+
+ /**
+ * @return the source integrity level (SIL)
+ */
+ byte getSIL();
+
+ /**
+ * @return whether TCAS Resolution Advisory (RA) is active
+ */
+ boolean hasTCASResolutionAdvisory();
+
+ /**
+ * @return whether the IDENT switch is active
+ */
+ boolean hasActiveIDENTSwitch();
+
+ /**
+ * @return whether ADS-B Transmitting Subsystem is receiving ATC services.
+ */
+ boolean hasReceivingATCServices();
+
+ /**
+ * @return 0 if horizontal reference direction is the true north, 1 if magnetic north
+ */
+ boolean getHorizontalReferenceDirection();
+}
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV2Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV2Msg.java
new file mode 100644
index 0000000..4620063
--- /dev/null
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV2Msg.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of lib1090.
+ * Copyright (C) 2026 SeRo Systems GmbH
+ *
+ * lib1090 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * lib1090 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with de.serosystems.lib1090. If not, see .
+ */
+
+package de.serosystems.lib1090.msgs.adsb;
+
+/**
+ * Common API for ADS-B operational status version 2 messages.
+ */
+public interface OperationalStatusV2Msg extends OperationalStatusV1Msg {
+
+ /**
+ * @return whether aircraft has an UAT receiver
+ */
+ boolean hasUATIn();
+
+ /**
+ * @return whether aircraft uses a single antenna or two
+ */
+ boolean hasSingleAntenna();
+
+ /**
+ * For interpretation see Table 2-65 in DO-260B
+ *
+ * @return system design assurance (see A.1.4.10.14 in RTCA DO-260B)
+ */
+ byte getSystemDesignAssurance();
+
+ /**
+ * DO-260B 2.2.3.2.7.2.14
+ *
+ * @return true if SIL (Source Integrity Level) is based on "per sample" probability, otherwise
+ * it's based on "per hour".
+ */
+ boolean hasSILSupplement();
+}
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV1Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV1Msg.java
index 0fa0dde..c261373 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV1Msg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV1Msg.java
@@ -32,14 +32,14 @@
* @author Matthias Schaefer (schaefer@sero-systems.de)
* @author Markus Fuchs (fuchs@opensky-network.org)
*/
-public class SurfaceOperationalStatusV1Msg extends ExtendedSquitter implements Serializable {
+public class SurfaceOperationalStatusV1Msg extends ExtendedSquitter implements Serializable, OperationalStatusV1Msg {
private static final long serialVersionUID = -3513411664476607448L;
+ private static final byte SUBTYPE_CODE = 1;
- private byte subtype_code;
protected int capability_class_code; // actually 16 bit unsigned
protected int operational_mode_code; // actually 16 bit unsigned
- private byte airplane_len_width; // only in subtype_code == 1 surface msgs
+ private byte airplane_len_width; // only for surface messages
private byte version;
private boolean nic_suppl; // may be passed to position messages
private byte nac_pos; // navigational accuracy category - position
@@ -89,10 +89,10 @@ public SurfaceOperationalStatusV1Msg(ExtendedSquitter squitter) throws BadFormat
byte[] msg = this.getMessage();
BitReader b = BitReader.forBigEndian(msg);
- subtype_code = b.readByte(6, 8);
- if (subtype_code > 1) { // currently only 0 and 1 specified, 2-7 are reserved
- throw new UnspecifiedFormatError("Operational status message subtype " + subtype_code + " reserved.");
- } else if (subtype_code != 1) {
+ byte subtypeCode = b.readByte(6, 8);
+ if (subtypeCode > 1) { // currently only 0 and 1 specified, 2-7 are reserved
+ throw new UnspecifiedFormatError("Operational status message subtype " + subtypeCode + " reserved.");
+ } else if (subtypeCode != SUBTYPE_CODE) {
throw new BadFormatException("Not surface operational status message");
}
@@ -119,8 +119,9 @@ public SurfaceOperationalStatusV1Msg(ExtendedSquitter squitter) throws BadFormat
* and 1 for surface operational status msgs; all other codes
* are "reserved"
*/
+ @Override
public byte getSubtypeCode() {
- return subtype_code;
+ return SUBTYPE_CODE;
}
/**
@@ -166,6 +167,13 @@ public boolean hasReceivingATCServices() {
}
+ /**
+ * @return raw aircraft vehicle length and width code (4 bit)
+ */
+ public byte getAircraftVehicleLengthAndWidthCode() {
+ return airplane_len_width;
+ }
+
/**
* According to DO-260B Table 2-74. Compatible with ADS-B version 1 and 2
*
@@ -247,7 +255,6 @@ public boolean getHorizontalReferenceDirection() {
@Override
public String toString() {
return "SurfaceOperationalStatusV1Msg{" +
- "subtype_code=" + subtype_code +
", capability_class_code=" + capability_class_code +
", operational_mode_code=" + operational_mode_code +
", airplane_len_width=" + airplane_len_width +
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV2Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV2Msg.java
index 5510eb1..aef2ef2 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV2Msg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV2Msg.java
@@ -27,7 +27,7 @@
/**
* @author Markus Fuchs (fuchs@opensky-network.org)
*/
-public class SurfaceOperationalStatusV2Msg extends SurfaceOperationalStatusV1Msg implements Serializable {
+public class SurfaceOperationalStatusV2Msg extends SurfaceOperationalStatusV1Msg implements Serializable, OperationalStatusV2Msg {
private static final long serialVersionUID = 5774750859726557576L;
@@ -77,6 +77,7 @@ public SurfaceOperationalStatusV2Msg(ExtendedSquitter squitter) throws BadFormat
/**
* @return whether aircraft has an UAT receiver
*/
+ @Override
public boolean hasUATIn() {
return (capability_class_code & 0x100) != 0;
}
@@ -98,6 +99,7 @@ public boolean getNICSupplementC() {
/**
* @return whether aircraft uses a single antenna or two
*/
+ @Override
public boolean hasSingleAntenna() {
return (operational_mode_code & 0x400) != 0;
}
@@ -107,6 +109,7 @@ public boolean hasSingleAntenna() {
*
* @return system design assurance (see A.1.4.10.14 in RTCA DO-260B)
*/
+ @Override
public byte getSystemDesignAssurance() {
return (byte) ((operational_mode_code & 0x300) >>> 8);
}
@@ -131,7 +134,7 @@ public boolean hasPositionOffsetApplied() {
*
values are measured from the longitudinal center line (=roll axis) of the aircraft
* values are given in meters
* positive values mean "toward left wing tip"
- * negative values mean "toward right wind tip
+ * negative values mean "toward right wind tip"
* values have a resolution of 2m
* values are capped at 6m
* {@code null} means "no data"
@@ -171,6 +174,7 @@ public Integer getLongitudinalAxisGPSAntennaOffset() {
* @return true if SIL (Source Integrity Level) is based on "per sample" probability, otherwise
* it's based on "per hour".
*/
+ @Override
public boolean hasSILSupplement() {
return sil_supplement;
}
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfacePositionV0Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfacePositionV0Msg.java
index c94f0fe..8507e1f 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfacePositionV0Msg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/SurfacePositionV0Msg.java
@@ -159,6 +159,13 @@ public byte getSIL() {
return (byte) (getFormatTypeCode() == 0 ? 0 : 2);
}
+ /**
+ * @return the raw movement field value (7 bit)
+ */
+ public byte getMovementRaw() {
+ return movement;
+ }
+
/**
* @return whether ground speed information is available
*/
@@ -182,6 +189,13 @@ public Double getGroundSpeedResolution() {
return SurfacePosition.groundSpeedResolution(movement);
}
+ /**
+ * @return the raw heading field value (7 bit)
+ */
+ public byte getHeadingRaw() {
+ return ground_track;
+ }
+
/**
* @return whether valid heading information is available
*/
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusMsg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusMsg.java
index 8d792e3..a2e62b9 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusMsg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusMsg.java
@@ -59,6 +59,12 @@ public interface TargetStateAndStatusMsg {
*/
Float getSelectedHeading();
+ /**
+ * Get raw selected heading including sign bit.
+ * @return the raw selected heading (named. target heading track in V1) field value including sign bit
+ */
+ int getSelectedHeadingRaw();
+
/**
* @return the navigation accuracy category for position
*/
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusV1Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusV1Msg.java
index 67f0e90..45b0b30 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusV1Msg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusV1Msg.java
@@ -76,7 +76,7 @@ public TargetStateAndStatusV1Msg(byte[] raw_message) throws BadFormatException,
*/
public TargetStateAndStatusV1Msg(ExtendedSquitter squitter) throws BadFormatException, UnspecifiedFormatError {
super(squitter);
- setType(subtype.ADSB_TARGET_STATE_AND_STATUS);
+ setType(subtype.ADSB_TARGET_STATE_AND_STATUS_V1);
if (getFormatTypeCode() != 29) {
throw new BadFormatException("Target state and status messages must have typecode 29.");
@@ -143,6 +143,11 @@ public Float getSelectedHeading() {
return target_heading_track_angle * (360.f / 512);
}
+ @Override
+ public int getSelectedHeadingRaw() {
+ return target_heading_track_angle;
+ }
+
@Override
public byte getNACp() {
return nac_p;
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusV2Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusV2Msg.java
index 07f53dd..7d6f6d1 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusV2Msg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsb/TargetStateAndStatusV2Msg.java
@@ -79,7 +79,7 @@ public TargetStateAndStatusV2Msg(byte[] raw_message) throws BadFormatException,
*/
public TargetStateAndStatusV2Msg(ExtendedSquitter squitter) throws BadFormatException, UnspecifiedFormatError {
super(squitter);
- setType(subtype.ADSB_TARGET_STATE_AND_STATUS);
+ setType(subtype.ADSB_TARGET_STATE_AND_STATUS_V2);
if (getFormatTypeCode() != 29) {
throw new BadFormatException("Target state and status messages must have typecode 29.");
@@ -198,6 +198,11 @@ public Float getSelectedHeading() {
return selected_heading * (180.f / 256) + (selected_heading_sign ? 180F : 0F);
}
+ @Override
+ public int getSelectedHeadingRaw() {
+ return ((selected_heading_sign ? 1 : 0) << 8) | selected_heading;
+ }
+
@Override
public byte getNACp() {
return nac_p;
diff --git a/src/main/java/de/serosystems/lib1090/msgs/adsr/SurfaceOperationalStatusV1Msg.java b/src/main/java/de/serosystems/lib1090/msgs/adsr/SurfaceOperationalStatusV1Msg.java
index 963c49d..56e48f6 100644
--- a/src/main/java/de/serosystems/lib1090/msgs/adsr/SurfaceOperationalStatusV1Msg.java
+++ b/src/main/java/de/serosystems/lib1090/msgs/adsr/SurfaceOperationalStatusV1Msg.java
@@ -198,6 +198,13 @@ public byte getGPSAntennaOffset() {
return (byte) (operational_mode_code&0xFF);
}
+ /**
+ * @return raw aircraft vehicle length and width code (4 bit)
+ */
+ public byte getAircraftVehicleLengthAndWidthCode() {
+ return airplane_len_width;
+ }
+
/**
* According to DO-260B Table 2-74. Compatible with ADS-R version 1 and 2
* @return the airplane's length in meters; -1 for unknown
diff --git a/src/test/java/de/serosystems/lib1090/StatefulModeSDecoderTest.java b/src/test/java/de/serosystems/lib1090/StatefulModeSDecoderTest.java
index 7a99ba4..be95513 100644
--- a/src/test/java/de/serosystems/lib1090/StatefulModeSDecoderTest.java
+++ b/src/test/java/de/serosystems/lib1090/StatefulModeSDecoderTest.java
@@ -52,7 +52,8 @@ public void tssV0Me11Set_shouldNotDecode() throws UnspecifiedFormatError, BadFor
final ModeSDownlinkMsg reply = decoder.decode(TargetStateAndStatusV2MsgTest.TSS_WITH_ME11_BIT_SET, 0L);
assertEquals(ModeSDownlinkMsg.subtype.EXTENDED_SQUITTER, reply.getType());
- assertNotEquals(ModeSDownlinkMsg.subtype.ADSB_TARGET_STATE_AND_STATUS, reply.getType());
+ assertNotEquals(ModeSDownlinkMsg.subtype.ADSB_TARGET_STATE_AND_STATUS_V1, reply.getType());
+ assertNotEquals(ModeSDownlinkMsg.subtype.ADSB_TARGET_STATE_AND_STATUS_V2, reply.getType());
}
@Test
@@ -63,7 +64,7 @@ public void tssV2ME11Set_shouldDecode() throws UnspecifiedFormatError, BadFormat
// decode message with ME bit 11 set
final ModeSDownlinkMsg reply = decoder.decode(TargetStateAndStatusV2MsgTest.TSS_WITH_ME11_BIT_SET, 0L);
- assertEquals(ModeSDownlinkMsg.subtype.ADSB_TARGET_STATE_AND_STATUS, reply.getType());
+ assertEquals(ModeSDownlinkMsg.subtype.ADSB_TARGET_STATE_AND_STATUS_V2, reply.getType());
TargetStateAndStatusV2Msg tss = (TargetStateAndStatusV2Msg) reply;
@@ -78,7 +79,7 @@ public void tssV1_shouldDecode() throws UnspecifiedFormatError, BadFormatExcepti
final ModeSDownlinkMsg reply = decoder.decode(TargetStateAndStatusV1MsgTest.TSS_V1, 0L);
- assertEquals(ModeSDownlinkMsg.subtype.ADSB_TARGET_STATE_AND_STATUS, reply.getType());
+ assertEquals(ModeSDownlinkMsg.subtype.ADSB_TARGET_STATE_AND_STATUS_V1, reply.getType());
assertTrue(reply instanceof TargetStateAndStatusV1Msg);
diff --git a/src/test/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV1MsgTest.java b/src/test/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV1MsgTest.java
index 5188d42..beabd7e 100644
--- a/src/test/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV1MsgTest.java
+++ b/src/test/java/de/serosystems/lib1090/msgs/adsb/AirborneOperationalStatusV1MsgTest.java
@@ -46,6 +46,8 @@ public void testValidVersion1Message() throws Exception {
// ME = F8 00 02 00 49 29 00
byte[] msg = Tools.hexStringToByteArray("8D000000F8000200492900000000");
AirborneOperationalStatusV1Msg status = new AirborneOperationalStatusV1Msg(msg);
+ assertTrue(status instanceof OperationalStatusV1Msg);
+ assertEquals(0, status.getSubtypeCode());
assertEquals(1, status.getVersion());
assertEquals(9, status.getNACp());
}
diff --git a/src/test/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusMsgTest.java b/src/test/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusMsgTest.java
index ab69c5f..a844da5 100644
--- a/src/test/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusMsgTest.java
+++ b/src/test/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusMsgTest.java
@@ -55,14 +55,19 @@ public class OperationalStatusMsgTest {
// parity (correct, not test here)
"209514";
+ // Surface operational status message with ADS-B version 2
+ public static final String S_OPSTAT_V2 = "8D000000F9000000004000000000";
+
@Test
public void testDecodeAirborneOpstat() throws UnspecifiedFormatError, BadFormatException {
final AirborneOperationalStatusV2Msg opstat = new AirborneOperationalStatusV2Msg(A_OPSTAT_V2);
+ assertTrue(opstat instanceof OperationalStatusV2Msg);
assertEquals("4d0131", opstat.getAddress().getHexAddress());
assertEquals(31, opstat.getFormatTypeCode());
assertEquals(2, opstat.getVersion());
+ assertFalse(opstat.has1090ESIn());
assertFalse(opstat.hasNICSupplementA());
assertEquals(9, opstat.getNACp());
@@ -71,4 +76,17 @@ public void testDecodeAirborneOpstat() throws UnspecifiedFormatError, BadFormatE
assertTrue(opstat.getBarometricAltitudeIntegrityCode());
assertFalse(opstat.getHorizontalReferenceDirection());
}
+
+ @Test
+ public void testDecodeSurfaceOpstat() throws UnspecifiedFormatError, BadFormatException {
+ final SurfaceOperationalStatusV2Msg opstat = new SurfaceOperationalStatusV2Msg(S_OPSTAT_V2);
+ assertTrue(opstat instanceof OperationalStatusV2Msg);
+
+ assertEquals(2, opstat.getVersion());
+ assertFalse(opstat.has1090ESIn());
+ assertFalse(opstat.hasUATIn());
+ assertFalse(opstat.hasSingleAntenna());
+ assertEquals(0, opstat.getSystemDesignAssurance());
+ assertFalse(opstat.hasSILSupplement());
+ }
}
diff --git a/src/test/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV0MsgTest.java b/src/test/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV0MsgTest.java
index 06e3475..d230980 100644
--- a/src/test/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV0MsgTest.java
+++ b/src/test/java/de/serosystems/lib1090/msgs/adsb/OperationalStatusV0MsgTest.java
@@ -32,8 +32,10 @@ class OperationalStatusV0MsgTest {
public void testValidEnrouteCapabilities() throws Exception {
byte[] msg = Tools.hexStringToByteArray("8D000000F8300000000000000000");
OperationalStatusV0Msg status = new OperationalStatusV0Msg(msg);
+ assertTrue(status instanceof OperationalStatusMsg);
assertFalse(status.hasOperationalTCAS());
assertTrue(status.hasOperationalCDTI());
+ assertTrue(status.has1090ESIn());
}
@Test
diff --git a/src/test/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV1MsgTest.java b/src/test/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV1MsgTest.java
index d48432a..2d6f5ac 100644
--- a/src/test/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV1MsgTest.java
+++ b/src/test/java/de/serosystems/lib1090/msgs/adsb/SurfaceOperationalStatusV1MsgTest.java
@@ -31,6 +31,7 @@ class SurfaceOperationalStatusV1MsgTest {
public void testValidVersion1Message() throws Exception {
byte[] msg = Tools.hexStringToByteArray("8D000000F9000000002000000000");
SurfaceOperationalStatusV1Msg status = new SurfaceOperationalStatusV1Msg(msg);
+ assertEquals(1, status.getSubtypeCode());
assertEquals(1, status.getVersion());
}