Coordinates |
Various translational and rotational coordinate types are available in the agi.foundation.coordinates package. These types provide common n-tuple representations for expressing locations of points and orientations of axes as well as their derivatives. Conversions are available for transforming between related coordinate representations.
Locations of points in 3-dimensional and 2-dimensional Euclidean space can be expressed using the following types:
2-dimensional
Rectangular (x, y)
UnitRectangular (x, y)
Polar (clock, radial)
3-dimensional
Cartesian (x, y, z)
UnitCartesian (x, y, z)
Cylindrical (clock, radial, z)
Spherical (clock, cone, magnitude)
UnitSpherical (clock, cone)
LongitudeLatitudeRadius (longitude, latitude, radius)
The following example shows how to convert between two compatible translational coordinate types by using an available constructor.
// Initialize a set of spherical coordinates. double clockAngle = Math.PI / 4; double coneAngle = Math.PI / 8; double magnitude = 100.0; Spherical spherical = new Spherical(clockAngle, coneAngle, magnitude); // Convert the set of spherical coordinates to a set of Cartesian coordinates. Cartesian cartesian = new Cartesian(spherical);
The UnitCartesian, UnitRectangular, and UnitSpherical types are normalized representations of the Cartesian, Rectangular, and Spherical types, respectively, and play an important role in DME Component Libraries. These types help to optimize the performance of the class library by avoiding unnecessary and redundant normalizations. The Cartesian, Rectangular, and Spherical types have methods that produce their normalized representations, as shown in the following example.
// Start with the following Cartesian vector. Cartesian cartesian = new Cartesian(3.1, -4.1, 5.9); // Normalize the Cartesian vector and retrieve its magnitude. double[] out_magnitude = new double[1]; UnitCartesian normalizedCartesian = cartesian.normalize(out_magnitude); double magnitude = out_magnitude[0]; // Start with the following rectangular vector. Rectangular rectangular = new Rectangular(1.2, -2.3); // Normalize the rectangular vector without retrieving its magnitude. UnitRectangular normalizedRectangular = rectangular.normalize(); // Start with the following Spherical vector. Spherical spherical = new Spherical(0.12, 1.17, 8.423); // Normalize the spherical vector. UnitSpherical normalizedSpherical = spherical.normalize();
Cartographic coordinates (longitude, latitude, height) allow the location of points to be expressed with respect to the surface of an ellipsoid. The Ellipsoid type provides methods for converting between Cartographic and Cartesian coordinates, as the next example shows.
// Initialize a set of cartographic coordinates. double longitude = Trig.degreesToRadians(-75.56); double latitude = Trig.degreesToRadians(40.034); double height = 100.0; Cartographic cartographic = new Cartographic(longitude, latitude, height); // Retrieve the default ellipsoidal shape of the Earth. EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth(); Ellipsoid shape = earth.getShape(); // Convert to a set of Cartesian coordinates. Cartesian position = shape.cartographicToCartesian(cartographic);
The next example shows how to perform basic vector operations with the Cartesian coordinate type.
Cartesian vectorOne = new Cartesian(3.14, 1.59, 2.65); Cartesian vectorTwo = new Cartesian(2.71, 8.28, 1.83); Matrix3By3 matrix = new Matrix3By3(2.0, 1.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 1.0); // Inversion. Cartesian inverse = vectorOne.invert(); // Sum and difference. Cartesian sum = Cartesian.add(vectorTwo, vectorOne); Cartesian difference = Cartesian.subtract(vectorTwo, vectorOne); // Dot and Cross Products. Cartesian crossProduct = vectorOne.cross(vectorTwo); double dotProduct = vectorOne.dot(vectorTwo); // Multiplication. Cartesian vectorResult = Cartesian.multiply(2.0, matrix.multiply(vectorOne));
Like the translational coordinate types, the conversion between the various rotational coordinate representations is accomplished by utilizing one of the available constructors.
// Initialize an angle-axis rotation. double angle = Math.PI / 3.0; UnitCartesian axis = new UnitCartesian(1.0, 1.0, 1.0); AngleAxisRotation angleAxis = new AngleAxisRotation(angle, axis); // Convert to various other representations. UnitQuaternion quaternion = new UnitQuaternion(angleAxis); Matrix3By3 matrix = new Matrix3By3(angleAxis); EulerSequence euler = new EulerSequence(angleAxis, EulerSequenceIndicator.EULER321); YawPitchRoll ypr = new YawPitchRoll(angleAxis, YawPitchRollIndicator.YPR);
The rotational coordinate types are commonly used to represent the orientation of one set of axes with respect to another. A series of rotations can be combined to compose a net rotation between an initial set of axes and a final set of axes. Transforming the representation of a vector between the two reference frames is then accomplished as shown in the following example.
// Create a rotational transformation from two elementary rotations. double angleOne = Math.PI / 2.0; double angleTwo = -Math.PI / 6.0; ElementaryRotation rotationOne = new ElementaryRotation(AxisIndicator.FIRST, angleOne); ElementaryRotation rotationTwo = new ElementaryRotation(AxisIndicator.SECOND, angleTwo); Matrix3By3 rotation = rotationTwo.multiply(rotationOne); // Perform the rotational transformation on a vector to produce its new representation. Cartesian vector = new Cartesian(2.0, 1.0, -3.0); Cartesian rotatedFromMatrix = vector.rotate(rotation);
Often, quaternions are used to represent rotations in a compact and efficient way. DME Component Libraries uses normalized (unit) quaternions as a standard for specifying orientation. The quaternions use 'alias' rotations which transform (or rotate) a coordinate vector from one basis to another. Unfortunately, 'rotate' can sometimes be ambiguous since there are two senses of a rotation. In 'alias' rotations (as in DME Component Libraries) the coordinate vector is modified so that the new coordinates represent the coordinates of the same vector as it is expressed in a different basis. This is as opposed to an 'alibi' rotation which modifies the coordinates of the vector so that it is represented in the same basis but reoriented by the given amount. DME Component Libraries generally does not use 'alibi' rotations. To illustrate the relationship between common rotation matrices and unit quaternions, the following example shows how quaternion operations can be formed to be equivalent to matrix rotations.
Cartesian v = new Cartesian(1.0, 1.0, 1.0); // Two simple unit quaternion rotations about different axes ... UnitQuaternion qA = new UnitQuaternion(new AngleAxisRotation(Math.PI / 4.0, UnitCartesian.getUnitX())); UnitQuaternion qB = new UnitQuaternion(new AngleAxisRotation(-Math.PI / 6.0, UnitCartesian.getUnitZ())); // ... and their matrix counterparts Matrix3By3 mA = new Matrix3By3(qA); Matrix3By3 mB = new Matrix3By3(qB); // The new Cartesian will be the equivalent of "v" expressed in // the coordinate basis represented by the quaternion rotation // with respect to the coordinate basis of "v" Cartesian vA = v.rotate(qA); Cartesian vAexpected = new Cartesian(1.0, Math.sqrt(2.0), 0.0); // The following two statements are equivalent to "vA" Cartesian vmA1 = v.rotate(mA); Cartesian vmA2 = mA.multiply(v); // The following statements are equivalent UnitQuaternion qAB = qB.multiply(qA); Matrix3By3 mAB = mB.multiply(mA); Matrix3By3 mqAB = new Matrix3By3(qAB); // The following statements are equivalent and are intended // to follow the rotation matrix order of operations. The new // Cartesian is then expressed in the basis resulting from rotating // the basis of "v" by "qA" and then "qB". Cartesian vAB = v.rotate(qA).rotate(qB); Cartesian vqAB = v.rotate(qB.multiply(qA)); Cartesian vmAB = v.rotate(mAB); // Now compare against the expected value of the final vector. // Notice how the angle we're adding here is the reverse of // the angle we specified in the rotation. This is because // here we are doing an 'alibi' rotation in spherical coordinates, // as opposed to the 'alias' rotation of the unit quaternion. Spherical vSphr = new Spherical(vAexpected); Cartesian vABexpected = new Cartesian( new Spherical( vSphr.getClock() + Math.PI / 6.0, // The angle rotated by the "B" rotation vSphr.getCone(), vSphr.getMagnitude()));
Another common use of rotational coordinates is to express the attitude of a vehicle, with respect to a reference set of axes. A common convention for an aircraft is to define the yaw or heading angle from north as measured about the nadir direction, the pitch or elevation angle from the local horizontal plane, and the roll or bank angle about the longitudinal axis of the vehicle. Thus, each angle is measured about an intermediate axis resulting from consecutive rotations forming a 321 EulerSequence.
Note, however, that since the YawPitchRoll type applies 'yaw', 'pitch', and 'roll' with respect to the original reference axes, rather than as consecutive rotations about intermediate axes, it is not possible to obtain the heading, elevation, and bank from simply converting a UnitQuaternion to a YawPitchRoll. It is also important to correctly configure the reference axes to the appropriate convention, such as a AxesNorthEastDown coordinate system. The following is an example of how to obtain yaw (heading), pitch (elevation), and roll (bank) with respect to north and the local horizontal relative to the Earth:
Point myVehiclePoint = propagator.createPoint(); EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth(); AxesNorthEastDown ned = new AxesNorthEastDown(earth, myVehiclePoint); Vector surfaceNormal = new VectorEllipsoidSurfaceNormal(earth.getShape(), earth.getFixedFrame(), myVehiclePoint); AxesAlignedConstrained vehicleBodyAxes = new AxesAlignedConstrained( new VectorVelocity(myVehiclePoint, earth.getFixedFrame()), AxisIndicator.FIRST, new VectorInverted(surfaceNormal), AxisIndicator.THIRD); AxesEvaluator rotationEvaluator = GeometryTransformer.getAxesTransformation(ned, vehicleBodyAxes); UnitQuaternion ned2body = rotationEvaluator.evaluate(propagator.getTimeInterval().getStart()); EulerSequence yawPitchRoll = new EulerSequence(ned2body, EulerSequenceIndicator.EULER321); double yaw = yawPitchRoll.getFirstRotation().getAngle(); double pitch = yawPitchRoll.getSecondRotation().getAngle(); double roll = yawPitchRoll.getThirdRotation().getAngle();
In addition to the fixed-size Cartesian shown in the Rotational Coordinates section, DME Component Libraries also features the variable sized Matrix class for more general linear algebra operations.
The concrete types DenseMatrix and DiagonalMatrix inherit from the abstract Matrix base class. Matrix provides static methods and operator overloads to perform matrix manipulation, and a double dispatch system ensures that the most efficient implementation of that operation is chosen based on the types of the matrices involved.
double[][] denseElements = { { 2, 4, 3, 1 }, { 7, 4, 8, 3 }, { 5, 1, 9, 5 }, }; double[] diagonalElements = { 2, 6, 5 }; DenseMatrix dense = new DenseMatrix(denseElements); DiagonalMatrix diagonal = new DiagonalMatrix(diagonalElements); Matrix product = Matrix.multiply(diagonal, dense);
The definitional object DynamicMatrix represents a matrix whose values (but not dimensions) vary with time. DynamicMatrices are used to create DynamicMatrixEvaluators, which can be evaluated at time to produce a Matrix. This mirrors the other DGL types, such as Point, which creates a PointEvaluator, which can be evaluated at a time to produce a Cartesian.