예제 #1
0
        private async void RunAddress()
        {
            this.SetSearchState(true);
            this.SearchDescription = "Ermittle Position der Adresse ...";
            GlobalCoordinate position = await GeoCoder.GetPositionForAddress(this.AddressStreet + "\n" + this.AddressTown);

            if (position == null)
            {
                this.SetSearchState(false);
                CrossToast.ShowToast("Positionsermittlung fehlgeschlagen");
                return;
            }
            this.SearchDescription = "Suche Tankstellen ...";
            List <PriceInfo> results = await ApiRequests.RequestGasStations(position);

            if (results == null)
            {
                this.SetSearchState(false);
                CrossToast.ShowToast("Suche fehlgeschlagen!");
                return;
            }
            if (results.Count > 0)
            {
                // Add prices to the database
                DbDataProvider.Instance.AddPricesToHistory(results);
                this.ResultsFound?.Invoke(this, results);
                this.SearchDescription = String.Empty; // bug fix for small return delay
            }
            else
            {
                this.SetSearchState(false);
                CrossToast.ShowToast("Es wurden keine Tankstellen gefunden.");
            }
        }
예제 #2
0
        private async void RunGps()
        {
            this.SetSearchState(true);
            this.SearchDescription = "Bestimme aktuelle Position ...";
            GlobalCoordinate position = await GeoLocator.GetCurrentPosition();

            if (position == null)
            {
                this.SetSearchState(false);
                CrossToast.ShowToast("Positionsbestimmung fehlgeschlagen!");
                return;
            }
            this.SearchDescription = "Suche Tankstellen ...";
            List <PriceInfo> results = await ApiRequests.RequestGasStations(new GlobalCoordinate(position.Latitude, position.Longitude));

            if (results == null)
            {
                this.SetSearchState(false);
                CrossToast.ShowToast("Suche fehlgeschlagen!");
                return;
            }
            if (results.Count > 0)
            {
                // Add prices to the database
                DbDataProvider.Instance.AddPricesToHistory(results);
                this.ResultsFound?.Invoke(this, results);
                this.SearchDescription = String.Empty; // bug fix for small return delay
            }
            else
            {
                this.SetSearchState(false);
                CrossToast.ShowToast("Es wurden keine Tankstellen gefunden.");
            }
        }
예제 #3
0
        public void CompactPositionReporting_Encode_GlobalDecode_Surface_Position_At_North_Pole_Can_Return_Null()
        {
            // I saw a slightly odd case in the global round-trip test where a movement from 89.991006/90 to 90/-180 (about 1km) returned
            // null. Null should be returned if the NL zones for the start and end are different, but in this case they should both be 1
            // so I couldn't quite see why it would return null. This test just splits out the case to make it quicker to debug.
            //
            // The reason it happens is because the latitude encodes to 0 for 90° with even encoding which decodes to a latitude of 0°,
            // i.e. the equator. The other CPR coordinate decoded to the correct latitude, so the comparison between the NL of 89.991°
            // and 0° produces different results, hence the null being returned.
            //
            // As far as I can see the even encoding of 90° is correct:
            //    yz = floor(2^19 * (mod(90, 6) / 2^19) + 0.5)
            //    yz = floor(2^19 * (0 / 2^19) + 0.5)
            //    yz = floor(2^19 * 0 + 0.5)
            //    yz = floor(0.5)
            //    yz = 0
            // So I guess global decoding can fail with an even encoding that is directly on the north pole. I will adjust the global
            // round trip test to ignore the poles, it all breaks down a bit there.

            var startLocation = new GlobalCoordinate(89.9910067839303, 90);
            var endLocation   = new GlobalCoordinate(90.0, -180.0);

            var earlyCpr = _Cpr.Encode(startLocation, true, 19);
            var laterCpr = _Cpr.Encode(endLocation, false, 19);

            var decoded = _Cpr.GlobalDecode(earlyCpr, laterCpr, startLocation);

            Assert.IsNull(decoded);
        }
