From f4d5df9df2edd6883e3b0f031830c495b291bd3a Mon Sep 17 00:00:00 2001 From: fjc40 Date: Sun, 14 May 2017 22:34:05 +1200 Subject: [PATCH] Mock.Polars: Fixed a bug where calculateVMG was placing velocity into the VMG object, instead of speed. Maybe fixed a bug when a bearingLowerBound which is greater than bearingUpperBound is provided. Added another test case. Mock.Constants: Added knots to millimeters per second conversion factor. Mock.Boat: Added calculateDIstanceToNextMarker function, which returns the distance, in nautical miles, to the next mark. Mock.Race: Added wind direction as a race property. Added wind speed as a race property. The race status messages sent by Race now use Race's wind speed and direction. #story[873] --- mock/src/main/java/seng302/Constants.java | 3 +++ mock/src/main/java/seng302/Model/Boat.java | 26 +++++++++++++++++++ mock/src/main/java/seng302/Model/Polars.java | 11 +++++--- mock/src/main/java/seng302/Model/Race.java | 18 ++++++++++--- .../test/java/seng302/Model/PolarsTest.java | 19 ++++++++++++++ .../Networking/Messages/BoatLocation.java | 2 +- 6 files changed, 71 insertions(+), 8 deletions(-) diff --git a/mock/src/main/java/seng302/Constants.java b/mock/src/main/java/seng302/Constants.java index cd2909bd..82a5f276 100644 --- a/mock/src/main/java/seng302/Constants.java +++ b/mock/src/main/java/seng302/Constants.java @@ -8,5 +8,8 @@ public class Constants { public static final int NMToMetersConversion = 1852; // 1 nautical mile = 1852 meters + ///1 knot = 514.444 millimeters per second. + public static final double KnotsToMMPerSecond = 514.444; + } diff --git a/mock/src/main/java/seng302/Model/Boat.java b/mock/src/main/java/seng302/Model/Boat.java index 9b81d975..539969de 100644 --- a/mock/src/main/java/seng302/Model/Boat.java +++ b/mock/src/main/java/seng302/Model/Boat.java @@ -1,6 +1,7 @@ package seng302.Model; import org.geotools.referencing.GeodeticCalculator; +import seng302.Constants; /** @@ -85,6 +86,31 @@ public class Boat { } + /** + * Calculates the distance between the boat and its target marker in nautical miles (1.852 km). + * @return The distance (in nautical miles) between the boat and its target marker. + */ + public double calculateDistanceToNextMarker() { + + GeodeticCalculator calc = new GeodeticCalculator(); + + GPSCoordinate startMarker = this.getCurrentPosition(); + + //When boats finish, their "current leg" doesn't have an end marker. + if (this.getCurrentLeg().getEndMarker() == null) { + return 0d; + } + + GPSCoordinate endMarker = this.getCurrentLeg().getEndMarker().getAverageGPSCoordinate(); + + //Add points to calculator. + calc.setStartingGeographicPoint(startMarker.getLongitude(), startMarker.getLatitude()); + calc.setDestinationGeographicPoint(endMarker.getLongitude(), endMarker.getLatitude()); + + //Convert meters to nautical miles. + return calc.getOrthodromicDistance() / Constants.NMToMetersConversion; + } + public String getName() { return name; } diff --git a/mock/src/main/java/seng302/Model/Polars.java b/mock/src/main/java/seng302/Model/Polars.java index 5d0aedac..33311f1f 100644 --- a/mock/src/main/java/seng302/Model/Polars.java +++ b/mock/src/main/java/seng302/Model/Polars.java @@ -85,10 +85,16 @@ public class Polars { * @param destinationAngle The angle between the boat and the destination point. * @param bearingLowerBound The lowest bearing (angle) that the boat may travel on. * @param bearingUpperBound The highest bearing (angle) that the boat may travel on. - * @return + * @return The VMG. */ public VMG calculateVMG(double trueWindAngle, double trueWindSpeed, double destinationAngle, double bearingLowerBound, double bearingUpperBound) { + //TODO BUG there may be a bug when you provide a bearingUpperBound < bearingLowerBound (e.g., lower = 340, upper = 15). + bearingLowerBound = ((bearingLowerBound % 360d) + 360d) % 360d; + bearingUpperBound = ((bearingUpperBound % 360d) + 360d) % 360d; + if (bearingLowerBound > bearingUpperBound) { + bearingLowerBound -= 360d; + } //Sorts polar angles. for (ArrayList angles : this.polarAngles.values()) { @@ -119,7 +125,6 @@ public class Polars { } } - //TODO for wind speeds larger than any in the Polars table, we will simply not find an upper bound, and therefore never calculate an upper VMG, or interpolate between them. @@ -218,7 +223,7 @@ public class Polars { //Check that the velocity is better. if (vmgTemp > bestVMGVelocity) { - bestVMGVelocity = vmgTemp; + bestVMGVelocity = interpolatedSpeed; bestVMGAngle = trueBoatBearing; } diff --git a/mock/src/main/java/seng302/Model/Race.java b/mock/src/main/java/seng302/Model/Race.java index 65942912..896c2253 100644 --- a/mock/src/main/java/seng302/Model/Race.java +++ b/mock/src/main/java/seng302/Model/Race.java @@ -10,6 +10,7 @@ import org.geotools.referencing.GeodeticCalculator; import seng302.Constants; import seng302.DataInput.RaceDataSource; import seng302.MockOutput; +import seng302.Networking.Messages.BoatLocation; import seng302.Networking.Messages.BoatStatus; import seng302.Networking.Messages.Enums.BoatStatusEnum; import seng302.Networking.Messages.RaceStatus; @@ -47,6 +48,11 @@ public class Race implements Runnable { private int finished = 0; private List boundary; + ///Wind direction bearing. + private double windDirection = 0; + ///Wind speed (knots). Convert this to millimeters per second before passing to RaceStatus. + private double windSpeed = 0; + /** * Initailiser for Race @@ -65,6 +71,9 @@ public class Race implements Runnable { this.mockOutput = mockOutput; this.boundary = boundary; + this.windSpeed = 12;//TODO could use input parameters for these. And should fluctuate during race. + this.windDirection = 0; + //TODO refactor this.startTime = System.currentTimeMillis() + (this.PRERACE_TIME / this.scaleFactor); @@ -140,7 +149,7 @@ public class Race implements Runnable { boatOffset = (boatOffset + 1) % (startingBoats.size()); if (timeLeft <= 60000/scaleFactor && timeLeft > 0) { - RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 2, startTime, 0, 2300, 1, boatStatuses); + RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 2, startTime, BoatLocation.convertHeadingDoubleToInt(windDirection), (int) (windSpeed * Constants.KnotsToMMPerSecond), 1, boatStatuses); mockOutput.parseRaceStatus(raceStatus); } else if (timeLeft <= 0) { @@ -151,7 +160,7 @@ public class Race implements Runnable { stop(); } else { - RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 1, startTime, 0, 2300,1, boatStatuses); + RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 1, startTime, BoatLocation.convertHeadingDoubleToInt(windDirection), (int) (windSpeed * Constants.KnotsToMMPerSecond),1, boatStatuses); mockOutput.parseRaceStatus(raceStatus); } currentTime = System.currentTimeMillis(); @@ -182,6 +191,7 @@ public class Race implements Runnable { if (boatsFinished < startingBoats.size()) { //Get the current time. long currentTime = System.currentTimeMillis(); + //Update the total elapsed time. totalTimeElapsed = currentTime - timeRaceStarted; ArrayList boatStatuses = new ArrayList(); @@ -205,7 +215,7 @@ public class Race implements Runnable { boat.getCurrentLeg().getLegNumber() >= 0 ? BoatStatusEnum.RACING : BoatStatusEnum.DNF, boat.getCurrentLeg().getLegNumber())); } if (startingBoats.size()==finished){ - RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 4, startTime, 0, 2300, 2, boatStatuses);//TODO FIX the second currentTime is a placeholder! Also, replace magic values. + RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 4, startTime, BoatLocation.convertHeadingDoubleToInt(windDirection), (int) (windSpeed * Constants.KnotsToMMPerSecond), 2, boatStatuses);//TODO FIX replace magic values. mockOutput.parseRaceStatus(raceStatus); } } else { @@ -213,7 +223,7 @@ public class Race implements Runnable { } } boatOffset = (boatOffset + 1) % (startingBoats.size()); - RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 3, startTime, 0, 2300, 2, boatStatuses);//TODO FIX the second currentTime is a placeholder! Also, replace magic values. + RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 3, startTime, BoatLocation.convertHeadingDoubleToInt(windDirection), (int) (windSpeed * Constants.KnotsToMMPerSecond), 2, boatStatuses);//TODO FIX replace magic values. mockOutput.parseRaceStatus(raceStatus); } } diff --git a/mock/src/test/java/seng302/Model/PolarsTest.java b/mock/src/test/java/seng302/Model/PolarsTest.java index 59680fab..fb885883 100644 --- a/mock/src/test/java/seng302/Model/PolarsTest.java +++ b/mock/src/test/java/seng302/Model/PolarsTest.java @@ -89,6 +89,25 @@ public class PolarsTest { //assertEquals(calcVMGAngle3, vmgAngle3, angleEpsilon); //assertEquals(calcVMGSpeed3, vmgSpeed3, speedEpsilon); + + //Test 4. + //This test has a wind speed of 0. + double windAngle4 = 5; + double destAngle4 = 100; + double windSpeed4 = 0;//knots + double vmgAngle4 = 88; + double vmgSpeed4 = 12; + + VMG calcVMG4 = polars.calculateVMG(windAngle4, windSpeed4, destAngle4, 0, 360); + double calcVMGAngle4 = calcVMG4.getBearing(); + double calcVMGSpeed4 = calcVMG4.getSpeed(); + + System.out.println("VMG speed = " + calcVMGSpeed4 + " , VMG angle = " + calcVMGAngle4);//TEMP DEBUG REMOVE + + + //assertEquals(calcVMGAngle4, vmgAngle4, angleEpsilon); + //assertEquals(calcVMGSpeed4, vmgSpeed4, speedEpsilon); + } } diff --git a/network/src/main/java/seng302/Networking/Messages/BoatLocation.java b/network/src/main/java/seng302/Networking/Messages/BoatLocation.java index 1309bc31..9cb0bc78 100644 --- a/network/src/main/java/seng302/Networking/Messages/BoatLocation.java +++ b/network/src/main/java/seng302/Networking/Messages/BoatLocation.java @@ -251,7 +251,7 @@ public class BoatLocation extends AC35Data { * @param angle Angle to convert. double. * @return short representation of heading. */ - public static short convertTrueWindAngleShortToDouble(double angle) { + public static short convertTrueWindAngleDoubleToShort(double angle) { short angleShort = (short) ((angle / 180.0) * 32768.0);