jupiters 0.0.3

Jupiter celestial simulation crate for the MilkyWay SolarSystem workspace
Documentation
use jupiters::temporal::calendar::{JUPITERDAYSECONDS, *};
use jupiters::temporal::epoch::*;
use jupiters::temporal::time_scale::*;

#[test]
fn calendarjupiterdayseconds() {
    assert!(
        (JUPITERDAYSECONDS - 35730.0).abs() < 1.0,
        "Jupiter day ~35730s: {JUPITERDAYSECONDS}"
    );
}

#[test]
fn calendarjupiteryearseconds() {
    const { assert!(JUPITERYEARSECONDS > 3.5e8 && JUPITERYEARSECONDS < 4.0e8) };
}

#[test]
fn calendarsecondsperday() {
    assert!((SECONDSPERDAY - 86400.0).abs() < 1e-6);
}

#[test]
fn epochj2000jd() {
    assert!(
        (J2000EPOCH - 2_451_545.0).abs() < 0.01,
        "J2000 epoch JD: {J2000EPOCH}"
    );
}

#[test]
fn datetimetofrromjdroundtrip() {
    let dt = DateTime::new(2000, 1, 1, 12, 0, 0.0);
    let jd = dt.tojuliandate();
    assert!(
        (jd - 2_451_545.0).abs() < 0.01,
        "J2000 Julian Date should be ~2451545: {jd}"
    );
}

#[test]
fn datetimefromjdroundtrip() {
    let jd = 2_460_000.5;
    let dt = DateTime::fromjuliandate(jd);
    let jd2 = dt.tojuliandate();
    assert!((jd - jd2).abs() < 1e-4, "JD roundtrip: {jd} -> {jd2}");
}

#[test]
fn datetimefromjdj2000() {
    let dt = DateTime::fromjuliandate(2_451_545.0);
    let jd = dt.tojuliandate();
    assert!((jd - 2_451_545.0).abs() < 1e-4);
}

#[test]
fn datetimejddifferentvalues() {
    let jd1 = DateTime::new(2000, 1, 1, 12, 0, 0.0).tojuliandate();
    let jd2 = DateTime::new(2024, 1, 1, 12, 0, 0.0).tojuliandate();
    assert!(
        (jd2 - jd1).abs() > 8000.0,
        "Different years should differ: {jd1} vs {jd2}"
    );
}

#[test]
fn datetimemonthfield() {
    let dt = DateTime::new(2000, 6, 15, 0, 0, 0.0);
    assert_eq!(dt.month, 6);
    assert_eq!(dt.day, 15);
}

#[test]
fn datetimehourminutesecond() {
    let dt = DateTime::new(2000, 1, 1, 14, 30, 45.5);
    assert_eq!(dt.hour, 14);
    assert_eq!(dt.minute, 30);
    assert!((dt.second - 45.5).abs() < 1e-6);
}

#[test]
fn epochj2000constructor() {
    let e = Epoch::j2000();
    assert!(
        (e.juliandate - 2_451_545.0).abs() < 0.01,
        "J2000 epoch JD: {}",
        e.juliandate
    );
}

#[test]
fn epochfromjd() {
    let e = Epoch::fromjd(2_460_000.0);
    assert!((e.juliandate - 2_460_000.0).abs() < 1e-6);
}

#[test]
fn epochfrommjd() {
    let e = Epoch::frommjd(51544.5);
    assert!(
        (e.juliandate - 2_451_545.0).abs() < 0.01,
        "MJD 51544.5 should give J2000: {}",
        e.juliandate
    );
}

#[test]
fn epochmjdroundtrip() {
    let e = Epoch::frommjd(60000.0);
    let mjd = e.tomjd();
    assert!((mjd - 60000.0).abs() < 1e-6, "MJD roundtrip: {mjd}");
}

#[test]
fn epochcenturiessincej2000atzero() {
    let e = Epoch::j2000();
    let c = e.centuriessincej2000();
    assert!(c.abs() < 0.001, "Centuries at J2000 should be 0: {c}");
}

#[test]
fn epochcenturiessincej2000at1century() {
    let e = Epoch::frommjd(51544.5 + 36525.0);
    let c = e.centuriessincej2000();
    assert!((c - 1.0).abs() < 0.01, "Should be ~1 century: {c}");
}

#[test]
fn epochdayssincej2000() {
    let e = Epoch::j2000();
    let d = e.dayssincej2000();
    assert!(d.abs() < 0.01, "Days at J2000 should be 0: {d}");
}

#[test]
fn epochdayssincej2000positive() {
    let e = Epoch::fromjd(2_451_545.0 + 100.0);
    let d = e.dayssincej2000();
    assert!((d - 100.0).abs() < 0.01, "Should be 100 days: {d}");
}

#[test]
fn epochjupitergmstdegrees() {
    let e = Epoch::j2000();
    let gmst = e.jupitergmstdegrees();
    assert!(
        (gmst - 280.0).abs() < 5.0,
        "Jupiter GMST at J2000 should be ~280°: {gmst}"
    );
}