예제 #4
0
        public void CompactPositionReporting_GlobalDecode_Returns_Null_If_The_Latitudes_Straddle_An_NL_Boundary()
        {
            var radiansToDegrees = 180.0 / Math.PI;
            var numerator        = 1.0 - Math.Cos(Math.PI / (2.0 * 15.0));

            for (int nl = 59, i = 0; i < 58; --nl, ++i)
            {
                var denominator      = 1.0 - Math.Cos((2.0 * Math.PI) / nl);
                var fraction         = numerator / denominator;
                var sqrootOfFraction = Math.Sqrt(fraction);
                var latitude         = radiansToDegrees * Math.Acos(sqrootOfFraction);

                var startLocation = new GlobalCoordinate(latitude - 0.0001, 80);
                var endLocation   = new GlobalCoordinate(latitude + 0.0001, 80);

                foreach (var firstFormat in new bool[] { true, false })
                {
                    foreach (var bits in new byte[] { 17, 19 })
                    {
                        var earlyCpr = _Cpr.Encode(startLocation, firstFormat, bits);
                        var laterCpr = _Cpr.Encode(endLocation, !firstFormat, bits);

                        Assert.IsNull(_Cpr.GlobalDecode(earlyCpr, laterCpr, endLocation), "{0}/{1} {2}-bit messages straddling NL{3} did not fail to decode", firstFormat, !firstFormat, bits, nl);
                    }
                }
            }
        }
예제 #5
0
        public void GlobalCoordinate_Constructor_Initialises_To_Known_State()
        {
            var coordinate = new GlobalCoordinate(120.123, -19.456);

            Assert.AreEqual(120.123, coordinate.Latitude);
            Assert.AreEqual(-19.456, coordinate.Longitude);
        }
예제 #6
0
        public void CompactPositionReporting_GlobalDecode_Calculates_Correct_Surface_Position()
        {
            foreach (var startLatitude in new double[] { 17.12345, -17.12345 })
            {
                foreach (var startLongitude in new double[] { 145.12345, 55.12345, -35.12345, -125.12345 })
                {
                    var startLocation = new GlobalCoordinate(startLatitude, startLongitude);

                    double?receiverLatitude, receiverLongitude;
                    GreatCircleMaths.Destination(startLatitude, startLongitude, 70, 80, out receiverLatitude, out receiverLongitude);
                    var receiverLocation = new GlobalCoordinate(receiverLatitude.GetValueOrDefault(), receiverLongitude.GetValueOrDefault());

                    double?endLatitude, endLongitude;
                    GreatCircleMaths.Destination(startLatitude, startLongitude, 90, 1.4, out endLatitude, out endLongitude);
                    var endLocation = new GlobalCoordinate(endLatitude.GetValueOrDefault(), endLongitude.GetValueOrDefault());

                    var earlyCpr = _Cpr.Encode(startLocation, false, 19);
                    var laterCpr = _Cpr.Encode(endLocation, true, 19);

                    var decoded = _Cpr.GlobalDecode(earlyCpr, laterCpr, endLocation);

                    var errorMessage = String.Format("Start: {0}, end (expected): {1}, receiver: {2}, decoded: {3}, earlyCpr: {4}, laterCpr: {5}", startLocation, endLocation, receiverLocation, decoded, earlyCpr, laterCpr);
                    Assert.AreEqual(endLocation.Latitude, decoded.Latitude, 0.00001, errorMessage);
                    Assert.AreEqual(endLocation.Longitude, decoded.Longitude, 0.00001, errorMessage);
                }
            }
        }
