SpaceIndices.jl API

This package defines an API to allow user to defin new space indices. We describe this API in the following.

Structure

Each space index set must have a structure that has SpaceIndexSet as its super-type. This structure must contain all the required field to process and return the indices provided by the set.

struct MySpaceIndex <: SpaceIndexSet
    ...
end

Required API Functions

We must define the following functions for every space index set defined as in the previous section.

function SpaceIndices.urls(::Type{T}) where T<:SpaceIndexFile -> Vector{String}

This function must return a Vector{String} with the URLs to download the files for the indices. For example:

SpaceIndices.urls(::Type{MySpaceIndex}) = ["https://url.for.my/space.file.txt"]

function SpaceIndices.expiry_periods(::Type{T}) where T<:SpaceIndexFile -> Vector{DatePeriod}

This function must return the list with the expiry periods for the files in the space index set T. The remote files will always be downloaded if a time greater than this period has elapsed after the last download. For example:

get_filenames(::Type{MySpaceIndex}) = [Day(7)]

SpaceIndices.parse_files(::Type{T}, filepaths::Vector{String}) where T<:SpaceIndexFile -> T

This function must parse the files related to the space index set T using the files in filepaths and return an instance of T with the parsed data. For example,

function SpaceIndices.parse_files(::Type{MySpaceIndex}, filepaths::Vector{String})
    for filepath in filepaths
        open(filepath, "r") do f
            ...
        end
    end
        
    return MySpaceIndex(...)
end

Finally, the new space index set must also implement a set of functions with the following signature:

SpaceIndices.space_index(::Val{:index}, instant::DateTime; kwargs...) -> Number
SpaceIndices.space_index(::Val{:index}, jd::Number; kwargs...) -> Number

where the space index for the instant will be returned.

Optional API Functions

function SpaceIndices.filenames(::Type{T}) where T<:SpaceIndexFile -> Vector{String}

This function can return a Vector{String} with the names of the remote files. The system will used this information to save the data in the package scratch space. For example:

SpaceIndices.filenames(::Type{MySpaceIndex}) = ["my_space_file.txt"]

If this function is not defined, the filename will be obtained from the URL using the function basename.

Warning

All functions that return a Vector must return an array with the same number of elements.

Example: Leap Seconds

We will use the API to define a new space index set that has the GPS leap seconds. The file has a CSV-like format but the values are separated by ;. It has two columns. The first contains the Julian days in which the leap seconds were modified to the values in the second column:

Julian Day;Leap Seconds
2441499.500000;11.0
2441683.500000;12.0
2442048.500000;13.0
2442413.500000;14.0
...

First, we need to load the required packages to process the information in the space index file:

julia> using DelimitedFiles, Dates

Now, we need to create its structure:

struct LeapSeconds <: SpaceIndexSet
    jd::Vector{Float64}
    leap_seconds::Vector{Float64}
end

where jd contains the Julian days in which the leap seconds were modified to the values in leap_seconds.

We also need to overload the API functions:

SpaceIndices.urls(::Type{LeapSeconds}) = ["https://ronanarraes.com/space-indices/leap_seconds.csv"]
SpaceIndices.expiry_periods(::Type{LeapSeconds}) = [Day(365)]

function SpaceIndices.parse_file(::Type{LeapSeconds}, filepaths::Vector{String})
    filepath = first(filepaths)
    raw_data, ~ = readdlm(filepath, ';'; header = true)
    return LeapSeconds(raw_data[:, 1], raw_data[:, 2])
end

We also need to populate the function space_index with the supported index in this file:

function SpaceIndices.space_index(::Val{:LeapSeconds}, instant::DateTime)
    obj = SpaceIndices.@object(LeapSeconds)
    jd = datetime2julian(instant)
    id = findfirst(>=(jd_utc), obj.jd)

    if isnothing(id)
        id = length(obj.jd)
    end

    return obj.leap_seconds[id]
end

Finally, we need to register the new space index file:

SpaceIndices.@register LeapSeconds

We can now use the SpaceIndices.jl system to fetch the information:

julia> SpaceIndices.init()
[ Info: Downloading the file 'DTCFILE.TXT' from 'http://sol.spacenvironment.net/jb2008/indices/DTCFILE.TXT'...
[ Info: Downloading the file 'SOLFSMY.TXT' from 'http://sol.spacenvironment.net/jb2008/indices/SOLFSMY.TXT'...
[ Info: Downloading the file 'SW-All.csv' from 'https://celestrak.org/SpaceData/SW-All.csv'...

julia> space_index(Val(:LeapSeconds), now())
37.0