iasinat-lib 0.1.4

Reader for IASI NAT L1C and L2 files
Documentation
use super::*;

pub type Timestamp = GregorianDateHMS;

#[derive(Debug)]
pub struct Mphr {
    pub product_name:String,
    pub parent_product_names:[Option<String>;4],
    pub spacecraft_id:String,
    pub sensing_start:Timestamp,
    pub sensing_end:Timestamp,
    pub state_vector_time:Timestamp,
    pub semi_major_axis:i64,
    pub eccentricity:i64,
    pub inclination:i64,
    pub perigee_argument:i64,
    pub right_ascension:i64,
    pub mean_anomaly:i64,
    pub x_position:i64,
    pub y_position:i64,
    pub z_position:i64,
    pub x_velocity:i64,
    pub y_velocity:i64,
    pub z_velocity:i64,
    pub earth_sun_distance_ratio:i64,
    pub location_tolerance_radial:i64,
    pub location_tolerance_crosstrack:i64,
    pub location_tolerance_alongtrack:i64,
    pub yaw_error:i64,
    pub roll_error:i64,
    pub pitch_error:i64,
    pub subsat_latitude_start:i64,
    pub subsat_longitude_start:i64,
    pub subsat_latitude_end:i64,
    pub subsat_longitude_end:i64,
    pub leap_second:i8,
    pub leap_second_utc:Option<Timestamp>,
    pub orbit_start:u32,
    pub orbit_end:u32,
}

impl Mphr {
    pub fn read_bin<R:Read+Seek>(rd:&mut NatReader<R>,rec:&Grh)->Result<Self> {
	let product_name = Self::read_kv_string_at(
	    rd,rec,20,100,
	    "PRODUCT_NAME")?;
	let parent_product_name_1 = Self::read_opt_kv_string_at(
	    rd,rec,120,100,
	    "PARENT_PRODUCT_NAME_1")?;
	let parent_product_name_2 = Self::read_opt_kv_string_at(
	    rd,rec,220,100,
	    "PARENT_PRODUCT_NAME_2")?;
	let parent_product_name_3 = Self::read_opt_kv_string_at(
	    rd,rec,320,100,
	    "PARENT_PRODUCT_NAME_3")?;
	let parent_product_name_4 = Self::read_opt_kv_string_at(
	    rd,rec,420,100,
	    "PARENT_PRODUCT_NAME_4")?;
	let parent_product_names = [
	    parent_product_name_1,
	    parent_product_name_2,
	    parent_product_name_3,
	    parent_product_name_4
	];
	let spacecraft_id = Self::read_kv_string_at(
	    rd,rec,664,36,
	    "SPACECRAFT_ID")?.parse()?;

	let orbit_start : u32 = Self::read_kv_string_at(
	    rd,rec,1377,38,
	    "ORBIT_START")?.parse()?;
	let orbit_end : u32 = Self::read_kv_string_at(
	    rd,rec,1415,38,
	    "ORBIT_END")?.parse()?;
	let semi_major_axis : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1548,44,
		"SEMI_MAJOR_AXIS")?.parse()?;