예제 #7
0
        public void CompactPositionReporting_Encode_LocalDecode_Produces_Expected_Results_For_CPR_ANOMALIES_AU_Investigation_Case()
        {
            // Coordinates etc. taken from the CPR_ANOMALIES_AU 02/03/2006 paper published on the Internet
            var location = new GlobalCoordinate(-36.850285934, 146.77314);
            var encoded  = _Cpr.Encode(location, true, 17);

            Assert.AreEqual(125914, encoded.Latitude);
            Assert.AreEqual(98874, encoded.Longitude);

            var decoded = _Cpr.LocalDecode(encoded, new GlobalCoordinate(-36.850285934, 146.77395435));

            Assert.AreEqual(location.Latitude, decoded.Latitude, 0.0000000009);
            Assert.AreEqual(location.Longitude, decoded.Longitude, 0.00009);

            // This reproduces the "erroneous" decode. The paper's discussion on the merits of relying on single-decodes is not important here,
            // what is important is that the CPR object reproduces the decoding accurately.
            decoded = _Cpr.LocalDecode(new CompactPositionReportingCoordinate(125914, 21240, true, 17), new GlobalCoordinate(-36.850285934, 146.77395435));
            Assert.AreEqual(-36.850285934, decoded.Latitude, 0.0000000009);
            Assert.AreEqual(149.963856573, decoded.Longitude, 0.0000000009);

            // This reproduces the decode in the table showing that incrementing the encoded latitude produces the correct longitude, showing
            // that single-decodes for a vehicle transitioning between NLs can be dodgy.
            decoded = _Cpr.LocalDecode(new CompactPositionReportingCoordinate(125915, 21240, true, 17), new GlobalCoordinate(-36.850285934, 146.77395435));
            Assert.AreEqual(-36.8502393819518, decoded.Latitude, 0.0000000000009);
            Assert.AreEqual(146.7731362200798, decoded.Longitude, 0.0000000000009);
        }
예제 #8
0
        public static async Task <List <PriceInfo> > RequestGasStations(GlobalCoordinate location, double radius = DEFAULT_RADIUS)
        {
            String requestString = ApiRequests.BuildRequest(LIST_REQUEST,
                                                            new Parameter("type", "all"), new Parameter("lat", location.Latitude.ToString()),
                                                            new Parameter("lng", location.Longitude.ToString()), new Parameter("rad", radius.ToString()),
                                                            new Parameter("apikey", Secrets.API_KEY));

            try
            {
                HttpWebRequest httpRequest = WebRequest.CreateHttp(requestString);
                WebResponse    response    = await httpRequest.GetResponseAsync();

                StationListResult result = null;
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    using (JsonTextReader jsonReader = new JsonTextReader(reader))
                    {
                        result = JsonSerializer.CreateDefault().Deserialize <StationListResult>(jsonReader);
                    }
                return(result.IsOk ? result.ToPriceInfos() : null);
            }
            catch (Exception ex)
            {
                // If anything went wrong (maybe better error handling later)
                return(null);
            }
        }
예제 #9
0
        public void GlobalCoordinate_Default_Constructor_Initialises_To_Known_State_And_Properties_Work()
        {
            var coordinate = new GlobalCoordinate();

            TestUtilities.TestProperty(coordinate, r => r.Latitude, 0.0, 90.0);
            TestUtilities.TestProperty(coordinate, r => r.Longitude, 0.0, 180.0);
        }
예제 #10
0
        public void CompactPositionReporting_Encode_GlobalDecode_Full_Globe_Round_Trip()
        {
            foreach (var numberOfBits in new byte[] { 17, 19 })
            {
                var expectedAccuracy = 0.0;
                switch (numberOfBits)
                {
                case 17:    expectedAccuracy = 0.002; break;

                case 19:    expectedAccuracy = 0.0004; break;
                }

                var latitudeResolution  = 0.25;
                var longitudeResolution = 0.25;
                var endLocation         = new GlobalCoordinate();
                for (var latitude = -89.75; latitude < 90.0; latitude += latitudeResolution)    // global decoding at the poles can produce odd results
                {
                    for (var longitude = -180.0; longitude <= 180.0; longitude += longitudeResolution)
                    {
                        endLocation.Latitude  = latitude;
                        endLocation.Longitude = longitude;

                        var startIsNorth = true;
                        if (latitude == 87.0 || latitude == -80.25 || latitude == 85.75 || latitude == 90.0)
                        {
                            startIsNorth = false;                                                                                 // a start position north of the end position would be in a different NL for these
                        }
                        var bearing = startIsNorth ? 315 : 225;

                        double?startLatitude, startLongitude;
                        GreatCircleMaths.Destination(latitude, longitude, bearing, 1, out startLatitude, out startLongitude);
                        var startLocation = new GlobalCoordinate(startLatitude.Value, startLongitude.Value);

                        var earlyCpr = _Cpr.Encode(startLocation, true, numberOfBits);
                        var laterCpr = _Cpr.Encode(endLocation, false, numberOfBits);

                        var decoded = _Cpr.GlobalDecode(earlyCpr, laterCpr, startLocation);
                        Assert.IsNotNull(decoded, "Returned null for start {0}, end {1} (early CPR {2} later CPR {3})", startLocation, endLocation, earlyCpr, laterCpr);

                        // Longitudes 180 and -180 are the same - if the decoded longitude has one sign and the expected longitude has the other then that's fine
                        if (longitude == -180.0 && decoded.Longitude == 180.0)
                        {
                            decoded.Longitude = -180.0;
                        }
                        else if (longitude == 180.0 && decoded.Longitude == -180.0)
                        {
                            decoded.Longitude = 180.0;
                        }

                        Assert.AreEqual(latitude, decoded.Latitude, expectedAccuracy, "Latitude incorrect for start {0}, end {1} (early CPR {2} later CPR {3})", startLocation, endLocation, earlyCpr, laterCpr);
                        Assert.AreEqual(longitude, decoded.Longitude, expectedAccuracy, "Longitude incorrect for start {0}, end {1} (early CPR {2} later CPR {3})", startLocation, endLocation, earlyCpr, laterCpr);
                    }
                }
            }
        }
