Added POlar Linear Interpolation #story[1186]
This commit is contained in:
@@ -104,6 +104,7 @@ public class Event {
|
||||
this.xmlFileType = XMLFileType.Contents;
|
||||
|
||||
this.boatPolars = PolarParser.parse("mock/polars/acc_polars.csv");
|
||||
PolarParser.parseNewPolars("mock/polars/acc_polars.csv");
|
||||
|
||||
|
||||
//Parse the XML files into data sources.
|
||||
|
||||
@@ -3,6 +3,7 @@ package mock.dataInput;
|
||||
|
||||
|
||||
import mock.exceptions.InvalidPolarFileException;
|
||||
import mock.model.NewPolars;
|
||||
import mock.model.Polars;
|
||||
import shared.model.Bearing;
|
||||
|
||||
@@ -89,7 +90,6 @@ public class PolarParser {
|
||||
Bearing angle = Bearing.fromDegrees(angleDegrees);
|
||||
double boatSpeedKnots = Double.parseDouble(row[i + 1]);
|
||||
polarTable.addEstimate(windSpeedKnots, angle, boatSpeedKnots);
|
||||
polarTable.addPolars(windSpeedKnots, angle, boatSpeedKnots);
|
||||
|
||||
} catch (NumberFormatException 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(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);
|
||||
//If the new vmg improves velocity, use it.
|
||||
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));
|
||||
//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) {
|
||||
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<>();
|
||||
|
||||
//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.
|
||||
* <br>
|
||||
|
||||
Reference in New Issue
Block a user