This repository has been archived by the owner on Aug 13, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Changed repo structure from src/ to src/pyfme
* Added aricraft folder with an empty file that will contain the test aircraft. * Added some functions in utils/coordinates to change from body to local horizon coords and from local horizon coords to body (tested).
- Loading branch information
Showing
12 changed files
with
259 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
__pycache__ | ||
.cache | ||
|
||
*.py[cod] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
|
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Frames of Reference orientation functions | ||
""" | ||
|
||
import numpy as np | ||
|
||
# Reassing for readability | ||
sin = np.sin | ||
cos = np.cos | ||
|
||
|
||
def check_theta_phi_psi_range(theta, phi, psi): | ||
"""Check theta, phi, psi values are inside the defined range. This | ||
comprobation can also detect if the value of the angle is in degrees in | ||
some cases. | ||
""" | ||
|
||
theta_min, theta_max = (-np.pi/2, np.pi/2) | ||
phi_min, phi_max = (-np.pi, np.pi) | ||
psi_min, psi_max = (0, 2 * np.pi) | ||
|
||
if not (theta_min <= theta <= theta_max): | ||
raise ValueError('Theta value is not inside correct range') | ||
elif not (phi_min <= phi <= phi_max): | ||
raise ValueError('Phi value is not inside correct range') | ||
elif not (psi_min <= phi <= psi_max): | ||
raise ValueError('Psi value is not inside correct range') | ||
|
||
|
||
|
||
def body2hor(body_coords, theta, phi, psi): | ||
"""Transforms the vector coordinates in body frame of reference to local | ||
horizon frame of reference. | ||
Parameters | ||
---------- | ||
body_coords : array_like | ||
3 dimensional vector with (x,y,z) coordinates in body axes. | ||
theta : float | ||
Pitch (or elevation) angle (rad). | ||
phi : float | ||
Bank angle (rad). | ||
psi : float | ||
Yaw (or azimuth) angle (rad) | ||
Returns | ||
------- | ||
hor_coords : array_like | ||
3 dimensional vector with (x,y,z) coordinates in local horizon axes. | ||
Raises | ||
------ | ||
ValueError | ||
If the values of the euler angles are outside the proper ranges. | ||
See Also | ||
-------- | ||
`hor2body` function. | ||
Notes | ||
----- | ||
See [1] or [2] for frame of reference definition. | ||
Note that in order to avoid ambiguities ranges in angles are limited to: | ||
* -pi/2 <= theta <= pi/2 | ||
* -pi <= phi <= pi | ||
* 0 <= psi <= 2*pi | ||
References | ||
---------- | ||
.. [1] B. Etkin, "Dynamics of Atmospheric Flight," Courier Corporation, | ||
pp. 104-120, 2012. | ||
.. [2] Gómez Tierno, M.A. et al, "Mecánica del Vuelo," Garceta, pp. 1-12, | ||
2012 | ||
""" | ||
|
||
check_theta_phi_psi_range(theta, phi, psi) | ||
|
||
# Transformation matrix from body to local horizon | ||
Lhb = np.array([ | ||
[cos(theta) * cos(psi), | ||
sin(phi) * sin(theta) * cos(psi) - cos(phi) * sin(psi), | ||
cos(phi) * sin(theta) * cos(psi) + sin(phi) * sin(psi)], | ||
[cos(theta) * sin(psi), | ||
sin(phi) * sin(theta) * sin(psi) + cos(phi) * cos(psi), | ||
cos(phi) * sin(theta) * sin(psi) - sin(phi) * cos(psi)], | ||
[- sin(theta), | ||
sin(phi) * cos(theta), | ||
cos(phi) * cos(theta)] | ||
]) | ||
|
||
hor_coords = Lhb.dot(body_coords) | ||
|
||
return hor_coords | ||
|
||
|
||
def hor2body(hor_coords, theta, phi, psi): | ||
"""Transforms the vector coordinates in local horizon frame of reference | ||
to body frame of reference. | ||
Parameters | ||
---------- | ||
hor_coords : array_like | ||
3 dimensional vector with (x,y,z) coordinates in local horizon axes. | ||
theta : float | ||
Pitch (or elevation) angle (rad). | ||
phi : float | ||
Bank angle (rad). | ||
psi : float | ||
Yaw (or azimuth) angle (rad) | ||
Returns | ||
------- | ||
body_coords : array_like | ||
3 dimensional vector with (x,y,z) coordinates in body axes. | ||
Raises | ||
------ | ||
ValueError | ||
If the values of the euler angles are outside the proper ranges. | ||
See Also | ||
-------- | ||
`body2hor` function. | ||
Notes | ||
----- | ||
See [1] or [2] for frame of reference definition. | ||
Note that in order to avoid ambiguities ranges in angles are limited to: | ||
* -pi/2 <= theta <= pi/2 | ||
* -pi <= phi <= pi | ||
* 0 <= psi <= 2*pi | ||
References | ||
---------- | ||
.. [1] B. Etkin, "Dynamics of Atmospheric Flight," Courier Corporation, | ||
pp. 104-120, 2012. | ||
.. [2] Gómez Tierno, M.A. et al, "Mecánica del Vuelo," Garceta, pp. 1-12, | ||
2012 | ||
""" | ||
|
||
check_theta_phi_psi_range(theta, phi, psi) | ||
|
||
# Transformation matrix local horizon to body | ||
Lbh = np.array([ | ||
[cos(theta) * cos(psi), | ||
cos(theta) * sin(psi), | ||
- sin(theta)], | ||
[sin(phi) * sin(theta) * cos(psi) - cos(phi) * sin(psi), | ||
sin(phi) * sin(theta) * sin(psi) + cos(phi) * cos(psi), | ||
sin(phi) * cos(theta)], | ||
[cos(phi) * sin(theta) * cos(psi) + sin(phi) * sin(psi), | ||
cos(phi) * sin(theta) * sin(psi) - sin(phi) * cos(psi), | ||
cos(phi) * cos(theta)] | ||
]) | ||
|
||
body_coords = Lbh.dot(hor_coords) | ||
|
||
return body_coords |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Frames of Reference orientation test functions | ||
""" | ||
import pytest | ||
|
||
import numpy as np | ||
from numpy.testing import (assert_equal, assert_almost_equal, | ||
assert_array_equal, assert_array_almost_equal) | ||
|
||
|
||
from pyfme.utils.coordinates import (body2hor, hor2body, | ||
check_theta_phi_psi_range) | ||
|
||
|
||
def test_check_theta_phi_psi_range(): | ||
|
||
wrong_values = (3 * np.pi, - 3 * np.pi) | ||
|
||
for value in wrong_values: | ||
# 0 is always a correct value | ||
angles = [0, 0, 0] | ||
for ii in range(3): | ||
angles[ii] = value | ||
with pytest.raises(ValueError): | ||
check_theta_phi_psi_range(*angles) | ||
|
||
|
||
def test_body2hor(): | ||
|
||
# Test with a pitch rotation | ||
vector_body = np.array([1, 1, 1]) | ||
theta, phi, psi = np.deg2rad(45), 0, 0 | ||
|
||
vector_hor = body2hor(vector_body, theta, phi, psi) | ||
|
||
vector_hor_expected = np.array([2 * 0.70710678118654757, 1, 0]) | ||
|
||
assert_array_almost_equal(vector_hor, vector_hor_expected) | ||
|
||
# Test with a roll rotation | ||
vector_body = np.array([1, 1, 1]) | ||
theta, phi, psi = 0, np.deg2rad(45), 0 | ||
|
||
vector_hor = body2hor(vector_body, theta, phi, psi) | ||
|
||
vector_hor_expected = np.array([1, 0, 2 * 0.70710678118654757]) | ||
|
||
assert_array_almost_equal(vector_hor, vector_hor_expected) | ||
|
||
# Test with a yaw rotation | ||
vector_body = np.array([1, 1, 1]) | ||
theta, phi, psi = 0, 0, np.deg2rad(45) | ||
|
||
vector_hor = body2hor(vector_body, theta, phi, psi) | ||
|
||
vector_hor_expected = np.array([0, 2 * 0.70710678118654757, 1]) | ||
|
||
assert_array_almost_equal(vector_hor, vector_hor_expected) | ||
|
||
|
||
def test_hor2body(): | ||
|
||
# Test with a pitch rotation | ||
vector_hor = np.array([2 * 0.70710678118654757, 1, 0]) | ||
theta, phi, psi = np.deg2rad(45), 0, 0 | ||
|
||
vector_body_expected = np.array([1, 1, 1]) | ||
|
||
vector_body = hor2body(vector_hor, theta, phi, psi) | ||
|
||
assert_array_almost_equal(vector_body, vector_body_expected) | ||
|
||
# Test with a roll rotation | ||
vector_hor = np.array([1, 0, 2 * 0.70710678118654757]) | ||
theta, phi, psi = 0, np.deg2rad(45), 0 | ||
|
||
vector_body_expected = np.array([1, 1, 1]) | ||
|
||
vector_body = hor2body(vector_hor, theta, phi, psi) | ||
|
||
assert_array_almost_equal(vector_body, vector_body_expected) | ||
|
||
# Test with a yaw rotation | ||
vector_hor = np.array([0, 2 * 0.70710678118654757, 1]) | ||
theta, phi, psi = 0, 0, np.deg2rad(45) | ||
|
||
vector_body_expected = np.array([1, 1, 1]) | ||
|
||
vector_body = hor2body(vector_hor, theta, phi, psi) | ||
|
||
assert_array_almost_equal(vector_body, vector_body_expected) |