|
|
|
|
@ -1,17 +1,14 @@
|
|
|
|
|
package mock.model;
|
|
|
|
|
|
|
|
|
|
import javafx.animation.AnimationTimer;
|
|
|
|
|
import network.Messages.BoatLocation;
|
|
|
|
|
import network.Messages.BoatStatus;
|
|
|
|
|
import network.Messages.Enums.BoatStatusEnum;
|
|
|
|
|
import network.Messages.Enums.RaceStatusEnum;
|
|
|
|
|
import network.Messages.LatestMessages;
|
|
|
|
|
import network.Messages.RaceStatus;
|
|
|
|
|
import network.Utils.AC35UnitConverter;
|
|
|
|
|
import org.opengis.geometry.primitive.*;
|
|
|
|
|
import shared.dataInput.BoatDataSource;
|
|
|
|
|
import shared.dataInput.RaceDataSource;
|
|
|
|
|
import shared.dataInput.RegattaDataSource;
|
|
|
|
|
import shared.model.*;
|
|
|
|
|
import shared.model.Bearing;
|
|
|
|
|
|
|
|
|
|
import java.time.ZonedDateTime;
|
|
|
|
|
import java.time.temporal.ChronoUnit;
|
|
|
|
|
@ -46,22 +43,11 @@ public class MockRace extends Race {
|
|
|
|
|
*/
|
|
|
|
|
private int scaleFactor;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The percent chance that a boat fails the race, and enters a DNF state, at each checkpoint.
|
|
|
|
|
* 0 = 0%, 100 = 100%.
|
|
|
|
|
*/
|
|
|
|
|
private int dnfChance = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Object used to generate changes in wind speed/direction.
|
|
|
|
|
*/
|
|
|
|
|
private WindGenerator windGenerator;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and sends events to the given mockOutput.
|
|
|
|
|
* @param boatDataSource Data source for boat related data (yachts and marker boats).
|
|
|
|
|
@ -81,7 +67,6 @@ public class MockRace extends Race {
|
|
|
|
|
|
|
|
|
|
this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.boundary);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Set up wind generator. It may be tidier to create this outside the race (with the values sourced from a data file maybe?) and pass it in.
|
|
|
|
|
this.windGenerator = new WindGenerator(
|
|
|
|
|
Bearing.fromDegrees(225),
|
|
|
|
|
@ -93,7 +78,6 @@ public class MockRace extends Race {
|
|
|
|
|
|
|
|
|
|
//Wind.
|
|
|
|
|
this.setWind(windGenerator.generateBaselineWind());
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -125,103 +109,11 @@ public class MockRace extends Race {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Runnable for the thread.
|
|
|
|
|
*/
|
|
|
|
|
public void run() {
|
|
|
|
|
initialiseBoats();
|
|
|
|
|
initialiseWindDirection();
|
|
|
|
|
this.countdownTimer.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parse the compound marker boats through mock output.
|
|
|
|
|
*/
|
|
|
|
|
private void parseMarks() {
|
|
|
|
|
for (CompoundMark compoundMark : this.compoundMarks) {
|
|
|
|
|
|
|
|
|
|
//Get the individual marks from the compound mark.
|
|
|
|
|
Mark mark1 = compoundMark.getMark1();
|
|
|
|
|
Mark mark2 = compoundMark.getMark2();
|
|
|
|
|
|
|
|
|
|
//If they aren't null, parse them (some compound marks only have one mark).
|
|
|
|
|
if (mark1 != null) {
|
|
|
|
|
this.parseIndividualMark(mark1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mark2 != null) {
|
|
|
|
|
this.parseIndividualMark(mark2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parses an individual marker boat, and sends it to mockOutput.
|
|
|
|
|
* @param mark The marker boat to parse.
|
|
|
|
|
*/
|
|
|
|
|
private void parseIndividualMark(Mark mark) {
|
|
|
|
|
|
|
|
|
|
//Create message.
|
|
|
|
|
BoatLocation boatLocation = new BoatLocation(
|
|
|
|
|
mark.getSourceID(),
|
|
|
|
|
mark.getPosition().getLatitude(),
|
|
|
|
|
mark.getPosition().getLongitude(),
|
|
|
|
|
this.boatLocationSequenceNumber,
|
|
|
|
|
0, 0,
|
|
|
|
|
this.raceClock.getCurrentTimeMilli());
|
|
|
|
|
|
|
|
|
|
//Iterates the sequence number.
|
|
|
|
|
this.boatLocationSequenceNumber++;
|
|
|
|
|
|
|
|
|
|
this.latestMessages.setBoatLocation(boatLocation);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parse the boats in the race, and send it to mockOutput.
|
|
|
|
|
*/
|
|
|
|
|
private void parseBoatLocations() {
|
|
|
|
|
|
|
|
|
|
//Parse each boat.
|
|
|
|
|
for (MockBoat boat : this.boats) {
|
|
|
|
|
|
|
|
|
|
this.parseIndividualBoatLocation(boat);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parses an individual boat, and sends it to mockOutput.
|
|
|
|
|
* @param boat The boat to parse.
|
|
|
|
|
*/
|
|
|
|
|
private void parseIndividualBoatLocation(MockBoat boat) {
|
|
|
|
|
|
|
|
|
|
BoatLocation boatLocation = new BoatLocation(
|
|
|
|
|
boat.getSourceID(),
|
|
|
|
|
boat.getCurrentPosition().getLatitude(),
|
|
|
|
|
boat.getCurrentPosition().getLongitude(),
|
|
|
|
|
this.boatLocationSequenceNumber,
|
|
|
|
|
boat.getBearing().degrees(),
|
|
|
|
|
boat.getCurrentSpeed(),
|
|
|
|
|
this.raceClock.getCurrentTimeMilli());
|
|
|
|
|
|
|
|
|
|
//Iterates the sequence number.
|
|
|
|
|
this.boatLocationSequenceNumber++;
|
|
|
|
|
|
|
|
|
|
this.latestMessages.setBoatLocation(boatLocation);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the race time to a specified value, in milliseconds since the unix epoch.
|
|
|
|
|
* @param currentTime Milliseconds since unix epoch.
|
|
|
|
|
*/
|
|
|
|
|
private void updateRaceTime(long currentTime) {
|
|
|
|
|
public void updateRaceTime(long currentTime) {
|
|
|
|
|
this.raceClock.setUTCTime(currentTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -229,7 +121,7 @@ public class MockRace extends Race {
|
|
|
|
|
/**
|
|
|
|
|
* Updates the race status enumeration based on the current time.
|
|
|
|
|
*/
|
|
|
|
|
private void updateRaceStatusEnum() {
|
|
|
|
|
public void updateRaceStatusEnum() {
|
|
|
|
|
|
|
|
|
|
//The millisecond duration of the race. Negative means it hasn't started, so we flip sign.
|
|
|
|
|
long timeToStart = - this.raceClock.getDurationMilli();
|
|
|
|
|
@ -254,55 +146,13 @@ public class MockRace extends Race {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parses the race status, and sends it to mockOutput.
|
|
|
|
|
*/
|
|
|
|
|
private void parseRaceStatus() {
|
|
|
|
|
|
|
|
|
|
//A race status message contains a list of boat statuses.
|
|
|
|
|
List<BoatStatus> boatStatuses = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
//Add each boat status to the status list.
|
|
|
|
|
for (MockBoat boat : this.boats) {
|
|
|
|
|
|
|
|
|
|
BoatStatus boatStatus = new BoatStatus(
|
|
|
|
|
boat.getSourceID(),
|
|
|
|
|
boat.getStatus(),
|
|
|
|
|
boat.getCurrentLeg().getLegNumber(),
|
|
|
|
|
boat.getEstimatedTimeAtNextMark().toInstant().toEpochMilli() );
|
|
|
|
|
|
|
|
|
|
boatStatuses.add(boatStatus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Convert wind direction and speed to ints. //TODO this conversion should be done inside the racestatus class.
|
|
|
|
|
int windDirectionInt = AC35UnitConverter.encodeHeading(this.getWindDirection().degrees());
|
|
|
|
|
int windSpeedInt = (int) (this.getWindSpeed() * Constants.KnotsToMMPerSecond);
|
|
|
|
|
|
|
|
|
|
//Create race status object, and send it.
|
|
|
|
|
RaceStatus raceStatus = new RaceStatus(
|
|
|
|
|
System.currentTimeMillis(),
|
|
|
|
|
this.raceId,
|
|
|
|
|
this.getRaceStatusEnum().getValue(),
|
|
|
|
|
this.raceClock.getStartingTimeMilli(),
|
|
|
|
|
windDirectionInt,
|
|
|
|
|
windSpeedInt,
|
|
|
|
|
this.getRaceType().getValue(),
|
|
|
|
|
boatStatuses);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.latestMessages.setRaceStatus(raceStatus);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets the status of all boats in the race to RACING.
|
|
|
|
|
*/
|
|
|
|
|
private void setBoatsStatusToRacing() {
|
|
|
|
|
public void setBoatsStatusToRacing() {
|
|
|
|
|
|
|
|
|
|
for (MockBoat boat : this.boats) {
|
|
|
|
|
boat.setStatus(BoatStatusEnum.RACING);
|
|
|
|
|
@ -314,7 +164,7 @@ public class MockRace extends Race {
|
|
|
|
|
* Sets the estimated time at next mark for each boat to a specified time. This is used during the countdown timer to provide this value to boat before the race starts.
|
|
|
|
|
* @param time The time to provide to each boat.
|
|
|
|
|
*/
|
|
|
|
|
private void setBoatsTimeNextMark(ZonedDateTime time) {
|
|
|
|
|
public void setBoatsTimeNextMark(ZonedDateTime time) {
|
|
|
|
|
|
|
|
|
|
for (MockBoat boat : this.boats) {
|
|
|
|
|
boat.setEstimatedTimeAtNextMark(time);
|
|
|
|
|
@ -322,150 +172,12 @@ public class MockRace extends Race {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Countdown timer until race starts.
|
|
|
|
|
*/
|
|
|
|
|
protected AnimationTimer countdownTimer = new AnimationTimer() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long currentTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long arg0) {
|
|
|
|
|
|
|
|
|
|
//Update race time.
|
|
|
|
|
updateRaceTime(currentTime);
|
|
|
|
|
|
|
|
|
|
//Update the race status based on the current time.
|
|
|
|
|
updateRaceStatusEnum();
|
|
|
|
|
|
|
|
|
|
//Provide boat's with an estimated time at next mark until the race starts.
|
|
|
|
|
setBoatsTimeNextMark(raceClock.getCurrentTime());
|
|
|
|
|
|
|
|
|
|
//Parse the boat locations.
|
|
|
|
|
parseBoatLocations();
|
|
|
|
|
|
|
|
|
|
//Parse the marks.
|
|
|
|
|
parseMarks();
|
|
|
|
|
|
|
|
|
|
// Change wind direction
|
|
|
|
|
changeWindDirection();
|
|
|
|
|
|
|
|
|
|
//Parse the race status.
|
|
|
|
|
parseRaceStatus();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (getRaceStatusEnum() == RaceStatusEnum.STARTED) {
|
|
|
|
|
setBoatsStatusToRacing();
|
|
|
|
|
raceTimer.start();
|
|
|
|
|
this.stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Update the animations timer's time.
|
|
|
|
|
currentTime = System.currentTimeMillis();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Timer that runs for the duration of the race, until all boats finish.
|
|
|
|
|
*/
|
|
|
|
|
private AnimationTimer raceTimer = new AnimationTimer() {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Start time of loop, in milliseconds.
|
|
|
|
|
*/
|
|
|
|
|
long timeRaceStarted = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Current time during a loop iteration.
|
|
|
|
|
*/
|
|
|
|
|
long currentTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The time of the previous frame, in milliseconds.
|
|
|
|
|
*/
|
|
|
|
|
long lastFrameTime = timeRaceStarted;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long arg0) {
|
|
|
|
|
|
|
|
|
|
//Get the current time.
|
|
|
|
|
currentTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
//Update race time.
|
|
|
|
|
updateRaceTime(currentTime);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//As long as there is at least one boat racing, we still simulate the race.
|
|
|
|
|
if (getNumberOfActiveBoats() != 0) {
|
|
|
|
|
|
|
|
|
|
//Get the time period of this frame.
|
|
|
|
|
long framePeriod = currentTime - lastFrameTime;
|
|
|
|
|
|
|
|
|
|
//For each boat, we update its position, and generate a BoatLocationMessage.
|
|
|
|
|
for (MockBoat boat : boats) {
|
|
|
|
|
|
|
|
|
|
//If it is still racing, update its position.
|
|
|
|
|
if (boat.getStatus() == BoatStatusEnum.RACING) {
|
|
|
|
|
|
|
|
|
|
updatePosition(boat, framePeriod, raceClock.getDurationMilli());
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
//Otherwise, the race is over!
|
|
|
|
|
raceFinished.start();
|
|
|
|
|
setRaceStatusEnum(RaceStatusEnum.FINISHED);
|
|
|
|
|
this.stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getNumberOfActiveBoats() != 0) {
|
|
|
|
|
// Change wind direction
|
|
|
|
|
changeWindDirection();
|
|
|
|
|
|
|
|
|
|
//Parse the boat locations.
|
|
|
|
|
parseBoatLocations();
|
|
|
|
|
|
|
|
|
|
//Parse the marks.
|
|
|
|
|
parseMarks();
|
|
|
|
|
|
|
|
|
|
//Parse the race status.
|
|
|
|
|
parseRaceStatus();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Update the last frame time.
|
|
|
|
|
this.lastFrameTime = currentTime;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Broadcast that the race has finished.
|
|
|
|
|
*/
|
|
|
|
|
protected AnimationTimer raceFinished = new AnimationTimer(){
|
|
|
|
|
int iters = 0;
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long now) {
|
|
|
|
|
|
|
|
|
|
parseRaceStatus();
|
|
|
|
|
|
|
|
|
|
if (iters > 500) {
|
|
|
|
|
stop();
|
|
|
|
|
}
|
|
|
|
|
iters++;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialise the boats in the race.
|
|
|
|
|
* This sets their starting positions and current legs.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
protected void initialiseBoats() {
|
|
|
|
|
public void initialiseBoats() {
|
|
|
|
|
|
|
|
|
|
//Gets the starting positions of the boats.
|
|
|
|
|
List<GPSCoordinate> startingPositions = getSpreadStartingPositions();
|
|
|
|
|
@ -498,8 +210,7 @@ public class MockRace extends Race {
|
|
|
|
|
boat.setStatus(BoatStatusEnum.PRESTART);
|
|
|
|
|
|
|
|
|
|
//We set a large time since tack change so that it calculates a new VMG when the simulation starts.
|
|
|
|
|
boat.setTimeSinceTackChange(999999);
|
|
|
|
|
|
|
|
|
|
boat.setTimeSinceTackChange(Long.MAX_VALUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
@ -510,7 +221,7 @@ public class MockRace extends Race {
|
|
|
|
|
*
|
|
|
|
|
* @return A list of starting positions.
|
|
|
|
|
*/
|
|
|
|
|
public List<GPSCoordinate> getSpreadStartingPositions() {
|
|
|
|
|
private List<GPSCoordinate> getSpreadStartingPositions() {
|
|
|
|
|
|
|
|
|
|
//The first compound marker of the race - the starting gate.
|
|
|
|
|
CompoundMark compoundMark = this.legs.get(0).getStartCompoundMark();
|
|
|
|
|
@ -552,33 +263,6 @@ public class MockRace extends Race {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculates a boat's VMG.
|
|
|
|
|
* @param boat The boat to calculate VMG for.
|
|
|
|
|
* @param bearingBounds An array containing the lower and upper acceptable bearing bounds to keep the boat in the course.
|
|
|
|
|
* @return VMG for the specified boat.
|
|
|
|
|
*/
|
|
|
|
|
private VMG calculateVMG(MockBoat boat, Bearing[] bearingBounds) {
|
|
|
|
|
|
|
|
|
|
//Get the lower and upper acceptable bounds.
|
|
|
|
|
Bearing lowerAcceptableBound = bearingBounds[0];
|
|
|
|
|
Bearing upperAcceptableBound = bearingBounds[1];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Find the VMG inside these bounds.
|
|
|
|
|
VMG bestVMG = boat.getPolars().calculateVMG(
|
|
|
|
|
this.getWindDirection(),
|
|
|
|
|
this.getWindSpeed(),
|
|
|
|
|
boat.calculateBearingToNextMarker(),
|
|
|
|
|
lowerAcceptableBound,
|
|
|
|
|
upperAcceptableBound);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return bestVMG;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determines whether or not a given VMG improves the velocity of a boat, if it were currently using currentVMG.
|
|
|
|
|
* @param currentVMG The current VMG of the boat.
|
|
|
|
|
@ -586,7 +270,7 @@ public class MockRace extends Race {
|
|
|
|
|
* @param bearingToDestination The bearing between the boat and its destination.
|
|
|
|
|
* @return True if the new VMG is improves velocity, false otherwise.
|
|
|
|
|
*/
|
|
|
|
|
private boolean improvesVelocity(VMG currentVMG, VMG potentialVMG, Bearing bearingToDestination) {
|
|
|
|
|
public boolean improvesVelocity(VMG currentVMG, VMG potentialVMG, Bearing bearingToDestination) {
|
|
|
|
|
|
|
|
|
|
//Calculates the angle between the boat and its destination.
|
|
|
|
|
Angle angleBetweenDestAndHeading = Angle.fromDegrees(currentVMG.getBearing().degrees() - bearingToDestination.degrees());
|
|
|
|
|
@ -630,13 +314,19 @@ public class MockRace extends Race {
|
|
|
|
|
* @param updatePeriodMilliseconds The time, in milliseconds, since the last update.
|
|
|
|
|
* @param totalElapsedMilliseconds The total number of milliseconds that have elapsed since the start of the race.
|
|
|
|
|
*/
|
|
|
|
|
protected void updatePosition(MockBoat boat, long updatePeriodMilliseconds, long totalElapsedMilliseconds) {
|
|
|
|
|
public void updatePosition(MockBoat boat, long updatePeriodMilliseconds, long totalElapsedMilliseconds) {
|
|
|
|
|
|
|
|
|
|
//Checks if the current boat has finished the race or not.
|
|
|
|
|
boolean finish = this.isLastLeg(boat.getCurrentLeg());
|
|
|
|
|
|
|
|
|
|
if (!finish) {
|
|
|
|
|
if (!finish && totalElapsedMilliseconds >= updatePeriodMilliseconds) {
|
|
|
|
|
|
|
|
|
|
if (boat.getCurrentSpeed() == 0) {
|
|
|
|
|
newOptimalVMG(boat);
|
|
|
|
|
boat.setBearing(boat.calculateBearingToNextMarker());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setBoatSpeed(boat);
|
|
|
|
|
|
|
|
|
|
//Calculates the distance travelled, in meters, in the current timeslice.
|
|
|
|
|
double distanceTravelledMeters = boat.calculateMetersTravelled(updatePeriodMilliseconds);
|
|
|
|
|
@ -646,42 +336,48 @@ public class MockRace extends Race {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Move the boat forwards that many meters, and advances its time counters by enough milliseconds.
|
|
|
|
|
boat.moveForwards(distanceTravelledMeters, updatePeriodMilliseconds * this.scaleFactor);
|
|
|
|
|
|
|
|
|
|
boat.moveForwards(distanceTravelledMeters);
|
|
|
|
|
boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + updatePeriodMilliseconds);
|
|
|
|
|
|
|
|
|
|
//Only get a new VMG if the boat will go outside the course, or X seconds have elapsed.
|
|
|
|
|
boolean willStayInsideCourse = this.checkBearingInsideCourse(boat.getBearing(), boat.getCurrentPosition());
|
|
|
|
|
long tackPeriod = 15000;
|
|
|
|
|
if (!willStayInsideCourse || (boat.getTimeSinceTackChange() > tackPeriod)) {
|
|
|
|
|
if (boat.isAutoVMG()) {
|
|
|
|
|
newOptimalVMG(boat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Calculate the boat's bearing bounds, to ensure that it doesn't go out of the course.
|
|
|
|
|
Bearing[] bearingBounds = this.calculateBearingBounds(boat);
|
|
|
|
|
this.updateEstimatedTime(boat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Calculate the new VMG.
|
|
|
|
|
VMG newVMG = this.calculateVMG(boat, bearingBounds);
|
|
|
|
|
private void newOptimalVMG(MockBoat boat) {
|
|
|
|
|
long tackPeriod = 15000;
|
|
|
|
|
|
|
|
|
|
if (boat.getTimeSinceTackChange() > tackPeriod) {
|
|
|
|
|
//Calculate the new VMG.
|
|
|
|
|
VMG newVMG = boat.getPolars().calculateVMG(
|
|
|
|
|
this.getWindDirection(),
|
|
|
|
|
this.getWindSpeed(),
|
|
|
|
|
boat.calculateBearingToNextMarker(),
|
|
|
|
|
Bearing.fromDegrees(0d),
|
|
|
|
|
Bearing.fromDegrees(359.99999d));
|
|
|
|
|
|
|
|
|
|
//If the new vmg improves velocity, use it.
|
|
|
|
|
if (improvesVelocity(boat, newVMG)) {
|
|
|
|
|
boat.setVMG(newVMG);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
//We also need to use the new VMG if our current bearing will take us out of the course.
|
|
|
|
|
if (!willStayInsideCourse) {
|
|
|
|
|
boat.setVMG(newVMG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//If the new vmg improves velocity, use it.
|
|
|
|
|
if (improvesVelocity(boat, newVMG)) {
|
|
|
|
|
boat.setVMG(newVMG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.updateEstimatedTime(boat);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Check the boats position (update leg and stuff).
|
|
|
|
|
this.checkPosition(boat, totalElapsedMilliseconds);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setBoatSpeed(MockBoat boat) {
|
|
|
|
|
VMG vmg = boat.getPolars().calculateVMG(
|
|
|
|
|
this.getWindDirection(),
|
|
|
|
|
this.getWindSpeed(),
|
|
|
|
|
boat.getBearing(),
|
|
|
|
|
boat.getBearing(),
|
|
|
|
|
boat.getBearing());
|
|
|
|
|
if (vmg.getSpeed() > 0) {
|
|
|
|
|
boat.setCurrentSpeed(vmg.getSpeed());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -1001,20 +697,10 @@ public class MockRace extends Race {
|
|
|
|
|
return boats;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialises the wind bearing with the value of the windBaselineBearing.
|
|
|
|
|
*/
|
|
|
|
|
protected void initialiseWindDirection() {
|
|
|
|
|
//Set the starting bearing.
|
|
|
|
|
this.setWind(windGenerator.generateBaselineWind());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Changes the wind direction randomly, while keeping it within [windLowerBound, windUpperBound].
|
|
|
|
|
*/
|
|
|
|
|
protected void changeWindDirection() {
|
|
|
|
|
public void changeWindDirection() {
|
|
|
|
|
|
|
|
|
|
Wind nextWind = windGenerator.generateNextWind(raceWind.getValue());
|
|
|
|
|
|
|
|
|
|
@ -1042,4 +728,8 @@ public class MockRace extends Race {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<CompoundMark> getCompoundMarks() {
|
|
|
|
|
return compoundMarks;
|
|
|
|
|
}
|
|
|
|
|
}
|