diff options
Diffstat (limited to 'src/main/java/com/benburwell/planes/sbs')
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(); + } +} |