d9818f86dc82d0f35af76fdcb6add53b077253ad
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseChangeBasedTrackApproximationTest.java
| ... | ... | @@ -37,7 +37,7 @@ public class CourseChangeBasedTrackApproximationTest { |
| 37 | 37 | final CompetitorWithBoat competitor = TrackBasedTest.createCompetitorWithBoat("Someone"); |
| 38 | 38 | track = new DynamicGPSFixMovingTrackImpl<Competitor>(competitor, |
| 39 | 39 | /* millisecondsOverWhichToAverage */5000, /* lossless compaction */true); |
| 40 | - approximation = new CourseChangeBasedTrackApproximation(track, competitor.getBoat().getBoatClass()); |
|
| 40 | + approximation = new CourseChangeBasedTrackApproximation(track, competitor.getBoat().getBoatClass(), /* logFixes */ false); |
|
| 41 | 41 | } |
| 42 | 42 | |
| 43 | 43 | @Test |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseChangeBasedTrackApproximationWithTracTracDataTest.java
| ... | ... | @@ -8,7 +8,7 @@ import java.net.MalformedURLException; |
| 8 | 8 | import java.net.URI; |
| 9 | 9 | import java.net.URISyntaxException; |
| 10 | 10 | import java.net.URL; |
| 11 | -import java.util.Random; |
|
| 11 | +import java.util.Iterator; |
|
| 12 | 12 | |
| 13 | 13 | import org.junit.jupiter.api.BeforeEach; |
| 14 | 14 | import org.junit.jupiter.api.Test; |
| ... | ... | @@ -44,7 +44,9 @@ public class CourseChangeBasedTrackApproximationWithTracTracDataTest extends Onl |
| 44 | 44 | assertFalse(Util.isEmpty(getTrackedRace().getRace().getCompetitors())); |
| 45 | 45 | do { |
| 46 | 46 | competitors = getTrackedRace().getRace().getCompetitors(); |
| 47 | - sampleCompetitor = (CompetitorWithBoat) Util.get(competitors, new Random().nextInt(Util.size(competitors))); |
|
| 47 | + sampleCompetitor = (CompetitorWithBoat) Util.first(Util.filter(competitors, c->c.getName().equals("Anton"))); // TODO "Anton" was offending for fixes indexed 71-73 (0-based) |
|
| 48 | + short TODO; |
|
| 49 | +// sampleCompetitor = (CompetitorWithBoat) Util.get(competitors, new Random().nextInt(Util.size(competitors))); |
|
| 48 | 50 | sampleTrack = getTrackedRace().getTrack(sampleCompetitor); |
| 49 | 51 | } while (sampleTrack.isEmpty()); |
| 50 | 52 | } |
| ... | ... | @@ -68,9 +70,10 @@ public class CourseChangeBasedTrackApproximationWithTracTracDataTest extends Onl |
| 68 | 70 | final DynamicGPSFixTrack<Competitor, GPSFixMoving> trackCopy = new DynamicGPSFixMovingTrackImpl<Competitor>( |
| 69 | 71 | sampleCompetitor, |
| 70 | 72 | /* millisecondsOverWhichToAverage */ boatClass.getApproximateManeuverDurationInMilliseconds()); |
| 71 | - final CourseChangeBasedTrackApproximation earlyInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass()); |
|
| 73 | + final CourseChangeBasedTrackApproximation earlyInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass(), /* logFixes */ true); |
|
| 72 | 74 | final TimePoint from = sampleTrack.getFirstRawFix().getTimePoint(); |
| 73 | 75 | final TimePoint to = sampleTrack.getLastRawFix().getTimePoint(); |
| 76 | + System.out.println("approxId,fixTimeMillis,validityCached,speedCached,COG,SOG"); |
|
| 74 | 77 | sampleTrack.lockForRead(); |
| 75 | 78 | try { |
| 76 | 79 | for (final GPSFixMoving fix : sampleTrack.getRawFixes()) { |
| ... | ... | @@ -79,10 +82,19 @@ public class CourseChangeBasedTrackApproximationWithTracTracDataTest extends Onl |
| 79 | 82 | } finally { |
| 80 | 83 | sampleTrack.unlockAfterRead(); |
| 81 | 84 | } |
| 82 | - final CourseChangeBasedTrackApproximation lateInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass()); |
|
| 85 | + final CourseChangeBasedTrackApproximation lateInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass(), /* logFixes */ true); |
|
| 83 | 86 | assertEquals(earlyInitApproximation.getNumberOfFixesAdded(), lateInitApproximation.getNumberOfFixesAdded(), "Number of fixes added to approximators differs"); |
| 84 | 87 | final Iterable<GPSFixMoving> earlyInitResult = earlyInitApproximation.approximate(from, to); |
| 85 | 88 | final Iterable<GPSFixMoving> lateInitResult = lateInitApproximation.approximate(from, to); |
| 89 | + final Iterator<GPSFixMoving> earlyIter = earlyInitResult.iterator(); |
|
| 90 | + final Iterator<GPSFixMoving> lateIter = lateInitResult.iterator(); |
|
| 91 | + int i=0; |
|
| 92 | + while (earlyIter.hasNext() && lateIter.hasNext()) { |
|
| 93 | + final GPSFixMoving earlyFix = earlyIter.next(); |
|
| 94 | + final GPSFixMoving lateFix = lateIter.next(); |
|
| 95 | + assertEquals(earlyFix.getTimePoint(), lateFix.getTimePoint(), "Time points of approximation fixes differ at index "+i+" for competitor "+sampleCompetitor.getName()); |
|
| 96 | + i++; |
|
| 97 | + } |
|
| 86 | 98 | assertEquals(Util.size(earlyInitResult), Util.size(lateInitResult), "Different numbers of approximation points for competitor "+sampleCompetitor.getName()); |
| 87 | 99 | assertEquals(Util.asSet(earlyInitResult), Util.asSet(lateInitResult)); |
| 88 | 100 | } |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CourseChangeBasedTrackApproximation.java
| ... | ... | @@ -135,7 +135,15 @@ public class CourseChangeBasedTrackApproximation implements Serializable, GPSTra |
| 135 | 135 | private final double maneuverAngleInDegreesThreshold; |
| 136 | 136 | private Duration windowDuration; |
| 137 | 137 | |
| 138 | - FixWindow() { |
|
| 138 | + /** |
|
| 139 | + * For debugging purposes, this flag can be set to {@code true} through the constructor, which will then log all |
|
| 140 | + * fixes' values about estimated COG/SOG when moving from the {@link #queueOfNewFixes} into the {@link #window}. |
|
| 141 | + * This is particularly helpful to analyze the influence of seeing or not seeing newer fixes on the track. |
|
| 142 | + */ |
|
| 143 | + private final boolean logFixes; |
|
| 144 | + |
|
| 145 | + FixWindow(boolean logFixes) { |
|
| 146 | + this.logFixes = logFixes; |
|
| 139 | 147 | this.window = new LinkedList<>(); |
| 140 | 148 | this.queueOfNewFixes = new LinkedList<>(); |
| 141 | 149 | this.speedForFixesInWindow = new LinkedList<>(); |
| ... | ... | @@ -237,11 +245,18 @@ public class CourseChangeBasedTrackApproximation implements Serializable, GPSTra |
| 237 | 245 | } |
| 238 | 246 | return result; |
| 239 | 247 | } |
| 240 | - |
|
| 248 | + |
|
| 241 | 249 | private GPSFixMoving addOldEnoughFix(GPSFixMoving next) { |
| 242 | 250 | assert window.isEmpty() || !next.getTimePoint().before(window.peekFirst().getTimePoint()); |
| 243 | 251 | final GPSFixMoving result; |
| 244 | 252 | final SpeedWithBearing nextSpeed = next.isEstimatedSpeedCached() ? next.getCachedEstimatedSpeed() : track.getEstimatedSpeed(next.getTimePoint()); |
| 253 | + if (logFixes) { |
|
| 254 | + // CSV logging: approxId, fixIndex, fixTimeMillis, validityCached, speedCached, COG, SOG |
|
| 255 | + System.out.println(System.identityHashCode(this) + "," + next.getTimePoint().asMillis() + "," |
|
| 256 | + + next.isValidityCached() + "," + next.isEstimatedSpeedCached() + "," |
|
| 257 | + + (nextSpeed == null ? "null" : nextSpeed.getBearing().getDegrees()) + "," |
|
| 258 | + + (nextSpeed == null ? "null" : nextSpeed.getKnots())); |
|
| 259 | + } |
|
| 245 | 260 | if (nextSpeed != null) { |
| 246 | 261 | numberOfFixesAdded++; |
| 247 | 262 | int insertPosition = window.size(); |
| ... | ... | @@ -398,15 +413,15 @@ public class CourseChangeBasedTrackApproximation implements Serializable, GPSTra |
| 398 | 413 | } |
| 399 | 414 | } |
| 400 | 415 | |
| 401 | - public CourseChangeBasedTrackApproximation(GPSFixTrack<Competitor, GPSFixMoving> track, BoatClass boatClass) { |
|
| 416 | + public CourseChangeBasedTrackApproximation(GPSFixTrack<Competitor, GPSFixMoving> track, BoatClass boatClass, boolean logFixes) { |
|
| 402 | 417 | this.track = track; |
| 403 | 418 | this.boatClass = boatClass; |
| 404 | - this.fixWindow = new FixWindow(); |
|
| 419 | + this.fixWindow = new FixWindow(logFixes); |
|
| 405 | 420 | this.maneuverCandidates = new TreeSet<>(TimedComparator.INSTANCE); |
| 406 | 421 | track.addListener(this); |
| 407 | 422 | addAllFixesOfTrack(); |
| 408 | 423 | } |
| 409 | - |
|
| 424 | + |
|
| 410 | 425 | /** |
| 411 | 426 | * Defined only in order to make it {@code synchronized} so that data will be written to the output stream |
| 412 | 427 | * consistently. |
| ... | ... | @@ -459,7 +474,7 @@ public class CourseChangeBasedTrackApproximation implements Serializable, GPSTra |
| 459 | 474 | if (fixWindow.isAtOrAfterFirst(fix.getTimePoint())) { |
| 460 | 475 | addFix(fix); |
| 461 | 476 | } else { |
| 462 | - final FixWindow outOfOrderWindow = new FixWindow(); |
|
| 477 | + final FixWindow outOfOrderWindow = new FixWindow(/* logFixes */ false); |
|
| 463 | 478 | final Duration maximumWindowLength = outOfOrderWindow.getMaximumWindowLength(); |
| 464 | 479 | // fix is an out-of-order delivery; construct a new FixWindow and analyze the track around the new fix. |
| 465 | 480 | // A time range around the fix is constructed that will be re-scanned. The time range covers at least |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java
| ... | ... | @@ -631,11 +631,15 @@ public abstract class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends |
| 631 | 631 | ConfidenceFactory.INSTANCE.createExponentialTimeDifferenceWeigher( |
| 632 | 632 | // use a minimum confidence to avoid the bearing to flip to 270deg in case all is zero |
| 633 | 633 | getMillisecondsOverWhichToAverageSpeed() / 2, /* minimumConfidence */ 0.00000001)); // half confidence if half averaging interval apart |
| 634 | - result = estimatedSpeed == null ? null : estimatedSpeed.getObject(); |
|
| 635 | 634 | if (estimatedSpeed != null) { |
| 636 | 635 | if (ceil != null && ceil.getTimePoint().equals(at)) { |
| 637 | - ceil.cacheEstimatedSpeed(result); |
|
| 636 | + ceil.cacheEstimatedSpeed(estimatedSpeed.getObject()); |
|
| 637 | + result = ceil.getCachedEstimatedSpeed(); // this way, should the fix apply compaction, we will still return consistent (compacted) results |
|
| 638 | + } else { |
|
| 639 | + result = estimatedSpeed.getObject(); |
|
| 638 | 640 | } |
| 641 | + } else { |
|
| 642 | + result = null; |
|
| 639 | 643 | } |
| 640 | 644 | } |
| 641 | 645 | if (logger.isLoggable(Level.FINEST) && (estimatedSpeedCacheHits + estimatedSpeedCacheMisses) % 1000 == 0) { |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
| ... | ... | @@ -585,7 +585,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl |
| 585 | 585 | markPassingsForCompetitor.put(competitor, new ConcurrentSkipListSet<MarkPassing>(MarkPassingByTimeComparator.INSTANCE)); |
| 586 | 586 | final DynamicGPSFixMovingTrackImpl<Competitor> track = new DynamicGPSFixMovingTrackImpl<Competitor>(competitor, millisecondsOverWhichToAverageSpeed); |
| 587 | 587 | tracks.put(competitor, track); |
| 588 | - maneuverApproximators.put(competitor, new CourseChangeBasedTrackApproximation(track, race.getBoatOfCompetitor(competitor).getBoatClass())); |
|
| 588 | + maneuverApproximators.put(competitor, new CourseChangeBasedTrackApproximation(track, race.getBoatOfCompetitor(competitor).getBoatClass(), /* logFixes */ false)); |
|
| 589 | 589 | } |
| 590 | 590 | markPassingsForWaypoint = new ConcurrentHashMap<Waypoint, NavigableSet<MarkPassing>>(); |
| 591 | 591 | for (Waypoint waypoint : race.getCourse().getWaypoints()) { |