-- Copyright (C) 1983 by Xerox Corporation. All rights reserved. -- CornPopper.mesa, HGM, 26-Nov-83 1:23:02 DIRECTORY Ascii USING [ControlA, CR, LF, SP], Process USING [Pause, SecondsToTicks], Stream USING [Delete, GetChar, Handle, PutChar, PutString, SendNow, TimeOut], String USING [AppendString, AppendChar], System USING [ AdjustGreenwichMeanTime, GetClockPulses, GetGreenwichMeanTime, gmtEpoch, GreenwichMeanTime, Pulses, PulsesToMicroseconds], Time USING [Pack, Unpack, Unpacked], PopCorn USING [ErrorType], PupStream USING [PupByteStreamCreate, SecondsToTocks, StreamClosing], PupTypes USING [PupAddress]; CornPopper: PROGRAM IMPORTS Process, Stream, String, System, Time, PupStream EXPORTS PopCorn = BEGIN Error: PUBLIC ERROR [why: PopCorn.ErrorType, text: LONG STRING] = CODE; GetClockOffset: PUBLIC PROCEDURE [where: PupTypes.PupAddress, tries: CARDINAL] RETURNS [msDiff: LONG INTEGER, msDelay: LONG CARDINAL] = BEGIN sh: Stream.Handle ¬ NIL; IF System.GetGreenwichMeanTime[] = System.gmtEpoch THEN ERROR Error[spare1, "Clock not set"L]; BEGIN ENABLE BEGIN PupStream.StreamClosing => BEGIN SELECT why FROM transmissionTimeout => ERROR Error[timeout, "Timeout during transmission"L]; remoteReject => ERROR Error[rejecting, text]; ENDCASE => ERROR Error[pupConfusion, text]; END; Stream.TimeOut => ERROR Error[timeout, "Stream Timeout"L]; UNWIND => IF sh # NIL THEN Stream.Delete[sh]; END; sh ¬ PupStream.PupByteStreamCreate[where, PupStream.SecondsToTocks[5]]; IF sh # NIL THEN BEGIN hits: CARDINAL ¬ 0; err: PopCorn.ErrorType; string: STRING = [200]; Stream.PutString[sh, "B2400"L]; -- DLS: Set Baud Rate Stream.PutChar[sh, Ascii.CR]; Stream.PutChar[sh, 'C]; -- DLS: Connect Stream.PutChar[sh, 'R]; -- Box: Reset everything (send every second) Stream.SendNow[sh]; Process.Pause[Process.SecondsToTicks[2]]; Stream.PutChar[sh, 'T]; -- Box: Send current time (once) Stream.SendNow[sh]; [] ¬ FlushThePipe[sh]; -- Skip over DLS header and partial lines Stream.PutString[sh, "F000:00:00:00.000Q"L]; -- Box: Set Format msDelay ¬ LAST[LONG CARDINAL]; FOR i: CARDINAL IN [0..20) UNTIL hits = tries DO thisDelta, thisDelay: LONG CARDINAL; [thisDelta, thisDelay] ¬ PokeBox[sh ! Error => BEGIN err ¬ why; string.length ¬ 0; String.AppendString[string, text]; LOOP; END ]; hits ¬ hits + 1; IF thisDelay < msDelay THEN BEGIN msDelay ¬ thisDelay; msDiff ¬ thisDelta; END; ENDLOOP; IF hits = 0 THEN ERROR Error[err, string]; Stream.PutChar[sh, 'R]; -- Box: Reset everything Stream.SendNow[sh]; Stream.Delete[sh]; sh ¬ NIL; END; END; END; FlushThePipe: PROCEDURE [sh: Stream.Handle] RETURNS [hit: BOOLEAN] = BEGIN n: CARDINAL ¬ 0; DO c: CHARACTER; c ¬ Stream.GetChar[sh ! Stream.TimeOut => EXIT]; n ¬ n + 1; IF n > 1000 THEN ERROR Error [cantParse, "Box stuck talking"L]; ENDLOOP; RETURN[n > 0]; END; PokeBox: PROCEDURE [sh: Stream.Handle] RETURNS [msDiff: LONG INTEGER, msDelay: LONG CARDINAL] = BEGIN start, stop: System.Pulses; local, remote, first: System.GreenwichMeanTime; diff: LONG INTEGER; string: STRING = [40]; days, hours, minutes, seconds, miliseconds: CARDINAL; slop: LONG CARDINAL; IF FlushThePipe[sh] THEN ERROR Error[cantParse, "Unexpected characters in pipeline"L]; start ¬ System.GetClockPulses[]; first ¬ System.GetGreenwichMeanTime[]; DO -- Wait until clock ticks temp: System.Pulses ¬ System.GetClockPulses[]; local ¬ System.GetGreenwichMeanTime[]; stop ¬ System.GetClockPulses[]; IF first # local THEN EXIT; start ¬ temp; ENDLOOP; slop ¬ System.PulsesToMicroseconds[[stop-start]]/1000 + 1; -- This sometimes hangs in WaitToSend. Maybe probeCounter is confusing things Stream.PutChar[sh, 'T]; -- Box: Send current time (once more) Stream.SendNow[sh]; DO c: CHARACTER ¬ Stream.GetChar[sh ! Stream.TimeOut => ERROR Error[cantParse, "Timeout while collecting answer"L]]; IF c = Ascii.CR THEN LOOP; IF c = Ascii.LF THEN EXIT; IF string.length = string.maxlength THEN ERROR Error[cantParse, "Too many characters"L]; String.AppendChar[string, c]; ENDLOOP; stop ¬ System.GetClockPulses[]; msDelay ¬ System.PulsesToMicroseconds[[stop-start]]/1000 + 1 + slop; -- Format should be ­Addd:hh:mm:ss.sssx (x might be ?) IF string.length < 18 THEN ERROR Error[cantParse, "18 characters expected"L]; IF string[0] # Ascii.ControlA THEN ERROR Error[cantParse, "­A expected"L]; IF string[17] # Ascii.SP THEN BEGIN temp: STRING = [50]; String.AppendString[temp, "Not Locked ("L]; String.AppendString[temp, string]; SELECT string[17] FROM '? => String.AppendString[temp, " => +/- 500ms"L]; '# => String.AppendString[temp, " => +/- 50ms"L]; '* => String.AppendString[temp, " => +/- 5ms"L]; '. => String.AppendString[temp, " => +/- 1ms"L]; ENDCASE => String.AppendString[temp, " = unknown quality indication"L]; String.AppendChar[temp, ')]; ERROR Error[notLocked, temp]; END; days ¬ ExtractDecimal[string, 1, 3]; hours ¬ ExtractDecimal[string, 5, 2]; minutes ¬ ExtractDecimal[string, 8, 2]; seconds ¬ ExtractDecimal[string, 11, 2]; miliseconds ¬ ExtractDecimal[string, 14, 3]; remote ¬ [ StartOfThisYear[local, days] + (days -1) * 86400 + -- Box thinks days are like Fortran hours * LONG[3600] + minutes * 60 + seconds ]; diff ¬ remote - local; IF ABS[diff] > 2*3600 THEN ERROR Error[localClockWayOff, "Local clock way off"L]; msDiff ¬ diff * 1000 + miliseconds; BEGIN -- Correct for transmission time of RS232 characters msPerCharacter: CARDINAL = 4; -- 2400 baud msDiff ¬ msDiff - msPerCharacter; -- Box strobes time at end of "T" msDelay ¬ msDelay - (1 + 20) * msPerCharacter; END; END; ExtractDecimal: PROCEDURE [s: STRING, offset, digits: CARDINAL] RETURNS [n: CARDINAL] = BEGIN n ¬ 0; FOR i: CARDINAL IN [offset..offset + digits) DO c: CHARACTER = s[i]; SELECT c FROM IN ['0..'9] => n ¬ n*10 + c-'0; ENDCASE => ERROR Error[cantParse, "Decimal digit expected"L]; ENDLOOP; END; StartOfThisYear: PROCEDURE [ local: System.GreenwichMeanTime, days: CARDINAL] RETURNS [System.GreenwichMeanTime] = BEGIN temp: Time.Unpacked; fudge: LONG INTEGER = 90*86400; SELECT days FROM IN [0..90] => local ¬ System.AdjustGreenwichMeanTime[local, fudge]; IN [365-90..366] => local ¬ System.AdjustGreenwichMeanTime[local, -fudge]; ENDCASE; temp ¬ Time.Unpack[local]; temp.month ¬ 0; temp.day ¬ 1; temp.hour ¬ 0; temp.minute ¬ 0; temp.second ¬ 0; temp.dst ¬ FALSE; temp.zone ¬ [west, 0, 0, 366, 366]; RETURN[Time.Pack[temp, FALSE]]; END; END.