1
1
#ifndef GPS_UTIL_H_
2
2
#define GPS_UTIL_H_
3
3
4
- #include " eigen3/Eigen/Dense"
5
4
#include < math.h>
5
+
6
+ #include < cmath>
7
+ #include < string>
6
8
#include < tuple>
7
9
10
+ #include " eigen3/Eigen/Dense"
11
+ #include " shared/math/math_util.h"
12
+ #include " shared/util/helpers.h"
13
+
14
+ using Eigen::Rotation2Dd;
15
+ using Eigen::Vector2d;
16
+ using math_util::DegToRad;
17
+ using std::string;
18
+
8
19
namespace gps_util {
9
20
10
- inline std::tuple<double , double , int > gpsToUTM (double lat, double lon) {
11
- // Constants for UTM conversion
12
- constexpr double a = 6378137.0 ; // WGS-84 major axis
13
- constexpr double f = 1 / 298.257223563 ; // WGS-84 flattening
14
- constexpr double k0 = 0.9996 ; // UTM scale factor
15
- constexpr double e = sqrt (f * (2 - f)); // Eccentricity
16
- constexpr double e2 = e * e;
17
-
18
- // UTM Zone
19
- int zone = static_cast <int >((lon + 180 ) / 6 ) + 1 ;
20
-
21
- // Convert latitude and longitude to radians
22
- double lat_rad = lat * M_PI / 180.0 ;
23
- double lon_rad = lon * M_PI / 180.0 ;
24
-
25
- // Central meridian of the UTM zone
26
- double lon_origin = (zone - 1 ) * 6 - 180 + 3 ;
27
- double lon_origin_rad = lon_origin * M_PI / 180.0 ;
28
-
29
- // Calculations for UTM coordinates
30
- double N = a / sqrt (1 - e2 * sin (lat_rad) * sin (lat_rad));
31
- double T = tan (lat_rad) * tan (lat_rad);
32
- double C = e2 / (1 - e2 ) * cos (lat_rad) * cos (lat_rad);
33
- double A = cos (lat_rad) * (lon_rad - lon_origin_rad);
34
-
35
- double M = a * ((1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256 ) * lat_rad
36
- - (3 * e2 / 8 + 3 * e2 * e2 / 32 + 45 * e2 * e2 * e2 / 1024 ) * sin (2 * lat_rad)
37
- + (15 * e2 * e2 / 256 + 45 * e2 * e2 * e2 / 1024 ) * sin (4 * lat_rad)
38
- - (35 * e2 * e2 * e2 / 3072 ) * sin (6 * lat_rad));
39
-
40
- double easting = k0 * N * (A + (1 - T + C) * A * A * A / 6
41
- + (5 - 18 * T + T * T + 72 * C - 58 * e2 ) * A * A * A * A * A / 120 ) + 500000.0 ;
42
-
43
- double northing = k0 * (M + N * tan (lat_rad) * (A * A / 2
44
- + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24
45
- + (61 - 58 * T + T * T + 600 * C - 330 * e2 ) * A * A * A * A * A * A / 720 ));
46
-
47
- // Correct for southern hemisphere
48
- if (lat < 0 ) {
49
- northing += 10000000.0 ;
21
+ struct GPSTranslator {
22
+ string GetMapFileFromName (const string& maps_dir, const string& map) {
23
+ return maps_dir + " /" + map + " /" + map + " .gpsmap.txt" ;
24
+ }
25
+
26
+ bool Load (const string& maps_dir, const string& map) {
27
+ const string file = GetMapFileFromName (maps_dir, map);
28
+ ScopedFile fid (file, " r" , true );
29
+ if (fid () == nullptr ) return false ;
30
+ if (fscanf (fid (), " %lf, %lf, %lf" , &gps_origin_latitude,
31
+ &gps_origin_longitude, &map_orientation) != 3 ) {
32
+ return false ;
50
33
}
34
+ printf (" Map origin: %12.8lf, %12.8lf\n " , gps_origin_latitude,
35
+ gps_origin_longitude);
36
+ return true ;
37
+ }
38
+
39
+ Vector2d GPSToMetric (const double latitude, const double longitude) {
40
+ const double theta = DegToRad (latitude);
41
+ const double c = std::cos (theta);
42
+ const double s = std::sin (theta);
43
+ const double r =
44
+ sqrt (Sq (wgs_84_a * wgs_84_b) / (Sq (c * wgs_84_b) + Sq (s * wgs_84_a)));
45
+ const double dlat = DegToRad (latitude - gps_origin_latitude);
46
+ const double dlong = DegToRad (longitude - gps_origin_longitude);
47
+ const double r1 = r * c;
48
+ const double x = r1 * dlong;
49
+ const double y = r * dlat;
50
+ return Rotation2Dd (map_orientation) * Vector2d (x, y);
51
+ }
52
+
53
+ void MetricToGPS (const Vector2d& loc, double * longitude, double * latitude) {
54
+ const double theta = DegToRad (gps_origin_latitude);
55
+ const double c = std::cos (theta);
56
+ const double s = std::sin (theta);
57
+ const double r =
58
+ sqrt (Sq (wgs_84_a * wgs_84_b) / (Sq (c * wgs_84_b) + Sq (s * wgs_84_a)));
59
+ const double r1 = r * c;
60
+ const double dlat = loc.y () / r;
61
+ const double dlong = loc.x () / r1;
62
+ *longitude = gps_origin_longitude + dlong;
63
+ *latitude = gps_origin_latitude + dlat;
64
+ }
65
+
66
+ double gps_origin_longitude;
67
+ double gps_origin_latitude;
68
+ double map_orientation;
69
+
70
+ // Earth geoid parameters from WGS 84 system
71
+ // https://en.wikipedia.org/wiki/World_Geodetic_System#A_new_World_Geodetic_System:_WGS_84
72
+ // a = Semimajor (Equatorial) axis
73
+ static constexpr double wgs_84_a = 6378137.0 ;
74
+ // b = Semiminor (Polar) axis
75
+ static constexpr double wgs_84_b = 6356752.314245 ;
76
+ };
51
77
52
- return std::make_tuple (easting, northing, zone);
78
+ inline std::tuple<double , double , int > gpsToUTM (double lat, double lon) {
79
+ // Constants for UTM conversion
80
+ constexpr double a = 6378137.0 ; // WGS-84 major axis
81
+ constexpr double f = 1 / 298.257223563 ; // WGS-84 flattening
82
+ constexpr double k0 = 0.9996 ; // UTM scale factor
83
+ constexpr double e = sqrt (f * (2 - f)); // Eccentricity
84
+ constexpr double e2 = e * e;
85
+
86
+ // UTM Zone
87
+ int zone = static_cast <int >((lon + 180 ) / 6 ) + 1 ;
88
+
89
+ // Convert latitude and longitude to radians
90
+ double lat_rad = lat * M_PI / 180.0 ;
91
+ double lon_rad = lon * M_PI / 180.0 ;
92
+
93
+ // Central meridian of the UTM zone
94
+ double lon_origin = (zone - 1 ) * 6 - 180 + 3 ;
95
+ double lon_origin_rad = lon_origin * M_PI / 180.0 ;
96
+
97
+ // Calculations for UTM coordinates
98
+ double N = a / sqrt (1 - e2 * sin (lat_rad) * sin (lat_rad));
99
+ double T = tan (lat_rad) * tan (lat_rad);
100
+ double C = e2 / (1 - e2 ) * cos (lat_rad) * cos (lat_rad);
101
+ double A = cos (lat_rad) * (lon_rad - lon_origin_rad);
102
+
103
+ double M =
104
+ a * ((1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256 ) * lat_rad -
105
+ (3 * e2 / 8 + 3 * e2 * e2 / 32 + 45 * e2 * e2 * e2 / 1024 ) *
106
+ sin (2 * lat_rad) +
107
+ (15 * e2 * e2 / 256 + 45 * e2 * e2 * e2 / 1024 ) * sin (4 * lat_rad) -
108
+ (35 * e2 * e2 * e2 / 3072 ) * sin (6 * lat_rad));
109
+
110
+ double easting =
111
+ k0 * N *
112
+ (A + (1 - T + C) * A * A * A / 6 +
113
+ (5 - 18 * T + T * T + 72 * C - 58 * e2 ) * A * A * A * A * A / 120 ) +
114
+ 500000.0 ;
115
+
116
+ double northing =
117
+ k0 *
118
+ (M + N * tan (lat_rad) *
119
+ (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 +
120
+ (61 - 58 * T + T * T + 600 * C - 330 * e2 ) * A * A * A * A * A *
121
+ A / 720 ));
122
+
123
+ // Correct for southern hemisphere
124
+ if (lat < 0 ) {
125
+ northing += 10000000.0 ;
126
+ }
127
+
128
+ return std::make_tuple (easting, northing, zone);
53
129
}
54
130
55
- inline std::tuple<double , double > gpsToGlobalCoord (double lat0, double lon0, double lat1, double lon1) {
56
- // Convert latitude and longitude to utm coordinates
57
- const auto & [e0 , n0, zone0] = gpsToUTM (lat0, lon0);
58
- const auto & [e1 , n1, zone1] = gpsToUTM (lat1, lon1);
59
- return {e1 - e0 , n1 - n0};
131
+ inline double gpsDistance (double lat0, double lon0, double lat1, double lon1) {
132
+ // Convert latitude and longitude to radians
133
+ double lat0_rad = lat0 * M_PI / 180.0 ;
134
+ double lon0_rad = lon0 * M_PI / 180.0 ;
135
+ double lat1_rad = lat1 * M_PI / 180.0 ;
136
+ double lon1_rad = lon1 * M_PI / 180.0 ;
137
+
138
+ // Haversine formula for distance between two GPS coordinates
139
+ double dlat = lat1_rad - lat0_rad;
140
+ double dlon = lon1_rad - lon0_rad;
141
+ double a = pow (sin (dlat / 2 ), 2 ) +
142
+ cos (lat0_rad) * cos (lat1_rad) * pow (sin (dlon / 2 ), 2 );
143
+ double c = 2 * atan2 (sqrt (a), sqrt (1 - a));
144
+ return 6371000 * c; // Radius of Earth in meters
60
145
}
61
146
62
- inline Eigen::Affine2f gpsToLocal (
63
- double current_lat, double current_lon, double current_heading,
64
- double goal_lat, double goal_lon, double goal_heading) {
65
-
66
- // Step 1: Convert current and goal GPS coordinates to UTM
67
- const auto & [curr_easting, curr_northing, curr_zone] = gpsToUTM (current_lat, current_lon);
68
- const auto & [goal_easting, goal_northing, goal_zone] = gpsToUTM (goal_lat, goal_lon);
69
- // Ensure that both coordinates are in the same UTM zone
70
- if (goal_zone != curr_zone) {
71
- throw std::runtime_error (" GPS points are in different UTM zones." );
72
- }
73
-
74
- // Step 2: Calculate the translation vector from the current position to the goal in UTM
75
- double dx = goal_easting - curr_easting;
76
- double dy = goal_northing - curr_northing;
77
-
78
- // Step 3: Convert the current heading to radians and adjust for x-axis reference
79
- // Since 0 degrees points along the y-axis and rotates counter-clockwise,
80
- // convert to radians with 0 degrees aligned along the x-axis
81
- double current_heading_rad = (90.0 - current_heading) * M_PI / 180.0 ;
82
-
83
- // Step 4: Rotate the translation vector to the robot's local frame
84
- double local_x = dx * cos (-current_heading_rad) - dy * sin (-current_heading_rad);
85
- double local_y = dx * sin (-current_heading_rad) + dy * cos (-current_heading_rad);
86
-
87
- // Step 5: Convert the goal heading to the local frame
88
- double goal_heading_rad = (90.0 - goal_heading) * M_PI / 180.0 ;
89
- double local_heading = goal_heading_rad - current_heading_rad;
90
-
91
- // Normalize local heading to the range [-pi, pi]
92
- while (local_heading > M_PI) local_heading -= 2 * M_PI;
93
- while (local_heading < -M_PI) local_heading += 2 * M_PI;
94
-
95
- // Step 6: Create the affine transformation for the local pose of the goal
96
- Eigen::Affine2f transform = Eigen::Translation2f (local_x, local_y) * Eigen::Rotation2Df (local_heading);
97
-
98
- return transform;
147
+ inline std::tuple<double , double > gpsToGlobalCoord (double lat0, double lon0,
148
+ double lat1, double lon1) {
149
+ // Convert latitude and longitude to utm coordinates
150
+ const auto & [e0 , n0, zone0] = gpsToUTM (lat0, lon0);
151
+ const auto & [e1 , n1, zone1] = gpsToUTM (lat1, lon1);
152
+ return {e1 - e0 , n1 - n0};
99
153
}
100
154
155
+ inline Eigen::Affine2f gpsToLocal (double current_lat, double current_lon,
156
+ double current_heading, double goal_lat,
157
+ double goal_lon, double goal_heading) {
158
+ // Step 1: Convert current and goal GPS coordinates to UTM
159
+ const auto & [curr_easting, curr_northing, curr_zone] =
160
+ gpsToUTM (current_lat, current_lon);
161
+ const auto & [goal_easting, goal_northing, goal_zone] =
162
+ gpsToUTM (goal_lat, goal_lon);
163
+ // Ensure that both coordinates are in the same UTM zone
164
+ if (goal_zone != curr_zone) {
165
+ throw std::runtime_error (" GPS points are in different UTM zones." );
166
+ }
167
+
168
+ // Step 2: Calculate the translation vector from the current position to the
169
+ // goal in UTM
170
+ double dx = goal_easting - curr_easting;
171
+ double dy = goal_northing - curr_northing;
172
+
173
+ // Step 3: Convert the current heading to radians and adjust for x-axis
174
+ // reference Since 0 degrees points along the y-axis and rotates
175
+ // counter-clockwise, convert to radians with 0 degrees aligned along the
176
+ // x-axis
177
+ double current_heading_rad = (90.0 - current_heading) * M_PI / 180.0 ;
178
+
179
+ // Step 4: Rotate the translation vector to the robot's local frame
180
+ double local_x =
181
+ dx * cos (-current_heading_rad) - dy * sin (-current_heading_rad);
182
+ double local_y =
183
+ dx * sin (-current_heading_rad) + dy * cos (-current_heading_rad);
184
+
185
+ // Step 5: Convert the goal heading to the local frame
186
+ double goal_heading_rad = (90.0 - goal_heading) * M_PI / 180.0 ;
187
+ double local_heading = goal_heading_rad - current_heading_rad;
188
+
189
+ // Normalize local heading to the range [-pi, pi]
190
+ while (local_heading > M_PI) local_heading -= 2 * M_PI;
191
+ while (local_heading < -M_PI) local_heading += 2 * M_PI;
192
+
193
+ // Step 6: Create the affine transformation for the local pose of the goal
194
+ Eigen::Affine2f transform = Eigen::Translation2f (local_x, local_y) *
195
+ Eigen::Rotation2Df (local_heading);
196
+
197
+ return transform;
101
198
}
102
199
200
+ } // namespace gps_util
201
+
103
202
#endif
0 commit comments