Skip to content

Study

lt.temporal.study(*, site, event, products, archive=None, box=...) returns a frozen Study. It builds the Lunar ODE query, resolves the event to an event basis, obtains the source pages and compiles the record with the unchanged v1 method.

ArgumentAcceptsRule
site(lat, lon) or a registered name such as "vikram"The query box is the centre plus or minus box, rounded to 4 decimal places.
event"START/END" or a registered basis idSTART and END are ISO UTC times ending in Z. END is exclusive.
products"IHID/IID/PT"Exactly three non-empty parts.
archiveNone, a directory, or a packaged snapshot nameNone fetches live. Anything else compiles offline.
box(dlat, dlon)Half-widths in degrees. Default (0.005, 0.015).

Coordinates are planetocentric, with east-positive longitude.

A tuple and its registered name give the same query box. For Vikram the box is latitude -69.3791 to -69.3691 and longitude 32.305 to 32.335.

>>> import lunartrace as lt
>>> call = dict(event="2023-08-23T12:33Z/12:35Z", products="LRO/LROC/CDRNAC4",
... archive="vikram-2026-10-14")
>>> by_tuple = lt.temporal.study(site=(-69.3741, 32.32), **call)
>>> by_name = lt.temporal.study(site="vikram", **call)
>>> by_tuple.digest == by_name.digest
True
>>> q = by_tuple.record["source"]["query"]
>>> (q["min_lat"], q["max_lat"], q["west_lon"], q["east_lon"])
(-69.3791, -69.3691, 32.305, 32.335)

The interval is half-open. END is not part of it, so 12:33Z/12:35Z is stored with latest = 12:34:59.999999Z. The end may be just a clock time on the start date.

When the interval equals an event basis registered for the site, the study uses that basis with its rationale, which cites the anchors (research/studies/vikram/ANCHORS.md). Vikram has four:

IntervalBasis id
2023-08-23T00:00Z/2023-08-24T00:00Zvikram:utc-day
2023-08-23T12:33Z/12:35Zvikram:minute-envelope
2023-08-23T12:33Z/12:34Zvikram:nasa-dsn-minute
2023-08-23T12:34Z/12:35Zvikram:isro-minute

You can pass the basis id as event instead of the interval. Any other interval is recorded for what it is, an interval you supplied:

>>> import lunartrace as lt
>>> call = dict(site="vikram", products="LRO/LROC/CDRNAC4", archive="vikram-2026-10-14")
>>> by_id = lt.temporal.study(event="vikram:minute-envelope", **call)
>>> by_interval = lt.temporal.study(event="2023-08-23T12:33Z/12:35Z", **call)
>>> by_id.digest == by_interval.digest
True
>>> adhoc = lt.temporal.study(event="2023-08-23T12:00Z/13:00Z", **call)
>>> adhoc.record["event_basis"]["basis_id"]
'adhoc:2023-08-23T12:00Z/13:00Z'
>>> adhoc.record["event_basis"]["rationale"]
'Interval supplied by the caller.'

study.source_mode is "live" or "retained", and repr(study) prints it.

archiveSourceNetwork
NoneLunar ODE now; the pages are kept in memory and can be savedyes
a directorythe retained pages in itno
"vikram-2026-10-14"the snapshot packaged with lunartraceno

A retained archive is checked before it is used: every page must match the SHA-256 in its receipt, and the receipt’s query must match the query you asked for. A live study is a new observation of a changing archive. It is never compared with the 14 October numbers.

import lunartrace as lt
live = lt.temporal.study(site=(-69.3741, 32.32), # no archive: fetches Lunar ODE now
event="2023-08-23T12:33Z/12:35Z",
products="LRO/LROC/CDRNAC4")
live.source_mode # "live"
live.save("live.study.json", sources=True)

This is the only call on these pages that uses the network, and it asks the archive about the present. The Vikram snapshot is the one to use when you want the numbers of 14 October 2026: lt.temporal.packaged_archives() lists the snapshot names that ship with the package.

Every refusal is a ValueError that says what was wrong.

>>> import lunartrace as lt
>>> lt.temporal.study(site="vikram", event="2023-08-23T12:35Z/12:33Z",
... products="LRO/LROC/CDRNAC4", archive="vikram-2026-10-14")
Traceback (most recent call last):
...
ValueError: event interval must end after it starts, got '2023-08-23T12:35Z/12:33Z'
>>> lt.temporal.study(site="vikram", event="2023-08-23T12:33Z/12:35Z",
... products="LRO/LROC", archive="vikram-2026-10-14")
Traceback (most recent call last):
...
ValueError: products must be 'IHID/IID/PT' with three non-empty parts, got 'LRO/LROC'

Other refusals: a time without Z, a latitude outside -90 to 90, an unregistered site name, an archive page whose bytes changed, and an archive fetched for a different query.