Skip to content

Commit

Permalink
Add BPC fetching
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherRabotin committed Jun 22, 2023
1 parent c021166 commit c2a377d
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 271 deletions.
16 changes: 5 additions & 11 deletions src/bin/anise/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use anise::cli::inspect::{BpcRow, SpkRow};
use anise::cli::CliErrors;
use anise::file_mmap;
use anise::naif::daf::{FileRecord, NAIFRecord, NAIFSummaryRecord};
use anise::naif::kpl::parser::convert_tpc;
use anise::prelude::*;
use anise::structure::dataset::{DataSet, DataSetType};
use anise::structure::metadata::Metadata;
Expand All @@ -18,6 +19,7 @@ use zerocopy::FromBytes;

const LOG_VAR: &str = "ANISE_LOG";

#[cfg(feature = "std")]
fn main() -> Result<(), CliErrors> {
if var(LOG_VAR).is_err() {
set_var(LOG_VAR, "INFO");
Expand Down Expand Up @@ -223,19 +225,11 @@ fn main() -> Result<(), CliErrors> {
gmfile,
outfile,
} => {
if cfg!(feature = "std") {
use anise::naif::kpl::parser::convert_tpc;
let dataset = convert_tpc(pckfile, gmfile).map_err(CliErrors::AniseError)?;

let dataset = convert_tpc(pckfile, gmfile).map_err(CliErrors::AniseError)?;
dataset.save_as(outfile, false)?;

dataset.save_as(outfile, false)?;

Ok(())
} else {
Err(CliErrors::ArgumentError(
"KPL parsing requires std feature".to_string(),
))
}
Ok(())
}
}
}
146 changes: 146 additions & 0 deletions src/context/bpc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* ANISE Toolkit
* Copyright (C) 2021-2022 Christopher Rabotin <[email protected]> et al. (cf. AUTHORS.md)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* Documentation: https://nyxspace.com/
*/

use hifitime::Epoch;

use crate::errors::AniseError;
use crate::naif::pck::BPCSummaryRecord;
use crate::naif::BPC;
use log::error;

use super::{Context, MAX_LOADED_BPCS};

