A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
geographic-positions.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2014 University of Washington
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Benjamin Cizdziel <ben.cizdziel@gmail.com>
18 */
19
21
22#include <ns3/angles.h>
23#include <ns3/log.h>
24
25#include <cmath>
26
27NS_LOG_COMPONENT_DEFINE("GeographicPositions");
28
29namespace
30{
31/**
32 * GRS80 and WGS84 sources
33 *
34 * Moritz, H. "Geodetic Reference System 1980." GEODETIC REFERENCE SYSTEM 1980.
35 * <https://web.archive.org/web/20170712034716/http://www.gfy.ku.dk/~iag/HB2000/part4/grs80_corr.htm>.
36 *
37 * "Department of Defense World Geodetic System 1984." National Imagery and
38 * Mapping Agency, 1 Jan. 2000.
39 * <https://web.archive.org/web/20200730231853/http://earth-info.nga.mil/GandG/publications/tr8350.2/wgs84fin.pdf>.
40 */
41
42/**
43 * \brief Lambda function for computing the curvature
44 */
45auto curvature = [](double e, double ph) { return sqrt(1 - e * e * sin(ph) * sin(ph)); };
46} // namespace
47
48namespace ns3
49{
50
51Vector
53 double longitude,
54 double altitude,
55 EarthSpheroidType sphType)
56{
58 double latitudeRadians = DegreesToRadians(latitude);
59 double longitudeRadians = DegreesToRadians(longitude);
60
61 // Retrieve radius, first eccentricity and flattening according to the specified Earth's model
62 auto [a, e, f] = GetRadiusEccentFlat(sphType);
63
64 double Rn = a / curvature(e, latitudeRadians); // radius of curvature
65 double x = (Rn + altitude) * cos(latitudeRadians) * cos(longitudeRadians);
66 double y = (Rn + altitude) * cos(latitudeRadians) * sin(longitudeRadians);
67 double z = ((1 - e * e) * Rn + altitude) * sin(latitudeRadians);
68 Vector cartesianCoordinates = Vector(x, y, z);
69 return cartesianCoordinates;
70}
71
72Vector
74{
75 NS_LOG_FUNCTION(pos << sphType);
76
77 // Retrieve radius, first eccentricity and flattening according to the specified Earth's model
78 auto [a, e, f] = GetRadiusEccentFlat(sphType);
79
80 Vector lla;
81 Vector tmp;
82 lla.y = atan2(pos.y, pos.x); // longitude (rad), in +/- pi
83
84 double e2 = e * e;
85 // sqrt (pos.x^2 + pos.y^2)
86 double p = CalculateDistance(pos, {0, 0, pos.z});
87 lla.x = atan2(pos.z, p * (1 - e2)); // init latitude (rad), in +/- pi
88
89 do
90 {
91 tmp = lla;
92 double N = a / sqrt(1 - e2 * sin(tmp.x) * sin(tmp.x));
93 double v = p / cos(tmp.x);
94 lla.z = v - N; // altitude
95 lla.x = atan2(pos.z, p * (1 - e2 * N / v));
96 }
97 // 1 m difference is approx 1 / 30 arc seconds = 9.26e-6 deg
98 while (fabs(lla.x - tmp.x) > DegreesToRadians(0.00000926));
99
100 lla.x = RadiansToDegrees(lla.x);
101 lla.y = RadiansToDegrees(lla.y);
102
103 // canonicalize (latitude) x in [-90, 90] and (longitude) y in [-180, 180)
104 if (lla.x > 90.0)
105 {
106 lla.x = 180 - lla.x;
107 lla.y += lla.y < 0 ? 180 : -180;
108 }
109 else if (lla.x < -90.0)
110 {
111 lla.x = -180 - lla.x;
112 lla.y += lla.y < 0 ? 180 : -180;
113 }
114 if (lla.y == 180.0)
115 {
116 lla.y = -180;
117 }
118
119 // make sure lat/lon in the right range to double check canonicalization
120 // and conversion routine
121 NS_ASSERT_MSG(-180.0 <= lla.y, "Conversion error: longitude too negative");
122 NS_ASSERT_MSG(180.0 > lla.y, "Conversion error: longitude too positive");
123 NS_ASSERT_MSG(-90.0 <= lla.x, "Conversion error: latitude too negative");
124 NS_ASSERT_MSG(90.0 >= lla.x, "Conversion error: latitude too positive");
125
126 return lla;
127}
128
129Vector
131 Vector refPoint,
132 EarthSpheroidType sphType)
133{
134 NS_LOG_FUNCTION(pos << sphType);
135
136 double phi = DegreesToRadians(pos.x);
137 double lambda = DegreesToRadians(pos.y);
138 double h = pos.z;
139 double phi0 = DegreesToRadians(refPoint.x);
140 double lambda0 = DegreesToRadians(refPoint.y);
141 double h0 = refPoint.z;
142 // Retrieve radius, first eccentricity and flattening according to the specified Earth's model
143 auto [a, e, f] = GetRadiusEccentFlat(sphType);
144
145 // the radius of curvature in the prime vertical at latitude
146 double v = a / curvature(e, phi);
147 // the radius of curvature in the prime vertical at latitude
148 // of the reference point
149 double v0 = a / curvature(e, phi0);
150
151 double U = (v + h) * cos(phi) * sin(lambda - lambda0);
152 double V = (v + h) * (sin(phi) * cos(phi0) - cos(phi) * sin(phi0) * cos(lambda - lambda0)) +
153 e * e * (v0 * sin(phi0) - v * sin(phi)) * cos(phi0);
154 double W = (v + h) * (sin(phi) * sin(phi0) + cos(phi) * cos(phi0) * cos(lambda - lambda0)) +
155 e * e * (v0 * sin(phi0) - v * sin(phi)) * sin(phi0) - (v0 + h0);
156
157 Vector topocentricCoordinates = Vector(U, V, W);
158 return topocentricCoordinates;
159}
160
161Vector
163 Vector refPoint,
164 EarthSpheroidType sphType)
165{
166 NS_LOG_FUNCTION(pos << sphType);
167
168 double U = pos.x;
169 double V = pos.y;
170 double W = pos.z;
171 double phi0 = DegreesToRadians(refPoint.x);
172 double lambda0 = DegreesToRadians(refPoint.y);
173 double h0 = refPoint.z;
174 // Retrieve radius, first eccentricity and flattening according to the specified Earth's model
175 auto [a, e, f] = GetRadiusEccentFlat(sphType);
176
177 // the radius of curvature in the prime vertical at latitude
178 // of the reference point
179 double v0 = a / curvature(e, phi0);
180
181 double X0 = (v0 + h0) * cos(phi0) * cos(lambda0);
182 double Y0 = (v0 + h0) * cos(phi0) * sin(lambda0);
183 double Z0 = ((1 - e * e) * v0 + h0) * sin(phi0);
184
185 double X = X0 - U * sin(lambda0) - V * sin(phi0) * cos(lambda0) + W * cos(phi0) * cos(lambda0);
186 double Y = Y0 + U * cos(lambda0) - V * sin(phi0) * sin(lambda0) + W * cos(phi0) * sin(lambda0);
187 double Z = Z0 + V * cos(phi0) + W * sin(phi0);
188
189 double epsilon = e * e / (1 - e * e);
190 double b = a * (1 - f);
191 double p = sqrt(X * X + Y * Y);
192 double q = atan2((Z * a), (p * b));
193
194 double phi = atan2((Z + epsilon * b * pow(sin(q), 3)), (p - e * e * a * pow(cos(q), 3)));
195 double lambda = atan2(Y, X);
196
197 double v = a / curvature(e, phi);
198 double h = (p / cos(phi)) - v;
199
200 phi = RadiansToDegrees(phi);
201 lambda = RadiansToDegrees(lambda);
202
203 Vector geographicCoordinates = Vector(phi, lambda, h);
204 return geographicCoordinates;
205}
206
207std::list<Vector>
209 double originLongitude,
210 double maxAltitude,
211 int numPoints,
212 double maxDistFromOrigin,
214{
216 // fixes divide by zero case and limits latitude bounds
217 if (originLatitude >= 90)
218 {
219 NS_LOG_WARN("origin latitude must be less than 90. setting to 89.999");
220 originLatitude = 89.999;
221 }
222 else if (originLatitude <= -90)
223 {
224 NS_LOG_WARN("origin latitude must be greater than -90. setting to -89.999");
225 originLatitude = -89.999;
226 }
227
228 // restricts maximum altitude from being less than zero (below earth's surface).
229 // sets maximum altitude equal to zero if parameter is set to be less than zero.
230 if (maxAltitude < 0)
231 {
232 NS_LOG_WARN("maximum altitude must be greater than or equal to 0. setting to 0");
233 maxAltitude = 0;
234 }
235
236 double originLatitudeRadians = DegreesToRadians(originLatitude);
237 double originLongitudeRadians = DegreesToRadians(originLongitude);
238 double originColatitude = (M_PI_2)-originLatitudeRadians;
239
240 // maximum alpha allowed (arc length formula)
241 double a = maxDistFromOrigin / EARTH_SPHERE_RADIUS;
242 if (a > M_PI)
243 {
244 // pi is largest alpha possible (polar angle from origin that
245 // points can be generated within)
246 a = M_PI;
247 }
248
249 std::list<Vector> generatedPoints;
250 for (int i = 0; i < numPoints; i++)
251 {
252 // random distance from North Pole (towards center of earth)
253 double d = uniRand->GetValue(0, EARTH_SPHERE_RADIUS - EARTH_SPHERE_RADIUS * cos(a));
254 // random angle in latitude slice (wrt Prime Meridian), radians
255 double phi = uniRand->GetValue(0, M_PI * 2);
256 // random angle from Center of Earth (wrt North Pole), radians
257 double alpha = acos((EARTH_SPHERE_RADIUS - d) / EARTH_SPHERE_RADIUS);
258
259 // shift coordinate system from North Pole referred to origin point referred
260 // reference: http://en.wikibooks.org/wiki/General_Astronomy/Coordinate_Systems
261 double theta = M_PI_2 - alpha; // angle of elevation of new point wrt
262 // origin point (latitude in coordinate
263 // system referred to origin point)
264 double randPointLatitude = asin(sin(theta) * cos(originColatitude) +
265 cos(theta) * sin(originColatitude) * sin(phi));
266 // declination
267 double intermedLong = asin((sin(randPointLatitude) * cos(originColatitude) - sin(theta)) /
268 (cos(randPointLatitude) * sin(originColatitude)));
269 // right ascension
270 intermedLong = intermedLong + M_PI_2; // shift to longitude 0
271
272 // flip / mirror point if it has phi in quadrant II or III (wasn't
273 // resolved correctly by arcsin) across longitude 0
274 if (phi > (M_PI_2) && phi <= (3 * M_PI_2))
275 {
276 intermedLong = -intermedLong;
277 }
278
279 // shift longitude to be referenced to origin
280 double randPointLongitude = intermedLong + originLongitudeRadians;
281
282 // random altitude above earth's surface
283 double randAltitude = uniRand->GetValue(0, maxAltitude);
284
285 // convert coordinates from geographic to cartesian
287 RadiansToDegrees(randPointLatitude),
288 RadiansToDegrees(randPointLongitude),
289 randAltitude,
290 SPHERE);
291
292 // add generated coordinate points to list
293 generatedPoints.push_back(pointPosition);
294 }
295 return generatedPoints;
296}
297
298std::tuple<double, double, double>
300{
301 double a; // radius, in meters
302 double e; // first eccentricity
303 double f; // first flattening
304
305 switch (type)
306 {
307 case SPHERE:
311 break;
312 case GRS80:
316 break;
317 case WGS84:
321 break;
322 default:
323 NS_FATAL_ERROR("The specified earth model is not supported!");
324 }
325
326 return std::make_tuple(a, e, f);
327}
328
329} // namespace ns3
static std::list< Vector > RandCartesianPointsAroundGeographicPoint(double originLatitude, double originLongitude, double maxAltitude, int numPoints, double maxDistFromOrigin, Ptr< UniformRandomVariable > uniRand)
Generates uniformly distributed random points (in ECEF Cartesian coordinates) within a given altitude...
static constexpr double EARTH_SPHERE_FLATTENING
Earth's flattening if modeled as a perfect sphere.
static constexpr double EARTH_SPHERE_ECCENTRICITY
Earth's eccentricity if modeled as a perfect sphere.
static constexpr double EARTH_SPHERE_RADIUS
Spheroid model to use for earth: perfect sphere (SPHERE), Geodetic Reference System 1980 (GRS80),...
EarthSpheroidType
The possible Earth spheroid models. .
static constexpr double EARTH_GRS80_FLATTENING
Earth's first flattening as defined by GRS80 https://en.wikipedia.org/wiki/Geodetic_Reference_System_...
static constexpr double EARTH_WGS84_ECCENTRICITY
Earth's first eccentricity as defined by https://en.wikipedia.org/wiki/World_Geodetic_System#WGS84.
static Vector TopocentricToGeographicCoordinates(Vector pos, Vector refPoint, EarthSpheroidType sphType)
Conversion from topocentric to geographic.
static Vector GeographicToCartesianCoordinates(double latitude, double longitude, double altitude, EarthSpheroidType sphType)
Converts earth geographic/geodetic coordinates (latitude and longitude in degrees) with a given altit...
static constexpr double EARTH_WGS84_FLATTENING
Earth's first flattening as defined by WGS84 https://en.wikipedia.org/wiki/World_Geodetic_System#WGS8...
static Vector GeographicToTopocentricCoordinates(Vector pos, Vector refPoint, EarthSpheroidType sphType)
Conversion from geographic to topocentric coordinates.
static Vector CartesianToGeographicCoordinates(Vector pos, EarthSpheroidType sphType)
Inverse of GeographicToCartesianCoordinates using [1].
static constexpr double EARTH_SEMIMAJOR_AXIS
<Earth's semi-major axis in meters as defined by both GRS80 and WGS84 https://en.wikipedia....
static std::tuple< double, double, double > GetRadiusEccentFlat(EarthSpheroidType type)
static constexpr double EARTH_GRS80_ECCENTRICITY
Earth's first eccentricity as defined by GRS80 https://en.wikipedia.org/wiki/Geodetic_Reference_Syste...
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:261
Every class exported by the ns3 library is enclosed in the ns3 namespace.
double DegreesToRadians(double degrees)
converts degrees to radians
Definition: angles.cc:39
double CalculateDistance(const Vector3D &a, const Vector3D &b)
Definition: vector.cc:109
double RadiansToDegrees(double radians)
converts radians to degrees
Definition: angles.cc:45