diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PathGenerator.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PathGenerator.java new file mode 100644 index 00000000000..3c6a092070f --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PathGenerator.java @@ -0,0 +1,13 @@ +package com.sap.sailing.simulator; + +import java.util.List; + +public interface PathGenerator { + + void setSimulationParameters(SimulationParameters params); + SimulationParameters getSimulationParameters(); + + Path getPath(); + List getPathEvenTimed(long millisecondsStep); + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagram.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagram.java index d30fa30f6c3..29ad3403593 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagram.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagram.java @@ -1,6 +1,5 @@ package com.sap.sailing.simulator; -import java.util.Map; import java.util.NavigableMap; import com.sap.sailing.domain.base.SpeedWithBearing; diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SailingSimulator.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SailingSimulator.java index 3253c29ad49..18f9c866bf2 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SailingSimulator.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SailingSimulator.java @@ -8,7 +8,6 @@ public interface SailingSimulator { void setSimulationParameters(SimulationParameters params); SimulationParameters getSimulationParameters(); - Path getOptimumPath(); Map getAllPaths(); Map> getAllPathsEvenTimed(long millisecondsStep); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1Turner.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1Turner.java new file mode 100644 index 00000000000..cb1f67e280a --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1Turner.java @@ -0,0 +1,214 @@ +package com.sap.sailing.simulator.impl; + +import java.util.LinkedList; +import java.util.List; + +import com.sap.sailing.domain.base.SpeedWithBearing; +import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.simulator.Path; +import com.sap.sailing.simulator.PathGenerator; +import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.SimulationParameters; +import com.sap.sailing.simulator.TimedPositionWithSpeed; +import com.sap.sailing.simulator.WindFieldGenerator; + +public class PathGenerator1Turner implements PathGenerator { + + class result1Turn { + public result1Turn(Path[] p, char s, int n) { + paths = p; + side = s; + middle = n; + } + + Path[] paths; + char side; + int middle; + } + + // private static Logger logger = Logger.getLogger("com.sap.sailing"); + private SimulationParameters simulationParameters; + private boolean leftSide; + private result1Turn result; + + public PathGenerator1Turner(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public void setSimulationParameters(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public SimulationParameters getSimulationParameters() { + return simulationParameters; + } + + public void setEvaluationParameters(boolean leftSideVal) { + this.leftSide = leftSideVal; + } + + public int getMiddle() { + return this.result.middle; + } + + @Override + public Path getPath() { + + WindFieldGenerator windField = simulationParameters.getWindField(); + PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); + Position start = simulationParameters.getCourse().get(0); + Position end = simulationParameters.getCourse().get(1); + TimePoint startTime = windField.getStartTime();// new MillisecondsTimePoint(0); + + long turnloss = polarDiagram.getTurnLoss(); // 4000; + + Distance courseLength = start.getDistance(end); + Bearing bearStart2End = start.getBearingGreatCircle(end); + Position currentPosition = start; + TimePoint currentTime = startTime; + TimePoint nextTime; + + double reachingTolerance = 0.03; + int stepMax = 800; + double[] reachTime = new double[stepMax]; + boolean targetFound; + long timeStep = windField.getTimeStep().asMillis() / 5; + Bearing direction; + + double newDistance; + double minimumDistance = courseLength.getMeters(); + double overallMinimumDistance = courseLength.getMeters(); + int stepOfOverallMinimumDistance = stepMax; + LinkedList path = null; + // LinkedList prevpath = null; + // LinkedList xpath1 = null; + // LinkedList xpath2 = null; + + LinkedList allminpath = null; + + for (int step = 0; step < stepMax; step++) { + + currentPosition = start; + currentTime = startTime; + reachTime[step] = courseLength.getMeters(); + targetFound = false; + minimumDistance = courseLength.getMeters(); + path = new LinkedList(); + path.addLast(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); + + int stepLeft = 0; + while ((stepLeft < step) && (!targetFound)) { + + SpeedWithBearing currentWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); + polarDiagram.setWind(currentWind); + if (leftSide) { + direction = polarDiagram.optimalDirectionsUpwind()[0]; + } else { + direction = polarDiagram.optimalDirectionsUpwind()[1]; + } + SpeedWithBearing currSpeed = polarDiagram.getSpeedAtBearing(direction); + nextTime = new MillisecondsTimePoint(currentTime.asMillis() + timeStep); + Position nextPosition = currSpeed.travelTo(currentPosition, currentTime, nextTime); + newDistance = nextPosition.getDistance(end).getMeters(); + if (newDistance < minimumDistance) { + minimumDistance = newDistance; + } + currentPosition = nextPosition; + currentTime = nextTime; + path.addLast(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); + + if (currentPosition.getDistance(end).getMeters() < reachingTolerance * courseLength.getMeters()) { + reachTime[step] = minimumDistance; + targetFound = true; + if (minimumDistance < overallMinimumDistance) { + overallMinimumDistance = minimumDistance; + stepOfOverallMinimumDistance = step; + allminpath = path; + } + } + stepLeft++; + } + + currentTime = new MillisecondsTimePoint(currentTime.asMillis() + turnloss); + + int stepRight = 0; + while ((stepRight < (stepMax - step)) && (!targetFound)) { + + SpeedWithBearing currentWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); + polarDiagram.setWind(currentWind); + if (leftSide) { + direction = polarDiagram.optimalDirectionsUpwind()[1]; + } else { + direction = polarDiagram.optimalDirectionsUpwind()[0]; + } + SpeedWithBearing currSpeed = polarDiagram.getSpeedAtBearing(direction); + nextTime = new MillisecondsTimePoint(currentTime.asMillis() + timeStep); + Position nextPosition = currSpeed.travelTo(currentPosition, currentTime, nextTime); + newDistance = nextPosition.getDistance(end).getMeters(); + if (newDistance < minimumDistance) { + minimumDistance = newDistance; + } + currentPosition = nextPosition; + currentTime = nextTime; + path.addLast(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); // currSpeed)); + + if (currentPosition.getDistance(end).getMeters() < reachingTolerance * courseLength.getMeters()) { + // System.out.println(""+s+":"+path.size()+" dist:"+mindist); + Bearing bearPath2End = currentPosition.getBearingGreatCircle(end); + double bearDiff = bearPath2End.getDegrees() - bearStart2End.getDegrees(); + // System.out.println(""+s+": "+mindist+" bearDiff: "+bearDiff); + reachTime[step] = minimumDistance * Math.signum(bearDiff); + /*if ((prevpath != null) && (Math.signum(reachTime[step]) != Math.signum(reachTime[step - 1]))) { + xpath1 = path; + xpath2 = prevpath; + } + prevpath = path;*/ + if (start.getDistance(currentPosition).getMeters() > start.getDistance(end).getMeters()) { + targetFound = true; + } + if (minimumDistance < overallMinimumDistance) { + overallMinimumDistance = minimumDistance; + stepOfOverallMinimumDistance = step; + allminpath = new LinkedList(path); + } + } + stepRight++; + } + } + + /* + * for (int i=0; i getPathEvenTimed(long millisecondsStep) { + + Path path = this.getPath(); + return path.getEvenTimedPoints(millisecondsStep); + + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerLeftDirect.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerLeftDirect.java new file mode 100644 index 00000000000..27b867c7b78 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerLeftDirect.java @@ -0,0 +1,161 @@ +package com.sap.sailing.simulator.impl; + +import java.util.LinkedList; +import java.util.List; + +import com.sap.sailing.domain.base.SpeedWithBearing; +import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Path; +import com.sap.sailing.simulator.PathGenerator; +import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.SimulationParameters; +import com.sap.sailing.simulator.TimedPositionWithSpeed; +import com.sap.sailing.simulator.WindFieldGenerator; + +public class PathGenerator1TurnerLeftDirect implements PathGenerator { + + // private static Logger logger = Logger.getLogger("com.sap.sailing"); + SimulationParameters simulationParameters; + + public PathGenerator1TurnerLeftDirect(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public void setSimulationParameters(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public SimulationParameters getSimulationParameters() { + return simulationParameters; + } + + @Override + public Path getPath() { + + // retrieve simulation parameters + Boundary boundary = new RectangularBoundary(simulationParameters.getCourse().get(0), simulationParameters + .getCourse().get(1));// simulationParameters.getBoundaries(); + WindFieldGenerator windField = simulationParameters.getWindField(); + PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); + Position start = simulationParameters.getCourse().get(0); + Position end = simulationParameters.getCourse().get(1); + TimePoint startTime = windField.getStartTime();// new MillisecondsTimePoint(0); + + Distance courseLength = start.getDistance(end); + + // the solution path + LinkedList lst = null; + // the minimal one-turn time + + Long timeResolution = 120000L; + boolean turned = true; + // boolean outOfBounds = false; + Long minTurn = Long.MAX_VALUE; + int turningStep = 0; + + while (turned) { + LinkedList tempLst = new LinkedList(); + Position currentPosition = start; + TimePoint currentTime = startTime; + turned = false; + // outOfBounds = false; + turningStep++; + int currentStep = 0; + + while (true) { + + SpeedWithBearing currWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); + polarDiagram.setWind(currWind); + TimePoint nextTime = new MillisecondsTimePoint(currentTime.asMillis() + timeResolution); + tempLst.add(new TimedPositionWithSpeedImpl(currentTime, currentPosition, currWind)); + + if (currentStep >= turningStep) { + turned = true; + nextTime = new MillisecondsTimePoint(nextTime.asMillis() + polarDiagram.getTurnLoss()); + } + + if (!turned) { + Bearing direction = polarDiagram.optimalDirectionsUpwind()[0]; + // for(Bearing b: polarDiagram.optimalDirectionsUpwind()) + // if(polarDiagram.getWindSide(b) == PolarDiagram.WindSide.LEFT) + // direction = b; + SpeedWithBearing currSpeed = polarDiagram.getSpeedAtBearing(direction); + currentPosition = currSpeed.travelTo(currentPosition, currentTime, nextTime); + } + if (turned) { + Bearing direction1 = currentPosition.getBearingGreatCircle(end); + Bearing direction2 = polarDiagram.optimalDirectionsUpwind()[1]; + // for(Bearing b: polarDiagram.optimalDirectionsUpwind()) + // if(polarDiagram.getWindSide(b) == PolarDiagram.WindSide.RIGHT) + // direction2 = b; + SpeedWithBearing currSpeed1 = polarDiagram.getSpeedAtBearing(direction1); + SpeedWithBearing currSpeed2 = polarDiagram.getSpeedAtBearing(direction2); + Position nextPosition1 = currSpeed1.travelTo(currentPosition, currentTime, nextTime); + Position nextPosition2 = currSpeed2.travelTo(currentPosition, currentTime, nextTime); + // nextPosition2. + if (nextPosition1.getDistance(end).compareTo(nextPosition2.getDistance(end)) < 0 + && Math.abs(direction1.getDifferenceTo(direction2).getDegrees()) < 45.0) + currentPosition = nextPosition1; + else + currentPosition = nextPosition2; + } + + currentStep++; + // System.out.println(currentStep + "/" + turningStep + "/" + turned); + currentTime = nextTime; + + if (currentTime.asMillis() > minTurn) { + // System.out.println("out of time"); + break; + } + if (!boundary.isWithinBoundaries(currentPosition)) { + // outOfBounds = true; + // System.out.println("out of bounds"); + break; + } + if (currentPosition.getDistance(end).compareTo(courseLength.scale(0.005)) < 0) { + minTurn = currentTime.asMillis(); + lst = new LinkedList(tempLst); + Bearing directionToEnd = currentPosition.getBearingGreatCircle(end); + SpeedWithBearing crtWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); + polarDiagram.setWind(crtWind); + Speed speedToEnd = polarDiagram.getSpeedAtBearing(directionToEnd); + Distance distanceToEnd = currentPosition.getDistance(end); + Long timeToEnd = (long) (1000.0 * distanceToEnd.getMeters() / speedToEnd.getMetersPerSecond()); + TimePoint endTime = new MillisecondsTimePoint(currentTime.asMillis() + timeToEnd); + lst.addLast(new TimedPositionWithSpeedImpl(endTime, end, crtWind)); + // System.out.println("end reached!!!"); + break; + } + + } + + } + + if (lst != null) { + // lst.addLast(new TimedPositionWithSpeedImpl(new + // MillisecondsTimePoint(lst.getLast().getTimePoint().asMillis() + timeResolution), end, + // lst.getLast().getSpeed())); + return new PathImpl(lst, windField); + } else + return null; + + } + + @Override + public List getPathEvenTimed(long millisecondsStep) { + + Path path = this.getPath(); + return path.getEvenTimedPoints(millisecondsStep); + + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerRightDirect.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerRightDirect.java new file mode 100644 index 00000000000..b5253aa3538 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerRightDirect.java @@ -0,0 +1,161 @@ +package com.sap.sailing.simulator.impl; + +import java.util.LinkedList; +import java.util.List; + +import com.sap.sailing.domain.base.SpeedWithBearing; +import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Path; +import com.sap.sailing.simulator.PathGenerator; +import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.SimulationParameters; +import com.sap.sailing.simulator.TimedPositionWithSpeed; +import com.sap.sailing.simulator.WindFieldGenerator; + +public class PathGenerator1TurnerRightDirect implements PathGenerator { + + // private static Logger logger = Logger.getLogger("com.sap.sailing"); + SimulationParameters simulationParameters; + + public PathGenerator1TurnerRightDirect(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public void setSimulationParameters(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public SimulationParameters getSimulationParameters() { + return simulationParameters; + } + + @Override + public Path getPath() { + + // retrieve simulation parameters + Boundary boundary = new RectangularBoundary(simulationParameters.getCourse().get(0), simulationParameters + .getCourse().get(1));// simulationParameters.getBoundaries(); + WindFieldGenerator windField = simulationParameters.getWindField(); + PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); + Position start = simulationParameters.getCourse().get(0); + Position end = simulationParameters.getCourse().get(1); + TimePoint startTime = windField.getStartTime();// new MillisecondsTimePoint(0); + + Distance courseLength = start.getDistance(end); + + // the solution path + LinkedList lst = null; + // the minimal one-turn time + + Long timeResolution = 120000L; + boolean turned = true; + // boolean outOfBounds = false; + Long minTurn = Long.MAX_VALUE; + int turningStep = 0; + + while (turned) { + LinkedList tempLst = new LinkedList(); + Position currentPosition = start; + TimePoint currentTime = startTime; + turned = false; + // outOfBounds = false; + turningStep++; + int currentStep = 0; + + while (true) { + + SpeedWithBearing currWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); + polarDiagram.setWind(currWind); + TimePoint nextTime = new MillisecondsTimePoint(currentTime.asMillis() + timeResolution); + tempLst.add(new TimedPositionWithSpeedImpl(currentTime, currentPosition, currWind)); + + if (currentStep >= turningStep) { + turned = true; + nextTime = new MillisecondsTimePoint(nextTime.asMillis() + polarDiagram.getTurnLoss()); + } + + if (!turned) { + Bearing direction = polarDiagram.optimalDirectionsUpwind()[1]; + // for(Bearing b: polarDiagram.optimalDirectionsUpwind()) + // if(polarDiagram.getWindSide(b) == PolarDiagram.WindSide.LEFT) + // direction = b; + SpeedWithBearing currSpeed = polarDiagram.getSpeedAtBearing(direction); + currentPosition = currSpeed.travelTo(currentPosition, currentTime, nextTime); + } + if (turned) { + Bearing direction1 = currentPosition.getBearingGreatCircle(end); + Bearing direction2 = polarDiagram.optimalDirectionsUpwind()[0]; + // for(Bearing b: polarDiagram.optimalDirectionsUpwind()) + // if(polarDiagram.getWindSide(b) == PolarDiagram.WindSide.RIGHT) + // direction2 = b; + SpeedWithBearing currSpeed1 = polarDiagram.getSpeedAtBearing(direction1); + SpeedWithBearing currSpeed2 = polarDiagram.getSpeedAtBearing(direction2); + Position nextPosition1 = currSpeed1.travelTo(currentPosition, currentTime, nextTime); + Position nextPosition2 = currSpeed2.travelTo(currentPosition, currentTime, nextTime); + // nextPosition2. + if (nextPosition1.getDistance(end).compareTo(nextPosition2.getDistance(end)) < 0 + && Math.abs(direction1.getDifferenceTo(direction2).getDegrees()) < 45.0) + currentPosition = nextPosition1; + else + currentPosition = nextPosition2; + } + + currentStep++; + // System.out.println(currentStep + "/" + turningStep + "/" + turned); + currentTime = nextTime; + + if (currentTime.asMillis() > minTurn) { + // System.out.println("out of time"); + break; + } + if (!boundary.isWithinBoundaries(currentPosition)) { + // outOfBounds = true; + // System.out.println("out of bounds"); + break; + } + if (currentPosition.getDistance(end).compareTo(courseLength.scale(0.005)) < 0) { + minTurn = currentTime.asMillis(); + lst = new LinkedList(tempLst); + Bearing directionToEnd = currentPosition.getBearingGreatCircle(end); + SpeedWithBearing crtWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); + polarDiagram.setWind(crtWind); + Speed speedToEnd = polarDiagram.getSpeedAtBearing(directionToEnd); + Distance distanceToEnd = currentPosition.getDistance(end); + Long timeToEnd = (long) (1000.0 * distanceToEnd.getMeters() / speedToEnd.getMetersPerSecond()); + TimePoint endTime = new MillisecondsTimePoint(currentTime.asMillis() + timeToEnd); + lst.addLast(new TimedPositionWithSpeedImpl(endTime, end, crtWind)); + // System.out.println("end reached!!!"); + break; + } + + } + + } + + if (lst != null) { + // lst.addLast(new TimedPositionWithSpeedImpl(new + // MillisecondsTimePoint(lst.getLast().getTimePoint().asMillis() + timeResolution), end, + // lst.getLast().getSpeed())); + return new PathImpl(lst, windField); + } else + return null; + + } + + @Override + public List getPathEvenTimed(long millisecondsStep) { + + Path path = this.getPath(); + return path.getEvenTimedPoints(millisecondsStep); + + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDijkstra.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDijkstra.java new file mode 100644 index 00000000000..c0b83f0e2bc --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDijkstra.java @@ -0,0 +1,190 @@ +package com.sap.sailing.simulator.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.sap.sailing.domain.base.SpeedWithBearing; +import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.Util.Pair; +import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Path; +import com.sap.sailing.simulator.PathGenerator; +import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.SimulationParameters; +import com.sap.sailing.simulator.TimedPosition; +import com.sap.sailing.simulator.TimedPositionWithSpeed; +import com.sap.sailing.simulator.WindFieldGenerator; + +public class PathGeneratorDijkstra implements PathGenerator { + + // private static Logger logger = Logger.getLogger("com.sap.sailing"); + SimulationParameters simulationParameters; + + public PathGeneratorDijkstra(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public void setSimulationParameters(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public SimulationParameters getSimulationParameters() { + return simulationParameters; + } + + @Override + public Path getPath() { + + // retrieve simulation parameters + Boundary boundary = new RectangularBoundary(simulationParameters.getCourse().get(0), simulationParameters + .getCourse().get(1));// simulationParameters.getBoundaries(); + WindFieldGenerator windField = simulationParameters.getWindField(); + PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); + Position start = simulationParameters.getCourse().get(0); + Position end = simulationParameters.getCourse().get(1); + TimePoint startTime = windField.getStartTime();// new MillisecondsTimePoint(0); + + // the solution path + LinkedList lst = new LinkedList(); + + // initiate grid + int gridv = simulationParameters.getProperty("Djikstra.gridv[int]").intValue(); // number of vertical grid steps + int gridh = simulationParameters.getProperty("Djikstra.gridh[int]").intValue(); // number of horizontal grid + // steps + Position[][] sailGrid = boundary.extractGrid(gridh, gridv); + + // create adjacency graph including start and end + Map> graph = new HashMap>(); + graph.put(start, Arrays.asList(sailGrid[1])); + for (int i = 1; i < gridv - 2; i++) { + for (Position p : sailGrid[i]) { + graph.put(p, Arrays.asList(sailGrid[i + 1])); + } + } + for (Position p : sailGrid[gridv - 2]) { + graph.put(p, Arrays.asList(end)); + } + + /* + * //create backwards adjacency graph, required to reconstruct the optimal path Map> + * backGraph = new HashMap>(); backGraph.put(end, Arrays.asList(sailGrid[gridv-2])); + * for(int i = gridv-2; i > 1; i--) { for(Position p: sailGrid[i]) { backGraph.put(p, + * Arrays.asList(sailGrid[i-1])); } } for(Position p : sailGrid[1]) { backGraph.put(p, Arrays.asList(start)); } + */ + + // create tentative distance matrix + // additional to tentative distances, the matrix also contains the root of each position + // that can be if unavailable + Map> tentativeDistances = new HashMap>(); + for (Position p : graph.keySet()) { + tentativeDistances.put(p, new Pair(Long.MAX_VALUE, null)); + } + tentativeDistances.put(start, new Pair(startTime.asMillis(), null)); + tentativeDistances.put(end, new Pair(Long.MAX_VALUE, null)); + + // create set of unvisited nodes + List unvisited = new ArrayList(graph.keySet()); + unvisited.add(end); + + // set the initial node as current + Position currentPosition = start; + TimePoint currentTime = startTime; + // Bearing previousBearing = null; + + // search loop + // ends when the end is visited + while (currentPosition != end) { + // set the polar diagram to the wind at the current position and time + TimedPosition currentTimedPosition = new TimedPositionImpl(currentTime, currentPosition); + SpeedWithBearing currentWind = windField.getWind(currentTimedPosition); + polarDiagram.setWind(currentWind); + + // compute the tentative distance to all the unvisited neighbours of the current node + // and replace it in the matrix if is smaller than the previous one + List unvisitedNeighbours = new LinkedList(graph.get(currentPosition)); + unvisitedNeighbours.retainAll(unvisited); + for (Position p : unvisitedNeighbours) { + Bearing bearingToP = currentPosition.getBearingGreatCircle(p); + Distance distanceToP = currentPosition.getDistance(p); + Speed speedToP = polarDiagram.getSpeedAtBearing(bearingToP); + // multiplied by 1000 to have milliseconds + Long timeToP = (long) (1000 * (distanceToP.getMeters() / speedToP.getMetersPerSecond())); + /* + * if (previousBearing != null) { Bearing windBearingFrom = currentWind.getBearing().reverse(); if( + * (PolarDiagram49.bearingComparator.compare(bearingToP, windBearingFrom) > 0) && + * (PolarDiagram49.bearingComparator.compare(previousBearing, windBearingFrom) < 0) ) timeToP = timeToP + * + 4000; if( (PolarDiagram49.bearingComparator.compare(bearingToP, windBearingFrom) < 0) && + * (PolarDiagram49.bearingComparator.compare(previousBearing, windBearingFrom) > 0) ) timeToP = timeToP + * + 4000; } + */ + + Long tentativeDistanceToP = currentTime.asMillis() + timeToP; + if (tentativeDistanceToP < tentativeDistances.get(p).getA()) { + tentativeDistances.put(p, new Pair(tentativeDistanceToP, currentPosition)); + } + } + + // mark current node as visited + unvisited.remove(currentPosition); + + // select the unvisited node with the smallest tentative distance + // and set it as current + Long minTentativeDistance = Long.MAX_VALUE; + for (Position p : unvisited) { + if (tentativeDistances.get(p).getA() < minTentativeDistance) { + currentPosition = p; + minTentativeDistance = tentativeDistances.get(p).getA(); + // previousBearing = tentativeDistances.get(p).getB().getBearingGreatCircle(currentPosition); + currentTime = new MillisecondsTimePoint(minTentativeDistance); + } + + } + } + // I need to add the end point to the distances matrix + // tentativeDistances.put(end,currentTime.asMillis()); + + // at this point currentPosition = end + // currentTime = total duration of the course + + // reconstruct the optimal path by going from start to end + /* + * while(currentPosition != start) { TimedPositionWithSpeed currentTimedPositionWithSpeed = new + * TimedPositionWithSpeedImpl(currentTime, currentPosition, null ); lst.addFirst(currentTimedPositionWithSpeed); + * System.out.println(boundary.getGridIndex(currentTimedPositionWithSpeed.getPosition())); List + * currentPredecessors = backGraph.get(currentPosition); Long minTime = Long.MAX_VALUE; for(Position p : + * currentPredecessors) { if(tentativeDistances.get(p) < minTime) { minTime = tentativeDistances.get(p); + * currentPosition = p; currentTime = new MillisecondsTimePoint(minTime); } } } //I need to add the first point + * to the path lst.addFirst(new TimedPositionWithSpeedImpl(startTime, start, null)); + */ + while (currentPosition != null) { + currentTime = new MillisecondsTimePoint(tentativeDistances.get(currentPosition).getA()); + SpeedWithBearing windAtPoint = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); + TimedPositionWithSpeed current = new TimedPositionWithSpeedImpl(currentTime, currentPosition, windAtPoint); + lst.addFirst(current); + currentPosition = tentativeDistances.get(currentPosition).getB(); + } + + return new PathImpl(lst, windField); + + } + + @Override + public List getPathEvenTimed(long millisecondsStep) { + + Path path = this.getPath(); + return path.getEvenTimedPoints(millisecondsStep); + + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDynProgForward.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDynProgForward.java new file mode 100644 index 00000000000..f231ce59c31 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDynProgForward.java @@ -0,0 +1,372 @@ +package com.sap.sailing.simulator.impl; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Vector; + +import com.sap.sailing.domain.base.SpeedWithBearing; +import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Path; +import com.sap.sailing.simulator.PathGenerator; +import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.SimulationParameters; +import com.sap.sailing.simulator.TimedPosition; +import com.sap.sailing.simulator.TimedPositionWithSpeed; +import com.sap.sailing.simulator.WindField; +import com.sap.sailing.simulator.WindFieldGenerator; + +public class PathGeneratorDynProgForward implements PathGenerator { + + // private static Logger logger = Logger.getLogger("com.sap.sailing"); + SimulationParameters simulationParameters; + + public PathGeneratorDynProgForward(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public void setSimulationParameters(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public SimulationParameters getSimulationParameters() { + return simulationParameters; + } + + // + // dynamic programming approach with forward iteration along start-end line + // + + // find minimum duration in alternate steps from h1=hidx to the calling h2 + private DPDuration findMinDur(ArrayList alldur) { + DPDuration mindur = alldur.get(0); + for (int i = 0; i < alldur.size(); i++) { + if (alldur.get(i).duration < mindur.duration) { + mindur = alldur.get(i); + } + } + + return mindur; + } + + // calculate duration of step from position p1 to position p2 at time curtime from side of wind side1 + private DPDuration calcDuration(TimePoint curtime, int side1, Position p1, Position p2, WindField windField, + PolarDiagram polarDiagram) { + + // set turnloss to be approximately 4sec (until polardiagram has getTurnloss() method) + long turnloss = polarDiagram.getTurnLoss(); // 4000; + + // System.out.println("p1: "+p1+" p2:"+p2); + + // take wind of starting position p1 at current time + TimedPosition currentTimedPosition = new TimedPositionImpl(curtime, p1); + SpeedWithBearing currentWind = windField.getWind(currentTimedPosition); + // alternate approach: take wind of target position p2 at current time + // TimedPosition windTimedPosition = new TimedPositionImpl(curtime, p2); + // SpeedWithBearing currentWind = windField.getWind(windTimedPosition); + + // System.out.println("wind: "+currentWind.getKnots()+", "+currentWind.getBearing().getDegrees()+"°"); + + // set polar diagram to current wind + polarDiagram.setWind(currentWind); + + // calculate bearing, distance and speed from p1 to p2 + Bearing bearingToP = p1.getBearingGreatCircle(p2); + Distance distanceToP = p1.getDistance(p2); + Speed speedToP = polarDiagram.getSpeedAtBearing(bearingToP); + // System.out.println("p1 to p2: angle: "+bearingToP.getDegrees()+"° dist: "+distanceToP.getMeters()+"m speed: "+speedToP.getMetersPerSecond()+"m/s"); + + // add time delta for sailing from p1 to p2 to current time + double deltat; + if (speedToP.getMetersPerSecond() <= 0.1) { + deltat = 86400; // set a high value for small times + } else { + deltat = (distanceToP.getMeters() / speedToP.getMetersPerSecond()); + } + long timeToP = curtime.asMillis() + (long) (deltat) * 1000; + // System.out.println("time: "+deltat/60.+"min"); + + // identify turn based on change of sign of relative bearing of p1 to p2 towards the wind + Bearing relativeBearing = currentWind.getBearing().reverse().getDifferenceTo(bearingToP); + int side2; + if (relativeBearing.getDegrees() == 0) { + side2 = 0; + } else { + side2 = (int) Math.signum(relativeBearing.getDegrees()); + } + if ((side1 != 0) && (side2 != 0) && (side1 == -side2)) { + timeToP = timeToP + turnloss; + } + + // return arrival time at p2 and wind side at p2 as durP instance + DPDuration durToP = new DPDuration(timeToP, side2, 0, 0); + + return durToP; + } + + // duration parameters: duration in millis, side of the wind, referencing horizontal grid column hidx, + // and path index pidx (pidx currently unused, maybe used for structuring in groups of equivalent time, see + // prototype R-script) + class DPDuration { + public DPDuration(long dur, int sid, int h, int p) { + duration = dur; + side = sid; + hidx = h; + pidx = p; + } + + long duration; + int side; + int hidx; + int pidx; + } + + // location parameters: horizontal grid index idx, duration dur to reach this point (within path) + class DPLocation { + public DPLocation(int i, long d) { + idx = i; + dur = d; + } + + int idx; + long dur; + } + + @Override + public Path getPath() { + + // retrieve simulation parameters + Boundary boundary = new RectangularBoundary(simulationParameters.getCourse().get(0), simulationParameters + .getCourse().get(1));// simulationParameters.getBoundaries(); + WindFieldGenerator windField = simulationParameters.getWindField(); + PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); + // Position start = simulationParameters.getCourse().get(0); + // Position end = simulationParameters.getCourse().get(1); + TimePoint startTime = windField.getStartTime(); + + // the optimal path + LinkedList optPath = new LinkedList(); + + // initiate grid: since performance is good, make it somewhat larger than what we do with dijkstra + int spatialGridsizeVertical = 15; // 21; //(int) Math.round(1.5 * + // simulationParameters.getProperty("Djikstra.gridv[int]").intValue()); // number + // of + // vertical + // grid + // steps + // Formula: sgridh ~ xscale*sgridv/tan(beatangle*pi/180) + int spatialGridsizeHorizontal = 121; // (25-1)*5; //(41-1)*5; //(int) Math.round(2 * + // simulationParameters.getProperty("Djikstra.gridh[int]").intValue()); // number + // of + // horizontal + // grid + // steps + + // make horizontal grid size uneven, to have an index for the middle line + if (spatialGridsizeHorizontal % 2 == 0) { + spatialGridsizeHorizontal++; + } + + // generate grid positions using sgridh and sgridv + Position[][] sailGrid = boundary.extractGrid(spatialGridsizeHorizontal, spatialGridsizeVertical); + + // optimization grid indexes: + // vertical steps: 0, ..., v-1 + // horizontal steps: -gridh, ..., 0, ..., +gridh + int optimizationGridsizeVertical = spatialGridsizeVertical; + int optimizationGridsizeHorizontal = spatialGridsizeHorizontal / 2; + + ArrayList alldur; + ArrayList>> paths = new ArrayList>>(); + ArrayList duras = new ArrayList(); + + // in each vertical step, paths having the same minimum duration are kept + double mintol = 1.0; // threshold of closeness to realy minimum duration for keeping paths + int maxeqpaths = 2; // maximum nmber of equal paths to keep [currently set to low values, to save Java heap + // memory] + + // loop over vertical steps + for (int idxv = 0; idxv < (optimizationGridsizeVertical - 1); idxv++) { + + // System.out.println(" vstep: "+idxv); + + ArrayList>> pathsnew = new ArrayList>>(); + ArrayList durasnew = new ArrayList(); + + // get start boundary correct + int range1; + if (idxv == 0) { + range1 = 0; + } else { + range1 = optimizationGridsizeHorizontal; + } + + // get end boundary correct + int range2; + if (idxv == (optimizationGridsizeVertical - 2)) { + range2 = 0; + } else { + range2 = optimizationGridsizeHorizontal; + } + + // target loop: idxh2 + for (int idxh2 = -range2; idxh2 <= range2; idxh2++) { + + alldur = new ArrayList(); + + if (idxv > 0) { // if not at start of the grid + + // origin loop: idxh1 + for (int idxh1 = -range1; idxh1 <= range1; idxh1++) { + + // calculate the time to go from [idxv,idxh1] to [idxv+1,idxh2] + DPDuration durationh1h2 = calcDuration(new MillisecondsTimePoint( + duras.get(idxh1 + range1).duration), duras.get(idxh1 + range1).side, + sailGrid[idxv][idxh1 + optimizationGridsizeHorizontal], sailGrid[idxv + 1][idxh2 + + optimizationGridsizeHorizontal], windField, polarDiagram); + + // *forbidden* angles + if (durationh1h2.duration < 0) { + continue; + } + + // remember durations of current vertical step in alldur + durationh1h2.hidx = idxh1 + range1; + alldur.add(durationh1h2); + + } // horizontal origin loop: idxh1 + + if (idxv != (optimizationGridsizeVertical - 2)) { // if not at end of the grid + + // find minimum duration in alldur + DPDuration mindur = findMinDur(alldur); + + // remember minimum duration for reaching [idxv,idxh2] + DPDuration tdur = new DPDuration(mindur.duration, mindur.side, idxh2 + + optimizationGridsizeHorizontal, 0); + durasnew.add(tdur); + + // keep all (see mintol, maxeqpaths) paths with minimum duration for reaching [idxv,idxh2] + int k = 0; + for (int j = 0; j < alldur.size(); j++) { + + if ((alldur.get(j).duration <= mintol * mindur.duration) && (k < maxeqpaths)) { + ArrayList> npaths; + if ((pathsnew.size() > 0) + && (pathsnew.size() == (idxh2 + optimizationGridsizeHorizontal + 1))) { + npaths = pathsnew.get(idxh2 + optimizationGridsizeHorizontal); + } else { + npaths = new ArrayList>(); + pathsnew.add(npaths); + } + ArrayList> tpaths = paths.get(alldur.get(j).hidx); + for (int i = 0; i < tpaths.size(); i++) { + Vector tpath; + tpath = new Vector(tpaths.get(i)); // (Vector) + // (tpaths.get(i)).clone(); + tpath.add(new DPLocation(idxh2, mindur.duration)); + npaths.add(tpath); + } + k++; + } + + } + + } else { + + // find minimum in alldur + DPDuration mindur = findMinDur(alldur); + + // remember minimum duration for reaching [idxv,idxh2] + DPDuration tdur = new DPDuration(mindur.duration, mindur.side, 0, 0); + durasnew.add(tdur); + + // keep all (see mintol, maxeqpaths) paths with minimum duration for reaching [idxv,idxh2] + int k = 0; + for (int j = 0; j < alldur.size(); j++) { + + if ((alldur.get(j).duration <= mintol * mindur.duration) && (k < maxeqpaths)) { + ArrayList> npaths; + if ((pathsnew.size() > 0) && (pathsnew.size() == (idxh2 + range2 + 1))) { + npaths = pathsnew.get(idxh2 + range2); + } else { + npaths = new ArrayList>(); + pathsnew.add(npaths); + } + ArrayList> tpaths = paths.get(alldur.get(j).hidx); + for (int i = 0; i < tpaths.size(); i++) { + Vector tpath; + tpath = new Vector(tpaths.get(i)); // (Vector) + // (tpaths.get(i)).clone(); + tpath.add(new DPLocation(0, mindur.duration)); + npaths.add(tpath); + } + k++; + } + + } + } + + } else { // start of grid + + Vector tpath = new Vector(); + tpath.add(new DPLocation(0, startTime.asMillis())); + ArrayList> tpaths = new ArrayList>(); + tpaths.add(tpath); + pathsnew.add(tpaths); + DPDuration tdur = calcDuration(startTime, 0, sailGrid[0][optimizationGridsizeHorizontal], + sailGrid[1][idxh2 + optimizationGridsizeHorizontal], windField, polarDiagram); + tdur.hidx = idxh2 + optimizationGridsizeHorizontal; + durasnew.add(tdur); + tpath.add(new DPLocation(idxh2, tdur.duration)); + } + + } // horizontal target loop: idxh2 + + paths = pathsnew; + duras = durasnew; + + /* + * if (idxv == (gridv-2)) { System.out.print(" optimal path:"); for(int i=0; i<1;i++) {//paths.size(); + * i++) { //System.out.print(""+duras.get(i).duration+" - "); for(int j=0; j<1;j++) {//paths.get(i).size(); + * j++) { int kmax = paths.get(i).get(j).size(); + * System.out.print(""+((paths.get(i).get(j).get(kmax-1).dur-paths + * .get(i).get(j).get(0).dur)/1000./60.)+"min: "); for(int k=0;k getPathEvenTimed(long millisecondsStep) { + + Path path = this.getPath(); + return path.getEvenTimedPoints(millisecondsStep); + + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorOpportunistEuclidian.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorOpportunistEuclidian.java new file mode 100644 index 00000000000..0c9a4d4060b --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorOpportunistEuclidian.java @@ -0,0 +1,344 @@ +package com.sap.sailing.simulator.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import com.sap.sailing.domain.base.SpeedWithBearing; +import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.simulator.Path; +import com.sap.sailing.simulator.PathGenerator; +import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.SimulationParameters; +import com.sap.sailing.simulator.TimedPositionWithSpeed; +import com.sap.sailing.simulator.WindFieldGenerator; + +public class PathGeneratorOpportunistEuclidian implements PathGenerator { + + private static Logger logger = Logger.getLogger("com.sap.sailing"); + SimulationParameters simulationParameters; + int maxLeft; + int maxRight; + boolean startLeft; + + public PathGeneratorOpportunistEuclidian(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public void setSimulationParameters(SimulationParameters params) { + simulationParameters = params; + } + + @Override + public SimulationParameters getSimulationParameters() { + return simulationParameters; + } + + public void setEvaluationParameters(int maxLeftVal, int maxRightVal, boolean startLeftVal) { + this.maxLeft = maxLeftVal; + this.maxRight = maxRightVal; + this.startLeft = startLeftVal; + } + + @Override + public Path getPath() { + + WindFieldGenerator wf = simulationParameters.getWindField(); + PolarDiagram pd = simulationParameters.getBoatPolarDiagram(); + Position start = simulationParameters.getCourse().get(0); + Position end = simulationParameters.getCourse().get(1); + TimePoint startTime = wf.getStartTime();// new MillisecondsTimePoint(0); + List lst = new ArrayList(); + + Position currentPosition = start; + TimePoint currentTime = startTime; + + int stepsLeft = 0; + int stepsRight = 0; + boolean allLeft = true; + boolean allRight = true; + + int prevDirection = -1; + long turnloss = pd.getTurnLoss(); // 4000; // time lost when doing a turn + long windpred = 1000; // time used to predict wind, i.e. hypothetical sailors prediction + + TimePoint leftTime; + TimePoint rightTime; + + Wind wndStart = wf.getWind(new TimedPositionWithSpeedImpl(startTime, start, null)); + logger.fine("wndStart speed:" + wndStart.getKnots() + " angle:" + wndStart.getBearing().getDegrees()); + pd.setWind(wndStart); + Bearing bearStart = currentPosition.getBearingGreatCircle(end); + SpeedWithBearing spdStart = pd.getSpeedAtBearing(bearStart); + lst.add(new TimedPositionWithSpeedImpl(startTime, start, spdStart)); + long timeStep = wf.getTimeStep().asMillis(); + logger.info("Time step :" + timeStep); + // while there is more than 5% of the total distance to the finish + + SpeedWithBearing slft = null; + SpeedWithBearing srght = null; + while (currentPosition.getDistance(end).compareTo(start.getDistance(end).scale(0.05)) > 0) { + + // TimePoint nextTime = new MillisecondsTimePoint(currentTime.asMillis() + 30000); + + long nextTimeVal = currentTime.asMillis() + timeStep;// + 30000; + TimePoint nextTime = new MillisecondsTimePoint(nextTimeVal); + + Wind cWind = wf.getWind(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); + logger.fine("cWind speed:" + cWind.getKnots() + " angle:" + cWind.getBearing().getDegrees()); + // System.out.println("Start WindBear: " + (cWind.getBearing().getDegrees() - bearStart.getDegrees())); + pd.setWind(cWind); + + // get wind of direction + Bearing wLft = pd.optimalDirectionsUpwind()[0]; + Bearing wRght = pd.optimalDirectionsUpwind()[1]; + // Bearing wDirect = currentPosition.getBearingGreatCircle(end); + + // SpeedWithBearing sWDirect = pd.getSpeedAtBearing(wDirect); + SpeedWithBearing sWLft = pd.getSpeedAtBearing(wLft); + SpeedWithBearing sWRght = pd.getSpeedAtBearing(wRght); + logger.fine("left boat speed:" + sWLft.getKnots() + " angle:" + sWLft.getBearing().getDegrees() + + " right boat speed:" + sWRght.getKnots() + " angle:" + sWRght.getBearing().getDegrees()); + + TimePoint wTime = new MillisecondsTimePoint(currentTime.asMillis() + windpred); + // Position pWDirect = sWDirect.travelTo(currentPosition, currentTime, wTime); + Position pWLft = sWLft.travelTo(currentPosition, currentTime, wTime); + Position pWRght = sWRght.travelTo(currentPosition, currentTime, wTime); + + logger.fine("current Pos:" + currentPosition.getLatDeg() + "," + currentPosition.getLngDeg()); + logger.fine("left Pos:" + pWLft.getLatDeg() + "," + pWLft.getLngDeg()); + logger.fine("right Pos:" + pWRght.getLatDeg() + "," + pWRght.getLngDeg()); + + // calculate next step + // Wind dWind = wf.getWind(new TimedPositionWithSpeedImpl(currentTime, pWDirect, null)); + // logger.fine("dWind speed:" + dWind.getKnots() + " angle:" + dWind.getBearing().getDegrees()); + // pd.setWind(dWind); + // Bearing direct = currentPosition.getBearingGreatCircle(end); + // SpeedWithBearing sdirect = pd.getSpeedAtBearing(direct); + + Wind lWind = wf.getWind(new TimedPositionWithSpeedImpl(currentTime, pWLft, null)); + logger.fine("lWind speed:" + lWind.getKnots() + " angle:" + lWind.getBearing().getDegrees()); + // System.out.println("Left WindBear: " + (lWind.getBearing().getDegrees() - bearStart.getDegrees())); + pd.setWind(lWind); + Bearing lft = pd.optimalDirectionsUpwind()[0]; + slft = pd.getSpeedAtBearing(lft); + + Wind rWind = wf.getWind(new TimedPositionWithSpeedImpl(currentTime, pWRght, null)); + logger.fine("rWind speed:" + rWind.getKnots() + " angle:" + rWind.getBearing().getDegrees()); + // System.out.println("Right WindBear: " + (rWind.getBearing().getDegrees() - bearStart.getDegrees())); + pd.setWind(rWind); + Bearing rght = pd.optimalDirectionsUpwind()[1]; + srght = pd.getSpeedAtBearing(rght); + + // System.out.println("Bearings : " + (lft.getDegrees() - bearStart.getDegrees()) + " " + // + (rght.getDegrees() - bearStart.getDegrees())); + + logger.fine("left boat speed:" + slft.getKnots() + " angle:" + slft.getBearing().getDegrees() + + " right boat speed:" + srght.getKnots() + " angle:" + srght.getBearing().getDegrees()); + + /* + * if (prevDirection == 0) { directTime = new MillisecondsTimePoint(nextTimeVal); leftTime = new + * MillisecondsTimePoint(nextTimeVal - turnloss); rightTime = new MillisecondsTimePoint(nextTimeVal - + * turnloss); } else + */ + if (prevDirection == 1) { + leftTime = new MillisecondsTimePoint(nextTimeVal); + rightTime = new MillisecondsTimePoint(nextTimeVal - turnloss); + } else if (prevDirection == 2) { + leftTime = new MillisecondsTimePoint(nextTimeVal - turnloss); + rightTime = new MillisecondsTimePoint(nextTimeVal); + } else { + leftTime = new MillisecondsTimePoint(nextTimeVal); + rightTime = new MillisecondsTimePoint(nextTimeVal); + } + + // Position pdirect = sdirect.travelTo(currentPosition, currentTime, directTime); + Position plft = slft.travelTo(currentPosition, currentTime, leftTime); + Position prght = srght.travelTo(currentPosition, currentTime, rightTime); + + // System.out.println("RelBearLeft : " + (slft.getBearing().getDegrees() - bearStart.getDegrees())); + // System.out.println("RelBearRight: " + (srght.getBearing().getDegrees() - bearStart.getDegrees())); + + // Distance ddirect = pdirect.getDistance(end); + Distance dlft = plft.getDistance(end); + Distance drght = prght.getDistance(end); + + double lDistCM = Math.round(dlft.getMeters() * 1000.) / 1000.; + double rDistCM = Math.round(drght.getMeters() * 1000.) / 1000.; + + /* + * if (ddirect.compareTo(dlft) <= 0 && ddirect.compareTo(drght) <= 0) { lst.add(new + * TimedPositionWithSpeedImpl(nextTime, pdirect, sdirect)); currentPosition = pdirect; prevDirection = 0; } + */ + + if (prevDirection == -1) { + + if (startLeft) { + lst.add(new TimedPositionWithSpeedImpl(nextTime, plft, slft)); + currentPosition = plft; + if (prevDirection == 2) { + allLeft = false; + } else { + stepsLeft++; + } + prevDirection = 1; + } else { + lst.add(new TimedPositionWithSpeedImpl(nextTime, prght, srght)); + currentPosition = prght; + if (prevDirection == 1) { + allRight = false; + } else { + stepsRight++; + } + prevDirection = 2; + } + + } else { + // System.out.println("Distance Left - Right: "+lDistCM+" - "+ rDistCM); + if (((lDistCM <= rDistCM) && (!allLeft || (stepsLeft < maxLeft))) + || (allRight && (stepsRight >= maxRight))) { + lst.add(new TimedPositionWithSpeedImpl(nextTime, plft, slft)); + currentPosition = plft; + if (prevDirection == 2) { + allLeft = false; + } else { + stepsLeft++; + } + prevDirection = 1; + } else { + // if (((drght.compareTo(dlft) < 0)&&(stepsRight < maxRight))||(stepsLeft >= maxLeft)) { + lst.add(new TimedPositionWithSpeedImpl(nextTime, prght, srght)); + currentPosition = prght; + if (prevDirection == 1) { + allRight = false; + } else { + stepsRight++; + } + prevDirection = 2; + } + } + + currentTime = nextTime; + + } + + // approach end + double bearingToEnd; + SpeedWithBearing bearingBoat; + if (prevDirection == 1) { + bearingToEnd = currentPosition.getBearingGreatCircle(end).getDegrees() + - pd.optimalDirectionsUpwind()[1].getDegrees(); + bearingBoat = slft; + } else { + bearingToEnd = pd.optimalDirectionsUpwind()[0].getDegrees() + - currentPosition.getBearingGreatCircle(end).getDegrees(); + bearingBoat = srght; + } + bearingToEnd = (bearingToEnd + 360 + 180) % 360 - 180; + // System.out.println("b2End:" + bearingToEnd); + double bearingToEndSign = Math.signum(bearingToEnd); + Position tackPosition = currentPosition; + Position tackPositionNew = currentPosition; + TimePoint tackTime = currentTime; + TimePoint tackTimeNext = currentTime; + long searchTime = 500; + while (Math.signum(bearingToEnd) == bearingToEndSign) { + tackTime = tackTimeNext; + tackPosition = tackPositionNew; + if (bearingToEnd < 0) { + tackTimeNext = new MillisecondsTimePoint(tackTime.asMillis() + searchTime); + tackPositionNew = bearingBoat.travelTo(tackPosition, tackTime, tackTimeNext); + } else { + tackTimeNext = new MillisecondsTimePoint(tackTime.asMillis() - searchTime); + tackPositionNew = bearingBoat.travelTo(tackPosition, tackTime, tackTimeNext); + } + if (prevDirection == 1) { + bearingToEnd = tackPositionNew.getBearingGreatCircle(end).getDegrees() + - pd.optimalDirectionsUpwind()[1].getDegrees(); + // System.out.println("bEnd :" + tackPosition.getBearingGreatCircle(end).getDegrees()); + // System.out.println("bBoat:" + pd.optimalDirectionsUpwind()[1].getDegrees()); + } else { + bearingToEnd = pd.optimalDirectionsUpwind()[0].getDegrees() + - tackPositionNew.getBearingGreatCircle(end).getDegrees(); + // System.out.println("bEnd :" + tackPosition.getBearingGreatCircle(end).getDegrees()); + // System.out.println("bBoat:" + pd.optimalDirectionsUpwind()[0].getDegrees()); + } + bearingToEnd = (bearingToEnd + 360 + 180) % 360 - 180; + // System.out.println("b2End:" + bearingToEnd); + } + + // scan more detailed between tackPosition and tackPositionNew + boolean endIteration = false; + while ((Math.abs(bearingToEnd) > 0.1) && (!endIteration)) { + TimePoint tackTimeMid = new MillisecondsTimePoint((tackTime.asMillis() + tackTimeNext.asMillis()) / 2); + if (tackTimeMid.asMillis() == tackTime.asMillis()) { + endIteration = true; + } + Position tackPositionMid = bearingBoat.travelTo(tackPosition, tackTime, tackTimeMid); + if (prevDirection == 1) { + bearingToEnd = tackPositionMid.getBearingGreatCircle(end).getDegrees() + - pd.optimalDirectionsUpwind()[1].getDegrees(); + // System.out.println("bEnd :" + tackPositionMid.getBearingGreatCircle(end).getDegrees()); + // System.out.println("bBoat:" + pd.optimalDirectionsUpwind()[1].getDegrees()); + } else { + bearingToEnd = pd.optimalDirectionsUpwind()[0].getDegrees() + - tackPositionMid.getBearingGreatCircle(end).getDegrees(); + // System.out.println("bEnd :" + tackPositionMid.getBearingGreatCircle(end).getDegrees()); + // System.out.println("bBoat:" + pd.optimalDirectionsUpwind()[0].getDegrees()); + } + bearingToEnd = (bearingToEnd + 360 + 180) % 360 - 180; + // System.out.println("fine b2End:" + bearingToEnd); + if (Math.signum(bearingToEnd) == bearingToEndSign) { + tackPosition = tackPositionMid; + tackTime = tackTimeMid; + } else { + tackPositionNew = tackPositionMid; + tackTimeNext = tackTimeMid; + } + } + + TimePoint tackTimeOpt = new MillisecondsTimePoint((tackTime.asMillis() + tackTimeNext.asMillis()) / 2); + Position tackPositionOpt = bearingBoat.travelTo(tackPosition, tackTime, tackTimeOpt); + + // System.out.println("tack time:"+tackPosition.) + lst.remove(lst.size() - 1); + lst.add(new TimedPositionWithSpeedImpl(tackTimeOpt, tackPositionOpt, null)); + + currentPosition = tackPosition; + currentTime = tackTime; + + Wind wndEnd = wf.getWind(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); + // System.out.println("wndEnd speed:" + wndEnd.getKnots() + " angle:" + wndEnd.getBearing().getDegrees()); + pd.setWind(wndEnd); + Bearing bearEnd = currentPosition.getBearingGreatCircle(end); + // System.out.println("bearEnd angle:" + bearEnd.getDegrees()); + SpeedWithBearing spdEnd = pd.getSpeedAtBearing(bearEnd); + + long nextTimeVal = currentTime.asMillis() + + Math.round(currentPosition.getDistance(end).getMeters() / spdEnd.getMetersPerSecond()) * 1000;// 30000; + // System.out.println("time:" + currentTime.asMillis() + " " + currentPosition.getDistance(end).getMeters() + // + " speed:" + spdEnd.getMetersPerSecond()); + // System.out.println("next:" + nextTimeVal); + TimePoint nextTime = new MillisecondsTimePoint(nextTimeVal + turnloss); + + lst.add(new TimedPositionWithSpeedImpl(nextTime, end, null)); + + return new PathImpl(lst, wf); + + } + + @Override + public List getPathEvenTimed(long millisecondsStep) { + + Path path = this.getPath(); + return path.getEvenTimedPoints(millisecondsStep); + + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49.java deleted file mode 100644 index f0c16818644..00000000000 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49.java +++ /dev/null @@ -1,559 +0,0 @@ -package com.sap.sailing.simulator.impl; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.NavigableMap; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; - -import com.sap.sailing.domain.base.SpeedWithBearing; -import com.sap.sailing.domain.base.impl.KnotSpeedImpl; -import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl; -import com.sap.sailing.domain.common.Bearing; -import com.sap.sailing.simulator.PolarDiagram; -import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.impl.DegreeBearingImpl; -import com.sap.sailing.domain.common.impl.RadianBearingImpl; - -public class PolarDiagram49 implements PolarDiagram { - - // the current speed and direction of the wind - private SpeedWithBearing wind = new KnotSpeedWithBearingImpl(6, - new DegreeBearingImpl(180)); - - // the preferred direction of movement - // is used by optimalDirectionsUpwind() and optimialDirectionsDownwind() - private Bearing targetDirection = new DegreeBearingImpl(0); - - protected NavigableMap> speedTable; - protected NavigableMap beatAngles; - protected NavigableMap gybeAngles; - protected NavigableMap beatSOG; - protected NavigableMap gybeSOG; - - // this constructor creates an instance with a hard-coded set of values - public PolarDiagram49() { - speedTable = new TreeMap>(); - NavigableMap tableRow; - - tableRow = new TreeMap(PolarDiagram49.bearingComparator); - tableRow.put(new DegreeBearingImpl(0), Speed.NULL); - tableRow.put(new DegreeBearingImpl(52), Speed.NULL); - tableRow.put(new DegreeBearingImpl(60), Speed.NULL); - tableRow.put(new DegreeBearingImpl(75), Speed.NULL); - tableRow.put(new DegreeBearingImpl(90), Speed.NULL); - tableRow.put(new DegreeBearingImpl(110), Speed.NULL); - tableRow.put(new DegreeBearingImpl(120), Speed.NULL); - tableRow.put(new DegreeBearingImpl(135), Speed.NULL); - tableRow.put(new DegreeBearingImpl(150), Speed.NULL); - tableRow.put(new DegreeBearingImpl(180), Speed.NULL); - speedTable.put(Speed.NULL, tableRow); - - tableRow = new TreeMap(PolarDiagram49.bearingComparator); - tableRow.put(new DegreeBearingImpl(0), Speed.NULL); - tableRow.put(new DegreeBearingImpl(52), new KnotSpeedImpl(6.57)); - tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(7.01)); - tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(7.36)); - tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(7.31)); - tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(6.73)); - tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(6.39)); - tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(5.58)); - tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(4.62)); - tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(2.5)); - speedTable.put(new KnotSpeedImpl(6), tableRow); - - tableRow = new TreeMap(PolarDiagram49.bearingComparator); - tableRow.put(new DegreeBearingImpl(0), Speed.NULL); - tableRow.put(new DegreeBearingImpl(52), new KnotSpeedImpl(7.78)); - tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(8.13)); - tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(8.38)); - tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(8.31)); - tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(8.04)); - tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(7.79)); - tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(7)); - tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(5.85)); - tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(3)); - speedTable.put(new KnotSpeedImpl(8), tableRow); - - tableRow = new TreeMap(PolarDiagram49.bearingComparator); - tableRow.put(new DegreeBearingImpl(0), Speed.NULL); - tableRow.put(new DegreeBearingImpl(52), new KnotSpeedImpl(8.34)); - tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(8.66)); - tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(9.01)); - tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(8.95)); - tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(8.87)); - tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(8.64)); - tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(8.02)); - tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(6.97)); - tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(4)); - speedTable.put(new KnotSpeedImpl(10), tableRow); - - tableRow = new TreeMap(PolarDiagram49.bearingComparator); - tableRow.put(new DegreeBearingImpl(0), Speed.NULL); - tableRow.put(new DegreeBearingImpl(52), new KnotSpeedImpl(8.64)); - tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(8.95)); - tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(9.39)); - tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(9.5)); - tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(9.54)); - tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(9.3)); - tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(8.71)); - tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(7.86)); - tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(5)); - speedTable.put(new KnotSpeedImpl(12), tableRow); - - tableRow = new TreeMap(PolarDiagram49.bearingComparator); - tableRow.put(new DegreeBearingImpl(0), Speed.NULL); - tableRow.put(new DegreeBearingImpl(52), new KnotSpeedImpl(8.84)); - tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(9.15)); - tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(9.62)); - tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(9.98)); - tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(10.08)); - tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(9.95)); - tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(9.29)); - tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(8.57)); - tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(6)); - speedTable.put(new KnotSpeedImpl(14), tableRow); - - tableRow = new TreeMap(PolarDiagram49.bearingComparator); - tableRow.put(new DegreeBearingImpl(0), Speed.NULL); - tableRow.put(new DegreeBearingImpl(52), new KnotSpeedImpl(8.95)); - tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(9.28)); - tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(9.8)); - tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(10.28)); - tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(10.44)); - tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(10.72)); - tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(9.88)); - tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(9.13)); - tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(7)); - speedTable.put(new KnotSpeedImpl(16), tableRow); - - tableRow = new TreeMap(PolarDiagram49.bearingComparator); - tableRow.put(new DegreeBearingImpl(0), Speed.NULL); - tableRow.put(new DegreeBearingImpl(52), new KnotSpeedImpl(8.91)); - tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(9.34)); - tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(10.01)); - tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(10.67)); - tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(11.17)); - tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(11.81)); - tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(11.47)); - tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(10.26)); - tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(7.5)); - speedTable.put(new KnotSpeedImpl(20), tableRow); - - beatAngles = new TreeMap(); - beatAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(45)); - beatAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(43.4)); - beatAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(41.8)); - beatAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(38.9)); - beatAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(37.5)); - beatAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(36.8)); - beatAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(36.3)); - beatAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(36.9)); - - double beatScale = 1.1; - beatSOG = new TreeMap(); - beatSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); - beatSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.85 * beatScale)); - beatSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(6.98 * beatScale)); - beatSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(7.39 * beatScale)); - beatSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(7.68 * beatScale)); - beatSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(7.89 * beatScale)); - beatSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(7.92 * beatScale)); - beatSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(7.94 * beatScale)); - - gybeAngles = new TreeMap(); - gybeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(120)); - gybeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(140)); - gybeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(142.6)); - gybeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(148.4)); - gybeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(154.4)); - gybeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(162.5)); - gybeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(169.6)); - gybeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(172.3)); - - gybeSOG = new TreeMap(); - gybeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); - gybeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.22)); - gybeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(6.38)); - gybeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(7.09)); - gybeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(7.6)); - gybeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(7.97)); - gybeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(8.39)); - gybeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(9.38)); - - for (Speed s : speedTable.keySet()) { - - if (beatAngles.containsKey(s) - && !speedTable.get(s).containsKey(beatAngles.get(s))) - speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); - - if (gybeAngles.containsKey(s) - && !speedTable.get(s).containsKey(gybeAngles.get(s))) - speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); - - } - - } - - // a constructor that allows a generic set of parameters - public PolarDiagram49( - NavigableMap> speeds, - NavigableMap beats, - NavigableMap gybes, - NavigableMap beatSOGs, - NavigableMap gybeSOGs) { - - wind = new KnotSpeedWithBearingImpl(0, new DegreeBearingImpl(180)); - - speedTable = speeds; - beatAngles = beats; - gybeAngles = gybes; - beatSOG = beatSOGs; - gybeSOG = gybeSOGs; - - for (Speed s : speedTable.keySet()) { - - if (beatAngles.containsKey(s) - && !speedTable.get(s).containsKey(beatAngles.get(s))) - speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); - - if (gybeAngles.containsKey(s) - && !speedTable.get(s).containsKey(gybeAngles.get(s))) - speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); - - } - - } - - @Override - public SpeedWithBearing getWind() { - return wind; - } - - @Override - public void setWind(SpeedWithBearing newWind) { - wind = newWind; - } - - @Override - public SpeedWithBearing getSpeedAtBearing(Bearing bearing) { - - Bearing relativeBearing = wind.getBearing().reverse() - .getDifferenceTo(bearing); - if (relativeBearing.getDegrees() < 0) - relativeBearing = relativeBearing - .getDifferenceTo(new DegreeBearingImpl(0)); - - Speed floorWind = speedTable.floorKey(wind); - Speed ceilingWind = speedTable.ceilingKey(wind); - - if (ceilingWind == null) { - ceilingWind = floorWind; - } - if (floorWind == null) { - floorWind = ceilingWind; - } - - NavigableMap floorSpeeds = speedTable.get(floorWind); - NavigableMap ceilingSpeeds = speedTable - .get(ceilingWind); - - // Taylor estimations of order 1 - Speed floorSpeed1 = floorSpeeds.floorEntry(relativeBearing).getValue(); - Speed floorSpeed2 = floorSpeeds.ceilingEntry(relativeBearing) - .getValue(); - Bearing floorBearing1 = floorSpeeds.floorKey(relativeBearing); - Bearing floorBearing2 = floorSpeeds.ceilingKey(relativeBearing); - double floorSpeed; - if (floorSpeed1.equals(floorSpeed2)) { - floorSpeed = floorSpeed1.getKnots(); - } else { - floorSpeed = floorSpeed1.getKnots() - + (relativeBearing.getRadians() - floorBearing1 - .getRadians()) - * (floorSpeed2.getKnots() - floorSpeed1.getKnots()) - / (floorBearing2.getRadians() - floorBearing1.getRadians()); - } - - Speed ceilingSpeed1 = ceilingSpeeds.floorEntry(relativeBearing) - .getValue(); - Speed ceilingSpeed2 = ceilingSpeeds.ceilingEntry(relativeBearing) - .getValue(); - Bearing ceilingBearing1 = ceilingSpeeds.floorKey(relativeBearing); - Bearing ceilingBearing2 = ceilingSpeeds.ceilingKey(relativeBearing); - double ceilingSpeed; - if (ceilingSpeed1.equals(ceilingSpeed2)) { - ceilingSpeed = ceilingSpeed1.getKnots(); - } else { - ceilingSpeed = ceilingSpeed1.getKnots() - + (relativeBearing.getRadians() - ceilingBearing1 - .getRadians()) - * (ceilingSpeed2.getKnots() - ceilingSpeed1.getKnots()) - / (ceilingBearing2.getRadians() - ceilingBearing1 - .getRadians()); - } - - double speed; - if (floorWind.equals(ceilingWind)) { - speed = floorSpeed; - } else { - speed = floorSpeed + (wind.getKnots() - floorWind.getKnots()) - * (ceilingSpeed - floorSpeed) - / (ceilingWind.getKnots() - floorWind.getKnots()); - } - if ((Math.abs(relativeBearing.getDegrees()) < 20) - || (Math.abs(relativeBearing.getDegrees()) > 330)) { - speed = 0.1; - } - - return new KnotSpeedWithBearingImpl(speed, bearing); - } - - @Override - public Bearing[] optimalDirectionsUpwind() { - Bearing windBearing = wind.getBearing().reverse(); - Bearing estBeatAngleRight = null; - Bearing estBeatAngleLeft = null; - if (targetDirection.equals(new DegreeBearingImpl(0))) { - Bearing floorBeatAngle; - if (beatAngles.floorEntry(wind) == null) { - floorBeatAngle = beatAngles.ceilingEntry(wind).getValue(); - } else { - floorBeatAngle = beatAngles.floorEntry(wind).getValue(); - } - Bearing ceilingBeatAngle; - if (beatAngles.ceilingEntry(wind) == null) { - ceilingBeatAngle = beatAngles.floorEntry(wind).getValue(); - } else { - ceilingBeatAngle = beatAngles.ceilingEntry(wind).getValue(); - } - if (floorBeatAngle == null) - floorBeatAngle = new DegreeBearingImpl(0); - if (ceilingBeatAngle == null) - ceilingBeatAngle = new DegreeBearingImpl(0); - - Speed floorSpeed = beatAngles.floorKey(wind); - if (floorSpeed == null) { - floorSpeed = beatAngles.ceilingKey(wind); - } - Speed ceilingSpeed = beatAngles.ceilingKey(wind); - if (beatAngles.ceilingKey(wind) == null) { - ceilingSpeed = beatAngles.floorKey(wind); - } - double beatAngle; - if (floorSpeed.equals(ceilingSpeed)) { - beatAngle = floorBeatAngle.getRadians(); - } else { - beatAngle = floorBeatAngle.getRadians() - + (wind.getKnots() - floorSpeed.getKnots()) - * (ceilingBeatAngle.getRadians() - floorBeatAngle - .getRadians()) - / (ceilingSpeed.getKnots() - floorSpeed.getKnots()); - } - estBeatAngleRight = new RadianBearingImpl(+beatAngle); - estBeatAngleLeft = new RadianBearingImpl(-beatAngle); - return new Bearing[] { - // windBearing.add(estBeatAngle), - // windBearing.add(estBeatAngle.getDifferenceTo(windBearing)) - windBearing.add(estBeatAngleLeft), - windBearing.add(estBeatAngleRight) }; - } else { - Set allKeys = new TreeSet(bearingComparator); - for (Double b = 0.0; b < 360.0; b += 5.0) - allKeys.add(new DegreeBearingImpl(b)); - Bearing _targetDirection = targetDirection; - setTargetDirection(new DegreeBearingImpl(0.0)); - allKeys.addAll(Arrays.asList(optimalDirectionsUpwind())); - allKeys.addAll(Arrays.asList(optimalDirectionsDownwind())); - setTargetDirection(_targetDirection); - Double maxSpeedRight = 0.0; - Double maxSpeedLeft = 0.0; - for (Bearing b : allKeys) { - if (b.getDifferenceTo(getWind().getBearing()).getDegrees() > 0) { - if (getSpeedAtBearing(b).getKnots() - * Math.cos(b.getDifferenceTo(getTargetDirection()) - .getRadians()) > maxSpeedRight) { - maxSpeedRight = getSpeedAtBearing(b).getKnots() - * Math.cos(b.getDifferenceTo( - getTargetDirection()).getRadians()); - estBeatAngleRight = b; - } - } else if (getSpeedAtBearing(b).getKnots() - * Math.cos(b.getDifferenceTo(getTargetDirection()) - .getRadians()) > maxSpeedLeft) { - maxSpeedLeft = getSpeedAtBearing(b).getKnots() - * Math.cos(b.getDifferenceTo(getTargetDirection()) - .getRadians()); - estBeatAngleLeft = b; - } - } - return new Bearing[] { - // windBearing.add(estBeatAngle), - // windBearing.add(estBeatAngle.getDifferenceTo(windBearing)) - estBeatAngleLeft, estBeatAngleRight }; - } - - } - - @Override - public Bearing[] optimalDirectionsDownwind() { - - Bearing windBearing = wind.getBearing().reverse(); - Bearing estGybeAngleRight = null; - Bearing estGybeAngleLeft = null; - if (getTargetDirection().equals(new DegreeBearingImpl(0))) { - windBearing = wind.getBearing().reverse(); - estGybeAngleRight = null; - estGybeAngleLeft = null; - Bearing floorGybeAngle = gybeAngles.floorEntry(wind).getValue(); - Bearing ceilingGybeAngle = gybeAngles.ceilingEntry(wind).getValue(); - if (floorGybeAngle == null) - floorGybeAngle = new DegreeBearingImpl(0); - if (ceilingGybeAngle == null) - ceilingGybeAngle = new DegreeBearingImpl(0); - Speed floorSpeed = gybeAngles.floorKey(wind); - Speed ceilingSpeed = gybeAngles.ceilingKey(wind); - double gybeAngle; - if (floorSpeed.equals(ceilingSpeed)) { - gybeAngle = floorGybeAngle.getRadians(); - } else { - gybeAngle = floorGybeAngle.getRadians() - + (wind.getKnots() - floorSpeed.getKnots()) - * (ceilingGybeAngle.getRadians() - floorGybeAngle - .getRadians()) - / (ceilingSpeed.getKnots() - floorSpeed.getKnots()); - } - // Bearing estGybeAngle = new RadianBearingImpl(gybeAngle); - estGybeAngleRight = new RadianBearingImpl(+gybeAngle); - estGybeAngleLeft = new RadianBearingImpl(-gybeAngle); - return new Bearing[] { windBearing.add(estGybeAngleRight), - windBearing.add(estGybeAngleLeft) }; - } else { - - Set allKeys = new TreeSet(bearingComparator); - for (Double b = 0.0; b < 360.0; b += 5.0) - allKeys.add(new DegreeBearingImpl(b)); - Bearing _targetDirection = targetDirection; - setTargetDirection(new DegreeBearingImpl(0.0)); - allKeys.addAll(Arrays.asList(optimalDirectionsUpwind())); - allKeys.addAll(Arrays.asList(optimalDirectionsDownwind())); - setTargetDirection(_targetDirection); - Double maxSpeedRight = 0.0; - Double maxSpeedLeft = 0.0; - for (Bearing b : allKeys) { - if (b.getDifferenceTo(getWind().getBearing()).getDegrees() > 0) { - if (getSpeedAtBearing(b).getKnots() - * Math.cos(b.getDifferenceTo( - getTargetDirection().reverse()) - .getRadians()) > maxSpeedRight) { - maxSpeedRight = getSpeedAtBearing(b).getKnots() - * Math.cos(b.getDifferenceTo( - getTargetDirection().reverse()) - .getRadians()); - estGybeAngleRight = b; - } - } else if (getSpeedAtBearing(b).getKnots() - * Math.cos(b.getDifferenceTo( - getTargetDirection().reverse()).getRadians()) > maxSpeedLeft) { - maxSpeedLeft = getSpeedAtBearing(b).getKnots() - * Math.cos(b.getDifferenceTo( - getTargetDirection().reverse()) - .getRadians()); - estGybeAngleLeft = b; - } - } - return new Bearing[] { - // windBearing.add(estBeatAngle), - // windBearing.add(estBeatAngle.getDifferenceTo(windBearing)) - estGybeAngleLeft, estGybeAngleRight }; - } - } - - // a Bearing Comparator useful in the creation of sorted sets of Bearing - public static Comparator bearingComparator = new Comparator() { - - @Override - public int compare(Bearing o1, Bearing o2) { - Double d1 = o1.getDegrees(); - if (d1 < 0) - d1 = 360 + d1; - Double d2 = o2.getDegrees(); - if (d2 < 0) - d2 = 360 + d2; - return d1.compareTo(d2); - } - - }; - - @Override - public long getTurnLoss() { - // TODO Auto-generated method stub - return 4000; - } - - // TO BE REVIEWED - // not sure I use the right terms and conventions - @Override - public WindSide getWindSide(Bearing bearing) { - WindSide windSide = null; - if (bearingComparator.compare(bearing, wind.getBearing().reverse()) > 0) - windSide = WindSide.LEFT; - if (bearingComparator.compare(bearing, wind.getBearing().reverse()) < 0) - windSide = WindSide.RIGHT; - if (bearing.equals(wind.getBearing())) - windSide = WindSide.OPPOSING; - if (bearing.equals(wind.getBearing().reverse())) - windSide = WindSide.FACING; - - return windSide; - } - - // returns a table of Bearing-Speed pairs with a bearingStep granularity - // for all Speeds in speedTable - @Override - public NavigableMap> polarDiagramPlot( - Double bearingStep) { - - NavigableMap> table = new TreeMap>(); - Set extraBearings = new HashSet(); - - for (Speed s : speedTable.keySet()) { - setWind(new KnotSpeedWithBearingImpl(s.getKnots(), - new DegreeBearingImpl(135))); - extraBearings.addAll(Arrays.asList(optimalDirectionsUpwind())); - extraBearings.addAll(Arrays.asList(optimalDirectionsDownwind())); - } - - for (Speed s : speedTable.keySet()) { - setWind(new KnotSpeedWithBearingImpl(s.getKnots(), - new DegreeBearingImpl(135))); - NavigableMap currentTable = new TreeMap( - bearingComparator); - table.put(s, currentTable); - - for (Double b = 0.0; b < 360.0; b += bearingStep) { - Bearing bearing = new DegreeBearingImpl(b); - currentTable.put(bearing, getSpeedAtBearing(bearing)); - } - for (Bearing extraBearing : extraBearings) - currentTable.put(extraBearing, getSpeedAtBearing(extraBearing)); - } - - return table; - } - - @Override - public Bearing getTargetDirection() { - return targetDirection; - } - - @Override - public void setTargetDirection(Bearing newTargetDirection) { - targetDirection = newTargetDirection; - } - -} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram505.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram505.java new file mode 100644 index 00000000000..4b1bf1257f7 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram505.java @@ -0,0 +1,177 @@ +package com.sap.sailing.simulator.impl; + +import java.util.NavigableMap; +import java.util.TreeMap; + +import com.sap.sailing.domain.base.impl.KnotSpeedImpl; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; + +public class PolarDiagram505 extends PolarDiagramBase { + + // this constructor creates an instance with a hard-coded set of values + public PolarDiagram505() { + speedTable = new TreeMap>(); + NavigableMap tableRow; + + double cutAngle = 35.0; + + tableRow = new TreeMap(PolarDiagramBase.bearingComparator); + tableRow.put(new DegreeBearingImpl(0), Speed.NULL); + tableRow.put(new DegreeBearingImpl(cutAngle), new KnotSpeedImpl(1)); + tableRow.put(new DegreeBearingImpl(60), Speed.NULL); + tableRow.put(new DegreeBearingImpl(75), Speed.NULL); + tableRow.put(new DegreeBearingImpl(90), Speed.NULL); + tableRow.put(new DegreeBearingImpl(110), Speed.NULL); + tableRow.put(new DegreeBearingImpl(120), Speed.NULL); + tableRow.put(new DegreeBearingImpl(135), Speed.NULL); + // tableRow.put(new DegreeBearingImpl(150), Speed.NULL); + tableRow.put(new DegreeBearingImpl(180), Speed.NULL); + speedTable.put(Speed.NULL, tableRow); + + tableRow = new TreeMap(PolarDiagramBase.bearingComparator); + tableRow.put(new DegreeBearingImpl(0), Speed.NULL); + tableRow.put(new DegreeBearingImpl(cutAngle), new KnotSpeedImpl(1)); + tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(5.09)); + tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(5.17)); + tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(5.25)); + tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(5.15)); + tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(5.1)); + tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(5.03)); + // tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(4.62)); + tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(2.5)); + speedTable.put(new KnotSpeedImpl(6), tableRow); + + tableRow = new TreeMap(PolarDiagramBase.bearingComparator); + tableRow.put(new DegreeBearingImpl(0), Speed.NULL); + tableRow.put(new DegreeBearingImpl(cutAngle), new KnotSpeedImpl(1)); + tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(5.38)); + tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(5.47)); + tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(5.57)); + tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(6.01)); + tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(6.23)); + tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(6.57)); + // tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(5.85)); + tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(3.35)); + speedTable.put(new KnotSpeedImpl(8), tableRow); + + tableRow = new TreeMap(PolarDiagramBase.bearingComparator); + tableRow.put(new DegreeBearingImpl(0), Speed.NULL); + tableRow.put(new DegreeBearingImpl(cutAngle), new KnotSpeedImpl(1)); + tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(5.67)); + tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(5.77)); + tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(5.88)); + tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(6.94)); + tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(7.47)); + tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(8.26)); + // tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(6.97)); + tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(4.25)); + speedTable.put(new KnotSpeedImpl(10), tableRow); + + tableRow = new TreeMap(PolarDiagramBase.bearingComparator); + tableRow.put(new DegreeBearingImpl(0), Speed.NULL); + tableRow.put(new DegreeBearingImpl(cutAngle), new KnotSpeedImpl(1)); + tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(6.06)); + tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(6.18)); + tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(6.30)); + tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(7.86)); + tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(8.64)); + tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(9.81)); + // tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(7.86)); + tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(5)); + speedTable.put(new KnotSpeedImpl(12), tableRow); + + tableRow = new TreeMap(PolarDiagramBase.bearingComparator); + tableRow.put(new DegreeBearingImpl(0), Speed.NULL); + tableRow.put(new DegreeBearingImpl(cutAngle), new KnotSpeedImpl(1)); + tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(6.55)); + tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(6.69)); + tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(6.83)); + tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(8.71)); + tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(9.66)); + tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(11.07)); + // tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(8.57)); + tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(5.75)); + speedTable.put(new KnotSpeedImpl(14), tableRow); + + tableRow = new TreeMap(PolarDiagramBase.bearingComparator); + tableRow.put(new DegreeBearingImpl(0), Speed.NULL); + tableRow.put(new DegreeBearingImpl(cutAngle), new KnotSpeedImpl(1)); + tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(7.05)); + tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(7.20)); + tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(7.35)); + tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(9.50)); + tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(10.58)); + tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(12.19)); + // tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(9.13)); + tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(6.5)); + speedTable.put(new KnotSpeedImpl(16), tableRow); + + tableRow = new TreeMap(PolarDiagramBase.bearingComparator); + tableRow.put(new DegreeBearingImpl(0), Speed.NULL); + tableRow.put(new DegreeBearingImpl(cutAngle), new KnotSpeedImpl(1)); + tableRow.put(new DegreeBearingImpl(60), new KnotSpeedImpl(7.04)); + tableRow.put(new DegreeBearingImpl(75), new KnotSpeedImpl(7.20)); + tableRow.put(new DegreeBearingImpl(90), new KnotSpeedImpl(7.35)); + tableRow.put(new DegreeBearingImpl(110), new KnotSpeedImpl(9.90)); + tableRow.put(new DegreeBearingImpl(120), new KnotSpeedImpl(11.18)); + tableRow.put(new DegreeBearingImpl(135), new KnotSpeedImpl(13.09)); + // tableRow.put(new DegreeBearingImpl(150), new KnotSpeedImpl(10.26)); + tableRow.put(new DegreeBearingImpl(180), new KnotSpeedImpl(7.5)); + speedTable.put(new KnotSpeedImpl(20), tableRow); + + NavigableMap beatAngles = new TreeMap(); + beatAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(44.0)); + beatAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(44.0)); + beatAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(46.5)); + beatAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(50.0)); + beatAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(52.0)); + beatAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(54.0)); + beatAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(55.0)); + beatAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(56.0)); + + double beatScale = 1.0; + NavigableMap beatSOG = new TreeMap(); + beatSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); + beatSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.00 * beatScale)); + beatSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(5.30 * beatScale)); + beatSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(5.60 * beatScale)); + beatSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(6.00 * beatScale)); + beatSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(6.50 * beatScale)); + beatSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(7.00 * beatScale)); + beatSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(7.00 * beatScale)); + + NavigableMap gybeAngles = new TreeMap(); + gybeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(141.0)); + gybeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(141.0)); + gybeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(141.0)); + gybeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(139.5)); + gybeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(137.5)); + gybeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(139.5)); + gybeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(142.5)); + gybeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(150.0)); + + NavigableMap gybeSOG = new TreeMap(); + gybeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); + gybeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.00)); + gybeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(6.70)); + gybeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(8.50)); + gybeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(10.00)); + gybeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(11.50)); + gybeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(13.00)); + gybeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(15.00)); + + for (Speed s : speedTable.keySet()) { + + if (beatAngles.containsKey(s) && !speedTable.get(s).containsKey(beatAngles.get(s))) + speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); + + if (gybeAngles.containsKey(s) && !speedTable.get(s).containsKey(gybeAngles.get(s))) + speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); + + } + + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java new file mode 100644 index 00000000000..28e70352693 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java @@ -0,0 +1,357 @@ +package com.sap.sailing.simulator.impl; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.NavigableMap; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import com.sap.sailing.domain.base.SpeedWithBearing; +import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; +import com.sap.sailing.domain.common.impl.RadianBearingImpl; +import com.sap.sailing.simulator.PolarDiagram; + +public class PolarDiagramBase implements PolarDiagram { + + // the current speed and direction of the wind + protected SpeedWithBearing wind = new KnotSpeedWithBearingImpl(6, new DegreeBearingImpl(180)); + + // the preferred direction of movement + // is used by optimalDirectionsUpwind() and optimialDirectionsDownwind() + protected Bearing targetDirection = new DegreeBearingImpl(0); + + protected NavigableMap> speedTable; + protected NavigableMap beatAngles; + protected NavigableMap gybeAngles; + protected NavigableMap beatSOG; + protected NavigableMap gybeSOG; + + // this constructor creates an instance with a hard-coded set of values + public PolarDiagramBase() { + // do nothing + } + + // a constructor that allows a generic set of parameters + public PolarDiagramBase(NavigableMap> speeds, + NavigableMap beats, NavigableMap gybes, + NavigableMap beatSOGs, NavigableMap gybeSOGs) { + + wind = new KnotSpeedWithBearingImpl(0, new DegreeBearingImpl(180)); + + speedTable = speeds; + beatAngles = beats; + gybeAngles = gybes; + beatSOG = beatSOGs; + gybeSOG = gybeSOGs; + + for (Speed s : speedTable.keySet()) { + + if (beatAngles.containsKey(s) && !speedTable.get(s).containsKey(beatAngles.get(s))) + speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); + + if (gybeAngles.containsKey(s) && !speedTable.get(s).containsKey(gybeAngles.get(s))) + speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); + + } + + } + + @Override + public SpeedWithBearing getWind() { + return wind; + } + + @Override + public void setWind(SpeedWithBearing newWind) { + wind = newWind; + } + + @Override + public SpeedWithBearing getSpeedAtBearing(Bearing bearing) { + + Bearing relativeBearing = wind.getBearing().reverse().getDifferenceTo(bearing); + if (relativeBearing.getDegrees() < 0) + relativeBearing = relativeBearing.getDifferenceTo(new DegreeBearingImpl(0)); + + Speed floorWind = speedTable.floorKey(wind); + Speed ceilingWind = speedTable.ceilingKey(wind); + + if (ceilingWind == null) { + ceilingWind = floorWind; + } + if (floorWind == null) { + floorWind = ceilingWind; + } + + NavigableMap floorSpeeds = speedTable.get(floorWind); + NavigableMap ceilingSpeeds = speedTable.get(ceilingWind); + + // Taylor estimations of order 1 + Speed floorSpeed1 = floorSpeeds.floorEntry(relativeBearing).getValue(); + Speed floorSpeed2 = floorSpeeds.ceilingEntry(relativeBearing).getValue(); + Bearing floorBearing1 = floorSpeeds.floorKey(relativeBearing); + Bearing floorBearing2 = floorSpeeds.ceilingKey(relativeBearing); + double floorSpeed; + if (floorSpeed1.equals(floorSpeed2)) { + floorSpeed = floorSpeed1.getKnots(); + } else { + floorSpeed = floorSpeed1.getKnots() + (relativeBearing.getRadians() - floorBearing1.getRadians()) + * (floorSpeed2.getKnots() - floorSpeed1.getKnots()) + / (floorBearing2.getRadians() - floorBearing1.getRadians()); + } + + Speed ceilingSpeed1 = ceilingSpeeds.floorEntry(relativeBearing).getValue(); + Speed ceilingSpeed2 = ceilingSpeeds.ceilingEntry(relativeBearing).getValue(); + Bearing ceilingBearing1 = ceilingSpeeds.floorKey(relativeBearing); + Bearing ceilingBearing2 = ceilingSpeeds.ceilingKey(relativeBearing); + double ceilingSpeed; + if (ceilingSpeed1.equals(ceilingSpeed2)) { + ceilingSpeed = ceilingSpeed1.getKnots(); + } else { + ceilingSpeed = ceilingSpeed1.getKnots() + (relativeBearing.getRadians() - ceilingBearing1.getRadians()) + * (ceilingSpeed2.getKnots() - ceilingSpeed1.getKnots()) + / (ceilingBearing2.getRadians() - ceilingBearing1.getRadians()); + } + + double speed; + if (floorWind.equals(ceilingWind)) { + speed = floorSpeed; + } else { + speed = floorSpeed + (wind.getKnots() - floorWind.getKnots()) * (ceilingSpeed - floorSpeed) + / (ceilingWind.getKnots() - floorWind.getKnots()); + } + if ((Math.abs(relativeBearing.getDegrees()) < 20) || (Math.abs(relativeBearing.getDegrees()) > 330)) { + speed = 0.1; + } + + return new KnotSpeedWithBearingImpl(speed, bearing); + } + + @Override + public Bearing[] optimalDirectionsUpwind() { + Bearing windBearing = wind.getBearing().reverse(); + Bearing estBeatAngleRight = null; + Bearing estBeatAngleLeft = null; + if (targetDirection.equals(new DegreeBearingImpl(0))) { + Bearing floorBeatAngle; + if (beatAngles.floorEntry(wind) == null) { + floorBeatAngle = beatAngles.ceilingEntry(wind).getValue(); + } else { + floorBeatAngle = beatAngles.floorEntry(wind).getValue(); + } + Bearing ceilingBeatAngle; + if (beatAngles.ceilingEntry(wind) == null) { + ceilingBeatAngle = beatAngles.floorEntry(wind).getValue(); + } else { + ceilingBeatAngle = beatAngles.ceilingEntry(wind).getValue(); + } + if (floorBeatAngle == null) + floorBeatAngle = new DegreeBearingImpl(0); + if (ceilingBeatAngle == null) + ceilingBeatAngle = new DegreeBearingImpl(0); + + Speed floorSpeed = beatAngles.floorKey(wind); + if (floorSpeed == null) { + floorSpeed = beatAngles.ceilingKey(wind); + } + Speed ceilingSpeed = beatAngles.ceilingKey(wind); + if (beatAngles.ceilingKey(wind) == null) { + ceilingSpeed = beatAngles.floorKey(wind); + } + double beatAngle; + if (floorSpeed.equals(ceilingSpeed)) { + beatAngle = floorBeatAngle.getRadians(); + } else { + beatAngle = floorBeatAngle.getRadians() + (wind.getKnots() - floorSpeed.getKnots()) + * (ceilingBeatAngle.getRadians() - floorBeatAngle.getRadians()) + / (ceilingSpeed.getKnots() - floorSpeed.getKnots()); + } + estBeatAngleRight = new RadianBearingImpl(+beatAngle); + estBeatAngleLeft = new RadianBearingImpl(-beatAngle); + return new Bearing[] { + // windBearing.add(estBeatAngle), + // windBearing.add(estBeatAngle.getDifferenceTo(windBearing)) + windBearing.add(estBeatAngleLeft), windBearing.add(estBeatAngleRight) }; + } else { + Set allKeys = new TreeSet(bearingComparator); + for (Double b = 0.0; b < 360.0; b += 5.0) + allKeys.add(new DegreeBearingImpl(b)); + Bearing _targetDirection = targetDirection; + setTargetDirection(new DegreeBearingImpl(0.0)); + allKeys.addAll(Arrays.asList(optimalDirectionsUpwind())); + allKeys.addAll(Arrays.asList(optimalDirectionsDownwind())); + setTargetDirection(_targetDirection); + Double maxSpeedRight = 0.0; + Double maxSpeedLeft = 0.0; + for (Bearing b : allKeys) { + if (b.getDifferenceTo(getWind().getBearing()).getDegrees() > 0) { + if (getSpeedAtBearing(b).getKnots() + * Math.cos(b.getDifferenceTo(getTargetDirection()).getRadians()) > maxSpeedRight) { + maxSpeedRight = getSpeedAtBearing(b).getKnots() + * Math.cos(b.getDifferenceTo(getTargetDirection()).getRadians()); + estBeatAngleRight = b; + } + } else if (getSpeedAtBearing(b).getKnots() + * Math.cos(b.getDifferenceTo(getTargetDirection()).getRadians()) > maxSpeedLeft) { + maxSpeedLeft = getSpeedAtBearing(b).getKnots() + * Math.cos(b.getDifferenceTo(getTargetDirection()).getRadians()); + estBeatAngleLeft = b; + } + } + return new Bearing[] { + // windBearing.add(estBeatAngle), + // windBearing.add(estBeatAngle.getDifferenceTo(windBearing)) + estBeatAngleLeft, estBeatAngleRight }; + } + + } + + @Override + public Bearing[] optimalDirectionsDownwind() { + + Bearing windBearing = wind.getBearing().reverse(); + Bearing estGybeAngleRight = null; + Bearing estGybeAngleLeft = null; + if (getTargetDirection().equals(new DegreeBearingImpl(0))) { + windBearing = wind.getBearing().reverse(); + estGybeAngleRight = null; + estGybeAngleLeft = null; + Bearing floorGybeAngle = gybeAngles.floorEntry(wind).getValue(); + Bearing ceilingGybeAngle = gybeAngles.ceilingEntry(wind).getValue(); + if (floorGybeAngle == null) + floorGybeAngle = new DegreeBearingImpl(0); + if (ceilingGybeAngle == null) + ceilingGybeAngle = new DegreeBearingImpl(0); + Speed floorSpeed = gybeAngles.floorKey(wind); + Speed ceilingSpeed = gybeAngles.ceilingKey(wind); + double gybeAngle; + if (floorSpeed.equals(ceilingSpeed)) { + gybeAngle = floorGybeAngle.getRadians(); + } else { + gybeAngle = floorGybeAngle.getRadians() + (wind.getKnots() - floorSpeed.getKnots()) + * (ceilingGybeAngle.getRadians() - floorGybeAngle.getRadians()) + / (ceilingSpeed.getKnots() - floorSpeed.getKnots()); + } + // Bearing estGybeAngle = new RadianBearingImpl(gybeAngle); + estGybeAngleRight = new RadianBearingImpl(+gybeAngle); + estGybeAngleLeft = new RadianBearingImpl(-gybeAngle); + return new Bearing[] { windBearing.add(estGybeAngleRight), windBearing.add(estGybeAngleLeft) }; + } else { + + Set allKeys = new TreeSet(bearingComparator); + for (Double b = 0.0; b < 360.0; b += 5.0) + allKeys.add(new DegreeBearingImpl(b)); + Bearing _targetDirection = targetDirection; + setTargetDirection(new DegreeBearingImpl(0.0)); + allKeys.addAll(Arrays.asList(optimalDirectionsUpwind())); + allKeys.addAll(Arrays.asList(optimalDirectionsDownwind())); + setTargetDirection(_targetDirection); + Double maxSpeedRight = 0.0; + Double maxSpeedLeft = 0.0; + for (Bearing b : allKeys) { + if (b.getDifferenceTo(getWind().getBearing()).getDegrees() > 0) { + if (getSpeedAtBearing(b).getKnots() + * Math.cos(b.getDifferenceTo(getTargetDirection().reverse()).getRadians()) > maxSpeedRight) { + maxSpeedRight = getSpeedAtBearing(b).getKnots() + * Math.cos(b.getDifferenceTo(getTargetDirection().reverse()).getRadians()); + estGybeAngleRight = b; + } + } else if (getSpeedAtBearing(b).getKnots() + * Math.cos(b.getDifferenceTo(getTargetDirection().reverse()).getRadians()) > maxSpeedLeft) { + maxSpeedLeft = getSpeedAtBearing(b).getKnots() + * Math.cos(b.getDifferenceTo(getTargetDirection().reverse()).getRadians()); + estGybeAngleLeft = b; + } + } + return new Bearing[] { + // windBearing.add(estBeatAngle), + // windBearing.add(estBeatAngle.getDifferenceTo(windBearing)) + estGybeAngleLeft, estGybeAngleRight }; + } + } + + // a Bearing Comparator useful in the creation of sorted sets of Bearing + public static Comparator bearingComparator = new Comparator() { + + @Override + public int compare(Bearing o1, Bearing o2) { + Double d1 = o1.getDegrees(); + if (d1 < 0) + d1 = 360 + d1; + Double d2 = o2.getDegrees(); + if (d2 < 0) + d2 = 360 + d2; + return d1.compareTo(d2); + } + + }; + + @Override + public long getTurnLoss() { + // TODO Auto-generated method stub + return 4000; + } + + // TO BE REVIEWED + // not sure I use the right terms and conventions + @Override + public WindSide getWindSide(Bearing bearing) { + WindSide windSide = null; + if (bearingComparator.compare(bearing, wind.getBearing().reverse()) > 0) + windSide = WindSide.LEFT; + if (bearingComparator.compare(bearing, wind.getBearing().reverse()) < 0) + windSide = WindSide.RIGHT; + if (bearing.equals(wind.getBearing())) + windSide = WindSide.OPPOSING; + if (bearing.equals(wind.getBearing().reverse())) + windSide = WindSide.FACING; + + return windSide; + } + + // returns a table of Bearing-Speed pairs with a bearingStep granularity + // for all Speeds in speedTable + @Override + public NavigableMap> polarDiagramPlot(Double bearingStep) { + + NavigableMap> table = new TreeMap>(); + Set extraBearings = new HashSet(); + + for (Speed s : speedTable.keySet()) { + setWind(new KnotSpeedWithBearingImpl(s.getKnots(), new DegreeBearingImpl(135))); + extraBearings.addAll(Arrays.asList(optimalDirectionsUpwind())); + extraBearings.addAll(Arrays.asList(optimalDirectionsDownwind())); + } + + for (Speed s : speedTable.keySet()) { + setWind(new KnotSpeedWithBearingImpl(s.getKnots(), new DegreeBearingImpl(135))); + NavigableMap currentTable = new TreeMap(bearingComparator); + table.put(s, currentTable); + + for (Double b = 0.0; b < 360.0; b += bearingStep) { + Bearing bearing = new DegreeBearingImpl(b); + currentTable.put(bearing, getSpeedAtBearing(bearing)); + } + for (Bearing extraBearing : extraBearings) + currentTable.put(extraBearing, getSpeedAtBearing(extraBearing)); + } + + return table; + } + + @Override + public Bearing getTargetDirection() { + return targetDirection; + } + + @Override + public void setTargetDirection(Bearing newTargetDirection) { + targetDirection = newTargetDirection; + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramImpl.java index 16a7a7abf9a..a420ee34bc6 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramImpl.java @@ -1,6 +1,5 @@ package com.sap.sailing.simulator.impl; -import java.util.Map; import java.util.NavigableMap; import com.sap.sailing.domain.base.SpeedWithBearing; @@ -12,78 +11,79 @@ import com.sap.sailing.simulator.PolarDiagram; public class PolarDiagramImpl implements PolarDiagram { - private SpeedWithBearing wind; - private Bearing northWest; - private Bearing northEast; - private Bearing southEast; - private Bearing southWest; - - public PolarDiagramImpl(double windSpeed) { - - setWind(new KilometersPerHourSpeedWithBearingImpl(windSpeed, new DegreeBearingImpl(0))); - - } - - @Override - public SpeedWithBearing getWind() { - return wind; - } + private SpeedWithBearing wind; + private Bearing northWest; + private Bearing northEast; + private Bearing southEast; + private Bearing southWest; - @Override - public void setWind(SpeedWithBearing newWind) { - wind = newWind; - northWest = wind.getBearing().add(new DegreeBearingImpl(330)); - northEast = wind.getBearing().add(new DegreeBearingImpl(30)); - southWest = wind.getBearing().add(new DegreeBearingImpl(210)); - southEast = wind.getBearing().add(new DegreeBearingImpl(150)); - } + public PolarDiagramImpl(double windSpeed) { - @Override - public SpeedWithBearing getSpeedAtBearing(Bearing bearing) { - if (bearing.equals(northEast) || bearing.equals(northWest)) return new KilometersPerHourSpeedWithBearingImpl(wind.getKilometersPerHour(), bearing); - if (bearing.equals(southEast) || bearing.equals(southWest)) return new KilometersPerHourSpeedWithBearingImpl(0.7*wind.getKilometersPerHour(), bearing); - return new KilometersPerHourSpeedWithBearingImpl(0.5*wind.getKilometersPerHour(), bearing); - } + setWind(new KilometersPerHourSpeedWithBearingImpl(windSpeed, new DegreeBearingImpl(0))); - @Override - public Bearing[] optimalDirectionsUpwind() { - return new Bearing[] {northWest, northEast}; - } + } - @Override - public Bearing[] optimalDirectionsDownwind() { - return new Bearing[] {southEast, southWest}; - } + @Override + public SpeedWithBearing getWind() { + return wind; + } - @Override - public long getTurnLoss() { - // TODO Auto-generated method stub - return 4000; - } + @Override + public void setWind(SpeedWithBearing newWind) { + wind = newWind; + northWest = wind.getBearing().add(new DegreeBearingImpl(330)); + northEast = wind.getBearing().add(new DegreeBearingImpl(30)); + southWest = wind.getBearing().add(new DegreeBearingImpl(210)); + southEast = wind.getBearing().add(new DegreeBearingImpl(150)); + } - @Override - public WindSide getWindSide(Bearing bearing) { - // TODO Auto-generated method stub - return null; - } + @Override + public SpeedWithBearing getSpeedAtBearing(Bearing bearing) { + if (bearing.equals(northEast) || bearing.equals(northWest)) + return new KilometersPerHourSpeedWithBearingImpl(wind.getKilometersPerHour(), bearing); + if (bearing.equals(southEast) || bearing.equals(southWest)) + return new KilometersPerHourSpeedWithBearingImpl(0.7 * wind.getKilometersPerHour(), bearing); + return new KilometersPerHourSpeedWithBearingImpl(0.5 * wind.getKilometersPerHour(), bearing); + } - @Override - public NavigableMap> polarDiagramPlot( - Double bearingStep) { - // TODO Auto-generated method stub - return null; - } + @Override + public Bearing[] optimalDirectionsUpwind() { + return new Bearing[] { northWest, northEast }; + } - @Override - public Bearing getTargetDirection() { - // TODO Auto-generated method stub - return null; - } + @Override + public Bearing[] optimalDirectionsDownwind() { + return new Bearing[] { southEast, southWest }; + } - @Override - public void setTargetDirection(Bearing newTargetDirection) { - // TODO Auto-generated method stub - - } + @Override + public long getTurnLoss() { + // TODO Auto-generated method stub + return 4000; + } + + @Override + public WindSide getWindSide(Bearing bearing) { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap> polarDiagramPlot(Double bearingStep) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Bearing getTargetDirection() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setTargetDirection(Bearing newTargetDirection) { + // TODO Auto-generated method stub + + } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularBoundary.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularBoundary.java index e322ef26b51..1b0e02545e2 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularBoundary.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularBoundary.java @@ -12,7 +12,7 @@ import com.sap.sailing.simulator.Boundary; public class RectangularBoundary implements Boundary { - private Position rcStart; // start position of race course + private Position rcStart; // start position of race course private Position rcEnd; // end position of race course private double lngScale; // scale of distances longitude : latitude private double[] nvVrt; // vertical normal vector with suitable length @@ -21,7 +21,7 @@ public class RectangularBoundary implements Boundary { private int hPoints; // number of horizontal steps private Position northWest; - private Position southEast; + //private Position southEast; private Position southWest; private Position northEast; @@ -30,7 +30,6 @@ public class RectangularBoundary implements Boundary { private Position appSouthWest; private Position appNorthEast; - private Bearing north; private Bearing south; private Bearing east; @@ -43,8 +42,8 @@ public class RectangularBoundary implements Boundary { public RectangularBoundary(Position p1, Position p2, double tlr) { - //System.out.println("Start:"+p1); - //System.out.println("End :"+p2); + // System.out.println("Start:"+p1); + // System.out.println("End :"+p2); rcStart = p1; rcEnd = p2; @@ -65,26 +64,26 @@ public class RectangularBoundary implements Boundary { // the following test shows, that numerics appear to be instable: // translating appNorthEast 2xEast should be appNorthWest, but numerically its NOT - //Position tstNorthEast = appNorthWest.translateGreatCircle(east, appHeight).translateGreatCircle(east, appHeight); - //appSouthEast = appNorthEast.translateGreatCircle(south, appHeight); - //appSouthWest = appNorthWest.translateGreatCircle(south, appHeight); + // Position tstNorthEast = appNorthWest.translateGreatCircle(east, appHeight).translateGreatCircle(east, + // appHeight); + // appSouthEast = appNorthEast.translateGreatCircle(south, appHeight); + // appSouthWest = appNorthWest.translateGreatCircle(south, appHeight); appSouthWest = p1.translateGreatCircle(west, appHeight); appSouthEast = p1.translateGreatCircle(east, appHeight); - //System.out.println("southWest:"+appSouthWest); - //System.out.println("southEast:"+appSouthEast); - //System.out.println("northWest:"+appNorthWest); - //System.out.println("northEast:"+appNorthEast); - //System.out.println("testnEast:"+tstNorthEast); - + // System.out.println("southWest:"+appSouthWest); + // System.out.println("southEast:"+appSouthEast); + // System.out.println("northWest:"+appNorthWest); + // System.out.println("northEast:"+appNorthEast); + // System.out.println("testnEast:"+tstNorthEast); Distance diag = appNorthWest.getDistance(appSouthEast); Bearing diag1 = appSouthEast.getBearingGreatCircle(appNorthWest); northWest = appNorthWest.translateGreatCircle(diag1, diag.scale(tolerance)); diag1 = diag1.reverse(); - southEast = appSouthEast.translateGreatCircle(diag1, diag.scale(tolerance)); + //southEast = appSouthEast.translateGreatCircle(diag1, diag.scale(tolerance)); Bearing diag2 = appSouthWest.getBearingGreatCircle(appNorthEast); northEast = appNorthEast.translateGreatCircle(diag2, diag.scale(tolerance)); diag2 = diag2.reverse(); @@ -121,11 +120,10 @@ public class RectangularBoundary implements Boundary { Distance northSouth = northProjection.getDistance(southProjection); Distance eastWest = eastProjection.getDistance(westProjection); - return - ( northSouth.compareTo(p.getDistance(northProjection)) >= 0 ) - && ( northSouth.compareTo(p.getDistance(southProjection)) >= 0 ) - && ( eastWest.compareTo(p.getDistance(eastProjection)) >= 0 ) - && ( eastWest.compareTo(p.getDistance(westProjection)) >=0 ); + return (northSouth.compareTo(p.getDistance(northProjection)) >= 0) + && (northSouth.compareTo(p.getDistance(southProjection)) >= 0) + && (eastWest.compareTo(p.getDistance(eastProjection)) >= 0) + && (eastWest.compareTo(p.getDistance(westProjection)) >= 0); } @@ -136,9 +134,9 @@ public class RectangularBoundary implements Boundary { this.hPoints = hPoints; double xscale = 1.5; - - double alat = (rcEnd.getLatDeg() + rcStart.getLatDeg())/2.; - lngScale = Math.cos(alat*Math.PI/180.); + + double alat = (rcEnd.getLatDeg() + rcStart.getLatDeg()) / 2.; + lngScale = Math.cos(alat * Math.PI / 180.); double[] dVrt = new double[2]; dVrt[0] = rcEnd.getLatDeg() - rcStart.getLatDeg(); @@ -146,143 +144,121 @@ public class RectangularBoundary implements Boundary { double[] dscVrt = new double[2]; dscVrt[0] = dVrt[0]; - dscVrt[1] = dVrt[1]*lngScale; + dscVrt[1] = dVrt[1] * lngScale; - double lscVrt = Math.sqrt(dscVrt[0]*dscVrt[0] + dscVrt[1]*dscVrt[1]); + double lscVrt = Math.sqrt(dscVrt[0] * dscVrt[0] + dscVrt[1] * dscVrt[1]); nvVrt = new double[2]; - nvVrt[0] = dscVrt[0]/lscVrt/lscVrt*(vPoints-1); - nvVrt[1] = dscVrt[1]/lscVrt/lscVrt*(vPoints-1); + nvVrt[0] = dscVrt[0] / lscVrt / lscVrt * (vPoints - 1); + nvVrt[1] = dscVrt[1] / lscVrt / lscVrt * (vPoints - 1); double[] nscHor = new double[2]; - nscHor[0] = -dscVrt[1]/lscVrt; - nscHor[1] = dscVrt[0]/lscVrt; + nscHor[0] = -dscVrt[1] / lscVrt; + nscHor[1] = dscVrt[0] / lscVrt; nvHor = new double[2]; - nvHor[0] = nscHor[0]/xscale/lscVrt*(hPoints-1); - nvHor[1] = nscHor[1]/xscale/lscVrt*(hPoints-1); + nvHor[0] = nscHor[0] / xscale / lscVrt * (hPoints - 1); + nvHor[1] = nscHor[1] / xscale / lscVrt * (hPoints - 1); double[] nHor = new double[2]; nHor[0] = nscHor[0]; - nHor[1] = nscHor[1]/lngScale; + nHor[1] = nscHor[1] / lngScale; - //System.out.println("LngScale:"+lngScale+", Diff.Vrt:"+dVrt); - Position[][] grid = new Position[vPoints][hPoints]; + // System.out.println("LngScale:"+lngScale+", Diff.Vrt:"+dVrt); + Position[][] grid = new Position[vPoints][hPoints]; Position pv; for (int i = 0; i < vPoints; i++) { - //System.out.print("nrow "+i+": "); + // System.out.print("nrow "+i+": "); if (i == 0) { pv = rcStart; - } else if (i == vPoints-1) { + } else if (i == vPoints - 1) { pv = rcEnd; } else { - pv = new DegreePosition(rcStart.getLatDeg() + i/(vPoints-1.0)*dVrt[0],rcStart.getLngDeg() + i/(vPoints-1.0)*dVrt[1]); + pv = new DegreePosition(rcStart.getLatDeg() + i / (vPoints - 1.0) * dVrt[0], rcStart.getLngDeg() + i + / (vPoints - 1.0) * dVrt[1]); } - int j=0; + int j = 0; // left side - while (j < hPoints/2) { - grid[i][j] = new DegreePosition(pv.getLatDeg() - ((hPoints-1)/2.-j)/(hPoints-1)*nHor[0]*xscale*lscVrt, pv.getLngDeg() - ((hPoints-1)/2.-j)/(hPoints-1)*nHor[1]*xscale*lscVrt); - //System.out.print(""+grid[i][j]+"("+((hPoints-1)/2.-j)+"), "); + while (j < hPoints / 2) { + grid[i][j] = new DegreePosition(pv.getLatDeg() - ((hPoints - 1) / 2. - j) / (hPoints - 1) * nHor[0] + * xscale * lscVrt, pv.getLngDeg() - ((hPoints - 1) / 2. - j) / (hPoints - 1) * nHor[1] * xscale + * lscVrt); + // System.out.print(""+grid[i][j]+"("+((hPoints-1)/2.-j)+"), "); j++; - } + } // middle - if (hPoints%2 == 1) { + if (hPoints % 2 == 1) { grid[i][j] = pv; - //System.out.print(""+grid[i][j]+", "); + // System.out.print(""+grid[i][j]+", "); j++; } // right side while (j < hPoints) { - grid[i][j] = new DegreePosition(pv.getLatDeg() + (j-(hPoints-1)/2.)/(hPoints-1)*nHor[0]*xscale*lscVrt, pv.getLngDeg() + (j-(hPoints-1)/2.)/(hPoints-1)*nHor[1]*xscale*lscVrt); - //System.out.print(""+grid[i][j]+"("+(j-(hPoints-1)/2.)+"), "); + grid[i][j] = new DegreePosition(pv.getLatDeg() + (j - (hPoints - 1) / 2.) / (hPoints - 1) * nHor[0] + * xscale * lscVrt, pv.getLngDeg() + (j - (hPoints - 1) / 2.) / (hPoints - 1) * nHor[1] * xscale + * lscVrt); + // System.out.print(""+grid[i][j]+"("+(j-(hPoints-1)/2.)+"), "); j++; - } - //System.out.println(); + } + // System.out.println(); } - /*System.out.println("Grid Index Test:"); - for (int i =0; i getGridIndex(Position x) { + public Pair getGridIndex(Position x) { double[] scX = new double[2]; - scX[0] = x.getLatDeg()-rcStart.getLatDeg(); - scX[1] = (x.getLngDeg()-rcStart.getLngDeg())*lngScale; + scX[0] = x.getLatDeg() - rcStart.getLatDeg(); + scX[1] = (x.getLngDeg() - rcStart.getLngDeg()) * lngScale; - double sPrd = scX[0]*nvHor[0]+scX[1]*nvHor[1]; + double sPrd = scX[0] * nvHor[0] + scX[1] * nvHor[1]; - int vIdx = Math.min(Math.max(0, (int) Math.round(scX[0]*nvVrt[0]+scX[1]*nvVrt[1])), vPoints-1); - int hIdx = Math.min(Math.max(0, (int) Math.round(sPrd+(hPoints-1)/2.)), hPoints-1); + int vIdx = Math.min(Math.max(0, (int) Math.round(scX[0] * nvVrt[0] + scX[1] * nvVrt[1])), vPoints - 1); + int hIdx = Math.min(Math.max(0, (int) Math.round(sPrd + (hPoints - 1) / 2.)), hPoints - 1); - //System.out.println("getGridIndex: "+vIdx+","+hIdx+"("+vPoints+","+hPoints+")"); - return new Pair(vIdx,hIdx); + // System.out.println("getGridIndex: "+vIdx+","+hIdx+"("+vPoints+","+hPoints+")"); + return new Pair(vIdx, hIdx); - } + } - - /* @Override - public List extractLattice(int hPoints, int vPoints) { - - Position[][] grid = extractGrid(hPoints, vPoints); - List lst = new ArrayList(); - for(Position[] line : grid) { - for(Position p : line) { - lst.add(p); - } - } - return lst; - } - - @Override - //may not return a rectangular lattice! - public List extractLattice(Distance hStep, Distance vStep) { - - Position startPoint = appSouthWest; - - Bearing vBearing = getNorth(); - Bearing hBearing = getEast(); - boolean hMode = true; - - List lst = new ArrayList(); - - Position current = startPoint; - lst.add(current); - Position next; - - while (true) { - - if (hMode) next = current.translateGreatCircle(hBearing, hStep); - else next = current.translateGreatCircle(vBearing, vStep); - - if (isWithinBoundaries(next)) { - current = next; - lst.add(current); - if (!hMode) { - hMode = true; - hBearing = hBearing.reverse(); - } - } - else { - if(hMode) hMode = false; - else break; - } - - } - - return lst; - } + /* + * @Override public List extractLattice(int hPoints, int vPoints) { + * + * Position[][] grid = extractGrid(hPoints, vPoints); List lst = new ArrayList(); for(Position[] + * line : grid) { for(Position p : line) { lst.add(p); } } return lst; } + * + * @Override //may not return a rectangular lattice! public List extractLattice(Distance hStep, Distance + * vStep) { + * + * Position startPoint = appSouthWest; + * + * Bearing vBearing = getNorth(); Bearing hBearing = getEast(); boolean hMode = true; + * + * List lst = new ArrayList(); + * + * Position current = startPoint; lst.add(current); Position next; + * + * while (true) { + * + * if (hMode) next = current.translateGreatCircle(hBearing, hStep); else next = + * current.translateGreatCircle(vBearing, vStep); + * + * if (isWithinBoundaries(next)) { current = next; lst.add(current); if (!hMode) { hMode = true; hBearing = + * hBearing.reverse(); } } else { if(hMode) hMode = false; else break; } + * + * } + * + * return lst; } */ @Override @@ -317,14 +293,15 @@ public class RectangularBoundary implements Boundary { @Override public Map getRelativeCoordinates(Position p) { - if (!isWithinBoundaries(p)) return null; + if (!isWithinBoundaries(p)) + return null; - Map map = new HashMap(); + Map map = new HashMap(); Position px = p.projectToLineThrough(appSouthWest, getEast()); Position py = p.projectToLineThrough(appSouthWest, getNorth()); - map.put("X", px.getDistance(appSouthWest).getMeters()/getWidth().getMeters()); - map.put("Y", py.getDistance(appSouthWest).getMeters()/getHeight().getMeters()); + map.put("X", px.getDistance(appSouthWest).getMeters() / getWidth().getMeters()); + map.put("Y", py.getDistance(appSouthWest).getMeters() / getHeight().getMeters()); return map; } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java index 07185e72786..200b239f123 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java @@ -1,32 +1,14 @@ package com.sap.sailing.simulator.impl; -import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Vector; -import java.util.logging.Logger; -import com.sap.sailing.domain.base.SpeedWithBearing; -import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; -import com.sap.sailing.domain.common.Bearing; -import com.sap.sailing.domain.common.Distance; -import com.sap.sailing.domain.common.Position; -import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.TimePoint; -import com.sap.sailing.domain.common.impl.Util.Pair; -import com.sap.sailing.domain.tracking.Wind; -import com.sap.sailing.simulator.Boundary; import com.sap.sailing.simulator.Path; -import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.PathGenerator; import com.sap.sailing.simulator.SailingSimulator; import com.sap.sailing.simulator.SimulationParameters; -import com.sap.sailing.simulator.TimedPosition; import com.sap.sailing.simulator.TimedPositionWithSpeed; -import com.sap.sailing.simulator.WindField; -import com.sap.sailing.simulator.WindFieldGenerator; public class SailingSimulatorImpl implements SailingSimulator { @@ -46,1036 +28,83 @@ public class SailingSimulatorImpl implements SailingSimulator { return simulationParameters; } - @Override - public Path getOptimumPath() { - - // calls either createDummy or createHeuristic() - // use getAllPaths() instead - - return createOneTurnLeft(); - } - - private static Logger logger = Logger.getLogger("com.sap.sailing"); - - private Path createDummy() { - Boundary boundary = simulationParameters.getBoundaries(); - WindFieldGenerator wf = simulationParameters.getWindField(); - PolarDiagram pd = simulationParameters.getBoatPolarDiagram(); - Position start = simulationParameters.getCourse().get(0); - Position end = simulationParameters.getCourse().get(1); - TimePoint startTime = wf.getStartTime();// new MillisecondsTimePoint(0); - List lst = new ArrayList(); - - pd.setWind(wf.getWind(new TimedPositionWithSpeedImpl(startTime, start, null))); - Bearing direct = start.getBearingGreatCircle(end); - TimedPositionWithSpeed p1 = new TimedPositionWithSpeedImpl(startTime, start, pd.getSpeedAtBearing(direct)); - TimedPositionWithSpeed p2 = new TimedPositionWithSpeedImpl(new MillisecondsTimePoint( - startTime.asMillis() + 5 * 30 * 1000), end, null); - lst.add(p1); - lst.add(p2); - - return new PathImpl(lst, wf); - } - - private Path createHeuristic() { - Boundary boundary = simulationParameters.getBoundaries(); - WindFieldGenerator wf = simulationParameters.getWindField(); - PolarDiagram pd = simulationParameters.getBoatPolarDiagram(); - Position start = simulationParameters.getCourse().get(0); - Position end = simulationParameters.getCourse().get(1); - TimePoint startTime = wf.getStartTime();// new MillisecondsTimePoint(0); - List lst = new ArrayList(); - - Position currentPosition = start; - TimePoint currentTime = startTime; - - int prevDirection = -1; - long turnloss = 4000; // time lost when doing a turn - long windpred = 3000; // time used to predict wind, i.e. hypothetical sailors prediction - TimePoint directTime; - TimePoint leftTime; - TimePoint rightTime; - - Wind wndStart = wf.getWind(new TimedPositionWithSpeedImpl(startTime, start, null)); - logger.fine("wndStart speed:" + wndStart.getKnots() + " angle:" + wndStart.getBearing().getDegrees()); - pd.setWind(wndStart); - Bearing bearStart = currentPosition.getBearingGreatCircle(end); - SpeedWithBearing spdStart = pd.getSpeedAtBearing(bearStart); - lst.add(new TimedPositionWithSpeedImpl(startTime, start, spdStart)); - long timeStep = wf.getTimeStep().asMillis(); - logger.info("Time step :" + timeStep); - // while there is more than 5% of the total distance to the finish - while (currentPosition.getDistance(end).compareTo(start.getDistance(end).scale(0.05)) > 0) { - - // TimePoint nextTime = new MillisecondsTimePoint(currentTime.asMillis() + 30000); - - long nextTimeVal = currentTime.asMillis() + timeStep;// + 30000; - TimePoint nextTime = new MillisecondsTimePoint(nextTimeVal); - - Wind cWind = wf.getWind(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); - logger.fine("cWind speed:" + cWind.getKnots() + " angle:" + cWind.getBearing().getDegrees()); - pd.setWind(cWind); - - // get wind of direction - Bearing wLft = pd.optimalDirectionsUpwind()[0]; - Bearing wRght = pd.optimalDirectionsUpwind()[1]; - Bearing wDirect = currentPosition.getBearingGreatCircle(end); - - SpeedWithBearing sWDirect = pd.getSpeedAtBearing(wDirect); - SpeedWithBearing sWLft = pd.getSpeedAtBearing(wLft); - SpeedWithBearing sWRght = pd.getSpeedAtBearing(wRght); - logger.fine("left boat speed:" + sWLft.getKnots() + " angle:" + sWLft.getBearing().getDegrees() - + " right boat speed:" + sWRght.getKnots() + " angle:" + sWRght.getBearing().getDegrees()); - - TimePoint wTime = new MillisecondsTimePoint(currentTime.asMillis() + windpred); - Position pWDirect = sWDirect.travelTo(currentPosition, currentTime, wTime); - Position pWLft = sWLft.travelTo(currentPosition, currentTime, wTime); - Position pWRght = sWRght.travelTo(currentPosition, currentTime, wTime); - - logger.fine("current Pos:" + currentPosition.getLatDeg() + "," + currentPosition.getLngDeg()); - logger.fine("left Pos:" + pWLft.getLatDeg() + "," + pWLft.getLngDeg()); - logger.fine("right Pos:" + pWRght.getLatDeg() + "," + pWRght.getLngDeg()); - - // calculate next step - Wind dWind = wf.getWind(new TimedPositionWithSpeedImpl(currentTime, pWDirect, null)); - logger.fine("dWind speed:" + dWind.getKnots() + " angle:" + dWind.getBearing().getDegrees()); - pd.setWind(dWind); - Bearing direct = currentPosition.getBearingGreatCircle(end); - SpeedWithBearing sdirect = pd.getSpeedAtBearing(direct); - - Wind lWind = wf.getWind(new TimedPositionWithSpeedImpl(currentTime, pWLft, null)); - logger.fine("lWind speed:" + lWind.getKnots() + " angle:" + lWind.getBearing().getDegrees()); - pd.setWind(lWind); - Bearing lft = pd.optimalDirectionsUpwind()[0]; - SpeedWithBearing slft = pd.getSpeedAtBearing(lft); - - Wind rWind = wf.getWind(new TimedPositionWithSpeedImpl(currentTime, pWRght, null)); - logger.fine("rWind speed:" + rWind.getKnots() + " angle:" + rWind.getBearing().getDegrees()); - pd.setWind(rWind); - Bearing rght = pd.optimalDirectionsUpwind()[1]; - SpeedWithBearing srght = pd.getSpeedAtBearing(rght); - - logger.fine("left boat speed:" + slft.getKnots() + " angle:" + slft.getBearing().getDegrees() - + " right boat speed:" + srght.getKnots() + " angle:" + srght.getBearing().getDegrees()); - - if (prevDirection == 0) { - directTime = new MillisecondsTimePoint(nextTimeVal); - leftTime = new MillisecondsTimePoint(nextTimeVal - turnloss); - rightTime = new MillisecondsTimePoint(nextTimeVal - turnloss); - } else if (prevDirection == 1) { - directTime = new MillisecondsTimePoint(nextTimeVal - turnloss); - leftTime = new MillisecondsTimePoint(nextTimeVal); - rightTime = new MillisecondsTimePoint(nextTimeVal - turnloss); - } else if (prevDirection == 2) { - directTime = new MillisecondsTimePoint(nextTimeVal - turnloss); - leftTime = new MillisecondsTimePoint(nextTimeVal - turnloss); - rightTime = new MillisecondsTimePoint(nextTimeVal); - } else { - directTime = new MillisecondsTimePoint(nextTimeVal); - leftTime = new MillisecondsTimePoint(nextTimeVal); - rightTime = new MillisecondsTimePoint(nextTimeVal); - } - - Position pdirect = sdirect.travelTo(currentPosition, currentTime, directTime); - Position plft = slft.travelTo(currentPosition, currentTime, leftTime); - Position prght = srght.travelTo(currentPosition, currentTime, rightTime); - - Distance ddirect = pdirect.getDistance(end); - Distance dlft = plft.getDistance(end); - Distance drght = prght.getDistance(end); - - if (ddirect.compareTo(dlft) <= 0 && ddirect.compareTo(drght) <= 0) { - lst.add(new TimedPositionWithSpeedImpl(nextTime, pdirect, sdirect)); - currentPosition = pdirect; - prevDirection = 0; - } - - if (dlft.compareTo(ddirect) <= 0 && dlft.compareTo(drght) <= 0) { - lst.add(new TimedPositionWithSpeedImpl(nextTime, plft, slft)); - currentPosition = plft; - prevDirection = 1; - } - - if (drght.compareTo(dlft) <= 0 && drght.compareTo(ddirect) <= 0) { - lst.add(new TimedPositionWithSpeedImpl(nextTime, prght, srght)); - currentPosition = prght; - prevDirection = 2; - } - currentTime = nextTime; - - } - - long nextTimeVal = currentTime.asMillis() + timeStep;// 30000; - TimePoint nextTime = new MillisecondsTimePoint(nextTimeVal); - - Wind wndEnd = wf.getWind(new TimedPositionWithSpeedImpl(nextTime, end, null)); - logger.fine("wndEnd speed:" + wndEnd.getKnots() + " angle:" + wndEnd.getBearing().getDegrees()); - pd.setWind(wndEnd); - Bearing bearEnd = currentPosition.getBearingGreatCircle(end); - SpeedWithBearing spdEnd = pd.getSpeedAtBearing(bearEnd); - lst.add(new TimedPositionWithSpeedImpl(nextTime, end, spdEnd)); - - return new PathImpl(lst, wf); - } - - // - // dynamic programming approach with forward iteration along start-end line - // - - // find minimum duration in alternate steps from h1=hidx to the calling h2 - private durP findMinDur(ArrayList alldur) { - durP mindur = alldur.get(0); - for (int i = 0; i < alldur.size(); i++) { - if (alldur.get(i).duration < mindur.duration) { - mindur = alldur.get(i); - } - } - - return mindur; - } - - // calculate duration of step from position p1 to position p2 at time curtime from side of wind side1 - private durP calcDuration(TimePoint curtime, int side1, Position p1, Position p2, WindField windField, - PolarDiagram polarDiagram) { - - // set turnloss to be approximately 4sec (until polardiagram has getTurnloss() method) - long turnloss = 4000; - - // System.out.println("p1: "+p1+" p2:"+p2); - - // take wind of starting position p1 at current time - TimedPosition currentTimedPosition = new TimedPositionImpl(curtime, p1); - SpeedWithBearing currentWind = windField.getWind(currentTimedPosition); - // alternate approach: take wind of target position p2 at current time - // TimedPosition windTimedPosition = new TimedPositionImpl(curtime, p2); - // SpeedWithBearing currentWind = windField.getWind(windTimedPosition); - - // System.out.println("wind: "+currentWind.getKnots()+", "+currentWind.getBearing().getDegrees()+"°"); - - // set polar diagram to current wind - polarDiagram.setWind(currentWind); - - // calculate bearing, distance and speed from p1 to p2 - Bearing bearingToP = p1.getBearingGreatCircle(p2); - Distance distanceToP = p1.getDistance(p2); - Speed speedToP = polarDiagram.getSpeedAtBearing(bearingToP); - // System.out.println("p1 to p2: angle: "+bearingToP.getDegrees()+"° dist: "+distanceToP.getMeters()+"m speed: "+speedToP.getMetersPerSecond()+"m/s"); - - // add time delta for sailing from p1 to p2 to current time - double deltat; - if (speedToP.getMetersPerSecond() <= 0.1) { - deltat = 86400; // set a high value for small times - } else { - deltat = (distanceToP.getMeters() / speedToP.getMetersPerSecond()); - } - long timeToP = curtime.asMillis() + (long) (deltat) * 1000; - // System.out.println("time: "+deltat/60.+"min"); - - // identify turn based on change of sign of relative bearing of p1 to p2 towards the wind - Bearing relativeBearing = currentWind.getBearing().reverse().getDifferenceTo(bearingToP); - int side2; - if (relativeBearing.getDegrees() == 0) { - side2 = 0; - } else { - side2 = (int) Math.signum(relativeBearing.getDegrees()); - } - if ((side1 != 0)&&(side2 != 0)&&(side1 == -side2)) { - timeToP = timeToP + turnloss; - } - - // return arrival time at p2 and wind side at p2 as durP instance - durP durToP = new durP(timeToP, side2, 0, 0); - - return durToP; - } - - // duration parameters: duration in millis, side of the wind, referencing horizontal grid column hidx, - // and path index pidx (pidx currently unused, maybe used for structuring in groups of equivalent time, see - // prototype R-script) - class durP { - public durP(long dur, int sid, int h, int p) { - duration = dur; - side = sid; - hidx = h; - pidx = p; - } - - long duration; - int side; - int hidx; - int pidx; - } - - // location parameters: horizontal grid index idx, duration dur to reach this point (within path) - class locP { - public locP(int i, long d) { - idx = i; - dur = d; - } - - int idx; - long dur; - } - - // dynamic programming forward iteration main algorithm (see also R-script prototype) - private Path createDP() { - - // retrieve simulation parameters - Boundary boundary = new RectangularBoundary(simulationParameters.getCourse().get(0), simulationParameters - .getCourse().get(1));// simulationParameters.getBoundaries(); - WindFieldGenerator windField = simulationParameters.getWindField(); - PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); - Position start = simulationParameters.getCourse().get(0); - Position end = simulationParameters.getCourse().get(1); - TimePoint startTime = windField.getStartTime(); - - // the solution path - LinkedList lst = new LinkedList(); - - // initiate grid: since performance is good, make it somewhat larger than what we do with dijkstra - int sgridv = (int) Math.round(1.5 * simulationParameters.getProperty("Djikstra.gridv[int]").intValue()); // number - // of - // vertical - // grid - // steps - int sgridh = (int) Math.round(1.5 * simulationParameters.getProperty("Djikstra.gridh[int]").intValue()); // number - // of - // horizontal - // grid - // steps - - // make horizontal grid size uneven, to have an index for the middle line - if (sgridh % 2 == 0) { - sgridh++; - } - - // generate grid positions using sgridh and sgridv - Position[][] sailGrid = boundary.extractGrid(sgridh, sgridv); - - // optimization grid indexes: - // vertical steps: 0, ..., v-1 - // horizontal steps: -gridh, ..., 0, ..., +gridh - int gridv = sgridv; - int gridh = sgridh / 2; - - ArrayList alldur; - ArrayList>> paths = new ArrayList>>(); - ArrayList duras = new ArrayList(); - - // in each vertical step, paths having the same minimum duration are kept - double mintol = 1.0; // threshold of closeness to realy minimum duration for keeping paths - int maxeqpaths = 2; // maximum nmber of equal paths to keep [currently set to low values, to save Java heap - // memory] - - // loop over vertical steps - for (int idxv = 0; idxv < (gridv - 1); idxv++) { - - // System.out.println(" vstep: "+idxv); - - ArrayList>> pathsnew = new ArrayList>>(); - ArrayList durasnew = new ArrayList(); - - // get start boundary correct - int range1; - if (idxv == 0) { - range1 = 0; - duras.add(new durP(startTime.asMillis(), 0, 0, 0)); - } else { - range1 = gridh; - } - - // get end boundary correct - int range2; - if (idxv == (gridv - 2)) { - range2 = 0; - } else { - range2 = gridh; - } - - // target loop: idxh2 - for (int idxh2 = -range2; idxh2 <= range2; idxh2++) { - - alldur = new ArrayList(); - - // origin loop: idxh1 - for (int idxh1 = -range1; idxh1 <= range1; idxh1++) { - - // calculate the time to go from [idxv,idxh1] to [idxv+1,idxh2] - durP durationh1h2 = calcDuration(new MillisecondsTimePoint(duras.get(idxh1 + range1).duration), - duras.get(idxh1 + range1).side, sailGrid[idxv][idxh1 + gridh], sailGrid[idxv + 1][idxh2 - + gridh], windField, polarDiagram); - - // *forbidden* angles - if (durationh1h2.duration < 0) { - continue; - } - - // remember durations of current vertical step in alldur - durationh1h2.hidx = idxh1 + range1; - alldur.add(durationh1h2); - - } // horizontal origin loop: idxh1 - - if (idxv > 0) { // if not at start of the grid - - if (idxv != (gridv - 2)) { // if not at end of the grid - - // find minimum duration in alldur - durP mindur = findMinDur(alldur); - - // remember minimum duration for reaching [idxv,idxh2] - durP tdur = new durP(mindur.duration, mindur.side, idxh2 + gridh, 0); - durasnew.add(tdur); - - // keep all (see mintol, maxeqpaths) paths with minimum duration for reaching [idxv,idxh2] - int k = 0; - for (int j = 0; j < alldur.size(); j++) { - - if ((alldur.get(j).duration <= mintol * mindur.duration) && (k < maxeqpaths)) { - ArrayList> npaths; - if ((pathsnew.size() > 0) && (pathsnew.size() == (idxh2 + gridh + 1))) { - npaths = pathsnew.get(idxh2 + gridh); - } else { - npaths = new ArrayList>(); - pathsnew.add(npaths); - } - ArrayList> tpaths = paths.get(alldur.get(j).hidx); - for (int i = 0; i < tpaths.size(); i++) { - Vector tpath; - tpath = (Vector) (tpaths.get(i)).clone(); - tpath.add(new locP(idxh2, mindur.duration)); - npaths.add(tpath); - } - k++; - } - - } - - } else { - - // find minimum in alldur - durP mindur = findMinDur(alldur); - - // remember minimum duration for reaching [idxv,idxh2] - durP tdur = new durP(mindur.duration, mindur.side, 0, 0); - durasnew.add(tdur); - - // keep all (see mintol, maxeqpaths) paths with minimum duration for reaching [idxv,idxh2] - int k = 0; - for (int j = 0; j < alldur.size(); j++) { - - if ((alldur.get(j).duration <= mintol * mindur.duration) && (k < maxeqpaths)) { - ArrayList> npaths; - if ((pathsnew.size() > 0) && (pathsnew.size() == (idxh2 + range2 + 1))) { - npaths = pathsnew.get(idxh2 + range2); - } else { - npaths = new ArrayList>(); - pathsnew.add(npaths); - } - ArrayList> tpaths = paths.get(alldur.get(j).hidx); - for (int i = 0; i < tpaths.size(); i++) { - Vector tpath; - tpath = (Vector) (tpaths.get(i)).clone(); - tpath.add(new locP(0, mindur.duration)); - npaths.add(tpath); - } - k++; - } - - } - } - - } else { // start of grid - - Vector tpath = new Vector(); - tpath.add(new locP(0, startTime.asMillis())); - ArrayList> tpaths = new ArrayList>(); - tpaths.add(tpath); - pathsnew.add(tpaths); - durP tdur = calcDuration(startTime, 0, sailGrid[0][gridh], sailGrid[1][idxh2 + gridh], windField, - polarDiagram); - tdur.hidx = idxh2 + gridh; - durasnew.add(tdur); - tpath.add(new locP(idxh2, tdur.duration)); - } - - } // horizontal target loop: idxh2 - - paths = pathsnew; - duras = durasnew; - - /* - * if (idxv == (gridv-2)) { System.out.print(" optimal path:"); for(int i=0; i<1;i++) {//paths.size(); - * i++) { //System.out.print(""+duras.get(i).duration+" - "); for(int j=0; j<1;j++) {//paths.get(i).size(); - * j++) { int kmax = paths.get(i).get(j).size(); - * System.out.print(""+((paths.get(i).get(j).get(kmax-1).dur-paths - * .get(i).get(j).get(0).dur)/1000./60.)+"min: "); for(int k=0;k lst = new LinkedList(); - - // initiate grid - int gridv = simulationParameters.getProperty("Djikstra.gridv[int]").intValue(); // number of vertical grid steps - int gridh = simulationParameters.getProperty("Djikstra.gridh[int]").intValue(); // number of horizontal grid - // steps - Position[][] sailGrid = boundary.extractGrid(gridh, gridv); - - // create adjacency graph including start and end - Map> graph = new HashMap>(); - graph.put(start, Arrays.asList(sailGrid[1])); - for (int i = 1; i < gridv - 2; i++) { - for (Position p : sailGrid[i]) { - graph.put(p, Arrays.asList(sailGrid[i + 1])); - } - } - for (Position p : sailGrid[gridv - 2]) { - graph.put(p, Arrays.asList(end)); - } - - /* - * //create backwards adjacency graph, required to reconstruct the optimal path Map> - * backGraph = new HashMap>(); backGraph.put(end, Arrays.asList(sailGrid[gridv-2])); - * for(int i = gridv-2; i > 1; i--) { for(Position p: sailGrid[i]) { backGraph.put(p, - * Arrays.asList(sailGrid[i-1])); } } for(Position p : sailGrid[1]) { backGraph.put(p, Arrays.asList(start)); } - */ - - // create tentative distance matrix - // additional to tentative distances, the matrix also contains the root of each position - // that can be if unavailable - Map> tentativeDistances = new HashMap>(); - for (Position p : graph.keySet()) { - tentativeDistances.put(p, new Pair(Long.MAX_VALUE, null)); - } - tentativeDistances.put(start, new Pair(startTime.asMillis(), null)); - tentativeDistances.put(end, new Pair(Long.MAX_VALUE, null)); - - // create set of unvisited nodes - List unvisited = new ArrayList(graph.keySet()); - unvisited.add(end); - - // set the initial node as current - Position currentPosition = start; - TimePoint currentTime = startTime; - Bearing previousBearing = null; - - // search loop - // ends when the end is visited - while (currentPosition != end) { - // set the polar diagram to the wind at the current position and time - TimedPosition currentTimedPosition = new TimedPositionImpl(currentTime, currentPosition); - SpeedWithBearing currentWind = windField.getWind(currentTimedPosition); - polarDiagram.setWind(currentWind); - - // compute the tentative distance to all the unvisited neighbours of the current node - // and replace it in the matrix if is smaller than the previous one - List unvisitedNeighbours = new LinkedList(graph.get(currentPosition)); - unvisitedNeighbours.retainAll(unvisited); - for (Position p : unvisitedNeighbours) { - Bearing bearingToP = currentPosition.getBearingGreatCircle(p); - Distance distanceToP = currentPosition.getDistance(p); - Speed speedToP = polarDiagram.getSpeedAtBearing(bearingToP); - // multiplied by 1000 to have milliseconds - Long timeToP = (long) (1000 * (distanceToP.getMeters() / speedToP.getMetersPerSecond())); - /* - * if (previousBearing != null) { Bearing windBearingFrom = currentWind.getBearing().reverse(); if( - * (PolarDiagram49.bearingComparator.compare(bearingToP, windBearingFrom) > 0) && - * (PolarDiagram49.bearingComparator.compare(previousBearing, windBearingFrom) < 0) ) timeToP = timeToP - * + 4000; if( (PolarDiagram49.bearingComparator.compare(bearingToP, windBearingFrom) < 0) && - * (PolarDiagram49.bearingComparator.compare(previousBearing, windBearingFrom) > 0) ) timeToP = timeToP - * + 4000; } - */ - - Long tentativeDistanceToP = currentTime.asMillis() + timeToP; - if (tentativeDistanceToP < tentativeDistances.get(p).getA()) { - tentativeDistances.put(p, new Pair(tentativeDistanceToP, currentPosition)); - } - } - - // mark current node as visited - unvisited.remove(currentPosition); - - // select the unvisited node with the smallest tentative distance - // and set it as current - Long minTentativeDistance = Long.MAX_VALUE; - for (Position p : unvisited) { - if (tentativeDistances.get(p).getA() < minTentativeDistance) { - currentPosition = p; - minTentativeDistance = tentativeDistances.get(p).getA(); - previousBearing = tentativeDistances.get(p).getB().getBearingGreatCircle(currentPosition); - currentTime = new MillisecondsTimePoint(minTentativeDistance); - } - - } - } - // I need to add the end point to the distances matrix - // tentativeDistances.put(end,currentTime.asMillis()); - - // at this point currentPosition = end - // currentTime = total duration of the course - - // reconstruct the optimal path by going from start to end - /* - * while(currentPosition != start) { TimedPositionWithSpeed currentTimedPositionWithSpeed = new - * TimedPositionWithSpeedImpl(currentTime, currentPosition, null ); lst.addFirst(currentTimedPositionWithSpeed); - * System.out.println(boundary.getGridIndex(currentTimedPositionWithSpeed.getPosition())); List - * currentPredecessors = backGraph.get(currentPosition); Long minTime = Long.MAX_VALUE; for(Position p : - * currentPredecessors) { if(tentativeDistances.get(p) < minTime) { minTime = tentativeDistances.get(p); - * currentPosition = p; currentTime = new MillisecondsTimePoint(minTime); } } } //I need to add the first point - * to the path lst.addFirst(new TimedPositionWithSpeedImpl(startTime, start, null)); - */ - while (currentPosition != null) { - currentTime = new MillisecondsTimePoint(tentativeDistances.get(currentPosition).getA()); - SpeedWithBearing windAtPoint = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); - TimedPositionWithSpeed current = new TimedPositionWithSpeedImpl(currentTime, currentPosition, windAtPoint); - lst.addFirst(current); - currentPosition = tentativeDistances.get(currentPosition).getB(); - } - - return new PathImpl(lst, windField); - } - - private Path createOneTurnLeft() { - // retrieve simulation parameters - Boundary boundary = new RectangularBoundary(simulationParameters.getCourse().get(0), simulationParameters - .getCourse().get(1));// simulationParameters.getBoundaries(); - WindFieldGenerator windField = simulationParameters.getWindField(); - PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); - Position start = simulationParameters.getCourse().get(0); - Position end = simulationParameters.getCourse().get(1); - TimePoint startTime = windField.getStartTime();// new MillisecondsTimePoint(0); - - Distance courseLength = start.getDistance(end); - - // the solution path - LinkedList lst = null; - // the minimal one-turn time - - Long timeResolution = 120000L; - boolean turned = true; - boolean outOfBounds = false; - Long minTurn = Long.MAX_VALUE; - int turningStep = 0; - - while (turned) { - LinkedList tempLst = new LinkedList(); - Position currentPosition = start; - TimePoint currentTime = startTime; - turned = false; - outOfBounds = false; - turningStep++; - int currentStep = 0; - - while (true) { - - SpeedWithBearing currWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); - polarDiagram.setWind(currWind); - TimePoint nextTime = new MillisecondsTimePoint(currentTime.asMillis() + timeResolution); - tempLst.add(new TimedPositionWithSpeedImpl(currentTime, currentPosition, currWind)); - - if (currentStep >= turningStep) { - turned = true; - nextTime = new MillisecondsTimePoint(nextTime.asMillis() + polarDiagram.getTurnLoss()); - } - - if (!turned) { - Bearing direction = polarDiagram.optimalDirectionsUpwind()[0]; - // for(Bearing b: polarDiagram.optimalDirectionsUpwind()) - // if(polarDiagram.getWindSide(b) == PolarDiagram.WindSide.LEFT) - // direction = b; - SpeedWithBearing currSpeed = polarDiagram.getSpeedAtBearing(direction); - currentPosition = currSpeed.travelTo(currentPosition, currentTime, nextTime); - } - if (turned) { - Bearing direction1 = currentPosition.getBearingGreatCircle(end); - Bearing direction2 = polarDiagram.optimalDirectionsUpwind()[1]; - // for(Bearing b: polarDiagram.optimalDirectionsUpwind()) - // if(polarDiagram.getWindSide(b) == PolarDiagram.WindSide.RIGHT) - // direction2 = b; - SpeedWithBearing currSpeed1 = polarDiagram.getSpeedAtBearing(direction1); - SpeedWithBearing currSpeed2 = polarDiagram.getSpeedAtBearing(direction2); - Position nextPosition1 = currSpeed1.travelTo(currentPosition, currentTime, nextTime); - Position nextPosition2 = currSpeed2.travelTo(currentPosition, currentTime, nextTime); - // nextPosition2. - if (nextPosition1.getDistance(end).compareTo(nextPosition2.getDistance(end)) < 0 - && Math.abs(direction1.getDifferenceTo(direction2).getDegrees()) < 45.0) - currentPosition = nextPosition1; - else - currentPosition = nextPosition2; - } - - currentStep++; - // System.out.println(currentStep + "/" + turningStep + "/" + turned); - currentTime = nextTime; - - if (currentTime.asMillis() > minTurn) { - // System.out.println("out of time"); - break; - } - if (!boundary.isWithinBoundaries(currentPosition)) { - outOfBounds = true; - // System.out.println("out of bounds"); - break; - } - if (currentPosition.getDistance(end).compareTo(courseLength.scale(0.005)) < 0) { - minTurn = currentTime.asMillis(); - lst = new LinkedList(tempLst); - Bearing directionToEnd = currentPosition.getBearingGreatCircle(end); - SpeedWithBearing crtWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); - polarDiagram.setWind(crtWind); - Speed speedToEnd = polarDiagram.getSpeedAtBearing(directionToEnd); - Distance distanceToEnd = currentPosition.getDistance(end); - Long timeToEnd = (long) (1000.0 * distanceToEnd.getMeters() / speedToEnd.getMetersPerSecond()); - TimePoint endTime = new MillisecondsTimePoint(currentTime.asMillis() + timeToEnd); - lst.addLast(new TimedPositionWithSpeedImpl(endTime, end, crtWind)); - // System.out.println("end reached!!!"); - break; - } - - } - - } - - if (lst != null) { - // lst.addLast(new TimedPositionWithSpeedImpl(new - // MillisecondsTimePoint(lst.getLast().getTimePoint().asMillis() + timeResolution), end, - // lst.getLast().getSpeed())); - return new PathImpl(lst, windField); - } else - return null; - - } - - private Path createOneTurnRight() { - // retrieve simulation parameters - Boundary boundary = new RectangularBoundary(simulationParameters.getCourse().get(0), simulationParameters - .getCourse().get(1));// simulationParameters.getBoundaries(); - WindFieldGenerator windField = simulationParameters.getWindField(); - PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); - Position start = simulationParameters.getCourse().get(0); - Position end = simulationParameters.getCourse().get(1); - TimePoint startTime = windField.getStartTime();// new MillisecondsTimePoint(0); - - Distance courseLength = start.getDistance(end); - - // the solution path - LinkedList lst = null; - // the minimal one-turn time - - Long timeResolution = 120000L; - boolean turned = true; - boolean outOfBounds = false; - Long minTurn = Long.MAX_VALUE; - int turningStep = 0; - - while (turned) { - LinkedList tempLst = new LinkedList(); - Position currentPosition = start; - TimePoint currentTime = startTime; - turned = false; - outOfBounds = false; - turningStep++; - int currentStep = 0; - - while (true) { - - SpeedWithBearing currWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); - polarDiagram.setWind(currWind); - TimePoint nextTime = new MillisecondsTimePoint(currentTime.asMillis() + timeResolution); - tempLst.add(new TimedPositionWithSpeedImpl(currentTime, currentPosition, currWind)); - - if (currentStep >= turningStep) { - turned = true; - nextTime = new MillisecondsTimePoint(nextTime.asMillis() + polarDiagram.getTurnLoss()); - } - - if (!turned) { - Bearing direction = polarDiagram.optimalDirectionsUpwind()[1]; - // for(Bearing b: polarDiagram.optimalDirectionsUpwind()) - // if(polarDiagram.getWindSide(b) == PolarDiagram.WindSide.LEFT) - // direction = b; - SpeedWithBearing currSpeed = polarDiagram.getSpeedAtBearing(direction); - currentPosition = currSpeed.travelTo(currentPosition, currentTime, nextTime); - } - if (turned) { - Bearing direction1 = currentPosition.getBearingGreatCircle(end); - Bearing direction2 = polarDiagram.optimalDirectionsUpwind()[0]; - // for(Bearing b: polarDiagram.optimalDirectionsUpwind()) - // if(polarDiagram.getWindSide(b) == PolarDiagram.WindSide.RIGHT) - // direction2 = b; - SpeedWithBearing currSpeed1 = polarDiagram.getSpeedAtBearing(direction1); - SpeedWithBearing currSpeed2 = polarDiagram.getSpeedAtBearing(direction2); - Position nextPosition1 = currSpeed1.travelTo(currentPosition, currentTime, nextTime); - Position nextPosition2 = currSpeed2.travelTo(currentPosition, currentTime, nextTime); - // nextPosition2. - if (nextPosition1.getDistance(end).compareTo(nextPosition2.getDistance(end)) < 0 - && Math.abs(direction1.getDifferenceTo(direction2).getDegrees()) < 45.0) - currentPosition = nextPosition1; - else - currentPosition = nextPosition2; - } - - currentStep++; - // System.out.println(currentStep + "/" + turningStep + "/" + turned); - currentTime = nextTime; - - if (currentTime.asMillis() > minTurn) { - // System.out.println("out of time"); - break; - } - if (!boundary.isWithinBoundaries(currentPosition)) { - outOfBounds = true; - // System.out.println("out of bounds"); - break; - } - if (currentPosition.getDistance(end).compareTo(courseLength.scale(0.005)) < 0) { - minTurn = currentTime.asMillis(); - lst = new LinkedList(tempLst); - Bearing directionToEnd = currentPosition.getBearingGreatCircle(end); - SpeedWithBearing crtWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); - polarDiagram.setWind(crtWind); - Speed speedToEnd = polarDiagram.getSpeedAtBearing(directionToEnd); - Distance distanceToEnd = currentPosition.getDistance(end); - Long timeToEnd = (long) (1000.0 * distanceToEnd.getMeters() / speedToEnd.getMetersPerSecond()); - TimePoint endTime = new MillisecondsTimePoint(currentTime.asMillis() + timeToEnd); - lst.addLast(new TimedPositionWithSpeedImpl(endTime, end, crtWind)); - // System.out.println("end reached!!!"); - break; - } - - } - - } - - if (lst != null) { - // lst.addLast(new TimedPositionWithSpeedImpl(new - // MillisecondsTimePoint(lst.getLast().getTimePoint().asMillis() + timeResolution), end, - // lst.getLast().getSpeed())); - return new PathImpl(lst, windField); - } else - return null; - - } - - private Path create1Turn(boolean tleft) { - - WindFieldGenerator windField = simulationParameters.getWindField(); - PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); - Position start = simulationParameters.getCourse().get(0); - Position end = simulationParameters.getCourse().get(1); - TimePoint startTime = windField.getStartTime();// new MillisecondsTimePoint(0); - - Distance courseLength = start.getDistance(end); - Position currentPosition = start; - TimePoint currentTime = startTime; - TimePoint nextTime; - - double reachingTolerance = 0.03; - int smax = 400; - double[] reachTime = new double[smax]; - boolean tfound; - long tdelta = windField.getTimeStep().asMillis() / 5; - Bearing direction; - - double newdist; - double mindist = courseLength.getMeters(); - double allmindist = courseLength.getMeters(); - //int allmins = smax; - LinkedList path = null; - LinkedList allminpath = null; - - for (int s = 0; s < smax; s++) { - - currentPosition = start; - currentTime = startTime; - reachTime[s] = courseLength.getMeters(); - tfound = false; - mindist = courseLength.getMeters(); - path = new LinkedList(); - path.addLast(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); // currSpeed)); - - int l = 0; - while ((l < s) && (!tfound)) { - - SpeedWithBearing currWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); - polarDiagram.setWind(currWind); - if (tleft) { - direction = polarDiagram.optimalDirectionsUpwind()[0]; - } else { - direction = polarDiagram.optimalDirectionsUpwind()[1]; - } - SpeedWithBearing currSpeed = polarDiagram.getSpeedAtBearing(direction); - nextTime = new MillisecondsTimePoint(currentTime.asMillis() + tdelta); - Position nextPosition = currSpeed.travelTo(currentPosition, currentTime, nextTime); - newdist = nextPosition.getDistance(end).getMeters(); - if (newdist < mindist) { - mindist = newdist; - } - currentPosition = nextPosition; - currentTime = nextTime; - path.addLast(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); // currSpeed)); - - if (currentPosition.getDistance(end).getMeters() < reachingTolerance * courseLength.getMeters()) { - reachTime[s] = mindist; - tfound = true; - if (mindist < allmindist) { - allmindist = mindist; - //allmins = s; - allminpath = path; - } - } - l++; - } - - currentTime = new MillisecondsTimePoint(currentTime.asMillis() + 4000); - - int r = 0; - while ((r < (smax - s)) && (!tfound)) { - - SpeedWithBearing currWind = windField.getWind(new TimedPositionImpl(currentTime, currentPosition)); - polarDiagram.setWind(currWind); - if (tleft) { - direction = polarDiagram.optimalDirectionsUpwind()[1]; - } else { - direction = polarDiagram.optimalDirectionsUpwind()[0]; - } - SpeedWithBearing currSpeed = polarDiagram.getSpeedAtBearing(direction); - nextTime = new MillisecondsTimePoint(currentTime.asMillis() + tdelta); - Position nextPosition = currSpeed.travelTo(currentPosition, currentTime, nextTime); - newdist = nextPosition.getDistance(end).getMeters(); - if (newdist < mindist) { - mindist = newdist; - } - currentPosition = nextPosition; - currentTime = nextTime; - path.addLast(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); // currSpeed)); - - if (currentPosition.getDistance(end).getMeters() < reachingTolerance * courseLength.getMeters()) { - // System.out.println(""+s+":"+path.size()+" dist:"+mindist); - reachTime[s] = mindist; - tfound = true; - if (mindist < allmindist) { - allmindist = mindist; - //allmins = s; - allminpath = path; - } - } - r++; - } - } - - // for (int i=0; i getAllPaths() { + Map allPaths = new HashMap(); - // allPaths.put("Dummy", createDummy()); - // allPaths.put("1-Turner Right", createOneTurnRight()); - // allPaths.put("1-Turner Left", createOneTurnLeft()); - allPaths.put("Opportunistic", createHeuristic()); - allPaths.put("Omniscient", createDjikstra()); - // allPaths.put("Omniscient", createDP()); + + // get 1-turners + PathGenerator1Turner gen1Turner = new PathGenerator1Turner(simulationParameters); + gen1Turner.setEvaluationParameters(true); + Path leftPath = gen1Turner.getPath(); + int left1TurnMiddle = gen1Turner.getMiddle(); + gen1Turner.setEvaluationParameters(false); + Path rightPath = gen1Turner.getPath(); + int right1TurnMiddle = gen1Turner.getMiddle(); + + // get left- and right-going heuristic based on 1-turner + PathGeneratorOpportunistEuclidian genOpportunistic = new PathGeneratorOpportunistEuclidian(simulationParameters); + genOpportunistic.setEvaluationParameters(left1TurnMiddle, right1TurnMiddle, true); + Path oppPathL = genOpportunistic.getPath(); + genOpportunistic.setEvaluationParameters(left1TurnMiddle, right1TurnMiddle, false); + Path oppPathR = genOpportunistic.getPath(); + + Path oppPath = null; + if (oppPathL.getPathPoints().get(oppPathL.getPathPoints().size() - 1).getTimePoint().asMillis() <= oppPathR + .getPathPoints().get(oppPathR.getPathPoints().size() - 1).getTimePoint().asMillis()) { + oppPath = oppPathL; + } else { + oppPath = oppPathR; + } + + // get optimal path from dynamic programming with forward iteration + PathGenerator genDynProgForward = new PathGeneratorDynProgForward(simulationParameters); + Path optPath = genDynProgForward.getPath(); + + // compare paths to avoid misleading display due to artifactual results from optimization (caused by finite + // resolution of optimization grid) + if (leftPath.getPathPoints() != null) { + if (leftPath.getPathPoints().get(leftPath.getPathPoints().size() - 1).getTimePoint().asMillis() <= optPath + .getPathPoints().get(optPath.getPathPoints().size() - 1).getTimePoint().asMillis()) { + optPath = leftPath; + } + allPaths.put("1-Turner Left", leftPath); + } + + if (rightPath.getPathPoints() != null) { + if (rightPath.getPathPoints().get(rightPath.getPathPoints().size() - 1).getTimePoint().asMillis() <= optPath + .getPathPoints().get(optPath.getPathPoints().size() - 1).getTimePoint().asMillis()) { + optPath = rightPath; + } + allPaths.put("1-Turner Right", rightPath); + } + + if (oppPath != null) { + if (oppPath.getPathPoints().get(oppPath.getPathPoints().size() - 1).getTimePoint().asMillis() <= optPath + .getPathPoints().get(optPath.getPathPoints().size() - 1).getTimePoint().asMillis()) { + optPath = oppPath; + } + allPaths.put("Opportunistic", oppPath); + } + + allPaths.put("Omniscient", optPath); + return allPaths; } public Map> getAllPathsEvenTimed(long millisecondsStep) { - Map> allPaths = new HashMap>(); - // allPaths.put("Dummy", createDummy().getEvenTimedPoints(millisecondsStep)); - // allPaths.put("1-Turner Right", createOneTurnRight().getEvenTimedPoints(millisecondsStep)); - /* - * Path oneLeft = createOneTurnLeft(); Path oneRight = createOneTurnRight(); if (oneLeft != null) - * allPaths.put("1-Turner Left", oneLeft.getEvenTimedPoints(millisecondsStep)); if (oneRight != null) - * allPaths.put("1-Turner Right", oneRight.getEvenTimedPoints(millisecondsStep)); allPaths.put("Opportunistic", - * createHeuristic().getEvenTimedPoints(millisecondsStep)); allPaths.put("Omniscient", - * createDjikstra().getEvenTimedPoints(millisecondsStep)); //allPaths.put("Omniscient", - * createDP().getEvenTimedPoints(millisecondsStep)); - */ - - Path leftPath = create1Turn(true); - Path rightPath = create1Turn(false); - Path oppPath = createHeuristic(); - Path optPath = createDP(); - - if (false) { - - if (leftPath != null) { - allPaths.put("1-Turner Left", leftPath.getEvenTimedPoints(millisecondsStep)); - } - - if (rightPath != null) { - allPaths.put("1-Turner Right", rightPath.getEvenTimedPoints(millisecondsStep)); - } - - if (oppPath != null) { - allPaths.put("Opportunistic", oppPath.getEvenTimedPoints(millisecondsStep)); - } - - } else { - - if (leftPath != null) { - if (leftPath.getPathPoints().get(leftPath.getPathPoints().size() - 1).getTimePoint().asMillis() <= optPath - .getPathPoints().get(optPath.getPathPoints().size() - 1).getTimePoint().asMillis()) { - optPath = leftPath; - } - allPaths.put("1-Turner Left", leftPath.getEvenTimedPoints(millisecondsStep)); - } - - if (rightPath != null) { - if (rightPath.getPathPoints().get(rightPath.getPathPoints().size() - 1).getTimePoint().asMillis() <= optPath - .getPathPoints().get(optPath.getPathPoints().size() - 1).getTimePoint().asMillis()) { - optPath = rightPath; - } - allPaths.put("1-Turner Right", rightPath.getEvenTimedPoints(millisecondsStep)); - } - - if (oppPath != null) { - if (oppPath.getPathPoints().get(oppPath.getPathPoints().size() - 1).getTimePoint().asMillis() <= optPath - .getPathPoints().get(optPath.getPathPoints().size() - 1).getTimePoint().asMillis()) { - optPath = oppPath; - } - allPaths.put("Opportunistic", oppPath.getEvenTimedPoints(millisecondsStep)); - } + Map> allTimedPaths = new HashMap>(); + Map allPaths = this.getAllPaths(); + String[] allKeys = allPaths.keySet().toArray(new String[0]); + for (String currentKey : allKeys) { + allTimedPaths.put(currentKey, allPaths.get(currentKey).getEvenTimedPoints(millisecondsStep)); } - // allPaths.put(, createHeuristic().getEvenTimedPoints(millisecondsStep)); - allPaths.put("Omniscient", optPath.getEvenTimedPoints(millisecondsStep)); - - return allPaths; + return allTimedPaths; } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/WindFieldGeneratorImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/WindFieldGeneratorImpl.java index 4051b4b5e64..d6cf8780ae6 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/WindFieldGeneratorImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/WindFieldGeneratorImpl.java @@ -1,9 +1,7 @@ package com.sap.sailing.simulator.impl; -import java.util.Comparator; import java.util.HashMap; import java.util.Map; -import java.util.logging.Logger; import com.sap.sailing.domain.base.SpeedWithBearing; import com.sap.sailing.domain.base.impl.KilometersPerHourSpeedWithBearingImpl; @@ -25,7 +23,7 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { protected WindControlParameters windParameters; protected Position[][] positions; protected Map, Position> indexPositionMap; - + protected Map timeSpeedWithBearingMap; protected TimePoint startTime; @@ -34,28 +32,18 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { * TimePoint which constitutes one unit of time */ protected TimePoint timeStep; - - private class LatLngComparator implements Comparator { - @Override - public int compare(Position p1, Position p2) { - - if (p1.getLatDeg() < p2.getLatDeg()) { - return -1; - } else if (p1.getLatDeg() == p2.getLngDeg()) { - if (p1.getLngDeg() < p2.getLngDeg()) { - return -1; - } else if (p1.getLngDeg() == p2.getLngDeg()) { - return 0; - } else { - return 1; - } - } else { - return 1; - } - } - - } + /* + * private class LatLngComparator implements Comparator { + * + * @Override public int compare(Position p1, Position p2) { + * + * if (p1.getLatDeg() < p2.getLatDeg()) { return -1; } else if (p1.getLatDeg() == p2.getLngDeg()) { if + * (p1.getLngDeg() < p2.getLngDeg()) { return -1; } else if (p1.getLngDeg() == p2.getLngDeg()) { return 0; } else { + * return 1; } } else { return 1; } } + * + * } + */ public WindFieldGeneratorImpl(Boundary boundary, WindControlParameters windParameters) { this.boundary = boundary; @@ -97,11 +85,11 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { } for (int i = 0; i < positions.length; ++i) { for (int j = 0; j < positions[0].length; ++j) { - Pair index = new Pair(i, j); + Pair index = new Pair(i, j); indexPositionMap.put(index, positions[i][j]); } } - + } public Position getPosition(int i, int j) { @@ -109,46 +97,45 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { return indexPositionMap.get(indexKey); } - - public Pair getPositionIndex(Position p) { - Pair gIdx = boundary.getGridIndex(p); - if ((gIdx.getA()!=null)&&(gIdx.getB()!=null)) { + + public Pair getPositionIndex(Position p) { + Pair gIdx = boundary.getGridIndex(p); + if ((gIdx.getA() != null) && (gIdx.getB() != null)) { return gIdx; } else { return null; } } - + /** * * @param t * @return time units relative to the startTime where each unit is timeStep long */ public int getTimeIndex(TimePoint t) { - return (int) ((t.asMillis()-startTime.asMillis())/timeStep.asMillis()); + return (int) ((t.asMillis() - startTime.asMillis()) / timeStep.asMillis()); } - + @Override public void generate(TimePoint start, TimePoint end, TimePoint step) { this.startTime = start; this.endTime = end; this.timeStep = step; } - + @Override public TimePoint getStartTime() { return this.startTime; } - + @Override public TimePoint getTimeStep() { return this.timeStep; } - - + @Override public TimePoint getEndTime() { return this.endTime; } - + }