Skip to content

Commit 684e1f9

Browse files
committed
Add sanity checks and additional tests that exercise both Global and Local position updates, both Airborne and Surface.
1 parent b94606b commit 684e1f9

File tree

4 files changed

+56
-29
lines changed

4 files changed

+56
-29
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ Most features of the DF17/18 protocol have been implemented, some message lack s
3434
| 2 | Aircraft Identification || |
3535
| 3 | Aircraft Identification || |
3636
| 4 | Aircraft Identification || |
37-
| 5 | Surface Position | | Not implemented yet |
38-
| 6 | Surface Position | | Not implemented yet |
39-
| 7 | Surface Position | | Not implemented yet |
40-
| 8 | Surface Position | | Not implemented yet |
37+
| 5 | Surface Position | | |
38+
| 6 | Surface Position | | |
39+
| 7 | Surface Position | | |
40+
| 8 | Surface Position | | |
4141
| 9 | Airborne Position || |
4242
| 10 | Airborne Position || |
4343
| 11 | Airborne Position || |

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

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package aero.t2s.modes.decoder.df.df17;
22

33
import aero.t2s.modes.CprPosition;
4+
import org.slf4j.LoggerFactory;
5+
46
import java.util.*;
57

68
public class PositionUpdate {
7-
private static double originLat; // Origin is passed-in as a command-line argument as an indication of where the receiver is located
9+
private static double originLat; // Origin is passed-in as a command-line argument as an indication of where the receiver is located
810
private static double originLon;
911

10-
private static double receiverLat; // Receiver position is calculated based on data received
12+
private static final double updateThreshold = 0.5d; // Positions which move between updates by more than this (in degrees) should be rejected
13+
private static final double receiverRange = 4.0d; // Positions further away from the receiver than this (in degrees) should be rejected
14+
private static double receiverLat; // Receiver position is calculated based on data received
1115
private static double receiverLon;
12-
private static double receiverSumLat; // Running-total of valid positions that can be used to calculate the average receiver position
16+
private static double receiverSumLat; // Running-total of valid positions that can be used to calculate the average receiver position
1317
private static double receiverSumLon;
14-
private static double receiverSumCount = 0; // number of valid positions received that are used to calculate the average
18+
private static double receiverSumCount = 0; // number of valid positions received that are used to calculate the average
1519

1620
private static Map<String, PositionUpdate> cache = new HashMap<>();
1721
private static Timer cacheCleanup;
@@ -70,7 +74,14 @@ private CprPosition calculate(boolean isCprEven, CprPosition newCpr) {
7074
}
7175

7276
if (current != null && current.isValid()) {
73-
previous = current;
77+
// Sanity Check: Is the position just received too far away from the receiver position?
78+
if ((Math.abs(current.getLat() - receiverLat) > receiverRange) || (Math.abs(current.getLon() - receiverLon) > receiverRange)) {
79+
LoggerFactory.getLogger(getClass()).info("Position Update discarded due to outside receiver range.");
80+
current = null;
81+
}
82+
else {
83+
previous = current;
84+
}
7485
}
7586

7687
return current;
@@ -95,29 +106,20 @@ private void calculateLocal(boolean isOdd) {
95106
double newLon = dLon * (m + cpr.getLon());
96107

97108
cpr.setZones(j, m);
98-
if ((j != otherCpr.getLatZone()) || (m != otherCpr.getLonZone())) {
99-
// The new frame is in a different CPR zone
100-
if (isOdd) {
101-
//even = null; // Keep the current odd frame but discard the previous even frame
102-
} else {
103-
//odd = null;
104-
}
105-
//previous = null;
106-
//current = null;
107-
//return;
108-
}
109109

110110
current = new CprPosition(newLat, newLon, cpr.getSurface());
111111
if (current.getSurface()) {
112112
validateSurface(current);
113113
}
114114

115-
// TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range
116-
// TODO Should be a sanity-check here to see if the calculated movement since the last update is too far
115+
if ((Math.abs(current.getLat() - previous.getLat()) > updateThreshold) || (Math.abs(current.getLon() - previous.getLon()) > updateThreshold)) {
116+
LoggerFactory.getLogger(getClass()).info("Position Update discarded due to unreasonable distance between updates.");
117+
current = null;
118+
}
117119
}
118120

119121
private void calculateGlobal() {
120-
double j = Math.floor(59.0 * even.getLat() - 60.0 * odd.getLat() + 0.5);
122+
double j = Math.floor(dLatOdd * even.getLat() - dLatEven * odd.getLat() + 0.5);
121123
double degrees = even.getSurface() ? 90.0 : 360.0; // Doesn't matter whether we check odd or even as they must both match by now
122124

123125
double latEven = (degrees / dLatEven) * (cprMod(j, dLatEven) + even.getLat());
@@ -161,7 +163,6 @@ private void calculateGlobal() {
161163
if (current.getSurface()) {
162164
validateSurface(current);
163165
}
164-
//TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range
165166
}
166167

167168
static private double cprMod(double a, double b) {

src/test/java/aero/t2s/modes/decoder/df/df17/AirbornePositionTest.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public void test_airborne_position_aircraft() throws UnknownDownlinkFormatExcept
2828
// We should NOT have a valid position after only one frame
2929
assertFalse(positionA.isPositionAvailable());
3030

31-
// This is an EVEN frame with Surface Position
31+
// This is an EVEN frame with Airborne Position... so we should be able to do a global calculation of position
3232
DownlinkFormat dfB = testMessage("8d407663588303313d84f719b2de");
3333

3434
assertInstanceOf(DF17.class, dfB);
@@ -38,14 +38,27 @@ public void test_airborne_position_aircraft() throws UnknownDownlinkFormatExcept
3838
assertInstanceOf(AirbornePosition.class, exSqB);
3939
AirbornePosition positionB = (AirbornePosition) exSqB;
4040
// We should now have a valid position after receiving both even and odd frames
41-
4241
assertTrue(positionB.isPositionAvailable());
4342
assertEquals(52.789, positionB.getLat(), 0.01);
4443
assertEquals(-2.405, positionB.getLon(), 0.01);
44+
45+
// This is another EVEN frame with Airborne Position... so we should be able to do a local calculation of position
46+
DownlinkFormat dfC = testMessage("8d407663588303312385138ddce6");
47+
48+
assertInstanceOf(DF17.class, dfC);
49+
DF17 df17C = (DF17) dfC;
50+
assertEquals("407663", df17C.getIcao());
51+
ExtendedSquitter exSqC = df17C.getExtendedSquitter();
52+
assertInstanceOf(AirbornePosition.class, exSqC);
53+
AirbornePosition positionC = (AirbornePosition) exSqC;
54+
// We should still have a valid position after receiving another even frame
55+
assertTrue(positionC.isPositionAvailable());
56+
assertEquals(52.788, positionC.getLat(), 0.01);
57+
assertEquals(-2.401, positionC.getLon(), 0.01);
4558
}
4659

4760
private DownlinkFormat testMessage(String message) throws UnknownDownlinkFormatException {
48-
Decoder decoder = new Decoder(new HashMap<>(), 50, 2, ModeSDatabase.createDatabase());
61+
Decoder decoder = new Decoder(new HashMap<>(), 53, -2, ModeSDatabase.createDatabase());
4962

5063
return decoder.decode(BinaryHelper.stringToByteArray(message));
5164
}

src/test/java/aero/t2s/modes/decoder/df/df17/SurfacePositionTest.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,25 @@ public void test_surface_position_aircraft() throws UnknownDownlinkFormatExcepti
4646
assertTrue(positionB.isPositionAvailable());
4747
assertEquals(53.3604, positionB.getLat(), 0.001);
4848
assertEquals(-2.2671, positionB.getLon(), 0.001);
49-
5049
assertTrue(positionB.isVelocityAvailable());
5150
assertEquals(1.75, positionB.getVelocity(), 0.1);
52-
5351
assertTrue(positionB.isTrackAvailable());
5452
assertEquals(230.625, positionB.getTrack(), 0.1);
53+
54+
// This is another ODD frame with Surface Position... so we should be able to do a local calculation of position
55+
DownlinkFormat dfC = testMessage("8c4ca6a9389d27ec46497d8ea39e");
56+
57+
assertInstanceOf(DF17.class, dfC);
58+
DF17 df17C = (DF17) dfC;
59+
assertEquals("4CA6A9", df17C.getIcao());
60+
ExtendedSquitter exSqC = df17C.getExtendedSquitter();
61+
assertInstanceOf(SurfacePosition.class, exSqC);
62+
SurfacePosition positionC = (SurfacePosition) exSqC;
63+
// We should still have a valid position after receiving another odd frame
64+
65+
assertTrue(positionC.isPositionAvailable());
66+
assertEquals(53.360, positionC.getLat(), 0.001);
67+
assertEquals(-2.267, positionC.getLon(), 0.001);
5568
}
5669

5770
@Test

0 commit comments

Comments
 (0)