impl<'a: 'b, 'b> Context<'a> {
/// Loads a Binary Planetary Constants kernel.
pub fn load_bpc(&self, bpc: &'b BPC) -> Result<Context<'b>, AniseError> {
// This is just a bunch of pointers so it doesn't use much memory.
let mut me = self.clone();
let mut data_idx = MAX_LOADED_BPCS;
for (idx, item) in self.bpc_data.iter().enumerate() {
if item.is_none() {
data_idx = idx;
break;
}
}
if data_idx == MAX_LOADED_BPCS {
return Err(AniseError::StructureIsFull);
}
me.bpc_data[data_idx] = Some(bpc);
Ok(me)
}

pub fn num_loaded_bpc(&self) -> usize {
let mut count = 0;
for maybe in self.bpc_data {
if maybe.is_none() {
break;
} else {
count += 1;
}
}

count
}

/// Returns the summary given the name of the summary record if that summary has data defined at the requested epoch and the BPC where this name was found to be valid at that epoch.
pub fn bpc_summary_from_name_at_epoch(
&self,
name: &str,
epoch: Epoch,
) -> Result<(&BPCSummaryRecord, usize, usize), AniseError> {
for (no, maybe_bpc) in self
.bpc_data
.iter()
.take(self.num_loaded_bpc())
.rev()
.enumerate()
{
let bpc = maybe_bpc.unwrap();
if let Ok((summary, idx_in_bpc)) = bpc.summary_from_name_at_epoch(name, epoch) {
return Ok((summary, no, idx_in_bpc));
}
}

// If we're reached this point, there is no relevant summary at this epoch.
error!("Context: No summary {name} valid at epoch {epoch}");
Err(AniseError::MissingInterpolationData(epoch))
}

/// Returns the summary given the name of the summary record if that summary has data defined at the requested epoch
pub fn bpc_summary_at_epoch(
&self,
id: i32,
epoch: Epoch,
) -> Result<(&BPCSummaryRecord, usize, usize), AniseError> {
// TODO: Consider a return type here
for (no, maybe_bpc) in self
.bpc_data
.iter()
.take(self.num_loaded_bpc())
.rev()
.enumerate()
{
let bpc = maybe_bpc.unwrap();
if let Ok((summary, idx_in_bpc)) = bpc.summary_from_id_at_epoch(id, epoch) {
// NOTE: We're iterating backward, so the correct BPC number is "total loaded" minus "current iteration".
return Ok((summary, self.num_loaded_bpc() - no - 1, idx_in_bpc));
}
}

error!("Context: No summary {id} valid at epoch {epoch}");
// If we're reached this point, there is no relevant summary at this epoch.
Err(AniseError::MissingInterpolationData(epoch))
}

/// Returns the summary given the name of the summary record.
pub fn bpc_summary_from_name(
&self,
name: &str,
) -> Result<(&BPCSummaryRecord, usize, usize), AniseError> {
for (bpc_no, maybe_bpc) in self
.bpc_data
.iter()
.take(self.num_loaded_bpc())
.rev()
.enumerate()
{
let bpc = maybe_bpc.unwrap();
if let Ok((summary, idx_in_bpc)) = bpc.summary_from_name(name) {
return Ok((summary, bpc_no, idx_in_bpc));
}
}

// If we're reached this point, there is no relevant summary at this epoch.
error!("Context: No summary {name} valid");
Err(AniseError::NoInterpolationData)
}

/// Returns the summary given the name of the summary record if that summary has data defined at the requested epoch
pub fn bpc_summary(&self, id: i32) -> Result<(&BPCSummaryRecord, usize, usize), AniseError> {
// TODO: Consider a return type here
for (no, maybe_bpc) in self
.bpc_data
.iter()
.take(self.num_loaded_bpc())
.rev()
.enumerate()
{
let bpc = maybe_bpc.unwrap();
if let Ok((summary, idx_in_bpc)) = bpc.summary_from_id(id) {
// NOTE: We're iterating backward, so the correct BPC number is "total loaded" minus "current iteration".
return Ok((summary, self.num_loaded_bpc() - no - 1, idx_in_bpc));
}
}

error!("Context: No summary {id} valid");
// If we're reached this point, there is no relevant summary
Err(AniseError::NoInterpolationData)
}
}
165 changes: 3 additions & 162 deletions src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,21 @@
* Documentation: https://nyxspace.com/
*/

use hifitime::Epoch;

use crate::errors::AniseError;
use crate::naif::spk::summary::SPKSummaryRecord;
use crate::naif::{BPC, SPK};
use crate::structure::dataset::DataSet;
use crate::structure::planetocentric::PlanetaryData;
use crate::structure::spacecraft::SpacecraftData;
use core::fmt;
use log::error;

// TODO: Switch these to build constants so that it's configurable when building the library.
pub const MAX_LOADED_SPKS: usize = 32;
pub const MAX_LOADED_BPCS: usize = 8;
pub const MAX_SPACECRAFT_DATA: usize = 16;
pub const MAX_PLANETARY_DATA: usize = 64;

pub mod bpc;
pub mod spk;

/// A SPICE context contains all of the loaded SPICE data.
///
/// # Limitations
Expand All @@ -41,163 +39,6 @@ pub struct Context<'a> {
pub spacecraft_data: DataSet<'a, SpacecraftData<'a>, MAX_SPACECRAFT_DATA>,
}

