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.
# 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_type — WEEK_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
"""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:
week = gps_week(date(2018, 2, 10)) # 1987
print(week, week * WEEK + 385_214.37 - 1e9) # 1987 202,122,814.37# 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.
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.
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.
# Related
- Metadata and Header Sync — keeping header and data consistent
- Upgrading LAS 1.2 Files to LAS 1.4 — where this conversion is required
- Repairing Stale LAS Header Bounds and Counts — the other common header repair
- Understanding LAS Point Data Record Formats — which formats carry GPS time
- Measuring Swath-to-Swath Relative Accuracy — work that depends on per-line identity