Refactored BoatLocation message, encoders, decoders. It now exposes datatypes that we actually use in the program (double knots, bearings, etc..), instead of the bits-on-the-wire packed units (like int mmPerSec). Also documented it, and updated test.

issue #35 #36
#story[1095]
main
fjc40 8 years ago
parent ff262a6227
commit 1fbdd09d70

@ -2,9 +2,11 @@ package mock.model;
import network.Messages.BoatLocation;
import network.Messages.BoatStatus;
import network.Messages.Enums.BoatLocationDeviceEnum;
import network.Messages.LatestMessages;
import network.Messages.RaceStatus;
import network.Utils.AC35UnitConverter;
import shared.model.Bearing;
import shared.model.CompoundMark;
import shared.model.Constants;
import shared.model.Mark;
@ -47,7 +49,9 @@ public class RaceServer {
mark.getPosition().getLatitude(),
mark.getPosition().getLongitude(),
this.boatLocationSequenceNumber,
0, 0,
BoatLocationDeviceEnum.Mark,
Bearing.fromDegrees(0),
0,
race.getRaceClock().getCurrentTimeMilli());
//Iterates the sequence number.
@ -99,7 +103,8 @@ public class RaceServer {
boat.getCurrentPosition().getLatitude(),
boat.getCurrentPosition().getLongitude(),
this.boatLocationSequenceNumber,
boat.getBearing().degrees(),
BoatLocationDeviceEnum.RacingYacht,
boat.getBearing(),
boat.getCurrentSpeed(),
race.getRaceClock().getCurrentTimeMilli());

