27#include "boost/algorithm/string/trim.hpp"
64int constexpr SERIALIZATION_VERSION = 5;
76double getDouble(daf::base::PropertySet
const& metadata,
std::string const& key) {
78 return metadata.exists(key) && !metadata.isUndefined(key) ? metadata.getAsDouble(key) :
nan;
79 }
catch (pex::exceptions::TypeError& err) {
108 return metadata.exists(key) && !metadata.isUndefined(key) ? metadata.getAsString(key) :
"";
119bool setDouble(daf::base::PropertySet& metadata,
std::string const& key,
double value,
122 metadata.set(key, value);
138 return setDouble(metadata, key,
angle.asDegrees(), comment);
151 if (!
value.empty()) {
152 metadata.set(key, value);
165 case RotType::UNKNOWN:
169 case RotType::HORIZON:
175 os <<
"Unknown RotType enum: " <<
static_cast<int>(
rotType);
185 if (rotTypeName ==
"UNKNOWN") {
186 return RotType::UNKNOWN;
187 }
else if (rotTypeName ==
"SKY") {
189 }
else if (rotTypeName ==
"HORIZON") {
190 return RotType::HORIZON;
191 }
else if (rotTypeName ==
"MOUNT") {
192 return RotType::MOUNT;
195 os <<
"Unknown RotType name: \"" << rotTypeName <<
"\"";
199class VisitInfoSchema {
204 table::Key<std::int64_t>
tai;
206 table::Key<lsst::geom::Angle>
era;
237 static VisitInfoSchema
const& get(
int version = SERIALIZATION_VERSION) {
239 static VisitInfoSchema instanceLatest(SERIALIZATION_VERSION);
241 static VisitInfoSchema instanceWithExposureId(4);
243 return instanceWithExposureId;
245 return instanceLatest;
250 VisitInfoSchema(
const VisitInfoSchema&) =
delete;
251 VisitInfoSchema& operator=(
const VisitInfoSchema&) =
delete;
254 VisitInfoSchema(VisitInfoSchema&&) =
delete;
255 VisitInfoSchema& operator=(VisitInfoSchema&&) =
delete;
258 VisitInfoSchema(
int _version) :
schema() {
267 "tai",
"TAI date and time at middle of exposure as nsec from unix epoch",
"nsec");
268 ut1 =
schema.
addField<
double>(
"ut1",
"UT1 date and time at middle of exposure",
"MJD");
271 "sky position of boresight at middle of exposure");
276 "refracted apparent topocentric position of boresight at middle of exposure",
"");
278 "boresightazalt_alt",
279 "refracted apparent topocentric position of boresight at middle of exposure",
"");
281 "boresightairmass",
"airmass at boresight, relative to zenith at sea level",
"");
283 "boresightrotangle",
"rotation angle at boresight at middle of exposure",
"");
285 schema.
addField<
int>(
"rottype",
"rotation type; see VisitInfo.getRotType for details",
"MJD");
289 "latitude of telescope (+ is east of Greenwich)",
"");
299 "instrumentlabel",
"Short name of the instrument that took this data",
"", 0);
309 "observationType",
"type of this observation (e.g. science, flat, bias)",
"", 0);
311 "scienceProgram",
"observing program (survey or proposal) identifier",
"", 0);
313 "observationReason",
"reason this observation was taken, or its purpose",
"", 0);
316 "hasSimulatedContent",
"Was any part of this observation simulated?");
319std::string const VisitInfoSchema::VERSION_KEY =
"version";
321class VisitInfoFactory :
public table::io::PersistableFactory {
324 CatalogVector
const& catalogs)
const override {
327 table::BaseRecord
const& record =
catalogs.front().front();
328 int version = getVersion(record);
329 if (
version > SERIALIZATION_VERSION) {
330 throw LSST_EXCEPT(pex::exceptions::TypeError,
"Cannot read VisitInfo FITS version > " +
336 VisitInfoSchema
const&
keys = VisitInfoSchema::get(
version);
350 new VisitInfo(record.get(
keys.exposureTime), record.get(
keys.darkTime),
352 record.get(
keys.era), record.get(
keys.boresightRaDec),
354 record.get(
keys.boresightAzAlt_alt)),
355 record.get(
keys.boresightAirmass), record.get(
keys.boresightRotAngle),
357 coord::Observatory(record.get(
keys.longitude), record.get(
keys.latitude),
358 record.get(
keys.elevation)),
359 coord::Weather(record.get(
keys.airTemperature), record.get(
keys.airPressure),
360 record.get(
keys.humidity)),
366 explicit VisitInfoFactory(
std::string const& name) : table::io::PersistableFactory(name) {}
369 int getVersion(table::BaseRecord
const& record)
const {
372 auto versionKey = record.getSchema().find<
int>(VisitInfoSchema::VERSION_KEY);
373 return record.get(versionKey.key);
374 }
catch (pex::exceptions::NotFoundError
const&) {
381std::string getVisitInfoPersistenceName() {
return "VisitInfo"; }
383VisitInfoFactory registration(getVisitInfoPersistenceName());
393 "TIME-MID",
"MJD-AVG-UT1",
"AVG-ERA",
"BORE-RA",
394 "BORE-DEC",
"BORE-AZ",
"BORE-ALT",
"BORE-AIRMASS",
395 "BORE-ROTANG",
"ROTTYPE",
"OBS-LONG",
"OBS-LAT",
396 "OBS-ELEV",
"AIRTEMP",
"AIRPRESS",
"HUMIDITY",
397 "INSTRUMENT",
"IDNUM",
"FOCUSZ",
"OBSTYPE",
398 "PROGRAM",
"REASON",
"OBJECT",
"HAS-SIMULATED-CONTENT"};
399 for (
auto&& key : keyList) {
400 if (metadata.
exists(key)) {
409 setDouble(metadata,
"EXPTIME",
visitInfo.getExposureTime(),
"Exposure time (sec)");
410 setDouble(metadata,
"DARKTIME",
visitInfo.getDarkTime(),
"Time from CCD flush to readout (sec)");
412 metadata.
set(
"DATE-AVG",
visitInfo.getDate().toString(::DateTime::TAI),
413 "TAI date at middle of observation");
414 metadata.
set(
"TIMESYS",
"TAI");
416 setDouble(metadata,
"MJD-AVG-UT1",
visitInfo.getUt1(),
"UT1 MJD date at ctr of obs");
417 setAngle(metadata,
"AVG-ERA",
visitInfo.getEra(),
"Earth rot ang at ctr of obs (deg)");
419 setAngle(metadata,
"BORE-RA",
boresightRaDec[0],
"ICRS RA (deg) at boresight");
420 setAngle(metadata,
"BORE-DEC",
boresightRaDec[1],
"ICRS Dec (deg) at boresight");
421 auto boresightAzAlt =
visitInfo.getBoresightAzAlt();
422 setAngle(metadata,
"BORE-AZ", boresightAzAlt[0],
"Refr app topo az (deg) at bore");
423 setAngle(metadata,
"BORE-ALT", boresightAzAlt[1],
"Refr app topo alt (deg) at bore");
424 setDouble(metadata,
"BORE-AIRMASS",
visitInfo.getBoresightAirmass(),
"Airmass at boresight");
425 setAngle(metadata,
"BORE-ROTANG",
visitInfo.getBoresightRotAngle(),
"Rotation angle (deg) at boresight");
426 metadata.
set(
"ROTTYPE", rotTypeStrFromEnum(
visitInfo.getRotType()),
"Type of rotation angle");
427 auto observatory =
visitInfo.getObservatory();
428 setAngle(metadata,
"OBS-LONG", observatory.getLongitude(),
"Telescope longitude (+E, deg)");
429 setAngle(metadata,
"OBS-LAT", observatory.getLatitude(),
"Telescope latitude (deg)");
430 setDouble(metadata,
"OBS-ELEV", observatory.getElevation(),
"Telescope elevation (m)");
432 setDouble(metadata,
"AIRTEMP", weather.getAirTemperature(),
"Outside air temperature (C)");
433 setDouble(metadata,
"AIRPRESS", weather.getAirPressure(),
"Outdoor air pressure (P)");
434 setDouble(metadata,
"HUMIDITY", weather.getHumidity(),
"Relative humidity (%)");
435 setString(metadata,
"INSTRUMENT",
visitInfo.getInstrumentLabel(),
436 "Short name of the instrument that took this data");
438 metadata.
set(
"IDNUM",
visitInfo.getId(),
"identifier of this full focal plane exposure");
440 setDouble(metadata,
"FOCUSZ",
visitInfo.getFocusZ(),
"Defocal distance (mm)");
441 setString(metadata,
"OBSTYPE",
visitInfo.getObservationType(),
"Type of this observation");
442 setString(metadata,
"PROGRAM",
visitInfo.getScienceProgram(),
443 "observing program (survey or proposal) identifier");
444 setString(metadata,
"REASON",
visitInfo.getObservationReason(),
445 "reason this observation was taken, or its purpose");
446 setString(metadata,
"OBJECT",
visitInfo.getObject(),
"object of interest or field name");
447 metadata.
set(
"HAS-SIMULATED-CONTENT",
visitInfo.getHasSimulatedContent(),
448 "Was any part of this observation simulated?");
454 : _exposureTime(nan),
455 _darkTime(getDouble(metadata,
"DARKTIME")),
457 _ut1(getDouble(metadata,
"MJD-AVG-UT1")),
458 _era(getAngle(metadata,
"AVG-ERA")),
460 lsst::
geom::SpherePoint(getAngle(metadata,
"BORE-RA"), getAngle(metadata,
"BORE-DEC"))),
462 lsst::
geom::SpherePoint(getAngle(metadata,
"BORE-AZ"), getAngle(metadata,
"BORE-ALT"))),
463 _boresightAirmass(getDouble(metadata,
"BORE-AIRMASS")),
464 _boresightRotAngle(getAngle(metadata,
"BORE-ROTANG")),
466 _observatory(getAngle(metadata,
"OBS-LONG"), getAngle(metadata,
"OBS-LAT"),
467 getDouble(metadata,
"OBS-ELEV")),
468 _weather(getDouble(metadata,
"AIRTEMP"), getDouble(metadata,
"AIRPRESS"),
469 getDouble(metadata,
"HUMIDITY")),
470 _instrumentLabel(getString(metadata,
"INSTRUMENT")),
472 _focusZ(getDouble(metadata,
"FOCUSZ")),
473 _observationType(getString(metadata,
"OBSTYPE")),
474 _scienceProgram(getString(metadata,
"PROGRAM")),
475 _observationReason(getString(metadata,
"REASON")),
476 _object(getString(metadata,
"OBJECT")),
478 _hasSimulatedContent(false) {
497 if (metadata.
exists(
"TIMESYS")) {
498 auto timesysName = boost::algorithm::trim_right_copy(metadata.
getAsString(
"TIMESYS"));
499 if (timesysName !=
"TAI") {
504 os <<
"TIMESYS = \"" << timesysName
505 <<
"\"; VisitInfo requires TIMESYS to exist and to equal \"TAI\"";
510 "TIMESYS not found; VistitInfo requires TIMESYS to exist and to equal \"TAI\"");
524 _rotType = rotTypeEnumFromStr(metadata.
getAsString(key));
527 key =
"HAS-SIMULATED-CONTENT";
529 _hasSimulatedContent = metadata.
getAsBool(key);
548 return (!lhs.isFinite() && !rhs.isFinite()) || lhs == rhs;
569 _boresightAzAlt, _boresightAirmass, _boresightRotAngle, _rotType, _observatory,
570 _weather, _instrumentLabel, _id, _focusZ, _observationType, _scienceProgram,
571 _observationReason, _object, _hasSimulatedContent);
577 VisitInfoSchema
const& keys = VisitInfoSchema::get();
582 record->set(keys.tai,
getDate().nsecs(::DateTime::TAI));
583 record->set(keys.ut1,
getUt1());
584 record->set(keys.era,
getEra());
587 record->set(keys.boresightAzAlt_az, boresightAzAlt[0]);
588 record->set(keys.boresightAzAlt_alt, boresightAzAlt[1]);
591 record->set(keys.rotType,
static_cast<int>(
getRotType()));
593 record->set(keys.latitude, observatory.getLatitude());
594 record->set(keys.longitude, observatory.getLongitude());
595 record->set(keys.elevation, observatory.getElevation());
597 record->set(keys.airTemperature, weather.getAirTemperature());
598 record->set(keys.airPressure, weather.getAirPressure());
599 record->set(keys.humidity, weather.getHumidity());
601 record->set(keys.version, SERIALIZATION_VERSION);
602 record->set(keys.idnum,
getId());
621 double _parallactic_y, _parallactic_x,
result;
626 result = atan2(_parallactic_y, _parallactic_x);
631 return std::make_unique<VisitInfo>(*
this);
635 return singleClassEquals(*
this, other);
640 buffer <<
"VisitInfo(";
645 buffer <<
"UT1=" <<
getUt1() <<
", ";
646 buffer <<
"ERA=" <<
getEra() <<
", ";
651 buffer <<
"rotType=" <<
static_cast<int>(
getRotType()) <<
", ";
655 buffer <<
"id=" <<
getId() <<
", ";
656 buffer <<
"focusZ=" <<
getFocusZ() <<
", ";
660 buffer <<
"object='" <<
getObject() <<
"', ";
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
table::Key< double > angle
table::Key< lsst::geom::Angle > longitude
table::Key< std::string > observationReason
table::Key< std::string > scienceProgram
table::Key< lsst::geom::Angle > latitude
table::Key< std::string > observationType
table::Key< lsst::geom::Angle > boresightAzAlt_az
table::Key< double > exposureTime
table::Key< lsst::geom::Angle > era
table::Key< double > airPressure
table::Key< lsst::geom::Angle > boresightAzAlt_alt
table::Key< int > rotType
table::Key< double > darkTime
table::Key< table::RecordId > idnum
table::Key< std::string > instrumentLabel
table::CoordKey boresightRaDec
table::Key< double > boresightAirmass
table::Key< std::string > object
table::Key< double > airTemperature
table::Key< std::int64_t > tai
table::Key< double > elevation
table::Key< double > humidity
table::Key< afw::table::Flag > hasSimulatedContent
table::Key< double > focusZ
table::Key< lsst::geom::Angle > boresightRotAngle
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
lsst::geom::Angle getLongitude() const noexcept
get telescope longitude (positive values are E of Greenwich)
Information about a single exposure of an imaging camera.
std::string getPersistenceName() const override
Return the unique name used to persist this object and look up its factory.
daf::base::DateTime getDate() const
get uniform date and time at middle of exposure
coord::Weather getWeather() const
get basic weather information
lsst::geom::Angle getLocalEra() const
double getUt1() const
get UT1 (universal time) MJD date at middle of exposure
double getBoresightAirmass() const
get airmass at the boresight, relative to zenith at sea level (and at the middle of the exposure,...
std::string getScienceProgram() const
lsst::geom::Angle getBoresightHourAngle() const
lsst::geom::Angle getBoresightRotAngle() const
Get rotation angle at boresight at middle of exposure.
std::size_t hash_value() const noexcept override
Return a hash of this object.
bool operator==(VisitInfo const &other) const
lsst::geom::Angle getEra() const
get earth rotation angle at middle of exposure
table::RecordId getId() const
std::string getObservationReason() const
std::string toString() const override
Create a string representation of this object.
double getExposureTime() const
get exposure duration (shutter open time); (sec)
RotType getRotType() const
get rotation type of boresightRotAngle
bool equals(typehandling::Storable const &other) const noexcept override
Compare this object to another Storable.
lsst::geom::SpherePoint getBoresightAzAlt() const
get refracted apparent topocentric Az/Alt position at the boresight (and at the middle of the exposur...
std::string getObject() const
lsst::geom::SpherePoint getBoresightRaDec() const
get ICRS RA/Dec position at the boresight (and at the middle of the exposure, if it varies with time)
std::shared_ptr< typehandling::Storable > cloneStorable() const override
Create a new VisitInfo that is a copy of this one.
std::string getInstrumentLabel() const
bool getHasSimulatedContent() const
double getDarkTime() const
get time from CCD flush to exposure readout, including shutter open time (despite the name); (sec)
coord::Observatory getObservatory() const
get observatory longitude, latitude and elevation
lsst::geom::Angle getBoresightParAngle() const
Get parallactic angle at the boresight.
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
std::string getObservationType() const
std::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
static CoordKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc)
Add a pair of _ra, _dec fields to a Schema, and return a CoordKey that points to them.
bool isValid() const noexcept
Return true if the key was initialized to valid offset.
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
An object passed to Persistable::write to allow it to persist itself.
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
static std::shared_ptr< T > dynamicCast(std::shared_ptr< Persistable > const &ptr)
Dynamically cast a shared_ptr.
Interface supporting iteration over heterogenous containers.
Class for handling dates/times, including MJD, UTC, and TAI.
std::string toString(Timescale scale) const
Get date as an ISO8601-formatted string.
bool isValid() const
Is this date valid?
double get(DateSystem system=MJD, Timescale scale=TAI) const
Get date as a double in a specified representation, such as MJD.
Class for storing ordered metadata with comments.
void set(std::string const &name, T const &value)
Replace all values for a property name (possibly hierarchical) with a new scalar value.
Class for storing generic metadata.
std::string getAsString(std::string const &name) const
Get the last value for a string property name (possibly hierarchical).
virtual void remove(std::string const &name)
Remove all values for a property name (possibly hierarchical).
int64_t getAsInt64(std::string const &name) const
Get the last value for a bool/char/short/int/int64_t property name (possibly hierarchical).
bool isUndefined(std::string const &name) const
Determine if a name (possibly hierarchical) has a defined value.
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
double getAsDouble(std::string const &name) const
Get the last value for any arithmetic property name (possibly hierarchical).
bool getAsBool(std::string const &name) const
Get the last value for a bool property name (possibly hierarchical).
A class representing an angle.
Point in an unspecified spherical coordinate system.
Reports errors that are due to events beyond the control of the program.
Reports errors from accepting an object of an unexpected or inappropriate type.
int stripVisitInfoKeywords(daf::base::PropertySet &metadata)
Remove VisitInfo-related keywords from the metadata.
void setVisitInfoMetadata(daf::base::PropertyList &metadata, VisitInfo const &visitInfo)
Set FITS metadata from a VisitInfo.
bool _eqOrNonFinite(lsst::geom::SpherePoint const &lhs, lsst::geom::SpherePoint const &rhs) noexcept
Test whether two SpherePoints are exactly equal or invalid.
std::ostream & operator<<(std::ostream &os, Measurement const &measurement)
@ UNKNOWN
Rotation angle is unknown.
bool _eqOrNan(double lhs, double rhs) noexcept
Test whether two numbers are exactly equal or both NaN.
std::int64_t RecordId
Type used for unique IDs for records.
std::size_t hashCombine(std::size_t seed) noexcept
Combine hashes.
AngleUnit constexpr degrees
constant with units of degrees
AngleUnit constexpr radians
constant with units of radians
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override