impl<'a: 'b, 'b> Context<'a> {
pub fn from_spk(spk: &'a SPK) -> Result<Context<'a>, AniseError> {
let me = Self::default();
me.load_spk(spk)
}

/// Loads a new SPK file into a new context.
/// This new context is needed to satisfy the unloading of files. In fact, to unload a file, simply let the newly loaded context drop out of scope and Rust will clean it up.
pub fn load_spk(&self, spk: &'b SPK) -> Result<Context<'b>, AniseError> {
// This is just a bunch of pointers so it doesn't use much memory.
let mut me = self.clone();
// Parse as SPK and place into the SPK list if there is room
let mut data_idx = MAX_LOADED_SPKS;
for (idx, item) in self.spk_data.iter().enumerate() {
if item.is_none() {
data_idx = idx;
break;
}
}
if data_idx == MAX_LOADED_SPKS {
return Err(AniseError::StructureIsFull);
}
me.spk_data[data_idx] = Some(spk);
Ok(me)
}

/// Loads a Binary Planetary Constants kernel.
pub fn load_bpc(&self, bpc: &'b BPC) -> Result<Context<'b>, AniseError> {
// This is just a bunch of pointers so it doesn't use much memory.
let mut me = self.clone();
// Parse as SPK and place into the SPK list if there is room
let mut data_idx = MAX_LOADED_SPKS;
for (idx, item) in self.bpc_data.iter().enumerate() {
if item.is_none() {
data_idx = idx;
break;
}
}
if data_idx == MAX_LOADED_SPKS {
return Err(AniseError::StructureIsFull);
}
me.bpc_data[data_idx] = Some(bpc);
Ok(me)
}

pub fn num_loaded_spk(&self) -> usize {
let mut count = 0;
for maybe in self.spk_data {
if maybe.is_none() {
break;
} else {
count += 1;
}
}

count
}

/// Returns the summary given the name of the summary record if that summary has data defined at the requested epoch and the SPK where this name was found to be valid at that epoch.
pub fn spk_summary_from_name_at_epoch(
&self,
name: &str,
epoch: Epoch,
) -> Result<(&SPKSummaryRecord, usize, usize), AniseError> {
for (spk_no, maybe_spk) in self
.spk_data
.iter()
.take(self.num_loaded_spk())
.rev()
.enumerate()
{
let spk = maybe_spk.unwrap();
if let Ok((summary, idx_in_spk)) = spk.summary_from_name_at_epoch(name, epoch) {
return Ok((summary, spk_no, idx_in_spk));
}
}

// If we're reached this point, there is no relevant summary at this epoch.
error!("Context: No summary {name} valid at epoch {epoch}");
Err(AniseError::MissingInterpolationData(epoch))
}

/// Returns the summary given the name of the summary record if that summary has data defined at the requested epoch
pub fn spk_summary_from_id_at_epoch(
&self,
id: i32,
epoch: Epoch,
) -> Result<(&SPKSummaryRecord, usize, usize), AniseError> {
// TODO: Consider a return type here
for (spk_no, maybe_spk) in self
.spk_data
.iter()
.take(self.num_loaded_spk())
.rev()
.enumerate()
{
let spk = maybe_spk.unwrap();
if let Ok((summary, idx_in_spk)) = spk.summary_from_id_at_epoch(id, epoch) {
// NOTE: We're iterating backward, so the correct SPK number is "total loaded" minus "current iteration".
return Ok((summary, self.num_loaded_spk() - spk_no - 1, idx_in_spk));
}
}

error!("Context: No summary {id} valid at epoch {epoch}");
// If we're reached this point, there is no relevant summary at this epoch.
Err(AniseError::MissingInterpolationData(epoch))
}

/// Returns the summary given the name of the summary record.
pub fn spk_summary_from_name(
&self,
name: &str,
) -> Result<(&SPKSummaryRecord, usize, usize), AniseError> {
for (spk_no, maybe_spk) in self
.spk_data
.iter()
.take(self.num_loaded_spk())
.rev()
.enumerate()
{
let spk = maybe_spk.unwrap();
if let Ok((summary, idx_in_spk)) = spk.summary_from_name(name) {
return Ok((summary, spk_no, idx_in_spk));
}
}

// If we're reached this point, there is no relevant summary at this epoch.
error!("Context: No summary {name} valid");
Err(AniseError::NoInterpolationData)
}

/// Returns the summary given the name of the summary record if that summary has data defined at the requested epoch
pub fn spk_summary_from_id(
&self,
id: i32,
) -> Result<(&SPKSummaryRecord, usize, usize), AniseError> {
// TODO: Consider a return type here
for (spk_no, maybe_spk) in self
.spk_data
.iter()
.take(self.num_loaded_spk())
.rev()
.enumerate()
{
let spk = maybe_spk.unwrap();
if let Ok((summary, idx_in_spk)) = spk.summary_from_id(id) {
// NOTE: We're iterating backward, so the correct SPK number is "total loaded" minus "current iteration".
return Ok((summary, self.num_loaded_spk() - spk_no - 1, idx_in_spk));
}
}

error!("Context: No summary {id} valid");
// If we're reached this point, there is no relevant summary
Err(AniseError::NoInterpolationData)
}
}

impl<'a> fmt::Display for Context<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "Context: #SPK = {}", self.num_loaded_spk())
Expand Down
Loading

0 comments on commit c2a377d

Please sign in to comment.