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
...
endRequired 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<:SpaceIndexSet -> 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<:SpaceIndexSet -> 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<:SpaceIndexSet -> TThis 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(...)
endFinally, 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...) -> Numberwhere the space index for the instant will be returned.
Optional API Functions
function SpaceIndices.auto_init(::Type{T}) where T<:SpaceIndexSet -> BoolThis function returns whether the space index set T should be initialized automatically by the no-argument init(). Sets that return false must be initialized explicitly via init(T). By default, it always returns true.
function SpaceIndices.filenames(::Type{T}) where T<:SpaceIndexSet -> 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.
function SpaceIndices.indices(::Type{T}) where T<:SpaceIndexSet -> TupleThis function can return a tuple with the symbols of the space indices provided by the space index set T. This information is used, for example, by the function SpaceIndices.status to list the indices of the registered sets. For example:
SpaceIndices.indices(::Type{MySpaceIndex}) = (:MyIndex,)If this function is not defined, it returns an empty tuple.
function SpaceIndices.timespan(::Val{:index}) -> Union{Nothing, NTuple{2, Float64}}This function can return a tuple with the initial and final Julian days (UTC) in which the data for the space index is available. If this information is returned, it must be guaranteed that calling space_index for the same index does not fail for any instant inside the returned interval. For example:
function SpaceIndices.timespan(::Val{:MyIndex})
obj = SpaceIndices.@object(MySpaceIndex)
return (first(obj.vjd), last(obj.vjd))
endIf this function is not defined for the index, it returns nothing, meaning that there is no information about the interval in which the index is available.
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, DatesNow, we need to create its structure:
struct LeapSeconds <: SpaceIndexSet
jd::Vector{Float64}
leap_seconds::Vector{Float64}
endwhere 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_files(::Type{LeapSeconds}, filepaths::Vector{String})
filepath = first(filepaths)
raw_data, ~ = readdlm(filepath, ';'; header = true)
return LeapSeconds(raw_data[:, 1], raw_data[:, 2])
endWe 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]
endFinally, we need to register the new space index file:
SpaceIndices.@register LeapSecondsWe 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