Write orientation & transform in C++ (#1637)
* locationd at 20hz * update ref * bump cereal * dont modify global state * add scons files * ecef2geodetic and geodetic2ecef * Finish local coords class * Add header file * Add orientation.cc * cleanup * Add functions to header file * Add cython wrapper * y u no work? * This passes the tests * test rot2quat and quat2rot * Teste euler2rot and rot2euler * rot_matrix * test ecef_euler_from_ned and ned_euler_from_ecef * add benchmark * Add test * Consistent newlines * no more radians supported in geodetic * test localcoord single * test localcoord single * all tests pass * Unused import * Add alternate namings * Add source for formulas * no explicit tests needed * remove benchmark * Add release files * Typo * Remove print statement * no access to raw transform matrix * temporarily add tolerance * handcode quat2euler * update refpull/1651/head
parent
90d97de74d
commit
c18e7da3c2
22 changed files with 683 additions and 420 deletions
@ -1 +1 @@ |
|||||||
Subproject commit 1aaf1bfd7c07e1c5184e8f13823e8ed02e2c7af2 |
Subproject commit 0021fa241994cd9d94945ba305b0f3f1c43feaae |
@ -0,0 +1,2 @@ |
|||||||
|
transformations |
||||||
|
transformations.cpp |
@ -0,0 +1,9 @@ |
|||||||
|
Import('env') |
||||||
|
|
||||||
|
d = Dir('.') |
||||||
|
|
||||||
|
env.Command( |
||||||
|
['transformations.so'], |
||||||
|
['transformations.pxd', 'transformations.pyx', |
||||||
|
'coordinates.cc', 'orientation.cc', 'coordinates.hpp', 'orientation.hpp'], |
||||||
|
'cd ' + d.path + ' && python3 setup.py build_ext --inplace') |
@ -0,0 +1,104 @@ |
|||||||
|
#define _USE_MATH_DEFINES |
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <cmath> |
||||||
|
#include <eigen3/Eigen/Dense> |
||||||
|
|
||||||
|
#include "coordinates.hpp" |
||||||
|
|
||||||
|
#define DEG2RAD(x) ((x) * M_PI / 180.0) |
||||||
|
#define RAD2DEG(x) ((x) * 180.0 / M_PI) |
||||||
|
|
||||||
|
|
||||||
|
double a = 6378137; |
||||||
|
double b = 6356752.3142; |
||||||
|
double esq = 6.69437999014 * 0.001; |
||||||
|
double e1sq = 6.73949674228 * 0.001; |
||||||
|
|
||||||
|
|
||||||
|
static Geodetic to_degrees(Geodetic geodetic){ |
||||||
|
geodetic.lat = RAD2DEG(geodetic.lat); |
||||||
|
geodetic.lon = RAD2DEG(geodetic.lon); |
||||||
|
return geodetic; |
||||||
|
} |
||||||
|
|
||||||
|
static Geodetic to_radians(Geodetic geodetic){ |
||||||
|
geodetic.lat = DEG2RAD(geodetic.lat); |
||||||
|
geodetic.lon = DEG2RAD(geodetic.lon); |
||||||
|
return geodetic; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
ECEF geodetic2ecef(Geodetic g){ |
||||||
|
g = to_radians(g); |
||||||
|
double xi = sqrt(1.0 - esq * pow(sin(g.lat), 2)); |
||||||
|
double x = (a / xi + g.alt) * cos(g.lat) * cos(g.lon); |
||||||
|
double y = (a / xi + g.alt) * cos(g.lat) * sin(g.lon); |
||||||
|
double z = (a / xi * (1.0 - esq) + g.alt) * sin(g.lat); |
||||||
|
return {x, y, z}; |
||||||
|
} |
||||||
|
|
||||||
|
Geodetic ecef2geodetic(ECEF e){ |
||||||
|
// Convert from ECEF to geodetic using Ferrari's methods
|
||||||
|
// https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#Ferrari.27s_solution
|
||||||
|
double x = e.x; |
||||||
|
double y = e.y; |
||||||
|
double z = e.z; |
||||||
|
|
||||||
|
double r = sqrt(x * x + y * y); |
||||||
|
double Esq = a * a - b * b; |
||||||
|
double F = 54 * b * b * z * z; |
||||||
|
double G = r * r + (1 - esq) * z * z - esq * Esq; |
||||||
|
double C = (esq * esq * F * r * r) / (pow(G, 3)); |
||||||
|
double S = cbrt(1 + C + sqrt(C * C + 2 * C)); |
||||||
|
double P = F / (3 * pow((S + 1 / S + 1), 2) * G * G); |
||||||
|
double Q = sqrt(1 + 2 * esq * esq * P); |
||||||
|
double r_0 = -(P * esq * r) / (1 + Q) + sqrt(0.5 * a * a*(1 + 1.0 / Q) - P * (1 - esq) * z * z / (Q * (1 + Q)) - 0.5 * P * r * r); |
||||||
|
double U = sqrt(pow((r - esq * r_0), 2) + z * z); |
||||||
|
double V = sqrt(pow((r - esq * r_0), 2) + (1 - esq) * z * z); |
||||||
|
double Z_0 = b * b * z / (a * V); |
||||||
|
double h = U * (1 - b * b / (a * V)); |
||||||
|
|
||||||
|
double lat = atan((z + e1sq * Z_0) / r); |
||||||
|
double lon = atan2(y, x); |
||||||
|
|
||||||
|
return to_degrees({lat, lon, h}); |
||||||
|
} |
||||||
|
|
||||||
|
LocalCoord::LocalCoord(Geodetic g, ECEF e){ |
||||||
|
init_ecef << e.x, e.y, e.z; |
||||||
|
|
||||||
|
g = to_radians(g); |
||||||
|
|
||||||
|
ned2ecef_matrix << |
||||||
|
-sin(g.lat)*cos(g.lon), -sin(g.lon), -cos(g.lat)*cos(g.lon), |
||||||
|
-sin(g.lat)*sin(g.lon), cos(g.lon), -cos(g.lat)*sin(g.lon), |
||||||
|
cos(g.lat), 0, -sin(g.lat); |
||||||
|
ecef2ned_matrix = ned2ecef_matrix.transpose(); |
||||||
|
} |
||||||
|
|
||||||
|
NED LocalCoord::ecef2ned(ECEF e) { |
||||||
|
Eigen::Vector3d ecef; |
||||||
|
ecef << e.x, e.y, e.z; |
||||||
|
|
||||||
|
Eigen::Vector3d ned = (ecef2ned_matrix * (ecef - init_ecef)); |
||||||
|
return {ned[0], ned[1], ned[2]}; |
||||||
|
} |
||||||
|
|
||||||
|
ECEF LocalCoord::ned2ecef(NED n) { |
||||||
|
Eigen::Vector3d ned; |
||||||
|
ned << n.n, n.e, n.d; |
||||||
|
|
||||||
|
Eigen::Vector3d ecef = (ned2ecef_matrix * ned) + init_ecef; |
||||||
|
return {ecef[0], ecef[1], ecef[2]}; |
||||||
|
} |
||||||
|
|
||||||
|
NED LocalCoord::geodetic2ned(Geodetic g) { |
||||||
|
ECEF e = ::geodetic2ecef(g); |
||||||
|
return ecef2ned(e); |
||||||
|
} |
||||||
|
|
||||||
|
Geodetic LocalCoord::ned2geodetic(NED n){ |
||||||
|
ECEF e = ned2ecef(n); |
||||||
|
return ::ecef2geodetic(e); |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
struct ECEF { |
||||||
|
double x, y, z; |
||||||
|
Eigen::Vector3d to_vector(){ |
||||||
|
return Eigen::Vector3d(x, y, z); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct NED { |
||||||
|
double n, e, d; |
||||||
|
}; |
||||||
|
|
||||||
|
struct Geodetic { |
||||||
|
double lat, lon, alt; |
||||||
|
bool radians=false; |
||||||
|
}; |
||||||
|
|
||||||
|
ECEF geodetic2ecef(Geodetic g); |
||||||
|
Geodetic ecef2geodetic(ECEF e); |
||||||
|
|
||||||
|
class LocalCoord { |
||||||
|
private: |
||||||
|
Eigen::Matrix3d ned2ecef_matrix; |
||||||
|
Eigen::Matrix3d ecef2ned_matrix; |
||||||
|
Eigen::Vector3d init_ecef; |
||||||
|
public: |
||||||
|
LocalCoord(Geodetic g, ECEF e); |
||||||
|
LocalCoord(Geodetic g) : LocalCoord(g, ::geodetic2ecef(g)) {} |
||||||
|
LocalCoord(ECEF e) : LocalCoord(::ecef2geodetic(e), e) {} |
||||||
|
|
||||||
|
NED ecef2ned(ECEF e); |
||||||
|
ECEF ned2ecef(NED n); |
||||||
|
NED geodetic2ned(Geodetic g); |
||||||
|
Geodetic ned2geodetic(NED n); |
||||||
|
}; |
@ -0,0 +1,147 @@ |
|||||||
|
#define _USE_MATH_DEFINES |
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <cmath> |
||||||
|
#include <eigen3/Eigen/Dense> |
||||||
|
|
||||||
|
#include "orientation.hpp" |
||||||
|
#include "coordinates.hpp" |
||||||
|
|
||||||
|
Eigen::Quaterniond ensure_unique(Eigen::Quaterniond quat){ |
||||||
|
if (quat.w() > 0){ |
||||||
|
return quat; |
||||||
|
} else { |
||||||
|
return Eigen::Quaterniond(-quat.w(), -quat.x(), -quat.y(), -quat.z()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Quaterniond euler2quat(Eigen::Vector3d euler){ |
||||||
|
Eigen::Quaterniond q; |
||||||
|
|
||||||
|
q = Eigen::AngleAxisd(euler(2), Eigen::Vector3d::UnitZ()) |
||||||
|
* Eigen::AngleAxisd(euler(1), Eigen::Vector3d::UnitY()) |
||||||
|
* Eigen::AngleAxisd(euler(0), Eigen::Vector3d::UnitX()); |
||||||
|
return ensure_unique(q); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Eigen::Vector3d quat2euler(Eigen::Quaterniond quat){ |
||||||
|
// TODO: switch to eigen implementation if the range of the Euler angles doesn't matter anymore
|
||||||
|
// Eigen::Vector3d euler = quat.toRotationMatrix().eulerAngles(2, 1, 0);
|
||||||
|
// return {euler(2), euler(1), euler(0)};
|
||||||
|
double gamma = atan2(2 * (quat.w() * quat.x() + quat.y() * quat.z()), 1 - 2 * (quat.x()*quat.x() + quat.y()*quat.y())); |
||||||
|
double theta = asin(2 * (quat.w() * quat.y() - quat.z() * quat.x())); |
||||||
|
double psi = atan2(2 * (quat.w() * quat.z() + quat.x() * quat.y()), 1 - 2 * (quat.y()*quat.y() + quat.z()*quat.z())); |
||||||
|
return {gamma, theta, psi}; |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Matrix3d quat2rot(Eigen::Quaterniond quat){ |
||||||
|
return quat.toRotationMatrix(); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Quaterniond rot2quat(Eigen::Matrix3d rot){ |
||||||
|
return ensure_unique(Eigen::Quaterniond(rot)); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Matrix3d euler2rot(Eigen::Vector3d euler){ |
||||||
|
return quat2rot(euler2quat(euler)); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Vector3d rot2euler(Eigen::Matrix3d rot){ |
||||||
|
return quat2euler(rot2quat(rot)); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Matrix3d rot_matrix(double roll, double pitch, double yaw){ |
||||||
|
return euler2rot({roll, pitch, yaw}); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Matrix3d rot(Eigen::Vector3d axis, double angle){ |
||||||
|
Eigen::Quaterniond q; |
||||||
|
q = Eigen::AngleAxisd(angle, axis); |
||||||
|
return q.toRotationMatrix(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Eigen::Vector3d ecef_euler_from_ned(ECEF ecef_init, Eigen::Vector3d ned_pose) { |
||||||
|
/*
|
||||||
|
Using Rotations to Build Aerospace Coordinate Systems |
||||||
|
Don Koks |
||||||
|
https://apps.dtic.mil/dtic/tr/fulltext/u2/a484864.pdf
|
||||||
|
*/ |
||||||
|
LocalCoord converter = LocalCoord(ecef_init); |
||||||
|
Eigen::Vector3d zero = ecef_init.to_vector(); |
||||||
|
|
||||||
|
Eigen::Vector3d x0 = converter.ned2ecef({1, 0, 0}).to_vector() - zero; |
||||||
|
Eigen::Vector3d y0 = converter.ned2ecef({0, 1, 0}).to_vector() - zero; |
||||||
|
Eigen::Vector3d z0 = converter.ned2ecef({0, 0, 1}).to_vector() - zero; |
||||||
|
|
||||||
|
Eigen::Vector3d x1 = rot(z0, ned_pose(2)) * x0; |
||||||
|
Eigen::Vector3d y1 = rot(z0, ned_pose(2)) * y0; |
||||||
|
Eigen::Vector3d z1 = rot(z0, ned_pose(2)) * z0; |
||||||
|
|
||||||
|
Eigen::Vector3d x2 = rot(y1, ned_pose(1)) * x1; |
||||||
|
Eigen::Vector3d y2 = rot(y1, ned_pose(1)) * y1; |
||||||
|
Eigen::Vector3d z2 = rot(y1, ned_pose(1)) * z1; |
||||||
|
|
||||||
|
Eigen::Vector3d x3 = rot(x2, ned_pose(0)) * x2; |
||||||
|
Eigen::Vector3d y3 = rot(x2, ned_pose(0)) * y2; |
||||||
|
|
||||||
|
|
||||||
|
x0 = Eigen::Vector3d(1, 0, 0); |
||||||
|
y0 = Eigen::Vector3d(0, 1, 0); |
||||||
|
z0 = Eigen::Vector3d(0, 0, 1); |
||||||
|
|
||||||
|
double psi = atan2(x3.dot(y0), x3.dot(x0)); |
||||||
|
double theta = atan2(-x3.dot(z0), sqrt(pow(x3.dot(x0), 2) + pow(x3.dot(y0), 2))); |
||||||
|
|
||||||
|
y2 = rot(z0, psi) * y0; |
||||||
|
z2 = rot(y2, theta) * z0; |
||||||
|
|
||||||
|
double phi = atan2(y3.dot(z2), y3.dot(y2)); |
||||||
|
|
||||||
|
return {phi, theta, psi}; |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Vector3d ned_euler_from_ecef(ECEF ecef_init, Eigen::Vector3d ecef_pose){ |
||||||
|
/*
|
||||||
|
Using Rotations to Build Aerospace Coordinate Systems |
||||||
|
Don Koks |
||||||
|
https://apps.dtic.mil/dtic/tr/fulltext/u2/a484864.pdf
|
||||||
|
*/ |
||||||
|
LocalCoord converter = LocalCoord(ecef_init); |
||||||
|
|
||||||
|
Eigen::Vector3d x0 = Eigen::Vector3d(1, 0, 0); |
||||||
|
Eigen::Vector3d y0 = Eigen::Vector3d(0, 1, 0); |
||||||
|
Eigen::Vector3d z0 = Eigen::Vector3d(0, 0, 1); |
||||||
|
|
||||||
|
Eigen::Vector3d x1 = rot(z0, ecef_pose(2)) * x0; |
||||||
|
Eigen::Vector3d y1 = rot(z0, ecef_pose(2)) * y0; |
||||||
|
Eigen::Vector3d z1 = rot(z0, ecef_pose(2)) * z0; |
||||||
|
|
||||||
|
Eigen::Vector3d x2 = rot(y1, ecef_pose(1)) * x1; |
||||||
|
Eigen::Vector3d y2 = rot(y1, ecef_pose(1)) * y1; |
||||||
|
Eigen::Vector3d z2 = rot(y1, ecef_pose(1)) * z1; |
||||||
|
|
||||||
|
Eigen::Vector3d x3 = rot(x2, ecef_pose(0)) * x2; |
||||||
|
Eigen::Vector3d y3 = rot(x2, ecef_pose(0)) * y2; |
||||||
|
|
||||||
|
Eigen::Vector3d zero = ecef_init.to_vector(); |
||||||
|
x0 = converter.ned2ecef({1, 0, 0}).to_vector() - zero; |
||||||
|
y0 = converter.ned2ecef({0, 1, 0}).to_vector() - zero; |
||||||
|
z0 = converter.ned2ecef({0, 0, 1}).to_vector() - zero; |
||||||
|
|
||||||
|
double psi = atan2(x3.dot(y0), x3.dot(x0)); |
||||||
|
double theta = atan2(-x3.dot(z0), sqrt(pow(x3.dot(x0), 2) + pow(x3.dot(y0), 2))); |
||||||
|
|
||||||
|
y2 = rot(z0, psi) * y0; |
||||||
|
z2 = rot(y2, theta) * z0; |
||||||
|
|
||||||
|
double phi = atan2(y3.dot(z2), y3.dot(y2)); |
||||||
|
|
||||||
|
return {phi, theta, psi}; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(void){ |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
#pragma once |
||||||
|
#include <eigen3/Eigen/Dense> |
||||||
|
#include "coordinates.hpp" |
||||||
|
|
||||||
|
|
||||||
|
Eigen::Quaterniond ensure_unique(Eigen::Quaterniond quat); |
||||||
|
|
||||||
|
Eigen::Quaterniond euler2quat(Eigen::Vector3d euler); |
||||||
|
Eigen::Vector3d quat2euler(Eigen::Quaterniond quat); |
||||||
|
Eigen::Matrix3d quat2rot(Eigen::Quaterniond quat); |
||||||
|
Eigen::Quaterniond rot2quat(Eigen::Matrix3d rot); |
||||||
|
Eigen::Matrix3d euler2rot(Eigen::Vector3d euler); |
||||||
|
Eigen::Vector3d rot2euler(Eigen::Matrix3d rot); |
||||||
|
Eigen::Matrix3d rot_matrix(double roll, double pitch, double yaw); |
||||||
|
Eigen::Matrix3d rot(Eigen::Vector3d axis, double angle); |
||||||
|
Eigen::Vector3d ecef_euler_from_ned(ECEF ecef_init, Eigen::Vector3d ned_pose); |
||||||
|
Eigen::Vector3d ned_euler_from_ecef(ECEF ecef_init, Eigen::Vector3d ecef_pose); |
@ -0,0 +1,42 @@ |
|||||||
|
import os |
||||||
|
import numpy |
||||||
|
import sysconfig |
||||||
|
|
||||||
|
from Cython.Build import cythonize |
||||||
|
from Cython.Distutils import build_ext |
||||||
|
from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module |
||||||
|
|
||||||
|
def get_ext_filename_without_platform_suffix(filename): |
||||||
|
name, ext = os.path.splitext(filename) |
||||||
|
ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') |
||||||
|
|
||||||
|
if ext_suffix == ext: |
||||||
|
return filename |
||||||
|
|
||||||
|
ext_suffix = ext_suffix.replace(ext, '') |
||||||
|
idx = name.find(ext_suffix) |
||||||
|
|
||||||
|
if idx == -1: |
||||||
|
return filename |
||||||
|
else: |
||||||
|
return name[:idx] + ext |
||||||
|
|
||||||
|
|
||||||
|
class BuildExtWithoutPlatformSuffix(build_ext): |
||||||
|
def get_ext_filename(self, ext_name): |
||||||
|
filename = super().get_ext_filename(ext_name) |
||||||
|
return get_ext_filename_without_platform_suffix(filename) |
||||||
|
|
||||||
|
|
||||||
|
setup( |
||||||
|
name='Cython transformations wrapper', |
||||||
|
cmdclass={'build_ext': BuildExtWithoutPlatformSuffix}, |
||||||
|
ext_modules=cythonize( |
||||||
|
Extension( |
||||||
|
"transformations", |
||||||
|
sources=["transformations.pyx"], |
||||||
|
language="c++", |
||||||
|
extra_compile_args=["-std=c++14"], |
||||||
|
include_dirs=[numpy.get_include()], |
||||||
|
) |
||||||
|
)) |
@ -0,0 +1,68 @@ |
|||||||
|
from libcpp cimport bool |
||||||
|
|
||||||
|
cdef extern from "orientation.cc": |
||||||
|
pass |
||||||
|
|
||||||
|
cdef extern from "orientation.hpp": |
||||||
|
cdef cppclass Quaternion "Eigen::Quaterniond": |
||||||
|
Quaternion() |
||||||
|
Quaternion(double, double, double, double) |
||||||
|
double w() |
||||||
|
double x() |
||||||
|
double y() |
||||||
|
double z() |
||||||
|
|
||||||
|
cdef cppclass Vector3 "Eigen::Vector3d": |
||||||
|
Vector3() |
||||||
|
Vector3(double, double, double) |
||||||
|
double operator()(int) |
||||||
|
|
||||||
|
cdef cppclass Matrix3 "Eigen::Matrix3d": |
||||||
|
Matrix3() |
||||||
|
Matrix3(double*) |
||||||
|
|
||||||
|
double operator()(int, int) |
||||||
|
|
||||||
|
Quaternion euler2quat(Vector3) |
||||||
|
Vector3 quat2euler(Quaternion) |
||||||
|
Matrix3 quat2rot(Quaternion) |
||||||
|
Quaternion rot2quat(Matrix3) |
||||||
|
Vector3 rot2euler(Matrix3) |
||||||
|
Matrix3 euler2rot(Vector3) |
||||||
|
Matrix3 rot_matrix(double, double, double) |
||||||
|
Vector3 ecef_euler_from_ned(ECEF, Vector3) |
||||||
|
Vector3 ned_euler_from_ecef(ECEF, Vector3) |
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "coordinates.cc": |
||||||
|
cdef struct ECEF: |
||||||
|
double x |
||||||
|
double y |
||||||
|
double z |
||||||
|
|
||||||
|
cdef struct NED: |
||||||
|
double n |
||||||
|
double e |
||||||
|
double d |
||||||
|
|
||||||
|
cdef struct Geodetic: |
||||||
|
double lat |
||||||
|
double lon |
||||||
|
double alt |
||||||
|
bool radians |
||||||
|
|
||||||
|
ECEF geodetic2ecef(Geodetic) |
||||||
|
Geodetic ecef2geodetic(ECEF) |
||||||
|
|
||||||
|
cdef cppclass LocalCoord_c "LocalCoord": |
||||||
|
LocalCoord_c(Geodetic, ECEF) |
||||||
|
LocalCoord_c(Geodetic) |
||||||
|
LocalCoord_c(ECEF) |
||||||
|
|
||||||
|
NED ecef2ned(ECEF) |
||||||
|
ECEF ned2ecef(NED) |
||||||
|
NED geodetic2ned(Geodetic) |
||||||
|
Geodetic ned2geodetic(NED) |
||||||
|
|
||||||
|
cdef extern from "coordinates.hpp": |
||||||
|
pass |
@ -0,0 +1,156 @@ |
|||||||
|
from transformations cimport Matrix3, Vector3, Quaternion |
||||||
|
from transformations cimport ECEF, NED, Geodetic |
||||||
|
|
||||||
|
from transformations cimport euler2quat as euler2quat_c |
||||||
|
from transformations cimport quat2euler as quat2euler_c |
||||||
|
from transformations cimport quat2rot as quat2rot_c |
||||||
|
from transformations cimport rot2quat as rot2quat_c |
||||||
|
from transformations cimport euler2rot as euler2rot_c |
||||||
|
from transformations cimport rot2euler as rot2euler_c |
||||||
|
from transformations cimport rot_matrix as rot_matrix_c |
||||||
|
from transformations cimport ecef_euler_from_ned as ecef_euler_from_ned_c |
||||||
|
from transformations cimport ned_euler_from_ecef as ned_euler_from_ecef_c |
||||||
|
from transformations cimport geodetic2ecef as geodetic2ecef_c |
||||||
|
from transformations cimport ecef2geodetic as ecef2geodetic_c |
||||||
|
from transformations cimport LocalCoord_c |
||||||
|
|
||||||
|
|
||||||
|
import cython |
||||||
|
import numpy as np |
||||||
|
cimport numpy as np |
||||||
|
|
||||||
|
cdef np.ndarray[double, ndim=2] matrix2numpy(Matrix3 m): |
||||||
|
return np.array([ |
||||||
|
[m(0, 0), m(0, 1), m(0, 2)], |
||||||
|
[m(1, 0), m(1, 1), m(1, 2)], |
||||||
|
[m(2, 0), m(2, 1), m(2, 2)], |
||||||
|
]) |
||||||
|
|
||||||
|
cdef Matrix3 numpy2matrix (np.ndarray[double, ndim=2, mode="fortran"] m): |
||||||
|
assert m.shape[0] == 3 |
||||||
|
assert m.shape[1] == 3 |
||||||
|
return Matrix3(<double*>m.data) |
||||||
|
|
||||||
|
cdef ECEF list2ecef(ecef): |
||||||
|
cdef ECEF e; |
||||||
|
e.x = ecef[0] |
||||||
|
e.y = ecef[1] |
||||||
|
e.z = ecef[2] |
||||||
|
return e |
||||||
|
|
||||||
|
cdef NED list2ned(ned): |
||||||
|
cdef NED n; |
||||||
|
n.n = ned[0] |
||||||
|
n.e = ned[1] |
||||||
|
n.d = ned[2] |
||||||
|
return n |
||||||
|
|
||||||
|
cdef Geodetic list2geodetic(geodetic): |
||||||
|
cdef Geodetic g |
||||||
|
g.lat = geodetic[0] |
||||||
|
g.lon = geodetic[1] |
||||||
|
g.alt = geodetic[2] |
||||||
|
return g |
||||||
|
|
||||||
|
def euler2quat_single(euler): |
||||||
|
cdef Vector3 e = Vector3(euler[0], euler[1], euler[2]) |
||||||
|
cdef Quaternion q = euler2quat_c(e) |
||||||
|
return [q.w(), q.x(), q.y(), q.z()] |
||||||
|
|
||||||
|
def quat2euler_single(quat): |
||||||
|
cdef Quaternion q = Quaternion(quat[0], quat[1], quat[2], quat[3]) |
||||||
|
cdef Vector3 e = quat2euler_c(q); |
||||||
|
return [e(0), e(1), e(2)] |
||||||
|
|
||||||
|
def quat2rot_single(quat): |
||||||
|
cdef Quaternion q = Quaternion(quat[0], quat[1], quat[2], quat[3]) |
||||||
|
cdef Matrix3 r = quat2rot_c(q) |
||||||
|
return matrix2numpy(r) |
||||||
|
|
||||||
|
def rot2quat_single(rot): |
||||||
|
cdef Matrix3 r = numpy2matrix(np.asfortranarray(rot, dtype=np.double)) |
||||||
|
cdef Quaternion q = rot2quat_c(r) |
||||||
|
return [q.w(), q.x(), q.y(), q.z()] |
||||||
|
|
||||||
|
def euler2rot_single(euler): |
||||||
|
cdef Vector3 e = Vector3(euler[0], euler[1], euler[2]) |
||||||
|
cdef Matrix3 r = euler2rot_c(e) |
||||||
|
return matrix2numpy(r) |
||||||
|
|
||||||
|
def rot2euler_single(rot): |
||||||
|
cdef Matrix3 r = numpy2matrix(np.asfortranarray(rot, dtype=np.double)) |
||||||
|
cdef Vector3 e = rot2euler_c(r) |
||||||
|
return [e(0), e(1), e(2)] |
||||||
|
|
||||||
|
def rot_matrix(roll, pitch, yaw): |
||||||
|
return matrix2numpy(rot_matrix_c(roll, pitch, yaw)) |
||||||
|
|
||||||
|
def ecef_euler_from_ned_single(ecef_init, ned_pose): |
||||||
|
cdef ECEF init = list2ecef(ecef_init) |
||||||
|
cdef Vector3 pose = Vector3(ned_pose[0], ned_pose[1], ned_pose[2]) |
||||||
|
|
||||||
|
cdef Vector3 e = ecef_euler_from_ned_c(init, pose) |
||||||
|
return [e(0), e(1), e(2)] |
||||||
|
|
||||||
|
def ned_euler_from_ecef_single(ecef_init, ecef_pose): |
||||||
|
cdef ECEF init = list2ecef(ecef_init) |
||||||
|
cdef Vector3 pose = Vector3(ecef_pose[0], ecef_pose[1], ecef_pose[2]) |
||||||
|
|
||||||
|
cdef Vector3 e = ned_euler_from_ecef_c(init, pose) |
||||||
|
return [e(0), e(1), e(2)] |
||||||
|
|
||||||
|
def geodetic2ecef_single(geodetic): |
||||||
|
cdef Geodetic g = list2geodetic(geodetic) |
||||||
|
cdef ECEF e = geodetic2ecef_c(g) |
||||||
|
return [e.x, e.y, e.z] |
||||||
|
|
||||||
|
def ecef2geodetic_single(ecef): |
||||||
|
cdef ECEF e = list2ecef(ecef) |
||||||
|
cdef Geodetic g = ecef2geodetic_c(e) |
||||||
|
return [g.lat, g.lon, g.alt] |
||||||
|
|
||||||
|
|
||||||
|
cdef class LocalCoord: |
||||||
|
cdef LocalCoord_c * lc |
||||||
|
|
||||||
|
def __init__(self, geodetic=None, ecef=None): |
||||||
|
assert (geodetic is not None) or (ecef is not None) |
||||||
|
if geodetic is not None: |
||||||
|
self.lc = new LocalCoord_c(list2geodetic(geodetic)) |
||||||
|
elif ecef is not None: |
||||||
|
self.lc = new LocalCoord_c(list2ecef(ecef)) |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def from_geodetic(cls, geodetic): |
||||||
|
return cls(geodetic=geodetic) |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def from_ecef(cls, ecef): |
||||||
|
return cls(ecef=ecef) |
||||||
|
|
||||||
|
def ecef2ned_single(self, ecef): |
||||||
|
assert self.lc |
||||||
|
cdef ECEF e = list2ecef(ecef) |
||||||
|
cdef NED n = self.lc.ecef2ned(e) |
||||||
|
return [n.n, n.e, n.d] |
||||||
|
|
||||||
|
def ned2ecef_single(self, ned): |
||||||
|
assert self.lc |
||||||
|
cdef NED n = list2ned(ned) |
||||||
|
cdef ECEF e = self.lc.ned2ecef(n) |
||||||
|
return [e.x, e.y, e.z] |
||||||
|
|
||||||
|
def geodetic2ned_single(self, geodetic): |
||||||
|
assert self.lc |
||||||
|
cdef Geodetic g = list2geodetic(geodetic) |
||||||
|
cdef NED n = self.lc.geodetic2ned(g) |
||||||
|
return [n.n, n.e, n.d] |
||||||
|
|
||||||
|
def ned2geodetic_single(self, ned): |
||||||
|
assert self.lc |
||||||
|
cdef NED n = list2ned(ned) |
||||||
|
cdef Geodetic g = self.lc.ned2geodetic(n) |
||||||
|
return [g.lat, g.lon, g.alt] |
||||||
|
|
||||||
|
def __dealloc__(self): |
||||||
|
del self.lc |
@ -1 +1 @@ |
|||||||
0533f640ab27f7b5af57aa4ebf4a29200550b3e8 |
834f4cd7e90ff266ced8ea142d7d7d05076186aa |
Loading…
Reference in new issue