fix tree-grow by using wind of next step for height comparison (ensure comparability of path candidates); cleanup tree-grow variety;

This commit is contained in:
Christopher Ronnewinkel (D036654) committed 2014-07-24 22:15:18 +02:00
1 parent a4c27b0783
commit 297cf65d60
9 files changed
+426 -1908

No files matched your search

@@ -79,7 +79,7 @@ public class WindPatternDisplayManagerImpl implements WindPatternDisplayManager
1, 1, 10);
display.addSetting(maxBlastSize);
WindPatternSetting<Double> blastWindSpeed = new WindPatternSettingSliderBar("blastWindSpeed",
"Average Speed (%)", 0, 200, 5, 100, 10);
"Average Speed (%)", 0, 200, 5, 120, 10);
display.addSetting(blastWindSpeed);
WindPatternSetting<Double> blastWindSpeedVar = new WindPatternSettingSliderBar("blastWindSpeedVar",
"Speed Variance (%)", 1e-4, 100, 5, 10, 10);
@@ -20,7 +20,7 @@ import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.simulator.Path;
import com.sap.sailing.simulator.PolarDiagram;
import com.sap.sailing.simulator.SimulationParameters;
import com.sap.sailing.simulator.impl.PathGeneratorTreeGrowWind3;
import com.sap.sailing.simulator.impl.PathGeneratorTreeGrowWind;
import com.sap.sailing.simulator.impl.PolarDiagram49STG;
import com.sap.sailing.simulator.impl.RectangularGrid;
import com.sap.sailing.simulator.impl.SimulationParametersImpl;
@@ -62,7 +62,7 @@ public class TreeGrowTest {
param.setProperty("Djikstra.gridv[int]", 10.0);
param.setProperty("Djikstra.gridh[int]", 100.0);*/
PathGeneratorTreeGrowWind3 treeGrow = new PathGeneratorTreeGrowWind3(param);
PathGeneratorTreeGrowWind treeGrow = new PathGeneratorTreeGrowWind(param);
Path path = treeGrow.getPath();
@@ -1,421 +0,0 @@
package com.sap.sailing.simulator.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
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.SpeedWithBearing;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.simulator.Path;
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.WindFieldGenerator;
public class PathGeneratorTreeGrowTarget extends PathGeneratorBase {
private static Logger logger = Logger.getLogger("com.sap.sailing");
int maxLeft;
int maxRight;
boolean startLeft;
public PathGeneratorTreeGrowTarget(SimulationParameters params) {
this.parameters = params;
}
public void setEvaluationParameters(int maxLeftVal, int maxRightVal, boolean startLeftVal) {
this.maxLeft = maxLeftVal;
this.maxRight = maxRightVal;
this.startLeft = startLeftVal;
}
class PathCand implements Comparable<PathCand> {
public PathCand(TimedPosition pos, double vrt, double hrz, String path) {
this.pos = pos;
this.vrt = vrt;
this.hrz = hrz;
this.path = path;
}
TimedPosition pos;
double vrt;
double hrz;
String path;
/*@Override
// sort ascending by horizontal distance
public int compareTo(PathCand other) {
if (Math.abs(this.hrz) == Math.abs(other.hrz))
return 0;
return (Math.abs(this.hrz) < Math.abs(other.hrz) ? -1 : +1);
}*/
@Override
// sort descending by height
public int compareTo(PathCand other) {
if (this.vrt == other.vrt) {
if (Math.abs(this.hrz) == Math.abs(other.hrz)) {
return 0;
} else {
return (Math.abs(this.hrz) < Math.abs(other.hrz) ? -1 : +1);
}
} else {
return (this.vrt > other.vrt ? -1 : +1);
}
}
}
// generate step
TimedPosition getStep(TimedPosition pos, long timeStep, long turnLoss, char prevDirection, char nextDirection) {
double offDeg = 5.0;
WindFieldGenerator wf = this.parameters.getWindField();
TimePoint curTime = pos.getTimePoint();
Position curPosition = pos.getPosition();
Wind posWind = wf.getWind(new TimedPositionWithSpeedImpl(curTime, curPosition, null));
PolarDiagram pd = this.parameters.getBoatPolarDiagram();
pd.setWind(posWind);
// get beat-angle left and right
Bearing travelBearing = null;
Bearing tmpBearing = null;
if (nextDirection == 'L') {
travelBearing = pd.optimalDirectionsUpwind()[0];
} else if (nextDirection == 'R') {
travelBearing = pd.optimalDirectionsUpwind()[1];
} else if (nextDirection == 'M') {
tmpBearing = pd.optimalDirectionsUpwind()[0];
travelBearing = tmpBearing.add(new DegreeBearingImpl(+offDeg));
} else if (nextDirection == 'S') {
tmpBearing = pd.optimalDirectionsUpwind()[1];
travelBearing = tmpBearing.add(new DegreeBearingImpl(-offDeg));
}
// determine beat-speed left and right
SpeedWithBearing travelSpeed = pd.getSpeedAtBearing(travelBearing);
TimePoint travelTime;
TimePoint nextTime = new MillisecondsTimePoint(curTime.asMillis()+timeStep);
char prevBaseDirection = prevDirection;
if (prevBaseDirection == 'M') {
prevBaseDirection = 'L';
}
if (prevBaseDirection == 'S') {
prevBaseDirection = 'R';
}
char nextBaseDirection = nextDirection;
if (nextBaseDirection == 'M') {
nextBaseDirection = 'L';
}
if (nextBaseDirection == 'S') {
nextBaseDirection = 'R';
}
boolean sameBaseDirection = (nextBaseDirection == prevBaseDirection)||(prevBaseDirection == '0');
if (sameBaseDirection) {
travelTime = nextTime;
} else {
travelTime = new MillisecondsTimePoint(nextTime.asMillis() - turnLoss);
}
return new TimedPositionImpl(nextTime, travelSpeed.travelTo(curPosition, curTime, travelTime));
}
PathCand getPathCand(PathCand path, char nextDirection, long timeStep, long turnLoss, Position posStart, Bearing bearVrt, double tgtHeight) {
// calculate next path position (taking turn-loss into account)
TimedPosition pathPos = this.getStep(path.pos, timeStep, turnLoss, path.path.charAt(path.path.length()-1), nextDirection);
// calculate height-position with reference to race course
Position posHeight = pathPos.getPosition().projectToLineThrough(posStart, bearVrt);
// calculate vertical distance as distance of height-position to start
double vrtDist = Math.round(posHeight.getDistance(posStart).getMeters()*100.0)/100.0;
/*if (vrtDist > tgtHeight) {
// scale last step so that vrtDist ~ tgtHeight
Position prevPos = path.pos.getPosition();
TimePoint prevTime = path.pos.getTimePoint();
double heightFrac = (tgtHeight - path.vrt) / (vrtDist - path.vrt);
Position newPos = prevPos.translateGreatCircle(prevPos.getBearingGreatCircle(pathPos.getPosition()), prevPos.getDistance(pathPos.getPosition()).scale(heightFrac));
TimePoint newTime = new MillisecondsTimePoint(Math.round(prevTime.asMillis() + (pathPos.getTimePoint().asMillis()-prevTime.asMillis())*heightFrac));
pathPos = new TimedPositionImpl(newTime, newPos);
posHeight = pathPos.getPosition().projectToLineThrough(posStart, bearVrt);
}*/
// calculate horizontal side: left or right in reference to race course
double posSide = 1;
double posBear = posStart.getBearingGreatCircle(pathPos.getPosition()).getDegrees();
if ((posBear < 0.0)||(posBear > 180.0)) {
posSide = -1;
} else if ((posBear == 0.0)||(posBear == 180.0)) {
posSide = 0;
}
// calculate horizontal distance as distance of height-position to current position
double hrzDist = Math.round(posSide*posHeight.getDistance(pathPos.getPosition()).getMeters()*100.0)/100.0;
//System.out.println(""+hrzDist+", "+vrtDist+", "+pathPos.getPosition().getLatDeg()+", "+pathPos.getPosition().getLngDeg()+", "+posHeight.getLatDeg()+", "+posHeight.getLngDeg());
// extend path-string by step-direction
String pathStr = path.path + nextDirection;
return (new PathCand(pathPos, vrtDist, hrzDist, pathStr));
}
// generate path candidates based on beat angles
List<PathCand> getPathCandsBeat(PathCand path, long timeStep, long turnLoss, Position posStart, Bearing bearVrt, double tgtHeight) {
List<PathCand> result = new ArrayList<PathCand>();
PathCand newPathCand;
// step left
newPathCand = getPathCand(path, 'L', timeStep, turnLoss, posStart, bearVrt, tgtHeight);
result.add(newPathCand);
// step wide left
newPathCand = getPathCand(path, 'M', timeStep, turnLoss, posStart, bearVrt, tgtHeight);
result.add(newPathCand);
// step right
newPathCand = getPathCand(path, 'R', timeStep, turnLoss, posStart, bearVrt, tgtHeight);
result.add(newPathCand);
// step wide right
newPathCand = getPathCand(path, 'S', timeStep, turnLoss, posStart, bearVrt, tgtHeight);
result.add(newPathCand);
return result;
}
@Override
public Path getPath() {
WindFieldGenerator wf = this.parameters.getWindField();
PolarDiagram pd = this.parameters.getBoatPolarDiagram();
Position startPos = this.parameters.getCourse().get(0);
Position endPos = this.parameters.getCourse().get(1);
TimePoint startTime = wf.getStartTime();// new MillisecondsTimePoint(0);
List<TimedPositionWithSpeed> path = new ArrayList<TimedPositionWithSpeed>();
Position currentPosition = startPos;
TimePoint currentTime = startTime;
Distance distStartEnd = startPos.getDistance(endPos);
double distStartEndMeters = distStartEnd.getMeters();
long timeStep = wf.getTimeStep().asMillis()/3;
logger.info("Time step :" + timeStep);
long turnLoss = pd.getTurnLoss(); // 4000; // time lost when doing a turn
Wind wndStart = wf.getWind(new TimedPositionWithSpeedImpl(startTime, startPos, null));
logger.fine("wndStart speed:" + wndStart.getKnots() + " angle:" + wndStart.getBearing().getDegrees());
pd.setWind(wndStart);
Bearing bearVrt = startPos.getBearingGreatCircle(endPos);
//Bearing bearHrz = bearVrt.add(new DegreeBearingImpl(90.0));
Position middlePos = startPos.translateGreatCircle(bearVrt, distStartEnd.scale(0.5));
System.out.println("start : "+startPos.getLatDeg()+", "+startPos.getLngDeg());
System.out.println("middle: "+middlePos.getLatDeg()+", "+middlePos.getLngDeg());
System.out.println("end : "+endPos.getLatDeg()+", "+endPos.getLngDeg());
// initialize list of paths
PathCand initPath = new PathCand(new TimedPositionImpl(currentTime, currentPosition), 0.0, 0.0, "0");
String initPathStr = "0";
//String initPathStr = "0RRRRRRRRRRRRRRRLLL";
//String initPathStr = "0RRRRRRRRRRRRRRRRRR";
//PathCand initPath = new PathCand(new TimedPositionImpl(currentTime, currentPosition), 0.0, 0.0, ); //"0RRRRRRRRRRRRRRR");
if (initPathStr.length()>1) {
//char prevDirection = '0';
char nextDirection = '0';
for(int idx=1; idx<initPathStr.length(); idx++) {
nextDirection = initPathStr.charAt(idx);
//TimedPosition newPosition = this.getStep(initPath.pos, timeStep, turnLoss, prevDirection, nextDirection);
//initPath.pos = newPosition;
PathCand newPathCand = getPathCand(initPath, nextDirection, timeStep, turnLoss, startPos, bearVrt, distStartEndMeters);
initPath = newPathCand;
//prevDirection = nextDirection;
}
}
List<PathCand> allPaths = new ArrayList<PathCand>();
List<PathCand> trgPaths = new ArrayList<PathCand>();
allPaths.add(initPath);
TimedPosition tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, '0', 'L');
double tstDist1 = startPos.getDistance(tstPosition.getPosition()).getMeters();
tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, '0', 'R');
double tstDist2 = startPos.getDistance(tstPosition.getPosition()).getMeters();
double hrzBinSize = (tstDist1 + tstDist2)/3; // horizontal bin size in meters
System.out.println("Horizontal Bin Size: "+hrzBinSize);
double oobFact = 0.75; // out-of-bounds factor
//int hrzLeft = (int)Math.round(Math.floor( -distStartEndMeters / 2.0 / hrzBinSize / oobFact ));
//int hrzRight = (int)Math.round(Math.floor( distStartEndMeters / 2.0 / hrzBinSize / oobFact ));
boolean reachedEnd = false;
int addSteps = 0;
int finalSteps = 0; // maximum number of additional steps after first target-path found
//for(int count=0; count<10; count++) {
while ((!reachedEnd)||(addSteps<finalSteps)) {
if (reachedEnd) {
addSteps++;
}
// generate new paths
double hrzMin = 0;
double hrzMax = 0;
List<PathCand> newPathCands;
List<PathCand> newPaths = new ArrayList<PathCand>();
for(PathCand curPath : allPaths) {
if ((curPath.vrt > distStartEndMeters)) {
continue;
} else {
newPathCands = this.getPathCandsBeat(curPath, timeStep, turnLoss, startPos, bearVrt, distStartEndMeters);
}
for(PathCand newPath : newPathCands) {
newPaths.add(newPath);
if (newPath.hrz < hrzMin) {
hrzMin = newPath.hrz;
}
if (newPath.hrz > hrzMax) {
hrzMax = newPath.hrz;
}
}
}
int hrzLeft = (int)Math.round(Math.floor( (hrzMin + hrzBinSize/2.0) / hrzBinSize ));
int hrzRight = (int)Math.round(Math.floor( (hrzMax + hrzBinSize/2.0) / hrzBinSize ));
//System.out.println("hrzLeft: "+hrzLeft+", hrzRight: "+hrzRight);
// build map of best path per hrz-bin
int countPath = 0;
int mapSize = hrzRight-hrzLeft+1;
//System.out.println("MapSize: "+mapSize);
int[] binIdx = new int[mapSize]; // TODO: init with zero?
double[] binVal = new double[mapSize]; // TODO: init with zero?
for(int curIdx=0; curIdx<newPaths.size(); curIdx++) {
PathCand curPath = newPaths.get(curIdx);
// check whether path is *outside* regatta-area
double distFromMiddleMeters = middlePos.getDistance(curPath.pos.getPosition()).getMeters();
if (distFromMiddleMeters > oobFact*distStartEndMeters) {
continue; // ignore curPath
}
// increase path-counter
countPath++;
// determine map-key
int curKey = (int)Math.round(Math.floor( (curPath.hrz + hrzBinSize/2.0) / hrzBinSize )) - hrzLeft;
/*String allR = "0" + (new String(new char[curPath.path.length()-1]).replace('\0', 'R'));
if (curPath.path.equals(allR)) {
System.out.println(""+curPath.path+": "+curPath.hrz+", "+curPath.vrt+", "+curPath.pos.getPosition().getLatDeg()+", "+curPath.pos.getPosition().getLngDeg());
}*/
// check whether curPath is better then the ones looked at
if ((curKey>=0)&&(curKey < mapSize)) {
if (curPath.vrt > binVal[curKey]) {
binVal[curKey] = curPath.vrt;
binIdx[curKey] = curIdx+1;
}
}
}
// check if there are still paths inside regatta-area
if (countPath > 0) {
// take best from each horizontal-bin and check if target is reached
allPaths = new ArrayList<PathCand>();
for (int curKey = 0; curKey < mapSize; curKey++) {
if (binIdx[curKey] > 0) {
PathCand curPath = newPaths.get(binIdx[curKey] - 1);
allPaths.add(curPath);
// debug output
/*if ((Math.abs(curKey + hrzLeft) <= 3)) {
System.out.println("" + curPath.path + ": " + (curKey + hrzLeft) + ", vrt:" + curPath.vrt + ", hrz:" + curPath.hrz + ", bin:" + (Math.floor((curPath.hrz + hrzBinSize / 2.0) / hrzBinSize)));
}*/
// terminate path-search if paths close enough to target are found
if ((curPath.vrt > distStartEndMeters)) {
if ((Math.abs(curKey + hrzLeft) <= 2)) {
reachedEnd = true;
trgPaths.add(curPath); // add path to list of target-paths
}
}
}
}
} else {
// terminate path-search as no path inside regatta-area are left
reachedEnd = true;
}
}
// if no target-paths were found, return empty path
if (trgPaths.size() == 0) {
//trgPaths = allPaths; // TODO: only for testing; remove lateron
return new PathImpl(path, wf); // return empty path
}
// sort target-paths ascending by distance-to-target
Collections.sort(trgPaths);
// debug output
for(PathCand curPath : trgPaths) {
System.out.print(""+curPath.path+": "+curPath.pos.getTimePoint().asMillis()+", "+curPath.pos.getPosition().getLatDeg()+", "+curPath.pos.getPosition().getLngDeg()+", ");
System.out.println(" height:"+curPath.vrt+" of "+startPos.getDistance(endPos).getMeters()+", dist:"+curPath.hrz+" ~ "+curPath.pos.getPosition().getDistance(endPos));
}
//
// fill gwt-path
//
// generate intermediate steps
PathCand bstPath = trgPaths.get(0); // target-path ending closest to target
TimedPositionWithSpeed curPosition = null;
char nextDirection = '0';
char prevDirection = '0';
for(int step=0; step<(bstPath.path.length()-1); step++) {
nextDirection = bstPath.path.charAt(step);
if (nextDirection == '0') {
curPosition = new TimedPositionWithSpeedImpl(startTime, startPos, null);
path.add(curPosition);
} else {
TimedPosition newPosition = this.getStep(curPosition, timeStep, turnLoss, prevDirection, nextDirection);
curPosition = new TimedPositionWithSpeedImpl(newPosition.getTimePoint(), newPosition.getPosition(), null);
path.add(curPosition);
}
prevDirection = nextDirection;
}
// add final position (rescaled before to end on height of target)
path.add(new TimedPositionWithSpeedImpl(bstPath.pos.getTimePoint(), bstPath.pos.getPosition(), null));
return new PathImpl(path, wf);
}
}
@@ -1,7 +1,11 @@
package com.sap.sailing.simulator.impl;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;
@@ -27,57 +31,62 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
private static Logger logger = Logger.getLogger("com.sap.sailing");
private boolean debugMsgOn = false;
double oobFact = 0.75; // out-of-bounds factor
int maxTurns = 0;
boolean upwindLeg = false;
String initPathStr = "0";
PathCandidate bestCand = null;
long usedTimeStep = 0;
boolean gridStore = false;
ArrayList<List<PathCandidate>> gridPositions = null;
ArrayList<List<PathCandidate>> isocPositions = null;
String gridFile = null;
public PathGeneratorTreeGrowWind(SimulationParameters params) {
this.parameters = params;
}
public void setEvaluationParameters(String startDirection, int maxTurns) {
public void setEvaluationParameters(String startDirection, int maxTurns, String gridFile) {
if (startDirection != null) {
this.initPathStr = "0" + startDirection;
} else {
this.initPathStr = "0";
}
this.maxTurns = maxTurns;
this.gridFile = gridFile;
if (this.gridFile != null) {
this.gridStore = true;
this.gridPositions = new ArrayList<List<PathCandidate>>();
this.isocPositions = new ArrayList<List<PathCandidate>>();
} else {
this.gridStore = false;
this.gridPositions = null;
this.isocPositions = null;
}
}
class PathCandidate implements Comparable<PathCandidate> {
public PathCandidate(TimedPosition pos, double vrt, double hrz, int trn, String path) {
this.pos = pos;
this.vrt = vrt;
this.hrz = hrz;
this.trn = trn;
this.path = path;
class SortPathCandsAbsHorizontally implements Comparator<PathCandidate> {
@Override
public int compare(PathCandidate p1, PathCandidate p2) {
if (Math.abs(p1.hrz) == Math.abs(p2.hrz)) {
return 0;
} else {
return (Math.abs(p1.hrz) < Math.abs(p2.hrz) ? -1 : +1);
}
}
TimedPosition pos;
double vrt;
double hrz;
int trn;
String path;
}
class SortPathCandsHorizontally implements Comparator<PathCandidate> {
/*@Override
// sort ascending by horizontal distance
public int compareTo(PathCand other) {
if (Math.abs(this.hrz) == Math.abs(other.hrz))
return 0;
return (Math.abs(this.hrz) < Math.abs(other.hrz) ? -1 : +1);
}*/
@Override
// sort descending by height
public int compareTo(PathCandidate other) {
if (this.vrt == other.vrt) {
if (Math.abs(this.hrz) == Math.abs(other.hrz)) {
return 0;
} else {
return (Math.abs(this.hrz) < Math.abs(other.hrz) ? -1 : +1);
}
public int compare(PathCandidate p1, PathCandidate p2) {
if (p1.hrz == p2.hrz) {
return 0;
} else {
return (this.vrt > other.vrt ? -1 : +1);
}
return (p1.hrz < p2.hrz ? -1 : +1);
}
}
}
@@ -86,36 +95,57 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
PathCandidate getBestCand() {
return this.bestCand;
}
long getUsedTimeStep() {
return this.usedTimeStep;
}
// generate step in one of the possible directions
// default: L - left, R - right
// extended: M - wide left, S - wide right
Util.Pair<TimedPosition,Wind> getStep(TimedPosition pos, long timeStep, long turnLoss, boolean sameBaseDirection, char nextDirection) {
TimedPosition getStep(TimedPosition pos, long timeStep, long turnLoss, boolean sameBaseDirection, char nextDirection) {
double offDeg = 5.0;
double offDeg = 3.0;
WindFieldGenerator wf = this.parameters.getWindField();
TimePoint curTime = pos.getTimePoint();
Position curPosition = pos.getPosition();
Wind posWind = wf.getWind(new TimedPositionWithSpeedImpl(curTime, curPosition, null));
Wind posWind = wf.getWind(new TimedPositionImpl(curTime, curPosition));
PolarDiagram pd = this.parameters.getBoatPolarDiagram();
pd.setWind(posWind);
Wind appWind = new WindImpl(posWind.getPosition(), posWind.getTimePoint(), pd.getWind());;
// get beat-angle left and right
Bearing travelBearing = null;
Bearing tmpBearing = null;
if (nextDirection == 'L') {
travelBearing = pd.optimalDirectionsUpwind()[0];
if (this.upwindLeg) {
travelBearing = pd.optimalDirectionsUpwind()[0];
} else {
travelBearing = pd.optimalDirectionsDownwind()[0];
}
} else if (nextDirection == 'R') {
travelBearing = pd.optimalDirectionsUpwind()[1];
if (this.upwindLeg) {
travelBearing = pd.optimalDirectionsUpwind()[1];
} else {
travelBearing = pd.optimalDirectionsDownwind()[1];
}
} else if (nextDirection == 'M') {
tmpBearing = pd.optimalDirectionsUpwind()[0];
travelBearing = tmpBearing.add(new DegreeBearingImpl(+offDeg));
if (this.upwindLeg) {
tmpBearing = pd.optimalDirectionsUpwind()[0];
travelBearing = tmpBearing.add(new DegreeBearingImpl(-offDeg));
} else {
tmpBearing = pd.optimalDirectionsDownwind()[0];
travelBearing = tmpBearing.add(new DegreeBearingImpl(-offDeg));
}
} else if (nextDirection == 'S') {
tmpBearing = pd.optimalDirectionsUpwind()[1];
travelBearing = tmpBearing.add(new DegreeBearingImpl(-offDeg));
if (this.upwindLeg) {
tmpBearing = pd.optimalDirectionsUpwind()[1];
travelBearing = tmpBearing.add(new DegreeBearingImpl(+offDeg));
} else {
tmpBearing = pd.optimalDirectionsDownwind()[1];
travelBearing = tmpBearing.add(new DegreeBearingImpl(+offDeg));
}
}
// determine beat-speed left and right
@@ -129,7 +159,8 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
travelTime = new MillisecondsTimePoint(nextTime.asMillis() - turnLoss);
}
return new Util.Pair<TimedPosition,Wind>(new TimedPositionImpl(nextTime, travelSpeed.travelTo(curPosition, curTime, travelTime)), appWind);
Position nextPosition = travelSpeed.travelTo(curPosition, curTime, travelTime);
return new TimedPositionImpl(nextTime, nextPosition);
}
// use base direction to distinguish direction changes that do or don't require a turn
@@ -167,20 +198,29 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
}
// calculate next path position (taking turn-loss into account)
Util.Pair<TimedPosition,Wind> nextStep = this.getStep(path.pos, timeStep, turnLoss, sameBaseDirection, nextDirection);
TimedPosition pathPos = nextStep.getA();
Wind posWind = nextStep.getB();
TimedPosition pathPos = this.getStep(path.pos, timeStep, turnLoss, sameBaseDirection, nextDirection);
// determine apparent wind at next path position & time
Wind posWind = this.parameters.getWindField().getWind(pathPos);
PolarDiagram pd = this.parameters.getBoatPolarDiagram();
pd.setWind(posWind);
Wind appWind = new WindImpl(posWind.getPosition(), posWind.getTimePoint(), pd.getWind());
// calculate height-position with reference to race course
Position posHeight = pathPos.getPosition().projectToLineThrough(posEnd, posWind.getBearing());
Bearing bearVrt = posStart.getBearingGreatCircle(posEnd);
Position posHeightTrgt = pathPos.getPosition().projectToLineThrough(posStart, bearVrt);
//Position posHeightWind = pathPos.getPosition().projectToLineThrough(posRef, posWind.getBearing());
// calculate height-position with reference to apparent wind
Position posHeight = pathPos.getPosition().projectToLineThrough(posEnd, appWind.getBearing());
// calculate vertical distance as distance of height-position to start
//double vrtDist = tgtHeight - Math.round(posHeight.getDistance(posEnd).getMeters()*100.0)/100.0;
double vrtDist = Math.round(posHeight.getDistance(posStart).getMeters()*100.0)/100.0;
// calculate vertical distance as distance of height-position to end
Bearing bearHeight = posEnd.getBearingGreatCircle(posHeight);
double bearHeightSide = appWind.getBearing().getDifferenceTo(bearHeight).getDegrees();
double vrtSide = (this.upwindLeg ? -1.0 : +1.0);
if (Math.abs(bearHeightSide) > 170.0) {
vrtSide = (this.upwindLeg ? +1.0 : -1.0);
}
double vrtDist = vrtSide*Math.round(posHeight.getDistance(posEnd).getMeters()*1000.0)/1000.0;
//
// TODO: scale last step to exactly reach height of posEnd, and adjust time correspondingly
//
/*if (vrtDist > tgtHeight) {
// scale last step so that vrtDist ~ tgtHeight
Position prevPos = path.pos.getPosition();
@@ -194,23 +234,23 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
// calculate horizontal side: left or right in reference to race course
double posSide = 1;
//double posBear = posWind.getBearing().getDegrees() - posEnd.getBearingGreatCircle(pathPos.getPosition()).getDegrees();
double posBear = posStart.getBearingGreatCircle(pathPos.getPosition()).getDegrees();
if ((posBear < 0.0)||(posBear > 180.0)) {
Bearing posBear = posStart.getBearingGreatCircle(pathPos.getPosition());
Bearing bearVrt = posStart.getBearingGreatCircle(posEnd);
double posBearDiff = bearVrt.getDifferenceTo(posBear).getDegrees();
if ((posBearDiff < 0.0)||(posBearDiff > 180.0)) {
posSide = -1;
} else if ((posBear == 0.0)||(posBear == 180.0)) {
} else if ((posBearDiff == 0.0)||(posBearDiff == 180.0)) {
posSide = 0;
}
// calculate horizontal distance as distance of height-position to current position
//double hrzDist = Math.round(posSide*posHeight.getDistance(pathPos.getPosition()).getMeters()*100.0)/100.0;
double hrzDist = Math.round(posSide*posHeightTrgt.getDistance(pathPos.getPosition()).getMeters()*100.0)/100.0;
//System.out.println(""+hrzDist+", "+vrtDist+", "+pathPos.getPosition().getLatDeg()+", "+pathPos.getPosition().getLngDeg()+", "+posHeight.getLatDeg()+", "+posHeight.getLngDeg());
Position posHeightTrgt = pathPos.getPosition().projectToLineThrough(posStart, bearVrt);
double hrzDist = Math.round(posSide*posHeightTrgt.getDistance(pathPos.getPosition()).getMeters()*1000.0)/1000.0;
// extend path-string by step-direction
String pathStr = path.path + nextDirection;
char nextBaseDirection = this.getBaseDirection(nextDirection);
return (new PathCandidate(pathPos, vrtDist, hrzDist, turnCount, pathStr));
return (new PathCandidate(pathPos, vrtDist, hrzDist, turnCount, pathStr, nextBaseDirection, appWind));
}
@@ -256,13 +296,200 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
return result;
}
Util.Pair<List<PathCandidate>,List<PathCandidate>> generateCandidate(List<PathCandidate> oldPaths, long timeStep, long turnLoss, Position posStart, Position posMiddle, Position posEnd, double tgtHeight) {
List<PathCandidate> newPathCands;
List<PathCandidate> leftPaths = new ArrayList<PathCandidate>();
List<PathCandidate> rightPaths = new ArrayList<PathCandidate>();
for(PathCandidate curPath : oldPaths) {
newPathCands = this.getPathCandsBeatWind(curPath, timeStep, turnLoss, posStart, posEnd, tgtHeight);
for (PathCandidate curNewPath : newPathCands) {
// check whether path is *outside* regatta-area
double distFromMiddleMeters = posMiddle.getDistance(curPath.pos.getPosition()).getMeters();
if (distFromMiddleMeters > oobFact * tgtHeight) {
continue; // ignore curPath
}
if (curNewPath.sid == 'L') {
leftPaths.add(curNewPath);
} else if (curNewPath.sid == 'R') {
rightPaths.add(curNewPath);
}
}
}
Util.Pair<List<PathCandidate>,List<PathCandidate>> newPaths = new Util.Pair<List<PathCandidate>,List<PathCandidate>>(leftPaths, rightPaths);
return newPaths;
}
List<PathCandidate> filterCandidates(List<PathCandidate> allCands, double hrzBinWidth) {
boolean[] filterMap = new boolean[allCands.size()];
// sort candidates by horizontal distance
Comparator<PathCandidate> sortHorizontal = new SortPathCandsHorizontally();
Collections.sort(allCands, sortHorizontal);
// start scan with index 0
int idxL = 0;
int idxR = 0;
// for each candidate, check the neighborhoods and identify bad candidates
for(int idx = 0; idx < allCands.size(); idx++) {
// current horizontal distance
double hrzDist = allCands.get(idx).hrz;
// align left index
while(Math.abs(hrzDist - allCands.get(idxL).hrz) > hrzBinWidth) {
idxL++;
}
// align right index
boolean finished = false;
while(!finished && (idxR < (allCands.size()-1))) {
if (Math.abs(hrzDist - allCands.get(idxR+1).hrz) <= hrzBinWidth) {
idxR++;
} else {
finished = true;
}
}
// search maximum height
// in neighborhood idxL, ..., idxR
// init max for search
int vrtIdx = idxL;
double vrtMax = allCands.get(vrtIdx).vrt;
filterMap[vrtIdx] = false;
// evaluate remainder of neighborhood
if (idxL < idxR) {
for(int jdx = (idxL+1); jdx <= idxR; jdx++) {
if (allCands.get(jdx).vrt > vrtMax) {
// reset previous max candidate
filterMap[vrtIdx] = true;
// keep max height
vrtMax = allCands.get(jdx).vrt;
// keep max index
vrtIdx = jdx;
// set current max candidate
filterMap[vrtIdx] = false;
} else {
filterMap[jdx] = true;
}
}
}
} // endfor each candidate
// collect all good candidates (i.e. filterMap == false)
List<PathCandidate> filterCands = new ArrayList<PathCandidate>();
for(int idx=0; idx < allCands.size(); idx++) {
if (!filterMap[idx]) {
filterCands.add(allCands.get(idx));
}
}
// return remaining good candidates
return filterCands;
}
List<PathCandidate> filterIsochrone(List<PathCandidate> allCands, double hrzBinWidth) {
boolean[] filterMap = new boolean[allCands.size()];
for(int idx = 0; idx < allCands.size(); idx++) {
filterMap[idx] = true;
}
// sort candidates by horizontal distance
Comparator<PathCandidate> sortHorizontal = new SortPathCandsHorizontally();
Collections.sort(allCands, sortHorizontal);
// start scan with index 0
int idxL = 0;
int idxR = 0;
// for each candidate, check the neighborhoods and identify bad candidates
for(int idx = 0; idx < allCands.size(); idx++) {
// current horizontal distance
double hrzDist = allCands.get(idx).hrz;
// align left index
while(Math.abs(hrzDist - allCands.get(idxL).hrz) > hrzBinWidth) {
idxL++;
}
// align right index
boolean finished = false;
while(!finished && (idxR < (allCands.size()-1))) {
if (Math.abs(hrzDist - allCands.get(idxR+1).hrz) <= hrzBinWidth) {
idxR++;
} else {
finished = true;
}
}
// search maximum height
// in neighborhood idxL, ..., idxR
// init max for search
ArrayList<Integer> vrtIdx = new ArrayList<Integer>();
vrtIdx.add(idxL);
double vrtMax = allCands.get(idxL).vrt;
// evaluate remainder of neighborhood
if (idxL < idxR) {
for(int jdx = (idxL+1); jdx <= idxR; jdx++) {
if (allCands.get(jdx).vrt > vrtMax) {
// keep max height
vrtMax = allCands.get(jdx).vrt;
// keep max index
vrtIdx = new ArrayList<Integer>();
vrtIdx.add(jdx);
} else if (allCands.get(jdx).vrt == vrtMax) {
// add further max indexes
vrtIdx.add(jdx);
}
}
}
for(Integer jdx : vrtIdx) {
filterMap[jdx] = false;
}
} // endfor each candidate
// collect all good candidates (i.e. filterMap == false)
List<PathCandidate> filterCands = new ArrayList<PathCandidate>();
for(int idx=0; idx < allCands.size(); idx++) {
if (!filterMap[idx]) {
filterCands.add(allCands.get(idx));
}
}
// return remaining good candidates
return filterCands;
}
@Override
public Path getPath() {
WindFieldGenerator wf = this.parameters.getWindField();
PolarDiagram pd = this.parameters.getBoatPolarDiagram();
Position startPos = this.parameters.getCourse().get(0);
Position endPos = this.parameters.getCourse().get(1);
// test downwind: exchange start and end
//Position startPos = this.parameters.getCourse().get(1);
//Position endPos = this.parameters.getCourse().get(0);
TimePoint startTime = wf.getStartTime();// new MillisecondsTimePoint(0);
List<TimedPositionWithSpeed> path = new ArrayList<TimedPositionWithSpeed>();
@@ -272,25 +499,42 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
Distance distStartEnd = startPos.getDistance(endPos);
double distStartEndMeters = distStartEnd.getMeters();
long timeStep = wf.getTimeStep().asMillis()/3;
logger.info("Time step :" + timeStep);
long turnLoss = pd.getTurnLoss(); // 4000; // time lost when doing a turn
Wind wndStart = wf.getWind(new TimedPositionWithSpeedImpl(startTime, startPos, null));
logger.fine("wndStart speed:" + wndStart.getKnots() + " angle:" + wndStart.getBearing().getDegrees());
pd.setWind(wndStart);
Bearing bearVrt = startPos.getBearingGreatCircle(endPos);
//Bearing bearHrz = bearVrt.add(new DegreeBearingImpl(90.0));
Position middlePos = startPos.translateGreatCircle(bearVrt, distStartEnd.scale(0.5));
Bearing bearRCWind = wndStart.getBearing().getDifferenceTo(bearVrt);
String legType = "downwind";
this.upwindLeg = false;
if ((Math.abs(bearRCWind.getDegrees()) > 90.0)&&(Math.abs(bearRCWind.getDegrees()) < 270.0)) {
legType = "upwind";
this.upwindLeg = true;
}
if (debugMsgOn) {
System.out.println("start : "+startPos.getLatDeg()+", "+startPos.getLngDeg());
System.out.println("middle: "+middlePos.getLatDeg()+", "+middlePos.getLngDeg());
System.out.println("end : "+endPos.getLatDeg()+", "+endPos.getLngDeg());
}
logger.info("Leg Direction: "+legType);
long timeStep = wf.getTimeStep().asMillis()/2;
if (!this.upwindLeg) {
timeStep = timeStep / 2;
}
this.usedTimeStep = timeStep;
logger.info("Time step :" + timeStep);
long turnLoss = pd.getTurnLoss(); // 4000; // time lost when doing a turn
if (!this.upwindLeg) {
turnLoss = turnLoss / 2;
}
// calculate initial position according to initPathStr
PathCandidate initPath = new PathCandidate(new TimedPositionImpl(currentTime, currentPosition), 0.0, 0.0, 0, "0");
PathCandidate initPath = new PathCandidate(new TimedPositionImpl(currentTime, currentPosition), 0.0, 0.0, 0, "0", '0', wndStart);
if (initPathStr.length()>1) {
char nextDirection = '0';
for(int idx=1; idx<initPathStr.length(); idx++) {
@@ -304,17 +548,17 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
allPaths.add(initPath);
TimedPosition tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, true, 'L').getA();
TimedPosition tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, true, 'L');
double tstDist1 = startPos.getDistance(tstPosition.getPosition()).getMeters();
tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, true, 'R').getA();
tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, true, 'R');
double tstDist2 = startPos.getDistance(tstPosition.getPosition()).getMeters();
double hrzBinSize = (tstDist1 + tstDist2)/4; // horizontal bin size in meters
double hrzBinSize = (tstDist1 + tstDist2)/6.0; // horizontal bin size in meters
if (debugMsgOn) {
System.out.println("Horizontal Bin Size: "+hrzBinSize);
}
double oobFact = 0.75; // out-of-bounds factor
//double oobFact = 0.75; // out-of-bounds factor
boolean reachedEnd = false;
int addSteps = 0;
int finalSteps = 0; // maximum number of additional steps after first target-path found
@@ -325,109 +569,123 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
addSteps++;
}
// generate new paths
double hrzMin = 0;
double hrzMax = 0;
List<PathCandidate> newPathCands;
List<PathCandidate> newPaths = new ArrayList<PathCandidate>();
for(PathCandidate curPath : allPaths) {
// generate new candidates (inside regatta-area)
Util.Pair<List<PathCandidate>,List<PathCandidate>> newPaths = this.generateCandidate(allPaths, timeStep, turnLoss, startPos, middlePos, endPos, distStartEndMeters);
if ((curPath.vrt > distStartEndMeters)) {
continue;
} else {
//newPathCands = this.getPathCandsBeat(curPath, timeStep, turnLoss, startPos, bearVrt, distStartEndMeters);
newPathCands = this.getPathCandsBeatWind(curPath, timeStep, turnLoss, startPos, endPos, distStartEndMeters);
// select good candidates
List<PathCandidate> leftPaths = this.filterCandidates(newPaths.getA(), hrzBinSize/2.0);
List<PathCandidate> rightPaths = this.filterCandidates(newPaths.getB(), hrzBinSize/2.0);
List<PathCandidate> nextPaths = new ArrayList<PathCandidate> ();
nextPaths.addAll(leftPaths);
nextPaths.addAll(rightPaths);
allPaths = nextPaths;
if (this.gridStore) {
/*ArrayList<TimedPosition> isoChrone = new ArrayList<TimedPosition>();
for(PathCand curCand : allPaths) {
isoChrone.add(curCand.pos);
}
this.gridPositions.add(isoChrone);*/
for(PathCandidate newPath : newPathCands) {
this.gridPositions.add(allPaths);
List<PathCandidate> isocPaths = this.filterIsochrone(allPaths, hrzBinSize);
this.isocPositions.add(isocPaths);
newPaths.add(newPath);
if (newPath.hrz < hrzMin) {
hrzMin = newPath.hrz;
}
if (newPath.hrz > hrzMax) {
hrzMax = newPath.hrz;
}
}
}
int hrzLeft = (int)Math.round(Math.floor( (hrzMin + hrzBinSize/2.0) / hrzBinSize ));
int hrzRight = (int)Math.round(Math.floor( (hrzMax + hrzBinSize/2.0) / hrzBinSize ));
//System.out.println("hrzLeft: "+hrzLeft+", hrzRight: "+hrzRight);
// check if there are still paths in the regatta-area
if (allPaths.size() > 0) {
// build map of best path per hrz-bin
int countPath = 0;
int mapSize = hrzRight-hrzLeft+1;
//System.out.println("MapSize: "+mapSize);
int[] binIdx = new int[mapSize]; // TODO: init with zero?
double[] binVal = new double[mapSize];
// TODO: init of binVal
/*for(int idx = 0; idx < mapSize; idx++) {
binVal[idx] = 2*distStartEndMeters;
}*/
for(int curIdx=0; curIdx<newPaths.size(); curIdx++) {
PathCandidate curPath = newPaths.get(curIdx);
//if (curPath.vrt > 3900) {
if (debugMsgOn) {
System.out.println(""+curPath.path+": "+curPath.hrz+", "+curPath.vrt+", "+curPath.pos.getPosition().getLatDeg()+", "+curPath.pos.getPosition().getLngDeg());
}
//}
// check whether path is *outside* regatta-area
double distFromMiddleMeters = middlePos.getDistance(curPath.pos.getPosition()).getMeters();
if (distFromMiddleMeters > oobFact*distStartEndMeters) {
continue; // ignore curPath
}
// increase path-counter
countPath++;
// determine map-key
int curKey = (int)Math.round(Math.floor( (curPath.hrz + hrzBinSize/2.0) / hrzBinSize )) - hrzLeft;
/*String allR = "0" + (new String(new char[curPath.path.length()-1]).replace('\0', 'R'));
if (curPath.path.equals(allR)) {
System.out.println(""+curPath.path+": "+curPath.hrz+", "+curPath.vrt+", "+curPath.pos.getPosition().getLatDeg()+", "+curPath.pos.getPosition().getLngDeg());
}*/
// check whether curPath is better then the ones looked at
if ((curKey>=0)&&(curKey < mapSize)) {
if (curPath.vrt > binVal[curKey]) {
binVal[curKey] = curPath.vrt;
binIdx[curKey] = curIdx+1;
}
}
}
// check if there are still paths inside regatta-area
if (countPath > 0) {
// take best from each horizontal-bin and check if target is reached
allPaths = new ArrayList<PathCandidate>();
for (int curKey = 0; curKey < mapSize; curKey++) {
if (binIdx[curKey] > 0) {
PathCandidate curPath = newPaths.get(binIdx[curKey] - 1);
allPaths.add(curPath);
// debug output
/*if ((Math.abs(curKey + hrzLeft) <= 10)) {
System.out.println("" + curPath.path + ": " + (curKey + hrzLeft) + ", vrt:" + curPath.vrt + ", hrz:" + curPath.hrz + ", bin:" + (Math.floor((curPath.hrz + hrzBinSize / 2.0) / hrzBinSize)));
}*/
// terminate path-search if paths close enough to target are found
if ((curPath.vrt > distStartEndMeters)) {
if ((Math.abs(curKey + hrzLeft) <= 3)) {
reachedEnd = true;
trgPaths.add(curPath); // add path to list of target-paths
}
for(PathCandidate curPath : allPaths) {
// terminate path-search if paths are found that are close enough to target
//if ((curPath.vrt > distStartEndMeters)) {
if ((curPath.vrt > 0.0)) {
//logger.info("\ntPath: " + curPath.path + "\n Time: " + (Math.round((curPath.pos.getTimePoint().asMillis()-startTime.asMillis())/1000.0/60.0*10.0)/10.0)+", Height: "+curPath.vrt+" of "+(Math.round(startPos.getDistance(endPos).getMeters()*100.0)/100.0)+", Dist: "+curPath.hrz+"m ~ "+(Math.round(curPath.pos.getPosition().getDistance(endPos).getMeters()*100.0)/100.0)+"m");
int curBin = (int)Math.round(Math.floor( (curPath.hrz + hrzBinSize/2.0) / hrzBinSize ));
if ((Math.abs(curBin) <= 2)) {
reachedEnd = true;
trgPaths.add(curPath); // add path to list of target-paths
}
}
}
} else {
// terminate path-search as no path inside regatta-area are left
reachedEnd = true;
}
}
if (this.gridStore) {
double distResolution = distStartEndMeters*0.01;
BufferedWriter outputCSV;
try {
outputCSV = new BufferedWriter(new FileWriter(this.gridFile+"-grid.csv"));
outputCSV.write("step; lat; lng; time; side; path; vrt\n");
outputCSV.write("0; "+startPos.getLatDeg()+"; "+startPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; "+(-distStartEndMeters)+"\n");
outputCSV.write("0; "+endPos.getLatDeg()+"; "+endPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; 0\n");
int stepCount = 0;
for(List<PathCandidate> isoChrone : this.gridPositions) {
stepCount++;
PathCandidate prevPos = null;
for(PathCandidate isoPos : isoChrone) {
if (prevPos != null) {
if (prevPos.pos.getPosition().getDistance(isoPos.pos.getPosition()).getMeters() < distResolution) {
continue;
}
}
String outStr = ""+stepCount+"; "+isoPos.pos.getPosition().getLatDeg()+"; "+isoPos.pos.getPosition().getLngDeg()+"; "+(isoPos.pos.getTimePoint().asMillis()/1000)+"; "+isoPos.sid;
outStr += "; "+isoPos.path+"; "+isoPos.vrt;
outStr += "\n";
outputCSV.write(outStr);
prevPos = isoPos;
}
}
outputCSV.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
outputCSV = new BufferedWriter(new FileWriter(this.gridFile+"-isoc.csv"));
outputCSV.write("step; lat; lng; time; side; path; vrt\n");
outputCSV.write("0; "+startPos.getLatDeg()+"; "+startPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; "+(-distStartEndMeters)+"\n");
outputCSV.write("0; "+endPos.getLatDeg()+"; "+endPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; 0\n");
int stepCount = 0;
for(List<PathCandidate> isoChrone : this.isocPositions) {
stepCount++;
PathCandidate prevPos = null;
for(PathCandidate isoPos : isoChrone) {
if (prevPos != null) {
if (prevPos.pos.getPosition().getDistance(isoPos.pos.getPosition()).getMeters() < distResolution) {
continue;
}
}
String outStr = ""+stepCount+"; "+isoPos.pos.getPosition().getLatDeg()+"; "+isoPos.pos.getPosition().getLngDeg()+"; "+(isoPos.pos.getTimePoint().asMillis()/1000)+"; "+isoPos.sid;
outStr += "; "+isoPos.path+"; "+isoPos.vrt;
outStr += "\n";
outputCSV.write(outStr);
prevPos = isoPos;
}
}
outputCSV.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@@ -443,13 +701,11 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
Collections.sort(trgPaths);
// debug output
//if (debugMsgOn) {
for(PathCandidate curPath : trgPaths) {
logger.info("\nPath: " + curPath.path + "\n Time: " + (Math.round((curPath.pos.getTimePoint().asMillis()-startTime.asMillis())/1000.0/60.0*10.0)/10.0)+", Height: "+curPath.vrt+" of "+(Math.round(startPos.getDistance(endPos).getMeters()*100.0)/100.0)+", Dist: "+curPath.hrz+"m ~ "+(Math.round(curPath.pos.getPosition().getDistance(endPos).getMeters()*100.0)/100.0)+"m");
//System.out.print(""+curPath.path+": "+curPath.pos.getTimePoint().asMillis()+", "+curPath.pos.getPosition().getLatDeg()+", "+curPath.pos.getPosition().getLngDeg()+", ");
//System.out.println(" height:"+curPath.vrt+" of "+startPos.getDistance(endPos).getMeters()+", dist:"+curPath.hrz+" ~ "+curPath.pos.getPosition().getDistance(endPos));
}
//}
//
// fill gwt-path
@@ -472,7 +728,7 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase {
} else {
boolean sameBaseDirection = this.isSameDirection(prevDirection, nextDirection);
TimedPosition newPosition = this.getStep(curPosition, timeStep, turnLoss, sameBaseDirection, nextDirection).getA();
TimedPosition newPosition = this.getStep(curPosition, timeStep, turnLoss, sameBaseDirection, nextDirection);
curPosition = new TimedPositionWithSpeedImpl(newPosition.getTimePoint(), newPosition.getPosition(), null);
path.add(curPosition);
@@ -1,566 +0,0 @@
package com.sap.sailing.simulator.impl;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;
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.SpeedWithBearing;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.simulator.Path;
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.WindFieldGenerator;
import com.sap.sse.common.Util;
public class PathGeneratorTreeGrowWind2 extends PathGeneratorBase {
private static Logger logger = Logger.getLogger("com.sap.sailing");
private boolean debugMsgOn = false;
double oobFact = 0.75; // out-of-bounds factor
int maxTurns = 0;
String initPathStr = "0";
PathCandidate bestCand = null;
boolean gridStore = false;
ArrayList<List<PathCandidate>> gridPositions = null;
String gridFile = null;
public PathGeneratorTreeGrowWind2(SimulationParameters params) {
this.parameters = params;
}
public void setEvaluationParameters(String startDirection, int maxTurns, String gridFile) {
if (startDirection != null) {
this.initPathStr = "0" + startDirection;
} else {
this.initPathStr = "0";
}
this.maxTurns = maxTurns;
this.gridFile = gridFile;
if (this.gridFile != null) {
this.gridStore = true;
this.gridPositions = new ArrayList<List<PathCandidate>>();
} else {
this.gridStore = false;
this.gridPositions = null;
}
}
class SortPathCandsAbsHorizontally implements Comparator<PathCandidate> {
@Override
public int compare(PathCandidate p1, PathCandidate p2) {
if (Math.abs(p1.hrz) == Math.abs(p2.hrz)) {
return 0;
} else {
return (Math.abs(p1.hrz) < Math.abs(p2.hrz) ? -1 : +1);
}
}
}
class SortPathCandsHorizontally implements Comparator<PathCandidate> {
@Override
public int compare(PathCandidate p1, PathCandidate p2) {
if (p1.hrz == p2.hrz) {
return 0;
} else {
return (p1.hrz < p2.hrz ? -1 : +1);
}
}
}
// getter for evaluating best path cand propoerties further
PathCandidate getBestCand() {
return this.bestCand;
}
// generate step in one of the possible directions
// default: L - left, R - right
// extended: M - wide left, S - wide right
Util.Pair<TimedPosition,Wind> getStep(TimedPosition pos, long timeStep, long turnLoss, boolean sameBaseDirection, char nextDirection) {
double offDeg = 3.0;
WindFieldGenerator wf = this.parameters.getWindField();
TimePoint curTime = pos.getTimePoint();
Position curPosition = pos.getPosition();
Wind posWind = wf.getWind(new TimedPositionWithSpeedImpl(curTime, curPosition, null));
PolarDiagram pd = this.parameters.getBoatPolarDiagram();
pd.setWind(posWind);
Wind appWind = new WindImpl(posWind.getPosition(), posWind.getTimePoint(), pd.getWind());;
// get beat-angle left and right
Bearing travelBearing = null;
Bearing tmpBearing = null;
if (nextDirection == 'L') {
travelBearing = pd.optimalDirectionsUpwind()[0];
} else if (nextDirection == 'R') {
travelBearing = pd.optimalDirectionsUpwind()[1];
} else if (nextDirection == 'M') {
tmpBearing = pd.optimalDirectionsUpwind()[0];
travelBearing = tmpBearing.add(new DegreeBearingImpl(-offDeg));
} else if (nextDirection == 'S') {
tmpBearing = pd.optimalDirectionsUpwind()[1];
travelBearing = tmpBearing.add(new DegreeBearingImpl(+offDeg));
}
// determine beat-speed left and right
SpeedWithBearing travelSpeed = pd.getSpeedAtBearing(travelBearing);
TimePoint travelTime;
TimePoint nextTime = new MillisecondsTimePoint(curTime.asMillis()+timeStep);
if (sameBaseDirection) {
travelTime = nextTime;
} else {
travelTime = new MillisecondsTimePoint(nextTime.asMillis() - turnLoss);
}
return new Util.Pair<TimedPosition,Wind>(new TimedPositionImpl(nextTime, travelSpeed.travelTo(curPosition, curTime, travelTime)), appWind);
}
// use base direction to distinguish direction changes that do or don't require a turn
char getBaseDirection(char direction) {
char baseDirection = direction;
if (direction == 'M') {
baseDirection = 'L';
}
if (direction == 'S') {
baseDirection = 'R';
}
return baseDirection;
}
// check whether nextDirection is same base direction as previous direction, i.e. no turn
boolean isSameDirection(char prevDirection, char nextDirection) {
char prevBaseDirection = this.getBaseDirection(prevDirection);
char nextBaseDirection = this.getBaseDirection(nextDirection);
return ((nextBaseDirection == prevBaseDirection)||(prevBaseDirection == '0'));
}
// get path candidate measuring height towards (local, current-apparent) wind
PathCandidate getPathCandWind(PathCandidate path, char nextDirection, long timeStep, long turnLoss, Position posStart, Position posEnd, double tgtHeight) {
char prevDirection = path.path.charAt(path.path.length()-1);
boolean sameBaseDirection = this.isSameDirection(prevDirection, nextDirection);
int turnCount = path.trn;
if (!sameBaseDirection) {
turnCount++;
}
// calculate next path position (taking turn-loss into account)
Util.Pair<TimedPosition,Wind> nextStep = this.getStep(path.pos, timeStep, turnLoss, sameBaseDirection, nextDirection);
TimedPosition pathPos = nextStep.getA();
Wind posWind = nextStep.getB();
// calculate height-position with reference to race course
Position posHeight = pathPos.getPosition().projectToLineThrough(posEnd, posWind.getBearing());
Bearing bearVrt = posStart.getBearingGreatCircle(posEnd);
Position posHeightTrgt = pathPos.getPosition().projectToLineThrough(posStart, bearVrt);
//Position posHeightWind = pathPos.getPosition().projectToLineThrough(posRef, posWind.getBearing());
// calculate vertical distance as distance of height-position to start
//double vrtDist = Math.round(posHeightTrgt.getDistance(posStart).getMeters()*100.0)/100.0;
double vrtDist = Math.round(posHeight.getDistance(posStart).getMeters()*100.0)/100.0;
/*if (vrtDist > tgtHeight) {
// scale last step so that vrtDist ~ tgtHeight
Position prevPos = path.pos.getPosition();
TimePoint prevTime = path.pos.getTimePoint();
double heightFrac = (tgtHeight - path.vrt) / (vrtDist - path.vrt);
Position newPos = prevPos.translateGreatCircle(prevPos.getBearingGreatCircle(pathPos.getPosition()), prevPos.getDistance(pathPos.getPosition()).scale(heightFrac));
TimePoint newTime = new MillisecondsTimePoint(Math.round(prevTime.asMillis() + (pathPos.getTimePoint().asMillis()-prevTime.asMillis())*heightFrac));
pathPos = new TimedPositionImpl(newTime, newPos);
posHeight = pathPos.getPosition().projectToLineThrough(posStart, bearVrt);
}*/
// calculate horizontal side: left or right in reference to race course
double posSide = 1;
//double posBear = posWind.getBearing().getDegrees() - posEnd.getBearingGreatCircle(pathPos.getPosition()).getDegrees();
Bearing posBear = posStart.getBearingGreatCircle(pathPos.getPosition());
double posBearDiff = bearVrt.getDifferenceTo(posBear).getDegrees();
if ((posBearDiff < 0.0)||(posBearDiff > 180.0)) {
posSide = -1;
} else if ((posBearDiff == 0.0)||(posBearDiff == 180.0)) {
posSide = 0;
}
// calculate horizontal distance as distance of height-position to current position
//double hrzDist = Math.round(posSide*posHeight.getDistance(pathPos.getPosition()).getMeters()*100.0)/100.0;
double hrzDist = Math.round(posSide*posHeightTrgt.getDistance(pathPos.getPosition()).getMeters()*100.0)/100.0;
//System.out.println(""+hrzDist+", "+vrtDist+", "+pathPos.getPosition().getLatDeg()+", "+pathPos.getPosition().getLngDeg()+", "+posHeight.getLatDeg()+", "+posHeight.getLngDeg());
// extend path-string by step-direction
String pathStr = path.path + nextDirection;
char nextBaseDirection = this.getBaseDirection(nextDirection);
return (new PathCandidate(pathPos, vrtDist, hrzDist, turnCount, pathStr, nextBaseDirection, posWind));
}
// generate path candidates based on beat angles
List<PathCandidate> getPathCandsBeatWind(PathCandidate path, long timeStep, long turnLoss, Position posStart, Position posEnd, double tgtHeight) {
List<PathCandidate> result = new ArrayList<PathCandidate>();
PathCandidate newPathCand;
if (this.maxTurns > 0) {
char prevDirection = path.path.charAt(path.path.length()-1);
if ((path.trn < this.maxTurns)||(this.isSameDirection(prevDirection, 'L'))) {
newPathCand = getPathCandWind(path, 'L', timeStep, turnLoss, posStart, posEnd, tgtHeight);
result.add(newPathCand);
}
if ((path.trn < this.maxTurns)||(this.isSameDirection(prevDirection, 'R'))) {
newPathCand = getPathCandWind(path, 'R', timeStep, turnLoss, posStart, posEnd, tgtHeight);
result.add(newPathCand);
}
} else {
// step left
newPathCand = getPathCandWind(path, 'L', timeStep, turnLoss, posStart, posEnd, tgtHeight);
result.add(newPathCand);
// step wide left
//newPathCand = getPathCandWind(path, 'M', timeStep, turnLoss, posStart, posEnd, tgtHeight);
//result.add(newPathCand);
// step right
newPathCand = getPathCandWind(path, 'R', timeStep, turnLoss, posStart, posEnd, tgtHeight);
result.add(newPathCand);
// step wide right
//newPathCand = getPathCandWind(path, 'S', timeStep, turnLoss, posStart, posEnd, tgtHeight);
//result.add(newPathCand);
}
return result;
}
Util.Pair<List<PathCandidate>,List<PathCandidate>> generateCandidate(List<PathCandidate> oldPaths, long timeStep, long turnLoss, Position posStart, Position posMiddle, Position posEnd, double tgtHeight) {
List<PathCandidate> newPathCands;
List<PathCandidate> leftPaths = new ArrayList<PathCandidate>();
List<PathCandidate> rightPaths = new ArrayList<PathCandidate>();
for(PathCandidate curPath : oldPaths) {
newPathCands = this.getPathCandsBeatWind(curPath, timeStep, turnLoss, posStart, posEnd, tgtHeight);
for (PathCandidate curNewPath : newPathCands) {
// check whether path is *outside* regatta-area
double distFromMiddleMeters = posMiddle.getDistance(curPath.pos.getPosition()).getMeters();
if (distFromMiddleMeters > oobFact * tgtHeight) {
continue; // ignore curPath
}
if (curNewPath.sid == 'L') {
leftPaths.add(curNewPath);
} else if (curNewPath.sid == 'R') {
rightPaths.add(curNewPath);
}
}
}
Util.Pair<List<PathCandidate>,List<PathCandidate>> newPaths = new Util.Pair<List<PathCandidate>,List<PathCandidate>>(leftPaths, rightPaths);
return newPaths;
}
List<PathCandidate> filterCandidates(List<PathCandidate> allCands, double hrzBinWidth) {
boolean[] filterMap = new boolean[allCands.size()];
// sort candidates by horizontal distance
Comparator<PathCandidate> sortHorizontal = new SortPathCandsHorizontally();
Collections.sort(allCands, sortHorizontal);
// start scan with index 0
int idxL = 0;
int idxR = 0;
// for each candidate, check the neighborhoos and identify bad candidates
for(int idx = 0; idx < allCands.size(); idx++) {
// current horizontal distance
double hrzDist = allCands.get(idx).hrz;
// align left index
while(Math.abs(hrzDist - allCands.get(idxL).hrz) > hrzBinWidth) {
idxL++;
}
// align right index
boolean finished = false;
while(!finished && (idxR < (allCands.size()-1))) {
if (Math.abs(hrzDist - allCands.get(idxR+1).hrz) <= hrzBinWidth) {
idxR++;
} else {
finished = true;
}
}
// search maximum height
// in neighborhood idxL, ..., idxR
// init max for search
int vrtIdx = idxL;
double vrtMax = allCands.get(vrtIdx).vrt;
filterMap[vrtIdx] = false;
// evaluate remainder of neighborhood
if (idxL < idxR) {
for(int jdx = (idxL+1); jdx <= idxR; jdx++) {
if (allCands.get(jdx).vrt > vrtMax) {
// reset previous max candidate
filterMap[vrtIdx] = true;
// keep max height
vrtMax = allCands.get(jdx).vrt;
// keep max index
vrtIdx = jdx;
// set current max candidate
filterMap[vrtIdx] = false;
} else {
filterMap[jdx] = true;
}
}
}
} // endfor each candidate
// collect all good candidates (i.e. filterMap == false)
List<PathCandidate> filterCands = new ArrayList<PathCandidate>();
for(int idx=0; idx < allCands.size(); idx++) {
if (!filterMap[idx]) {
filterCands.add(allCands.get(idx));
}
}
// return remaining good candidates
return filterCands;
}
@Override
public Path getPath() {
WindFieldGenerator wf = this.parameters.getWindField();
PolarDiagram pd = this.parameters.getBoatPolarDiagram();
Position startPos = this.parameters.getCourse().get(0);
Position endPos = this.parameters.getCourse().get(1);
TimePoint startTime = wf.getStartTime();// new MillisecondsTimePoint(0);
List<TimedPositionWithSpeed> path = new ArrayList<TimedPositionWithSpeed>();
Position currentPosition = startPos;
TimePoint currentTime = startTime;
Distance distStartEnd = startPos.getDistance(endPos);
double distStartEndMeters = distStartEnd.getMeters();
long timeStep = wf.getTimeStep().asMillis()/3;
logger.info("Time step :" + timeStep);
long turnLoss = pd.getTurnLoss(); // 4000; // time lost when doing a turn
Wind wndStart = wf.getWind(new TimedPositionWithSpeedImpl(startTime, startPos, null));
logger.fine("wndStart speed:" + wndStart.getKnots() + " angle:" + wndStart.getBearing().getDegrees());
pd.setWind(wndStart);
Bearing bearVrt = startPos.getBearingGreatCircle(endPos);
//Bearing bearHrz = bearVrt.add(new DegreeBearingImpl(90.0));
Position middlePos = startPos.translateGreatCircle(bearVrt, distStartEnd.scale(0.5));
if (debugMsgOn) {
System.out.println("start : "+startPos.getLatDeg()+", "+startPos.getLngDeg());
System.out.println("middle: "+middlePos.getLatDeg()+", "+middlePos.getLngDeg());
System.out.println("end : "+endPos.getLatDeg()+", "+endPos.getLngDeg());
}
// calculate initial position according to initPathStr
PathCandidate initPath = new PathCandidate(new TimedPositionImpl(currentTime, currentPosition), 0.0, 0.0, 0, "0", '0', wndStart);
if (initPathStr.length()>1) {
char nextDirection = '0';
for(int idx=1; idx<initPathStr.length(); idx++) {
nextDirection = initPathStr.charAt(idx);
PathCandidate newPathCand = getPathCandWind(initPath, nextDirection, timeStep, turnLoss, startPos, endPos, distStartEndMeters);
initPath = newPathCand;
}
}
List<PathCandidate> allPaths = new ArrayList<PathCandidate>();
List<PathCandidate> trgPaths = new ArrayList<PathCandidate>();
allPaths.add(initPath);
TimedPosition tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, true, 'L').getA();
double tstDist1 = startPos.getDistance(tstPosition.getPosition()).getMeters();
tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, true, 'R').getA();
double tstDist2 = startPos.getDistance(tstPosition.getPosition()).getMeters();
double hrzBinSize = (tstDist1 + tstDist2)/2.0; // horizontal bin size in meters
if (debugMsgOn) {
System.out.println("Horizontal Bin Size: "+hrzBinSize);
}
//double oobFact = 0.75; // out-of-bounds factor
boolean reachedEnd = false;
int addSteps = 0;
int finalSteps = 0; // maximum number of additional steps after first target-path found
while ((!reachedEnd)||(addSteps<finalSteps)) {
if (reachedEnd) {
addSteps++;
}
// generate new candidates (inside regatta-area)
Util.Pair<List<PathCandidate>,List<PathCandidate>> newPaths = this.generateCandidate(allPaths, timeStep, turnLoss, startPos, middlePos, endPos, distStartEndMeters);
// select good candidates
List<PathCandidate> leftPaths = this.filterCandidates(newPaths.getA(), hrzBinSize/2.0);
List<PathCandidate> rightPaths = this.filterCandidates(newPaths.getB(), hrzBinSize/2.0);
List<PathCandidate> nextPaths = new ArrayList<PathCandidate> ();
nextPaths.addAll(leftPaths);
nextPaths.addAll(rightPaths);
allPaths = nextPaths;
if (this.gridStore) {
/*ArrayList<TimedPosition> isoChrone = new ArrayList<TimedPosition>();
for(PathCand curCand : allPaths) {
isoChrone.add(curCand.pos);
}
this.gridPositions.add(isoChrone);*/
this.gridPositions.add(allPaths);
}
// check if there are still paths in the regatta-area
if (allPaths.size() > 0) {
for(PathCandidate curPath : allPaths) {
// terminate path-search if paths are found that are close enough to target
if ((curPath.vrt > distStartEndMeters)) {
int curBin = (int)Math.round(Math.floor( (curPath.hrz + hrzBinSize/2.0) / hrzBinSize ));
if ((Math.abs(curBin) <= 2)) {
reachedEnd = true;
trgPaths.add(curPath); // add path to list of target-paths
}
}
}
} else {
// terminate path-search as no path inside regatta-area are left
reachedEnd = true;
}
}
if (this.gridStore) {
BufferedWriter outputCSV;
try {
outputCSV = new BufferedWriter(new FileWriter(this.gridFile));
outputCSV.write("step; lat; lng; time; side\n");
int stepCount = 0;
for(List<PathCandidate> isoChrone : this.gridPositions) {
stepCount++;
for(PathCandidate isoPos : isoChrone) {
outputCSV.write(""+stepCount+"; "+isoPos.pos.getPosition().getLatDeg()+"; "+isoPos.pos.getPosition().getLngDeg()+"; "+(isoPos.pos.getTimePoint().asMillis()/1000)+"; "+isoPos.sid+"\n");
}
}
outputCSV.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// if no target-paths were found, return empty path
if (trgPaths.size() == 0) {
//trgPaths = allPaths; // TODO: only for testing; remove lateron
TimedPositionWithSpeed curPosition = new TimedPositionWithSpeedImpl(startTime, startPos, null);
path.add(curPosition);
return new PathImpl(path, wf); // return empty path
}
// sort target-paths ascending by distance-to-target
Collections.sort(trgPaths);
// debug output
//if (debugMsgOn) {
for(PathCandidate curPath : trgPaths) {
logger.info("\nPath: " + curPath.path + "\n Time: " + (Math.round((curPath.pos.getTimePoint().asMillis()-startTime.asMillis())/1000.0/60.0*10.0)/10.0)+", Height: "+curPath.vrt+" of "+(Math.round(startPos.getDistance(endPos).getMeters()*100.0)/100.0)+", Dist: "+curPath.hrz+"m ~ "+(Math.round(curPath.pos.getPosition().getDistance(endPos).getMeters()*100.0)/100.0)+"m");
//System.out.print(""+curPath.path+": "+curPath.pos.getTimePoint().asMillis()+", "+curPath.pos.getPosition().getLatDeg()+", "+curPath.pos.getPosition().getLngDeg()+", ");
//System.out.println(" height:"+curPath.vrt+" of "+startPos.getDistance(endPos).getMeters()+", dist:"+curPath.hrz+" ~ "+curPath.pos.getPosition().getDistance(endPos));
}
//}
//
// fill gwt-path
//
// generate intermediate steps
bestCand = trgPaths.get(0); // target-path ending closest to target
TimedPositionWithSpeed curPosition = null;
char nextDirection = '0';
char prevDirection = '0';
for(int step=0; step<(bestCand.path.length()-1); step++) {
nextDirection = bestCand.path.charAt(step);
if (nextDirection == '0') {
curPosition = new TimedPositionWithSpeedImpl(startTime, startPos, null);
path.add(curPosition);
} else {
boolean sameBaseDirection = this.isSameDirection(prevDirection, nextDirection);
TimedPosition newPosition = this.getStep(curPosition, timeStep, turnLoss, sameBaseDirection, nextDirection).getA();
curPosition = new TimedPositionWithSpeedImpl(newPosition.getTimePoint(), newPosition.getPosition(), null);
path.add(curPosition);
}
prevDirection = nextDirection;
}
// add final position (rescaled before to end on height of target)
path.add(new TimedPositionWithSpeedImpl(bestCand.pos.getTimePoint(), bestCand.pos.getPosition(), null));
return new PathImpl(path, wf);
}
}
@@ -1,751 +0,0 @@
package com.sap.sailing.simulator.impl;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;
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.SpeedWithBearing;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.simulator.Path;
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.WindFieldGenerator;
import com.sap.sse.common.Util;
public class PathGeneratorTreeGrowWind3 extends PathGeneratorBase {
private static Logger logger = Logger.getLogger("com.sap.sailing");
private boolean debugMsgOn = false;
double oobFact = 0.75; // out-of-bounds factor
int maxTurns = 0;
boolean upwindLeg = false;
String initPathStr = "0";
PathCandidate bestCand = null;
long usedTimeStep = 0;
boolean gridStore = false;
ArrayList<List<PathCandidate>> gridPositions = null;
ArrayList<List<PathCandidate>> isocPositions = null;
String gridFile = null;
public PathGeneratorTreeGrowWind3(SimulationParameters params) {
this.parameters = params;
}
public void setEvaluationParameters(String startDirection, int maxTurns, String gridFile) {
if (startDirection != null) {
this.initPathStr = "0" + startDirection;
} else {
this.initPathStr = "0";
}
this.maxTurns = maxTurns;
this.gridFile = gridFile;
if (this.gridFile != null) {
this.gridStore = true;
this.gridPositions = new ArrayList<List<PathCandidate>>();
this.isocPositions = new ArrayList<List<PathCandidate>>();
} else {
this.gridStore = false;
this.gridPositions = null;
this.isocPositions = null;
}
}
class SortPathCandsAbsHorizontally implements Comparator<PathCandidate> {
@Override
public int compare(PathCandidate p1, PathCandidate p2) {
if (Math.abs(p1.hrz) == Math.abs(p2.hrz)) {
return 0;
} else {
return (Math.abs(p1.hrz) < Math.abs(p2.hrz) ? -1 : +1);
}
}
}
class SortPathCandsHorizontally implements Comparator<PathCandidate> {
@Override
public int compare(PathCandidate p1, PathCandidate p2) {
if (p1.hrz == p2.hrz) {
return 0;
} else {
return (p1.hrz < p2.hrz ? -1 : +1);
}
}
}
// getter for evaluating best path cand propoerties further
PathCandidate getBestCand() {
return this.bestCand;
}
long getUsedTimeStep() {
return this.usedTimeStep;
}
// generate step in one of the possible directions
// default: L - left, R - right
// extended: M - wide left, S - wide right
Util.Pair<TimedPosition,Wind> getStep(TimedPosition pos, long timeStep, long turnLoss, boolean sameBaseDirection, char nextDirection) {
double offDeg = 3.0;
WindFieldGenerator wf = this.parameters.getWindField();
TimePoint curTime = pos.getTimePoint();
Position curPosition = pos.getPosition();
Wind posWind = wf.getWind(new TimedPositionWithSpeedImpl(curTime, curPosition, null));
PolarDiagram pd = this.parameters.getBoatPolarDiagram();
pd.setWind(posWind);
Wind appWind = new WindImpl(posWind.getPosition(), posWind.getTimePoint(), pd.getWind());;
// get beat-angle left and right
Bearing travelBearing = null;
Bearing tmpBearing = null;
if (nextDirection == 'L') {
if (this.upwindLeg) {
travelBearing = pd.optimalDirectionsUpwind()[0];
} else {
travelBearing = pd.optimalDirectionsDownwind()[0];
}
} else if (nextDirection == 'R') {
if (this.upwindLeg) {
travelBearing = pd.optimalDirectionsUpwind()[1];
} else {
travelBearing = pd.optimalDirectionsDownwind()[1];
}
} else if (nextDirection == 'M') {
if (this.upwindLeg) {
tmpBearing = pd.optimalDirectionsUpwind()[0];
travelBearing = tmpBearing.add(new DegreeBearingImpl(-offDeg));
} else {
tmpBearing = pd.optimalDirectionsDownwind()[0];
travelBearing = tmpBearing.add(new DegreeBearingImpl(-offDeg));
}
} else if (nextDirection == 'S') {
if (this.upwindLeg) {
tmpBearing = pd.optimalDirectionsUpwind()[1];
travelBearing = tmpBearing.add(new DegreeBearingImpl(+offDeg));
} else {
tmpBearing = pd.optimalDirectionsDownwind()[1];
travelBearing = tmpBearing.add(new DegreeBearingImpl(+offDeg));
}
}
// determine beat-speed left and right
SpeedWithBearing travelSpeed = pd.getSpeedAtBearing(travelBearing);
TimePoint travelTime;
TimePoint nextTime = new MillisecondsTimePoint(curTime.asMillis()+timeStep);
if (sameBaseDirection) {
travelTime = nextTime;
} else {
travelTime = new MillisecondsTimePoint(nextTime.asMillis() - turnLoss);
}
return new Util.Pair<TimedPosition,Wind>(new TimedPositionImpl(nextTime, travelSpeed.travelTo(curPosition, curTime, travelTime)), appWind);
}
// use base direction to distinguish direction changes that do or don't require a turn
char getBaseDirection(char direction) {
char baseDirection = direction;
if (direction == 'M') {
baseDirection = 'L';
}
if (direction == 'S') {
baseDirection = 'R';
}
return baseDirection;
}
// check whether nextDirection is same base direction as previous direction, i.e. no turn
boolean isSameDirection(char prevDirection, char nextDirection) {
char prevBaseDirection = this.getBaseDirection(prevDirection);
char nextBaseDirection = this.getBaseDirection(nextDirection);
return ((nextBaseDirection == prevBaseDirection)||(prevBaseDirection == '0'));
}
// get path candidate measuring height towards (local, current-apparent) wind
PathCandidate getPathCandWind(PathCandidate path, char nextDirection, long timeStep, long turnLoss, Position posStart, Position posEnd, double tgtHeight) {
char prevDirection = path.path.charAt(path.path.length()-1);
boolean sameBaseDirection = this.isSameDirection(prevDirection, nextDirection);
int turnCount = path.trn;
if (!sameBaseDirection) {
turnCount++;
}
// calculate next path position (taking turn-loss into account)
Util.Pair<TimedPosition,Wind> nextStep = this.getStep(path.pos, timeStep, turnLoss, sameBaseDirection, nextDirection);
TimedPosition pathPos = nextStep.getA();
Wind posWind = nextStep.getB();
// calculate height-position with reference to race course
Position posHeight = pathPos.getPosition().projectToLineThrough(posEnd, posWind.getBearing());
Bearing bearVrt = posStart.getBearingGreatCircle(posEnd);
Position posHeightTrgt = pathPos.getPosition().projectToLineThrough(posStart, bearVrt);
// calculate vertical distance as distance of height-position to start
//double vrtDist = Math.round(posHeightTrgt.getDistance(posStart).getMeters()*100.0)/100.0;
Bearing bearHeight = posEnd.getBearingGreatCircle(posHeight);
double bearHeightSide = posWind.getBearing().getDifferenceTo(bearHeight).getDegrees();
/*if (Math.abs(bearHeightSide) > 5.0) {
System.out.println("bearHeightSide: "+bearHeightSide);
}*/
double vrtSide = (this.upwindLeg ? -1.0 : +1.0);
if (Math.abs(bearHeightSide) > 170.0) {
vrtSide = (this.upwindLeg ? +1.0 : -1.0);
}
double vrtDist = vrtSide*Math.round(posHeight.getDistance(posEnd).getMeters()*1000.0)/1000.0;
/*if (vrtDist > tgtHeight) {
// scale last step so that vrtDist ~ tgtHeight
Position prevPos = path.pos.getPosition();
TimePoint prevTime = path.pos.getTimePoint();
double heightFrac = (tgtHeight - path.vrt) / (vrtDist - path.vrt);
Position newPos = prevPos.translateGreatCircle(prevPos.getBearingGreatCircle(pathPos.getPosition()), prevPos.getDistance(pathPos.getPosition()).scale(heightFrac));
TimePoint newTime = new MillisecondsTimePoint(Math.round(prevTime.asMillis() + (pathPos.getTimePoint().asMillis()-prevTime.asMillis())*heightFrac));
pathPos = new TimedPositionImpl(newTime, newPos);
posHeight = pathPos.getPosition().projectToLineThrough(posStart, bearVrt);
}*/
// calculate horizontal side: left or right in reference to race course
double posSide = 1;
//double posBear = posWind.getBearing().getDegrees() - posEnd.getBearingGreatCircle(pathPos.getPosition()).getDegrees();
Bearing posBear = posStart.getBearingGreatCircle(pathPos.getPosition());
double posBearDiff = bearVrt.getDifferenceTo(posBear).getDegrees();
if ((posBearDiff < 0.0)||(posBearDiff > 180.0)) {
posSide = -1;
} else if ((posBearDiff == 0.0)||(posBearDiff == 180.0)) {
posSide = 0;
}
// calculate horizontal distance as distance of height-position to current position
//double hrzDist = Math.round(posSide*posHeight.getDistance(pathPos.getPosition()).getMeters()*100.0)/100.0;
double hrzDist = Math.round(posSide*posHeightTrgt.getDistance(pathPos.getPosition()).getMeters()*1000.0)/1000.0;
//System.out.println(""+hrzDist+", "+vrtDist+", "+pathPos.getPosition().getLatDeg()+", "+pathPos.getPosition().getLngDeg()+", "+posHeight.getLatDeg()+", "+posHeight.getLngDeg());
// extend path-string by step-direction
String pathStr = path.path + nextDirection;
char nextBaseDirection = this.getBaseDirection(nextDirection);
return (new PathCandidate(pathPos, vrtDist, hrzDist, turnCount, pathStr, nextBaseDirection, posWind));
}
// generate path candidates based on beat angles
List<PathCandidate> getPathCandsBeatWind(PathCandidate path, long timeStep, long turnLoss, Position posStart, Position posEnd, double tgtHeight) {
List<PathCandidate> result = new ArrayList<PathCandidate>();
PathCandidate newPathCand;
if (this.maxTurns > 0) {
char prevDirection = path.path.charAt(path.path.length()-1);
if ((path.trn < this.maxTurns)||(this.isSameDirection(prevDirection, 'L'))) {
newPathCand = getPathCandWind(path, 'L', timeStep, turnLoss, posStart, posEnd, tgtHeight);
result.add(newPathCand);
}
if ((path.trn < this.maxTurns)||(this.isSameDirection(prevDirection, 'R'))) {
newPathCand = getPathCandWind(path, 'R', timeStep, turnLoss, posStart, posEnd, tgtHeight);
result.add(newPathCand);
}
} else {
// step left
newPathCand = getPathCandWind(path, 'L', timeStep, turnLoss, posStart, posEnd, tgtHeight);
result.add(newPathCand);
// step wide left
//newPathCand = getPathCandWind(path, 'M', timeStep, turnLoss, posStart, posEnd, tgtHeight);
//result.add(newPathCand);
// step right
newPathCand = getPathCandWind(path, 'R', timeStep, turnLoss, posStart, posEnd, tgtHeight);
result.add(newPathCand);
// step wide right
//newPathCand = getPathCandWind(path, 'S', timeStep, turnLoss, posStart, posEnd, tgtHeight);
//result.add(newPathCand);
}
return result;
}
Util.Pair<List<PathCandidate>,List<PathCandidate>> generateCandidate(List<PathCandidate> oldPaths, long timeStep, long turnLoss, Position posStart, Position posMiddle, Position posEnd, double tgtHeight) {
List<PathCandidate> newPathCands;
List<PathCandidate> leftPaths = new ArrayList<PathCandidate>();
List<PathCandidate> rightPaths = new ArrayList<PathCandidate>();
for(PathCandidate curPath : oldPaths) {
newPathCands = this.getPathCandsBeatWind(curPath, timeStep, turnLoss, posStart, posEnd, tgtHeight);
for (PathCandidate curNewPath : newPathCands) {
// check whether path is *outside* regatta-area
double distFromMiddleMeters = posMiddle.getDistance(curPath.pos.getPosition()).getMeters();
if (distFromMiddleMeters > oobFact * tgtHeight) {
continue; // ignore curPath
}
if (curNewPath.sid == 'L') {
leftPaths.add(curNewPath);
} else if (curNewPath.sid == 'R') {
rightPaths.add(curNewPath);
}
}
}
Util.Pair<List<PathCandidate>,List<PathCandidate>> newPaths = new Util.Pair<List<PathCandidate>,List<PathCandidate>>(leftPaths, rightPaths);
return newPaths;
}
List<PathCandidate> filterCandidates(List<PathCandidate> allCands, double hrzBinWidth) {
boolean[] filterMap = new boolean[allCands.size()];
// sort candidates by horizontal distance
Comparator<PathCandidate> sortHorizontal = new SortPathCandsHorizontally();
Collections.sort(allCands, sortHorizontal);
// start scan with index 0
int idxL = 0;
int idxR = 0;
// for each candidate, check the neighborhoods and identify bad candidates
for(int idx = 0; idx < allCands.size(); idx++) {
// current horizontal distance
double hrzDist = allCands.get(idx).hrz;
// align left index
while(Math.abs(hrzDist - allCands.get(idxL).hrz) > hrzBinWidth) {
idxL++;
}
// align right index
boolean finished = false;
while(!finished && (idxR < (allCands.size()-1))) {
if (Math.abs(hrzDist - allCands.get(idxR+1).hrz) <= hrzBinWidth) {
idxR++;
} else {
finished = true;
}
}
// search maximum height
// in neighborhood idxL, ..., idxR
// init max for search
int vrtIdx = idxL;
double vrtMax = allCands.get(vrtIdx).vrt;
filterMap[vrtIdx] = false;
// evaluate remainder of neighborhood
if (idxL < idxR) {
for(int jdx = (idxL+1); jdx <= idxR; jdx++) {
if (allCands.get(jdx).vrt > vrtMax) {
// reset previous max candidate
filterMap[vrtIdx] = true;
// keep max height
vrtMax = allCands.get(jdx).vrt;
// keep max index
vrtIdx = jdx;
// set current max candidate
filterMap[vrtIdx] = false;
} else {
filterMap[jdx] = true;
}
}
}
} // endfor each candidate
// collect all good candidates (i.e. filterMap == false)
List<PathCandidate> filterCands = new ArrayList<PathCandidate>();
for(int idx=0; idx < allCands.size(); idx++) {
if (!filterMap[idx]) {
filterCands.add(allCands.get(idx));
}
}
// return remaining good candidates
return filterCands;
}
List<PathCandidate> filterIsochrone(List<PathCandidate> allCands, double hrzBinWidth) {
boolean[] filterMap = new boolean[allCands.size()];
for(int idx = 0; idx < allCands.size(); idx++) {
filterMap[idx] = true;
}
// sort candidates by horizontal distance
Comparator<PathCandidate> sortHorizontal = new SortPathCandsHorizontally();
Collections.sort(allCands, sortHorizontal);
// start scan with index 0
int idxL = 0;
int idxR = 0;
// for each candidate, check the neighborhoods and identify bad candidates
for(int idx = 0; idx < allCands.size(); idx++) {
// current horizontal distance
double hrzDist = allCands.get(idx).hrz;
// align left index
while(Math.abs(hrzDist - allCands.get(idxL).hrz) > hrzBinWidth) {
idxL++;
}
// align right index
boolean finished = false;
while(!finished && (idxR < (allCands.size()-1))) {
if (Math.abs(hrzDist - allCands.get(idxR+1).hrz) <= hrzBinWidth) {
idxR++;
} else {
finished = true;
}
}
// search maximum height
// in neighborhood idxL, ..., idxR
// init max for search
ArrayList<Integer> vrtIdx = new ArrayList<Integer>();
vrtIdx.add(idxL);
double vrtMax = allCands.get(idxL).vrt;
// evaluate remainder of neighborhood
if (idxL < idxR) {
for(int jdx = (idxL+1); jdx <= idxR; jdx++) {
if (allCands.get(jdx).vrt > vrtMax) {
// keep max height
vrtMax = allCands.get(jdx).vrt;
// keep max index
vrtIdx = new ArrayList<Integer>();
vrtIdx.add(jdx);
} else if (allCands.get(jdx).vrt == vrtMax) {
// add further max indexes
vrtIdx.add(jdx);
}
}
}
for(Integer jdx : vrtIdx) {
filterMap[jdx] = false;
}
} // endfor each candidate
// collect all good candidates (i.e. filterMap == false)
List<PathCandidate> filterCands = new ArrayList<PathCandidate>();
for(int idx=0; idx < allCands.size(); idx++) {
if (!filterMap[idx]) {
filterCands.add(allCands.get(idx));
}
}
// return remaining good candidates
return filterCands;
}
@Override
public Path getPath() {
WindFieldGenerator wf = this.parameters.getWindField();
PolarDiagram pd = this.parameters.getBoatPolarDiagram();
Position startPos = this.parameters.getCourse().get(0);
Position endPos = this.parameters.getCourse().get(1);
// test downwind: exchange start and end
//Position startPos = this.parameters.getCourse().get(1);
//Position endPos = this.parameters.getCourse().get(0);
TimePoint startTime = wf.getStartTime();// new MillisecondsTimePoint(0);
List<TimedPositionWithSpeed> path = new ArrayList<TimedPositionWithSpeed>();
Position currentPosition = startPos;
TimePoint currentTime = startTime;
Distance distStartEnd = startPos.getDistance(endPos);
double distStartEndMeters = distStartEnd.getMeters();
Wind wndStart = wf.getWind(new TimedPositionWithSpeedImpl(startTime, startPos, null));
logger.fine("wndStart speed:" + wndStart.getKnots() + " angle:" + wndStart.getBearing().getDegrees());
pd.setWind(wndStart);
Bearing bearVrt = startPos.getBearingGreatCircle(endPos);
//Bearing bearHrz = bearVrt.add(new DegreeBearingImpl(90.0));
Position middlePos = startPos.translateGreatCircle(bearVrt, distStartEnd.scale(0.5));
Bearing bearRCWind = wndStart.getBearing().getDifferenceTo(bearVrt);
String legType = "downwind";
this.upwindLeg = false;
if ((Math.abs(bearRCWind.getDegrees()) > 90.0)&&(Math.abs(bearRCWind.getDegrees()) < 270.0)) {
legType = "upwind";
this.upwindLeg = true;
}
if (debugMsgOn) {
System.out.println("start : "+startPos.getLatDeg()+", "+startPos.getLngDeg());
System.out.println("middle: "+middlePos.getLatDeg()+", "+middlePos.getLngDeg());
System.out.println("end : "+endPos.getLatDeg()+", "+endPos.getLngDeg());
}
logger.info("Leg Direction: "+legType);
long timeStep = wf.getTimeStep().asMillis()/2;
if (!this.upwindLeg) {
timeStep = timeStep / 2;
}
this.usedTimeStep = timeStep;
logger.info("Time step :" + timeStep);
long turnLoss = pd.getTurnLoss(); // 4000; // time lost when doing a turn
if (!this.upwindLeg) {
turnLoss = turnLoss / 2;
}
// calculate initial position according to initPathStr
PathCandidate initPath = new PathCandidate(new TimedPositionImpl(currentTime, currentPosition), 0.0, 0.0, 0, "0", '0', wndStart);
if (initPathStr.length()>1) {
char nextDirection = '0';
for(int idx=1; idx<initPathStr.length(); idx++) {
nextDirection = initPathStr.charAt(idx);
PathCandidate newPathCand = getPathCandWind(initPath, nextDirection, timeStep, turnLoss, startPos, endPos, distStartEndMeters);
initPath = newPathCand;
}
}
List<PathCandidate> allPaths = new ArrayList<PathCandidate>();
List<PathCandidate> trgPaths = new ArrayList<PathCandidate>();
allPaths.add(initPath);
TimedPosition tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, true, 'L').getA();
double tstDist1 = startPos.getDistance(tstPosition.getPosition()).getMeters();
tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), timeStep, turnLoss, true, 'R').getA();
double tstDist2 = startPos.getDistance(tstPosition.getPosition()).getMeters();
double hrzBinSize = (tstDist1 + tstDist2)/6.0; // horizontal bin size in meters
if (debugMsgOn) {
System.out.println("Horizontal Bin Size: "+hrzBinSize);
}
//double oobFact = 0.75; // out-of-bounds factor
boolean reachedEnd = false;
int addSteps = 0;
int finalSteps = 0; // maximum number of additional steps after first target-path found
while ((!reachedEnd)||(addSteps<finalSteps)) {
if (reachedEnd) {
addSteps++;
}
// generate new candidates (inside regatta-area)
Util.Pair<List<PathCandidate>,List<PathCandidate>> newPaths = this.generateCandidate(allPaths, timeStep, turnLoss, startPos, middlePos, endPos, distStartEndMeters);
// select good candidates
List<PathCandidate> leftPaths = this.filterCandidates(newPaths.getA(), hrzBinSize/2.0);
List<PathCandidate> rightPaths = this.filterCandidates(newPaths.getB(), hrzBinSize/2.0);
List<PathCandidate> nextPaths = new ArrayList<PathCandidate> ();
nextPaths.addAll(leftPaths);
nextPaths.addAll(rightPaths);
allPaths = nextPaths;
if (this.gridStore) {
/*ArrayList<TimedPosition> isoChrone = new ArrayList<TimedPosition>();
for(PathCand curCand : allPaths) {
isoChrone.add(curCand.pos);
}
this.gridPositions.add(isoChrone);*/
this.gridPositions.add(allPaths);
List<PathCandidate> isocPaths = this.filterIsochrone(allPaths, hrzBinSize);
this.isocPositions.add(isocPaths);
}
// check if there are still paths in the regatta-area
if (allPaths.size() > 0) {
for(PathCandidate curPath : allPaths) {
// terminate path-search if paths are found that are close enough to target
//if ((curPath.vrt > distStartEndMeters)) {
if ((curPath.vrt > 0.0)) {
//logger.info("\ntPath: " + curPath.path + "\n Time: " + (Math.round((curPath.pos.getTimePoint().asMillis()-startTime.asMillis())/1000.0/60.0*10.0)/10.0)+", Height: "+curPath.vrt+" of "+(Math.round(startPos.getDistance(endPos).getMeters()*100.0)/100.0)+", Dist: "+curPath.hrz+"m ~ "+(Math.round(curPath.pos.getPosition().getDistance(endPos).getMeters()*100.0)/100.0)+"m");
int curBin = (int)Math.round(Math.floor( (curPath.hrz + hrzBinSize/2.0) / hrzBinSize ));
if ((Math.abs(curBin) <= 2)) {
reachedEnd = true;
trgPaths.add(curPath); // add path to list of target-paths
}
}
}
} else {
// terminate path-search as no path inside regatta-area are left
reachedEnd = true;
}
}
if (this.gridStore) {
double distResolution = distStartEndMeters*0.01;
BufferedWriter outputCSV;
try {
outputCSV = new BufferedWriter(new FileWriter(this.gridFile+"-grid.csv"));
outputCSV.write("step; lat; lng; time; side; path; vrt\n");
outputCSV.write("0; "+startPos.getLatDeg()+"; "+startPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; "+(-distStartEndMeters)+"\n");
outputCSV.write("0; "+endPos.getLatDeg()+"; "+endPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; 0\n");
int stepCount = 0;
for(List<PathCandidate> isoChrone : this.gridPositions) {
stepCount++;
PathCandidate prevPos = null;
for(PathCandidate isoPos : isoChrone) {
if (prevPos != null) {
if (prevPos.pos.getPosition().getDistance(isoPos.pos.getPosition()).getMeters() < distResolution) {
continue;
}
}
String outStr = ""+stepCount+"; "+isoPos.pos.getPosition().getLatDeg()+"; "+isoPos.pos.getPosition().getLngDeg()+"; "+(isoPos.pos.getTimePoint().asMillis()/1000)+"; "+isoPos.sid;
outStr += "; "+isoPos.path+"; "+isoPos.vrt;
outStr += "\n";
outputCSV.write(outStr);
prevPos = isoPos;
}
}
outputCSV.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
outputCSV = new BufferedWriter(new FileWriter(this.gridFile+"-isoc.csv"));
outputCSV.write("step; lat; lng; time; side; path; vrt\n");
outputCSV.write("0; "+startPos.getLatDeg()+"; "+startPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; "+(-distStartEndMeters)+"\n");
outputCSV.write("0; "+endPos.getLatDeg()+"; "+endPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; 0\n");
int stepCount = 0;
for(List<PathCandidate> isoChrone : this.isocPositions) {
stepCount++;
PathCandidate prevPos = null;
for(PathCandidate isoPos : isoChrone) {
if (prevPos != null) {
if (prevPos.pos.getPosition().getDistance(isoPos.pos.getPosition()).getMeters() < distResolution) {
continue;
}
}
String outStr = ""+stepCount+"; "+isoPos.pos.getPosition().getLatDeg()+"; "+isoPos.pos.getPosition().getLngDeg()+"; "+(isoPos.pos.getTimePoint().asMillis()/1000)+"; "+isoPos.sid;
outStr += "; "+isoPos.path+"; "+isoPos.vrt;
outStr += "\n";
outputCSV.write(outStr);
prevPos = isoPos;
}
}
outputCSV.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// if no target-paths were found, return empty path
if (trgPaths.size() == 0) {
//trgPaths = allPaths; // TODO: only for testing; remove lateron
TimedPositionWithSpeed curPosition = new TimedPositionWithSpeedImpl(startTime, startPos, null);
path.add(curPosition);
return new PathImpl(path, wf); // return empty path
}
// sort target-paths ascending by distance-to-target
Collections.sort(trgPaths);
// debug output
//if (debugMsgOn) {
for(PathCandidate curPath : trgPaths) {
logger.info("\nPath: " + curPath.path + "\n Time: " + (Math.round((curPath.pos.getTimePoint().asMillis()-startTime.asMillis())/1000.0/60.0*10.0)/10.0)+", Height: "+curPath.vrt+" of "+(Math.round(startPos.getDistance(endPos).getMeters()*100.0)/100.0)+", Dist: "+curPath.hrz+"m ~ "+(Math.round(curPath.pos.getPosition().getDistance(endPos).getMeters()*100.0)/100.0)+"m");
//System.out.print(""+curPath.path+": "+curPath.pos.getTimePoint().asMillis()+", "+curPath.pos.getPosition().getLatDeg()+", "+curPath.pos.getPosition().getLngDeg()+", ");
//System.out.println(" height:"+curPath.vrt+" of "+startPos.getDistance(endPos).getMeters()+", dist:"+curPath.hrz+" ~ "+curPath.pos.getPosition().getDistance(endPos));
}
//}
//
// fill gwt-path
//
// generate intermediate steps
bestCand = trgPaths.get(0); // target-path ending closest to target
TimedPositionWithSpeed curPosition = null;
char nextDirection = '0';
char prevDirection = '0';
for(int step=0; step<(bestCand.path.length()-1); step++) {
nextDirection = bestCand.path.charAt(step);
if (nextDirection == '0') {
curPosition = new TimedPositionWithSpeedImpl(startTime, startPos, null);
path.add(curPosition);
} else {
boolean sameBaseDirection = this.isSameDirection(prevDirection, nextDirection);
TimedPosition newPosition = this.getStep(curPosition, timeStep, turnLoss, sameBaseDirection, nextDirection).getA();
curPosition = new TimedPositionWithSpeedImpl(newPosition.getTimePoint(), newPosition.getPosition(), null);
path.add(curPosition);
}
prevDirection = nextDirection;
}
// add final position (rescaled before to end on height of target)
path.add(new TimedPositionWithSpeedImpl(bestCand.pos.getTimePoint(), bestCand.pos.getPosition(), null));
return new PathImpl(path, wf);
}
}
@@ -113,7 +113,7 @@ public class SailingSimulatorImpl implements SailingSimulator {
//
// get instance of heuristic searcher
PathGeneratorTreeGrowWind3 genTreeGrow = new PathGeneratorTreeGrowWind3(this.simulationParameters);
PathGeneratorTreeGrowWind genTreeGrow = new PathGeneratorTreeGrowWind(this.simulationParameters);
// search best left-starting 1-turner
genTreeGrow.setEvaluationParameters("L", 1, null);
@@ -373,7 +373,7 @@ public class SimulatorUtils {
Map<String, Path> paths = new HashMap<String, Path>();
// get instance of heuristic searcher
PathGeneratorTreeGrowWind3 genTreeGrow = new PathGeneratorTreeGrowWind3(parameters);
PathGeneratorTreeGrowWind genTreeGrow = new PathGeneratorTreeGrowWind(parameters);
// search best left-starting 1-turner
genTreeGrow.setEvaluationParameters("L", 1, null);
@@ -140,7 +140,7 @@ public class WindControlParameters implements Serializable {
blastProbability = 25.0;
maxBlastSize = 1.0;
blastWindSpeed = 100.0;
blastWindSpeed = 120.0;
blastWindSpeedVar = 10.0;
if (blastRandomStreamManager == null || !resetBlastRandomStream) {