Current section
Files
Jump to
Current section
Files
lib/motion.ex
defmodule ViaUtils.Motion do
require Logger
require ViaUtils.Constants, as: VC
require ViaUtils.Shared.ValueNames, as: SVN
@moduledoc """
Documentation for `UtilsMotion`.
"""
# Convert North/East velocity to Speed/Course
@spec get_speed_course_for_velocity(number(), number(), number(), number()) :: tuple()
def get_speed_course_for_velocity(v_north_mps, v_east_mps, min_speed_for_course_mps, yaw_rad) do
speed = ViaUtils.Math.hypot(v_north_mps, v_east_mps)
course =
if speed >= min_speed_for_course_mps do
:math.atan2(v_east_mps, v_north_mps)
|> ViaUtils.Math.constrain_angle_to_compass()
else
# Logger.info("too slow: #{speed}")
yaw_rad
end
{speed, course}
end
@spec adjust_velocity_for_min_speed(map(), number(), number()) :: map()
def adjust_velocity_for_min_speed(velocity_mps, min_speed_for_course_mps, yaw_rad) do
%{SVN.v_north_mps() => v_north, SVN.v_east_mps() => v_east} = velocity_mps
speed = ViaUtils.Math.hypot(v_north, v_east)
if speed >= min_speed_for_course_mps do
velocity_mps
else
%{
velocity_mps
| SVN.v_north_mps() => speed * :math.cos(yaw_rad),
SVN.v_east_mps() => speed * :math.sin(yaw_rad)
}
end
end
# Turn correctly left or right using delta Yaw/Course
@spec turn_left_or_right_for_correction(number()) :: number()
def turn_left_or_right_for_correction(correction) do
cond do
correction < -:math.pi() -> correction + 2.0 * :math.pi()
correction > :math.pi() -> correction - 2.0 * :math.pi()
true -> correction
end
end
@spec attitude_to_accel(map()) :: map()
def attitude_to_accel(attitude_rad) do
%{SVN.roll_rad() => roll, SVN.pitch_rad() => pitch} = attitude_rad
cos_theta = :math.cos(pitch)
ax = :math.sin(pitch)
ay = -:math.sin(roll) * cos_theta
az = -:math.cos(roll) * cos_theta
%{
SVN.accel_x_mpss() => ax * VC.gravity(),
SVN.accel_y_mpss() => ay * VC.gravity(),
SVN.accel_z_mpss() => az * VC.gravity()
}
end
@spec inertial_to_body_euler_rad(map(), tuple()) :: tuple()
def inertial_to_body_euler_rad(attitude_rad, vector) do
%{SVN.roll_rad() => roll, SVN.pitch_rad() => pitch, SVN.yaw_rad() => yaw} = attitude_rad
cosphi = :math.cos(roll)
sinphi = :math.sin(roll)
costheta = :math.cos(pitch)
sintheta = :math.sin(pitch)
cospsi = :math.cos(yaw)
sinpsi = :math.sin(yaw)
{vx, vy, vz} = vector
bx = costheta * cospsi * vx + costheta * sinpsi * vy - sintheta * vz
by =
(-cosphi * sinpsi + sinphi * sintheta * cospsi) * vx +
(cosphi * cospsi + sinphi * sintheta * sinpsi) * vy + sinphi * costheta * vz
bz =
(sinphi * sinpsi + cosphi * sintheta * cospsi) * vx -
(sinphi * cospsi + cosphi * sintheta * sinpsi) * vy + cosphi * costheta * vz
{bx, by, bz}
end
@spec inertial_to_body_euler_deg(map(), tuple()) :: tuple()
def inertial_to_body_euler_deg(attitude_deg, vector) do
%{roll_deg: roll, pitch_deg: pitch, yaw_deg: yaw} = attitude_deg
attitude_rad = %{
SVN.roll_rad() => ViaUtils.Math.deg2rad(roll),
SVN.pitch_rad() => ViaUtils.Math.deg2rad(pitch),
SVN.yaw_rad() => ViaUtils.Math.deg2rad(yaw)
}
inertial_to_body_euler_rad(attitude_rad, vector)
end
@spec quaternion_to_euler(float(), float(), float(), float()) :: map()
def quaternion_to_euler(q0, q1, q2, q3) do
roll = :math.atan2(2.0 * (q0 * q1 + q2 * q3), 1.0 - 2.0 * (q1 * q1 + q2 * q2))
pitch = :math.asin(2 * (q0 * q2 - q3 * q1))
yaw = :math.atan2(2.0 * (q0 * q3 + q1 * q2), 1.0 - 2.0 * (q2 * q2 + q3 * q3))
yaw =
cond do
yaw < 0 -> yaw + VC.two_pi()
yaw >= VC.two_pi() -> yaw - VC.two_pi()
true -> yaw
end
%{SVN.roll_rad() => roll, SVN.pitch_rad() => pitch, SVN.yaw_rad() => yaw}
end
@spec imu_rpy_to_string(struct(), integer()) :: binary()
def imu_rpy_to_string(imu, decimals) do
rpy =
Enum.map([imu.roll_rad, imu.pitch_rad, imu.yaw_rad], fn x ->
ViaUtils.Math.rad2deg(x)
end)
ViaUtils.Format.eftb_list(rpy, decimals)
end
end