-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathAirspeedHeadingMsg.java
More file actions
266 lines (229 loc) · 8.07 KB
/
AirspeedHeadingMsg.java
File metadata and controls
266 lines (229 loc) · 8.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
package org.opensky.libadsb.msgs;
import org.opensky.libadsb.exceptions.BadFormatException;
import java.io.Serializable;
/*
* This file is part of org.opensky.libadsb.
*
* org.opensky.libadsb 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.
*
* org.opensky.libadsb 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 org.opensky.libadsb. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Decoder for ADS-B airspeed and heading messages
* @author Matthias Schäfer (schaefer@opensky-network.org)
*/
public class AirspeedHeadingMsg extends ExtendedSquitter implements Serializable {
private static final long serialVersionUID = -7072061713588878404L;
private byte msg_subtype;
private boolean intent_change;
private boolean ifr_capability;
private byte navigation_accuracy_category;
private boolean heading_status_bit;
private double heading; // in degrees
private boolean true_airspeed; // 0 = indicated AS, 1 = true AS
private short airspeed; // in knots
private boolean airspeed_available;
private boolean vertical_source; // 0 = geometric, 1 = barometric
private boolean vertical_rate_down; // 0 = up, 1 = down
private short vertical_rate; // in ft/s
private boolean vertical_rate_info_available;
private int geo_minus_baro; // in ft
private boolean geo_minus_baro_available;
/** protected no-arg constructor e.g. for serialization with Kryo **/
protected AirspeedHeadingMsg() { }
/**
* @param raw_message raw ADS-B airspeed and heading message as hex string
* @throws BadFormatException if message has wrong format
*/
public AirspeedHeadingMsg(String raw_message) throws BadFormatException {
this(new ExtendedSquitter(raw_message));
}
/**
* @param raw_message raw ADS-B airspeed and heading message as byte array
* @throws BadFormatException if message has wrong format
*/
public AirspeedHeadingMsg(byte[] raw_message) throws BadFormatException {
this(new ExtendedSquitter(raw_message));
}
/**
* @param squitter extended squitter containing the airspeed and heading msg
* @throws BadFormatException if message has wrong format
*/
public AirspeedHeadingMsg(ExtendedSquitter squitter) throws BadFormatException {
super(squitter);
setType(subtype.ADSB_AIRSPEED);
if (this.getFormatTypeCode() != 19) {
throw new BadFormatException("Airspeed and heading messages must have typecode 19.");
}
byte[] msg = this.getMessage();
msg_subtype = (byte) (msg[0]&0x7);
if (msg_subtype != 3 && msg_subtype != 4) {
throw new BadFormatException("Airspeed and heading messages have subtype 3 or 4.");
}
intent_change = (msg[1]&0x80)>0;
ifr_capability = (msg[1]&0x40)>0;
navigation_accuracy_category = (byte) ((msg[1]>>>3)&0x7);
// check this later
vertical_rate_info_available = true;
geo_minus_baro_available = true;
// heading available in ADS-B version 1+, indicates true/magnetic north for version 0
heading_status_bit = (msg[1]&0x4)>0;
heading = ((msg[1]&0x3)<<8 | msg[2]&0xFF) * 360.0/1024.0;
true_airspeed = (msg[3]&0x80)>0;
airspeed = (short) (((msg[3]&0x7F)<<3 | msg[4]>>>5&0x07)-1);
if (airspeed != -1) {
airspeed_available = true;
if (msg_subtype == 4) airspeed<<=2;
}
vertical_source = (msg[4]&0x10)>0;
vertical_rate_down = (msg[4]&0x08)>0;
int raw_vr = ((msg[4]&0x07)<<6 | msg[5]>>>2&0x3F);
if (raw_vr == 0) vertical_rate_info_available = false;
else vertical_rate = (short) ((raw_vr-1)<<6);
int raw_gmb = msg[6]&0x7F;
if (raw_gmb == 0) geo_minus_baro_available = false;
else geo_minus_baro = (short) ((raw_gmb-1)*25);
if ((msg[6]&0x80)>0) geo_minus_baro *= -1;
}
/**
* For ADS-B version 1 and 2 this must be checked before retrieving heading information.
*
* @return Depending on the ADS-B version, different interpretations:
* <ul>
* <li><strong>Version 0</strong> the flag indicates whether heading is relative to magnetic north (true) or
* true north (false)</li>
* <li><strong>Version 1+</strong> the flag indicates whether heading information is available or not</li>
* </ul>
*/
public boolean hasHeadingStatusFlag() {
return heading_status_bit;
}
/**
* Must be checked before accessing airspeed!
*
* @return whether airspeed info is available
*/
public boolean hasAirspeedInfo() {
return airspeed_available;
}
/**
* Must be checked before accessing vertical rate!
*
* @return whether vertical rate info is available
*/
public boolean hasVerticalRateInfo() {
return vertical_rate_info_available;
}
/**
* Must be checked before accessing geo minus baro!
*
* @return whether geo-baro difference info is available
*/
public boolean hasGeoMinusBaroInfo() {
return geo_minus_baro_available;
}
/**
* @return If supersonic, velocity has only 4 kts accuracy, otherwise 1 kt
*/
public boolean isSupersonic() {
return msg_subtype == 4;
}
/**
* @return true, if aircraft wants to change altitude for instance
*/
public boolean hasChangeIntent() {
return intent_change;
}
/**
* Note: only in ADS-B version 0 and 1 transponders!!
* @return true, iff aircraft has equipage class A1 or higher
*/
public boolean hasIFRCapability() {
return ifr_capability;
}
/**
* The 95% accuracy for horizontal velocity. We interpret the coding according to
* DO-260B Table 2-22 for all ADS-B versions.
* @return Navigation Accuracy Category for velocity according to RTCA DO-260B 2.2.3.2.6.1.5 in m/s, -1 means
* "unknown" or >10m
*/
public double getNACv() {
switch(navigation_accuracy_category) {
case 1:
return 10;
case 2:
return 3;
case 3:
return 1;
case 4:
return 0.3F;
default:
return -1;
}
}
/**
* @return airspeed in knots or null if information is not available. The latter can also be checked using
* {@link #hasAirspeedInfo()}.
*/
public Integer getAirspeed() {
if (!airspeed_available) return null;
return (int) airspeed;
}
/**
* @return whether altitude is derived by barometric sensor or GNSS
*/
public boolean isBarometricVerticalSpeed() {
return vertical_source;
}
/**
* @return vertical rate in feet/min (negative value means descending) or null if information is not available. The
* latter can also be checked with {@link #hasVerticalRateInfo()}.
*/
public Integer getVerticalRate() {
if (!vertical_rate_info_available) return null;
return (vertical_rate_down ? -vertical_rate : vertical_rate);
}
/**
* @return difference between barometric and geometric altitude in feet or null if no information is available.
* The latter can also be checked using {@link #hasGeoMinusBaroInfo()}.
*/
public Integer getGeoMinusBaro() {
if (!geo_minus_baro_available) return null;
return geo_minus_baro;
}
/**
* @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 true if airspeed is true airspeed, false if airspeed is indicated airspeed
*/
public boolean isTrueAirspeed() {
return true_airspeed;
}
public String toString() {
String ret = super.toString()+"\n"+
"Airspeed and heading:\n";
try { ret += "\tAirspeed:\t"+getAirspeed()+" m/s\n"; }
catch (Exception e) { ret += "\tAirspeed:\t\tnot available\n"; }
ret += "\tAirspeed Type:\t\t"+(isTrueAirspeed() ? "true" : "indicated")+"\n";
try { ret += "\tHeading\t\t\t\t"+getHeading()+"\n"; }
catch (Exception e) { ret += "\tHeading\t\t\t\tnot available\n"; }
try { ret += "\tVertical rate:\t\t\t"+getVerticalRate(); }
catch (Exception e) { ret += "\tVertical rate:\t\t\tnot available"; }
return ret;
}
}