|
59 | 59 | *
|
60 | 60 | * Returns: None
|
61 | 61 | *
|
| 62 | + * Idea for future: |
| 63 | + * Non zero ambiguity removes least significant digits without rounding. |
| 64 | + * Maybe we could use -1 and -2 to add extra digits using !DAO! as |
| 65 | + * documented in http://www.aprs.org/datum.txt |
| 66 | + * |
| 67 | + * For example, -1 adds one more human readable digit. |
| 68 | + * lat minutes 12.345 would produce "12.34" and !W5 ! |
| 69 | + * |
| 70 | + * -2 would encode almost 2 digits in base 91. |
| 71 | + * lat minutes 10.0027 would produce "10.00" and !w: ! |
| 72 | + * |
62 | 73 | *----------------------------------------------------------------*/
|
63 | 74 |
|
64 | 75 | void latitude_to_str (double dlat, int ambiguity, char *slat)
|
@@ -555,6 +566,92 @@ double ll_distance_km (double lat1, double lon1, double lat2, double lon2)
|
555 | 566 | }
|
556 | 567 |
|
557 | 568 |
|
| 569 | +/*------------------------------------------------------------------ |
| 570 | + * |
| 571 | + * Function: ll_bearing_deg |
| 572 | + * |
| 573 | + * Purpose: Calculate bearing between two locations. |
| 574 | + * |
| 575 | + * Inputs: lat1, lon1 - starting location, in degrees. |
| 576 | + * lat2, lon2 - destination location |
| 577 | + * |
| 578 | + * Returns: Initial Bearing, in degrees. |
| 579 | + * The calculation produces Range +- 180 degrees. |
| 580 | + * But I think that 0 - 360 would be more customary? |
| 581 | + * |
| 582 | + *------------------------------------------------------------------*/ |
| 583 | + |
| 584 | +double ll_bearing_deg (double lat1, double lon1, double lat2, double lon2) |
| 585 | +{ |
| 586 | + double b; |
| 587 | + |
| 588 | + lat1 *= M_PI / 180; |
| 589 | + lon1 *= M_PI / 180; |
| 590 | + lat2 *= M_PI / 180; |
| 591 | + lon2 *= M_PI / 180; |
| 592 | + |
| 593 | + b = atan2 (sin(lon2-lon1) * cos(lat2), |
| 594 | + cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2-lon1)); |
| 595 | + |
| 596 | + b *= 180 / M_PI; |
| 597 | + if (b < 0) b += 360; |
| 598 | + |
| 599 | + return (b); |
| 600 | +} |
| 601 | + |
| 602 | + |
| 603 | +/*------------------------------------------------------------------ |
| 604 | + * |
| 605 | + * Function: ll_dest_lat |
| 606 | + * ll_dest_lon |
| 607 | + * |
| 608 | + * Purpose: Calculate the destination location given a starting point, |
| 609 | + * distance, and bearing, |
| 610 | + * |
| 611 | + * Inputs: lat1, lon1 - starting location, in degrees. |
| 612 | + * dist - distance in km. |
| 613 | + * bearing - direction in degrees. Shouldn't matter |
| 614 | + * if it is in +- 180 or 0 to 360 range. |
| 615 | + * |
| 616 | + * Returns: New latitude or longitude. |
| 617 | + * |
| 618 | + *------------------------------------------------------------------*/ |
| 619 | + |
| 620 | +double ll_dest_lat (double lat1, double lon1, double dist, double bearing) |
| 621 | +{ |
| 622 | + double lat2; |
| 623 | + |
| 624 | + lat1 *= M_PI / 180; // Everything to radians. |
| 625 | + lon1 *= M_PI / 180; |
| 626 | + bearing *= M_PI / 180; |
| 627 | + |
| 628 | + lat2 = asin(sin(lat1) * cos(dist/R) + cos(lat1) * sin(dist/R) * cos(bearing)); |
| 629 | + |
| 630 | + lat2 *= 180 / M_PI; // Back to degrees. |
| 631 | + |
| 632 | + return (lat2); |
| 633 | +} |
| 634 | + |
| 635 | +double ll_dest_lon (double lat1, double lon1, double dist, double bearing) |
| 636 | +{ |
| 637 | + double lon2; |
| 638 | + double lat2; |
| 639 | + |
| 640 | + lat1 *= M_PI / 180; // Everything to radians. |
| 641 | + lon1 *= M_PI / 180; |
| 642 | + bearing *= M_PI / 180; |
| 643 | + |
| 644 | + lat2 = asin(sin(lat1) * cos(dist/R) + cos(lat1) * sin(dist/R) * cos(bearing)); |
| 645 | + |
| 646 | + lon2 = lon1 + atan2(sin(bearing) * sin(dist/R) * cos(lat1), cos(dist/R) - sin(lat1) * sin(lat2)); |
| 647 | + |
| 648 | + lon2 *= 180 / M_PI; // Back to degrees. |
| 649 | + |
| 650 | + return (lon2); |
| 651 | +} |
| 652 | + |
| 653 | + |
| 654 | + |
558 | 655 | /*------------------------------------------------------------------
|
559 | 656 | *
|
560 | 657 | * Function: ll_from_grid_square
|
@@ -776,6 +873,7 @@ int main (int argc, char *argv[])
|
776 | 873 | int errors = 0;
|
777 | 874 | int ok;
|
778 | 875 | double dlat, dlon;
|
| 876 | + double d, b; |
779 | 877 |
|
780 | 878 | /* Latitude to APRS format. */
|
781 | 879 |
|
@@ -860,6 +958,55 @@ int main (int argc, char *argv[])
|
860 | 958 | // to be continued for others... NMEA...
|
861 | 959 |
|
862 | 960 |
|
| 961 | +/* Distance & bearing - Take a couple examples from other places and see if we get similar results. */ |
| 962 | + |
| 963 | + // http://www.movable-type.co.uk/scripts/latlong.html |
| 964 | + |
| 965 | + d = ll_distance_km (35., 45., 35., 135.); |
| 966 | + b = ll_bearing_deg (35., 45., 35., 135.); |
| 967 | + |
| 968 | + if (d < 7862 || d > 7882) { errors++; dw_printf ("Error 5.1: Did not expect distance %.1f\n", d); } |
| 969 | + |
| 970 | + if (b < 59.7 || b > 60.3) { errors++; dw_printf ("Error 5.2: Did not expect bearing %.1f\n", b); } |
| 971 | + |
| 972 | + // Sydney to Kinsale. https://woodshole.er.usgs.gov/staffpages/cpolloni/manitou/ccal.htm |
| 973 | + |
| 974 | + d = ll_distance_km (-33.8688, 151.2093, 51.7059, -8.5222); |
| 975 | + b = ll_bearing_deg (-33.8688, 151.2093, 51.7059, -8.5222); |
| 976 | + |
| 977 | + if (d < 17435 || d > 17455) { errors++; dw_printf ("Error 5.3: Did not expect distance %.1f\n", d); } |
| 978 | + |
| 979 | + if (b < 327-1 || b > 327+1) { errors++; dw_printf ("Error 5.4: Did not expect bearing %.1f\n", b); } |
| 980 | + |
| 981 | + |
| 982 | +/* |
| 983 | + * More distance and bearing. |
| 984 | + * Here we will start at some location1 (lat1,lon1) and go some distance (d1) at some bearing (b1). |
| 985 | + * This results in a new location2 (lat2, lon2). |
| 986 | + * We then calculate the distance and bearing from location1 to location2 and compare with the intention. |
| 987 | + */ |
| 988 | + int lat1, lon1, d1 = 10, b1; |
| 989 | + double lat2, lon2, d2, b2; |
| 990 | + |
| 991 | + for (lat1 = -60; lat1 <= 60; lat1 += 30) { |
| 992 | + for (lon1 = -180; lon1 <= 180; lon1 +=30) { |
| 993 | + for (b1 = 0; b1 < 360; b1 += 15) { |
| 994 | + |
| 995 | + lat2 = ll_dest_lat ((double)lat1, (double)lon1, (double)d1, (double)b1); |
| 996 | + lon2 = ll_dest_lon ((double)lat1, (double)lon1, (double)d1, (double)b1); |
| 997 | + |
| 998 | + d2 = ll_distance_km ((double)lat1, (double)lon1, lat2, lon2); |
| 999 | + b2 = ll_bearing_deg ((double)lat1, (double)lon1, lat2, lon2); |
| 1000 | + if (b2 > 359.9 && b2 < 360.1) b2 = 0; |
| 1001 | + |
| 1002 | + // must be within 0.1% of distance and 0.1 degree. |
| 1003 | + if (d2 < 0.999 * d1 || d2 > 1.001 * d1) { errors++; dw_printf ("Error 5.8: lat1=%d, lon2=%d, d1=%d, b1=%d, d2=%.2f\n", lat1, lon1, d1, b1, d2); } |
| 1004 | + if (b2 < b1 - 0.1 || b2 > b1 + 0.1) { errors++; dw_printf ("Error 5.9: lat1=%d, lon2=%d, d1=%d, b1=%d, b2=%.2f\n", lat1, lon1, d1, b1, b2); } |
| 1005 | + } |
| 1006 | + } |
| 1007 | + } |
| 1008 | + |
| 1009 | + |
863 | 1010 | /* Maidenhead locator to lat/long. */
|
864 | 1011 |
|
865 | 1012 |
|
|
0 commit comments