From 9da7878fe2ba9a884f398185e2b0f2bd60fc4153 Mon Sep 17 00:00:00 2001 From: Helge Eichhorn Date: Sat, 2 Feb 2019 13:56:53 +0100 Subject: [PATCH] Allow additional parameters for custom time scales --- REQUIRE | 1 + src/AstroTime.jl | 58 +++++++++++++++++++++++++++++++++++------------- src/Epochs.jl | 30 +++++++++---------------- src/offsets.jl | 10 ++++----- test/runtests.jl | 24 ++++++++++++++++++++ 5 files changed, 83 insertions(+), 40 deletions(-) diff --git a/REQUIRE b/REQUIRE index 28a98c5..6151005 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,5 +1,6 @@ julia 0.7 EarthOrientation 0.4.0 LeapSeconds 0.2.0 +MacroTools 0.4.4 MuladdMacro 0.0.2 Reexport 0.1.0 diff --git a/src/AstroTime.jl b/src/AstroTime.jl index c2da56a..4d9090c 100644 --- a/src/AstroTime.jl +++ b/src/AstroTime.jl @@ -4,6 +4,7 @@ using EarthOrientation using Reexport import Dates +import MacroTools export @timescale @@ -116,16 +117,24 @@ function __init__() end """ - @timescale scale + @timescale scale epoch [args...] body -Define a new timescale and the corresponding `Epoch` type alias. +Define a new time scale, the corresponding `Epoch` type alias, and a function that calculates +the offset from TAI for the new time scale. + +### Arguments ### + +- `scale`: The name of the time scale +- `epoch`: The name of the `Epoch` parameter that is passed to the `tai_offset` function +- `args`: Optional additional parameters that are passed to the `tai_offset` function +- `body`: The body of the `tai_offset` function # Example ```jldoctest julia> @timescale GMT ep tai_offset(UTC, ep) -julia> GMT <: TimeScale +julia> GMT isa TimeScale true julia> GMTEpoch @@ -133,16 +142,36 @@ Epoch{GMT,T} where T ``` """ macro timescale(scale::Symbol, ep::Symbol, args...) - epoch = Expr(:escape, Symbol(scale, "Epoch")) - sc = Expr(:escape, scale) + scale_type = Symbol(scale, "Type") + epoch_type = Symbol(scale, "Epoch") name = String(scale) - return quote - struct $sc <: TimeScale end - const $epoch = Epoch{$sc()} - Base.show(io::IO, $sc) = print(io, string(typeof($sc))) - AstroTime.TimeScales.tryparse(::Val{Symbol($name)}) = $sc() - Dates.CONVERSION_TRANSLATIONS[$epoch] = ( + arglist = [ + (nothing, scale_type, false, nothing), + (ep, Epoch, false, nothing), + ] + body = args[end] + append!(arglist, MacroTools.splitarg.(args[1:end-1])) + args = map(x->MacroTools.combinearg(x...), arglist) + def = Dict{Symbol, Any}( + :name => :((AstroTime.Epochs).tai_offset), + :args => map(x->MacroTools.combinearg(x...), arglist), + :body => body, + :kwargs => Any[], + :whereparams => (), + ) + func = esc(MacroTools.combinedef(def)) + + MacroTools.@esc scale scale_type epoch_type + + MacroTools.@q begin + struct $scale_type <: TimeScale end + const $scale = $scale_type() + const $epoch_type = Epoch{$scale} + Base.show(io::IO, ::$scale_type) = print(io, $name) + AstroTime.TimeScales.tryparse(::Val{Symbol($name)}) = $scale + + Dates.CONVERSION_TRANSLATIONS[$epoch_type] = ( Dates.Year, Dates.Month, Dates.Day, @@ -151,10 +180,9 @@ macro timescale(scale::Symbol, ep::Symbol, args...) Dates.Second, Dates.Millisecond, ) - Dates.default_format(::Type{$epoch}) = Dates.ISODateTimeFormat - function AstroTime.Epochs.tai_offset(::$sc, $ep) - $(args[end]) - end + Dates.default_format(::Type{$epoch_type}) = Dates.ISODateTimeFormat + + $func nothing end diff --git a/src/Epochs.jl b/src/Epochs.jl index 3beb797..2b40878 100644 --- a/src/Epochs.jl +++ b/src/Epochs.jl @@ -116,7 +116,7 @@ julia> Epoch{UTC}(2.451545e6, origin=:julian) 2000-01-01T12:00:00.000 UTC ``` """ -function Epoch{S}(jd1::T, jd2::T=zero(T); origin=:j2000) where {S, T<:Number} +function Epoch{S}(jd1::T, jd2::T=zero(T), args...; origin=:j2000) where {S, T<:Number} if jd2 > jd1 jd1, jd2 = jd2, jd1 end @@ -146,7 +146,7 @@ function Epoch{S}(jd1::T, jd2::T=zero(T); origin=:j2000) where {S, T<:Number} ftype = float(T) tai = Epoch{TAI}(epoch, ftype(offset), zero(ftype)) - ts_offset = tai_offset(S, tai) + ts_offset = tai_offset(S, tai, args...) ep = Epoch{TAI}(tai, -ts_offset) Epoch{S}(ep.epoch, ep.offset, ts_offset) end @@ -285,9 +285,9 @@ include("accessors.jl") show(io::IO, ep::Epoch{S}) where {S} = print(io, DateTime(ep), " ", S) -function Epoch{S}(date::Date, time::Time) where S +function Epoch{S}(date::Date, time::Time, args...) where S seconds = second(Float64, time) - ts_offset = tai_offset(S, date, time) + ts_offset = tai_offset(S, date, time, args...) sum = seconds + ts_offset s′ = sum - ts_offset @@ -300,7 +300,7 @@ function Epoch{S}(date::Date, time::Time) where S offset = (sum - dl) + residual epoch = Int64(60) * ((j2000(date) * Int64(24) + hour(time)) * Int64(60) + minute(time) - Int64(720)) + dl - from_tai = tai_offset(S, Epoch{TAI}(epoch, offset, 0.0)) + from_tai = tai_offset(S, Epoch{TAI}(epoch, offset, 0.0), args...) Epoch{S}(epoch, offset, from_tai) end @@ -389,8 +389,8 @@ julia> Epoch{UTC}(2018, 2, 6) ``` """ function Epoch{S}(year::Int, month::Int, day::Int, hour::Int=0, - minute::Int=0, second::Float64=0.0) where S - Epoch{S}(Date(year, month, day), Time(hour, minute, second)) + minute::Int=0, second::Float64=0.0, args...) where S + Epoch{S}(Date(year, month, day), Time(hour, minute, second), args...) end function Epoch{S}(year::Int64, month::Int64, day::Int64, dayofyear::Int64, @@ -419,7 +419,7 @@ function Epoch(year::Int64, month::Int64, day::Int64, dayofyear::Int64, Epoch{scale}(date, Time(hour, minute, second + 1e-3milliseconds)) end -function Epoch{S2}(ep::Epoch{S1}, Δtai) where {S1, S2} +function Epoch{S2}(Δtai, ep::Epoch{S1}) where {S1, S2} Epoch{S2}(ep.epoch, ep.offset, Δtai) end @@ -439,8 +439,8 @@ julia> TAIEpoch(ep) 1999-12-31T23:59:27.816 TAI ``` """ -function Epoch{S2}(ep::Epoch{S1}) where {S1, S2} - Epoch{S2}(ep.epoch, ep.offset, tai_offset(S2, ep)) +function Epoch{S2}(ep::Epoch{S1}, args...) where {S1, S2} + Epoch{S2}(ep.epoch, ep.offset, tai_offset(S2, ep, args...)) end Epoch{S, T}(ep::Epoch{S, T}) where {S, T} = ep @@ -548,16 +548,6 @@ for scale in TimeScales.ACRONYMS end end -function TDBEpoch(ep::TTEpoch, ut, elong, u, v) - offset = tai_offset(TDB, ep, ut, elong, u, v) - TDBEpoch(ep, offset) -end - -function TTEpoch(ep::TDBEpoch, ut, elong, u, v) - offset = tai_offset(TDB, ep, ut, elong, u, v) - TTEpoch(ep, offset) -end - include("leapseconds.jl") include("range.jl") diff --git a/src/offsets.jl b/src/offsets.jl index 54133c0..9be5289 100644 --- a/src/offsets.jl +++ b/src/offsets.jl @@ -133,9 +133,9 @@ function tai_offset(::BarycentricDynamicalTime, ep, elong, u, v) tai_offset(TT, ep) + w end -tai_offset(::InternationalAtomicTime, date, time) = 0.0 +tai_offset(::InternationalAtomicTime, date::Date, time::Time) = 0.0 -@inline function tai_offset(::CoordinatedUniversalTime, date, time) +@inline function tai_offset(::CoordinatedUniversalTime, date::Date, time::Time) minute_in_day = hour(time) * 60 + minute(time) correction = minute_in_day < 0 ? (minute_in_day - 1439) ÷ 1440 : minute_in_day ÷ 1440 offset = findoffset(julian(date) + correction) @@ -144,12 +144,12 @@ tai_offset(::InternationalAtomicTime, date, time) = 0.0 getoffset(offset, date, time) end -@inline function tai_offset(scale, date, time) +@inline function tai_offset(scale, date::Date, time::Time, args...) ref = Epoch{TAI}(date, time) offset = 0.0 - # Maybe replace this with a simple convergence check + # TODO: Maybe replace this with a simple convergence check for _ in 1:8 - offset = -tai_offset(scale, Epoch{TAI}(ref, offset)) + offset = -tai_offset(scale, Epoch{TAI}(ref, offset), args...) end offset end diff --git a/test/runtests.jl b/test/runtests.jl index ad2e846..3c785e9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,9 +4,33 @@ using ERFA AstroTime.update() +const speed_of_light = 299792458.0 # m/s +const astronomical_unit = 149597870700.0 # m + +@timescale GMT ep tai_offset(UTC, ep) + +@timescale SCET ep distance begin + tai_offset(UTC, ep) + distance / speed_of_light +end + @testset "AstroTime" begin include("periods.jl") include("astrodates.jl") include("offsets.jl") include("epochs.jl") + @testset "Custom Time Scales" begin + utc = UTCEpoch(2000, 1, 1, 0, 0, 0.1) + gmt = GMTEpoch(2000, 1, 1, 0, 0, 0.1) + @test utc == gmt + + @test string(GMT) == "GMT" + @test typeof(GMT) == GMTType + @test string(gmt) == "2000-01-01T00:00:00.100 GMT" + + @test string(SCET) == "SCET" + @test typeof(SCET) == SCETType + scet = SCETEpoch(2000, 1, 1, 0, 8, 19.10478383615643, astronomical_unit) + @test SCETEpoch(2000, 1, 1, 0, 8, 19.10478383615643, astronomical_unit) ≈ utc + @test SCETEpoch(value(j2000(scet)), 0.0, astronomical_unit) ≈ scet + end end