Every run needs three things: the event file, the mission’s orbit file (where the spacecraft was), and a position on the sky. Some missions need a fourth, a spacecraft clock file. This page says where each of those comes from for each mission, gives the command, and names the one thing that catches people out.
What is not mission-specific is worth saying once. The mission is recognised from the
TELESCOP keyword, so you never name it. The position can be given as --ra/--dec in
degrees, or with --parfile, which is usually better because a parameter file carries
proper motion and parallax. --ephem selects the JPL ephemeris and defaults to DE440.
Times, good-time intervals and the TSTART/TSTOP keywords are all corrected, in every
extension of the file — if they were not, the events and the GTIs would end up on
different time scales and up to 500 s of exposure could silently vanish.
For how any of it works, see Technical details. For what is wrong or missing, Known issues.
| Orbit file | nu<obsid>A.attorb in the observation’s auxil/ directory |
| Clock file | nuCclock*.fits — fetched automatically |
| Reference | agrees with HEASOFT barycorr to +22 ns |
barycenter nu30702012003A06_cl.evt nu30702012003A.attorb \
--ra 148.9584 --dec 69.6794 --ephem DE440
The clock file is the thing to know about. NuSTAR’s onboard clock needs a correction of
20–30 milliseconds, which is five orders of magnitude above the 100 ns target, so it is
not optional. If you do not pass --clockfile, the newest file is downloaded from the
CALDB and cached under your user cache directory — they are about 12 MB, so this happens
once per machine, not once per run. With no network, the newest already-cached file is
used.
The catch: clock files from before 2019 carry a different extension
(CLOCK_CORRECT, a quadratic HEASOFT documents as good only to the millisecond) and are
refused rather than used. A millisecond is ten thousand times the target, and a file
corrected that way would look corrected while being nothing of the kind. Fetch a current
clock file.
| Orbit file | orbit/FPorbit_* in the observation directory |
| Clock file | tdc.dat — bundled with this package |
| Reference | agrees with HEASOFT barycorr to +25 ns |
barycenter FS37_be7ab98-be8f0ab.evt FPorbit_D06-05-18 \
--ra 228.4818 --dec -59.1358
RXTE’s clock coefficients are not in a FITS file but in tdc.dat, a 53 kB ASCII table.
HEASOFT ignores its own clockfile parameter for RXTE and always reads
$LHEA_DATA/tdc.dat, so a copy ships inside the package: RXTE stopped observing in
January 2012 and the file is final. $TIMING_DIR and $LHEA_DATA are checked first, so
an installation’s own copy wins. Naming a clock file on the command line warns and uses
tdc.dat anyway — again, what HEASOFT does.
The catch: the correction is only 17.4 µs, which sounds negligible and is 170 times
the target. --clockfile none skips it, and should only be used deliberately.
| Orbit file | auxil/sw<obsid>sao.fits |
| Clock file | swclockcor*.fits, from swift/mis/bcf/clock/ in the CALDB |
| Reference | agrees with HEASOFT barycorr to +37 ns |
barycenter sw00037258040xwtw2po_cl.evt sw00037258040sao.fits \
--ra 182.6358 --dec 39.4058 --clockfile swclockcor20041120v110.fits
Swift is the awkward one, for two reasons that both come from the same place: its mission elapsed time counts UTC seconds, not the TT seconds every other mission here uses.
First, the leap seconds since 2001 have to be added — four seconds on a 2015
observation — and this package always does so, whatever --clockfile says, because a
flag should not be able to cause a whole-second error.
Second, Swift’s “clock file” is not a fine clock correction but the UTC correction factor, the offset between the onboard clock and UTC. It is −15.56 s on the test observation, and it drifts 4.6 ms per day, so the quadratic in the table has to be evaluated and not just its constant term.
The catch: an observation within about 15 seconds of a leap second may come out a second wrong. The leap-second step and the clock file’s own compensating step are ~15 s apart in different time bases, and no reference exists to settle which should win. Leap seconds are rare, observations straddling them rarer; it is written up honestly in Known issues rather than guessed at.
| Orbit file | PPS product P*OBX000ORBTSR*.FTZ |
| Clock file | none needed |
| Reference | agrees with SAS barycen to −42 ns |
barycenter P0112290201PNS003PIEVLI0000.FTZ P0112290201OBX000ORBTSR0000.FTZ \
--ra 148.9583 --dec 69.6792 --ephem DE430
This is the only route for XMM if you do not have SAS: HEASOFT barycorr refuses the
mission outright, despite listing it.
The catch, for anyone extending the reader: the ORBTSR file tabulates the
spacecraft position twice, as GEI_* and as GSE_*. The first is geocentric
equatorial, which is what is wanted; the second is the same vector rotated into the
Earth–Sun frame. They are exactly the same length as each other, so nothing catches the
mistake, and it is a 160 millisecond error. The committed test file keeps both triples
so that the reader’s choice stays under test.
| Orbit file | primary/orbitf*_eph1.fits |
| Clock file | none needed |
| Reference | agrees with CIAO axbary to +47 ns |
barycenter acisf10026N003_evt2.fits orbitf301093602N002_eph1.fits \
--ra 148.9583 --dec 69.6792 --ephem DE440
Like XMM, this is the only route without the mission’s own software — and unlike XMM
there is a positive reason to prefer it even if you have CIAO. axbary picks its
ephemeris from the reference frame and can reach only DE200 (with FK5) and DE405 (with
ICRS). On the test observation DE405 is 79 µs away from DE440, so being able to ask
for a modern ephemeris is not a detail.
The catch: Chandra writes its column names in mixed case — time in the events,
Time in the orbit file, START/STOP in the GTI beside them. FITS says column names
are case-insensitive and this package honours that, but be aware of it if you write
your own reader: a time column that is not found leaves the events untouched while
the GTIs are corrected, and nothing complains.
| Orbit file | ni<obsid>.orb in auxil/ |
| Clock file | none needed |
| Reference | +53 ns against barycorr in a one-off check; no tracked reference |
barycenter ni1013010101_0mpu7_cl.evt ni1013010101.orb --parfile Crab.par
NICER needs no clock correction and its orbit file is the plain ORBIT-extension
dialect, which makes it the least fussy mission here. The 100 ns agreement was measured
once, against a barycorr run on a Crab observation too large to commit, so continuous
integration does not re-check it the way it re-checks the five missions above.
That comparison did settle one useful thing, because it was run with DE200 and FK5
rather than DE440 and ICRS: reading DE200 coordinates in the wrong frame costs 45 µs.
DE200 is referred to FK5 and DE405 onwards to ICRS, --radecsys overrides the pairing,
and the default gets it right.
Validated against the mission’s own gtbary to 29.8 ns — one unit in the last place of
the reference file’s float64 times, which is the floor. The positions come from the
SC_DATA extension of an FT2 (spacecraft) file, timed by its START column.
Two things about Fermi are worth knowing.
The spacecraft file may have no velocity column. Older FT2 files carry SC_POSITION
alone, and the velocity is then obtained by differentiating it, with a warning. That is
accurate at the native 30 s sampling — it is what the committed reference is made with —
but it degrades quickly if the file is thinned.
Older files write MJDREFF in Fortran notation. 7.428703703703703D-4 is a string
as far as FITS is concerned, not a number. It is read anyway; without that, MJDREF is
unreadable and the file’s DATE-OBS cannot be recomputed. Note that gtbary keeps its
dates in UTC while we write the date of the time the file now records, so the two differ
by TT − UTC — 66.2 s in 2009. That is the same convention difference as XMM and Swift;
see “The keywords derived from TSTART and TSTOP” in the technical details.
Fermi’s MET counts TT seconds, not UTC seconds, despite an MJDREF of
51910.00074287037 that is bit-for-bit Swift’s, whose fractional part encodes TT − UTC at
2001-01-01. On Swift that is the signature of a MET counting UTC seconds. On Fermi it is
not: the test observation sits 2.0 s of leap seconds after MJDREF, so applying Swift’s
term would put us 2 s from gtbary, and we are 30 ns from it. MISSIONS["fermi"].met_is_utc
is correctly False. This used to be an open question here and is now settled.
GBM’s spacecraft positions come from its own daily position history,
glg_poshist_all_<yymmdd>_v*.fit in the HEASARC daily directory: extension
GLAST POS HIST, sampled every second, with scalar POS_* and VEL_* columns in metres
and the time in a column called SCLK_UTC. It says TELESCOP = GLAST, exactly like a
LAT spacecraft file, so Fermi’s registry entry lists both layouts and the reader takes
the one whose extension the file has.
The LAT spacecraft file cannot stand in for it. The LAT is switched off in the South Atlantic Anomaly and its spacecraft file has no rows there, while GBM comes back on sooner. On 2024-03-15 the LAT file had nine such gaps, 3.3 hours in all, and on one hour of NaI 0 it left 42 000 of 2.67 million good events with no spacecraft position.
SCLK_UTC is not UTC. It holds the same TT-based mission elapsed time as the event
files. Against the LAT spacecraft file for the same day, a leap second of difference
would show as a 7.5 km disagreement in position; there is none.
There is no official reference. gtbary refuses a GBM event file outright
(“Unsupported timing extension”). It can be made to run only by relabelling the file
INSTRUME = LAT and dropping its EBOUNDS extension, which is fine for a check by hand
but not something to build a committed reference on. The test therefore shows that
the position-history route gives bit-identical times to the LAT-layout route given the
same positions, and that route is the one checked against gtbary above.
Three numbers from that hand check, on 2024-03-15, NaI 0, 12:00–13:00, at Her X-1’s position, recorded here because they are not explained yet:
gtbary on this 2024 file, constant across the hour, the
same with either position file, while on the 2008 tutorial file above we are within
30 ns. One unit in the last place at this epoch is 119.2 ns, so a committed reference
could not tell the two apart, but the mean over 2.5 million events can. The PINT
engine was too noisy here (268 ns scatter) to say which side is off.dt=0).IXPE has a registry entry and its orbit file is read, but it has never been compared
against an official tool, so treat the output as unverified. It uses the same
ORBIT-extension dialect as NICER.
(xrism)=
| Event file | xa<obsid><inst>_*_cl.evt, e.g. resolve/event_cl/xa101002010rsl_p0px1000_cl.evt.gz |
| Orbit file | auxil/xa<obsid>.orb, extension ORBIT |
| Clock file | none needed: the pipeline has already applied the time correction (CLOCKAPP = T) |
| Reference | agrees with HEASOFT barycen to +41 ns mean, 89 ns worst |
barycenter xa101002010rsl_p0px1000_cl.evt.gz xa101002010.orb.gz \
--ra 83.6331221 --dec 22.0144819
--apply-official hands the file to HEASOFT barycen. That is HEASOFT’s
mission-independent task, not SAS’s tool of the same name, and its defaults are set for
XRISM. It has no ephemeris parameter, only a reference frame, so with
--apply-official the ephemeris must be DE440 (the default) or DE200. The native engine
has no such limit.
RA_OBJ and RA_NOM
are where the telescope pointed. For the Crab observation used in the tests that is
65 arcsec from the pulsar, an error of up to 0.16 s.ORBITELEM, holds only Keplerian
elements (the six numbers that describe an orbit’s shape and phase). Positions and
velocities are in the second, ORBIT, in kilometres, every 60 s. The registry names
ORBIT explicitly.barycen by hand, keep its default orbit settings. It can also read the
X..VZ columns of the same table (orbform=COMPONENTS), but on a 60 s orbit its
interpolation of those is off by up to 12 µs with orbinterp=WEIGHTED and 1 µs with
TAYLOR. Its default Keplerian mode is the one that agrees with us; see
Technical details.TIME counts TT seconds from MJDREF = 58484.000800740741, which
is 2019-01-01T00:00:00 UTC written in TT. As with SVOM, that is after the most recent
leap second, so whether XRISM’s elapsed time would count UTC seconds across a future
one cannot be told yet.barycen), so it belongs to instrument timing or the radio
ephemeris, and was not pursued.barycen leaves DATE-OBS, DATE-END and TELAPSE as they were. We update them
from the corrected TSTART and TSTOP, so those keywords will differ from a barycen
output by the size of the correction.(svom)=
| Event file | SVOM_ECL-EVT-CAL_<program>.B-<burst>.O-<obsid>.P-<pass>.v1.fits, extension ECL-EVT-CAL |
| Orbit file | SVOM_SVO-ORB-CNV_ALL.P-<pass>.000.v1.fits, extension SVO-ORB-CNV |
| GTI file | SVOM_ECL-GTI-CAL_<program>.B-<burst>.O-<obsid>.P-<pass>.v1.fits, separate, one extension per criterion |
| Clock file | none needed: the event files are written already clock-corrected (CLOCKCOR = T) |
| Reference | no official tool exists; 1 ulp (29.8 ns) against barycorr on synthetic SVOM-format data relabelled as NICER |
There is no official SVOM barycentring tool, and HEASOFT barycorr refuses
TELESCOP=SVOM. What is checked is everything SVOM-specific — the file layout, units,
frame and time scale — and the barycentring itself against barycorr given the same
numbers in NICER’s layout, which it does read. See the test data notes in
Technical details.
The ECLAIRs data of one observation arrive in pieces, one per X-band downlink pass,
and each piece has its own event, GTI and orbit file. They are matched by the pass
identifier, P-<6 digits>_<3-letter ground station> in the file name and the PASS_ID
keyword, not by the observation ID: an orbit file covers one pass and nothing else.
The GTIs are not in the event file. They come in their own file, as one extension per criterion, and choosing which criteria apply is up to you. List them first:
barycenter-apply-gti SVOM_ECL-EVT-CAL_<...>.fits SVOM_ECL-GTI-CAL_<...>.fits
The file itself does not say what the extensions mean. The ECLAIRs pipeline paper (arXiv:2604.24254, Sect. 3.1 and 4.1) lists the criteria GTIs are made from: outside the South Atlantic Anomaly (SAA), stable attitude, no telemetry gaps, good instrument functioning, and the state of Earth occultation. The extensions map onto them as follows (the first four guessed from their names, since neither the paper nor the files spell them out):
| extension | good when |
|---|---|
GTICAL-NSA |
not in the SAA |
GTICAL-STA |
the attitude is stable (not slewing) |
GTICAL-TLM |
there is no telemetry gap |
GTICAL-EGP |
not documented; presumably the instrument is working well |
GTICAL-NEO |
no Earth occultation: the Earth is out of the field of view |
GTICAL-PEO |
partial Earth occultation: the Earth covers part of the field of view |
GTICAL-TEO |
total Earth occultation: the Earth covers all of the field of view |
The last three are three different states of the same pass, not three spellings of one criterion, and a pass may have any subset of them. The paper’s standard selection is NEO. For timing, that matters more than it looks: ECLAIRs is a coded-mask instrument whose count rate is dominated by background, so the rate hardly drops when the Earth covers the field of view, and nothing in a light curve warns that the source has gone. Time in TEO adds background and no pulses; time in PEO may or may not contain the source, depending on which part of the field of view the Earth covers. So for timing:
barycenter-apply-gti SVOM_ECL-EVT-CAL_<...>.fits SVOM_ECL-GTI-CAL_<...>.fits \
-e GTICAL-STA,GTICAL-NSA,GTICAL-NEO --filter-events -o gti_events.fits
The extensions are intersected, and the result is written as a GTI extension of a copy
of the event file. Use --filter-events unless you know that every tool downstream
reads that extension: without it the events outside the GTIs are kept, and a folding or
pulsar-search tool that only reads the event list will use them anyway. A pass with no
GTICAL-NEO extension at all is an error rather than a silent fallback to PEO or TEO;
skip it, or include PEO deliberately after checking that the source is not behind the
Earth. See
Good time intervals in a separate file.
The event headers carry no source position (RA_PNT and friends are empty), so it
always has to be given, as --ra/--dec in degrees or with --parfile:
barycenter gti_events.fits SVOM_SVO-ORB-CNV_ALL.P-<pass>.000.v1.fits \
--ra <deg> --dec <deg> --ephem DE440
Both steps from Python, for every pass in a directory:
import glob
import re
from barycenter import add_gti_extension, apply_barycenter_correction
RA, DEC = 244.97956, -15.64022 # the source, in degrees (here Sco X-1)
for events in glob.glob("SVOM_ECL-EVT-CAL_*.fits"):
pass_id = re.search(r"\.P-([0-9]{6}_[A-Z]{3})", events).group(1)
(orbit,) = glob.glob(f"SVOM_SVO-ORB-CNV_*{pass_id}*.fits*")
(gtis,) = glob.glob(f"SVOM_ECL-GTI-CAL_*{pass_id}*.fits*")
try:
with_gti = add_gti_extension(
events,
gtis,
["GTICAL-STA", "GTICAL-NSA", "GTICAL-NEO"],
filter_events=True,
overwrite=True,
)
except KeyError: # no Earth-free time in this pass
continue
apply_barycenter_correction(
with_gti,
orbit,
ra=RA,
dec=DEC,
ephem="DE440",
outfile="bary_" + with_gti,
overwrite=True,
)
TIME counts TT seconds from MJDREF = 57754.000800740741,
which is 2017-01-01T00:00:00 UTC (DATEREF) written in TT. That epoch follows the
most recent leap second, so whether SVOM’s MET would count UTC seconds across a future
one — as Swift’s does — cannot be told yet. The orbit file says TIMESYS = TT too, and
that it means it can be checked from the file itself: its Earth-fixed
POSITION_SPHERICAL turned into the inertial frame agrees with POSITION to about a
metre only if TIME is read that way. A one-second mistake would show up as 500 m,
and the 18 s between GPS time and UTC as 9 km.POSITION is in metres in the J2000 frame and VELOCITY in
m/s, both as float32 vectors. The column names are the same as Swift’s and NuSTAR’s,
which hold kilometres; reading SVOM’s as kilometres would be a 20 ms error, which is
why the units are declared in the registry rather than guessed. Float32 rounds the
position to 0.5 m, about 2 ns of light travel time.CLOCKCOR = F where the events say T. If the orbit time tags
were off by a millisecond, the spacecraft would be misplaced by 7.5 m, 25 ns: harmless.SIMFLAG = 1 (“simulated”) is set on flight data. Do not read anything into it.--dt, used above
100 000 events) evaluates the correction every 5 s and interpolates, which costs about
1.6 ns and is about 50 times faster than evaluating it at every event.ASCA has no native orbit reader. It can only be done by handing the job to HEASOFT
timeconv, with --apply-official, which also means DE200 only.
Adding one is usually about five lines of registry entry, plus the work of producing a reference file to check it against. See Adding a mission.