From 572e54b076cbfcc934cae74da44e04f3f2e829c5 Mon Sep 17 00:00:00 2001 From: fjc40 Date: Mon, 15 May 2017 14:13:36 +1200 Subject: [PATCH] Mock.Race: An alternate tacking implementation during race simulate. Doesn't handle keeping the boats in-bounds, and sometimes has issues with landing directly on a mark. Also seems to use wrong starting velocities (from the XML file). #story[873] --- mock/src/main/java/seng302/Model/Boat.java | 20 ++++ mock/src/main/java/seng302/Model/Event.java | 2 +- mock/src/main/java/seng302/Model/Race.java | 101 +++++++++++++------- 3 files changed, 90 insertions(+), 33 deletions(-) diff --git a/mock/src/main/java/seng302/Model/Boat.java b/mock/src/main/java/seng302/Model/Boat.java index 539969de..90b4c526 100644 --- a/mock/src/main/java/seng302/Model/Boat.java +++ b/mock/src/main/java/seng302/Model/Boat.java @@ -23,6 +23,9 @@ public class Boat { private double heading; private Polars polars; + ///This stores the milliseconds since the boat has changed its tack, to allow for only updating the tack every X milliseconds. + private long timeSinceTackChange = 0; + /** * Boat initialiser which keeps all of the information of the boat. * @@ -223,4 +226,21 @@ public class Boat { public void setPolars(Polars polars) { this.polars = polars; } + + + /** + * Returns the time since the boat changed its tack, in milliseconds. + * @return Time since the boat changed its tack, in milliseconds. + */ + public long getTimeSinceTackChange() { + return timeSinceTackChange; + } + + /** + * Sets the time since the boat changed it's tack, in milliseconds. + * @param timeSinceTackChange Time since the boat changed its tack, in milliseconds. + */ + public void setTimeSinceTackChange(long timeSinceTackChange) { + this.timeSinceTackChange = timeSinceTackChange; + } } diff --git a/mock/src/main/java/seng302/Model/Event.java b/mock/src/main/java/seng302/Model/Event.java index 3811b4f2..64afebfe 100644 --- a/mock/src/main/java/seng302/Model/Event.java +++ b/mock/src/main/java/seng302/Model/Event.java @@ -45,7 +45,7 @@ public class Event { System.out.println("Sending Boat"); sendBoatData(); - int scaleFactor = 5;//TEMP - was 15. + int scaleFactor = 25;//TEMP - was 15. Race newRace = new Race(raceDataSource, scaleFactor, mockOutput); new Thread((newRace)).start(); } diff --git a/mock/src/main/java/seng302/Model/Race.java b/mock/src/main/java/seng302/Model/Race.java index 896c2253..890293cf 100644 --- a/mock/src/main/java/seng302/Model/Race.java +++ b/mock/src/main/java/seng302/Model/Race.java @@ -72,7 +72,7 @@ public class Race implements Runnable { this.boundary = boundary; this.windSpeed = 12;//TODO could use input parameters for these. And should fluctuate during race. - this.windDirection = 0; + this.windDirection = 180; //TODO refactor this.startTime = System.currentTimeMillis() + (this.PRERACE_TIME / this.scaleFactor); @@ -246,6 +246,7 @@ public class Race implements Runnable { startLeg.calculateDistance(); boat.setCurrentLeg(startLeg); boat.setHeading(boat.calculateHeading()); + boat.setTimeSinceTackChange(999999);//We set a large time since tack change so that it calculates a new VMG when the simulation starts. } } } @@ -308,27 +309,40 @@ public class Race implements Runnable { protected void updatePosition(Boat boat, int millisecondsElapsed) { //distanceTravelled = velocity (nm p hr) * time taken to update loop - double distanceTravelled = (boat.getScaledVelocity() * millisecondsElapsed) / 3600000; + double distanceTravelled = (boat.getVelocity() * this.scaleFactor * millisecondsElapsed) / 3600000; double totalDistanceTravelled = distanceTravelled + boat.getDistanceTravelledInLeg(); boolean finish = boat.getCurrentLeg().getName().equals("Finish"); if (!finish) { - int windAngle = 360;//todo - get the wind speed from somewhere, using 360 for now - if(boat.getCurrentLeg().getName().endsWith("to Windward Gate")){//todo something is broken in this if statement, not sure what - double totalDistanceTravelledInTack = distanceTravelled + boat.getDistanceTravelledInTack(); + double totalDistanceTravelledInTack = distanceTravelled + boat.getDistanceTravelledInTack(); - double bound1 = (boat.calculateBearingToDestination()-90)%360; - double bound2 = (boat.calculateBearingToDestination()+90)%360; + double bound1 = (boat.calculateBearingToDestination() - 90) % 360; + double bound2 = (boat.calculateBearingToDestination() + 90) % 360; + //TODO the actual bearing bounds need to be the interval in which the boat won't go out of bounds. + bound1 = 0; + bound2 = 360; - VMG newHeading = boat.getPolars().calculateVMG(windAngle, 30, - boat.calculateBearingToDestination(), min(bound1, bound2), max(bound1,bound2)); + boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + this.scaleFactor * millisecondsElapsed); - double azimuth = newHeading.getBearing(); - if (newHeading.getBearing() > 180){ - azimuth = newHeading.getBearing() -360; - } + //How fast a boat can turn, in degrees per millisecond. + double turnRate = 0.03; //Roughly 30 per second, or 12 seconds per revolution. + + //How much the boat is allowed to turn, considering how long since it last turned. + double turnAngle = turnRate * boat.getTimeSinceTackChange(); + + + //System.out.println("boat " + boat.getAbbrev() + " turn angle is " + turnAngle + ".");//TEMP DEBUG REMOVE + + //Find the bounds on what angle the boat is allowed to travel at. + bound1 = boat.getHeading() - turnAngle; + bound2 = boat.getHeading() + turnAngle; + + + VMG newHeading = boat.getPolars().calculateVMG(this.windDirection, this.windSpeed, + boat.calculateBearingToDestination(), bound1, bound2); + // if (!GPSCoordinate.isInsideBoundary(calculatePosition(boat.getCurrentPosition(), // 1, azimuth), boundary)){ @@ -337,35 +351,55 @@ public class Race implements Runnable { // newHeading.bearing = tempHeading; // } + + //Is this new VMG better than the current VMG? + + double angleBetweenDestAndHeading = boat.getHeading() - boat.calculateBearingToDestination(); + double angleBetweenDestAndNewVMG = newHeading.getBearing() - boat.calculateBearingToDestination(); + double currentVelocity = cos(Math.toRadians(angleBetweenDestAndHeading)) * boat.getVelocity(); + double vmgVelocity = cos(Math.toRadians(angleBetweenDestAndNewVMG)) * newHeading.getSpeed(); + //System.out.println("boat " + boat.getAbbrev() + " current velocity is " + currentVelocity + " knots, possible VMG is " + vmgVelocity + " knots.");//TEMP DEBUG REMOVE + if (vmgVelocity > currentVelocity) { boat.setHeading(newHeading.getBearing()); boat.setVelocity(newHeading.getSpeed()); + boat.setTimeSinceTackChange(0); - //calc the distance travelled in a straight line to windward - double angleBetweenDestAndHeading = newHeading.getBearing() - boat.calculateBearingToDestination(); - totalDistanceTravelled = cos(angleBetweenDestAndHeading)*totalDistanceTravelledInTack; - boat.setDistanceTravelledInLeg(totalDistanceTravelled); + //System.out.println("boat " + boat.getAbbrev() + " has a new bearing " + boat.getHeading() + " degrees, and is " + boat.calculateDistanceToNextMarker() + " nautical miles to the next marker. Velocity to next marker is " + boat.getVelocity() + " knots.");//TEMP DEBUG REMOVE + } - //Calculate boat's new position by adding the distance travelled onto the start point of the leg - boat.setCurrentPosition(calculatePosition(boat.getCurrentPosition(), - totalDistanceTravelledInTack, azimuth)); - }else{ - boat.setHeading(boat.calculateHeading()); - //update boat's distance travelled - boat.setDistanceTravelledInLeg(totalDistanceTravelled); - //Calculate boat's new position by adding the distance travelled onto the start point of the leg - boat.setCurrentPosition(calculatePosition(boat.getCurrentLeg().getStartMarker().getAverageGPSCoordinate(), - totalDistanceTravelled, boat.calculateAzimuth(boat.getCurrentLeg().getStartMarker().getAverageGPSCoordinate(), - boat.getCurrentLeg().getEndMarker().getAverageGPSCoordinate()))); + + + //TODO one way to fix the boat's rapid turning it to only update the velocity/heading every X seconds (e.g., every 5 seconds). + //TODO may need a lower tack period + //TODO another way would be to allow boats use a better VMG if it is within turnRate * timeSinceTackChange. E.g., after 100ms a boat can select a more optimal VMG within 5deg of their current bearing. After 500ms VMG can be within 25deg of current bearing. After 2sec it can be 100deg of bearing, etc... + + //calc the distance travelled in a straight line to windward + //double angleBetweenDestAndHeading = boat.getHeading() - boat.calculateBearingToDestination(); + totalDistanceTravelled = cos(Math.toRadians(angleBetweenDestAndHeading))*totalDistanceTravelledInTack; + boat.setDistanceTravelledInLeg(totalDistanceTravelled); + + + //Calculate boat's new position by adding the distance travelled onto the start point of the leg + double azimuth = boat.getHeading(); + if (azimuth > 180) { + azimuth = azimuth - 360; } + boat.setCurrentPosition(calculatePosition(boat.getCurrentPosition(), + totalDistanceTravelledInTack, azimuth)); + + } } protected void checkPosition(Boat boat, long timeElapsed) { - System.out.println(boat.getDistanceTravelledInLeg()); - System.out.println(boat.getCurrentLeg().getDistance()); - System.out.println(" "); - if (boat.getDistanceTravelledInLeg() > boat.getCurrentLeg().getDistance()) { + //System.out.println(boat.getDistanceTravelledInLeg()); + //System.out.println(boat.getCurrentLeg().getDistance()); + //System.out.println(" "); + //if (boat.getDistanceTravelledInLeg() > boat.getCurrentLeg().getDistance()) { + //The distance (in nautical miles) within which the boat needs to get in order to consider that it has reached the marker. + double epsilon = 100.0 / Constants.NMToMetersConversion; //100 meters. TODO should be more like 5-10. + if (boat.calculateDistanceToNextMarker() < epsilon) { //boat has passed onto new leg if (boat.getCurrentLeg().getName().equals("Finish")) { //boat has finished @@ -387,6 +421,9 @@ public class Race implements Runnable { boat.setCurrentLeg(nextLeg); //Add overshoot distance into the distance travelled for the next leg boat.setDistanceTravelledInLeg(boat.getDistanceTravelledInLeg()); + + //Setting a high value for this allows the boat to immediately do a large turn, as it has needs to in order to get to the next mark. + boat.setTimeSinceTackChange(999999); } } }