[Git][NTPsec/ntpsec][master] 24 commits: refclock_trimble: invalid UTC offset doesn't mean invalid packet

Gary E. Miller gitlab at mg.gitlab.com
Sat Sep 16 05:08:34 UTC 2017


Gary E. Miller pushed to branch master at NTPsec / ntpsec


Commits:
53b64748 by Trevor N at 2017-09-16T01:40:46Z
refclock_trimble: invalid UTC offset doesn't mean invalid packet

- - - - -
38c37cc0 by Trevor N at 2017-09-16T01:40:46Z
refclock_trimble: fix classic bug 2659

The buffer length check was incorrect, so large packets were overwriting
the clock type stored in the control structure.

Expanded the buffer size to accommodate the largest TSIP packet listed
in the receiver manuals.

- - - - -
bfddacca by Trevor N at 2017-09-16T01:40:46Z
refclock_trimble: build Praecis SPSTAT message response in trimble_io

removing file-level state variable allows for multiple receivers to
be used simultaneously

- - - - -
6f113661 by Trevor N at 2017-09-16T01:40:46Z
refclock_trimble: use timecode packet's timestamp

When not using event capture, offset can vary by hundreds of ms
between ntpd startups because the poll timer's random relationship
to the top of the second causes the timestamp of a random packet to
be used, but only the first packet at the top of the second
contains the timecode (for the top of the second).

- - - - -
52cc8485 by Trevor N at 2017-09-16T01:44:09Z
refclock_trimble: check serial parity

TSIP does not contain checksums, but most receivers have parity
transmission enabled by default so check it when available.

- - - - -
2b50a20b by Trevor N at 2017-09-16T01:44:16Z
refclock_trimble: require event polling for non-Thunderbolt receivers

Remove the ability to disable event polling because packets 8f-ad and 8f-0b
are needed for the rollover workaround (#461) but the Palisade and Acutime
receivers transmit only packet 8f-ad if their event inputs aren't used. Running
with event polling disabled is normally done for troubleshooting purposes, but
it's also used for some Palisades with firmware that doesn't allow event
inputs. A no-cost firmware upgrade is available which enables event inputs
for those receivers.

- - - - -
564b187d by Trevor N at 2017-09-16T01:44:16Z
refclock_trimble: remove unused and unnecessary code

* remove partially-implemented Acutime Gold 'Port B' mode -- gpsd can be
 used when connected to port B.
* remove decoding of packets that have information also contained in the
 timing superpackets
* remove decoding of packets that are not helpful for troubleshooting

- - - - -
5b70a2ea by Trevor N at 2017-09-16T01:51:53Z
refclock_trimble: improved debugging messages

- - - - -
3bdde6fe by Trevor N at 2017-09-16T01:51:56Z
refclock_trimble: fix leap-scheduled detection, simplify polling

Testing of the leap detection logic with a Spirent GSS6100 GPS
signal generator revealed that the logic for Palisade and Acutime
receivers was incorrect. The leap-second-flag bits (packet 8f-ad
byte 19) are the same for both insertion and deletion, so assume
insertion.

To simplify polling: don't count packets, instead finish the poll
with the last packet in the sequence sent after an event or
auto-report.

- - - - -
21317d50 by Trevor N at 2017-09-16T01:51:56Z
refclock_trimble: add Thunderbolt holdover and stored position check

- - - - -
1ef5c918 by Trevor N at 2017-09-16T01:51:56Z
refclock_trimble: configurable event trigger method

feature request listed as bug #1765 in the reference
implementation

- - - - -
8b3559df by Trevor N at 2017-09-16T01:51:56Z
refclock_trimble: print a debug message when no packets were decoded

- - - - -
f4fea7fe by Trevor N at 2017-09-16T01:55:15Z
refclock_trimble: remove redundant debug messages

- - - - -
4ee142d4 by Trevor N at 2017-09-16T01:55:18Z
refclock_trimble: fix for classic bug 1075

The simplest solution of waiting a few microseconds between setting
and clearing RTS causes problems with the Acutime 2000 and Praecis.
The Acutime 2000 may become unresponsive if the pulse time is
between 5us and 600ms, and the Praecis will occasionally produce
event timestamps that are incorrect by a few us when the pulse width
is between ~5us and 100us.
Extending the RTS set time to the entire polling interval works for
all supported receivers, but a sleep for at least 12ms is needed
between clearing and re-setting RTS for the receivers to re-arm
their event inputs.

- - - - -
93ea72cb by Trevor N at 2017-09-16T01:55:18Z
refclock_trimble: improve 'no packets detected' message

fixed displaying when time transfer skipped
now works with Thunderbolt

- - - - -
82320a5f by Trevor N at 2017-09-16T01:57:35Z
refclock_trimble: set dispersion

- - - - -
a01dfba0 by Trevor N at 2017-09-16T01:57:38Z
refclock_trimble: use the median filter

- - - - -
64d5a2bb by Trevor N at 2017-09-16T01:59:29Z
refclock_trimble: add log message topic labels

- - - - -
85c8e246 by Trevor N at 2017-09-16T02:01:39Z
refclock_trimble: check tcesetattr and ioctl results at startup

- - - - -
28e0476f by Trevor N at 2017-09-16T02:01:41Z
refclock_trimble: pass 4-digit year through refclock_process()

tested for proper startup with host time set to 1970 and 2037

- - - - -
9513772c by Trevor N at 2017-09-16T02:01:41Z
refclock_trimble: update documentation

- - - - -
fd077e04 by Trevor N at 2017-09-16T02:01:41Z
refclock_trimble: time2 sets holdover duration limit

- - - - -
34b30741 by Trevor N at 2017-09-16T02:15:33Z
refclock_trimble: compat. with 'attempted fix of trimble logging'

- - - - -
2d5caf47 by Trevor N at 2017-09-16T04:38:03Z
refclock_trimble: fix 'attempted fix of trimble logging'

- - - - -


2 changed files:

- docs/driver_trimble.txt
- ntpd/refclock_trimble.c


Changes:

=====================================
docs/driver_trimble.txt
=====================================
--- a/docs/driver_trimble.txt
+++ b/docs/driver_trimble.txt
@@ -1,4 +1,4 @@
-= Trimble Palisade/Thunderbolt/Acutime Receivers =
+= Trimble Palisade/Thunderbolt/Acutime & Praecis Receivers =
 
 [width="100%",cols="<50%,<50%",frame="none",grid="none"]
 |==============================================================
@@ -11,245 +11,244 @@ Name: trimble
 Reference ID: GPS
 Serial Port: /dev/trimble__u__; 9600bps 8N1/8O1
 
-== Deprecation warning ==
-
-This refclock is deprecated and obsolete. The NTPsec maintainers plan
-to remove it in a future release.  If you have a requirement for it,
-please make this known to us.
-
-This driver reports only two-digit years, and may thus be subject to
-both lingering Y2K effects and issues near future century rollovers.
-
 == Description ==
 
-The *refclock trimble* driver supports
+The *refclock trimble* driver version 3.00 supports
 ftp://ftp.trimble.com/pub/sct/embedded/bin/Manuals/Old%20Manuals/PALISADE.PDF[Trimble
-Navigation's Palisade Smart Antenna GPS receiver], Thunderbolt, and Acutime.
-
-Additional software and information about the Trimble GPS models is
-available from:
-http://www.trimble.com/timing
-
-NTP driver source, executables and documentation was maintained
-at: ftp://ftp.trimble.com/pub/ntp , but seems no longer available.
-
-This documentation describes version 7.12 of the GPS Firmware and
-version 2.46 (July 15, 1999) and later, of the driver source.
-
-This documentation describes version 1 of the Thunderbolt Receiver
-Firmware, no tests have been made on further firmwares, please read
-"Notes on the Thunderbolt Receiver's Firmware" at the end of this
-documentation for more information.
-
-== Operating System Compatibility ==
-
-Note: There is no current support from Trimble for this
-product, and the links in the following table are the last known
-location of resources.
-
-The Trimble driver has been tested on the following software and
-hardware platforms:
-
-[width="100%",cols="<25%,<25%,<25%,<25%",frame="none",grid="none",options="header"]
-|=============================================================================
-| Platform        | Operating System | NTP Sources                  | Accuracy
-| i386 (PC)       | Linux            | NTP Distribution             |10 us
-| SUN             | Solaris 2.x      | NTP Distribution             |50 us
-| Hewlett-Packard | HPUX 9, 10, 11   | http://us-support.external.hp.com|50 us
-| Various         | Free BSD         | NTP Distribution             |20 us
-|=============================================================================
-
-*Attention*: Thunderbolt Receiver has not been tested on the previous
-software and hardware platforms.
+Navigation's Palisade Smart Antenna GPS receiver], Thunderbolt, Acutime 2000
+and Acutime Gold. The EndRun Technologies Praecis Cf, Ct, Ce, and II modules
+(in Palisade emulation mode) are also supported.
+
+== Tested Receivers ==
+The driver has been tested with the following receivers :
+
+[width="60%",cols="<50%,<50%",options="header"]
+|============================================
+| Model                    | Firmware Version
+| Palisade 26664-00        | 7.12
+| Praecis Cf 3001-0000-000 | 2.21
+| Thunderbolt  48051-61    | 3.00
+| Acutime 2000 39091-00    | 2.02
+| Acutime Gold 55238-00    | 1.12
+|============================================
+
+The driver has been tested with the receivers listed above on the
+following host computers:
+
+[width="50%",cols="<40%,<40%,<20%",options="header"]
+|===========================================
+| Processor        | Operating System | Note
+| i686 (PC)        | FreeBSD 11.1     | 1
+| amd64 (PC)       | Linux, Gentoo    | 2
+| powerpc64 (Apple)| Linux, Gentoo    | 3
+| sparc64 (Oracle) | Solaris 11.3     | 4
+|===========================================
+1. HP Pavilion a000, Athlon 64, SIIG CyberPro v5
+2. Supermicro X9SCL, E3-1220, onboard serial ports, kernel 4.12.11
+3. Power Mac G5(7,2), DP 1.8GHz, StarTech PCI1S550, kernel 4.9.34
+4. Sun SPARC Enterprise T5220, onboard serial port, gcc 4.8.2
+
+
+[[sym]]
+== Operating System Serial Port Configuration ==
 
-== GPS Receiver ==
-
-The Palisade GPS receiver is an 8-channel smart antenna, housing the GPS
-receiver, antenna and interface in a single unit, and is designed for
-rooftop deployment in static timing applications.
-
-Palisade generates a PPS synchronized to UTC within +/- 100 ns.  The
-Palisade's external event input with 40 nanosecond resolution is
-utilized by the Trimble NTP driver for asynchronous precision time
-transfer.
-
-No user initialization of the receiver is required. This driver is
-compatible with the following versions of Palisade:
-
-[width="50%",cols="<34%,<33%,<33%",align="center",frame="none",grid="none"]
-|======================================================
-| Version  | Event Input  |Trimble Part Number
-| 7.02     | No           |26664-00
-| 7.02E    | Yes          | 26664-10
-| 7.12     | Yes          | 38158-00
-|======================================================
-
-When using Palisade 26664-00, you must set the +flag2+ option
-to 1 in {ntpconf}. 
-
-See Configuration[[configuration]].
-
-== GPS Installation ==
-
-A location with unobstructed view of the horizon is recommended.
-Palisade is designed to be securely mounted atop standard 3/4 inch
-threaded pipe.
-
-The 12 conductor (dia. 10 mm)  power and I/O cable must be routed from
-the rooftop site to the NTP server and properly strain relieved.
-
-== GPS Connection ==
+The driver attempts to open the device /dev/trimble__u__ where 'u' is
+the NTP refclock xref:un[unit number], or the LSB of the refclock
+address when using the legacy syntax.
 
-The Palisade is equipped with dual (A & B) RS-422 serial interfaces and
-a differential TTL PPS output. An RS-232 / RS-422 Interface Module is
-supplied with the Palisade NTP Synchronization Kit. Palisade
-link:#PortA[port A] must be connected to the NTP host server. Maximum
-antenna cable length is 500 meters. See the link:#Pinouts[pinouts] table
-for detailed connection Information.
+The user must provide a symbolic link to an available serial port device. This
+is typically performed by a command such as:
 
-Palisade's link:#PortB[port B] provides a TSIP (Trimble Standard
-Interface Protocol) interface for diagnostics, configuration, and
-monitoring. Port B and the PPS output are not currently used by the
-Palisade NTP reference clock driver.
+[width="50%",cols="<20%,<80%",options="header"]
+|=========================================
+|OS      | Command
+|FreeBSD | ln -s /dev/ttyu0 /dev/trimble0
+|Linux   | ln -s /dev/ttyS0 /dev/trimble0
+|Solaris | ln -s /dev/term/a /dev/trimble0
+|=========================================
 
-== O/S Serial Port Configuration ==
+All receivers except the Thunderbolt have a factory default serial port
+configuration of 8O1 (odd parity). The Thunderbolt defaults to 8N1 (no parity).
+The driver automatically sets the baud rate and parity of the host to
+match the receiver's factory default values.
 
-The driver attempts to open the device /dev/trimble__u__ where 'u' is
-the NTP refclock unit number.  Valid refclock unit numbers are 0 - 3.
+[[cfg]]
+== NTP Configuration Examples ==
 
-The user is expected to provide a symbolic link to an available serial
-port device.  This is typically performed by a command such as:
+NTP configuration file "{ntpconf}"
 
+Acutime 2000 or Acutime Gold:
 ---------------------------------
-ln -s /dev/ttyS0 /dev/trimble0
+refclock trimble unit 0 subtype 3
 ---------------------------------
 
