use super::common::{clamp_pry, dispatch_gimbal_command};
use crate::daemon::mavlink_manager::math::{clamp_rate_dt, compute_rate_target_deg};
use crate::daemon::mavlink_manager::RxCtx;
use crate::daemon::models::{CommandMode, ControlSource, GimbalCommand};
use std::time::Instant;
pub(super) fn any_rate_active(rates_deg_per_s: (f32, f32, f32)) -> bool {
let (p, r, y) = rates_deg_per_s;
[p, r, y].iter().any(|v| v.is_finite() && *v != 0.0)
}
pub(super) async fn apply_rate_increment(
source: ControlSource,
rates_deg_per_s: (f32, f32, f32),
ctx: &RxCtx<'_>,
label: &'static str,
) {
let now = Instant::now();
let dt = match ctx.state.last_rate_tick(&source) {
Some(prev) => clamp_rate_dt(now.duration_since(prev)),
None => clamp_rate_dt(std::time::Duration::ZERO), };
let dt_secs = dt.as_secs_f32();
let (current_pitch, current_roll, current_yaw) = current_position(ctx);
let (rate_pitch, rate_roll, rate_yaw) = rates_deg_per_s;
let target_pitch = compute_rate_target_deg(current_pitch, rate_pitch, dt_secs);
let target_roll = compute_rate_target_deg(current_roll, rate_roll, dt_secs);
let target_yaw = compute_rate_target_deg(current_yaw, rate_yaw, dt_secs);
let (pitch, roll, yaw) = clamp_pry(target_pitch, target_roll, target_yaw, &ctx.config.limits);
let command = GimbalCommand::new(
source.clone(),
CommandMode::Position,
Some(yaw),
Some(pitch),
Some(roll),
);
dispatch_gimbal_command(command, ctx, label).await;
ctx.state.record_rate_tick(source, now);
}
fn current_position(ctx: &RxCtx<'_>) -> (f32, f32, f32) {
if let Some(last) = ctx.state.get_last_command() {
return (
last.pitch.unwrap_or(0.0),
last.roll.unwrap_or(0.0),
last.yaw.unwrap_or(0.0),
);
}
let snapshot = ctx.state.get_state();
if snapshot.measured_at.is_some() {
return (snapshot.pitch, snapshot.roll, snapshot.yaw);
}
(0.0, 0.0, 0.0)
}