From 9a5d20bdf28bae996d0d2ffb14f46b4f57f560a1 Mon Sep 17 00:00:00 2001 From: Fan-Wu Yang Date: Thu, 31 Aug 2017 17:39:17 +1200 Subject: [PATCH 1/8] Added new polar function, currently it does not distinguish between up wind and downwind and doesn't find it in the correct quadrant #story[1186] --- .../main/java/mock/dataInput/PolarParser.java | 1 + .../src/main/java/mock/model/MockRace.java | 32 ++++++------ .../src/main/java/mock/model/Polars.java | 49 +++++++++++++++++-- .../src/main/java/mock/model/RaceServer.java | 4 +- .../src/main/java/mock/model/VMG.java | 4 ++ 5 files changed, 69 insertions(+), 21 deletions(-) diff --git a/racevisionGame/src/main/java/mock/dataInput/PolarParser.java b/racevisionGame/src/main/java/mock/dataInput/PolarParser.java index d33c0ac5..3fa3e6de 100644 --- a/racevisionGame/src/main/java/mock/dataInput/PolarParser.java +++ b/racevisionGame/src/main/java/mock/dataInput/PolarParser.java @@ -89,6 +89,7 @@ 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); diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 379123af..2c19ea10 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -356,14 +356,15 @@ public class MockRace extends Race { if (boat.getTimeSinceTackChange() > tackPeriod) { //Calculate the new VMG. - VMG newVMG = boat.getPolars().calculateVMG( - this.getWindDirection(), - this.getWindSpeed(), - boat.calculateBearingToNextMarker(), - Bearing.fromDegrees(0d), - Bearing.fromDegrees(359.99999d)); - - +// VMG newVMG = boat.getPolars().calculateVMG( +// this.getWindDirection(), +// this.getWindSpeed(), +// boat.calculateBearingToNextMarker(), +// Bearing.fromDegrees(0d), +// Bearing.fromDegrees(359.99999d)); + + VMG newVMG = boat.getPolars().setBestVMG(this.getWindDirection(), this.getWindSpeed(), boat.getBearing()); + System.out.println(newVMG); //If the new vmg improves velocity, use it. if (improvesVelocity(boat, newVMG)) { boat.setVMG(newVMG); @@ -372,12 +373,13 @@ public class MockRace extends Race { } private void setBoatSpeed(MockBoat boat) { - VMG vmg = boat.getPolars().calculateVMG( - this.getWindDirection(), - this.getWindSpeed(), - boat.getBearing(), - Bearing.fromDegrees(boat.getBearing().degrees() - 1), - Bearing.fromDegrees(boat.getBearing().degrees() + 1)); +// VMG vmg = boat.getPolars().calculateVMG( +// this.getWindDirection(), +// this.getWindSpeed(), +// boat.getBearing(), +// Bearing.fromDegrees(boat.getBearing().degrees() - 1), +// Bearing.fromDegrees(boat.getBearing().degrees() + 1)); + VMG vmg = boat.getPolars().setBestVMG(this.getWindDirection(), this.getWindSpeed(), boat.getBearing()); if (vmg.getSpeed() > 0) { boat.setCurrentSpeed(vmg.getSpeed()); } @@ -527,7 +529,7 @@ public class MockRace extends Race { if (boat.isStarboardSide(roundingMark) && GPSCoordinate.passesLine(roundingMark.getPosition(), roundingChecks.get(0), boat.getPosition(), legBearing) && - gateCheck && + gateCheck && boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) { boat.increaseRoundingStatus(); if (boat.getCurrentLeg().getLegNumber() + 2 >= legs.size()){ diff --git a/racevisionGame/src/main/java/mock/model/Polars.java b/racevisionGame/src/main/java/mock/model/Polars.java index 32ee8842..a18aa627 100644 --- a/racevisionGame/src/main/java/mock/model/Polars.java +++ b/racevisionGame/src/main/java/mock/model/Polars.java @@ -3,10 +3,7 @@ package mock.model; import javafx.util.Pair; import shared.model.Bearing; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Encapsulates an entire polar table. Has a function to calculate VMG. @@ -25,6 +22,8 @@ public class Polars { */ private HashMap> polarAngles = new HashMap<>(); + //true wind speed, + private HashMap> polars = new HashMap<>(); @@ -78,7 +77,49 @@ 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); + } + + public 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; + } + /** + * + * @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()); + + double closestAngle = getClosest(angle, polars.get(closestSpeed).keySet()); + + Bearing vmgAngle = Bearing.fromDegrees(closestAngle); + double boatSpeed = Math.abs(closestSpeed * 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. diff --git a/racevisionGame/src/main/java/mock/model/RaceServer.java b/racevisionGame/src/main/java/mock/model/RaceServer.java index cbbe1971..a28f389d 100644 --- a/racevisionGame/src/main/java/mock/model/RaceServer.java +++ b/racevisionGame/src/main/java/mock/model/RaceServer.java @@ -151,7 +151,7 @@ public class RaceServer { List boatStatuses = new ArrayList<>(); //Add each boat status to the status list. - for (MockBoat boat : race.getBoats()) { + /*for (MockBoat boat : race.getBoats()) { BoatStatus boatStatus = new BoatStatus( boat.getSourceID(), @@ -160,7 +160,7 @@ public class RaceServer { boat.getEstimatedTimeAtNextMark().toInstant().toEpochMilli() ); boatStatuses.add(boatStatus); - } + }*/ diff --git a/racevisionGame/src/main/java/mock/model/VMG.java b/racevisionGame/src/main/java/mock/model/VMG.java index 905fadce..370df835 100644 --- a/racevisionGame/src/main/java/mock/model/VMG.java +++ b/racevisionGame/src/main/java/mock/model/VMG.java @@ -45,4 +45,8 @@ public class VMG { return bearing; } + public String toString(){ + return String.format("VMG Object: Speed %f, Bearing %f.", speed, bearing.degrees()); + } + } From 34cfe48df0d205f62c7d237ad1344c95596235ca Mon Sep 17 00:00:00 2001 From: Fan-Wu Yang Date: Fri, 1 Sep 2017 20:30:28 +1200 Subject: [PATCH 2/8] Got VMG quad detection working, however, the speeds are completley wrong. #story[1186] --- .../src/main/java/mock/model/MockRace.java | 3 +- .../src/main/java/mock/model/Polars.java | 47 ++++++++++++++++--- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 2c19ea10..276fc329 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -379,7 +379,8 @@ public class MockRace extends Race { // boat.getBearing(), // 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()) ; if (vmg.getSpeed() > 0) { boat.setCurrentSpeed(vmg.getSpeed()); } diff --git a/racevisionGame/src/main/java/mock/model/Polars.java b/racevisionGame/src/main/java/mock/model/Polars.java index a18aa627..35a22fc4 100644 --- a/racevisionGame/src/main/java/mock/model/Polars.java +++ b/racevisionGame/src/main/java/mock/model/Polars.java @@ -23,7 +23,7 @@ public class Polars { private HashMap> polarAngles = new HashMap<>(); //true wind speed, - private HashMap> polars = new HashMap<>(); + private Map> polars = new TreeMap<>(); @@ -87,7 +87,7 @@ public class Polars { polars.get(tws).put(twa, bs); } - public double getClosest(double value, Set set){ + private static double getClosest(double value, Set set){ double closestVal = 0; double smallestDiff = Double.MAX_VALUE; for (double d: set){ @@ -100,6 +100,39 @@ public class Polars { 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 @@ -111,13 +144,15 @@ public class Polars { //speed double closestSpeed = getClosest(trueWindSpeed, polars.keySet()); - //not using true vmg using general concept + //not using true vmg, using general concept double angle = Math.abs(trueWindAngle.degrees() - boatAngle.degrees()); - double closestAngle = getClosest(angle, polars.get(closestSpeed).keySet()); + 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; - Bearing vmgAngle = Bearing.fromDegrees(closestAngle); - double boatSpeed = Math.abs(closestSpeed * Math.cos(vmgAngle.radians())) * 4; return new VMG(boatSpeed, vmgAngle); } From 71dcc8ee6c0d608cd61378223b0edd1ffc443bbb Mon Sep 17 00:00:00 2001 From: Fan-Wu Yang Date: Mon, 4 Sep 2017 14:17:13 +1200 Subject: [PATCH 3/8] Added POlar Linear Interpolation #story[1186] --- .../src/main/java/mock/app/Event.java | 1 + .../main/java/mock/dataInput/PolarParser.java | 89 ++++++++++- .../src/main/java/mock/model/MockRace.java | 8 +- .../src/main/java/mock/model/NewPolars.java | 151 ++++++++++++++++++ .../src/main/java/mock/model/Polars.java | 81 ---------- 5 files changed, 246 insertions(+), 84 deletions(-) create mode 100644 racevisionGame/src/main/java/mock/model/NewPolars.java 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. *
From 7aaa880f40cac02136e848ca5985c983a3cff49e Mon Sep 17 00:00:00 2001 From: Fan-Wu Yang Date: Tue, 5 Sep 2017 13:05:37 +1200 Subject: [PATCH 4/8] VMG now works with some edge cases issues #story[1182] --- .../src/main/java/mock/model/NewPolars.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/racevisionGame/src/main/java/mock/model/NewPolars.java b/racevisionGame/src/main/java/mock/model/NewPolars.java index 1810ce52..3f070a3a 100644 --- a/racevisionGame/src/main/java/mock/model/NewPolars.java +++ b/racevisionGame/src/main/java/mock/model/NewPolars.java @@ -2,6 +2,7 @@ package mock.model; import shared.model.Bearing; +import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -28,7 +29,9 @@ public class NewPolars { if (!polars.containsKey(tws)){ polars.put(tws, new TreeMap<>()); } - polars.get(tws).put(twa, bs); + polars.get(tws).putIfAbsent(twa, bs); + polars.get(tws).putIfAbsent(360d - twa, bs); + System.out.println(String.format("tws %f, twa %f, bs %f", tws, twa, bs)); } public static void linearInterpolatePolars(){ @@ -38,8 +41,15 @@ public class NewPolars { // maxTWS = key; // } // } + /*for (double windSpeed: polars.keySet()){ + if (!polars.get(windSpeed).containsKey(180d)){ + polars.get(windSpeed).put(180d, windSpeed); + } + }*/ TreeMap prevTWS = null; - for (TreeMap tws: polars.values()){ + TreeMap> iterablePolars = new TreeMap<>(polars); + for (double windSpeed: iterablePolars.keySet()){ + TreeMap tws = iterablePolars.get(windSpeed); if (prevTWS == null){ prevTWS = tws; continue; @@ -58,9 +68,11 @@ public class NewPolars { for (double i = previousTWA; i < twa; i ++){ double mult = i - previousTWA; double newSpeed = diff * mult + prevSpeed; - System.out.println(newS); + //System.out.println(newSpeed); + System.out.println(String.format("tws %f, twa %f, bs %f", windSpeed, i, newSpeed)); tws.put(i, newSpeed); } + previousTWA = twa; } } } @@ -88,13 +100,12 @@ public class NewPolars { * @return */ private static int getQuadrant(double degrees){ - return ((int) degrees % 360) / 90 + 1; + return ((((int) degrees % 360) + 360) % 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 min = quad* 90; + double max = (quad + 1) * 90; double maxAngle = 0; double maxSpeed = 0; for (Double s: set.keySet()){ From 7331ad1a20212dff99903c01a8858d18f9477db9 Mon Sep 17 00:00:00 2001 From: Fan-Wu Yang Date: Tue, 5 Sep 2017 19:26:06 +1200 Subject: [PATCH 5/8] VMG now works however some little bug in the interpolation in some quadrant that is making the speed all the same in the quadrant #story[1182] --- racevisionGame/pom.xml | 24 +++- .../main/java/mock/dataInput/PolarParser.java | 1 - .../src/main/java/mock/model/MockRace.java | 8 +- .../src/main/java/mock/model/NewPolars.java | 25 ++-- .../test/java/mock/model/NewPolarsTest.java | 107 ++++++++++++++++++ 5 files changed, 148 insertions(+), 17 deletions(-) create mode 100644 racevisionGame/src/test/java/mock/model/NewPolarsTest.java diff --git a/racevisionGame/pom.xml b/racevisionGame/pom.xml index 41157414..b137baa8 100644 --- a/racevisionGame/pom.xml +++ b/racevisionGame/pom.xml @@ -25,10 +25,30 @@ org.mockito - mockito-all - 1.9.5 + mockito-core + 2.9.0 + + + net.bytebuddy + byte-buddy + 1.7.0 + runtime + + + net.bytebuddy + byte-buddy-agent + 1.7.0 + runtime + + + org.objenesis + objenesis + 2.6 + runtime + + diff --git a/racevisionGame/src/main/java/mock/dataInput/PolarParser.java b/racevisionGame/src/main/java/mock/dataInput/PolarParser.java index 0e87696b..9a94d1f0 100644 --- a/racevisionGame/src/main/java/mock/dataInput/PolarParser.java +++ b/racevisionGame/src/main/java/mock/dataInput/PolarParser.java @@ -108,7 +108,6 @@ public class PolarParser { /** * 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(); diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 77e7b04d..4e5858f3 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -209,7 +209,9 @@ public class MockRace extends Race { boat.setStatus(BoatStatusEnum.PRESTART); //We set a large time since tack change so that it calculates a new VMG when the simulation starts. - boat.setTimeSinceTackChange(Long.MAX_VALUE); + //boat.setTimeSinceTackChange(Long.MAX_VALUE); + boat.setTimeSinceTackChange(0); + } } @@ -353,8 +355,8 @@ public class MockRace extends Race { private void newOptimalVMG(MockBoat boat) { long tackPeriod = 1000; - if (boat.getTimeSinceTackChange() > tackPeriod) { + //System.out.println("optim called"); //Calculate the new VMG. // VMG newVMG = boat.getPolars().calculateVMG( // this.getWindDirection(), @@ -364,7 +366,7 @@ public class MockRace extends Race { // Bearing.fromDegrees(359.99999d)); 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 (improvesVelocity(boat, newVMG)) { boat.setVMG(newVMG); diff --git a/racevisionGame/src/main/java/mock/model/NewPolars.java b/racevisionGame/src/main/java/mock/model/NewPolars.java index 3f070a3a..71b041ed 100644 --- a/racevisionGame/src/main/java/mock/model/NewPolars.java +++ b/racevisionGame/src/main/java/mock/model/NewPolars.java @@ -31,7 +31,7 @@ public class NewPolars { } polars.get(tws).putIfAbsent(twa, bs); polars.get(tws).putIfAbsent(360d - twa, bs); - System.out.println(String.format("tws %f, twa %f, bs %f", tws, twa, bs)); + //System.out.println(String.format("tws %f, twa %f, bs %f", tws, twa, bs)); } public static void linearInterpolatePolars(){ @@ -69,7 +69,7 @@ public class NewPolars { double mult = i - previousTWA; double newSpeed = diff * mult + prevSpeed; //System.out.println(newSpeed); - System.out.println(String.format("tws %f, twa %f, bs %f", windSpeed, i, newSpeed)); + //System.out.println(String.format("tws %f, twa %f, bs %f", windSpeed, i, newSpeed)); tws.put(i, newSpeed); } previousTWA = twa; @@ -104,8 +104,10 @@ public class NewPolars { } private static double getBestSpeedInQuadrant(int quad, Map set){ - double min = quad* 90; - double max = (quad + 1) * 90; + double min = (quad - 1)* 90; + double max = quad * 90; + System.out.println(quad); + System.out.println(min + " " + max); double maxAngle = 0; double maxSpeed = 0; for (Double s: set.keySet()){ @@ -116,9 +118,6 @@ public class NewPolars { } } } - if (quad > 2){ - maxAngle += 180; - } return maxAngle; } @@ -130,13 +129,12 @@ public class NewPolars { * @return */ public static VMG setBestVMG(Bearing trueWindAngle, double trueWindSpeed, Bearing boatAngle){ + //System.out.println("VMG AUTO CALLED"); //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 angle = ((boatAngle.degrees() - trueWindAngle.degrees()) % 360 + 360) % 360; + int quad = getQuadrant(angle); double bestAngle = getBestSpeedInQuadrant(quad, polars.get(closestSpeed)); Bearing vmgAngle = Bearing.fromDegrees((bestAngle) % 360); @@ -159,4 +157,9 @@ public class NewPolars { } + private Map> getPolars(){ + //this function is just for testing so therefore it is private + return polars; + } + } diff --git a/racevisionGame/src/test/java/mock/model/NewPolarsTest.java b/racevisionGame/src/test/java/mock/model/NewPolarsTest.java new file mode 100644 index 00000000..4b5a0c80 --- /dev/null +++ b/racevisionGame/src/test/java/mock/model/NewPolarsTest.java @@ -0,0 +1,107 @@ +package mock.model; + +import mock.dataInput.PolarParser; +import org.junit.Before; +import org.junit.Test; +import shared.model.Bearing; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; + +/** + * Created by fwy13 on 5/09/17. + */ +public class NewPolarsTest { + + @Before + public void setUp(){ + PolarParser.parseNewPolars("mock/polars/acc_polars.csv"); + NewPolars.linearInterpolatePolars(); + } + + @Test + public void testQuads() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + //reflection for private class + Class[] parameterTypes = new Class[1]; + parameterTypes[0] = Double.TYPE; + Method getQuads = NewPolars.class.getDeclaredMethod("getQuadrant", parameterTypes); + getQuads.setAccessible(true); + + //start invoking + Object[] paras1 = new Object[1]; + paras1[0] = (new Double(0)).doubleValue(); + int q1 = (int) getQuads.invoke(NewPolars.newPolars, paras1); + assertEquals(q1, 1); + + //start invoking + Object[] paras2 = new Object[1]; + paras2[0] = (new Double(90)).doubleValue(); + int q2 = (int) getQuads.invoke(NewPolars.newPolars, paras2); + assertEquals(q2, 2); + + //start invoking + Object[] paras3 = new Object[1]; + paras3[0] = (new Double(180)).doubleValue(); + int q3 = (int) getQuads.invoke(NewPolars.newPolars, paras3); + assertEquals(q3, 3); + + //start invoking + Object[] paras4 = new Object[1]; + paras4[0] = (new Double(270)).doubleValue(); + int q4 = (int) getQuads.invoke(NewPolars.newPolars, paras4); + assertEquals(q4, 4); + + //start invoking + Object[] paras5 = new Object[1]; + paras5[0] = (new Double(360)).doubleValue(); + int q5 = (int) getQuads.invoke(NewPolars.newPolars, paras5); + assertEquals(q5, 1); + + } + + @Test + public void testEdgeSpeeds(){ + //just make sure that speeds at certain angles do not throw a null exception and are not negative + double maxTWS = 15; + + for (double tws = 0; tws < maxTWS; tws += 0.1){ + for (double j = 0; j < 360; j++){ + Bearing twa = Bearing.fromDegrees(j); + for (double i = 0; i < 360; i++){ + Bearing boatBearing = Bearing.fromDegrees(i); + double speed = NewPolars.calculateSpeed(twa, tws, boatBearing); + assertTrue(speed >= 0); + } + } + } + + } + + @Test + public void testClosestSpeeds() throws NoSuchMethodException, NoSuchFieldException, InvocationTargetException, IllegalAccessException { + //reflection for private class + Method getClosest = NewPolars.class.getDeclaredMethod("getClosest", double.class, Set.class); + getClosest.setAccessible(true); + + Method getPolars = NewPolars.class.getDeclaredMethod("getPolars"); + getPolars.setAccessible(true); + + double maxTWS = 15; + + //only catches for nulls + for (double tws = 0; tws < maxTWS; tws += 0.1){ + Map> polars = (Map>) getPolars.invoke(NewPolars.newPolars); + double speed = (double) getClosest.invoke(NewPolars.newPolars, tws, polars.keySet()); + assertTrue(speed >= 0); + } + } + +} From d602c1ec6fe0102c5842cb807363e698a1ae418d Mon Sep 17 00:00:00 2001 From: Fan-Wu Yang Date: Tue, 5 Sep 2017 21:03:19 +1200 Subject: [PATCH 6/8] VMG is now working and fixed, however, it feels a bit odd #story[1182] --- .../src/main/java/mock/model/MockRace.java | 6 +-- .../src/main/java/mock/model/NewPolars.java | 31 +++++++++++--- .../test/java/mock/model/NewPolarsTest.java | 40 +++++++++++++++++-- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 4e5858f3..8ffc2ba2 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -368,9 +368,9 @@ public class MockRace extends Race { 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)) { - boat.setVMG(newVMG); - } + /*if (improvesVelocity(boat, newVMG)) { + }*/ + boat.setVMG(newVMG); } } diff --git a/racevisionGame/src/main/java/mock/model/NewPolars.java b/racevisionGame/src/main/java/mock/model/NewPolars.java index 71b041ed..a1fafeeb 100644 --- a/racevisionGame/src/main/java/mock/model/NewPolars.java +++ b/racevisionGame/src/main/java/mock/model/NewPolars.java @@ -106,18 +106,25 @@ public class NewPolars { private static double getBestSpeedInQuadrant(int quad, Map set){ double min = (quad - 1)* 90; double max = quad * 90; - System.out.println(quad); - System.out.println(min + " " + max); + //System.out.println(quad); + //System.out.println(min + " " + max); double maxAngle = 0; double maxSpeed = 0; + double dupAngle = 0;//index where speed is duplicated for (Double s: set.keySet()){ if (s >= min && s < max){ if (set.get(s) > maxSpeed){ + dupAngle = 0; maxAngle = s; maxSpeed = set.get(s); + } else if (set.get(s) == maxSpeed){ + dupAngle = s; } } - } + }/* + if (dupAngle != 0 ){ + return getClosest((dupAngle + maxAngle) / 2, set.keySet()); + }*/ return maxAngle; } @@ -140,14 +147,16 @@ public class NewPolars { Bearing vmgAngle = Bearing.fromDegrees((bestAngle) % 360); double boatSpeed = polars.get(closestSpeed).get(bestAngle); - return new VMG(boatSpeed, Bearing.fromDegrees(vmgAngle.degrees() + trueWindAngle.degrees())); + double newAngle = (bestAngle + trueWindAngle.degrees() % 360 + 360) % 360; + + return new VMG(boatSpeed, Bearing.fromDegrees(newAngle)); } 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 angleDiff = ((boatAngle.degrees() - trueWindAngle.degrees()) % 360 + 360) % 360; double closestAngle = getClosest(angleDiff, polars.get(closestSpeed).keySet()); double boatSpeed = polars.get(closestSpeed).get(closestAngle); @@ -162,4 +171,16 @@ public class NewPolars { return polars; } + private void printOutLinearInterpolated(){ + for (double tws: polars.keySet()){ + System.out.println("=================================================="); + System.out.println("Speed: " + tws); + System.out.println("=================================================="); + for (double twa: polars.get(tws).keySet()){ + System.out.println("TWA: " + twa + ", Boat Speed: " + polars.get(tws).get(twa)); + //System.out.println("--------------------------------------------------"); + } + } + } + } diff --git a/racevisionGame/src/test/java/mock/model/NewPolarsTest.java b/racevisionGame/src/test/java/mock/model/NewPolarsTest.java index 4b5a0c80..24aa0b0f 100644 --- a/racevisionGame/src/test/java/mock/model/NewPolarsTest.java +++ b/racevisionGame/src/test/java/mock/model/NewPolarsTest.java @@ -25,6 +25,21 @@ public class NewPolarsTest { public void setUp(){ PolarParser.parseNewPolars("mock/polars/acc_polars.csv"); NewPolars.linearInterpolatePolars(); + + Method getPolars = null; + try { + getPolars = NewPolars.class.getDeclaredMethod("printOutLinearInterpolated"); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + getPolars.setAccessible(true); + try { + getPolars.invoke(NewPolars.newPolars); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } } @Test @@ -70,9 +85,9 @@ public class NewPolarsTest { @Test public void testEdgeSpeeds(){ //just make sure that speeds at certain angles do not throw a null exception and are not negative - double maxTWS = 15; + double maxTWS = 30; - for (double tws = 0; tws < maxTWS; tws += 0.1){ + for (double tws = 0; tws < maxTWS; tws += 1){ for (double j = 0; j < 360; j++){ Bearing twa = Bearing.fromDegrees(j); for (double i = 0; i < 360; i++){ @@ -94,14 +109,31 @@ public class NewPolarsTest { Method getPolars = NewPolars.class.getDeclaredMethod("getPolars"); getPolars.setAccessible(true); - double maxTWS = 15; + double maxTWS = 30; //only catches for nulls - for (double tws = 0; tws < maxTWS; tws += 0.1){ + for (double tws = 0; tws < maxTWS; tws += 1){ Map> polars = (Map>) getPolars.invoke(NewPolars.newPolars); double speed = (double) getClosest.invoke(NewPolars.newPolars, tws, polars.keySet()); assertTrue(speed >= 0); } } + @Test + public void testAutoVSCalculated(){ + //test that the auto chosen speed is the same speed that is calculated + double maxTWS = 30; + for (double tws = 0; tws < maxTWS; tws ++){ + for (double twa = 0; twa < 360; twa ++){ + Bearing TW = Bearing.fromDegrees(twa); + for (double ba = 0; ba < 360; ba ++){ + Bearing boatBearing = Bearing.fromDegrees(ba); + VMG autoVMG = NewPolars.setBestVMG(TW, tws, boatBearing); + double speed = NewPolars.calculateSpeed(TW, tws, autoVMG.getBearing()); + assertTrue(autoVMG.getSpeed() == speed); + } + } + } + } + } From 06517b7b47a3a267da92fcbe43c3f4ad1dc74d8c Mon Sep 17 00:00:00 2001 From: Fan-Wu Yang Date: Tue, 5 Sep 2017 21:37:33 +1200 Subject: [PATCH 7/8] Tidied up code - Removed prints from tests - Added Javadocs in necessary places - Reenabled boat status message to send - Added boat average angle - pulled out modulateAngle function - Commented harder to read loops - Disabled toggled VMG #story[1182] --- .../src/main/java/mock/model/MockRace.java | 1 + .../src/main/java/mock/model/NewPolars.java | 80 ++++++++++++------- .../src/main/java/mock/model/RaceServer.java | 4 +- .../Controllers/MainController.java | 1 + .../test/java/mock/model/NewPolarsTest.java | 30 +++---- 5 files changed, 69 insertions(+), 47 deletions(-) diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 8ffc2ba2..6a112e93 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -343,6 +343,7 @@ public class MockRace extends Race { if (boat.getAutoVMG()) { newOptimalVMG(boat); + boat.setAutoVMG(false); } } else { diff --git a/racevisionGame/src/main/java/mock/model/NewPolars.java b/racevisionGame/src/main/java/mock/model/NewPolars.java index a1fafeeb..c525316c 100644 --- a/racevisionGame/src/main/java/mock/model/NewPolars.java +++ b/racevisionGame/src/main/java/mock/model/NewPolars.java @@ -8,7 +8,8 @@ import java.util.Set; import java.util.TreeMap; /** - * Created by fwy13 on 4/09/17. + * New Polars are the revampe of the old Polars class which interpolates the data after being parsed from the Polar Parser + * There can only be one NewPolars instance stored statically however if a boat does happen to have a special case it can be assigned. */ public class NewPolars { @@ -22,6 +23,12 @@ public class NewPolars { newPolars = this; } + /** + * Add polars from the polar table + * @param trueWindSpeed True Wind Speed that the true wind angle and speed corresponds to + * @param trueWindAngle True Wind Angle of the race + * @param boatSpeed The speed the boat should be going at given the true wind angle + */ public static void addPolars(double trueWindSpeed, Bearing trueWindAngle, double boatSpeed){ double tws = trueWindSpeed; double bs = boatSpeed; @@ -31,31 +38,33 @@ public class NewPolars { } polars.get(tws).putIfAbsent(twa, bs); polars.get(tws).putIfAbsent(360d - twa, bs); - //System.out.println(String.format("tws %f, twa %f, bs %f", tws, twa, bs)); } + /** + * Linearly Interpolates this should only be called once per parsing of a polar table + */ public static void linearInterpolatePolars(){ -// double maxTWS = 0; -// for (double key: polars.keySet()){ -// if (maxTWS < key){ -// maxTWS = key; -// } -// } - /*for (double windSpeed: polars.keySet()){ - if (!polars.get(windSpeed).containsKey(180d)){ - polars.get(windSpeed).put(180d, windSpeed); - } - }*/ TreeMap prevTWS = null; TreeMap> iterablePolars = new TreeMap<>(polars); + //this loop averages out the speed between tow angles + //Example: Pair one: 0 degrees, 0 knots + // Pair two: 3 degrees, 6 knots + //This loop will add + //Pair one: 0 degrees, 0 knots + //Pair two: 1 degrees, 2 knots + //Pair three: 2 degrees, 4 knots + //Pair four: 3 degrees, 6 knots for (double windSpeed: iterablePolars.keySet()){ TreeMap tws = iterablePolars.get(windSpeed); + if (prevTWS == null){ prevTWS = tws; continue; } + double previousTWA = -1; TreeMap iterableTWS = new TreeMap<>(tws); + for (double twa: iterableTWS.keySet()){ if (previousTWA == -1){ previousTWA = twa; @@ -65,11 +74,10 @@ public class NewPolars { 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(newSpeed); - //System.out.println(String.format("tws %f, twa %f, bs %f", windSpeed, i, newSpeed)); tws.put(i, newSpeed); } previousTWA = twa; @@ -100,17 +108,18 @@ public class NewPolars { * @return */ private static int getQuadrant(double degrees){ - return ((((int) degrees % 360) + 360) % 360) / 90 + 1; + return (int) modulateAngle(degrees) / 90 + 1; } private static double getBestSpeedInQuadrant(int quad, Map set){ double min = (quad - 1)* 90; double max = quad * 90; - //System.out.println(quad); - //System.out.println(min + " " + max); double maxAngle = 0; double maxSpeed = 0; - double dupAngle = 0;//index where speed is duplicated + double dupAngle = 0; + //DupAngle will average the angle between maxAngles that have the same speed + //Example: if 150 degrees, 180 degrees, and 210 degrees all go at 10 knots + //then the average will be taken as (150 + 210) / 2 and the angle will be returned on that. for (Double s: set.keySet()){ if (s >= min && s < max){ if (set.get(s) > maxSpeed){ @@ -121,42 +130,48 @@ public class NewPolars { dupAngle = s; } } - }/* + } if (dupAngle != 0 ){ return getClosest((dupAngle + maxAngle) / 2, set.keySet()); - }*/ + } return maxAngle; } /** - * - * @param trueWindAngle - * @param trueWindSpeed - * @param boatAngle - * @return + * Returns the best VMG that the boat can change to given it's current diagonal heading direction. + * @param trueWindAngle True wind angle of the race + * @param trueWindSpeed True wind speed of the race + * @param boatAngle Angle that the boat is currently at + * @return the best vmg that the boat can change to */ public static VMG setBestVMG(Bearing trueWindAngle, double trueWindSpeed, Bearing boatAngle){ //System.out.println("VMG AUTO CALLED"); //speed double closestSpeed = getClosest(trueWindSpeed, polars.keySet()); - double angle = ((boatAngle.degrees() - trueWindAngle.degrees()) % 360 + 360) % 360; + double angle = modulateAngle(boatAngle.degrees() - trueWindAngle.degrees()); int quad = getQuadrant(angle); double bestAngle = getBestSpeedInQuadrant(quad, polars.get(closestSpeed)); - Bearing vmgAngle = Bearing.fromDegrees((bestAngle) % 360); double boatSpeed = polars.get(closestSpeed).get(bestAngle); - double newAngle = (bestAngle + trueWindAngle.degrees() % 360 + 360) % 360; + double newAngle = modulateAngle(bestAngle + trueWindAngle.degrees()); return new VMG(boatSpeed, Bearing.fromDegrees(newAngle)); } + /** + * Calculates the speed that a certain angle should be doing + * @param trueWindAngle TrueWind Angle of the race + * @param trueWindSpeed True Wind Speed of the race + * @param boatAngle Angle that the boat is current at + * @return the speed that the boat should be traveling at. + */ public static double calculateSpeed(Bearing trueWindAngle, double trueWindSpeed, Bearing boatAngle){ //speed double closestSpeed = getClosest(trueWindSpeed, polars.keySet()); - double angleDiff = ((boatAngle.degrees() - trueWindAngle.degrees()) % 360 + 360) % 360; + double angleDiff = modulateAngle(boatAngle.degrees() - trueWindAngle.degrees()); double closestAngle = getClosest(angleDiff, polars.get(closestSpeed).keySet()); double boatSpeed = polars.get(closestSpeed).get(closestAngle); @@ -166,6 +181,10 @@ public class NewPolars { } + public static double modulateAngle(double angle){ + return (angle % 360 + 360) % 360; + } + private Map> getPolars(){ //this function is just for testing so therefore it is private return polars; @@ -178,7 +197,6 @@ public class NewPolars { System.out.println("=================================================="); for (double twa: polars.get(tws).keySet()){ System.out.println("TWA: " + twa + ", Boat Speed: " + polars.get(tws).get(twa)); - //System.out.println("--------------------------------------------------"); } } } diff --git a/racevisionGame/src/main/java/mock/model/RaceServer.java b/racevisionGame/src/main/java/mock/model/RaceServer.java index a28f389d..cbbe1971 100644 --- a/racevisionGame/src/main/java/mock/model/RaceServer.java +++ b/racevisionGame/src/main/java/mock/model/RaceServer.java @@ -151,7 +151,7 @@ public class RaceServer { List boatStatuses = new ArrayList<>(); //Add each boat status to the status list. - /*for (MockBoat boat : race.getBoats()) { + for (MockBoat boat : race.getBoats()) { BoatStatus boatStatus = new BoatStatus( boat.getSourceID(), @@ -160,7 +160,7 @@ public class RaceServer { boat.getEstimatedTimeAtNextMark().toInstant().toEpochMilli() ); boatStatuses.add(boatStatus); - }*/ + } diff --git a/racevisionGame/src/main/java/visualiser/Controllers/MainController.java b/racevisionGame/src/main/java/visualiser/Controllers/MainController.java index 57d18830..9e557d38 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/MainController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/MainController.java @@ -40,6 +40,7 @@ public class MainController extends Controller { * Transitions from the StartController screen (displays pre-race information) to the RaceController (displays the actual race). * @param visualiserRace The object modelling the race. * @param controllerClient Socket Client that manipulates the controller. + * @param isHost whether this window is the host of the race or not. */ public void beginRace(VisualiserRaceEvent visualiserRace, ControllerClient controllerClient, Boolean isHost) { raceController.startRace(visualiserRace, controllerClient, isHost); diff --git a/racevisionGame/src/test/java/mock/model/NewPolarsTest.java b/racevisionGame/src/test/java/mock/model/NewPolarsTest.java index 24aa0b0f..dcbcc6cc 100644 --- a/racevisionGame/src/test/java/mock/model/NewPolarsTest.java +++ b/racevisionGame/src/test/java/mock/model/NewPolarsTest.java @@ -26,20 +26,22 @@ public class NewPolarsTest { PolarParser.parseNewPolars("mock/polars/acc_polars.csv"); NewPolars.linearInterpolatePolars(); - Method getPolars = null; - try { - getPolars = NewPolars.class.getDeclaredMethod("printOutLinearInterpolated"); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - getPolars.setAccessible(true); - try { - getPolars.invoke(NewPolars.newPolars); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } + +// Uncomment if you want to read the linear interpolation in text +// Method getPolars = null; +// try { +// getPolars = NewPolars.class.getDeclaredMethod("printOutLinearInterpolated"); +// } catch (NoSuchMethodException e) { +// e.printStackTrace(); +// } +// getPolars.setAccessible(true); +// try { +// getPolars.invoke(NewPolars.newPolars); +// } catch (IllegalAccessException e) { +// e.printStackTrace(); +// } catch (InvocationTargetException e) { +// e.printStackTrace(); +// } } @Test From dfd3cb96f38c72b5a74a974e6f906f1e2702442c Mon Sep 17 00:00:00 2001 From: hba56 Date: Wed, 6 Sep 2017 20:05:54 +1200 Subject: [PATCH 8/8] removed System.out.println --- .../src/main/java/mock/model/wind/ShiftingWindGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/racevisionGame/src/main/java/mock/model/wind/ShiftingWindGenerator.java b/racevisionGame/src/main/java/mock/model/wind/ShiftingWindGenerator.java index 5d5e4995..4ceff627 100644 --- a/racevisionGame/src/main/java/mock/model/wind/ShiftingWindGenerator.java +++ b/racevisionGame/src/main/java/mock/model/wind/ShiftingWindGenerator.java @@ -105,7 +105,7 @@ public class ShiftingWindGenerator implements WindGenerator { if (shiftedSoFar >= 180){ shiftAnticlockwise = Math.random() > 0.5; shiftedSoFar = 0; - System.out.println("Swapping"); +// System.out.println("Swapping"); } timeOfLastShift = System.currentTimeMillis();