Skip to content

Timing Precision Full Pipeline: End-to-End Error Budget

Status: Technical specification Relates to: [[Timing Precision Budget]], [[TTV Sensitivity and N-Body Math]], [[Occultation Geometry and Chord Reconstruction]] Depends on: astropy >= 5.0, barycorrpy (optional cross-check) Last derived: 2026-03-21


1. The Full Pipeline Map

The complete signal chain from physical photon to science output, with every timing contribution:

Physical photon arrival at CCD/CMOS
    โ”‚
    โ”œโ”€[A] Exposure start/end (mechanical shutter or electronic rolling/global)
    โ”‚       Offset: 0โ€“50 ms from command to actual integration start
    โ”‚       Uncertainty: ยฑ5โ€“50 ms (random, OS-scheduler-dependent)
    โ”‚       Type: Random (jitter from USB/driver latency)
    โ”‚
    โ”œโ”€[B] Camera driver timestamp (OS system clock at readout completion)
    โ”‚       Offset: depends on whether driver stamps start or end of exposure
    โ”‚       Uncertainty: ยฑ0 ms if camera hardware reports; ยฑ10โ€“50 ms if OS clock
    โ”‚       Type: Quasi-random
    โ”‚
    โ”œโ”€[C] OS system clock accuracy (NTP or GPS-PPS disciplined)
    โ”‚       NTP offset: 0 mean, ยฑ5โ€“50 ms (1ฯƒ) โ€” random
    โ”‚       GPS-PPS offset: 0 mean, ยฑ0.001โ€“0.05 ms (1ฯƒ) โ€” random
    โ”‚       Type: Random (zero-mean for NTP; near-zero for GPS-PPS)
    โ”‚
    โ”œโ”€[D] ASCOM Alpaca driver latency (REST API round-trip)
    โ”‚       Offset: ~5โ€“20 ms (localhost); ~50โ€“200 ms (network)
    โ”‚       Uncertainty: ยฑ5โ€“15 ms (random, from HTTP + OS scheduler)
    โ”‚       Type: Random (jitter); small systematic bias possible
    โ”‚
    โ”œโ”€[E] FITS header write (DATE-OBS, DATE-BEG, DATE-END)
    โ”‚       Offset: 0 if correctly written at exposure start
    โ”‚       Uncertainty: 0 if format is unambiguous ISO 8601 UTC
    โ”‚       Type: Systematic if wrong (e.g., DATE-OBS = end instead of start)
    โ”‚       CRITICAL: ambiguity in whether DATE-OBS is start or end of exposure
    โ”‚
    โ”œโ”€[F] Upload to OpenAstro server
    โ”‚       No timing contribution (timestamps already fixed in FITS header)
    โ”‚
    โ”œโ”€[G] Plate solve + WCS fit
    โ”‚       No timing contribution (spatial calibration only)
    โ”‚
    โ”œโ”€[H] Time standard conversion: UTC โ†’ TDB
    โ”‚       Offset: +69.184 s (deterministic: TT-TAI = 32.184 s + 37 leap seconds)
    โ”‚       Uncertainty: <1 ยตs (known to sub-microsecond from IERS bulletins)
    โ”‚       Type: Systematic (known, must be applied)
    โ”‚
    โ”œโ”€[I] Barycentric correction: topocentric โ†’ solar system barycentre
    โ”‚       Offset: up to ยฑ8.3 minutes (light travel time projection)
    โ”‚       Uncertainty: ~1 ms (from Earth ephemeris accuracy, DE440)
    โ”‚       Type: Systematic (known, must be applied)
    โ”‚
    โ”œโ”€[J] Exposure mid-point calculation: T_mid = T_start + t_exp/2
    โ”‚       Offset: t_exp/2 (deterministic)
    โ”‚       Uncertainty: ฮด(t_exp)/2, typically <1 ms for cameras reporting ms precision
    โ”‚       Type: Deterministic
    โ”‚
    โ”œโ”€[K] Light curve extraction (differential photometry)
    โ”‚       No direct timing contribution (photometric noise affects fitted time)
    โ”‚
    โ”œโ”€[L] Transit model fit / occultation edge fit
    โ”‚       Output: T_mid (transit) or T_D, T_R (occultation) with fitted uncertainty
    โ”‚       The fitted uncertainty incorporates photometric noise, cadence, and
    โ”‚       ingress/egress duration. This is the dominant uncertainty for transits.
    โ”‚
    โ””โ”€[OUTPUT] T_mid in BJD_TDB with uncertainty ฯƒ_Tmid

Classification of contributions

Step Magnitude Type Reducible?
[A] Shutter/trigger latency 5โ€“50 ms Random Yes: hardware trigger
[B] Driver timestamp 0โ€“50 ms Quasi-random Yes: use camera-reported time
[C] OS clock (NTP) 5โ€“50 ms Random Yes: GPS-PPS
[C] OS clock (GPS-PPS) 0.001โ€“0.05 ms Random Near floor
[D] ASCOM Alpaca latency 5โ€“20 ms Random Yes: local timestamp
[E] FITS header ambiguity 0 or t_exp Systematic if wrong Yes: validate format
[H] UTC โ†’ TDB 69.184 s Systematic (known) Apply correction
[I] Barycentric correction ยฑ8.3 min Systematic (known) Apply correction
[J] Exposure mid-point t_exp/2 Deterministic Apply formula
[L] Model fit 30โ€“120 s (transit) Random More data, better SNR
[L] Model fit 2โ€“50 ms (occultation) Random Higher frame rate, better SNR

2. Hardware Clock Sources โ€” Detailed Comparison

2.1 NTP (Network Time Protocol)

Achievable accuracy on consumer hardware:

Condition Offset from UTC (1ฯƒ) Source
Windows PC, default W32Time service, Stratum 2 ยฑ50โ€“200 ms Microsoft documentation
Windows PC, Meinberg NTP client, Stratum 2 ยฑ10โ€“50 ms Meinberg NTP docs
Linux, chrony, Stratum 2 over broadband ยฑ5โ€“30 ms chrony documentation
Linux, chrony, Stratum 1 on same LAN ยฑ1โ€“5 ms Mills RFC 5905
Any OS, WiFi connection ยฑ20โ€“100 ms Variable; WiFi adds 1โ€“50 ms jitter

References: Mills, D.L. (1991), RFC 1305; updated RFC 5905 (NTPv4). Meinberg NTP documentation (meinbergglobal.com).

Sources of NTP degradation:

  1. Asymmetric routing: NTP assumes symmetric network delay. If outbound latency differs from return latency (common on residential connections with asymmetric upload/download), the offset estimate is biased by half the asymmetry. A 20 ms asymmetry produces a 10 ms systematic bias.

  2. OS scheduler jitter:

  3. Windows: The default system timer resolution is 15.625 ms (1/64 s). This means the OS can only service NTP interrupts with ~16 ms granularity. The timeBeginPeriod(1) API call or high-resolution multimedia timer reduces this to ~1 ms, but many astronomy applications do not invoke it. The W32Time service is particularly poor โ€” it syncs infrequently and has no sub-second granularity.
  4. Linux: The kernel timer resolution is typically 1 ms (HZ=1000) or 4 ms (HZ=250). chrony achieves better performance than ntpd because it uses kernel timestamping and adaptive polling.

  5. Network latency variation (jitter): On residential broadband, round-trip times fluctuate by 5โ€“50 ms due to buffer bloat, routing changes, and congestion. NTP filters this with the clock filter algorithm, but cannot eliminate it entirely.

  6. Clock drift between polls: Between NTP synchronizations (typically every 64โ€“1024 s), the local quartz oscillator drifts at 10โ€“100 ppm. Over a 5-minute poll interval: 50 ppm ร— 300 s = 15 ms maximum drift. chrony compensates with frequency tracking (it learns the oscillator drift rate), but the residual after correction is still ~1โ€“5 ms.

When is NTP sufficient?

The criterion: NTP is adequate when the science timing requirement is at least 5ร— the NTP uncertainty. This provides a comfortable margin where the clock error is negligible in quadrature with other error sources.

Science case Required ฯƒ_t NTP uncertainty (1ฯƒ) Ratio NTP sufficient?
TTV transit mid-times 30 s 50 ms 600ร— Yes โ€” overwhelmingly
Variable star periods 10 s 50 ms 200ร— Yes
GRB classification 60 s 50 ms 1200ร— Yes
GRB afterglow light curve 1 s 50 ms 20ร— Yes
Eclipsing binary O-C 1โ€“60 s 50 ms 20โ€“1200ร— Yes
Microlensing caustic 1 min 50 ms 1200ร— Yes
Stellar occultation (KBO) 100 ms 50 ms 2ร— Marginal โ€” GPS-PPS recommended
Stellar occultation (main-belt) 10โ€“30 ms 50 ms 0.2โ€“0.6ร— No โ€” GPS-PPS required
FRB optical counterpart 1โ€“10 ms 50 ms 0.02โ€“0.2ร— No โ€” GPS-PPS mandatory

Conclusion: NTP is sufficient for all OpenAstro science cases except stellar occultations of main-belt asteroids and FRB optical counterpart searches. For these, GPS-PPS is mandatory.

2.2 GPS with PPS (Pulse Per Second)

Achievable accuracy:

Component Accuracy
GPS PPS signal (1PPS output) ยฑ50โ€“100 ns relative to UTC(USNO)
PPS signal to kernel (interrupt latency) ยฑ1โ€“10 ยตs
chrony disciplined to PPS ยฑ1โ€“50 ยตs system clock offset
gpsd parsing NMEA sentences ยฑ1โ€“10 ms (for coarse time only; PPS provides fine correction)

Hardware options:

Module Price (approx.) Interface PPS output Notes
u-blox NEO-M8T $40โ€“60 UART/USB Yes (dedicated pin) Timing-grade module, raw measurements
u-blox NEO-M9N $25โ€“40 UART/USB Yes Multi-band, good for general use
u-blox ZED-F9T $80โ€“120 UART/USB Yes (dual) High-precision timing module
Adafruit Ultimate GPS (MTK3339) $30 UART Yes Budget option, adequate PPS
Raspberry Pi GPS HAT (various) $30โ€“50 GPIO Yes (GPIO pin) Plug-and-play with RPi

Software stack: gpsd โ†’ chrony โ†’ OS clock

The PPS disciplining chain works as follows:

  1. gpsd receives two data streams from the GPS module:
  2. NMEA sentences (GGA, RMC) providing coarse time (accurate to ยฑ1โ€“10 ms) plus position
  3. PPS pulse on a dedicated pin (accurate to ยฑ100 ns)

  4. gpsd makes both available as NTP reference clocks:

  5. SHM(0): coarse NMEA time (refclock type 28 in ntpd, or refclock SHM 0 in chrony)
  6. SHM(1) or PPS: the PPS pulse (refclock type 22 in ntpd, or refclock PPS /dev/pps0 in chrony)

  7. chrony (preferred over ntpd) uses the PPS signal to discipline the system clock:

  8. The NMEA time resolves the integer-second ambiguity (PPS only provides the fractional-second edge)
  9. The PPS signal provides sub-microsecond fractional-second accuracy
  10. chrony adjusts the system clock frequency to track the PPS, achieving ยฑ1โ€“50 ยตs steady-state offset

  11. Configuration example (chrony.conf):

    refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
    refclock PPS /dev/pps0 lock NMEA refid PPS prefer
    

The lock NMEA directive tells chrony to use the NMEA source only for second numbering, and the PPS for precise edge timing. The prefer directive gives PPS priority over any network NTP sources.

ASCOM Alpaca timing: camera driver vs OS clock