@ -2,12 +2,14 @@ package network.MessageDecoders;
import network.Messages.BoatLocation;
import network.Messages.Enums.BoatLocationDeviceEnum;
import shared.model.Azimuth;
import shared.model.Bearing;
import java.util.Arrays;
import static network.Utils.ByteConverter.bytesToInt;
import static network.Utils.ByteConverter.bytesToLong;
import static network.Utils.ByteConverter.bytesToShort;
import static network.Utils.AC35UnitConverter.*;
import static network.Utils.ByteConverter.*;
/**
@ -45,96 +47,110 @@ public class BoatLocationDecoder {
*/
private void decode() {
byte[] messageVersionNumber = Arrays.copyOfRange(encodedMessage, 0, 1);
byte numMessageVersionNumber = messageVersionNumber[0];
byte[] messageVersionNumberBytes = Arrays.copyOfRange(encodedMessage, 0, 1);
byte messageVersionNumber = messageVersionNumberBytes[0];
byte[] time = Arrays.copyOfRange(encodedMessage, 1, 7);
long numTime = bytesToLong(time);
byte[] timeBytes = Arrays.copyOfRange(encodedMessage, 1, 7);
long time = bytesToLong(timeBytes);
byte[] sourceID = Arrays.copyOfRange(encodedMessage, 7, 11);
int numSourceID = bytesToInt(sourceID);
byte[] sourceIDBytes = Arrays.copyOfRange(encodedMessage, 7, 11);
int sourceID = bytesToInt(sourceIDBytes);
byte[] seqNum = Arrays.copyOfRange(encodedMessage, 11, 15);
int numSeqNum = bytesToInt(seqNum);
byte[] seqNumBytes = Arrays.copyOfRange(encodedMessage, 11, 15);
int seqNum = bytesToInt(seqNumBytes);
byte[] deviceType = Arrays.copyOfRange(encodedMessage, 15, 16);
byte numDeviceType = deviceType[0];
byte[] deviceTypeBytes = Arrays.copyOfRange(encodedMessage, 15, 16);
BoatLocationDeviceEnum deviceType = BoatLocationDeviceEnum.fromByte(deviceTypeBytes[0]);
byte[] latitude = Arrays.copyOfRange(encodedMessage, 16, 20);
int numLatitude = bytesToInt(latitude);
byte[] latitudeBytes = Arrays.copyOfRange(encodedMessage, 16, 20);
int numLatitude = bytesToInt(latitudeBytes);
double latitude = unpackGPS(numLatitude);
byte[] longitude = Arrays.copyOfRange(encodedMessage, 20, 24);
int numLongitude = bytesToInt(longitude);
byte[] longitudeBytes = Arrays.copyOfRange(encodedMessage, 20, 24);
int numLongitude = bytesToInt(longitudeBytes);
double longitude = unpackGPS(numLongitude);
byte[] altitude = Arrays.copyOfRange(encodedMessage, 24, 28);
int numAltitude = bytesToInt(altitude);
byte[] altitudeBytes = Arrays.copyOfRange(encodedMessage, 24, 28);
int numAltitude = bytesToInt(altitudeBytes);
byte[] heading = Arrays.copyOfRange(encodedMessage, 28, 30);
int numHeading = bytesToInt(heading);
byte[] headingBytes = Arrays.copyOfRange(encodedMessage, 28, 30);
int numHeading = bytesToInt(headingBytes);
Bearing heading = Bearing.fromDegrees(unpackHeading(numHeading));
byte[] pitch = Arrays.copyOfRange(encodedMessage, 30, 32);
short numPitch = bytesToShort(pitch);
byte[] pitchBytes = Arrays.copyOfRange(encodedMessage, 30, 32);
short numPitch = bytesToShort(pitchBytes);
byte[] roll = Arrays.copyOfRange(encodedMessage, 32, 34);
short numRoll = bytesToShort(roll);
byte[] rollBytes = Arrays.copyOfRange(encodedMessage, 32, 34);
short numRoll = bytesToShort(rollBytes);
byte[] boatSpeed = Arrays.copyOfRange(encodedMessage, 34, 36);
int numBoatSpeed = bytesToInt(boatSpeed);
byte[] boatSpeedBytes = Arrays.copyOfRange(encodedMessage, 34, 36);
int numBoatSpeed = bytesToInt(boatSpeedBytes);
double boatSpeedKnots = unpackMMperSecToKnots(numBoatSpeed);
byte[] cog = Arrays.copyOfRange(encodedMessage, 36, 38);
int numCog = bytesToInt(cog);
byte[] cogBytes = Arrays.copyOfRange(encodedMessage, 36, 38);
int numCog = bytesToInt(cogBytes);
Bearing cog = Bearing.fromDegrees(unpackHeading(numCog));
byte[] sog = Arrays.copyOfRange(encodedMessage, 38, 40);
int numSog = bytesToInt(sog);
byte[] sogBytes = Arrays.copyOfRange(encodedMessage, 38, 40);
int numSog = bytesToInt(sogBytes);
double sogKnots = unpackMMperSecToKnots(numSog);
byte[] apparentWindSpeed = Arrays.copyOfRange(encodedMessage, 40, 42);
int numApparentWindSpeed = bytesToInt(apparentWindSpeed);
byte[] apparentWindSpeedBytes = Arrays.copyOfRange(encodedMessage, 40, 42);
int numApparentWindSpeed = bytesToInt(apparentWindSpeedBytes);
double apparentWindSpeedKnots = unpackMMperSecToKnots(numApparentWindSpeed);
byte[] apparentWindAngle = Arrays.copyOfRange(encodedMessage, 42, 44);
short numApparentWindAngle = bytesToShort(apparentWindAngle);
byte[] apparentWindAngleBytes = Arrays.copyOfRange(encodedMessage, 42, 44);
short numApparentWindAngle = bytesToShort(apparentWindAngleBytes);
Azimuth apparentWindAngle = Azimuth.fromDegrees(unpackTrueWindAngle(numApparentWindAngle));
byte[] trueWindSpeed = Arrays.copyOfRange(encodedMessage, 44, 46);
int numTrueWindSpeed = bytesToInt(trueWindSpeed);
byte[] trueWindSpeedBytes = Arrays.copyOfRange(encodedMessage, 44, 46);
int numTrueWindSpeed = bytesToInt(trueWindSpeedBytes);
double trueWindSpeedKnots = unpackMMperSecToKnots(numTrueWindSpeed);
byte[] trueWindDirection = Arrays.copyOfRange(encodedMessage, 46, 48);
short numTrueWindDirection = bytesToShort(trueWindDirection);
byte[] trueWindDirectionBytes = Arrays.copyOfRange(encodedMessage, 46, 48);
short numTrueWindDirection = bytesToShort(trueWindDirectionBytes);
Bearing trueWindDirection = Bearing.fromDegrees(unpackHeading(numTrueWindDirection));
byte[] trueWindAngle = Arrays.copyOfRange(encodedMessage, 48, 50);
short numTrueWindAngle = bytesToShort(trueWindAngle);
byte[] trueWindAngleBytes = Arrays.copyOfRange(encodedMessage, 48, 50);
short numTrueWindAngle = bytesToShort(trueWindAngleBytes);
Azimuth trueWindAngle = Azimuth.fromDegrees(unpackTrueWindAngle(numTrueWindAngle));
byte[] currentDrift = Arrays.copyOfRange(encodedMessage, 50, 52);
int numCurrentDrift = bytesToInt(currentDrift);
byte[] currentDriftBytes = Arrays.copyOfRange(encodedMessage, 50, 52);
int numCurrentDrift = bytesToInt(currentDriftBytes);
double currentDriftKnots = unpackMMperSecToKnots(numCurrentDrift);
byte[] currentSet = Arrays.copyOfRange(encodedMessage, 52, 54);
int numCurrentSet = bytesToShort(currentSet);
byte[] currentSetBytes = Arrays.copyOfRange(encodedMessage, 52, 54);
int numCurrentSet = bytesToShort(currentSetBytes);
Bearing currentSet = Bearing.fromDegrees(unpackHeading(numCurrentSet));
byte[] rudderAngle = Arrays.copyOfRange(encodedMessage, 54, 56);
short numRudderAngle = bytesToShort(rudderAngle);
byte[] rudderAngleBytes = Arrays.copyOfRange(encodedMessage, 54, 56);
short numRudderAngle = bytesToShort(rudderAngleBytes);
Azimuth rudderAngle = Azimuth.fromDegrees(unpackTrueWindAngle(numRudderAngle));
message = new BoatLocation(
numMessageVersionNumber,
numTime,
numSourceID,
numSeqNum,
numDeviceType,
numLatitude,
numLongitude,
messageVersionNumber,
time,
sourceID,
seqNum,
deviceType,
latitude,
longitude,
numAltitude,
numHeading,
heading,
numPitch,
numRoll,
numBoatSpeed,
numCog,
numSog,
numApparentWindSpeed,
numApparentWindAngle,
numTrueWindSpeed,
numTrueWindDirection,
numTrueWindAngle,
numCurrentDrift,
numCurrentSet,
numRudderAngle );
boatSpeedKnots,
cog,
sogKnots,
apparentWindSpeedKnots,
apparentWindAngle,
trueWindSpeedKnots,
trueWindDirection,
trueWindAngle,
currentDriftKnots,
currentSet,
rudderAngle );
}

