Skip to content

SurfacePosition #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ Most features of the DF17/18 protocol have been implemented, some message lack s
| 2 | Aircraft Identification | ✅ | |
| 3 | Aircraft Identification | ✅ | |
| 4 | Aircraft Identification | ✅ | |
| 5 | Surface Position | | Not implemented yet |
| 6 | Surface Position | | Not implemented yet |
| 7 | Surface Position | | Not implemented yet |
| 8 | Surface Position | | Not implemented yet |
| 5 | Surface Position | | |
| 6 | Surface Position | | |
| 7 | Surface Position | | |
| 8 | Surface Position | | |
| 9 | Airborne Position | ✅ | |
| 10 | Airborne Position | ✅ | |
| 11 | Airborne Position | ✅ | |
Expand Down
2 changes: 1 addition & 1 deletion examples/gui/src/main/java/example/Demo.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
public class Demo extends JPanel {
private static final Logger logger = LoggerFactory.getLogger(Demo.class);

private static final String IP = "192.168.178.190";
private static final String IP = "127.0.0.1";
private static final int PORT = 30002;
private static final double LAT = 51;
private static final double LON = 2;
Expand Down
49 changes: 48 additions & 1 deletion src/main/java/aero/t2s/modes/CprPosition.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,33 @@
public class CprPosition {
private double lat;
private double lon;
private boolean valid;
private boolean surface = false;
private boolean valid = false;
private double latZone;
private double lonZone;
private long time;

public CprPosition() {
this.lat = 0.0;
this.lon = 0.0;
this.surface = false;
this.valid = false;
}

public CprPosition(double lat, double lon, boolean surface) {
setLatLon(lat ,lon);
this.surface = surface;
}

public CprPosition(double lat, double lon) {
setLatLon(lat ,lon);
}

public CprPosition(int cprLat, int cprLon, boolean surface) {
setLatLon(cprLat / (double) (1 << 17), cprLon / (double) (1 << 17));
this.surface = surface;
}

public void setLatLon(double lat, double lon) {
this.lat = lat;
this.lon = lon;
Expand All @@ -40,6 +55,34 @@ public double getLon() {
return lon;
}

public void setSurface(boolean surface) {
this.surface = surface;
}

public boolean getSurface() {
return this.surface;
}

public void setZones(double latZone, double lonZone) {
this.latZone = latZone;
this.lonZone = lonZone;
}
public void setLatZone(double zone) {
this.latZone = zone;
}

public double getLatZone() {
return this.latZone;
}

public void setLonZone(double zone) {
this.lonZone = zone;
}

public double getLonZone() {
return this.lonZone;
}

public void setTime(long time) {
this.time = time;
}
Expand All @@ -52,6 +95,10 @@ public boolean isValid() {
return valid;
}

public void setValid(boolean valid) {
this.valid = valid;
}

public boolean isExpired() {
return time < Instant.now().minusSeconds(10).toEpochMilli();
}
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/aero/t2s/modes/ModeSHandler.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package aero.t2s.modes;

import aero.t2s.modes.decoder.df.DownlinkFormat;
import aero.t2s.modes.decoder.df.df17.AirbornePosition;
import aero.t2s.modes.decoder.df.df17.PositionUpdate;

import java.util.function.Consumer;

abstract public class ModeSHandler {
protected double originLat;
protected double originLon;
protected Consumer<Track> onDeleted = track -> {};
protected Consumer<Track> onCreated = track -> {};
protected Consumer<Track> onUpdated = track -> {};
Expand Down Expand Up @@ -43,10 +45,10 @@ protected short[] toData(final String input) throws EmptyMessageException, ModeA
}

public void start() {
AirbornePosition.start();
PositionUpdate.start(originLat, originLon);
}

public void stop() {
AirbornePosition.stop();
PositionUpdate.stop();
}
}
2 changes: 2 additions & 0 deletions src/main/java/aero/t2s/modes/ModeSMessageHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class ModeSMessageHandler extends ModeSHandler {
private Consumer<DownlinkFormat> onMessage;

public ModeSMessageHandler(double originLat, double originLon) {
this.originLat = originLat;
this.originLon = originLon;
this.decoder = new Decoder(new HashMap<>(), originLat, originLon, ModeSDatabase.createDatabase());
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/aero/t2s/modes/ModeSTrackHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public class ModeSTrackHandler extends ModeSHandler {

public ModeSTrackHandler(Map<String, Track> tracks, double originLat, double originLon, ModeSDatabase database) {
this.tracks = tracks;
this.originLat = originLat;
this.originLon = originLon;
this.decoder = new Decoder(tracks, originLat, originLon, database);

timer = new Timer();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/aero/t2s/modes/decoder/df/DF17.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public DF17 decode() {
case 6:
case 7:
case 8:
extendedSquitter = new SurfacePosition(data);
extendedSquitter = new SurfacePosition(data, getIcao());
break;
case 19:
extendedSquitter = new AirborneVelocity(data);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/aero/t2s/modes/decoder/df/DF18.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public DF18 decode() {
case 6:
case 7:
case 8:
extendedSquitter = new SurfacePosition(data);
extendedSquitter = new SurfacePosition(data, getIcao());
break;
case 19:
extendedSquitter = new AirborneVelocity(data);
Expand Down
189 changes: 8 additions & 181 deletions src/main/java/aero/t2s/modes/decoder/df/df17/AirbornePosition.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import aero.t2s.modes.Track;
import aero.t2s.modes.CprPosition;
import aero.t2s.modes.constants.*;
import aero.t2s.modes.decoder.Common;
import aero.t2s.modes.registers.Register05;
import aero.t2s.modes.registers.Register05V0;
import aero.t2s.modes.registers.Register05V2;
Expand All @@ -18,12 +17,9 @@ public class AirbornePosition extends ExtendedSquitter {
private boolean altitudeSourceBaro;
private int altitude;

private boolean positionAvailable;

private double lat;
private double lon;
private static Map<String, PositionUpdate> cache = new HashMap<>();
private static Timer cacheCleanup;
private boolean positionAvailable;

public AirbornePosition(short[] data, String address) {
super(data);
Expand Down Expand Up @@ -58,39 +54,14 @@ public AirbornePosition decode() {
cprLon = cprLon | (data[9] << 8);
cprLon = cprLon | data[10];


if (!cache.containsKey(address)) {
if (!isCprEven) {
return this;
}

synchronized (cache) {
cache.putIfAbsent(address, new PositionUpdate(
new CprPosition(cprLat / (double)(1 << 17), cprLon / (double)(1 << 17))
));
}
}

PositionUpdate positionUpdate;
synchronized (cache) {
positionUpdate = cache.get(address);
}
if (isCprEven) {
positionUpdate.setEven(new CprPosition(cprLat / (double) (1 << 17), cprLon / (double) (1 << 17)));
} else {
positionUpdate.setOdd(new CprPosition(cprLat / (double) (1 << 17), cprLon / (double) (1 << 17)));
}

if (positionUpdate.isComplete()) {
calculateGlobal(positionUpdate.even, positionUpdate.odd);
} else if (positionUpdate.isPreviousPositionAvailable() && positionUpdate.isPreviousPositionAvailable()) {
calculateLocal(positionUpdate.odd, true, positionUpdate.previousLat, positionUpdate.previousLon);
}

if (positionAvailable) {
positionUpdate.setPreviousPosition(this.lat, this.lon);
CprPosition newPosition = PositionUpdate.calculate(address, isCprEven, new CprPosition(cprLat, cprLon, false));
if (newPosition != null) {
this.lat = newPosition.getLat();
this.lon = newPosition.getLon();
this.positionAvailable = true;
} else {
this.positionAvailable = false;
}

return this;
}

Expand Down Expand Up @@ -230,88 +201,6 @@ private AltitudeSource determineAltitudeSource() {
return AltitudeSource.GNSS_HAE;
}

private void calculateLocal(CprPosition cpr, boolean isOdd, double previousLat, double previousLon) {

double dlat = isOdd ? 360.0 / 59.0 : 360.0 / 60.0;

double j = Math.floor(previousLat / dlat) + Math.floor((previousLat % dlat) / dlat - cpr.getLat() + 0.5);

double newLat = dlat * (j + previousLat);

double nl = NL(newLat) - (isOdd ? 1.0 : 0.0);
double dlon = nl > 0 ? 360.0 / nl : 360;

double m = Math.floor(previousLon / dlon) + Math.floor((previousLon % dlon) / dlon - cpr.getLon() + 0.5);
double newLon = dlon * (m + lon);

//TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range
//TODO Should be a sanity-check here to see if the calculated movement since the last update is too far
this.lat = newLat;
this.lon = newLon;
this.positionAvailable = true;
}

private void calculateGlobal(CprPosition cprEven, CprPosition cprOdd) {
double dLat0 = 360.0 / 60.0;
double dLat1 = 360.0 / 59.0;

double j = Math.floor(59.0 * cprEven.getLat() - 60.0 * cprOdd.getLat() + 0.5);

double latEven = dLat0 * (j % 60.0 + cprEven.getLat());
double latOdd = dLat1 * (j % 59.0 + cprOdd.getLat());

if (latEven >= 270.0 && latEven <= 360.0) {
latEven -= 360.0;
}

if (latOdd >= 270.0 && latOdd <= 360.0) {
latOdd -= 360.0;
}

if (NL(latEven) != NL(latOdd)) {
return;
}

double lat;
double lon;
if (cprEven.getTime() > cprOdd.getTime()) {
double ni = cprN(latEven, 0);
double m = Math.floor(cprEven.getLon() * (NL(latEven) - 1) - cprOdd.getLon() * NL(latEven) + 0.5);

lat = latEven;
lon = (360d / ni) * (m % ni + cprEven.getLon());
} else {
double ni = cprN(latOdd, 1);
double m = Math.floor(cprEven.getLon() * (NL(latOdd) - 1) - cprOdd.getLon() * NL(latOdd) + 0.5);

lat = latOdd;
lon = (360d / ni) * (m % ni + cprOdd.getLon());
}

if (lon > 180d) {
lon -= 360d;
}

//TODO Should be a sanity-check here to make sure the calculated position isn't outside receiver origin range,
this.lat = lat;
this.lon = lon;
this.positionAvailable = true;
}
private double cprN(double lat, double isOdd) {
double nl = NL(lat) - isOdd;

return nl > 1 ? nl : 1;
}

private double NL(double lat) {
if (lat == 0) return 59;
else if (Math.abs(lat) == 87) return 2;
else if (Math.abs(lat) > 87) return 1;

double tmp = 1 - (1 - Math.cos(Math.PI / (2.0 * 15.0))) / Math.pow(Math.cos(Math.PI / 180.0 * Math.abs(lat)), 2);
return Math.floor(2 * Math.PI / Math.acos(tmp));
}

private int calculateAltitude(short[] data, int typeCode) {
// TODO this should use AltitudeEncoding class flagged with mBit false feature.
int n = (data[5] >>> 1) << 4;
Expand All @@ -321,66 +210,4 @@ private int calculateAltitude(short[] data, int typeCode) {

return (n * qBit) - 1000;
}

public static void start() {
AirbornePosition.cache.clear();
AirbornePosition.cacheCleanup.schedule(new TimerTask() {
@Override
public void run() {
List<String> expired = new LinkedList<>();

synchronized (cache) {
cache.entrySet().stream().filter(entry -> entry.getValue().isExpired()).forEach(entry -> expired.add(entry.getKey()));
expired.forEach(cache::remove);
}
}
}, 0, 10_000);
}

public static void stop() {
AirbornePosition.cacheCleanup.cancel();
AirbornePosition.cacheCleanup = null;

AirbornePosition.cache.clear();
}

class PositionUpdate {
private CprPosition even;
private CprPosition odd;


private boolean previousPositionAvailable = false;
private double previousLat;
private double previousLon;

public PositionUpdate(CprPosition even) {
this.even = even;
}

public void setEven(CprPosition even) {
this.even = even;
this.odd = null;
}

public void setOdd(CprPosition odd) {
this.odd = odd;
}

public void setPreviousPosition(double lat, double lon) {
this.previousLat = lat;
this.previousLon = lon;
}

public boolean isPreviousPositionAvailable() {
return this.previousPositionAvailable;
}

public boolean isComplete() {
return even != null && odd != null;
}

public boolean isExpired() {
return even.isExpired() || odd.isExpired();
}
}
}
Loading