Converting GPS Week Time to Adjusted Standard Time

TL;DR: Adjusted standard GPS time = GPS week × 604,800 + seconds of week − 1,000,000,000. Get the GPS week from the flight date (weeks count from 6 January 1980), watch for flights that cross Saturday/Sunday midnight UTC — seconds of week reset to zero there — then write the new times and set global encoding bit 0 so readers know the time type.

# Context and Motivation

This guide is part of Metadata and Header Sync. LAS files store a GPS timestamp per point, used to match points to the aircraft trajectory, separate flightlines, order returns and diagnose timing problems. Older files commonly store GPS week time: seconds since the start of the current GPS week, which resets every Sunday at 00:00 GPS time. That is compact but ambiguous: the same value occurs every week, and two flights a week apart look simultaneous. Adjusted standard GPS time is seconds since the GPS epoch minus one billion, a continuous scale that keeps values in a comfortable double-precision range. LAS 1.4 point formats 6–10 require it, and any work combining flights or matching trajectories needs it.

A sawtooth versus a straight line Two lines over three weeks of calendar time. GPS week time rises from 0 to 604,800 seconds and drops back to zero at the start of each week, a sawtooth. Adjusted standard time rises steadily without resets. Two flights one week apart have identical week times but different standard times. same week time, one week apart adjusted standard week time week 1987week 1988week 1989

# Prerequisites and Assumptions

  • LAS files with GPS time (point formats 1, 3–10) and global encoding bit 0 = 0, meaning week time.
  • The flight date, or better the flight’s start time in UTC or GPS time, from the flight log, the trajectory file or the acquisition report.
  • laspy 2.x or PDAL; Python’s datetime.

# Step-by-Step Implementation

# Step 1 — Confirm the time type

With laspy: las.header.global_encoding.gps_time_typeWEEK_TIME or STANDARD. Values between 0 and 604,800 also point to week time; values in the hundreds of millions to standard time.

# Step 2 — Compute the GPS week

Days since 1980-01-06 divided by 7, using the flight date. GPS time runs ahead of UTC by the accumulated leap seconds (18 s since 2017), which only matters for flights within seconds of the week boundary.

# Step 3 — Handle week rollover

If a flight crosses Sunday 00:00 GPS time, week times jump from ~604,800 back to ~0 mid-flight. Points with small week times after the jump belong to the next week; detect them by a large negative step in time order, or by comparing against the flight’s start time.

# Step 4 — Convert

standard = week × 604,800 + seconds_of_week − 1e9, adding one week to points after a rollover.

# Step 5 — Write and flag

Write new times, set the time type to standard (bit 0 = 1) and, for LAS 1.4 formats, keep the WKT bit (bit 4) set as well.

# Complete Working Example

python
"""Convert LAS GPS week time to adjusted standard GPS time, handling week rollover."""
from __future__ import annotations

from datetime import date

import laspy
import numpy as np

GPS_EPOCH = date(1980, 1, 6)
WEEK = 604_800


def gps_week(flight_date: date) -> int:
    return (flight_date - GPS_EPOCH).days // 7


def convert(src: str, dst: str, flight_date: date) -> None:
    las = laspy.read(src)
    if las.header.global_encoding.gps_time_type == laspy.header.GpsTimeType.STANDARD:
        print("already adjusted standard time; nothing to do")
        return
    sow = np.asarray(las.gps_time, dtype=np.float64)
    if sow.min() < 0 or sow.max() > WEEK:
        raise ValueError(f"values {sow.min():.0f}{sow.max():.0f} are not seconds of week")

    week = gps_week(flight_date)
    # A flight that crosses the week boundary has late points with small seconds-of-week.
    start = np.percentile(sow, 1)
    rolled = sow < start - WEEK / 2           # far below the flight's early times
    weeks = np.where(rolled, week + 1, week)
    std = weeks * WEEK + sow - 1e9

    las.gps_time = std
    las.header.global_encoding.gps_time_type = laspy.header.GpsTimeType.STANDARD
    las.write(dst)
    print(f"GPS week {week}; {rolled.sum():,} points after rollover; "
          f"new range {std.min():.1f}{std.max():.1f}")


