Merge branch 'simulator_merge_from_master' of ssh://git.wdf.sap.corp:29418/SAPSail/sapsailingcapture into simulator_merge_from_master

Conflicts:
	java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49.java
This commit is contained in:
I059829 committed 2012-08-09 11:18:43 +03:00
commit 9504ddabba
16 files changed
+2243 -1822

No files matched your search

@@ -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<TimedPositionWithSpeed> getPathEvenTimed(long millisecondsStep);
}
@@ -1,6 +1,5 @@
package com.sap.sailing.simulator;
import java.util.Map;
import java.util.NavigableMap;
import com.sap.sailing.domain.base.SpeedWithBearing;
@@ -8,7 +8,6 @@ public interface SailingSimulator {
void setSimulationParameters(SimulationParameters params);
SimulationParameters getSimulationParameters();
Path getOptimumPath();
Map<String, Path> getAllPaths();
Map<String, List<TimedPositionWithSpeed>> getAllPathsEvenTimed(long millisecondsStep);
@@ -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<TimedPositionWithSpeed> path = null;
// LinkedList<TimedPositionWithSpeed> prevpath = null;
// LinkedList<TimedPositionWithSpeed> xpath1 = null;
// LinkedList<TimedPositionWithSpeed> xpath2 = null;
LinkedList<TimedPositionWithSpeed> 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<TimedPositionWithSpeed>();
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<TimedPositionWithSpeed>(path);
}
}
stepRight++;
}
}
/*
* for (int i=0; i<reachTime.length; i++) { System.out.println(""+i+": "+reachTime[i]); }
*/
// PathImpl[] paths = new PathImpl[2];
// paths[0] = new PathImpl(xpath1,windField);
// paths[1] = new PathImpl(xpath2, windField);
PathImpl[] paths = new PathImpl[1];
paths[0] = new PathImpl(allminpath, windField);
char side;
if (leftSide) {
side = 'L';
} else {
side = 'R';
}
this.result = new result1Turn(paths, side, stepOfOverallMinimumDistance);
return result.paths[0];
}
@Override
public List<TimedPositionWithSpeed> getPathEvenTimed(long millisecondsStep) {
Path path = this.getPath();
return path.getEvenTimedPoints(millisecondsStep);
}
}
@@ -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<TimedPositionWithSpeed> 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<TimedPositionWithSpeed> tempLst = new LinkedList<TimedPositionWithSpeed>();
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<TimedPositionWithSpeed>(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<TimedPositionWithSpeed> getPathEvenTimed(long millisecondsStep) {
Path path = this.getPath();
return path.getEvenTimedPoints(millisecondsStep);
}
}
@@ -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<TimedPositionWithSpeed> 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<TimedPositionWithSpeed> tempLst = new LinkedList<TimedPositionWithSpeed>();
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<TimedPositionWithSpeed>(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<TimedPositionWithSpeed> getPathEvenTimed(long millisecondsStep) {
Path path = this.getPath();
return path.getEvenTimedPoints(millisecondsStep);
}
}
@@ -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<TimedPositionWithSpeed> lst = new LinkedList<TimedPositionWithSpeed>();
// 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<Position, List<Position>> graph = new HashMap<Position, List<Position>>();
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<Position, List<Position>>
* backGraph = new HashMap<Position, List<Position>>(); 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 </null> if unavailable
Map<Position, Pair<Long, Position>> tentativeDistances = new HashMap<Position, Pair<Long, Position>>();
for (Position p : graph.keySet()) {
tentativeDistances.put(p, new Pair<Long, Position>(Long.MAX_VALUE, null));
}
tentativeDistances.put(start, new Pair<Long, Position>(startTime.asMillis(), null));
tentativeDistances.put(end, new Pair<Long, Position>(Long.MAX_VALUE, null));
// create set of unvisited nodes
List<Position> unvisited = new ArrayList<Position>(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<Position> unvisitedNeighbours = new LinkedList<Position>(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<Long, Position>(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<Position>
* 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<TimedPositionWithSpeed> getPathEvenTimed(long millisecondsStep) {
Path path = this.getPath();
return path.getEvenTimedPoints(millisecondsStep);
}
}
@@ -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<DPDuration> 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<TimedPositionWithSpeed> optPath = new LinkedList<TimedPositionWithSpeed>();
// 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<DPDuration> alldur;
ArrayList<ArrayList<Vector<DPLocation>>> paths = new ArrayList<ArrayList<Vector<DPLocation>>>();
ArrayList<DPDuration> duras = new ArrayList<DPDuration>();
// 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<ArrayList<Vector<DPLocation>>> pathsnew = new ArrayList<ArrayList<Vector<DPLocation>>>();
ArrayList<DPDuration> durasnew = new ArrayList<DPDuration>();
// 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<DPDuration>();
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<Vector<DPLocation>> npaths;
if ((pathsnew.size() > 0)
&& (pathsnew.size() == (idxh2 + optimizationGridsizeHorizontal + 1))) {
npaths = pathsnew.get(idxh2 + optimizationGridsizeHorizontal);
} else {
npaths = new ArrayList<Vector<DPLocation>>();
pathsnew.add(npaths);
}
ArrayList<Vector<DPLocation>> tpaths = paths.get(alldur.get(j).hidx);
for (int i = 0; i < tpaths.size(); i++) {
Vector<DPLocation> tpath;
tpath = new Vector<DPLocation>(tpaths.get(i)); // (Vector<DPLocation>)
// (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<Vector<DPLocation>> npaths;
if ((pathsnew.size() > 0) && (pathsnew.size() == (idxh2 + range2 + 1))) {
npaths = pathsnew.get(idxh2 + range2);
} else {
npaths = new ArrayList<Vector<DPLocation>>();
pathsnew.add(npaths);
}
ArrayList<Vector<DPLocation>> tpaths = paths.get(alldur.get(j).hidx);
for (int i = 0; i < tpaths.size(); i++) {
Vector<DPLocation> tpath;
tpath = new Vector<DPLocation>(tpaths.get(i)); // (Vector<DPLocation>)
// (tpaths.get(i)).clone();
tpath.add(new DPLocation(0, mindur.duration));
npaths.add(tpath);
}
k++;
}
}
}
} else { // start of grid
Vector<DPLocation> tpath = new Vector<DPLocation>();
tpath.add(new DPLocation(0, startTime.asMillis()));
ArrayList<Vector<DPLocation>> tpaths = new ArrayList<Vector<DPLocation>>();
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<kmax; k++) {
* System.out.print(" "+paths.get(i).get(j).get(k).idx); } System.out.println(); } } }
*/
}
// generate timed position path
for (int i = 0; i < paths.size(); i++) {
// for(int j=0; j<paths.get(i).size(); j++) {
for (int j = 0; j < 1; j++) { // restrict to first path
for (int k = 0; k < paths.get(i).get(j).size(); k++) {
Position p = sailGrid[k][paths.get(i).get(j).get(k).idx + optimizationGridsizeHorizontal];
TimePoint t = new MillisecondsTimePoint(paths.get(i).get(j).get(k).dur);
// System.out.println("deltaT: "+(t.asMillis()-startTime.asMillis())/1000.);
optPath.add(new TimedPositionWithSpeedImpl(t, p, null));
}
}
}
return new PathImpl(optPath, windField);
}
@Override
public List<TimedPositionWithSpeed> getPathEvenTimed(long millisecondsStep) {
Path path = this.getPath();
return path.getEvenTimedPoints(millisecondsStep);
}
}
@@ -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<TimedPositionWithSpeed> lst = new ArrayList<TimedPositionWithSpeed>();
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<TimedPositionWithSpeed> getPathEvenTimed(long millisecondsStep) {
Path path = this.getPath();
return path.getEvenTimedPoints(millisecondsStep);
}
}
@@ -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<Speed, NavigableMap<Bearing, Speed>> speedTable;
protected NavigableMap<Speed, Bearing> beatAngles;
protected NavigableMap<Speed, Bearing> gybeAngles;
protected NavigableMap<Speed, Speed> beatSOG;
protected NavigableMap<Speed, Speed> gybeSOG;
// this constructor creates an instance with a hard-coded set of values
public PolarDiagram49() {
speedTable = new TreeMap<Speed, NavigableMap<Bearing, Speed>>();
NavigableMap<Bearing, Speed> tableRow;
tableRow = new TreeMap<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Speed, Bearing>();
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<Speed, Speed>();
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<Speed, Bearing>();
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<Speed, Speed>();
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<Speed, NavigableMap<Bearing, Speed>> speeds,
NavigableMap<Speed, Bearing> beats,
NavigableMap<Speed, Bearing> gybes,
NavigableMap<Speed, Speed> beatSOGs,
NavigableMap<Speed, Speed> 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<Bearing, Speed> floorSpeeds = speedTable.get(floorWind);
NavigableMap<Bearing, Speed> 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<Bearing> allKeys = new TreeSet<Bearing>(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<Bearing> allKeys = new TreeSet<Bearing>(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<Bearing> bearingComparator = new Comparator<Bearing>() {
@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<Speed, NavigableMap<Bearing, Speed>> polarDiagramPlot(
Double bearingStep) {
NavigableMap<Speed, NavigableMap<Bearing, Speed>> table = new TreeMap<Speed, NavigableMap<Bearing, Speed>>();
Set<Bearing> extraBearings = new HashSet<Bearing>();
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<Bearing, Speed> currentTable = new TreeMap<Bearing, Speed>(
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;
}
}
@@ -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<Speed, NavigableMap<Bearing, Speed>>();
NavigableMap<Bearing, Speed> tableRow;
double cutAngle = 35.0;
tableRow = new TreeMap<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Bearing, Speed>(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<Speed, Bearing> beatAngles = new TreeMap<Speed, Bearing>();
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<Speed, Speed> beatSOG = new TreeMap<Speed, Speed>();
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<Speed, Bearing> gybeAngles = new TreeMap<Speed, Bearing>();
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<Speed, Speed> gybeSOG = new TreeMap<Speed, Speed>();
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));
}
}
}
@@ -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<Speed, NavigableMap<Bearing, Speed>> speedTable;
protected NavigableMap<Speed, Bearing> beatAngles;
protected NavigableMap<Speed, Bearing> gybeAngles;
protected NavigableMap<Speed, Speed> beatSOG;
protected NavigableMap<Speed, Speed> 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<Speed, NavigableMap<Bearing, Speed>> speeds,
NavigableMap<Speed, Bearing> beats, NavigableMap<Speed, Bearing> gybes,
NavigableMap<Speed, Speed> beatSOGs, NavigableMap<Speed, Speed> 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<Bearing, Speed> floorSpeeds = speedTable.get(floorWind);
NavigableMap<Bearing, Speed> 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<Bearing> allKeys = new TreeSet<Bearing>(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<Bearing> allKeys = new TreeSet<Bearing>(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<Bearing> bearingComparator = new Comparator<Bearing>() {
@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<Speed, NavigableMap<Bearing, Speed>> polarDiagramPlot(Double bearingStep) {
NavigableMap<Speed, NavigableMap<Bearing, Speed>> table = new TreeMap<Speed, NavigableMap<Bearing, Speed>>();
Set<Bearing> extraBearings = new HashSet<Bearing>();
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<Bearing, Speed> currentTable = new TreeMap<Bearing, Speed>(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;
}
}
@@ -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<Speed, NavigableMap<Bearing, Speed>> 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<Speed, NavigableMap<Bearing, Speed>> 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
}
}
@@ -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<vPoints; i++) {
for (int j=0; j<hPoints; j++) {
System.out.print(""+this.getGridIndex(grid[i][j])+", ");
}
System.out.println();
}*/
/*
* System.out.println("Grid Index Test:"); for (int i =0; i<vPoints; i++) { for (int j=0; j<hPoints; j++) {
* System.out.print(""+this.getGridIndex(grid[i][j])+", "); } System.out.println(); }
*/
return grid;
}
}
public Pair<Integer,Integer> getGridIndex(Position x) {
public Pair<Integer, Integer> 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<Integer, Integer>(vIdx,hIdx);
// System.out.println("getGridIndex: "+vIdx+","+hIdx+"("+vPoints+","+hPoints+")");
return new Pair<Integer, Integer>(vIdx, hIdx);
}
}
/* @Override
public List<Position> extractLattice(int hPoints, int vPoints) {
Position[][] grid = extractGrid(hPoints, vPoints);
List<Position> lst = new ArrayList<Position>();
for(Position[] line : grid) {
for(Position p : line) {
lst.add(p);
}
}
return lst;
}
@Override
//may not return a rectangular lattice!
public List<Position> extractLattice(Distance hStep, Distance vStep) {
Position startPoint = appSouthWest;
Bearing vBearing = getNorth();
Bearing hBearing = getEast();
boolean hMode = true;
List<Position> lst = new ArrayList<Position>();
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<Position> extractLattice(int hPoints, int vPoints) {
*
* Position[][] grid = extractGrid(hPoints, vPoints); List<Position> lst = new ArrayList<Position>(); for(Position[]
* line : grid) { for(Position p : line) { lst.add(p); } } return lst; }
*
* @Override //may not return a rectangular lattice! public List<Position> extractLattice(Distance hStep, Distance
* vStep) {
*
* Position startPoint = appSouthWest;
*
* Bearing vBearing = getNorth(); Bearing hBearing = getEast(); boolean hMode = true;
*
* List<Position> lst = new ArrayList<Position>();
*
* 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<String, Double> getRelativeCoordinates(Position p) {
if (!isWithinBoundaries(p)) return null;
if (!isWithinBoundaries(p))
return null;
Map<String,Double> map = new HashMap<String,Double>();
Map<String, Double> map = new HashMap<String, Double>();
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;
}
@@ -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<Pair<Integer, Integer>, Position> indexPositionMap;
protected Map<TimePoint, SpeedWithBearing[][]> 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<Position> {
@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<Position> {
*
* @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<Integer,Integer> index = new Pair<Integer, Integer>(i, j);
Pair<Integer, Integer> index = new Pair<Integer, Integer>(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<Integer,Integer> getPositionIndex(Position p) {
Pair<Integer,Integer> gIdx = boundary.getGridIndex(p);
if ((gIdx.getA()!=null)&&(gIdx.getB()!=null)) {
public Pair<Integer, Integer> getPositionIndex(Position p) {
Pair<Integer, Integer> 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;
}
}