Added POlar Linear Interpolation #story[1186]
This commit is contained in:
@@ -104,6 +104,7 @@ public class Event {
|
|||||||
this.xmlFileType = XMLFileType.Contents;
|
this.xmlFileType = XMLFileType.Contents;
|
||||||
|
|
||||||
this.boatPolars = PolarParser.parse("mock/polars/acc_polars.csv");
|
this.boatPolars = PolarParser.parse("mock/polars/acc_polars.csv");
|
||||||
|
PolarParser.parseNewPolars("mock/polars/acc_polars.csv");
|
||||||
|
|
||||||
|
|
||||||
//Parse the XML files into data sources.
|
//Parse the XML files into data sources.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package mock.dataInput;
|
|||||||
|
|
||||||
|
|
||||||
import mock.exceptions.InvalidPolarFileException;
|
import mock.exceptions.InvalidPolarFileException;
|
||||||
|
import mock.model.NewPolars;
|
||||||
import mock.model.Polars;
|
import mock.model.Polars;
|
||||||
import shared.model.Bearing;
|
import shared.model.Bearing;
|
||||||
|
|
||||||
@@ -89,7 +90,6 @@ public class PolarParser {
|
|||||||
Bearing angle = Bearing.fromDegrees(angleDegrees);
|
Bearing angle = Bearing.fromDegrees(angleDegrees);
|
||||||
double boatSpeedKnots = Double.parseDouble(row[i + 1]);
|
double boatSpeedKnots = Double.parseDouble(row[i + 1]);
|
||||||
polarTable.addEstimate(windSpeedKnots, angle, boatSpeedKnots);
|
polarTable.addEstimate(windSpeedKnots, angle, boatSpeedKnots);
|
||||||
polarTable.addPolars(windSpeedKnots, angle, boatSpeedKnots);
|
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new InvalidPolarFileException("Could not convert (Row,Col): (" + rowNumber + "," + i +") = " + row[i] + " to a double.", e);
|
throw new InvalidPolarFileException("Could not convert (Row,Col): (" + rowNumber + "," + i +") = " + row[i] + " to a double.", e);
|
||||||
@@ -105,4 +105,91 @@ public class PolarParser {
|
|||||||
return polarTable;
|
return polarTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a filename, this function parses it and generates a Polar object, which can be queried for polar information.
|
||||||
|
* @param filename The filename to load and read data from (loaded as a resource).
|
||||||
|
* @return A Polar table containing data from the given file.
|
||||||
|
*/
|
||||||
|
public static void parseNewPolars(String filename) throws InvalidPolarFileException {
|
||||||
|
NewPolars newPolars = new NewPolars();
|
||||||
|
|
||||||
|
|
||||||
|
//Open the file for reading.
|
||||||
|
InputStream fileStream = PolarParser.class.getClassLoader().getResourceAsStream(filename);
|
||||||
|
if (fileStream == null) {
|
||||||
|
throw new InvalidPolarFileException("Could not open polar data file: " + filename);
|
||||||
|
}
|
||||||
|
//Wrap it with buffered input stream to set encoding and buffer.
|
||||||
|
InputStreamReader in = null;
|
||||||
|
try {
|
||||||
|
in = new InputStreamReader(fileStream, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InvalidPolarFileException("Unsupported encoding: UTF-8", e);
|
||||||
|
}
|
||||||
|
BufferedReader inputStream = new BufferedReader(in);
|
||||||
|
|
||||||
|
|
||||||
|
//We expect the polar data file to have the column headings:
|
||||||
|
// Tws, Twa0, Bsp0, Twa1, Bsp1, UpTwa, UpBsp, Twa2, Bsp2, Twa3, Bsp3, Twa4, Bsp4, Twa5, Bsp5, Twa6, Bsp6, DnTwa, DnBsp, Twa7, Bsp7
|
||||||
|
//and to have 7 rows of data.
|
||||||
|
//Angles are expected to be in degrees, and velocities in knots.
|
||||||
|
|
||||||
|
|
||||||
|
//We read data rows, and split them into arrays of elements.
|
||||||
|
ArrayList<String[]> dataRows = new ArrayList<>(7);
|
||||||
|
try {
|
||||||
|
//Heading row.
|
||||||
|
//We skip the heading row by reading it.
|
||||||
|
String headingRow = inputStream.readLine();
|
||||||
|
|
||||||
|
//Data rows.
|
||||||
|
while (inputStream.ready()) {
|
||||||
|
//Read line.
|
||||||
|
String dataRow = inputStream.readLine();
|
||||||
|
|
||||||
|
//Split line.
|
||||||
|
String[] dataElements = dataRow.split(",");
|
||||||
|
|
||||||
|
//Add to collection.
|
||||||
|
dataRows.add(dataElements);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InvalidPolarFileException("Could not read from polar data file: " + filename, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Finished reading in data, now we need to construct polar rows and table from it.
|
||||||
|
//For each row...
|
||||||
|
int rowNumber = 0;
|
||||||
|
for (String[] row : dataRows) {
|
||||||
|
|
||||||
|
//For each pair of columns (the pair is angle, speed).
|
||||||
|
//We start at column 1 since column 0 is the wind speed column.
|
||||||
|
for (int i = 1; i < row.length; i += 2) {
|
||||||
|
|
||||||
|
//Add angle+speed=velocity estimate to polar table.
|
||||||
|
try {
|
||||||
|
|
||||||
|
//Add the polar value to the polar table
|
||||||
|
double windSpeedKnots = Double.parseDouble(row[0]);
|
||||||
|
double angleDegrees = Double.parseDouble(row[i]);
|
||||||
|
Bearing angle = Bearing.fromDegrees(angleDegrees);
|
||||||
|
double boatSpeedKnots = Double.parseDouble(row[i + 1]);
|
||||||
|
newPolars.addPolars(windSpeedKnots, angle, boatSpeedKnots);
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new InvalidPolarFileException("Could not convert (Row,Col): (" + rowNumber + "," + i +") = " + row[i] + " to a double.", e);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Increment row number.
|
||||||
|
rowNumber++;
|
||||||
|
|
||||||
|
}
|
||||||
|
newPolars.linearInterpolatePolars();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ public class MockRace extends Race {
|
|||||||
// Bearing.fromDegrees(0d),
|
// Bearing.fromDegrees(0d),
|
||||||
// Bearing.fromDegrees(359.99999d));
|
// Bearing.fromDegrees(359.99999d));
|
||||||
|
|
||||||
VMG newVMG = boat.getPolars().setBestVMG(this.getWindDirection(), this.getWindSpeed(), boat.getBearing());
|
VMG newVMG = NewPolars.setBestVMG(this.getWindDirection(), this.getWindSpeed(), boat.getBearing());
|
||||||
System.out.println(newVMG);
|
System.out.println(newVMG);
|
||||||
//If the new vmg improves velocity, use it.
|
//If the new vmg improves velocity, use it.
|
||||||
if (improvesVelocity(boat, newVMG)) {
|
if (improvesVelocity(boat, newVMG)) {
|
||||||
@@ -380,7 +380,11 @@ public class MockRace extends Race {
|
|||||||
// Bearing.fromDegrees(boat.getBearing().degrees() - 1),
|
// Bearing.fromDegrees(boat.getBearing().degrees() - 1),
|
||||||
// Bearing.fromDegrees(boat.getBearing().degrees() + 1));
|
// Bearing.fromDegrees(boat.getBearing().degrees() + 1));
|
||||||
//VMG vmg = boat.getPolars().setBestVMG(this.getWindDirection(), this.getWindSpeed(), boat.getBearing());
|
//VMG vmg = boat.getPolars().setBestVMG(this.getWindDirection(), this.getWindSpeed(), boat.getBearing());
|
||||||
VMG vmg = new VMG(Math.cos (boat.getBearing().radians() - this.getWindDirection().radians()) * this.getWindSpeed() * 4, boat.getBearing()) ;
|
VMG vmg = new VMG(NewPolars.calculateSpeed(
|
||||||
|
this.getWindDirection(),
|
||||||
|
this.getWindSpeed(),
|
||||||
|
boat.getBearing()
|
||||||
|
), boat.getBearing()) ;
|
||||||
if (vmg.getSpeed() > 0) {
|
if (vmg.getSpeed() > 0) {
|
||||||
boat.setCurrentSpeed(vmg.getSpeed());
|
boat.setCurrentSpeed(vmg.getSpeed());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package mock.model;
|
||||||
|
|
||||||
|
import shared.model.Bearing;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by fwy13 on 4/09/17.
|
||||||
|
*/
|
||||||
|
public class NewPolars {
|
||||||
|
|
||||||
|
|
||||||
|
//true wind speed, <true wind angle, best boat angle>
|
||||||
|
private static Map<Double, TreeMap<Double, Double>> polars = new TreeMap<>();
|
||||||
|
|
||||||
|
public static NewPolars newPolars = null;
|
||||||
|
|
||||||
|
public NewPolars(){
|
||||||
|
newPolars = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addPolars(double trueWindSpeed, Bearing trueWindAngle, double boatSpeed){
|
||||||
|
double tws = trueWindSpeed;
|
||||||
|
double bs = boatSpeed;
|
||||||
|
double twa = trueWindAngle.degrees();
|
||||||
|
if (!polars.containsKey(tws)){
|
||||||
|
polars.put(tws, new TreeMap<>());
|
||||||
|
}
|
||||||
|
polars.get(tws).put(twa, bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void linearInterpolatePolars(){
|
||||||
|
// double maxTWS = 0;
|
||||||
|
// for (double key: polars.keySet()){
|
||||||
|
// if (maxTWS < key){
|
||||||
|
// maxTWS = key;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
TreeMap<Double, Double> prevTWS = null;
|
||||||
|
for (TreeMap<Double, Double> tws: polars.values()){
|
||||||
|
if (prevTWS == null){
|
||||||
|
prevTWS = tws;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double previousTWA = -1;
|
||||||
|
TreeMap<Double, Double> iterableTWS = new TreeMap<>(tws);
|
||||||
|
for (double twa: iterableTWS.keySet()){
|
||||||
|
if (previousTWA == -1){
|
||||||
|
previousTWA = twa;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double twaDiff = twa - previousTWA;
|
||||||
|
double speedDiff = iterableTWS.get(twa) - iterableTWS.get(previousTWA);
|
||||||
|
double prevSpeed = iterableTWS.get(previousTWA);
|
||||||
|
double diff = speedDiff/twaDiff;
|
||||||
|
for (double i = previousTWA; i < twa; i ++){
|
||||||
|
double mult = i - previousTWA;
|
||||||
|
double newSpeed = diff * mult + prevSpeed;
|
||||||
|
System.out.println(newS);
|
||||||
|
tws.put(i, newSpeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getClosest(double value, Set<Double> set){
|
||||||
|
double closestVal = 0;
|
||||||
|
double smallestDiff = Double.MAX_VALUE;
|
||||||
|
for (double d: set){
|
||||||
|
double diff = Math.abs(value - d);
|
||||||
|
if (diff < smallestDiff){
|
||||||
|
closestVal = d;
|
||||||
|
smallestDiff = diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines which quadrant degrees are in
|
||||||
|
* 0/360 Degrees
|
||||||
|
* Quadrant 4 | Quadrant 1
|
||||||
|
* -----------------------
|
||||||
|
* Quadrant 3 | Quadrant 2
|
||||||
|
* @param degrees
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static int getQuadrant(double degrees){
|
||||||
|
return ((int) degrees % 360) / 90 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getBestSpeedInQuadrant(int quad, Map<Double, Double> set){
|
||||||
|
double quadVal = (double)((quad - 1) % 2);// this is as the hash map only has values 0 - 180
|
||||||
|
double min = quadVal* 90;
|
||||||
|
double max = (quadVal + 1) * 90;
|
||||||
|
double maxAngle = 0;
|
||||||
|
double maxSpeed = 0;
|
||||||
|
for (Double s: set.keySet()){
|
||||||
|
if (s >= min && s < max){
|
||||||
|
if (set.get(s) > maxSpeed){
|
||||||
|
maxAngle = s;
|
||||||
|
maxSpeed = set.get(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (quad > 2){
|
||||||
|
maxAngle += 180;
|
||||||
|
}
|
||||||
|
return maxAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param trueWindAngle
|
||||||
|
* @param trueWindSpeed
|
||||||
|
* @param boatAngle
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static VMG setBestVMG(Bearing trueWindAngle, double trueWindSpeed, Bearing boatAngle){
|
||||||
|
//speed
|
||||||
|
double closestSpeed = getClosest(trueWindSpeed, polars.keySet());
|
||||||
|
|
||||||
|
//not using true vmg, using general concept
|
||||||
|
double angle = Math.abs(trueWindAngle.degrees() - boatAngle.degrees());
|
||||||
|
|
||||||
|
int quad = getQuadrant(boatAngle.degrees());
|
||||||
|
double bestAngle = getBestSpeedInQuadrant(quad, polars.get(closestSpeed));
|
||||||
|
|
||||||
|
Bearing vmgAngle = Bearing.fromDegrees((bestAngle) % 360);
|
||||||
|
double boatSpeed = polars.get(closestSpeed).get(bestAngle);
|
||||||
|
|
||||||
|
return new VMG(boatSpeed, Bearing.fromDegrees(vmgAngle.degrees() + trueWindAngle.degrees()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double calculateSpeed(Bearing trueWindAngle, double trueWindSpeed, Bearing boatAngle){
|
||||||
|
//speed
|
||||||
|
double closestSpeed = getClosest(trueWindSpeed, polars.keySet());
|
||||||
|
|
||||||
|
double angleDiff = Math.abs(trueWindAngle.degrees() - boatAngle.degrees()) % 180;
|
||||||
|
double closestAngle = getClosest(angleDiff, polars.get(closestSpeed).keySet());
|
||||||
|
|
||||||
|
double boatSpeed = polars.get(closestSpeed).get(closestAngle);
|
||||||
|
|
||||||
|
return boatSpeed;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -22,8 +22,6 @@ public class Polars {
|
|||||||
*/
|
*/
|
||||||
private HashMap<Double, List<Bearing>> polarAngles = new HashMap<>();
|
private HashMap<Double, List<Bearing>> polarAngles = new HashMap<>();
|
||||||
|
|
||||||
//true wind speed, <true wind angle, best boat angle>
|
|
||||||
private Map<Double, HashMap<Double, Double>> polars = new TreeMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -77,85 +75,6 @@ public class Polars {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPolars(double trueWindSpeed, Bearing trueWindAngle, double boatSpeed){
|
|
||||||
double tws = trueWindSpeed;
|
|
||||||
double bs = boatSpeed;
|
|
||||||
double twa = trueWindAngle.degrees();
|
|
||||||
if (!polars.containsKey(tws)){
|
|
||||||
polars.put(tws, new HashMap<>());
|
|
||||||
}
|
|
||||||
polars.get(tws).put(twa, bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double getClosest(double value, Set<Double> set){
|
|
||||||
double closestVal = 0;
|
|
||||||
double smallestDiff = Double.MAX_VALUE;
|
|
||||||
for (double d: set){
|
|
||||||
double diff = Math.abs(value - d);
|
|
||||||
if (diff < smallestDiff){
|
|
||||||
closestVal = d;
|
|
||||||
smallestDiff = diff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return closestVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines which quadrant degrees are in
|
|
||||||
* 0/360 Degrees
|
|
||||||
* Quadrant 4 | Quadrant 1
|
|
||||||
* -----------------------
|
|
||||||
* Quadrant 3 | Quadrant 2
|
|
||||||
* @param degrees
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static int getQuadrant(double degrees){
|
|
||||||
return ((int) degrees % 360) / 90 + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double getBestSpeedInQuadrant(int quad, HashMap<Double, Double> set){
|
|
||||||
double quadVal = (double)((quad - 1) % 2);// this is as the hash map only has values 0 - 180
|
|
||||||
double min = quadVal* 90;
|
|
||||||
double max = (quadVal + 1) * 90;
|
|
||||||
double maxAngle = 0;
|
|
||||||
double maxSpeed = 0;
|
|
||||||
for (Double s: set.keySet()){
|
|
||||||
if (s >= min && s < max){
|
|
||||||
if (set.get(s) > maxSpeed){
|
|
||||||
maxAngle = s;
|
|
||||||
maxSpeed = set.get(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (quad > 2){
|
|
||||||
maxAngle += 180;
|
|
||||||
}
|
|
||||||
return maxAngle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param trueWindAngle
|
|
||||||
* @param trueWindSpeed
|
|
||||||
* @param boatAngle
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public VMG setBestVMG(Bearing trueWindAngle, double trueWindSpeed, Bearing boatAngle){
|
|
||||||
//speed
|
|
||||||
double closestSpeed = getClosest(trueWindSpeed, polars.keySet());
|
|
||||||
|
|
||||||
//not using true vmg, using general concept
|
|
||||||
double angle = Math.abs(trueWindAngle.degrees() - boatAngle.degrees());
|
|
||||||
|
|
||||||
int quad = getQuadrant(boatAngle.degrees());
|
|
||||||
double bestAngle = getBestSpeedInQuadrant(quad, polars.get(closestSpeed));
|
|
||||||
|
|
||||||
Bearing vmgAngle = Bearing.fromDegrees((bestAngle + trueWindSpeed) % 360);
|
|
||||||
double boatSpeed = Math.abs(trueWindSpeed * Math.cos(vmgAngle.radians())) * 4;
|
|
||||||
|
|
||||||
return new VMG(boatSpeed, vmgAngle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the VMG for a given wind angle, wind speed, and angle to destination. Will only return VMGs that have a true bearing (angle) within a given bound - this is to ensure that you can calculate VMGs without going out of bounds.
|
* Calculates the VMG for a given wind angle, wind speed, and angle to destination. Will only return VMGs that have a true bearing (angle) within a given bound - this is to ensure that you can calculate VMGs without going out of bounds.
|
||||||
* <br>
|
* <br>
|
||||||
|
|||||||
Reference in New Issue
Block a user