GeographicPositions class for coordinate conversion
authorBenjamin Cizdziel <ben.cizdziel@gmail.com>
Wed, 08 Apr 2015 19:42:20 -0700
changeset 11284 63c62abdd7ba
parent 11283 a6eb8e9ceb2c
child 11285 90aeba821efe
GeographicPositions class for coordinate conversion
src/mobility/model/geographic-positions.cc
src/mobility/model/geographic-positions.h
src/mobility/test/geo-to-cartesian-test.cc
src/mobility/test/rand-cart-around-geo-test.cc
src/mobility/wscript
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/mobility/model/geographic-positions.cc	Wed Apr 08 19:42:20 2015 -0700
@@ -0,0 +1,178 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Benjamin Cizdziel <ben.cizdziel@gmail.com>
+ */
+
+#include <ns3/log.h>
+#include <cmath>
+#include "geographic-positions.h"
+
+NS_LOG_COMPONENT_DEFINE ("GeographicPositions");
+
+namespace ns3 {
+
+// earth's radius in meters if modeled as a perfect sphere
+static const double EARTH_RADIUS = 6371e3; 
+
+/**
+ * GRS80 and WGS84 sources
+ * 
+ * Moritz, H. "Geodetic Reference System 1980." GEODETIC REFERENCE SYSTEM 1980. 
+ * <http://www.gfy.ku.dk/~iag/HB2000/part4/grs80_corr.htm>.
+ * 
+ * "Department of Defense World Geodetic System 1984." National Imagery and 
+ * Mapping Agency, 1 Jan. 2000. 
+ * <http://earth-info.nga.mil/GandG/publications/tr8350.2/wgs84fin.pdf>.
+ */
+
+// earth's semi-major axis in meters as defined by both GRS80 and WGS84
+static const double EARTH_SEMIMAJOR_AXIS = 6378137;
+
+// earth's first eccentricity as defined by GRS80
+static const double EARTH_GRS80_ECCENTRICITY = 0.0818191910428158;
+
+// earth's first eccentricity as defined by WGS84
+static const double EARTH_WGS84_ECCENTRICITY = 0.0818191908426215;
+
+Vector
+GeographicPositions::GeographicToCartesianCoordinates (double latitude, 
+                                                       double longitude, 
+                                                       double altitude,
+                                                       EarthSpheroidType sphType)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  double latitudeRadians = 0.01745329 * latitude;
+  double longitudeRadians = 0.01745329 * longitude;
+  double a; // semi-major axis of earth
+  double e; // first eccentricity of earth
+  if (sphType == SPHERE)
+    {
+      a = EARTH_RADIUS;
+      e = 0;
+    }
+  else if (sphType == GRS80)
+    {
+      a = EARTH_SEMIMAJOR_AXIS;
+      e = EARTH_GRS80_ECCENTRICITY;
+    }
+  else // if sphType == WGS84
+    {
+      a = EARTH_SEMIMAJOR_AXIS;
+      e = EARTH_WGS84_ECCENTRICITY;
+    }
+
+  double Rn = a / (sqrt (1 - pow (e, 2) * pow (sin (latitudeRadians), 2))); // radius of
+                                                                           // curvature
+  double x = (Rn + altitude) * cos (latitudeRadians) * cos (longitudeRadians);
+  double y = (Rn + altitude) * cos (latitudeRadians) * sin (longitudeRadians);
+  double z = ((1 - pow (e, 2)) * Rn + altitude) * sin (latitudeRadians);
+  Vector cartesianCoordinates = Vector (x, y, z);
+  return cartesianCoordinates;
+}
+
+std::list<Vector>
+GeographicPositions::RandCartesianPointsAroundGeographicPoint (double originLatitude, 
+                                                               double originLongitude, 
+                                                               double maxAltitude,
+                                                               int numPoints, 
+                                                               double maxDistFromOrigin,
+                                                               Ptr<UniformRandomVariable> uniRand)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  // fixes divide by zero case and limits latitude bounds
+  if (originLatitude >= 90)
+    {
+      NS_LOG_WARN ("origin latitude must be less than 90. setting to 89.999");
+      originLatitude = 89.999;
+    }
+  else if (originLatitude <= -90)
+    {
+      NS_LOG_WARN ("origin latitude must be greater than -90. setting to -89.999");
+      originLatitude = -89.999;
+    }
+
+  // restricts maximum altitude from being less than zero (below earth's surface).
+  // sets maximum altitude equal to zero if parameter is set to be less than zero.
+  if (maxAltitude < 0)
+    {
+      NS_LOG_WARN ("maximum altitude must be greater than or equal to 0. setting to 0");
+      maxAltitude = 0;
+    }
+
+  double originLatitudeRadians = originLatitude * (M_PI / 180);
+  double originLongitudeRadians = originLongitude * (M_PI / 180);
+  double originColatitude = (M_PI / 2) - originLatitudeRadians;
+
+  double a = maxDistFromOrigin / EARTH_RADIUS; // maximum alpha allowed 
+                                               // (arc length formula)
+  if (a > M_PI)
+    {
+      a = M_PI; // pi is largest alpha possible (polar angle from origin that 
+                // points can be generated within)
+    }
+  
+  std::list<Vector> generatedPoints;
+  for (int i = 0; i < numPoints; i++)
+    {
+      // random distance from North Pole (towards center of earth)
+      double d = uniRand->GetValue (0, EARTH_RADIUS - EARTH_RADIUS * cos (a)); 
+      // random angle in latitude slice (wrt Prime Meridian), radians
+      double phi = uniRand->GetValue (0, M_PI * 2); 
+      // random angle from Center of Earth (wrt North Pole), radians
+      double alpha = acos((EARTH_RADIUS - d) / EARTH_RADIUS); 
+
+      // shift coordinate system from North Pole referred to origin point referred
+      // reference: http://en.wikibooks.org/wiki/General_Astronomy/Coordinate_Systems
+      double theta = M_PI / 2 - alpha; // angle of elevation of new point wrt 
+                                       // origin point (latitude in coordinate 
+                                       // system referred to origin point)
+      double randPointLatitude = asin(sin(theta)*cos(originColatitude) + 
+                                 cos(theta)*sin(originColatitude)*sin(phi)); 
+                                 // declination
+      double intermedLong = asin((sin(randPointLatitude)*cos(originColatitude) - 
+                            sin(theta)) / (cos(randPointLatitude)*sin(originColatitude))); 
+                            // right ascension
+      intermedLong = intermedLong + M_PI / 2; // shift to longitude 0
+
+      //flip / mirror point if it has phi in quadrant II or III (wasn't 
+      //resolved correctly by arcsin) across longitude 0
+      if (phi > (M_PI / 2) && phi <= ((3 * M_PI) / 2))
+      intermedLong = -intermedLong;
+
+      // shift longitude to be referenced to origin
+      double randPointLongitude = intermedLong + originLongitudeRadians; 
+
+      // random altitude above earth's surface
+      double randAltitude = uniRand->GetValue (0, maxAltitude);
+
+      Vector pointPosition = GeographicPositions::GeographicToCartesianCoordinates 
+                             (randPointLatitude * (180/M_PI), 
+                              randPointLongitude * (180/M_PI),
+                              randAltitude,
+                              SPHERE);
+                              // convert coordinates 
+                              // from geographic to cartesian
+
+      generatedPoints.push_back (pointPosition); //add generated coordinate 
+                                                      //points to list
+    }
+  return generatedPoints;
+}
+
+} // namespace ns3
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/mobility/model/geographic-positions.h	Wed Apr 08 19:42:20 2015 -0700
@@ -0,0 +1,113 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Benjamin Cizdziel <ben.cizdziel@gmail.com>
+ */
+
+#include <ns3/vector.h>
+#include <ns3/random-variable-stream.h>
+
+#ifndef GEOGRAPHIC_POSITIONS_H
+#define GEOGRAPHIC_POSITIONS_H
+
+namespace ns3
+{
+
+/**
+ * \ingroup mobility
+ *
+ * Consists of methods dealing with Earth geographic coordinates and locations.
+ */
+class GeographicPositions
+{
+
+public:
+  /** 
+   * Spheroid model to use for earth: perfect sphere (SPHERE), Geodetic 
+   * Reference System 1980 (GRS80), or World Geodetic System 1984 (WGS84)
+   * 
+   * Moritz, H. "Geodetic Reference System 1980." GEODETIC REFERENCE SYSTEM 1980. 
+   * <http://www.gfy.ku.dk/~iag/HB2000/part4/grs80_corr.htm>.
+   * 
+   * "Department of Defense World Geodetic System 1984." National Imagery and 
+   * Mapping Agency, 1 Jan. 2000. 
+   * <http://earth-info.nga.mil/GandG/publications/tr8350.2/wgs84fin.pdf>.
+   */
+  enum EarthSpheroidType
+  {
+    SPHERE,
+    GRS80,
+    WGS84
+  };
+
+  /**
+   * Converts earth geographic/geodetic coordinates (latitude and longitude in 
+   * degrees) with a given altitude above earth's surface (in meters) to Earth 
+   * Centered Earth Fixed (ECEF) Cartesian coordinates (x, y, z in meters), 
+   * where origin (0, 0, 0) is the center of the earth.
+   *
+   * @param latitude earth-referenced latitude (in degrees) of the point
+   * @param longitude earth-referenced longitude (in degrees) of the point
+   * @param altitude height of the point (in meters) above earth's surface
+   * @param sphType earth spheroid model to use for conversion
+   *
+   * @return a vector containing the Cartesian coordinates (x, y, z referenced 
+   * in meters) of the point (origin (0, 0, 0) is center of earth)
+   */
+  static Vector GeographicToCartesianCoordinates (double latitude, 
+                                                  double longitude, 
+                                                  double altitude,
+                                                  EarthSpheroidType sphType);
+
+  /**
+   * Generates uniformly distributed random points (in ECEF Cartesian 
+   * coordinates) within a given altitude above earth's surface centered around 
+   * a given origin point (on earth's surface, in geographic/geodetic coordinates) 
+   * within a given distance radius (using arc length of earth's surface, not 
+   * pythagorean distance).
+   * Distance radius is measured as if all generated points are on earth's 
+   * surface (with altitude = 0).
+   * Assumes earth is a perfect sphere.
+   * 
+   * @param originLatitude origin point latitude in degrees
+   * @param originLongitude origin point longitude in degrees
+   * @param maxAltitude maximum altitude in meters above earth's surface with
+   * which random points can be generated
+   * @param numPoints number of points to generate
+   * @param maxDistFromOrigin max distance in meters from origin with which 
+   * random transmitters can be generated (all transmitters are less than or 
+   * equal to this distance from the origin, relative to points being on earth's 
+   * surface)
+   * @param uniRand pointer to the uniform random variable to use for random 
+   * location and altitude generation
+   *
+   * @return a list containing the vectors (x, y, z location referenced in 
+   * meters from origin at center of earth) of each point generated
+   */
+  static std::list<Vector> RandCartesianPointsAroundGeographicPoint (double originLatitude, 
+                                                                     double originLongitude,
+                                                                     double maxAltitude, 
+                                                                     int numPoints, 
+                                                                     double maxDistFromOrigin,
+                                                                     Ptr<UniformRandomVariable> uniRand);
+
+};
+
+} // namespace ns3
+
+#endif /* GEOGRAPHIC_POSITIONS_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/mobility/test/geo-to-cartesian-test.cc	Wed Apr 08 19:42:20 2015 -0700
@@ -0,0 +1,671 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Benjamin Cizdziel <ben.cizdziel@gmail.com>
+ */
+
+#include <ns3/test.h>
+#include <ns3/log.h>
+#include <ns3/geographic-positions.h>
+
+/**
+ * This test verifies the accuracy of the GeographicToCartesianCoordinates()
+ * method in the GeographicPositions class, which converts earth 
+ * geographic/geodetic coordinates to ECEF Cartesian coordinates. To do so, it 
+ * compares the values generated from the method to values generated from the 
+ * MATLAB function geodetic2ecef(), which is part of the MATLAB Mapping Toolbox, 
+ * using the built-in earth referenceSphere, GRS80 referenceEllipsoid, and WGS84 
+ * referenceEllipsoid in MATLAB. A description of the MATLAB function can be 
+ * found at this webpage: http://www.mathworks.com/help/map/ref/geodetic2ecef.html
+ * Values are compared using 216 test cases for each of the three earth spheroid
+ * models.
+ */
+NS_LOG_COMPONENT_DEFINE ("GeoToCartesianTest");
+
+using namespace ns3;
+
+// 10 meter tolerance for testing, which is very small with respect to ECEF
+// Cartesian coordinates on or above earth's surface
+const double TOLERANCE = 10;
+
+const double XSPHERE_MATLAB[216] = {6371000, 1968747.27116279, -5154247.27116279, 
+-5154247.27116279, 1968747.27116279, 6371000, 1968747.27116279, 608376.364418605, 
+-1592750, -1592750.00000000, 608376.364418605, 1968747.27116279, 
+-5154247.27116279, -1592750, 4169873.63558139, 4169873.63558139, 
+-1592750.00000000, -5154247.27116279, -5154247.27116279, -1592750.00000000, 
+4169873.63558139, 4169873.63558140, -1592750.00000000, -5154247.27116279, 
+1968747.27116279, 608376.364418605, -1592750.00000000, -1592750.00000000, 
+608376.364418604, 1968747.27116279, 6371000, 1968747.27116279, -5154247.27116279, 
+-5154247.27116279, 1968747.27116279, 6371000, 6371200, 1968809.07456167, 
+-5154409.07456167, -5154409.07456167, 1968809.07456166, 6371200, 1968809.07456167, 
+608395.462719168, -1592800, -1592800.00000000, 608395.462719167, 1968809.07456167, 
+-5154409.07456167, -1592800, 4170004.53728083, 4170004.53728083, 
+-1592800.00000000, -5154409.07456167, -5154409.07456167, -1592800, 
+4170004.53728083, 4170004.53728083, -1592800.00000000, -5154409.07456167, 
+1968809.07456166, 608395.462719167, -1592800.00000000, -1592800.00000000, 
+608395.462719167, 1968809.07456166, 6371200, 1968809.07456167, -5154409.07456167, 
+-5154409.07456167, 1968809.07456166, 6371200, 6371400, 1968870.87796054, 
+-5154570.87796054, -5154570.87796054, 1968870.87796054, 6371400, 1968870.87796054, 
+608414.561019730, -1592850, -1592850.00000000, 608414.561019730, 1968870.87796054, 
+-5154570.87796054, -1592850, 4170135.43898027, 4170135.43898027, 
+-1592850.00000000, -5154570.87796054, -5154570.87796054, -1592850.00000000, 
+4170135.43898027, 4170135.43898027, -1592850.00000000, -5154570.87796054, 
+1968870.87796054, 608414.561019730, -1592850.00000000, -1592850.00000000, 
+608414.561019729, 1968870.87796054, 6371400, 1968870.87796054, -5154570.87796054, 
+-5154570.87796054, 1968870.87796054, 6371400, 6371600, 1968932.68135942, 
+-5154732.68135941, -5154732.68135942, 1968932.68135941, 6371600, 
+1968932.68135942, 608433.659320293, -1592900, -1592900.00000000, 608433.659320292, 
+1968932.68135942, -5154732.68135941, -1592900.00000000, 4170266.34067971, 
+4170266.34067971, -1592900.00000000, -5154732.68135941, -5154732.68135942, 
+-1592900.00000000, 4170266.34067971, 4170266.34067971, -1592900.00000000, 
+-5154732.68135942, 1968932.68135941, 608433.659320292, -1592900.00000000, 
+-1592900.00000000, 608433.659320292, 1968932.68135941, 6371600, 1968932.68135942, 
+-5154732.68135941, -5154732.68135942, 1968932.68135941, 6371600, 6371800, 
+1968994.48475829, -5154894.48475829, -5154894.48475829, 1968994.48475829, 
+6371800, 1968994.48475829, 608452.757620855, -1592950, -1592950.00000000, 
+608452.757620855, 1968994.48475829, -5154894.48475829, -1592950, 4170397.24237914, 
+4170397.24237914, -1592950.00000000, -5154894.48475829, -5154894.48475829, 
+-1592950.00000000, 4170397.24237914, 4170397.24237915, -1592950.00000000, 
+-5154894.48475829, 1968994.48475829, 608452.757620855, -1592950.00000000, 
+-1592950.00000000, 608452.757620854, 1968994.48475829, 6371800, 1968994.48475829, 
+-5154894.48475829, -5154894.48475829, 1968994.48475829, 6371800, 6372000, 
+1969056.28815717, -5155056.28815716, -5155056.28815717, 1969056.28815716, 
+6372000, 1969056.28815717, 608471.855921418, -1593000, -1593000, 608471.855921417, 
+1969056.28815717, -5155056.28815716, -1593000.00000000, 4170528.14407858, 
+4170528.14407858, -1593000.00000000, -5155056.28815716, -5155056.28815717, 
+-1593000.00000000, 4170528.14407858, 4170528.14407858, -1593000.00000000, 
+-5155056.28815717, 1969056.28815716, 608471.855921417, -1593000.00000000, 
+-1593000.00000000, 608471.855921417, 1969056.28815716, 6372000, 1969056.28815717, 
+-5155056.28815716, -5155056.28815717, 1969056.28815716, 6372000};
+
+const double YSPHERE_MATLAB[216] = {0, 6059181.06531642, 3744779.84235535, 
+-3744779.84235535, -6059181.06531642, -1.56044495147356e-09, 0, 1872389.92117767, 
+1157200.61148054, -1157200.61148054, -1872389.92117767, -4.82204008791920e-10, 0, 
+-4901980.45383588, -3029590.53265821, 3029590.53265821, 4901980.45383588, 
+1.26242648452870e-09, 0, -4901980.45383589, -3029590.53265821, 3029590.53265821, 
+4901980.45383589, 1.26242648452870e-09, 0, 1872389.92117767, 1157200.61148054, 
+-1157200.61148054, -1872389.92117767, -4.82204008791919e-10, 0, 6059181.06531642, 
+3744779.84235535, -3744779.84235535, -6059181.06531642, -1.56044495147356e-09, 0, 
+6059371.27661968, 3744897.39940581, -3744897.39940580, -6059371.27661968, 
+-1.56049393734552e-09, 0, 1872448.69970290, 1157236.93860694, -1157236.93860694, 
+-1872448.69970290, -4.82219146258841e-10, 0, -4902134.33801274, -3029685.63830984, 
+3029685.63830984, 4902134.33801274, 1.26246611493160e-09, 0, -4902134.33801274, 
+-3029685.63830984, 3029685.63830984, 4902134.33801274, 1.26246611493160e-09, 0, 
+1872448.69970290, 1157236.93860694, -1157236.93860694, -1872448.69970290, 
+-4.82219146258841e-10, 0, 6059371.27661968, 3744897.39940581, -3744897.39940580, 
+-6059371.27661968, -1.56049393734552e-09, 0, 6059561.48792294, 3745014.95645626, 
+-3745014.95645626, -6059561.48792294, -1.56054292321749e-09, 0, 1872507.47822813, 
+1157273.26573334, -1157273.26573334, -1872507.47822813, -4.82234283725763e-10, 0, 
+-4902288.22218960, -3029780.74396147, 3029780.74396147, 4902288.22218960, 
+1.26250574533451e-09, 0, -4902288.22218960, -3029780.74396147, 3029780.74396147, 
+4902288.22218960, 1.26250574533451e-09, 0, 1872507.47822813, 1157273.26573334, 
+-1157273.26573334, -1872507.47822813, -4.82234283725763e-10, 0, 6059561.48792294, 
+3745014.95645626, -3745014.95645626, -6059561.48792294, -1.56054292321749e-09, 0, 
+6059751.69922620, 3745132.51350672, -3745132.51350672, -6059751.69922620, 
+-1.56059190908946e-09, 0, 1872566.25675336, 1157309.59285974, -1157309.59285974, 
+-1872566.25675336, -4.82249421192685e-10, 0, -4902442.10636646, -3029875.84961310, 
+3029875.84961310, 4902442.10636646, 1.26254537573741e-09, 0, -4902442.10636646, 
+-3029875.84961310, 3029875.84961310, 4902442.10636646, 1.26254537573741e-09, 0, 
+1872566.25675336, 1157309.59285974, -1157309.59285974, -1872566.25675336, 
+-4.82249421192684e-10, 0, 6059751.69922620, 3745132.51350672, -3745132.51350672, 
+-6059751.69922620, -1.56059190908946e-09, 0, 6059941.91052946, 3745250.07055718, 
+-3745250.07055718, -6059941.91052946, -1.56064089496142e-09, 0, 1872625.03527859, 
+1157345.91998614, -1157345.91998614, -1872625.03527859, -4.82264558659606e-10, 0, 
+-4902595.99054332, -3029970.95526473, 3029970.95526473, 4902595.99054332, 
+1.26258500614032e-09, 0, -4902595.99054332, -3029970.95526473, 3029970.95526473, 
+4902595.99054332, 1.26258500614032e-09, 0, 1872625.03527859, 1157345.91998614, 
+-1157345.91998614, -1872625.03527859, -4.82264558659606e-10, 0, 6059941.91052946, 
+3745250.07055718, -3745250.07055718, -6059941.91052946, -1.56064089496142e-09, 0, 
+6060132.12183272, 3745367.62760764, -3745367.62760764, -6060132.12183272, 
+-1.56068988083339e-09, 0, 1872683.81380382, 1157382.24711254, -1157382.24711254, 
+-1872683.81380382, -4.82279696126528e-10, 0, -4902749.87472018, -3030066.06091636, 
+3030066.06091636, 4902749.87472018, 1.26262463654322e-09, 0, -4902749.87472018, 
+-3030066.06091636, 3030066.06091636, 4902749.87472018, 1.26262463654322e-09, 0, 
+1872683.81380382, 1157382.24711254, -1157382.24711254, -1872683.81380382, 
+-4.82279696126528e-10, 0, 6060132.12183272, 3745367.62760764, -3745367.62760764, 
+-6060132.12183272, -1.56068988083339e-09};
+
+const double ZSPHERE_MATLAB[216] = {0, 0, 0, 0, 0, 0, 6059181.06531642, 
+6059181.06531642, 6059181.06531642, 6059181.06531642, 6059181.06531642, 
+6059181.06531642, 3744779.84235535, 3744779.84235535, 3744779.84235535, 
+3744779.84235535, 3744779.84235535, 3744779.84235535, -3744779.84235535, 
+-3744779.84235535, -3744779.84235535, -3744779.84235535, -3744779.84235535, 
+-3744779.84235535, -6059181.06531642, -6059181.06531642, -6059181.06531642, 
+-6059181.06531642, -6059181.06531642, -6059181.06531642, -1.56044495147356e-09, 
+-1.56044495147356e-09, -1.56044495147356e-09, -1.56044495147356e-09, 
+-1.56044495147356e-09, -1.56044495147356e-09, 0, 0, 0, 0, 0, 0, 6059371.27661968, 
+6059371.27661968, 6059371.27661968, 6059371.27661968, 6059371.27661968, 
+6059371.27661968, 3744897.39940581, 3744897.39940581, 3744897.39940581, 
+3744897.39940581, 3744897.39940581, 3744897.39940581, -3744897.39940580, 
+-3744897.39940580, -3744897.39940580, -3744897.39940580, -3744897.39940580, 
+-3744897.39940580, -6059371.27661968, -6059371.27661968, -6059371.27661968, 
+-6059371.27661968, -6059371.27661968, -6059371.27661968, -1.56049393734552e-09, 
+-1.56049393734552e-09, -1.56049393734552e-09, -1.56049393734552e-09, 
+-1.56049393734552e-09, -1.56049393734552e-09, 0, 0, 0, 0, 0, 0, 6059561.48792294, 
+6059561.48792294, 6059561.48792294, 6059561.48792294, 6059561.48792294, 
+6059561.48792294, 3745014.95645626, 3745014.95645626, 3745014.95645626, 
+3745014.95645626, 3745014.95645626, 3745014.95645626, -3745014.95645626, 
+-3745014.95645626, -3745014.95645626, -3745014.95645626, -3745014.95645626, 
+-3745014.95645626, -6059561.48792294, -6059561.48792294, -6059561.48792294, 
+-6059561.48792294, -6059561.48792294, -6059561.48792294, -1.56054292321749e-09, 
+-1.56054292321749e-09, -1.56054292321749e-09, -1.56054292321749e-09, 
+-1.56054292321749e-09, -1.56054292321749e-09, 0, 0, 0, 0, 0, 0, 6059751.69922620, 
+6059751.69922620, 6059751.69922620, 6059751.69922620, 6059751.69922620, 
+6059751.69922620, 3745132.51350672, 3745132.51350672, 3745132.51350672, 
+3745132.51350672, 3745132.51350672, 3745132.51350672, -3745132.51350672, 
+-3745132.51350672, -3745132.51350672, -3745132.51350672, -3745132.51350672, 
+-3745132.51350672, -6059751.69922620, -6059751.69922620, -6059751.69922620, 
+-6059751.69922620, -6059751.69922620, -6059751.69922620, -1.56059190908946e-09, 
+-1.56059190908946e-09, -1.56059190908946e-09, -1.56059190908946e-09, 
+-1.56059190908946e-09, -1.56059190908946e-09, 0, 0, 0, 0, 0, 0, 6059941.91052946, 
+6059941.91052946, 6059941.91052946, 6059941.91052946, 6059941.91052946, 
+6059941.91052946, 3745250.07055718, 3745250.07055718, 3745250.07055718, 
+3745250.07055718, 3745250.07055718, 3745250.07055718, -3745250.07055718, 
+-3745250.07055718, -3745250.07055718, -3745250.07055718, -3745250.07055718, 
+-3745250.07055718, -6059941.91052946, -6059941.91052946, -6059941.91052946, 
+-6059941.91052946, -6059941.91052946, -6059941.91052946, -1.56064089496142e-09, 
+-1.56064089496142e-09, -1.56064089496142e-09, -1.56064089496142e-09, 
+-1.56064089496142e-09, -1.56064089496142e-09, 0, 0, 0, 0, 0, 0, 6060132.12183272, 
+6060132.12183272, 6060132.12183272, 6060132.12183272, 6060132.12183272, 
+6060132.12183272, 3745367.62760764, 3745367.62760764, 3745367.62760764, 
+3745367.62760764, 3745367.62760764, 3745367.62760764, -3745367.62760764, 
+-3745367.62760764, -3745367.62760764, -3745367.62760764, -3745367.62760764, 
+-3745367.62760764, -6060132.12183272, -6060132.12183272, -6060132.12183272, 
+-6060132.12183272, -6060132.12183272, -6060132.12183272, -1.56068988083339e-09, 
+-1.56068988083339e-09, -1.56068988083339e-09, -1.56068988083339e-09, 
+-1.56068988083339e-09, -1.56068988083339e-09};
+
+const double XGRS80_MATLAB[216] = {6378137, 1970952.72545164, -5160021.22545164, 
+-5160021.22545164, 1970952.72545164, 6378137, 1976947.14312992, 610910.264208145, 
+-1599383.83577310, -1599383.83577310, 610910.264208145, 1976947.14312992, 
+-5165998.77753988, -1596381.41518003, 4179380.80394997, 4179380.80394997, 
+-1596381.41518003, -5165998.77753988, -5165998.77753988, -1596381.41518003, 
+4179380.80394997, 4179380.80394997, -1596381.41518003, -5165998.77753988, 
+1976947.14312991, 610910.264208145, -1599383.83577310, -1599383.83577310, 
+610910.264208145, 1976947.14312991, 6378137, 1970952.72545164, -5160021.22545164, 
+-5160021.22545164, 1970952.72545164, 6378137, 6378337, 1971014.52885052, 
+-5160183.02885052, -5160183.02885052, 1971014.52885052, 6378337, 1977008.94652879, 
+610929.362508708, -1599433.83577310, -1599433.83577310, 610929.362508708, 
+1977008.94652879, -5166160.58093876, -1596431.41518003, 4179511.70564940, 
+4179511.70564940, -1596431.41518003, -5166160.58093876, -5166160.58093876, 
+-1596431.41518003, 4179511.70564940, 4179511.70564940, -1596431.41518003, 
+-5166160.58093876, 1977008.94652879, 610929.362508708, -1599433.83577310, 
+-1599433.83577310, 610929.362508707, 1977008.94652879, 6378337, 1971014.52885052, 
+-5160183.02885052, -5160183.02885052, 1971014.52885052, 6378337, 6378537, 
+1971076.33224939, -5160344.83224939, -5160344.83224940, 1971076.33224939, 
+6378537, 1977070.74992767, 610948.460809271, -1599483.83577310, -1599483.83577310, 
+610948.460809270, 1977070.74992767, -5166322.38433763, -1596481.41518003, 
+4179642.60734884, 4179642.60734884, -1596481.41518003, -5166322.38433763, 
+-5166322.38433763, -1596481.41518003, 4179642.60734884, 4179642.60734884, 
+-1596481.41518003, -5166322.38433763, 1977070.74992766, 610948.460809270, 
+-1599483.83577310, -1599483.83577310, 610948.460809270, 1977070.74992766, 
+6378537, 1971076.33224939, -5160344.83224939, -5160344.83224940, 1971076.33224939, 
+6378537, 6378737, 1971138.13564827, -5160506.63564827, -5160506.63564827, 
+1971138.13564827, 6378737, 1977132.55332654, 610967.559109833, -1599533.83577310, 
+-1599533.83577310, 610967.559109833, 1977132.55332654, -5166484.18773651, 
+-1596531.41518003, 4179773.50904828, 4179773.50904828, -1596531.41518003, 
+-5166484.18773651, -5166484.18773651, -1596531.41518003, 4179773.50904828, 
+4179773.50904828, -1596531.41518003, -5166484.18773651, 1977132.55332654, 
+610967.559109833, -1599533.83577310, -1599533.83577310, 610967.559109832, 
+1977132.55332654, 6378737, 1971138.13564827, -5160506.63564827, -5160506.63564827, 
+1971138.13564827, 6378737, 6378937, 1971199.93904714, -5160668.43904714, 
+-5160668.43904714, 1971199.93904714, 6378937, 1977194.35672542, 610986.657410395, 
+-1599583.83577310, -1599583.83577310, 610986.657410395, 1977194.35672542, 
+-5166645.99113538, -1596581.41518003, 4179904.41074772, 4179904.41074772, 
+-1596581.41518003, -5166645.99113538, -5166645.99113538, -1596581.41518003, 
+4179904.41074772, 4179904.41074772, -1596581.41518003, -5166645.99113538, 
+1977194.35672541, 610986.657410395, -1599583.83577310, -1599583.83577310, 
+610986.657410395, 1977194.35672541, 6378937, 1971199.93904714, -5160668.43904714, 
+-5160668.43904714, 1971199.93904714, 6378937, 6379137, 1971261.74244602, 
+-5160830.24244602, -5160830.24244602, 1971261.74244602, 6379137, 1977256.16012429, 
+611005.755710958, -1599633.83577310, -1599633.83577310, 611005.755710958, 
+1977256.16012429, -5166807.79453425, -1596631.41518003, 4180035.31244715, 
+4180035.31244715, -1596631.41518003, -5166807.79453425, -5166807.79453426, 
+-1596631.41518003, 4180035.31244715, 4180035.31244715, -1596631.41518003, 
+-5166807.79453426, 1977256.16012429, 611005.755710958, -1599633.83577310, 
+-1599633.83577310, 611005.755710957, 1977256.16012429, 6379137, 1971261.74244602, 
+-5160830.24244602, -5160830.24244602, 1971261.74244602, 6379137};
+
+const double YGRS80_MATLAB[216] = {0, 6065968.75567322, 3748974.86570096, 
+-3748974.86570096, -6065968.75567322, -1.56219301231466e-09, 0, 1880188.46284479, 
+1162020.37529350, -1162020.37529350, -1880188.46284479, -4.84212398183511e-10, 0, 
+-4913156.80055210, -3036497.89479889, 3036497.89479889, 4913156.80055210, 
+1.26530477346267e-09, 0, -4913156.80055210, -3036497.89479889, 3036497.89479889, 
+4913156.80055210, 1.26530477346267e-09, 0, 1880188.46284479, 1162020.37529350, 
+-1162020.37529350, -1880188.46284479, -4.84212398183511e-10, 0, 6065968.75567322, 
+3748974.86570096, -3748974.86570096, -6065968.75567322, -1.56219301231466e-09, 0, 
+6066158.96697648, 3749092.42275142, -3749092.42275142, -6066158.96697648, 
+-1.56224199818663e-09, 0, 1880247.24137002, 1162056.70241990, -1162056.70241990, 
+-1880247.24137002, -4.84227535650433e-10, 0, -4913310.68472896, -3036593.00045052, 
+3036593.00045051, 4913310.68472896, 1.26534440386558e-09, 0, -4913310.68472896, 
+-3036593.00045052, 3036593.00045051, 4913310.68472896, 1.26534440386558e-09, 0, 
+1880247.24137002, 1162056.70241990, -1162056.70241990, -1880247.24137002, 
+-4.84227535650433e-10, 0, 6066158.96697648, 3749092.42275142, -3749092.42275142, 
+-6066158.96697648, -1.56224199818663e-09, 0, 6066349.17827974, 3749209.97980188, 
+-3749209.97980187, -6066349.17827974, -1.56229098405859e-09, 0, 1880306.01989525, 
+1162093.02954630, -1162093.02954630, -1880306.01989525, -4.84242673117355e-10, 0, 
+-4913464.56890582, -3036688.10610215, 3036688.10610214, 4913464.56890582, 
+1.26538403426848e-09, 0, -4913464.56890582, -3036688.10610215, 3036688.10610215, 
+4913464.56890582, 1.26538403426848e-09, 0, 1880306.01989525, 1162093.02954630, 
+-1162093.02954630, -1880306.01989525, -4.84242673117354e-10, 0, 6066349.17827974, 
+3749209.97980188, -3749209.97980187, -6066349.17827974, -1.56229098405859e-09, 0, 
+6066539.38958300, 3749327.53685233, -3749327.53685233, -6066539.38958300, 
+-1.56233996993056e-09, 0, 1880364.79842048, 1162129.35667270, -1162129.35667270, 
+-1880364.79842048, -4.84257810584276e-10, 0, -4913618.45308268, -3036783.21175378, 
+3036783.21175377, 4913618.45308268, 1.26542366467138e-09, 0, -4913618.45308268, 
+-3036783.21175378, 3036783.21175377, 4913618.45308268, 1.26542366467138e-09, 0, 
+1880364.79842048, 1162129.35667270, -1162129.35667270, -1880364.79842048, 
+-4.84257810584276e-10, 0, 6066539.38958300, 3749327.53685233, -3749327.53685233, 
+-6066539.38958300, -1.56233996993056e-09, 0, 6066729.60088626, 3749445.09390279, 
+-3749445.09390279, -6066729.60088626, -1.56238895580252e-09, 0, 1880423.57694571, 
+1162165.68379910, -1162165.68379910, -1880423.57694571, -4.84272948051198e-10, 0, 
+-4913772.33725954, -3036878.31740540, 3036878.31740540, 4913772.33725954, 
+1.26546329507429e-09, 0, -4913772.33725954, -3036878.31740541, 3036878.31740540, 
+4913772.33725954, 1.26546329507429e-09, 0, 1880423.57694571, 1162165.68379910, 
+-1162165.68379910, -1880423.57694571, -4.84272948051198e-10, 0, 6066729.60088626, 
+3749445.09390279, -3749445.09390279, -6066729.60088626, -1.56238895580252e-09, 0, 
+6066919.81218952, 3749562.65095325, -3749562.65095325, -6066919.81218952, 
+-1.56243794167449e-09, 0, 1880482.35547094, 1162202.01092550, -1162202.01092550, 
+-1880482.35547094, -4.84288085518120e-10, 0, -4913926.22143639, -3036973.42305703, 
+3036973.42305703, 4913926.22143639, 1.26550292547719e-09, 0, -4913926.22143639, 
+-3036973.42305703, 3036973.42305703, 4913926.22143639, 1.26550292547719e-09, 0, 
+1880482.35547094, 1162202.01092550, -1162202.01092550, -1880482.35547094, 
+-4.84288085518119e-10, 0, 6066919.81218952, 3749562.65095325, -3749562.65095325, 
+-6066919.81218952, -1.56243794167449e-09};
+
+const double ZGRS80_MATLAB[216] = {0, 0, 0, 0, 0, 0, 6043686.27213354, 
+6043686.27213354, 6043686.27213354, 6043686.27213354, 6043686.27213354, 
+6043686.27213354, 3728191.67572948, 3728191.67572948, 3728191.67572948, 
+3728191.67572948, 3728191.67572948, 3728191.67572948, -3728191.67572948, 
+-3728191.67572948, -3728191.67572948, -3728191.67572948, -3728191.67572948, 
+-3728191.67572948, -6043686.27213354, -6043686.27213354, -6043686.27213354, 
+-6043686.27213354, -6043686.27213354, -6043686.27213354, -1.55173509862111e-09, 
+-1.55173509862111e-09, -1.55173509862111e-09, -1.55173509862111e-09, 
+-1.55173509862111e-09, -1.55173509862111e-09, 0, 0, 0, 0, 0, 0, 6043876.48343680, 
+6043876.48343680, 6043876.48343680, 6043876.48343680, 6043876.48343680, 
+6043876.48343680, 3728309.23277994, 3728309.23277994, 3728309.23277994, 
+3728309.23277994, 3728309.23277994, 3728309.23277994, -3728309.23277994, 
+-3728309.23277994, -3728309.23277994, -3728309.23277994, -3728309.23277994, 
+-3728309.23277994, -6043876.48343680, -6043876.48343680, -6043876.48343680, 
+-6043876.48343680, -6043876.48343680, -6043876.48343680, -1.55178408449307e-09, 
+-1.55178408449307e-09, -1.55178408449307e-09, -1.55178408449307e-09, 
+-1.55178408449307e-09, -1.55178408449307e-09, 0, 0, 0, 0, 0, 0, 6044066.69474006, 
+6044066.69474006, 6044066.69474006, 6044066.69474006, 6044066.69474006, 
+6044066.69474006, 3728426.78983040, 3728426.78983040, 3728426.78983040, 
+3728426.78983040, 3728426.78983040, 3728426.78983040, -3728426.78983040, 
+-3728426.78983040, -3728426.78983040, -3728426.78983040, -3728426.78983040, 
+-3728426.78983040, -6044066.69474006, -6044066.69474006, -6044066.69474006, 
+-6044066.69474006, -6044066.69474006, -6044066.69474006, -1.55183307036504e-09, 
+-1.55183307036504e-09, -1.55183307036504e-09, -1.55183307036504e-09, 
+-1.55183307036504e-09, -1.55183307036504e-09, 0, 0, 0, 0, 0, 0, 6044256.90604332, 
+6044256.90604332, 6044256.90604332, 6044256.90604332, 6044256.90604332, 
+6044256.90604332, 3728544.34688086, 3728544.34688086, 3728544.34688086, 
+3728544.34688086, 3728544.34688086, 3728544.34688086, -3728544.34688086, 
+-3728544.34688086, -3728544.34688086, -3728544.34688086, -3728544.34688086, 
+-3728544.34688086, -6044256.90604332, -6044256.90604332, -6044256.90604332, 
+-6044256.90604332, -6044256.90604332, -6044256.90604332, -1.55188205623700e-09, 
+-1.55188205623700e-09, -1.55188205623700e-09, -1.55188205623700e-09, 
+-1.55188205623700e-09, -1.55188205623700e-09, 0, 0, 0, 0, 0, 0, 6044447.11734658, 
+6044447.11734658, 6044447.11734658, 6044447.11734658, 6044447.11734658, 
+6044447.11734658, 3728661.90393132, 3728661.90393132, 3728661.90393132, 
+3728661.90393132, 3728661.90393132, 3728661.90393132, -3728661.90393132, 
+-3728661.90393132, -3728661.90393132, -3728661.90393132, -3728661.90393132, 
+-3728661.90393132, -6044447.11734658, -6044447.11734658, -6044447.11734658, 
+-6044447.11734658, -6044447.11734658, -6044447.11734658, -1.55193104210897e-09, 
+-1.55193104210897e-09, -1.55193104210897e-09, -1.55193104210897e-09, 
+-1.55193104210897e-09, -1.55193104210897e-09, 0, 0, 0, 0, 0, 0, 6044637.32864983, 
+6044637.32864983, 6044637.32864983, 6044637.32864983, 6044637.32864983, 
+6044637.32864983, 3728779.46098178, 3728779.46098178, 3728779.46098178, 
+3728779.46098178, 3728779.46098178, 3728779.46098178, -3728779.46098177, 
+-3728779.46098177, -3728779.46098177, -3728779.46098177, -3728779.46098177, 
+-3728779.46098177, -6044637.32864983, -6044637.32864983, -6044637.32864983, 
+-6044637.32864983, -6044637.32864983, -6044637.32864983, -1.55198002798094e-09, 
+-1.55198002798094e-09, -1.55198002798094e-09, -1.55198002798094e-09, 
+-1.55198002798094e-09, -1.55198002798094e-09};
+
+const double XWGS84_MATLAB[216] = {6378137, 1970952.72545164, -5160021.22545164, 
+-5160021.22545164, 1970952.72545164, 6378137, 1976947.14310045, 610910.264199039, 
+-1599383.83574926, -1599383.83574926, 610910.264199039, 1976947.14310045, 
+-5165998.77751058, -1596381.41517097, 4179380.80392626, 4179380.80392626, 
+-1596381.41517097, -5165998.77751058, -5165998.77751058, -1596381.41517097, 
+4179380.80392626, 4179380.80392626, -1596381.41517097, -5165998.77751058, 
+1976947.14310045, 610910.264199039, -1599383.83574926, -1599383.83574926, 
+610910.264199039, 1976947.14310045, 6378137, 1970952.72545164, -5160021.22545164, 
+-5160021.22545164, 1970952.72545164, 6378137, 6378337, 1971014.52885052, 
+-5160183.02885052, -5160183.02885052, 1971014.52885052, 6378337, 1977008.94649932, 
+610929.362499602, -1599433.83574926, -1599433.83574926, 610929.362499601, 
+1977008.94649932, -5166160.58090945, -1596431.41517097, 4179511.70562570, 
+4179511.70562570, -1596431.41517097, -5166160.58090945, -5166160.58090945, 
+-1596431.41517097, 4179511.70562570, 4179511.70562570, -1596431.41517097, 
+-5166160.58090945, 1977008.94649932, 610929.362499601, -1599433.83574926, 
+-1599433.83574926, 610929.362499601, 1977008.94649932, 6378337, 1971014.52885052, 
+-5160183.02885052, -5160183.02885052, 1971014.52885052, 6378337, 6378537, 
+1971076.33224939, -5160344.83224939, -5160344.83224940, 1971076.33224939, 
+6378537, 1977070.74989820, 610948.460800164, -1599483.83574926, -1599483.83574926, 
+610948.460800164, 1977070.74989820, -5166322.38430833, -1596481.41517097, 
+4179642.60732513, 4179642.60732514, -1596481.41517097, -5166322.38430833, 
+-5166322.38430833, -1596481.41517097, 4179642.60732514, 4179642.60732514, 
+-1596481.41517097, -5166322.38430833, 1977070.74989820, 610948.460800164, 
+-1599483.83574926, -1599483.83574926, 610948.460800163, 1977070.74989820, 
+6378537, 1971076.33224939, -5160344.83224939, -5160344.83224940, 1971076.33224939, 
+6378537, 6378737, 1971138.13564827, -5160506.63564827, -5160506.63564827, 
+1971138.13564827, 6378737, 1977132.55329707, 610967.559100727, -1599533.83574926, 
+-1599533.83574926, 610967.559100726, 1977132.55329707, -5166484.18770720, 
+-1596531.41517097, 4179773.50902457, 4179773.50902457, -1596531.41517097, 
+-5166484.18770720, -5166484.18770720, -1596531.41517097, 4179773.50902457, 
+4179773.50902457, -1596531.41517097, -5166484.18770720, 1977132.55329707, 
+610967.559100726, -1599533.83574926, -1599533.83574926, 610967.559100726, 
+1977132.55329707, 6378737, 1971138.13564827, -5160506.63564827, -5160506.63564827, 
+1971138.13564827, 6378737, 6378937, 1971199.93904714, -5160668.43904714, 
+-5160668.43904714, 1971199.93904714, 6378937, 1977194.35669595, 610986.657401289, 
+-1599583.83574926, -1599583.83574926, 610986.657401289, 1977194.35669595, 
+-5166645.99110608, -1596581.41517097, 4179904.41072401, 4179904.41072401, 
+-1596581.41517097, -5166645.99110608, -5166645.99110608, -1596581.41517097, 
+4179904.41072401, 4179904.41072401, -1596581.41517097, -5166645.99110608, 
+1977194.35669595, 610986.657401289, -1599583.83574926, -1599583.83574926, 
+610986.657401288, 1977194.35669595, 6378937, 1971199.93904714, -5160668.43904714, 
+-5160668.43904714, 1971199.93904714, 6378937, 6379137, 1971261.74244602, 
+-5160830.24244602, -5160830.24244602, 1971261.74244602, 6379137, 1977256.16009482, 
+611005.755701852, -1599633.83574926, -1599633.83574926, 611005.755701851, 
+1977256.16009482, -5166807.79450495, -1596631.41517097, 4180035.31242345, 
+4180035.31242345, -1596631.41517097, -5166807.79450495, -5166807.79450495, 
+-1596631.41517097, 4180035.31242345, 4180035.31242345, -1596631.41517097, 
+-5166807.79450495, 1977256.16009482, 611005.755701851, -1599633.83574926, 
+-1599633.83574926, 611005.755701851, 1977256.16009482, 6379137, 1971261.74244602, 
+-5160830.24244602, -5160830.24244602, 1971261.74244602, 6379137};
+
+const double YWGS84_MATLAB[216] = {0, 6065968.75567322, 3748974.86570096, 
+-3748974.86570096, -6065968.75567322, -1.56219301231466e-09, 0, 1880188.46281677, 
+1162020.37527618, -1162020.37527618, -1880188.46281677, -4.84212398176294e-10, 0, 
+-4913156.80052423, -3036497.89478166, 3036497.89478166, 4913156.80052423, 
+1.26530477345549e-09, 0, -4913156.80052423, -3036497.89478166, 3036497.89478166, 
+4913156.80052423, 1.26530477345549e-09, 0, 1880188.46281677, 1162020.37527618, 
+-1162020.37527618, -1880188.46281677, -4.84212398176293e-10, 0, 6065968.75567322, 
+3748974.86570096, -3748974.86570096, -6065968.75567322, -1.56219301231466e-09, 0, 
+6066158.96697648, 3749092.42275142, -3749092.42275142, -6066158.96697648, 
+-1.56224199818663e-09, 0, 1880247.24134200, 1162056.70240258, -1162056.70240258, 
+-1880247.24134200, -4.84227535643215e-10, 0, -4913310.68470109, -3036593.00043329, 
+3036593.00043329, 4913310.68470109, 1.26534440385840e-09, 0, -4913310.68470109, 
+-3036593.00043329, 3036593.00043329, 4913310.68470109, 1.26534440385840e-09, 0, 
+1880247.24134200, 1162056.70240258, -1162056.70240258, -1880247.24134200, 
+-4.84227535643215e-10, 0, 6066158.96697648, 3749092.42275142, -3749092.42275142, 
+-6066158.96697648, -1.56224199818663e-09, 0, 6066349.17827974, 3749209.97980188, 
+-3749209.97980187, -6066349.17827974, -1.56229098405859e-09, 0, 1880306.01986723, 
+1162093.02952898, -1162093.02952898, -1880306.01986723, -4.84242673110137e-10, 0, 
+-4913464.56887795, -3036688.10608492, 3036688.10608492, 4913464.56887795, 
+1.26538403426130e-09, 0, -4913464.56887795, -3036688.10608492, 3036688.10608492, 
+4913464.56887795, 1.26538403426130e-09, 0, 1880306.01986722, 1162093.02952898, 
+-1162093.02952898, -1880306.01986723, -4.84242673110137e-10, 0, 6066349.17827974, 
+3749209.97980188, -3749209.97980187, -6066349.17827974, -1.56229098405859e-09, 0, 
+6066539.38958300, 3749327.53685233, -3749327.53685233, -6066539.38958300, 
+-1.56233996993056e-09, 0, 1880364.79839246, 1162129.35665538, -1162129.35665538, 
+-1880364.79839246, -4.84257810577059e-10, 0, -4913618.45305481, -3036783.21173655, 
+3036783.21173655, 4913618.45305481, 1.26542366466421e-09, 0, -4913618.45305481, 
+-3036783.21173655, 3036783.21173655, 4913618.45305481, 1.26542366466421e-09, 0, 
+1880364.79839245, 1162129.35665538, -1162129.35665538, -1880364.79839245, 
+-4.84257810577058e-10, 0, 6066539.38958300, 3749327.53685233, -3749327.53685233, 
+-6066539.38958300, -1.56233996993056e-09, 0, 6066729.60088626, 3749445.09390279, 
+-3749445.09390279, -6066729.60088626, -1.56238895580252e-09, 0, 1880423.57691768, 
+1162165.68378178, -1162165.68378178, -1880423.57691768, -4.84272948043980e-10, 0, 
+-4913772.33723167, -3036878.31738818, 3036878.31738818, 4913772.33723167, 
+1.26546329506711e-09, 0, -4913772.33723167, -3036878.31738818, 3036878.31738818, 
+4913772.33723167, 1.26546329506711e-09, 0, 1880423.57691768, 1162165.68378178, 
+-1162165.68378178, -1880423.57691768, -4.84272948043980e-10, 0, 6066729.60088626, 
+3749445.09390279, -3749445.09390279, -6066729.60088626, -1.56238895580252e-09, 0, 
+6066919.81218952, 3749562.65095325, -3749562.65095325, -6066919.81218952, 
+-1.56243794167449e-09, 0, 1880482.35544291, 1162202.01090818, -1162202.01090818, 
+-1880482.35544291, -4.84288085510902e-10, 0, -4913926.22140853, -3036973.42303981, 
+3036973.42303981, 4913926.22140853, 1.26550292547002e-09, 0, -4913926.22140853, 
+-3036973.42303981, 3036973.42303981, 4913926.22140853, 1.26550292547002e-09, 0, 
+1880482.35544291, 1162202.01090818, -1162202.01090818, -1880482.35544291, 
+-4.84288085510902e-10, 0, 6066919.81218952, 3749562.65095325, -3749562.65095325, 
+-6066919.81218952, -1.56243794167449e-09};
+
+const double ZWGS84_MATLAB[216] = {0, 0, 0, 0, 0, 0, 6043686.27224277, 
+6043686.27224277, 6043686.27224277, 6043686.27224277, 6043686.27224277, 
+6043686.27224277, 3728191.67583129, 3728191.67583129, 3728191.67583129, 
+3728191.67583129, 3728191.67583129, 3728191.67583129, -3728191.67583129, 
+-3728191.67583129, -3728191.67583129, -3728191.67583129, -3728191.67583129, 
+-3728191.67583129, -6043686.27224277, -6043686.27224277, -6043686.27224277, 
+-6043686.27224277, -6043686.27224277, -6043686.27224277, -1.55173509867228e-09, 
+-1.55173509867228e-09, -1.55173509867228e-09, -1.55173509867228e-09, 
+-1.55173509867228e-09, -1.55173509867228e-09, 0, 0, 0, 0, 0, 0, 6043876.48354603, 
+6043876.48354603, 6043876.48354603, 6043876.48354603, 6043876.48354603, 
+6043876.48354603, 3728309.23288175, 3728309.23288175, 3728309.23288175, 
+3728309.23288175, 3728309.23288175, 3728309.23288175, -3728309.23288175, 
+-3728309.23288175, -3728309.23288175, -3728309.23288175, -3728309.23288175, 
+-3728309.23288175, -6043876.48354603, -6043876.48354603, -6043876.48354603, 
+-6043876.48354603, -6043876.48354603, -6043876.48354603, -1.55178408454425e-09, 
+-1.55178408454425e-09, -1.55178408454425e-09, -1.55178408454425e-09, 
+-1.55178408454425e-09, -1.55178408454425e-09, 0, 0, 0, 0, 0, 0, 6044066.69484929, 
+6044066.69484929, 6044066.69484929, 6044066.69484929, 6044066.69484929, 
+6044066.69484929, 3728426.78993221, 3728426.78993221, 3728426.78993221, 
+3728426.78993221, 3728426.78993221, 3728426.78993221, -3728426.78993221, 
+-3728426.78993221, -3728426.78993221, -3728426.78993221, -3728426.78993221, 
+-3728426.78993221, -6044066.69484929, -6044066.69484929, -6044066.69484929, 
+-6044066.69484929, -6044066.69484929, -6044066.69484929, -1.55183307041621e-09, 
+-1.55183307041621e-09, -1.55183307041621e-09, -1.55183307041621e-09, 
+-1.55183307041621e-09, -1.55183307041621e-09, 0, 0, 0, 0, 0, 0, 6044256.90615255, 
+6044256.90615255, 6044256.90615255, 6044256.90615255, 6044256.90615255, 
+6044256.90615255, 3728544.34698267, 3728544.34698267, 3728544.34698267, 
+3728544.34698267, 3728544.34698267, 3728544.34698267, -3728544.34698267, 
+-3728544.34698267, -3728544.34698267, -3728544.34698267, -3728544.34698267, 
+-3728544.34698267, -6044256.90615255, -6044256.90615255, -6044256.90615255, 
+-6044256.90615255, -6044256.90615255, -6044256.90615255, -1.55188205628818e-09, 
+-1.55188205628818e-09, -1.55188205628818e-09, -1.55188205628818e-09, 
+-1.55188205628818e-09, -1.55188205628818e-09, 0, 0, 0, 0, 0, 0, 6044447.11745581, 
+6044447.11745581, 6044447.11745581, 6044447.11745581, 6044447.11745581, 
+6044447.11745581, 3728661.90403313, 3728661.90403313, 3728661.90403313, 
+3728661.90403313, 3728661.90403313, 3728661.90403313, -3728661.90403313, 
+-3728661.90403313, -3728661.90403313, -3728661.90403313, -3728661.90403313, 
+-3728661.90403313, -6044447.11745581, -6044447.11745581, -6044447.11745581, 
+-6044447.11745581, -6044447.11745581, -6044447.11745581, -1.55193104216015e-09, 
+-1.55193104216015e-09, -1.55193104216015e-09, -1.55193104216015e-09, 
+-1.55193104216015e-09, -1.55193104216015e-09, 0, 0, 0, 0, 0, 0, 6044637.32875907, 
+6044637.32875907, 6044637.32875907, 6044637.32875907, 6044637.32875907, 
+6044637.32875907, 3728779.46108359, 3728779.46108359, 3728779.46108359, 
+3728779.46108359, 3728779.46108359, 3728779.46108359, -3728779.46108358, 
+-3728779.46108358, -3728779.46108358, -3728779.46108358, -3728779.46108358, 
+-3728779.46108358, -6044637.32875907, -6044637.32875907, -6044637.32875907, 
+-6044637.32875907, -6044637.32875907, -6044637.32875907, -1.55198002803211e-09, 
+-1.55198002803211e-09, -1.55198002803211e-09, -1.55198002803211e-09, 
+-1.55198002803211e-09, -1.55198002803211e-09};
+
+
+class GeoToCartesianTestCase : public TestCase
+{
+public:
+  GeoToCartesianTestCase (double latitude, 
+                          double longitude, 
+                          double altitude,
+                          GeographicPositions::EarthSpheroidType sphType,
+                          int i);
+  virtual ~GeoToCartesianTestCase ();
+
+private:
+  virtual void DoRun (void);
+  static std::string Name (double latitude, 
+                           double longitude, 
+                           double altitude,
+                           GeographicPositions::EarthSpheroidType sphType);
+  double m_latitude;
+  double m_longitude;
+  double m_altitude;
+  GeographicPositions::EarthSpheroidType m_sphType;
+  int m_i;
+};
+
+std::string 
+GeoToCartesianTestCase::Name (double latitude, 
+                              double longitude, 
+                              double altitude,
+                              GeographicPositions::EarthSpheroidType sphType)
+{
+  std::ostringstream oss;
+  oss << "latitude = " << latitude << " degrees, "
+      << "longitude = " << longitude << " degrees, "
+      << "altitude = " << altitude << " meters, "
+      << "earth spheroid type = " << sphType;
+  return oss.str();
+}
+
+GeoToCartesianTestCase::GeoToCartesianTestCase (double latitude, 
+                                                double longitude, 
+                                                double altitude,
+                                                GeographicPositions::EarthSpheroidType sphType,
+                                                int i)
+  : TestCase (Name (latitude, longitude, altitude, sphType)),
+    m_latitude (latitude),
+    m_longitude (longitude),
+    m_altitude (altitude),
+    m_sphType (sphType),
+    m_i (i)
+{
+}
+
+GeoToCartesianTestCase::~GeoToCartesianTestCase ()
+{
+}
+
+void
+GeoToCartesianTestCase::DoRun (void)
+{  
+  Vector cart = GeographicPositions::GeographicToCartesianCoordinates (m_latitude, 
+                                                                       m_longitude, 
+                                                                       m_altitude,
+                                                                       m_sphType);
+  if (m_sphType == GeographicPositions::SPHERE)
+    {
+      NS_TEST_ASSERT_MSG_EQ_TOL (cart.x, 
+                                 XSPHERE_MATLAB [m_i], 
+                                 TOLERANCE, 
+                                 "x coordinate (" << cart.x << ") is incorrect "
+                                 "for perfect sphere model in iteration " << m_i);
+      NS_TEST_ASSERT_MSG_EQ_TOL (cart.y, 
+                                 YSPHERE_MATLAB [m_i], 
+                                 TOLERANCE, 
+                                 "y coordinate (" << cart.y << ") is incorrect "
+                                 "for perfect sphere model in iteration " << m_i);
+      NS_TEST_ASSERT_MSG_EQ_TOL (cart.z, 
+                                 ZSPHERE_MATLAB [m_i], 
+                                 TOLERANCE, 
+                                 "z coordinate (" << cart.z << ") is incorrect "
+                                 "for perfect sphere model in iteration " << m_i);
+    }
+  else if (m_sphType == GeographicPositions::GRS80)
+    {
+      NS_TEST_ASSERT_MSG_EQ_TOL (cart.x, 
+                                 XGRS80_MATLAB [m_i], 
+                                 TOLERANCE, 
+                                 "x coordinate (" << cart.x << ") is incorrect "
+                                 "for GRS80 model in iteration " << m_i);
+      NS_TEST_ASSERT_MSG_EQ_TOL (cart.y, 
+                                 YGRS80_MATLAB [m_i], 
+                                 TOLERANCE, 
+                                 "y coordinate (" << cart.y << ") is incorrect "
+                                 "for GRS80 model in iteration " << m_i);
+      NS_TEST_ASSERT_MSG_EQ_TOL (cart.z, 
+                                 ZGRS80_MATLAB [m_i], 
+                                 TOLERANCE, 
+                                 "z coordinate (" << cart.z << ") is incorrect "
+                                 "for GRS80 model in iteration " << m_i);
+    }
+  else // if m_sphType == GeographicPositions::WGS84
+    {
+      NS_TEST_ASSERT_MSG_EQ_TOL (cart.x, 
+                                 XWGS84_MATLAB [m_i], 
+                                 TOLERANCE, 
+                                 "x coordinate (" << cart.x << ") is incorrect "
+                                 "for WGS84 model in iteration " << m_i);
+      NS_TEST_ASSERT_MSG_EQ_TOL (cart.y, 
+                                 YWGS84_MATLAB [m_i], 
+                                 TOLERANCE, 
+                                 "y coordinate (" << cart.y << ") is incorrect "
+                                 "for WGS84 model in iteration " << m_i);
+      NS_TEST_ASSERT_MSG_EQ_TOL (cart.z, 
+                                 ZWGS84_MATLAB [m_i], 
+                                 TOLERANCE, 
+                                 "z coordinate (" << cart.z << ") is incorrect "
+                                 "for WGS84 model in iteration " << m_i);
+    }
+}
+
+
+class GeoToCartesianTestSuite : public TestSuite
+{
+public:
+  GeoToCartesianTestSuite ();
+};
+
+GeoToCartesianTestSuite::GeoToCartesianTestSuite ()
+  : TestSuite ("geo-to-cartesian", UNIT)
+{
+  NS_LOG_INFO ("creating GeoToCartesianTestSuite");
+  int i = 0; // iteration number
+  for (double altitude = 0; altitude <= 1000; altitude += 200)
+    {
+      for (double latitude = 0; latitude <= 360; latitude += 72)
+        {
+          for (double longitude = 0; longitude <= 360; longitude += 72)
+            {
+              AddTestCase (new GeoToCartesianTestCase (latitude, 
+                                                       longitude, 
+                                                       altitude, 
+                                                       GeographicPositions::SPHERE,
+                                                       i), 
+                           TestCase::QUICK);
+              ++i;
+            }
+        }
+    }
+  i = 0;
+  for (double altitude = 0; altitude <= 1000; altitude += 200)
+    {
+      for (double latitude = 0; latitude <= 360; latitude += 72)
+        {
+          for (double longitude = 0; longitude <= 360; longitude += 72)
+            {
+              AddTestCase (new GeoToCartesianTestCase (latitude, 
+                                                       longitude, 
+                                                       altitude, 
+                                                       GeographicPositions::GRS80,
+                                                       i), 
+                           TestCase::QUICK);
+              ++i;
+            }
+        }
+    }
+  i = 0;
+  for (double altitude = 0; altitude <= 1000; altitude += 200)
+    {
+      for (double latitude = 0; latitude <= 360; latitude += 72)
+        {
+          for (double longitude = 0; longitude <= 360; longitude += 72)
+            {
+              AddTestCase (new GeoToCartesianTestCase (latitude, 
+                                                       longitude, 
+                                                       altitude, 
+                                                       GeographicPositions::WGS84,
+                                                       i), 
+                           TestCase::QUICK);
+              ++i;
+            }
+        }
+    }
+}
+
+static GeoToCartesianTestSuite g_GeoToCartesianTestSuite;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/mobility/test/rand-cart-around-geo-test.cc	Wed Apr 08 19:42:20 2015 -0700
@@ -0,0 +1,177 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Benjamin Cizdziel <ben.cizdziel@gmail.com>
+ */
+
+#include <ns3/test.h>
+#include <ns3/log.h>
+#include <cmath>
+#include <ns3/geographic-positions.h>
+
+/**
+ * This test verifies the accuracy of the RandCartesianPointsAroundGeographicPoint()
+ * method in the GeographicPositions class, which generates uniformly 
+ * distributed random points (in ECEF Cartesian coordinates) within a given 
+ * altitude above earth's surface centered around a given origin point (on 
+ * earth's surface, in geographic/geodetic coordinates) within a given distance 
+ * radius (using arc length of earth's surface, not pythagorean distance).
+ * Distance radius is measured as if all generated points are on earth's 
+ * surface (with altitude = 0). Assumes earth is a perfect sphere. To verify the 
+ * method, this test checks that the generated points are within the given 
+ * maximum distance radius from the origin. Since this is testing the distance 
+ * radius from the origin, all points generated in this test are on earth's 
+ * surface (altitude = 0), since the distance radius has been defined as if all 
+ * points are on earth's surface. The pythagorean (straight-line) distance 
+ * between each generated point and the origin is first calculated, and then 
+ * using this distance and the radius of the earth, the distance radius to the
+ * origin (arc length) is calculated. This distance radius is compared to the 
+ * max distance radius to ensure that it is less than the maximum.
+ */
+NS_LOG_COMPONENT_DEFINE ("RandCartAroundGeoTest");
+
+using namespace ns3;
+
+// 0.1 meter tolerance for testing, which is very small compared to the maximum 
+// distances from origin being tested
+const double TOLERANCE = 0.1;
+
+// earth's radius in meters if modeled as a perfect sphere
+static const double EARTH_RADIUS = 6371e3; 
+
+class RandCartAroundGeoTestCase : public TestCase
+{
+public:
+  RandCartAroundGeoTestCase (double originLatitude, 
+                             double originLongitude,
+                             double maxAltitude, 
+                             int numPoints, 
+                             double maxDistFromOrigin,
+                             Ptr<UniformRandomVariable> uniRand);
+  virtual ~RandCartAroundGeoTestCase ();
+
+private:
+  virtual void DoRun (void);
+  static std::string Name (double originLatitude, 
+                           double originLongitude,
+                           double maxDistFromOrigin);
+  double m_originLatitude;
+  double m_originLongitude;
+  double m_maxAltitude;
+  int m_numPoints;
+  double m_maxDistFromOrigin;
+  Ptr<UniformRandomVariable> m_uniRand;
+};
+
+std::string 
+RandCartAroundGeoTestCase::Name (double originLatitude, 
+                                 double originLongitude,
+                                 double maxDistFromOrigin)
+{
+  std::ostringstream oss;
+  oss << "origin latitude = " << originLatitude << " degrees, "
+      << "origin longitude = " << originLongitude << " degrees, "
+      << "max distance from origin = " << maxDistFromOrigin;
+  return oss.str();
+}
+
+RandCartAroundGeoTestCase::RandCartAroundGeoTestCase (double originLatitude, 
+                                                      double originLongitude,
+                                                      double maxAltitude, 
+                                                      int numPoints, 
+                                                      double maxDistFromOrigin,
+                                                      Ptr<UniformRandomVariable> uniRand)
+  : TestCase (Name (originLatitude, originLongitude, maxDistFromOrigin)),
+    m_originLatitude (originLatitude),
+    m_originLongitude (originLongitude),
+    m_maxAltitude (maxAltitude),
+    m_numPoints (numPoints),
+    m_maxDistFromOrigin (maxDistFromOrigin),
+    m_uniRand (uniRand)
+{
+}
+
+RandCartAroundGeoTestCase::~RandCartAroundGeoTestCase ()
+{
+}
+
+void
+RandCartAroundGeoTestCase::DoRun (void)
+{  
+  std::list<Vector> points =  GeographicPositions::RandCartesianPointsAroundGeographicPoint (m_originLatitude, 
+                                                                                             m_originLongitude,
+                                                                                             m_maxAltitude, 
+                                                                                             m_numPoints, 
+                                                                                             m_maxDistFromOrigin,
+                                                                                             m_uniRand);
+  Vector origin = GeographicPositions::GeographicToCartesianCoordinates (m_originLatitude, 
+                                                                         m_originLongitude, 
+                                                                         m_maxAltitude,
+                                                                         GeographicPositions::SPHERE);
+  Vector randPoint;
+  while (!points.empty ())
+    {
+      randPoint = points.front ();
+      points.pop_front ();
+
+      // pythagorean distance between random point and origin, not distance on surface of earth
+      double straightDistFromOrigin = sqrt (pow (randPoint.x - origin.x, 2) + 
+                                            pow (randPoint.y - origin.y, 2) + 
+                                            pow (randPoint.z - origin.z, 2));
+
+      // arc length distance between random point and origin, on surface of earth
+      double arcDistFromOrigin = 2 * EARTH_RADIUS * asin (straightDistFromOrigin / (2 * EARTH_RADIUS));
+
+      NS_TEST_ASSERT_MSG_LT (arcDistFromOrigin, 
+                             m_maxDistFromOrigin + TOLERANCE,  
+                             "random point (" << randPoint.x << ", " << randPoint.y 
+                             << ", " << randPoint.z << ") is outside of max radius from origin");
+    }
+}
+
+
+class RandCartAroundGeoTestSuite : public TestSuite
+{
+public:
+  RandCartAroundGeoTestSuite ();
+};
+
+RandCartAroundGeoTestSuite::RandCartAroundGeoTestSuite ()
+  : TestSuite ("rand-cart-around-geo", UNIT)
+{
+  NS_LOG_INFO ("creating RandCartAroundGeoTestSuite");
+  Ptr<UniformRandomVariable> uniRand = CreateObject<UniformRandomVariable> ();
+  uniRand->SetStream (5);
+  for (double originLatitude = -89.9; originLatitude <= 89.9; originLatitude += 35.96)
+    {
+      for (double originLongitude = 0; originLongitude <= 360; originLongitude += 72)
+        {
+          for (double maxDistFromOrigin = 1000; maxDistFromOrigin <= 1000000; maxDistFromOrigin *= 10)
+            {
+              AddTestCase (new RandCartAroundGeoTestCase (originLatitude, 
+                                                          originLongitude, 
+                                                          0, // on earth's surface
+                                                          50, // 50 points generated
+                                                          maxDistFromOrigin,
+                                                          uniRand), 
+                           TestCase::QUICK);
+            }
+        }
+    }
+}
+
+static RandCartAroundGeoTestSuite g_RandCartAroundGeoTestSuite;
--- a/src/mobility/wscript	Wed Apr 08 18:18:49 2015 +0200
+++ b/src/mobility/wscript	Wed Apr 08 19:42:20 2015 -0700
@@ -9,6 +9,7 @@
         'model/constant-velocity-helper.cc',
         'model/constant-velocity-mobility-model.cc',
         'model/gauss-markov-mobility-model.cc',
+        'model/geographic-positions.cc',
         'model/hierarchical-mobility-model.cc',
         'model/mobility-model.cc',
         'model/position-allocator.cc',
@@ -30,6 +31,8 @@
         'test/ns2-mobility-helper-test-suite.cc',
         'test/steady-state-random-waypoint-mobility-model-test.cc',
         'test/waypoint-mobility-model-test.cc',
+        'test/geo-to-cartesian-test.cc',
+        'test/rand-cart-around-geo-test.cc',
         ]
 
     headers = bld(features='ns3header')
@@ -41,6 +44,7 @@
         'model/constant-velocity-helper.h',
         'model/constant-velocity-mobility-model.h',
         'model/gauss-markov-mobility-model.h',
+        'model/geographic-positions.h',
         'model/hierarchical-mobility-model.h',
         'model/mobility-model.h',
         'model/position-allocator.h',