@ -6,6 +6,7 @@ import network.Messages.BoatLocation;
import java.nio.ByteBuffer;
import static network.Utils.AC35UnitConverter.*;
import static network.Utils.ByteConverter.intToBytes;
import static network.Utils.ByteConverter.longToBytes;
@ -34,24 +35,24 @@ public class BoatLocationEncoder implements MessageEncoder {
byte[] time = longToBytes(boatLocation.getTime(), 6);
byte[] sourceID = intToBytes(boatLocation.getSourceID(), 4);
byte[] seqNum = longToBytes(boatLocation.getSequenceNumber(), 4);
byte[] deviceType = intToBytes(boatLocation.getDeviceType(), 1);
byte[] latitude = intToBytes(boatLocation.getLatitude(), 4);
byte[] longitude = intToBytes(boatLocation.getLongitude(), 4);
byte[] deviceType = intToBytes(boatLocation.getDeviceType().getValue(), 1);
byte[] latitude = intToBytes(packGPS(boatLocation.getLatitude()), 4);
byte[] longitude = intToBytes(packGPS(boatLocation.getLongitude()), 4);
byte[] altitude = intToBytes(boatLocation.getAltitude(), 4);
byte[] heading = intToBytes(boatLocation.getHeading(), 2);
byte[] heading = intToBytes(packHeading(boatLocation.getHeading().degrees()), 2);
byte[] pitch = intToBytes(boatLocation.getPitch(), 2);
byte[] roll = intToBytes(boatLocation.getRoll(), 2);
byte[] boatSpeed = intToBytes(boatLocation.getBoatSpeed(), 2);
byte[] cog = intToBytes(boatLocation.getBoatCOG(), 2);
byte[] sog = intToBytes(boatLocation.getBoatSOG(), 2);
byte[] apparentWindSpeed = intToBytes(boatLocation.getApparentWindSpeed(), 2);
byte[] apparentWindAngle = intToBytes(boatLocation.getApparentWindAngle(), 2);
byte[] trueWindSpeed = intToBytes(boatLocation.getTrueWindSpeed(), 2);
byte[] trueWindDirection = intToBytes(boatLocation.getTrueWindDirection(), 2);
byte[] trueWindAngle = intToBytes(boatLocation.getTrueWindAngle(), 2);
byte[] currentDrift = intToBytes(boatLocation.getCurrentDrift(), 2);
byte[] currentSet = intToBytes(boatLocation.getCurrentSet(), 2);
byte[] rudderAngle = intToBytes(boatLocation.getRudderAngle(), 2);
byte[] boatSpeed = intToBytes(packKnotsToMMperSec(boatLocation.getBoatSpeedKnots()), 2);
byte[] cog = intToBytes(packHeading(boatLocation.getBoatCOG().degrees()), 2);
byte[] sog = intToBytes(packKnotsToMMperSec(boatLocation.getBoatSOGKnots()), 2);
byte[] apparentWindSpeed = intToBytes(packKnotsToMMperSec(boatLocation.getApparentWindSpeedKnots()), 2);
byte[] apparentWindAngle = intToBytes(packTrueWindAngle(boatLocation.getApparentWindAngle().degrees()), 2);
byte[] trueWindSpeed = intToBytes(packKnotsToMMperSec(boatLocation.getTrueWindSpeedKnots()), 2);
byte[] trueWindDirection = intToBytes(packHeading(boatLocation.getTrueWindDirection().degrees()), 2);
byte[] trueWindAngle = intToBytes(packTrueWindAngle(boatLocation.getTrueWindAngle().degrees()), 2);
byte[] currentDrift = intToBytes(packKnotsToMMperSec(boatLocation.getCurrentDriftKnots()), 2);
byte[] currentSet = intToBytes(packHeading(boatLocation.getCurrentSet().degrees()), 2);
byte[] rudderAngle = intToBytes(packTrueWindAngle(boatLocation.getRudderAngle().degrees()), 2);
ByteBuffer result = ByteBuffer.allocate(56);
result.put(messageVersionBytes);

@ -1,130 +1,188 @@
package network.Messages;
import network.Messages.Enums.BoatLocationDeviceEnum;
import network.Messages.Enums.MessageType;
import network.Utils.AC35UnitConverter;
import shared.model.Constants;
import shared.model.Azimuth;
import shared.model.Bearing;
import static network.Utils.AC35UnitConverter.unpackGPS;
import static network.Utils.AC35UnitConverter.packGPS;
/**
* Represents the information in a boat location message (AC streaming spec: 4.9).
*/
public class BoatLocation extends AC35Data {
//TODO move these to an enum.
public static final byte Unknown = 0;
public static final byte RacingYacht = 1;
public static final byte CommitteeBoat = 2;
public static final byte Mark = 3;
public static final byte Pin = 4;
public static final byte ChaseBoat = 5;
public static final byte MedicalBoat = 6;
public static final byte MarshallBoat = 7;
public static final byte UmpireBoat = 8;
public static final byte UmpireSoftwareApplication = 9;
public static final byte PrincipalRaceOfficerApplication = 10;
public static final byte WeatherStation = 11;
public static final byte Helicopter = 12;
public static final byte DataProcessingApplication = 13;
/**
* The current messageVersionNumber according to the API spec.
*/
public static final byte currentMessageVersionNumber = 1;
///Version number of the message.
/**
* Version number of the message.
*/
private byte messageVersionNumber;
///Time of the event - milliseconds since jan 1 1970. Proper type is 6 byte int.
/**
* Time of the event - milliseconds since jan 1 1970. Proper type is 6 byte int.
*/
private long time;
///Source ID of the boat.
/**
* Source ID of the boat.
*/
private int sourceID;
///Sequence number of the message.
/**
* Sequence number of the message.
*/
private long sequenceNumber;
///Device type of the message (physical source of the message).
private byte deviceType;
///Latitude of the boat.
private int latitude;
///Longitude of the boat.
private int longitude;
/**
* Device type of the message (physical source of the message).
*/
private BoatLocationDeviceEnum deviceType;
///Altitude of the boat.
/**
* Latitude of the boat.
*/
private double latitude;
/**
* Longitude of the boat.
*/
private double longitude;
/**
* Altitude of the boat.
*/
private int altitude;
///Heading of the boat. Clockwise, 0 = north. Proper type is unsigned 2 byte int.
private int heading;
/**
* Heading of the boat. Clockwise, 0 = north. Proper type is unsigned 2 byte int.
*/
private Bearing heading;
///Pitch of the boat.
/**
* Pitch of the boat.
*/
private short pitch;
///Roll of the boat.
/**
* Roll of the boat.
*/
private short roll;
///Speed of the boat. Proper type is unsigned 2 byte int. millimeters per second.
private int boatSpeed;
/**
* Speed of the boat, in knots.
*/
private double boatSpeedKnots;
///Course over ground (COG) of the boat. Proper type is unsigned 2 byte int.
private int boatCOG;
/**
* Course over ground (COG) of the boat.
*/
private Bearing boatCOG;
///Speed over ground (SOG) of the boat. Proper type is unsigned 2 byte int. millimeters per second.
private int boatSOG;
/**
* Speed over ground (SOG) of the boat, in knots.
*/
private double boatSOGKnots;
///Apparent wind speed at time of event. Proper type is unsigned 2 byte int. millimeters per second.
private int apparentWindSpeed;
/**
* Apparent wind speed at time of event. Proper type is unsigned 2 byte int. millimeters per second.
*/
private double apparentWindSpeedKnots;
///Apparent wind angle at time of the event. Wind over starboard = positive.
private short apparentWindAngle;
/**
* Apparent wind angle at time of the event. Wind over starboard = positive.
*/
private Azimuth apparentWindAngle;
///True wind speed. Proper type is unsigned 2 byte int. millimeters per second.
private int trueWindSpeed;
/**
* True wind speed, in knots.
*/
private double trueWindSpeedKnots;
///True wind direction. Proper type is unsigned 2 byte int. 0x0000 = North, etc..
private int trueWindDirection;
/**
* True wind direction.
*/
private Bearing trueWindDirection;
///True wind angle. Clockwise compass direction, 0 = north.
private short trueWindAngle;
/**
* True wind angle. Clockwise compass direction, 0 = north.
*/
private Azimuth trueWindAngle;
///Current drift. Proper type is unsigned 2 byte int. millimeters per second.
private int currentDrift;
/**
* Current drift, in knots.
*/
private double currentDriftKnots;
///Current set. Proper type is unsigned 2 byte int. Clockwise compass direction, 0 = north.
private int currentSet;
/**
* Current set.
*/
private Bearing currentSet;
///Rudder angle. Positive is rudder set to turn yacht to port.
private short rudderAngle;
/**
* Rudder angle. Positive is rudder set to turn yacht to port.
*/
private Azimuth rudderAngle;
/**
* Ctor, with all parameters.
* Constructs a BoatLocation message with the given parameters.
*
* @param messageVersionNumber message number
* @param time time of message
* @param sourceID id of boat
* @param sequenceNumber number of boat message
* @param deviceType type of boat
* @param deviceType The source of the BoatLocation message.
* @param latitude lat of boat
* @param longitude lon of boat
* @param altitude altitude of boat
* @param heading heading of boat
* @param pitch pitch of boat
* @param roll roll of boat
* @param boatSpeed boats speed
* @param boatSpeedKnots boats speed
* @param boatCOG boat cog
* @param boatSOG boat sog
* @param apparentWindSpeed wind speed
* @param boatSOGKnots boat sog
* @param apparentWindSpeedKnots wind speed
* @param apparentWindAngle wind angle
* @param trueWindSpeed true wind speed
* @param trueWindSpeedKnots true wind speed
* @param trueWindDirection true wind direction
* @param trueWindAngle true wind angle
* @param currentDrift current drift
* @param currentDriftKnots current drift
* @param currentSet current set
* @param rudderAngle rudder angle
*/
public BoatLocation(byte messageVersionNumber, long time, int sourceID, long sequenceNumber, byte deviceType, int latitude, int longitude, int altitude, int heading, short pitch, short roll, int boatSpeed, int boatCOG, int boatSOG, int apparentWindSpeed, short apparentWindAngle, int trueWindSpeed, int trueWindDirection, short trueWindAngle, int currentDrift, int currentSet, short rudderAngle) {
public BoatLocation(
byte messageVersionNumber,
long time,
int sourceID,
long sequenceNumber,
BoatLocationDeviceEnum deviceType,
double latitude,
double longitude,
int altitude,
Bearing heading,
short pitch,
short roll,
double boatSpeedKnots,
Bearing boatCOG,
double boatSOGKnots,
double apparentWindSpeedKnots,
Azimuth apparentWindAngle,
double trueWindSpeedKnots,
Bearing trueWindDirection,
Azimuth trueWindAngle,
double currentDriftKnots,
Bearing currentSet,
Azimuth rudderAngle ) {
super(MessageType.BOATLOCATION);
this.messageVersionNumber = messageVersionNumber;
@ -138,348 +196,251 @@ public class BoatLocation extends AC35Data {
this.heading = heading;
this.pitch = pitch;
this.roll = roll;
this.boatSpeed = boatSpeed;
this.boatSpeedKnots = boatSpeedKnots;
this.boatCOG = boatCOG;
this.boatSOG = boatSOG;
this.apparentWindSpeed = apparentWindSpeed;
this.boatSOGKnots = boatSOGKnots;
this.apparentWindSpeedKnots = apparentWindSpeedKnots;
this.apparentWindAngle = apparentWindAngle;
this.trueWindSpeed = trueWindSpeed;
this.trueWindSpeedKnots = trueWindSpeedKnots;
this.trueWindDirection = trueWindDirection;
this.trueWindAngle = trueWindAngle;
this.currentDrift = currentDrift;
this.currentDriftKnots = currentDriftKnots;
this.currentSet = currentSet;
this.rudderAngle = rudderAngle;
}
public BoatLocation(int sourceID, double lat, double lon, long sequenceNumber, double heading, double boatSpeed, long time) {
super(MessageType.BOATLOCATION);
this.messageVersionNumber = BoatLocation.currentMessageVersionNumber;
this.time = time;
this.sourceID = sourceID;
this.sequenceNumber = sequenceNumber;
this.deviceType = 1;
this.latitude = packGPS(lat);
this.longitude = packGPS(lon);
this.altitude = 0;
this.heading = convertHeadingDoubleToInt(heading);
this.pitch = 0;
this.roll = 0;
this.boatSpeed = convertBoatSpeedDoubleToInt(boatSpeed);
this.boatCOG = 0;
this.boatSOG = convertBoatSpeedDoubleToInt(boatSpeed);
this.apparentWindSpeed = 0;
this.apparentWindAngle = 0;
this.trueWindSpeed = 0;
this.trueWindDirection = 0;
this.trueWindAngle = 0;
this.currentDrift = 0;
this.currentSet = 0;
this.rudderAngle = 0;
}
//Getters and setters for message properties.
/**
* Converts a double representing a latitude or longitude coordinate to an int, as required by the streaming spec format.
*
* @param coordinate Latitude or longitude to convert. Double.
* @return int representation of coordinate.
*/
public static int convertCoordinateDoubleToInt(double coordinate) {
int coordinateInt = (int) ((coordinate / 180.0) * 2147483648.0);
return coordinateInt;
}
/**
* Converts an int representing a latitude or longitude coordinate to a double, as required by the streaming spec format.
*
* @param coordinate Latitude or longitude to convert. int.
* @return double representation of coordinate.
*/
public static double convertCoordinateIntToDouble(int coordinate) {
double coordinateDouble = (double) ((coordinate * 180.0) / 2147483648.0);
return coordinateDouble;
}
/**
* Converts an int representing a heading to a double, as required by the streaming spec format.
*
* @param heading Heading to convert. int.
* @return double representation of heading.
*/
public static double convertHeadingIntToDouble(int heading) {
double headingDouble = (double) ((heading * 360.0) / 65536.0);
return headingDouble;
}
/**
* Converts a double representing a heading to an int, as required by the streaming spec format.
*
* @param heading Heading to convert. double.
* @return int representation of heading.
*/
public static int convertHeadingDoubleToInt(double heading) {
int headingInt = (int) ((heading * 65536.0) / 360.0);
return headingInt;
public BoatLocation(
int sourceID,
double lat,
double lon,
long sequenceNumber,
BoatLocationDeviceEnum deviceType,
Bearing heading,
double boatSpeedKnots,
long time ) {
this(
BoatLocation.currentMessageVersionNumber,
time,
sourceID,
sequenceNumber,
deviceType,
lat,
lon,
0,
heading,
(short) 0,
(short) 0,
boatSpeedKnots,
heading,
boatSpeedKnots,
0,
Azimuth.fromDegrees(0),
0,
Bearing.fromDegrees(0),
Azimuth.fromDegrees(0),
0,
Bearing.fromDegrees(0),
Azimuth.fromDegrees(0) );
}
/**
* Converts a short representing the wind's true angle to a double, as required by the streaming spec format.
*
* @param angle Angle to convert. short.
* @return double representation of heading.
*/
public static double convertTrueWindAngleShortToDouble(short angle) {
double angleDouble = (double) ((angle * 180.0) / 32768.0);
return angleDouble;
}
/**
* Converts a double representing the wind's true angle to a short, as required by the streaming spec format.
*
* @param angle Angle to convert. double.
* @return short representation of heading.
*/
public static short convertTrueWindAngleDoubleToShort(double angle) {
short angleShort = (short) ((angle / 180.0) * 32768.0);
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 millimeters per second.
double millimetersPerSecond = speed * Constants.KnotsToMMPerSecond;
//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.
* Returns the version number of the message.
* @return The version number of the message.
*/
public static double convertBoatSpeedIntToDouble(int speed) {
//Calculate knots.
double knots = speed / Constants.KnotsToMMPerSecond;
return knots;
}
public byte getMessageVersionNumber() {
return messageVersionNumber;
}
public void setMessageVersionNumber(byte messageVersionNumber) {
this.messageVersionNumber = messageVersionNumber;
}
/**
* Returns the time that this message was generated at.
* @return Time message was generated at, in milliseconds since unix epoch.
*/
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
/**
* Returns the sourceID of the boat this message relates to.
* @return SourceID of the boat this message relates to.
*/
public int getSourceID() {
return sourceID;
}
public void setSourceID(int sourceID) {
this.sourceID = sourceID;
}
/**
* Returns the sequence number of this message.
* @return The sequence number of the message.
*/
public long getSequenceNumber() {
return sequenceNumber;
}
public void setSequenceNumber(long sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
public byte getDeviceType() {
/**
* Returns the device source of this message.
* @return The device source of this message.
*/
public BoatLocationDeviceEnum getDeviceType() {
return deviceType;
}
public void setDeviceType(byte deviceType) {
this.deviceType = deviceType;
}
public int getLatitude() {
/**
* Returns the latitude, in degrees, that the boat is located at.
* @return Latitude, in degrees, of boat.
*/
public double getLatitude() {
return latitude;
}
public void setLatitude(int latitude) {
this.latitude = latitude;
}
public int getLongitude() {
/**
* Returns the longitude, in degrees, that the boat is located at.
* @return Longitude, in degrees, of boat.
*/
public double getLongitude() {
return longitude;
}
public double getLatitudeDouble(){
return unpackGPS(this.latitude);
}
public double getLongitudeDouble(){
return unpackGPS(this.longitude);
}
public void setLongitude(int longitude) {
this.longitude = longitude;
}
/**
* Returns the altitude of the boat.
* @return The altitude of the boat.
*/
public int getAltitude() {
return altitude;
}
public void setAltitude(int altitude) {
this.altitude = altitude;
}
public int getHeading() {
/**
* Returns the current heading/bearing of the boat.
* @return Heading of the boat.
*/
public Bearing getHeading() {
return heading;
}
public void setHeading(int heading) {
this.heading = heading;
}
/**
* Returns the current pitch of the boat.
* @return Pitch of the boat.
*/
public short getPitch() {
return pitch;
}
public void setPitch(short pitch) {
this.pitch = pitch;
}
/**
* Returns the current roll of the boat.
* @return Roll of the boat.
*/
public short getRoll() {
return roll;
}
public void setRoll(short roll) {
this.roll = roll;
}
public int getBoatSpeed() {
return boatSpeed;
/**
* Returns the current boat speed, in knots.
* @return Current boat speed, in knots.
*/
public double getBoatSpeedKnots() {
return boatSpeedKnots;
}
public void setBoatSpeed(int boatSpeed) {
this.boatSpeed = boatSpeed;
}
public int getBoatCOG() {
/**
* Returns the boat's Course Over Ground.
* @return Boat's COG.
*/
public Bearing getBoatCOG() {
return boatCOG;
}
public void setBoatCOG(int boatCOG) {
this.boatCOG = boatCOG;
}
public int getBoatSOG() {
return boatSOG;
}
public void setBoatSOG(int boatSOG) {
this.boatSOG = boatSOG;
/**
* Returns the boats Speed Over Ground, in knots.
* @return Boat's SOG.
*/
public double getBoatSOGKnots() {
return boatSOGKnots;
}
public int getApparentWindSpeed() {
return apparentWindSpeed;
}
public void setApparentWindSpeed(int apparentWindSpeed) {
this.apparentWindSpeed = apparentWindSpeed;
/**
* Returns the apparent wind speed, in knots, at the boat.
* @return Wind speed, in knots, at the boat.
*/
public double getApparentWindSpeedKnots() {
return apparentWindSpeedKnots;
}
public short getApparentWindAngle() {
/**
* Returns the apparent wind angle at the boat.
* @return Wind angle at the boat.
*/
public Azimuth getApparentWindAngle() {
return apparentWindAngle;
}
public void setApparentWindAngle(short apparentWindAngle) {
this.apparentWindAngle = apparentWindAngle;
}
public int getTrueWindSpeed() {
return trueWindSpeed;
/**
* Returns the true wind speed, in knots.
* @return True wind speed, in knots.
*/
public double getTrueWindSpeedKnots() {
return trueWindSpeedKnots;
}
public void setTrueWindSpeed(int trueWindSpeed) {
this.trueWindSpeed = trueWindSpeed;
}
public int getTrueWindDirection()
/**
* Returns the true wind direction.
* @return True wind direction.
*/
public Bearing getTrueWindDirection()
{
return trueWindDirection;
}
public void setTrueWindDirection(int trueWindDirection)
{
this.trueWindDirection = trueWindDirection;
}
public short getTrueWindAngle() {
/**
* Returns the true wind angle.
* @return True wind angle.
*/
public Azimuth getTrueWindAngle() {
return trueWindAngle;
}
public void setTrueWindAngle(short trueWindAngle) {
this.trueWindAngle = trueWindAngle;
}
public int getCurrentDrift() {
return currentDrift;
/**
* Returns the current drift of the boat, in knots.
* @return Current drift, in knots.
*/
public double getCurrentDriftKnots() {
return currentDriftKnots;
}
public void setCurrentDrift(int currentDrift) {
this.currentDrift = currentDrift;
}
public int getCurrentSet() {
/**
* Returns the current set of the boat.
* @return Current set of the boat.
*/
public Bearing getCurrentSet() {
return currentSet;
}
public void setCurrentSet(int currentSet) {
this.currentSet = currentSet;
}
public short getRudderAngle() {
/**
* Returns the current rudder angle of the boat.
* @return Current rudder angle of the boat.
*/
public Azimuth getRudderAngle() {
return rudderAngle;
}
public void setRudderAngle(short rudderAngle) {
this.rudderAngle = rudderAngle;
}
public double getHeadingDegrees(){
return AC35UnitConverter.unpackHeading(getHeading());
}
public double getTrueWindAngleDegrees(){
return AC35UnitConverter.unpackTrueWindAngle(getTrueWindAngle());
}
@Override
public String toString() {
@ -519,28 +480,28 @@ public class BoatLocation extends AC35Data {
builder.append(this.getRoll());
builder.append("\nBoat speed (mm/sec): ");
builder.append(this.getBoatSpeed());
builder.append(this.getBoatSpeedKnots());
builder.append("\nBoat COG: ");
builder.append(this.getBoatCOG());
builder.append("\nBoat SOG: ");
builder.append(this.getBoatSOG());
builder.append(this.getBoatSOGKnots());
builder.append("\nApparent wind speed: ");
builder.append(this.getApparentWindSpeed());
builder.append(this.getApparentWindSpeedKnots());
builder.append("\nApparent wind angle: ");
builder.append(this.getApparentWindAngle());
builder.append("\nTrue wind speed: ");
builder.append(this.getTrueWindSpeed());
builder.append(this.getTrueWindSpeedKnots());
builder.append("\nTrue wind angle: ");
builder.append(this.getTrueWindAngle());
builder.append("\nCurrent drift: ");
builder.append(this.getCurrentDrift());
builder.append(this.getCurrentDriftKnots());
builder.append("\nCurrent set: ");
builder.append(this.getCurrentSet());

@ -0,0 +1,107 @@
package network.Messages.Enums;
import java.util.HashMap;
import java.util.Map;
/**
* Various device sources for a BoatLocation message.
*/
public enum BoatLocationDeviceEnum {
NOT_A_DEVICE(-1),
Unknown(0),
/**
* A yacht particpating in the race.
*/
RacingYacht(1),
CommitteeBoat(2),
/**
* A marker boat.
*/
Mark(3),
Pin(4),
ChaseBoat(5),
MedicalBoat(6),
MarshallBoat(7),
UmpireBoat(8),
UmpireSoftwareApplication(9),
PrincipalRaceOfficerApplication(10),
WeatherStation(11),
Helicopter(12),
DataProcessingApplication(13);
/**
* Value of the enum.
*/
private byte value;
/**
* Creates a BoatLocationDeviceEnum from a given primitive integer value, cast to a byte.
* @param value Integer, which is cast to byte, to construct from.
*/
private BoatLocationDeviceEnum(int value) {
this.value = (byte) value;
}
/**
* Returns the primitive value of the enum.
* @return Primitive value of the enum.
*/
public byte getValue() {
return value;
}
/**
* Stores a mapping between Byte values and BoatLocationDeviceEnum values.
*/
private static final Map<Byte, BoatLocationDeviceEnum> byteToDeviceMap = new HashMap<>();
/*
Static initialization block. Initializes the byteToDeviceMap.
*/
static {
for (BoatLocationDeviceEnum type : BoatLocationDeviceEnum.values()) {
BoatLocationDeviceEnum.byteToDeviceMap.put(type.value, type);
}
}
/**
* Returns the enumeration value which corresponds to a given byte value.
* @param deviceValue Byte value to convert to a BoatLocationDeviceEnum value.
* @return The BoatLocationDeviceEnum value which corresponds to the given byte value.
*/
public static BoatLocationDeviceEnum fromByte(byte deviceValue) {
//Gets the corresponding BoatLocationDeviceEnum from the map.
BoatLocationDeviceEnum type = BoatLocationDeviceEnum.byteToDeviceMap.get(deviceValue);
if (type == null) {
//If the byte value wasn't found, return the NOT_A_DEVICE BoatLocationDeviceEnum.
return BoatLocationDeviceEnum.NOT_A_DEVICE;
} else {
//Otherwise, return the BoatLocationDeviceEnum.
return type;
}
}
}

@ -24,7 +24,7 @@ public class AC35UnitConverter {
* @return The packed value.
*/
public static int packGPS(double value) {
return (int) (value * 2147483648.0/180.0);//2^31 = 2147483648
return (int) (value * 2147483648.0 / 180.0);//2^31 = 2147483648
}

@ -16,7 +16,6 @@ import shared.dataInput.RegattaDataSource;
import shared.model.*;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -198,20 +197,20 @@ public class VisualiserRace extends Race implements Runnable {
if (boatLocation != null && boatStatus != null) {
//Get the new position.
double latitude = boatLocation.getLatitudeDouble();
double longitude = boatLocation.getLongitudeDouble();
double latitude = boatLocation.getLatitude();
double longitude = boatLocation.getLongitude();
GPSCoordinate gpsCoordinate = new GPSCoordinate(latitude, longitude);
boat.setCurrentPosition(gpsCoordinate);
//Bearing.
boat.setBearing(Bearing.fromDegrees(boatLocation.getHeadingDegrees()));
boat.setBearing(boatLocation.getHeading());
//Time until next mark.
boat.setEstimatedTimeAtNextMark(raceClock.getLocalTime(boatStatus.getEstTimeAtNextMark()));
//Speed.
boat.setCurrentSpeed(boatLocation.getBoatSOG() / Constants.KnotsToMMPerSecond);
boat.setCurrentSpeed(boatLocation.getBoatSpeedKnots());
//Boat status.
@ -292,8 +291,8 @@ public class VisualiserRace extends Race implements Runnable {
if (boatLocation != null) {
//We only update the boat's position.
double latitude = boatLocation.getLatitudeDouble();
double longitude = boatLocation.getLongitudeDouble();
double latitude = boatLocation.getLatitude();
double longitude = boatLocation.getLongitude();
GPSCoordinate gpsCoordinate = new GPSCoordinate(latitude, longitude);
mark.setPosition(gpsCoordinate);

@ -2,8 +2,11 @@ package network.MessageDecoders;
import network.MessageEncoders.RaceVisionByteEncoder;
import network.Messages.BoatLocation;
import network.Messages.Enums.BoatLocationDeviceEnum;
import org.junit.Assert;
import org.junit.Test;
import shared.model.Azimuth;
import shared.model.Bearing;
/**
@ -23,28 +26,28 @@ public class BoatLocationDecoderTest {
long time = System.currentTimeMillis();
BoatLocation testMessage = new BoatLocation(
(byte) 1,
BoatLocation.currentMessageVersionNumber,
time,
2,
3,
(byte) 1,
BoatLocationDeviceEnum.RacingYacht,
180,
-180,
4,
5,
Bearing.fromDegrees(45),
(short) 6,
(short) 7,
8,
9,
Bearing.fromDegrees(40),
10,
11,
(short) 12,
Azimuth.fromDegrees(35),
13,
14,
(short) 15,
Bearing.fromDegrees(80),
Azimuth.fromDegrees(80),
16,
17,
(short) 18 );
Bearing.fromDegrees(80),
Azimuth.fromDegrees(22) );
//Encode.
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(testMessage);
@ -58,22 +61,22 @@ public class BoatLocationDecoderTest {
Assert.assertEquals(testMessage.getTime(), decodedTest.getTime());
Assert.assertEquals(testMessage.getSequenceNumber(), decodedTest.getSequenceNumber());
Assert.assertEquals(testMessage.getDeviceType(), decodedTest.getDeviceType());
Assert.assertEquals(testMessage.getLatitude(), decodedTest.getLatitude());
Assert.assertEquals(testMessage.getLongitude(), decodedTest.getLongitude());
Assert.assertEquals(testMessage.getLatitude(), decodedTest.getLatitude(), 0.01);
Assert.assertEquals(testMessage.getLongitude(), decodedTest.getLongitude(), 0.01);
Assert.assertEquals(testMessage.getAltitude(), decodedTest.getAltitude());
Assert.assertEquals(testMessage.getHeading(), decodedTest.getHeading());
Assert.assertEquals(testMessage.getHeading().degrees(), decodedTest.getHeading().degrees(), 0.01);
Assert.assertEquals(testMessage.getPitch(), decodedTest.getPitch());
Assert.assertEquals(testMessage.getRoll(), decodedTest.getRoll());
Assert.assertEquals(testMessage.getBoatSpeed(), decodedTest.getBoatSpeed());
Assert.assertEquals(testMessage.getBoatSpeedKnots(), decodedTest.getBoatSpeedKnots(), 0.01);
Assert.assertEquals(testMessage.getBoatCOG(), decodedTest.getBoatCOG());
Assert.assertEquals(testMessage.getBoatSOG(), decodedTest.getBoatSOG());
Assert.assertEquals(testMessage.getApparentWindSpeed(), decodedTest.getApparentWindSpeed());
Assert.assertEquals(testMessage.getTrueWindSpeed(), decodedTest.getTrueWindSpeed());
Assert.assertEquals(testMessage.getTrueWindDirection(), decodedTest.getTrueWindDirection());
Assert.assertEquals(testMessage.getTrueWindAngle(), decodedTest.getTrueWindAngle());
Assert.assertEquals(testMessage.getCurrentDrift(), decodedTest.getCurrentDrift());
Assert.assertEquals(testMessage.getCurrentSet(), decodedTest.getCurrentSet());
Assert.assertEquals(testMessage.getRudderAngle(), decodedTest.getRudderAngle());
Assert.assertEquals(testMessage.getBoatCOG().degrees(), decodedTest.getBoatCOG().degrees(), 0.01);
Assert.assertEquals(testMessage.getBoatSOGKnots(), decodedTest.getBoatSOGKnots(), 0.01);
Assert.assertEquals(testMessage.getApparentWindSpeedKnots(), decodedTest.getApparentWindSpeedKnots(), 0.01);
Assert.assertEquals(testMessage.getTrueWindSpeedKnots(), decodedTest.getTrueWindSpeedKnots(), 0.01);
Assert.assertEquals(testMessage.getTrueWindDirection().degrees(), decodedTest.getTrueWindDirection().degrees(), 0.01);
Assert.assertEquals(testMessage.getTrueWindAngle().degrees(), decodedTest.getTrueWindAngle().degrees(), 0.01);
Assert.assertEquals(testMessage.getCurrentDriftKnots(), decodedTest.getCurrentDriftKnots(), 0.01);
Assert.assertEquals(testMessage.getCurrentSet().degrees(), decodedTest.getCurrentSet().degrees(), 0.01);
Assert.assertEquals(testMessage.getRudderAngle().degrees(), decodedTest.getRudderAngle().degrees(), 0.01);
}
}

Loading…
Cancel
Save