<< Basic orbit determination Cookbook B-plane >>

celestlab >> - Introduction - > Cookbook > Handling time scales

Handling time scales

Time scales - best practices

Explanations on the definition of time scales

Handling time scales correctly may be complex, so this page shows ways to make it as simple as possible. See also Dates and time scales for more information.

All date arguments in CelestLab functions are relative to the TREF time scale. This may be a little disturbing as TREF is a time scale that only exists in CelestLab. Yet, the definition of TREF is simple. TREF (according to the default definition) is as uniform and continuous as TAI:

TREF = TAI + C (with C: constant)

The constant C is defined so that TREF is identical to UTC now ("now" meaning: at the time of CelestLab release). Doing this, the orientation of the Earth should be accurate enough when TREF is used in place of UT1 (for most applications, at least).

When necessary, other function arguments enable the user to specify how TREF relates to TT and UT1. These arguments are: ut1_tref (= UT1 - TREF) and tt_tref (= TT - TREF), and are defined by:

time/UT1 = time/TREF + ut1_tref

time/TT = time/TREF + tt_tref

where time/T represents the time of some event in time scale T. For instance, the time in time scale UT1 (time/UT1) is computed by adding the quantity ut1_tref (= UT1 - TREF) to the time in time scale TREF (time/TREF). Thus ut1_tref is function of time/TREF.

The quantities ut1_tref and tt_tref are expressed in seconds. For "date" quantities, using the notation "cjd": number of days since 1 Jan 1950 0h, we have by definition: cjd/UT1 = cjd/TREF + ut1_tref / 86400 and cjd/TT = cjd/TREF + tt_tref / 86400. Thus, and to be very precise, ut1_tref and tt_tref are expressed in "TREF" seconds (seconds in TREF time scale, which is the same as "TAI" seconds, and also the same as "UTC" seconds).

Note that TT - TREF is constant (it does not depend on time) and that UT1 - TREF may not be constant. Also note that UT1 - TREF only matters if the orientation of the Earth has an influence on the results.

The functions for which the argument "ut1_tref" exists are: CL_dat_scaleConvert, CL_fr_convert, CL_fr_convertMat, CL_mod_siderealTime, CL_op_locTime, and also a few functions related to STELA and TLEs.

The functions for which the argument "tt_tref" exists are more numerous. But as we'll see in the examples, we generally don't have to care too much about this argument.

The 2 values "ut1_tref" and "tt_tref" enable the conversion to any other time scale, in particular TAI through the use of a constant that defines TT - TAI, UTC through the use of global data that define TAI - UTC (leap seconds), and of course UT1.

Here is a summary of recommended practices in order to minimize risks of errors - see examples for illustrations:

Example 1: Using TREF and default settings

This example illustrates the most common use of time scales in CelestLab: no time scale explicitly referred to, all dates are relative to TREF.

That concerns cases where:

// -----------------------------------------------------
// Example 1: 
// - Using default "time" arguments 
// - Only TREF used 
// -----------------------------------------------------
// cjd: Date/time, origin: 1 jan 1950 0h
// Time scale is supposed to be TREF (implicitly)    
cjd_tref = CL_dat_cal2cjd(2018, 8, 1, 0, 0, 0); 

// Position of the Sun
pos_sun = CL_eph_sun(cjd_tref) 

// Sidereal time 
tsid = CL_mod_siderealTime(cjd_tref)

Example 2: Using TREF, default settings, and some other time scale

This second example is similar to example 1, except that the input / output dates are known or expected in some reference time scale that is not TREF.

That concerns cases where:

// -----------------------------------------------------
// Example 2 (based on example 1): 
// - Using default "time" arguments 
// - Convertion to TREF - computation in TREF
// -----------------------------------------------------
// cjd: Date/time, origin: 1 jan 1950 0h
// Time scale is supposed to be TAI (implicitly)      
cjd0_tai = CL_dat_cal2cjd(2018, 8, 1, 0, 0, 0);  

// Output dates (TAI time scale) 
sec_tai = (0 : 3600 : 4*3600); 
cjd_tai = cjd0_tai + sec_tai / 86400; 

// Important: all computations done in TREF  
// => convert to TREF first 
// Note: default values for ut1_tref and tt_tref are used
cjd_tref = CL_dat_scaleConvert("TAI", "TREF", cjd_tai); 

// All computations as in example 1

// Position of the Sun
pos_sun = CL_eph_sun(cjd_tref) 

// Sidereal time 
tsid = CL_mod_siderealTime(cjd_tref)

Example 3: Using TREF, UT1 matters

This third example is slightly more complex. It concerns cases where:

The only way to be sure not to make mistakes is to pass the correct value of ut1_tref to all functions that expect it. The value of the argument tt_tref is the default value.

Warning:

// -----------------------------------------------------
// Example 3 (based on example 2): 
// - Using default value for TT-TREF 
// - Convertion to TREF - computation in TREF
// - UT1 matters
// 
// This is an example of processing of IERS Bulletin B
// that gives precise Earth Orientation Parameters (EOP)
// Additional data and functions are used. See below. 
// -----------------------------------------------------