예제 #11
0
        public void CompactPositionReporting_Encode_LocalDecode_RoundTrip_Example_Produces_Correct_Results()
        {
            // This test was added in an investigation as to why the LocalDecode test using the CPR101 tables was producing wrong longitudes on the decode. It turned out that
            // the longitude decode was selecting the wrong m value, it was out by one.
            var location         = new GlobalCoordinate(29.9113597534596, 45.0);
            var cprCoordinate    = _Cpr.Encode(location, false, 19);
            var globalCoordinate = _Cpr.LocalDecode(cprCoordinate, location);

            Assert.AreEqual(location.Latitude, globalCoordinate.Latitude, 0.00001);
            Assert.AreEqual(location.Longitude, globalCoordinate.Longitude, 0.000000000001);
        }
예제 #12
0
        public void CompactPositionReporting_Encode_GlobalDecode_Over_Meridian_Produces_Correct_Longitude()
        {
            var startLocation = new GlobalCoordinate(45.0, -0.0001); // on the western side of the meridian
            var endLocation   = new GlobalCoordinate(45.0, 0.0001);  // a movement of a few metres onto the eastern side of the meridian

            var earlyCpr = _Cpr.Encode(startLocation, true, 19);
            var laterCpr = _Cpr.Encode(endLocation, false, 19);

            var decoded = _Cpr.GlobalDecode(earlyCpr, laterCpr, startLocation);

            Assert.AreEqual(45.0, decoded.Latitude, 0.0001);
            Assert.AreEqual(0.0001, decoded.Longitude, 0.0001);
        }
예제 #13
0
        public void CompactPositionReporting_GlobalDecode_Calculates_Correct_Position_From_Two_Coordinates()
        {
            var firstLocation  = new GlobalCoordinate(54.12345, -0.61234);
            var secondLocation = new GlobalCoordinate(54.17, -0.5801);
            var distance       = GreatCircleMaths.Distance(firstLocation.Latitude, firstLocation.Longitude, secondLocation.Latitude, secondLocation.Longitude);

            var earlyCpr = _Cpr.Encode(firstLocation, false, 17);
            var laterCpr = _Cpr.Encode(secondLocation, true, 17);

            var decoded = _Cpr.GlobalDecode(earlyCpr, laterCpr, null);

            Assert.AreEqual(secondLocation.Latitude, decoded.Latitude, 0.0001);
            Assert.AreEqual(secondLocation.Longitude, decoded.Longitude, 0.0001);
        }
