use crate::angle::{
Compass, CompassCourse, Deviation, Direction, Frame, Magnetic, MagneticCourse, True,
TrueCourse, Variation,
};
use crate::deviation::{DeviationTable, InterpolatedTable, Interpolation};
use crate::environment::CompassModel;
use crate::error::{NavigationError, Result};
use crate::math;
use super::corrections::{compass_to_magnetic, magnetic_to_true, true_to_magnetic};
use super::solver::solve_compass_course;
use super::{
Advisories, CourseSolution, COARSE_TABLE_GAP_DEG, LARGE_DEVIATION_DEG, LARGE_VARIATION_DEG,
};
pub fn compass_to_true_by(
compass_course: CompassCourse,
model: &impl CompassModel,
variation: Variation,
) -> Result<TrueCourse> {
let deviation = model.deviation(compass_course)?;
Ok(magnetic_to_true(
compass_to_magnetic(compass_course, deviation),
variation,
))
}
pub fn true_to_compass_by(
true_course: TrueCourse,
model: &impl CompassModel,
variation: Variation,
) -> Result<CompassCourse> {
let magnetic = true_to_magnetic(true_course, variation);
let compass_degrees = solve_compass_course(
|compass| {
model
.deviation(CompassCourse::from_degrees_wrapped(compass))
.map(Deviation::degrees)
.map_err(NavigationError::from)
},
magnetic.degrees(),
)?;
Ok(CompassCourse::from_degrees_wrapped(compass_degrees))
}
pub fn compass_to_magnetic_by(
compass_course: CompassCourse,
model: &impl CompassModel,
) -> Result<MagneticCourse> {
let deviation = model.deviation(compass_course)?;
Ok(compass_to_magnetic(compass_course, deviation))
}
pub fn magnetic_to_compass_by(
magnetic_course: MagneticCourse,
model: &impl CompassModel,
) -> Result<CompassCourse> {
true_to_compass_by(magnetic_course.relabel::<True>(), model, Variation::ZERO)
}
pub fn convert_compass_course_to_true_course<'a>(
compass_course: Direction<Compass>,
variation: Variation,
deviation_table: &DeviationTable,
interpolation: impl Into<Interpolation<'a>>,
) -> Result<CourseSolution<True>> {
let model = deviation_table.interpolated(interpolation.into())?;
let course = compass_to_true_by(compass_course, &model, variation)?;
solution(course, compass_course, variation, deviation_table, &model)
}
pub fn convert_true_course_to_compass_course<'a>(
true_course: Direction<True>,
variation: Variation,
deviation_table: &DeviationTable,
interpolation: impl Into<Interpolation<'a>>,
) -> Result<CourseSolution<Compass>> {
let model = deviation_table.interpolated(interpolation.into())?;
let compass_course = true_to_compass_by(true_course, &model, variation)?;
solution(
compass_course,
compass_course,
variation,
deviation_table,
&model,
)
}
pub fn convert_compass_course_to_magnetic_course<'a>(
compass_course: Direction<Compass>,
deviation_table: &DeviationTable,
interpolation: impl Into<Interpolation<'a>>,
) -> Result<CourseSolution<Magnetic>> {
let model = deviation_table.interpolated(interpolation.into())?;
let course = compass_to_magnetic_by(compass_course, &model)?;
solution(
course,
compass_course,
Variation::ZERO,
deviation_table,
&model,
)
}
pub fn convert_magnetic_course_to_compass_course<'a>(
magnetic_course: Direction<Magnetic>,
deviation_table: &DeviationTable,
interpolation: impl Into<Interpolation<'a>>,
) -> Result<CourseSolution<Compass>> {
convert_true_course_to_compass_course(
magnetic_course.relabel::<True>(),
Variation::ZERO,
deviation_table,
interpolation,
)
}
fn solution<F: Frame>(
course: Direction<F>,
compass_course: CompassCourse,
variation: Variation,
table: &DeviationTable,
model: &InterpolatedTable<'_>,
) -> Result<CourseSolution<F>> {
let deviation = model.deviation(compass_course)?;
Ok(CourseSolution {
course,
deviation,
variation,
total_correction: variation.degrees() + deviation.degrees(),
estimated_error: model.uncertainty(compass_course),
advisories: Advisories {
large_variation: math::abs(variation.degrees()) > LARGE_VARIATION_DEG,
large_deviation: math::abs(deviation.degrees()) > LARGE_DEVIATION_DEG,
coarse_table: table.max_gap() > COARSE_TABLE_GAP_DEG,
non_invertible_table: !table.is_invertible(),
},
})
}