-The Palisade ships 8N1 (no parity).  The Thunderbolt ships 8O1 (odd
-parity).
-
-[[Configuration]]
-== NTP Configuration ==
-
-Trimble NTP configuration file "{ntpconf}" with event polling:
-
-["literal",subs="normal"]
-#------------------------------------------------------------------------------
-# The Primary reference
-refclock trimble unit 0
-peer terrapin.csc.ncsu.edu # internet server
-# Drift file for expedient re-synchronization after downtime or reboot.
-driftfile /etc/ntp.drift
-#------------------------------------------------------------------------------
-
-Configuration without event polling:
-
-["literal",subs="normal"]
-#------------------------------------------------------------------------------
-# The Primary reference.
-# Set packet delay and set flag2 to turn off event polling.
-refclock trimble unit 0 link:#time1[time1 0.020] link:#flag2[flag2 1]
-#------------------------------------------------------------------------------
-
-== Thunderbolt NTP Configuration file ==
-
-Configuration without event polling:
-
-["literal",subs="normal"]
-#------------------------------------------------------------------------------
-# The Primary reference
-refclock trimble unit 0 mode 2 link:#time1[time1 0.020] link:#flag2[flag2 1]
-#------------------------------------------------------------------------------
-
-Currently the Thunderbolt mode doesn't support event polling, the
-reasons are explained on the "Notes on the Thunderbolt Receiver's
-Firmware" section at the end of this documentation.
-
-== Time Transfer and Polling ==
-
-Time transfer to the NTP host is performed via the Palisade's
-comprehensive time packet output. The time packets are output once per
-second, and whenever an event timestamp is requested.
-
-The driver requests an event time stamp at the end of each polling
-interval, by pulsing the RTS (request to send) line on the serial port.
-The Palisade GPS responds with a time stamped event packet.
-
-Time stamps are reported by the Palisade with respect to UTC time. The
-GPS receiver must download UTC offset information from GPS satellites.
-After an initial UTC download, the receiver will always start with
-correct UTC offset information.
-
-== Run NTP in Debugging Mode ==
-
-The following procedure is recommended for installing and testing a
-Palisade NTP driver:
-
-1.  Perform initial checkout procedures. Place the GPS receiver
-outdoors; with clear view of the sky. Allow the receiver to obtain an
-UTC almanac.
-2.  Verify presence of timing packets by observing the 1 Hz (PPS) led on
-the interface module. It should flash once per second.
-3.  Connect Palisade's port A to the NTP host.
-4.  Configure NTP and the serial I/O port on the host system.
-5.  Initially use the link:#flag2[+flag2+] option in
-*link:#Configuration[{ntpconf}],* to disable event polling (see
-configuration).
-6.  Run NTP in debug mode (-d -d), to observe Palisade_receive events.
-7.  The driver reports the link:#TrackingStatus[tracking status of the
-receiver]. Make sure it is tracking several satellites.
-8.  Remove the +flag2+ option and restart the daemon in debug mode to observe
-trimble_receive events.
-9.  If event polling fails, verify the link:#Pinouts[connections] and
-that the host hardware supports RTS control.
-
+Thunderbolt:
+---------------------------------------------
+refclock trimble unit 0 subtype 2 time1 0.020
+---------------------------------------------
+
+
+== Initial Setup and Testing for Palisade / Acutime Receivers ==
+1.  Read the xref:Pal[Palisade / Acutime] notes.
+2.  Place the GPS receiver outdoors, with a clear view of the sky for best 
+    results -- these receivers do not work well indoors.
+3.  Power up and allow the receiver to obtain UTC offset data. This can take
+    13 to 30 minutes with outdoor placement, or up to a few hours indoors.
+4.  Optionally wait for the receiver to pulse its PPS output. The 'PPS' LED
+    will blink if using an interface module. This indicates that the receiver
+	has entered a state that is usable by the driver.
+..  If the PPS is not produced after a few hours: The antenna placement may be
+    too poor, signal level mask values are too high, or PPS was disabled in the
+    receiver. Relocate to a better position, or reset the receiver's
+	configuration to factory defaults.
+5.  Connect the host to 'Port A'.
+6.  Configure the serial I/O port and its xref:sym[symbolic link] on the host.
+7.  Add the refclock to your +ntpd+ xref:cfg[configuration file].
+8.  Run +ntpd+ with debug level 2 without detaching from the terminal 
+    (-d -d -n). Note: debug level 1 may also be used; only errors will be
+	printed to stdout.
+9.  Check the ntpd xref:log[event log] or stdout for a line similar to 
+'TRIMBLE(0) open at /dev/trimble0' to verify that your serial port opened.
+10.  The driver will print +TSIP_decode+ lines to stdout as it processes
+    message packets from the receiver. Note: ntpd must be built with debugging
+	enabled to see +TSIP_decode+ +trimble_poll+ and +trimble_receive+ messages
+..  Check your serial connection if 'trimble_poll: unit __u__: no packets found'
+    appears.
+..  If no +TSIP_decode+ lines are seen within 60s the event triggering method
+    may be incorrect, check your xref:f3[+flag3+] setting. It's also possible
+    that the receiver is misconfigured: try resetting it to factory defaults.
+... The driver will print the message
+    'trimble_poll: unit __u__: packets found but none were usable' to stdout if
+    xref:f3[+flag3+] is incorrect, and also if the host is connected to Port B.
+11. The driver will print a +trimble_poll+ line with a timecode to stdout when
+    time is successfully transferred.
+..  If TSIP_decode lines are seen but trimble_receive never appears: 
+... TSIP_decode lines with 'Sats: {list of numbers} : Tracking' display the
+    number of usable satellites after 'Tracking'. At least 4 must be tracking
+    if the receiver does not have a valid stored position, but only 1 is needed
+	if it does.
+... TSIP_decode lines with 'unusable tracking status' will appear if there are
+    insufficient usable satellites.
+
+
+== Initial Setup and Testing for Thunderbolt Receivers ==
+1.  Read the xref:TB[Thunderbolt] notes.
+2.  Place the GPS antenna outdoors, with a clear view of the sky for best 
+    results -- Thunderbolt is not very good at tracking weak signals.
+3.  Power up and allow the receiver to obtain UTC offset data. This can take
+    13 to 30 minutes with outdoor placement, or up to a few hours indoors.
+4.  Configure the serial I/O port and its xref:sym[symbolic link] on the host.
+5.  Add the refclock to your +ntpd+ xref:cfg[configuration file].
+6.  Run +ntpd+ with debug level 2 without detaching from the terminal 
+    (-d -d -n). Note: debug level 1 may also be used; only errors will be
+	printed to stdout.
+7.  Check the ntpd xref:log[event log] or stdout for a line similar to 
+'Trimble(0) fd: 4 dev: /dev/trimble0' to verify that your serial port opened.
+8.  The driver will print +TSIP_decode+ lines to stdout as it processes
+    message packets from the receiver. Note: ntpd must be built with debugging
+	enabled to see +TSIP_decode+ +trimble_poll+ and +trimble_receive+ messages
+..  Check your serial connection if 'trimble_poll: unit __u__: no packets found'
+    appears.
+... The driver may print the message
+    'trimble_poll: unit __u__: packets found but none were usable' to stdout if
+    it failed to reconfigure the receiver to transmit auto-report superpackets.
+9. The driver will print a +trimble_poll+ line with a timecode to stdout when
+    time is successfully transferred.
+..  If TSIP_decode lines are seen but trimble_receive never appears:
+... TSIP_decode lines with 'not in holdover...unusable' will appear if there
+    are insufficient usable satellites and the receiver is not in holdover.
+... TSIP_decode lines with 'misconfigured' will appear if the driver failed
+    to reconfigure the receiver at startup.
+
+
+== Initial Setup and Testing for Praecis Receivers ==
+1.  Read the xref:Pra[Praecis] notes.
+2.  Power up and allow the receiver to lock with a cell tower. This can take
+    a few minutes if the unit has been powered on recently and has good
+	reception, but may take much longer with poor reception or if the unit has
+	been powered off for many months.
+3.  Configure the serial I/O port and its xref:sym[symbolic link] on the host.
+4.  Add the refclock to your +ntpd+ xref:cfg[configuration file].
+5.  Run +ntpd+ with debug level 2 without detaching from the terminal 
+    (-d -d -n). Note: debug level 1 may also be used; only errors will be
+	printed to stdout.
+6.  Check the ntpd xref:log[event log] or stdout for a line similar to 
+'Trimble(0) fd: 4 dev: /dev/trimble0' to verify that your serial port opened.
+7.  The driver will print +TSIP_decode+ lines to stdout as it processes
+    message packets from the receiver. Note: ntpd must be built with debugging
+	enabled to see +TSIP_decode+ +trimble_poll+ and +trimble_receive+ messages
+..  Check your serial connection if 'trimble_poll: unit __u__: no packets found'
+    appears, and ensure that xref:f3[+flag3+] is not set.
+8. The driver will print a +trimble_poll+ line with a timecode to stdout when
+   time is successfully transferred.
+..  If the time data has random large offsets the receiver's CTIME is probably
+    set ON. See the Praecis manual for the command to change this setting.
+
+
+[[log]]
 == Event Logging ==
 
 System and Event log entries are generated by NTP to report significant
 system events. Administrators should monitor the system log to observe
-NTP error messages. Log entries generated by the Palisade NTP reference
-clock driver will be of the form:
+NTP error messages. Log entries generated by the driver will be of the form:
 
----------------------------------------------------------
-Nov 14 16:16:21 terrapin ntpd[1127]: Palisade #0: message
----------------------------------------------------------
+-------------------------------------------------------
+Nov 14 16:16:21 terrapin ntpd[1127]: Trimble(0) message
+-------------------------------------------------------
 
 == Driver Options ==
-[[time1]]link:#Configuration[+time1+ 'time]'::
-   Specifies the time offset calibration factor, in seconds and fraction,
-   with default 0.0. If event capture is not used, time1 should be set to
-   20 milliseconds to correct serial line and operating system delays
-   incurred in capturing time stamps from the synchronous packets.
+[[un]]+unit+ 'number'::
+  Specifies the receiver number, with default 0. Used as an identifier so that
+  the driver can control multiple receivers.
 
-+stratum number+::
-   Specifies the driver stratum, in decimal from 0 to 15, with default 0.
+[[t1]]+time1+ 'time'::
+  Specifies the time offset calibration factor, in seconds and fraction,
+  with default 0.0. 'time1' should be set to about 20 milliseconds when using a
+  Thunderbolt.
 
-link:#refid[+refid string+]::
-   Specifies the driver reference identifier, *GPS*.
+[[t2]]+time2+ 'time'::
+  Specifies the holdover duration for Thunderbolt, in seconds, with 
+  default 0 (holdover disabled).
+
++stratum+ 'number'::
+  Specifies the driver stratum, in decimal from 0 to 15, with default 0.
+
++refid+ 'string'::
+  Specifies the driver reference identifier, an ASCII string from one to
+  four characters, with default +GPS+. When using a Praecis this should be
+  set to +CDMA+.
 
 +flag1 {0 | 1}+::
   Not used by this driver.
 
-[[flag2]]link:#Configuration[+flag2 {0 | 1}+]::
-   When set to 1, driver does not use hardware event capture. The
-   synchronous packet output by the receiver at the beginning of each
-   second is time stamped by the driver. If triggering the event pulse
-   fails, the driver falls back to this mode automatically.
++flag2 {0 | 1}+::
+  Not used by this driver. NOTE: Versions of the driver before 3.00 used flag2
+  to disable hardware event capture. Event capture is now required for all
+  receivers except the Thunderbolt.
 
-+flag3 {0 | 1}+::
-  Not used by this driver.
+[[f3]]+flag3 {0 | 1}+::
+  Specifies the method used for triggering the receiver's hardware event input.
+  The default of 0 uses the serial port RTS line. Set to 1 to use the serial 
+  port's TXD line instead of RTS. Value is ignored when using a Thunderbolt.
 
 +flag4 {0 | 1}+::
   Not used by this driver.
 
-+subtype+ 'number'::
-   The mode parameter to the server command specifies the specific hardware
-   this driver is for. The default is 0 for a normal Trimble Palisade. The
-   other options are *1* for an *Endrun Praecis* in Trimble emulation mode,
-   *2* for the *Trimble Thunderbolt* GPS Disciplined Clock Receiver,
-   *3* for the Trimble Acutime Gold, and *4* for the Acutime Gold port B.
+[[st]]+subtype+ 'number'::
+  Specifies the receiver model, default is 0:
+[width="60%",cols="^20%,<80%",options="header"]
+|================================
+| # | Model
+| 0 | Palisade
+| 1 | Praecis
+| 2 | Thunderbolt
+| 3 | Acutime 2000 & Acutime Gold
+|================================
+note: There is currently no difference between subtype 0 and subtype 3 other
+than the driver startup message.
 
 +mode+ 'number'::
-   Synonym for subtype, retained for backward compatibility.
+  Synonym for subtype, retained for backward compatibility.
 
 +path+ 'filename'::
   Overrides the default device path.
@@ -261,279 +260,184 @@ link:#refid[+refid string+]::
   Overrides the default baud rate.
 
 
