diff --git a/.mailmap b/.mailmap index 667de320..c5401576 100644 --- a/.mailmap +++ b/.mailmap @@ -16,4 +16,5 @@ # https://www.kernel.org/pub/software/scm/git/docs/git-shortlog.html # http://stacktoheap.com/blog/2013/01/06/using-mailmap-to-fix-authors-list-in-git/ Erika Savell -Connor Taylor-Brown \ No newline at end of file +Connor Taylor-Brown +Fraser Cope diff --git a/mock/src/main/java/seng302/App.java b/mock/src/main/java/seng302/App.java index 4fb6e21c..2868abbc 100644 --- a/mock/src/main/java/seng302/App.java +++ b/mock/src/main/java/seng302/App.java @@ -10,6 +10,7 @@ import seng302.Model.Event; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; +import java.io.OutputStream; public class App extends Application { @@ -25,9 +26,10 @@ public class App extends Application { @Override public void start(Stage primaryStage) { try { + OutputStream outputStream = System.out;//TEMP currently using System.out, but should replace this with tcp socket we are sending over. RaceDataSource raceData = new RaceXMLReader("raceXML/bermuda_AC35.xml"); RegattaDataSource regattaData = new RegattaXMLReader("mockXML/regattaTest.xml"); - Event raceEvent = new Event(raceData, regattaData); + Event raceEvent = new Event(raceData, regattaData, outputStream); raceEvent.start(); } catch (IOException e) { e.printStackTrace(); diff --git a/mock/src/main/java/seng302/Constants.java b/mock/src/main/java/seng302/Constants.java index b4b3033f..a052b2b0 100644 --- a/mock/src/main/java/seng302/Constants.java +++ b/mock/src/main/java/seng302/Constants.java @@ -11,6 +11,9 @@ public class Constants { public static final int NMToMetersConversion = 1852; // 1 nautical mile = 1852 meters + //Knots x this = meters per second. + public static final double KnotsToMetersPerSecondConversionFactor = 0.514444; + public static final GPSCoordinate startLineMarker1 = new GPSCoordinate(32.296577, -64.854304); public static final GPSCoordinate startLineMarker2 = new GPSCoordinate(32.293771, -64.855242); public static final GPSCoordinate mark1 = new GPSCoordinate(32.293039, -64.843983); diff --git a/mock/src/main/java/seng302/Data/RaceData.java b/mock/src/main/java/seng302/Data/RaceData.java index d16177da..fdc941b6 100644 --- a/mock/src/main/java/seng302/Data/RaceData.java +++ b/mock/src/main/java/seng302/Data/RaceData.java @@ -18,6 +18,7 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import java.io.StringWriter; import java.time.OffsetDateTime; import java.util.List; @@ -47,7 +48,7 @@ public class RaceData { } - public void createXML() { + public String createXML() { try { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); @@ -148,14 +149,13 @@ public class RaceData { TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(doc); - StreamResult result = new StreamResult(System.out); - // Output to console for testing - // StreamResult result = new StreamResult(System.out); + //Serialize document. + StringWriter stringWriter = new StringWriter(); + StreamResult result = new StreamResult(stringWriter); + transformer.transform(source,result); - transformer.transform(source, result); - - System.out.println("File saved!"); + return stringWriter.toString(); } catch (ParserConfigurationException pce) { @@ -164,6 +164,7 @@ public class RaceData { tfe.printStackTrace(); } + return "";//TEMP this is probably bad. This shouldn't really be reached, but seems necessary due to the use of catches above. } diff --git a/mock/src/main/java/seng302/Exceptions/InvalidBoatDataException.java b/mock/src/main/java/seng302/Exceptions/InvalidBoatDataException.java new file mode 100644 index 00000000..7bf7a360 --- /dev/null +++ b/mock/src/main/java/seng302/Exceptions/InvalidBoatDataException.java @@ -0,0 +1,21 @@ +package seng302.Exceptions; + +/** + * Created by f123 on 25-Apr-17. + */ + +/** + * An exception thrown when we cannot generate Boats.xml and send an XML message. + */ +public class InvalidBoatDataException extends RuntimeException +{ + + public InvalidBoatDataException() + { + } + + public InvalidBoatDataException(String message) + { + super(message); + } +} diff --git a/mock/src/main/java/seng302/Exceptions/InvalidRaceDataException.java b/mock/src/main/java/seng302/Exceptions/InvalidRaceDataException.java new file mode 100644 index 00000000..424fb0d5 --- /dev/null +++ b/mock/src/main/java/seng302/Exceptions/InvalidRaceDataException.java @@ -0,0 +1,20 @@ +package seng302.Exceptions; + +/** + * Created by f123 on 25-Apr-17. + */ + +/** + * Exception thrown when we cannot generate Race.xml data, and send an XML message. + */ +public class InvalidRaceDataException extends RuntimeException +{ + public InvalidRaceDataException() + { + } + + public InvalidRaceDataException(String message) + { + super(message); + } +} diff --git a/mock/src/main/java/seng302/Exceptions/InvalidRegattaDataException.java b/mock/src/main/java/seng302/Exceptions/InvalidRegattaDataException.java new file mode 100644 index 00000000..bff8ce03 --- /dev/null +++ b/mock/src/main/java/seng302/Exceptions/InvalidRegattaDataException.java @@ -0,0 +1,20 @@ +package seng302.Exceptions; + +/** + * Created by f123 on 25-Apr-17. + */ + +/** + * An exception thrown when a Regatta.xml message cannot be generated and sent. + */ +public class InvalidRegattaDataException extends RuntimeException +{ + public InvalidRegattaDataException() + { + } + + public InvalidRegattaDataException(String message) + { + super(message); + } +} diff --git a/mock/src/main/java/seng302/Model/BoatInRace.java b/mock/src/main/java/seng302/Model/BoatInRace.java index 50ef24e9..2935d5c9 100644 --- a/mock/src/main/java/seng302/Model/BoatInRace.java +++ b/mock/src/main/java/seng302/Model/BoatInRace.java @@ -29,6 +29,9 @@ public class BoatInRace extends Boat { private StringProperty position; private double heading; + ///While generating BoatLocationMessages, each one needs a sequence number relating to each boat. + private long sequenceNumber = 0; + private boolean trackVisible = true; /** @@ -263,4 +266,18 @@ public class BoatInRace extends Boat { this.position.set(position); } + + /** + * Returns the current sequence number, and increments the internal value, such that that next call will return a value 1 larger than the current call. + * @return Current sequence number. + */ + public long getNextSequenceNumber(){ + //Make a copy of current value. + long oldNumber = this.sequenceNumber; + //Increment. + this.sequenceNumber += 1; + //Return the previous value. + return oldNumber; + } + } diff --git a/mock/src/main/java/seng302/Model/Event.java b/mock/src/main/java/seng302/Model/Event.java index 98c3dceb..8fbb321d 100644 --- a/mock/src/main/java/seng302/Model/Event.java +++ b/mock/src/main/java/seng302/Model/Event.java @@ -6,15 +6,24 @@ import org.w3c.dom.Element; import seng302.Data.RaceData; import seng302.Mock.Regatta; import seng302.Mock.RegattaDataSource; +import seng302.Exceptions.InvalidBoatDataException; +import seng302.Exceptions.InvalidRaceDataException; +import seng302.Exceptions.InvalidRegattaDataException; import seng302.Model.Race; import seng302.RaceDataSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; import java.util.List; /** @@ -24,34 +33,50 @@ public class Event { RaceDataSource raceDataSource; RegattaDataSource regattaDataSource; + ///The stream to which we send all data. + private OutputStream outputStream; - public Event(RaceDataSource raceData, RegattaDataSource regattaData) { + public Event(RaceDataSource raceData, RegattaDataSource regattaData, OutputStream outputStream) { this.raceDataSource = raceData; this.regattaDataSource = regattaData; + this.outputStream = outputStream; } - public void start() { + public void start() + { + System.out.println("\nREGATTA DATA\n");//TEMP REMOVE debug sendRegattaData(); + System.out.println("\nRACE DATA\n");//TEMP REMOVE debug sendRaceData(); + System.out.println("\nBOAT DATA\n");//TEMP REMOVE debug sendBoatData(); - Race newRace = new Race(raceDataSource, 15); + System.out.println("RACE STARTING!!\n\n");//TEMP REMOVE debug + + Race newRace = new Race(raceDataSource, 15, this.outputStream); new Thread((newRace)).start(); } - public void sendRegattaData() { - try { + public void sendRegattaData() throws InvalidRegattaDataException { - Regatta regatta = regattaDataSource.getRegatta(); + Regatta regatta = regattaDataSource.getRegatta(); - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = null; + try + { + docBuilder = docFactory.newDocumentBuilder(); + } + catch (ParserConfigurationException e) + { + throw new InvalidRegattaDataException(); + } - //root element - Document doc = docBuilder.newDocument(); - Element rootElement = doc.createElement("RegattaConfig"); - doc.appendChild(rootElement); + //root element + Document doc = docBuilder.newDocument(); + Element rootElement = doc.createElement("RegattaConfig"); + doc.appendChild(rootElement); //regattaID element Element regattaID = doc.createElement("RegattaID"); @@ -93,121 +118,199 @@ public class Event { magneticVariation.appendChild(doc.createTextNode(Double.toString(regatta.getMagneticVariation()))); rootElement.appendChild(magneticVariation); - TransformerFactory trasformerFactory = TransformerFactory.newInstance(); - Transformer transformer = trasformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = null; + try + { + transformer = transformerFactory.newTransformer(); + } + catch (TransformerConfigurationException e) + { + throw new InvalidRegattaDataException(); + } + DOMSource source = new DOMSource(doc); + + - //print XML object to check for correctness - StreamResult result = new StreamResult(System.out); + //Serialize document. + StringWriter stringWriter = new StringWriter(); + StreamResult result = new StreamResult(stringWriter); + try + { transformer.transform(source,result); + } + catch (TransformerException e) + { + throw new InvalidRegattaDataException(); + } + //TODO now we should place in XML message object. + //TODO now we should serialize xml message object. + //TODO now we should write serialized xml message over this.outputStream. - } catch (Exception e){ - e.printStackTrace(); + try + { + this.outputStream.write(stringWriter.toString().getBytes());//TEMP currently we output the XML doc, not the serialized message. } + catch (IOException e) + { + throw new InvalidRegattaDataException(); + } + } - public void sendRaceData() { + public void sendRaceData() throws InvalidRaceDataException + { RaceData raceData = new RaceData(raceDataSource); - raceData.createXML(); + //Serialize race data to an XML as a string. + String xmlString = raceData.createXML(); + + //TODO now we should place in XML message object. + //TODO now we should serialize xml message object. + //TODO now we should write serialized xml message over this.outputStream. + + try + { + this.outputStream.write(xmlString.getBytes());//TEMP currently we output the XML doc, not the serialized message. + } + catch (IOException e) + { + throw new InvalidRaceDataException(); + } } - public void sendBoatData() { + public void sendBoatData() throws InvalidBoatDataException + { List boatData = raceDataSource.getBoats(); - try { - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - //root element - Document doc = docBuilder.newDocument(); - Element rootElement = doc.createElement("BoatConfig"); - doc.appendChild(rootElement); + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = null; - //Boats element - Element boats = doc.createElement("Boats"); - rootElement.appendChild(boats); + try + { + docBuilder = docFactory.newDocumentBuilder(); + } + catch (ParserConfigurationException e) + { + throw new InvalidBoatDataException(); + } - for (int i=0; i < boatData.size(); i++) { + //root element + Document doc = docBuilder.newDocument(); + Element rootElement = doc.createElement("BoatConfig"); + doc.appendChild(rootElement); - //Boat element - Element boat = doc.createElement("Boat"); + //Boats element + Element boats = doc.createElement("Boats"); + rootElement.appendChild(boats); - //Type attribute - Attr attrType = doc.createAttribute("Type"); - attrType.setValue("Mark"); - boat.setAttributeNode(attrType); + for (int i=0; i < boatData.size(); i++) { - //SourceID attribute - Attr attrSourceID = doc.createAttribute("SourceID"); - attrSourceID.setValue(Integer.toString(boatData.get(i).getSourceID())); - boat.setAttributeNode(attrSourceID); + //Boat element + Element boat = doc.createElement("Boat"); - //ShapeID attribute - Attr attrShapeID = doc.createAttribute("ShapeID"); - attrShapeID.setValue("0"); - boat.setAttributeNode(attrShapeID); + //Type attribute + Attr attrType = doc.createAttribute("Type"); + attrType.setValue("Mark"); + boat.setAttributeNode(attrType); - //HullNum attribute - Attr attrHullNum = doc.createAttribute("HullNum"); - attrHullNum.setValue("RG01"); - boat.setAttributeNode(attrHullNum); + //SourceID attribute + Attr attrSourceID = doc.createAttribute("SourceID"); + attrSourceID.setValue(Integer.toString(boatData.get(i).getSourceID())); + boat.setAttributeNode(attrSourceID); - //StoweName attribute - Attr attrStoweName = doc.createAttribute("StoweName"); - attrStoweName.setValue(boatData.get(i).getAbbrev()); - boat.setAttributeNode(attrStoweName); + //ShapeID attribute + Attr attrShapeID = doc.createAttribute("ShapeID"); + attrShapeID.setValue("0"); + boat.setAttributeNode(attrShapeID); - //ShortName attribute - Attr attrShortName = doc.createAttribute("ShortName"); - attrShortName.setValue(boatData.get(i).getAbbrev()); - boat.setAttributeNode(attrShortName); + //HullNum attribute + Attr attrHullNum = doc.createAttribute("HullNum"); + attrHullNum.setValue("RG01"); + boat.setAttributeNode(attrHullNum); - //BoatName attribute - Attr attrBoatName = doc.createAttribute("BoatName"); - attrBoatName.setValue(boatData.get(i).toString()); - boat.setAttributeNode(attrBoatName); + //StoweName attribute + Attr attrStoweName = doc.createAttribute("StoweName"); + attrStoweName.setValue(boatData.get(i).getAbbrev()); + boat.setAttributeNode(attrStoweName); - //GPSCoord for element - Element GPSCoord = doc.createElement("GPSposition"); + //ShortName attribute + Attr attrShortName = doc.createAttribute("ShortName"); + attrShortName.setValue(boatData.get(i).getAbbrev()); + boat.setAttributeNode(attrShortName); - //Z axis attribute - Attr attrZCoord = doc.createAttribute("Z"); - attrZCoord.setValue("0"); - GPSCoord.setAttributeNode(attrZCoord); + //BoatName attribute + Attr attrBoatName = doc.createAttribute("BoatName"); + attrBoatName.setValue(boatData.get(i).toString()); + boat.setAttributeNode(attrBoatName); - //Y axis attribute - Attr attrYCoord = doc.createAttribute("Y"); - attrYCoord.setValue(Double.toString(boatData.get(i).getCurrentPosition().getLatitude())); - GPSCoord.setAttributeNode(attrYCoord); + //GPSCoord for element + Element GPSCoord = doc.createElement("GPSposition"); - //X axis attribute - Attr attrXCoord = doc.createAttribute("X"); - attrXCoord.setValue(Double.toString(boatData.get(i).getCurrentPosition().getLongitude())); - GPSCoord.setAttributeNode(attrXCoord); + //Z axis attribute + Attr attrZCoord = doc.createAttribute("Z"); + attrZCoord.setValue("0"); + GPSCoord.setAttributeNode(attrZCoord); - //Write GPSCoord to boat - boat.appendChild(GPSCoord); + //Y axis attribute + Attr attrYCoord = doc.createAttribute("Y"); + attrYCoord.setValue(Double.toString(boatData.get(i).getCurrentPosition().getLatitude())); + GPSCoord.setAttributeNode(attrYCoord); - //Write boat to boats - boats.appendChild(boat); + //X axis attribute + Attr attrXCoord = doc.createAttribute("X"); + attrXCoord.setValue(Double.toString(boatData.get(i).getCurrentPosition().getLongitude())); + GPSCoord.setAttributeNode(attrXCoord); - } + //Write GPSCoord to boat + boat.appendChild(GPSCoord); - TransformerFactory trasformerFactory = TransformerFactory.newInstance(); - Transformer transformer = trasformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); + //Write boat to boats + boats.appendChild(boat); - //print XML object to check for correctness - StreamResult result = new StreamResult(System.out); + } + + TransformerFactory trasformerFactory = TransformerFactory.newInstance(); + Transformer transformer = null; + try + { + transformer = trasformerFactory.newTransformer(); + } + catch (TransformerConfigurationException e) + { + throw new InvalidBoatDataException(); + } + DOMSource source = new DOMSource(doc); + + + //Serialize document. + StringWriter stringWriter = new StringWriter(); + StreamResult result = new StreamResult(stringWriter); + try + { transformer.transform(source,result); + } + catch (TransformerException e) + { + throw new InvalidBoatDataException(); + } + //TODO now we should place in XML message object. + //TODO now we should serialize xml message object. + //TODO now we should write serialized xml message over this.outputStream. - } catch (Exception e) { - e.printStackTrace(); + try + { + this.outputStream.write(stringWriter.toString().getBytes());//TEMP currently we output the XML doc, not the serialized message. + } + catch (IOException e) + { + throw new InvalidBoatDataException(); } + } } diff --git a/mock/src/main/java/seng302/Model/Race.java b/mock/src/main/java/seng302/Model/Race.java index 0de5c45f..a1e727f6 100644 --- a/mock/src/main/java/seng302/Model/Race.java +++ b/mock/src/main/java/seng302/Model/Race.java @@ -11,9 +11,12 @@ import org.geotools.referencing.GeodeticCalculator; import seng302.Constants; import seng302.GPSCoordinate; import seng302.RaceDataSource; +import seng302.RaceEventMessages.BoatLocationMessage; import java.awt.geom.Point2D; +import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -38,26 +41,33 @@ public class Race implements Runnable { protected int PRERACE_TIME = 120000; //time in milliseconds to pause during pre-race + //Outputstream to write messages to. + private OutputStream outputStream; + /** * Initailiser for Race * * @param boats Takes in an array of boats that are participating in the race. * @param legs Number of marks in order that the boats pass in order to complete the race. * @param scaleFactor for race + * @param outputStream Outputstream to write messages to. */ - public Race(List boats, List legs, int scaleFactor) { + public Race(List boats, List legs, int scaleFactor, OutputStream outputStream) { this.startingBoats = FXCollections.observableArrayList(boats); this.legs = legs; this.legs.add(new Leg("Finish", this.legs.size())); this.scaleFactor = scaleFactor; + this.outputStream = outputStream; + if (startingBoats != null && startingBoats.size() > 0) { initialiseBoats(); } + } - public Race(RaceDataSource raceData, int scaleFactor) { - this(raceData.getBoats(), raceData.getLegs(), scaleFactor); + public Race(RaceDataSource raceData, int scaleFactor, OutputStream outputStream) { + this(raceData.getBoats(), raceData.getLegs(), scaleFactor, outputStream); } /** @@ -158,20 +168,70 @@ public class Race implements Runnable { } new AnimationTimer() { - - long timeRaceStarted = System.currentTimeMillis(); //start time of loop + //Start time of loop. + long timeRaceStarted = System.currentTimeMillis(); @Override public void handle(long arg0) { if (boatsFinished < startingBoats.size()) { - totalTimeElapsed = System.currentTimeMillis() - timeRaceStarted; + //Get the current time. + long currentTime = System.currentTimeMillis(); + //Update the total elapsed time. + totalTimeElapsed = currentTime - timeRaceStarted; + //For each boat, we update it's position, and generate a BoatLocationMessage. for (BoatInRace boat : startingBoats) { if (boat != null && !boat.isFinished()) { + //Update position. updatePosition(boat, Math.round(1000 / lastFPS) > 20 ? 15 : Math.round(1000 / lastFPS)); checkPosition(boat, totalTimeElapsed); + + + //Generate a boat location message for the updated boat. + BoatLocationMessage boatLocationMessage = new BoatLocationMessage(); + boatLocationMessage.setTime(currentTime); + boatLocationMessage.setSourceID(boat.getSourceID()); + boatLocationMessage.setSequenceNumber(boat.getNextSequenceNumber()); + boatLocationMessage.setDeviceType(BoatLocationMessage.RacingYacht); + + boatLocationMessage.setLatitude(BoatLocationMessage.convertCoordinateDoubleToInt(boat.getCurrentPosition().getLatitude())); + boatLocationMessage.setLongitude(BoatLocationMessage.convertCoordinateDoubleToInt(boat.getCurrentPosition().getLongitude())); + + boatLocationMessage.setAltitude(0);//Junk value. + boatLocationMessage.setHeading(BoatLocationMessage.convertHeadingDoubleToInt(boat.getHeading())); + + boatLocationMessage.setPitch((short)0);//Junk value. + boatLocationMessage.setRoll((short)0);//Junk value. + + boatLocationMessage.setBoatSpeed(BoatLocationMessage.convertBoatSpeedDoubleToInt(boat.getVelocity())); + boatLocationMessage.setBoatCOG(0);//Junk value. + boatLocationMessage.setBoatSOG(0);//Junk value. + + boatLocationMessage.setApparentWindSpeed(0);//Junk value. + boatLocationMessage.setApparentWindAngle((short)0);//Junk value. + boatLocationMessage.setTrueWindSpeed(0);//Junk value. + boatLocationMessage.setTrueWindAngle((short)0);//Junk value. + + boatLocationMessage.setCurrentDrift(0);//Junk value. + boatLocationMessage.setCurrentSet(0);//Junk value. + boatLocationMessage.setRudderAngle((short)0);//Junk value. + + //We have finished creating the message. + //TODO at this point, we need to send the event to the visualiser. + //System.out.println(boatLocationMessage);//TEMP debug print + try + { + //TODO we should actually serialize the boat message before writing to output. + outputStream.write(boatLocationMessage.toString().getBytes()); + } + catch (IOException e) + { + e.printStackTrace(); + } + + } else { - System.out.println("Race is over"); + System.out.println("Race is over");//TEMP debug print //raceFinish = true; stop(); } @@ -194,7 +254,7 @@ public class Race implements Runnable { boat.setPosition("-"); } } - System.out.println("====="); + System.out.println("=====");//TEMP debug print } public void initialiseBoats() { diff --git a/mock/src/main/java/seng302/RaceEventMessages/BoatLocationMessage.java b/mock/src/main/java/seng302/RaceEventMessages/BoatLocationMessage.java index 9bacd840..425b86f0 100644 --- a/mock/src/main/java/seng302/RaceEventMessages/BoatLocationMessage.java +++ b/mock/src/main/java/seng302/RaceEventMessages/BoatLocationMessage.java @@ -4,6 +4,8 @@ package seng302.RaceEventMessages; * Created by f123 on 21-Apr-17. */ +import seng302.Constants; + /** * Represents the information in a boat location message (AC streaming spec: 4.9). */ @@ -442,4 +444,112 @@ public class BoatLocationMessage return angleShort; } + + /** + * Converts a double representing the speed of a boat in knots to an int in millimeters per second, as required by the streaming spec format. + * @param speed Speed in knots, stored as a double. + * @return Speed in millimeters per second, stored as an int (using only the two least significant bytes). + */ + public static int convertBoatSpeedDoubleToInt(double speed) + { + //Calculate meters per second. + double metersPerSecond = speed * Constants.KnotsToMetersPerSecondConversionFactor; + + //Calculate millimeters per second. + double millimetersPerSecond = metersPerSecond * 1000.0; + + //Convert to an int. + int millimetersPerSecondInt = (int)Math.round(millimetersPerSecond); + + return millimetersPerSecondInt; + } + + + /** + * Converts an int representing the speed of a boat in millimeters per second to a double in knots, as required by the streaming spec format. + * @param speed Speed in millimeters per second, stored as an int. + * @return Speed in knots, stored as a double. + */ + public static double convertBoatSpeedIntToDouble(int speed) + { + //Calculate meters per second. + double metersPerSecond = speed / 1000.0; + + //Calculate knots. + double knots = metersPerSecond / Constants.KnotsToMetersPerSecondConversionFactor; + + return knots; + } + + + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(); + + builder.append("Message version number: "); + builder.append(this.getMessageVersionNumber()); + + builder.append("\nTime: "); + builder.append(this.getTime()); + + builder.append("\nSource ID: "); + builder.append(this.getSourceID()); + + builder.append("\nSequence number: "); + builder.append(this.getSequenceNumber()); + + builder.append("\nDevice type: "); + builder.append(this.getDeviceType()); + + builder.append("\nLatitude: "); + builder.append(this.getLatitude()); + + builder.append("\nLongitude: "); + builder.append(this.getLongitude()); + + builder.append("\nAltitude: "); + builder.append(this.getAltitude()); + + builder.append("\nHeading: "); + builder.append(this.getHeading()); + + builder.append("\nPitch: "); + builder.append(this.getPitch()); + + builder.append("\nRoll: "); + builder.append(this.getRoll()); + + builder.append("\nBoat speed (mm/sec): "); + builder.append(this.getBoatSpeed()); + + builder.append("\nBoat COG: "); + builder.append(this.getBoatCOG()); + + builder.append("\nBoat SOG: "); + builder.append(this.getBoatSOG()); + + builder.append("\nApparent wind speed: "); + builder.append(this.getApparentWindSpeed()); + + builder.append("\nApparent wind angle: "); + builder.append(this.getApparentWindAngle()); + + builder.append("\nTrue wind speed: "); + builder.append(this.getTrueWindSpeed()); + + builder.append("\nTrue wind angle: "); + builder.append(this.getTrueWindAngle()); + + builder.append("\nCurrent drift: "); + builder.append(this.getCurrentDrift()); + + builder.append("\nCurrent set: "); + builder.append(this.getCurrentSet()); + + builder.append("\nRudder angle: "); + builder.append(this.getRudderAngle()); + + return builder.toString(); + } }