Skip to content

Commit 73f1f94

Browse files
committed
✨ Add Euler angles => CRP and MRP conversions
1 parent f639d02 commit 73f1f94

7 files changed

Lines changed: 135 additions & 2 deletions

File tree

src/ReferenceFrameRotations.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ include("deprecations.jl")
6060

6161
include("./conversions/angle_to_angle.jl")
6262
include("./conversions/angle_to_angleaxis.jl")
63+
include("./conversions/angle_to_crp.jl")
6364
include("./conversions/angle_to_dcm.jl")
65+
include("./conversions/angle_to_mrp.jl")
6466
include("./conversions/angle_to_quat.jl")
6567
include("./conversions/angle_to_rot.jl")
6668
include("./conversions/angleaxis_to_angle.jl")

src/conversions/angle_to_crp.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
## Description #############################################################################
2+
#
3+
# Functions related to the conversion from Euler angles to CRP.
4+
#
5+
############################################################################################
6+
7+
export angle_to_crp
8+
9+
"""
10+
angle_to_crp(θ₁::Number, θ₂::Number, θ₃::Number, rot_seq::Symbol = :ZYX) -> CRP
11+
angle_to_crp(Θ::EulerAngles) -> CRP
12+
13+
Convert the Euler angles `θ₁`, `θ₂`, and `θ₃` [rad] with the rotation sequence `rot_seq` to
14+
classical Rodrigues parameters.
15+
16+
Those values can also be passed inside the structure `Θ` (see [`EulerAngles`](@ref)).
17+
18+
The rotation sequence is defined by a `:Symbol`. The possible values are: `:XYX`, `XYZ`,
19+
`:XZX`, `:XZY`, `:YXY`, `:YXZ`, `:YZX`, `:YZY`, `:ZXY`, `:ZXZ`, `:ZYX`, and `:ZYZ`. If no
20+
value is specified, it defaults to `:ZYX`.
21+
"""
22+
@inline function angle_to_crp(
23+
θ₁::Number,
24+
θ₂::Number,
25+
θ₃::Number,
26+
rot_seq::Symbol = :ZYX
27+
)
28+
return dcm_to_crp(angle_to_dcm(θ₁, θ₂, θ₃, rot_seq))
29+
end
30+
31+
@inline angle_to_crp::EulerAngles) = angle_to_crp.a1, Θ.a2, Θ.a3, Θ.rot_seq)

src/conversions/angle_to_mrp.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
## Description #############################################################################
2+
#
3+
# Functions related to the conversion from Euler angles to MRP.
4+
#
5+
############################################################################################
6+
7+
export angle_to_mrp
8+
9+
"""
10+
angle_to_mrp(θ₁::Number, θ₂::Number, θ₃::Number, rot_seq::Symbol = :ZYX) -> MRP
11+
angle_to_mrp(Θ::EulerAngles) -> MRP
12+
13+
Convert the Euler angles `θ₁`, `θ₂`, and `θ₃` [rad] with the rotation sequence `rot_seq` to
14+
modified Rodrigues parameters.
15+
16+
Those values can also be passed inside the structure `Θ` (see [`EulerAngles`](@ref)).
17+
18+
The rotation sequence is defined by a `:Symbol`. The possible values are: `:XYX`, `XYZ`,
19+
`:XZX`, `:XZY`, `:YXY`, `:YXZ`, `:YZX`, `:YZY`, `:ZXY`, `:ZXZ`, `:ZYX`, and `:ZYZ`. If no
20+
value is specified, it defaults to `:ZYX`.
21+
"""
22+
@inline function angle_to_mrp(
23+
θ₁::Number,
24+
θ₂::Number,
25+
θ₃::Number,
26+
rot_seq::Symbol = :ZYX
27+
)
28+
return dcm_to_mrp(angle_to_dcm(θ₁, θ₂, θ₃, rot_seq))
29+
end
30+
31+
@inline angle_to_mrp::EulerAngles) = angle_to_mrp.a1, Θ.a2, Θ.a3, Θ.rot_seq)

src/conversions/api.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@ Base.convert(::Type{<:Quaternion}, a::MRP) = mrp_to_quat(a)
6464

6565
Base.convert(::Type{<:CRP}, a::DCM) = dcm_to_crp(a)
6666
Base.convert(::Type{<:CRP}, a::Quaternion) = quat_to_crp(a)
67-
Base.convert(::Type{<:CRP}, a::EulerAngles) = dcm_to_crp(angle_to_dcm(a))
67+
Base.convert(::Type{<:CRP}, a::EulerAngles) = angle_to_crp(a)
6868
Base.convert(::Type{<:CRP}, a::EulerAngleAxis) = dcm_to_crp(angleaxis_to_dcm(a))
6969
Base.convert(::Type{<:CRP}, a::MRP) = dcm_to_crp(mrp_to_dcm(a))
7070

