Added Mock.Angle, Mock.Azimith, Mock.Bearing. These encapsulate the relevant angle type, to allow for better type safety (e.g., to avoid passing a bearing into an azimuth function).
Added more comments to Mock.Boat. Moved calculateAzimuth to GPSCoordinate, from Boat.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package seng302.Model;
|
||||
|
||||
/**
|
||||
* This represents an angle.
|
||||
* Has functions to return angle as either degrees or radians.
|
||||
*/
|
||||
public class Angle {
|
||||
|
||||
/**
|
||||
* The angle stored in this object.
|
||||
* Degrees.
|
||||
*/
|
||||
private double degrees;
|
||||
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
* Don't use this.
|
||||
* This is protected because you need to use the static helper functions {@link #fromDegrees(double)} and {@link #fromRadians(double)} to construct an Angle object.
|
||||
*
|
||||
* @param degrees The value, in degrees, to initialize this Angle object with.
|
||||
*/
|
||||
protected Angle(double degrees) {
|
||||
this.degrees = degrees;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an Angle object from an angle value in degrees.
|
||||
* @param degrees Angle value in degrees.
|
||||
* @return Angle object.
|
||||
*/
|
||||
public static Angle fromDegrees(double degrees) {
|
||||
Angle angle = new Angle(degrees);
|
||||
return angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an Angle object from an angle value in radians.
|
||||
* @param radians Angle value in radians.
|
||||
* @return Angle object.
|
||||
*/
|
||||
public static Angle fromRadians(double radians) {
|
||||
return Angle.fromDegrees(Math.toDegrees(radians));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value of this Angle object, in degrees.
|
||||
* @return The value of this Angle object, in degrees.
|
||||
*/
|
||||
public double degrees() {
|
||||
return this.degrees;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value of this Angle object, in radians.
|
||||
* @return The value of this Angle object, in radians.
|
||||
*/
|
||||
public double radians() {
|
||||
return Math.toRadians(this.degrees);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package seng302.Model;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Represents an azimuth.
|
||||
* This is the angle between north and a target point.
|
||||
* It has the interval [-180, 180), and clockwise from north is positive.
|
||||
*/
|
||||
public class Azimuth extends Angle{
|
||||
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
* This is protected because you need to use the static helper functions {@link #fromDegrees(double)} and {@link #fromRadians(double)} to construct an Azimuth object.
|
||||
*
|
||||
* @param degrees The value, in degrees, to initialize this Azimuth object with.
|
||||
*/
|
||||
protected Azimuth(double degrees) {
|
||||
super(degrees);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts an angle in degrees into an angle in degrees in the correct interval for an azimuth - [-180, 180).
|
||||
* E.g., converts -183 to 177, or converts 250 to -110, or converts 180 to -180.
|
||||
* @param degrees Degree value to convert.
|
||||
* @return Degree value in interval [-180, 180).
|
||||
*/
|
||||
public static double toAzimuthInterval(double degrees) {
|
||||
|
||||
if (degrees >= 180) {
|
||||
//Too large.
|
||||
degrees -= 360;
|
||||
} else if (degrees < -180) {
|
||||
//Too small.
|
||||
degrees += 360;
|
||||
}
|
||||
|
||||
return degrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an Azimuth object from an angle value in degrees.
|
||||
* @param degrees Azimuth value in degrees.
|
||||
* @return Azimuth object.
|
||||
*/
|
||||
public static Azimuth fromDegrees(double degrees) {
|
||||
//Ensure the angle is in the correct interval.
|
||||
double degreesInInterval = Azimuth.toAzimuthInterval(degrees);
|
||||
|
||||
Azimuth azimuth = new Azimuth(degreesInInterval);
|
||||
return azimuth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an Azimuth object from an angle value in radians.
|
||||
* @param radians Azimuth value in radians.
|
||||
* @return Azimuth object.
|
||||
*/
|
||||
public static Azimuth fromRadians(double radians) {
|
||||
return Azimuth.fromDegrees(Math.toDegrees(radians));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package seng302.Model;
|
||||
|
||||
/**
|
||||
* Represents a bearing. Also known as a heading.
|
||||
* This is the angle between north and a target point.
|
||||
* Has the interval [0, 360), and clockwise from north is positive.
|
||||
*/
|
||||
public class Bearing extends Angle {
|
||||
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
* This is protected because you need to use the static helper functions {@link #fromDegrees(double)} and {@link #fromRadians(double)} to construct a Bearing object.
|
||||
*
|
||||
* @param degrees The value, in degrees, to initialize this Bearing object with.
|
||||
*/
|
||||
protected Bearing(double degrees) {
|
||||
super(degrees);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts an angle in degrees into an angle in degrees in the correct interval for a bearing - [0, 360).
|
||||
* E.g., converts -183 to 177, or converts 425 to 65.
|
||||
* @param degrees Degree value to convert.
|
||||
* @return Degree value in interval [0, 360).
|
||||
*/
|
||||
public static double toBearingInterval(double degrees) {
|
||||
|
||||
if (degrees >= 360) {
|
||||
//Too large.
|
||||
degrees -= 360;
|
||||
} else if (degrees < 0) {
|
||||
//Too small.
|
||||
degrees += 360;
|
||||
}
|
||||
|
||||
return degrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Bearing object from an angle value in degrees.
|
||||
* @param degrees Bearing value in degrees.
|
||||
* @return Bearing object.
|
||||
*/
|
||||
public static Bearing fromDegrees(double degrees) {
|
||||
//Ensure the angle is in the correct interval.
|
||||
double degreesInInterval = Bearing.toBearingInterval(degrees);
|
||||
|
||||
Bearing bearing = new Bearing(degreesInInterval);
|
||||
return bearing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Bearing object from an angle value in radians.
|
||||
* @param radians Bearing value in radians.
|
||||
* @return Bearing object.
|
||||
*/
|
||||
public static Bearing fromRadians(double radians) {
|
||||
return Bearing.fromDegrees(Math.toDegrees(radians));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -5,34 +5,91 @@ import seng302.Constants;
|
||||
|
||||
|
||||
/**
|
||||
* Created by esa46 on 1/05/17.
|
||||
* This class represents a boat during a race.
|
||||
*/
|
||||
public class Boat {
|
||||
private String name;
|
||||
private double velocity;
|
||||
private double scaledVelocity;
|
||||
private String country;
|
||||
private int sourceID;
|
||||
private Leg currentLeg;
|
||||
private double distanceTravelledInLeg;
|
||||
private double distanceTravelledInTack;
|
||||
private double distanceForTack;
|
||||
private GPSCoordinate currentPosition;
|
||||
private long timeFinished = -1;
|
||||
private boolean started = false;
|
||||
private double heading;
|
||||
/**
|
||||
* This stores a boat's polars table. Can be used to calculate VMG.
|
||||
* The name of the boat/team.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The current speed of the boat, in knots.
|
||||
* TODO knots
|
||||
*/
|
||||
private double currentSpeed;
|
||||
|
||||
/**
|
||||
* The current heading of the boat.
|
||||
* TODO bearing
|
||||
*/
|
||||
private double heading;
|
||||
|
||||
/**
|
||||
* The current position of the boat.
|
||||
*/
|
||||
private GPSCoordinate currentPosition;
|
||||
|
||||
/**
|
||||
* The country or team abbreviation of the boat.
|
||||
*/
|
||||
private String country;
|
||||
|
||||
/**
|
||||
* The source ID of the boat.
|
||||
* This uniquely identifies an entity during a race.
|
||||
*/
|
||||
private int sourceID;
|
||||
|
||||
/**
|
||||
* The leg of the race that the boat is currently on.
|
||||
*/
|
||||
private Leg currentLeg;
|
||||
|
||||
/**
|
||||
* The distance, in meters, that the boat has travelled in the current leg.
|
||||
* TODO meters
|
||||
*/
|
||||
private double distanceTravelledInLeg;
|
||||
|
||||
/**
|
||||
* The time, in milliseconds, that has elapsed during the current leg.
|
||||
* TODO milliseconds
|
||||
*/
|
||||
private double timeElapsedInCurrentLeg;
|
||||
|
||||
/**
|
||||
* The timestamp, in milliseconds, of when the boat finished the race.
|
||||
* Is -1 if it hasn't finished.
|
||||
* TODO milliseconds
|
||||
*/
|
||||
private long timeFinished = -1;
|
||||
|
||||
/**
|
||||
* Whether or not the boat has finished the race.
|
||||
*/
|
||||
private boolean hasFinishedRace = false;
|
||||
|
||||
/**
|
||||
* Whether or not the boat has started the race.
|
||||
*/
|
||||
private boolean hasStartedRace = false;
|
||||
|
||||
/**
|
||||
* This stores a boat's polars table.
|
||||
* Can be used to calculate VMG.
|
||||
*/
|
||||
private Polars polars;
|
||||
|
||||
/**
|
||||
* This stores the milliseconds since the boat has changed its tack, to allow for only updating the tack every X milliseconds.
|
||||
* TODO milliseconds
|
||||
*/
|
||||
private long timeSinceTackChange = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Boat initialiser which keeps all of the information of the boat.
|
||||
* Constructs a boat object with a given sourceID, name, country/team abbreviation, and polars table.
|
||||
*
|
||||
* @param sourceID The id of the boat
|
||||
* @param name The name of the Boat.
|
||||
@@ -46,34 +103,24 @@ public class Boat {
|
||||
this.polars = polars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the azimuth of the travel via map coordinates of the raceMarkers
|
||||
*
|
||||
* @return the direction that the boat is heading towards in degrees (-180 to 180).
|
||||
*/
|
||||
public double calculateAzimuth(GPSCoordinate start, GPSCoordinate end) {
|
||||
|
||||
GeodeticCalculator calc = new GeodeticCalculator();
|
||||
|
||||
calc.setStartingGeographicPoint(start.getLongitude(), start.getLatitude());
|
||||
calc.setDestinationGeographicPoint(end.getLongitude(), end.getLatitude());
|
||||
|
||||
return calc.getAzimuth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the heding depending on the calculated azimuth value
|
||||
* Calculate the heading depending on the calculated azimuth value
|
||||
* @return The azimuth value which is greater than 0
|
||||
*/
|
||||
public double calculateHeading() {
|
||||
double azimuth = this.calculateAzimuth(currentLeg.getStartCompoundMark().getAverageGPSCoordinate(),
|
||||
double azimuth = GPSCoordinate.calculateAzimuth(currentLeg.getStartCompoundMark().getAverageGPSCoordinate(),
|
||||
currentLeg.getEndCompoundMark().getAverageGPSCoordinate());
|
||||
|
||||
if (azimuth >= 0) {
|
||||
return azimuth;
|
||||
} else {
|
||||
//Azimuth is in the interval (-180, 180), but we need a heading in the interval [0, 360).
|
||||
if (azimuth < 0) {
|
||||
return azimuth + 360;
|
||||
}
|
||||
|
||||
return azimuth;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +128,7 @@ public class Boat {
|
||||
* @return The azimuth value which is greater than 0
|
||||
*/
|
||||
public double calculateBearingToDestination() {
|
||||
double azimuth = this.calculateAzimuth(this.currentPosition,
|
||||
double azimuth = GPSCoordinate.calculateAzimuth(this.currentPosition,
|
||||
currentLeg.getEndCompoundMark().getAverageGPSCoordinate());
|
||||
|
||||
if (azimuth >= 0) {
|
||||
@@ -125,21 +172,14 @@ public class Boat {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public double getVelocity() {
|
||||
return velocity;
|
||||
public double getCurrentSpeed() {
|
||||
return currentSpeed;
|
||||
}
|
||||
|
||||
public void setVelocity(double velocity) {
|
||||
this.velocity = velocity;
|
||||
public void setCurrentSpeed(double currentSpeed) {
|
||||
this.currentSpeed = currentSpeed;
|
||||
}
|
||||
|
||||
public double getScaledVelocity() {
|
||||
return scaledVelocity;
|
||||
}
|
||||
|
||||
public void setScaledVelocity(double scaledVelocity) {
|
||||
this.scaledVelocity = scaledVelocity;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
@@ -173,21 +213,6 @@ public class Boat {
|
||||
this.distanceTravelledInLeg = distanceTravelledInLeg;
|
||||
}
|
||||
|
||||
public double getDistanceTravelledInTack() {
|
||||
return distanceTravelledInTack;
|
||||
}
|
||||
|
||||
public void setDistanceTravelledInTack(double distanceTravelledInTack) {
|
||||
this.distanceTravelledInTack = distanceTravelledInTack;
|
||||
}
|
||||
|
||||
public double getDistanceForTack() {
|
||||
return distanceForTack;
|
||||
}
|
||||
|
||||
public void setDistanceForTack(double distanceForTack) {
|
||||
this.distanceForTack = distanceForTack;
|
||||
}
|
||||
|
||||
public GPSCoordinate getCurrentPosition() {
|
||||
return currentPosition;
|
||||
@@ -205,12 +230,12 @@ public class Boat {
|
||||
this.timeFinished = timeFinished;
|
||||
}
|
||||
|
||||
public boolean isStarted() {
|
||||
return started;
|
||||
public boolean isHasStartedRace() {
|
||||
return hasStartedRace;
|
||||
}
|
||||
|
||||
public void setStarted(boolean started) {
|
||||
this.started = started;
|
||||
public void setHasStartedRace(boolean hasStartedRace) {
|
||||
this.hasStartedRace = hasStartedRace;
|
||||
}
|
||||
|
||||
public double getHeading() {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package seng302.Model;
|
||||
|
||||
import org.geotools.referencing.GeodeticCalculator;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -152,5 +154,22 @@ public class GPSCoordinate {
|
||||
return aRatio >= bRatio;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the azimuth between two points.
|
||||
* This is an angle in the interval (-180, 180), with
|
||||
*
|
||||
* @return the direction that the boat is heading towards in degrees (-180 to 180).
|
||||
*/
|
||||
public static double calculateAzimuth(GPSCoordinate start, GPSCoordinate end) {
|
||||
|
||||
GeodeticCalculator calc = new GeodeticCalculator();
|
||||
|
||||
calc.setStartingGeographicPoint(start.getLongitude(), start.getLatitude());
|
||||
calc.setDestinationGeographicPoint(end.getLongitude(), end.getLatitude());
|
||||
|
||||
return calc.getAzimuth();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -143,10 +143,10 @@ public class Race implements Runnable {
|
||||
checkPosition(boat, totalTimeElapsed);
|
||||
}
|
||||
if (boat.getTimeFinished() > 0) {
|
||||
mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), boat.getVelocity());
|
||||
mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), boat.getCurrentSpeed());
|
||||
boatStatuses.add(new BoatStatus(boat.getSourceID(), BoatStatusEnum.FINISHED, boat.getCurrentLeg().getLegNumber()));
|
||||
} else {
|
||||
mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), boat.getVelocity());
|
||||
mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), boat.getCurrentSpeed());
|
||||
boatStatuses.add(new BoatStatus(boat.getSourceID(),
|
||||
boat.getCurrentLeg().getLegNumber() >= 0 ? BoatStatusEnum.RACING : BoatStatusEnum.DNF, boat.getCurrentLeg().getLegNumber()));
|
||||
}
|
||||
@@ -174,8 +174,7 @@ public class Race implements Runnable {
|
||||
if (boat != null) {
|
||||
Leg newLeg = new Leg(name, new Marker(startingPositions.get(i)), endMark, 0);
|
||||
boat.setCurrentLeg(newLeg);
|
||||
boat.setVelocity(Constants.TEST_VELOCITIES[i]);
|
||||
boat.setScaledVelocity(boat.getVelocity() * scaleFactor);
|
||||
boat.setCurrentSpeed(Constants.TEST_VELOCITIES[i]);//TODO we should get rid of TEST_VELOCITIES since speed is based off of wind speed/angle.
|
||||
boat.setCurrentPosition(startingPositions.get(i));
|
||||
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.
|
||||
@@ -248,14 +247,14 @@ 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.getVelocity() * this.scaleFactor * millisecondsElapsed) / 3600000;
|
||||
double distanceTravelled = (boat.getCurrentSpeed() * this.scaleFactor * millisecondsElapsed) / 3600000;
|
||||
double totalDistanceTravelled = distanceTravelled + boat.getDistanceTravelledInLeg();
|
||||
|
||||
boolean finish = boat.getCurrentLeg().getName().equals("Finish");
|
||||
if (!finish) {
|
||||
|
||||
|
||||
double totalDistanceTravelledInTack = distanceTravelled + boat.getDistanceTravelledInTack();
|
||||
double totalDistanceTravelledInTack = distanceTravelled;//TODO FIX// + boat.getDistanceTravelledInTack();
|
||||
|
||||
double bound1 = (boat.calculateBearingToDestination() - 90) % 360;
|
||||
double bound2 = (boat.calculateBearingToDestination() + 90) % 360;
|
||||
@@ -295,15 +294,15 @@ public class Race implements Runnable {
|
||||
|
||||
double angleBetweenDestAndHeading = boat.getHeading() - boat.calculateBearingToDestination();
|
||||
double angleBetweenDestAndNewVMG = newHeading.getBearing() - boat.calculateBearingToDestination();
|
||||
double currentVelocity = cos(Math.toRadians(angleBetweenDestAndHeading)) * boat.getVelocity();
|
||||
double currentVelocity = cos(Math.toRadians(angleBetweenDestAndHeading)) * boat.getCurrentSpeed();
|
||||
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.setCurrentSpeed(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
|
||||
//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.getCurrentSpeed() + " knots.");//TEMP DEBUG REMOVE
|
||||
}
|
||||
|
||||
|
||||
@@ -346,14 +345,12 @@ public class Race implements Runnable {
|
||||
boatsFinished++;
|
||||
boat.setTimeFinished(timeElapsed);
|
||||
boat.setTimeFinished(timeElapsed);
|
||||
boat.setVelocity(0);
|
||||
boat.setScaledVelocity(0);
|
||||
boat.setCurrentSpeed(0);
|
||||
} else if (doNotFinish()) {
|
||||
boatsFinished++;
|
||||
boat.setTimeFinished(timeElapsed);
|
||||
boat.setCurrentLeg(new Leg("DNF", -1));
|
||||
boat.setVelocity(0);
|
||||
boat.setScaledVelocity(0);
|
||||
boat.setCurrentSpeed(0);
|
||||
} else {
|
||||
//Calculate how much the boat overshot the marker by
|
||||
boat.setDistanceTravelledInLeg(boat.getDistanceTravelledInLeg() - boat.getCurrentLeg().getDistance());
|
||||
|
||||
Reference in New Issue
Block a user