File Coverage

blib/lib/Time/TT.pm
Criterion Covered Total %
statement 38 112 33.9
branch 0 24 0.0
condition 0 3 0.0
subroutine 14 19 73.6
pod 5 5 100.0
total 57 163 34.9


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Time::TT - Terrestrial Time and its realisations
4              
5             =head1 SYNOPSIS
6              
7             use Time::TT qw(tt_instant_to_mjd tt_mjd_to_instant);
8              
9             $mjd = tt_instant_to_mjd($instant);
10             $instant = tt_mjd_to_instant($mjd);
11              
12             use Time::TT qw(tt_instant_to_jepoch tt_jepoch_to_instant);
13              
14             $jepoch = tt_instant_to_jepoch($instant);
15             $instant = tt_jepoch_to_instant($jepoch);
16              
17             use Time::TT qw(tt_realisation);
18              
19             $rln = tt_realisation("bipm05");
20             $instant = $rln->from_tai($tai_instant);
21              
22             =head1 DESCRIPTION
23              
24             Terrestrial Time (TT) is a time scale representing time on the surface
25             of Terra. Specifically, it is the proper time experienced by a clock
26             located on the rotating geoid (i.e., at sea level). It is indirectly the
27             basis for Terran civil timekeeping, via its realisation International
28             Atomic Time (TAI). It is linearly related to (and in fact now defined
29             in terms of) the astronomical time scale Geocentric Coordinate Time (TCG).
30              
31             This module represents instants on the TT time scale as a scalar number
32             of SI seconds since an epoch. This is an appropriate form for all manner
33             of calculations. The TT scale is defined with a well-known point at
34             TAI instant 1977-01-01T00:00:00.0. That instant is assigned the scalar
35             value 599_616_000 exactly, corresponding to an epoch (scalar value zero)
36             near the TAI epoch 1958-01-01T00:00:00.0. This matches the convention
37             used by C for instants on the TAI scale. Because TAI does
38             not match the rate of TT perfectly, the TT epoch is not precisely equal
39             to the TAI epoch, but is instead around 600 us earlier than it.
40              
41             There is also a conventional way to represent TT instants using day-based
42             notations associated with planetary rotation `time' scales. The `day'
43             of TT is a nominal period of exactly 86400 SI seconds, which is slightly
44             shorter than an actual Terran day. The well-known point at TAI instant
45             1977-01-01T00:00:00.0 is assigned the label 1977-01-01T00:00:32.184
46             (MJD 43144.0003725). Because TT is not connected to Terran rotation,
47             and so has no inherent concept of a day, it is somewhat misleading to
48             use such day-based notations. Conversion between this notation and the
49             linear count of seconds is supported by this module. This notation does
50             not match the similar day-based notation used for TAI.
51              
52             There is another conventional way to represent TT instants, using a larger
53             unit approximating the duration of a Terran year. The `Julian year'
54             is a nominal period of exactly 365.25 `days' of exactly 86400 SI seconds
55             each. The TT instant 2000-01-01T12:00:00.0 (MJD 51544.5) is labelled as
56             Julian epoch 2000.0. Julian epochs are used only with TT, not with any
57             other time scale. The Julian epoch numbers correspond approximately to
58             Gregorian calendar years, for dates within a few kiloyears of the epoch.
59             Because TT is not connected to the Terran orbit, and so has no inherent
60             concept of a year, the year-based notation is somewhat misleading.
61             Conversion between this notation and the linear count of seconds is
62             supported by this module.
63              
64             Because TT is a theoretical time scale, not directly accessible for
65             practical use, it must be realised using atomic clocks. This is done
66             by metrological agencies, each with different imperfections. To achieve
67             microsecond accuracy it is necessary to take account of these differences.
68             This module supports conversion of times between different realisations
69             of TT.
70              
71             =cut
72              
73             package Time::TT;
74              
75 2     2   50542 { use 5.006; }
  2         7  
  2         82  
