From e2ae5749a522b0d4d2657a2cac571b1524d33cfa Mon Sep 17 00:00:00 2001 From: Ben Burwell Date: Tue, 15 Nov 2016 21:30:47 -0500 Subject: Initial commit --- .gitignore | 4 + run.sh | 7 ++ src/com/benburwell/planes/gui/Main1090.java | 61 +++++++++ .../benburwell/planes/sbs/AggregateDataSource.java | 23 ++++ src/com/benburwell/planes/sbs/DataListener.java | 8 ++ src/com/benburwell/planes/sbs/DataSource.java | 8 ++ .../planes/sbs/MalformedPacketException.java | 15 +++ src/com/benburwell/planes/sbs/MessageType.java | 31 +++++ src/com/benburwell/planes/sbs/SBSPacket.java | 140 +++++++++++++++++++++ src/com/benburwell/planes/sbs/TCPDataSource.java | 56 +++++++++ .../benburwell/planes/sbs/TransmissionType.java | 34 +++++ .../sbs/UnrecognizedMessageTypeException.java | 20 +++ .../sbs/UnrecognizedTransmissionTypeException.java | 20 +++ 13 files changed, 427 insertions(+) create mode 100644 .gitignore create mode 100755 run.sh create mode 100644 src/com/benburwell/planes/gui/Main1090.java create mode 100644 src/com/benburwell/planes/sbs/AggregateDataSource.java create mode 100644 src/com/benburwell/planes/sbs/DataListener.java create mode 100644 src/com/benburwell/planes/sbs/DataSource.java create mode 100644 src/com/benburwell/planes/sbs/MalformedPacketException.java create mode 100644 src/com/benburwell/planes/sbs/MessageType.java create mode 100644 src/com/benburwell/planes/sbs/SBSPacket.java create mode 100644 src/com/benburwell/planes/sbs/TCPDataSource.java create mode 100644 src/com/benburwell/planes/sbs/TransmissionType.java create mode 100644 src/com/benburwell/planes/sbs/UnrecognizedMessageTypeException.java create mode 100644 src/com/benburwell/planes/sbs/UnrecognizedTransmissionTypeException.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..023292b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +out/ +planes.jar +.idea/ +1090.iml diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..ea99c27 --- /dev/null +++ b/run.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +javac **/*.java +cd out/production/1090 +jar -cvfe planes.jar com.benburwell.planes.gui.Main1090 ** +java -jar planes.jar + diff --git a/src/com/benburwell/planes/gui/Main1090.java b/src/com/benburwell/planes/gui/Main1090.java new file mode 100644 index 0000000..5393dc0 --- /dev/null +++ b/src/com/benburwell/planes/gui/Main1090.java @@ -0,0 +1,61 @@ +/** + * Created by ben on 11/15/16. + */ + +package com.benburwell.planes.gui; + +import com.benburwell.planes.sbs.*; + +import java.awt.*; +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +public class Main1090 extends JFrame { + private AggregateDataSource sbsDataSource = new AggregateDataSource(); + + public Main1090() { + this.initUI(); + } + + private void initUI() { + this.createMenuBar(); + + this.setTitle("1090"); + this.setSize(100, 100); + this.setLocationRelativeTo(null); + this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + + this.openDataSource(); + } + + private void createMenuBar() { + JMenuBar menubar = new JMenuBar(); + JMenu file = new JMenu("1090"); + file.setMnemonic(KeyEvent.VK_F); + JMenuItem eMenuItem = new JMenuItem("Quit"); + eMenuItem.setMnemonic(KeyEvent.VK_E); + eMenuItem.setToolTipText("Exit 1090"); + eMenuItem.addActionListener((ActionEvent event) -> { + System.exit(0); + }); + file.add(eMenuItem); + menubar.add(file); + this.setJMenuBar(menubar); + } + + private void openDataSource() { + System.out.println("asdfasdfasdfasdfasdf"); + this.sbsDataSource.addSource(new TCPDataSource("10.0.0.111", 30003)); + this.sbsDataSource.subscribe((SBSPacket packet) -> { + System.out.println("Got message: " + packet.toString()); + }); + } + + public static void main(String[] args) { + EventQueue.invokeLater(() -> { + Main1090 app = new Main1090(); + app.setVisible(true); + }); + } +} diff --git a/src/com/benburwell/planes/sbs/AggregateDataSource.java b/src/com/benburwell/planes/sbs/AggregateDataSource.java new file mode 100644 index 0000000..08b89ea --- /dev/null +++ b/src/com/benburwell/planes/sbs/AggregateDataSource.java @@ -0,0 +1,23 @@ +package com.benburwell.planes.sbs; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by ben on 11/15/16. + */ +public class AggregateDataSource implements DataSource { + private List subscribers = new ArrayList<>(); + + public void addSource(DataSource source) { + source.subscribe((SBSPacket packet) -> { + for (DataListener listener : subscribers) { + listener.handleMessage(packet); + } + }); + } + + public void subscribe(DataListener listener) { + this.subscribers.add(listener); + } +} diff --git a/src/com/benburwell/planes/sbs/DataListener.java b/src/com/benburwell/planes/sbs/DataListener.java new file mode 100644 index 0000000..b0da2ef --- /dev/null +++ b/src/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/com/benburwell/planes/sbs/DataSource.java b/src/com/benburwell/planes/sbs/DataSource.java new file mode 100644 index 0000000..959e273 --- /dev/null +++ b/src/com/benburwell/planes/sbs/DataSource.java @@ -0,0 +1,8 @@ +package com.benburwell.planes.sbs; + +/** + * Created by ben on 11/15/16. + */ +public interface DataSource { + void subscribe(DataListener listener); +} diff --git a/src/com/benburwell/planes/sbs/MalformedPacketException.java b/src/com/benburwell/planes/sbs/MalformedPacketException.java new file mode 100644 index 0000000..6cbd1a3 --- /dev/null +++ b/src/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/com/benburwell/planes/sbs/MessageType.java b/src/com/benburwell/planes/sbs/MessageType.java new file mode 100644 index 0000000..a2ecc66 --- /dev/null +++ b/src/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/com/benburwell/planes/sbs/SBSPacket.java b/src/com/benburwell/planes/sbs/SBSPacket.java new file mode 100644 index 0000000..0988f3a --- /dev/null +++ b/src/com/benburwell/planes/sbs/SBSPacket.java @@ -0,0 +1,140 @@ +package com.benburwell.planes.sbs; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.DoubleSummaryStatistics; + +/** + * Created by ben on 11/15/16. + */ +public class SBSPacket { + private MessageType messageType; + private TransmissionType transmissionType; + private String sessionId; + private String aircraftId; + private String hexIdent; + private String flightId; + private Date dateGenerated; + private Date dateLogged; + private String callsign; + private double altitude; + private double groundSpeed; + private double track; + private double latitude; + private double longitude; + private double verticalRate; + private String squawk; + private boolean alert; + private boolean emergency; + private boolean spi; + private boolean isOnGround; + + public SBSPacket() { + } + + 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(); + } +} diff --git a/src/com/benburwell/planes/sbs/TCPDataSource.java b/src/com/benburwell/planes/sbs/TCPDataSource.java new file mode 100644 index 0000000..6c0e51d --- /dev/null +++ b/src/com/benburwell/planes/sbs/TCPDataSource.java @@ -0,0 +1,56 @@ +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 subscribers = new ArrayList<>(); + + public TCPDataSource(String host, int port) { + new Thread(() -> { + System.out.println("Starting socket client"); + Socket clientSocket; + BufferedReader socketReader; + try { + clientSocket = new Socket(host, port); + } catch (IOException e) { + System.out.println("Could not connect to " + host + " on port " + port + ": " + e.getMessage()); + return; + } + try { + socketReader = new BufferedReader(new InputStreamReader(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()); + } + } + }).start(); + } + + public void subscribe(DataListener listener) { + this.subscribers.add(listener); + } +} diff --git a/src/com/benburwell/planes/sbs/TransmissionType.java b/src/com/benburwell/planes/sbs/TransmissionType.java new file mode 100644 index 0000000..fb1761e --- /dev/null +++ b/src/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/com/benburwell/planes/sbs/UnrecognizedMessageTypeException.java b/src/com/benburwell/planes/sbs/UnrecognizedMessageTypeException.java new file mode 100644 index 0000000..ee30a87 --- /dev/null +++ b/src/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/com/benburwell/planes/sbs/UnrecognizedTransmissionTypeException.java b/src/com/benburwell/planes/sbs/UnrecognizedTransmissionTypeException.java new file mode 100644 index 0000000..abab067 --- /dev/null +++ b/src/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(); + } +} -- cgit v1.2.3