# `event::solar`, day/night & twilight periods
Sun-specific helpers for computing **time windows** where the Sun's altitude satisfies
some condition (night, day, twilight bands, etc.).
Internal code lives in `src/event/solar/`; period finding delegates to the generic
engine in `src/event/search/`.
---
## Quick start
```rust
use siderust::bodies::Sun;
use siderust::event::altitude::{below_threshold, SearchOpts};
use siderust::event::solar::twilight;
use siderust::coordinates::centers::ObserverSite;
use siderust::time::{ModifiedJulianDate, Interval};
use qtty::{Degrees, Meter, Quantity};
let site = ObserverSite::new(
Degrees::new(0.0), // lon
Degrees::new(51.4769), // lat
Quantity::<Meter>::new(0.0),
);
let period = Interval::new(ModifiedJulianDate::new(60000.0), ModifiedJulianDate::new(60007.0));
let nights = below_threshold(&Sun, &site, period, twilight::ASTRONOMICAL, SearchOpts::default());
```
See `examples/06_night_events.rs`.
---
## Public API
Night, day, and twilight windows use the unified altitude period functions in
`siderust::event::altitude`. Pass `&Sun` as the subject; twilight constants live in
[`twilight`] (`night_types`).
| [`below_threshold`] | Periods where altitude is **below** a threshold (e.g. astronomical night at −18°). |
| [`above_threshold`] | Periods where altitude is **above** a threshold (e.g. daylight above the horizon). |
| [`altitude_ranges`] | Periods where altitude lies **between** `(min, max)` (e.g. nautical twilight −18° to −12°). |
[`SearchOpts`] exposes a single public field:
- `time_tolerance` — root-refinement tolerance in days (default ~86 µs).
---
## Implementation
Solar period searches use an internal **daily predictor** that brackets rise/set
candidates per day and refines each crossing against `sun_altitude_rad`. When the
analytic model is unreliable, the engine falls back to Chebyshev polynomial fitting
or scan + Brent refinement.
For `altitude_ranges`, the band is computed as `above(min) ∩ complement(above(max))`.
---
## Twilight thresholds (`night_types`)
- `Twilight` enum (`Civil`, `Nautical`, `Astronomical`, `Horizon`, `ApparentHorizon`)
— `Degrees::from(Twilight)` converts to a threshold angle.
- `twilight::*` constants (Sun **center** altitude):
- `CIVIL = -6°`, `NAUTICAL = -12°`, `ASTRONOMICAL = -18°`
- `HORIZON = 0°` (geometric)
- `APPARENT_HORIZON = -0.833°` (rule-of-thumb refraction + Sun semi-diameter)
Altitude is **geometric** (no refraction). Treat `APPARENT_HORIZON` as a convenient
approximation, not a site-specific refraction model.
---
## Accuracy & edge cases
- **Numerical precision**: Brent refinement with `time_tolerance` typically yields
sub-millisecond event times.
- **Physical limits**: VSOP87 ephemeris, threshold choice (center vs limb), and
ignored atmospheric refraction dominate real-world error.
- **Polar day/night**: empty results are normal when the Sun never crosses the
requested threshold.
- **Grazing events**: tangent crossings near polar boundaries may be missed by
sign-change bracketing.
Benchmarks (require `bench-internals`):
```bash
cargo bench --features bench-internals --bench solar_altitude
```
[`below_threshold`]: https://docs.rs/siderust/latest/siderust/event/altitude/fn.below_threshold.html
[`above_threshold`]: https://docs.rs/siderust/latest/siderust/event/altitude/fn.above_threshold.html
[`altitude_ranges`]: https://docs.rs/siderust/latest/siderust/event/altitude/fn.altitude_ranges.html
[`twilight`]: https://docs.rs/siderust/latest/siderust/event/solar/night_types/index.html
[`SearchOpts`]: https://docs.rs/siderust/latest/siderust/event/altitude/struct.SearchOpts.html