Skip to content

Commit

Permalink
Add from_attitude class method for more permissive init
Browse files Browse the repository at this point in the history
  • Loading branch information
taldcroft committed Oct 22, 2021
1 parent a09dcb8 commit ca4dd3d
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
61 changes: 61 additions & 0 deletions Quaternion/Quaternion.py
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,67 @@ def rotate_about_vec(self, vec, alpha):
att_out = dq * self
return att_out

@classmethod
def from_attitude(cls, att):
"""Initial Quat from attitude(s)
This allows initialization from several possibilities that are less
strict than the normal initializer:
- Quat already (returns same)
- Input that works with Quat already
- Input that initializes a float ndarray which has shape[-1] == 3 or 4
- Input that initializes an object ndarray where each element inits Quat,
e.g. a list of Quat
Parameters
----------
att : Quaternion-like
Attitude(s)
Returns
-------
Quat
Attitude(s) as a Quat
"""
if isinstance(att, Quat):
return att

# Input that works with Quat already
try:
att = cls(att)
except Exception:
pass
else:
return att

# Input that initializes a float ndarray which has shape[-1] == 3 or 4
try:
att_array = np.asarray(att, dtype=np.float64)
except Exception:
pass
else:
if att_array.shape[-1] == 4:
return cls(q=att_array)
elif att_array.shape[-1] == 3:
return cls(equatorial=att_array)
else:
raise ValueError("Float input must be a Nx3 or Nx4 array")

# Input that initializes an object ndarray where each element inits Quat,
# e.g. a list of Quat
try:
att_array = np.asarray(att, dtype=object)
qs = []
for val in att_array.ravel():
qs.append(val.q if isinstance(val, Quat) else cls(val).q)
qs = np.array(qs, dtype=np.float64).reshape(att_array.shape + (4,))
return cls(q=qs)
except Exception:
pass

raise ValueError(f"Unable to initialize {cls.__name__} from {att!r}")



def normalize(array):
"""
Expand Down
38 changes: 38 additions & 0 deletions Quaternion/tests/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,44 @@ def test_pickle():
[0.34202014, 0.46984631, 0.81379768]]))


def test_init_quat_from_attitude():
# Basic tests for Quat.from_attitude
q = Quat.from_attitude([Quat([0, 1, 2]),
Quat([3, 4, 5])])
# 1-d list of Quat
assert np.allclose(q.equatorial, [[0, 1, 2],
[3, 4, 5]])

# 2-d list of Quat
q = Quat.from_attitude([[Quat([0, 1, 2]), Quat([3, 4, 5])]])
assert np.allclose(q.equatorial, [[[0, 1, 2],
[3, 4, 5]]])

# 1-d list of equatorial floats
q = Quat.from_attitude([[0, 1, 2], [3, 4, 5]])
assert np.allclose(q.equatorial, [[[0, 1, 2],
[3, 4, 5]]])

# Heterogenous list of floats
q = Quat.from_attitude([[0, 1, 2], [0, 1, 0, 0]])
assert np.allclose(q.equatorial, [[0, 1, 2],
[180, 0, 180]])

# Bad 1-d list of equatorial floats
with pytest.raises(ValueError, match="Float input must be a Nx3 or Nx4 array"):
q = Quat.from_attitude([[0, 1, 2, 4, 5], [3, 4, 5, 6, 7]])

# 1-d list of 4-vectors
q_list = [[0, 0, 1, 0], [0, 1, 0, 0]]
q = Quat.from_attitude(q_list)
assert np.allclose(q.q, q_list)

# Bad input
with pytest.raises(ValueError, match="Unable to initialize Quat from 'blah'"):
Quat.from_attitude('blah')



def test_rotate_x_to_vec_regress():
"""Note that truth values are just results from original code in Ska.quatutil.
They have not been independently validated"""
Expand Down

0 comments on commit ca4dd3d

Please sign in to comment.