672d1697b2a8b401c91e60d92a3b8582805fad73
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseChangeBasedTrackApproximationTest.java
| ... | ... | @@ -4,18 +4,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; |
| 4 | 4 | import static org.junit.jupiter.api.Assertions.assertFalse; |
| 5 | 5 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 6 | 6 | |
| 7 | -import java.io.File; |
|
| 8 | -import java.net.MalformedURLException; |
|
| 9 | -import java.net.URI; |
|
| 10 | -import java.net.URISyntaxException; |
|
| 11 | -import java.net.URL; |
|
| 12 | -import java.util.Random; |
|
| 13 | - |
|
| 14 | 7 | import org.junit.jupiter.api.BeforeEach; |
| 15 | 8 | import org.junit.jupiter.api.Test; |
| 16 | 9 | |
| 10 | +import com.sap.sailing.domain.base.BoatClass; |
|
| 17 | 11 | import com.sap.sailing.domain.base.Competitor; |
| 18 | 12 | import com.sap.sailing.domain.base.CompetitorWithBoat; |
| 13 | +import com.sap.sailing.domain.base.impl.BoatClassImpl; |
|
| 19 | 14 | import com.sap.sailing.domain.common.impl.DegreePosition; |
| 20 | 15 | import com.sap.sailing.domain.common.impl.KnotSpeedImpl; |
| 21 | 16 | import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; |
| ... | ... | @@ -24,81 +19,27 @@ import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| 24 | 19 | import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 25 | 20 | import com.sap.sailing.domain.tracking.impl.CourseChangeBasedTrackApproximation; |
| 26 | 21 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; |
| 27 | -import com.sap.sailing.domain.tractracadapter.ReceiverType; |
|
| 22 | +import com.sap.sse.common.Duration; |
|
| 23 | +import com.sap.sse.common.Speed; |
|
| 28 | 24 | import com.sap.sse.common.TimePoint; |
| 29 | 25 | import com.sap.sse.common.Util; |
| 30 | 26 | import com.sap.sse.common.impl.DegreeBearingImpl; |
| 31 | 27 | import com.sap.sse.common.impl.MillisecondsDurationImpl; |
| 32 | 28 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
| 33 | 29 | |
| 34 | -public class CourseChangeBasedTrackApproximationTest extends OnlineTracTracBasedTest { |
|
| 30 | +public class CourseChangeBasedTrackApproximationTest { |
|
| 35 | 31 | private DynamicGPSFixTrack<Competitor, GPSFixMoving> track; |
| 36 | 32 | private CourseChangeBasedTrackApproximation approximation; |
| 37 | - private Iterable<Competitor> competitors; |
|
| 38 | - private CompetitorWithBoat sampleCompetitor; |
|
| 39 | - private DynamicGPSFixTrack<Competitor, GPSFixMoving> sampleTrack; |
|
| 33 | + private final static BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */true); |
|
| 40 | 34 | |
| 41 | - public CourseChangeBasedTrackApproximationTest() throws MalformedURLException, URISyntaxException { |
|
| 42 | - super(); |
|
| 43 | - } |
|
| 44 | - |
|
| 45 | 35 | @BeforeEach |
| 46 | 36 | public void setUp() throws Exception { |
| 47 | - super.setUp(); |
|
| 48 | - URI storedUri = new URI("file:///" + new File("resources/event_20110609_KielerWoch-505_Race_2.mtb").getCanonicalPath().replace('\\', '/')); |
|
| 49 | - super.setUp( |
|
| 50 | - new URL("file:///" + new File("resources/event_20110609_KielerWoch-505_Race_2.txt").getCanonicalPath()), |
|
| 51 | - /* liveUri */ null, /* storedUri */ storedUri, |
|
| 52 | - new ReceiverType[] { ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS }); |
|
| 53 | - getTrackedRace().waitUntilNotLoading(); |
|
| 54 | - assertFalse(Util.isEmpty(getTrackedRace().getRace().getCompetitors())); |
|
| 55 | - do { |
|
| 56 | - competitors = getTrackedRace().getRace().getCompetitors(); |
|
| 57 | - sampleCompetitor = (CompetitorWithBoat) Util.get(competitors, new Random().nextInt(Util.size(competitors))); |
|
| 58 | - sampleTrack = getTrackedRace().getTrack(sampleCompetitor); |
|
| 59 | - } while (sampleTrack.isEmpty()); |
|
| 60 | 37 | final CompetitorWithBoat competitor = TrackBasedTest.createCompetitorWithBoat("Someone"); |
| 61 | 38 | track = new DynamicGPSFixMovingTrackImpl<Competitor>(competitor, |
| 62 | 39 | /* millisecondsOverWhichToAverage */5000, /* lossless compaction */true); |
| 63 | 40 | approximation = new CourseChangeBasedTrackApproximation(track, competitor.getBoat().getBoatClass()); |
| 64 | 41 | } |
| 65 | 42 | |
| 66 | - /** |
|
| 67 | - * During the work on bug5959 (https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=5959) we identified an issue |
|
| 68 | - * with early vs. late initialization of the approximation. When initializing the |
|
| 69 | - * {@link CourseChangeBasedTrackApproximation} objects before adding any fixes to the track, we received different |
|
| 70 | - * results than when initializing it after adding fixes and first trying to compute maneuvers. This goes against the |
|
| 71 | - * specification that the approximation should be independent of when it is initialized.<p> |
|
| 72 | - * |
|
| 73 | - * To conduct the test, we take the fixes from a loaded competitor track, create a new {@link DynamicGPSFixMovingTrackImpl}, |
|
| 74 | - * construct a {@link CourseChangeBasedTrackApproximation} based on this track (early initialization), then copy all fixes |
|
| 75 | - * from the loaded competitor track to the new test track (which adds these fixes to the approximation), then create a second |
|
| 76 | - * {@link CourseChangeBasedTrackApproximation} which will then add all fixes in one sweep. Then, we compare the results of |
|
| 77 | - * {@link CourseChangeBasedTrackApproximation#approximate(TimePoint, TimePoint)} for the duration of the entire track, |
|
| 78 | - * expecting them to be equal. |
|
| 79 | - */ |
|
| 80 | - @Test |
|
| 81 | - public void testNoDiffBetweenEarlyAndLateInitialization() { |
|
| 82 | - final DynamicGPSFixTrack<Competitor, GPSFixMoving> trackCopy = new DynamicGPSFixMovingTrackImpl<Competitor>(sampleCompetitor, /* millisecondsOverWhichToAverage */ 15000); |
|
| 83 | - final CourseChangeBasedTrackApproximation earlyInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass()); |
|
| 84 | - final TimePoint from = sampleTrack.getFirstRawFix().getTimePoint(); |
|
| 85 | - final TimePoint to = sampleTrack.getLastRawFix().getTimePoint(); |
|
| 86 | - sampleTrack.lockForRead(); |
|
| 87 | - try { |
|
| 88 | - for (final GPSFixMoving fix : sampleTrack.getRawFixes()) { |
|
| 89 | - trackCopy.add(fix); |
|
| 90 | - } |
|
| 91 | - } finally { |
|
| 92 | - sampleTrack.unlockAfterRead(); |
|
| 93 | - } |
|
| 94 | - final CourseChangeBasedTrackApproximation lateInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass()); |
|
| 95 | - assertEquals(earlyInitApproximation.getNumberOfFixesAdded(), lateInitApproximation.getNumberOfFixesAdded(), "Number of fixes added to approximators differs"); |
|
| 96 | - final Iterable<GPSFixMoving> earlyInitResult = earlyInitApproximation.approximate(from, to); |
|
| 97 | - final Iterable<GPSFixMoving> lateInitResult = lateInitApproximation.approximate(from, to); |
|
| 98 | - assertEquals(Util.size(earlyInitResult), Util.size(lateInitResult), "Different numbers of approximation points for competitor "+sampleCompetitor.getName()); |
|
| 99 | - assertEquals(Util.asSet(earlyInitResult), Util.asSet(lateInitResult)); |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | 43 | @Test |
| 103 | 44 | public void simpleTackRecognition() { |
| 104 | 45 | final GPSFixMoving start = fix(10000l, 0, 0, 5, 0); |
| ... | ... | @@ -125,6 +66,62 @@ public class CourseChangeBasedTrackApproximationTest extends OnlineTracTracBased |
| 125 | 66 | } |
| 126 | 67 | } |
| 127 | 68 | |
| 69 | + @Test |
|
| 70 | + public void testDirectionChangeJustAboveThreshold() { |
|
| 71 | + final Duration samplingInterval = Duration.ONE_SECOND; |
|
| 72 | + final double aBitOverMinimumManeuverAngleDegrees = boatClass.getManeuverDegreeAngleThreshold() * 1.2; |
|
| 73 | + final TimePoint start = TimePoint.of(10000l); |
|
| 74 | + final Speed speed = new KnotSpeedImpl(5.0); |
|
| 75 | + GPSFixMoving next = fix(start.asMillis(), 0, 0, speed.getKnots(), 0); |
|
| 76 | + track.add(next); |
|
| 77 | + // perform aBitOverMinimumManeuverAngleDegrees within five fixes: |
|
| 78 | + final int NUMBER_OF_FIXES_FOR_MANEUVER = 5; |
|
| 79 | + for (int i=0; i<NUMBER_OF_FIXES_FOR_MANEUVER; i++) { |
|
| 80 | + next = travel(next, samplingInterval.asMillis(), speed.getKnots(), ((double) i+1.0)/((double) NUMBER_OF_FIXES_FOR_MANEUVER) * aBitOverMinimumManeuverAngleDegrees); |
|
| 81 | + track.add(next); |
|
| 82 | + } |
|
| 83 | + final Iterable<GPSFixMoving> oneManeuverCandidate = approximation.approximate(start, start.plus(samplingInterval.times(NUMBER_OF_FIXES_FOR_MANEUVER))); |
|
| 84 | + assertFalse(Util.isEmpty(oneManeuverCandidate)); |
|
| 85 | + assertEquals(1, Util.size(oneManeuverCandidate)); |
|
| 86 | + } |
|
| 87 | + |
|
| 88 | + /** |
|
| 89 | + * See also https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=6209#c5 which talks about FixWindow contents |
|
| 90 | + * that describe COG changes in different directions across the FixWindow's duration. This way, a COG change just |
|
| 91 | + * barely exceeding the threshold wouldn't be recognized/emitted as an approximated fix if it is preceded by a COG |
|
| 92 | + * change in the other direction that does not by itself pass the threshold. |
|
| 93 | + */ |
|
| 94 | + @Test |
|
| 95 | + public void testTurningDirectionChangeInSameWindow() { |
|
| 96 | + final Duration samplingInterval = Duration.ONE_SECOND; |
|
| 97 | + final double halfMinimumManeuverAngleDegrees = boatClass.getManeuverDegreeAngleThreshold() / 2.0; |
|
| 98 | + final TimePoint start = TimePoint.of(10000l); |
|
| 99 | + final Speed speed = new KnotSpeedImpl(5.0); |
|
| 100 | + double cog = 0.0; |
|
| 101 | + GPSFixMoving next = fix(start.asMillis(), 0, 0, speed.getKnots(), cog); |
|
| 102 | + track.add(next); |
|
| 103 | + // perform halfMinimumManeuverAngleDegrees within five fixes: |
|
| 104 | + final int NUMBER_OF_FIXES_FOR_NON_MANEUVER = 5; |
|
| 105 | + for (int i=0; i<NUMBER_OF_FIXES_FOR_NON_MANEUVER; i++) { |
|
| 106 | + cog -= 1.0/((double) NUMBER_OF_FIXES_FOR_NON_MANEUVER) * halfMinimumManeuverAngleDegrees; |
|
| 107 | + next = travel(next, samplingInterval.asMillis(), speed.getKnots(), cog); |
|
| 108 | + track.add(next); |
|
| 109 | + } |
|
| 110 | + final Iterable<GPSFixMoving> emptyManeuverCandidates = approximation.approximate(start, start.plus(samplingInterval.times(NUMBER_OF_FIXES_FOR_NON_MANEUVER))); |
|
| 111 | + assertTrue(Util.isEmpty(emptyManeuverCandidates)); |
|
| 112 | + final double aBitOverMinimumManeuverAngleDegrees = boatClass.getManeuverDegreeAngleThreshold() * 1.2; |
|
| 113 | + // perform aBitOverMinimumManeuverAngleDegrees within five fixes: |
|
| 114 | + final int NUMBER_OF_FIXES_FOR_MANEUVER = 5; |
|
| 115 | + for (int i=0; i<NUMBER_OF_FIXES_FOR_MANEUVER; i++) { |
|
| 116 | + cog += 1.0/((double) NUMBER_OF_FIXES_FOR_MANEUVER) * aBitOverMinimumManeuverAngleDegrees; |
|
| 117 | + next = travel(next, samplingInterval.asMillis(), speed.getKnots(), cog); |
|
| 118 | + track.add(next); |
|
| 119 | + } |
|
| 120 | + final Iterable<GPSFixMoving> oneManeuverCandidate = approximation.approximate(start, start.plus(samplingInterval.times(NUMBER_OF_FIXES_FOR_NON_MANEUVER+NUMBER_OF_FIXES_FOR_MANEUVER))); |
|
| 121 | + assertFalse(Util.isEmpty(oneManeuverCandidate)); |
|
| 122 | + assertEquals(1, Util.size(oneManeuverCandidate)); |
|
| 123 | + } |
|
| 124 | + |
|
| 128 | 125 | private GPSFixMoving fix(long timepoint, double lat, double lon, double speedInKnots, double cogDeg) { |
| 129 | 126 | return new GPSFixMovingImpl(new DegreePosition(lat, lon), new MillisecondsTimePoint(timepoint), new KnotSpeedWithBearingImpl(speedInKnots, new DegreeBearingImpl(cogDeg)), /* optionalTrueHeading */ null); |
| 130 | 127 | } |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseChangeBasedTrackApproximationWithTracTracDataTest.java
| ... | ... | @@ -0,0 +1,87 @@ |
| 1 | +package com.sap.sailing.domain.test; |
|
| 2 | + |
|
| 3 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
|
| 4 | +import static org.junit.jupiter.api.Assertions.assertFalse; |
|
| 5 | + |
|
| 6 | +import java.io.File; |
|
| 7 | +import java.net.MalformedURLException; |
|
| 8 | +import java.net.URI; |
|
| 9 | +import java.net.URISyntaxException; |
|
| 10 | +import java.net.URL; |
|
| 11 | +import java.util.Random; |
|
| 12 | + |
|
| 13 | +import org.junit.jupiter.api.BeforeEach; |
|
| 14 | +import org.junit.jupiter.api.Test; |
|
| 15 | + |
|
| 16 | +import com.sap.sailing.domain.base.Competitor; |
|
| 17 | +import com.sap.sailing.domain.base.CompetitorWithBoat; |
|
| 18 | +import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
|
| 19 | +import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
|
| 20 | +import com.sap.sailing.domain.tracking.impl.CourseChangeBasedTrackApproximation; |
|
| 21 | +import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; |
|
| 22 | +import com.sap.sailing.domain.tractracadapter.ReceiverType; |
|
| 23 | +import com.sap.sse.common.TimePoint; |
|
| 24 | +import com.sap.sse.common.Util; |
|
| 25 | + |
|
| 26 | +public class CourseChangeBasedTrackApproximationWithTracTracDataTest extends OnlineTracTracBasedTest { |
|
| 27 | + private Iterable<Competitor> competitors; |
|
| 28 | + private CompetitorWithBoat sampleCompetitor; |
|
| 29 | + private DynamicGPSFixTrack<Competitor, GPSFixMoving> sampleTrack; |
|
| 30 | + |
|
| 31 | + public CourseChangeBasedTrackApproximationWithTracTracDataTest() throws MalformedURLException, URISyntaxException { |
|
| 32 | + super(); |
|
| 33 | + } |
|
| 34 | + |
|
| 35 | + @BeforeEach |
|
| 36 | + public void setUp() throws Exception { |
|
| 37 | + super.setUp(); |
|
| 38 | + URI storedUri = new URI("file:///" + new File("resources/event_20110609_KielerWoch-505_Race_2.mtb").getCanonicalPath().replace('\\', '/')); |
|
| 39 | + super.setUp( |
|
| 40 | + new URL("file:///" + new File("resources/event_20110609_KielerWoch-505_Race_2.txt").getCanonicalPath()), |
|
| 41 | + /* liveUri */ null, /* storedUri */ storedUri, |
|
| 42 | + new ReceiverType[] { ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS }); |
|
| 43 | + getTrackedRace().waitUntilNotLoading(); |
|
| 44 | + assertFalse(Util.isEmpty(getTrackedRace().getRace().getCompetitors())); |
|
| 45 | + do { |
|
| 46 | + competitors = getTrackedRace().getRace().getCompetitors(); |
|
| 47 | + sampleCompetitor = (CompetitorWithBoat) Util.get(competitors, new Random().nextInt(Util.size(competitors))); |
|
| 48 | + sampleTrack = getTrackedRace().getTrack(sampleCompetitor); |
|
| 49 | + } while (sampleTrack.isEmpty()); |
|
| 50 | + } |
|
| 51 | + |
|
| 52 | + /** |
|
| 53 | + * During the work on bug5959 (https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=5959) we identified an issue |
|
| 54 | + * with early vs. late initialization of the approximation. When initializing the |
|
| 55 | + * {@link CourseChangeBasedTrackApproximation} objects before adding any fixes to the track, we received different |
|
| 56 | + * results than when initializing it after adding fixes and first trying to compute maneuvers. This goes against the |
|
| 57 | + * specification that the approximation should be independent of when it is initialized.<p> |
|
| 58 | + * |
|
| 59 | + * To conduct the test, we take the fixes from a loaded competitor track, create a new {@link DynamicGPSFixMovingTrackImpl}, |
|
| 60 | + * construct a {@link CourseChangeBasedTrackApproximation} based on this track (early initialization), then copy all fixes |
|
| 61 | + * from the loaded competitor track to the new test track (which adds these fixes to the approximation), then create a second |
|
| 62 | + * {@link CourseChangeBasedTrackApproximation} which will then add all fixes in one sweep. Then, we compare the results of |
|
| 63 | + * {@link CourseChangeBasedTrackApproximation#approximate(TimePoint, TimePoint)} for the duration of the entire track, |
|
| 64 | + * expecting them to be equal. |
|
| 65 | + */ |
|
| 66 | + @Test |
|
| 67 | + public void testNoDiffBetweenEarlyAndLateInitialization() { |
|
| 68 | + final DynamicGPSFixTrack<Competitor, GPSFixMoving> trackCopy = new DynamicGPSFixMovingTrackImpl<Competitor>(sampleCompetitor, /* millisecondsOverWhichToAverage */ 15000); |
|
| 69 | + final CourseChangeBasedTrackApproximation earlyInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass()); |
|
| 70 | + final TimePoint from = sampleTrack.getFirstRawFix().getTimePoint(); |
|
| 71 | + final TimePoint to = sampleTrack.getLastRawFix().getTimePoint(); |
|
| 72 | + sampleTrack.lockForRead(); |
|
| 73 | + try { |
|
| 74 | + for (final GPSFixMoving fix : sampleTrack.getRawFixes()) { |
|
| 75 | + trackCopy.add(fix); |
|
| 76 | + } |
|
| 77 | + } finally { |
|
| 78 | + sampleTrack.unlockAfterRead(); |
|
| 79 | + } |
|
| 80 | + final CourseChangeBasedTrackApproximation lateInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass()); |
|
| 81 | + assertEquals(earlyInitApproximation.getNumberOfFixesAdded(), lateInitApproximation.getNumberOfFixesAdded(), "Number of fixes added to approximators differs"); |
|
| 82 | + final Iterable<GPSFixMoving> earlyInitResult = earlyInitApproximation.approximate(from, to); |
|
| 83 | + final Iterable<GPSFixMoving> lateInitResult = lateInitApproximation.approximate(from, to); |
|
| 84 | + assertEquals(Util.size(earlyInitResult), Util.size(lateInitResult), "Different numbers of approximation points for competitor "+sampleCompetitor.getName()); |
|
| 85 | + assertEquals(Util.asSet(earlyInitResult), Util.asSet(lateInitResult)); |
|
| 86 | + } |
|
| 87 | +} |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CourseChangeBasedTrackApproximation.java
| ... | ... | @@ -14,6 +14,7 @@ import java.util.TreeSet; |
| 14 | 14 | |
| 15 | 15 | import com.sap.sailing.domain.base.BoatClass; |
| 16 | 16 | import com.sap.sailing.domain.base.Competitor; |
| 17 | +import com.sap.sailing.domain.common.NauticalSide; |
|
| 17 | 18 | import com.sap.sailing.domain.common.SpeedWithBearing; |
| 18 | 19 | import com.sap.sailing.domain.common.tracking.GPSFixMoving; |
| 19 | 20 | import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; |
| ... | ... | @@ -28,8 +29,25 @@ import com.sap.sse.common.impl.TimeRangeImpl; |
| 28 | 29 | |
| 29 | 30 | /** |
| 30 | 31 | * Given a {@link GPSFixTrack} containing {@link GPSFixMoving}, an instance of this class finds areas on the track where |
| 31 | - * the course changes sufficiently quickly to indicate a relevant maneuver. The basis for this is the |
|
| 32 | - * {@link BoatClass}'s {@link BoatClass#getApproximateManeuverDuration() maneuver duration}. |
|
| 32 | + * the course changes sufficiently quickly and far to indicate a relevant maneuver. The basis for the duration to |
|
| 33 | + * consider is the {@link BoatClass}'s {@link BoatClass#getApproximateManeuverDuration() maneuver duration}. The basis |
|
| 34 | + * for the angular threshold is {@link BoatClass#getManeuverDegreeAngleThreshold()}. |
|
| 35 | + * <p> |
|
| 36 | + * |
|
| 37 | + * Analyzing the track works with a {@link FixWindow analysis window} that contains up to a certain duration worth of |
|
| 38 | + * GPS fixes, keeping track of their respective {@link SpeedWithBearing} COG/SOG vectors as well as the cumulative |
|
| 39 | + * course change counted from the beginning of the window up to each of the fixes stored in the window. Course changes |
|
| 40 | + * within the window are all in the same {@link NauticalSide direction} (so either all to {@link NauticalSide#PORT PORT} |
|
| 41 | + * or all to {@link NauticalSide#STARBOARD STARBOARD}). When a fix addition leads to a maximum cumulative course change |
|
| 42 | + * exceeding the threshold, a maneuver candidate is added to {@link #maneuverCandidates}, and the fixes from the start |
|
| 43 | + * of the window up to the fix where the |
|
| 44 | + * <p> |
|
| 45 | + * |
|
| 46 | + * When a fix is added to the window that at the insertion point leads to a change in COG change direction, all fixes |
|
| 47 | + * from the beginning of the window up to the fix preceding the one added are removed, so the window now starts with a |
|
| 48 | + * course change in the other direction. If the fix insert position was not at the end of the window, the course change |
|
| 49 | + * direction from the fix added to the next fix is also checked for consistency with the first course change direction |
|
| 50 | + * of the window. |
|
| 33 | 51 | * <p> |
| 34 | 52 | * |
| 35 | 53 | * Upon construction, all maneuver candidates for all fixes already contained by the {@link GPSFixTrack} are determined. |
| ... | ... | @@ -197,6 +215,7 @@ public class CourseChangeBasedTrackApproximation implements Serializable, GPSTra |
| 197 | 215 | absoluteMaximumTotalCourseChangeFromBeginningOfWindowInDegrees = Math.abs(totalCourseChangeFromBeginningOfWindowForCurrentFix); |
| 198 | 216 | indexOfMaximumTotalCourseChange = insertPosition-1; |
| 199 | 217 | } |
| 218 | + // TODO bug6209: now check if the course change direction has changed (from "to port" to "to starboard" or vice versa); if so, remove fixes from beginning of window up to the change point |
|
| 200 | 219 | } |
| 201 | 220 | if (windowDuration.compareTo(getMaximumWindowLength()) > 0) { |
| 202 | 221 | result = tryToExtractManeuverCandidate(); |