@ -72,7 +72,7 @@ public class Race implements Runnable {
this . boundary = boundary ;
this . boundary = boundary ;
this . windSpeed = 12 ; //TODO could use input parameters for these. And should fluctuate during race.
this . windSpeed = 12 ; //TODO could use input parameters for these. And should fluctuate during race.
this . windDirection = 0;
this . windDirection = 18 0;
//TODO refactor
//TODO refactor
this . startTime = System . currentTimeMillis ( ) + ( this . PRERACE_TIME / this . scaleFactor ) ;
this . startTime = System . currentTimeMillis ( ) + ( this . PRERACE_TIME / this . scaleFactor ) ;
@ -246,6 +246,7 @@ public class Race implements Runnable {
startLeg . calculateDistance ( ) ;
startLeg . calculateDistance ( ) ;
boat . setCurrentLeg ( startLeg ) ;
boat . setCurrentLeg ( startLeg ) ;
boat . setHeading ( boat . calculateHeading ( ) ) ;
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 ) {
protected void updatePosition ( Boat boat , int millisecondsElapsed ) {
//distanceTravelled = velocity (nm p hr) * time taken to update loop
//distanceTravelled = velocity (nm p hr) * time taken to update loop
double distanceTravelled = ( boat . get Scaled Velocity( ) * millisecondsElapsed ) / 3600000 ;
double distanceTravelled = ( boat . get Velocity( ) * this . scaleFactor * millisecondsElapsed ) / 3600000 ;
double totalDistanceTravelled = distanceTravelled + boat . getDistanceTravelledInLeg ( ) ;
double totalDistanceTravelled = distanceTravelled + boat . getDistanceTravelledInLeg ( ) ;
boolean finish = boat . getCurrentLeg ( ) . getName ( ) . equals ( "Finish" ) ;
boolean finish = boat . getCurrentLeg ( ) . getName ( ) . equals ( "Finish" ) ;
if ( ! 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 bound1 = ( boat . calculateBearingToDestination ( ) - 90 ) % 360 ;
double bound2 = ( 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 . setTimeSinceTackChange ( boat . getTimeSinceTackChange ( ) + this . scaleFactor * millisecondsElapsed ) ;
boat . calculateBearingToDestination ( ) , min ( bound1 , bound2 ) , max ( bound1 , bound2 ) ) ;
double azimuth = newHeading . getBearing ( ) ;
//How fast a boat can turn, in degrees per millisecond.
if ( newHeading . getBearing ( ) > 180 ) {
double turnRate = 0.03 ; //Roughly 30 per second, or 12 seconds per revolution.
azimuth = newHeading . getBearing ( ) - 360 ;
}
//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(),
// if (!GPSCoordinate.isInsideBoundary(calculatePosition(boat.getCurrentPosition(),
// 1, azimuth), boundary)){
// 1, azimuth), boundary)){
@ -337,35 +351,55 @@ public class Race implements Runnable {
// newHeading.bearing = tempHeading;
// 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 . setHeading ( newHeading . getBearing ( ) ) ;
boat . setVelocity ( newHeading . getSpeed ( ) ) ;
boat . setVelocity ( newHeading . getSpeed ( ) ) ;
boat . setTimeSinceTackChange ( 0 ) ;
//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
}
//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
//calc the distance travelled in a straight line to windward
double angleBetweenDestAndHeading = newHeading . getBearing ( ) - boat . calculateBearingToDestination ( ) ;
//double angleBetweenDestAndHeading = boat.getHeading() - boat.calculateBearingToDestination();
totalDistanceTravelled = cos ( angleBetweenDestAndHeading ) * totalDistanceTravelledInTack ;
totalDistanceTravelled = cos ( Math. toRadians ( angleBetweenDestAndHeading) ) * totalDistanceTravelledInTack ;
boat . setDistanceTravelledInLeg ( totalDistanceTravelled ) ;
boat . setDistanceTravelledInLeg ( totalDistanceTravelled ) ;
//Calculate boat's new position by adding the distance travelled onto the start point of the leg
//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 ( ) ,
boat . setCurrentPosition ( calculatePosition ( boat . getCurrentPosition ( ) ,
totalDistanceTravelledInTack , azimuth ) ) ;
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 ( ) ) ) ) ;
}
}
}
}
}
protected void checkPosition ( Boat boat , long timeElapsed ) {
protected void checkPosition ( Boat boat , long timeElapsed ) {
System . out . println ( boat . getDistanceTravelledInLeg ( ) ) ;
//System.out.println(boat.getDistanceTravelledInLeg());
System . out . println ( boat . getCurrentLeg ( ) . getDistance ( ) ) ;
//System.out.println(boat.getCurrentLeg().getDistance());
System . out . println ( " " ) ;
//System.out.println(" ");
if ( boat . getDistanceTravelledInLeg ( ) > boat . getCurrentLeg ( ) . getDistance ( ) ) {
//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
//boat has passed onto new leg
if ( boat . getCurrentLeg ( ) . getName ( ) . equals ( "Finish" ) ) {
if ( boat . getCurrentLeg ( ) . getName ( ) . equals ( "Finish" ) ) {
//boat has finished
//boat has finished
@ -387,6 +421,9 @@ public class Race implements Runnable {
boat . setCurrentLeg ( nextLeg ) ;
boat . setCurrentLeg ( nextLeg ) ;
//Add overshoot distance into the distance travelled for the next leg
//Add overshoot distance into the distance travelled for the next leg
boat . setDistanceTravelledInLeg ( boat . getDistanceTravelledInLeg ( ) ) ;
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 ) ;
}
}
}
}
}
}