astrodynamics-gnss 0.9.4

GNSS domain layer (SP3, broadcast ephemeris, multi-GNSS single-point positioning, ionosphere/troposphere, DOP) built on the astrodynamics core
Documentation
//! Small fixed-width text parsing helpers.
//!
//! SP3, RINEX, IONEX, and CRINEX are fixed-column text products. Keeping the
//! common field slicing and Fortran-float handling here keeps product parsers
//! focused on record semantics instead of repeating byte-window plumbing.

/// A fixed-column field, trimmed. Returns `None` if the requested range is out
/// of bounds or the trimmed field is blank.
pub(crate) fn field(line: &str, start: usize, end: usize) -> Option<&str> {
    let s = line.get(start..end.min(line.len()))?.trim();
    if s.is_empty() {
        None
    } else {
        Some(s)
    }
}

/// A fixed-column field, untrimmed. Missing ranges are returned as an empty
/// string so callers that historically treated short lines as blank can preserve
/// that behavior.
pub(crate) fn raw_field(line: &str, start: usize, end: usize) -> &str {
    let s = floor_char_boundary(line, start);
    let e = floor_char_boundary(line, end);
    if e <= s {
        return "";
    }
    &line[s..e]
}

/// The remainder of a fixed-column line, untrimmed. Missing ranges are returned
/// as an empty string.
pub(crate) fn raw_field_from(line: &str, start: usize) -> &str {
    let s = floor_char_boundary(line, start);
    &line[s..]
}

/// Parse a RINEX-style numeric field, accepting Fortran `D`/`d` exponent
/// markers. Returns `None` for missing, blank, or malformed fields.
pub(crate) fn fortran_f64(line: &str, start: usize, end: usize) -> Option<f64> {
    let s = field(line, start, end)?;
    s.replace(['D', 'd'], "e").parse::<f64>().ok()
}

/// Largest char boundary `<= index` and `<= line.len()`.
fn floor_char_boundary(line: &str, index: usize) -> usize {
    let mut i = index.min(line.len());
    while i > 0 && !line.is_char_boundary(i) {
        i -= 1;
    }
    i
}