// ------------------------
// Process IERS data 
// Returns a structure that contains: 
// cjd/TREF, UT1-TREF(s) (1xN)
// using data_iers (struct) that contains: 
// cjd/UTC, UT1-UTC(s), ...  (1xN)
// ------------------------
function [data]=process_EOP(data_iers)
  // Conversion: UTC => TREF
  cjd_tref = CL_dat_scaleConvert("UTC", "TREF", data_iers.cjd_utc);
  
  // Compute UT1 - TREF = (UT1 - UTC) + (UTC - TREF)
  // Note: UTC - TREF is a whole number of seconds  
  // (=> rounded-off for more accuracy)    
  utc_tref = round((data_iers.cjd_utc - cjd_tref) * 86400); 
  ut1_tref = data_iers.ut1_utc + utc_tref; // sec
  
  // Check UT1-TREF is continuous (less than 0.1 sec variation / day)
  if (find(abs(diff(ut1_tref) ./ diff(cjd_tref)) > 0.1) <>  [])
    error("ut1_tref is not continuous"); 
  end
  
  data = struct("cjd_tref", cjd_tref, "ut1_tref", ut1_tref); 
endfunction 

// ------------------------
// Computes UT1-TREF from date in TREF
// ------------------------
function [ut1_tref]=get_UT1(cjd_tref, data) 
  // Interpolate (order 3)
  // Note: initial date subtracted => interpolation on durations 
  ut1_tref = CL_interpLagrange(data.cjd_tref-data.cjd_tref(1), ...
                data.ut1_tref, cjd_tref-data.cjd_tref(1), n=4); 
endfunction  

// ------------------------
// MAIN
// ------------------------
// Prepare data for the computation of ut1_tref 
// Load additional data and functions (in particular "load_EOP"). 
// data_iers: IERS data loaded from external files 
// => See directory "D" for more details
D = CL_path("EOP", fullfile(CL_home(), "data")); 
exec(fullfile(D, "iers_util.sce")); 
data_iers = load_EOP(D); 
data = process_EOP(data_iers); 

// Date is relative to TDB   
cjd0_tdb = CL_dat_cal2cjd(2016, 12, 31, 0, 0, 0);
cjd_tdb = cjd0_tdb + (0 : 0.5 : 2); 

// Computations done in TREF => convert to TREF first 
// Note: ut1_tref has no impact (argument not provided)     
cjd_tref = CL_dat_scaleConvert("TDB", "TREF", cjd_tdb); 

// All computations as in examples 1 and 2, except for the argument 
// "ut1_tref" that is explicitly passed to the functions (when needed) 

// Position of the Sun
pos_sun = CL_eph_sun(cjd_tref) 

// Sidereal time 
ut1_tref = get_UT1(cjd_tref, data); 
tsid = CL_mod_siderealTime(cjd_tref, ut1_tref=ut1_tref)

Example 4: Advanced uses

This last example is for users know what they do. Time arguments are given as necessary. All these advanced uses are dependent on not described features that may change in future versions, so be careful.

// -----------------------------------------------------
// Example 4: 
// - Using time arguments as needed  
// -----------------------------------------------------

// Example 4-a:
// We know that the time scale used in the computation is in actually TT.  
// So setting tt_tref to 0 redefines TREF as (locally) identical to TT 
cjd_tt = CL_dat_cal2cjd(2018, 8, 1, 0, 0, 0); // TT time scale
pos_sun = CL_eph_sun(cjd_tt, tt_tref=0) 

// Example 4-b:
// We know that the time scale that matters is UT1.  
// So setting ut1_tref to 0 redefines TREF as (locally) identical to UT1 
cjd_ut1 = CL_dat_cal2cjd(2018, 8, 1, 0, 0, 0); // UT1 time scale
tsid = CL_mod_siderealTime(cjd_ut1, ut1_tref=0) 

// Example 4-c: 
// We know that the time scale that matters is TT, but the date in known in TAI.  
// So setting tt_tref to TT-TAI redefines TREF as (locally) identical to TAI 
cjd_tai = CL_dat_cal2cjd(2018, 8, 1, 0, 0, 0); // TAI time scale
pos_sun = CL_eph_de405("Sun", cjd_tai, tt_tref=CL_dataGet("TT_TAI")) 
// Assuming UT1-TAI is known
ut1_tai = -37.1; 
tsid = CL_mod_siderealTime(cjd_tai, ut1_tref=ut1_tai)

Annex: How to be sure no time argument is missing

To be sure no "ut1_tref" or "tt_tref" argument has been forgotten in function calls, a simple way is to define %CL_UT1_TREF and/or %CL_TT_TREF and set them to %nan. As these values are used if no argument is given, the results will be impacted which will enable the error (missing argument) to be detected.

Notes:

// -----------------------------------------------------
// Annex: 
// - Be sure no arguments are forgotten   
// -----------------------------------------------------
cjd_tref = CL_dat_cal2cjd(2018, 8, 1, 0, 0, 0); 

// Get (default) values in user variables  
TT_TREF = CL_dataGet("TT_TREF"); 
UT1_TREF = CL_dataGet("UT1_TREF"); 

// Redefine "global" variables  
%CL_TT_TREF = %nan; 
%CL_UT1_TREF = %nan; 

// Argument tt_tref is missing => result is Nan 
pos_sun = CL_eph_sun(cjd_tref) 
// Argument tt_tref is misspelled => result is Nan 
pos_sun = CL_eph_sun(cjd_tref, TT_TREF=TT_TREF) 
// Argument tt_tref is present => OK 
pos_sun = CL_eph_sun(cjd_tref, tt_tref=TT_TREF) 

// Argument ut1_tref is missing => result is Nan 
tsid = CL_mod_siderealTime(cjd_tref) 
// Argument ut1_tref is present => OK 
tsid = CL_mod_siderealTime(cjd_tref, ut1_tref=UT1_TREF) 

// Restore "global" variables  
%CL_TT_TREF = TT_TREF; 
%CL_UT1_TREF = UT1_TREF;


Report an issue
<< Basic orbit determination Cookbook B-plane >>