diff --git a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java index b0d5b2a6..71765e12 100644 --- a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java +++ b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java @@ -199,8 +199,8 @@ public class BinaryMessageDecoder { case AVGWIND: //System.out.println("Average Wind Message!"); - AverageWindDecoder awDecoder = new AverageWindDecoder(messageBody); - return awDecoder.getAverageWind(); + AverageWindDecoder awDecoder = new AverageWindDecoder(); + return awDecoder.decode(messageBody); case BOATACTION: BoatActionDecoder baDecoder = new BoatActionDecoder(); diff --git a/racevisionGame/src/main/java/network/MessageDecoders/AverageWindDecoder.java b/racevisionGame/src/main/java/network/MessageDecoders/AverageWindDecoder.java index 13de3c80..1b0f12d5 100644 --- a/racevisionGame/src/main/java/network/MessageDecoders/AverageWindDecoder.java +++ b/racevisionGame/src/main/java/network/MessageDecoders/AverageWindDecoder.java @@ -1,56 +1,103 @@ package network.MessageDecoders; +import network.Messages.AC35Data; import network.Messages.AverageWind; import network.Utils.ByteConverter; +import static network.Utils.AC35UnitConverter.*; + import java.util.Arrays; /** - * Created by hba56 on 23/04/17. + * Decodes {@link AverageWind} messages. */ -public class AverageWindDecoder { - byte messageVersionNumber; - byte[] byteTime; - byte[] byteRawPeriod; - byte[] byteRawSpeed; - byte[] bytePeriod2; - byte[] byteSpeed2; - byte[] bytePeriod3; - byte[] byteSpeed3; - byte[] bytePeriod4; - byte[] byteSpeed4; - - AverageWind averageWind; - - public AverageWindDecoder(byte[] encodedAverageWind) { - messageVersionNumber = encodedAverageWind[0]; - byteTime = Arrays.copyOfRange(encodedAverageWind, 1, 7); - byteRawPeriod = Arrays.copyOfRange(encodedAverageWind, 7, 9); - byteRawSpeed = Arrays.copyOfRange(encodedAverageWind, 9, 11); - bytePeriod2 = Arrays.copyOfRange(encodedAverageWind, 11, 13); - byteSpeed2 = Arrays.copyOfRange(encodedAverageWind, 13, 15); - bytePeriod3 = Arrays.copyOfRange(encodedAverageWind, 15, 17); - byteSpeed3 = Arrays.copyOfRange(encodedAverageWind, 17, 19); - bytePeriod4 = Arrays.copyOfRange(encodedAverageWind, 19, 21); - byteSpeed4 = Arrays.copyOfRange(encodedAverageWind, 21, 23); - - int msgNum = ByteConverter.bytesToInt(messageVersionNumber); - long lngTime = ByteConverter.bytesToLong(byteTime); +public class AverageWindDecoder implements MessageDecoder { + + /** + * The encoded message. + */ + private byte[] encodedMessage; + + /** + * The decoded message. + */ + private AverageWind message; + + + + public AverageWindDecoder() { + } + + + + @Override + public AC35Data decode(byte[] encodedMessage) { + this.encodedMessage = encodedMessage; + + + byte messageVersionNumber = encodedMessage[0]; + + + byte[] byteTime = Arrays.copyOfRange(encodedMessage, 1, 7); + long time = ByteConverter.bytesToLong(byteTime); + + byte[] byteRawPeriod = Arrays.copyOfRange(encodedMessage, 7, 9); int intRawPeriod = ByteConverter.bytesToInt(byteRawPeriod); + long rawPeriod = unpackAverageWindPeriod(intRawPeriod); + + byte[] byteRawSpeed = Arrays.copyOfRange(encodedMessage, 9, 11); int intRawSpeed = ByteConverter.bytesToInt(byteRawSpeed); + double rawSpeedKnots = unpackMMperSecToKnots(intRawSpeed); + + byte[] bytePeriod2 = Arrays.copyOfRange(encodedMessage, 11, 13); int intPeriod2 = ByteConverter.bytesToInt(bytePeriod2); + long period2 = unpackAverageWindPeriod(intPeriod2); + + byte[] byteSpeed2 = Arrays.copyOfRange(encodedMessage, 13, 15); int intSpeed2 = ByteConverter.bytesToInt(byteSpeed2); + double speed2Knots = unpackMMperSecToKnots(intSpeed2); + + byte[] bytePeriod3 = Arrays.copyOfRange(encodedMessage, 15, 17); int intPeriod3 = ByteConverter.bytesToInt(bytePeriod3); + long period3 = unpackAverageWindPeriod(intPeriod3); + + byte[] byteSpeed3 = Arrays.copyOfRange(encodedMessage, 17, 19); int intSpeed3 = ByteConverter.bytesToInt(byteSpeed3); + double speed3Knots = unpackMMperSecToKnots(intSpeed3); + + byte[] bytePeriod4 = Arrays.copyOfRange(encodedMessage, 19, 21); int intPeriod4 = ByteConverter.bytesToInt(bytePeriod4); + long period4 = unpackAverageWindPeriod(intPeriod4); + + byte[] byteSpeed4 = Arrays.copyOfRange(encodedMessage, 21, 23); int intSpeed4 = ByteConverter.bytesToInt(byteSpeed4); + double speed4Knots = unpackMMperSecToKnots(intSpeed4); + + + + + message = new AverageWind( + messageVersionNumber, + time, + rawPeriod, + rawSpeedKnots, + period2, + speed2Knots, + period3, + speed3Knots, + period4, + speed4Knots ); - this.averageWind = new AverageWind(msgNum, lngTime, intRawPeriod, intRawSpeed, intPeriod2, intSpeed2, intPeriod3, intSpeed3, intPeriod4, intSpeed4); + return message; } - public AverageWind getAverageWind() { - return averageWind; + /** + * Returns the decoded message. + * @return The decoded message. + */ + public AverageWind getMessage() { + return message; } } diff --git a/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java b/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java index 3121f7f7..9b04803c 100644 --- a/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java +++ b/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java @@ -51,7 +51,7 @@ public class DecoderFactory { case COURSEWIND: return new CourseWindsDecoder(); - //case AVGWIND: return new AverageWindDecoder()//TODO; + case AVGWIND: return new AverageWindDecoder(); case REQUEST_TO_JOIN: return new RequestToJoinDecoder(); diff --git a/racevisionGame/src/main/java/network/MessageEncoders/AverageWindEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/AverageWindEncoder.java new file mode 100644 index 00000000..dc1966fc --- /dev/null +++ b/racevisionGame/src/main/java/network/MessageEncoders/AverageWindEncoder.java @@ -0,0 +1,86 @@ +package network.MessageEncoders; + + +import network.Messages.AC35Data; +import network.Messages.AverageWind; + +import java.nio.ByteBuffer; + +import static network.Utils.AC35UnitConverter.*; +import static network.Utils.ByteConverter.intToBytes; +import static network.Utils.ByteConverter.longToBytes; + +/** + * This encoder can encode a {@link AverageWind} message. + */ +public class AverageWindEncoder implements MessageEncoder { + + + /** + * Constructor. + */ + public AverageWindEncoder() { + } + + + @Override + public byte[] encode(AC35Data message) { + + //Downcast. + AverageWind averageWind = (AverageWind) message; + + + byte messageVersionNumber = averageWind.getMessageVersionNumber(); + + long time = averageWind.getTime(); + byte[] byteTime = longToBytes(time,6); + + long rawPeriod = averageWind.getRawPeriod(); + int rawPeriodInt = packAverageWindPeriod(rawPeriod); + byte[] byteRawPeriod = intToBytes(rawPeriodInt, 2); + + double rawSampleSpeed = averageWind.getRawSpeedKnots(); + int rawSampleSpeedInt = packKnotsToMMperSec(rawSampleSpeed); + byte[] byteRawSpeed = intToBytes(rawSampleSpeedInt, 2); + + long period2 = averageWind.getSampleTwoPeriod(); + int period2Int = packAverageWindPeriod(period2); + byte[] bytePeriod2 = intToBytes(period2Int, 2); + + double speed2 = averageWind.getSampleTwoSpeedKnots(); + int speed2Int = packKnotsToMMperSec(speed2); + byte[] byteSpeed2 = intToBytes(speed2Int, 2); + + long period3 = averageWind.getSampleThreePeriod(); + int period3Int = packAverageWindPeriod(period3); + byte[] bytePeriod3 = intToBytes(period3Int, 2); + + double speed3 = averageWind.getSampleThreeSpeedKnots(); + int speed3Int = packKnotsToMMperSec(speed3); + byte[] byteSpeed3 = intToBytes(speed3Int, 2); + + long period4 = averageWind.getSampleFourPeriod(); + int period4Int = packAverageWindPeriod(period4); + byte[] bytePeriod4 = intToBytes(period4Int, 2); + + double speed4 = averageWind.getSampleFourSpeedKnots(); + int speed4Int = packKnotsToMMperSec(speed4); + byte[] byteSpeed4 = intToBytes(speed4Int, 2); + + + + ByteBuffer result = ByteBuffer.allocate(23); + result.put(messageVersionNumber); + result.put(byteTime); + result.put(byteRawPeriod); + result.put(byteRawSpeed); + result.put(bytePeriod2); + result.put(byteSpeed2); + result.put(bytePeriod3); + result.put(byteSpeed3); + result.put(bytePeriod4); + result.put(byteSpeed4); + return result.array(); + + } +} diff --git a/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java b/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java index fadf6dea..e023ffb7 100644 --- a/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java +++ b/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java @@ -51,7 +51,7 @@ public class EncoderFactory { case COURSEWIND: return new CourseWindsEncoder(); - //case AVGWIND: return new AverageWindEncoder();//TODO + case AVGWIND: return new AverageWindEncoder(); case REQUEST_TO_JOIN: return new RequestToJoinEncoder(); diff --git a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java index 45690bca..154e25e4 100644 --- a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java +++ b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java @@ -126,34 +126,6 @@ public class RaceVisionByteEncoder { - public static byte[] averageWind(int time, int rawPeriod, int rawSampleSpeed, int period2, int speed2, int period3, int speed3, int period4, int speed4){ - int messageVersionNumber = 0b1; - byte[] byteTime = longToBytes(time,6); - byte[] byteRawPeriod = intToBytes(rawPeriod, 2); - byte[] byteRawSpeed = intToBytes(rawSampleSpeed, 2); - byte[] bytePeriod2 = intToBytes(period2, 2); - byte[] byteSpeed2 = intToBytes(speed2, 2); - byte[] bytePeriod3 = intToBytes(period3, 2); - byte[] byteSpeed3 = intToBytes(speed3, 2); - byte[] bytePeriod4 = intToBytes(period4, 2); - byte[] byteSpeed4 = intToBytes(speed4, 2); - - ByteBuffer result = ByteBuffer.allocate(23); - result.put(intToBytes(messageVersionNumber, 1)); - result.put(byteTime); - result.put(byteRawPeriod); - result.put(byteRawSpeed); - result.put(bytePeriod2); - result.put(byteSpeed2); - result.put(bytePeriod3); - result.put(byteSpeed3); - result.put(bytePeriod4); - result.put(byteSpeed4); - return result.array(); - } - - - /** * Encodes a given message. * @param message Message to encode. diff --git a/racevisionGame/src/main/java/network/Messages/AverageWind.java b/racevisionGame/src/main/java/network/Messages/AverageWind.java index 1ba17bd5..1b59f30f 100644 --- a/racevisionGame/src/main/java/network/Messages/AverageWind.java +++ b/racevisionGame/src/main/java/network/Messages/AverageWind.java @@ -4,33 +4,187 @@ package network.Messages; import network.Messages.Enums.MessageType; /** - * Created by fwy13 on 25/04/17. + * Represents an AverageWind message in the streaming API (see section 4.12). */ public class AverageWind extends AC35Data { - private int msgNum; - private long lngTime; - private int rawPeriod; - private int rawSpeed; - private int period2; - private int speed2; - private int period3; - private int speed3; - private int period4; - private int speed4; - - public AverageWind(int msgNum, long lngTime, int rawPeriod, int rawSpeed, int period2, int speed2, int period3, int speed3, int period4, int speed4){ + /** + * The current version number for this message type. + */ + public static final byte currentMessageVersionNumber = 1; + + + /** + * The version number for this message. + */ + private byte messageVersionNumber; + + /** + * Timestamp for when the measurement was taken. Milliseconds since unix epoch. + */ + private long time; + + /** + * Raw sample rate period. Milliseconds. + */ + private long rawPeriod; + + /** + * Raw wind speed, in knots. + */ + private double rawSpeedKnots; + + /** + * Wind speed average period for second sample. Milliseconds. + */ + private long sampleTwoPeriod; + + /** + * Wind speed of second sample. Knots. + */ + private double sampleTwoSpeedKnots; + + /** + * Wind speed average period for third sample. Milliseconds. + */ + private long sampleThreePeriod; + + /** + * Wind speed of third sample. Knots. + */ + private double sampleThreeSpeedKnots; + + /** + * Wind speed average period for fourth sample. Milliseconds. + */ + private long sampleFourPeriod; + + /** + * Wind speed of fourth sample. Knots. + */ + private double sampleFourSpeedKnots; + + + /** + * Creates an AverageWind message, with the given parameters. + * @param messageVersionNumber The version number of message. + * @param time The timestamp of the message. + * @param rawPeriod The period of the raw measurement. Milliseconds. + * @param rawSpeedKnots The speed of the raw measurement. Knots. + * @param sampleTwoPeriod The period of the second measurement. Milliseconds. + * @param sampleTwoSpeedKnots The speed of the second measurement. Knots. + * @param sampleThreePeriod The period of the third measurement. Milliseconds. + * @param sampleThreeSpeedKnots The speed of the third measurement. Knots. + * @param sampleFourPeriod The period of the fourth measurement. Milliseconds. + * @param sampleFourSpeedKnots The speed of the fourth measurement. Knots. + */ + public AverageWind( + byte messageVersionNumber, + long time, + long rawPeriod, + double rawSpeedKnots, + long sampleTwoPeriod, + double sampleTwoSpeedKnots, + long sampleThreePeriod, + double sampleThreeSpeedKnots, + long sampleFourPeriod, + double sampleFourSpeedKnots ) { + super(MessageType.AVGWIND); - this.msgNum = msgNum; - this.lngTime = lngTime; + + this.messageVersionNumber = messageVersionNumber; + this.time = time; this.rawPeriod = rawPeriod; - this.rawSpeed = rawSpeed; - this.period2 = period2; - this.speed2 = speed2; - this.period3 = period3; - this.speed3 = speed3; - this.period4 = period4; - this.speed4 = speed4; + this.rawSpeedKnots = rawSpeedKnots; + this.sampleTwoPeriod = sampleTwoPeriod; + this.sampleTwoSpeedKnots = sampleTwoSpeedKnots; + this.sampleThreePeriod = sampleThreePeriod; + this.sampleThreeSpeedKnots = sampleThreeSpeedKnots; + this.sampleFourPeriod = sampleFourPeriod; + this.sampleFourSpeedKnots = sampleFourSpeedKnots; + } + + + /** + * Returns the version number of this message. + * @return Message version number. + */ + public byte getMessageVersionNumber() { + return messageVersionNumber; + } + + /** + * Returns the timestamp of this message. + * @return Timestamp of this message. Milliseconds since unix epoch. + */ + public long getTime() { + return time; + } + + /** + * Returns the period of time over which the raw sample was done. Milliseconds. + * @return Raw sample's period. + */ + public long getRawPeriod() { + return rawPeriod; + } + + /** + * Returns the wind speed of the raw sample. Knots. + * @return Wind speed of raw sample. Knots + */ + public double getRawSpeedKnots() { + return rawSpeedKnots; } + /** + * Returns the period of time over which the second sample was done. Milliseconds. + * @return Second sample's period. + */ + public long getSampleTwoPeriod() { + return sampleTwoPeriod; + } + + /** + * Returns the wind speed of the second sample. Knots. + * @return Wind speed of second sample. Knots + */ + public double getSampleTwoSpeedKnots() { + return sampleTwoSpeedKnots; + } + + /** + * Returns the period of time over which the third sample was done. Milliseconds. + * @return Third sample's period. + */ + public long getSampleThreePeriod() { + return sampleThreePeriod; + } + + /** + * Returns the wind speed of the third sample. Knots. + * @return Wind speed of third sample. Knots + */ + public double getSampleThreeSpeedKnots() { + return sampleThreeSpeedKnots; + } + + /** + * Returns the period of time over which the fourth sample was done. Milliseconds. + * @return Fourth sample's period. + */ + public long getSampleFourPeriod() { + return sampleFourPeriod; + } + + /** + * Returns the wind speed of the fourth sample. Knots. + * @return Wind speed of fourth sample. Knots + */ + public double getSampleFourSpeedKnots() { + return sampleFourSpeedKnots; + } + + + } diff --git a/racevisionGame/src/main/java/network/Utils/AC35UnitConverter.java b/racevisionGame/src/main/java/network/Utils/AC35UnitConverter.java index 6d894ab6..40b87628 100644 --- a/racevisionGame/src/main/java/network/Utils/AC35UnitConverter.java +++ b/racevisionGame/src/main/java/network/Utils/AC35UnitConverter.java @@ -85,4 +85,23 @@ public class AC35UnitConverter { } + /** + * Packs an average wind period, in milliseconds, into an int, in tenths of a second. + * @param periodMilliseconds Period in milliseconds. + * @return Period in tenths of a second. + */ + public static int packAverageWindPeriod(long periodMilliseconds) { + return (int) (periodMilliseconds / 100); + } + + /** + * Unpacks an average wind period, in tenths of a second, into an long, in milliseconds. + * @param periodTenthsOfSecond Period in tenths of a second. + * @return Period in milliseconds + */ + public static long unpackAverageWindPeriod(int periodTenthsOfSecond) { + return (periodTenthsOfSecond * 100); + } + + } diff --git a/racevisionGame/src/test/java/network/MessageDecoders/AverageWindDecoderTest.java b/racevisionGame/src/test/java/network/MessageDecoders/AverageWindDecoderTest.java new file mode 100644 index 00000000..001c54eb --- /dev/null +++ b/racevisionGame/src/test/java/network/MessageDecoders/AverageWindDecoderTest.java @@ -0,0 +1,75 @@ +package network.MessageDecoders; + +import network.MessageEncoders.RaceVisionByteEncoder; +import network.Messages.AverageWind; +import static org.junit.Assert.*; +import org.junit.Test; +import shared.model.Bearing; + +import java.util.ArrayList; + +/** + * Test for the AverageWind encoder and decoder + */ +public class AverageWindDecoderTest { + + + /** + * Creates a AverageWind message, encodes it, decodes it, and checks that the result matches the starting message. + * @throws Exception if test fails. + */ + @Test + public void averageWindEncodeDecodeTest() throws Exception { + + AverageWind averageWind = new AverageWind( + AverageWind.currentMessageVersionNumber, + System.currentTimeMillis(), + 3000, + 12.5, + 4050, + 12.6, + 3055, + 12.7, + 6051, + 13.37 + ); + + byte[] encodedMessage = RaceVisionByteEncoder.encode(averageWind); + + AverageWindDecoder averageWindDecoder = new AverageWindDecoder(); + averageWindDecoder.decode(encodedMessage); + AverageWind averageWindDecoded = averageWindDecoder.getMessage(); + + compareAverageWindMessages(averageWind, averageWindDecoded); + + } + + + /** + * Compares two AverageWind messages to check that they are equal. + * @param original The original AverageWind message. + * @param decoded The decoded AverageWind message. + */ + public static void compareAverageWindMessages(AverageWind original, AverageWind decoded) { + + + assertEquals(original.getMessageVersionNumber(), decoded.getMessageVersionNumber()); + assertEquals(original.getTime(), decoded.getTime()); + + assertEquals(original.getRawPeriod(), decoded.getRawPeriod(), 100); + assertEquals(original.getRawSpeedKnots(), decoded.getRawSpeedKnots(), 0.01); + + assertEquals(original.getSampleTwoPeriod(), decoded.getSampleTwoPeriod(), 100); + assertEquals(original.getSampleTwoSpeedKnots(), decoded.getSampleTwoSpeedKnots(), 0.01); + + assertEquals(original.getSampleThreePeriod(), decoded.getSampleThreePeriod(), 100); + assertEquals(original.getSampleThreeSpeedKnots(), decoded.getSampleThreeSpeedKnots(), 0.01); + + assertEquals(original.getSampleFourPeriod(), decoded.getSampleFourPeriod(), 100); + assertEquals(original.getSampleFourSpeedKnots(), decoded.getSampleFourSpeedKnots(), 0.01); + + + } + + +}