-== Configuration Example ==
-
-----------------------------------------------------------------------------
-refclock trimble
-----------------------------------------------------------------------------
-
-== DEFINEs ==
-
-The following constants are defined in the driver source code. These
-defines may be modified to improve performance or adapt to new operating
-systems.
-
-[width="50%",cols="<34%,<33%,<33%",options="header"]
-|===============================================================================
-| *Label*    | Definition                                      |Default Value
-| DEVICE     | The serial port device to be used by the driver | /dev/trimble__u__
-| PRECISION  | Accuracy of time transfer                       | 1 microsecond
-|CURRENT_UTC | Valid GPS - UTC offset                          | 13
-|SPEED232    | Host RS-232 baud rate                           | B9600
-|TRMB_MINPOLL| Minimum polling interval                        | 5 (32 seconds)
-|TRMB_MAXPOLL| Maximum interval between polls                  |7 (128 seconds)
-|===============================================================================
-
-== Data Format ==
-
-Palisade port A can output two synchronous time packets. The NTP driver
-can use either packet for synchronization. Packets are formatted as
-follows:
-
-=== Packet 8F-AD (Primary NTP Packet) ===
-
-[width="50%",cols="<25%,<25%,<25%,<25%",frame="none",grid="none"]
-|========================================================================
-| Byte  | Item              |Type   | Meaning
-| 0     | Sub-Packet ID     |BYTE   | Subcode 0xAD
-| 1 - 2 | Event Count       |INTEGER| External event count recorded (0 = PPS)
-| 3 - 10| Fractional Second |DOUBLE | Time elapsed in current second (s)
-| 11    | Hour              |BYTE   | Hour (0 - 23)
-| 12    | Minute            |BYTE   | Minute (0 - 59)
-| 13    | Second            |BYTE   | Second (0 - 59; 60 = leap)
-| 14    | Day               |BYTE   | Date (1 - 31)
-| 15    | Month             |BYTE   | Month (1 - 12)
-|16 - 17| Year              |INTEGER| Year (4 digit)
-| 18    | Receiver Status   |BYTE   |Tracking Status
-|19     | UTC Flags         |BYTE   |Leap Second Flags
-|20     | Reserved          |BYTE   |Contains 0xFF
-|21     | Reserved          |BYTE   |Contains 0xFF
-|========================================================================
-
-.Leap Second Flag Definition:
-[verse]
-Bit 0: (1) UTC Time is available
-Bit 1 - 3: Undefined
-Bit 4: (1) Leap Scheduled: Leap second pending asserted by GPS control segment.
-Bit 5: (1) Leap Pending: set 24 hours before, until beginning of leap second.
-Bit 6: (1) GPS Leap Warning: 6 hours before until 6 hours after leap event
-Bit 7: (1) Leap In Progress. Only set during the leap second.
-
-.Tracking Status Flag Definitions:
-[width="50%",cols="<25%,<25%,<25%,<25%",frame="none",grid="none"]
-|========================================================================
-| Code  | Meaning                       | Accuracy   | Receiver Mode
-| 0     | Receiver is Navigating        | +/- 1 us   | Self Survey
-| 1     | Static 1 Sat. Timing Mode     | +/- 1 us   | 1-D Timing
-| 2     | Approximate Time              | 20 - 50 ms | Acquisition
-| 3     | Startup                       | N/A        | Initialization
-| 4     | Startup                       | N/A        | Initialization
-| 5     | Dilution of Position too High |5 ppm       | Self Survey
-| 6     | Static 1 Sat. Timing: Sat. not usable|5 ppm| 1-D Timing
-| 7     | No Satellites Usable          | N/A        | Self Survey
-| 8     | Only 1 Satellite Usable       | 20 - 50 ms | Self Survey
-| 9     | Only 2 Satellite Usable       |20 - 50 ms  | Self Survey
-| 10    | Only 3 Satellites Usable      |20 - 50 ms  | Self Survey
-| 11    | Invalid Solution              |N/A         |Error
-| 12    | Differential Corrections      |N/A         |N/A
-| 13    | Overdetermined Fixes          |+/- 100 ns  | Timing Steady State
-|========================================================================
-
-.*Packet 8F-0B (Comprehensive Timing Packet)*
-[width="50%",cols="<25%,<25%,<25%,<25%",frame="none",grid="none",style="verse"]
-|=========================================
-| Byte    | Item           | Type    | Meaning
-| 0       | Sub-Packet ID  | BYTE    | Subcode 0x0B
-| 1 - 2   | Event Count    | INTEGER | External event count recorded (0 = PPS)
-| 3 - 10  | UTC / GPS TOW  | DOUBLE  | UTC / GPS time of week (seconds)
-| 11      | Date           | BYTE    | Day of Month
-| 12      | Month          | BYTE    | Month of Event
-| 13 - 14 | Year           | INT     | Year of event
-| 15      | Receiver Mode  | BYTE    | Receiver operating dimensions:
-   0: Horizontal (2D)
-   1: Full Position (3D)
-   2: Single Satellite (0D)
-   3: Automatic (2D / 3D)
-   4: DGPS reference
-   5: Clock hold (2D)
-   6: Over determined Clock
-| 15 - 17 | UTC Offset           |INTEGER| UTC Offset value (seconds)
-| 18 - 25 | Oscillator Bias      |DOUBLE| Oscillator BIAS (meters)
-| 26 - 33 | Oscillator Drift Rate|DOUBLE|Oscillator Drift (meters / second)
-| 34 - 37 | Bias Uncertainty     |SINGLE|Oscillator bias uncertainty (meters)
-| 38 - 41 | Drift Uncertainty    |SINGLE|Oscillator bias rate uncertainty (m / sec)
-|42 - 49  | Latitude             |DOUBLE|Latitude in radians
-|50 - 57  | Longitude            |DOUBLE|Longitude in radians
-|58 - 65  |Altitude              |DOUBLE|Altitude above mean sea level, in meters
-|66 - 73  |Satellite ID          |BYTE  |SV Id No. of tracked satellites
-|=========================================
+[[Pal]]
+== Palisade / Acutime Notes ==
+
+The driver uses the receiver's 'external event' input and 'Port A' TSIP output
+packets for time transfer, so use the 'Port A' RS232 connector. Operation with
+'Port B' is not supported. The event input must be attached to the host serial
+port's RTS or TXD lines -- set xref:f3[+flag3+] accordingly. The host will
+pulse the event input which will cause the receiver to emit a timestamp packet.
+Jitter of less than 400ns has been observed with a Palisade on a machine with
+'-24 precision' using a 16550-compatible serial port and RTS for event
+triggering.
+
+The Palisade, Acutime 2000 and Acutime Gold are typically used with a
+'Synchronization Interface Module' which converts the receiver's RS422 I/O
+lines to RS232. Generic RS422 to RS232 adapters will also work. Current part
+numbers for the 12-pin antenna connector: DEUTSCH IMC26-2212X body,
+6862-201-22278 pins, and IMC2AD backshell. See the receiver manual for pinouts.
+If you opt to build your own interface with RTS triggering, ensure that
+positive voltage on the RS232 RTS line produces positive voltage on the Port A
+Receive+ line.
+
+The module supplied by Trimble for the Palisade and Acutime 2000, and the
+TrimTim modules available from http://www.navox.pl[Navimor Oxer] have two RS232
+ports which allow a host to communicate with the receiver's 'Port A' and
+'Port B'. With these modules the PPS is not connected to either RS232 port,
+but is made available on a BNC connector. The RS232 connector routed to 'Port A'
+connects the host's RTS line to the receiver's 'external event' input, so the
+host's TXD line is not connected. Since data can't be transmitted from the host
+to the receiver because of the unconnected TXD line, the driver expects the
+receiver to be set to its factory default configuration.
+
+If resetting the receiver to defaults is not desired, verify that 'time base'
+is set to UTC in 8e-4a. For the Palisade, verify that 8e-0b is 2 or 3 and 
+8e-ad is 2 or 3. For the Acutimes, verify that the 8e-a5 packet mask has at
+least 'Event 8f-0b on Port A' and 'Event 8f-ad on Port A' enabled.
+
+If it is not possible to use the external event input with your interface and
+you have an Acutime Gold or Acutime 2000, try using the
+link:driver_gpsd.html[+gpsd+] driver instead.
+
+The supported receivers will have 'GPS Week Number Rollover' problems after the
+following dates:
+[width="50%",cols="<50%,<50%",options="header"]
+|=============================
+| Model        | Rollover Date
+| Palisade     | 17-Nov-2018
+| Acutime 2000 | 5-Aug-2018
+| Acutime Gold | 1-Sep-2029
+|=============================
+
+A workaround for this has been implemented in the driver which relies on the
+ntpd program build date. Ensure that the build date reported during ntpd
+startup is less than 19 years from the current date.
+
+Palisade firmware versions previous to 7.12 are untested.
+The Palisade 26664-00 with pre-7.12 firmware must be upgraded to 7.12 because
+pre-7.12 firmware versions disable the receiver's external event input.
+Firmware version 7.12 for all Palisade receivers is available at:
+ftp://ftp.trimble.com/pub/sct/timing/
+
+The Acutime Gold Starter Kit contains a USB interface module. This driver has
+been tested using an RS232 interface module so the performance of the USB
+version is unknown. Navimor Oxer recommends that the USB port on their IF2
+module not be used for precision timing. This is likely also true for the
+Trimble interface. xref:f3[+Flag3+] may need to be set when using a USB
+interface.
+
+Refer to the manuals for hardware setup instructions, cable pinouts and packet
+formats. The manuals are available at:
+ftp://ftp.trimble.com/pub/sct/embedded/bin/Manuals/
+
+The Palisade can't automatically save its self-survey position. It must
+perform a self-survey every time power is lost unless its
+'accurate initial position' is manually set. Once set, the receiver will power
+up with this position and skip its self-survey. The driver does not wait for
+the self-survey to complete before allowing time transfer.
+
+Acutime 2000 transmits incorrect position data in its event-trigger response,
+so the TSIP_decode Latitude / Longitude / Altitude debugging message will not
+display correctly.
+
+The receiver must know the current UTC offset from GPS time to be usable with
+ntpd. The receiver automatically decodes the UTC offset data from the +Almanac+
+transmitted by GPS satellites. With good antenna placement, Almanac
+reception can be expected to take 13 minutes or more after receiver power-up.
+The driver will wait for the receiver to report that its UTC offset is valid
+before enabling time transfer. The receiver stores the UTC offset in NVRAM so
+it will become usable before the Almanac is available, but if it was powered
+off during a leap second change the stored offset may be incorrect until the
+current Almanac is obtained.
+
+
+[[Pra]]
+== EndRun Technologies Praecis Notes ==
+
+The driver has been tested with a Praecis Cf. The Ct, Ce, and Praecis II
+should also work. The receiver must be set to use Trimble emulation mode.
+To configure the receiver, see:
+https://net.its.hawaii.edu/network-performance/using-praecis/
+NTP setup instructions are also in the receiver manuals, which can be found at:
+https://www.endruntechnologies.com/documentation.htm
+
+Check the receiver's LEAP setting. Incorrect values will cause a time offset.
+See the note for this command in the receiver manual.
+
+Jitter of less than 100ns has been observed with a Praecis Cf on a machine with
+'-24 precision' using a 16550-compatible serial port and RTS for event
+triggering, but the offset relative to a Palisade on the same machine may vary
+in steps by several microseconds, likely due to the receiver switching between
+cell towers.
+
+IMPORTANT: Turn off the receiver's auto-report feature (CTIME=OFF) because the
+driver can't distinguish auto-reports from event capture responses. This is 
+mentioned in the receiver manual.
+
+
+[[TB]]
+== Thunderbolt Notes ==
+
+The driver will attempt to set the time report packet and PPS output alignment
+to UTC time since the Thunderbolt defaults to GPS alignment. Time transfer will
+not be allowed unless the receiver reports that it is aligned to UTC time.
+
+The Thunderbolt does not have an event input. Initially it was thought that
+the firmware could be upgraded to enable event input so that it would operate
+with this driver in a way similar to a Palisade or Acutime, but no upgrade was
+ever released. The Thunderbolt's serial port CTS line is not connected with the
+standard board configuration, and there is no mention of event capability in
+any documentation. Newer receivers in the Thunderbolt line also do not have
+event inputs. Here is a link explaining the firmware situation:
+https://lists.ntp.org/pipermail/hackers/2006-April/002216.html
 
