28 #include "boost/algorithm/string/trim.hpp"
62 int constexpr SERIALIZATION_VERSION = 2;
74 double getDouble(daf::base::PropertySet
const& metadata,
std::string const& key) {
75 return metadata.exists(key) ? metadata.getAsDouble(key) :
nan;
99 return metadata.exists(key) ? metadata.getAsString(key) :
"";
110 bool setDouble(daf::base::PropertySet& metadata,
std::string const& key,
double value,
113 metadata.set(key, value);
129 return setDouble(metadata, key,
angle.asDegrees(), comment);
142 if (!value.
empty()) {
143 metadata.set(key, value);
156 case RotType::UNKNOWN:
160 case RotType::HORIZON:
166 os <<
"Unknown RotType enum: " <<
static_cast<int>(
rotType);
176 if (rotTypeName ==
"UNKNOWN") {
177 return RotType::UNKNOWN;
178 }
else if (rotTypeName ==
"SKY") {
180 }
else if (rotTypeName ==
"HORIZON") {
181 return RotType::HORIZON;
182 }
else if (rotTypeName ==
"MOUNT") {
183 return RotType::MOUNT;
186 os <<
"Unknown RotType name: \"" << rotTypeName <<
"\"";
190 class VisitInfoSchema {
196 table::Key<std::int64_t>
tai;
198 table::Key<lsst::geom::Angle>
era;
221 static VisitInfoSchema
const& get() {
222 static VisitInfoSchema instance;
227 VisitInfoSchema(
const VisitInfoSchema&) =
delete;
228 VisitInfoSchema&
operator=(
const VisitInfoSchema&) =
delete;
231 VisitInfoSchema(VisitInfoSchema&&) =
delete;
232 VisitInfoSchema&
operator=(VisitInfoSchema&&) =
delete;
239 darkTime(
schema.addField<double>(
"darktime",
"time from CCD flush to readout",
"s")),
241 "tai",
"TAI date and time at middle of exposure as nsec from unix epoch",
"nsec")),
242 ut1(
schema.addField<double>(
"ut1",
"UT1 date and time at middle of exposure",
"MJD")),
243 era(
schema.addField<
lsst::geom::
Angle>(
"era",
"earth rotation angle at middle of exposure",
246 "sky position of boresight at middle of exposure")),
251 "refracted apparent topocentric position of boresight at middle of exposure",
"")),
253 "boresightazalt_alt",
254 "refracted apparent topocentric position of boresight at middle of exposure",
"")),
256 "boresightairmass",
"airmass at boresight, relative to zenith at sea level",
"")),
258 "boresightrotangle",
"rotation angle at boresight at middle of exposure",
"")),
259 rotType(
schema.addField<int>(
"rottype",
"rotation type; see VisitInfo.getRotType for details",
263 "latitude",
"latitude of telescope (+ is east of Greenwich)",
"")),
265 elevation(
schema.addField<double>(
"elevation",
"elevation of telescope",
"")),
270 humidity(
schema.addField<double>(
"humidity",
"humidity (%)",
"")),
273 "instrumentlabel",
"Short name of the instrument that took this data",
"", 0)),
276 version(
schema.addField<int>(VERSION_KEY,
"version of this VisitInfo")),
278 idnum(
schema.addField<table::
RecordId>(
"idnum",
"identifier of this full focal plane exposure",
281 std::string const VisitInfoSchema::VERSION_KEY =
"version";
283 class VisitInfoFactory :
public table::io::PersistableFactory {
286 CatalogVector
const& catalogs)
const override {
287 VisitInfoSchema
const&
keys = VisitInfoSchema::get();
290 table::BaseRecord
const& record = catalogs.front().front();
291 int version = getVersion(record);
292 if (
version > SERIALIZATION_VERSION) {
293 throw LSST_EXCEPT(pex::exceptions::TypeError,
"Cannot read VisitInfo FITS version > " +
302 new VisitInfo(record.get(
keys.exposureId), record.get(
keys.exposureTime),
303 record.get(
keys.darkTime), ::
DateTime(record.get(
keys.tai), ::DateTime::TAI),
304 record.
get(
keys.ut1), record.get(
keys.era), record.get(
keys.boresightRaDec),
306 record.get(
keys.boresightAzAlt_alt)),
307 record.get(
keys.boresightAirmass), record.get(
keys.boresightRotAngle),
309 coord::Observatory(record.get(
keys.longitude), record.get(
keys.latitude),
310 record.get(
keys.elevation)),
311 coord::Weather(record.get(
keys.airTemperature), record.get(
keys.airPressure),
312 record.get(
keys.humidity)),
317 explicit VisitInfoFactory(
std::string const&
name) : table::io::PersistableFactory(
name) {}
320 int getVersion(table::BaseRecord
const& record)
const {
323 auto versionKey = record.getSchema().find<
int>(VisitInfoSchema::VERSION_KEY);
324 return record.get(versionKey.key);
325 }
catch (pex::exceptions::NotFoundError
const&) {
332 std::string getVisitInfoPersistenceName() {
return "VisitInfo"; }
334 VisitInfoFactory registration(getVisitInfoPersistenceName());
344 "TIME-MID",
"MJD-AVG-UT1",
"AVG-ERA",
"BORE-RA",
"BORE-DEC",
345 "BORE-AZ",
"BORE-ALT",
"BORE-AIRMASS",
"BORE-ROTANG",
"ROTTYPE",
346 "OBS-LONG",
"OBS-LAT",
"OBS-ELEV",
"AIRTEMP",
"AIRPRESS",
347 "HUMIDITY",
"INSTRUMENT",
"IDNUM"};
348 for (
auto&& key : keyList) {
349 if (metadata.
exists(key)) {
361 setDouble(metadata,
"EXPTIME",
visitInfo.getExposureTime(),
"Exposure time (sec)");
362 setDouble(metadata,
"DARKTIME",
visitInfo.getDarkTime(),
"Time from CCD flush to readout (sec)");
364 metadata.
set(
"DATE-AVG",
visitInfo.getDate().toString(::DateTime::TAI),
365 "TAI date at middle of observation");
366 metadata.
set(
"TIMESYS",
"TAI");
368 setDouble(metadata,
"MJD-AVG-UT1",
visitInfo.getUt1(),
"UT1 MJD date at ctr of obs");
369 setAngle(metadata,
"AVG-ERA",
visitInfo.getEra(),
"Earth rot ang at ctr of obs (deg)");
371 setAngle(metadata,
"BORE-RA",
boresightRaDec[0],
"ICRS RA (deg) at boresight");
372 setAngle(metadata,
"BORE-DEC",
boresightRaDec[1],
"ICRS Dec (deg) at boresight");
373 auto boresightAzAlt =
visitInfo.getBoresightAzAlt();
374 setAngle(metadata,
"BORE-AZ", boresightAzAlt[0],
"Refr app topo az (deg) at bore");
375 setAngle(metadata,
"BORE-ALT", boresightAzAlt[1],
"Refr app topo alt (deg) at bore");
376 setDouble(metadata,
"BORE-AIRMASS",
visitInfo.getBoresightAirmass(),
"Airmass at boresight");
377 setAngle(metadata,
"BORE-ROTANG",
visitInfo.getBoresightRotAngle(),
"Rotation angle (deg) at boresight");
378 metadata.
set(
"ROTTYPE", rotTypeStrFromEnum(
visitInfo.getRotType()),
"Type of rotation angle");
379 auto observatory =
visitInfo.getObservatory();
380 setAngle(metadata,
"OBS-LONG", observatory.getLongitude(),
"Telescope longitude (+E, deg)");
381 setAngle(metadata,
"OBS-LAT", observatory.getLatitude(),
"Telescope latitude (deg)");
382 setDouble(metadata,
"OBS-ELEV", observatory.getElevation(),
"Telescope elevation (m)");
384 setDouble(metadata,
"AIRTEMP", weather.getAirTemperature(),
"Outside air temperature (C)");
385 setDouble(metadata,
"AIRPRESS", weather.getAirPressure(),
"Outdoor air pressure (P)");
386 setDouble(metadata,
"HUMIDITY", weather.getHumidity(),
"Relative humidity (%)");
387 setString(metadata,
"INSTRUMENT",
visitInfo.getInstrumentLabel(),
388 "Short name of the instrument that took this data");
390 metadata.
set(
"IDNUM",
visitInfo.getId(),
"identifier of this full focal plane exposure");
399 _darkTime(getDouble(metadata,
"DARKTIME")),
401 _ut1(getDouble(metadata,
"MJD-AVG-UT1")),
402 _era(getAngle(metadata,
"AVG-ERA")),
404 lsst::geom::
SpherePoint(getAngle(metadata,
"BORE-RA"), getAngle(metadata,
"BORE-DEC"))),
406 lsst::geom::
SpherePoint(getAngle(metadata,
"BORE-AZ"), getAngle(metadata,
"BORE-ALT"))),
407 _boresightAirmass(getDouble(metadata,
"BORE-AIRMASS")),
408 _boresightRotAngle(getAngle(metadata,
"BORE-ROTANG")),
410 _observatory(getAngle(metadata,
"OBS-LONG"), getAngle(metadata,
"OBS-LAT"),
411 getDouble(metadata,
"OBS-ELEV")),
412 _weather(getDouble(metadata,
"AIRTEMP"), getDouble(metadata,
"AIRPRESS"),
413 getDouble(metadata,
"HUMIDITY")),
414 _instrumentLabel(getString(metadata,
"INSTRUMENT")),
417 if (metadata.
exists(key)) {
421 if (metadata.
exists(key)) {
426 if (metadata.
exists(key)) {
437 if (metadata.
exists(key)) {
438 if (metadata.
exists(
"TIMESYS")) {
439 auto timesysName = boost::algorithm::trim_right_copy(metadata.
getAsString(
"TIMESYS"));
440 if (timesysName !=
"TAI") {
445 os <<
"TIMESYS = \"" << timesysName
446 <<
"\"; VisitInfo requires TIMESYS to exist and to equal \"TAI\"";
451 "TIMESYS not found; VistitInfo requires TIMESYS to exist and to equal \"TAI\"");
458 if (metadata.
exists(key)) {
464 if (metadata.
exists(key)) {
465 _rotType = rotTypeEnumFromStr(metadata.
getAsString(key));
481 return utils::hashCombine(17, _exposureId, _exposureTime, _darkTime, _date, _ut1, _era, _boresightRaDec,
482 _boresightAzAlt, _boresightAirmass, _boresightRotAngle, _rotType, _observatory,
483 _weather, _instrumentLabel, _id);
489 VisitInfoSchema
const&
keys = VisitInfoSchema::get();
495 record->set(
keys.tai,
getDate().nsecs(::DateTime::TAI));
500 record->set(
keys.boresightAzAlt_az, boresightAzAlt[0]);
501 record->set(
keys.boresightAzAlt_alt, boresightAzAlt[1]);
506 record->set(
keys.latitude, observatory.getLatitude());
507 record->set(
keys.longitude, observatory.getLongitude());
508 record->set(
keys.elevation, observatory.getElevation());
510 record->set(
keys.airTemperature, weather.getAirTemperature());
511 record->set(
keys.airPressure, weather.getAirPressure());
512 record->set(
keys.humidity, weather.getHumidity());
514 record->set(
keys.version, SERIALIZATION_VERSION);
528 double _parallactic_y, _parallactic_x, result;
533 result = atan2(_parallactic_y, _parallactic_x);
538 return std::make_unique<VisitInfo>(*
this);
542 return singleClassEquals(*
this, other);
547 buffer <<
"VisitInfo(";
552 buffer <<
"UT1=" <<
getUt1() <<
", ";
553 buffer <<
"ERA=" <<
getEra() <<
", ";
558 buffer <<
"rotType=" <<
static_cast<int>(
getRotType()) <<
", ";
562 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< 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.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
std::ostream & operator<<(std::ostream &os, Measurement const &measurement)
@ UNKNOWN
Rotation angle is unknown.
FilterProperty & operator=(FilterProperty const &)=default
std::int64_t RecordId
Type used for unique IDs for records.
lsst::geom::SpherePoint SpherePoint
constexpr AngleUnit degrees
constexpr AngleUnit radians
std::size_t hashCombine(std::size_t seed) noexcept
A base class for image defects.
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override