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]
main
fjc40 9 years ago
parent 7f37dbbcb5
commit f4d5df9df2

@ -8,5 +8,8 @@ public class Constants {
public static final int NMToMetersConversion = 1852; // 1 nautical mile = 1852 meters 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;
} }

@ -1,6 +1,7 @@
package seng302.Model; package seng302.Model;
import org.geotools.referencing.GeodeticCalculator; 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() { public String getName() {
return name; return name;
} }

@ -85,10 +85,16 @@ public class Polars {
* @param destinationAngle The angle between the boat and the destination point. * @param destinationAngle The angle between the boat and the destination point.
* @param bearingLowerBound The lowest bearing (angle) that the boat may travel on. * @param bearingLowerBound The lowest bearing (angle) that the boat may travel on.
* @param bearingUpperBound The highest 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) { 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. //Sorts polar angles.
for (ArrayList<Double> angles : this.polarAngles.values()) { for (ArrayList<Double> 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. //Check that the velocity is better.
if (vmgTemp > bestVMGVelocity) { if (vmgTemp > bestVMGVelocity) {
bestVMGVelocity = vmgTemp; bestVMGVelocity = interpolatedSpeed;
bestVMGAngle = trueBoatBearing; bestVMGAngle = trueBoatBearing;
} }

@ -10,6 +10,7 @@ import org.geotools.referencing.GeodeticCalculator;
import seng302.Constants; import seng302.Constants;
import seng302.DataInput.RaceDataSource; import seng302.DataInput.RaceDataSource;
import seng302.MockOutput; import seng302.MockOutput;
import seng302.Networking.Messages.BoatLocation;
import seng302.Networking.Messages.BoatStatus; import seng302.Networking.Messages.BoatStatus;
import seng302.Networking.Messages.Enums.BoatStatusEnum; import seng302.Networking.Messages.Enums.BoatStatusEnum;
import seng302.Networking.Messages.RaceStatus; import seng302.Networking.Messages.RaceStatus;
@ -47,6 +48,11 @@ public class Race implements Runnable {
private int finished = 0; private int finished = 0;
private List<GPSCoordinate> boundary; private List<GPSCoordinate> 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 * Initailiser for Race
@ -65,6 +71,9 @@ public class Race implements Runnable {
this.mockOutput = mockOutput; this.mockOutput = mockOutput;
this.boundary = boundary; this.boundary = boundary;
this.windSpeed = 12;//TODO could use input parameters for these. And should fluctuate during race.
this.windDirection = 0;
//TODO refactor //TODO refactor
this.startTime = System.currentTimeMillis() + (this.PRERACE_TIME / this.scaleFactor); this.startTime = System.currentTimeMillis() + (this.PRERACE_TIME / this.scaleFactor);
@ -140,7 +149,7 @@ public class Race implements Runnable {
boatOffset = (boatOffset + 1) % (startingBoats.size()); boatOffset = (boatOffset + 1) % (startingBoats.size());
if (timeLeft <= 60000/scaleFactor && timeLeft > 0) { 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); mockOutput.parseRaceStatus(raceStatus);
} }
else if (timeLeft <= 0) { else if (timeLeft <= 0) {
@ -151,7 +160,7 @@ public class Race implements Runnable {
stop(); stop();
} }
else { 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); mockOutput.parseRaceStatus(raceStatus);
} }
currentTime = System.currentTimeMillis(); currentTime = System.currentTimeMillis();
@ -182,6 +191,7 @@ public class Race implements Runnable {
if (boatsFinished < startingBoats.size()) { if (boatsFinished < startingBoats.size()) {
//Get the current time. //Get the current time.
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
//Update the total elapsed time. //Update the total elapsed time.
totalTimeElapsed = currentTime - timeRaceStarted; totalTimeElapsed = currentTime - timeRaceStarted;
ArrayList<BoatStatus> boatStatuses = new ArrayList<BoatStatus>(); ArrayList<BoatStatus> boatStatuses = new ArrayList<BoatStatus>();
@ -205,7 +215,7 @@ public class Race implements Runnable {
boat.getCurrentLeg().getLegNumber() >= 0 ? BoatStatusEnum.RACING : BoatStatusEnum.DNF, boat.getCurrentLeg().getLegNumber())); boat.getCurrentLeg().getLegNumber() >= 0 ? BoatStatusEnum.RACING : BoatStatusEnum.DNF, boat.getCurrentLeg().getLegNumber()));
} }
if (startingBoats.size()==finished){ 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); mockOutput.parseRaceStatus(raceStatus);
} }
} else { } else {
@ -213,7 +223,7 @@ public class Race implements Runnable {
} }
} }
boatOffset = (boatOffset + 1) % (startingBoats.size()); 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); mockOutput.parseRaceStatus(raceStatus);
} }
} }

@ -89,6 +89,25 @@ public class PolarsTest {
//assertEquals(calcVMGAngle3, vmgAngle3, angleEpsilon); //assertEquals(calcVMGAngle3, vmgAngle3, angleEpsilon);
//assertEquals(calcVMGSpeed3, vmgSpeed3, speedEpsilon); //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);
} }
} }

@ -251,7 +251,7 @@ public class BoatLocation extends AC35Data {
* @param angle Angle to convert. double. * @param angle Angle to convert. double.
* @return short representation of heading. * @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); short angleShort = (short) ((angle / 180.0) * 32768.0);

Loading…
Cancel
Save