76 2     2   13 use warnings;
  2         3  
  2         61  
77 2     2   25 use strict;
  2         9  
  2         78  
78              
79 2     2   10 use Carp qw(croak);
  2         3  
  2         196  
80 2     2   2134 use Math::BigRat 0.13;
  2         181182  
  2         12  
81              
82             our $VERSION = "0.005";
83              
84 2     2   4520 use parent "Exporter";
  2         681  
  2         14  
85             our @EXPORT_OK = qw(
86             tt_instant_to_mjd tt_mjd_to_instant
87             tt_instant_to_jepoch tt_jepoch_to_instant
88             tt_realisation
89             );
90              
91             =head1 FUNCTIONS
92              
93             =over
94              
95             =item tt_instant_to_mjd(INSTANT)
96              
97             Converts from a count of seconds to a Modified Julian Date in the manner
98             conventional for TT. The MJD can be further converted to other forms of
99             day-based date using other modules. The input must be a C
100             object, and the result is the same type.
101              
102             =cut
103              
104 2     2   208 use constant TT_EPOCH_MJD => Math::BigRat->new("36204.0003725");
  2         3  
  2         13  
105              
106             sub tt_instant_to_mjd($) {
107 4     4 1 5796 my($tt) = @_;
108 4         18 return TT_EPOCH_MJD + ($tt / 86400);
109             }
110              
111             =item tt_mjd_to_instant(MJD)
112              
113             Converts from a Modified Julian Date, interpreted in the manner
114             conventional for TT, to a count of seconds. The input must be a
115             C object, and the result is the same type.
116              
117             =cut
118              
119             sub tt_mjd_to_instant($) {
120 4     4 1 4559 my($mjd) = @_;
121 4         14 return ($mjd - TT_EPOCH_MJD) * 86400;
122             }
123              
124             =item tt_instant_to_jepoch(INSTANT)
125              
126             Converts from a count of seconds to a Julian epoch. The input must be
127             a C object, and the result is the same type.
128              
129             =cut
130              
131 2     2   2296 use constant TT_EPOCH_JEPOCH => 1958 + Math::BigRat->new("0.0003725/365.25");
  2         4  
  2         12  
132              
133             sub tt_instant_to_jepoch($) {
134 3     3 1 24464 my($tt) = @_;
135 3         12 return TT_EPOCH_JEPOCH + ($tt / 31557600);
136             }
137              
138             =item tt_jepoch_to_instant(JEPOCH)
139              
140             Converts from a Julian epoch to a count of seconds. The input must be
141             a C object, and the result is the same type.
142              
143             =cut
144              
145             sub tt_jepoch_to_instant($) {
146 3     3 1 5242 my($jepoch) = @_;
147 3         14 return ($jepoch - TT_EPOCH_JEPOCH) * 31557600;
148             }
149              
150             =item tt_realisation(NAME)
151              
152             Looks up and returns an object representing a named realisation of TT.
153             The object returned is of the class C; see the
154             documentation of that class for its interface.
155              
156             The name, recognised case-insensitively, may be of these forms:
157              
158             =over
159              
160             =item B
161              
162             Retrospective best estimate of TT, published by the BIPM. TT(BIPM05)
163             was published in 2005, and other versions were (and will be) published
164             in other years, with the digits in the name varying accordingly.
165             These time scales are currently based on reanalysis of the TAI data.
166             They are defined by isolated data points, so conversions in general
167             involve interpolation; the process is by its nature inexact.
168              
169             =item B
170              
171             TT(EAL) is derived from the Free Atomic Scale (EAL). EAL is the
172             weighted average of the time ticked by the clocks contributing to TAI,
173             with no gravitational correction applied. TAI is generated by applying
174             a frequency shift to EAL to correct for gravitational time dilation.
175             The relationship between EAL and TAI is precisely defined, so conversions
176             are exact.
177              
178             =item B
179              
180             TT(TAI) is the principal realisation of TT, derived directly from
181             International Atomic Time (TAI). This is defined monthly in retrospect
182             and then never revised.
183              
184             =item B
185              
186             TT(TAI) based on TAI(NPL), the real-time estimate of TAI supplied by
187             the National Physical Laboratory in the UK. Other real-time estimates
188             of TAI are named similarly using an abbreviation of the name of the
189             supplying agency. See the C function in L
190             for more discussion, or L for a list of agencies.
191              
192             =back
193              
194             Other names may be recognised in the future, as more TT(k) time scales
195             are defined.
196              
197             In order to use any of the TAI-based realisations the C
198             module is required.
199              
200             =cut
201              
202             #
203             # general
204             #
205              
206             sub _get_bipm_file($) {
207 0     0     my($fn) = @_;
208 0           require Net::FTP::Tiny;
209 0           Net::FTP::Tiny->VERSION(0.001);
210 0           return Net::FTP::Tiny::ftp_get("ftp://ftp2.bipm.fr/pub/tai/$fn");
211             }
212              
213             #
214             # TT(BIPMnn)
215             #
216              
217 2     2   6438 use constant TT_SYNCH_TIME => Math::BigRat->new(599616000);
  2         6  
  2         12  
