Skip to main content

evaluate

Function evaluate 

Source
pub fn evaluate(
    req: &SolveRequestV1,
    ranges_m: &[f64],
) -> Result<Vec<Observation>, KernelError>
Expand description

Solve req once and read a checked observation at each of ranges_m, in the order given.

§Which TrajectoryResult path this uses, and why

This task’s brief assumed a SolveSuccessV1::trajectory_result_for_observation() accessor; no such method exists. crate::solve_json::SolveSuccessV1 does not expose a raw crate::TrajectoryResult at all – only the wire-shaped, already-downsampled samples: Vec<TrajectorySampleV1> (the brief’s option (a), ruled out for that reason).

Instead this follows the brief’s fallback (option (b)), sharing solve_v1’s own building blocks rather than re-implementing them: solve_v1::prepare_request resolves req exactly as solve_v1 does, and solve_v1::build_zeroed_solver builds and zeroes a TrajectorySolver exactly as solve_v1 does (both widened from module-private to pub(crate) for this task). This function then calls TrajectorySolver::solve itself and reads observations straight off the result: one solve per evaluate call, not two, and not solve_v1 internally.

(Revision note: an earlier version of this function hand-duplicated solve_v1’s zero-handling match here instead of sharing build_zeroed_solver, kept honest only by an outboard cross-check test. Review judged that duplication risk too costly to keep – see “Drop reference plane” below for a related place this function deliberately does NOT mirror solve_v1 – so it was replaced with this structural share.)

§Drop reference plane

evaluate always reports drop_m perpendicular to the line of sight, regardless of the request’s shot.drops_reference. solve_v1 applies the MBA-1403 target-plane transform (drop_m /= shooting_angle_rad.cos()) as a wire-only rescaling at its own boundary (src/solve_v1.rs, applied once to each sample after TrajectoryResult::sample_observations runs); evaluate deliberately does not reproduce it, so the kernel always speaks one geometry no matter which reference plane the original request asked for on output. This is a decision, not an oversight – see evaluate_ignores_drops_reference_and_always_reports_los_drop in this module’s tests, which pins it down. A caller that wants the target-plane number can still derive it from drop_m / shooting_angle_rad.cos(), using the SAME shooting_angle_rad this request resolved to (ResolvedShotV1::shooting_angle_rad) – and should do that explicitly rather than have it baked silently into drop_m here, because a later task differentiates observations with respect to taxonomy axes including ShootingAngle (InputAxis::ShootingAngle, taxonomy.rs): folding the target-plane transform into drop_m in this function would silently add an extra d/dtheta[sec(theta)] term to that derivative, and only for target-referenced requests.

§Errors

  • KernelError::Solve if resolving or solving req fails – the same failure modes solve_v1 itself reports (invalid or conflicting fields, a zero search that does not converge, a non-finite effective muzzle angle), uniformly converted from SolveErrorEnvelopeV1 regardless of which stage (prepare_request, build_zeroed_solver, or TrajectorySolver::solve) produced it.
  • KernelError::Observation if any requested range cannot be read off the resulting trajectory – most notably a range outside [0, actual_range] – via crate::TrajectoryResult::observation_at_range_checked, used specifically because it ERRORS on an out-of-range query instead of clamping to the nearest sample (the pattern the CLI card commands use, src/main.rs:17734).