diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/TestCorrectionOfSingleDigitLatitudesWithSystemPropertySet.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/TestCorrectionOfSingleDigitLatitudesWithSystemPropertySet.java new file mode 100644 index 00000000000..6ff17c9deee --- /dev/null +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/test/TestCorrectionOfSingleDigitLatitudesWithSystemPropertySet.java @@ -0,0 +1,119 @@ +package com.sap.sailing.domain.igtimiadapter.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.domain.igtimiadapter.datatypes.GpsLatLong; +import com.sap.sailing.domain.igtimiadapter.impl.FixFactory; + +/** + * See bug 1794: http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=1794. Here is an offending string: + *

+ * {"DD-EE-AAHE":{"1":{"t":[1392881700000,1392881732500,1392881772500,1392881812500,1392881852500,1392881892500, + * 1392881932500 + * ,1392881972500,1392882012500,1392882052500,1392882092500,1392882132500,1392882172500,1392882212500,1392882252500 + * ,1392882292500 + * ,1392882332500,1392882372500,1392882412500,1392882452500,1392882492500,1392882532500,1392882572500,1392882612500 + * ,1392882652500 + * ,1392882692500,1392882732500,1392882772500,1392882812500,1392882852500,1392882892500,1392882932500,1392882972500 + * ,1392883012500 + * ,1392883052500,1392883092500,1392883132500,1392883172500,1392883212500,1392883252500,1392883292500,1392883332500 + * ,1392883359750 + * ,1392883360000,1392883360500,1392883400500,1392883462000],"1":[103.85452,103.854519,103.854514,103.854511333333 + * ,103.8545015 + * ,103.854510666667,103.854509833333,103.854512333333,103.854507833333,103.854507166667,103.854510833333,103.854509 + * ,103.854512 + * ,103.854511166667,103.854514333333,103.854517833333,103.854509833333,103.8545115,103.854516833333,103.854520833333 + * ,103.854525666667 + * ,103.854527166667,103.854529333333,103.854525166667,103.854525,103.854524333333,103.854520333333,103.854518166667 + * ,103.8545165 + * ,103.854516666667,103.854511666667,103.854515,103.854514666667,103.8545145,103.854514,103.854517,103.854527 + * ,103.854531166667 + * ,103.854531666667,103.8545335,103.8545335,103.854534,103.854534666667,103.854534833333,103.854537833333 + * ,103.854537166667 + * ,103.854540166667],"2":[11.1155463333333,11.1155478333333,11.1155546666667,11.1155586666667,11.1155595 + * ,11.1155543333333 + * ,11.115552,11.1155461666667,11.1155483333333,11.115549,11.1155618333333,11.1155745,11.1155771666667,11.1155746666667 + * ,11.1155708333333 + * ,11.1155701666667,11.1155716666667,11.1155733333333,11.1155781666667,11.1155795,11.1155785,11.1155735 + * ,11.1155711666667 + * ,11.1155741666667,11.1155763333333,11.1155748333333,11.1155756666667,11.1155768333333,11.1155791666667 + * ,11.1155798333333 + * ,11.1155795,11.1155746666667,11.1155781666667,11.1155813333333,11.1155773333333,11.115566,11.1155595, + * 11.1155568333333, + * 11.1155596666667,11.1155646666667,11.1155635,11.115557,11.1155571666667,11.1155571666667,11.1155565,11.115568 + * ,11.1155695]}}} + *

+ * + * @author Axel Uhl (d043530) + * + */ +public class TestCorrectionOfSingleDigitLatitudesWithSystemPropertySet { + @Before + public void setSystemPropertyToEnableWorkaround() { + System.setProperty(GpsLatLong.IGTIMI_ENABLE_WORKAROUND_FOR_SINGLE_DIGIT_LATITUDES, "true"); + } + + @After + public void unsetSystemPropertyToDisableWorkaround() { + System.clearProperty(GpsLatLong.IGTIMI_ENABLE_WORKAROUND_FOR_SINGLE_DIGIT_LATITUDES); + assertNull(System.getProperty(GpsLatLong.IGTIMI_ENABLE_WORKAROUND_FOR_SINGLE_DIGIT_LATITUDES)); + } + + @Test + public void testIntRounding() { + assertEquals(1, (int) 1.1); + assertEquals(1, (int) 1.0); + assertEquals(1, (int) 1.9); + assertEquals(-1, (int) -1.1); + assertEquals(-1, (int) -1.0); + assertEquals(-1, (int) -1.9); + } + + @Test + public void testSimpleLatLongConversionForTwoNorthLatDigitsWithTwoDigitMinutes() throws ParseException { + double latitude = 11.0 + 17.0/60.0; // with two-digit minutes it cannot have been an incorrect parse + JSONObject json = (JSONObject) new JSONParser().parse("{ \"AA-AA-AAA-5\":{ \"1\":{ \"t\":[ 1360618858970 ], \"1\":[ 172.04 ], \"2\":[ "+latitude+" ] } } }"); + GpsLatLong pos = (GpsLatLong) new FixFactory().createFixes(json).iterator().next(); + assertEquals(latitude, pos.getPosition().getLatDeg(), 0.0000001); + assertEquals(172.04, pos.getPosition().getLngDeg(), 0.0000001); + } + + @Test + public void testSimpleLatLongConversionForTwoSouthLatDigitsWithTwoDigitMinutes() throws ParseException { + double latitude = -11.0 - 17.0/60.0; // with two-digit minutes it cannot have been an incorrect parse + JSONObject json = (JSONObject) new JSONParser().parse("{ \"AA-AA-AAA-5\":{ \"1\":{ \"t\":[ 1360618858970 ], \"1\":[ 172.04 ], \"2\":[ "+latitude+" ] } } }"); + GpsLatLong pos = (GpsLatLong) new FixFactory().createFixes(json).iterator().next(); + assertEquals(latitude, pos.getPosition().getLatDeg(), 0.0000001); + assertEquals(172.04, pos.getPosition().getLngDeg(), 0.0000001); + } + + @Test + public void testSimpleLatLongConversionForTwoLatDigitsWithOneDigitMinutesFromOriginalSingaporeData() throws ParseException { + // NMEA latitude was "116.9341N", meaning 1° 16.9341' North + final double incorrectLatitude = 11. + 6.9341/60.; + final double correctLatitude = 1.0 + 16.9341/60.; + JSONObject json = (JSONObject) new JSONParser().parse("{\"DD-EE-AAHE\":{\"1\":{\"t\":[1392881700000], \"1\":[ 103.85452 ], \"2\":[ "+incorrectLatitude+" ] } } }"); + GpsLatLong pos = (GpsLatLong) new FixFactory().createFixes(json).iterator().next(); + assertEquals(correctLatitude, pos.getPosition().getLatDeg(), 0.0000001); + assertEquals(103.85452, pos.getPosition().getLngDeg(), 0.0000001); + } + + @Test + public void testSimpleLatLongConversionForTwoSouthLatDigitsWithOneDigitMinutesFromOriginalSingaporeData() throws ParseException { + // NMEA latitude was "116.9341N", meaning 1° 16.9341' North + final double incorrectLatitude = -11. - 6.9341/60.; + final double correctLatitude = -1.0 - 16.9341/60.; + JSONObject json = (JSONObject) new JSONParser().parse("{\"DD-EE-AAHE\":{\"1\":{\"t\":[1392881700000], \"1\":[ 103.85452 ], \"2\":[ "+incorrectLatitude+" ] } } }"); + GpsLatLong pos = (GpsLatLong) new FixFactory().createFixes(json).iterator().next(); + assertEquals(correctLatitude, pos.getPosition().getLatDeg(), 0.0000001); + assertEquals(103.85452, pos.getPosition().getLngDeg(), 0.0000001); + } +} diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/datatypes/GpsLatLong.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/datatypes/GpsLatLong.java index f3d59b4b3ab..1b0f8ebc21e 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/datatypes/GpsLatLong.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/datatypes/GpsLatLong.java @@ -9,12 +9,29 @@ import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiver; import com.sap.sailing.domain.igtimiadapter.Sensor; public class GpsLatLong extends Fix { + public static final String IGTIMI_ENABLE_WORKAROUND_FOR_SINGLE_DIGIT_LATITUDES = "igtimi.enableWorkaroundForSingleDigitLatitudes"; private static final long serialVersionUID = 5056284867725893553L; private final Position position; public GpsLatLong(TimePoint timePoint, Sensor sensor, Map valuesPerSubindex) { super(sensor, timePoint); - position = new DegreePosition(((Number) valuesPerSubindex.get(2)).doubleValue(), ((Number) valuesPerSubindex.get(1)).doubleValue()); + final double longitudeInDegrees = ((Number) valuesPerSubindex.get(1)).doubleValue(); + final double preliminaryLatitudeInDegrees = ((Number) valuesPerSubindex.get(2)).doubleValue(); + final double latitudeInDegrees; + if (Boolean.valueOf(System.getProperty(IGTIMI_ENABLE_WORKAROUND_FOR_SINGLE_DIGIT_LATITUDES, "false"))) { + final int degrees = (int) preliminaryLatitudeInDegrees; + final double minutes = (preliminaryLatitudeInDegrees - degrees)*60.0; + if (Math.abs(minutes) >= 10) { + // the minutes have two digits; cannot have been a mis-parse + latitudeInDegrees = preliminaryLatitudeInDegrees; + } else { + final double correctedMinutes = minutes + 10.0*(degrees%10); + latitudeInDegrees = ((int) (degrees/10)) + correctedMinutes/60.0; + } + } else { + latitudeInDegrees = preliminaryLatitudeInDegrees; + } + position = new DegreePosition(latitudeInDegrees, longitudeInDegrees); } public Position getPosition() { diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java index b1c444834bb..3d0f211b1be 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java @@ -189,7 +189,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements * * Potentially, you can create 8 threads per TTCM (connecting only with one single race)." */ - static final Integer MAX_STORED_PACKET_HOP_ALLOWANCE = 8; + static final Integer MAX_STORED_PACKET_HOP_ALLOWANCE = 1000; private final Event tractracEvent; private final com.sap.sailing.domain.base.Regatta regatta; diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java index 375b000225a..daef1695585 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CompetitorsJsonExportServlet.java @@ -16,6 +16,8 @@ import com.sap.sailing.domain.racelog.RaceLogServletConstants; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.gateway.AbstractJsonHttpServlet; +import com.sap.sailing.server.gateway.serialization.impl.BoatClassJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.NationalityJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.PersonJsonSerializer; @@ -74,7 +76,7 @@ public class CompetitorsJsonExportServlet extends AbstractJsonHttpServlet { if (trackedRace != null) { CompetitorJsonSerializer serializer = new CompetitorJsonSerializer(new TeamJsonSerializer( - new PersonJsonSerializer(new NationalityJsonSerializer())), null); + new PersonJsonSerializer(new NationalityJsonSerializer())), new BoatJsonSerializer(new BoatClassJsonSerializer())); for (Competitor competitor : raceColumn.getRaceDefinition(fleet).getCompetitors()) { result.add(serializer.serialize(competitor)); diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java index d99045d00d3..fa7bbbe540f 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/ExportAction.java @@ -23,8 +23,10 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.Util; +import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; +import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.server.RacingEventService; @@ -168,6 +170,22 @@ public abstract class ExportAction { } return result; } + + public Speed getMaximumSpeedOverGround(Competitor competitor, TrackedRace trackedRace) { + Speed maxSpeed = null; + if (Util.contains(trackedRace.getRace().getCompetitors(), competitor)) { + NavigableSet markPassings = trackedRace.getMarkPassings(competitor); + if (!markPassings.isEmpty()) { + TimePoint from = markPassings.first().getTimePoint(); + TimePoint to = trackedRace.getEndOfRace(); + Pair maxSpeedWithGPSFix = trackedRace.getTrack(competitor).getMaximumSpeedOverGround(from, to); + if (maxSpeedWithGPSFix != null) { + maxSpeed = maxSpeedWithGPSFix.getB(); + } + } + } + return maxSpeed; + } public Speed getAverageSpeedOverGround(Leaderboard leaderboard, Competitor competitor, TimePoint timePoint, boolean alsoIncludeNonFinishedRaces) { Speed result = null; diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java index 4ecf1f0191c..8d4aafaed0c 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java @@ -312,6 +312,7 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(competitorRaceDataElement, "distance_from_starboard_side_of_start_line_when_passing_start_in_meters", race.getDistanceFromStarboardSideOfStartLineWhenPassingStart(competitor).getMeters()); addNamedElementWithValue(competitorRaceDataElement, "rank_based_on_distance_from_starboard_side_of_start_line", competitorToDistanceRank.get(competitor)); addNamedElementWithValue(competitorRaceDataElement, "speed_when_crossing_start_line_in_knots", race.getSpeedWhenCrossingStartLine(competitor).getKnots()); + addNamedElementWithValue(competitorRaceDataElement, "maximum_race_speed_over_ground_in_knots", getMaximumSpeedOverGround(competitor, race).getKnots()); addNamedElementWithValue(competitorRaceDataElement, "start_advantage_in_meters", start.getAdvantage().getMeters()); addNamedElementWithValue(competitorRaceDataElement, "advantageous_side_while_approaching_start_line", start.getAdvantageousSideWhileApproachingLine().name()); Distance distanceTraveledInThisRace = race.getDistanceTraveled(competitor, race.getEndOfRace()); diff --git a/mobile/com.sap.sailing.racecommittee.app/res/layout/race_positioning_view.xml b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_positioning_view.xml index 434c7aa3550..d263e78970a 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/layout/race_positioning_view.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_positioning_view.xml @@ -45,7 +45,7 @@ >> create(int id, Bundle args) throws Exception { ExLog.i(TAG, String.format("Creating Competitor-OnlineDataLoader %d", id)); - JsonDeserializer competitorDeserializer = new CompetitorJsonDeserializer(domainFactory.getCompetitorStore()); + JsonDeserializer competitorDeserializer = new CompetitorJsonDeserializer(domainFactory.getCompetitorStore(), + new TeamJsonDeserializer(new PersonJsonDeserializer(new NationalityJsonDeserializer(domainFactory))), + new BoatJsonDeserializer(new BoatClassJsonDeserializer(domainFactory))); DataParser> parser = new CompetitorsDataParser(competitorDeserializer); DataHandler> handler = new CompetitorsDataHandler(OnlineDataManager.this, managedRace);