218 2     2   402 use constant TT_SYNCH_MJD => 43144;
  2         5  
  2         3228  
219              
220             my %tt_bipmnn;
221              
222             sub _tt_bipmnn($) {
223 0     0     my($yr) = @_;
224 0           my $r = $tt_bipmnn{$yr};
225 0 0         return $r if defined $r;
226 0           my $content = _get_bipm_file("scale/ttbipm.$yr");
227 0 0         $content =~ /\A[\ \t\n]*TT\(BIPM[0-9]{2,4}\)\ is\ a\ realization/
228             or die "doesn't look like a TT(BIPMnn) file\n";
229 0           require Time::TT::OffsetKnot;
230 0           Time::TT::OffsetKnot->VERSION(0.005);
231 0           my @data;
232 0           my $last_mjd = 0;
233 0           while($content =~ /^\ *([0-9]+)\.(?:[-+]|\ +[-+]?)[0-9]+(?:\.[0-9]+)?
234             ([-+]|\ +[-+]?)([0-9]+(?:\.[0-9]+)?)\ *[\r\n]/xmg) {
235 0           my($mjd, $sign, $offset_us) = ($1, $2, $3);
236 0 0         die "data out of order at mjd=$mjd" unless $mjd > $last_mjd;
237 0 0 0       if($last_mjd < TT_SYNCH_MJD && $mjd >= TT_SYNCH_MJD) {
238 0           require Math::Interpolator::Knot;
239 0           Math::Interpolator::Knot->VERSION(0.003);
240 0           push @data, Math::Interpolator::Knot
241             ->new(TT_SYNCH_TIME, TT_SYNCH_TIME);
242             }
243 0 0         $offset_us = "-$offset_us" unless $sign =~ /-\z/;
244 0           push @data, Time::TT::OffsetKnot->new($mjd, $offset_us, 6);
245 0           $last_mjd = $mjd;
246             }
247 0           require Math::Interpolator::Robust;
248 0           Math::Interpolator::Robust->VERSION(0.003);
249 0           require Time::TT::InterpolatingRealisation;
250 0           Time::TT::InterpolatingRealisation->VERSION(0.005);
251 0           $r = Time::TT::InterpolatingRealisation->new(
252             Math::Interpolator::Robust->new(@data));
253 0           return $tt_bipmnn{$yr} = $r;
254             }
255              
256             #
257             # TT(EAL)
258             #
259              
260             my $tt_eal;
261              
262             sub _tt_eal() {
263 0 0   0     return $tt_eal if defined $tt_eal;
264 0           my $content = _get_bipm_file("scale/ealtai04.ar");
265 0 0         $content =~ /\A[\ \t\n]*[^\n]*differences between the normalized/i
266             or die "doesn't look like an EAL file\n";
267 0           require Math::Interpolator::Knot;
268 0           Math::Interpolator::Knot->VERSION(0.003);
269 0           my @data;
270 0           my $tai = Math::BigRat->new(-94694400); # 1955-01-01
271 0           push @data, Math::Interpolator::Knot->new($tai, $tai);
272 0           $tai = Math::BigRat->new(599616000); # 1977-01-01
273 0           push @data, Math::Interpolator::Knot->new($tai, $tai);
274 0           my $mjd = Math::BigRat->new(43144);
275 0           my $eal = $tai;
276 0           my $fdiff_scale = Math::BigRat->new("0.0000000000001");
277 0           while($content =~ /^\ *[0-9]+\ +[A-Za-z]+\ +[0-9]+\ +-
278             \ +[0-9]+\ +[A-Za-z]+\ +[0-9]+
279             \ +([0-9]+)\ +-\ +([0-9]+)
280             \ +([0-9]+(?:\.[0-9]+)?)[\ \t\n]/xmg) {
281 0           my($old_mjd, $new_mjd, $fdiff) = ($1, $2, $3);
282 0           $old_mjd = Math::BigRat->new($old_mjd);
283 0 0         die "data not contiguous at mjd=$mjd" unless $old_mjd == $mjd;
284 0           $new_mjd = Math::BigRat->new($new_mjd);
285 0           $fdiff = Math::BigRat->new($fdiff) * $fdiff_scale;
286 0           my $tai_s = ($new_mjd - $mjd) * 86400;
287 0           my $eal_s = $tai_s * (1 + $fdiff);
288 0           $mjd = $new_mjd;
289 0           $tai += $tai_s;
290 0           $eal += $eal_s;
291 0           push @data, Math::Interpolator::Knot->new($tai, $eal);
292             }
293 0           $tai += 1000000;
294 0           $eal += 1000000;
295 0           require Math::Interpolator::Source;
296 0           Math::Interpolator::Source->VERSION(0.003);
297             push @data, Math::Interpolator::Source->new(
298 0     0     sub () { croak "later data for TT(EAL) is missing"; },
299 0           $tai, $eal);
300 0           require Math::Interpolator::Linear;
301 0           Math::Interpolator::Linear->VERSION(0.003);
302 0           require Time::TT::InterpolatingRealisation;
303 0           Time::TT::InterpolatingRealisation->VERSION(0.005);
304 0           $tt_eal = Time::TT::InterpolatingRealisation->new(
305             Math::Interpolator::Linear->new(@data));
306 0           return $tt_eal;
307             }
308              
309             #
310             # invocation of realisations
311             #
312              
313             sub tt_realisation($) {
314 0     0 1   my($k) = @_;
315 0 0         if($k =~ m#\Atai(?:/(.+))?\z#si) {
    0          
    0          
316 0           require Time::TAI;
317 0 0         return Time::TAI::tai_realisation(defined($1) ? $1 : "");
318             } elsif($k =~ m#\Abipm([0-9][0-9])\z#i) {
319 0           my $yr = $1;
320 0           return _tt_bipmnn($yr);
321             } elsif($k =~ m#\Aeal\z#i) {
322 0           return _tt_eal();
323             } else {
324 0           croak "no realisation TT(".uc($k).") known";
325             }
326             }
327              
328             =back
329              
330             =head1 BUGS
331              
332             The data for EAL only goes forward to mid-2005. There is no
333             machine-readable source of subsequent data.
334              
335             =head1 SEE ALSO
336              
337             L,
338             L,
339             L,
340             L,
341             L
342              
343             =head1 AUTHOR
344              
345             Andrew Main (Zefram)
346              
347             =head1 COPYRIGHT
348              
349             Copyright (C) 2006, 2007, 2010, 2012
350             Andrew Main (Zefram)
351              
352             =head1 LICENSE
353              
354             This module is free software; you can redistribute it and/or modify it
355             under the same terms as Perl itself.
356              
357             =cut
358              
359             1;