27#include "boost/algorithm/string/trim.hpp"
29#include "lsst/utils/hashCombine.h"
62int constexpr SERIALIZATION_VERSION = 3;
74double getDouble(daf::base::PropertySet
const& metadata,
std::string const& key) {
76 return metadata.exists(key) ? metadata.getAsDouble(key) :
nan;
77 }
catch (pex::exceptions::TypeError& err) {
106 return metadata.exists(key) ? metadata.getAsString(key) :
"";
117bool setDouble(daf::base::PropertySet& metadata,
std::string const& key,
double value,
120 metadata.set(key, value);
136 return setDouble(metadata, key,
angle.asDegrees(), comment);
149 if (!value.
empty()) {
150 metadata.set(key, value);
163 case RotType::UNKNOWN:
167 case RotType::HORIZON:
173 os <<
"Unknown RotType enum: " <<
static_cast<int>(
rotType);
183 if (rotTypeName ==
"UNKNOWN") {
184 return RotType::UNKNOWN;
185 }
else if (rotTypeName ==
"SKY") {
187 }
else if (rotTypeName ==
"HORIZON") {
188 return RotType::HORIZON;
189 }
else if (rotTypeName ==
"MOUNT") {
190 return RotType::MOUNT;
193 os <<
"Unknown RotType name: \"" << rotTypeName <<
"\"";
197class VisitInfoSchema {
203 table::Key<std::int64_t>
tai;
205 table::Key<lsst::geom::Angle>
era;
230 static VisitInfoSchema
const& get() {
231 static VisitInfoSchema instance;
236 VisitInfoSchema(
const VisitInfoSchema&) =
delete;
237 VisitInfoSchema& operator=(
const VisitInfoSchema&) =
delete;
240 VisitInfoSchema(VisitInfoSchema&&) =
delete;
241 VisitInfoSchema& operator=(VisitInfoSchema&&) =
delete;
248 darkTime(
schema.addField<double>(
"darktime",
"time from CCD flush to readout",
"s")),
250 "tai",
"TAI date and time at middle of exposure as nsec from unix epoch",
"nsec")),
251 ut1(
schema.addField<double>(
"ut1",
"UT1 date and time at middle of exposure",
"MJD")),
252 era(
schema.addField<
lsst::geom::
Angle>(
"era",
"earth rotation angle at middle of exposure",
255 "sky position of boresight at middle of exposure")),
260 "refracted apparent topocentric position of boresight at middle of exposure",
"")),
262 "boresightazalt_alt",
263 "refracted apparent topocentric position of boresight at middle of exposure",
"")),
265 "boresightairmass",
"airmass at boresight, relative to zenith at sea level",
"")),
267 "boresightrotangle",
"rotation angle at boresight at middle of exposure",
"")),
268 rotType(
schema.addField<int>(
"rottype",
"rotation type; see VisitInfo.getRotType for details",
273 "latitude",
"latitude of telescope (+ is east of Greenwich)",
"")),
275 elevation(
schema.addField<double>(
"elevation",
"elevation of telescope",
"")),
280 humidity(
schema.addField<double>(
"humidity",
"humidity (%)",
"")),
283 "instrumentlabel",
"Short name of the instrument that took this data",
"", 0)),
286 version(
schema.addField<int>(VERSION_KEY,
"version of this VisitInfo")),
288 idnum(
schema.addField<table::
RecordId>(
"idnum",
"identifier of this full focal plane exposure",
291 focusZ(
schema.addField<double>(
"focusz",
"defocal distance",
"mm")) {}
293std::string const VisitInfoSchema::VERSION_KEY =
"version";
295class VisitInfoFactory :
public table::io::PersistableFactory {
298 CatalogVector
const& catalogs)
const override {
299 VisitInfoSchema
const&
keys = VisitInfoSchema::get();
302 table::BaseRecord
const& record = catalogs.front().front();
303 int version = getVersion(record);
304 if (
version > SERIALIZATION_VERSION) {
305 throw LSST_EXCEPT(pex::exceptions::TypeError,
"Cannot read VisitInfo FITS version > " +
315 new VisitInfo(record.get(
keys.exposureId), record.get(
keys.exposureTime),
317 record.
get(
keys.ut1), record.get(
keys.era), record.get(
keys.boresightRaDec),
319 record.get(
keys.boresightAzAlt_alt)),
320 record.get(
keys.boresightAirmass), record.get(
keys.boresightRotAngle),
322 coord::Observatory(record.get(
keys.longitude), record.get(
keys.latitude),
323 record.get(
keys.elevation)),
324 coord::Weather(record.get(
keys.airTemperature), record.get(
keys.airPressure),
325 record.get(
keys.humidity)),
330 explicit VisitInfoFactory(
std::string const&
name) : table::io::PersistableFactory(
name) {}
333 int getVersion(table::BaseRecord
const& record)
const {
336 auto versionKey = record.getSchema().find<
int>(VisitInfoSchema::VERSION_KEY);
337 return record.get(versionKey.key);
338 }
catch (pex::exceptions::NotFoundError
const&) {
345std::string getVisitInfoPersistenceName() {
return "VisitInfo"; }
347VisitInfoFactory registration(getVisitInfoPersistenceName());
357 "TIME-MID",
"MJD-AVG-UT1",
"AVG-ERA",
"BORE-RA",
"BORE-DEC",
358 "BORE-AZ",
"BORE-ALT",
"BORE-AIRMASS",
"BORE-ROTANG",
"ROTTYPE",
359 "OBS-LONG",
"OBS-LAT",
"OBS-ELEV",
"AIRTEMP",
"AIRPRESS",
360 "HUMIDITY",
"INSTRUMENT",
"IDNUM",
"FOCUSZ"};
361 for (
auto&& key : keyList) {
362 if (metadata.
exists(key)) {
374 setDouble(metadata,
"EXPTIME",
visitInfo.getExposureTime(),
"Exposure time (sec)");
375 setDouble(metadata,
"DARKTIME",
visitInfo.getDarkTime(),
"Time from CCD flush to readout (sec)");
377 metadata.
set(
"DATE-AVG",
visitInfo.getDate().toString(::DateTime::TAI),
378 "TAI date at middle of observation");
379 metadata.
set(
"TIMESYS",
"TAI");
381 setDouble(metadata,
"MJD-AVG-UT1",
visitInfo.getUt1(),
"UT1 MJD date at ctr of obs");
382 setAngle(metadata,
"AVG-ERA",
visitInfo.getEra(),
"Earth rot ang at ctr of obs (deg)");
384 setAngle(metadata,
"BORE-RA",
boresightRaDec[0],
"ICRS RA (deg) at boresight");
385 setAngle(metadata,
"BORE-DEC",
boresightRaDec[1],
"ICRS Dec (deg) at boresight");
386 auto boresightAzAlt =
visitInfo.getBoresightAzAlt();
387 setAngle(metadata,
"BORE-AZ", boresightAzAlt[0],
"Refr app topo az (deg) at bore");
388 setAngle(metadata,
"BORE-ALT", boresightAzAlt[1],
"Refr app topo alt (deg) at bore");
389 setDouble(metadata,
"BORE-AIRMASS",
visitInfo.getBoresightAirmass(),
"Airmass at boresight");
390 setAngle(metadata,
"BORE-ROTANG",
visitInfo.getBoresightRotAngle(),
"Rotation angle (deg) at boresight");
391 metadata.
set(
"ROTTYPE", rotTypeStrFromEnum(
visitInfo.getRotType()),
"Type of rotation angle");
392 auto observatory =
visitInfo.getObservatory();
393 setAngle(metadata,
"OBS-LONG", observatory.getLongitude(),
"Telescope longitude (+E, deg)");
394 setAngle(metadata,
"OBS-LAT", observatory.getLatitude(),
"Telescope latitude (deg)");
395 setDouble(metadata,
"OBS-ELEV", observatory.getElevation(),
"Telescope elevation (m)");
397 setDouble(metadata,
"AIRTEMP", weather.getAirTemperature(),
"Outside air temperature (C)");
398 setDouble(metadata,
"AIRPRESS", weather.getAirPressure(),
"Outdoor air pressure (P)");
399 setDouble(metadata,
"HUMIDITY", weather.getHumidity(),
"Relative humidity (%)");
400 setString(metadata,
"INSTRUMENT",
visitInfo.getInstrumentLabel(),
401 "Short name of the instrument that took this data");
403 metadata.
set(
"IDNUM",
visitInfo.getId(),
"identifier of this full focal plane exposure");
404 setDouble(metadata,
"FOCUSZ",
visitInfo.getFocusZ(),
"Defocal Distance (mm)");
413 _darkTime(getDouble(metadata,
"DARKTIME")),
415 _ut1(getDouble(metadata,
"MJD-AVG-UT1")),
416 _era(getAngle(metadata,
"AVG-ERA")),
418 lsst::geom::
SpherePoint(getAngle(metadata,
"BORE-RA"), getAngle(metadata,
"BORE-DEC"))),
420 lsst::geom::
SpherePoint(getAngle(metadata,
"BORE-AZ"), getAngle(metadata,
"BORE-ALT"))),
421 _boresightAirmass(getDouble(metadata,
"BORE-AIRMASS")),
422 _boresightRotAngle(getAngle(metadata,
"BORE-ROTANG")),
424 _observatory(getAngle(metadata,
"OBS-LONG"), getAngle(metadata,
"OBS-LAT"),
425 getDouble(metadata,
"OBS-ELEV")),
426 _weather(getDouble(metadata,
"AIRTEMP"), getDouble(metadata,
"AIRPRESS"),
427 getDouble(metadata,
"HUMIDITY")),
428 _instrumentLabel(getString(metadata,
"INSTRUMENT")),
430 _focusZ(getDouble(metadata,
"FOCUSZ")) {
432 if (metadata.
exists(key)) {
436 if (metadata.
exists(key)) {
441 if (metadata.
exists(key)) {
452 if (metadata.
exists(key)) {
453 if (metadata.
exists(
"TIMESYS")) {
454 auto timesysName = boost::algorithm::trim_right_copy(metadata.
getAsString(
"TIMESYS"));
455 if (timesysName !=
"TAI") {
460 os <<
"TIMESYS = \"" << timesysName
461 <<
"\"; VisitInfo requires TIMESYS to exist and to equal \"TAI\"";
466 "TIMESYS not found; VistitInfo requires TIMESYS to exist and to equal \"TAI\"");
473 if (metadata.
exists(key)) {
479 if (metadata.
exists(key)) {
480 _rotType = rotTypeEnumFromStr(metadata.
getAsString(key));
499 return (!lhs.isFinite() && !rhs.isFinite()) || lhs == rhs;
517 return utils::hashCombine(17, _exposureId, _exposureTime, _darkTime, _date, _ut1, _era, _boresightRaDec,
518 _boresightAzAlt, _boresightAirmass, _boresightRotAngle, _rotType, _observatory,
519 _weather, _instrumentLabel, _id, _focusZ);
525 VisitInfoSchema
const&
keys = VisitInfoSchema::get();
531 record->set(
keys.tai,
getDate().nsecs(::DateTime::TAI));
536 record->set(
keys.boresightAzAlt_az, boresightAzAlt[0]);
537 record->set(
keys.boresightAzAlt_alt, boresightAzAlt[1]);
542 record->set(
keys.latitude, observatory.getLatitude());
543 record->set(
keys.longitude, observatory.getLongitude());
544 record->set(
keys.elevation, observatory.getElevation());
546 record->set(
keys.airTemperature, weather.getAirTemperature());
547 record->set(
keys.airPressure, weather.getAirPressure());
548 record->set(
keys.humidity, weather.getHumidity());
550 record->set(
keys.version, SERIALIZATION_VERSION);
565 double _parallactic_y, _parallactic_x, result;
570 result = atan2(_parallactic_y, _parallactic_x);
575 return std::make_unique<VisitInfo>(*
this);
579 return singleClassEquals(*
this, other);
584 buffer <<
"VisitInfo(";
590 buffer <<
"UT1=" <<
getUt1() <<
", ";
591 buffer <<
"ERA=" <<
getEra() <<
", ";
596 buffer <<
"rotType=" <<
static_cast<int>(
getRotType()) <<
", ";
600 buffer <<
"id=" <<
getId() <<
", ";
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< lsst::geom::Angle > latitude
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< double > airTemperature
table::Key< std::int64_t > tai
table::Key< double > elevation
table::Key< double > humidity
table::Key< int > version
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,...
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 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...
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
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::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
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