[comp.sys.handhelds] Clock Adjustment/Timekeeping Routines

edp@jareth.enet.dec.com (Eric Postpischil (Always mount a scratch monkey.)) (09/05/90)

Due to continued interest, I am reposting the clock adjustment routines I
wrote.  Since I now have the cable, this message includes the actual routines
uploaded from the 48 as a single directory object, rather than the individual
typed-in routines.


Here's a set of timekeeping routines that compensate for inaccuracy in the
48's clock.  (They could also be used to keep sidereal time.)

In a list called CLKDAT (clock data), there are, in order:

	o The value of TICKS at the start of some reference period.

	o The cumulative number of ticks added to the clock since then.

	o The difference between true time and clock time as a fraction
	  of the number of elapsed clock ticks (called "accuracy factor").

The following routines are provided:

KICK	Called with no arguments to update the clock by the correct
	number of ticks.

ADJT	Called with a unit object specifying an amount of time by which
	to change the time zone or system.  E.g., call ADJT with -1_h
	on the stack to change from Daylight Savings to Standard Time.

ADJC	Called to adjust the clock by a specified number of ticks.  Once
	the clock is set exactly with ADJC, EXACT should be called or the
	data will be lost when KICK is next called.

EXACT	Uses the cumulative number of ticks added to the clock, by KICK
	and by ADJC, to compute a new accuracy factor.

SCHEDULE
	Schedules an alarm at 4 a.m. to call KICK daily.

CANCEL
	Cancels the alarm.

RESET	Set a new reference time and discard cumulative ticks added.
	Keep the old accuracy factor until a new one is computed.

To use these timekeeping routines:

	o Set the time with the built-in functions.
	o Make the clock as exact as desired with CLKADJ.
	o Execute RESET (no arguments).
	o After using RESET, do not use the built-in time adjustment commands.
	o Let time pass.
	o Make the clock as exact as desired with ADJC.  (ADJC takes as an
	  argument a number of ticks, just as CLKADJ does.)
	o Execute EXACT.
	o Execute SCHEDULE.

Every night at 4 a.m., KICK will update the clock.

After the initial setting, you can update the accuracy factor by again
adjusting the clock with ADJC and executing EXACT.  If your calculator's
environment changes, you can establish a new reference time by making the clock
exact and executing RESET.  This keeps the current accuracy factor but resets
the reference time, so the next execution of EXACT will base the new accuracy
factor on the time elapsed since RESET.

To change time zones or systems, use ADJT.

o If you call ADJC by accident, call KICK to correct it.
o If you call ADJT by accident, call it again with the negation of the
argument.
o If you call EXACT by accident, you are out of luck unless you have another
  copy of your accuracy factor.  If so, replace it in CLKDAT.

KICK makes the clock correct by computing the number of ticks to add to the
clock to make it correct now:

    (accuracy factor)*(TICKS - reference time - added ticks) - added ticks

Because this calculation is used rather than simply adding a fixed number
of ticks at regular intervals, KICK can be called at any time, at frequent
or infrequent intervals.  Updating the clock daily keeps it close to the
correct time.  You can change the time of the alarm and the repeat interval
in the SCHEDULE routine.  (Be sure not to set a time that is skipped over.
E.g., if you set the KICK alarm for 3 a.m. and also have an alarm that adds
1 hour to the clock at 2 a.m. for the change to Daylight Savings Time, then
the clock will go from slightly after 2 a.m. to slightly after 3 a.m., and
the KICK alarm will not be executed or scheduled for the next day.)

SCHEDULE uses AO (alarm object) as the object for the alarm to execute.  This
object discards its argument, gets the current path, executes an object called
JOB, restores the current path, and turns the calculator off.  If an error
occurs, the error message is left in the stack before shutting the calculator
off.  If a variable called JOB does not exist when SCHEDULE is executed, it is
created as a list specifying the current directory and the KICK program.  If
you would like to add additional routines to be executed, add them to the list
in JOB.  JOB is created in the home directory but can be moved to any port.  If
you need to make more complicated changes, like removing the call to OFF,
execute CANCEL, change the AO variable, and call SCHEDULE.

KICK can be modified to adjust the clock so that it will be correct at some
time in the future.  E.g., you could arrange it so that KICK runs at 4 a.m.
and adds the number of ticks needed to make the clock correct at 4 p.m.  By
doing this, the clock is early part of the day and late part of the day,
instead of only early or only late.  This cuts in half the frequency with
which KICK must be executed to keep the clock within a specified distance
of the correct time.

To make this modification to KICK, add the number of ticks to the number
in stack level one just after the call to PA.  For example, to adjust KICK
to prepare the clock to be correct in 12 hours, put

	353894400 +

after the call to PA in KICK.

Here is the directory object.  I call it TIMEKEEP, but you can use any name
you wish without needing any changes.  The SCHEDULE routine assumes the
current path (when SCHEDULE is executed) is the path to the directory.

%%HP: T(2)A(R)F(.);
DIR
  KICK
    + PA 3 CDG * 0
RND SWAP - ADJC
STOF
    ;
  ADJT
    + '1_s' CONVERT
UVAL 8192 * DUP 1
CDG + 1 CDP CLKADJ
    ;
  ADJC
    + DUP 2 CDG + 2
CDP CLKADJ
    ;
  EXACT
    + PA / 3 CDP
STOF
    ;
  SCHEDULE
    + CANCEL PATH
HOME
      IF :&: JOB
VTYPE 0 <
      THEN DUP
'KICK' + :&: JOB
STO
      END EVAL DATE
1 DATE+ 4 'AO' RCL
707788800 4 \->LIST
STOALARM DROP
    ;
  CANCEL
    + RCLF -55 SF
      IFERR 1 \-> I
        +
          WHILE I
RCLALARM
            IF 3
GET 'AO' RCL SAME
            THEN I
DELALARM 1
            ELSE
'I' INCR
            END
          REPEAT
          END
        ;
      THEN
      END STOF
    ;
  CLKDAT {
# 1D3F08CCBB5D8h
420806
8.84386616084E-6 }
  RESET
    + TICKS 1 CDP 0
2 CDP
    ;
  AO
    +
      IFERR DROP
PATH :&: JOB RCL
EVAL EVAL
      THEN ERRM
      END OFF
    ;
  PA
    + RCLF 64 STWS
2 CDG TICKS 1 CDG -
OVER - B\->R
    ;
  CDG
    + 'CLKDAT' SWAP
GET
    ;
  CDP
    + \-> V L
      + 'CLKDAT' L
V PUT
      ;
    ;
END


				-- edp