File size: 3,143 Bytes
b3d14e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
"""Tests for the max-climbable-slope helper."""

from __future__ import annotations

import pytest

from roverdevkit.mission.capability import (
    DEFAULT_LUNAR_GRAVITY_M_PER_S2,
    SLOPE_SEARCH_UPPER_DEG,
    max_climbable_slope_deg,
)
from roverdevkit.terramechanics.bekker_wong import WheelGeometry
from roverdevkit.terramechanics.soils import get_soil_parameters


@pytest.fixture
def micro_wheel() -> WheelGeometry:
    return WheelGeometry(radius_m=0.10, width_m=0.06)


def test_max_slope_is_in_valid_range(micro_wheel: WheelGeometry) -> None:
    soil = get_soil_parameters("Apollo_regolith_nominal")
    slope = max_climbable_slope_deg(micro_wheel, soil, total_mass_kg=15.0, n_wheels=4)
    assert 0.0 <= slope <= SLOPE_SEARCH_UPPER_DEG


def test_softer_soil_gives_lower_slope_capability(micro_wheel: WheelGeometry) -> None:
    dense = get_soil_parameters("Apollo_regolith_dense")
    loose = get_soil_parameters("Apollo_regolith_loose")
    dense_slope = max_climbable_slope_deg(micro_wheel, dense, 15.0, 4)
    loose_slope = max_climbable_slope_deg(micro_wheel, loose, 15.0, 4)
    assert dense_slope > loose_slope


def test_heavier_rover_sinks_more_and_climbs_less_or_equal(
    micro_wheel: WheelGeometry,
) -> None:
    soil = get_soil_parameters("Apollo_regolith_loose")
    light = max_climbable_slope_deg(micro_wheel, soil, 10.0, 4)
    heavy = max_climbable_slope_deg(micro_wheel, soil, 40.0, 4)
    # In general heavier vehicle on soft soil climbs less (more sinkage,
    # higher resistance). Allow equality because the cap at 35 deg can
    # saturate both.
    assert light >= heavy


def test_larger_wheel_climbs_at_least_as_well() -> None:
    soil = get_soil_parameters("Apollo_regolith_nominal")
    small = WheelGeometry(radius_m=0.06, width_m=0.05)
    large = WheelGeometry(radius_m=0.15, width_m=0.10)
    small_slope = max_climbable_slope_deg(small, soil, 15.0, 4)
    large_slope = max_climbable_slope_deg(large, soil, 15.0, 4)
    assert large_slope >= small_slope


def test_rejects_invalid_mass(micro_wheel: WheelGeometry) -> None:
    soil = get_soil_parameters("Apollo_regolith_nominal")
    with pytest.raises(ValueError, match="total_mass_kg"):
        max_climbable_slope_deg(micro_wheel, soil, 0.0, 4)


def test_cap_returned_when_rover_exceeds_schema_bound(
    micro_wheel: WheelGeometry,
) -> None:
    # On very dense soil, a light rover with big wheels easily exceeds
    # 35 deg; the helper must return the schema cap rather than the
    # unbounded physical slope.
    soil = get_soil_parameters("Apollo_regolith_dense")
    big_wheel = WheelGeometry(radius_m=0.18, width_m=0.12)
    slope = max_climbable_slope_deg(big_wheel, soil, total_mass_kg=5.0, n_wheels=4)
    assert slope == pytest.approx(SLOPE_SEARCH_UPPER_DEG)


def test_lunar_gravity_constant_matches_mass_model() -> None:
    # Guardrail: the lunar-gravity default must match the mass model's
    # constant so the two never drift apart.
    from roverdevkit.mass.parametric_mers import MassModelParams

    mass_g = MassModelParams().gravity_moon_m_per_s2
    assert mass_g == pytest.approx(DEFAULT_LUNAR_GRAVITY_M_PER_S2)