-== Thunderbolt Timing packets Data Format ==
-
-Thunderbolt can output 2 synchronous packets.
-
-.Primary Timing Packet - 0x8FAB
-[width="50%",frame="none",grid="none",style="verse",options="header"]
-|============================================================================
-|Byte| Bit | Item        | Type     | Value | Description
-| 0  |     | Subcode     | UINT8    | 0xAB  |
-|1-4 |     | Time of Week| UINT32   |       | GPS seconds of week
-|5-6 |     | Week Number | UINT16   |       | GPS Week Number
-|7-8 |     | UTC Offset  | SINT16   |       | UTC Offset (seconds)
-|9   |   0 | Timing flag | Bit field|0 or 1 | GPS Time or UTC Time
-|    |   1 |             |          |0 or 1 | GPS PPS or UTC PPS
-|    |   2 |             |          |0 or 1 | time is set or time is not set
-|    |   3 |             |          |0 or 1 | have UTC info or no UTC info
-|    |   4 |             |          |0 or 1 | Time from GPS or time from user:
-|10  |     | Seconds     | UINT8    |0-59   | (60 for UTC leap second event)
-|11  |     | Minutes     | UINT8    |0-59   | Minutes of Hour
-|12  |     | Hours       | UINT8    |0-23   | Hour of Day
-|13  |     | Day of Month| UINT8    |1-31   | Day of Month
-|14  |     | Month       | UINT8    |1-12   | Month of Year
-|15-16|    | Year        | UINT16   |       | Four digits of Year (e.g. 1998)
-|============================================================================
-
-.Supplemental Timing Packet - 0x8FAC
-[width="50%",frame="none",grid="none",style="verse",options="header"]
-|=================================================================================
-|Byte  | Bit | Item                 | Type     | Value    | Description
-| 0    |     | Subcode              | UINT8    |          | 0xAC
-| 1    |     | Receiver Mode        | UINT8    | 0        | Automatic (2D/3D)
-|      |     |                      |          | 1        | Single Satellite (Time)
-|      |     |                      |          | 2        | Horizontal (2D)
-|      |     |                      |          | 3        | Full Position (3D)
-|      |     |                      |          | 4        | DGPS Reference
-|      |     |                      |          | 5        | Clock Hold (2D)
-|      |     |                      |          | 6        | Overdetermined Clock
-| 2    |     | Disciplining Mode    | UINT8    | 0        | Normal
-|      |     |                      |          | 1        | Power-Up
-|      |     |                      |          | 2        | Auto Holdover
-|      |     |                      |          | 3        | Manual Holdover
-|      |     |                      |          | 4        | Recovery
-|      |     |                      |          | 5        | Not Used
-|      |     |                      |          | 6        | Disciplining disabled
-| 3    |     | Self-Survey Progress | UINT 8   | 0-100%   |
-| 4-7  |     | Holdover Duration    | UINT 32  |          | seconds
-| 8-9  | 0   | Critical Alarms      | UINT16   | Bit field| ROM checksum error
-|      | 1   |                      |          |          | RAM check has failed
-|      | 2   |                      |          |          | Power supply failure
-|      | 3   |                      |          |          | FPGA check has failed
-|      | 4   |                      |          |          | Oscillator control voltage at rail
-| 10-11| 0   | Minor Alarms         | UINT16   | Bit field| Normal
-|      | 1   |                      |          |          | Power-Up
-|      | 2   |                      |          |          | Auto Holdover
-|      | 3   |                      |          |          | Manual Holdover
-|      | 4   |                      |          |          | Recovery
-|      | 5   |                      |          |          | Not Used
-|      | 6   |                      |          |          | Disciplining disabled
-| 12   |     | GPS Decoding Status  | UINT8    | 0        | Doing fixes
-|      |     |                      |          | 1        | Don t have GPS time
-|      |     |                      |          | 3        | PDOP is too high
-|      |     |                      |          | 8        | No usable sats
-|      |     |                      |          | 9        | Only 1 usable sat
-|      |     |                      |          | 0x0A     | Only 2 usable sats
-|      |     |                      |          | 0x0B     | Only 3 usable sats
-|      |     |                      |          | 0x0C     | The chosen sat is unusable
-|      |     |                      |          | 0x10     | TRAIM rejected the fix
-| 13   |     | Disciplining Activity| UINT8    | 0        | Phase locking
-|      |     |                      |          | 1        | Oscillator warming up
-|      |     |                      |          | 2        | Frequency locking
-|      |     |                      |          | 3        | Placing PPS
-|      |     |                      |          | 4        | Initializing loop filter
-|      |     |                      |          | 5        | Compensating OCXO
-|      |     |                      |          | 6        | Inactive
-|      |     |                      |          | 7        | Not used
-|      |     |                      |          | 8        | Recovery mode
-| 14   |     | Spare Status 1       | UINT8    | 0        | 	
-| 15   |     | Spare Status 2       | UINT8    | 0        |
-| 16-19|     | PPS Offset           | Single   |          |Estimate of UTC/GPS offset (ns)
-| 20-23|     | 10 MHz Offset        |Single    |          |Estimate of UTC/GPS offset (ns)
-| 24-27|     | DAC Value            |UINT32    |          |Offset binary (0x00 - 0xFFFFF)
-| 28-31|     | DAC Voltage          |Single    |          |Volts
-| 32-35|     | Temperature          |Single    |          |degrees C
-| 36-43|     | Latitude             |Double    |          |radians
-| 44-51|     | Longitude            |Double    |          |radians
-| 52-59|     | Altitude             |Double    |          |Meters
-| 60-67|     | Spare                |          |          |For Future Expansion
-|=================================================================================
-
-
-[[Pinouts]]
-== Pinouts ==
-
-link:#_gps_connection[The following connections are required when connecting
-Palisade with a host:]
-
-[width="50%",frame="none",grid="none"]
-|===============================================================================
-|[underline]#Description#   3+| *Host*    3+| *Palisade*
-| [[PortA]] *Port A* |[underline]#DB-9#   | [underline]#DB-25# |    | [underline]#RS-232# | [underline]#RS-422#       | [underline]#Palisade Pin#
-| Receive Data       |2       |3      |<-->| Green  | Green / Blue | 8 (T-) & 10 (T+)
-| Request to Send    |7       |4      |<-->| Gray   | Gray / White | 6 (R-) & 7 (R+)
-| Signal Ground      |5       |7      |<-->|Black   | Black        | 9 (GND)
-| [[PortB]] *Port B* |        |       |    |        |              |
-| Receive Data       |2       |3      |<-->|Brown   |Brown / Yellow| 4 (T-) & 5 (T+)
-| Transmit Data      |3       |2      |<-->|Violet  |Orange/ Violet| 2 (R-) & 3 (R+)
-| Signal Ground      |5       |7      |<-->|Black   |Black         | 9 (GND)
+Due to the lack of an event input the driver uses the timecode packet
+transmitted by the Thunderbolt shortly after the beginning of each second, but
+this packet is not very well aligned: delay varies from 7ms to 32ms depending
+on the number of satellites the receiver is attempting to track, and the delay
+is not included in the timecode. For this reason the xref:t1[+time1+] should be
+set to about 20ms. The link:driver_pps.html[+pps+] driver should be used along
+with this driver for best results, but a hardware modification is required to
+route the receiver's 1PPS output to the serial port DCD line for serial PPS.
+
+The Thunderbolt will have a "GPS Week Number Rollover" problem after
+30-Jul-2017. The same workaround mentioned in the
+xref:Pal[Palisade / Acutime Notes] section is implemented for the Thunderbolt.
+
+The receiver must know the current UTC offset from GPS time to be usable with
+ntpd. The receiver automatically decodes the UTC offset data from the +Almanac+
+transmitted by GPS satellites. With good antenna placement, Almanac reception
+can be expected to take 13 minutes or more after receiver power-up. The driver
+will wait for the receiver to report that its UTC offset is valid before
+enabling time transfer. 
+
+Time transfer during holdover may be enabled by setting xref:t2[time2] to the 
+maximum allowable holdover duration in seconds.
+
+
+== Change Log ==
+Since version 2.45 (30/08/09)
+[width="100%",cols="<2%,<98%",frame="none",grid="none"]
+|==============================================================================
+|3.00|
+||add GPS week number rollover workaround
+||remove support for using the Palisade, Acutime 2000, Acutime Gold and
+  Praecis without event triggering (due to the GPS week number rollover
+  workaround)
+||add flag3 to select event triggering method
+||simplify debugging messages
+||allow for time transfer during Thunderbolt holdover
+||check serial parity for receivers that generate parity by default
 |===============================================================================
 
-*Note: If driving the RS-422 inputs on the Palisade single ended, i.e.
-using the Green and Gray connections only, does not work on all serial
-ports. Use of the Palisade NTP Synchronization Interface Module is
-recommended.*
-
-*The 12 pin connector pinout definition:*
-
-*Face the round 12 pin connector at the end of the cable, with the
-notch Pin 1 is to the left of the notch. Pins 2 - 8 wrap around the
-bottom, counterclockwise to pin 9 on the right of the notch. Pin 10
-is just below the notch. Pins 10 (top), 11 (bottom left) and 12
-(bottom right) form a triangle in the center of the connector.*
-
-*Pinouts for the Palisade NTP host adapter (Trimble PN 37070) DB-25 M
-connector are as follows:*
-
-[width="100%",cols="<25%,<25%,<25%,<25%",]
-|============================================================================
-| DB-25M | Conductor   | Palisade | Description
-| 1      | Red         | 1        | Power
-| 7      | Black       | 9        | Ground
-| 9      | Black/White |12        | PPS
-| 10     | Green       | 8        | Transmit Port A (T-)
-| 11     | Brown       | 4        | Transmit Port B (T-)
-| 12     | Gray        | 7        | Receive Port A (R+)
-| 13     | Orange      | 3        | Receive Port B (R+)
-| 21     | Orange/White|11        | PPS+
-| 22     | Blue        |10        | Transmit Port A (T+)
-| 23     | Yellow      | 5        | Transmit Port B (T+)
-| 24     | White       | 6        | Receive Port A (R-)
-| 25     | Violet      | 2        | Receive Port B (R-)
-|============================================================================
-
-== Notes on the Thunderbolt Receiver's Firmware ==
-
-*The support for Thunderbolt Receiver in the trimble driver doesn't
-support (for now) event-polling, the reason is that the Thunderbolt
-receiver the patch is written for doesn't support time-on-request, so
-you just have to sit there and wait for the time to arrive with the PPS.
-We tried to contact Trimble because there's presumably a firmware update
-that support it, but we didn't have much luck. Here is a link explaining
-the situation:*
-
-https://lists.ntp.org/pipermail/hackers/2006-April/002216.html
-
 == Authors ==
+mailto:trv-n at comcast.net[*Questions or Comments : ntpsec version 3.00+*]
 
-https://lists.ntp.org/pipermail/hackers/2006-April/002216.html[*Questions or Comments:*]
-
-mailto:sven_dietrich at trimble.com[*Sven Dietrich*]
-
-http://www.trimble.com/[*Trimble Navigation Ltd.*]
+Sven-Thorsten Dietrich
 
 mailto:fernandoph at iar.unlp.edu.ar[*Fernando P. Hauscarriaga*]
 