This is a critical distinction. When an ASCOM camera driver reports LastExposureStartTime (an ASCOM standard property), the timestamp may come from one of two sources:

  1. Camera hardware clock: Some cameras (e.g., SBIG, QHY with GPS option, FLI) have onboard clocks or GPS receivers. The driver reports the hardware-measured start time. This is the most accurate option (ยฑ1 ms or better).

  2. OS system clock at the moment the driver initiates the exposure: This is the common case for most consumer CMOS cameras (ZWO ASI, QHY without GPS, Atik, etc.). The driver reads DateTime.UtcNow (or equivalent) when it sends the "start exposure" command to the camera. The actual integration start occurs some variable time later (USB transfer of command, camera firmware processing). This latency is typically 5โ€“50 ms and is not accounted for in the timestamp.

The ASCOM LastExposureStartTime property documentation states it returns "the actual exposure start in the FITS-standard CCYY-MM-DDThh:mm:ss[.sss...] format" โ€” but "actual" is ambiguous. Most drivers return the time the software command was issued.

For OpenAstro: The pipeline must record which timing source the camera driver uses. This should be stored in the FITS header as TIMESRC and in the observation metadata.

2.3 Dedicated GPS-PPS to FITS Header Solutions

For demanding timing applications (occultations, FRBs), hardware-level timestamping bypasses the OS clock entirely:

IOTA Video Time Inserter (VTI) / VideoTimerII: - Overlays GPS time (from an integrated GPS receiver) directly onto the analog or digital video stream as text characters - Each video frame carries its own GPS timestamp, visible in the image - Accuracy: ยฑ1 ms (limited by the VTI's internal processing, not the OS) - Used extensively by IOTA occultation observers - Limitation: designed for video cameras (analog or NTSC/PAL), not modern CMOS science cameras

Kiwi OSD (On-Screen Display): - Similar to VTI but with enhanced features for digital video - GPS-disciplined, ยฑ1 ms accuracy per frame - Works with video-rate cameras

For modern CMOS cameras without hardware trigger:

The shutter latency problem: When software commands a CMOS camera to start an exposure, the actual start of photon integration occurs ฮ”t after the command, where ฮ”t is: - USB command transfer time: 1โ€“5 ms - Camera firmware processing: 1โ€“10 ms - Total ฮ”t: 2โ€“15 ms, variable and uncalibrated

This ฮ”t varies from exposure to exposure because USB is not a real-time bus โ€” other USB traffic, OS scheduler interrupts, and hub contention all contribute jitter.

Mitigation strategies: 1. Camera-side timestamping: Some cameras (ZWO ASI with latest SDK) report the actual frame start time from an internal counter. Use this instead of the OS clock if available. 2. External hardware trigger: A GPS-PPS signal triggers the camera via an external trigger input (available on some scientific CMOS cameras). The exposure start is synchronized to the PPS edge, giving ยฑ1 ยตs accuracy. Requires cameras with an external trigger port (e.g., QHY cameras with GPS option, Andor, PCO). 3. Post-hoc calibration: Measure the systematic delay ฮ”t for a specific camera + computer combination using a pulsed LED with known timing, then apply the correction. The random jitter component (~5 ms) cannot be removed this way.


3. The ASCOM Alpaca Timing Uncertainty

3.1 The Exposure Initiation Sequence

When an OpenAstro client initiates an exposure via ASCOM Alpaca:

[1] Python client: t_command = time.time()
[2] Python client: POST http://localhost:11111/api/v1/camera/0/startexposure
    Body: {"Duration": 30.0, "Light": true}
[3] HTTP transport (localhost): 0.5โ€“5 ms
[4] Alpaca server receives request: parses JSON, validates
[5] Alpaca server calls ASCOM driver: ICameraV3.StartExposure(30.0, true)
[6] ASCOM driver sends USB command to camera hardware
[7] Camera firmware processes command and begins photon integration
    โ†’ t_actual_start (unknown to software)
[8] Camera driver notes start time: t_driver = DateTime.UtcNow
    โ†’ This is what appears in LastExposureStartTime

The total latency from [1] to [7] is:

Component Typical latency Variability
HTTP round-trip (localhost) 1โ€“5 ms ยฑ2 ms
Alpaca server processing 1โ€“3 ms ยฑ1 ms
ASCOM COM interop 0.5โ€“2 ms ยฑ1 ms
Driver โ†’ USB โ†’ camera 2โ€“15 ms ยฑ5 ms
Total 5โ€“25 ms ยฑ10 ms (1ฯƒ)

If the software timestamps the exposure at step [1] (the POST request), then DATE-OBS is early by 5โ€“25 ms relative to the actual exposure start. If it timestamps at step [8] (the driver's reading), the offset is smaller (2โ€“15 ms from USB latency only) but still present.

3.2 Over a network (not localhost)

If the Alpaca device is on a different machine (e.g., a Raspberry Pi controlling the camera, with the scheduler on a separate PC):

Component Typical latency Variability
HTTP round-trip (LAN) 5โ€“50 ms ยฑ10 ms
HTTP round-trip (WAN) 50โ€“200 ms ยฑ50 ms

Recommendation: Always run the Alpaca server on the same machine as the camera driver. Never initiate exposures over WAN โ€” the client software must be local.

3.3 Impact on Transit Mid-Time

For a transit with: - Total duration: T_dur = 2 hours - Ingress/egress duration: ฯ„_ing = 20 minutes - Exposure cadence: ฮ”t = 1 minute (60 s exposures) - Number of in-transit points: N_transit = 120 - Number of ingress/egress points: N_ing = 20 per limb

The transit mid-time uncertainty from the model fit is dominated by photometric noise, not timestamp noise. But we can derive the contribution of timestamp jitter to ฯƒ(T_mid):

Each timestamp has an independent random error of ฯƒ_ts = 10โ€“50 ms. The transit model fit uses all N points, and the mid-time is determined primarily by the ingress and egress shapes. The timestamp jitter propagates to the mid-time as:

$$\sigma_{T_\text{mid, timestamp}} \approx \frac{\sigma_\text{ts}}{\sqrt{N_\text{ing}}}$$

For ฯƒ_ts = 50 ms and N_ing = 20 points in each ingress:

$$\sigma_{T_\text{mid, timestamp}} \approx \frac{50\,\text{ms}}{\sqrt{20}} \approx 11\,\text{ms}$$

This 11 ms contribution is completely negligible compared to the photometric noise contribution of 30โ€“120 seconds (from [[Timing Precision Budget]] Section 3).

Quantitative conclusion: For transit timing (TTV science), ASCOM Alpaca timestamp jitter contributes ~11 ms to the fitted mid-time โ€” a factor of 3000ร— below the photometric noise floor. It is irrelevant.

For occultation timing, the 10โ€“50 ms jitter from ASCOM Alpaca is significant and comparable to the science requirement. This is why occultation observers use hardware triggers or video time inserters rather than software-initiated exposures.


4. BJD_TDB Conversion โ€” The Dominant Systematic

4.1 The Two Components

Converting a topocentric UTC timestamp to Barycentric Julian Date in Barycentric Dynamical Time (BJD_TDB) involves two distinct corrections:

Component 1: UTC โ†’ TDB (time standard conversion)

The chain of time standards:

UTC โ†’ TAI โ†’ TT โ†’ TDB
  • UTC โ†’ TAI: add current leap seconds. As of 2026, TAI = UTC + 37 s.
  • TAI โ†’ TT (Terrestrial Time): TT = TAI + 32.184 s (fixed by definition).
  • TT โ†’ TDB (Barycentric Dynamical Time): TDB differs from TT by a small periodic term (amplitude ~1.7 ms, period 1 year) due to relativistic time dilation from Earth's orbital motion. For most purposes TDB โ‰ˆ TT to within 2 ms.

Total UTC โ†’ TDB offset: 37 + 32.184 = 69.184 s (as of 2026). This is a known, deterministic correction โ€” not an uncertainty. It changes only when a new leap second is announced (last one was December 2016).

Uncertainty: < 1 ยตs. The UTC-TAI offset is defined by the IERS. The TT-TAI offset is exact by definition. The TDB-TT correction is computed to sub-microsecond accuracy from solar system ephemerides (JPL DE440).

Component 2: Barycentric correction (light travel time)

Light from a distant star arrives at the Earth at a time that depends on the Earth's position relative to the solar system barycentre (SSB). The correction is:

$$\Delta t_\text{bary} = -\frac{\vec{r}_\text{obs} \cdot \hat{n}}{c}$$

where $\vec{r}_\text{obs}$ is the vector from the SSB to the observer and $\hat{n}$ is the unit vector toward the target star.

  • Maximum amplitude: The Earth-Sun distance is ~1 AU = 499 light-seconds. The projection $\vec{r}_\text{obs} \cdot \hat{n}$ has maximum magnitude ~499 s โ‰ˆ 8.3 minutes. The actual correction varies sinusoidally over the year as the Earth orbits.
  • Uncertainty: ~1 ms, dominated by the observer's position accuracy (Earth ephemeris from DE440 is accurate to ~1 m, which is ~3 ns in light travel time; the dominant uncertainty is the observer's geodetic coordinates โ€” a 300 m error in position contributes ~1 ยตs).

4.2 Critical Error Mode 1: HJD vs BJD

  • HJD (Heliocentric Julian Date): Corrects light travel time to the centre of the Sun.
  • BJD (Barycentric Julian Date): Corrects light travel time to the solar system barycentre (SSB).

The SSB is offset from the Sun's centre by up to ~1.5 solar radii (~0.01 AU), primarily due to Jupiter's gravitational influence. This offset projects to a maximum timing difference of:

$$\Delta t_\text{HJD-BJD} \leq \frac{0.01\,\text{AU}}{c} \approx 5\,\text{s}$$

Typical values are 0โ€“4 seconds, varying with the relative geometry of Jupiter and the target.

Why this matters: A 4-second systematic error is detectable in TTV analysis (where TTV amplitudes are 1โ€“10 minutes and measurement precision is 30โ€“120 s). If one observer reports HJD and another reports BJD, the combined dataset will show a spurious ~4 s offset that could be misinterpreted as a TTV signal or corrupt a period determination.

OpenAstro policy: All times MUST be reported in BJD_TDB. HJD is never acceptable. The pipeline must validate this.

4.3 Critical Error Mode 2: JD_UTC vs JD_TDB

If a timestamp is recorded in UTC but stored or reported as if it were TDB (or vice versa) without applying the 69.184-second correction:

  • The error is up to 69.184 seconds โ€” a fixed systematic.
  • For TTV science with amplitudes of 1โ€“10 minutes, a 69 s error completely swamps the signal.
  • For occultation work, 69 s ร— 15 km/s = 1037 km position error โ€” rendering the chord useless.
  • This error is insidious because it is constant: it does not produce scatter in the data, just a systematic offset. If all observations from one site have this error, the O-C diagram will show a constant offset that could be absorbed into the fitted ephemeris, corrupting the period.

OpenAstro policy: The observation metadata MUST explicitly state the time system (TIMESYS = 'UTC' in the FITS header for raw data, BJD_TDB for processed times). The pipeline must apply the UTC โ†’ TDB conversion and log that it was applied.

4.4 Python Code: Correct UTC to BJD_TDB Conversion

The following code uses astropy.time and astropy.coordinates to correctly convert an observation timestamp to BJD_TDB. This is the reference implementation for the OpenAstro pipeline.

"""
Convert DATE-OBS (UTC) to BJD_TDB for a given observer location and target.

Requirements: astropy >= 5.0
Reference: Eastman, Siverd & Gaudi (2010), PASP 122, 935

This code computes:
    BJD_TDB = JD_TDB(mid-exposure) + light_travel_time_to_barycentre
"""

from astropy.time import Time, TimeDelta
from astropy.coordinates import SkyCoord, EarthLocation
import astropy.units as u


def utc_to_bjd_tdb(
    date_obs: str,
    exptime: float,
    ra_deg: float,
    dec_deg: float,
    lat_deg: float,
    lon_deg: float,
    elevation_m: float,
) -> tuple[float, float]:
    """
    Convert a DATE-OBS (ISO 8601 UTC) to BJD_TDB at exposure mid-point.

    Parameters
    ----------
    date_obs : str
        UTC timestamp of exposure start, ISO 8601 format.
        Example: '2026-06-15T02:34:17.456'
    exptime : float
        Exposure duration in seconds.
    ra_deg : float
        Target Right Ascension in degrees (J2000 / ICRS).
    dec_deg : float
        Target Declination in degrees (J2000 / ICRS).
    lat_deg : float
        Observer geodetic latitude in degrees (north positive).
    lon_deg : float
        Observer geodetic longitude in degrees (east positive).
    elevation_m : float
        Observer elevation above WGS84 ellipsoid in metres.

    Returns
    -------
    bjd_tdb : float
        Barycentric Julian Date in TDB at exposure mid-point.
    ltt_seconds : float
        The applied light travel time correction in seconds
        (for diagnostic logging).
    """
    # Step 1: Parse the UTC timestamp of exposure start
    t_start_utc = Time(date_obs, format='isot', scale='utc')

    # Step 2: Compute exposure mid-point in UTC
    dt_half = TimeDelta(exptime / 2.0, format='sec')
    t_mid_utc = t_start_utc + dt_half

    # Step 3: Convert UTC mid-point to TDB
    # This applies the UTC โ†’ TAI โ†’ TT โ†’ TDB chain automatically.
    # astropy handles leap seconds internally from its built-in tables.
    t_mid_tdb = t_mid_utc.tdb

    # Step 4: Define observer location and target coordinates
    observer = EarthLocation.from_geodetic(
        lon=lon_deg * u.deg,
        lat=lat_deg * u.deg,
        height=elevation_m * u.m,
    )
    target = SkyCoord(ra=ra_deg * u.deg, dec=dec_deg * u.deg, frame='icrs')

    # Step 5: Compute the light travel time from observer to SSB
    # This is the barycentric correction: the time it takes light to
    # travel from the solar system barycentre to the observer, projected
    # along the direction to the target.
    # kind='barycentric' gives BJD; kind='heliocentric' would give HJD.
    ltt = t_mid_tdb.light_travel_time(
        target,
        kind='barycentric',
        location=observer,
        ephemeris='builtin',  # uses JPL DE440 or DE432s
    )

    # Step 6: Apply the correction
    # BJD_TDB = JD_TDB(mid-exposure) + light_travel_time
    bjd_tdb = t_mid_tdb + ltt

    return bjd_tdb.jd, ltt.to(u.s).value


# โ”€โ”€ Example usage โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
if __name__ == '__main__':
    # Example: observing WASP-12 b transit from a site in Arizona
    bjd, ltt = utc_to_bjd_tdb(
        date_obs='2026-06-15T02:34:17.456',
        exptime=60.0,
        ra_deg=97.6364,       # WASP-12, RA = 06h30m32.8s
        dec_deg=29.6723,      # WASP-12, Dec = +29d40m20s
        lat_deg=32.2217,      # Tucson, AZ
        lon_deg=-110.9265,
        elevation_m=728.0,
    )
    print(f"BJD_TDB = {bjd:.8f}")
    print(f"Light travel time correction = {ltt:.3f} s")
    print(f"Light travel time correction = {ltt / 60:.4f} min")

Validation: Cross-check the output against the Ohio State BJD calculator (https://astroutils.astronomy.osu.edu/time/utc2bjd.html) or the barycorrpy package. Agreement should be within 1 ms.

Important implementation notes:

  1. The ephemeris='builtin' parameter uses astropy's bundled JPL ephemeris (DE432s). For higher accuracy (<1 ms), download and use DE440: ephemeris='jpl' with astropy.coordinates.solar_system_ephemeris.set('de440').

  2. astropy handles leap seconds automatically via its built-in IERS tables. Ensure the tables are up to date: from astropy.utils.iers import IERS_Auto; IERS_Auto.open() or set astropy.utils.iers.conf.auto_download = True.

  3. The observer elevation matters: a 1 km elevation error changes the barycentric correction by ~3 ยตs. GPS-derived elevation (typically ยฑ10 m) is adequate.

  4. The light_travel_time() method returns a positive value when the barycentre is closer to the target than the observer (light arrives at the observer after the barycentre), and negative when the barycentre is farther.


5. Exposure Mid-Point

5.1 Standard Science Camera Exposures

For a CCD or CMOS camera taking a single exposure of duration $t_\text{exp}$:

$$T_\text{mid} = T_\text{start} + \frac{t_\text{exp}}{2}$$

where $T_\text{start}$ is the moment the detector begins integrating photons (not the moment the software command was sent).

The uncertainty in $T_\text{mid}$ from the exposure duration:

$$\sigma_{T_\text{mid}} = \frac{\sigma_{t_\text{exp}}}{2}$$

For cameras that report exposure duration to 1 ms precision: $\sigma_{T_\text{mid}} = 0.5$ ms โ€” negligible.

For cameras that report exposure duration to 1 s precision (rare, but some older CCDs): $\sigma_{T_\text{mid}} = 0.5$ s. This is significant for occultation work but still negligible for TTV.

5.2 Fast-Cadence Video for Occultations

For a video camera running at frame rate $f_r$ with frame interval $\Delta t_\text{frame} = 1/f_r$:

$$T_\text{mid,frame} = T_\text{frame_start} + \frac{\Delta t_\text{frame}}{2}$$

If the camera has a dead time $t_\text{dead}$ between frames (readout time):

$$T_\text{mid,frame} = T_\text{frame_start} + \frac{t_\text{exp}}{2}$$

where $t_\text{exp} = \Delta t_\text{frame} - t_\text{dead}$.

5.3 Edge Case: Partial-Frame Occultation Events

Consider an occultation where the star disappears at time $t_D$ during a single exposure that runs from $T_\text{start}$ to $T_\text{start} + t_\text{exp}$. The exposure captures flux from two intervals:

  • Star visible: $T_\text{start}$ to $t_D$ โ€” duration $(t_D - T_\text{start})$, flux $F_\star + F_\text{sky}$
  • Star occulted: $t_D$ to $T_\text{start} + t_\text{exp}$ โ€” duration $(T_\text{start} + t_\text{exp} - t_D)$, flux $F_\text{sky}$ only

The measured total flux in the aperture is:

$$F_\text{measured} = F_\star \cdot (t_D - T_\text{start}) + F_\text{sky} \cdot t_\text{exp}$$

The photon-weighted temporal mid-point (the effective time of the measurement for the stellar flux) is:

For the unocculted portion (where the star contributes flux):

$$T_\text{photon-weighted} = \frac{\int_{T_\text{start}}^{t_D} t \cdot F_\star \, dt}{\int_{T_\text{start}}^{t_D} F_\star \, dt} = \frac{T_\text{start} + t_D}{2}$$

This is simply the midpoint of the unocculted interval โ€” not the geometric midpoint of the full exposure.

The geometric midpoint of the full exposure is $T_\text{start} + t_\text{exp}/2$. The difference is:

$$\Delta T = \left(\frac{T_\text{start} + t_D}{2}\right) - \left(T_\text{start} + \frac{t_\text{exp}}{2}\right) = \frac{t_D - T_\text{start}}{2} - \frac{t_\text{exp}}{2} = -\frac{T_\text{start} + t_\text{exp} - t_D}{2}$$

If the disappearance occurs at the midpoint of the exposure ($t_D = T_\text{start} + t_\text{exp}/2$), then $\Delta T = -t_\text{exp}/4$. The photon-weighted midpoint is $t_\text{exp}/4$ before the geometric midpoint.

For occultation fitting: The correct approach is to fit a step-function model to the light curve, where each frame's expected flux is integrated over the exposure duration. This naturally handles the partial-frame case without needing to compute photon-weighted midpoints manually. The edge time $t_D$ is a free parameter in the fit. Software such as PyOTE and Occult4 implements this correctly.

For reappearance events: The same analysis applies with the unocculted portion being $[t_R, T_\text{start} + t_\text{exp}]$ instead of $[T_\text{start}, t_D]$.


6. Complete Error Budget Table

All values assume a typical OpenAstro amateur setup: 20โ€“30 cm telescope, CMOS camera (ZWO ASI or similar), ASCOM Alpaca control, Linux or Windows PC.

6.1 Transit Timing (TTV Science)

Pipeline step Typical offset Uncertainty (1ฯƒ) Systematic or random? Mitigation
OS clock โ€” NTP (broadband) 0 (mean) 20 ms Random Use chrony on Ethernet
OS clock โ€” GPS-PPS 0 (mean) 0.01 ms Random Recommended for occultations only
ASCOM Alpaca shutter command latency +10 ms (late) ยฑ10 ms Quasi-random Use camera driver timestamp, not HTTP request time
Camera USB trigger jitter +5 ms (late) ยฑ5 ms Random Hardware trigger (not needed for TTV)
DATE-OBS format ambiguity 0 if correct; up to t_exp if wrong 0 if validated Systematic if wrong Pipeline validates DATE-OBS = exposure start
Exposure mid-point calculation +t_exp/2 (deterministic) <1 ms Deterministic Apply T_mid = T_start + t_exp/2
UTC โ†’ TDB offset +69.184 s (deterministic) <0.001 ms Systematic (known) Apply astropy conversion โ€” mandatory
HJD vs BJD confusion 0 if BJD used; up to 4 s if HJD 0 if correct Systematic if wrong Enforce BJD_TDB in all submissions
Barycentric light travel time up to ยฑ499 s (deterministic) ~1 ms Systematic (known) Apply with astropy.time.light_travel_time()
Clock drift during observation (NTP) 0 (mean, if NTP active) 5โ€“15 ms per 5-min poll Random Monitor with chronyc tracking
Photometric noise on T_mid 0 (mean) 30โ€“120 s Random Dominant term. More data, better SNR, deeper transits
Detrending systematics 0 (mean) 10โ€“60 s Quasi-systematic Careful baseline selection, GP detrending
Total random (NTP setup) โ€” ~30โ€“120 s โ€” Photometry-dominated; clock is negligible
Total random (GPS-PPS setup) โ€” ~30โ€“120 s โ€” Same: photometry-dominated

Key insight for TTV: The hardware clock uncertainty (~20 ms with NTP) is 1500ร— smaller than the photometric noise floor (~30 s). GPS-PPS provides no benefit for transit timing. Invest in aperture and comparison star selection, not clocks.

6.2 Occultation Timing (Main-Belt Asteroid, 25 fps Camera)

Pipeline step GPS-PPS uncertainty (1ฯƒ) NTP-only uncertainty (1ฯƒ) Systematic or random? Mitigation
OS clock 0.01 ms 20โ€“50 ms Random GPS-PPS mandatory
Camera trigger jitter 1 ms (hw trigger) or 10 ms (sw) 10 ms Random Hardware trigger if available
Frame timing ($t_\text{exp}/2\sqrt{3}$ at 25 fps) 11.5 ms 11.5 ms Random Higher frame rate
DATE-OBS / frame timestamp <1 ms (GPS-stamped video) <1 ms (if camera reports correctly) Systematic if wrong Use VTI or validated driver
UTC โ†’ TDB <0.001 ms <0.001 ms Known Apply correction
Barycentric correction ~1 ms ~1 ms Known Apply with astropy
Light curve edge fit 2โ€“10 ms (SNR-dependent) 2โ€“10 ms Random Brighter stars, larger aperture
Fresnel diffraction floor (main-belt) ~14 ms (physical) ~14 ms (physical) Physical limit Cannot reduce
Total (quadrature, GPS-PPS, hw trigger) ~19 ms โ€” Mix
Total (quadrature, GPS-PPS, sw trigger) ~21 ms โ€” Mix
Total (quadrature, NTP-only) โ€” ~35โ€“60 ms Mix Marginal for main-belt

6.3 Occultation Timing (KBO/TNO, 8 fps Camera)

Pipeline step GPS-PPS uncertainty (1ฯƒ) NTP-only uncertainty (1ฯƒ)
OS clock 0.01 ms 20โ€“50 ms
Camera trigger jitter 1โ€“10 ms 10 ms
Frame timing ($t_\text{exp}/2\sqrt{3}$ at 8 fps) 36 ms 36 ms
Light curve edge fit 5โ€“20 ms 5โ€“20 ms
Fresnel floor (KBO at 40 AU) ~120 ms ~120 ms
Total (quadrature) ~127 ms ~133 ms

Key insight for KBO occultations: The Fresnel diffraction floor dominates. NTP is nearly as good as GPS-PPS for this case because the physical limit (~120 ms) dwarfs the clock uncertainty. GPS-PPS is still recommended for the absolute timing needed for orbit refinement.


7. Science Case Requirements vs Achieved Precision

Summary Table

Science case Required ฯƒ_t Achieved with NTP Achieved with GPS-PPS NTP sufficient? Notes
Stellar occultations (main-belt) โ‰ค30 ms 35โ€“60 ms ~19 ms No GPS-PPS required; frame rate is the secondary bottleneck
Stellar occultations (KBO/TNO) โ‰ค100 ms ~133 ms ~127 ms Marginal Fresnel floor dominates; GPS-PPS helps with absolute timing for orbit refinement
TTV transit mid-times โ‰ค30 s ~30โ€“120 s (photometry limited) ~30โ€“120 s Yes Clock is negligible; invest in aperture and cadence
GRB optical follow-up (classification) โ‰ค60 s ~50 ms (clock) + response time ~0.01 ms (clock) + response time Yes The bottleneck is slew/response time (~30โ€“120 s), not the clock
GRB afterglow light curve โ‰ค1 s ~50 ms ~0.01 ms Yes NTP is 20ร— better than requirement
Variable star period measurement โ‰ค10 s ~50 ms ~0.01 ms Yes NTP is 200ร— better than requirement
Eclipsing binary O-C โ‰ค1โ€“10 s ~50 ms ~0.01 ms Yes NTP is adequate
FRB optical counterpart โ‰ค1โ€“10 ms 20โ€“50 ms ~1 ms (with hw trigger) No GPS-PPS + hardware trigger + fast camera mandatory

Detailed Assessment

Stellar occultations (main-belt): - Requirement: โ‰ค30 ms for sub-km chord precision at $v_s$ = 15 km/s (30 ms ร— 15 km/s = 450 m) - NTP-only delivers 35โ€“60 ms โ†’ 525โ€“900 m limb uncertainty โ†’ insufficient for shape modelling - GPS-PPS delivers ~19 ms โ†’ 285 m limb uncertainty โ†’ meets requirement - Verdict: GPS-PPS required. NTP fails.

TTV transit mid-times: - Requirement: โ‰ค30 s for detecting TTV amplitudes of 1โ€“10 minutes - Both NTP and GPS-PPS deliver 30โ€“120 s, dominated entirely by photometric noise - The 20 ms NTP clock error is 0.07% of the 30 s requirement - Verdict: NTP is overwhelmingly sufficient. GPS-PPS provides zero benefit.

GRB optical follow-up: - Requirement: โ‰ค60 s for classification (report detection to GCN within minutes) - The bottleneck is never the clock โ€” it is the alert latency (GCN propagation: 1โ€“30 s), telescope slew time (30โ€“120 s), and first-image readout - Verdict: NTP is sufficient. The clock is irrelevant compared to mechanical response time.

FRB optical counterpart: - Requirement: โ‰ค1โ€“10 ms to match with radio trigger timestamp - NTP delivers 20โ€“50 ms โ†’ timing mismatch too large for confident association - GPS-PPS with hardware trigger delivers ~1 ms โ†’ sufficient for association - This is the most demanding timing case in the OpenAstro science portfolio - Verdict: GPS-PPS with hardware trigger and fast camera (โ‰ฅ100 fps) required.


8. Validation Recipe

8.1 End-to-End Pipeline Verification

Method: Observe a well-characterised eclipsing binary with a published mid-eclipse time in BJD_TDB, process through the full OpenAstro pipeline, and compare the output mid-eclipse time with the published value.

Recommended target: CM Draconis (CM Dra)

  • Type: Detached eclipsing binary (dM4.5 + dM4.5)
  • V magnitude: 12.9
  • Period: 1.268389985 days (extremely well determined)
  • Eclipse depth: ~0.45 mag (primary) and ~0.40 mag (secondary) โ€” deep, easy to detect
  • Eclipse duration: ~53 minutes โ€” well-suited to amateur apertures with 1-min cadence
  • Ingress/egress: ~4 minutes
  • Published ephemeris: Deeg et al. (2008), A&A 480, 563, and updated by Latham et al.
  • Mid-eclipse times have been measured to ยฑ3โ€“10 s precision by multiple professional and amateur groups
  • The O-C diagram is well-characterized, with no significant TTVs larger than ~20 s

Why CM Dra: 1. Deep eclipses โ€” high SNR even with small apertures 2. Short period โ€” observable every 1.27 days, frequent opportunities 3. Well-published BJD_TDB ephemeris โ€” direct comparison possible 4. Extensively observed by ETD (Exoplanet Transit Database) and AAVSO โ€” independent cross-checks available 5. V = 12.9 โ€” reachable with 20 cm aperture at โ‰ค5 mmag precision with 60 s exposures

Alternative targets: - NSVS 01031772 (V = 11.6, P = 0.368 d, depth 0.6 mag) โ€” very deep, very short period - V1143 Cyg (V = 5.9, P = 7.64 d) โ€” bright, but long period limits observation opportunities

8.2 Procedure

  1. Observe: Capture a full primary eclipse of CM Dra with โ‰ฅ1-min cadence, V or R filter, using the standard OpenAstro client and ASCOM Alpaca control.

  2. Record metadata: Ensure FITS headers contain DATE-OBS (UTC), EXPTIME, TIMESYS='UTC', site coordinates.

  3. Run pipeline: Process through the OpenAstro pipeline:

  4. Extract differential photometry (target minus comparison star)
  5. Convert timestamps to BJD_TDB using the code in Section 4.4
  6. Fit a transit/eclipse model (Mandel & Agol 2002, or simple trapezoidal model) to determine the mid-eclipse time $T_\text{mid}$ and its uncertainty $\sigma_{T_\text{mid}}$

  7. Compare with published ephemeris:

  8. Compute the predicted mid-eclipse time from the published ephemeris: $T_\text{predicted} = T_0 + N \times P$ where $N$ is the cycle number
  9. Compute $\text{O-C} = T_\text{measured} - T_\text{predicted}$

  10. Expected agreement:

  11. The O-C value should be consistent with zero (within the CM Dra's known O-C scatter of ~10โ€“20 s)
  12. Specifically: $|\text{O-C}| < 3\sigma_{T_\text{mid}}$ where $\sigma_{T_\text{mid}}$ is the pipeline's reported uncertainty
  13. For a 20 cm telescope with 1-min cadence and 5 mmag photometric precision: $\sigma_{T_\text{mid}} \approx 30$โ€“60 s
  14. Therefore: the pipeline output should agree with the published value to within ~2 minutes at 3ฯƒ

  15. Failure modes this test catches:

  16. UTC vs TDB confusion: would produce O-C โ‰ˆ ยฑ69 s (clearly detectable at 1โ€“2ฯƒ for a single eclipse, unambiguously detectable from 2+ eclipses)
  17. HJD vs BJD confusion: would produce O-C โ‰ˆ 0โ€“4 s (not detectable from a single eclipse at 30 s precision, but detectable from observations at different times of year when the HJD-BJD difference changes sign)
  18. DATE-OBS = exposure end instead of start: would produce O-C โ‰ˆ t_exp/2 = 30 s (detectable if t_exp is large)
  19. Barycentric correction not applied: would produce O-C up to ยฑ499 s (immediately obvious)
  20. Sign error in barycentric correction: would produce O-C up to ยฑ998 s (immediately obvious)

8.3 Ongoing Monitoring

Once validated, the pipeline should be periodically re-tested:

  • Leap second updates: When a new leap second is announced, verify that the UTC-TAI offset in astropy is updated. Outdated leap second tables produce a 1 s systematic.
  • IERS table freshness: astropy's Earth orientation parameters (for precise barycentric correction) need periodic updates. Set astropy.utils.iers.conf.auto_download = True or manually update.
  • Cross-observer consistency: When multiple OpenAstro sites observe the same transit, their fitted mid-times should agree to within their individual uncertainties. A systematic offset between sites indicates a calibration problem at one site.

9. Summary of Recommendations for OpenAstro

For the MVP (Phase 1)

  1. Require NTP synchronization at all sites. Validate that TIMESYS = 'UTC' and that the NTP offset is < 1 s. Flag observations from unsynchronized clocks.

  2. Implement the BJD_TDB conversion (Section 4.4 code) in the server-side pipeline. Never store or analyse times in JD_UTC or HJD. The conversion must be applied server-side, not left to individual observers.

  3. Record timing metadata in the observation record: TIMESRC (NTP or GPS-PPS), NTPOFF (NTP offset at observation time), EXPTIME, DATE-OBS.

  4. Apply exposure mid-point correction automatically: $T_\text{mid} = T_\text{start} + t_\text{exp}/2$.

  5. Validate with CM Dra or equivalent eclipsing binary before declaring the pipeline operational.

For Occultation Campaigns

  1. Require GPS-PPS for main-belt asteroid occultation observers. Provide a recommended hardware list (Section 2.2) and setup guide.

  2. Encourage 25+ fps frame rates with high-sensitivity CMOS cameras (IMX462, IMX585).

  3. Support IOTA VTI timestamps as an alternative to OS-clock timestamps for video-rate observations.

For FRB Optical Counterpart Work

  1. Require GPS-PPS + hardware trigger + โ‰ฅ100 fps camera. This is the only OpenAstro science case that demands sub-millisecond timing.

  2. This capability is aspirational for the MVP โ€” it requires specialized hardware that most amateurs do not have. Defer to Phase 2 or later.


10. References

  • Mills, D.L. (2010), RFC 5905 โ€” NTPv4 specification
  • Eastman, Siverd & Gaudi (2010), PASP, 122, 935 โ€” "Achieving Better Than 1 Minute Accuracy in the Heliocentric and Barycentric Julian Dates" (essential reference for BJD_TDB conversion)
  • Eastman, J. (2010), Ohio State BJD Calculator โ€” http://astroutils.astronomy.osu.edu/time/utc2bjd.html
  • IOTA (2020), Video Timing Standards for Occultation Observers
  • Herald et al. (2020), MNRAS, 499, 4570 โ€” timing methods for stellar occultations
  • Deeg et al. (2008), A&A, 480, 563 โ€” CM Draconis eclipsing binary ephemeris
  • Mandel & Agol (2002), ApJ, 580, L171 โ€” analytic transit light curve models
  • Kipping, D.M. (2010), MNRAS, 408, 1758 โ€” binning and timing in transit photometry
  • Wright & Eastman (2014), PASP, 126, 838 โ€” barycentric corrections at 1 cm/s level
  • ASCOM Standards Committee, ASCOM Alpaca API Reference โ€” https://ascom-standards.org/api/
  • chrony documentation โ€” https://chrony-project.org/documentation.html
  • Meinberg NTP documentation โ€” https://www.meinbergglobal.com/english/info/ntp.htm