예제 #14
0
        /// <summary>
        /// See interface docs.
        /// </summary>
        /// <param name="globalCoordinate"></param>
        /// <param name="oddFormat"></param>
        /// <param name="numberOfBits"></param>
        /// <returns></returns>
        public CompactPositionReportingCoordinate Encode(GlobalCoordinate globalCoordinate, bool oddFormat, byte numberOfBits)
        {
            var maxResult = MaxResultSize(numberOfBits);
            var dlat      = oddFormat ? DLatOdd : DLatEven;
            var yz        = Math.Floor(maxResult * (CircleModulus(globalCoordinate.Latitude, dlat) / dlat) + 0.5);

            var rlat      = dlat * ((yz / maxResult) + Math.Floor(globalCoordinate.Latitude / dlat));
            var oddEvenNL = NL(rlat) - (oddFormat ? 1 : 0);
            var dlon      = oddEvenNL == 0 ? 360.0 : 360.0 / oddEvenNL;
            var xz        = Math.Floor(maxResult * (CircleModulus(globalCoordinate.Longitude, dlon) / dlon) + 0.5);

            var encodedSize = numberOfBits == 19 ? MaxResultSize(17) : maxResult;

            return(new CompactPositionReportingCoordinate((int)CircleModulus(yz, encodedSize), (int)CircleModulus(xz, encodedSize), oddFormat, numberOfBits));
        }
예제 #15
0
        /// <summary>
        /// Figures out the first position that we've seen for the aircraft that would chime with the most recently seen position.
        /// </summary>
        /// <param name="history"></param>
        /// <returns></returns>
        private GlobalCoordinate FindFirstGoodPosition(List <TimedValue <GlobalCoordinate> > history)
        {
            GlobalCoordinate result = null;

            if (history != null)
            {
                var goodPosition = history[history.Count - 1];
                foreach (var value in history)
                {
                    if (CalculatePositionCertainty(false, value, goodPosition) == Certainty.ProbablyRight)
                    {
                        result = value.Value;
                        break;
                    }
                }
            }

            return(result);
        }
예제 #16
0
        /// <summary>
        /// See interface docs.
        /// </summary>
        /// <param name="cprCoordinate"></param>
        /// <param name="referenceCoordinate"></param>
        /// <returns></returns>
        /// <remarks><para>
        /// It was found in testing that encoded latitudes of zero are incredibly sensitive to rounding errors introduced by the use of doubles. This
        /// can be fixed by using decimals, which have much greater precision, and rounding to a very large number of decimal places - but using decimals
        /// is bad news, they are a lot slower than doubles because all of the operations are done in software, there is no hardware FPU support for them.
        /// 10 million divisions in LINQPad took 58 seconds on my development machine when using decimals and less than a second using doubles.
        /// </para><para>
        /// However it's only on the boundary between zones where rounding errors cause huge problems (e.g. returning a longitude of -87 instead of -90).
        /// The problem revolves around the selection of the wrong value of m, rounding errors in doubles at boundaries between zones can push m out by
        /// one, placing the longitude into the wrong zone and getting a longitude that it out by miles. Once the longitude is above zero rounding errors
        /// can have an effect but the correct m is still selected and they only produce inaccuracies of a few feet. So the compromise was to use decimals
        /// when accuracy is paramount - i.e. when the encoded longitude is 0 - and doubles for the rest of the time.
        /// </para></remarks>
        public GlobalCoordinate LocalDecode(CompactPositionReportingCoordinate cprCoordinate, GlobalCoordinate referenceCoordinate)
        {
            var result = new GlobalCoordinate();

            var maxResult     = MaxResultSize(cprCoordinate.NumberOfBits == 19 ? 17 : cprCoordinate.NumberOfBits);
            var dlat          = cprCoordinate.NumberOfBits == 19 ? cprCoordinate.OddFormat ? DLatOdd19Bit : DLatEven19Bit : cprCoordinate.OddFormat ? DLatOdd : DLatEven;
            var dlonNumerator = cprCoordinate.NumberOfBits == 19 ? 90.0 : 360.0;

            var refLat = referenceCoordinate.Latitude;
            var encodedLatitudeSlice = (double)cprCoordinate.Latitude / maxResult;
            var j = Math.Floor(refLat / dlat) + Math.Floor(0.5 + (CircleModulus(refLat, dlat) / dlat) - encodedLatitudeSlice);

            result.Latitude = dlat * (j + encodedLatitudeSlice);

            var oddEvenNL = NL(result.Latitude) - (cprCoordinate.OddFormat ? 1 : 0);

            if (cprCoordinate.Longitude != 0)
            {
                // The version that uses doubles for speed at the expense of potential rounding errors. Any changes here need duplicating below!
                var refLon = referenceCoordinate.Longitude;
                var dlon   = oddEvenNL == 0 ? dlonNumerator : dlonNumerator / oddEvenNL;
                var encodedLongitudeSlice = (double)cprCoordinate.Longitude / maxResult;
                var m = Math.Floor(refLon / dlon) + Math.Floor(0.5 + (CircleModulus(refLon, dlon) / dlon) - encodedLongitudeSlice);
                result.Longitude = dlon * (m + encodedLongitudeSlice);
            }
            else
            {
                // Same as the block above but uses decimals for accuracy at the expense of speed. It would be nice if I could use C# generic functions
                // to remove this redundancy, but they're not very good for that. Sometimes I miss C++, I'd have 1000 ways to make the code common :)
                // Anyway, any changes here need duplicating above!
                var refLon = (decimal)referenceCoordinate.Longitude;
                var dlon   = oddEvenNL == 0 ? (decimal)dlonNumerator : (decimal)dlonNumerator / oddEvenNL;
                var encodedLongitudeSlice = (decimal)cprCoordinate.Longitude / maxResult;
                var m = Math.Floor(Math.Round(refLon / dlon, 20)) + Math.Floor(0.5M + (CircleModulus(refLon, dlon) / dlon) - encodedLongitudeSlice);
                result.Longitude = (double)(dlon * (m + encodedLongitudeSlice));
            }

            return(result);
        }