	let tsp = TimestampParser::new()?;
	let sensing_start =
	    tsp.parse(&Self::read_kv_string_at(
		rd,rec,700,48,
		"SENSING_START")?)?;
	let sensing_end =
	    tsp.parse(&Self::read_kv_string_at(
		rd,rec,748,48,
		"SENSING_END")?)?;
	let state_vector_time =
	    tsp.parse(&Self::read_kv_string_at(
		rd,rec,1497,51,
		"STATE_VECTOR_TIME")?)?;
	let eccentricity : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1592,44,
		"ECCENTRICITY")?.parse()?;
	let inclination : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1636,44,
		"INCLINATION")?.parse()?;
	let perigee_argument : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1680,44,
		"PERIGEE_ARGUMENT")?.parse()?;
	let right_ascension : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1724,44,
		"RIGHT_ASCENSION")?.parse()?;
	let mean_anomaly : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1768,44,
		"MEAN_ANOMALY")?.parse()?;
	let x_position : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1812,44,
		"X_POSITION")?.parse()?;
	let y_position : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1856,44,
		"Y_POSITION")?.parse()?;
	let z_position : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1900,44,
		"Z_POSITION")?.parse()?;
	let x_velocity : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1944,44,
		"X_VELOCITY")?.parse()?;
	let y_velocity : i64 = 
	    Self::read_kv_string_at(
		rd,rec,1988,44,
		"Y_VELOCITY")?.parse()?;
	let z_velocity : i64 = 
	    Self::read_kv_string_at(
		rd,rec,2032,44,
		"Z_VELOCITY")?.parse()?;
	let earth_sun_distance_ratio : i64 = 
	    Self::read_kv_string_at(
		rd,rec,2076,44,
		"EARTH_SUN_DISTANCE_RATIO")?.parse()?;
	let location_tolerance_radial : i64 =
	    Self::read_kv_string_at(
		rd,rec,2120,44,
		"LOCATION_TOLERANCE_RADIAL")?.parse()?;
	let location_tolerance_crosstrack : i64 =
	    Self::read_kv_string_at(
		rd,rec,2164,44,
		"LOCATION_TOLERANCE_CROSSTRACK")?.parse()?;
	let location_tolerance_alongtrack : i64 =
	    Self::read_kv_string_at(
		rd,rec,2208,44,
		"LOCATION_TOLERANCE_ALONGTRACK")?.parse()?;
	let yaw_error : i64 =
	    Self::read_kv_string_at(
		rd,rec,2252,44,
		"YAW_ERROR")?.parse()?;
	let roll_error : i64 =
	    Self::read_kv_string_at(
		rd,rec,2296,44,
		"ROLL_ERROR")?.parse()?;
	let pitch_error : i64 =
	    Self::read_kv_string_at(
		rd,rec,2340,44,
		"PITCH_ERROR")?.parse()?;
	let subsat_latitude_start : i64 = 
	    Self::read_kv_string_at(
		rd,rec,2384,44,
		"SUBSAT_LATITUDE_START")?.parse()?;
	let subsat_longitude_start : i64 = 
	    Self::read_kv_string_at(
		rd,rec,2428,44,
		"SUBSAT_LONGITUDE_START")?.parse()?;
	let subsat_latitude_end : i64 = 
	    Self::read_kv_string_at(
		rd,rec,2472,44,
		"SUBSAT_LATITUDE_END")?.parse()?;
	let subsat_longitude_end : i64 = 
	    Self::read_kv_string_at(
		rd,rec,2516,44,
		"SUBSAT_LONGITUDE_END")?.parse()?;
	let leap_second : i8 = 
	    Self::read_kv_string_at(
		rd,rec,2560,35,
		"LEAP_SECOND")?.parse()?;
	let leap_second_utc =
	    if leap_second != 0 {
		Some(tsp.parse(&Self::read_kv_string_at(
		    rd,rec,2595,48,
		    "LEAP_SECOND_UTC")?)?)
	    } else {
		None
	    };

	Ok(Self {
	    product_name,
	    parent_product_names,
	    spacecraft_id,
	    sensing_start,
	    sensing_end,
	    state_vector_time,
	    semi_major_axis,
	    eccentricity,
	    inclination,
	    perigee_argument,
	    right_ascension,
	    mean_anomaly,
	    x_position,
	    y_position,
	    z_position,
	    x_velocity,
	    y_velocity,
	    z_velocity,
	    earth_sun_distance_ratio,
	    location_tolerance_radial,
	    location_tolerance_crosstrack,
	    location_tolerance_alongtrack,
	    yaw_error,
	    roll_error,
	    pitch_error,
	    subsat_latitude_start,
	    subsat_longitude_start,
	    subsat_latitude_end,
	    subsat_longitude_end,
	    leap_second,
	    leap_second_utc,
	    orbit_start,
	    orbit_end,
	})
    }

    pub fn read_kv_string_at<R:Read+Seek>(rd:&mut NatReader<R>,rec:&Grh,
					  offset:u64,
					  size:usize,
					  name:&str)->
	Result<String>
    {
	Self::read_opt_kv_string_at(rd,rec,offset,size,name)?
	.ok_or_else(|| anyhow!("Missing value for {}",name))
    }

    pub fn read_opt_kv_string_at<R:Read+Seek>(rd:&mut NatReader<R>,
					      rec:&Grh,
					      offset:u64,
					      size:usize,
					      name:&str)->
	Result<Option<String>>
    {
	rec.seek_to_record(rd,offset)?;
	let mut u = vec![0;size];
	rd.read_exact(&mut u)?;
	let u = String::from_utf8_lossy(&u[..]);
	if let Some((v,w)) = u.trim().split_once('=') {
	    let v = v.trim();
	    if v == name {
		let w = w.trim();
		if w.chars().all(|c| c == 'x') {
		    Ok(None)
		} else {
		    Ok(Some(w.to_string()))
		}
	    } else {
		bail!("Unexpected key {:?}, was expecing {}",v,name);
	    }
	} else {
	    bail!("Invalid string {:?}",u);
	}
    }

    pub fn read_string_at<R:Read+Seek>(rd:&mut NatReader<R>,rec:&Grh,
				       offset:u64,
				       size:usize)->
	Result<String>
    {
	rec.seek_to_record(rd,offset)?;
	let mut u = vec![0;size];
	rd.read_exact(&mut u)?;
	Ok(String::from_utf8_lossy(&u[..]).to_string())
    }
}