# zhl16
`zhl16` is a small `#![no_std]` Rust crate for core Bühlmann ZHL-16 tissue loading primitives.
The crate currently provides:
- 16-compartment nitrogen and helium tissue loading.
- ZHL-16C tissue constants loaded from `data/zhl16c.csv` at compile time.
- Gas mixture handling for oxygen, helium, and derived nitrogen fractions.
- Maximum operating pressure and equivalent narcotic pressure helpers.
- Per-compartment safe ambient pressure calculation from a gradient factor.
- Lightweight unit wrappers for pressure, time, pressure rate, and frequency.
This is not a complete dive computer. It does not currently provide dive profile planning, ascent scheduling, decompression stop generation, CNS/OTU tracking, gas switching, validation against dive-computer reference profiles, or safety policy around user input.
## Installation
Add the crate to your `Cargo.toml`:
```toml
[dependencies]
zhl16 = "0.1"
```
## Example
```rust
use zhl16::{BodyState, GasMixture, Pressure, PressureUnit, Time, TimeUnit};
let mut body = BodyState::new(
Pressure::new(0.79, PressureUnit::Bar).expect("valid pressure"),
Pressure::zero(),
);
let trimix_10_20 = GasMixture::new(0.10, 0.20).expect("valid gas mixture");
body.evolve(
Pressure::new(2.0, PressureUnit::Bar).expect("valid pressure"),
&trimix_10_20,
Time::new(60.0, TimeUnit::Seconds).expect("valid time"),
)
.expect("positive time step");
let first_compartment = &body.compartments()[0];
let n2 = first_compartment
.get_nitrogen_pressure()
.to(PressureUnit::Bar);
let he = first_compartment
.get_helium_pressure()
.to(PressureUnit::Bar);
let safe_pressure = first_compartment
.safe_ambient_pressure(0.85)
.expect("valid gradient factor")
.to(PressureUnit::Bar);
assert!(n2 > 0.79);
assert!(he > 0.0);
let _ = safe_pressure;
```
## Gas Mixtures
`GasMixture::new(oxygen_fraction, helium_fraction)` stores oxygen and helium fractions. Nitrogen is derived as:
```text
1.0 - oxygen_fraction - helium_fraction
```
The constructor returns `Result<GasMixture, GasMixtureError>` if either supplied fraction is not finite, oxygen is zero, either fraction is negative, or oxygen plus helium is greater than `1.0`.
```rust
use zhl16::{Gas, GasMixture, GasMixtureError, Pressure, PressureUnit};
fn main() -> Result<(), GasMixtureError> {
let nitrox_32 = GasMixture::new(0.32, 0.0)?;
assert_eq!(nitrox_32.helium_fraction(), 0.0);
assert!((nitrox_32.inert_fraction(Gas::Nitrogen) - 0.68).abs() < 1e-12);
let mod_pressure = nitrox_32
.max_operational_pressure(
Pressure::new(1.4, PressureUnit::Bar).expect("valid pressure"),
)?
.to(PressureUnit::Bar);
assert!((mod_pressure - 4.375).abs() < 1e-12);
Ok(())
}
```
## Tissue Constants
The ZHL-16-C constants live in `data/zhl16c.csv`. A build script validates that file and generates the Rust `TISSUE_CONSTANTS` array at compile time.
This keeps the runtime crate `no_std`; the CSV parsing happens only during compilation.
## Units
The crate uses simple typed quantity wrappers:
- `Pressure`
- `Time`
- `PressureRate`
- `Frequency`
Each quantity stores a base-unit `f64` internally and converts through unit enums such as `PressureUnit::Bar` and `TimeUnit::Seconds`.
Public quantity constructors return `Result<_, QuantityError>` and reject `NaN` values.
## Safety
Do not use this crate as the sole basis for real dive planning or life-support decisions. The API is still early and intentionally exposes low-level model primitives rather than a validated decompression algorithm.
## License
Licensed under MIT license