diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java index 0620b7f5082..3ba765dc2fa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java @@ -55,6 +55,7 @@ import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternDisplayManager; import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternNotFoundException; import com.sap.sailing.gwt.ui.simulator.windpattern.WindPatternSetting; import com.sap.sailing.simulator.BoatClassProperties; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SailingSimulator; @@ -62,11 +63,11 @@ import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.TimedPosition; import com.sap.sailing.simulator.TimedPositionWithSpeed; import com.sap.sailing.simulator.impl.ConfigurationManager; +import com.sap.sailing.simulator.impl.CurvedGrid; import com.sap.sailing.simulator.impl.PathGenerator1Turner; import com.sap.sailing.simulator.impl.PathImpl; import com.sap.sailing.simulator.impl.PolarDiagramCSV; import com.sap.sailing.simulator.impl.ReadingConfigurationFileStatus; -import com.sap.sailing.simulator.impl.RectangularBoundary; import com.sap.sailing.simulator.impl.SailingSimulatorImpl; import com.sap.sailing.simulator.impl.SimulationParametersImpl; import com.sap.sailing.simulator.impl.TimedPositionImpl; @@ -185,9 +186,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula course.add(nw); course.add(se); - RectangularBoundary bd = new RectangularBoundary(nw, se, 0.1); - // List lattice = bd.extractLattice(params.getxRes(), - // params.getyRes()); + Grid bd = new CurvedGrid(nw, se); controlParameters.resetBlastRandomStream = params.isKeepState(); retreiveWindControlParameters(pattern); @@ -201,7 +200,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula throw new WindPatternNotFoundException("Please select a valid wind pattern."); } - Position[][] grid = bd.extractGrid(params.getxRes(), params.getyRes(), params.getBorderY(), params.getBorderX()); + Position[][] grid = bd.generatePositions(params.getxRes(), params.getyRes(), params.getBorderY(), params.getBorderX()); wf.setPositionGrid(grid); TimePoint startTime = new MillisecondsTimePoint(params.getStartTime().getTime()); @@ -549,9 +548,9 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula SailingSimulator sailingSimulator = new SailingSimulatorImpl(simulationParameters); Path gpsWind = sailingSimulator.getLegGPSTrack(SimulatorServiceUtils.toSimulatorUISelection(requestData.selection)); - RectangularBoundary rectangularBoundry = new RectangularBoundary(oldMovedPosition, newMovedPosition, 0.1); - this.controlParameters.baseWindBearing += rectangularBoundry.getSouth().getDegrees(); - WindFieldGeneratorMeasured windFieldGenerator = new WindFieldGeneratorMeasured(rectangularBoundry, this.controlParameters); + Grid grid = new CurvedGrid(oldMovedPosition, newMovedPosition); + this.controlParameters.baseWindBearing += grid.getSouth().getDegrees(); + WindFieldGeneratorMeasured windFieldGenerator = new WindFieldGeneratorMeasured(grid, this.controlParameters); windFieldGenerator.setGPSWind(gpsWind); TimePoint startTime = new MillisecondsTimePoint(requestData.oldMovedPointTimePoint); diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/RectangularBoundaryTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/RectangularBoundaryTest.java index b5c6ca5b64c..05c8dd9191e 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/RectangularBoundaryTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/RectangularBoundaryTest.java @@ -6,8 +6,8 @@ import org.junit.Test; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.impl.DegreePosition; -import com.sap.sailing.simulator.Boundary; -import com.sap.sailing.simulator.impl.RectangularBoundary; +import com.sap.sailing.simulator.Grid; +import com.sap.sailing.simulator.impl.RectangularGrid; public class RectangularBoundaryTest { @@ -16,8 +16,8 @@ public class RectangularBoundaryTest { Position p1 = new DegreePosition(25.661333, -90.752563); Position p2 = new DegreePosition(24.522137, -90.774536); - Boundary b = new RectangularBoundary(p1, p2, 0.1); - Position[][] grid = b.extractGrid(20,20,0,0); + Grid b = new RectangularGrid(p1, p2); + Position[][] grid = b.generatePositions(20,20,0,0); assertEquals("Number of lattice points",400,grid.length*grid[0].length); } diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java index a29bb6039c2..16fd9731d41 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java @@ -20,7 +20,7 @@ import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.impl.PolarDiagram49STG; -import com.sap.sailing.simulator.impl.RectangularBoundary; +import com.sap.sailing.simulator.impl.RectangularGrid; import com.sap.sailing.simulator.impl.SailingSimulatorImpl; import com.sap.sailing.simulator.impl.SimulationParametersImpl; import com.sap.sailing.simulator.util.SailingSimulatorConstants; @@ -43,8 +43,8 @@ public class SimulatorTest { course.add(end); PolarDiagram pd = new PolarDiagram49STG();//PolarDiagram49.CreateStandard49(); - RectangularBoundary bd = new RectangularBoundary(start, end, 0.1); - Position[][] positions = bd.extractGrid(10, 10, 0, 0); + RectangularGrid bd = new RectangularGrid(start, end); + Position[][] positions = bd.generatePositions(10, 10, 0, 0); Bearing windBear = end.getBearingGreatCircle(start); WindControlParameters windParameters = new WindControlParameters(12.0, windBear.getDegrees()); WindFieldGenerator wf = new WindFieldGeneratorBlastImpl(bd, windParameters); diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java index 9070c7b8565..6d102285d06 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java @@ -20,9 +20,9 @@ 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.RectangularBoundary; +import com.sap.sailing.simulator.impl.RectangularGrid; import com.sap.sailing.simulator.impl.SimulationParametersImpl; import com.sap.sailing.simulator.util.SailingSimulatorConstants; import com.sap.sailing.simulator.windfield.WindControlParameters; @@ -44,8 +44,8 @@ public class TreeGrowTest { course.add(start); course.add(end); PolarDiagram pd = new PolarDiagram49STG();//PolarDiagram49.CreateStandard49(); - RectangularBoundary bd = new RectangularBoundary(start, end, 0.1); - Position[][] positions = bd.extractGrid(10, 10, 0, 0); + RectangularGrid bd = new RectangularGrid(start, end); + Position[][] positions = bd.generatePositions(10, 10, 0, 0); //RectangularBoundary new_bd = new RectangularBoundary(start, end, 0.1); //Speed knotSpeed = new KnotSpeedImpl(8); WindControlParameters windParameters = new WindControlParameters(12, start.getBearingGreatCircle(end).reverse().getDegrees()); @@ -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(); diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/WindFieldGeneratorTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/WindFieldGeneratorTest.java index 742748b1292..42ea29dc9b3 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/WindFieldGeneratorTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/WindFieldGeneratorTest.java @@ -23,7 +23,7 @@ import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.domain.tracking.Wind; -import com.sap.sailing.simulator.impl.RectangularBoundary; +import com.sap.sailing.simulator.impl.RectangularGrid; import com.sap.sailing.simulator.impl.TimedPositionWithSpeedImpl; import com.sap.sailing.simulator.windfield.WindControlParameters; import com.sap.sailing.simulator.windfield.impl.WindFieldGeneratorBlastImpl; @@ -51,11 +51,11 @@ public class WindFieldGeneratorTest { course.add(end); WindControlParameters windParameters = new WindControlParameters(3, 180); - RectangularBoundary bd = new RectangularBoundary(start, end, 0.1); + RectangularGrid bd = new RectangularGrid(start, end); WindFieldGeneratorImpl wf = new WindFieldGeneratorBlastImpl(bd, windParameters); int hSteps = 10; int vSteps = 5; - Position[][] positions = bd.extractGrid(hSteps, vSteps, 0, 0); + Position[][] positions = bd.generatePositions(hSteps, vSteps, 0, 0); assert (positions.length*positions[0].length == hSteps * vSteps); int index = 0; for (int i = 0; i < positions.length; ++i) { @@ -63,7 +63,7 @@ public class WindFieldGeneratorTest { logger.info("P" + ++index + ":" + positions[i][j]); } } - wf.setPositionGrid(bd.extractGrid(hSteps, vSteps, 0, 0)); + wf.setPositionGrid(bd.generatePositions(hSteps, vSteps, 0, 0)); Position[][] positionGrid = wf.getPositionGrid(); assertNotNull("Position Grid is not null", positionGrid); assertEquals("Position Grid Number of Rows", vSteps, positionGrid.length); @@ -91,13 +91,13 @@ public class WindFieldGeneratorTest { windParameters.frequency = 0.375; windParameters.amplitude = 20.0; - RectangularBoundary bd = new RectangularBoundary(start, end, 0.1); + RectangularGrid bd = new RectangularGrid(start, end); WindFieldGeneratorOscillationImpl wf = new WindFieldGeneratorOscillationImpl(bd, windParameters); int hSteps = 30; int vSteps = 15; - wf.setPositionGrid(bd.extractGrid(hSteps, vSteps, 0, 0)); + wf.setPositionGrid(bd.generatePositions(hSteps, vSteps, 0, 0)); Position[][] positionGrid = wf.getPositionGrid(); TimePoint startTime = new MillisecondsTimePoint(0); Duration timeStep = new MillisecondsDurationImpl(30 * 1000); @@ -235,12 +235,12 @@ public class WindFieldGeneratorTest { windParameters.blastWindSpeed = 0.0; windParameters.blastWindSpeedVar = 0.0; - RectangularBoundary bd = new RectangularBoundary(start, end, 0.1); + RectangularGrid bd = new RectangularGrid(start, end); WindFieldGeneratorCombined wf = new WindFieldGeneratorCombined(bd, windParameters); int hSteps = 30; int vSteps = 15; - wf.setPositionGrid(bd.extractGrid(hSteps, vSteps, 0, 0)); + wf.setPositionGrid(bd.generatePositions(hSteps, vSteps, 0, 0)); Position[][] positionGrid = wf.getPositionGrid(); TimePoint startTime = new MillisecondsTimePoint(0); Duration timeStep = new MillisecondsDurationImpl(30 * 1000); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Boundary.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Boundary.java deleted file mode 100644 index d89f2dab8bc..00000000000 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Boundary.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.sap.sailing.simulator; - -import java.io.Serializable; -import java.util.Map; - -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.impl.DegreeBearingImpl; -import com.sap.sse.common.Util; - -public interface Boundary extends Serializable { - - static Bearing TRUENORTH = new DegreeBearingImpl(0); - static Bearing TRUESOUTH = new DegreeBearingImpl(180); - static Bearing TRUEEAST = new DegreeBearingImpl(90); - static Bearing TRUEWEST = new DegreeBearingImpl(270); - - double getTolerance(); - - Map getCorners(); - - boolean isWithinBoundaries(Position P); - - //List extractLattice(int hPoints, int vPoints); - Position[][] extractGrid(int hPoints, int vPoints, int borderY, int borderX); - //List extractLattice(Distance hStep, Distance vstep); - public Util.Pair getGridIndex(Position x); - - public int getResY(); - public int getResX(); - public int getBorderY(); - public int getBorderX(); - - Bearing getNorth(); - Bearing getSouth(); - Bearing getEast(); - Bearing getWest(); - - Distance getHeight(); - Distance getWidth(); - - Map getRelativeCoordinates(Position p); - Position getRelativePoint(double x, double y); - -} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Grid.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Grid.java new file mode 100644 index 00000000000..4d6ccb2a4a1 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/Grid.java @@ -0,0 +1,54 @@ +package com.sap.sailing.simulator; + +import java.io.Serializable; +import java.util.Map; + +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.impl.DegreeBearingImpl; +import com.sap.sse.common.Util; + +/** + * The Grid interface defines access to a set of GPS-positions which form a lattice of supporting positions for + * representing a vectorfield or performing optimization. + * + * @author Christopher Ronnewinkel (D036654) + * + */ +public interface Grid extends Serializable { + + static Bearing relativeNorth = new DegreeBearingImpl(0); + static Bearing relativeSouth = new DegreeBearingImpl(180); + static Bearing relativeEast = new DegreeBearingImpl(90); + static Bearing relativeWest = new DegreeBearingImpl(270); + + Map getCorners(); + + boolean inBounds(Position P); + + Position[][] generatePositions(int hPoints, int vPoints, int borderY, int borderX); + + public Util.Pair getIndex(Position x); + + public int getResY(); + + public int getResX(); + + public int getBorderY(); + + public int getBorderX(); + + Bearing getNorth(); + + Bearing getSouth(); + + Bearing getEast(); + + Bearing getWest(); + + Distance getHeight(); + + Distance getWidth(); + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SimulationParameters.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SimulationParameters.java index 768b28e05d8..29d23fa9afb 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SimulationParameters.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SimulationParameters.java @@ -18,7 +18,7 @@ public interface SimulationParameters { WindFieldGenerator getWindField(); - Boundary getBoundaries(); + Grid getGrid(); Map getSettings(); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/CurvedGrid.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/CurvedGrid.java new file mode 100644 index 00000000000..2020c299a82 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/CurvedGrid.java @@ -0,0 +1,218 @@ +package com.sap.sailing.simulator.impl; + +import java.util.HashMap; +import java.util.Map; + +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.simulator.Grid; +import com.sap.sse.common.Util; + +/** + * Implements the {@link Grid} interface by providing a grid of GPS-positions based on great circles and the + * corresponding index-calculation to associate an arbitrary GPS-position with the closest grid-GPS-position. + * + * Since the grid is constructed in spherical coordinates, we call it the curved grid. It stays perfectly symmetric no + * matter along which bearing to North it is aligned with. + * + * @author Christopher Ronnewinkel (D036654) + * + */ +public class CurvedGrid implements Grid { + + private static final long serialVersionUID = 3598121983120213464L; + private Position rcStart; // start position of race course + private Position rcEnd; // end position of race course + private int vPoints; // number of vertical steps + private int hPoints; // number of horizontal steps + private int borderY; + private int borderX; + + private Position gridNorthWest; + private Position gridSouthEast; + private Position gridSouthWest; + private Position gridNorthEast; + + private Bearing gridNorth; + private Bearing gridSouth; + private Bearing gridEast; + private Bearing gridWest; + + private Distance gridWidth; + private double xscale = 1.5; + private Distance gridHeight; + + public CurvedGrid(Position p1, Position p2) { + + rcStart = p1; + rcEnd = p2; + + gridNorth = p1.getBearingGreatCircle(p2); + gridSouth = gridNorth.reverse(); + gridEast = gridNorth.add(relativeEast); + gridWest = gridNorth.add(relativeWest); + + gridHeight = p1.getDistance(p2); + gridWidth = gridHeight.scale(2); + + gridNorthWest = p2.translateGreatCircle(gridWest, gridHeight); + gridNorthEast = p2.translateGreatCircle(gridEast, gridHeight); + + gridSouthWest = p1.translateGreatCircle(gridWest, gridHeight); + gridSouthEast = p1.translateGreatCircle(gridEast, gridHeight); + + } + + @Override + public Map getCorners() { + + Map map = new HashMap(); + map.put("NorthWest", gridNorthWest); + map.put("SouthWest", gridSouthWest); + map.put("SouthEast", gridSouthEast); + map.put("NorthEast", gridNorthEast); + + return map; + + } + + @Override + public boolean inBounds(Position p) { + + Position northProjection = p.projectToLineThrough(gridNorthWest, getEast()); + Position southProjection = p.projectToLineThrough(gridSouthWest, getEast()); + Position westProjection = p.projectToLineThrough(gridNorthWest, getNorth()); + Position eastProjection = p.projectToLineThrough(gridNorthEast, getNorth()); + + 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); + + } + + @Override + public Position[][] generatePositions(int hPoints, int vPoints, int borderY, int borderX) { + + this.vPoints = vPoints; + this.hPoints = hPoints; + + this.borderY = borderY; + this.borderX = borderX; + + Distance vStep = gridHeight.scale(1.0 / (vPoints - 1)); + Distance hStep = gridHeight.scale(xscale / (hPoints - 1)); + + Position[][] grid = new Position[vPoints + 2 * borderY][hPoints + 2 * borderX]; + Position pv; + for (int i = -borderY; i < (vPoints + borderY); i++) { + + if (i == 0) { + pv = rcStart; + } else if (i == vPoints - 1) { + pv = rcEnd; + } else { + pv = rcStart.translateGreatCircle(gridNorth, vStep.scale(i)); + } + + int j = 0; + // left side + while (j < (hPoints + 2 * borderX) / 2) { + grid[i + borderY][j] = pv.translateGreatCircle(gridWest, + hStep.scale((hPoints + 2 * borderX - 1) / 2. - j)); + j++; + } + + // middle + if ((hPoints + 2 * borderX) % 2 == 1) { + grid[i + borderY][j] = pv; + j++; + } + + // right side + while (j < (hPoints + 2 * borderX)) { + grid[i + borderY][j] = pv.translateGreatCircle(gridEast, + hStep.scale(j - (hPoints + 2 * borderX - 1) / 2.)); + j++; + } + } + + return grid; + } + + public Util.Pair getIndex(Position x) { + + double vFlt = 0; + double hFlt = 0; + + if (!x.equals(rcStart)) { + Position h = x.projectToLineThrough(rcStart, gridNorth); + vFlt = vPoints * rcStart.getDistance(h).getMeters() / gridHeight.getMeters(); + int sign = +1; + if (Math.abs(h.getBearingGreatCircle(x).getDifferenceTo(gridWest).getDegrees()) < 90.0) { + sign = -1; + } + hFlt = sign * (hPoints - 1) * x.getDistance(h).getMeters() / gridHeight.getMeters() / xscale; + } + + int vIdx = Math.min(Math.max(-this.borderY, (int) Math.round(vFlt) + this.borderY), vPoints - 1 + this.borderY); + int hIdx = Math.min(Math.max(-this.borderX, (int) Math.round((hPoints - 1) / 2. + hFlt)), hPoints - 1 + this.borderX); + + return new Util.Pair(vIdx, hIdx); + } + + @Override + public int getResY() { + return this.vPoints; + } + + @Override + public int getResX() { + return this.hPoints; + } + + @Override + public int getBorderY() { + return this.borderY; + } + + @Override + public int getBorderX() { + return this.borderX; + } + + @Override + public Bearing getNorth() { + return gridNorth; + } + + @Override + public Bearing getSouth() { + return gridSouth; + } + + @Override + public Bearing getEast() { + return gridEast; + } + + @Override + public Bearing getWest() { + return gridWest; + } + + @Override + public Distance getWidth() { + return gridWidth; + } + + @Override + public Distance getHeight() { + return gridHeight; + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java index ee33d92f8e4..734a6e919d6 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java @@ -25,14 +25,14 @@ public class PathCandidate implements Comparable { // sort descending by length, width, height public int compareTo(PathCandidate other) { if (this.path.length() == other.path.length()) { - if (this.vrt == other.vrt) { + if (this.trn == other.trn) { 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); + return (this.trn < other.trn ? -1 : +1); } } else { return (this.path.length() < other.path.length() ? -1 : +1); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerLeftDirect.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerLeftDirect.java index 6a7b4e88990..cea23d98d76 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerLeftDirect.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerLeftDirect.java @@ -9,7 +9,7 @@ import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; @@ -26,7 +26,7 @@ public class PathGenerator1TurnerLeftDirect extends PathGeneratorBase { public Path getPath() { // retrieve simulation parameters - Boundary boundary = new RectangularBoundary(this.parameters.getCourse().get(0), this.parameters + Grid boundary = new RectangularGrid(this.parameters.getCourse().get(0), this.parameters .getCourse().get(1));// simulationParameters.getBoundaries(); WindFieldGenerator windField = this.parameters.getWindField(); PolarDiagram polarDiagram = this.parameters.getBoatPolarDiagram(); @@ -102,7 +102,7 @@ public class PathGenerator1TurnerLeftDirect extends PathGeneratorBase { // System.out.println("out of time"); break; } - if (!boundary.isWithinBoundaries(currentPosition)) { + if (!boundary.inBounds(currentPosition)) { // outOfBounds = true; // System.out.println("out of bounds"); break; diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerRightDirect.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerRightDirect.java index 44225dbe694..837e71af5e4 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerRightDirect.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGenerator1TurnerRightDirect.java @@ -9,7 +9,7 @@ import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; @@ -26,7 +26,7 @@ public class PathGenerator1TurnerRightDirect extends PathGeneratorBase { public Path getPath() { // retrieve simulation parameters - Boundary boundary = new RectangularBoundary(this.parameters.getCourse().get(0), this.parameters + Grid boundary = new RectangularGrid(this.parameters.getCourse().get(0), this.parameters .getCourse().get(1));// simulationParameters.getBoundaries(); WindFieldGenerator windField = this.parameters.getWindField(); PolarDiagram polarDiagram = this.parameters.getBoatPolarDiagram(); @@ -102,7 +102,7 @@ public class PathGenerator1TurnerRightDirect extends PathGeneratorBase { // System.out.println("out of time"); break; } - if (!boundary.isWithinBoundaries(currentPosition)) { + if (!boundary.inBounds(currentPosition)) { // outOfBounds = true; // System.out.println("out of bounds"); break; diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDijkstra.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDijkstra.java index 3e17816a5cb..58d92730a11 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDijkstra.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDijkstra.java @@ -14,7 +14,7 @@ import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; @@ -34,7 +34,7 @@ public class PathGeneratorDijkstra extends PathGeneratorBase { public Path getPath() { // retrieve simulation parameters - Boundary boundary = new RectangularBoundary(this.parameters.getCourse().get(0), this.parameters + Grid boundary = new RectangularGrid(this.parameters.getCourse().get(0), this.parameters .getCourse().get(1));// simulationParameters.getBoundaries(); WindFieldGenerator windField = this.parameters.getWindField(); PolarDiagram polarDiagram = this.parameters.getBoatPolarDiagram(); @@ -49,7 +49,7 @@ public class PathGeneratorDijkstra extends PathGeneratorBase { int gridv = this.parameters.getProperty("Djikstra.gridv[int]").intValue(); // number of vertical grid steps int gridh = this.parameters.getProperty("Djikstra.gridh[int]").intValue(); // number of horizontal grid // steps - Position[][] sailGrid = boundary.extractGrid(gridh, gridv, 0, 0); + Position[][] sailGrid = boundary.generatePositions(gridh, gridv, 0, 0); // create adjacency graph including start and end Map> graph = new HashMap>(); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDynProgForward.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDynProgForward.java index f633e2dbe33..f74afafc7d8 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDynProgForward.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorDynProgForward.java @@ -11,7 +11,7 @@ import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; @@ -130,7 +130,7 @@ public class PathGeneratorDynProgForward extends PathGeneratorBase { public Path getPath() { // retrieve simulation parameters - Boundary boundary = new RectangularBoundary(this.parameters.getCourse().get(0), this.parameters + Grid boundary = new RectangularGrid(this.parameters.getCourse().get(0), this.parameters .getCourse().get(1));// simulationParameters.getBoundaries(); WindFieldGenerator windField = this.parameters.getWindField(); PolarDiagram polarDiagram = this.parameters.getBoatPolarDiagram(); @@ -162,7 +162,7 @@ public class PathGeneratorDynProgForward extends PathGeneratorBase { } // generate grid positions using sgridh and sgridv - Position[][] sailGrid = boundary.extractGrid(spatialGridsizeHorizontal, spatialGridsizeVertical, 0, 0); + Position[][] sailGrid = boundary.generatePositions(spatialGridsizeHorizontal, spatialGridsizeVertical, 0, 0); // optimization grid indexes: // vertical steps: 0, ..., v-1 diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowTarget.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowTarget.java deleted file mode 100644 index 33a01c330cc..00000000000 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowTarget.java +++ /dev/null @@ -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 { - 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 getPathCandsBeat(PathCand path, long timeStep, long turnLoss, Position posStart, Bearing bearVrt, double tgtHeight) { - - List result = new ArrayList(); - 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 path = new ArrayList(); - - 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 allPaths = new ArrayList(); - List trgPaths = new ArrayList(); - 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 newPathCands; - List newPaths = new ArrayList(); - 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 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(); - 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); - - } - -} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java index 6e843497337..f398ffe6eb1 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java @@ -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> gridPositions = null; + ArrayList> 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>(); + this.isocPositions = new ArrayList>(); + } else { + this.gridStore = false; + this.gridPositions = null; + this.isocPositions = null; + } } - class PathCandidate implements Comparable { - 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 { + + @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 { - /*@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 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(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 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> generateCandidate(List oldPaths, long timeStep, long turnLoss, Position posStart, Position posMiddle, Position posEnd, double tgtHeight) { + + List newPathCands; + List leftPaths = new ArrayList(); + List rightPaths = new ArrayList(); + 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> newPaths = new Util.Pair,List>(leftPaths, rightPaths); + return newPaths; + } + + + List filterCandidates(List allCands, double hrzBinWidth) { + + boolean[] filterMap = new boolean[allCands.size()]; + + // sort candidates by horizontal distance + Comparator 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 filterCands = new ArrayList(); + for(int idx=0; idx < allCands.size(); idx++) { + if (!filterMap[idx]) { + filterCands.add(allCands.get(idx)); + } + } + + // return remaining good candidates + return filterCands; + } + + + List filterIsochrone(List 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 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 vrtIdx = new ArrayList(); + 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(); + 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 filterCands = new ArrayList(); + 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 path = new ArrayList(); @@ -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 newPathCands; - List newPaths = new ArrayList(); - for(PathCandidate curPath : allPaths) { + // generate new candidates (inside regatta-area) + Util.Pair,List> 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 leftPaths = this.filterCandidates(newPaths.getA(), hrzBinSize/2.0); + List rightPaths = this.filterCandidates(newPaths.getB(), hrzBinSize/2.0); + + List nextPaths = new ArrayList (); + nextPaths.addAll(leftPaths); + nextPaths.addAll(rightPaths); + + allPaths = nextPaths; + + if (this.gridStore) { + + /*ArrayList isoChrone = new ArrayList(); + for(PathCand curCand : allPaths) { + isoChrone.add(curCand.pos); } + this.gridPositions.add(isoChrone);*/ - for(PathCandidate newPath : newPathCands) { + this.gridPositions.add(allPaths); + + List 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 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(); - 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 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 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); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind2.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind2.java deleted file mode 100644 index 8cdac6d5ca6..00000000000 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind2.java +++ /dev/null @@ -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> 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>(); - } else { - this.gridStore = false; - this.gridPositions = null; - } - } - - class SortPathCandsAbsHorizontally implements Comparator { - - @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 { - - @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 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(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 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 getPathCandsBeatWind(PathCandidate path, long timeStep, long turnLoss, Position posStart, Position posEnd, double tgtHeight) { - - List result = new ArrayList(); - 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> generateCandidate(List oldPaths, long timeStep, long turnLoss, Position posStart, Position posMiddle, Position posEnd, double tgtHeight) { - - List newPathCands; - List leftPaths = new ArrayList(); - List rightPaths = new ArrayList(); - 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> newPaths = new Util.Pair,List>(leftPaths, rightPaths); - return newPaths; - } - - - List filterCandidates(List allCands, double hrzBinWidth) { - - boolean[] filterMap = new boolean[allCands.size()]; - - // sort candidates by horizontal distance - Comparator 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 filterCands = new ArrayList(); - 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 path = new ArrayList(); - - 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 allPaths = new ArrayList(); - List trgPaths = new ArrayList(); - 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,List> newPaths = this.generateCandidate(allPaths, timeStep, turnLoss, startPos, middlePos, endPos, distStartEndMeters); - - - // select good candidates - List leftPaths = this.filterCandidates(newPaths.getA(), hrzBinSize/2.0); - List rightPaths = this.filterCandidates(newPaths.getB(), hrzBinSize/2.0); - - List nextPaths = new ArrayList (); - nextPaths.addAll(leftPaths); - nextPaths.addAll(rightPaths); - - allPaths = nextPaths; - - if (this.gridStore) { - - /*ArrayList isoChrone = new ArrayList(); - 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 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); - - } - -} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind3.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind3.java deleted file mode 100644 index c5a903d8802..00000000000 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind3.java +++ /dev/null @@ -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> gridPositions = null; - ArrayList> 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>(); - this.isocPositions = new ArrayList>(); - } else { - this.gridStore = false; - this.gridPositions = null; - this.isocPositions = null; - } - } - - class SortPathCandsAbsHorizontally implements Comparator { - - @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 { - - @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 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(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 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()*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 getPathCandsBeatWind(PathCandidate path, long timeStep, long turnLoss, Position posStart, Position posEnd, double tgtHeight) { - - List result = new ArrayList(); - 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> generateCandidate(List oldPaths, long timeStep, long turnLoss, Position posStart, Position posMiddle, Position posEnd, double tgtHeight) { - - List newPathCands; - List leftPaths = new ArrayList(); - List rightPaths = new ArrayList(); - 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> newPaths = new Util.Pair,List>(leftPaths, rightPaths); - return newPaths; - } - - - List filterCandidates(List allCands, double hrzBinWidth) { - - boolean[] filterMap = new boolean[allCands.size()]; - - // sort candidates by horizontal distance - Comparator 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 filterCands = new ArrayList(); - for(int idx=0; idx < allCands.size(); idx++) { - if (!filterMap[idx]) { - filterCands.add(allCands.get(idx)); - } - } - - // return remaining good candidates - return filterCands; - } - - - List filterIsochrone(List 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 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 vrtIdx = new ArrayList(); - 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(); - 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 filterCands = new ArrayList(); - 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 path = new ArrayList(); - - 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 allPaths = new ArrayList(); - List trgPaths = new ArrayList(); - 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)/3.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,List> newPaths = this.generateCandidate(allPaths, timeStep, turnLoss, startPos, middlePos, endPos, distStartEndMeters); - - - // select good candidates - List leftPaths = this.filterCandidates(newPaths.getA(), hrzBinSize/2.0); - List rightPaths = this.filterCandidates(newPaths.getB(), hrzBinSize/2.0); - - List nextPaths = new ArrayList (); - nextPaths.addAll(leftPaths); - nextPaths.addAll(rightPaths); - - allPaths = nextPaths; - - if (this.gridStore) { - - /*ArrayList isoChrone = new ArrayList(); - for(PathCand curCand : allPaths) { - isoChrone.add(curCand.pos); - } - this.gridPositions.add(isoChrone);*/ - - this.gridPositions.add(allPaths); - - List 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) <= 1)) { - 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 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 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); - - } - -} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularBoundary.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularGrid.java similarity index 51% rename from java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularBoundary.java rename to java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularGrid.java index 4dd22f70006..013c3d96a56 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularBoundary.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/RectangularGrid.java @@ -7,10 +7,23 @@ 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.impl.DegreePosition; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sse.common.Util; -public class RectangularBoundary implements Boundary { +/** + * Implements the {@link Grid} interface by providing a grid of GPS-positions based on the plane approximation of the + * earth and the corresponding index-calculation to associate an arbitrary GPS-position with the closest + * grid-GPS-position. + * + * Since the grid has rectangular shape in the plane approximation, we call it rectangular grid. The plane approximation + * allows for a simple index calculation based on CPU-light operations. If not aligned with North the grid gets slightly + * distorted leading to asymmetries which may influence the accuracy of path calculations or optimizations. + * Still, for typical race course diameters of up to 5 nautical miles, it is a very good and fast approximation. + * + * @author Christopher Ronnewinkel (D036654) + * + */ +public class RectangularGrid implements Grid { private static final long serialVersionUID = 3598121983120213464L; private Position rcStart; // start position of race course @@ -24,7 +37,7 @@ public class RectangularBoundary implements Boundary { private int borderX; private Position northWest; - //private Position southEast; + // private Position southEast; private Position southWest; private Position northEast; @@ -43,20 +56,17 @@ public class RectangularBoundary implements Boundary { double tolerance; - public RectangularBoundary(Position p1, Position p2, double tlr) { - - // System.out.println("Start:"+p1); - // System.out.println("End :"+p2); + public RectangularGrid(Position p1, Position p2) { rcStart = p1; rcEnd = p2; - tolerance = tlr; + tolerance = 0.1; north = p1.getBearingGreatCircle(p2); south = north.reverse(); - east = north.add(TRUEEAST); - west = north.add(TRUEWEST); + east = north.add(relativeEast); + west = north.add(relativeWest); appHeight = p1.getDistance(p2); appWidth = appHeight.scale(2); @@ -65,40 +75,21 @@ public class RectangularBoundary implements Boundary { appNorthWest = p2.translateGreatCircle(west, appHeight); appNorthEast = p2.translateGreatCircle(east, appHeight); - // 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); - 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); - 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)); + // diag1 = diag1.reverse(); + // southEast = appSouthEast.translateGreatCircle(diag1, diag.scale(tolerance)); Bearing diag2 = appSouthWest.getBearingGreatCircle(appNorthEast); northEast = appNorthEast.translateGreatCircle(diag2, diag.scale(tolerance)); diag2 = diag2.reverse(); southWest = appSouthWest.translateGreatCircle(diag2, diag.scale(tolerance)); } - public RectangularBoundary(Position p1, Position p2) { - - this(p1, p2, 0.1); - - } - @Override public Map getCorners() { @@ -113,7 +104,7 @@ public class RectangularBoundary implements Boundary { } @Override - public boolean isWithinBoundaries(Position p) { + public boolean inBounds(Position p) { Position northProjection = p.projectToLineThrough(northWest, getEast()); Position southProjection = p.projectToLineThrough(southWest, getEast()); @@ -131,14 +122,14 @@ public class RectangularBoundary implements Boundary { } @Override - public Position[][] extractGrid(int hPoints, int vPoints, int borderY, int borderX) { + public Position[][] generatePositions(int hPoints, int vPoints, int borderY, int borderX) { this.vPoints = vPoints; this.hPoints = hPoints; this.borderY = borderY; this.borderX = borderX; - + double xscale = 1.5; double alat = (rcEnd.getLatDeg() + rcStart.getLatDeg()) / 2.; @@ -170,11 +161,9 @@ public class RectangularBoundary implements Boundary { nHor[0] = nscHor[0]; nHor[1] = nscHor[1] / lngScale; - // System.out.println("LngScale:"+lngScale+", Diff.Vrt:"+dVrt); - Position[][] grid = new Position[vPoints+2*borderY][hPoints+2*borderX]; + Position[][] grid = new Position[vPoints + 2 * borderY][hPoints + 2 * borderX]; Position pv; - for (int i = -borderY; i < (vPoints+borderY); i++) { - // System.out.print("nrow "+i+": "); + for (int i = -borderY; i < (vPoints + borderY); i++) { if (i == 0) { pv = rcStart; @@ -187,41 +176,32 @@ public class RectangularBoundary implements Boundary { int j = 0; // left side - while (j < (hPoints+2*borderX) / 2) { - grid[i+borderY][j] = new DegreePosition(pv.getLatDeg() - ((hPoints + 2*borderX - 1) / 2. - j) / (hPoints - 1) * nHor[0] - * xscale * lscVrt, pv.getLngDeg() - ((hPoints + 2*borderX - 1) / 2. - j) / (hPoints - 1) * nHor[1] * xscale - * lscVrt); - // System.out.print(""+grid[i][j]+"("+((hPoints-1)/2.-j)+"), "); + while (j < (hPoints + 2 * borderX) / 2) { + grid[i + borderY][j] = new DegreePosition(pv.getLatDeg() - ((hPoints + 2 * borderX - 1) / 2. - j) + / (hPoints - 1) * nHor[0] * xscale * lscVrt, pv.getLngDeg() + - ((hPoints + 2 * borderX - 1) / 2. - j) / (hPoints - 1) * nHor[1] * xscale * lscVrt); j++; } // middle - if ((hPoints+2*borderX) % 2 == 1) { - grid[i+borderY][j] = pv; - // System.out.print(""+grid[i][j]+", "); + if ((hPoints + 2 * borderX) % 2 == 1) { + grid[i + borderY][j] = pv; j++; } // right side - while (j < (hPoints+2*borderX)) { - grid[i+borderY][j] = new DegreePosition(pv.getLatDeg() + (j - (hPoints + 2*borderX - 1) / 2.) / (hPoints - 1) * nHor[0] - * xscale * lscVrt, pv.getLngDeg() + (j - (hPoints + 2*borderX - 1) / 2.) / (hPoints - 1) * nHor[1] * xscale - * lscVrt); - // System.out.print(""+grid[i][j]+"("+(j-(hPoints-1)/2.)+"), "); + while (j < (hPoints + 2 * borderX)) { + grid[i + borderY][j] = new DegreePosition(pv.getLatDeg() + (j - (hPoints + 2 * borderX - 1) / 2.) + / (hPoints - 1) * nHor[0] * xscale * lscVrt, pv.getLngDeg() + + (j - (hPoints + 2 * borderX - 1) / 2.) / (hPoints - 1) * nHor[1] * xscale * lscVrt); j++; } - // System.out.println(); } - /* - * System.out.println("Grid Index Test:"); for (int i =0; i getGridIndex(Position x) { + public Util.Pair getIndex(Position x) { double[] scX = new double[2]; scX[0] = x.getLatDeg() - rcStart.getLatDeg(); @@ -229,64 +209,35 @@ public class RectangularBoundary implements Boundary { double sPrd = scX[0] * nvHor[0] + scX[1] * nvHor[1]; - int vIdx = Math.min(Math.max(-this.borderY, (int) Math.round(scX[0] * nvVrt[0] + scX[1] * nvVrt[1])), vPoints - 1 + this.borderY); - int hIdx = Math.min(Math.max(-this.borderX, (int) Math.round(sPrd + (hPoints - 1) / 2.)), hPoints - 1 + this.borderX); + int vIdx = Math.min(Math.max(-this.borderY, (int) Math.round(scX[0] * nvVrt[0] + scX[1] * nvVrt[1])), vPoints + - 1 + this.borderY); + int hIdx = Math.min(Math.max(-this.borderX, (int) Math.round(sPrd + (hPoints - 1) / 2.)), hPoints - 1 + + this.borderX); - // System.out.println("getGridIndex: "+vIdx+","+hIdx+"("+vPoints+","+hPoints+")"); return new Util.Pair(vIdx, hIdx); } - /* - * @Override public List extractLattice(int hPoints, int vPoints) { - * - * Position[][] grid = extractGrid(hPoints, vPoints); List lst = new ArrayList(); for(Position[] - * line : grid) { for(Position p : line) { lst.add(p); } } return lst; } - * - * @Override //may not return a rectangular lattice! public List extractLattice(Distance hStep, Distance - * vStep) { - * - * Position startPoint = appSouthWest; - * - * Bearing vBearing = getNorth(); Bearing hBearing = getEast(); boolean hMode = true; - * - * List lst = new ArrayList(); - * - * Position current = startPoint; lst.add(current); Position next; - * - * while (true) { - * - * if (hMode) next = current.translateGreatCircle(hBearing, hStep); else next = - * current.translateGreatCircle(vBearing, vStep); - * - * if (isWithinBoundaries(next)) { current = next; lst.add(current); if (!hMode) { hMode = true; hBearing = - * hBearing.reverse(); } } else { if(hMode) hMode = false; else break; } - * - * } - * - * return lst; } - */ - @Override public int getResY() { - return this.vPoints; + return this.vPoints; } - + @Override public int getResX() { - return this.hPoints; + return this.hPoints; } - + @Override public int getBorderY() { - return this.borderY; + return this.borderY; } - + @Override public int getBorderX() { - return this.borderX; + return this.borderX; } - + @Override public Bearing getNorth() { return north; @@ -317,32 +268,4 @@ public class RectangularBoundary implements Boundary { return appHeight; } - @Override - public Map getRelativeCoordinates(Position p) { - if (!isWithinBoundaries(p)) - return null; - - Map map = new HashMap(); - Position px = p.projectToLineThrough(appSouthWest, getEast()); - Position py = p.projectToLineThrough(appSouthWest, getNorth()); - - map.put("X", px.getDistance(appSouthWest).getMeters() / getWidth().getMeters()); - map.put("Y", py.getDistance(appSouthWest).getMeters() / getHeight().getMeters()); - return map; - } - - @Override - public Position getRelativePoint(double x, double y) { - - Position point = appSouthWest; - point = point.translateGreatCircle(getEast(), getWidth().scale(x)); - point = point.translateGreatCircle(getNorth(), getHeight().scale(y)); - return point; - } - - @Override - public double getTolerance() { - return tolerance; - } - } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java index 6144fdffa4a..e5b97963a33 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java @@ -12,7 +12,7 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.MeterDistance; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.RaceProperties; import com.sap.sailing.simulator.SailingSimulator; @@ -79,7 +79,7 @@ public class SailingSimulatorImpl implements SailingSimulator { LOGGER.info("showOpportunist: "+this.simulationParameters.showOpportunist()); if (gridArea != null) { - Boundary bd = new RectangularBoundary(gridArea[0], gridArea[1], 0.1); + Grid bd = new CurvedGrid(gridArea[0], gridArea[1]); // set base wind bearing wf.getWindParameters().baseWindBearing += bd.getSouth().getDegrees(); @@ -103,7 +103,7 @@ public class SailingSimulatorImpl implements SailingSimulator { } wf.setBoundary(bd); - Position[][] positionGrid = bd.extractGrid(gridRes[0], gridRes[1], gridRes[2], gridRes[3]); + Position[][] positionGrid = bd.generatePositions(gridRes[0], gridRes[1], gridRes[2], gridRes[3]); wf.setPositionGrid(positionGrid); wf.generate(wf.getStartTime(), wf.getEndTime(), wf.getTimeStep()); } @@ -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); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulationParametersImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulationParametersImpl.java index 522fb79f62b..cc791cb732f 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulationParametersImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulationParametersImpl.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.Map; import com.sap.sailing.domain.common.Position; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.windfield.WindFieldGenerator; @@ -64,8 +64,8 @@ public class SimulationParametersImpl implements SimulationParameters { } @Override - public Boundary getBoundaries() { - return windField.getBoundaries(); + public Grid getGrid() { + return windField.getGrid(); } @Override diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorUtils.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorUtils.java index ddd8b3a47ca..c4b41214098 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorUtils.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorUtils.java @@ -373,7 +373,7 @@ public class SimulatorUtils { Map paths = new HashMap(); // 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); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindField.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindField.java index 7ff6505ced7..4025fce637f 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindField.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindField.java @@ -3,7 +3,7 @@ package com.sap.sailing.simulator.windfield; import java.io.Serializable; import com.sap.sailing.domain.tracking.Wind; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.TimedPosition; @@ -11,7 +11,7 @@ public interface WindField extends Serializable { public Wind getWind(TimedPosition coordinates); - public Boundary getBoundaries(); + public Grid getGrid(); public Path getLine(TimedPosition seed, boolean forward); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java index 450b417995a..45748eb3cab 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGenerator.java @@ -7,13 +7,13 @@ import java.io.Serializable; import com.sap.sailing.domain.common.Duration; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.TimePoint; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; public interface WindFieldGenerator extends WindField, Serializable { public WindControlParameters getWindParameters(); - public void setBoundary(Boundary boundary); + public void setBoundary(Grid boundary); public void setPositionGrid(Position[][] positions); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGeneratorFactory.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGeneratorFactory.java index ce293021260..6fdaa686138 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGeneratorFactory.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/WindFieldGeneratorFactory.java @@ -1,12 +1,12 @@ package com.sap.sailing.simulator.windfield; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.windfield.impl.WindFieldGeneratorFactoryImpl; public interface WindFieldGeneratorFactory { static WindFieldGeneratorFactory INSTANCE = new WindFieldGeneratorFactoryImpl(); - public WindFieldGenerator createWindFieldGenerator(String patternName, Boundary boundary, + public WindFieldGenerator createWindFieldGenerator(String patternName, Grid boundary, WindControlParameters windParameters); } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorBlastImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorBlastImpl.java index 23d74cfffd8..af35125b379 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorBlastImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorBlastImpl.java @@ -14,7 +14,7 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.impl.WindImpl; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.TimedPosition; import com.sap.sailing.simulator.windfield.WindControlParameters; import com.sap.sailing.simulator.windfield.WindFieldGenerator; @@ -32,7 +32,7 @@ public class WindFieldGeneratorBlastImpl extends WindFieldGeneratorImpl implemen private double blastSizeProbability = 70; private double blastEdgeProbability = 30; private double blastBearingMean = 0; - private double blastBearingVar = 8; + private double blastBearingVar = 6; private double defaultWindSpeed = 0; private double defaultWindBearing = 0; private SpeedWithBearing defaultSpeedWithBearing; @@ -44,7 +44,7 @@ public class WindFieldGeneratorBlastImpl extends WindFieldGeneratorImpl implemen private static Logger logger = Logger.getLogger(WindFieldGeneratorBlastImpl.class.getName()); - public WindFieldGeneratorBlastImpl(Boundary boundary, WindControlParameters windParameters) { + public WindFieldGeneratorBlastImpl(Grid boundary, WindControlParameters windParameters) { super(boundary, windParameters); } @@ -164,8 +164,7 @@ public class WindFieldGeneratorBlastImpl extends WindFieldGeneratorImpl implemen //System.out.println("blast speed mean: "+bSpeedMean+" var: "+bSpeedVar); //return NormalGen.nextDouble(new LFSR113("BlastSpeedStream"), bSpeedMean, bSpeedVar); RandomStream speedStream = windParameters.getBlastRandomStreamManager().getRandomStream(BlastRandomSeedManagerImpl.BlastStream.SPEED.name()); - return NormalGen.nextDouble(speedStream, bSpeedMean, bSpeedVar); - + return Math.max(0.5*bSpeedMean, Math.min(1.5*bSpeedMean, NormalGen.nextDouble(speedStream, bSpeedMean, bSpeedVar))); } private int getBlastSize() { @@ -177,8 +176,7 @@ public class WindFieldGeneratorBlastImpl extends WindFieldGeneratorImpl implemen private double getBlastAngle() { //return NormalGen.nextDouble(new LFSR113("BlastAngleStream"), blastBearingMean, blastBearingVar); RandomStream bearingStream = windParameters.getBlastRandomStreamManager().getRandomStream(BlastRandomSeedManagerImpl.BlastStream.BEARING.name()); - return NormalGen.nextDouble(bearingStream, blastBearingMean, blastBearingVar); - + return Math.max(-1.5*blastBearingVar, Math.min(1.5*blastBearingVar, NormalGen.nextDouble(bearingStream, blastBearingMean, blastBearingVar))); } private SpeedWithBearing getSpeedWithBearing(TimedPosition timedPosition) { diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorCombined.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorCombined.java index 863891c471d..9e384c819a6 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorCombined.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorCombined.java @@ -7,7 +7,7 @@ import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.impl.WindImpl; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.TimedPosition; import com.sap.sailing.simulator.windfield.WindControlParameters; import com.sap.sailing.simulator.windfield.WindFieldGenerator; @@ -18,14 +18,14 @@ public class WindFieldGeneratorCombined extends WindFieldGeneratorImpl implement private WindFieldGeneratorBlastImpl blastGen; private WindFieldGeneratorOscillationImpl oscillationGen; - public WindFieldGeneratorCombined(Boundary boundary, WindControlParameters windParameters) { + public WindFieldGeneratorCombined(Grid boundary, WindControlParameters windParameters) { super(boundary, windParameters); blastGen = new WindFieldGeneratorBlastImpl(boundary, windParameters); oscillationGen = new WindFieldGeneratorOscillationImpl(boundary, windParameters); } @Override - public void setBoundary(Boundary boundary) { + public void setBoundary(Grid boundary) { super.setBoundary(boundary); blastGen.setBoundary(boundary); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorFactoryImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorFactoryImpl.java index d51cafe9d85..c20a53e8849 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorFactoryImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorFactoryImpl.java @@ -1,7 +1,7 @@ package com.sap.sailing.simulator.windfield.impl; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.windfield.WindControlParameters; import com.sap.sailing.simulator.windfield.WindFieldGenerator; import com.sap.sailing.simulator.windfield.WindFieldGeneratorFactory; @@ -9,7 +9,7 @@ import com.sap.sailing.simulator.windfield.WindFieldGeneratorFactory; public class WindFieldGeneratorFactoryImpl implements WindFieldGeneratorFactory { @Override - public WindFieldGenerator createWindFieldGenerator(String patternName, Boundary boundary, + public WindFieldGenerator createWindFieldGenerator(String patternName, Grid boundary, WindControlParameters windParameters) { if (patternName.equals("BLASTS")) { return new WindFieldGeneratorBlastImpl(boundary, windParameters); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorImpl.java index f1d1e5ac3c7..c696bf8b115 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorImpl.java @@ -13,7 +13,7 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.impl.WindImpl; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.TimedPosition; import com.sap.sailing.simulator.TimedPositionWithSpeed; @@ -27,7 +27,7 @@ import com.sap.sse.common.Util; public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { private static final long serialVersionUID = -6366698648104363491L; - protected Boundary boundary; + protected Grid boundary; protected WindControlParameters windParameters; protected Position[][] positions; @@ -46,7 +46,7 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { private static Logger logger = Logger.getLogger("com.sap.sailing.windfield"); - public WindFieldGeneratorImpl(Boundary boundary, WindControlParameters windParameters) { + public WindFieldGeneratorImpl(Grid boundary, WindControlParameters windParameters) { this.boundary = boundary; this.windParameters = windParameters; this.positions = null; @@ -58,7 +58,7 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { } @Override - public void setBoundary(Boundary boundary) { + public void setBoundary(Grid boundary) { this.boundary = boundary; } @@ -77,7 +77,7 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { } @Override - public Boundary getBoundaries() { + public Grid getGrid() { return boundary; } @@ -97,7 +97,7 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { LinkedList path = new LinkedList(); path.add(new TimedPositionWithSpeedImpl(currentTime, currentPosition, null)); - while(boundary.isWithinBoundaries(currentPosition)) { + while(boundary.inBounds(currentPosition)) { Wind currentWind = this.getWind(new TimedPositionImpl(startTime, currentPosition)); TimePoint middleTime = currentTime.plus(timeStep/2); Position middlePosition; @@ -138,7 +138,7 @@ public abstract class WindFieldGeneratorImpl implements WindFieldGenerator { } public Util.Pair getPositionIndex(Position p) { - Util.Pair gIdx = boundary.getGridIndex(p); + Util.Pair gIdx = boundary.getIndex(p); if ((gIdx.getA() != null) && (gIdx.getB() != null)) { return gIdx; } else { diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorMeasured.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorMeasured.java index a8cdba604eb..f7ad8712628 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorMeasured.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorMeasured.java @@ -11,7 +11,7 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.impl.WindImpl; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.TimedPosition; import com.sap.sailing.simulator.TimedPositionWithSpeed; @@ -30,7 +30,7 @@ public class WindFieldGeneratorMeasured extends WindFieldGeneratorImpl implement // super(); // } - public WindFieldGeneratorMeasured(Boundary boundary, WindControlParameters windParameters) { + public WindFieldGeneratorMeasured(Grid boundary, WindControlParameters windParameters) { super(boundary, windParameters); } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorOscillationImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorOscillationImpl.java index 012a949084f..67b2a1f53c7 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorOscillationImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldGeneratorOscillationImpl.java @@ -14,7 +14,7 @@ import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.impl.WindImpl; -import com.sap.sailing.simulator.Boundary; +import com.sap.sailing.simulator.Grid; import com.sap.sailing.simulator.TimedPosition; import com.sap.sailing.simulator.windfield.WindControlParameters; import com.sap.sailing.simulator.windfield.WindFieldGenerator; @@ -32,7 +32,7 @@ public class WindFieldGeneratorOscillationImpl extends WindFieldGeneratorImpl im private static Logger logger = Logger.getLogger(WindFieldGeneratorOscillationImpl.class.getName()); - public WindFieldGeneratorOscillationImpl(Boundary boundary, WindControlParameters windParameters) { + public WindFieldGeneratorOscillationImpl(Grid boundary, WindControlParameters windParameters) { super(boundary, windParameters); }