if __name__ == "__main__":
    convert("legacy/line_1102.las", "fixed/line_1102.las", date(2018, 2, 10))

A quick sanity check of the arithmetic for one timestamp:

python
week = gps_week(date(2018, 2, 10))            # 1987
print(week, week * WEEK + 385_214.37 - 1e9)   # 1987  202,122,814.37
Flights that cross Sunday midnight Week time of points in acquisition order for a flight starting Saturday evening. Times climb toward 604,800 seconds, then drop to near zero at the week boundary and continue climbing. Points after the drop are assigned to the following GPS week before conversion; without that, they would appear a week earlier than the rest of the flight. 604,800 s week boundary week N week N + 1 points in acquisition order

# Key Parameter Table

Quantity Value Notes
GPS epoch 1980-01-06 00:00 GPS Week 0 begins
seconds per week 604,800 Week time range 0 to 604,800
adjustment 1,000,000,000 s Subtracted to keep values small
GPS − UTC 18 s since 2017 Leap seconds; matters only at the boundary
global encoding bit 0 1 = standard 0 = week time
global encoding bit 4 1 = WKT CRS Required for PDRF 6–10

# Verification

  • Range. Converted times for flights between 2012 and 2026 fall between roughly 0 and 460 million seconds. Values outside that range for recent data mean the wrong week.
  • Monotonic per line. Within a flightline, times sorted by acquisition should be monotonic after conversion, with no drop at the week boundary.
  • Trajectory match. If an SBET or trajectory file exists, the point times should fall inside its time range; convert the trajectory’s times the same way if it too is in week time.
python
out = laspy.read("fixed/line_1102.las")
t = np.sort(np.asarray(out.gps_time))
assert np.all(np.diff(t) >= 0) and t.min() > 0 and t.max() < 5e8

# Gotchas and Edge Cases

Wrong week from a local date. A flight on Saturday evening in a western time zone is already Sunday in GPS time. Use the flight’s UTC date and time near the boundary, not the local calendar date.

Files merged from different weeks. A file combining flights from several weeks in week time is not convertible without knowing which points came from which flight. PointSourceId usually identifies the flightline; convert per line with each line’s date.

Header says standard, values say week. Some writers set bit 0 without converting. Trust the values: 385,214 is a week time whatever the header claims.

Time zones and the boundary A timeline around Saturday night. A flight begins at 18:30 local time on Saturday in UTC minus 7. In UTC and GPS time that is already 01:30 on Sunday, the start of a new GPS week. Using the local calendar date would assign the flight to the previous week. local GPS GPS week boundary Sat 18:30 (UTC−7) Sun 01:30 GPS

PDAL route. PDAL can perform the same arithmetic with filters.assign ("GpsTime = GpsTime + 201737600" for week 1987, which is 1987 × 604,800 − 1,000,000,000) and write global_encoding with writers.las, which suits batch pipelines; the week-rollover logic is easier in Python.

# Frequently Asked Questions

What is adjusted standard GPS time?

It is the number of seconds since the GPS epoch of 6 January 1980, minus one billion. The subtraction keeps values small enough to store with full precision in a double, and the scale is continuous across weeks and years.

How do I find the GPS week for a flight?

Count the days from 6 January 1980 to the flight’s date in GPS time and divide by seven, discarding the remainder. Near the week boundary, use the flight’s UTC time rather than its local calendar date.

Why does LAS 1.4 require adjusted standard time?

Point formats 6 to 10 are defined with it so that timestamps are unambiguous across weeks and flights, which matters for trajectory matching, merging flights and multi-temporal work.

How do I tell which time type a file uses?

Check bit 0 of the header’s global encoding field, and confirm with the values: week times lie between 0 and 604,800, adjusted standard times for recent data lie in the hundreds of millions.