7171
# == Conversions to MRP ====================================================================
7272

7373
Base.convert(::Type{<:MRP}, a::DCM) = dcm_to_mrp(a)
7474
Base.convert(::Type{<:MRP}, a::Quaternion) = quat_to_mrp(a)
75-
Base.convert(::Type{<:MRP}, a::EulerAngles) = dcm_to_mrp(angle_to_dcm(a))
75+
Base.convert(::Type{<:MRP}, a::EulerAngles) = angle_to_mrp(a)
7676
Base.convert(::Type{<:MRP}, a::EulerAngleAxis) = dcm_to_mrp(angleaxis_to_dcm(a))
7777
Base.convert(::Type{<:MRP}, a::CRP) = dcm_to_mrp(crp_to_dcm(a))

test/conversions/angle_to_crp.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
## Desription ##############################################################################
2+
#
3+
# Tests related to conversion from Euler angles to CRP.
4+
#
5+
############################################################################################
6+
7+
# == File: ./src/conversions/angle_to_crp.jl ===============================================
8+
9+
# -- Functions: angle_to_crp ---------------------------------------------------------------
10+
11+
@testset "Euler angles => CRP" begin
12+
for T in (Float32, Float64)
13+
# We do not need comprehensive tests here because `angle_to_crp` first converts the
14+
# Euler angles to DCM and then to CRP. Those two operations are already heavily
15+
# tested.
16+
testset = [
17+
EulerAngles(T( 0.2), T(-0.1), T( 0.3), :ZYX)
18+
EulerAngles(T( 0.5), T( 0.4), T(-0.2), :XYZ)
19+
EulerAngles(T(-0.3), T( 0.2), T( 0.6), :ZXZ)
20+
]
21+
22+
for ea in testset
23+
c = angle_to_crp(ea)
24+
@test c isa CRP{T}
25+
@test c dcm_to_crp(angle_to_dcm(ea))
26+
27+
c2 = angle_to_crp(ea.a1, ea.a2, ea.a3, ea.rot_seq)
28+
@test c2 c
29+
end
30+
end
31+
end
32+
33+
@testset "Euler angles => CRP (Singularity)" begin
34+
@test_throws ArgumentError angle_to_crp(π, 0.0, 0.0, :XYZ)
35+
@test_throws ArgumentError angle_to_crp(EulerAngles(π, 0.0, 0.0, :XYZ))
36+
end

test/conversions/angle_to_mrp.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
## Desription ##############################################################################
2+
#
3+
# Tests related to conversion from Euler angles to MRP.
4+
#
5+
############################################################################################
6+
7+
# == File: ./src/conversions/angle_to_mrp.jl ===============================================
8+
9+
# -- Functions: angle_to_mrp --------------------------------------------------------------
10+
11+
@testset "Euler angles => MRP" begin
12+
for T in (Float32, Float64)
13+
# We do not need comprehensive tests here because `angle_to_mrp` first converts the
14+
# Euler angles to DCM and then to MRP. Those two operations are already heavily
15+
# tested.
16+
testset = [
17+
EulerAngles(T( 0.2), T(-0.1), T( 0.3), :ZYX)
18+
EulerAngles(T( 0.5), T( 0.4), T(-0.2), :XYZ)
19+
EulerAngles(T(-0.3), T( 0.2), T( 0.6), :ZXZ)
20+
]
21+
22+
for ea in testset
23+
m = angle_to_mrp(ea)
24+
@test m isa MRP{T}
25+
@test m dcm_to_mrp(angle_to_dcm(ea))
26+
27+
m2 = angle_to_mrp(ea.a1, ea.a2, ea.a3, ea.rot_seq)
28+
@test m2 m
29+
end
30+
end
31+
end

test/runtests.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ println("")
116116
@time @testset "Conversions" verbose = true begin
117117
include("./conversions/angle_to_angle.jl")
118118
include("./conversions/angle_to_angleaxis.jl")
119+
include("./conversions/angle_to_crp.jl")
119120
include("./conversions/angle_to_dcm.jl")
121+
include("./conversions/angle_to_mrp.jl")
120122
include("./conversions/angle_to_quat.jl")
121123
include("./conversions/angle_to_rot.jl")
122124
include("./conversions/angleaxis_to_angle.jl")

0 commit comments

Comments
 (0)