-*(last updated January 15, 2007)*
-
-'''''
+*(last updated September 10, 2017)*
 
 include::includes/footer.txt[]


=====================================
ntpd/refclock_trimble.c
=====================================
--- a/ntpd/refclock_trimble.c
+++ b/ntpd/refclock_trimble.c
@@ -50,6 +50,7 @@
 
 #include <termios.h>
 #include <sys/stat.h>
+#include <time.h>
 
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
@@ -71,6 +72,7 @@
 #define	REFID		"GPS\0"		/* reference ID */
 #define TRMB_MINPOLL    4		/* 16 seconds */
 #define TRMB_MAXPOLL	5		/* 32 seconds */
+#define MIN_SAMPLES	7		/* minimum number of samples in the median filter to allow a poll */
 
 /*
  * I/O Definitions
@@ -82,31 +84,13 @@
 #endif
 #define	SPEED232	B9600		  	/* uart speed (9600 baud) */
 
-/*
- * TSIP Report Definitions
- */
-#define LENCODE_8F0B	74	/* Length of TSIP 8F-0B Packet & header */
-#define LENCODE_NTP     22	/* Length of Trimble NTP Packet */
-
-#define LENCODE_8FAC    68      /* Length of Thunderbolt 8F-AC Position Packet*/
-#define LENCODE_8FAB    17      /* Length of Thunderbolt Primary Timing Packet*/
-
-/* Allowed Sub-Packet ID's */
-#define PACKET_8F0B	0x0B
-#define PACKET_NTP	0xAD
-
-/* Thunderbolt Packets */
-#define PACKET_8FAC     0xAC	/* Supplementary Thunderbolt Time Packet */
-#define PACKET_8FAB     0xAB	/* Primary Thunderbolt Time Packet */
-#define PACKET_6D	0x6D	/* Supplementary Thunderbolt Tracking Stats */
-#define PACKET_41	0x41	/* Thunderbolt I don't know what this packet is, it's not documented on my manual*/
-
-/* Acutime Packets */
-#define PACKET_41A      0x41    /* GPS time */
-#define PACKET_46       0x46    /* Receiver Health */
-
+/* parse consts */
+#define RMAX 172 /* TSIP packet 0x58 can be 172 bytes */
 #define DLE 0x10
 #define ETX 0x03
+#define MSG_TSIP 0
+#define MSG_PRAECIS 1
+#define SPSTAT_LEN 34 /* length of reply from Praecis SPSTAT message */
 
 /* parse states */
 #define TSIP_PARSED_EMPTY       0	
@@ -114,16 +98,8 @@
 #define TSIP_PARSED_DLE_1       2
 #define TSIP_PARSED_DATA        3
 #define TSIP_PARSED_DLE_2       4
-
-/* 
- * Leap-Insert and Leap-Delete are encoded as follows:
- * 	TRIMBLE_UTC_TIME set   and TRIMBLE_LEAP_PENDING set: INSERT leap
- */
-
-/* #define TRIMBLE_LEAP_INPROGRESS 0x08 * This is the leap flag	UNUSED */
-/* #define TRIMBLE_LEAP_WARNING    0x04 * GPS Leap Warning (ICD-200) UNUSED */
-#define TRIMBLE_LEAP_PENDING    0x02 /* Leap Pending (24 hours)		*/
-#define TRIMBLE_UTC_TIME        0x01 /* UTC time available				*/
+#define TSIP_PARSED_ASCII       5
+#define TSIP_PARSED_PARITY      6
 
 #define mb(_X_) (up->rpt_buf[(_X_ + 1)]) /* shortcut for buffer access	*/
 
@@ -148,12 +124,19 @@ struct packettx
  */
 struct trimble_unit {
 	short		unit;		/* NTP refclock unit number */
-	int 		polled;		/* flag to detect noreplies */
-	char		leap_status;	/* leap second flag */
+	bool		got_pkt;	/* decoded a packet this poll */
+	bool		got_time;	/* got a time packet this poll */
+	int		samples;	/* samples in filter this poll */
+	unsigned char	UTC_flags;	/* UTC & leap second flag */
+	unsigned char	trk_status;	/* reported tracking status */
 	char		rpt_status;	/* TSIP Parser State */
 	size_t 		rpt_cnt;	/* TSIP packet length so far */
-	char 		rpt_buf[BMAX]; 	/* packet assembly buffer */
+	char 		rpt_buf[RMAX]; 	/* packet assembly buffer */
 	int		type;		/* Clock mode type */
+	bool		use_event;	/* receiver has event input */
+	int		MCR;		/* modem control register value at startup */
+	bool		parity_chk;	/* enable parity checking */
+	l_fp		p_recv_time;	/* timestamp of last received packet */
 	unsigned int	week;		/* GPS week number */
 	unsigned int	TOW;		/* GPS time of week */
 	int		UTC_offset;	/* GPS-UTC offset */
@@ -164,36 +147,44 @@ struct trimble_unit {
 /*
  * Function prototypes
  */
-
 static	bool	trimble_start		(int, struct peer *);
 static	void	trimble_shutdown	(int, struct peer *);
-static	void	trimble_receive	(struct peer *);
 static	void	trimble_poll		(int, struct peer *);
+static	void	trimble_timer		(int, struct peer *);
 static	void 	trimble_io		(struct recvbuf *);
-int 		trimble_configure	(int, struct peer *);
-int 		TSIP_decode		(struct peer *);
-long		HW_poll			(struct refclockproc *);
+static	void	trimble_receive		(struct peer *, int);
+static	bool	TSIP_decode		(struct peer *);
+static	void	HW_poll			(struct refclockproc *);
 static	double	getdbl 			(uint8_t *);
 static	short	getint 			(uint8_t *);
 static	int32_t	getlong			(uint8_t *);
-#ifdef __UNUSED__
-static  void	sendcmd			(struct packettx *buffer, int c);
-#endif
 static  void	sendsupercmd		(struct packettx *buffer, int c1, int c2);
 static  void	sendbyte		(struct packettx *buffer, int b);
 static  void	sendint			(struct packettx *buffer, int a);
 static  int	sendetx			(struct packettx *buffer, int fd);
 static  void	init_thunderbolt	(int fd);
-static  void	init_acutime		(int fd);
 
-#ifdef DEBUG
-const char * Tracking_Status[15][15] = { 
-	{ "Doing Fixes\0" }, { "Good 1SV\0" }, { "Approx. 1SV\0" },
-	{"Need Time\0" }, { "Need INIT\0" }, { "PDOP too High\0" },
-	{ "Bad 1SV\0" }, { "0SV Usable\0" }, { "1SV Usable\0" },
-	{ "2SV Usable\0" }, { "3SV Usable\0" }, { "No Integrity\0" },
-	{ "Diff Corr\0" }, { "Overdet Clock\0" }, { "Invalid\0" } };
-#endif
+#define PAL_TSTATS 14
+static const char tracking_status[PAL_TSTATS+1][16] = { 
+	"Doing Fixes", "Good 1SV", "Approx. 1SV", "Need Time", "Need INIT",
+	"PDOP too High", "Bad 1SV", "0SV Usable", "1SV Usable", "2SV Usable",
+	"3SV Usable", "No Integrity", "Diff Corr", "Overdet Clock", "Invalid"};
+static const bool tracking_status_usable[PAL_TSTATS+1] = {
+	true, true, false, false, false,
+	false, false, false, false, false,
+	false, false, false, true, false};
+
+#define TB_DECOD_STATS 16 /* convert TB decoding status to tracking_status */
+static const unsigned int tb_decod_conv[TB_DECOD_STATS+1] = {
+	0, 3, 14, 5, 14, 14, 14, 14, 7, 8, 9, 10, 6, 14, 14, 14, 11};
+	
+#define TB_DISC_MODES 7
+static const char tb_disc_mode[TB_DISC_MODES+1][16] = {
+	"normal", "power-up", "auto holdover", "manual holdover",
+	"recovery", "unknown", "disabled", "invalid"};
+static const bool tb_disc_in_holdover[TB_DISC_MODES+1] = {
+	false, false, true, true,
+	false, false, false, false};
 
 /*
  * Transfer vector
@@ -205,41 +196,21 @@ struct refclock refclock_trimble = {
 	trimble_poll,		/* transmit poll message */
 	NULL,			/* control - not used  */
 	NULL,			/* initialize driver (not used) */
-	NULL			/* timer - not used */
+	trimble_timer		/* called at 1Hz by mainloop */
 };
 
 /* Extract the clock type from the mode setting */
 #define CLK_TYPE(x) ((int)(((x)->cfg.ttl) & 0x7F))
 
 /* Supported clock types */
-#define CLK_TRIMBLE	0	/* Trimble Palisade */
+#define CLK_PALISADE	0	/* Trimble Palisade */
 #define CLK_PRAECIS	1	/* Endrun Technologies Praecis */
 #define CLK_THUNDERBOLT	2	/* Trimble Thunderbolt GPS Receiver */
 #define CLK_ACUTIME     3	/* Trimble Acutime Gold */
-/* #define CLK_ACUTIMEB 4	* Trimble Actutime Gold Port B UNUSED */
-
-static bool praecis_msg;
-static void praecis_parse(struct recvbuf *rbufp, struct peer *peer);
 
-/* These routines are for sending packets to the Thunderbolt receiver
- * They are taken from Markus Prosch
- */
-
-#ifdef __UNUSED__
-/*
- * sendcmd - Build data packet for sending
- */
-static void 
-sendcmd (
-	struct packettx *buffer,
-	int c
-	)
-{
-	*buffer->data = DLE;
-	*(buffer->data + 1) = (unsigned char)c;
-	buffer->size = 2;
-}
-#endif	/* __UNUSED_ */
+/* packet 8f-ad UTC flags */
+#define UTC_AVAILABLE	0x01
+#define LEAP_SCHEDULED	0x10
 
 /*
  * sendsupercmd - Build super data packet for sending
@@ -333,31 +304,6 @@ init_thunderbolt (
 }
 
 /*
- * init_acutime - Prepares Acutime Receiver to be used with NTP
- */
-static void
-init_acutime (
-	int fd
-	)
-{
-	/* Disable all outputs, Enable Event-Polling on PortA so
-	   we can ask for time packets */
-	struct packettx tx;
-
-	tx.size = 0;
-	tx.data = (uint8_t *) malloc(100);
-
-	sendsupercmd(&tx, 0x8E, 0xA5);
-	sendbyte(&tx, 0x02);
-	sendbyte(&tx, 0x00);
-	sendbyte(&tx, 0x00);
-	sendbyte(&tx, 0x00);
-	sendetx(&tx, fd);
-
-	free(tx.data);
-}
-
-/*
  * trimble_start - open the devices and initialize data for processing
  */
 static bool
@@ -369,17 +315,21 @@ trimble_start (
 	struct trimble_unit *up;
 	struct refclockproc *pp;
 	int fd;
-	char device[20];
 	struct termios tio;
 	struct calendar build_date;
-        int rcode;
+	unsigned int cflag, iflag;
 
-	snprintf(device, sizeof(device), DEVICE, unit);
+	pp = peer->procptr;
+	pp->clockname = NAME;
 
 	/*
 	 * Open serial port. 
 	 */
 	if ( !peer->cfg.path ) {
+	    int rcode;
+	    char device[20];
+	    snprintf(device, sizeof(device), DEVICE, unit);
+
 	    /* build a path */
 	    rcode = snprintf(device, sizeof(device), DEVICE, unit);
 	    if ( 0 > rcode ) {
@@ -402,37 +352,43 @@ trimble_start (
 			  refclock_name(peer), peer->cfg.path));
 
 	if (tcgetattr(fd, &tio) < 0) {
-		msyslog(LOG_ERR, 
-			"REFCLOCK: %s tcgetattr(fd, &tio): %m",
-			refclock_name(peer));
-#ifdef DEBUG
-		printf("%s tcgetattr(fd, &tio)\n", refclock_name(peer));
-#endif
+		msyslog(LOG_ERR, "REFCLOCK: %s tcgetattr failed: %m",
+		        refclock_name(peer));
 		close(fd);
 		return false;
 	}
 
-	tio.c_cflag |= (PARENB|PARODD);
-	tio.c_iflag &= (unsigned)~ICRNL;
-
 	/*
 	 * Allocate and initialize unit structure
 	 */
 	up = emalloc_zero(sizeof(*up));
 
 	up->type = CLK_TYPE(peer);
+	up->parity_chk = true;
+	up->use_event = true;
+	pp->disp = 1000 * S_PER_NS; /* extra ~500ns for serial port delay */
+
 	switch (up->type) {
-	    case CLK_TRIMBLE:
-		/* Normal mode, do nothing */
+	    case CLK_PALISADE:
+		msyslog(LOG_NOTICE, "REFCLOCK: %s Palisade mode enabled",
+		        refclock_name(peer));
 		break;
 	    case CLK_PRAECIS:
 		msyslog(LOG_NOTICE, "REFCLOCK: %s Praecis mode enabled",
 			refclock_name(peer));
+		/* account for distance to tower */
+		pp->disp = .00002;
 		break;
 	    case CLK_THUNDERBOLT:
 		msyslog(LOG_NOTICE, "REFCLOCK: %s Thunderbolt mode enabled",
 			refclock_name(peer));
-		tio.c_cflag = (CS8|CLOCAL|CREAD);
+		up->parity_chk = false;
+		up->use_event = false;
+		/*
+		 * packet transmission delay varies from 9ms to 32ms depending
+		 * on the number of SVs the receiver is attempting to track
+		 */
+		pp->disp = .023;
 		break;
 	    case CLK_ACUTIME:
 		msyslog(LOG_NOTICE, "REFCLOCK: %s Acutime Gold mode enabled",
@@ -442,27 +398,62 @@ trimble_start (
 	        msyslog(LOG_NOTICE, "REFCLOCK: %s mode unknown",
 			refclock_name(peer));
 		break;
+		close(fd);
+		free(up);
+		return false;
 	}
-	if (tcsetattr(fd, TCSANOW, &tio) == -1) {
-	    msyslog(LOG_ERR, "REFCLOCK: %s tcsetattr(fd, &tio): %m",
-		refclock_name(peer));
-#ifdef DEBUG
-		printf("%s tcsetattr(fd, &tio)\n",refclock_name(peer));
-#endif
+	tio.c_cflag = (CS8|CLOCAL|CREAD);
+	tio.c_iflag &= (unsigned)~ICRNL;
+	if (up->parity_chk) {
+		tio.c_cflag |= (PARENB|PARODD);
+		tio.c_iflag &= (unsigned)~IGNPAR;
+		tio.c_iflag |= (INPCK|PARMRK);
+	}
+	cflag = tio.c_cflag;
+	iflag = tio.c_iflag;
+	if (tcsetattr(fd, TCSANOW, &tio) == -1 || tcgetattr(fd, &tio) == -1 ||
+	    tio.c_cflag != cflag || tio.c_iflag != iflag) {
+		msyslog(LOG_ERR, "REFCLOCK: %s tcsetattr failed: wanted cflag 0x%x got 0x%x, wanted iflag 0x%x got 0x%x, return: %m",
+		        refclock_name(peer), cflag, tio.c_cflag, iflag,
+		        tio.c_iflag);
 		close(fd);
 		free(up);
 		return false;
 	}
-
-	pp = peer->procptr;
+	if (up->use_event) {
+		/*
+		 * The width of the RTS pulse must be either less than 5us or
+		 * greater than 600ms or the Acutime 2000 may try to switch its
+		 * port A baud rate because of "Auto-DGPS". The Praecis will
+		 * produce unstable timestamps (-7us instead of +-40ns offsets)
+		 * when pulse width is more than a few us and less than 100us.
+		 * Palisade minimum puse width is specified as 1us. To satisfy
+		 * these constraints the RTS pin is idled with a positive
+		 * voltage and pulsed negative.
+		 */
+		if (ioctl(fd, TIOCMGET, &up->MCR) < 0) {
+			msyslog(LOG_ERR, "REFCLOCK: %s TIOCMGET failed: %m",
+			        refclock_name(peer));
+			close(fd);
+			free(up);
+			return false;
+		}
+		up->MCR |= TIOCM_RTS;
+		if (ioctl(fd, TIOCMSET, &up->MCR) < 0 || 
+		    !(up->MCR & TIOCM_RTS)) {
+			msyslog(LOG_ERR, "REFCLOCK: %s TIOCMSET failed: MCR=0x%x, return=%m",
+			        refclock_name(peer), (unsigned int)up->MCR);
+			close(fd);
+			free(up);
+			return false;
+		}
+	}
 	pp->io.clock_recv = trimble_io;
 	pp->io.srcclock = peer;
 	pp->io.datalen = 0;
 	pp->io.fd = fd;
 	if (!io_addclock(&pp->io)) {
-#ifdef DEBUG
-		printf("%s io_addclock\n", refclock_name(peer));
-#endif
+		msyslog(LOG_ERR, "%s io_addclock failed", refclock_name(peer));
 		close(fd);
 		pp->io.fd = -1;
 		free(up);
@@ -473,7 +464,6 @@ trimble_start (
 	 * Initialize miscellaneous variables
 	 */
 	pp->unitptr = up;
-	pp->clockname = NAME;
 	pp->clockdesc = DESCRIPTION;
 
 	peer->precision = PRECISION;
@@ -482,7 +472,6 @@ trimble_start (
 	peer->cfg.maxpoll = TRMB_MAXPOLL;
 	memcpy((char *)&pp->refid, REFID, REFIDLEN);
 	
-	up->leap_status = 0;
 	up->unit = (short) unit;
 	up->rpt_status = TSIP_PARSED_EMPTY;
 	up->rpt_cnt = 0;
@@ -505,8 +494,6 @@ trimble_start (
 
 	if (up->type == CLK_THUNDERBOLT)
 		init_thunderbolt(fd);
-	if (up->type == CLK_ACUTIME)
-		init_acutime(fd);
 
 	return true;
 }
@@ -538,70 +525,47 @@ trimble_shutdown (
 /* 
  * TSIP_decode - decode the TSIP data packets 
  */
-int
+static bool
 TSIP_decode (
 	struct peer *peer
 	)
 {
-	int st;
-	long   secint;
-	double secs;
-	double secfrac;
-	unsigned short event = 0;
-
+	unsigned char id, decod_stat, disc_mode, timing_flags;
+	long secint;
+	double secs, secfrac;
+	unsigned short event, m_alarms;
+	uint32_t holdover_t;
+	
 	struct trimble_unit *up;
 	struct refclockproc *pp;
 
 	pp = peer->procptr;
 	up = pp->unitptr;
+	id = (unsigned char)up->rpt_buf[0];
 
-	/*
-	 * Check the time packet, decode its contents. 
-	 * If the timecode has invalid length or is not in
-	 * proper format, declare bad format and exit.
-	 */
-
-	if ((up->type != CLK_THUNDERBOLT) & (up->type != CLK_ACUTIME)){
-		if ((up->rpt_buf[0] == (char) 0x41) ||
-		    (up->rpt_buf[0] == (char) 0x46) ||
-		    (up->rpt_buf[0] == (char) 0x54) ||
-		    (up->rpt_buf[0] == (char) 0x4B) ||
-		    (up->rpt_buf[0] == (char) 0x6D)) {
-
-			/* standard time packet - GPS time and GPS week number */
-#ifdef DEBUG
-			printf("Trimble Port B packets detected. Connect to Port A\n");
-#endif
-
-			return 0;
-		}
-	}
-
-	/*
-	 * We cast both to uint8_t to as 0x8f uses the sign bit on a char
-	 */
-	if ((uint8_t) up->rpt_buf[0] == (uint8_t) 0x8f) {
-		/* 
-		 * Superpackets
-		 */
+	if (id == 0x8f) {
+		/* Superpackets */
 		event = (unsigned short) (getint((uint8_t *) &mb(1)) & 0xffff);
-		if (!((pp->sloppyclockflag & CLK_FLAG2) || event)) 
-			/* Ignore Packet */
-			return 0;	   
-	
-		switch (mb(0) & 0xff) {
-
-		    case PACKET_8F0B: 
-			if (up->rpt_cnt != LENCODE_8F0B)  /* check length */
-				break;
+		if ((up->type != CLK_THUNDERBOLT) && !event)
+			/* ignore auto-report */
+			return false;
 
-			up->UTC_offset = getint((uint8_t *) &mb(16));
-			if (up->polled <= 0)
-				return 0;
-		
+		switch (mb(0) & 0xff) {
+		    case 0x0B:
+			/*
+			 * comprehensive time packet: sent after 8f-ad from
+			 * Palisade and Acutime
+			 */
+			if (up->rpt_cnt != 74) {
+				DPRINT(1, ("TSIP_decode: unit %d: 8f-0b packet length is not 74 (%d)\n",
+				       up->unit, (int)up->rpt_cnt));
+				refclock_report(peer, CEVNT_BADREPLY);
+				return false;
+			}
+			up->got_time = true;
 #ifdef DEBUG
 			if (debug > 1) { /* SPECIAL DEBUG */
-				int ts;
+				int st, ts;
 				double lat, lon, alt;
 				lat = getdbl((uint8_t *) &mb(42)) * R2D;
 				lon = getdbl((uint8_t *) &mb(50)) * R2D;
@@ -619,12 +583,19 @@ TSIP_decode (
 				printf(" : Tracking %d\n", ts); 
 			}
 #endif
-
-			if (up->UTC_offset == 0) { /* Check UTC offset */ 
-#ifdef DEBUG
-				printf("TSIP_decode: UTC Offset Unknown\n");
-#endif
-				break;
+			if (!tracking_status_usable[up->trk_status]) {
+				DPRINT(1, ("TSIP_decode: unit %d: unusable tracking status: %s\n",
+				       up->unit,
+				       tracking_status[up->trk_status]));
+				return false;
+			}
+			up->UTC_offset = getint((uint8_t *) &mb(16));
+			if (!(up->UTC_flags & UTC_AVAILABLE) ||
+			    (up->UTC_offset == 0)) {
+				pp->leap = LEAP_NOTINSYNC;
+				DPRINT(1, ("TSIP_decode: unit %d: UTC data not available\n",
+				       up->unit));
+				return false;
 			}
 
 			secs = getdbl((uint8_t *) &mb(3));
@@ -646,83 +617,59 @@ TSIP_decode (
 			caltogps(&up->date, up->UTC_offset, &up->week, &up->TOW);
 			gpsweekadj(&up->week, up->build_week);
 			gpstocal(up->week, up->TOW, up->UTC_offset, &up->date);
-#ifdef DEBUG
-			if (debug > 1) /* SPECIAL DEBUG */
-				printf("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC %02d\n",
-				       up->unit, (unsigned int)(mb(0) & 0xff), event,
-				       up->date.hour, up->date.minute, up->date.second, pp->nsec,
-				       up->date.month, up->date.monthday, up->date.year, up->UTC_offset);
-#endif
-			/* Only use this packet when no
-			 * 8F-AD's are being received
-			 */
 
-			if (up->leap_status) {
-				up->leap_status = 0;
-				return 0;
-			}
-
-			return 2;
-			break;
-
-		    case PACKET_NTP:
-			/* Trimble-NTP Packet */
+			if ((up->UTC_flags & LEAP_SCHEDULED) &&
+			    /* Avoid early announce: https://bugs.ntp.org/2773 */
+			    (6 == up->date.month || 12 == up->date.month))
+				pp->leap = LEAP_ADDSECOND;
+			else
+				pp->leap = LEAP_NOWARNING;
 
-			if (up->rpt_cnt != LENCODE_NTP) /* check length */
-				break;
-	
-			up->leap_status = mb(19);
+			DPRINT(2, ("TSIP_decode: unit %d: 8f-0b #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC %d\n    tracking status: %s\n",
+			       up->unit, event, up->date.hour, up->date.minute,
+			       up->date.second, pp->nsec, up->date.month,
+			       up->date.monthday, up->date.year,
+			       up->UTC_offset, tracking_status[up->trk_status]));
 
-			if (up->polled  <= 0) 
-				return 0;
-				
-			/* Praecis reports only 8f-ad with UTC offset applied */
-			if (up->type == CLK_PRAECIS) {
-				up->UTC_offset = 0;
-			} else if (up->UTC_offset == 0) {
-#ifdef DEBUG
-				printf("TSIP_decode 8f-ad: need UTC offset from 8f-0b\n");
-#endif
-				return 0;
-			}
+			/* don't reuse UTC flags or tracking status */
+			up->UTC_flags = 0;
+			up->trk_status = PAL_TSTATS;
+			return true;
+			break;
 
-			/* Check Tracking Status */
-			st = mb(18);
-			if (st < 0 || st > 14)
-				st = 14;
-			if ((st >= 2 && st <= 7) || st == 11 || st == 12) {
-#ifdef DEBUG
-				printf("TSIP_decode: Not Tracking Sats : %s\n",
-				       *Tracking_Status[st]);
-#endif
-				refclock_report(peer, CEVNT_BADTIME);
-				up->polled = -1;
-				return 0;
-				break;
+		    case 0xAD:
+			/*
+			 * primary UTC time packet: first packet sent after PPS
+			 * from Palisade, Acutime, and Praecis
+			 */
+			if (up->rpt_cnt != 22) {
+				DPRINT(1, ("TSIP_decode: unit %d: 8f-ad packet length is not 22 (%d)\n",
+				       up->unit, (int)up->rpt_cnt));
+				refclock_report(peer, CEVNT_BADREPLY);
+				return false;
 			}
 
-			if ( (up->leap_status & TRIMBLE_LEAP_PENDING) &&
-			/* Avoid early announce: https://bugs.ntp.org/2773 */
-				(6 == up->date.month || 12 == up->date.month) ) {
-				if (up->leap_status & TRIMBLE_UTC_TIME)  
-					pp->leap = LEAP_ADDSECOND;
-				else
-					pp->leap = LEAP_DELSECOND;
+			/* flags checked in 8f-0b for Palisade and Acutime */
+			up->trk_status = (unsigned char)mb(18);
+			if (up->trk_status > PAL_TSTATS)
+				up->trk_status = PAL_TSTATS;
+			up->UTC_flags = (unsigned char)mb(19);
+
+			/* get timecode from 8f-0b except with Praecis */
+			if (up->type != CLK_PRAECIS)
+				return false;
+
+			if (!tracking_status_usable[up->trk_status]) {
+				DPRINT(1, ("TSIP_decode: unit %d: unusable tracking status: %s\n",
+				       up->unit,
+				       tracking_status[up->trk_status]));
+				return false;
 			}
-			else if (up->leap_status)
-				pp->leap = LEAP_NOWARNING;
-		
-			else {  /* UTC flag is not set:
-				 * Receiver may have been reset, and lost
-				 * its UTC almanac data */
+			if (!(up->UTC_flags & UTC_AVAILABLE)) {
 				pp->leap = LEAP_NOTINSYNC;
-#ifdef DEBUG
-				printf("TSIP_decode: UTC Almanac unavailable: %d\n",
-				       mb(19));	
-#endif
-				refclock_report(peer, CEVNT_BADTIME);
-				up->polled = -1;
-				return 0;
+				DPRINT(1, ("TSIP_decode: unit %d: UTC data not available\n",
+				       up->unit));
+				return false;
 			}
 
 			pp->nsec = (long) (getdbl((uint8_t *) &mb(3)) * NS_PER_S);
@@ -732,263 +679,185 @@ TSIP_decode (
 			up->date.second = (uint8_t)mb(13);
 			up->date.month = (uint8_t)mb(15);
 			up->date.monthday = (uint8_t)mb(14);
-			caltogps(&up->date, up->UTC_offset, &up->week, &up->TOW);
+			caltogps(&up->date, 0, &up->week, &up->TOW);
 			gpsweekadj(&up->week, up->build_week);
-			gpstocal(up->week, up->TOW, up->UTC_offset, &up->date);
-			up->UTC_offset = 0; /* don't re-use offset */
-			DPRINT(2, ("TSIP_decode: unit %d: %02X #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC %02x %s\n",
-				   up->unit, (unsigned int)(mb(0) & 0xff), event,
-				   up->date.hour, up->date.minute, up->date.second, pp->nsec,
-				   up->date.month, up->date.monthday, up->date.year,
-				   (unsigned int)mb(19), *Tracking_Status[st]));
-			return 1;
-			break;
+			gpstocal(up->week, up->TOW, 0, &up->date);
 
-		    case PACKET_8FAC:   
-			if (up->polled <= 0)
-				return 0; 
+			if ((up->UTC_flags & LEAP_SCHEDULED) &&
+			    /* Avoid early announce: https://bugs.ntp.org/2773 */
+			    (6 == up->date.month || 12 == up->date.month))
+				pp->leap = LEAP_ADDSECOND;
+			else
+				pp->leap = LEAP_NOWARNING;
 
-			if (up->rpt_cnt != LENCODE_8FAC)/* check length */
-				break;
+			DPRINT(2, ("TSIP_decode: unit %d: 8f-ad #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC 0x%02x\n    tracking status: %s\n",
+			       up->unit, event, up->date.hour, up->date.minute,
+			       up->date.second, pp->nsec, up->date.month,
+			       up->date.monthday, up->date.year,
+			       up->UTC_flags, tracking_status[up->trk_status]));
+			return true;
+			break;
 
+		    case 0xAC:
+			/*
+			 * supplemental timing packet: sent after 8f-ab from
+			 * Thunderbolt
+			 */
+			if (up->rpt_cnt != 68) {
+				DPRINT(1, ("TSIP_decode: unit %d: 8f-ac packet length is not 68 (%d)\n",
+				       up->unit, (int)up->rpt_cnt));
+				refclock_report(peer, CEVNT_BADREPLY);
+				return false;
+			}
+			up->got_time = true;
 #ifdef DEBUG
 			if (debug > 1) { /* SPECIAL DEBUG */
 				double lat, lon, alt;
 				lat = getdbl((uint8_t *) &mb(36)) * R2D;
 				lon = getdbl((uint8_t *) &mb(44)) * R2D;
 				alt = getdbl((uint8_t *) &mb(52));
-
 				printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n",
 				       up->unit, lat,lon,alt);
-				printf("TSIP_decode: unit %d\n", up->unit);
 			}
 #endif
-			if ( (getint((uint8_t *) &mb(10)) & 0x80) &&
+			decod_stat = (unsigned char)mb(12);
+			if (decod_stat > TB_DECOD_STATS)
+				decod_stat = TB_DECOD_STATS;
+			disc_mode = (unsigned char)mb(2);
+			if (disc_mode > TB_DISC_MODES)
+				disc_mode = TB_DISC_MODES;
+			DPRINT(2, ("TSIP_decode: unit %d: leap=%d  decod.stat=%s  disc.mode=%s\n",
+			       up->unit, pp->leap,
+			       tracking_status[tb_decod_conv[decod_stat]],
+			       tb_disc_mode[disc_mode]));
+
+			m_alarms = (unsigned short)getint((uint8_t *) &mb(10));
+			if (m_alarms & 0x200) {
+				DPRINT(1, ("TSIP_decode: unit %d: 'position questionable' flag is set,\n    you must update the unit's stored position.\n",
+				       up->unit));
+				return false;
+			}
+
+			holdover_t = (uint32_t)getlong((uint8_t *) &mb(4));
+			if (!tracking_status_usable[tb_decod_conv[decod_stat]])	{
+				if (pp->fudgetime2 < 0.5) {
+					/* holdover not enabled */
+					DPRINT(1, ("TSIP_decode: unit %d: decod.stat of '%s' is unusable\n",
+					       up->unit,
+					       tracking_status[tb_decod_conv[decod_stat]]));
+					return false;
+				}else if (tb_disc_in_holdover[disc_mode] &&
+				          holdover_t > pp->fudgetime2) {
+					DPRINT(1, ("TSIP_decode: unit %d: unit in holdover (disc.mode=%s) with decod.stat of '%s' but holdover time of %us exceeds time2(%.fs)\n",
+					       up->unit,
+					       tb_disc_mode[disc_mode],
+					       tracking_status[tb_decod_conv[decod_stat]],
+					       holdover_t, pp->fudgetime2));
+					return false;
+				} else if (!tb_disc_in_holdover[disc_mode]) {
+					DPRINT(1, ("TSIP_decode: unit %d: not in holdover (disc.mode=%s) and decod.stat of '%s' is unusable\n",
+					       up->unit, tb_disc_mode[disc_mode],
+					       tracking_status[tb_decod_conv[decod_stat]]));
+					return false;
+				}
+			}
+
+			if (up->UTC_flags != UTC_AVAILABLE)
+				return false;
+
+			gpsweekadj(&up->week, up->build_week);
+			gpstocal(up->week, up->TOW, up->UTC_offset, &up->date);
+			if ((m_alarms & 0x80) &&
 			/* Avoid early announce: https://bugs.ntp.org/2773 */
 			    (6 == up->date.month || 12 == up->date.month) )
 				pp->leap = LEAP_ADDSECOND;  /* we ASSUME addsecond */
 			else 
 				pp->leap = LEAP_NOWARNING;
 
-#ifdef DEBUG
-			if (debug > 1) /* SPECIAL DEBUG */
-				printf("TSIP_decode: unit %d: 0x%02x leap %d\n",
-				       up->unit, (unsigned int)(mb(0) & 0xff), pp->leap);
-			if (debug > 1) { /* SPECIAL DEBUG */
-				printf("Receiver MODE: 0x%02X\n", (uint8_t)mb(1));
-				if (mb(1) == 0x00)
-					printf("                AUTOMATIC\n");
-				if (mb(1) == 0x01)
-					printf("                SINGLE SATELLITE\n");   
-				if (mb(1) == 0x03)
-					printf("                HORIZONTAL(2D)\n");
-				if (mb(1) == 0x04)
-					printf("                FULL POSITION(3D)\n");
-				if (mb(1) == 0x05)
-					printf("                DGPR REFERENCE\n");
-				if (mb(1) == 0x06)
-					printf("                CLOCK HOLD(2D)\n");
-				if (mb(1) == 0x07)
-					printf("                OVERDETERMINED CLOCK\n");
-
-				printf("\n** Disciplining MODE 0x%02X:\n", (uint8_t)mb(2));
-				if (mb(2) == 0x00)
-					printf("                NORMAL\n");
-				if (mb(2) == 0x01)
-					printf("                POWER-UP\n");
-				if (mb(2) == 0x02)
-					printf("                AUTO HOLDOVER\n");
-				if (mb(2) == 0x03)
-					printf("                MANUAL HOLDOVER\n");
-				if (mb(2) == 0x04)
-					printf("                RECOVERY\n");
-				if (mb(2) == 0x06)
-					printf("                DISCIPLINING DISABLED\n");
-			}
-#endif   
-			return 0;
+			DPRINT(2, ("TSIP_decode: unit %d: 8f-ac TOW: %u week: %u adj.t: %02d:%02d:%02d.0 %02d/%02d/%04d\n",
+			       up->unit, up->TOW, up->week,
+			       up->date.hour, up->date.minute, up->date.second,
+			       up->date.month, up->date.monthday, up->date.year));
+			return true;
 			break;
 
-		    case PACKET_8FAB:
-			/* Thunderbolt Primary Timing Packet */
-
-			if (up->rpt_cnt != LENCODE_8FAB) /* check length */
-				break;
-
-			if (up->polled  <= 0)
+		    case 0xAB:
+			/*
+			 * primary timing packet: first packet sent after PPS
+			 * from Thunderbolt
+			 */
+			if (up->rpt_cnt != 17) {
+				DPRINT(1, ("TSIP_decode: unit %d: 8f-ab packet length is not 17 (%d)\n",
+				       up->unit, (int)up->rpt_cnt));
+				refclock_report(peer, CEVNT_BADREPLY);
 				return 0;
-
-			up->UTC_offset = getint((uint8_t *) &mb(7));
-			if (up->UTC_offset == 0){ /* Check UTC Offset */
+			}
+			timing_flags = (unsigned char)mb(9);
 #ifdef DEBUG
-				printf("TSIP_decode: UTC Offset Unknown\n");
+			if (debug > 1) { /* SPECIAL DEBUG */
+				printf("TSIP_decode: unit %d: timing flags:0x%02X=\n",
+				       up->unit, timing_flags);
+				if (timing_flags & 0x08) {
+					printf("    timecode aligned to GPS(UTC not avail.), PPS aligned to GPS(UTC not avail.)\n");
+				} else {
+					if (timing_flags & 0x01)
+						printf("    timecode aligned to UTC, ");
+					else
+						printf("    timecode aligned to GPS(misconfigured), ");
+					if (timing_flags & 0x02)
+						printf("PPS aligned to UTC\n");
+					else
+						printf("PPS aligned to GPS(misconfigured)\n");
+				}
+				if (timing_flags & 0x04)
+					printf("    time is NOT set, ");
+				else
+					printf("    time is set, ");
+				if (timing_flags & 0x08)
+					printf("UTC is NOT available, ");
+				else
+					printf("UTC is available, ");
+				if (timing_flags & 0x10)
+					printf("test-mode timesource(misconfigured)\n");
+				else
+					printf("satellite timesource\n");
+			}
 #endif
-				break;
+			up->UTC_flags = 0;
+			up->UTC_offset = getint((uint8_t *) &mb(7));
+			if (timing_flags & 0x04 || timing_flags & 0x08 ||
+			    up->UTC_offset == 0) {
+				DPRINT(1, ("TSIP_decode: unit %d: time not set or UTC offset unavailable\n",
+					up->unit));
+				return false;
 			}
-
-
-			if ((mb(9) & 0x1d) == 0x0) {
-				/* if we know the GPS time and the UTC offset,
-				   we expect UTC timing information !!! */
-
+			/*
+			 * configuration is sent only at ntpd startup. if unit
+			 * loses power it will revert to the factory default
+			 * time alignment (GPS)
+			 */
+			if (!(timing_flags & 0x01) || !(timing_flags & 0x02) ||
+			    (timing_flags & 0x10)) {
+				DPRINT(1, ("TSIP_decode: unit %d: 8f-ab flags: not UTC time: unit is misconfigured (0x%02X)\n",
+				       up->unit, timing_flags));
 				pp->leap = LEAP_NOTINSYNC;
 				refclock_report(peer, CEVNT_BADTIME);
-				up->polled = -1;
-				return 0;
+				return false;
 			}
-
-			pp->nsec = 0;
-#ifdef DEBUG		
-			printf("\nTiming Flags are:\n");
-			printf("Timing flag value is: 0x%X\n", (unsigned int)mb(9));
-			if ((mb(9) & 0x01) != 0)
-				printf ("	Getting UTC time\n");
-			else
-				printf ("	Getting GPS time\n");
-			if ((mb(9) & 0x02) != 0)
-				printf ("	PPS is from UTC\n");
-			else
-				printf ("	PPS is from GPS\n");
-			if ((mb(9) & 0x04) != 0)
-				printf ("	Time is not Set\n");
-			else
-				printf ("	Time is Set\n");
-			if ((mb(9) & 0x08) != 0)
-				printf("	I don't have UTC info\n");
-			else
-				printf ("	I have UTC info\n");
-			if ((mb(9) & 0x10) != 0)
-				printf ("	Time is from USER\n\n");
-			else
-				printf ("	Time is from GPS\n\n");	
-#endif		
-
 			up->TOW = (uint32_t)getlong((uint8_t *) &mb(1));
 			up->week = (uint32_t)getint((uint8_t *) &mb(5));
-			gpsweekadj(&up->week, up->build_week);
-			gpstocal(up->week, up->TOW, up->UTC_offset, &up->date);
-			DPRINT(2, ("TSIP_decode: unit %d: %02X #%d TOW: %u  week: %u  adj.t: %02d:%02d:%02d.0 %02d/%02d/%04d\n",
-				   up->unit, (unsigned int)(mb(0) & 0xff), event, up->TOW, up->week, 
-				   up->date.hour, up->date.minute, up->date.second,
-				   up->date.month, up->date.monthday, up->date.year));
-			return 1;
+
+			pp->lastrec = up->p_recv_time;
+			pp->nsec = 0;
+			up->UTC_flags = UTC_AVAILABLE; /* flag for 8f-ac */
+			return false;
 			break;
 
 		    default:
-			/* Ignore Packet */
-			return 0;
+			break;
 		} /* switch */
-	} /* if 8F packets */
-
-	else if (up->rpt_buf[0] == (uint8_t)0x42) {
-		printf("0x42\n");
-		return 0;
-	}
-	else if (up->rpt_buf[0] == (uint8_t)0x43) {
-		printf("0x43\n");
-		return 0;
-	}
-	else if ((up->rpt_buf[0] == PACKET_41) & (up->type == CLK_THUNDERBOLT)){
-		printf("Undocumented 0x41 packet on Thunderbolt\n");
-		return 0;
-	}
-	else if ((up->rpt_buf[0] == PACKET_41A) & (up->type == CLK_ACUTIME)) {
-#ifdef DEBUG
-		printf("GPS TOW: %ld\n", (long)getlong((uint8_t *) &mb(0)));
-		printf("GPS WN: %d\n", getint((uint8_t *) &mb(4)));
-		printf("GPS UTC-GPS Offser: %ld\n", (long)getlong((uint8_t *) &mb(6)));
-#endif
-		return 0;
-	}
-
-	/* Health Status for Acutime Receiver */
-	else if ((up->rpt_buf[0] == PACKET_46) & (up->type == CLK_ACUTIME)) {
-#ifdef DEBUG
-		if (debug > 1) /* SPECIAL DEBUG */
-		/* Status Codes */
-			switch (mb(0)) {
-			    case 0x00:
-				printf ("Doing Position Fixes\n");
-				break;
-			    case 0x01:
-				printf ("Do no have GPS time yet\n");
-				break;
-			    case 0x03:
-				printf ("PDOP is too high\n");
-				break;
-			    case 0x08:
-				printf ("No usable satellites\n");
-				break;
-			    case 0x09:
-				printf ("Only 1 usable satellite\n");
-				break;
-			    case 0x0A:
-				printf ("Only 2 usable satellites\n");
-				break;
-			    case 0x0B:
-				printf ("Only 3 usable satellites\n");
-				break;
-			    case 0x0C:
-				printf("The Chosen satellite is unusable\n");
-				break;
-			    default:
-				printf("(Status code is invalid)\n");
-			}
-#endif
-		/* Error Codes */
-		if (mb(1) != 0)	{
-			
-			refclock_report(peer, CEVNT_BADTIME);
-			up->polled = -1;
-#ifdef DEBUG
-			if (debug > 1) { /* SPECIAL DEBUG */
-				if (mb(1) & 0x01)
-					printf ("Signal Processor Error, reset unit.\n");
-				if (mb(1) & 0x02)
-					printf ("Alignment error, channel or chip 1, reset unit.\n");
-				if (mb(1) & 0x03)
-					printf ("Alignment error, channel or chip 2, reset unit.\n");
-				if (mb(1) & 0x04)
-					printf ("Antenna feed line fault (open or short)\n");
-				if (mb(1) & 0x05)
-					printf ("Excessive reference frequency error, refer to packet 0x2D and packet 0x4D documentation for further information\n");
-			}
-#endif
-		
-		return 0;
-		}
 	}
-	else if (up->rpt_buf[0] == 0x54)
-		return 0;
-
-	else if (up->rpt_buf[0] == PACKET_6D) {
-#ifdef DEBUG
-		int sats;
-
-		if ((mb(0) & 0x01) && (mb(0) & 0x02))
-			printf("2d Fix Dimension\n");
-		if (mb(0) & 0x04)
-			printf("3d Fix Dimension\n");
-
-		if (mb(0) & 0x08)
-			printf("Fix Mode is MANUAL\n");
-		else
-			printf("Fix Mode is AUTO\n");
-	
-		sats = mb(0) & 0xF0;
-		sats = sats >> 4;
-		printf("Tracking %d Satellites\n", sats);
-#endif
-		return 0;
-	} /* else if not super packet */
-	refclock_report(peer, CEVNT_BADREPLY);
-	up->polled = -1;
-#ifdef DEBUG
-	printf("TSIP_decode: unit %d: bad packet %02x-%02x event %d len %d\n", 
-	       up->unit, (unsigned int)(up->rpt_buf[0] & 0xff), (unsigned int)(mb(0) & 0xff),
-	       event, (int)up->rpt_cnt);
-#endif
-	return 0;
+	return false;
 }
 
 /*
@@ -997,7 +866,8 @@ TSIP_decode (
 
 static void
 trimble_receive (
-	struct peer * peer
+	struct peer * peer,
+	int type
 	)
 {
 	struct trimble_unit *up;
@@ -1008,55 +878,44 @@ trimble_receive (
 	 */
 	pp = peer->procptr;
 	up = pp->unitptr;
-		
-	if (! TSIP_decode(peer)) return;
-	
-	if (up->polled <= 0) 
-		return;   /* no poll pending, already received or timeout */
 
-	up->polled = 0;  /* Poll reply received */
-	pp->lencode = 0; /* clear time code */
-	
+	/*
+	 * Wait for fudge flags to initialize. Also, startup may have caused
+	 * a spurious edge, so wait for first HW_poll()
+	 */
+	if (pp->polls < 1)
+		return;
+
+	up->got_pkt = true;
+	if (MSG_TSIP == type) {
+		if (!TSIP_decode(peer))
+			return;
+	} else {
+		if (SPSTAT_LEN == up->rpt_cnt &&
+		    up->rpt_buf[up->rpt_cnt - 1] == '\r') {
+			up->rpt_buf[up->rpt_cnt - 1] = '\0';
+			record_clock_stats(peer, up->rpt_buf);
+		}
+		return;
+	}
+
+	/* add sample to filter */
+	pp->lastref = pp->lastrec;
+	pp->year = up->date.year;
 	pp->day = up->date.yearday;
 	pp->hour = up->date.hour;
 	pp->minute = up->date.minute;
 	pp->second = up->date.second;
-	DPRINT(1, ("trimble_receive: unit %d: %4d %03d %02d:%02d:%02d.%09ld\n",
+	DPRINT(2, ("trimble_receive: unit %d: %4d %03d %02d:%02d:%02d.%09ld\n",
 		   up->unit, pp->year, pp->day, pp->hour, pp->minute, 
 		   pp->second, pp->nsec));
-
-	/*
-	 * Process the sample
-	 * Generate timecode: YYYY DoY HH:MM:SS.microsec 
-	 * report and process 
-	 */
-
-	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
-		 "%4d %03d %02d:%02d:%02d.%09ld",
-		 pp->year, pp->day,
-		 pp->hour,pp->minute, pp->second, pp->nsec); 
-	pp->lencode = 24;
-
 	if (!refclock_process(pp)) {
 		refclock_report(peer, CEVNT_BADTIME);
-
-#ifdef DEBUG
-		printf("trimble_receive: unit %d: refclock_process failed!\n",
-		       up->unit);
-#endif
+		DPRINT(1, ("trimble_receive: unit %d: refclock_process failed!\n",
+		       up->unit));
 		return;
 	}
-
-	record_clock_stats(peer, pp->a_lastcode); 
-
-	DPRINT(1, ("trimble_receive: unit %d: %s\n",
-		   up->unit, prettydate(pp->lastrec)));
-	if (pp->hour == 0 && up->week > up->build_week + 1000)
-		msyslog(LOG_WARNING, "REFCLOCK: Trimble(%d) current GPS week number (%u) is more than 1000 weeks past ntpd's build date (%u), please update",
-		        up->unit, up->week, up->build_week);
-
-	pp->lastref = pp->lastrec;
-	refclock_receive(peer);
+	up->samples++;
 }
 
 
@@ -1072,72 +931,74 @@ trimble_poll (
 {
 	struct trimble_unit *up;
 	struct refclockproc *pp;
-	
-	pp = peer->procptr;
-	up = pp->unitptr;
+	int cl;
+	bool err;
 
-	pp->polls++;
-	if (up->polled > 0) /* last reply never arrived or error */ 
-		refclock_report(peer, CEVNT_TIMEOUT);
+	UNUSED_ARG(unit);
 
-	up->polled = 2; /* synchronous packet + 1 event */
+	pp = peer->procptr;
+	up = pp->unitptr;
 
-	DPRINT(1, ("trimble_poll: unit %d: polling %s\n", unit,
-		   (pp->sloppyclockflag & CLK_FLAG2) ? 
-		   "synchronous packet" : "event"));
+	/* samples are not taken until second poll */
+	if (++pp->polls < 2)
+		return;
 
-	if (pp->sloppyclockflag & CLK_FLAG2) 
-		return;  /* using synchronous packet input */
+	/* check status for the previous poll interval */
+	err = (up->samples < MIN_SAMPLES);
+	if (err) {
+		refclock_report(peer, CEVNT_TIMEOUT);
+		if (!up->got_pkt) {
+			DPRINT(1, ("trimble_poll: unit %d: no packets found\n",
+			       up->unit));
+		} else if (!up->got_time) {
+			DPRINT(1, ("trimble_poll: unit %d: packet(s) found but none were usable.\nVerify unit isn't connected to Port B and flag3 is correct for Palisade/Acutime\n",
+			       up->unit));
+		} else {
+			DPRINT(1, ("trimble_poll: unit %d: not enough samples (%d, min %d), skipping poll\n",
+			       up->unit, up->samples, MIN_SAMPLES));
+			pp->codeproc = pp->coderecv; /* reset filter */
+		}
+	}
+	up->got_time = false;
+	up->got_pkt = false;
+	up->samples = 0;
+	if (err)
+		return;
 
+	/* ask Praecis for its signal status */
 	if(up->type == CLK_PRAECIS) {
 		if(write(peer->procptr->io.fd,"SPSTAT\r\n",8) < 0)
-			msyslog(LOG_ERR, "REFCLOCK: Trimble(%d) write: %m:",unit);
-		else {
-			praecis_msg = true;
-			return;
-		}
+			msyslog(LOG_ERR, "REFCLOCK: %s write: %m:",
+			        refclock_name(peer));
 	}
 
-	if (HW_poll(pp) < 0) 
-		refclock_report(peer, CEVNT_FAULT); 
-}
-
-static void
-praecis_parse (
-	struct recvbuf *rbufp,
-	struct peer *peer
-	)
-{
-	static char buf[100];
-	static size_t p = 0;
-	struct refclockproc *pp;
-
-	pp = peer->procptr;
-
-	memcpy(buf+p,rbufp->recv_space.X_recv_buffer, rbufp->recv_length);
-	p += rbufp->recv_length;
-
-	if(buf[p-2] == '\r' && buf[p-1] == '\n') {
-		buf[p-2] = '\0';
-		record_clock_stats(peer, buf);
+	/* record clockstats */
+	cl = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
+		 "%4d %03d %02d:%02d:%02d.%09ld",
+		 pp->year, pp->day, pp->hour,pp->minute, pp->second, pp->nsec);
+	pp->lencode = (cl < (int)sizeof(pp->a_lastcode)) ? cl : 0;
+	record_clock_stats(peer, pp->a_lastcode); 
 
-		p = 0;
-		praecis_msg = false;
+	DPRINT(2, ("trimble_poll: unit %d: %s\n",
+	       up->unit, prettydate(pp->lastrec)));
 
-		if (HW_poll(pp) < 0)
-			refclock_report(peer, CEVNT_FAULT);
+	if (pp->hour == 0 && up->week > up->build_week + 1000)
+		msyslog(LOG_WARNING, "REFCLOCK: %s current GPS week number (%u) is more than 1000 weeks past ntpd's build date (%u), please update",
+		        refclock_name(peer), up->week, up->build_week);
 
-	}
+	/* process samples in filter */
+	refclock_receive(peer);
 }
 
+
+/*
+ * trimble_io - create TSIP packets or ASCII strings from serial data stream
+ */
 static void
 trimble_io (
 	struct recvbuf *rbufp
 	)
 {
-	/*
-	 * Initialize pointers and read the timecode and timestamp.
-	 */
 	struct trimble_unit *up;
 	struct refclockproc *pp;
 	struct peer *peer;
@@ -1148,21 +1009,11 @@ trimble_io (
 	pp = peer->procptr;
 	up = pp->unitptr;
 
-	if(up->type == CLK_PRAECIS) {
-		if(praecis_msg) {
-			praecis_parse(rbufp,peer);
-			return;
-		}
-	}
-
 	c = (char *) &rbufp->recv_space;
 	d = c + rbufp->recv_length;
-		
-	while (c != d) {
 
-		/* Build time packet */
+	while (c != d) {
 		switch (up->rpt_status) {
-
 		    case TSIP_PARSED_DLE_1:
 			switch (*c)
 			{
@@ -1176,6 +1027,8 @@ trimble_io (
 				up->rpt_status = TSIP_PARSED_DATA;
 				/* save packet ID */
 				up->rpt_buf[0] = *c;
+				/* save packet receive time */
+				up->p_recv_time = rbufp->recv_time;
 				break;
 			}
 			break;
@@ -1183,110 +1036,131 @@ trimble_io (
 		    case TSIP_PARSED_DATA:
 			if (*c == DLE)
 				up->rpt_status = TSIP_PARSED_DLE_2;
+			else if (up->parity_chk && *c == '\377')
+				up->rpt_status = TSIP_PARSED_PARITY;
 			else 
 				mb(up->rpt_cnt++) = *c;
 			break;
 
+		    case TSIP_PARSED_PARITY:
+			if (*c == '\377') {
+				up->rpt_status = TSIP_PARSED_DATA;
+				mb(up->rpt_cnt++) = *c;
+			} else {
+				msyslog(LOG_ERR, "REFCLOCK: %s: detected serial parity error or receive buffer overflow",
+					refclock_name(peer));
+				up->rpt_status = TSIP_PARSED_EMPTY;
+			}
+			break;
+
 		    case TSIP_PARSED_DLE_2:
 			if (*c == DLE) {
 				up->rpt_status = TSIP_PARSED_DATA;
-				/* prevent overrun - should never happen */
-				if (up->rpt_cnt < BMAX - 2)
-					mb(up->rpt_cnt++) = *c;
-			}
-			else if (*c == ETX) 
+				mb(up->rpt_cnt++) = *c;
+			} else if (*c == ETX) {
 				up->rpt_status = TSIP_PARSED_FULL;
-			else 	{
+				trimble_receive(peer, MSG_TSIP);
+			} else {
 				/* error: start new report packet */
 				up->rpt_status = TSIP_PARSED_DLE_1;
 				up->rpt_buf[0] = *c;
 			}
 			break;
 
+		    case TSIP_PARSED_ASCII:
+			if (*c == '\n') {
+				mb(up->rpt_cnt++) = *c;
+				up->rpt_status = TSIP_PARSED_FULL;
+				trimble_receive(peer, MSG_PRAECIS);
+			} else if (up->parity_chk && *c == '\377') {
+				up->rpt_status = TSIP_PARSED_PARITY;
+			} else {
+				mb(up->rpt_cnt++) = *c;
+			}
+			break;
+
 		    case TSIP_PARSED_FULL:
 		    case TSIP_PARSED_EMPTY:
 		    default:
-			if ( *c != DLE)
-				up->rpt_status = TSIP_PARSED_EMPTY;
-			else 
+			up->rpt_cnt = 0;
+			if (*c == DLE) {
 				up->rpt_status = TSIP_PARSED_DLE_1;
+			} else if (up->type == CLK_PRAECIS && NULL != strchr("6L789ADTP", *c)) {
+				/* Praecis command reply */
+				up->rpt_buf[0] = *c;
+				up->rpt_status = TSIP_PARSED_ASCII;
+			} else {
+ 				up->rpt_status = TSIP_PARSED_EMPTY;
+			}
 			break;
 		}
-		
 		c++;
-
-		if (up->rpt_status == TSIP_PARSED_DLE_1) {
-			up->rpt_cnt = 0;
-			if (pp->sloppyclockflag & CLK_FLAG2) 
-				/* stamp it */
-				get_systime(&pp->lastrec);
+		if (up->rpt_cnt > RMAX - 2) {/* additional byte for ID */
+			up->rpt_status = TSIP_PARSED_EMPTY;
+			DPRINT(1, ("trimble_io: unit %d: oversize serial message (%luB) 0x%02x discarded",
+			        up->unit, (unsigned long)up->rpt_cnt,
+				(uint8_t)up->rpt_buf[0]));
 		}
-		else if (up->rpt_status == TSIP_PARSED_EMPTY)
-			up->rpt_cnt = 0;
+	} /* while chars in buffer */
+}
 
-		else if (up->rpt_cnt > sizeof(up->rpt_buf)) 
-			up->rpt_status =TSIP_PARSED_EMPTY;
 
-		if (up->rpt_status == TSIP_PARSED_FULL) 
-			trimble_receive(peer);
+/*
+ * trimble_timer - trigger an event at 1Hz
+ */
+static void
+trimble_timer(
+	int unit,
+	struct peer * peer
+	)
+{
+	struct trimble_unit *up;
+	struct refclockproc *pp;
 
-	} /* while chars in buffer */
+	UNUSED_ARG(unit);
+
+	pp = peer->procptr;
+	up = pp->unitptr;
+
+	if (up->use_event)
+		HW_poll(pp);
 }
 
 
 /*
- * Trigger the Trimble's event input, which is driven off the RTS
- *
- * Take a system time stamp to match the GPS time stamp.
- *
+ * Trigger the event input
  */
-long
+static void
 HW_poll (
-	struct refclockproc * pp 	/* pointer to unit structure */
+	struct refclockproc * pp
 	)
 {	
-	int x;	/* state before & after RTS set */
 	struct trimble_unit *up;
+	static const struct timespec ts = {0, 13 * NS_PER_MS};
 
 	up = pp->unitptr;
 
-	/* read the current status, so we put things back right */
-	if (ioctl(pp->io.fd, TIOCMGET, &x) < 0) {
-		DPRINT(1, ("Trimble HW_poll: unit %d: GET %m\n",
-			   up->unit));
-		msyslog(LOG_ERR, "REFCLOCK: Trimble(%d) HW_poll: ioctl(fd,GET): %m", 
-			up->unit);
-		return -1;
-	}
-  
-	x |= TIOCM_RTS;        /* turn on RTS  */
-
 	/* Edge trigger */
-	if (up->type == CLK_ACUTIME)
+	if (pp->sloppyclockflag & CLK_FLAG3) {
 		IGNORE(write (pp->io.fd, "", 1));
-		
-	if (ioctl(pp->io.fd, TIOCMSET, &x) < 0) {
-		DPRINT(1, ("Trimble HW_poll: unit %d: SET \n", up->unit));
-		msyslog(LOG_ERR,
-			"REFCLOCK: Trimble(%d) HW_poll: ioctl(fd, SET, RTS_on): %m", 
-			up->unit);
-		return -1;
-	}
-
-	x &= ~TIOCM_RTS;        /* turn off RTS  */
-	
-	/* poll timestamp */
-	get_systime(&pp->lastrec);
+	} else {
+		up->MCR &= ~TIOCM_RTS; /* set RTS low from high idle state */
+		IGNORE(ioctl(pp->io.fd, TIOCMSET, &up->MCR));
+
+		/*
+		 * The Acutime 2000 will occasionally transmit with parity
+		 * errors if the low state is held for less than 1ms, and the
+		 * Praecis will produce unstable timestamps if the low state is
+		 * held for less than 12ms.
+		 */
+		nanosleep(&ts, NULL);
 
-	if (ioctl(pp->io.fd, TIOCMSET, &x) == -1) {
-		DPRINT(1, ("Trimble HW_poll: unit %d: UNSET \n", up->unit));
-		msyslog(LOG_ERR,
-			"RECLOCK: Trimble(%d) HW_poll: ioctl(fd, UNSET, RTS_off): %m", 
-			up->unit);
-		return -1;
+		up->MCR |= TIOCM_RTS;  /* make edge / restore idle */
+		IGNORE(ioctl(pp->io.fd, TIOCMSET, &up->MCR));
 	}
 
-	return 0;
+	/* get timestamp after triggering since RAND_bytes is slow */
+	get_systime(&pp->lastrec);
 }
 
 /*



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/e0982408424cace68ca1d463f21cc2f14682e382...2d5caf4769499400c5ee17da8fabbe9331c92170

---
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/e0982408424cace68ca1d463f21cc2f14682e382...2d5caf4769499400c5ee17da8fabbe9331c92170
You're receiving this email because of your account on gitlab.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20170916/1e92bc5d/attachment.html>


More information about the vc mailing list