diff --git a/racevisionGame/src/main/java/mock/app/Event.java b/racevisionGame/src/main/java/mock/app/Event.java index c054e2fb..2e758ad5 100644 --- a/racevisionGame/src/main/java/mock/app/Event.java +++ b/racevisionGame/src/main/java/mock/app/Event.java @@ -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. diff --git a/racevisionGame/src/main/java/mock/dataInput/PolarParser.java b/racevisionGame/src/main/java/mock/dataInput/PolarParser.java index 3fa3e6de..0e87696b 100644 --- a/racevisionGame/src/main/java/mock/dataInput/PolarParser.java +++ b/racevisionGame/src/main/java/mock/dataInput/PolarParser.java @@ -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 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(); + + } + } diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 276fc329..77e7b04d 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -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()); } diff --git a/racevisionGame/src/main/java/mock/model/NewPolars.java b/racevisionGame/src/main/java/mock/model/NewPolars.java new file mode 100644 index 00000000..1810ce52 --- /dev/null +++ b/racevisionGame/src/main/java/mock/model/NewPolars.java @@ -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, + private static Map> 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 prevTWS = null; + for (TreeMap tws: polars.values()){ + if (prevTWS == null){ + prevTWS = tws; + continue; + } + double previousTWA = -1; + TreeMap 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 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 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; + + + } + +} diff --git a/racevisionGame/src/main/java/mock/model/Polars.java b/racevisionGame/src/main/java/mock/model/Polars.java index 35a22fc4..943214cf 100644 --- a/racevisionGame/src/main/java/mock/model/Polars.java +++ b/racevisionGame/src/main/java/mock/model/Polars.java @@ -22,8 +22,6 @@ public class Polars { */ private HashMap> polarAngles = new HashMap<>(); - //true wind speed, - private Map> 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 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 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. *