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.
main
fjc40 9 years ago
parent 60458f0f7a
commit cc54c92d5b

@ -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 {
/**
* The name of the boat/team.
*/
private String name;
private double velocity;
private double scaledVelocity;
/**
* 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;
private double distanceTravelledInTack;
private double distanceForTack;
private GPSCoordinate currentPosition;
/**
* 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;
private boolean started = false;
private double heading;
/**
* Whether or not the boat has finished the race.
*/
private boolean hasFinishedRace = false;
/**
* This stores a boat's polars table. Can be used to calculate VMG.
* 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 void setVelocity(double velocity) {
this.velocity = velocity;
public double getCurrentSpeed() {
return currentSpeed;
}
public double getScaledVelocity() {
return scaledVelocity;
public void setCurrentSpeed(double currentSpeed) {
this.currentSpeed = currentSpeed;
}
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());

@ -22,7 +22,7 @@ public class BoatTest {
Marker endMarker = new Marker(new GPSCoordinate(50, 0));
Leg start = new Leg("Start", startMarker, endMarker, 0);
TEST_BOAT.setCurrentLeg(start);
assertEquals(TEST_BOAT.calculateAzimuth(startMarker.getAverageGPSCoordinate(), endMarker.getAverageGPSCoordinate()), 0, 1e-8);
assertEquals(GPSCoordinate.calculateAzimuth(startMarker.getAverageGPSCoordinate(), endMarker.getAverageGPSCoordinate()), 0, 1e-8);
}
@Test
@ -31,7 +31,7 @@ public class BoatTest {
Marker endMarker = new Marker(new GPSCoordinate(-50, 0));
Leg start = new Leg("Start", startMarker, endMarker, 0);
TEST_BOAT.setCurrentLeg(start);
assertEquals(TEST_BOAT.calculateAzimuth(startMarker.getAverageGPSCoordinate(), endMarker.getAverageGPSCoordinate()), 180, 1e-8);
assertEquals(GPSCoordinate.calculateAzimuth(startMarker.getAverageGPSCoordinate(), endMarker.getAverageGPSCoordinate()), 180, 1e-8);
}
@ -42,7 +42,7 @@ public class BoatTest {
Marker endMarker = new Marker(new GPSCoordinate(0, 50));
Leg start = new Leg("Start", startMarker, endMarker, 0);
TEST_BOAT.setCurrentLeg(start);
assertEquals(TEST_BOAT.calculateAzimuth(startMarker.getAverageGPSCoordinate(), endMarker.getAverageGPSCoordinate()), 90, 1e-8);
assertEquals(GPSCoordinate.calculateAzimuth(startMarker.getAverageGPSCoordinate(), endMarker.getAverageGPSCoordinate()), 90, 1e-8);
}
@ -52,7 +52,7 @@ public class BoatTest {
Marker endMarker = new Marker(new GPSCoordinate(0, -50));
Leg start = new Leg("Start", startMarker, endMarker, 0);
TEST_BOAT.setCurrentLeg(start);
assertEquals(TEST_BOAT.calculateAzimuth(startMarker.getAverageGPSCoordinate(), endMarker.getAverageGPSCoordinate()), -90, 1e-8);
assertEquals(GPSCoordinate.calculateAzimuth(startMarker.getAverageGPSCoordinate(), endMarker.getAverageGPSCoordinate()), -90, 1e-8);
}

@ -119,7 +119,7 @@ public class RaceTest{
testBoat.setDistanceTravelledInLeg(1);
testRace.checkPosition(testBoat, 1);
assertEquals(testBoat.getVelocity(), 0, 1e-8);
assertEquals(testBoat.getCurrentSpeed(), 0, 1e-8);
} catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
e.printStackTrace();

Loading…
Cancel
Save