Skip to content

Commit 48b9f12

Browse files
committed
Improve global position updating
1 parent eeefc19 commit 48b9f12

File tree

7 files changed

+120
-76
lines changed

7 files changed

+120
-76
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
GROUP=aero.t2s
2-
VERSION_NAME=0.2.5-SNAPSHOT
2+
VERSION_NAME=0.2.6-SNAPSHOT
33

44
POM_ARTIFACT_ID=mode-s
55
POM_NAME=Mode-S/ADS-B (1090Mhz)

src/main/java/aero/t2s/modes/CprPosition.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package aero.t2s.modes;
22

3+
import java.time.Instant;
4+
35
public class CprPosition {
46
private double lat;
57
private double lon;
68
private boolean valid;
7-
private int time;
9+
private long time;
810

911
public CprPosition() {
1012
this.lat = 0.0;
@@ -14,9 +16,11 @@ public CprPosition() {
1416
public CprPosition(double lat, double lon) {
1517
setLatLon(lat ,lon);
1618
}
19+
1720
public void setLatLon(double lat, double lon) {
1821
this.lat = lat;
1922
this.lon = lon;
23+
this.time = Instant.now().toEpochMilli();
2024
this.valid = true;
2125
}
2226

@@ -36,15 +40,19 @@ public double getLon() {
3640
return lon;
3741
}
3842

39-
public void setTime(int time) {
43+
public void setTime(long time) {
4044
this.time = time;
4145
}
4246

43-
public int getTime() {
47+
public long getTime() {
4448
return time;
4549
}
4650

4751
public boolean isValid() {
4852
return valid;
4953
}
54+
55+
public boolean isExpired() {
56+
return time < Instant.now().minusSeconds(10).toEpochMilli();
57+
}
5058
}

src/main/java/aero/t2s/modes/ModeSHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package aero.t2s.modes;
22

33
import aero.t2s.modes.decoder.df.DownlinkFormat;
4+
import aero.t2s.modes.decoder.df.df17.AirbornePosition;
45

56
import java.util.function.Consumer;
67

@@ -42,10 +43,10 @@ protected short[] toData(final String input) throws EmptyMessageException, ModeA
4243
}
4344

4445
public void start() {
45-
46+
AirbornePosition.start();
4647
}
4748

4849
public void stop() {
49-
50+
AirbornePosition.stop();
5051
}
5152
}

src/main/java/aero/t2s/modes/Track.java

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ public class Track {
99
private String icao;
1010
private String callsign;
1111
private Altitude altitude = new Altitude();
12-
private boolean cprEvenValid = false;
13-
private CprPosition cprEven = new CprPosition();
14-
private CprPosition cprOdd = new CprPosition();
1512
private double lat;
1613
private double lon;
1714
private boolean positionAvailable = false;
@@ -199,22 +196,6 @@ public double getLon() {
199196
return lon;
200197
}
201198

202-
public CprPosition getCprEven() {
203-
return cprEven;
204-
}
205-
206-
public void setCprEven(CprPosition cprEven) {
207-
this.cprEven = cprEven;
208-
}
209-
210-
public CprPosition getCprOdd() {
211-
return cprOdd;
212-
}
213-
214-
public void setCprOdd(CprPosition cprOdd) {
215-
this.cprOdd = cprOdd;
216-
}
217-
218199
public Version getVersion() {
219200
return version;
220201
}

src/main/java/aero/t2s/modes/decoder/Decoder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public DownlinkFormat decode(short[] data) throws UnknownDownlinkFormatException
5050
df = new DF16(data);
5151
break;
5252
case 17:
53-
df = new DF17(data, originLat, originLon);
53+
df = new DF17(data);
5454
break;
5555
case 18:
5656
df = new DF18(data);

src/main/java/aero/t2s/modes/decoder/df/DF17.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,10 @@
44
import aero.t2s.modes.decoder.df.df17.*;
55

66
public class DF17 extends DownlinkFormat {
7-
private final double originLat;
8-
private final double originLon;
9-
107
private ExtendedSquitter extendedSquitter;
118

12-
public DF17(short[] data, double originLat, double originLon) {
9+
public DF17(short[] data) {
1310
super(data, IcaoAddress.FROM_MESSAGE);
14-
this.originLat = originLat;
15-
this.originLon = originLon;
1611
}
1712

1813
@Override
@@ -34,7 +29,7 @@ public DF17 decode() {
3429
case 20:
3530
case 21:
3631
case 22:
37-
extendedSquitter = new AirbornePosition(data, originLat, originLon);
32+
extendedSquitter = new AirbornePosition(data, getIcao());
3833
break;
3934
case 1:
4035
case 2:

src/main/java/aero/t2s/modes/decoder/df/df17/AirbornePosition.java

Lines changed: 102 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
import aero.t2s.modes.Track;
44
import aero.t2s.modes.CprPosition;
55
import aero.t2s.modes.constants.*;
6+
import aero.t2s.modes.decoder.Common;
67
import aero.t2s.modes.registers.Register05;
78
import aero.t2s.modes.registers.Register05V0;
89
import aero.t2s.modes.registers.Register05V2;
910

10-
public class AirbornePosition extends ExtendedSquitter {
11-
private final double originLat;
12-
private final double originLon;
11+
import java.util.*;
1312

13+
public class AirbornePosition extends ExtendedSquitter {
14+
private final String address;
1415
private SurveillanceStatus surveillanceStatus;
1516
private int singleAntennaFlag;
1617

@@ -19,16 +20,14 @@ public class AirbornePosition extends ExtendedSquitter {
1920

2021
private boolean positionAvailable;
2122

22-
private CprPosition cprEven = new CprPosition();
23-
private CprPosition cprOdd = new CprPosition();
24-
2523
private double lat;
2624
private double lon;
25+
private static Map<String, PositionUpdate> cache = new HashMap<>();
26+
private static Timer cacheCleanup;
2727

28-
public AirbornePosition(short[] data, final double originLat, final double originLon) {
28+
public AirbornePosition(short[] data, String address) {
2929
super(data);
30-
this.originLat = originLat;
31-
this.originLon = originLon;
30+
this.address = address;
3231
}
3332

3433
@Override
@@ -59,14 +58,38 @@ public AirbornePosition decode() {
5958
cprLon = cprLon | (data[9] << 8);
6059
cprLon = cprLon | data[10];
6160

61+
62+
if (!cache.containsKey(address)) {
63+
if (!isCprEven) {
64+
return this;
65+
}
66+
67+
synchronized (cache) {
68+
cache.putIfAbsent(address, new PositionUpdate(
69+
new CprPosition(cprLat / (double)(1 << 17), cprLon / (double)(1 << 17))
70+
));
71+
}
72+
}
73+
74+
PositionUpdate positionUpdate;
75+
synchronized (cache) {
76+
positionUpdate = cache.get(address);
77+
}
6278
if (isCprEven) {
63-
this.cprEven.setLatLon(cprLat/(double)(1 << 17), cprLon/(double)(1 << 17));
79+
positionUpdate.setEven(new CprPosition(cprLat / (double) (1 << 17), cprLon / (double) (1 << 17)));
80+
} else {
81+
positionUpdate.setOdd(new CprPosition(cprLat, cprLon));
6482
}
65-
else {
66-
this.cprOdd.setLatLon(cprLat, cprLon);
83+
84+
if (positionUpdate.isComplete()) {
85+
calculateGlobal(positionUpdate.even, positionUpdate.odd);
86+
} else if (positionUpdate.isPreviousPositionAvailable() && positionUpdate.isPreviousPositionAvailable()) {
87+
calculateLocal(positionUpdate.odd, true, positionUpdate.previousLat, positionUpdate.previousLon);
6788
}
6889

69-
calculatePosition(isCprEven);
90+
if (positionAvailable) {
91+
positionUpdate.setPreviousPosition(this.lat, this.lon);
92+
}
7093

7194
return this;
7295
}
@@ -77,9 +100,9 @@ public void apply(Track track) {
77100
track.setSpi(surveillanceStatus == SurveillanceStatus.SPI);
78101
track.setTempAlert(surveillanceStatus == SurveillanceStatus.TEMPORARY_ALERT);
79102
track.setEmergency(surveillanceStatus == SurveillanceStatus.PERMANENT_ALERT);
80-
track.setCprEven(cprEven);
81-
track.setCprOdd(cprOdd);
82-
track.setLatLon(lat, lon);
103+
if (positionAvailable) {
104+
track.setLatLon(lat, lon);
105+
}
83106

84107
if (versionChanged(track)) {
85108
switch (track.getVersion()) {
@@ -130,14 +153,6 @@ public BarometricAltitudeIntegrityCode getNICbaro() {
130153
}
131154
}
132155

133-
public double getOriginLat() {
134-
return originLat;
135-
}
136-
137-
public double getOriginLon() {
138-
return originLon;
139-
}
140-
141156
public SurveillanceStatus getSurveillanceStatus() {
142157
return surveillanceStatus;
143158
}
@@ -215,25 +230,6 @@ private AltitudeSource determineAltitudeSource() {
215230
return AltitudeSource.GNSS_HAE;
216231
}
217232

218-
private void calculatePosition(boolean isEven) {
219-
if (!positionAvailable) {
220-
//TODO Could be other cases where we need to do global calculation, such as too much time elapsed since last position update
221-
calculateGlobal(cprEven, cprOdd);
222-
positionAvailable = true;
223-
}
224-
else {
225-
if (isEven) {
226-
if (cprOdd.isValid()) {
227-
calculateLocal(cprEven, false, this.lat, this.lon);
228-
}
229-
} else {
230-
if (cprEven.isValid()) {
231-
calculateLocal(cprOdd, true, this.lat, this.lon);
232-
}
233-
}
234-
}
235-
}
236-
237233
private void calculateLocal(CprPosition cpr, boolean isOdd, double previousLat, double previousLon) {
238234

239235
double dlat = isOdd ? 360.0 / 59.0 : 360.0 / 60.0;
@@ -298,6 +294,7 @@ private void calculateGlobal(CprPosition cprEven, CprPosition cprOdd) {
298294
//TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range,
299295
this.lat = lat;
300296
this.lon = lon;
297+
this.positionAvailable = true;
301298
}
302299
private double cprN(double lat, double isOdd) {
303300
double nl = NL(lat) - isOdd;
@@ -323,4 +320,66 @@ private int calculateAltitude(short[] data, int typeCode) {
323320

324321
return (n * qBit) - 1000;
325322
}
323+
324+
public static void start() {
325+
AirbornePosition.cache.clear();
326+
AirbornePosition.cacheCleanup.schedule(new TimerTask() {
327+
@Override
328+
public void run() {
329+
List<String> expired = new LinkedList<>();
330+
331+
synchronized (cache) {
332+
cache.entrySet().stream().filter(entry -> entry.getValue().isExpired()).forEach(entry -> expired.add(entry.getKey()));
333+
expired.forEach(cache::remove);
334+
}
335+
}
336+
}, 0, 10_000);
337+
}
338+
339+
public static void stop() {
340+
AirbornePosition.cacheCleanup.cancel();
341+
AirbornePosition.cacheCleanup = null;
342+
343+
AirbornePosition.cache.clear();
344+
}
345+
346+
class PositionUpdate {
347+
private CprPosition even;
348+
private CprPosition odd;
349+
350+
351+
private boolean previousPositionAvailable = false;
352+
private double previousLat;
353+
private double previousLon;
354+
355+
public PositionUpdate(CprPosition even) {
356+
this.even = even;
357+
}
358+
359+
public void setEven(CprPosition even) {
360+
this.even = even;
361+
this.odd = null;
362+
}
363+
364+
public void setOdd(CprPosition odd) {
365+
this.odd = odd;
366+
}
367+
368+
public void setPreviousPosition(double lat, double lon) {
369+
this.previousLat = lat;
370+
this.previousLon = lon;
371+
}
372+
373+
public boolean isPreviousPositionAvailable() {
374+
return this.previousPositionAvailable;
375+
}
376+
377+
public boolean isComplete() {
378+
return even != null && odd != null;
379+
}
380+
381+
public boolean isExpired() {
382+
return even.isExpired() || odd.isExpired();
383+
}
384+
}
326385
}

0 commit comments

Comments
 (0)