Merge remote-tracking branch 'origin/master' into ribdashboard

This commit is contained in:
Alexander Ries committed 2015-01-29 10:43:22 +01:00
commit 5bbb80891e
18 files changed
+1085 -95

No files matched your search

+8 -10
View File
@@ -200,7 +200,7 @@ if [[ "$@" == "release" ]]; then
RELEASE_NOTES="" RELEASE_NOTES=""
COMMIT_WEEK_COUNT=4 COMMIT_WEEK_COUNT=4
if [ $suppress_confirmation -eq 0 ]; then if [ $suppress_confirmation -eq 0 ]; then
echo "" echo ""
echo "Please provide me with some notes about this release. You can add more than" echo "Please provide me with some notes about this release. You can add more than"
echo "one line. Please include major changes or new features. After your notes I will" echo "one line. Please include major changes or new features. After your notes I will"
@@ -357,7 +357,7 @@ if [[ "$@" == "local-deploy" ]]; then
echo "WARNING: Bundle versions do not differ. Update not needed." echo "WARNING: Bundle versions do not differ. Update not needed."
fi fi
if [ $suppress_confirmation -eq 0 ]; then if [ $suppress_confirmation -eq 0 ]; then
read -s -n1 -p "Do you really want to locally deploy bundle $OSGI_BUNDLE_NAME to $TARGET_DIR? (y/N): " answer read -s -n1 -p "Do you really want to locally deploy bundle $OSGI_BUNDLE_NAME to $TARGET_DIR? (y/N): " answer
case $answer in case $answer in
"Y" | "y") echo "Continuing";; "Y" | "y") echo "Continuing";;
@@ -420,7 +420,7 @@ if [[ "$@" == "hot-deploy" ]]; then
echo "WARNING: Bundle versions do not differ. Update not needed." echo "WARNING: Bundle versions do not differ. Update not needed."
fi fi
if [ $suppress_confirmation -eq 0 ]; then if [ $suppress_confirmation -eq 0 ]; then
read -s -n1 -p "Do you really want to hot-deploy bundle $OSGI_BUNDLE_NAME to $SERVERS_HOME/$active_branch? (y/N): " answer read -s -n1 -p "Do you really want to hot-deploy bundle $OSGI_BUNDLE_NAME to $SERVERS_HOME/$active_branch? (y/N): " answer
case $answer in case $answer in
"Y" | "y") echo "Continuing";; "Y" | "y") echo "Continuing";;
@@ -571,13 +571,13 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
# back to root! # back to root!
cd $PROJECT_HOME cd $PROJECT_HOME
if [ $testing -eq 0 ]; then if [ $testing -eq 0 ]; then
echo "INFO: Skipping tests" echo "INFO: Skipping tests"
extra="$extra -Dmaven.test.skip=true -DskipTests=true" extra="$extra -Dmaven.test.skip=true -DskipTests=true"
else else
extra="$extra -DskipTests=false" extra="$extra -DskipTests=false"
# TODO: Think about http://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html # TODO: Think about http://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html
fi fi
if [ $android -eq 0 ] && [ $gwtcompile -eq 0 ] && [ $testing -eq 0 ]; then if [ $android -eq 0 ] && [ $gwtcompile -eq 0 ] && [ $testing -eq 0 ]; then
parallelexecution=1 parallelexecution=1
@@ -602,8 +602,6 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
TRACKING_APP_VERSION=`grep "android:versionCode=" mobile/com.sap.$PROJECT_TYPE.android.tracking.app/AndroidManifest.xml | cut -d "\"" -f 2` TRACKING_APP_VERSION=`grep "android:versionCode=" mobile/com.sap.$PROJECT_TYPE.android.tracking.app/AndroidManifest.xml | cut -d "\"" -f 2`
echo "TRACKING_APP_VERSION=$TRACKING_APP_VERSION" echo "TRACKING_APP_VERSION=$TRACKING_APP_VERSION"
extra="$extra -Dtracking-app-version=$TRACKING_APP_VERSION" extra="$extra -Dtracking-app-version=$TRACKING_APP_VERSION"
else else
echo "INFO: Deactivating mobile modules" echo "INFO: Deactivating mobile modules"
extra="$extra -P !with-mobile" extra="$extra -P !with-mobile"
@@ -653,7 +651,7 @@ fi
if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
if [ $suppress_confirmation -eq 0 ]; then if [ $suppress_confirmation -eq 0 ]; then
read -s -n1 -p "Currently branch $active_branch is active and I will deploy to $ACDIR. Do you want to proceed with $@ (y/N): " answer read -s -n1 -p "Currently branch $active_branch is active and I will deploy to $ACDIR. Do you want to proceed with $@ (y/N): " answer
case $answer in case $answer in
"Y" | "y") echo "Continuing";; "Y" | "y") echo "Continuing";;
@@ -777,7 +775,7 @@ if [[ "$@" == "remote-deploy" ]]; then
REMOTE_HOME=`ssh $REMOTE_SERVER_LOGIN 'echo $HOME/servers'` REMOTE_HOME=`ssh $REMOTE_SERVER_LOGIN 'echo $HOME/servers'`
REMOTE_SERVER="$REMOTE_HOME/$SERVER" REMOTE_SERVER="$REMOTE_HOME/$SERVER"
if [ $suppress_confirmation -eq 0 ]; then if [ $suppress_confirmation -eq 0 ]; then
read -s -n1 -p "I will deploy the current GIT branch to $REMOTE_SERVER_LOGIN:$REMOTE_SERVER. Is this correct (y/n)? " answer read -s -n1 -p "I will deploy the current GIT branch to $REMOTE_SERVER_LOGIN:$REMOTE_SERVER. Is this correct (y/n)? " answer
case $answer in case $answer in
"Y" | "y") OK=1;; "Y" | "y") OK=1;;
@@ -829,7 +827,7 @@ if [[ "$@" == "remote-deploy" ]]; then
echo "Deployed successfully. I did NOT change any configuration (no env.sh or config.ini or jetty.xml adaption), only code!" echo "Deployed successfully. I did NOT change any configuration (no env.sh or config.ini or jetty.xml adaption), only code!"
if [ $suppress_confirmation -eq 0 ]; then if [ $suppress_confirmation -eq 0 ]; then
read -s -n1 -p "Do you want me to restart the remote server (y/n)? " answer read -s -n1 -p "Do you want me to restart the remote server (y/n)? " answer
case $answer in case $answer in
"Y" | "y") OK=1;; "Y" | "y") OK=1;;
@@ -17,14 +17,12 @@ import com.sap.sse.common.TimePoint;
public class TracTracStartTimeUpdateHandler extends UpdateHandler implements StartTimeChangedListener { public class TracTracStartTimeUpdateHandler extends UpdateHandler implements StartTimeChangedListener {
private static final String ACTION = "update_race_start_time"; private static final String ACTION = "update_race_start_time";
private final TracTracStartTimeResetHandler resetHandler;
private final static Logger logger = Logger.getLogger(TracTracStartTimeUpdateHandler.class.getName()); private final static Logger logger = Logger.getLogger(TracTracStartTimeUpdateHandler.class.getName());
private final static String FIELD_RACE_START_TIME = "race_start_time"; private final static String FIELD_RACE_START_TIME = "race_start_time";
public TracTracStartTimeUpdateHandler(URI updateURI, String tracTracUsername, String tracTracPassword, Serializable tracTracEventId, Serializable raceId) { public TracTracStartTimeUpdateHandler(URI updateURI, String tracTracUsername, String tracTracPassword, Serializable tracTracEventId, Serializable raceId) {
super(updateURI, ACTION, tracTracUsername, tracTracPassword, tracTracEventId, raceId); super(updateURI, ACTION, tracTracUsername, tracTracPassword, tracTracEventId, raceId);
resetHandler = new TracTracStartTimeResetHandler(updateURI, tracTracUsername, tracTracPassword, tracTracEventId, raceId);
} }
@Override @Override
@@ -34,8 +32,20 @@ public class TracTracStartTimeUpdateHandler extends UpdateHandler implements Sta
} }
if (newStartTime == null) { if (newStartTime == null) {
// reset start time with TracTrac /*
resetHandler.startTimeChanged(null); * Do not reset start time based on request by Jorge from TracTrac:
*
* """
* We have detected that when you want to update the race start time your
* system sends before a message to reset the race start time. When your
* system invokes the first service our system changes the race start time
* to null and this value causes some secondary effects in our side.
*
* Can you just send the update_race_start_time without the
* reset_race_start_time? The reset_race_start_time service has to be used
* if you want to set the value to null.
* """
*/
} else { } else {
HashMap<String, String> additionalParameters = new HashMap<String, String>(); HashMap<String, String> additionalParameters = new HashMap<String, String>();
additionalParameters.put(FIELD_RACE_START_TIME, String.valueOf(newStartTime.asMillis())); additionalParameters.put(FIELD_RACE_START_TIME, String.valueOf(newStartTime.asMillis()));
@@ -674,7 +674,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource.get(windSource); WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource.get(windSource);
switch (windSource.getType()) { switch (windSource.getType()) {
case EXPEDITION: case EXPEDITION:
// we filter out measured wind sources with a very little confidence // we filter out measured wind sources with vary low confidence
if (windTrackInfoDTO.minWindConfidence > 0.0001) { if (windTrackInfoDTO.minWindConfidence > 0.0001) {
windSourcesToShow.add(new com.sap.sse.common.Util.Pair<WindSource, WindTrackInfoDTO>(windSource, windTrackInfoDTO)); windSourcesToShow.add(new com.sap.sse.common.Util.Pair<WindSource, WindTrackInfoDTO>(windSource, windTrackInfoDTO));
} }
@@ -82,7 +82,7 @@ public class TestWindEstimationFromManeuversOnAFew505Races extends OnlineTracTra
CreateModelException, NotEnoughDataHasBeenAddedException { CreateModelException, NotEnoughDataHasBeenAddedException {
setUp("OBMR 2012", "event_20121031_OBMR-OBMR_2012_VR_Fr_Fleet_1_2"); setUp("OBMR 2012", "event_20121031_OBMR-OBMR_2012_VR_Fr_Fleet_1_2");
Wind average = getManeuverBasedAverageWind(); Wind average = getManeuverBasedAverageWind();
assertEquals(222, average.getFrom().getDegrees(), 5.0); // wind in this race was from 075deg on average assertEquals(222, average.getFrom().getDegrees(), 5.0);
} }
@Test @Test
@@ -91,7 +91,7 @@ public class TestWindEstimationFromManeuversOnAFew505Races extends OnlineTracTra
CreateModelException, NotEnoughDataHasBeenAddedException { CreateModelException, NotEnoughDataHasBeenAddedException {
setUp("event_20110609_KielerWoch-505_Race_2"); setUp("event_20110609_KielerWoch-505_Race_2");
Wind average = getManeuverBasedAverageWind(); Wind average = getManeuverBasedAverageWind();
assertEquals(235, average.getFrom().getDegrees(), 5.0); // wind in this race was from 075deg on average assertEquals(235, average.getFrom().getDegrees(), 5.0);
} }
@Test @Test
@@ -100,7 +100,7 @@ public class TestWindEstimationFromManeuversOnAFew505Races extends OnlineTracTra
CreateModelException, NotEnoughDataHasBeenAddedException { CreateModelException, NotEnoughDataHasBeenAddedException {
setUp("event_20110609_KielerWoch-505_Race_3"); setUp("event_20110609_KielerWoch-505_Race_3");
Wind average = getManeuverBasedAverageWind(); Wind average = getManeuverBasedAverageWind();
assertEquals(245, average.getFrom().getDegrees(), 5.0); // wind in this race was from 075deg on average assertEquals(245, average.getFrom().getDegrees(), 5.0);
} }
@Test @Test
@@ -109,7 +109,7 @@ public class TestWindEstimationFromManeuversOnAFew505Races extends OnlineTracTra
CreateModelException, NotEnoughDataHasBeenAddedException { CreateModelException, NotEnoughDataHasBeenAddedException {
setUp("event_20110609_KielerWoch-505_race_4"); setUp("event_20110609_KielerWoch-505_race_4");
Wind average = getManeuverBasedAverageWind(); Wind average = getManeuverBasedAverageWind();
assertEquals(275, average.getFrom().getDegrees(), 5.0); // wind in this race was from 075deg on average assertEquals(270, average.getFrom().getDegrees(), 5.0);
} }
@Test @Test
@@ -139,8 +139,11 @@ public class TestWindEstimationFromManeuversOnAFew505Races extends OnlineTracTra
.sorted((c1, c2) -> (int) -Math.signum(getAverageLikelihood(c1, ManeuverType.TACK) .sorted((c1, c2) -> (int) -Math.signum(getAverageLikelihood(c1, ManeuverType.TACK)
- getAverageLikelihood(c2, ManeuverType.TACK))).collect(Collectors.toList()); - getAverageLikelihood(c2, ManeuverType.TACK))).collect(Collectors.toList());
// expecting a wind direction that is from around 245deg, +/- 10deg // expecting a wind direction that is from around 245deg, +/- 10deg
assertEquals(245., windTrack.getWeightedAverageMiddleManeuverCOGDeg(clustersSortedByAverageTackLikelihood.get(0), ManeuverType.TACK).getDegrees(), 10.); assertEquals(
assertEquals(245., windTrack.getWeightedAverageMiddleManeuverCOGDeg(clustersSortedByAverageTackLikelihood.get(1), ManeuverType.TACK).getDegrees(), 10.); 245., windTrack.getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(
clustersSortedByAverageTackLikelihood.get(0), ManeuverType.TACK).getA().getDegrees(), 10.);
assertEquals(245., windTrack.getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(
clustersSortedByAverageTackLikelihood.get(1), ManeuverType.TACK).getA().getDegrees(), 10.);
} }
private double getAverageLikelihood( private double getAverageLikelihood(
@@ -70,26 +70,59 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
* for another cluster to be a cluster's opposite tack cluster is determined by first computing the * for another cluster to be a cluster's opposite tack cluster is determined by first computing the
* {@link #getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(Cluster, double, Bearing) angular * {@link #getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(Cluster, double, Bearing) angular
* distance}. For each so many degrees of distance as specified by this constant, the likelihood of the cluster to be the * distance}. For each so many degrees of distance as specified by this constant, the likelihood of the cluster to be the
* opposite tack cluster is halved. * opposite tack cluster is halved. This constant can also be used to assign likelihoods for other cluster types
* based on their distance from an expected configuration of maneuver angle and middle COG.
*/ */
private static final double ANGULAR_DISTANCE_FOR_HALF_CONFIDENCE_FOR_OPPOSITE_TACK_CLUSTER_DEG = 20.; private static final double ANGULAR_DISTANCE_FOR_HALF_CONFIDENCE_FOR_OPPOSITE_TACK_CLUSTER_DEG = 20.;
/**
* When one or two jibe clusters have been found whose
* {@link #getLikelihoodOfBeingJibeCluster(Speed, Stream, BoatClass, Set) likelihood of being a jibe cluster} is 100%,
* this is the boost factor by which the base likelihood for the tack cluster candidate will be increased (up to a
* maximum of 1.0).
*/
private static final double BOOST_FACTOR_FOR_FULL_JIBE_CLUSTER_LIKELIHOOD = 0.2;
/**
* When one or two head-up / bear-away clusters on the two tacks adjacent to the middle upwind COG have been found
* whose {@link #getLikelihoodOfClusterBasedOnDistanceFromExpected(double, Bearing, Cluster) likelihood of being
* such a cluster} is 100%, this is the boost factor by which the base likelihood for the tack cluster candidate
* will be increased (up to a maximum of 1.0).
*/
private static final double BOOST_FACTOR_FOR_FULL_HEAD_UP_BEAR_AWAY_CLUSTER_LIKELIHOOD = 0.1;
/**
* When looking for the head-up/bear-away clusters, which average maneuver angle should be expected? This is a bit
* fuzzy because in some cases the head-up and bear-away maneuvers may be grouped into one cluster, resulting in an
* average maneuver angle that is close to zero whereas in other cases they may form two clusters, one with positive
* and the other with negative maneuver angles of somewhere between 10-20deg, typically.
*/
private static final double EXPECTED_AVERAGE_HEAD_UP_AND_BEAR_AWAY_MANEUVER_ANGLE_DEG = 10.;
/** /**
* We're trying to find the jibe cluster based on a candidate for the tack cluster. This constant tells how many degrees * We're trying to find the jibe cluster based on a candidate for the tack cluster. This constant tells how many degrees
* the jibe cluster may be off from the reversed tack cluster's centroid in order to be accepted as jibe cluster candidate. * the jibe cluster may be off from the reversed tack cluster's centroid in order to be accepted as jibe cluster candidate.
*/ */
private static final double THRESHOLD_JIBE_CLUSTER_DIFFERENCE_DEGREES = 20; private static final double THRESHOLD_JIBE_CLUSTER_DIFFERENCE_DEGREES = 20;
/**
* For judging the likelihood of a cluster being a speed cluster, the speed ratio of the tack cluster candidate and
* the jibe cluster candidate is computed and compared to what the polar service predicts. The basic likelihood of the
* jibe cluster candidate (based on the maneuver angles relative to their speeds) is boosted by this factor for a perfect
* speed ratio match.
*/
private static final double BOOST_FACTOR_FOR_JIBE_TACK_SPEED_RATIO_LIKELIHOOD = 0;
private final PolarDataService polarService; private final PolarDataService polarService;
private final TrackedRace trackedRace; private final TrackedRace trackedRace;
/** /**
* Caches the {@link #getWeightedAverageMiddleManeuverCOGDeg(Cluster, ManeuverType)} results because these values are required several times to * Caches the {@link #getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(Cluster, ManeuverType)} results because these values are required several times to
* determine the likelihood with which a cluster is a cluster of tacks or a cluster of jibes. Note that the un-weighted averages can simply be obtained * determine the likelihood with which a cluster is a cluster of tacks or a cluster of jibes. Note that the un-weighted averages can simply be obtained
* by asking the cluster for its {@link Cluster#getCentroid() centroid}. * by asking the cluster for its {@link Cluster#getCentroid() centroid}.
*/ */
private final Map<Pair<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>, ManeuverType>, Bearing> private final Map<Pair<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>, ManeuverType>, Pair<Bearing, Double>>
weightedAverageMiddleCOGForManeuverType; weightedAverageMiddleCOGForManeuverType;
/** /**
@@ -308,17 +341,16 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
* There are usually no other maneuvers with similar middle COG because boats can't sail straight against the wind. * There are usually no other maneuvers with similar middle COG because boats can't sail straight against the wind.
* <p> * <p>
* *
* We evaluate each cluster's likelihood of being the tack cluster by for each of its maneuvers adding up the * We evaluate each cluster's likelihood of being the tack cluster by computing the average across all its maneuvers
* likelihood of that maneuver being a tack, solely based on its maneuver angle, as judged by the polar service. If * for that maneuver being a tack, solely based on its speed and maneuver angle, as
* a cluster exists that has approximately the opposite average middle COG (the jibe cluster candidate), the * {@link PolarDataService#getManeuverLikelihoodAndTwsTwa(BoatClass, Speed, double, ManeuverType) judged by the
* likelihoods for its maneuvers being a jibe are added. (TODO adding is statistically not adequate here; however, * polar service}. If a cluster exists that has approximately the opposite average middle COG (the jibe cluster
* multiplying hundreds if not thousands of probability values would easily lead to a double value going to 0.0 * candidate), the {@link #getLikelihoodOfBeingJibeCluster(Speed, Stream, BoatClass, Set) likelihood for its maneuvers
* quickly. But is adding adequate to estimate the cluster having the maximum likelihood of being the tack cluster? * being jibes} are determined and may result in a "boost" for the basic probability of up to 20%. Finally, the
* We'd only need a monotonous function.) Finally, the resulting likelihood is multiplied by how likely the speed * resulting likelihood is multiplied by how likely the speed ratio between the tack and jibe cluster candidates is,
* ratio between the tack and jibe cluster candidates is, based on the polar diagram. For this task, the cluster's * based on the polar diagram. For this task, the cluster's maneuvers' average speed (weighted by each maneuver's
* maneuvers' average speed (weighted by each maneuver's likelihood of being a tack) is divided by that of the jibe * likelihood of being a tack) is divided by that of the jibe cluster's maneuvers' average speed (weighted by each
* cluster's maneuvers' average speed (weighted by each maneuver's likelihood of being a jibe) and assigned a * maneuver's likelihood of being a jibe) and assigned a likelihood by the polar service.
* likelihood by the polar service.
* <p> * <p>
* *
* The cluster that has the highest likelihood of being the tack cluster is selected. The confidence of the choice * The cluster that has the highest likelihood of being the tack cluster is selected. The confidence of the choice
@@ -361,21 +393,22 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
final Map<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>, Double> final Map<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>, Double>
likelihoodOfBeingTackCluster = new HashMap<>(); likelihoodOfBeingTackCluster = new HashMap<>();
for (Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> c : clusters) { for (Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> c : clusters) {
likelihoodOfBeingTackCluster.put(c, getLikelihoodOfBeingTackCluster(c, clusters)); likelihoodOfBeingTackCluster.put(c,
getLikelihoodOfBeingTackCluster(c, clusters));
} }
// The first two clusters in the resulting list are now our best bet for the tack clusters, assuming that the likelihood for the third element // The first two clusters in the resulting list are now our best bet for the tack clusters, assuming that the likelihood for the third element
// in the list is already much lower. Should there be a significant degradation of likelihood between the first and the second cluster, we // in the list is already much lower. Should there be a significant degradation of likelihood between the first and the second cluster, we
// will only use the first, particularly if the average middle COGs of the first two clusters differ significantly. // will only use the first, particularly if the average middle COGs of the first two clusters differ significantly.
final List<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> tackClusters = final List<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> tackClusters =
clusters.stream() clusters.stream()
.sorted((c1, c2) -> (int) -Math.signum(likelihoodOfBeingTackCluster.get(c1) // TODO cache result of getLikelihoodOfBeingTackCluster .sorted((c1, c2) -> (int) -Math.signum(likelihoodOfBeingTackCluster.get(c1)
- likelihoodOfBeingTackCluster.get(c2))).limit(2).collect(Collectors.toList()); - likelihoodOfBeingTackCluster.get(c2))).limit(2).collect(Collectors.toList());
addWindFixes(tackClusters.stream(), ManeuverType.TACK); addWindFixes(tackClusters.stream(), ManeuverType.TACK);
Bearing estimatedJibeMiddleCOG = Bearing estimatedJibeMiddleCOG =
tackClusters.isEmpty() ? null tackClusters.isEmpty() ? null
: (tackClusters.size() == 1 ? getWeightedAverageMiddleManeuverCOGDeg(tackClusters.get(0), ManeuverType.TACK) : (tackClusters.size() == 1 ? getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(tackClusters.get(0), ManeuverType.TACK).getA()
: new ScalableBearing(getWeightedAverageMiddleManeuverCOGDeg(tackClusters.get(0), ManeuverType.TACK)).add( : new ScalableBearing(getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(tackClusters.get(0), ManeuverType.TACK).getA()).add(
new ScalableBearing(getWeightedAverageMiddleManeuverCOGDeg(tackClusters.get(1), ManeuverType.TACK))). new ScalableBearing(getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(tackClusters.get(1), ManeuverType.TACK).getA())).
divide(2)).reverse(); divide(2)).reverse();
final List<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> jibeClusters = final List<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> jibeClusters =
getJibeClusters(estimatedJibeMiddleCOG, clusters).collect(Collectors.toList()); getJibeClusters(estimatedJibeMiddleCOG, clusters).collect(Collectors.toList());
@@ -402,12 +435,14 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
/** /**
* Determines the likelihood of <code>cluster</code> being a cluster of tacks, based on the average likelihood of * Determines the likelihood of <code>cluster</code> being a cluster of tacks, based on the average likelihood of
* the maneuver classifications in <code>cluster</code> to be maneuvers of type {@link ManeuverType#TACK TACK}, in * the maneuver classifications in <code>cluster</code> to be maneuvers of type {@link ManeuverType#TACK TACK}, in
* turn based on what the {@link #polarService} thinks that this likelihood is and based on whether we find other * turn based on what the {@link #polarService} thinks that this
* <code>clusters</code> that support this hypothesis. The likelihood is positively affected * {@link PolarDataService#getManeuverLikelihoodAndTwsTwa(BoatClass, Speed, double, ManeuverType) likelihood} is and
* based on whether we find other <code>clusters</code> that support this hypothesis. The likelihood is positively
* affected
* *
* <ul> * <ul>
* <li>if a second cluster with similar average middle COG and a similar average maneuver angle with opposite sign * <li>if a second cluster with similar average middle COG and a similar average maneuver angle with opposite sign
* is found that also has a high average likelihood of the maneuvers contained being tacks</li> * is found that also has a high average likelihood of its maneuvers being tacks</li>
* <li>if other clusters are found that for the average true wind speed estimated from the maneuvers in * <li>if other clusters are found that for the average true wind speed estimated from the maneuvers in
* <code>cluster</code> seem like head-up/bear-away clusters on port/starboard tack with an average middle COG that * <code>cluster</code> seem like head-up/bear-away clusters on port/starboard tack with an average middle COG that
* is half the tacking angle for that wind speed away from the tack cluster's average middle COG</li> * is half the tacking angle for that wind speed away from the tack cluster's average middle COG</li>
@@ -423,7 +458,14 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
* apart from the tacks even if they have similar maneuver angles.</li> * apart from the tacks even if they have similar maneuver angles.</li>
* </ul> * </ul>
* *
* @return a likelihood between 0..1 (inclusive) * @return a likelihood between 0..1 (inclusive), obtained by multiplying the based likelihood of the
* <code>cluster</code> to hold tacks, based on the maneuver angles and their speed and how likely the
* {@link #polarService} considers this to be a tack; with boost factors added for the likelihood of having
* found a good jibe cluster and boost factors for the likelihoods of having found starboard and port tack
* clusters that hold the head-up/bear-away maneuvers. These boosts can add as much as
* {@link #BOOST_FACTOR_FOR_FULL_JIBE_CLUSTER_LIKELIHOOD} + 2*
* {@link #BOOST_FACTOR_FOR_FULL_HEAD_UP_BEAR_AWAY_CLUSTER_LIKELIHOOD} of the original value but will be
* capped at the maximum likelihood of 1.0.
* *
* @see ManeuverClassification#getLikelihoodAndTWSBasedOnSpeedAndAngle(ManeuverType) * @see ManeuverClassification#getLikelihoodAndTWSBasedOnSpeedAndAngle(ManeuverType)
* @see PolarDataService#getManeuverLikelihoodAndTwsTwa(BoatClass, Speed, double, ManeuverType) * @see PolarDataService#getManeuverLikelihoodAndTwsTwa(BoatClass, Speed, double, ManeuverType)
@@ -433,8 +475,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
Set<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> clusters) { Set<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> clusters) {
// start with the average of all of the cluster's maneuvers' likelihood to be a tack // start with the average of all of the cluster's maneuvers' likelihood to be a tack
double averageTackLikelihood = getAverageLikelihoodOfBeingManeuver(ManeuverType.TACK, cluster.stream()); double averageTackLikelihood = getAverageLikelihoodOfBeingManeuver(ManeuverType.TACK, cluster.stream());
final Bearing approximateMiddleCOG = getWeightedAverageMiddleManeuverCOGDeg(cluster, ManeuverType.TACK); final Bearing approximateMiddleCOG = getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(cluster, ManeuverType.TACK).getA();
final Bearing approximateMiddleCOGForJibes = approximateMiddleCOG.reverse();
// search for the opposite tack cluster by finding the one that has the smallest angular distance to the expected // search for the opposite tack cluster by finding the one that has the smallest angular distance to the expected
// middle COG (same as for the candidate cluster) and expected maneuver angle (the cluster's average maneuver angle with inverted sign) // middle COG (same as for the candidate cluster) and expected maneuver angle (the cluster's average maneuver angle with inverted sign)
final Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> oppositeTackCluster = final Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> oppositeTackCluster =
@@ -442,11 +483,29 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(a, -cluster.getCentroid().getB(), approximateMiddleCOG)- getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(a, -cluster.getCentroid().getB(), approximateMiddleCOG)-
getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(b, -cluster.getCentroid().getB(), approximateMiddleCOG))). getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(b, -cluster.getCentroid().getB(), approximateMiddleCOG))).
orElse(null); orElse(null);
final double likelihoodOfOppositeCluster = getLikelihoodOfOppositeTackCluster(-cluster.getCentroid().getB(), approximateMiddleCOG, oppositeTackCluster); // if the opposite tack cluster is empty, it's pretty unlikely that it's a tack cluster; assign a low default probability of 10%
final double likelihoodOfOppositeCluster = oppositeTackCluster.isEmpty() ? 0.1 :
getLikelihoodOfClusterBasedOnDistanceFromExpected(-cluster.getCentroid().getB(), approximateMiddleCOG, oppositeTackCluster);
// compute weighted averages across the current candidate cluster and its supposed opposite tack cluster where the latter
// is weighted by the likelihoodOfOppositeCluster whereas the candidate cluster is always weighed as 1.0 for this purpose
final double averageTackingAngleDeg = (Math.abs(getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(cluster, ManeuverType.TACK).getB()) +
likelihoodOfOppositeCluster*Math.abs(getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(oppositeTackCluster, ManeuverType.TACK).getB()))
/ (1+likelihoodOfOppositeCluster);
final Bearing averageUpwindCOG = new ScalableBearing(
getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(cluster, ManeuverType.TACK).getA()).add(new ScalableBearing(
getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(oppositeTackCluster, ManeuverType.TACK).getA()).
multiply(likelihoodOfOppositeCluster)).divide(1+likelihoodOfOppositeCluster);
// TODO search for head-up/bear-away cluster(s) to port and to starboard of cluster's weighted middle COG // Now search for head-up/bear-away cluster(s) to port and to starboard of cluster's weighted middle COG
final Bearing expectedUpwindStarboardTackCOG = averageUpwindCOG.add(new DegreeBearingImpl(-averageTackingAngleDeg/2.));
double starboardHeadUpBearAwayClusterLikelihood = getLikelihoodOfBestFittingHeadUpBearAwayCluster(
clusters.stream().filter(c->c!=cluster), expectedUpwindStarboardTackCOG);
final Bearing expectedUpwindPortTackCOG = averageUpwindCOG.add(new DegreeBearingImpl(averageTackingAngleDeg/2.));
double portHeadUpBearAwayClusterLikelihood = getLikelihoodOfBestFittingHeadUpBearAwayCluster(
clusters.stream().filter(c->c!=cluster), expectedUpwindPortTackCOG);
// under the assumption that cluster holds tacks, find the clusters that then most likely hold the jibes // under the assumption that cluster holds tacks, find the clusters that then most likely hold the jibes
final Bearing approximateMiddleCOGForJibes = averageUpwindCOG.reverse();
Stream<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> jibeClusters = Stream<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> jibeClusters =
getJibeClusters(approximateMiddleCOGForJibes, clusters); getJibeClusters(approximateMiddleCOGForJibes, clusters);
// increase cluster's score if "good" jibe clusters are found; quality is defined by how well the angle matches and how well the speed ratio fits // increase cluster's score if "good" jibe clusters are found; quality is defined by how well the angle matches and how well the speed ratio fits
@@ -455,12 +514,14 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
if (tackClusterWeightedAverageSpeed != null) { if (tackClusterWeightedAverageSpeed != null) {
// find out how likely the speed ratio is between the candidate tack cluster and the corresponding // find out how likely the speed ratio is between the candidate tack cluster and the corresponding
// hypothetical jibe clusters // hypothetical jibe clusters
double tackJibeLikelihoodBasedOnSpeedRatioAndAverageManeuverAngle = getLikelihoodOfBeingJibeCluster( double jibeClusterLikelihood = getLikelihoodOfBeingJibeCluster(
tackClusterWeightedAverageSpeed, jibeClusters.map((jc) -> jc.stream()).reduce(Stream::concat) tackClusterWeightedAverageSpeed, jibeClusters.map((jc) -> jc.stream()).reduce(Stream::concat)
.orElse(Stream.empty()), getBoatClass()); .orElse(Stream.empty()), getBoatClass(), clusters);
// Likely jibe clusters may raise the general likelihood by up to 20% of the value so far, but not to over 1.0 // Likely jibe clusters may raise the general likelihood by up to 20% of the value so far, but not to over 1.0
result = Math.min(1.0, averageTackLikelihood * likelihoodOfOppositeCluster * result = Math.min(1.0, averageTackLikelihood * likelihoodOfOppositeCluster *
(1.0 + 0.2 * tackJibeLikelihoodBasedOnSpeedRatioAndAverageManeuverAngle)); (1.0 + BOOST_FACTOR_FOR_FULL_JIBE_CLUSTER_LIKELIHOOD * jibeClusterLikelihood +
BOOST_FACTOR_FOR_FULL_HEAD_UP_BEAR_AWAY_CLUSTER_LIKELIHOOD * starboardHeadUpBearAwayClusterLikelihood +
BOOST_FACTOR_FOR_FULL_HEAD_UP_BEAR_AWAY_CLUSTER_LIKELIHOOD * portHeadUpBearAwayClusterLikelihood));
} else { } else {
result = .1; // no elements in the cluster, therefore no average speed; low propability that this would be a tack cluster result = .1; // no elements in the cluster, therefore no average speed; low propability that this would be a tack cluster
} }
@@ -468,28 +529,86 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
} }
/** /**
* Tells how likely it is that the two clusters are opposite tack clusters. This is determined by looking at the * Determines a likelihood for <code>cluster</code> being a head-up/bear-away cluster for the
* {@link #getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(Cluster, double, Bearing) angular distance} * <code>expectedMiddleCOG</code>.
* between the two clusters regarding their middle COG and their average maneuver angles. The likelihood decreases exponentially *
* with increasing angle differences. It is 1.0 for an exact match and reaches 0.5 for an angular distance of 20deg. * @return a value between 0..1 (exclusive)
* @see #getLikelihoodOfClusterBasedOnDistanceFromExpected(double, Bearing, Cluster)
*/ */
private double getLikelihoodOfOppositeTackCluster( private double getLikelihoodOfBestFittingHeadUpBearAwayCluster(
Stream<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> clusters,
final Bearing expectedAverageMiddleCOG) {
final Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> starboardTackHeadUpAndBearAwayCluster =
clusters.max((a, b)->(int) Math.signum(
getLikelihoodOfClusterBasedOnDistanceFromExpected(EXPECTED_AVERAGE_HEAD_UP_AND_BEAR_AWAY_MANEUVER_ANGLE_DEG, expectedAverageMiddleCOG, a)-
getLikelihoodOfClusterBasedOnDistanceFromExpected(EXPECTED_AVERAGE_HEAD_UP_AND_BEAR_AWAY_MANEUVER_ANGLE_DEG, expectedAverageMiddleCOG, b))).
orElse(null);
double starboardHeadUpBearAwayClusterLikelihood = starboardTackHeadUpAndBearAwayCluster == null ? 0.0 :
getLikelihoodOfClusterBasedOnDistanceFromExpected(
EXPECTED_AVERAGE_HEAD_UP_AND_BEAR_AWAY_MANEUVER_ANGLE_DEG, expectedAverageMiddleCOG, starboardTackHeadUpAndBearAwayCluster);
return starboardHeadUpBearAwayClusterLikelihood;
}
/**
* Assigns a likelihood to a cluster based on its
* {@link #getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(Cluster, double, Bearing)
* "angular distance"} from an expected average middle COG and Tells how likely it is that the two clusters are opposite tack clusters. This is determined
* by looking at the
* {@link #getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(Cluster, double, Bearing)
* angular distance} between the two clusters regarding their middle COG and their average maneuver angles. The
* likelihood decreases exponentially with increasing angle differences. It is 1.0 for an exact match and reaches
* 0.5 for an angular distance of 20deg.
*/
private double getLikelihoodOfClusterBasedOnDistanceFromExpected(
double expectedManeuverAngleDeg, Bearing expectedApproximateMiddleCOG, double expectedManeuverAngleDeg, Bearing expectedApproximateMiddleCOG,
Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> oppositeTackCluster) { Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> oppositeTackCluster) {
return Math.exp(Math.log(0.5) * (getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG( final double clusterDistanceFromExpected = getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(
oppositeTackCluster, expectedManeuverAngleDeg, expectedApproximateMiddleCOG) / ANGULAR_DISTANCE_FOR_HALF_CONFIDENCE_FOR_OPPOSITE_TACK_CLUSTER_DEG)); oppositeTackCluster, expectedManeuverAngleDeg, expectedApproximateMiddleCOG);
return getLikelihoodOfClusterBasedOnDistanceFromExpected(clusterDistanceFromExpected);
}
/**
* Assigns a likelihood to a cluster based on its
* {@link #getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(Cluster, double, Bearing)
* "angular distance"} from an expected average middle COG and Tells how likely it is that the two clusters are
* opposite tack clusters. This is determined by looking at the
* {@link #getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(Cluster, double, Bearing)
* angular distance} between the two clusters regarding their middle COG and their average maneuver angles. The
* likelihood decreases exponentially with increasing angle differences. It is 1.0 for an exact match and reaches
* 0.5 for an angular distance of 20deg (see
* {@link #ANGULAR_DISTANCE_FOR_HALF_CONFIDENCE_FOR_OPPOSITE_TACK_CLUSTER_DEG}).
*
* @param clusterDistanceFromExpected
* an angular distance as computed by {@link #getClusterDistance(double, Bearing, double, Bearing)} and
* {@link #getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(Cluster, double, Bearing)}
* .
*/
private double getLikelihoodOfClusterBasedOnDistanceFromExpected(final double clusterDistanceFromExpected) {
return Math.exp(Math.log(0.5) * (clusterDistanceFromExpected / ANGULAR_DISTANCE_FOR_HALF_CONFIDENCE_FOR_OPPOSITE_TACK_CLUSTER_DEG));
}
/**
* A euklidian degree distance based on the two degree distances between the maneuver angles and the
* middle COGs. The expected pair is passed as <code>expcetedManeuverAngleDeg</code> and <code>expectedApproximateMiddleCOG</code> whereas
* the actuals are determined by computing the {@link ManeuverType#TACK}-weighted average for those values from <code>cluster</code>.
*/
private double getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG(
Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> cluster,
double expectedManeuverAngleDeg, Bearing expectedApproximateMiddleCOG) {
final Pair<Bearing, Double> weightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg = getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(cluster, ManeuverType.TACK);
final Bearing weightedAverageMiddleCOG = weightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg.getA();
final Double weightedAverageManeuverAngle = weightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg.getB();
return getClusterDistance(expectedManeuverAngleDeg, expectedApproximateMiddleCOG, weightedAverageManeuverAngle, weightedAverageMiddleCOG);
} }
/** /**
* A euklidian degree distance based on the two degree distances between the maneuver angles and the * A euklidian degree distance based on the two degree distances between the maneuver angles and the
* middle COGs. * middle COGs.
*/ */
private double getClusterDistanceBasedOnAverageManeuverLikelihoodAndWeightedAverageMiddleCOG( private double getClusterDistance(double expectedManeuverAngleDeg, Bearing expectedApproximateMiddleCOG,
Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> cluster, final double weightedAverageManeuverAngle, final Bearing weightedAverageMiddleCOG) {
double expectedManeuverAngleDeg, Bearing expectedApproximateMiddleCOG) { double middleCOGDiff = weightedAverageMiddleCOG.getDifferenceTo(expectedApproximateMiddleCOG).getDegrees();
final Bearing approximateMiddleCOG = getWeightedAverageMiddleManeuverCOGDeg(cluster, ManeuverType.TACK); double maneuverAngleDiff = weightedAverageManeuverAngle - expectedManeuverAngleDeg;
double middleCOGDiff = approximateMiddleCOG.getDifferenceTo(expectedApproximateMiddleCOG).getDegrees();
double maneuverAngleDiff = cluster.getCentroid().getB() - expectedManeuverAngleDeg;
return Math.sqrt(middleCOGDiff*middleCOGDiff + maneuverAngleDiff*maneuverAngleDiff); return Math.sqrt(middleCOGDiff*middleCOGDiff + maneuverAngleDiff*maneuverAngleDiff);
} }
@@ -508,7 +627,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
/** /**
* Returns at most two clusters that hold maneuvers with the highest average probability of being a jibe and whose * Returns at most two clusters that hold maneuvers with the highest average probability of being a jibe and whose
* {@link #getWeightedAverageMiddleManeuverCOGDeg(Cluster, ManeuverType) weighted average middle COG} fits that of * {@link #getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(Cluster, ManeuverType) weighted average middle COG} fits that of
* the expected value (<code>approximateMiddleCOGForJibes</code>) within * the expected value (<code>approximateMiddleCOGForJibes</code>) within
* {@link #THRESHOLD_JIBE_CLUSTER_DIFFERENCE_DEGREES} degrees. * {@link #THRESHOLD_JIBE_CLUSTER_DIFFERENCE_DEGREES} degrees.
*/ */
@@ -519,17 +638,17 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
return clusters.stream().sorted((a, b)-> return clusters.stream().sorted((a, b)->
(int) -Math.signum(getAverageLikelihoodOfBeingManeuver(ManeuverType.JIBE, a.stream()) - (int) -Math.signum(getAverageLikelihoodOfBeingManeuver(ManeuverType.JIBE, a.stream()) -
getAverageLikelihoodOfBeingManeuver(ManeuverType.JIBE, b.stream()))).filter( getAverageLikelihoodOfBeingManeuver(ManeuverType.JIBE, b.stream()))).filter(
(jibeClusterCandidate)->Math.abs(getWeightedAverageMiddleManeuverCOGDeg(jibeClusterCandidate, ManeuverType.JIBE). (jibeClusterCandidate)->Math.abs(getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(jibeClusterCandidate, ManeuverType.JIBE).getA().
getDifferenceTo(approximateMiddleCOGForJibes).getDegrees()) < THRESHOLD_JIBE_CLUSTER_DIFFERENCE_DEGREES). getDifferenceTo(approximateMiddleCOGForJibes).getDegrees()) < THRESHOLD_JIBE_CLUSTER_DIFFERENCE_DEGREES).
limit(2); limit(2);
} }
/** /**
* By looking at the weighted speeds over ground averages, and based on the {@link #polarService} determines how * By looking at the weighted speeds over ground averages, and based on the {@link #polarService} determines how
* likely it is that the jibe cluster contents are actually jibes. This is based on the idea that if for the most * {@link PolarDataService#getManeuverLikelihoodAndTwsTwa(BoatClass, Speed, double, ManeuverType) likely} it is that
* likely wind speeds the jibes have a different (usually significantly higher) entry speed than the tacks then this * the jibe cluster contents are actually jibes. This is based on the idea that if for the most likely wind speeds
* difference show show in the cluster elements. If they don't, then this should give a penalty for the cluster * the jibes have a different (usually significantly higher) entry speed than the tacks then this difference should
* likelihoods. * show in the cluster elements. If they don't, then this should give a penalty for the cluster likelihoods.
* <p> * <p>
* *
* The weight of a maneuver for computing the weighted speed average is determined to be the * The weight of a maneuver for computing the weighted speed average is determined to be the
@@ -539,26 +658,48 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
private double getLikelihoodOfBeingJibeCluster( private double getLikelihoodOfBeingJibeCluster(
Speed tackClusterWeightedAverageSpeed, Speed tackClusterWeightedAverageSpeed,
Stream<ManeuverClassification> jibeClustersContent, Stream<ManeuverClassification> jibeClustersContent,
BoatClass boatClass) { BoatClass boatClass, Set<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>> clusters) {
final int[] count = new int[1]; final int[] count = new int[1];
final double[] likelihoodSum = new double[1]; final double[] likelihoodSum = new double[1];
final ScalableBearing[] scaledAverageDownwindCOG = new ScalableBearing[1];
final double[] scaledAbsJibingAngleSum = new double[1];
Stream<ManeuverClassification> jibeClustersContentPeeker = jibeClustersContent.peek((mc)->{ Stream<ManeuverClassification> jibeClustersContentPeeker = jibeClustersContent.peek((mc)->{
count[0]++; count[0]++;
likelihoodSum[0] += mc.getLikelihoodAndTWSBasedOnSpeedAndAngle(ManeuverType.JIBE).getA(); final double likelihood = mc.getLikelihoodAndTWSBasedOnSpeedAndAngle(ManeuverType.JIBE).getA();
likelihoodSum[0] += likelihood;
final ScalableBearing scaledCOG = new ScalableBearing(mc.getMiddleManeuverCourse()).multiply(likelihood);
if (scaledAverageDownwindCOG[0] == null) {
scaledAverageDownwindCOG[0] = scaledCOG;
} else {
scaledAverageDownwindCOG[0] = scaledAverageDownwindCOG[0].add(scaledCOG);
}
scaledAbsJibingAngleSum[0] += likelihood * Math.abs(mc.getManeuverAngleDeg());
}); });
Speed jibeClusterWeightedAverageSpeed = getWeightedAverageSpeed(jibeClustersContentPeeker, ManeuverType.JIBE); Speed jibeClusterWeightedAverageSpeed = getWeightedAverageSpeed(jibeClustersContentPeeker, ManeuverType.JIBE);
Bearing averageDownwindCOG = scaledAverageDownwindCOG[0] == null ? null : scaledAverageDownwindCOG[0].divide(likelihoodSum[0]);
double absWeightedAverageJibingAbgle = scaledAbsJibingAngleSum[0] / likelihoodSum[0];
final double result; final double result;
if (jibeClusterWeightedAverageSpeed != null) { if (jibeClusterWeightedAverageSpeed != null) {
double tackJibeSpeedRatioLikelihood = polarService.getConfidenceForTackJibeSpeedRatio( double tackJibeSpeedRatioLikelihood = polarService.getConfidenceForTackJibeSpeedRatio(
tackClusterWeightedAverageSpeed, jibeClusterWeightedAverageSpeed, boatClass); tackClusterWeightedAverageSpeed, jibeClusterWeightedAverageSpeed, boatClass);
if (count[0] > 0) { if (count[0] > 0) {
double averageJibeLikelihood = likelihoodSum[0] / count[0]; double averageJibeLikelihood = likelihoodSum[0] / count[0];
result = averageJibeLikelihood * tackJibeSpeedRatioLikelihood; // Now search for head-up/bear-away cluster(s) to port and to starboard of cluster's weighted middle COG
final Bearing expectedDownwindStarboardTackCOG = averageDownwindCOG.add(new DegreeBearingImpl(+absWeightedAverageJibingAbgle/2.));
double starboardHeadUpBearAwayClusterLikelihood = getLikelihoodOfBestFittingHeadUpBearAwayCluster(
clusters.stream(), expectedDownwindStarboardTackCOG);
final Bearing expectedDownwindPortTackCOG = averageDownwindCOG.add(new DegreeBearingImpl(-absWeightedAverageJibingAbgle/2.));
double portHeadUpBearAwayClusterLikelihood = getLikelihoodOfBestFittingHeadUpBearAwayCluster(
clusters.stream(), expectedDownwindPortTackCOG);
result = Math.min(1.0, averageJibeLikelihood
* (1.0 + BOOST_FACTOR_FOR_JIBE_TACK_SPEED_RATIO_LIKELIHOOD * tackJibeSpeedRatioLikelihood +
BOOST_FACTOR_FOR_FULL_HEAD_UP_BEAR_AWAY_CLUSTER_LIKELIHOOD * starboardHeadUpBearAwayClusterLikelihood +
BOOST_FACTOR_FOR_FULL_HEAD_UP_BEAR_AWAY_CLUSTER_LIKELIHOOD * portHeadUpBearAwayClusterLikelihood));
} else { } else {
throw new RuntimeException("Internal error: no maneuvers in jibe cluster candidate but still a valid weighted average speed "+jibeClusterWeightedAverageSpeed); throw new RuntimeException("Internal error: no maneuvers in jibe cluster candidate but still a valid weighted average speed "+jibeClusterWeightedAverageSpeed);
} }
} else { } else {
result = 0.5; // no maneuvers in the jibe clusters; could be but may also not be a jibe cluster result = 0.1; // no maneuvers in the jibe clusters; could be but may also not be a jibe cluster
} }
return result; return result;
} }
@@ -606,28 +747,35 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
/** /**
* For a cluster of maneuver classifications, clustered two-dimensionally by middle COG and maneuver angle, computes the * For a cluster of maneuver classifications, clustered two-dimensionally by middle COG and maneuver angle, computes the
* weighted average of the middle COG where as the weight of each maneuver the likelihood that the maneuver was of type * weighted average of the middle COG and maneuver angle where as the weight of each maneuver the likelihood that the maneuver was of type
* <code>maneuverType</code> is used.<p> * <code>maneuverType</code> is used.<p>
* *
* The method has default package scope to allow tests in the same package in the test fragment to use it. * The method has default package scope to allow tests in the same package in the test fragment to use it.
* *
* @return the weighted average middle COG * @return the weighted average middle COG
*/ */
Bearing getWeightedAverageMiddleManeuverCOGDeg( Pair<Bearing, Double> getWeightedAverageMiddleManeuverCOGDegAndManeuverAngleDeg(
Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> cluster, Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble> cluster,
ManeuverType maneuverType) { ManeuverType maneuverType) {
Pair<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>, ManeuverType> key = new Pair<>( Pair<Cluster<ManeuverClassification, Pair<ScalableBearing, ScalableDouble>, Pair<Bearing, Double>, ScalableBearingAndScalableDouble>, ManeuverType> key = new Pair<>(
cluster, maneuverType); cluster, maneuverType);
Bearing result = weightedAverageMiddleCOGForManeuverType.get(key); Pair<Bearing, Double> result = weightedAverageMiddleCOGForManeuverType.get(key);
if (result == null) { if (result == null) {
ConfidenceBasedAverager<DoublePair, Bearing, Void> averager = ConfidenceFactory.INSTANCE ConfidenceBasedAverager<DoublePair, Bearing, Void> middleCOGAverager = ConfidenceFactory.INSTANCE
.createAverager(/* weigher */null); .createAverager(/* weigher */null);
HasConfidence<DoublePair, Bearing, Void> average = averager.getAverage( HasConfidence<DoublePair, Bearing, Void> average = middleCOGAverager.getAverage(
cluster.stream() cluster.stream()
.map(new ManeuverClassificationToHasConfidenceAndIsScalableAdapter<DoublePair, Bearing>( .map(new ManeuverClassificationToHasConfidenceAndIsScalableAdapter<DoublePair, Bearing>(
maneuverType, (mc) -> new ScalableBearing(mc.getMiddleManeuverCourse()), maneuverType, (mc) -> new ScalableBearing(mc.getMiddleManeuverCourse()),
polarService)).iterator(), /* at */null); polarService)).iterator(), /* at */null);
result = average.getObject(); ConfidenceBasedAverager<Double, Double, Void> maneuverAngleAverager = ConfidenceFactory.INSTANCE
.createAverager(/* weigher */null);
HasConfidence<Double, Double, Void> maneuverAngleAverageDeg = maneuverAngleAverager.getAverage(
cluster.stream()
.map(new ManeuverClassificationToHasConfidenceAndIsScalableAdapter<Double, Double>(
maneuverType, (mc) -> new ScalableDouble(mc.getManeuverAngleDeg()),
polarService)).iterator(), /* at */null);
result = new Pair<>(average.getObject(), maneuverAngleAverageDeg.getObject());
weightedAverageMiddleCOGForManeuverType.put(key, result); weightedAverageMiddleCOGForManeuverType.put(key, result);
} }
return result; return result;
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.xmlexport.test</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,12 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -0,0 +1,12 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Test
Bundle-SymbolicName: com.sap.sailing.xmlexport.test
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Fragment-Host: com.sap.sailing.xmlexport
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.junit4;bundle-version="4.8.2",
com.sap.sse.common,
com.sap.sailing.domain.test;bundle-version="1.0.0",
com.tractrac.clientmodule;bundle-version="3.0.7"
+5
View File
@@ -0,0 +1,5 @@
source.. = src/,\
resources/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.xmlexport.test</artifactId>
<packaging>eclipse-test-plugin</packaging>
</project>
@@ -0,0 +1,643 @@
stored-uri:event_20141001_ESSNice-Race_1.mtb
StartReplay:1
// Input from params_static:
// Input from event_parameters:
DefaultPanel:
ReCalcDirAndSpeed:1
CompMarkerUUID:2dee0384-2c45-11e4-a9f6-14dae9614d7b
CodanIsSponsor:0
DefaultTailLength:60
APzoomChangeTime:2500
disableAutoProduce:0
autoProduce:0
APstartLineShowOfLeg:0.25
APsideOfStartLine:0.30
APstartSwitch:0.8
APwholeLegBehindMarks:0.15
APmarkSwitch:0.13
APmarkZoomBehindMarks:0.10
APmarkZoomInFrontOfMarks:0.20
PopupMarkerUUID:84199024-2c45-11e4-a9f6-14dae9614d7b
ManualUrl:http://www.tractrac.com/files/userguide_sailing.pdf
//live-uri:tcp://192.168.1.203:4400
//stored-uri:tcp://192.168.1.203:4401
Marker1UUID:2dee0384-2c45-11e4-a9f6-14dae9614d7b
Marker1Type:RGBMarkerFactory
Marker1Directional:1
Marker1Image:markers/boat_FF0000_big.png
Marker1Length:5
Marker1MinPixelSize:30
Startlocation:7.269026586914038,43.68669104379883
MaxDiffTime:1200
MaxMetersPerSecond:20
PrecisionMeters:20
ShortAvgTime:180
LongAvgTime:900
TimeZone:2
CompMarkerTimeout:60
FramesPerSecond:8
replayspeedup:5
MatchRaceClock:0
MatchRaceClockNo:3
MatchRaceClockSpacing:300
Minimap:1
MinimapUseLayers:2
Minimapgeorect:7.241326904296898,43.705766636035165,7.296516491699208,43.67209091170523
MinimapMarkerUUID:84199024-2c45-11e4-a9f6-14dae9614d7b
Marker4UUID:84199024-2c45-11e4-a9f6-14dae9614d7b
Marker4Type:RGBmarkerFactory
Marker4Directional:1
Marker4Image:markers/boat_FF0000_small.png
MetersPerPixel:18
MetersPerPixelMax:30
MetersPerPixelMin:0.2
isMatchRace:0
DefaultDistanceUnits:meters
weatherEnabled:0
DefaultSpeedUnits:KmH
showRaceClock:1
BackGroundImage:graphics/background.jpg
DisableRotation:0
ControlPointMarkerRadius:13
disableselection:0
disableleaderline:0
DisableChangeRace:0
PopUpRow1Name:
PopUpRow1Type:Name
PopUpRow2Name:Knots
PopUpRow2Type:BoatSpeed
PopUpRow3Name:Ranking
PopUpRow3Type:RowNumber
PopUpRow4Name:Sail
PopUpRow4Type:Ini
PopUpRow5Name:After
PopUpRow5Type:BoatAfter
WindEstimator:0
MapUrl:http://tile.traclive.dk/ttm
ResultColumn1Name:#
ResultColumn1Size:20
ResultColumn1Type:RowNumber
ResultColumn2Name:sail
ResultColumn2Size:45
ResultColumn2Type:Ini
ResultColumn3Name:name
ResultColumn3Size:110
ResultColumn3Type:Name
ResultColumn4Name:after
ResultColumn4Size:45
ResultColumn4Type:BoatAfter
ResultColumn5Name:kts
ResultColumn5Size:40
ResultColumn5Type:BoatSpeed
DisableLeaderboard:0
DisableAnalysis:0
TableFontSize:11
compInitialsFontSize:11
AutoRotate:0
MassStart:0
SegmentFile:segments.php
StartType:Mass
RaceHandicapSystem:NONE
TeamCompetitionType:NONE
LiveDelaySecs: 6
//-------- AUTO GENERATED PARAMETERS START ----------------
EventID:8638bc00-2aad-0132-18bc-448a5b8481d8
EventName:ESS Nice 2014
EventDB:event_20141001_ESSNice
EventStartTime:2014-09-30 22:00:00
EventEndTime:2014-10-05 21:00:00
EventType:a1b74ca0-fdd8-11dc-8811-005056c00008
RaceID:09f4b690-2bc6-0132-1eef-10b11c4ed8fd
RaceName:Race 1
RaceStartTime:2014-10-02 12:02:00
RaceEndTime:
RaceTrackingStartTime:2014-10-02 11:58:14
RaceTrackingEndTime:2014-10-02 12:17:02
RaceDefaultRouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
RaceHandicapSystem:NONE
Image1UUID:64a7b348-83db-11e2-88c0-10bf48d758ce
Image1Type:Logo
Image1Description:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/SUI.gif
Image1Image:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/SUI.gif
Image2UUID:64c3a8fa-83db-11e2-88c0-10bf48d758ce
Image2Type:Logo
Image2Description:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/NZL.gif
Image2Image:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/NZL.gif
Image3UUID:651130a2-83db-11e2-88c0-10bf48d758ce
Image3Type:Logo
Image3Description:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/KOR.gif
Image3Image:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/KOR.gif
Image4UUID:653c1ab0-83db-11e2-88c0-10bf48d758ce
Image4Type:Logo
Image4Description:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/AUT.gif
Image4Image:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/AUT.gif
Image5UUID:65637da8-83db-11e2-88c0-10bf48d758ce
Image5Type:Logo
Image5Description:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/DEN.gif
Image5Image:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/DEN.gif
Image6UUID:65875138-83db-11e2-88c0-10bf48d758ce
Image6Type:Logo
Image6Description:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/OMN.gif
Image6Image:http://germanmaster.traclive.dk/events/event_20100806_SailforGol/images/OMN.gif
Class1UUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Class1Name:Extreme40
Team1UUID:649f1dbe-83db-11e2-88c0-10bf48d758ce
Team1Name:TeamA
Team1Comp1UUID:648bd4f2-83db-11e2-88c0-10bf48d758ce
Team2UUID:64bf0854-83db-11e2-88c0-10bf48d758ce
Team2Name:TeamB
Team2Comp1UUID:64bbff7e-83db-11e2-88c0-10bf48d758ce
Team3UUID:64dd7988-83db-11e2-88c0-10bf48d758ce
Team3Name:TeamC
Team3Comp1UUID:64da7c24-83db-11e2-88c0-10bf48d758ce
Team4UUID:64ffc402-83db-11e2-88c0-10bf48d758ce
Team4Name:TeamD
Team5UUID:65351d82-83db-11e2-88c0-10bf48d758ce
Team5Name:TeamE
Team5Comp1UUID:65322500-83db-11e2-88c0-10bf48d758ce
Team6UUID:655ed82a-83db-11e2-88c0-10bf48d758ce
Team6Name:TeamF
Team6Comp1UUID:655bd56c-83db-11e2-88c0-10bf48d758ce
Team7UUID:65829fa8-83db-11e2-88c0-10bf48d758ce
Team7Name:TeamG
Team7Comp1UUID:657fa5a0-83db-11e2-88c0-10bf48d758ce
Team8UUID:e4459a20-83db-11e2-88c0-10bf48d758ce
Team8Name:TeamH
Team9UUID:a73aabc0-d6d6-0130-8be2-60a44ce903c3
Team9Name:TeamI
Team9Comp1UUID:93085e80-7ad9-0131-e5b3-60a44ce903c3
Team10UUID:bbf81f60-d6d6-0130-8be3-60a44ce903c3
Team10Name:TeamJ
Team10Comp1UUID:f097f4f0-7ad9-0131-e5b4-60a44ce903c3
Team11UUID:65659280-eda3-0130-c5c0-10b11c4ed8fd
Team11Name:TeamK
Team11Comp1UUID:19dc3eb0-7ada-0131-e5b5-60a44ce903c3
Team12UUID:9ee14560-77af-0131-e5af-60a44ce903c3
Team12Name:TeamL
Team12Comp1UUID:5e15e7c0-7ada-0131-e5b6-60a44ce903c3
Team13UUID:b9677300-7ad7-0131-e5b0-60a44ce903c3
Team13Name:TeamM
Team13Comp1UUID:84e68b70-7ada-0131-e5b7-60a44ce903c3
Team14UUID:c9689a00-7ad7-0131-e5b1-60a44ce903c3
Team14Name:TeamN
Team15UUID:d5712f30-7ad7-0131-e5b2-60a44ce903c3
Team15Name:TeamO
Comp1UUID:648bd4f2-83db-11e2-88c0-10bf48d758ce
Comp1Name:Alinghi
Comp1NameShort:Alinghi
Comp1Color:00FF00
Comp1Nationality:SUI
Comp1Description:
Comp1Handicap:1.0000000000
Comp1HandicapDistance:0.00000
Comp1StartTime:2014-10-02 12:02:00
Comp1PictureUUID:64a7b348-83db-11e2-88c0-10bf48d758ce
Comp1ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp1RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp2UUID:64bbff7e-83db-11e2-88c0-10bf48d758ce
Comp2Name:GAC Pindar
Comp2NameShort:GAC Pindar
Comp2Color:000080
Comp2Nationality:AUS
Comp2Description:
Comp2Handicap:1.0000000000
Comp2HandicapDistance:0.00000
Comp2StartTime:2014-10-02 12:02:00
Comp2PictureUUID:64c3a8fa-83db-11e2-88c0-10bf48d758ce
Comp2ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp2RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp3UUID:64da7c24-83db-11e2-88c0-10bf48d758ce
Comp3Name:Realteam by Realstone
Comp3NameShort:Realstone
Comp3Color:000000
Comp3Nationality:AFG
Comp3Description:
Comp3Handicap:1.0000000000
Comp3HandicapDistance:0.00000
Comp3StartTime:2014-10-02 12:02:00
Comp3PictureUUID:64a7b348-83db-11e2-88c0-10bf48d758ce
Comp3ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp3RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp4UUID:65322500-83db-11e2-88c0-10bf48d758ce
Comp4Name:Red Bull Sailing Team
Comp4NameShort:Red Bull
Comp4Color:008080
Comp4Nationality:AUT
Comp4Description:
Comp4Handicap:1.0000000000
Comp4HandicapDistance:0.00000
Comp4StartTime:2014-10-02 12:02:00
Comp4PictureUUID:653c1ab0-83db-11e2-88c0-10bf48d758ce
Comp4ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp4RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp5UUID:655bd56c-83db-11e2-88c0-10bf48d758ce
Comp5Name:SAP Extreme Sailing Team
Comp5NameShort:SAP
Comp5Color:FFFFFF
Comp5Nationality:DEN
Comp5Description:
Comp5Handicap:1.0000000000
Comp5HandicapDistance:0.00000
Comp5StartTime:2014-10-02 12:02:00
Comp5PictureUUID:65637da8-83db-11e2-88c0-10bf48d758ce
Comp5ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp5RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp6UUID:657fa5a0-83db-11e2-88c0-10bf48d758ce
Comp6Name:The Wave, Muscat
Comp6NameShort:The Wave
Comp6Color:800080
Comp6Nationality:OMA
Comp6Description:
Comp6Handicap:1.0000000000
Comp6HandicapDistance:0.00000
Comp6StartTime:2014-10-02 12:02:00
Comp6PictureUUID:65875138-83db-11e2-88c0-10bf48d758ce
Comp6ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp6RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp7UUID:93085e80-7ad9-0131-e5b3-60a44ce903c3
Comp7Name:Emirates Team New Zealand
Comp7NameShort:Emirates NZ
Comp7Color:CC9900
Comp7Nationality:NZL
Comp7Description:
Comp7Handicap:1.0000000000
Comp7HandicapDistance:0.00000
Comp7StartTime:2014-10-02 12:02:00
Comp7PictureUUID:64c3a8fa-83db-11e2-88c0-10bf48d758ce
Comp7ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp7RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp8UUID:f097f4f0-7ad9-0131-e5b4-60a44ce903c3
Comp8Name:Gazprom Team Russia
Comp8NameShort:Team Russia
Comp8Color:999900
Comp8Nationality:RUS
Comp8Description:
Comp8Handicap:1.0000000000
Comp8HandicapDistance:0.00000
Comp8StartTime:2014-10-02 12:02:00
Comp8ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp8RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp9UUID:19dc3eb0-7ada-0131-e5b5-60a44ce903c3
Comp9Name:Groupama sailing team
Comp9NameShort:Groupama
Comp9Color:33CC00
Comp9Nationality:AFG
Comp9Description:
Comp9Handicap:1.0000000000
Comp9HandicapDistance:0.00000
Comp9StartTime:2014-10-02 12:02:00
Comp9ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp9RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp10UUID:5e15e7c0-7ada-0131-e5b6-60a44ce903c3
Comp10Name:J.P. Morgan BAR
Comp10NameShort:J.P. Morgan
Comp10Color:CCFF00
Comp10Nationality:GBR
Comp10Description:
Comp10Handicap:1.0000000000
Comp10HandicapDistance:0.00000
Comp10StartTime:2014-10-02 12:02:00
Comp10ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp10RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Comp11UUID:84e68b70-7ada-0131-e5b7-60a44ce903c3
Comp11Name:Oman Air
Comp11NameShort:Oman Air
Comp11Color:CCFF99
Comp11Nationality:OMA
Comp11Description:
Comp11Handicap:1.0000000000
Comp11HandicapDistance:0.00000
Comp11StartTime:2014-10-02 12:02:00
Comp11PictureUUID:65875138-83db-11e2-88c0-10bf48d758ce
Comp11ClassUUID:648ee25a-83db-11e2-88c0-10bf48d758ce
Comp11RouteUUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
ControlPoint1UUID:1100ea60-7adb-0131-e5b8-60a44ce903c3
ControlPoint1Name:Red-Green
ControlPoint1HasTwoPoints:1
ControlPoint1ShortName:RG
ControlPoint1DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Red###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Name=Red###BREAKLINE###P1.UUID=5427c7d2-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Green###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Green###BREAKLINE###P2.UUID=785c3e44-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint1Mark1Lon:7.273181
ControlPoint1Mark1Lat:43.688370
ControlPoint1Mark2Lon:7.272257
ControlPoint1Mark2Lat:43.687305
ControlPoint2UUID:1286f15e-83be-11e2-88c0-10bf48d758ce
ControlPoint2Name:Blue
ControlPoint2HasTwoPoints:0
ControlPoint2ShortName:Bl
ControlPoint2DataSheet:Name=Blue###BREAKLINE###Type=Buoy###BREAKLINE###Color=Blue###BREAKLINE###Shape=Conical
ControlPoint2Mark1Lon:7.269418
ControlPoint2Mark1Lat:43.687290
ControlPoint3UUID:1a94c6a0-7adc-0131-e5f4-60a44ce903c3
ControlPoint3Name:Black Conical-Blue
ControlPoint3HasTwoPoints:1
ControlPoint3ShortName:BB
ControlPoint3DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Black###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Pattern=Checkered###BREAKLINE###P1.Name=Black Conical###BREAKLINE###P1.UUID=d78695ea-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Blue###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Blue###BREAKLINE###P2.UUID=1286f15e-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint3Mark1Lon:7.272432
ControlPoint3Mark1Lat:43.692131
ControlPoint3Mark2Lon:7.269418
ControlPoint3Mark2Lat:43.687290
ControlPoint4UUID:2ce99970-2c47-0132-20ce-10b11c4ed8fd
ControlPoint4Name:Black Conical-White P
ControlPoint4HasTwoPoints:1
ControlPoint4ShortName:F
ControlPoint4DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Black###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Pattern=Checkered###BREAKLINE###P1.Name=Black Conical###BREAKLINE###P1.UUID=d78695ea-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=White###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=White P###BREAKLINE###P2.UUID=e9ec42f8-83bd-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint4Mark1Lon:7.272432
ControlPoint4Mark1Lat:43.692131
ControlPoint4Mark2Lon:7.271063
ControlPoint4Mark2Lat:43.691803
ControlPoint5UUID:2dc7d9d0-7b84-0131-b9c3-10b11c4ed8fd
ControlPoint5Name:Black Cylinder - Black Conical
ControlPoint5HasTwoPoints:1
ControlPoint5ShortName:BB
ControlPoint5DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Black###BREAKLINE###P1.Shape=Cylinder###BREAKLINE###P1.Pattern=Checkered###BREAKLINE###P1.Name=Black Cylinder###BREAKLINE###P1.UUID=b153dfd6-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Black###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Pattern=Checkered###BREAKLINE###P2.Name=Black Conical###BREAKLINE###P2.UUID=d78695ea-83be-11e2-88c0-10bf48d758ce
ControlPoint5Mark1Lon:7.269445
ControlPoint5Mark1Lat:43.687302
ControlPoint5Mark2Lon:7.272432
ControlPoint5Mark2Lat:43.692131
ControlPoint6UUID:2e37b410-7adc-0131-e5fe-60a44ce903c3
ControlPoint6Name:Black Conical-Yellow (white S)
ControlPoint6HasTwoPoints:1
ControlPoint6ShortName:BY
ControlPoint6DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Black###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Pattern=Checkered###BREAKLINE###P1.Name=Black Conical###BREAKLINE###P1.UUID=d78695ea-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Yellow###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Yellow###BREAKLINE###P2.UUID=3fdd2402-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint6Mark1Lon:7.272432
ControlPoint6Mark1Lat:43.692131
ControlPoint6Mark2Lon:7.269320
ControlPoint6Mark2Lat:43.687366
ControlPoint7UUID:3240586c-83c0-11e2-88c0-10bf48d758ce
ControlPoint7Name:Finish Pole
ControlPoint7HasTwoPoints:0
ControlPoint7ShortName:F
ControlPoint7DataSheet:Color=Black###BREAKLINE###Type=Landmark###BREAKLINE###Name=Finish Pole
ControlPoint7Mark1Lon:7.271467
ControlPoint7Mark1Lat:43.694897
ControlPoint8UUID:37cb36e0-7adb-0131-e5c2-60a44ce903c3
ControlPoint8Name:Red-Blue
ControlPoint8HasTwoPoints:1
ControlPoint8ShortName:RB
ControlPoint8DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Red###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Name=Red###BREAKLINE###P1.UUID=5427c7d2-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Blue###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Blue###BREAKLINE###P2.UUID=1286f15e-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint8Mark1Lon:7.273181
ControlPoint8Mark1Lat:43.688370
ControlPoint8Mark2Lon:7.269418
ControlPoint8Mark2Lat:43.687290
ControlPoint9UUID:3e6c1c94-83bf-11e2-88c0-10bf48d758ce
ControlPoint9Name:Race Committee
ControlPoint9HasTwoPoints:0
ControlPoint9ShortName:RC
ControlPoint9DataSheet:Type=StartBoat###BREAKLINE###Name=Race Committee
ControlPoint9Mark1Lon:7.267022
ControlPoint9Mark1Lat:43.691895
ControlPoint10UUID:3fdd2402-83be-11e2-88c0-10bf48d758ce
ControlPoint10Name:Yellow
ControlPoint10HasTwoPoints:0
ControlPoint10ShortName:Y
ControlPoint10DataSheet:Type=Buoy###BREAKLINE###Color=Yellow###BREAKLINE###Shape=Conical###BREAKLINE###Name=Yellow
ControlPoint10Mark1Lon:7.269320
ControlPoint10Mark1Lat:43.687366
ControlPoint11UUID:4c304720-7adc-0131-e608-60a44ce903c3
ControlPoint11Name:Black Conical-Red
ControlPoint11HasTwoPoints:1
ControlPoint11ShortName:BR
ControlPoint11DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Black###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Pattern=Checkered###BREAKLINE###P1.Name=Black Conical###BREAKLINE###P1.UUID=d78695ea-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Red###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Red###BREAKLINE###P2.UUID=5427c7d2-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint11Mark1Lon:7.272432
ControlPoint11Mark1Lat:43.692131
ControlPoint11Mark2Lon:7.273181
ControlPoint11Mark2Lat:43.688370
ControlPoint12UUID:4e0c2a90-7adb-0131-e5cc-60a44ce903c3
ControlPoint12Name:Red-Yellow
ControlPoint12HasTwoPoints:1
ControlPoint12ShortName:RY
ControlPoint12DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Red###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Name=Red###BREAKLINE###P1.UUID=5427c7d2-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Yellow###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Yellow###BREAKLINE###P2.UUID=3fdd2402-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint12Mark1Lon:7.273181
ControlPoint12Mark1Lat:43.688370
ControlPoint12Mark2Lon:7.269320
ControlPoint12Mark2Lat:43.687366
ControlPoint13UUID:5427c7d2-83be-11e2-88c0-10bf48d758ce
ControlPoint13Name:Red
ControlPoint13HasTwoPoints:0
ControlPoint13ShortName:R
ControlPoint13DataSheet:Type=Buoy###BREAKLINE###Color=Red###BREAKLINE###Shape=Conical###BREAKLINE###Name=Red
ControlPoint13Mark1Lon:7.273181
ControlPoint13Mark1Lat:43.688370
ControlPoint14UUID:621831d0-7adc-0131-e612-60a44ce903c3
ControlPoint14Name:Black Conical-Green
ControlPoint14HasTwoPoints:1
ControlPoint14ShortName:BG
ControlPoint14DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Black###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Pattern=Checkered###BREAKLINE###P1.Name=Black Conical###BREAKLINE###P1.UUID=d78695ea-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Green###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Green###BREAKLINE###P2.UUID=785c3e44-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint14Mark1Lon:7.272432
ControlPoint14Mark1Lat:43.692131
ControlPoint14Mark2Lon:7.272257
ControlPoint14Mark2Lat:43.687305
ControlPoint15UUID:77b9e620-7adb-0131-e5d6-60a44ce903c3
ControlPoint15Name:Green-Blue
ControlPoint15HasTwoPoints:1
ControlPoint15ShortName:GB
ControlPoint15DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Green###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Name=Green###BREAKLINE###P1.UUID=785c3e44-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Blue###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Blue###BREAKLINE###P2.UUID=1286f15e-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint15Mark1Lon:7.272257
ControlPoint15Mark1Lat:43.687305
ControlPoint15Mark2Lon:7.269418
ControlPoint15Mark2Lat:43.687290
ControlPoint16UUID:785c3e44-83be-11e2-88c0-10bf48d758ce
ControlPoint16Name:Green
ControlPoint16HasTwoPoints:0
ControlPoint16ShortName:G
ControlPoint16DataSheet:Type=Buoy###BREAKLINE###Color=Green###BREAKLINE###Shape=Conical###BREAKLINE###Name=Green
ControlPoint16Mark1Lon:7.272257
ControlPoint16Mark1Lat:43.687305
ControlPoint17UUID:7d9d1730-b3e8-0131-1d10-10b11c4ed8fd
ControlPoint17Name:Race Committee - Finish Pole
ControlPoint17HasTwoPoints:1
ControlPoint17ShortName:RF
ControlPoint17DataSheet:P1.Type=StartBoat###BREAKLINE###P1.Name=Race Committee###BREAKLINE###P1.UUID=3e6c1c94-83bf-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Color=Black###BREAKLINE###P2.Type=Landmark###BREAKLINE###P2.Name=Finish Pole###BREAKLINE###P2.UUID=3240586c-83c0-11e2-88c0-10bf48d758ce
ControlPoint17Mark1Lon:7.267022
ControlPoint17Mark1Lat:43.691895
ControlPoint17Mark2Lon:7.271342
ControlPoint17Mark2Lat:43.694851
ControlPoint18UUID:88ef6d40-7adc-0131-e61c-60a44ce903c3
ControlPoint18Name:Finish Pole-Cylinder
ControlPoint18HasTwoPoints:1
ControlPoint18ShortName:F
ControlPoint18DataSheet:P1.Color=Black###BREAKLINE###P1.Type=Landmark###BREAKLINE###P1.Name=Finish Pole###BREAKLINE###P1.UUID=3240586c-83c0-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Black###BREAKLINE###P2.Shape=Cylinder###BREAKLINE###P2.Pattern=Checkered###BREAKLINE###P2.Name=Black Cylinder###BREAKLINE###P2.UUID=b153dfd6-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint18Mark1Lon:7.271342
ControlPoint18Mark1Lat:43.694851
ControlPoint18Mark2Lon:7.269445
ControlPoint18Mark2Lat:43.687302
ControlPoint19UUID:8f9a4ee0-7adb-0131-e5e0-60a44ce903c3
ControlPoint19Name:Green-Yellow
ControlPoint19HasTwoPoints:1
ControlPoint19ShortName:GY
ControlPoint19DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Green###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Name=Green###BREAKLINE###P1.UUID=785c3e44-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Yellow###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Yellow###BREAKLINE###P2.UUID=3fdd2402-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint19Mark1Lon:7.272257
ControlPoint19Mark1Lat:43.687305
ControlPoint19Mark2Lon:7.269320
ControlPoint19Mark2Lat:43.687366
ControlPoint20UUID:a0f71190-7adb-0131-e5ea-60a44ce903c3
ControlPoint20Name:Blue-Yellow
ControlPoint20HasTwoPoints:1
ControlPoint20ShortName:BY
ControlPoint20DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Blue###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Name=Blue###BREAKLINE###P1.UUID=1286f15e-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Yellow###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Yellow###BREAKLINE###P2.UUID=3fdd2402-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint20Mark1Lon:7.269418
ControlPoint20Mark1Lat:43.687290
ControlPoint20Mark2Lon:7.269320
ControlPoint20Mark2Lat:43.687366
ControlPoint21UUID:a68c6350-7adc-0131-e626-60a44ce903c3
ControlPoint21Name:RC-Black Conical
ControlPoint21HasTwoPoints:1
ControlPoint21ShortName:S
ControlPoint21DataSheet:P1.Type=StartBoat###BREAKLINE###P1.Name=Race Committee###BREAKLINE###P1.UUID=3e6c1c94-83bf-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Black###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Pattern=Checkered###BREAKLINE###P2.Name=Black Conical###BREAKLINE###P2.UUID=d78695ea-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint21Mark1Lon:7.267022
ControlPoint21Mark1Lat:43.691895
ControlPoint21Mark2Lon:7.272432
ControlPoint21Mark2Lat:43.692131
ControlPoint22UUID:b153dfd6-83be-11e2-88c0-10bf48d758ce
ControlPoint22Name:Black Cylinder
ControlPoint22HasTwoPoints:0
ControlPoint22ShortName:B
ControlPoint22DataSheet:Type=Buoy###BREAKLINE###Color=Black###BREAKLINE###Shape=Cylinder###BREAKLINE###Pattern=Checkered###BREAKLINE###Name=Black Cylinder
ControlPoint22Mark1Lon:7.269445
ControlPoint22Mark1Lat:43.687302
ControlPoint23UUID:b62e0bf0-7adc-0131-e630-60a44ce903c3
ControlPoint23Name:RC-Black Cylinder
ControlPoint23HasTwoPoints:1
ControlPoint23ShortName:S
ControlPoint23DataSheet:P1.Type=StartBoat###BREAKLINE###P1.Name=Race Committee###BREAKLINE###P1.UUID=3e6c1c94-83bf-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Black###BREAKLINE###P2.Shape=Cylinder###BREAKLINE###P2.Pattern=Checkered###BREAKLINE###P2.Name=Black Cylinder###BREAKLINE###P2.UUID=b153dfd6-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint23Mark1Lon:7.267022
ControlPoint23Mark1Lat:43.691895
ControlPoint23Mark2Lon:7.269445
ControlPoint23Mark2Lat:43.687302
ControlPoint24UUID:bd1fc330-7c10-0131-9c24-10b11c4ed8fd
ControlPoint24Name:Black Cylinder-Yellow
ControlPoint24HasTwoPoints:1
ControlPoint24ShortName:S
ControlPoint24DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=Black###BREAKLINE###P1.Shape=Cylinder###BREAKLINE###P1.Pattern=Checkered###BREAKLINE###P1.Name=Black Cylinder###BREAKLINE###P1.UUID=b153dfd6-83be-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Yellow###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Yellow###BREAKLINE###P2.UUID=3fdd2402-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint24Mark1Lon:7.269445
ControlPoint24Mark1Lat:43.687302
ControlPoint24Mark2Lon:7.269320
ControlPoint24Mark2Lat:43.687366
ControlPoint25UUID:bf6777e6-83bd-11e2-88c0-10bf48d758ce
ControlPoint25Name:White S
ControlPoint25HasTwoPoints:0
ControlPoint25ShortName:W
ControlPoint25DataSheet:Type=Buoy###BREAKLINE###Color=White###BREAKLINE###Shape=Conical###BREAKLINE###Name=White S
ControlPoint25Mark1Lon:7.268352
ControlPoint25Mark1Lat:43.691936
ControlPoint26UUID:cd3c1dc0-7c11-0131-9c2e-10b11c4ed8fd
ControlPoint26Name:Finish Pole-Conical
ControlPoint26HasTwoPoints:1
ControlPoint26ShortName:S
ControlPoint26DataSheet:P1.Color=Black###BREAKLINE###P1.Type=Landmark###BREAKLINE###P1.Name=Finish Pole###BREAKLINE###P1.UUID=3240586c-83c0-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Black###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Pattern=Checkered###BREAKLINE###P2.Name=Black Conical###BREAKLINE###P2.UUID=d78695ea-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint26Mark1Lon:7.271342
ControlPoint26Mark1Lat:43.694851
ControlPoint26Mark2Lon:7.272432
ControlPoint26Mark2Lat:43.692131
ControlPoint27UUID:d39a0d00-7adc-0131-e63a-60a44ce903c3
ControlPoint27Name:White Gate
ControlPoint27HasTwoPoints:1
ControlPoint27ShortName:W
ControlPoint27DataSheet:P1.Type=Buoy###BREAKLINE###P1.Color=White###BREAKLINE###P1.Shape=Conical###BREAKLINE###P1.Name=White S###BREAKLINE###P1.UUID=bf6777e6-83bd-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=White###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=White P###BREAKLINE###P2.UUID=e9ec42f8-83bd-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint27Mark1Lon:7.268352
ControlPoint27Mark1Lat:43.691936
ControlPoint27Mark2Lon:7.271063
ControlPoint27Mark2Lat:43.691803
ControlPoint28UUID:d78695ea-83be-11e2-88c0-10bf48d758ce
ControlPoint28Name:Black Conical
ControlPoint28HasTwoPoints:0
ControlPoint28ShortName:B
ControlPoint28DataSheet:Type=Buoy###BREAKLINE###Color=Black###BREAKLINE###Shape=Conical###BREAKLINE###Pattern=Checkered###BREAKLINE###Name=Black Conical
ControlPoint28Mark1Lon:7.272432
ControlPoint28Mark1Lat:43.692131
ControlPoint29UUID:e9ec42f8-83bd-11e2-88c0-10bf48d758ce
ControlPoint29Name:White P
ControlPoint29HasTwoPoints:0
ControlPoint29ShortName:W
ControlPoint29DataSheet:Type=Buoy###BREAKLINE###Color=White###BREAKLINE###Shape=Conical###BREAKLINE###Name=White P
ControlPoint29Mark1Lon:7.271063
ControlPoint29Mark1Lat:43.691803
ControlPoint30UUID:eff23330-7b84-0131-b9d2-10b11c4ed8fd
ControlPoint30Name:RC-Red
ControlPoint30HasTwoPoints:1
ControlPoint30ShortName:S
ControlPoint30DataSheet:P1.Type=StartBoat###BREAKLINE###P1.Name=Race Committee###BREAKLINE###P1.UUID=3e6c1c94-83bf-11e2-88c0-10bf48d758ce###BREAKLINE###P2.Type=Buoy###BREAKLINE###P2.Color=Red###BREAKLINE###P2.Shape=Conical###BREAKLINE###P2.Name=Red###BREAKLINE###P2.UUID=5427c7d2-83be-11e2-88c0-10bf48d758ce###BREAKLINE###
ControlPoint30Mark1Lon:7.267022
ControlPoint30Mark1Lat:43.691895
ControlPoint30Mark2Lon:7.273181
ControlPoint30Mark2Lat:43.688370
SegmentFile:segments.php
Route1UUID:09f125d0-2bc6-0132-1eee-10b11c4ed8fd
Route1Description:Race 1
Route1Length:0
Route1DataSheet:CourseDiagram=Course###BREAKLINE###Seq.1=Line###BREAKLINE###Seq.2=Port###BREAKLINE###Seq.3=Gate###BREAKLINE###Seq.4=Port###BREAKLINE###Seq.5=Line###BREAKLINE###
Route1ControlPoint1UUID:d39a0d00-7adc-0131-e63a-60a44ce903c3
Route1ControlPoint2UUID:785c3e44-83be-11e2-88c0-10bf48d758ce
Route1ControlPoint3UUID:2ce99970-2c47-0132-20ce-10b11c4ed8fd
//-------- AUTO GENERATED PARAMETERS END ------------------
@@ -0,0 +1,76 @@
package com.sap.sailing.xmlexport.test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.junit.Test;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.test.OnlineTracTracBasedTest;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sailing.xmlexport.LeaderboardData;
public class XMLExportTest extends OnlineTracTracBasedTest {
public XMLExportTest() throws MalformedURLException, URISyntaxException {
super();
}
private void setUpESS2014Nice(String raceNumber) {
URI storedUri;
try {
storedUri = new URI("file:///" + new File("resources/" + getFileName() + raceNumber + ".mtb").getCanonicalPath().replace('\\', '/'));
super.setUp(new URL("file:///" + new File("resources/" + getFileName() + raceNumber + ".txt").getCanonicalPath()),
/* liveUri */null, /* storedUri */storedUri,
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.RACECOURSE, ReceiverType.MARKPOSITIONS, ReceiverType.RAWPOSITIONS });
} catch (Exception ex) {
ex.printStackTrace();
}
boolean result = getTrackedRace().recordWind(new WindImpl(/* position */null, getTrackedRace().getStartOfRace(), new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(65))),
new WindSourceImpl(WindSourceType.WEB));
assert result==true;
}
protected String getExpectedEventName() {
return "ESS Nice 2014";
}
protected String getFileName() {
return "event_20141001_ESSNice-Race_";
}
@Test
public void testExportingESSNice2014() {
setUpESS2014Nice("1");
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("ESS 2014 Nice", new ThresholdBasedResultDiscardingRuleImpl(new int[] {0}), new HighPointFirstGets10LastBreaksTie(),
null);
leaderboard.addRace(getTrackedRace(), "R1", false);
LeaderboardData leaderboardData = new LeaderboardData(leaderboard);
try {
leaderboardData.perform();
} catch (Exception e) {
e.printStackTrace();
fail("No exception should occur");
}
String resultData = leaderboardData.getResultXML();
assertNotNull(resultData);
assertTrue(resultData.length()>261000);
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.xmlexport; package com.sap.sailing.xmlexport;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter;
import java.util.List; import java.util.List;
import java.util.NavigableSet; import java.util.NavigableSet;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@@ -41,11 +42,21 @@ public abstract class ExportAction {
private HttpServletResponse res; private HttpServletResponse res;
private RacingEventService service; private RacingEventService service;
private Leaderboard leaderboard;
protected final boolean useProvidedLeaderboard;
private String resultingXMLData;
public ExportAction(HttpServletRequest req, HttpServletResponse res, RacingEventService service) { public ExportAction(HttpServletRequest req, HttpServletResponse res, RacingEventService service) {
this.req = req; this.req = req;
this.res = res; this.res = res;
this.service = service; this.service = service;
this.leaderboard = null;
this.useProvidedLeaderboard = false;
}
public ExportAction(Leaderboard leaderboard) {
this.leaderboard = leaderboard;
this.useProvidedLeaderboard = true;
} }
public String getAttribute(String name) { public String getAttribute(String name) {
@@ -56,14 +67,20 @@ public abstract class ExportAction {
return service; return service;
} }
public String getResultXML() {
return resultingXMLData;
}
public Leaderboard getLeaderboard() throws IOException, ServletException { public Leaderboard getLeaderboard() throws IOException, ServletException {
final String leaderboardName = getAttribute("name"); if (!useProvidedLeaderboard) {
if (leaderboardName == null) { final String leaderboardName = getAttribute("name");
throw new ServletException("Use the name= parameter to specify the leaderboard"); if (leaderboardName == null) {
} throw new ServletException("Use the name= parameter to specify the leaderboard");
final Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); }
if (leaderboard == null) { leaderboard = getService().getLeaderboardByName(leaderboardName);
throw new ServletException("Leaderboard " + leaderboardName + " not found."); if (leaderboard == null) {
throw new ServletException("Leaderboard " + leaderboardName + " not found.");
}
} }
return leaderboard; return leaderboard;
} }
@@ -315,13 +332,22 @@ public abstract class ExportAction {
format.setEncoding("UTF-8"); format.setEncoding("UTF-8");
XMLOutputter xmlOutputter = new XMLOutputter(format); XMLOutputter xmlOutputter = new XMLOutputter(format);
res.setContentType("text/xml"); if (!useProvidedLeaderboard) {
res.addHeader("Content-Disposition", "attachment; filename=" + fileName); res.setContentType("text/xml");
res.addHeader("Content-Disposition", "attachment; filename=" + fileName);
try { try {
xmlOutputter.output(element, res.getOutputStream()); xmlOutputter.output(element, res.getOutputStream());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
}
} else {
StringWriter resultStringWriter = new StringWriter();
try {
xmlOutputter.output(element, resultStringWriter);
resultingXMLData = resultStringWriter.toString();
} catch (IOException e) {
e.printStackTrace();
}
} }
} }
@@ -75,6 +75,10 @@ public class LeaderboardData extends ExportAction {
super(req, res, service); super(req, res, service);
} }
public LeaderboardData(Leaderboard leaderboard) {
super(leaderboard);
}
/** /**
* Creates XML for a leaderboard. Expects a list of generated competitors and races XML elements that are associated with the leaderboard. * Creates XML for a leaderboard. Expects a list of generated competitors and races XML elements that are associated with the leaderboard.
* <pre> * <pre>
@@ -907,6 +911,7 @@ public class LeaderboardData extends ExportAction {
} }
} }
log.info("Finished XML export of leaderboard " + leaderboard.getName() + " in " + MillisecondsTimePoint.now().minus(timeSpent.asMillis()).asMillis() + " milliseconds"); log.info("Finished XML export of leaderboard " + leaderboard.getName() + " in " + MillisecondsTimePoint.now().minus(timeSpent.asMillis()).asMillis() + " milliseconds");
sendDocument(createLeaderboardXML(leaderboard, competitorElements, racesElements, leaderboardConfidenceAndErrorMessages), leaderboard.getName() + ".xml"); Element dataToExport = createLeaderboardXML(leaderboard, competitorElements, racesElements, leaderboardConfidenceAndErrorMessages);
sendDocument(dataToExport, leaderboard.getName() + ".xml");
} }
} }
+1
View File
@@ -67,6 +67,7 @@
<module>com.sap.sailing.www</module> <module>com.sap.sailing.www</module>
<module>com.sap.sailing.www.events</module> <module>com.sap.sailing.www.events</module>
<module>com.sap.sailing.xmlexport</module> <module>com.sap.sailing.xmlexport</module>
<module>com.sap.sailing.xmlexport.test</module>
<module>com.google.gwt.osgi</module> <module>com.google.gwt.osgi</module>
<module>com.tractrac.clientmodule</module> <module>com.tractrac.clientmodule</module>
<module>org.json.simple</module> <module>org.json.simple</module>