예제 #17
0
        public void CompactPositionReporting_Encode_Produces_Correct_Results_For_CPR101_Tables()
        {
            // The values for this test are all taken directly from the transition latitude test tables in 1090-WP30-12 Proposed New Appendix CPR101.
            var worksheet = new ExcelWorksheetData(TestContext);

            var numberOfBits     = worksheet.Byte("Bits");
            var oddFormat        = worksheet.Bool("OddFormat");
            var latitude         = worksheet.Double("Latitude");
            var longitude        = worksheet.Double("Longitude");
            var globalCoordinate = new GlobalCoordinate(latitude, longitude);

            var expectedLatitude  = Convert.ToInt32(worksheet.String("ExpectedLatitude"), 16);
            var expectedLongitude = Convert.ToInt32(worksheet.String("ExpectedLongitude"), 16);
            var dataRow           = worksheet.Int("DataRow"); // helps set conditional breakpoints, VSTS doesn't always process rows in ascending order as they appear in the worksheet

            // In testing some of the input latitudes and longitudes couldn't produce the expected results from table 6-1 etc. of 1090-WP30-12 because of small
            // rounding errors in the handling of doubles. Switching to decimals didn't help and it would make the code slower because the FPU doesn't work with
            // decimals. So the "RELatitude" and "RELongitude" columns were added - if they are empty then the code is expected to produce the values in
            // the Expected columns, which corresponds with the test results from 1090-WP30-12, but if they contain values then these are the actual results after
            // the rounding error has had its wicked way. In most cases they are 1 out for latitude but that can move the resolved latitude into a different NL and produce
            // a large difference in longitude. There are very few of these anomalies, they represent errors of a few feet and as this isn't going into an aircraft I can't
            // say I'm too bothered about them. However I do want them to be obvious in the test data, hence the reason for adding new columns rather than just changing
            // the expected results.
            int?reLatitude  = null;
            int?reLongitude = null;

            if (worksheet.String("RELatitude") != null)
            {
                reLatitude  = Convert.ToInt32(worksheet.String("RELatitude"), 16);
                reLongitude = Convert.ToInt32(worksheet.String("RELongitude"), 16);
            }

            var coordinate = _Cpr.Encode(globalCoordinate, oddFormat, numberOfBits);

            Assert.AreEqual(reLatitude ?? expectedLatitude, coordinate.Latitude);
            Assert.AreEqual(reLongitude ?? expectedLongitude, coordinate.Longitude);
            Assert.AreEqual(numberOfBits, coordinate.NumberOfBits);
            Assert.AreEqual(oddFormat, coordinate.OddFormat);
        }
