summaryrefslogtreecommitdiff
path: root/src/main/java/com/benburwell/planes/sbs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/benburwell/planes/sbs')
-rw-r--r--src/main/java/com/benburwell/planes/sbs/AggregateDataSource.java48
-rw-r--r--src/main/java/com/benburwell/planes/sbs/DataListener.java8
-rw-r--r--src/main/java/com/benburwell/planes/sbs/DataSource.java10
-rw-r--r--src/main/java/com/benburwell/planes/sbs/MalformedPacketException.java15
-rw-r--r--src/main/java/com/benburwell/planes/sbs/MessageType.java31
-rw-r--r--src/main/java/com/benburwell/planes/sbs/SBSPacket.java216
-rw-r--r--src/main/java/com/benburwell/planes/sbs/TCPDataSource.java99
-rw-r--r--src/main/java/com/benburwell/planes/sbs/TransmissionType.java34
-rw-r--r--src/main/java/com/benburwell/planes/sbs/UnrecognizedMessageTypeException.java20
-rw-r--r--src/main/java/com/benburwell/planes/sbs/UnrecognizedTransmissionTypeException.java20
10 files changed, 501 insertions, 0 deletions
diff --git a/src/main/java/com/benburwell/planes/sbs/AggregateDataSource.java b/src/main/java/com/benburwell/planes/sbs/AggregateDataSource.java
new file mode 100644
index 0000000..2850404
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/AggregateDataSource.java
@@ -0,0 +1,48 @@
+package com.benburwell.planes.sbs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public class AggregateDataSource implements DataSource {
+ private List<DataListener> subscribers = new ArrayList<>();
+ private boolean isOpen = false;
+ private int nextSourceNumber = 1;
+ private Map<Integer,DataSource> sources = new HashMap<>();
+
+ public int addSource(DataSource source) {
+ int thisSourceNumber = this.nextSourceNumber++;
+ this.sources.put(thisSourceNumber, source);
+ source.subscribe((SBSPacket packet) -> {
+ if (isOpen) {
+ for (DataListener listener : subscribers) {
+ listener.handleMessage(packet);
+ }
+ }
+ });
+ return thisSourceNumber;
+ }
+
+ public void subscribe(DataListener listener) {
+ this.subscribers.add(listener);
+ }
+
+ public void open() {
+ this.isOpen = true;
+ }
+
+ public void close() {
+ this.isOpen = false;
+ }
+
+ public void closeSource(int sourceNumber) {
+ if (this.sources.containsKey(sourceNumber)) {
+ this.sources.get(sourceNumber).close();
+ this.sources.remove(sourceNumber);
+ }
+ }
+}
diff --git a/src/main/java/com/benburwell/planes/sbs/DataListener.java b/src/main/java/com/benburwell/planes/sbs/DataListener.java
new file mode 100644
index 0000000..b0da2ef
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/DataListener.java
@@ -0,0 +1,8 @@
+package com.benburwell.planes.sbs;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public interface DataListener {
+ void handleMessage(SBSPacket packet);
+}
diff --git a/src/main/java/com/benburwell/planes/sbs/DataSource.java b/src/main/java/com/benburwell/planes/sbs/DataSource.java
new file mode 100644
index 0000000..961c6e2
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/DataSource.java
@@ -0,0 +1,10 @@
+package com.benburwell.planes.sbs;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public interface DataSource {
+ void subscribe(DataListener listener);
+ void open();
+ void close();
+}
diff --git a/src/main/java/com/benburwell/planes/sbs/MalformedPacketException.java b/src/main/java/com/benburwell/planes/sbs/MalformedPacketException.java
new file mode 100644
index 0000000..6cbd1a3
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/MalformedPacketException.java
@@ -0,0 +1,15 @@
+package com.benburwell.planes.sbs;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public class MalformedPacketException extends Exception {
+ private String message;
+ public MalformedPacketException(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+}
diff --git a/src/main/java/com/benburwell/planes/sbs/MessageType.java b/src/main/java/com/benburwell/planes/sbs/MessageType.java
new file mode 100644
index 0000000..a2ecc66
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/MessageType.java
@@ -0,0 +1,31 @@
+package com.benburwell.planes.sbs;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public enum MessageType {
+ SELECTION_CHANGE("SEL"),
+ NEW_ID("ID"),
+ NEW_AIRCRAFT("AIR"),
+ STATUS_CHANGE("STA"),
+ CLICK("CLK"),
+ TRANSMISSION("MSG");
+
+ private String code;
+ MessageType(String code) {
+ this.code = code;
+ }
+
+ public String getCode() {
+ return this.code;
+ }
+
+ public static MessageType parse(String messageType) throws UnrecognizedMessageTypeException {
+ for (MessageType type : MessageType.values()) {
+ if (type.getCode().equals(messageType)) {
+ return type;
+ }
+ }
+ throw new UnrecognizedMessageTypeException(messageType);
+ }
+}
diff --git a/src/main/java/com/benburwell/planes/sbs/SBSPacket.java b/src/main/java/com/benburwell/planes/sbs/SBSPacket.java
new file mode 100644
index 0000000..5684803
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/SBSPacket.java
@@ -0,0 +1,216 @@
+package com.benburwell.planes.sbs;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public class SBSPacket {
+ private MessageType messageType;
+ private TransmissionType transmissionType = null;
+ private String sessionId = null;
+ private String aircraftId = null;
+ private String hexIdent = null;
+ private String flightId = null;
+ private Date dateGenerated = null;
+ private Date dateLogged = null;
+ private String callsign = null;
+ private Double altitude = null;
+ private Double groundSpeed = null;
+ private Double track = null;
+ private Double latitude = null;
+ private Double longitude = null;
+ private Double verticalRate = null;
+ private String squawk = null;
+ private Boolean alert = null;
+ private Boolean emergency = null;
+ private Boolean spi = null;
+ private Boolean isOnGround = null;
+
+ public SBSPacket(String packet) throws MalformedPacketException {
+ this.parse(packet);
+ }
+
+ public void parse(String packet) throws MalformedPacketException {
+ String[] segments = packet.split(",", -1);
+ if (segments.length < 11) {
+ throw new MalformedPacketException("Packet must have at least 11 fields, but has only " + segments.length);
+ }
+
+ // get the message type
+ try {
+ this.messageType = MessageType.parse(segments[0]);
+ } catch (UnrecognizedMessageTypeException e) {
+ throw new MalformedPacketException("Packet has an unrecognized message type: " + e.getType());
+ }
+
+ // get the transmission type
+ if (this.messageType.equals(MessageType.TRANSMISSION)) {
+ try {
+ this.transmissionType = TransmissionType.parse(segments[1]);
+ } catch (UnrecognizedTransmissionTypeException e) {
+ throw new MalformedPacketException("Packet has an unrecognized transmission type code: " + e.getCode());
+ }
+ }
+
+ this.sessionId = segments[2];
+ this.aircraftId = segments[3];
+ this.hexIdent = segments[4];
+ this.flightId = segments[5];
+ this.dateGenerated = this.parseDateAndTime(segments[6], segments[7]);
+ this.dateLogged = this.parseDateAndTime(segments[8], segments[9]);
+ this.callsign = segments[10];
+
+ if (this.messageType.equals(MessageType.TRANSMISSION)) {
+ if (segments.length < 22) {
+ throw new MalformedPacketException("Packet is a message (22 fields), but only has " + segments.length);
+ }
+ if (segments[11].length() > 0) {
+ this.altitude = Double.parseDouble(segments[11]);
+ }
+ if (segments[12].length() > 0) {
+ this.groundSpeed = Double.parseDouble(segments[12]);
+ }
+ if (segments[13].length() > 0) {
+ this.track = Double.parseDouble(segments[13]);
+ }
+ if (segments[14].length() > 0) {
+ this.latitude = Double.parseDouble(segments[14]);
+ }
+ if (segments[15].length() > 0) {
+ this.longitude = Double.parseDouble(segments[15]);
+ }
+ if (segments[16].length() > 0) {
+ this.verticalRate = Double.parseDouble(segments[16]);
+ }
+ this.squawk = segments[17];
+ if (segments[18].length() > 0) {
+ this.alert = segments[18].equals("1");
+ }
+ if (segments[19].length() > 0) {
+ this.emergency = segments[19].equals("1");
+ }
+ if (segments[20].length() > 0) {
+ this.spi = segments[20].equals("1");
+ }
+ if (segments[21].length() > 0) {
+ this.isOnGround = segments[21].equals("1");
+ }
+ }
+ }
+
+ public Date parseDateAndTime(String date, String time) {
+ String combined = "";
+ Calendar now = Calendar.getInstance();
+ if (date == null || date.isEmpty()) {
+ combined += now.get(Calendar.YEAR) + "/" + now.get(Calendar.MONTH) + "/" + now.get(Calendar.DAY_OF_MONTH);
+ } else {
+ combined += date;
+ }
+
+ combined += " ";
+
+ if (time == null || time.isEmpty()) {
+ combined += now.get(Calendar.HOUR_OF_DAY) + ":" +
+ now.get(Calendar.MINUTE) + ":" +
+ now.get(Calendar.SECOND) + "." +
+ now.get(Calendar.MILLISECOND);
+ } else {
+ combined += time;
+ }
+
+ SimpleDateFormat fmt = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss.sss");
+ try {
+ return fmt.parse(combined);
+ } catch (ParseException e) {
+ return null;
+ }
+ }
+
+ public String toString() {
+ return this.messageType.name();
+ }
+
+ public MessageType getMessageType() {
+ return messageType;
+ }
+
+ public TransmissionType getTransmissionType() {
+ return transmissionType;
+ }
+
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ public String getAircraftId() {
+ return aircraftId;
+ }
+
+ public String getHexIdent() {
+ return hexIdent;
+ }
+
+ public String getFlightId() {
+ return flightId;
+ }
+
+ public Date getDateGenerated() {
+ return dateGenerated;
+ }
+
+ public Date getDateLogged() {
+ return dateLogged;
+ }
+
+ public String getCallsign() {
+ return callsign;
+ }
+
+ public Double getAltitude() {
+ return altitude;
+ }
+
+ public Double getGroundSpeed() {
+ return groundSpeed;
+ }
+
+ public Double getTrack() {
+ return track;
+ }
+
+ public Double getLatitude() {
+ return latitude;
+ }
+
+ public Double getLongitude() {
+ return longitude;
+ }
+
+ public Double getVerticalRate() {
+ return verticalRate;
+ }
+
+ public String getSquawk() {
+ return squawk;
+ }
+
+ public Boolean isAlert() {
+ return alert;
+ }
+
+ public Boolean isEmergency() {
+ return emergency;
+ }
+
+ public Boolean isSpi() {
+ return spi;
+ }
+
+ public Boolean isOnGround() {
+ return isOnGround;
+ }
+}
diff --git a/src/main/java/com/benburwell/planes/sbs/TCPDataSource.java b/src/main/java/com/benburwell/planes/sbs/TCPDataSource.java
new file mode 100644
index 0000000..2224d36
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/TCPDataSource.java
@@ -0,0 +1,99 @@
+package com.benburwell.planes.sbs;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.*;
+import java.net.*;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public class TCPDataSource implements DataSource {
+ private List<DataListener> subscribers = new ArrayList<>();
+ private String host;
+ private int port;
+ private Thread clientThread = null;
+ private SocketClient client = null;
+
+ public TCPDataSource(String host, int port) {
+ this.host = host;
+ this.port = port;
+ }
+
+ public void subscribe(DataListener listener) {
+ this.subscribers.add(listener);
+ }
+
+ public void open() {
+ this.client = new SocketClient(this.host, this.port);
+ this.clientThread = new Thread(this.client);
+ this.clientThread.start();
+ }
+
+ public void close() {
+ if (this.client != null) {
+ this.client.terminate();
+ try {
+ this.clientThread.join();
+ } catch (InterruptedException ignored) {}
+ }
+ }
+
+ private class SocketClient implements Runnable {
+ private String host;
+ private int port;
+ private Socket clientSocket = null;
+
+ public SocketClient(String host, int port) {
+ this.host = host;
+ this.port = port;
+ }
+
+ public void terminate() {
+ if (this.clientSocket != null) {
+ try {
+ this.clientSocket.close();
+ } catch (IOException e) {
+ System.out.println("Got exception closing socket: " + e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ System.out.println("Starting socket client");
+ BufferedReader socketReader;
+ try {
+ this.clientSocket = new Socket(this.host, this.port);
+ } catch (IOException e) {
+ System.out.println("Could not connect to " + this.host + " on port " + this.port + ": " + e.getMessage());
+ return;
+ }
+ try {
+ socketReader = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
+ } catch (IOException e) {
+ System.out.println("Could not create socket reader: " + e.getMessage());
+ return;
+ }
+
+ String receivedMessage;
+ while (true) {
+ try {
+ receivedMessage = socketReader.readLine();
+ } catch (IOException e) {
+ System.out.println("Error reading from socket: " + e.getMessage());
+ return;
+ }
+ try {
+ SBSPacket packet = new SBSPacket(receivedMessage);
+ for (DataListener subscriber : subscribers) {
+ subscriber.handleMessage(packet);
+ }
+ } catch (MalformedPacketException e) {
+ System.out.println("Discarding malformed packet: " + receivedMessage);
+ System.out.println(e.getMessage());
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/benburwell/planes/sbs/TransmissionType.java b/src/main/java/com/benburwell/planes/sbs/TransmissionType.java
new file mode 100644
index 0000000..fb1761e
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/TransmissionType.java
@@ -0,0 +1,34 @@
+package com.benburwell.planes.sbs;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public enum TransmissionType {
+ ES_IDENTIFICATION(1),
+ ES_SURFACE_POSITION(2),
+ ES_AIRBORNE_POSITION(3),
+ ES_AIRBORNE_VELOCITY(4),
+ SURVEILLANCE_ALT(5),
+ SURVEILLANCE_ID(6),
+ AIR_TO_AIR(7),
+ ALL_CALL_REPLY(8);
+
+ private int id;
+ TransmissionType(int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return this.id;
+ }
+
+ public static TransmissionType parse(String codeString) throws UnrecognizedTransmissionTypeException {
+ int code = Integer.parseInt(codeString);
+ for (TransmissionType transmissionType : TransmissionType.values()) {
+ if (transmissionType.getId() == code) {
+ return transmissionType;
+ }
+ }
+ throw new UnrecognizedTransmissionTypeException(code);
+ }
+}
diff --git a/src/main/java/com/benburwell/planes/sbs/UnrecognizedMessageTypeException.java b/src/main/java/com/benburwell/planes/sbs/UnrecognizedMessageTypeException.java
new file mode 100644
index 0000000..ee30a87
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/UnrecognizedMessageTypeException.java
@@ -0,0 +1,20 @@
+package com.benburwell.planes.sbs;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public class UnrecognizedMessageTypeException extends Exception {
+ private String type;
+
+ public UnrecognizedMessageTypeException(String type) {
+ this.type = type;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ public String getMessage() {
+ return "Unrecognized message type: " + this.getType();
+ }
+}
diff --git a/src/main/java/com/benburwell/planes/sbs/UnrecognizedTransmissionTypeException.java b/src/main/java/com/benburwell/planes/sbs/UnrecognizedTransmissionTypeException.java
new file mode 100644
index 0000000..abab067
--- /dev/null
+++ b/src/main/java/com/benburwell/planes/sbs/UnrecognizedTransmissionTypeException.java
@@ -0,0 +1,20 @@
+package com.benburwell.planes.sbs;
+
+/**
+ * Created by ben on 11/15/16.
+ */
+public class UnrecognizedTransmissionTypeException extends Exception {
+ private int code;
+
+ public UnrecognizedTransmissionTypeException(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return this.code;
+ }
+
+ public String getMessage() {
+ return "Unrecognized transmission type: " + this.getCode();
+ }
+}