Skip to content

Commit 2020e84

Browse files
committed
SurfacePosition #51
Revised SurfacePosition decoding to pick the lat/lon quadrant closest to the current active position. This initialises at the receiver lat/lon argument (if supplied), and updates a rolling-average with each global position update (this may not be strictly necessary... may revise later to just use the first N updates received).
1 parent ee04901 commit 2020e84

File tree

7 files changed

+97
-38
lines changed

7 files changed

+97
-38
lines changed

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,6 @@ public boolean getSurface() {
6363
return this.surface;
6464
}
6565

66-
public void validateSurface() {
67-
// For SurfacePositions we get 8 possible solutions: 2x latitude, 4x longitude zones at 90 degree increments
68-
// TODO Could use receiver/reference lat/lon to chose which possibile solution is closest... or build-up an average over data received to-date
69-
// Just for temporary, assume northern hemisphere and west of prime meridian
70-
this.lon = this.lon - 90.0;
71-
}
72-
7366
public void setZones(double latZone, double lonZone) {
7467
this.latZone = latZone;
7568
this.lonZone = lonZone;

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import java.util.function.Consumer;
77

88
abstract public class ModeSHandler {
9+
protected double originLat;
10+
protected double originLon;
911
protected Consumer<Track> onDeleted = track -> {};
1012
protected Consumer<Track> onCreated = track -> {};
1113
protected Consumer<Track> onUpdated = track -> {};
@@ -43,7 +45,7 @@ protected short[] toData(final String input) throws EmptyMessageException, ModeA
4345
}
4446

4547
public void start() {
46-
PositionUpdate.start();
48+
PositionUpdate.start(originLat, originLon);
4749
}
4850

4951
public void stop() {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public class ModeSMessageHandler extends ModeSHandler {
2222
private Consumer<DownlinkFormat> onMessage;
2323

2424
public ModeSMessageHandler(double originLat, double originLon) {
25+
this.originLat = originLat;
26+
this.originLon = originLon;
2527
this.decoder = new Decoder(new HashMap<>(), originLat, originLon, ModeSDatabase.createDatabase());
2628
}
2729

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public class ModeSTrackHandler extends ModeSHandler {
2424

2525
public ModeSTrackHandler(Map<String, Track> tracks, double originLat, double originLon, ModeSDatabase database) {
2626
this.tracks = tracks;
27+
this.originLat = originLat;
28+
this.originLon = originLon;
2729
this.decoder = new Decoder(tracks, originLat, originLon, database);
2830

2931
timer = new Timer();

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

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,22 @@
44
import java.util.*;
55

66
public class PositionUpdate {
7-
static Map<String, PositionUpdate> cache = new HashMap<>();
7+
private static double originLat; // Origin is passed-in as a command-line argument as an indication of where the receiver is located
8+
private static double originLon;
9+
10+
private static double receiverLat; // Receiver position is calculated based on data received
11+
private static double receiverLon;
12+
private static double receiverSumLat; // Running-total of valid positions that can be used to calculate the average receiver position
13+
private static double receiverSumLon;
14+
private static double receiverSumCount = 0; // number of valid positions received that are used to calculate the average
15+
16+
private static Map<String, PositionUpdate> cache = new HashMap<>();
817
private static Timer cacheCleanup;
918

10-
static final private double dLatEven = 4.0 * 15.0;
11-
static final private double dLatOdd = 4.0 * 15.0 - 1.0;
12-
static final private double nlNumerator = 1 - Math.cos(Math.PI / (2.0 * 15.0));
13-
static final private double nlPi180 = Math.PI / 180.0;
19+
private static final double dLatEven = 4.0 * 15.0;
20+
private static final double dLatOdd = 4.0 * 15.0 - 1.0;
21+
private static final double nlNumerator = 1 - Math.cos(Math.PI / (2.0 * 15.0));
22+
private static final double nlPi180 = Math.PI / 180.0;
1423

1524
private CprPosition even;
1625
private CprPosition odd;
@@ -51,6 +60,8 @@ private CprPosition calculate(boolean isCprEven, CprPosition newCpr) {
5160
if (isComplete() && previous == null) {
5261
// We've now got a pair of odd/even frames but there was no previous position... so we must use Global calculation to get accurate position
5362
calculateGlobal();
63+
// Update the receiver position only from Global updates (likely to be the first time updating from this aircraft)
64+
PositionUpdate.receiverUpdate(current.getLat(), current.getLon());
5465
} else if (previous != null) {
5566
// We had a previous accurate position, so we can use the most recent frame to do Local calculation
5667
calculateLocal(!isCprEven);
@@ -96,14 +107,13 @@ private void calculateLocal(boolean isOdd) {
96107
//return;
97108
}
98109

99-
// TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range
100-
// TODO Should be a sanity-check here to see if the calculated movement since the last update is too far
101-
102110
current = new CprPosition(newLat, newLon, cpr.getSurface());
103111
if (current.getSurface()) {
104-
current.validateSurface();
112+
validateSurface(current);
105113
}
106114

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
107117
}
108118

109119
private void calculateGlobal() {
@@ -113,13 +123,8 @@ private void calculateGlobal() {
113123
double latEven = (degrees / dLatEven) * (cprMod(j, dLatEven) + even.getLat());
114124
double latOdd = (degrees / dLatOdd) * (cprMod(j, dLatOdd) + odd.getLat());
115125

116-
if (latEven >= 270.0 && latEven <= 360.0) {
117-
latEven -= 360.0;
118-
}
119-
120-
if (latOdd >= 270.0 && latOdd <= 360.0) {
121-
latOdd -= 360.0;
122-
}
126+
latEven = normaliseLat(latEven);
127+
latOdd = normaliseLat(latOdd);
123128

124129
double nlLatEven = NL(latEven);
125130
double nlLatOdd = NL(latOdd);
@@ -147,31 +152,27 @@ private void calculateGlobal() {
147152
newLat = latOdd;
148153
newLon = (degrees / ni) * (cprMod(m, ni) + odd.getLon());
149154
}
150-
151-
if (newLon > 180d) {
152-
newLon -= 360d;
153-
}
154-
155-
//TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range
155+
newLon = normaliseLon(newLon);
156156

157157
even.setZones(nlLatEven, m);
158158
odd.setZones(nlLatOdd, m);
159159

160160
current = new CprPosition(newLat, newLon, even.getSurface());
161161
if (current.getSurface()) {
162-
current.validateSurface();
162+
validateSurface(current);
163163
}
164+
//TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range
164165
}
165166

166-
private double cprMod(double a, double b) {
167+
static private double cprMod(double a, double b) {
167168
return a - b * Math.floor(a/b);
168169
}
169170

170-
private double cprN(double nlLat, double isOdd) {
171+
static private double cprN(double nlLat, double isOdd) {
171172
return Math.max(1, nlLat - isOdd);
172173
}
173174

174-
private double NL(double lat) {
175+
static private double NL(double lat) {
175176
if (lat == 0) return 59;
176177
else if (Math.abs(lat) == 87) return 2;
177178
else if (Math.abs(lat) > 87) return 1;
@@ -197,7 +198,66 @@ public boolean isExpired() {
197198
return even.isExpired() || odd.isExpired();
198199
}
199200

200-
public static void start() {
201+
static public double normaliseLat(double lat) {
202+
if (lat >= 270.0 && lat <= 360.0) {
203+
lat -= 360.0;
204+
}
205+
return lat;
206+
}
207+
208+
static public double normaliseLon(double lon) {
209+
if (lon > 180.0) {
210+
lon -= 360.0;
211+
}
212+
return lon;
213+
}
214+
215+
private void validateSurface(CprPosition pos) {
216+
// For SurfacePositions we get 8 possible solutions: 2x latitude, 4x longitude zones at 90 degree increments
217+
double diff;
218+
double test;
219+
double testDiff;
220+
221+
// Pick the lat which is closest to the receiver
222+
test = pos.getLat();
223+
diff = Math.abs(normaliseLat(test) - receiverLat);
224+
for (int i = 0 ; i < 1 ; i++){
225+
test = normaliseLat(test + 90.0);
226+
testDiff = Math.abs(test - receiverLat);
227+
if (testDiff < diff) {
228+
diff = testDiff;
229+
pos.setLat(test);
230+
}
231+
}
232+
233+
// Pick the lon which is closest to the receiver
234+
test = pos.getLon();
235+
diff = Math.abs(normaliseLon(test) - receiverLon);
236+
for (int i = 0 ; i < 3 ; i++){
237+
test = normaliseLon(test + 90.0);
238+
testDiff = Math.abs(test - receiverLon);
239+
if (testDiff < diff) {
240+
diff = testDiff;
241+
pos.setLon(test);
242+
}
243+
}
244+
}
245+
246+
static private void receiverUpdate(double lat, double lon) {
247+
if (!((lat == 0) && (lon == 0))) {
248+
receiverSumCount ++;
249+
receiverSumLat += lat;
250+
receiverSumLon += lon;
251+
receiverLat = receiverSumLat / receiverSumCount;
252+
receiverLon = receiverSumLon / receiverSumCount;
253+
}
254+
}
255+
256+
static public void start(double originLat, double originLon) {
257+
PositionUpdate.originLat = originLat;
258+
PositionUpdate.originLon = originLon;
259+
260+
receiverUpdate(originLat, originLon);
201261

202262
cache.clear();
203263
cacheCleanup.schedule(new TimerTask() {
@@ -213,7 +273,7 @@ public void run() {
213273
}, 0, 10_000);
214274
}
215275

216-
public static void stop() {
276+
static public void stop() {
217277
cacheCleanup.cancel();
218278
cacheCleanup = null;
219279

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
class AirbornePositionTest {
1818
@Test
19-
public void test_surface_position_aircraft() throws UnknownDownlinkFormatException {
19+
public void test_airborne_position_aircraft() throws UnknownDownlinkFormatException {
2020
// This is an ODD frame with Airborne Position
2121
DownlinkFormat dfA = testMessage("8d4076635883069b318845770698");
2222

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void test_surface_position_aircraft() throws UnknownDownlinkFormatExcepti
2929
assertEquals(2.0, positionA.getVelocity());
3030
assertEquals(true, positionA.isTrackValid());
3131
assertEquals(82, positionA.getTrackEncoded());
32-
assertEquals(230.625, positionA.getTrack());
32+
assertEquals(230.625 * 100, positionA.getTrack() * 100);
3333
// We should NOT have a valid position after only one frame
3434
assertEquals(false, positionA.isPositionAvailable());
3535

0 commit comments

Comments
 (0)