예제 #18
0
        public void CompactPositionReporting_Encode_LocalDecode_Full_Globe_Round_Trip()
        {
            foreach (var oddFormat in new bool[] { true, false })
            {
                foreach (var numberOfBits in new byte[] { 12, 14, 17, 19 })
                {
                    var expectedAccuracy = 0.0;
                    switch (numberOfBits)
                    {
                    case 12:    expectedAccuracy = 0.05; break;

                    case 14:    expectedAccuracy = 0.02; break;

                    case 17:    expectedAccuracy = 0.002; break;

                    case 19:    expectedAccuracy = 0.0004; break;
                    }

                    var latitudeResolution  = 0.25;
                    var longitudeResolution = 0.25;
                    var location            = new GlobalCoordinate();
                    for (var latitude = -90.0; latitude <= 90.0; latitude += latitudeResolution)
                    {
                        for (var longitude = -180.0; longitude <= 180.0; longitude += longitudeResolution)
                        {
                            location.Latitude  = latitude;
                            location.Longitude = longitude;
                            var cprCoordinate    = _Cpr.Encode(location, oddFormat, numberOfBits);
                            var globalCoordinate = _Cpr.LocalDecode(cprCoordinate, location);

                            Assert.AreEqual(latitude, globalCoordinate.Latitude, expectedAccuracy, "Lat/Lon/Format/Bits {0}/{1}/{2}/{3}", latitude, longitude, oddFormat ? "Odd" : "Even", numberOfBits);
                            Assert.AreEqual(longitude, globalCoordinate.Longitude, expectedAccuracy, "Lat/Lon/Format/Bits {0}/{1}/{2}/{3}", latitude, longitude, oddFormat ? "Odd" : "Even", numberOfBits);
                        }
                    }
                }
            }
        }
예제 #19
0
        public void CompactPositionReporting_LocalDecode_Produces_Correct_Results_For_CPR101_Tables()
        {
            // The values for this test are all taken directly from the transition latitude test tables in 1090-WP30-12 Proposed New Appendix CPR101
            var worksheet = new ExcelWorksheetData(TestContext);

            var numberOfBits      = worksheet.Byte("Bits");
            var oddFormat         = worksheet.Bool("OddFormat");
            var encodedLatitude   = Convert.ToInt32(worksheet.String("ExpectedLatitude"), 16);
            var encodedLongitude  = Convert.ToInt32(worksheet.String("ExpectedLongitude"), 16);
            var expectedLatitude  = worksheet.Double("Latitude");
            var expectedLongitude = worksheet.Double("Longitude");
            var cprCoordinate     = new CompactPositionReportingCoordinate(encodedLatitude, encodedLongitude, oddFormat, numberOfBits);

            // The reference latitude and longitude is set to roughly 50km of the expected latitude and longitude
            double?referenceLatitude, referenceLongitude;

            GreatCircleMaths.Destination(expectedLatitude, expectedLongitude, 45, 50, out referenceLatitude, out referenceLongitude);
            var referenceCoordinate = new GlobalCoordinate(referenceLatitude.Value, referenceLongitude.Value);

            var dataRow = worksheet.Int("DataRow"); // helps set conditional breakpoints, VSTS doesn't always process rows in ascending order as they appear in the worksheet

            var decodedCoordinate = _Cpr.LocalDecode(cprCoordinate, referenceCoordinate);

            // We need to accept 180 and -180 as being the same longitude, taking into account rounding errors
            if (expectedLongitude == -180.0 && decodedCoordinate.Longitude > 179.9999999999)
            {
                expectedLongitude = 180.0;
            }
            else if (expectedLongitude == 180.0 && decodedCoordinate.Longitude < -179.9999999999)
            {
                expectedLongitude = -180.0;
            }

            Assert.AreEqual(expectedLatitude, decodedCoordinate.Latitude, 0.0008);           // The CPR tables cover all latitudes, sometimes the rounding introduced by selecting the midpoint of a zone can be quite large
            Assert.AreEqual(expectedLongitude, decodedCoordinate.Longitude, 0.000000000001); // This can have a lower tolerance as the CPR101 tables aren't testing longitude zone boundaries so much
        }