#[test]
fn epochjupitergmstvaries() {
    let e1 = Epoch::frommjd(51544.5);
    let e2 = Epoch::frommjd(51544.5 + 100.0);
    let g1 = e1.jupitergmstdegrees();
    let g2 = e2.jupitergmstdegrees();
    assert!((g1 - g2).abs() > 0.1, "GMST should vary with time");
}

#[test]
fn epochadvancedays() {
    let mut e = Epoch::j2000();
    e.advancedays(10.0);
    assert!((e.dayssincej2000() - 10.0).abs() < 0.01);
}

#[test]
fn epochadvanceseconds() {
    let mut e = Epoch::j2000();
    e.advanceseconds(86400.0);
    assert!((e.dayssincej2000() - 1.0).abs() < 0.01);
}

#[test]
fn timescalerealtime() {
    let ts = TimeScale::realtime();
    assert!((ts.speedmultiplier - 1.0).abs() < 1e-9);
    assert!(!ts.paused);
}

#[test]
fn timescalefastforward() {
    let ts = TimeScale::fastforward(100.0);
    assert!((ts.speedmultiplier - 100.0).abs() < 1e-9);
}

#[test]
fn timescaleslowmotion() {
    let ts = TimeScale::slowmotion(0.1);
    assert!(
        (ts.speedmultiplier - 10.0).abs() < 1e-6,
        "slowmotion(0.1) → 1/0.1=10x: {}",
        ts.speedmultiplier
    );
}

#[test]
fn timescalestep() {
    let mut ts = TimeScale::fastforward(10.0);
    ts.step(1.0);
    assert!(
        (ts.simulationtimes - 10.0).abs() < 1e-6,
        "10x speed * 1s dt = 10s sim: {}",
        ts.simulationtimes
    );
    assert!((ts.realtimes - 1.0).abs() < 1e-6);
}

#[test]
fn timescalestepcumulates() {
    let mut ts = TimeScale::fastforward(2.0);
    ts.step(5.0);
    ts.step(5.0);
    assert!((ts.realtimes - 10.0).abs() < 0.1);
    assert!((ts.simulationtimes - 20.0).abs() < 0.1);
}

#[test]
fn timescalepauseresume() {
    let mut ts = TimeScale::realtime();
    ts.pause();
    assert!(ts.paused);
    ts.step(100.0);
    assert!(
        ts.simulationtimes.abs() < 1e-9,
        "Paused step should give 0 sim time: {}",
        ts.simulationtimes
    );
    ts.resume();
    assert!(!ts.paused);
    ts.step(1.0);
    assert!(
        (ts.simulationtimes - 1.0).abs() < 1e-6,
        "Resumed should work: {}",
        ts.simulationtimes
    );
}

#[test]
fn timescaletogglepause() {
    let mut ts = TimeScale::realtime();
    ts.togglepause();
    assert!(ts.paused);
    ts.togglepause();
    assert!(!ts.paused);
}

#[test]
fn timescalesetspeed() {
    let mut ts = TimeScale::realtime();
    ts.setspeed(50.0);
    assert!((ts.speedmultiplier - 50.0).abs() < 1e-9);
}

#[test]
fn timescalesimulationdt() {
    let ts = TimeScale::fastforward(10.0);
    let sdt = ts.simulationdt(2.0);
    assert!((sdt - 20.0).abs() < 1e-6);
}

#[test]
fn timescalesimulationdtpaused() {
    let mut ts = TimeScale::fastforward(10.0);
    ts.pause();
    let sdt = ts.simulationdt(2.0);
    assert!(sdt.abs() < 1e-9, "Paused simulationdt should be 0: {sdt}");
}

#[test]
fn timescalesimhours() {
    let mut ts = TimeScale::realtime();
    ts.step(3600.0);
    assert!((ts.simhours() - 1.0).abs() < 0.01);
}

#[test]
fn timescalesimdays() {
    let mut ts = TimeScale::realtime();
    ts.step(86400.0);
    assert!((ts.simdays() - 1.0).abs() < 0.01);
}

#[test]
fn timescalesimjupiterdays() {
    let mut ts = TimeScale::realtime();
    ts.step(35730.0);
    assert!(
        (ts.simjupiterdays() - 1.0).abs() < 0.01,
        "Should be ~1 Jupiter day: {}",
        ts.simjupiterdays()
    );
}

#[test]
fn timescalesimjupiteryears() {
    let mut ts = TimeScale::fastforward(1000.0);
    let jyear_s = 86400.0 * 4332.59;
    ts.step(jyear_s / 1000.0);
    assert!(
        (ts.simjupiteryears() - 1.0).abs() < 0.01,
        "Should be ~1 Jupiter year: {}",
        ts.simjupiteryears()
    );
}

#[test]
fn timescalemultiplestepconsistency() {
    let mut ts = TimeScale::fastforward(5.0);
    for _ in 0..100 {
        ts.step(0.1);
    }
    assert!((ts.realtimes - 10.0).abs() < 0.1);
    assert!((ts.simulationtimes - 50.0).abs() < 0.1);
}

#[test]
fn timescaleinitialzero() {
    let ts = TimeScale::realtime();
    assert!(ts.simulationtimes.abs() < 1e-15);
    assert!(ts.realtimes.abs() < 1e-15);
}