1 package net.sf.openrocket.util;
3 import net.sf.openrocket.l10n.Translator;
4 import net.sf.openrocket.startup.Application;
7 * A strategy that performs computations on WorldCoordinates.
9 public enum GeodeticComputationStrategy {
13 * Perform no geodetic computations. The addCoordinate method does nothing and
14 * getCoriolisAcceleration returns Coordinate.NUL.
18 public WorldCoordinate addCoordinate(WorldCoordinate latlon, Coordinate delta) {
23 public Coordinate getCoriolisAcceleration(WorldCoordinate latlon, Coordinate velocity) {
24 return Coordinate.NUL;
29 * Perform geodetic computations with a spherical Earch approximation.
34 public WorldCoordinate addCoordinate(WorldCoordinate location, Coordinate delta) {
35 double newAlt = location.getAltitude() + delta.z;
37 // bearing (in radians, clockwise from north);
38 // d/R is the angular distance (in radians), where d is the distance traveled and R is the earth’s radius
39 double d = MathUtil.hypot(delta.x, delta.y);
40 double bearing = Math.atan(delta.x / delta.y);
42 bearing = bearing + Math.PI;
44 // Calculate the new lat and lon
45 double newLat, newLon;
46 double sinLat = Math.sin(location.getLatitudeRad());
47 double cosLat = Math.cos(location.getLatitudeRad());
48 double sinDR = Math.sin(d / WorldCoordinate.REARTH);
49 double cosDR = Math.cos(d / WorldCoordinate.REARTH);
51 newLat = Math.asin(sinLat * cosDR + cosLat * sinDR * Math.cos(bearing));
52 newLon = location.getLongitudeRad() + Math.atan2(Math.sin(bearing) * sinDR * cosLat, cosDR - sinLat * Math.sin(newLat));
54 if (Double.isNaN(newLat)) {
55 newLat = location.getLatitudeRad();
58 if (Double.isNaN(newLon)) {
59 newLon = location.getLongitudeRad();
62 return new WorldCoordinate(Math.toDegrees(newLat), Math.toDegrees(newLon), newAlt);
66 public Coordinate getCoriolisAcceleration(WorldCoordinate latlon, Coordinate velocity) {
67 return computeCoriolisAcceleration(latlon, velocity);
73 * Perform geodetic computations on a WGS84 reference ellipsoid using Vincenty Direct Solution.
78 public WorldCoordinate addCoordinate(WorldCoordinate location, Coordinate delta) {
79 double newAlt = location.getAltitude() + delta.z;
81 // bearing (in radians, clockwise from north);
82 // d/R is the angular distance (in radians), where d is the distance traveled and R is the earth’s radius
83 double d = MathUtil.hypot(delta.x, delta.y);
84 double bearing = Math.atan(delta.x / delta.y);
86 bearing = bearing + Math.PI;
88 // Calculate the new lat and lon
89 double newLat, newLon;
90 double ret[] = dirct1(location.getLatitudeRad(), location.getLongitudeRad(), bearing, d, 6378137, 1.0 / 298.25722210088);
94 if (Double.isNaN(newLat)) {
95 newLat = location.getLatitudeRad();
98 if (Double.isNaN(newLon)) {
99 newLon = location.getLongitudeRad();
101 return new WorldCoordinate(newLat, newLon, newAlt);
105 public Coordinate getCoriolisAcceleration(WorldCoordinate latlon, Coordinate velocity) {
106 return computeCoriolisAcceleration(latlon, velocity);
111 private static final Translator trans = Application.getTranslator();
113 private static final double PRECISION_LIMIT = 0.5e-13;
117 * Return the name of this geodetic computation method.
119 public String getName() {
120 return trans.get(name().toLowerCase() + ".name");
124 * Return a description of the geodetic computation methods.
126 public String getDescription() {
127 return trans.get(name().toLowerCase() + ".desc");
131 public String toString() {
137 * Add a cartesian movement coordinate to a WorldCoordinate.
139 public abstract WorldCoordinate addCoordinate(WorldCoordinate latlon, Coordinate delta);
143 * Compute the coriolis acceleration at a specified WorldCoordinate and velocity.
145 public abstract Coordinate getCoriolisAcceleration(WorldCoordinate latlon, Coordinate velocity);
151 private static Coordinate computeCoriolisAcceleration(WorldCoordinate latlon, Coordinate velocity) {
153 double sinlat = Math.sin(latlon.getLatitudeRad());
154 double coslat = Math.cos(latlon.getLatitudeRad());
156 double v_n = velocity.y;
157 double v_e = -1 * velocity.x;
158 double v_u = velocity.z;
160 // Not exactly sure why I have to reverse the x direction, but this gives the precession in the
161 // correct direction (e.g, flying north in northern hemisphere should cause defection to the east (+ve x))
162 // All the directions are very confusing because they are tied to the wind direction (to/from?), in which
163 // +ve x or east according to WorldCoordinate is what everything is relative to.
164 // The directions of everything need so thought, ideally the wind direction and launch rod should be
165 // able to be set independently and in terms of bearing with north == +ve y.
167 Coordinate coriolis = new Coordinate(2.0 * WorldCoordinate.EROT * (v_n * sinlat - v_u * coslat),
168 2.0 * WorldCoordinate.EROT * (-1.0 * v_e * sinlat),
169 2.0 * WorldCoordinate.EROT * (v_e * coslat)
176 // ******************************************************************** //
177 // The Vincenty Direct Solution.
178 // Code from GeoConstants.java, Ian Cameron Smith, GPL
179 // ******************************************************************** //
182 * Solution of the geodetic direct problem after T. Vincenty.
183 * Modified Rainsford's method with Helmert's elliptical terms.
184 * Effective in any azimuth and at any distance short of antipodal.
186 * Programmed for the CDC-6600 by lcdr L. Pfeifer, NGS Rockville MD,
189 * @param glat1 The latitude of the starting point, in radians,
191 * @param glon1 The latitude of the starting point, in radians,
193 * @param azimuth The azimuth to the desired location, in radians
194 * clockwise from north.
195 * @param dist The distance to the desired location, in meters.
196 * @param axis The semi-major axis of the reference ellipsoid,
198 * @param flat The flattening of the reference ellipsoid.
199 * @return An array containing the latitude and longitude
200 * of the desired point, in radians, and the
201 * azimuth back from that point to the starting
202 * point, in radians clockwise from north.
204 private static double[] dirct1(double glat1, double glon1,
205 double azimuth, double dist,
206 double axis, double flat) {
207 double r = 1.0 - flat;
209 double tu = r * Math.sin(glat1) / Math.cos(glat1);
211 double sf = Math.sin(azimuth);
212 double cf = Math.cos(azimuth);
217 baz = Math.atan2(tu, cf) * 2.0;
219 double cu = 1.0 / Math.sqrt(tu * tu + 1.0);
222 double c2a = -sa * sa + 1.0;
224 double x = Math.sqrt((1.0 / r / r - 1.0) * c2a + 1.0) + 1.0;
227 c = (x * x / 4.0 + 1) / c;
228 double d = (0.375 * x * x - 1.0) * x;
229 tu = dist / r / axis / c;
232 double sy, cy, cz, e;
236 cz = Math.cos(baz + y);
237 e = cz * cz * 2.0 - 1.0;
242 y = (((sy * sy * 4.0 - 3.0) * y * cz * d / 6.0 + x) *
243 d / 4.0 - cz) * sy * d + tu;
244 } while (Math.abs(y - c) > PRECISION_LIMIT);
246 baz = cu * cy * cf - su * sy;
247 c = r * Math.sqrt(sa * sa + baz * baz);
248 d = su * cy + cu * sy * cf;
249 double glat2 = Math.atan2(d, c);
250 c = cu * cy - su * sy * cf;
251 x = Math.atan2(sy * sf, c);
252 c = ((-3.0 * c2a + 4.0) * flat + 4.0) * c2a * flat / 16.0;
253 d = ((e * cy * c + cz) * sy * c + y) * sa;
254 double glon2 = glon1 + x - (1.0 - c) * d * flat;
255 baz = Math.atan2(sa, baz) + Math.PI;
257 double[] ret = new double[3];