Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/vodf_schema/level1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@

from .eventlist import EventList
from .groups import IRFGroupingTable, ObservationGroupingTable
from .observation import Observation

__all__ = ["ObservationGroupingTable", "IRFGroupingTable", "EventList"]
__all__ = ["ObservationGroupingTable", "IRFGroupingTable", "EventList", "Observation"]
16 changes: 12 additions & 4 deletions src/vodf_schema/level1/eventlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,34 @@ class __header__(
EVENT_ID = Int64(
description="ID of this event, unique within an observation",
)

TIME = Double(
description="Event time of arrival, as an MET",
unit=u.s,
ucd="time",
reference=Ref.ogip_event_lists,
)
RA = Double(

RA_RECO = Double(
description="Reconstructed Right Ascension of event point-of-origin.",
unit=u.deg,
ucd="pos.eq.ra;stat.fit",
reference=Ref.ogip_event_lists,
)
DEC = Double(

DEC_RECO = Double(
description="Reconstructed Declination of event point-of-origin",
unit=u.deg,
ucd="pos.eq.dec;stat.fit",
reference=Ref.ogip_event_lists,
)
ENERGY = Double(
description="Reconstructed event energy",

# TODO: how to deal with non-energy-like energy proxies (like n_hits)
ENERGY_RECO = Double(
description=(
"Reconstructed event energy, or proxy for that value. "
"This value can be folded through the IRF to measure true energy"
),
unit=u.TeV,
ucd="phys.energy;stat.fit",
reference=Ref.ogip_event_lists,
Expand Down
35 changes: 35 additions & 0 deletions src/vodf_schema/level1/observation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
# Licensed un a 3-clause BSD-style license - see LICENSE

"""The minimum contents of a "Observation HDU".

- Headers: Observation, DataRelease, and ObservationCollection

- Bintable: a grouping table containing the links (URI) to the HDUs of that
observation to its associated EventList, Pointing, OnTime , IRF HDUs ... qThe
sub-HDUs in the group may or may not be included, and could be fetched
separately (e.g. if the URI points to a new file)
"""

from fits_schema import BinaryTableHeader, HeaderCard

from ..hdu import GroupingTable
from ..metadata import (
ObservationHeader,
SpatialReferenceHeader,
TemporalReferenceHeader,
VODFFormatHeader,
)


class Observation(GroupingTable):
"""Defines what HDUs link to a single observation."""

class __header__(
VODFFormatHeader,
BinaryTableHeader,
ObservationHeader,
SpatialReferenceHeader,
TemporalReferenceHeader,
):
EXTNAME = HeaderCard(allowed_values=["OBSERVATION"])
125 changes: 78 additions & 47 deletions src/vodf_schema/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .references import Ref
from .version import __version__ as vodf_version

URL = "https://PUT_VODF_DOCUMENTATION_URL_FOR_THIS_VERSION_HERE/"
URL = "https://vodf.readthedocs.org"

__all__ = [
"VODFFormatHeader",
Expand All @@ -35,13 +35,6 @@ class SpatialReferenceHeader(Header):
This version of VODF supports only Earth-centered locations.
"""

TREFPOS = HeaderCard(
description="Code for the spatial location at which the observation time is valid",
reference=Ref.fits_v4,
type_=str,
allowed_values=["TOPOCENTER"],
)

OBSGEO_B = HeaderCard(
keyword="OBSGEO-B",
description="the latitude of the observation, with North positive",
Expand All @@ -68,9 +61,36 @@ class SpatialReferenceHeader(Header):
)


# TODO: unify names of ground coordinate reference and sky coordinate reference.
class CoordinateSystemHeader(Header):
"""Coordinate system definition."""

EQUINOX = HeaderCard(
description="Coordinate epoch. Optional since implied by RADECSYS",
reference=Ref.fits_v4,
type_=float,
unit=u.yr,
allowed_values=2000.0,
required=False,
)
RADECSYS = HeaderCard(
description="Coordinate stellar reference frame",
reference=Ref.fits_v4,
type_=str,
allowed_values={"ICRS", "FK5"},
)


class TemporalReferenceHeader(Header):
"""Defines the reference time, to which all time columns in the HDU are relative."""

TREFPOS = HeaderCard(
description="Code for the spatial location at which the observation time is valid",
reference=Ref.fits_v4,
type_=str,
allowed_values=["TOPOCENTER", "RELOCATABLE"],
)

# TODO: do we really need to split MJDREF into (I/F) parts? Is that level of
# precision required?
MJDREFI = HeaderCard(
Expand Down Expand Up @@ -112,42 +132,36 @@ class TemporalReferenceHeader(Header):
type_=str,
allowed_values=["TT", "UTC", "UT1", "TAI", "GPS", "LOCAL"],
)
TREFPOS = HeaderCard(
description="spatial location at which the observation time is valid.",
reference=Ref.fits_v4,
type_=str,
allowed_values=[
"TOPOCENTER",
"GEOCENTER",
"BARYCENTER",
"RELOCATABLE",
"CUSTOM",
],
)
TIMEDEL = HeaderCard(
required=False,
description="time resolution in the units of TIMEUNIT, useful for binned time-series.",
type_=float,
)


# TODO: better name to avoid
class ObservationHeader(Header):
"""Describes an observation."""

OBS_ID = HeaderCard(ivoa_name="obs_id") # TODO: define better
OBS_ID = HeaderCard(
type_=str,
description="unique ID of this observation, within a Data Release.",
ucd="",
ivoa_name="ObsCore.obs_id",
)

DATE_OBS = HeaderCard(
"DATE-OBS",
DATE_BEG = HeaderCard(
"DATE-BEG",
type_=str,
description="Human-readable observation start date/time, in UTC and ISO format.",
description="Human-readable observation start date/time, in the TIMESYS scale and ISO format.",
examples=["2025-01-01 15:34:21"],
reference=Ref.fits_v4,
)

DATE_END = HeaderCard(
"DATE-END",
type_=str,
description="Human-readable observation stop date/time, in UTC and ISO format",
description="Human-readable observation stop date/time, in TIMESYS scale and ISO format",
examples=["2025-01-01 15:44:21"],
reference=Ref.fits_v4,
)
Expand All @@ -159,6 +173,7 @@ class ObservationHeader(Header):
reference=Ref.fits_v4,
ivoa_name="ObsCore.facility_name",
)

INSTRUME = HeaderCard(
type_=str,
required=False,
Expand All @@ -170,16 +185,49 @@ class ObservationHeader(Header):
TSTART = HeaderCard(
type_=float,
description="Precise start time of the observation in the units and scale defined by the temporal reference headers",
reference=Ref.ogip,
reference=Ref.fits_v4,
)
TSTOP = HeaderCard(
type_=float,
description="Precise stop time of the observation in the units and scale defined by the temporal reference headers",
reference=Ref.ogip,
reference=Ref.fits_v4,
)


class TemporalCoverageHeader(Header):
pass


class SpectralCoverageHeader(Header):
"""Spectral coverage of this observation."""

E_MIN = HeaderCard(
description="Estimated minimum energy of the observation, for findability purproses.",
unit="TeV",
ivoa_name="ObsCore.em_min",
)
E_MAX = HeaderCard(
description="Estimated maximum energy of the observation, for findability purproses.",
unit="TeV",
ivoa_name="ObsCore.em_max",
)

# TODO: Should we separate these exposure headers into another Header?

class SpatialCoverageHeader(Header):
"""Spatial Coverage."""

RA_OBS = HeaderCard(
description="Center position of the observation, or mean in time."
)
DEC_OBS = HeaderCard(
description="Center position of the observation, or mean in time."
)
FOV_OBS = HeaderCard()

pass


class ExposureHeader(Header):
ONTIME = HeaderCard(
description=(
"the total 'good' time (in seconds) on 'source'. If a 'Good Time Interval' (GTI) table is provided, "
Expand All @@ -188,7 +236,9 @@ class ObservationHeader(Header):
),
reference=Ref.heasarc_r11,
)

LIVETIME = HeaderCard(
required=False,
type_=float,
unit="s",
description=(
Expand Down Expand Up @@ -269,25 +319,6 @@ class FixityHeader(Header):
# Copied from GADF so far, need to update:


class CoordinateSystemHeader(Header):
"""Coordinate system definition."""

EQUINOX = HeaderCard(
description="Coordinate epoch. Optional since implied by RADECSYS",
reference=Ref.fits_v4,
type_=float,
unit=u.yr,
allowed_values=2000.0,
required=False,
)
RADECSYS = HeaderCard(
description="Coordinate stellar reference frame",
reference=Ref.fits_v4,
type_=str,
allowed_values={"ICRS", "FK5"},
)


class Object(Header):
"""Name and coordinates of observerd object, if any."""

Expand Down