80a48393b7096cd631065af611daf0f3f0cb89fa
java/com.sap.sailing.declination.test/src/com/sap/sailing/declination/impl/WMMFileTest.java
| ... | ... | @@ -9,6 +9,7 @@ import java.io.InputStreamReader; |
| 9 | 9 | import java.text.ParseException; |
| 10 | 10 | import java.util.GregorianCalendar; |
| 11 | 11 | |
| 12 | +import org.junit.jupiter.api.BeforeAll; |
|
| 12 | 13 | import org.junit.jupiter.api.Test; |
| 13 | 14 | |
| 14 | 15 | import com.sap.sailing.declination.Declination; |
| ... | ... | @@ -17,22 +18,55 @@ import com.sap.sailing.domain.common.impl.DegreePosition; |
| 17 | 18 | import com.sap.sse.common.TimePoint; |
| 18 | 19 | |
| 19 | 20 | public class WMMFileTest { |
| 21 | + private static Geomagnetism g; |
|
| 22 | + |
|
| 23 | + @BeforeAll |
|
| 24 | + public static void setUpForClass() throws IOException, ParseException { |
|
| 25 | + g = new Geomagnetism(new BufferedReader(new InputStreamReader(WMMFileTest.class.getClassLoader().getResourceAsStream("WMM2025.COF")))); |
|
| 26 | + } |
|
| 27 | + |
|
| 20 | 28 | @Test |
| 21 | 29 | public void testWMM2025Reading() throws IOException { |
| 22 | - final Geomagnetism g = new Geomagnetism(new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("WMM2025.COF")))); |
|
| 23 | 30 | assertNotNull(g); |
| 24 | 31 | } |
| 25 | 32 | |
| 26 | 33 | @Test |
| 27 | - public void testWMM2025Content() throws IOException, ParseException { |
|
| 28 | - final Geomagnetism g = new Geomagnetism(new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("WMMHR2025.COF")))); |
|
| 34 | + public void testWMM2025Content1() throws IOException, ParseException { |
|
| 29 | 35 | final double lat = 44.123; |
| 30 | 36 | final double lng = 8.234; |
| 31 | 37 | final GregorianCalendar cal = new GregorianCalendar(2025, 10, 4, 0, 46, 9); |
| 32 | - g.calculate(lng, lat, /* altitude */ 0, cal); |
|
| 33 | - final double declinationWMM2025 = g.getDeclination(); |
|
| 38 | + assertWMMEqualToOneTenthOfADegreeToDeclinationService(g, lat, lng, cal); |
|
| 39 | + } |
|
| 40 | + |
|
| 41 | + @Test |
|
| 42 | + public void testWMM2025Content2() throws IOException, ParseException { |
|
| 43 | + final double lat = 49.234; |
|
| 44 | + final double lng = 9.777; |
|
| 45 | + final GregorianCalendar cal = new GregorianCalendar(2025, 2, 7, 10, 22, 17); |
|
| 46 | + assertWMMEqualToOneTenthOfADegreeToDeclinationService(g, lat, lng, cal); |
|
| 47 | + } |
|
| 48 | + |
|
| 49 | + @Test |
|
| 50 | + public void testWMM2025ContentSouthernHemisphere() throws IOException, ParseException { |
|
| 51 | + final double lat = -44.123; |
|
| 52 | + final double lng = -8.234; |
|
| 53 | + final GregorianCalendar cal = new GregorianCalendar(2026, 8, 1, 3, 26, 7); |
|
| 54 | + assertWMMEqualToOneTenthOfADegreeToDeclinationService(g, lat, lng, cal); |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + @Test |
|
| 58 | + public void testWMM2025ContentFarEastEquator() throws IOException, ParseException { |
|
| 59 | + final double lat = 0.000; |
|
| 60 | + final double lng = 170.444; |
|
| 61 | + final GregorianCalendar cal = new GregorianCalendar(2027, 1, 24, 18, 0, 0); |
|
| 62 | + assertWMMEqualToOneTenthOfADegreeToDeclinationService(g, lat, lng, cal); |
|
| 63 | + } |
|
| 64 | + |
|
| 65 | + private void assertWMMEqualToOneTenthOfADegreeToDeclinationService(final Geomagnetism g, final double lat, |
|
| 66 | + final double lng, final GregorianCalendar cal) throws IOException, ParseException { |
|
| 67 | + final double declinationWMM2025 = g.calculate(lng, lat, /* altitude */ 0, cal).getDeclination(); |
|
| 34 | 68 | final DeclinationService s = DeclinationService.INSTANCE; |
| 35 | 69 | final Declination declinationFromService = s.getDeclination(TimePoint.of(cal.getTimeInMillis()), new DegreePosition(lat, lng), /* timeout */ 10000); |
| 36 | - assertEquals(declinationWMM2025, declinationFromService.getBearing().getDegrees()); |
|
| 70 | + assertEquals(declinationFromService.getBearing().getDegrees(), declinationWMM2025, /* delta in degrees */ 0.15, "Deviation of more than 0.15 degrees; that's too much!"); |
|
| 37 | 71 | } |
| 38 | 72 | } |
java/com.sap.sailing.declination.test/src/com/sap/sailing/declination/test/DeclinationServiceTest.java
| ... | ... | @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; |
| 13 | 13 | import com.sap.sailing.declination.Declination; |
| 14 | 14 | import com.sap.sailing.declination.DeclinationService; |
| 15 | 15 | import com.sap.sailing.declination.impl.DeclinationImporter; |
| 16 | -import com.sap.sailing.declination.impl.DeclinationServiceImpl; |
|
| 16 | +import com.sap.sailing.declination.impl.DeclinationServiceImplWithStore; |
|
| 17 | 17 | import com.sap.sailing.domain.common.impl.CentralAngleDistance; |
| 18 | 18 | import com.sap.sailing.domain.common.impl.DegreePosition; |
| 19 | 19 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
| ... | ... | @@ -23,7 +23,7 @@ public abstract class DeclinationServiceTest<I extends DeclinationImporter> exte |
| 23 | 23 | |
| 24 | 24 | @BeforeEach |
| 25 | 25 | public void setUp() { |
| 26 | - service = new DeclinationServiceImpl(new CentralAngleDistance(1./180.*Math.PI), importer); |
|
| 26 | + service = new DeclinationServiceImplWithStore(new CentralAngleDistance(1./180.*Math.PI), importer); |
|
| 27 | 27 | } |
| 28 | 28 | |
| 29 | 29 | @Test |
java/com.sap.sailing.declination.test/src/com/sap/sailing/declination/test/WMMDeclinationServiceTest.java
| ... | ... | @@ -0,0 +1,41 @@ |
| 1 | +package com.sap.sailing.declination.test; |
|
| 2 | + |
|
| 3 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
|
| 4 | +import static org.junit.jupiter.api.Assertions.assertNotNull; |
|
| 5 | + |
|
| 6 | +import java.io.IOException; |
|
| 7 | +import java.text.ParseException; |
|
| 8 | +import java.text.SimpleDateFormat; |
|
| 9 | + |
|
| 10 | +import org.junit.jupiter.api.BeforeAll; |
|
| 11 | +import org.junit.jupiter.api.Test; |
|
| 12 | + |
|
| 13 | +import com.sap.sailing.declination.Declination; |
|
| 14 | +import com.sap.sailing.declination.impl.WMMCalculatorDeclinationService; |
|
| 15 | +import com.sap.sailing.domain.common.impl.DegreePosition; |
|
| 16 | +import com.sap.sse.common.impl.MillisecondsTimePoint; |
|
| 17 | + |
|
| 18 | +public class WMMDeclinationServiceTest { |
|
| 19 | + private static WMMCalculatorDeclinationService service; |
|
| 20 | + private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
|
| 21 | + |
|
| 22 | + @BeforeAll |
|
| 23 | + public static void setUp() { |
|
| 24 | + service = new WMMCalculatorDeclinationService(); |
|
| 25 | + } |
|
| 26 | + |
|
| 27 | + @Test |
|
| 28 | + public void testSimpleDeclinationQueryMatchedInStore() throws IOException, ClassNotFoundException, ParseException { |
|
| 29 | + Declination result = service.getDeclination(new MillisecondsTimePoint(simpleDateFormat.parse("2011-02-03").getTime()), |
|
| 30 | + new DegreePosition(51, -5), /* timeoutForOnlineFetchInMilliseconds */ 3000); |
|
| 31 | + assertEquals(-3.-14./60., result.getBearing().getDegrees(), 0.25); |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + @Test |
|
| 35 | + public void testDeclinationQueryNotMatchedInStore() throws IOException, ClassNotFoundException, ParseException { |
|
| 36 | + Declination result = service.getDeclination(new MillisecondsTimePoint(simpleDateFormat.parse("2020-02-03").getTime()), |
|
| 37 | + new DegreePosition(51, -5), /* timeoutForOnlineFetchInMilliseconds */ 5000); |
|
| 38 | + assertNotNull(result); |
|
| 39 | + assertEquals(-1.531, result.getBearing().getDegrees(), 0.1); |
|
| 40 | + } |
|
| 41 | +} |
java/com.sap.sailing.declination/resources/WMM2010.COF
| ... | ... | @@ -0,0 +1,93 @@ |
| 1 | + 2010.0 WMM-2010 12/10/2009 |
|
| 2 | +1 0 -29496.6 0.0 11.6 0.0 |
|
| 3 | +1 1 -1586.3 4944.4 16.5 -25.9 |
|
| 4 | +2 0 -2396.6 0.0 -12.1 0.0 |
|
| 5 | +2 1 3026.1 -2707.7 -4.4 -27.2 |
|
| 6 | +2 2 1668.6 -576.1 -10.0 -2.2 |
|
| 7 | +3 0 1351.1 0.0 -1.4 0.0 |
|
| 8 | +3 1 -2352.3 -214.9 -9.4 6.7 |
|
| 9 | +3 2 1225.6 245.0 7.2 -3.1 |
|
| 10 | +3 3 581.9 -538.3 -2.6 -1.1 |
|
| 11 | +4 0 907.3 0.0 0.8 0.0 |
|
| 12 | +4 1 813.8 283.9 -0.4 0.6 |
|
| 13 | +4 2 120.3 -188.1 -3.1 0.1 |
|
| 14 | +4 3 -335.0 180.9 0.8 1.7 |
|
| 15 | +4 4 70.3 -329.5 -1.8 2.2 |
|
| 16 | +5 0 -232.6 0.0 -0.3 0.0 |
|
| 17 | +5 1 360.1 47.0 0.1 0.4 |
|
| 18 | +5 2 192.4 196.9 -0.5 -0.6 |
|
| 19 | +5 3 -141.1 -119.0 0.1 0.5 |
|
| 20 | +5 4 -157.4 16.1 0.6 0.6 |
|
| 21 | +5 5 13.7 93.8 0.0 0.0 |
|
| 22 | +6 0 67.4 0.0 0.1 0.0 |
|
| 23 | +6 1 65.9 -20.6 -0.3 0.1 |
|
| 24 | +6 2 73.3 23.6 -0.1 -0.2 |
|
| 25 | +6 3 -121.5 54.3 0.0 0.1 |
|
| 26 | +6 4 -36.2 -64.4 -0.1 0.0 |
|
| 27 | +6 5 13.5 9.0 0.0 0.0 |
|
| 28 | +6 6 -64.7 68.0 0.0 0.0 |
|
| 29 | +7 0 80.6 0.0 0.0 0.0 |
|
| 30 | +7 1 -76.8 -51.4 0.0 0.1 |
|
| 31 | +7 2 -8.3 -16.8 0.0 0.0 |
|
| 32 | +7 3 56.5 2.3 0.0 0.0 |
|
| 33 | +7 4 15.8 23.5 0.0 0.0 |
|
| 34 | +7 5 6.4 -2.2 0.0 0.0 |
|
| 35 | +7 6 -7.2 8.6 0.0 0.0 |
|
| 36 | +7 7 9.8 -3.9 0.0 0.0 |
|
| 37 | +8 0 23.6 0.0 0.0 0.0 |
|
| 38 | +8 1 9.8 8.4 0.0 0.0 |
|
| 39 | +8 2 -17.5 -15.3 0.0 0.0 |
|
| 40 | +8 3 -0.4 12.8 0.0 0.0 |
|
| 41 | +8 4 -21.1 -7.4 0.0 0.0 |
|
| 42 | +8 5 15.3 9.3 0.0 0.0 |
|
| 43 | +8 6 13.7 6.7 0.0 0.0 |
|
| 44 | +8 7 -16.5 -0.3 0.0 0.0 |
|
| 45 | +8 8 -0.3 3.0 0.0 0.0 |
|
| 46 | +9 0 5.0 0.0 0.0 0.0 |
|
| 47 | +9 1 8.2 -23.3 0.0 0.0 |
|
| 48 | +9 2 2.9 11.1 0.0 0.0 |
|
| 49 | +9 3 -1.4 9.8 0.0 0.0 |
|
| 50 | +9 4 -1.1 -6.4 0.0 0.0 |
|
| 51 | +9 5 -13.3 1.5 0.0 0.0 |
|
| 52 | +9 6 1.1 4.2 0.0 0.0 |
|
| 53 | +9 7 8.9 0.1 0.0 0.0 |
|
| 54 | +9 8 -9.3 -1.4 0.0 0.0 |
|
| 55 | +9 9 -11.9 9.9 0.0 0.0 |
|
| 56 | +10 0 -1.9 0.0 0.0 0.0 |
|
| 57 | +10 1 -6.2 3.4 0.0 0.0 |
|
| 58 | +10 2 0.3 -0.4 0.0 0.0 |
|
| 59 | +10 3 0.6 -0.8 0.0 0.0 |
|
| 60 | +10 4 1.7 -0.7 0.0 0.0 |
|
| 61 | +10 5 -0.5 0.3 0.0 0.0 |
|
| 62 | +10 6 0.2 -1.7 0.0 0.0 |
|
| 63 | +10 7 1.7 1.3 0.0 0.0 |
|
| 64 | +10 8 -0.2 0.2 0.0 0.0 |
|
| 65 | +10 9 0.3 0.4 0.0 0.0 |
|
| 66 | +10 10 -1.2 0.0 0.0 0.0 |
|
| 67 | +11 0 3.1 0.0 0.0 0.0 |
|
| 68 | +11 1 -1.5 -0.9 0.0 0.0 |
|
| 69 | +11 2 -2.3 -2.2 0.0 0.0 |
|
| 70 | +11 3 2.1 -0.3 0.0 0.0 |
|
| 71 | +11 4 -0.9 0.3 0.0 0.0 |
|
| 72 | +11 5 0.6 0.9 0.0 0.0 |
|
| 73 | +11 6 0.5 0.2 0.0 0.0 |
|
| 74 | +11 7 -0.4 0.1 0.0 0.0 |
|
| 75 | +11 8 0.1 0.5 0.0 0.0 |
|
| 76 | +11 9 -0.1 0.4 0.0 0.0 |
|
| 77 | +11 10 -0.3 -0.2 0.0 0.0 |
|
| 78 | +11 11 0.2 -0.2 0.0 0.0 |
|
| 79 | +12 0 -2.0 0.0 0.0 0.0 |
|
| 80 | +12 1 -0.1 0.6 0.0 0.0 |
|
| 81 | +12 2 0.5 -0.6 0.0 0.0 |
|
| 82 | +12 3 1.3 0.1 0.0 0.0 |
|
| 83 | +12 4 -0.5 0.3 0.0 0.0 |
|
| 84 | +12 5 0.4 -0.1 0.0 0.0 |
|
| 85 | +12 6 0.1 0.1 0.0 0.0 |
|
| 86 | +12 7 0.0 -0.1 0.0 0.0 |
|
| 87 | +12 8 0.2 0.2 0.0 0.0 |
|
| 88 | +12 9 -0.3 0.0 0.0 0.0 |
|
| 89 | +12 10 -0.1 0.0 0.0 0.0 |
|
| 90 | +12 11 0.0 0.0 0.0 0.0 |
|
| 91 | +12 12 0.1 0.0 0.0 0.0 |
|
| 92 | +999999999999999999999999999999999999999999999999 |
|
| 93 | +999999999999999999999999999999999999999999999999 |
java/com.sap.sailing.declination/resources/WMM2015.COF
| ... | ... | @@ -0,0 +1,93 @@ |
| 1 | + 2015.0 WMM-2015 12/10/2014 |
|
| 2 | +1 0 -29404.8 0.0 6.7 0.0 |
|
| 3 | +1 1 -1450.9 4652.5 7.7 -25.9 |
|
| 4 | +2 0 -2500.0 0.0 -11.5 0.0 |
|
| 5 | +2 1 2982.0 -2991.6 -7.1 -30.2 |
|
| 6 | +2 2 1677.0 -734.8 -2.2 -23.9 |
|
| 7 | +3 0 1363.9 0.0 2.8 0.0 |
|
| 8 | +3 1 -2381.0 -82.2 -6.2 5.2 |
|
| 9 | +3 2 1236.2 241.8 3.4 -1.0 |
|
| 10 | +3 3 525.7 -542.9 -12.2 1.1 |
|
| 11 | +4 0 903.1 0.0 -1.1 0.0 |
|
| 12 | +4 1 809.4 282.0 -1.6 0.2 |
|
| 13 | +4 2 86.2 -158.4 -6.0 6.9 |
|
| 14 | +4 3 -309.4 199.8 5.4 3.7 |
|
| 15 | +4 4 47.9 -350.1 -5.5 8.6 |
|
| 16 | +5 0 -234.4 0.0 -0.5 0.0 |
|
| 17 | +5 1 363.1 47.7 0.5 1.0 |
|
| 18 | +5 2 187.8 208.4 -0.7 -0.8 |
|
| 19 | +5 3 -140.7 -121.3 0.8 1.0 |
|
| 20 | +5 4 -151.2 32.3 1.2 3.0 |
|
| 21 | +5 5 13.7 99.1 1.0 0.5 |
|
| 22 | +6 0 65.9 0.0 -0.6 0.0 |
|
| 23 | +6 1 65.6 -19.1 -0.4 0.1 |
|
| 24 | +6 2 73.0 25.0 -0.2 -0.4 |
|
| 25 | +6 3 -121.5 52.7 0.4 1.2 |
|
| 26 | +6 4 -36.2 -64.4 -0.7 0.6 |
|
| 27 | +6 5 13.5 9.0 0.1 0.1 |
|
| 28 | +6 6 -64.7 68.0 0.8 -0.1 |
|
| 29 | +7 0 80.6 0.0 -0.1 0.0 |
|
| 30 | +7 1 -76.8 -51.4 -0.3 0.5 |
|
| 31 | +7 2 -8.3 -16.8 0.1 0.1 |
|
| 32 | +7 3 56.5 2.3 0.4 -0.7 |
|
| 33 | +7 4 15.8 23.5 0.0 -0.5 |
|
| 34 | +7 5 6.4 -2.2 -0.3 0.3 |
|
| 35 | +7 6 -7.2 8.6 0.0 0.0 |
|
| 36 | +7 7 9.8 -3.9 0.3 0.2 |
|
| 37 | +8 0 23.6 0.0 -0.3 0.0 |
|
| 38 | +8 1 9.8 8.4 -0.1 0.1 |
|
| 39 | +8 2 -17.5 -15.3 0.3 0.1 |
|
| 40 | +8 3 -0.4 12.8 0.1 -0.3 |
|
| 41 | +8 4 -21.1 -7.4 0.0 0.0 |
|
| 42 | +8 5 15.3 9.3 -0.1 0.2 |
|
| 43 | +8 6 13.7 6.7 0.1 0.0 |
|
| 44 | +8 7 -16.5 -0.3 0.2 0.0 |
|
| 45 | +8 8 -0.3 3.0 0.0 0.0 |
|
| 46 | +9 0 5.0 0.0 0.0 0.0 |
|
| 47 | +9 1 8.2 -23.3 -0.1 -0.2 |
|
| 48 | +9 2 2.9 11.1 -0.1 -0.1 |
|
| 49 | +9 3 -1.4 9.8 0.1 0.2 |
|
| 50 | +9 4 -1.1 -6.4 0.0 0.1 |
|
| 51 | +9 5 -13.3 1.5 0.3 0.0 |
|
| 52 | +9 6 1.1 4.2 0.0 0.0 |
|
| 53 | +9 7 8.9 0.1 -0.1 0.0 |
|
| 54 | +9 8 -9.3 -1.4 -0.1 0.0 |
|
| 55 | +9 9 -11.9 9.9 0.1 0.0 |
|
| 56 | +10 0 -1.9 0.0 0.0 0.0 |
|
| 57 | +10 1 -6.2 3.4 0.0 0.0 |
|
| 58 | +10 2 0.3 -0.4 0.0 0.0 |
|
| 59 | +10 3 0.6 -0.8 0.0 0.0 |
|
| 60 | +10 4 1.7 -0.7 0.0 0.0 |
|
| 61 | +10 5 -0.5 0.3 0.0 0.0 |
|
| 62 | +10 6 0.2 -1.7 0.0 0.0 |
|
| 63 | +10 7 1.7 1.3 0.0 0.0 |
|
| 64 | +10 8 -0.2 0.2 0.0 0.0 |
|
| 65 | +10 9 0.3 0.4 0.0 0.0 |
|
| 66 | +10 10 -1.2 0.0 0.0 0.0 |
|
| 67 | +11 0 3.1 0.0 0.0 0.0 |
|
| 68 | +11 1 -1.5 -0.9 0.0 0.0 |
|
| 69 | +11 2 -2.3 -2.2 0.0 0.0 |
|
| 70 | +11 3 2.1 -0.3 0.0 0.0 |
|
| 71 | +11 4 -0.9 0.3 0.0 0.0 |
|
| 72 | +11 5 0.6 0.9 0.0 0.0 |
|
| 73 | +11 6 0.5 0.2 0.0 0.0 |
|
| 74 | +11 7 -0.4 0.1 0.0 0.0 |
|
| 75 | +11 8 0.1 0.5 0.0 0.0 |
|
| 76 | +11 9 -0.1 0.4 0.0 0.0 |
|
| 77 | +11 10 -0.3 -0.2 0.0 0.0 |
|
| 78 | +11 11 0.2 -0.2 0.0 0.0 |
|
| 79 | +12 0 -2.0 0.0 0.0 0.0 |
|
| 80 | +12 1 -0.1 0.6 0.0 0.0 |
|
| 81 | +12 2 0.5 -0.6 0.0 0.0 |
|
| 82 | +12 3 1.3 0.1 0.0 0.0 |
|
| 83 | +12 4 -0.5 0.3 0.0 0.0 |
|
| 84 | +12 5 0.4 -0.1 0.0 0.0 |
|
| 85 | +12 6 0.1 0.1 0.0 0.0 |
|
| 86 | +12 7 0.0 -0.1 0.0 0.0 |
|
| 87 | +12 8 0.2 0.2 0.0 0.0 |
|
| 88 | +12 9 -0.3 0.0 0.0 0.0 |
|
| 89 | +12 10 -0.1 0.0 0.0 0.0 |
|
| 90 | +12 11 0.0 0.0 0.0 0.0 |
|
| 91 | +12 12 0.1 0.0 0.0 0.0 |
|
| 92 | +999999999999999999999999999999999999999999999999 |
|
| 93 | +999999999999999999999999999999999999999999999999 |
java/com.sap.sailing.declination/resources/WMM2020.COF
| ... | ... | @@ -0,0 +1,93 @@ |
| 1 | + 2020.0 WMM-2020 12/10/2019 |
|
| 2 | + 1 0 -29404.5 0.0 6.7 0.0 |
|
| 3 | + 1 1 -1450.7 4652.9 7.7 -25.1 |
|
| 4 | + 2 0 -2500.0 0.0 -11.5 0.0 |
|
| 5 | + 2 1 2982.0 -2991.6 -7.1 -30.2 |
|
| 6 | + 2 2 1676.8 -734.8 -2.2 -23.9 |
|
| 7 | + 3 0 1363.9 0.0 2.8 0.0 |
|
| 8 | + 3 1 -2381.0 -82.2 -6.2 5.7 |
|
| 9 | + 3 2 1236.2 241.8 3.4 -1.0 |
|
| 10 | + 3 3 525.7 -542.9 -12.2 1.1 |
|
| 11 | + 4 0 903.1 0.0 -1.1 0.0 |
|
| 12 | + 4 1 809.4 282.0 -1.6 0.2 |
|
| 13 | + 4 2 86.2 -158.4 -6.0 6.9 |
|
| 14 | + 4 3 -309.4 199.8 5.4 3.7 |
|
| 15 | + 4 4 47.9 -350.1 -5.5 -5.6 |
|
| 16 | + 5 0 -234.4 0.0 -0.3 0.0 |
|
| 17 | + 5 1 363.1 47.7 0.6 0.1 |
|
| 18 | + 5 2 187.8 208.4 -0.7 2.5 |
|
| 19 | + 5 3 -140.7 -121.3 0.1 -0.9 |
|
| 20 | + 5 4 -151.2 32.2 1.2 3.0 |
|
| 21 | + 5 5 13.7 99.1 1.0 0.5 |
|
| 22 | + 6 0 65.9 0.0 -0.6 0.0 |
|
| 23 | + 6 1 65.6 -19.1 -0.4 0.1 |
|
| 24 | + 6 2 73.0 25.0 0.5 -1.8 |
|
| 25 | + 6 3 -121.5 52.7 1.4 -1.4 |
|
| 26 | + 6 4 -36.2 -64.4 -1.4 0.9 |
|
| 27 | + 6 5 13.5 9.0 -0.0 0.1 |
|
| 28 | + 6 6 -64.7 68.1 0.8 1.0 |
|
| 29 | + 7 0 80.6 0.0 -0.1 0.0 |
|
| 30 | + 7 1 -76.8 -51.4 -0.3 0.5 |
|
| 31 | + 7 2 -8.3 -16.8 -0.1 0.6 |
|
| 32 | + 7 3 56.5 2.3 0.7 -0.7 |
|
| 33 | + 7 4 15.8 23.5 0.2 -0.2 |
|
| 34 | + 7 5 6.4 -2.2 -0.5 -1.2 |
|
| 35 | + 7 6 -7.2 -27.2 -0.8 0.2 |
|
| 36 | + 7 7 9.8 -1.9 1.0 0.3 |
|
| 37 | + 8 0 23.6 0.0 -0.1 0.0 |
|
| 38 | + 8 1 9.8 8.4 0.1 -0.3 |
|
| 39 | + 8 2 -17.5 -15.3 -0.1 0.7 |
|
| 40 | + 8 3 -0.4 12.8 0.5 -0.2 |
|
| 41 | + 8 4 -21.1 -11.8 -0.1 0.5 |
|
| 42 | + 8 5 15.3 14.9 0.4 -0.3 |
|
| 43 | + 8 6 13.7 3.6 0.5 -0.5 |
|
| 44 | + 8 7 -16.5 -6.9 0.0 0.4 |
|
| 45 | + 8 8 -0.3 2.8 0.4 0.1 |
|
| 46 | + 9 0 5.0 0.0 -0.1 0.0 |
|
| 47 | + 9 1 8.2 -23.3 -0.2 -0.3 |
|
| 48 | + 9 2 2.9 11.1 -0.0 0.2 |
|
| 49 | + 9 3 -1.4 9.8 0.4 -0.4 |
|
| 50 | + 9 4 -1.1 -5.1 -0.3 0.4 |
|
| 51 | + 9 5 -13.3 -6.2 -0.0 0.1 |
|
| 52 | + 9 6 1.1 7.8 0.3 -0.0 |
|
| 53 | + 9 7 8.9 0.4 -0.0 -0.2 |
|
| 54 | + 9 8 -9.3 -1.5 -0.0 0.5 |
|
| 55 | + 9 9 -11.9 9.7 -0.4 0.2 |
|
| 56 | + 10 0 -1.9 0.0 0.0 0.0 |
|
| 57 | + 10 1 -6.2 3.4 -0.0 -0.0 |
|
| 58 | + 10 2 -0.1 -0.2 -0.0 0.1 |
|
| 59 | + 10 3 1.7 3.5 0.2 -0.3 |
|
| 60 | + 10 4 -0.9 4.8 -0.1 0.1 |
|
| 61 | + 10 5 0.6 -8.6 -0.2 -0.2 |
|
| 62 | + 10 6 -0.9 -0.1 -0.0 0.1 |
|
| 63 | + 10 7 1.9 -4.2 -0.1 -0.0 |
|
| 64 | + 10 8 1.4 -3.4 -0.2 -0.1 |
|
| 65 | + 10 9 -2.4 -0.1 -0.1 0.2 |
|
| 66 | + 10 10 -3.9 -8.8 -0.0 -0.0 |
|
| 67 | + 11 0 3.0 0.0 -0.0 0.0 |
|
| 68 | + 11 1 -1.4 -0.0 -0.1 -0.0 |
|
| 69 | + 11 2 -2.5 2.6 -0.0 0.1 |
|
| 70 | + 11 3 2.4 -0.5 0.0 0.0 |
|
| 71 | + 11 4 -0.9 -0.4 -0.0 0.2 |
|
| 72 | + 11 5 0.3 0.6 -0.1 -0.0 |
|
| 73 | + 11 6 -0.7 -0.2 0.0 0.0 |
|
| 74 | + 11 7 -0.1 -1.7 -0.0 0.1 |
|
| 75 | + 11 8 1.4 -1.6 -0.1 -0.0 |
|
| 76 | + 11 9 -0.6 -3.0 -0.1 -0.1 |
|
| 77 | + 11 10 0.2 -2.0 -0.1 0.0 |
|
| 78 | + 11 11 3.1 -2.6 -0.1 -0.0 |
|
| 79 | + 12 0 -2.0 0.0 0.0 0.0 |
|
| 80 | + 12 1 -0.1 -1.2 -0.0 -0.0 |
|
| 81 | + 12 2 0.5 0.5 -0.0 0.0 |
|
| 82 | + 12 3 1.3 1.3 0.0 -0.1 |
|
| 83 | + 12 4 -1.2 -1.8 -0.0 0.1 |
|
| 84 | + 12 5 0.7 0.1 -0.0 -0.0 |
|
| 85 | + 12 6 0.3 0.7 0.0 0.0 |
|
| 86 | + 12 7 0.5 -0.1 -0.0 -0.0 |
|
| 87 | + 12 8 -0.2 0.6 0.0 0.1 |
|
| 88 | + 12 9 -0.5 0.2 -0.0 -0.0 |
|
| 89 | + 12 10 0.1 -0.9 -0.0 -0.0 |
|
| 90 | + 12 11 -1.1 -0.0 -0.0 0.0 |
|
| 91 | + 12 12 -0.3 0.5 -0.1 -0.1 |
|
| 92 | +999999999999999999999999999999999999999999999999 |
|
| 93 | +999999999999999999999999999999999999999999999999 |
java/com.sap.sailing.declination/resources/WMM2024.COF
| ... | ... | @@ -1,93 +0,0 @@ |
| 1 | - 2020.0 WMM-2020 12/10/2019 |
|
| 2 | - 1 0 -29404.5 0.0 6.7 0.0 |
|
| 3 | - 1 1 -1450.7 4652.9 7.7 -25.1 |
|
| 4 | - 2 0 -2500.0 0.0 -11.5 0.0 |
|
| 5 | - 2 1 2982.0 -2991.6 -7.1 -30.2 |
|
| 6 | - 2 2 1676.8 -734.8 -2.2 -23.9 |
|
| 7 | - 3 0 1363.9 0.0 2.8 0.0 |
|
| 8 | - 3 1 -2381.0 -82.2 -6.2 5.7 |
|
| 9 | - 3 2 1236.2 241.8 3.4 -1.0 |
|
| 10 | - 3 3 525.7 -542.9 -12.2 1.1 |
|
| 11 | - 4 0 903.1 0.0 -1.1 0.0 |
|
| 12 | - 4 1 809.4 282.0 -1.6 0.2 |
|
| 13 | - 4 2 86.2 -158.4 -6.0 6.9 |
|
| 14 | - 4 3 -309.4 199.8 5.4 3.7 |
|
| 15 | - 4 4 47.9 -350.1 -5.5 -5.6 |
|
| 16 | - 5 0 -234.4 0.0 -0.3 0.0 |
|
| 17 | - 5 1 363.1 47.7 0.6 0.1 |
|
| 18 | - 5 2 187.8 208.4 -0.7 2.5 |
|
| 19 | - 5 3 -140.7 -121.3 0.1 -0.9 |
|
| 20 | - 5 4 -151.2 32.2 1.2 3.0 |
|
| 21 | - 5 5 13.7 99.1 1.0 0.5 |
|
| 22 | - 6 0 65.9 0.0 -0.6 0.0 |
|
| 23 | - 6 1 65.6 -19.1 -0.4 0.1 |
|
| 24 | - 6 2 73.0 25.0 0.5 -1.8 |
|
| 25 | - 6 3 -121.5 52.7 1.4 -1.4 |
|
| 26 | - 6 4 -36.2 -64.4 -1.4 0.9 |
|
| 27 | - 6 5 13.5 9.0 -0.0 0.1 |
|
| 28 | - 6 6 -64.7 68.1 0.8 1.0 |
|
| 29 | - 7 0 80.6 0.0 -0.1 0.0 |
|
| 30 | - 7 1 -76.8 -51.4 -0.3 0.5 |
|
| 31 | - 7 2 -8.3 -16.8 -0.1 0.6 |
|
| 32 | - 7 3 56.5 2.3 0.7 -0.7 |
|
| 33 | - 7 4 15.8 23.5 0.2 -0.2 |
|
| 34 | - 7 5 6.4 -2.2 -0.5 -1.2 |
|
| 35 | - 7 6 -7.2 -27.2 -0.8 0.2 |
|
| 36 | - 7 7 9.8 -1.9 1.0 0.3 |
|
| 37 | - 8 0 23.6 0.0 -0.1 0.0 |
|
| 38 | - 8 1 9.8 8.4 0.1 -0.3 |
|
| 39 | - 8 2 -17.5 -15.3 -0.1 0.7 |
|
| 40 | - 8 3 -0.4 12.8 0.5 -0.2 |
|
| 41 | - 8 4 -21.1 -11.8 -0.1 0.5 |
|
| 42 | - 8 5 15.3 14.9 0.4 -0.3 |
|
| 43 | - 8 6 13.7 3.6 0.5 -0.5 |
|
| 44 | - 8 7 -16.5 -6.9 0.0 0.4 |
|
| 45 | - 8 8 -0.3 2.8 0.4 0.1 |
|
| 46 | - 9 0 5.0 0.0 -0.1 0.0 |
|
| 47 | - 9 1 8.2 -23.3 -0.2 -0.3 |
|
| 48 | - 9 2 2.9 11.1 -0.0 0.2 |
|
| 49 | - 9 3 -1.4 9.8 0.4 -0.4 |
|
| 50 | - 9 4 -1.1 -5.1 -0.3 0.4 |
|
| 51 | - 9 5 -13.3 -6.2 -0.0 0.1 |
|
| 52 | - 9 6 1.1 7.8 0.3 -0.0 |
|
| 53 | - 9 7 8.9 0.4 -0.0 -0.2 |
|
| 54 | - 9 8 -9.3 -1.5 -0.0 0.5 |
|
| 55 | - 9 9 -11.9 9.7 -0.4 0.2 |
|
| 56 | - 10 0 -1.9 0.0 0.0 0.0 |
|
| 57 | - 10 1 -6.2 3.4 -0.0 -0.0 |
|
| 58 | - 10 2 -0.1 -0.2 -0.0 0.1 |
|
| 59 | - 10 3 1.7 3.5 0.2 -0.3 |
|
| 60 | - 10 4 -0.9 4.8 -0.1 0.1 |
|
| 61 | - 10 5 0.6 -8.6 -0.2 -0.2 |
|
| 62 | - 10 6 -0.9 -0.1 -0.0 0.1 |
|
| 63 | - 10 7 1.9 -4.2 -0.1 -0.0 |
|
| 64 | - 10 8 1.4 -3.4 -0.2 -0.1 |
|
| 65 | - 10 9 -2.4 -0.1 -0.1 0.2 |
|
| 66 | - 10 10 -3.9 -8.8 -0.0 -0.0 |
|
| 67 | - 11 0 3.0 0.0 -0.0 0.0 |
|
| 68 | - 11 1 -1.4 -0.0 -0.1 -0.0 |
|
| 69 | - 11 2 -2.5 2.6 -0.0 0.1 |
|
| 70 | - 11 3 2.4 -0.5 0.0 0.0 |
|
| 71 | - 11 4 -0.9 -0.4 -0.0 0.2 |
|
| 72 | - 11 5 0.3 0.6 -0.1 -0.0 |
|
| 73 | - 11 6 -0.7 -0.2 0.0 0.0 |
|
| 74 | - 11 7 -0.1 -1.7 -0.0 0.1 |
|
| 75 | - 11 8 1.4 -1.6 -0.1 -0.0 |
|
| 76 | - 11 9 -0.6 -3.0 -0.1 -0.1 |
|
| 77 | - 11 10 0.2 -2.0 -0.1 0.0 |
|
| 78 | - 11 11 3.1 -2.6 -0.1 -0.0 |
|
| 79 | - 12 0 -2.0 0.0 0.0 0.0 |
|
| 80 | - 12 1 -0.1 -1.2 -0.0 -0.0 |
|
| 81 | - 12 2 0.5 0.5 -0.0 0.0 |
|
| 82 | - 12 3 1.3 1.3 0.0 -0.1 |
|
| 83 | - 12 4 -1.2 -1.8 -0.0 0.1 |
|
| 84 | - 12 5 0.7 0.1 -0.0 -0.0 |
|
| 85 | - 12 6 0.3 0.7 0.0 0.0 |
|
| 86 | - 12 7 0.5 -0.1 -0.0 -0.0 |
|
| 87 | - 12 8 -0.2 0.6 0.0 0.1 |
|
| 88 | - 12 9 -0.5 0.2 -0.0 -0.0 |
|
| 89 | - 12 10 0.1 -0.9 -0.0 -0.0 |
|
| 90 | - 12 11 -1.1 -0.0 -0.0 0.0 |
|
| 91 | - 12 12 -0.3 0.5 -0.1 -0.1 |
|
| 92 | -999999999999999999999999999999999999999999999999 |
|
| 93 | -999999999999999999999999999999999999999999999999 |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/DeclinationService.java
| ... | ... | @@ -3,12 +3,10 @@ package com.sap.sailing.declination; |
| 3 | 3 | import java.io.IOException; |
| 4 | 4 | import java.text.ParseException; |
| 5 | 5 | |
| 6 | -import com.sap.sailing.declination.impl.BGSImporter; |
|
| 7 | 6 | import com.sap.sailing.declination.impl.DeclinationImporter; |
| 8 | -import com.sap.sailing.declination.impl.DeclinationServiceImpl; |
|
| 7 | +import com.sap.sailing.declination.impl.WMMCalculatorDeclinationService; |
|
| 9 | 8 | import com.sap.sailing.domain.common.Mile; |
| 10 | 9 | import com.sap.sailing.domain.common.Position; |
| 11 | -import com.sap.sailing.domain.common.impl.CentralAngleDistance; |
|
| 12 | 10 | import com.sap.sse.common.Distance; |
| 13 | 11 | import com.sap.sse.common.TimePoint; |
| 14 | 12 | |
| ... | ... | @@ -17,7 +15,7 @@ public interface DeclinationService { |
| 17 | 15 | * A default implementation with a spatial default precision of 60 {@link Mile#METERS_PER_GEOGRAPHICAL_MILE |
| 18 | 16 | * nautical miles} which equals the length of an arc with one degree on a meridian. |
| 19 | 17 | */ |
| 20 | - DeclinationService INSTANCE = new DeclinationServiceImpl(new CentralAngleDistance(1./180.*Math.PI), new BGSImporter()); |
|
| 18 | + DeclinationService INSTANCE = new WMMCalculatorDeclinationService(); |
|
| 21 | 19 | |
| 22 | 20 | /** |
| 23 | 21 | * Obtains declination information with the default precision of this declination service in time and space |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/AbstractDeclinationRecord.java
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | +package com.sap.sailing.declination.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.declination.Declination; |
|
| 4 | +import com.sap.sailing.domain.common.Position; |
|
| 5 | +import com.sap.sse.common.Bearing; |
|
| 6 | +import com.sap.sse.common.TimePoint; |
|
| 7 | + |
|
| 8 | +public abstract class AbstractDeclinationRecord implements Declination { |
|
| 9 | + private static final long serialVersionUID = 2701783831406402231L; |
|
| 10 | + private final Position position; |
|
| 11 | + private final TimePoint timePoint; |
|
| 12 | + private final Bearing bearing; |
|
| 13 | + |
|
| 14 | + public AbstractDeclinationRecord(Position position, TimePoint timePoint, Bearing bearing) { |
|
| 15 | + this.position = position; |
|
| 16 | + this.timePoint = timePoint; |
|
| 17 | + this.bearing = bearing; |
|
| 18 | + } |
|
| 19 | + |
|
| 20 | + @Override |
|
| 21 | + public Position getPosition() { |
|
| 22 | + return position; |
|
| 23 | + } |
|
| 24 | + |
|
| 25 | + @Override |
|
| 26 | + public TimePoint getTimePoint() { |
|
| 27 | + return timePoint; |
|
| 28 | + } |
|
| 29 | + |
|
| 30 | + @Override |
|
| 31 | + public Bearing getBearing() { |
|
| 32 | + return bearing; |
|
| 33 | + } |
|
| 34 | + |
|
| 35 | + @Override |
|
| 36 | + public String toString() { |
|
| 37 | + return ""+getTimePoint()+"@"+getPosition()+": "+getBearing(); |
|
| 38 | + } |
|
| 39 | +} |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/BGSImporter.java
| ... | ... | @@ -67,7 +67,8 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; |
| 67 | 67 | public class BGSImporter extends DeclinationImporter { |
| 68 | 68 | // private static final String URL_PATTERN = "http://geomag.bgs.ac.uk/web_service/GMModels/bggm/2015/?latitude=%f&longitude=%f&altitude=0&date=%d-%d-%d&format=xml"; |
| 69 | 69 | // private static final String URL_PATTERN = "http://geomag.bgs.ac.uk/web_service/GMModels/wmm/2020/?latitude=%f&longitude=%f&altitude=0&date=%d-%d-%d&format=xml"; |
| 70 | - private static final String URL_PATTERN = "http://geomag.bgs.ac.uk/web_service/GMModels/igrf/13/?latitude=%f&longitude=%f&altitude=0&date=%d-%d-%d&format=xml"; |
|
| 70 | + private static final String URL_PATTERN_PRE_2025 = "http://geomag.bgs.ac.uk/web_service/GMModels/igrf/13/?latitude=%f&longitude=%f&altitude=0&date=%d-%d-%d&format=xml"; |
|
| 71 | + private static final String URL_PATTERN_POST_2025 = "https://geomag.bgs.ac.uk/web_service/GMModels/wmm/2025?latitude=%f&longitude=%f&altitude=0&date=%d-%d-%d&format=xml"; |
|
| 71 | 72 | |
| 72 | 73 | private static class XmlElementHandler extends DefaultHandler { |
| 73 | 74 | private Date dateAsDecimalYear; |
| ... | ... | @@ -156,7 +157,8 @@ public class BGSImporter extends DeclinationImporter { |
| 156 | 157 | throws IOException, ParserConfigurationException, SAXException { |
| 157 | 158 | final Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); |
| 158 | 159 | cal.setTime(timePoint.asDate()); |
| 159 | - final URL url = new URL(String.format(URL_PATTERN, position.getLatDeg(), position.getLngDeg(), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH))); |
|
| 160 | + final int year = cal.get(Calendar.YEAR); |
|
| 161 | + final URL url = new URL(String.format(year >= 2025 ? URL_PATTERN_POST_2025 : URL_PATTERN_PRE_2025, position.getLatDeg(), position.getLngDeg(), year, cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH))); |
|
| 160 | 162 | return getDeclinationFromXml(url.openStream()); |
| 161 | 163 | } |
| 162 | 164 | } |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationRecordForExactTimePoint.java
| ... | ... | @@ -0,0 +1,41 @@ |
| 1 | +package com.sap.sailing.declination.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.declination.Declination; |
|
| 4 | +import com.sap.sailing.domain.common.Position; |
|
| 5 | +import com.sap.sse.common.Bearing; |
|
| 6 | +import com.sap.sse.common.TimePoint; |
|
| 7 | +import com.sap.sse.common.Util; |
|
| 8 | + |
|
| 9 | +/** |
|
| 10 | + * A declination record that does not know about any annual change because it has been computed precisely for |
|
| 11 | + * the time point and position requested. Therefore, no {@link #getBearingCorrectedTo(TimePoint) correction} to |
|
| 12 | + * any time point other than the one {@link #getTimePoint() requested} can be performed. An exception will |
|
| 13 | + * be thrown if that happens. |
|
| 14 | + * |
|
| 15 | + * @author Axel Uhl (d043530) |
|
| 16 | + * |
|
| 17 | + */ |
|
| 18 | +public class DeclinationRecordForExactTimePoint extends AbstractDeclinationRecord implements Declination { |
|
| 19 | + private static final long serialVersionUID = -94512743120385233L; |
|
| 20 | + |
|
| 21 | + public DeclinationRecordForExactTimePoint(Position position, TimePoint timePoint, Bearing bearing) { |
|
| 22 | + super(position, timePoint, bearing); |
|
| 23 | + } |
|
| 24 | + |
|
| 25 | + @Override |
|
| 26 | + public Bearing getAnnualChange() { |
|
| 27 | + return null; |
|
| 28 | + } |
|
| 29 | + |
|
| 30 | + @Override |
|
| 31 | + public Bearing getBearingCorrectedTo(TimePoint timePoint) { |
|
| 32 | + if (Util.equalsWithNull(timePoint, getTimePoint())) { |
|
| 33 | + return getBearing(); |
|
| 34 | + } else { |
|
| 35 | + throw new IllegalArgumentException("Declination computed precisely for " + getTimePoint() |
|
| 36 | + + " cannot be corrected to any other time point " + timePoint |
|
| 37 | + + " because we lack knowledge of the annual change here."); |
|
| 38 | + } |
|
| 39 | + } |
|
| 40 | + |
|
| 41 | +} |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationRecordImpl.java
| ... | ... | @@ -6,36 +6,16 @@ import com.sap.sse.common.Bearing; |
| 6 | 6 | import com.sap.sse.common.TimePoint; |
| 7 | 7 | import com.sap.sse.common.impl.DegreeBearingImpl; |
| 8 | 8 | |
| 9 | -public class DeclinationRecordImpl implements Declination { |
|
| 9 | +public class DeclinationRecordImpl extends AbstractDeclinationRecord implements Declination { |
|
| 10 | 10 | private static final long serialVersionUID = 6918630656182340186L; |
| 11 | - private final Position position; |
|
| 12 | - private final TimePoint timePoint; |
|
| 13 | - private final Bearing bearing; |
|
| 14 | 11 | private final Bearing annualChange; |
| 12 | + |
|
| 15 | 13 | public DeclinationRecordImpl(Position position, TimePoint timePoint, Bearing bearing, Bearing annualChange) { |
| 16 | - super(); |
|
| 17 | - this.position = position; |
|
| 18 | - this.timePoint = timePoint; |
|
| 19 | - this.bearing = bearing; |
|
| 14 | + super(position, timePoint, bearing); |
|
| 20 | 15 | this.annualChange = annualChange; |
| 21 | 16 | } |
| 22 | 17 | |
| 23 | 18 | @Override |
| 24 | - public Position getPosition() { |
|
| 25 | - return position; |
|
| 26 | - } |
|
| 27 | - |
|
| 28 | - @Override |
|
| 29 | - public TimePoint getTimePoint() { |
|
| 30 | - return timePoint; |
|
| 31 | - } |
|
| 32 | - |
|
| 33 | - @Override |
|
| 34 | - public Bearing getBearing() { |
|
| 35 | - return bearing; |
|
| 36 | - } |
|
| 37 | - |
|
| 38 | - @Override |
|
| 39 | 19 | public Bearing getAnnualChange() { |
| 40 | 20 | return annualChange; |
| 41 | 21 | } |
| ... | ... | @@ -50,5 +30,4 @@ public class DeclinationRecordImpl implements Declination { |
| 50 | 30 | public String toString() { |
| 51 | 31 | return ""+getTimePoint()+"@"+getPosition()+": "+getBearing()+", "+getAnnualChange()+"/year"; |
| 52 | 32 | } |
| 53 | - |
|
| 54 | 33 | } |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationServiceImpl.java
| ... | ... | @@ -1,126 +0,0 @@ |
| 1 | -package com.sap.sailing.declination.impl; |
|
| 2 | - |
|
| 3 | -import java.io.IOException; |
|
| 4 | -import java.text.ParseException; |
|
| 5 | -import java.util.Calendar; |
|
| 6 | -import java.util.GregorianCalendar; |
|
| 7 | -import java.util.HashMap; |
|
| 8 | -import java.util.Map; |
|
| 9 | - |
|
| 10 | -import com.sap.sailing.declination.Declination; |
|
| 11 | -import com.sap.sailing.declination.DeclinationService; |
|
| 12 | -import com.sap.sailing.domain.common.Position; |
|
| 13 | -import com.sap.sailing.domain.common.quadtree.QuadTree; |
|
| 14 | -import com.sap.sse.common.Distance; |
|
| 15 | -import com.sap.sse.common.TimePoint; |
|
| 16 | - |
|
| 17 | -public class DeclinationServiceImpl implements DeclinationService { |
|
| 18 | - private final Distance defaultMaxDistance; |
|
| 19 | - private final DeclinationStore persistentStore; |
|
| 20 | - private final DeclinationImporter declinationImporter; |
|
| 21 | - |
|
| 22 | - /** |
|
| 23 | - * Keys are years |
|
| 24 | - */ |
|
| 25 | - private final Map<Integer, QuadTree<Declination>> yearStore; |
|
| 26 | - |
|
| 27 | - /** |
|
| 28 | - * Keys are years |
|
| 29 | - */ |
|
| 30 | - private final Map<Integer, QuadTree<Declination>> importerCache; |
|
| 31 | - |
|
| 32 | - public DeclinationServiceImpl(Distance defaultMaxDistance, DeclinationImporter declinationImporter) { |
|
| 33 | - this.declinationImporter = declinationImporter; |
|
| 34 | - this.defaultMaxDistance = defaultMaxDistance; |
|
| 35 | - this.yearStore = new HashMap<Integer, QuadTree<Declination>>(); |
|
| 36 | - this.persistentStore = new DeclinationStore(declinationImporter); |
|
| 37 | - this.importerCache = new HashMap<Integer, QuadTree<Declination>>(); |
|
| 38 | - } |
|
| 39 | - |
|
| 40 | - @Override |
|
| 41 | - public Declination getDeclination(TimePoint timePoint, Position position, |
|
| 42 | - long timeoutForOnlineFetchInMilliseconds) throws IOException, ParseException { |
|
| 43 | - return getDeclination(timePoint, position, defaultMaxDistance, timeoutForOnlineFetchInMilliseconds); |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - @Override |
|
| 47 | - public Declination getDeclination(TimePoint timePoint, Position position, Distance maxDistance, |
|
| 48 | - long timeoutForOnlineFetchInMilliseconds) throws IOException, ParseException { |
|
| 49 | - Calendar cal = new GregorianCalendar(); |
|
| 50 | - cal.setTime(timePoint.asDate()); |
|
| 51 | - Declination result = null; |
|
| 52 | - double minDistance = Double.MAX_VALUE; |
|
| 53 | - int step = -1; |
|
| 54 | - int year = cal.get(Calendar.YEAR); |
|
| 55 | - QuadTree<Declination> set; |
|
| 56 | - while ((set = getYearStore(year)) != null) { |
|
| 57 | - Declination resultForYear; |
|
| 58 | - synchronized (set) { |
|
| 59 | - resultForYear = set.get(position); |
|
| 60 | - } |
|
| 61 | - Distance spatialDistance = resultForYear.getPosition().getDistance(position); |
|
| 62 | - // consider result only if it's closer than maxDistance |
|
| 63 | - if (spatialDistance.compareTo(maxDistance) <= 0) { |
|
| 64 | - double distance = timeAndSpaceDistance(spatialDistance, resultForYear.getTimePoint(), timePoint); |
|
| 65 | - if (distance < minDistance) { |
|
| 66 | - result = resultForYear; |
|
| 67 | - minDistance = distance; |
|
| 68 | - } |
|
| 69 | - } |
|
| 70 | - year += step; |
|
| 71 | - step = -step - (int) Math.signum(step); // alternate around the original year until no more stored declinations are found |
|
| 72 | - } |
|
| 73 | - if (result == null) { |
|
| 74 | - QuadTree<Declination> importerCacheForYear = importerCache.get(year); |
|
| 75 | - if (importerCacheForYear != null) { |
|
| 76 | - synchronized (importerCacheForYear) { |
|
| 77 | - result = importerCacheForYear.get(position); |
|
| 78 | - } |
|
| 79 | - if (result.getPosition().getDistance(position).compareTo(maxDistance) <= 0) { |
|
| 80 | - return result; |
|
| 81 | - // else it's further away from the requested position as demanded by maxDistance |
|
| 82 | - } |
|
| 83 | - } |
|
| 84 | - result = declinationImporter.getDeclination(position, timePoint, timeoutForOnlineFetchInMilliseconds); |
|
| 85 | - if (result != null) { |
|
| 86 | - if (importerCacheForYear == null) { |
|
| 87 | - importerCacheForYear = new QuadTree<Declination>(); |
|
| 88 | - importerCache.put(year, importerCacheForYear); |
|
| 89 | - } |
|
| 90 | - synchronized (importerCacheForYear) { |
|
| 91 | - importerCacheForYear.put(result.getPosition(), result); |
|
| 92 | - } |
|
| 93 | - } |
|
| 94 | - } |
|
| 95 | - return result; |
|
| 96 | - } |
|
| 97 | - |
|
| 98 | - private QuadTree<Declination> getYearStore(int year) throws IOException, ParseException { |
|
| 99 | - QuadTree<Declination> result = yearStore.get(year); |
|
| 100 | - if (result == null) { |
|
| 101 | - synchronized (this) { |
|
| 102 | - // make sure we only trigger one invocation of persistentStore.getStoredDeclinations(year) even for multiple threads |
|
| 103 | - result = yearStore.get(year); |
|
| 104 | - if (result == null) { |
|
| 105 | - result = persistentStore.getStoredDeclinations(year); |
|
| 106 | - if (result != null) { |
|
| 107 | - yearStore.put(year, result); |
|
| 108 | - } |
|
| 109 | - } |
|
| 110 | - } |
|
| 111 | - } |
|
| 112 | - return result; |
|
| 113 | - } |
|
| 114 | - |
|
| 115 | - /** |
|
| 116 | - * Computes a measure for a "distance" based on time and space, between two positions and time points records. Being |
|
| 117 | - * six months off is deemed to be as bad as being sixty nautical miles off. |
|
| 118 | - */ |
|
| 119 | - static double timeAndSpaceDistance(Distance spatialDistance, TimePoint t1, TimePoint t2) { |
|
| 120 | - double nauticalMileDistance = spatialDistance.getNauticalMiles(); |
|
| 121 | - long millisDistance = Math.abs(t1.asMillis()-t2.asMillis()); |
|
| 122 | - return ((double) millisDistance)/1000. /*s*/ / 3600. /*h*/ / 24. /*days*/ / 186. /*six months*/ + |
|
| 123 | - nauticalMileDistance/60.; |
|
| 124 | - } |
|
| 125 | - |
|
| 126 | -} |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationServiceImplWithStore.java
| ... | ... | @@ -0,0 +1,126 @@ |
| 1 | +package com.sap.sailing.declination.impl; |
|
| 2 | + |
|
| 3 | +import java.io.IOException; |
|
| 4 | +import java.text.ParseException; |
|
| 5 | +import java.util.Calendar; |
|
| 6 | +import java.util.GregorianCalendar; |
|
| 7 | +import java.util.HashMap; |
|
| 8 | +import java.util.Map; |
|
| 9 | + |
|
| 10 | +import com.sap.sailing.declination.Declination; |
|
| 11 | +import com.sap.sailing.declination.DeclinationService; |
|
| 12 | +import com.sap.sailing.domain.common.Position; |
|
| 13 | +import com.sap.sailing.domain.common.quadtree.QuadTree; |
|
| 14 | +import com.sap.sse.common.Distance; |
|
| 15 | +import com.sap.sse.common.TimePoint; |
|
| 16 | + |
|
| 17 | +public class DeclinationServiceImplWithStore implements DeclinationService { |
|
| 18 | + private final Distance defaultMaxDistance; |
|
| 19 | + private final DeclinationStore persistentStore; |
|
| 20 | + private final DeclinationImporter declinationImporter; |
|
| 21 | + |
|
| 22 | + /** |
|
| 23 | + * Keys are years |
|
| 24 | + */ |
|
| 25 | + private final Map<Integer, QuadTree<Declination>> yearStore; |
|
| 26 | + |
|
| 27 | + /** |
|
| 28 | + * Keys are years |
|
| 29 | + */ |
|
| 30 | + private final Map<Integer, QuadTree<Declination>> importerCache; |
|
| 31 | + |
|
| 32 | + public DeclinationServiceImplWithStore(Distance defaultMaxDistance, DeclinationImporter declinationImporter) { |
|
| 33 | + this.declinationImporter = declinationImporter; |
|
| 34 | + this.defaultMaxDistance = defaultMaxDistance; |
|
| 35 | + this.yearStore = new HashMap<Integer, QuadTree<Declination>>(); |
|
| 36 | + this.persistentStore = new DeclinationStore(declinationImporter); |
|
| 37 | + this.importerCache = new HashMap<Integer, QuadTree<Declination>>(); |
|
| 38 | + } |
|
| 39 | + |
|
| 40 | + @Override |
|
| 41 | + public Declination getDeclination(TimePoint timePoint, Position position, |
|
| 42 | + long timeoutForOnlineFetchInMilliseconds) throws IOException, ParseException { |
|
| 43 | + return getDeclination(timePoint, position, defaultMaxDistance, timeoutForOnlineFetchInMilliseconds); |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + @Override |
|
| 47 | + public Declination getDeclination(TimePoint timePoint, Position position, Distance maxDistance, |
|
| 48 | + long timeoutForOnlineFetchInMilliseconds) throws IOException, ParseException { |
|
| 49 | + Calendar cal = new GregorianCalendar(); |
|
| 50 | + cal.setTime(timePoint.asDate()); |
|
| 51 | + Declination result = null; |
|
| 52 | + double minDistance = Double.MAX_VALUE; |
|
| 53 | + int step = -1; |
|
| 54 | + int year = cal.get(Calendar.YEAR); |
|
| 55 | + QuadTree<Declination> set; |
|
| 56 | + while ((set = getYearStore(year)) != null) { |
|
| 57 | + Declination resultForYear; |
|
| 58 | + synchronized (set) { |
|
| 59 | + resultForYear = set.get(position); |
|
| 60 | + } |
|
| 61 | + Distance spatialDistance = resultForYear.getPosition().getDistance(position); |
|
| 62 | + // consider result only if it's closer than maxDistance |
|
| 63 | + if (spatialDistance.compareTo(maxDistance) <= 0) { |
|
| 64 | + double distance = timeAndSpaceDistance(spatialDistance, resultForYear.getTimePoint(), timePoint); |
|
| 65 | + if (distance < minDistance) { |
|
| 66 | + result = resultForYear; |
|
| 67 | + minDistance = distance; |
|
| 68 | + } |
|
| 69 | + } |
|
| 70 | + year += step; |
|
| 71 | + step = -step - (int) Math.signum(step); // alternate around the original year until no more stored declinations are found |
|
| 72 | + } |
|
| 73 | + if (result == null) { |
|
| 74 | + QuadTree<Declination> importerCacheForYear = importerCache.get(year); |
|
| 75 | + if (importerCacheForYear != null) { |
|
| 76 | + synchronized (importerCacheForYear) { |
|
| 77 | + result = importerCacheForYear.get(position); |
|
| 78 | + } |
|
| 79 | + if (result.getPosition().getDistance(position).compareTo(maxDistance) <= 0) { |
|
| 80 | + return result; |
|
| 81 | + // else it's further away from the requested position as demanded by maxDistance |
|
| 82 | + } |
|
| 83 | + } |
|
| 84 | + result = declinationImporter.getDeclination(position, timePoint, timeoutForOnlineFetchInMilliseconds); |
|
| 85 | + if (result != null) { |
|
| 86 | + if (importerCacheForYear == null) { |
|
| 87 | + importerCacheForYear = new QuadTree<Declination>(); |
|
| 88 | + importerCache.put(year, importerCacheForYear); |
|
| 89 | + } |
|
| 90 | + synchronized (importerCacheForYear) { |
|
| 91 | + importerCacheForYear.put(result.getPosition(), result); |
|
| 92 | + } |
|
| 93 | + } |
|
| 94 | + } |
|
| 95 | + return result; |
|
| 96 | + } |
|
| 97 | + |
|
| 98 | + private QuadTree<Declination> getYearStore(int year) throws IOException, ParseException { |
|
| 99 | + QuadTree<Declination> result = yearStore.get(year); |
|
| 100 | + if (result == null) { |
|
| 101 | + synchronized (this) { |
|
| 102 | + // make sure we only trigger one invocation of persistentStore.getStoredDeclinations(year) even for multiple threads |
|
| 103 | + result = yearStore.get(year); |
|
| 104 | + if (result == null) { |
|
| 105 | + result = persistentStore.getStoredDeclinations(year); |
|
| 106 | + if (result != null) { |
|
| 107 | + yearStore.put(year, result); |
|
| 108 | + } |
|
| 109 | + } |
|
| 110 | + } |
|
| 111 | + } |
|
| 112 | + return result; |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + /** |
|
| 116 | + * Computes a measure for a "distance" based on time and space, between two positions and time points records. Being |
|
| 117 | + * six months off is deemed to be as bad as being sixty nautical miles off. |
|
| 118 | + */ |
|
| 119 | + static double timeAndSpaceDistance(Distance spatialDistance, TimePoint t1, TimePoint t2) { |
|
| 120 | + double nauticalMileDistance = spatialDistance.getNauticalMiles(); |
|
| 121 | + long millisDistance = Math.abs(t1.asMillis()-t2.asMillis()); |
|
| 122 | + return ((double) millisDistance)/1000. /*s*/ / 3600. /*h*/ / 24. /*days*/ / 186. /*six months*/ + |
|
| 123 | + nauticalMileDistance/60.; |
|
| 124 | + } |
|
| 125 | + |
|
| 126 | +} |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/DeclinationStore.java
| ... | ... | @@ -207,7 +207,7 @@ public class DeclinationStore { |
| 207 | 207 | existingDeclinationRecord = storedDeclinations.get(point); |
| 208 | 208 | } |
| 209 | 209 | if (existingDeclinationRecord == null |
| 210 | - || DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord.getPosition().getDistance(point), |
|
| 210 | + || DeclinationServiceImplWithStore.timeAndSpaceDistance(existingDeclinationRecord.getPosition().getDistance(point), |
|
| 211 | 211 | timePoint, existingDeclinationRecord.getTimePoint()) > 0.1) { |
| 212 | 212 | // less than ~6 nautical miles and/or ~.6 months off |
| 213 | 213 | fetchAndAppendDeclination(timePoint, point, importer, out); |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/Geomagnetism.java
| ... | ... | @@ -2,6 +2,8 @@ package com.sap.sailing.declination.impl; |
| 2 | 2 | |
| 3 | 3 | import java.io.BufferedReader; |
| 4 | 4 | import java.io.IOException; |
| 5 | +import java.text.ParseException; |
|
| 6 | +import java.text.SimpleDateFormat; |
|
| 5 | 7 | |
| 6 | 8 | /* License Statement from the NOAA |
| 7 | 9 | * The WMM source code is in the public domain and not licensed or |
| ... | ... | @@ -14,24 +16,115 @@ import java.io.IOException; |
| 14 | 16 | |
| 15 | 17 | import java.util.GregorianCalendar; |
| 16 | 18 | |
| 19 | +import com.sap.sse.common.Duration; |
|
| 20 | +import com.sap.sse.common.Named; |
|
| 21 | +import com.sap.sse.common.TimePoint; |
|
| 22 | + |
|
| 17 | 23 | /** |
| 18 | 24 | * <p> |
| 19 | 25 | * Class to calculate magnetic declination, magnetic field strength, inclination etc. for any point on the earth. |
| 20 | 26 | * </p> |
| 21 | 27 | * <p> |
| 22 | 28 | * Adapted from the geomagc software and World Magnetic Model of the NOAA Satellite and Information Service, National |
| 23 | - * Geophysical Data Center |
|
| 29 | + * Geophysical Data Center. Caching removed to make stateless, except for the reading of the coefficients file. |
|
| 30 | + * Results are now returned as instances of the inner class {@link Result}. |
|
| 24 | 31 | * </p> |
| 25 | 32 | * http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml |
| 26 | 33 | * <p> |
| 27 | 34 | * © Deep Pradhan, 2017 |
| 28 | 35 | * </p> |
| 29 | 36 | */ |
| 30 | -class Geomagnetism { |
|
| 37 | +class Geomagnetism implements Named { |
|
| 38 | + private static final long serialVersionUID = -2814152697634383730L; |
|
| 39 | + |
|
| 40 | + class Result { |
|
| 41 | + /** Geomagnetic declination (decimal degrees) [opposite of variation, positive Eastward/negative Westward] */ |
|
| 42 | + private final double declination; |
|
| 43 | + |
|
| 44 | + /** Geomagnetic inclination/dip angle (degrees) [positive downward] */ |
|
| 45 | + private final double inclination; |
|
| 46 | + |
|
| 47 | + /** Geomagnetic field intensity/strength (nano Teslas) */ |
|
| 48 | + private final double intensity; |
|
| 49 | + |
|
| 50 | + /** Geomagnetic horizontal field intensity/strength (nano Teslas) */ |
|
| 51 | + private final double bh; |
|
| 52 | + |
|
| 53 | + /** Geomagnetic vertical field intensity/strength (nano Teslas) [positive downward] */ |
|
| 54 | + private final double bz; |
|
| 55 | + |
|
| 56 | + /** Geomagnetic North South (northerly component) field intensity/strength (nano Tesla) */ |
|
| 57 | + private final double bx; |
|
| 58 | + |
|
| 59 | + /** Geomagnetic East West (easterly component) field intensity/strength (nano Teslas) */ |
|
| 60 | + private final double by; |
|
| 61 | + |
|
| 62 | + public Result(double declination, double inclination, double intensity, double bh, double bz, double bx, double by) { |
|
| 63 | + this.declination = declination; |
|
| 64 | + this.inclination = inclination; |
|
| 65 | + this.intensity = intensity; |
|
| 66 | + this.bh = bh; |
|
| 67 | + this.bz = bz; |
|
| 68 | + this.bx = bx; |
|
| 69 | + this.by = by; |
|
| 70 | + } |
|
| 71 | + |
|
| 72 | + /** @return Geomagnetic declination (degrees) [opposite of variation, positive Eastward/negative Westward] */ |
|
| 73 | + double getDeclination() { |
|
| 74 | + return declination; |
|
| 75 | + } |
|
| 76 | + |
|
| 77 | + /** @return Geomagnetic inclination/dip angle (degrees) [positive downward] */ |
|
| 78 | + double getInclination() { |
|
| 79 | + return inclination; |
|
| 80 | + } |
|
| 81 | + |
|
| 82 | + /** @return Geomagnetic field intensity/strength (nano Teslas) */ |
|
| 83 | + double getIntensity() { |
|
| 84 | + return intensity; |
|
| 85 | + } |
|
| 86 | + |
|
| 87 | + /** @return Geomagnetic horizontal field intensity/strength (nano Teslas) */ |
|
| 88 | + double getHorizontalIntensity() { |
|
| 89 | + return bh; |
|
| 90 | + } |
|
| 91 | + |
|
| 92 | + /** @return Geomagnetic vertical field intensity/strength (nano Teslas) [positive downward] */ |
|
| 93 | + double getVerticalIntensity() { |
|
| 94 | + return bz; |
|
| 95 | + } |
|
| 96 | + |
|
| 97 | + /** @return Geomagnetic North South (northerly component) field intensity/strength (nano Tesla) */ |
|
| 98 | + double getNorthIntensity() { |
|
| 99 | + return bx; |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + /** @return Geomagnetic East West (easterly component) field intensity/strength (nano Teslas) */ |
|
| 103 | + double getEastIntensity() { |
|
| 104 | + return by; |
|
| 105 | + } |
|
| 106 | + |
|
| 107 | + String getModelName() { |
|
| 108 | + return getName(); |
|
| 109 | + } |
|
| 110 | + |
|
| 111 | + TimePoint getModelIssueTimePoint() { |
|
| 112 | + return getIssueTimePoint(); |
|
| 113 | + } |
|
| 114 | + } |
|
| 115 | + |
|
| 116 | + private final static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/dd/yyyy"); |
|
| 117 | + |
|
| 118 | + private final TimePoint startOfValidity; |
|
| 119 | + |
|
| 31 | 120 | /** |
| 32 | 121 | * Initialise the instance without calculations |
| 122 | + * |
|
| 123 | + * @param cofReader |
|
| 124 | + * a reader for a file in .COF format, such as WMM2025.COF; expects to find trailing lines with at least |
|
| 125 | + * "999999" in them |
|
| 33 | 126 | */ |
| 34 | - Geomagnetism(BufferedReader r) throws IOException { |
|
| 127 | + Geomagnetism(BufferedReader cofReader) throws IOException, ParseException { |
|
| 35 | 128 | // Initialize constants |
| 36 | 129 | maxord = MAX_DEG; |
| 37 | 130 | sp[0] = 0; |
| ... | ... | @@ -39,12 +132,16 @@ class Geomagnetism { |
| 39 | 132 | dp[0][0] = 0; |
| 40 | 133 | c[0][0] = 0; |
| 41 | 134 | cd[0][0] = 0; |
| 42 | - final String headerLine = r.readLine(); |
|
| 43 | - epoch = Double.parseDouble(headerLine.trim().split("\\s+")[0]); |
|
| 135 | + final String headerLine = cofReader.readLine(); |
|
| 136 | + final String[] headerFields = headerLine.trim().split("\\s+"); |
|
| 137 | + epoch = Double.parseDouble(headerFields[0]); |
|
| 138 | + startOfValidity = TimePoint.of(new GregorianCalendar((int) epoch, 1, 1, 0, 0, 0).getTimeInMillis()).plus(Duration.ONE_HOUR.times(365.0*24.0*(epoch-((int) epoch)))); |
|
| 139 | + name = headerFields[1]; |
|
| 140 | + issueTimePoint = TimePoint.of(simpleDateFormat.parse(headerFields[2])); |
|
| 44 | 141 | String[] tokens; |
| 45 | 142 | double gnm, hnm, dgnm, dhnm; |
| 46 | 143 | String line; |
| 47 | - while (!(line=r.readLine()).startsWith("999999")) { |
|
| 144 | + while (!(line=cofReader.readLine()).startsWith("999999")) { |
|
| 48 | 145 | tokens = line.trim().split("\\s+"); |
| 49 | 146 | final int n = Integer.parseInt(tokens[0]); |
| 50 | 147 | final int m = Integer.parseInt(tokens[1]); |
| ... | ... | @@ -84,7 +181,6 @@ class Geomagnetism { |
| 84 | 181 | } |
| 85 | 182 | k[1][1] = 0; |
| 86 | 183 | fm[0] = 0; |
| 87 | - otime = oalt = olat = olon = -1000; |
|
| 88 | 184 | } |
| 89 | 185 | |
| 90 | 186 | /** |
| ... | ... | @@ -99,7 +195,7 @@ class Geomagnetism { |
| 99 | 195 | * @param calendar |
| 100 | 196 | * Calendar for date of calculation |
| 101 | 197 | */ |
| 102 | - void calculate(double longitude, double latitude, double altitude, GregorianCalendar calendar) { |
|
| 198 | + Result calculate(double longitude, double latitude, double altitude, GregorianCalendar calendar) { |
|
| 103 | 199 | double rlon = Math.toRadians(longitude), rlat = Math.toRadians(latitude), |
| 104 | 200 | altitudeKm = Double.isNaN(altitude) ? 0 : altitude / 1000, |
| 105 | 201 | yearFraction = calendar.get(GregorianCalendar.YEAR) |
| ... | ... | @@ -111,54 +207,46 @@ class Geomagnetism { |
| 111 | 207 | sp[1] = srlon; |
| 112 | 208 | cp[1] = crlon; |
| 113 | 209 | // Convert from geodetic coords. to spherical coords. |
| 114 | - if (altitudeKm != oalt || latitude != olat) { |
|
| 115 | - double q = Math.sqrt(a2 - c2 * srlat2), q1 = altitudeKm * q, |
|
| 116 | - q2 = ((q1 + a2) / (q1 + b2)) * ((q1 + a2) / (q1 + b2)), |
|
| 117 | - r2 = ((altitudeKm * altitudeKm) + 2 * q1 + (a4 - c4 * srlat2) / (q * q)); |
|
| 118 | - ct = srlat / Math.sqrt(q2 * crlat2 + srlat2); |
|
| 119 | - st = Math.sqrt(1 - (ct * ct)); |
|
| 120 | - r = Math.sqrt(r2); |
|
| 121 | - d = Math.sqrt(a2 * crlat2 + b2 * srlat2); |
|
| 122 | - ca = (altitudeKm + d) / r; |
|
| 123 | - sa = c2 * crlat * srlat / (r * d); |
|
| 124 | - } |
|
| 125 | - if (longitude != olon) { |
|
| 126 | - for (int m = 2; m <= maxord; m++) { |
|
| 127 | - sp[m] = sp[1] * cp[m - 1] + cp[1] * sp[m - 1]; |
|
| 128 | - cp[m] = cp[1] * cp[m - 1] - sp[1] * sp[m - 1]; |
|
| 129 | - } |
|
| 210 | + double q = Math.sqrt(a2 - c2 * srlat2), q1 = altitudeKm * q, |
|
| 211 | + q2 = ((q1 + a2) / (q1 + b2)) * ((q1 + a2) / (q1 + b2)), |
|
| 212 | + r2 = ((altitudeKm * altitudeKm) + 2 * q1 + (a4 - c4 * srlat2) / (q * q)); |
|
| 213 | + ct = srlat / Math.sqrt(q2 * crlat2 + srlat2); |
|
| 214 | + st = Math.sqrt(1 - (ct * ct)); |
|
| 215 | + r = Math.sqrt(r2); |
|
| 216 | + d = Math.sqrt(a2 * crlat2 + b2 * srlat2); |
|
| 217 | + ca = (altitudeKm + d) / r; |
|
| 218 | + sa = c2 * crlat * srlat / (r * d); |
|
| 219 | + for (int m = 2; m <= maxord; m++) { |
|
| 220 | + sp[m] = sp[1] * cp[m - 1] + cp[1] * sp[m - 1]; |
|
| 221 | + cp[m] = cp[1] * cp[m - 1] - sp[1] * sp[m - 1]; |
|
| 130 | 222 | } |
| 131 | 223 | double aor = IAU66_RADIUS / r, ar = aor * aor, br = 0, bt = 0, bp = 0, bpp = 0, par, parp, temp1, temp2; |
| 132 | 224 | for (int n = 1; n <= maxord; n++) { |
| 133 | 225 | ar = ar * aor; |
| 134 | 226 | for (int m = 0, d3 = 1, d4 = (n + m + d3) / d3; d4 > 0; d4--, m += d3) { |
| 135 | 227 | // Compute unnormalized associated legendre polynomials and derivatives via recursion relations |
| 136 | - if (altitudeKm != oalt || latitude != olat) { |
|
| 137 | - if (n == m) { |
|
| 138 | - snorm[n + m * 13] = st * snorm[n - 1 + (m - 1) * 13]; |
|
| 139 | - dp[m][n] = st * dp[m - 1][n - 1] + ct * snorm[n - 1 + (m - 1) * 13]; |
|
| 140 | - } |
|
| 141 | - if (n == 1 && m == 0) { |
|
| 142 | - snorm[n + m * 13] = ct * snorm[n - 1 + m * 13]; |
|
| 143 | - dp[m][n] = ct * dp[m][n - 1] - st * snorm[n - 1 + m * 13]; |
|
| 228 | + if (n == m) { |
|
| 229 | + snorm[n + m * 13] = st * snorm[n - 1 + (m - 1) * 13]; |
|
| 230 | + dp[m][n] = st * dp[m - 1][n - 1] + ct * snorm[n - 1 + (m - 1) * 13]; |
|
| 231 | + } |
|
| 232 | + if (n == 1 && m == 0) { |
|
| 233 | + snorm[n + m * 13] = ct * snorm[n - 1 + m * 13]; |
|
| 234 | + dp[m][n] = ct * dp[m][n - 1] - st * snorm[n - 1 + m * 13]; |
|
| 235 | + } |
|
| 236 | + if (n > 1 && n != m) { |
|
| 237 | + if (m > n - 2) { |
|
| 238 | + snorm[n - 2 + m * 13] = 0; |
|
| 144 | 239 | } |
| 145 | - if (n > 1 && n != m) { |
|
| 146 | - if (m > n - 2) { |
|
| 147 | - snorm[n - 2 + m * 13] = 0; |
|
| 148 | - } |
|
| 149 | - if (m > n - 2) { |
|
| 150 | - dp[m][n - 2] = 0; |
|
| 151 | - } |
|
| 152 | - snorm[n + m * 13] = ct * snorm[n - 1 + m * 13] - k[m][n] * snorm[n - 2 + m * 13]; |
|
| 153 | - dp[m][n] = ct * dp[m][n - 1] - st * snorm[n - 1 + m * 13] - k[m][n] * dp[m][n - 2]; |
|
| 240 | + if (m > n - 2) { |
|
| 241 | + dp[m][n - 2] = 0; |
|
| 154 | 242 | } |
| 243 | + snorm[n + m * 13] = ct * snorm[n - 1 + m * 13] - k[m][n] * snorm[n - 2 + m * 13]; |
|
| 244 | + dp[m][n] = ct * dp[m][n - 1] - st * snorm[n - 1 + m * 13] - k[m][n] * dp[m][n - 2]; |
|
| 155 | 245 | } |
| 156 | 246 | // Time adjust the gauss coefficients |
| 157 | - if (yearFraction != otime) { |
|
| 158 | - tc[m][n] = c[m][n] + dt * cd[m][n]; |
|
| 159 | - if (m != 0) { |
|
| 160 | - tc[n][m - 1] = c[n][m - 1] + dt * cd[n][m - 1]; |
|
| 161 | - } |
|
| 247 | + tc[m][n] = c[m][n] + dt * cd[m][n]; |
|
| 248 | + if (m != 0) { |
|
| 249 | + tc[n][m - 1] = c[n][m - 1] + dt * cd[n][m - 1]; |
|
| 162 | 250 | } |
| 163 | 251 | // Accumulate terms of the spherical harmonic expansions |
| 164 | 252 | par = ar * snorm[n + m * 13]; |
| ... | ... | @@ -193,19 +281,16 @@ class Geomagnetism { |
| 193 | 281 | // bx must be the east-west field component |
| 194 | 282 | // by must be the north-south field component |
| 195 | 283 | // bz must be the vertical field component. |
| 196 | - bx = -bt * ca - br * sa; |
|
| 197 | - by = bp; |
|
| 198 | - bz = bt * sa - br * ca; |
|
| 284 | + final double bx = -bt * ca - br * sa; |
|
| 285 | + final double by = bp; |
|
| 286 | + final double bz = bt * sa - br * ca; |
|
| 199 | 287 | // Compute declination (dec), inclination (dip) and total intensity (ti) |
| 200 | - bh = Math.sqrt((bx * bx) + (by * by)); |
|
| 201 | - intensity = Math.sqrt((bh * bh) + (bz * bz)); |
|
| 288 | + final double bh = Math.sqrt((bx * bx) + (by * by)); |
|
| 289 | + final double intensity = Math.sqrt((bh * bh) + (bz * bz)); |
|
| 202 | 290 | // Calculate the declination. |
| 203 | - declination = Math.toDegrees(Math.atan2(by, bx)); |
|
| 204 | - inclination = Math.toDegrees(Math.atan2(bz, bh)); |
|
| 205 | - otime = yearFraction; |
|
| 206 | - oalt = altitudeKm; |
|
| 207 | - olat = latitude; |
|
| 208 | - olon = longitude; |
|
| 291 | + final double declination = Math.toDegrees(Math.atan2(by, bx)); |
|
| 292 | + final double inclination = Math.toDegrees(Math.atan2(bz, bh)); |
|
| 293 | + return new Result(declination, inclination, intensity, bh, bz, bx, by); |
|
| 209 | 294 | } |
| 210 | 295 | |
| 211 | 296 | /** |
| ... | ... | @@ -218,8 +303,8 @@ class Geomagnetism { |
| 218 | 303 | * @param altitude |
| 219 | 304 | * Altitude in metres (with respect to WGS-1984 ellipsoid) |
| 220 | 305 | */ |
| 221 | - void calculate(double longitude, double latitude, double altitude) { |
|
| 222 | - calculate(longitude, latitude, altitude, new GregorianCalendar()); |
|
| 306 | + Result calculate(double longitude, double latitude, double altitude) { |
|
| 307 | + return calculate(longitude, latitude, altitude, new GregorianCalendar()); |
|
| 223 | 308 | } |
| 224 | 309 | |
| 225 | 310 | /** |
| ... | ... | @@ -230,45 +315,30 @@ class Geomagnetism { |
| 230 | 315 | * @param latitude |
| 231 | 316 | * Latitude in decimal degrees |
| 232 | 317 | */ |
| 233 | - void calculate(double longitude, double latitude) { |
|
| 234 | - calculate(longitude, latitude, 0); |
|
| 318 | + Result calculate(double longitude, double latitude) { |
|
| 319 | + return calculate(longitude, latitude, 0); |
|
| 235 | 320 | } |
| 236 | - |
|
| 237 | - /** @return Geomagnetic declination (degrees) [opposite of variation, positive Eastward/negative Westward] */ |
|
| 238 | - double getDeclination() { |
|
| 239 | - return declination; |
|
| 321 | + |
|
| 322 | + /** @return The date in years, for the start of the valid time of the fit coefficients */ |
|
| 323 | + public double getEpoch() { |
|
| 324 | + return epoch; |
|
| 240 | 325 | } |
| 241 | 326 | |
| 242 | - /** @return Geomagnetic inclination/dip angle (degrees) [positive downward] */ |
|
| 243 | - double getInclination() { |
|
| 244 | - return inclination; |
|
| 327 | + public TimePoint getStartOfValidity() { |
|
| 328 | + return startOfValidity; |
|
| 245 | 329 | } |
| 246 | 330 | |
| 247 | - /** @return Geomagnetic field intensity/strength (nano Teslas) */ |
|
| 248 | - double getIntensity() { |
|
| 249 | - return intensity; |
|
| 331 | + public String getName() { |
|
| 332 | + return name; |
|
| 250 | 333 | } |
| 251 | 334 | |
| 252 | - /** @return Geomagnetic horizontal field intensity/strength (nano Teslas) */ |
|
| 253 | - double getHorizontalIntensity() { |
|
| 254 | - return bh; |
|
| 255 | - } |
|
| 256 | - |
|
| 257 | - /** @return Geomagnetic vertical field intensity/strength (nano Teslas) [positive downward] */ |
|
| 258 | - double getVerticalIntensity() { |
|
| 259 | - return bz; |
|
| 260 | - } |
|
| 261 | - |
|
| 262 | - /** @return Geomagnetic North South (northerly component) field intensity/strength (nano Tesla) */ |
|
| 263 | - double getNorthIntensity() { |
|
| 264 | - return bx; |
|
| 265 | - } |
|
| 266 | - |
|
| 267 | - /** @return Geomagnetic East West (easterly component) field intensity/strength (nano Teslas) */ |
|
| 268 | - double getEastIntensity() { |
|
| 269 | - return by; |
|
| 335 | + public TimePoint getIssueTimePoint() { |
|
| 336 | + return issueTimePoint; |
|
| 270 | 337 | } |
| 338 | + private final String name; |
|
| 271 | 339 | |
| 340 | + private final TimePoint issueTimePoint; |
|
| 341 | + |
|
| 272 | 342 | /** Mean radius of IAU-66 ellipsoid, in km */ |
| 273 | 343 | private static final double IAU66_RADIUS = 6371.2; |
| 274 | 344 | |
| ... | ... | @@ -281,30 +351,6 @@ class Geomagnetism { |
| 281 | 351 | /** The maximum number of degrees of the spherical harmonic model */ |
| 282 | 352 | private static final int MAX_DEG = 12; |
| 283 | 353 | |
| 284 | - /** Geomagnetic declination (decimal degrees) [opposite of variation, positive Eastward/negative Westward] */ |
|
| 285 | - private double declination = 0; |
|
| 286 | - |
|
| 287 | - /** Geomagnetic inclination/dip angle (degrees) [positive downward] */ |
|
| 288 | - private double inclination = 0; |
|
| 289 | - |
|
| 290 | - /** Geomagnetic field intensity/strength (nano Teslas) */ |
|
| 291 | - private double intensity = 0; |
|
| 292 | - |
|
| 293 | - /** Geomagnetic horizontal field intensity/strength (nano Teslas) */ |
|
| 294 | - private double bh; |
|
| 295 | - |
|
| 296 | - /** Geomagnetic vertical field intensity/strength (nano Teslas) [positive downward] */ |
|
| 297 | - private double bz; |
|
| 298 | - |
|
| 299 | - /** Geomagnetic North South (northerly component) field intensity/strength (nano Tesla) */ |
|
| 300 | - private double bx; |
|
| 301 | - |
|
| 302 | - /** Geomagnetic East West (easterly component) field intensity/strength (nano Teslas) */ |
|
| 303 | - private double by; |
|
| 304 | - |
|
| 305 | - /** The maximum order of spherical harmonic model */ |
|
| 306 | - private int maxord; |
|
| 307 | - |
|
| 308 | 354 | /** The Gauss coefficients of main geomagnetic model (nt) */ |
| 309 | 355 | private double c[][] = new double[300][300]; |
| 310 | 356 | |
| ... | ... | @@ -328,17 +374,14 @@ class Geomagnetism { |
| 328 | 374 | private double fn[] = new double[13]; |
| 329 | 375 | private double fm[] = new double[13]; |
| 330 | 376 | |
| 377 | + /** The maximum order of spherical harmonic model */ |
|
| 378 | + private int maxord; |
|
| 379 | + |
|
| 331 | 380 | /** The associated Legendre polynomials for m = 1 (unnormalized) */ |
| 332 | 381 | private double pp[] = new double[13]; |
| 333 | 382 | |
| 334 | 383 | private double k[][] = new double[13][13]; |
| 335 | 384 | |
| 336 | - /** |
|
| 337 | - * The variables otime (old time), oalt (old altitude), olat (old latitude), olon (old longitude), are used to store |
|
| 338 | - * the values used from the previous calculation to save on calculation time if some inputs don't change |
|
| 339 | - */ |
|
| 340 | - private double otime, oalt, olat, olon; |
|
| 341 | - |
|
| 342 | 385 | /** The date in years, for the start of the valid time of the fit coefficients */ |
| 343 | 386 | private double epoch; |
| 344 | 387 |
java/com.sap.sailing.declination/src/com/sap/sailing/declination/impl/WMMCalculatorDeclinationService.java
| ... | ... | @@ -0,0 +1,50 @@ |
| 1 | +package com.sap.sailing.declination.impl; |
|
| 2 | + |
|
| 3 | +import java.io.BufferedReader; |
|
| 4 | +import java.io.IOException; |
|
| 5 | +import java.io.InputStreamReader; |
|
| 6 | +import java.text.ParseException; |
|
| 7 | +import java.util.GregorianCalendar; |
|
| 8 | +import java.util.TreeMap; |
|
| 9 | + |
|
| 10 | +import com.sap.sailing.declination.Declination; |
|
| 11 | +import com.sap.sailing.declination.DeclinationService; |
|
| 12 | +import com.sap.sailing.declination.impl.Geomagnetism.Result; |
|
| 13 | +import com.sap.sailing.domain.common.Position; |
|
| 14 | +import com.sap.sse.common.Distance; |
|
| 15 | +import com.sap.sse.common.TimePoint; |
|
| 16 | +import com.sap.sse.common.impl.DegreeBearingImpl; |
|
| 17 | + |
|
| 18 | +public class WMMCalculatorDeclinationService implements DeclinationService { |
|
| 19 | + /** |
|
| 20 | + * The magnetic models, in ascending order by their {@link Geomagnetism#getIssueTimePoint() issue time point} |
|
| 21 | + */ |
|
| 22 | + private final TreeMap<TimePoint, Geomagnetism> worldMagneticModelsByIssueTimePoint; |
|
| 23 | + |
|
| 24 | + public WMMCalculatorDeclinationService() { |
|
| 25 | + final String[] modelNames = new String[] { "/WMM2010.COF", "/WMM2015.COF", "/WMM2020.COF", "/WMM2025.COF" }; |
|
| 26 | + worldMagneticModelsByIssueTimePoint = new TreeMap<>(); |
|
| 27 | + try { |
|
| 28 | + for (final String modelFileName : modelNames) { |
|
| 29 | + final Geomagnetism model = new Geomagnetism(new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(modelFileName)))); |
|
| 30 | + worldMagneticModelsByIssueTimePoint.put(model.getStartOfValidity(), model); |
|
| 31 | + } |
|
| 32 | + } catch (IOException | ParseException e) { |
|
| 33 | + throw new RuntimeException(e); |
|
| 34 | + } |
|
| 35 | + } |
|
| 36 | + |
|
| 37 | + @Override |
|
| 38 | + public Declination getDeclination(TimePoint timePoint, Position position, long timeoutForOnlineFetchInMilliseconds) throws IOException, ParseException { |
|
| 39 | + final GregorianCalendar calendar = new GregorianCalendar(); |
|
| 40 | + calendar.setTimeInMillis(timePoint.asMillis()); |
|
| 41 | + final Result wmmResult = worldMagneticModelsByIssueTimePoint.floorEntry(timePoint).getValue().calculate(position.getLngDeg(), position.getLatDeg(), /* altitude */ 0, calendar); |
|
| 42 | + return new DeclinationRecordForExactTimePoint(position, timePoint, new DegreeBearingImpl(wmmResult.getDeclination())); |
|
| 43 | + } |
|
| 44 | + |
|
| 45 | + @Override |
|
| 46 | + public Declination getDeclination(TimePoint timePoint, Position position, Distance maxDistance, |
|
| 47 | + long timeoutForOnlineFetchInMilliseconds) throws IOException, ParseException { |
|
| 48 | + return getDeclination(timePoint, position, timeoutForOnlineFetchInMilliseconds); |
|
| 49 | + } |
|
| 50 | +} |