예제 #20
0
        /// <summary>
        /// See interface docs.
        /// </summary>
        /// <param name="earlyCpr"></param>
        /// <param name="laterCpr"></param>
        /// <param name="receiverLocation"></param>
        /// <returns></returns>
        public GlobalCoordinate GlobalDecode(CompactPositionReportingCoordinate earlyCpr, CompactPositionReportingCoordinate laterCpr, GlobalCoordinate receiverLocation)
        {
            GlobalCoordinate result = null;

            if (earlyCpr != null && laterCpr != null && earlyCpr.OddFormat != laterCpr.OddFormat && earlyCpr.NumberOfBits == laterCpr.NumberOfBits && (laterCpr.NumberOfBits < 19 || receiverLocation != null))
            {
                var numberOfBits    = earlyCpr.NumberOfBits;
                var surfaceEncoding = numberOfBits == 19;
                var dlatOdd         = surfaceEncoding ? DLatOdd19Bit : DLatOdd;
                var dlatEven        = surfaceEncoding ? DLatEven19Bit : DLatEven;
                var maxResult       = MaxResultSize(surfaceEncoding ? 17 : numberOfBits);

                var oddCoordinate  = earlyCpr.OddFormat ? earlyCpr : laterCpr;
                var evenCoordinate = oddCoordinate == earlyCpr ? laterCpr : earlyCpr;

                var j = Math.Floor(((59 * (double)evenCoordinate.Latitude - 60 * (double)oddCoordinate.Latitude) / maxResult) + 0.5);

                var rlatEven = dlatEven * (CircleModulus(j, 60) + ((double)evenCoordinate.Latitude / maxResult));
                var rlatOdd  = dlatOdd * (CircleModulus(j, 59) + ((double)oddCoordinate.Latitude / maxResult));
                if (rlatEven >= 270.0)
                {
                    rlatEven -= 360.0;
                }
                if (rlatOdd >= 270.0)
                {
                    rlatOdd -= 360.0;
                }
                var rlat = laterCpr.OddFormat ? rlatOdd : rlatEven;
                if (surfaceEncoding && AbsoluteDifference(rlat, receiverLocation.Latitude) > AbsoluteDifference(rlat - 90.0, receiverLocation.Latitude))
                {
                    rlat     -= 90.0;
                    rlatEven -= 90.0;
                    rlatOdd  -= 90.0;
                }

                var nlEven = NL(rlatEven);
                var nlOdd  = NL(rlatOdd);
                if (nlEven == nlOdd)
                {
                    var nl = nlEven;

                    var mNumerator = (double)evenCoordinate.Longitude * (nl - 1) - (double)oddCoordinate.Longitude * nl;
                    var m          = Math.Floor((mNumerator / maxResult) + 0.5);
                    var ni         = Math.Max(nl - (laterCpr.OddFormat ? 1 : 0), 1);
                    var dlon       = (surfaceEncoding ? 90.0 : 360.0) / ni;
                    var rlon       = dlon * (CircleModulus(m, ni) + ((double)laterCpr.Longitude / maxResult));
                    if (!surfaceEncoding)
                    {
                        rlon = CircleModulus(rlon + 180.0, 360.0) - 180.0;
                    }
                    else
                    {
                        var deltaDegrees    = double.MaxValue;
                        var receiverBearing = LongitudeToBearing(receiverLocation.Longitude);
                        foreach (var possibleRlon in new double[] { rlon, rlon - 90, rlon - 180, rlon - 270 })
                        {
                            var adjustedRlon = CircleModulus(possibleRlon + 180.0, 360.0) - 180.0;

                            var rlonBearing = LongitudeToBearing(adjustedRlon);
                            var delta       = SmallestDegreesBetweenBearings(rlonBearing, receiverBearing);
                            if (delta < 0.0)
                            {
                                delta += 360.0;
                            }
                            if (delta < deltaDegrees)
                            {
                                rlon         = adjustedRlon;
                                deltaDegrees = delta;
                            }
                        }
                    }

                    result = new GlobalCoordinate(rlat, rlon);
                }
            }

            return(result);
        }