#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Copyright 2012 California Institute of Technology. ALL RIGHTS RESERVED.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# United States Government Sponsorship acknowledged. This software is subject to
# U.S. export control laws and regulations and has been classified as 'EAR99 NLR'
# (No [Export] License Required except when exporting to an embargoed country,
# end user, or in support of a prohibited end use). By downloading this software,
# the user agrees to comply with all applicable U.S. export laws and regulations.
# The user has the responsibility to obtain export licenses, or other export
# authority as may be required before exporting this software to any 'EAR99'
# embargoed foreign country or citizen of those countries.
#
# Author: Eric Belz
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""Affine
======
Last but not least is the Affine transformation. This is made up of a
rotation and a translation. The rotation can be any a Matrix, Versor,
EulerAngle, YPR, ... or something else, as long as it's call method takes
the x, y, z attributes where they need to be). They key is in the operator
overalods:
__invert__ --> "~" --> inverse transformation
x', y', z' = A(x, y, z) goes from ECEF to tangent plane cartesian, then
x, y, z = (~A)(x', y', z') goes from tangent plane to ECEF.
That is really handy. The coordinate transform is a 1st class object, and
it's invertible via an operator.
__mul__ --> "*" --> composition of transformations
So, for instance, you can go from ECEF to an airplane with A, the go
from the platforms's IMU and the Antenna with A', then
A" = A*A'
will go from ECEF to your antenna, including the motion. It's really that
simple.
__call__ --> () -> applies the transformation to the object.
With the affine transformation's compose() method, you can build any
transformation you want from intermediate steps. Support for a and
aircraft frame is forthcoming-- on in which you diffrentiate a motion
history from a GPS record, define a velocity-tangent frame, and then
correct for platform attitude.
Final Note:
----------
With the helmert() function, you get a standard geodesy affine transformation
that includes a scaling factor-- all this means is that you have to use a
Matrix (Tensor) for the now mis-named "rotation" attribute.
Nothing is type checked-- so you are responsible for your transformations.
"""
## \namespace geo::affine Affine Transformations
from isceobj.Util.geo import euclid
## Limited Affine
## Transformations.
class Affine(euclid.Alias):
"""Affine(rotation, translation)
rotation: perferably a euclid.chart on SO3
translation: euclid.Vector in E3
Methods:
========
A(v) applies transformation
A*A' composes transformations [see Note]
~A returns the inverse transformation
NOTE: A and A' need to have their rotation attribute be the same class for
composition to work. (You can't multiply a versor and an euler angle)--
also, if you're doing scaling, skewing, or gliding -- you better use a
Matrix (Tensor).
"""
## Init: a callable rotation and translation
def __init__(self, rotation, translation):
"""see class docstring for signature"""
## Alias rotation (or not, you could make it a shear, dilation, ...)
self.rotation = rotation
## Translation
self.translation = translation
return None
## Affine transform:\n
## \f$ A(\vec{v}) \rightarrow \vec{v}' = R(\vec{v}) + \vec{T} \f$
def __call__(self, vector):
"""vector = affine(vector) ==>
affine.translation + affine.rotation(vector)"""
return self.translation+self.rotation(vector)
## Convolution \n
## \f$ AA' = (R, T)(R', T') \rightarrow (RR', R(T') + T) \f$ \n
def __mul__(self, other):
"""A*A' = (R, T)*(R', T') --? (R*R', R(T') + T)
is the composition of two affine transformations.
"""
return self.__class__(
self.rotation*other.rotation,
self.rotation*other.translation + self.translation
)
## Inverse \n
## \f$ AA^{-1} = ({\bf 1}, 0) \rightarrow A^{-1} = (R^{-1}, -R^{-1}(T)) \f$
def __invert__(self):
"""~A = ~(R, T) --> (~R, -(~R(T)))
is the affine transformation such that:
(~A)*(A) == 1"""
inv_rot = ~(self.rotation)
return self.__class__(inv_rot, -(inv_rot(self.translation)))
pass
## \f$ \vec{v}' = \vec{C} + [\mu {\bf I} + \vec{r} {\bf \times}]\vec{v} \f$ \n
## A Helmert
## transformation.
def helmert(cx, cy, cz, s, rx, ry, rz):
"""
affine = Helmert(C, s, rx, ry, rz)
cx, cy, cz in meters (a Vector)
mu in ppm
rx, ry, rz in arcseconds (*r as a Vector-- since it is a small rotation)
"""
from .euclid import IDEM, Vector
from math import pi
C = Vector(*map(float, (cx, cy, cz)))
# note: sign is correct, since R will take an anterior product,
# recall dual() makes a matrix representing the cross product.
R = (Vector(rx, ry, rz)*pi/180./3600.).dual()
mu = 1.+s/1.e6
return Affine(mu*IDEM + R, C)
## An example of a Helmert transform in ISCE today.
WGS84_TO_MGI = helmert(-577.326, -90.129, -463.920, -2.423, 5.137, 1.474, 5.297)