use marss::lighting::day_night::*;
use marss::lighting::seasons::*;
use marss::lighting::solar_position::*;
#[test]
fn solar_declination_at_equinox() {
let d = solar_declination_deg(0.0);
assert!(d.abs() < 1.0, "Equinox decl ~0: {d}");
}
#[test]
fn solar_declination_at_solstice() {
let d = solar_declination_deg(90.0);
assert!(d > 20.0, "Summer solstice decl > 20: {d}");
}
#[test]
fn equation_of_time_bounded() {
let eot = equation_of_time_minutes(180.0);
assert!(eot.abs() < 60.0, "EOT bounded: {eot}");
}
#[test]
fn solar_position_compute() {
let sp = SolarPosition::compute(0.0, 12.0, 0.0, 0.0);
assert!(sp.elevation_deg.is_finite());
assert!(sp.azimuth_deg.is_finite());
}
#[test]
fn solar_position_above_horizon_noon() {
let sp = SolarPosition::compute(0.0, 12.0, 0.0, 0.0);
assert!(sp.is_above_horizon(), "Noon equator: above horizon");
}
#[test]
fn solar_position_distance_au() {
let sp = SolarPosition::compute(0.0, 12.0, 0.0, 0.0);
assert!(
sp.distance_au > 1.3 && sp.distance_au < 1.7,
"Mars ~1.5 AU: {}",
sp.distance_au
);
}
#[test]
fn solar_position_distance_m() {
let sp = SolarPosition::compute(0.0, 12.0, 0.0, 0.0);
let d = sp.distance_m();
assert!(d > 2e11, "Mars > 2e11 m: {d}");
}
#[test]
fn solar_position_direction_unit() {
let sp = SolarPosition::compute(0.0, 12.0, 0.0, 0.0);
let d = &sp.direction;
let mag = (d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt();
assert!(
(mag - 1.0).abs() < 0.1 || mag.is_finite(),
"Direction: {mag}"
);
}
#[test]
fn solar_elevation_range() {
let sp = SolarPosition::compute(45.0, 6.0, 45.0, 0.0);
assert!(sp.elevation_deg >= -90.0 && sp.elevation_deg <= 90.0);
}
#[test]
fn day_night_state_at_noon() {
let dnc = DayNightCycle::new(0.0);
let state = dnc.state_at(0.0, 12.0);
assert!(matches!(state, DaylightState::Day), "Noon = Day");
}
#[test]
fn day_night_state_at_midnight() {
let dnc = DayNightCycle::new(0.0);
let state = dnc.state_at(0.0, 0.0);
assert!(
matches!(
state,
DaylightState::Night | DaylightState::AstronomicalTwilight
),
"Midnight dark"
);
}
#[test]
fn ambient_light_noon() {
let dnc = DayNightCycle::new(0.0);
let a = dnc.ambient_light(0.0, 12.0);
assert!(a > 0.5, "Noon ambient > 0.5: {a}");
}
#[test]
fn ambient_light_midnight() {
let dnc = DayNightCycle::new(0.0);
let a = dnc.ambient_light(0.0, 0.0);
assert!(a < 0.5, "Midnight ambient < 0.5: {a}");
}
#[test]
fn day_length_equator_near_half_sol() {
let dnc = DayNightCycle::new(0.0);
let dl = dnc.day_length_hours(0.0);
let sol_h = marss::SOL_S / 3600.0;
assert!((dl - sol_h / 2.0).abs() < sol_h / 2.0, "Day length: {dl}");
}
#[test]
fn day_length_varies_with_lat() {
let dnc = DayNightCycle::new(90.0);
let eq = dnc.day_length_hours(0.0);
let hi = dnc.day_length_hours(70.0);
assert!((eq - hi).abs() > 0.1 || eq.is_finite());
}
#[test]
fn season_at_equinox() {
let s = season_at(0.0);
assert_eq!(s.season_north, Season::NorthernSpring);
}
#[test]
fn season_at_summer_solstice() {
let s = season_at(90.0);
assert_eq!(s.season_north, Season::NorthernSummer);
}
#[test]
fn season_at_autumnal_equinox() {
let s = season_at(180.0);
assert_eq!(s.season_north, Season::NorthernAutumn);
}
#[test]
fn season_at_winter_solstice() {
let s = season_at(270.0);
assert_eq!(s.season_north, Season::NorthernWinter);
}
#[test]
fn opposite_hemispheres() {
let s = season_at(90.0);
assert_eq!(s.season_south, Season::NorthernWinter);
}
#[test]
fn solar_declination_in_season() {
let s = season_at(90.0);
assert!(
s.solar_declination_deg > 20.0,
"Summer decl > 20: {}",
s.solar_declination_deg
);
}
#[test]
fn perihelion_ls_value() {
assert!((perihelion_ls() - 251.0).abs() < 5.0);
}
#[test]
fn irradiance_ratio_gt_one() {
let r = irradiance_season_ratio();
assert!(r > 1.0, "Perihelion/aphelion ratio > 1: {r}");
}
#[test]
fn dust_storm_season_detection() {
assert!(is_dust_storm_season(250.0), "Ls 250 in dust season");
assert!(!is_dust_storm_season(100.0), "Ls 100 not in dust season");
}
#[test]
fn dust_storm_season_range() {
assert!((dust_storm_season_start_ls() - 180.0).abs() < 1.0);
assert!((dust_storm_season_end_ls() - 360.0).abs() < 1.0);
}