27#include "boost/algorithm/string/trim.hpp"
29#include "lsst/utils/hashCombine.h"
63int constexpr SERIALIZATION_VERSION = 4;
75double getDouble(daf::base::PropertySet
const& metadata,
std::string const& key) {
77 return metadata.exists(key) ? metadata.getAsDouble(key) :
nan;
78 }
catch (pex::exceptions::TypeError& err) {
107 return metadata.exists(key) ? metadata.getAsString(key) :
"";
118bool setDouble(daf::base::PropertySet& metadata,
std::string const& key,
double value,
121 metadata.set(key, value);
137 return setDouble(metadata, key,
angle.asDegrees(), comment);
150 if (!value.
empty()) {
151 metadata.set(key, value);
164 case RotType::UNKNOWN:
168 case RotType::HORIZON:
174 os <<
"Unknown RotType enum: " <<
static_cast<int>(
rotType);
184 if (rotTypeName ==
"UNKNOWN") {
185 return RotType::UNKNOWN;
186 }
else if (rotTypeName ==
"SKY") {
188 }
else if (rotTypeName ==
"HORIZON") {
189 return RotType::HORIZON;
190 }
else if (rotTypeName ==
"MOUNT") {
191 return RotType::MOUNT;
194 os <<
"Unknown RotType name: \"" << rotTypeName <<
"\"";
198class VisitInfoSchema {
204 table::Key<std::int64_t>
tai;
206 table::Key<lsst::geom::Angle>
era;
237 static VisitInfoSchema
const& get() {
238 static VisitInfoSchema instance;
243 VisitInfoSchema(
const VisitInfoSchema&) =
delete;
244 VisitInfoSchema& operator=(
const VisitInfoSchema&) =
delete;
247 VisitInfoSchema(VisitInfoSchema&&) =
delete;
248 VisitInfoSchema& operator=(VisitInfoSchema&&) =
delete;
255 darkTime(
schema.addField<double>(
"darktime",
"time from CCD flush to readout",
"s")),
257 "tai",
"TAI date and time at middle of exposure as nsec from unix epoch",
"nsec")),
258 ut1(
schema.addField<double>(
"ut1",
"UT1 date and time at middle of exposure",
"MJD")),
259 era(
schema.addField<
lsst::geom::
Angle>(
"era",
"earth rotation angle at middle of exposure",
262 "sky position of boresight at middle of exposure")),
267 "refracted apparent topocentric position of boresight at middle of exposure",
"")),
269 "boresightazalt_alt",
270 "refracted apparent topocentric position of boresight at middle of exposure",
"")),
272 "boresightairmass",
"airmass at boresight, relative to zenith at sea level",
"")),
274 "boresightrotangle",
"rotation angle at boresight at middle of exposure",
"")),
275 rotType(
schema.addField<
int>(
"rottype",
"rotation type; see VisitInfo.getRotType for details",
280 "latitude",
"latitude of telescope (+ is east of Greenwich)",
"")),
282 elevation(
schema.addField<double>(
"elevation",
"elevation of telescope",
"")),
287 humidity(
schema.addField<double>(
"humidity",
"humidity (%)",
"")),
290 "instrumentlabel",
"Short name of the instrument that took this data",
"", 0)),
295 idnum(
schema.addField<table::
RecordId>(
"idnum",
"identifier of this full focal plane exposure",
298 focusZ(
schema.addField<double>(
"focusz",
"defocal distance",
"mm")),
301 "observationType",
"type of this observation (e.g. science, flat, bias)",
"", 0)),
303 "scienceProgram",
"observing program (survey or proposal) identifier",
"", 0)),
305 "observationReason",
"reason this observation was taken, or its purpose",
"", 0)),
306 object(
schema.addField<
std::string>(
"object",
"object of interest or field name",
"", 0)),
308 "hasSimulatedContent",
"Was any part of this observation simulated?")) {}
310std::string const VisitInfoSchema::VERSION_KEY =
"version";
312class VisitInfoFactory :
public table::io::PersistableFactory {
315 CatalogVector
const& catalogs)
const override {
316 VisitInfoSchema
const&
keys = VisitInfoSchema::get();
319 table::BaseRecord
const& record = catalogs.front().front();
320 int version = getVersion(record);
321 if (
version > SERIALIZATION_VERSION) {
322 throw LSST_EXCEPT(pex::exceptions::TypeError,
"Cannot read VisitInfo FITS version > " +
339 new VisitInfo(record.get(
keys.exposureId), record.get(
keys.exposureTime),
341 record.
get(
keys.ut1), record.get(
keys.era), record.get(
keys.boresightRaDec),
343 record.get(
keys.boresightAzAlt_alt)),
344 record.get(
keys.boresightAirmass), record.get(
keys.boresightRotAngle),
346 coord::Observatory(record.get(
keys.longitude), record.get(
keys.latitude),
347 record.get(
keys.elevation)),
348 coord::Weather(record.get(
keys.airTemperature), record.get(
keys.airPressure),
349 record.get(
keys.humidity)),
355 explicit VisitInfoFactory(
std::string const&
name) : table::io::PersistableFactory(
name) {}
358 int getVersion(table::BaseRecord
const& record)
const {
361 auto versionKey = record.getSchema().find<
int>(VisitInfoSchema::VERSION_KEY);
362 return record.get(versionKey.key);
363 }
catch (pex::exceptions::NotFoundError
const&) {
370std::string getVisitInfoPersistenceName() {
return "VisitInfo"; }
372VisitInfoFactory registration(getVisitInfoPersistenceName());
409 "HAS-SIMULATED-CONTENT"};
410 for (
auto&& key : keyList) {
411 if (metadata.
exists(key)) {
423 setDouble(metadata,
"EXPTIME",
visitInfo.getExposureTime(),
"Exposure time (sec)");
424 setDouble(metadata,
"DARKTIME",
visitInfo.getDarkTime(),
"Time from CCD flush to readout (sec)");
426 metadata.
set(
"DATE-AVG",
visitInfo.getDate().toString(::DateTime::TAI),
427 "TAI date at middle of observation");
428 metadata.
set(
"TIMESYS",
"TAI");
430 setDouble(metadata,
"MJD-AVG-UT1",
visitInfo.getUt1(),
"UT1 MJD date at ctr of obs");
431 setAngle(metadata,
"AVG-ERA",
visitInfo.getEra(),
"Earth rot ang at ctr of obs (deg)");
433 setAngle(metadata,
"BORE-RA",
boresightRaDec[0],
"ICRS RA (deg) at boresight");
434 setAngle(metadata,
"BORE-DEC",
boresightRaDec[1],
"ICRS Dec (deg) at boresight");
435 auto boresightAzAlt =
visitInfo.getBoresightAzAlt();
436 setAngle(metadata,
"BORE-AZ", boresightAzAlt[0],
"Refr app topo az (deg) at bore");
437 setAngle(metadata,
"BORE-ALT", boresightAzAlt[1],
"Refr app topo alt (deg) at bore");
438 setDouble(metadata,
"BORE-AIRMASS",
visitInfo.getBoresightAirmass(),
"Airmass at boresight");
439 setAngle(metadata,
"BORE-ROTANG",
visitInfo.getBoresightRotAngle(),
"Rotation angle (deg) at boresight");
440 metadata.
set(
"ROTTYPE", rotTypeStrFromEnum(
visitInfo.getRotType()),
"Type of rotation angle");
441 auto observatory =
visitInfo.getObservatory();
442 setAngle(metadata,
"OBS-LONG", observatory.getLongitude(),
"Telescope longitude (+E, deg)");
443 setAngle(metadata,
"OBS-LAT", observatory.getLatitude(),
"Telescope latitude (deg)");
444 setDouble(metadata,
"OBS-ELEV", observatory.getElevation(),
"Telescope elevation (m)");
446 setDouble(metadata,
"AIRTEMP", weather.getAirTemperature(),
"Outside air temperature (C)");
447 setDouble(metadata,
"AIRPRESS", weather.getAirPressure(),
"Outdoor air pressure (P)");
448 setDouble(metadata,
"HUMIDITY", weather.getHumidity(),
"Relative humidity (%)");
449 setString(metadata,
"INSTRUMENT",
visitInfo.getInstrumentLabel(),
450 "Short name of the instrument that took this data");
452 metadata.
set(
"IDNUM",
visitInfo.getId(),
"identifier of this full focal plane exposure");
454 setDouble(metadata,
"FOCUSZ",
visitInfo.getFocusZ(),
"Defocal distance (mm)");
455 setString(metadata,
"OBSTYPE",
visitInfo.getObservationType(),
"Type of this observation");
456 setString(metadata,
"PROGRAM",
visitInfo.getScienceProgram(),
457 "observing program (survey or proposal) identifier");
458 setString(metadata,
"REASON",
visitInfo.getObservationReason(),
459 "reason this observation was taken, or its purpose");
460 setString(metadata,
"OBJECT",
visitInfo.getObject(),
"object of interest or field name");
461 metadata.
set(
"HAS-SIMULATED-CONTENT",
visitInfo.getHasSimulatedContent(),
462 "Was any part of this observation simulated?");
470 _darkTime(getDouble(metadata,
"DARKTIME")),
472 _ut1(getDouble(metadata,
"MJD-AVG-UT1")),
473 _era(getAngle(metadata,
"AVG-ERA")),
475 lsst::geom::
SpherePoint(getAngle(metadata,
"BORE-RA"), getAngle(metadata,
"BORE-DEC"))),
477 lsst::geom::
SpherePoint(getAngle(metadata,
"BORE-AZ"), getAngle(metadata,
"BORE-ALT"))),
478 _boresightAirmass(getDouble(metadata,
"BORE-AIRMASS")),
479 _boresightRotAngle(getAngle(metadata,
"BORE-ROTANG")),
481 _observatory(getAngle(metadata,
"OBS-LONG"), getAngle(metadata,
"OBS-LAT"),
482 getDouble(metadata,
"OBS-ELEV")),
483 _weather(getDouble(metadata,
"AIRTEMP"), getDouble(metadata,
"AIRPRESS"),
484 getDouble(metadata,
"HUMIDITY")),
485 _instrumentLabel(getString(metadata,
"INSTRUMENT")),
487 _focusZ(getDouble(metadata,
"FOCUSZ")),
488 _observationType(getString(metadata,
"OBSTYPE")),
489 _scienceProgram(getString(metadata,
"PROGRAM")),
490 _observationReason(getString(metadata,
"REASON")),
491 _object(getString(metadata,
"OBJECT")),
493 _hasSimulatedContent(false) {
495 if (metadata.
exists(key)) {
499 if (metadata.
exists(key)) {
504 if (metadata.
exists(key)) {
515 if (metadata.
exists(key)) {
516 if (metadata.
exists(
"TIMESYS")) {
517 auto timesysName = boost::algorithm::trim_right_copy(metadata.
getAsString(
"TIMESYS"));
518 if (timesysName !=
"TAI") {
523 os <<
"TIMESYS = \"" << timesysName
524 <<
"\"; VisitInfo requires TIMESYS to exist and to equal \"TAI\"";
529 "TIMESYS not found; VistitInfo requires TIMESYS to exist and to equal \"TAI\"");
536 if (metadata.
exists(key)) {
542 if (metadata.
exists(key)) {
543 _rotType = rotTypeEnumFromStr(metadata.
getAsString(key));
546 key =
"HAS-SIMULATED-CONTENT";
547 if (metadata.
exists(key)) {
548 _hasSimulatedContent = metadata.
getAsBool(key);
567 return (!lhs.isFinite() && !rhs.isFinite()) || lhs == rhs;
588 return utils::hashCombine(17, _exposureId, _exposureTime, _darkTime, _date, _ut1, _era, _boresightRaDec,
589 _boresightAzAlt, _boresightAirmass, _boresightRotAngle, _rotType, _observatory,
590 _weather, _instrumentLabel, _id, _focusZ, _observationType, _scienceProgram,
591 _observationReason, _object, _hasSimulatedContent);
597 VisitInfoSchema
const&
keys = VisitInfoSchema::get();
603 record->set(
keys.tai,
getDate().nsecs(::DateTime::TAI));
608 record->set(
keys.boresightAzAlt_az, boresightAzAlt[0]);
609 record->set(
keys.boresightAzAlt_alt, boresightAzAlt[1]);
614 record->set(
keys.latitude, observatory.getLatitude());
615 record->set(
keys.longitude, observatory.getLongitude());
616 record->set(
keys.elevation, observatory.getElevation());
618 record->set(
keys.airTemperature, weather.getAirTemperature());
619 record->set(
keys.airPressure, weather.getAirPressure());
620 record->set(
keys.humidity, weather.getHumidity());
622 record->set(
keys.version, SERIALIZATION_VERSION);
642 double _parallactic_y, _parallactic_x, result;
647 result = atan2(_parallactic_y, _parallactic_x);
652 return std::make_unique<VisitInfo>(*
this);
656 return singleClassEquals(*
this, other);
661 buffer <<
"VisitInfo(";
667 buffer <<
"UT1=" <<
getUt1() <<
", ";
668 buffer <<
"ERA=" <<
getEra() <<
", ";
673 buffer <<
"rotType=" <<
static_cast<int>(
getRotType()) <<
", ";
677 buffer <<
"id=" <<
getId() <<
", ";
678 buffer <<
"focusZ=" <<
getFocusZ() <<
", ";
682 buffer <<
"object='" <<
getObject() <<
"', ";
table::Key< std::string > name
#define LSST_EXCEPT(type,...)
table::Key< double > angle
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
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< int > version
table::Key< afw::table::Flag > hasSimulatedContent
table::Key< double > focusZ
table::Key< lsst::geom::Angle > boresightRotAngle
table::Key< table::RecordId > exposureId
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.
table::RecordId getExposureId() const
get exposure ID
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.
bool isValid() const noexcept
Return true if the key was initialized to valid offset.
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.
std::string toString(Timescale scale) const
double get(DateSystem system=MJD, Timescale scale=TAI) const
void set(std::string const &name, T const &value)
std::string getAsString(std::string const &name) const
virtual void remove(std::string const &name)
int64_t getAsInt64(std::string const &name) const
bool exists(std::string const &name) const
double getAsDouble(std::string const &name) const
bool getAsBool(std::string const &name) const
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.
lsst::geom::SpherePoint SpherePoint
std::int64_t RecordId
Type used for unique IDs for records.
AngleUnit constexpr degrees
AngleUnit constexpr radians
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override