Phil Ehrens with a little neatening up by Donal Fellows
Usage
set gpstime [ gpsTime "04/20/99 01:20:59" ] or set gpstime [ gpsTime 91232121 ] or set gpstime [ gpsTime 01/20/01 ] or set gpstime [ gpsTime 01/20/2001 ]
(of course, you can't calculate GPS time in the future unless you know when the leap seconds are...)
or set gpstime [ gpsTime now ]
proc gpsInit {} { uplevel { ;## the difference between the UNIX epoch and GPS epoch. set epochdiff 315964819 ;## 1972-01-01 00:00:00 UTC was 1972-01-01 00:00:10 TAI. set offset 10 ;## WARNING! KEEP THE NEXT VARIABLE IN SYNC OR ELSE! set nowOffset 23 ;# must be [llength [gpsLeapSecondTimes]] } } # This is factored out to make for easy upgrading to handle updates. proc gpsLeapSecondTimes {} { ;## assumes 00:00:00. foreach date { "June 30, 1972" "December 31, 1972" "December 31, 1973" "December 31, 1974" "December 31, 1975" "December 31, 1976" "December 31, 1977" "December 31, 1978" "December 31, 1979" "June 30, 1981" "June 30, 1982" "June 30, 1983" "June 30, 1985" "December 31, 1987" "December 31, 1989" "December 31, 1990" "June 30, 1992" "June 30, 1993" "June 30, 1994" "December 31, 1995" "June 30, 1997" "January 1, 1999" "January 1, 2006" } { lappend times [clock scan $date -gmt 1] } ;## Ensure that we're sorted by number (i.e. by date) return [lsort -integer $times] } proc gpsTime { { time "" } } { gpsInit ;## quick short-circuit for "now". if { [ regexp {^$|now} $time ] } { set time [ clock seconds ] set fudge [ expr {$offset + $nowOffset} ] return [ expr {$time - $epochdiff + $fudge} ] } ;## canonicalise input to UNIX epoch seconds if { ! [ regexp {^-?[0-9]+$} $time ] } { if { [ catch { set time [ clock scan $time -gmt 1 ] } err ] } then { return -code error $err } } set index 0 foreach sec [gpsLeapSecondTimes] { if {$time <= $sec} {break} incr index } set offset [expr {$offset + $index}] set gpstime [expr {$time - $epochdiff + $offset}] return $gpstime }
Usage
set utctime [ utcTime gpsTime ] '''Comments''': proc utcTime { { time "" } } { Here is a proc for getting leap second info from a standard source.
It contains the useful if { $force_update } { unset ::leapdates }
if { [ info exists ::leapdates ] } { return $::leapdates } set months { JAN 1 FEB 2 MAR 3 APR 4 MAY 5 JUN 6 \ JUL 7 AUG 8 SEP 9 OCT 10 NOV 11 DEC 12 } if { [ catch { FTP::new leap leap::Open maia.usno.navy.mil anonymous [email protected] leap::Get ser7/tai-utc.dat leapseconds leap::Close } err ] } { set msg "error encountered while trying to update " append msg "leap seconds list: $err (falling back to " append msg "old list)." return -code errif {![regexp {^-?[0-9]+$} $time]} { return -code error "utcTime requires integer argument" } gpsInit set index 0 foreach sec [gpsLeapSecondTimes] { if { $time <= $sec } {break} incr index } set offset [expr {$offset + $index}] set utctime [expr {$time + $epochdiff - $offset}] return $utctime } ----if { [ catch { set fid [ open leapseconds r ] set data [ read $fid ] close $fid } err ] } { return -code error $err } or $msg } foreach { mo ord } $months { regsub -all $mo $data $ord data } foreach line [ split $data "\n" ] { foreach { y m d 1 ;## there are currently 36 set ::leapdates $leapdates lappend leapdates $utc [ expr { int($leap) } ] }2 3 leap 4 5 6 7 8 9 10 11 } $line { set utc [ clock scan $m/$d/$y -gmt 1 ] } }entries in the leap seconds table if { [ llength $leapdates ] < 72 } { return -code error "CANNOT CALCULATE LEAP SECONDS!!" }