|
|
|
|
@ -27,6 +27,7 @@ import static java.lang.Math.cos;
|
|
|
|
|
* Is responsible for simulating the race, and sending messages to a MockOutput instance.
|
|
|
|
|
*/
|
|
|
|
|
public class MockRace extends Race {
|
|
|
|
|
private RaceServer server;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* An observable list of boats in the race.
|
|
|
|
|
@ -72,6 +73,8 @@ public class MockRace extends Race {
|
|
|
|
|
|
|
|
|
|
//Wind.
|
|
|
|
|
this.setWind(windGenerator.generateBaselineWind());
|
|
|
|
|
|
|
|
|
|
this.server = new RaceServer(this, latestMessages);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -112,88 +115,6 @@ public class MockRace extends Race {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
@ -231,48 +152,6 @@ 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);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -320,16 +199,16 @@ public class MockRace extends Race {
|
|
|
|
|
setBoatsTimeNextMark(raceClock.getCurrentTime());
|
|
|
|
|
|
|
|
|
|
//Parse the boat locations.
|
|
|
|
|
parseBoatLocations();
|
|
|
|
|
server.parseBoatLocations();
|
|
|
|
|
|
|
|
|
|
//Parse the marks.
|
|
|
|
|
parseMarks();
|
|
|
|
|
server.parseMarks();
|
|
|
|
|
|
|
|
|
|
// Change wind direction
|
|
|
|
|
changeWindDirection();
|
|
|
|
|
|
|
|
|
|
//Parse the race status.
|
|
|
|
|
parseRaceStatus();
|
|
|
|
|
server.parseRaceStatus();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (getRaceStatusEnum() == RaceStatusEnum.STARTED) {
|
|
|
|
|
@ -404,13 +283,13 @@ public class MockRace extends Race {
|
|
|
|
|
changeWindDirection();
|
|
|
|
|
|
|
|
|
|
//Parse the boat locations.
|
|
|
|
|
parseBoatLocations();
|
|
|
|
|
server.parseBoatLocations();
|
|
|
|
|
|
|
|
|
|
//Parse the marks.
|
|
|
|
|
parseMarks();
|
|
|
|
|
server.parseMarks();
|
|
|
|
|
|
|
|
|
|
//Parse the race status.
|
|
|
|
|
parseRaceStatus();
|
|
|
|
|
server.parseRaceStatus();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Update the last frame time.
|
|
|
|
|
@ -427,7 +306,7 @@ public class MockRace extends Race {
|
|
|
|
|
@Override
|
|
|
|
|
public void handle(long now) {
|
|
|
|
|
|
|
|
|
|
parseRaceStatus();
|
|
|
|
|
server.parseRaceStatus();
|
|
|
|
|
|
|
|
|
|
if (iters > 500) {
|
|
|
|
|
stop();
|
|
|
|
|
@ -442,7 +321,7 @@ public class MockRace extends 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();
|
|
|
|
|
@ -487,7 +366,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();
|
|
|
|
|
@ -529,23 +408,6 @@ public class MockRace extends Race {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculates a boat's VMG.
|
|
|
|
|
* @param boat The boat to calculate VMG for.
|
|
|
|
|
* @return VMG for the specified boat.
|
|
|
|
|
*/
|
|
|
|
|
private VMG calculateVMG(MockBoat boat) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Find the VMG inside these bounds.
|
|
|
|
|
VMG bestVMG = boat.getPolars().calculateVMG(this.getWindDirection(), this.getWindSpeed(), boat.calculateBearingToNextMarker(), Bearing.fromDegrees(0d), Bearing.fromDegrees(359.99999d));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
@ -553,7 +415,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());
|
|
|
|
|
@ -597,7 +459,7 @@ 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());
|
|
|
|
|
@ -613,12 +475,19 @@ 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);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long tackPeriod = 15000;
|
|
|
|
|
|
|
|
|
|
if (boat.getTimeSinceTackChange() > tackPeriod) {
|
|
|
|
|
//Calculate the new VMG.
|
|
|
|
|
VMG newVMG = this.calculateVMG(boat);
|
|
|
|
|
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.
|
|
|
|
|
@ -629,56 +498,6 @@ public class MockRace extends Race {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.updateEstimatedTime(boat);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Check the boats position (update leg and stuff).
|
|
|
|
|
this.checkPosition(boat, totalElapsedMilliseconds);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if a boat has finished any legs, or has pulled out of race (DNF).
|
|
|
|
|
* @param boat The boat to check.
|
|
|
|
|
* @param timeElapsed The total time, in milliseconds, that has elapsed since the race started.
|
|
|
|
|
*/
|
|
|
|
|
protected void checkPosition(MockBoat boat, long timeElapsed) {
|
|
|
|
|
|
|
|
|
|
//The distance, in nautical miles, within which the boat needs to get in order to consider that it has reached the marker.
|
|
|
|
|
double epsilonNauticalMiles = 100.0 / Constants.NMToMetersConversion; //100 meters. TODO should be more like 5-10.
|
|
|
|
|
|
|
|
|
|
if (boat.calculateDistanceToNextMarker() < epsilonNauticalMiles) {
|
|
|
|
|
//Boat has reached its target marker, and has moved on to a new leg.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Calculate how much the boat overshot the marker by.
|
|
|
|
|
double overshootMeters = boat.calculateDistanceToNextMarker();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Move boat on to next leg.
|
|
|
|
|
Leg nextLeg = this.legs.get(boat.getCurrentLeg().getLegNumber() + 1);
|
|
|
|
|
boat.setCurrentLeg(nextLeg);
|
|
|
|
|
|
|
|
|
|
//Add overshoot distance into the distance travelled for the next leg.
|
|
|
|
|
boat.setDistanceTravelledInLeg(overshootMeters);
|
|
|
|
|
|
|
|
|
|
//Setting a high value for this allows the boat to immediately do a large turn, as it needs to in order to get to the next mark.
|
|
|
|
|
boat.setTimeSinceTackChange(999999);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Check if the boat has finished or stopped racing.
|
|
|
|
|
|
|
|
|
|
if (this.isLastLeg(boat.getCurrentLeg())) {
|
|
|
|
|
//Boat has finished.
|
|
|
|
|
boat.setTimeFinished(timeElapsed);
|
|
|
|
|
boat.setCurrentSpeed(0);
|
|
|
|
|
boat.setStatus(BoatStatusEnum.FINISHED);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
@ -713,18 +532,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());
|
|
|
|
|
|
|
|
|
|
@ -752,4 +563,8 @@ public class MockRace extends Race {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<CompoundMark> getCompoundMarks() {
|
|
|
|
|
return compoundMarks;
|
|
|
|
|
}
|
|
|
|
|
}
|