Skip to content

Managing External Data

Modern astrodynamics applications often require the use of external data for precision modeling of common phenomena. In particular, information on Earth reference frames, space weather, and planetary ephemerides (positions) are commonly provided through external data files calculated regularly by product centers (e.g. NASA JPL, ESA, etc.) and made available to the public. While some applications don't need the level of accuracy provided by these data files, they are necessary for working with the current de facto standards in astrodynamics and are needed to perform calculations accurately and make them predictable.

Historically, managing these data files has been a significant source of friction for users of astrodynamics software. You need to know where to get the data, how to download it, configure your software to use it, as well as regularly check for updates and download new versions of the data as they are released. For long-running applications, if you don't regularly update the data, the results degrade in accuracy over time or the application may stop working entirely if there are no data entries for the needed date.

Brahe provides a wide set of capabilities for downloading, setting, updating, and manging EOP and other external data files. However, the library still requires the user to be aware of these data because the selection of which data to use is a modeling choice that affects the accuracy and correctness of the results, which can ultimately only be determined by the user. As such, Brahe requires the user to explicitly load the data files before use, but generally the library handles the rest of the management process for you.

Do the Rightest Thing

One of the core design principles of Brahe is to "Do the Rightest Thing". This means that the library should make it easy for users to do the most typically correct and accurate thing per current conventions in the field. However, it should not force users to only do the default choice. It should enable informed users to be able to make different modeling choices if they want to. However, since in most cases the users just want the "standard" choice, that should be able to be done with as little friction and boilerplate code as possible.

Global Provider Design

One design choice of Brahe is that EOP and SW data providers are global entities, shared across all threads. This choice was made becaue the data is normally used extensively across many functions and calculations, so it would make for a repetitive API to pass around an EOP provider to nearly every function. Additionally, the data is only typically loaded once and then read many times, so it's possible to safely share a single copy across many threads reading it.

Default Caching Provider

If you just want to get started quickly you can use the following two lines of code to initialize the default EOP and Space Weather (SW) data providers. Internally, brahe initializes the global gravity using the CachingEOPProvider and CachingSpaceWeatherProvider data providers, which use locally cached copies of the data files. If the data files are not present, or the data is too old (default 7 days), the providers will automatically download the latest versions of the data files from the source websites and cache them locally for future use.

The age of the data is only checked when the provider is initialized, so if you want to check for updates more frequently, or even whenever the data is accessed, you need to configure a different provider.

Tip

Remember to add the necessary headers to your script. See the First Script page for more details.

import brahe as bh

# Default initializers use caching providers that automatically download new 
# data if the local data is more than 7 days old. Only updates on initialization.
bh.initialize_eop()
bh.initialize_sw()

# Print last date of data
print("EOP data available through:", bh.Epoch(bh.get_global_eop_mjd_max()))
print("SW data available through:", bh.Epoch(bh.get_global_sw_mjd_max()))
1
2
3
4
5
6
7
8
9
#[allow(unused_imports)]
use brahe as bh;

fn main() {
    // Default initializers use caching providers that automatically download new 
    // data if the local data is more than 7 days old. Only updates on initialization.
    bh::initialize_eop().unwrap();
    bh::initialize_sw().unwrap();
}
1
```

Other Providers

If you don't want to use the default providers, such as needing to check for updates regularly (for long-running processes), you want to use a local file to avoid any network calls or guarantee reproducibility, or you want to ignore the data entirely, Brahe provides a wide variety of other providers that you can use. See the EOP Providers and Space Weather sections of the user guide for more information.

For quick reference the major provides you might want to use are shown below: - Static Providers: These providers always return the same value regardless of the date. This is useful for testing, or if you want to ignore the data entirely. - Default File Providers: These providers load data from a file. This can either be a local path or one of the data files bundled with the package. - Caching Providers: These are a variant of file providers that automatically check the age of the data and download new versions of the file when needed. This is useful for ensuring the data is always up-to-date without needing to manaually manage it.

import brahe as bh

## Static Providers

# The simplest data providers are static providers. These always return the same value regarless of date.
static_provider = bh.StaticEOPProvider.from_zero()  # All values are zero

# Now we set the global provider as the static provider, which will be used for all EOP data access across the library.
bh.set_global_eop_provider(static_provider)

## File Providers

# Similarly we can use a file provider, which loads data from a local file
# The from_default_standard loads the IERS Standard EOP product that is bundled with the package
# The first argument (True) says to inerpolate values for times between data points
# The second argument ("Hold") says to hold the last value for times outside the range of the data, instead of throwing an error
default_file_provider = bh.FileEOPProvider.from_default_standard(True, "Hold")
bh.set_global_eop_provider(default_file_provider)

# There are also functions to explicitly download a specific file and and load it from a file
# Uncomment these lines to try them out
# eop_path = "eop_c04.txt"
# bh.download_c04_eop_file(eop_path)
# c04_file_provider = bh.FileEOPProvider.from_c04_file(eop_path, True, "Hold")
# bh.set_global_eop_provider(c04_file_provider)
# print("EOP data available through:", bh.Epoch(bh.get_global_eop_mjd_max()))

# eop_path = "eop_standard.txt"
# bh.download_standard_eop_file(eop_path)
# standard_file_provider = bh.FileEOPProvider.from_standard_file(eop_path, True, "Hold")
# bh.set_global_eop_provider(standard_file_provider)
# print("EOP data available through:", bh.Epoch(bh.get_global_eop_mjd_max()))
#[allow(unused_imports)]
use brahe as bh;

fn main() {
    // Static Providers

    // The simplest data providers are static providers. These always return the same value regarless of date.
    let static_provider = bh::StaticEOPProvider::from_zero();  // All values are zero

    // Now we set the global provider as the static provider, which will be used for all EOP data access across the library.
    bh::set_global_eop_provider(static_provider);

    // File Providers

    // Similarly we can use a file provider, which loads data from a local file
    // The from_default_standard loads the IERS Standard EOP product that is bundled with the package
    // The first argument (True) says to inerpolate values for times between data points
    // The second argument ("Hold") says to hold the last value for times outside the range of the data, instead of throwing an error
    let default_file_provider = bh::FileEOPProvider::from_default_standard(true, bh::EOPExtrapolation::Hold).unwrap();
    bh::set_global_eop_provider(default_file_provider);

    // There are also functions to explicitly download a specific file and and load it from a file
    // Uncomment these lines to try them out
    // let eop_path = "eop_c04.txt";
    // bh::download_c04_eop_file(eop_path).unwrap();
    // let c04_file_provider = bh::FileEOPProvider::from_c04_file(std::path::Path::new(eop_path), true, bh::EOPExtrapolation::Hold).unwrap();
    // bh::set_global_eop_provider(c04_file_provider);
    // println!("EOP data available through: {}", bh::Epoch::from_mjd(bh::get_global_eop_mjd_max(), bh::TimeSystem::UTC));

    // let eop_path = "eop_standard.txt";
    // bh::download_standard_eop_file(eop_path).unwrap();
    // let standard_file_provider = bh::FileEOPProvider::from_standard_file(std::path::Path::new(eop_path), true, bh::EOPExtrapolation::Hold).unwrap();
    // bh::set_global_eop_provider(standard_file_provider);
    // println!("EOP data available through: {}", bh::Epoch::from_mjd(bh::get_global_eop_mjd_max(), bh::TimeSystem::UTC));
}
Output