27 #include "boost/algorithm/string/trim.hpp"
61 int constexpr SERIALIZATION_VERSION = 2;
73 double getDouble(daf::base::PropertySet
const& metadata,
std::string const& key) {
75 return metadata.exists(key) ? metadata.getAsDouble(key) :
nan;
76 }
catch (pex::exceptions::TypeError& err) {
105 return metadata.exists(key) ? metadata.getAsString(key) :
"";
116 bool setDouble(daf::base::PropertySet& metadata,
std::string const& key,
double value,
119 metadata.set(key, value);
135 return setDouble(metadata, key,
angle.asDegrees(), comment);
148 if (!value.
empty()) {
149 metadata.set(key, value);
162 case RotType::UNKNOWN:
166 case RotType::HORIZON:
172 os <<
"Unknown RotType enum: " <<
static_cast<int>(
rotType);
182 if (rotTypeName ==
"UNKNOWN") {
183 return RotType::UNKNOWN;
184 }
else if (rotTypeName ==
"SKY") {
186 }
else if (rotTypeName ==
"HORIZON") {
187 return RotType::HORIZON;
188 }
else if (rotTypeName ==
"MOUNT") {
189 return RotType::MOUNT;
192 os <<
"Unknown RotType name: \"" << rotTypeName <<
"\"";
196 class VisitInfoSchema {
202 table::Key<std::int64_t>
tai;
204 table::Key<lsst::geom::Angle>
era;
227 static VisitInfoSchema
const& get() {
228 static VisitInfoSchema instance;
233 VisitInfoSchema(
const VisitInfoSchema&) =
delete;
234 VisitInfoSchema&
operator=(
const VisitInfoSchema&) =
delete;
237 VisitInfoSchema(VisitInfoSchema&&) =
delete;
238 VisitInfoSchema&
operator=(VisitInfoSchema&&) =
delete;
245 darkTime(
schema.addField<double>(
"darktime",
"time from CCD flush to readout",
"s")),
247 "tai",
"TAI date and time at middle of exposure as nsec from unix epoch",
"nsec")),
248 ut1(
schema.addField<double>(
"ut1",
"UT1 date and time at middle of exposure",
"MJD")),
252 "sky position of boresight at middle of exposure")),
257 "refracted apparent topocentric position of boresight at middle of exposure",
"")),
259 "boresightazalt_alt",
260 "refracted apparent topocentric position of boresight at middle of exposure",
"")),
262 "boresightairmass",
"airmass at boresight, relative to zenith at sea level",
"")),
264 "boresightrotangle",
"rotation angle at boresight at middle of exposure",
"")),
265 rotType(
schema.addField<int>(
"rottype",
"rotation type; see VisitInfo.getRotType for details",
269 "latitude",
"latitude of telescope (+ is east of Greenwich)",
"")),
271 elevation(
schema.addField<double>(
"elevation",
"elevation of telescope",
"")),
276 humidity(
schema.addField<double>(
"humidity",
"humidity (%)",
"")),
279 "instrumentlabel",
"Short name of the instrument that took this data",
"", 0)),
282 version(
schema.addField<int>(VERSION_KEY,
"version of this VisitInfo")),
284 idnum(
schema.addField<table::
RecordId>(
"idnum",
"identifier of this full focal plane exposure",
287 std::string const VisitInfoSchema::VERSION_KEY =
"version";
289 class VisitInfoFactory :
public table::io::PersistableFactory {
292 CatalogVector
const& catalogs)
const override {
293 VisitInfoSchema
const&
keys = VisitInfoSchema::get();
296 table::BaseRecord
const& record = catalogs.front().front();
297 int version = getVersion(record);
298 if (
version > SERIALIZATION_VERSION) {
299 throw LSST_EXCEPT(pex::exceptions::TypeError,
"Cannot read VisitInfo FITS version > " +
308 new VisitInfo(record.get(
keys.exposureId), record.get(
keys.exposureTime),
309 record.get(
keys.darkTime), ::
DateTime(record.get(
keys.tai), ::DateTime::TAI),
310 record.
get(
keys.ut1), record.get(
keys.era), record.get(
keys.boresightRaDec),
312 record.get(
keys.boresightAzAlt_alt)),
313 record.get(
keys.boresightAirmass), record.get(
keys.boresightRotAngle),
315 coord::Observatory(record.get(
keys.longitude), record.get(
keys.latitude),
316 record.get(
keys.elevation)),
317 coord::Weather(record.get(
keys.airTemperature), record.get(
keys.airPressure),
318 record.get(
keys.humidity)),
323 explicit VisitInfoFactory(
std::string const&
name) : table::io::PersistableFactory(
name) {}
326 int getVersion(table::BaseRecord
const& record)
const {
329 auto versionKey = record.getSchema().find<
int>(VisitInfoSchema::VERSION_KEY);
330 return record.get(versionKey.key);
331 }
catch (pex::exceptions::NotFoundError
const&) {
338 std::string getVisitInfoPersistenceName() {
return "VisitInfo"; }
340 VisitInfoFactory registration(getVisitInfoPersistenceName());
350 "TIME-MID",
"MJD-AVG-UT1",
"AVG-ERA",
"BORE-RA",
"BORE-DEC",
351 "BORE-AZ",
"BORE-ALT",
"BORE-AIRMASS",
"BORE-ROTANG",
"ROTTYPE",
352 "OBS-LONG",
"OBS-LAT",
"OBS-ELEV",
"AIRTEMP",
"AIRPRESS",
353 "HUMIDITY",
"INSTRUMENT",
"IDNUM"};
354 for (
auto&& key : keyList) {
355 if (metadata.
exists(key)) {
367 setDouble(metadata,
"EXPTIME",
visitInfo.getExposureTime(),
"Exposure time (sec)");
368 setDouble(metadata,
"DARKTIME",
visitInfo.getDarkTime(),
"Time from CCD flush to readout (sec)");
370 metadata.
set(
"DATE-AVG",
visitInfo.getDate().toString(::DateTime::TAI),
371 "TAI date at middle of observation");
372 metadata.
set(
"TIMESYS",
"TAI");
374 setDouble(metadata,
"MJD-AVG-UT1",
visitInfo.getUt1(),
"UT1 MJD date at ctr of obs");
375 setAngle(metadata,
"AVG-ERA",
visitInfo.getEra(),
"Earth rot ang at ctr of obs (deg)");
377 setAngle(metadata,
"BORE-RA",
boresightRaDec[0],
"ICRS RA (deg) at boresight");
378 setAngle(metadata,
"BORE-DEC",
boresightRaDec[1],
"ICRS Dec (deg) at boresight");
379 auto boresightAzAlt =
visitInfo.getBoresightAzAlt();
380 setAngle(metadata,
"BORE-AZ", boresightAzAlt[0],
"Refr app topo az (deg) at bore");
381 setAngle(metadata,
"BORE-ALT", boresightAzAlt[1],
"Refr app topo alt (deg) at bore");
382 setDouble(metadata,
"BORE-AIRMASS",
visitInfo.getBoresightAirmass(),
"Airmass at boresight");
383 setAngle(metadata,
"BORE-ROTANG",
visitInfo.getBoresightRotAngle(),
"Rotation angle (deg) at boresight");
384 metadata.
set(
"ROTTYPE", rotTypeStrFromEnum(
visitInfo.getRotType()),
"Type of rotation angle");
385 auto observatory =
visitInfo.getObservatory();
386 setAngle(metadata,
"OBS-LONG", observatory.getLongitude(),
"Telescope longitude (+E, deg)");
387 setAngle(metadata,
"OBS-LAT", observatory.getLatitude(),
"Telescope latitude (deg)");
388 setDouble(metadata,
"OBS-ELEV", observatory.getElevation(),
"Telescope elevation (m)");
390 setDouble(metadata,
"AIRTEMP", weather.getAirTemperature(),
"Outside air temperature (C)");
391 setDouble(metadata,
"AIRPRESS", weather.getAirPressure(),
"Outdoor air pressure (P)");
392 setDouble(metadata,
"HUMIDITY", weather.getHumidity(),
"Relative humidity (%)");
393 setString(metadata,
"INSTRUMENT",
visitInfo.getInstrumentLabel(),
394 "Short name of the instrument that took this data");
396 metadata.
set(
"IDNUM",
visitInfo.getId(),
"identifier of this full focal plane exposure");
405 _darkTime(getDouble(metadata,
"DARKTIME")),
407 _ut1(getDouble(metadata,
"MJD-AVG-UT1")),
408 _era(getAngle(metadata,
"AVG-ERA")),
413 _boresightAirmass(getDouble(metadata,
"BORE-AIRMASS")),
414 _boresightRotAngle(getAngle(metadata,
"BORE-ROTANG")),
416 _observatory(getAngle(metadata,
"OBS-LONG"), getAngle(metadata,
"OBS-LAT"),
417 getDouble(metadata,
"OBS-ELEV")),
418 _weather(getDouble(metadata,
"AIRTEMP"), getDouble(metadata,
"AIRPRESS"),
419 getDouble(metadata,
"HUMIDITY")),
420 _instrumentLabel(getString(metadata,
"INSTRUMENT")),
423 if (metadata.
exists(key)) {
427 if (metadata.
exists(key)) {
432 if (metadata.
exists(key)) {
443 if (metadata.
exists(key)) {
444 if (metadata.
exists(
"TIMESYS")) {
445 auto timesysName = boost::algorithm::trim_right_copy(metadata.
getAsString(
"TIMESYS"));
446 if (timesysName !=
"TAI") {
451 os <<
"TIMESYS = \"" << timesysName
452 <<
"\"; VisitInfo requires TIMESYS to exist and to equal \"TAI\"";
457 "TIMESYS not found; VistitInfo requires TIMESYS to exist and to equal \"TAI\"");
464 if (metadata.
exists(key)) {
470 if (metadata.
exists(key)) {
471 _rotType = rotTypeEnumFromStr(metadata.
getAsString(key));
490 return (!lhs.isFinite() && !rhs.isFinite()) || lhs == rhs;
507 return utils::hashCombine(17, _exposureId, _exposureTime, _darkTime, _date, _ut1, _era, _boresightRaDec,
508 _boresightAzAlt, _boresightAirmass, _boresightRotAngle, _rotType, _observatory,
509 _weather, _instrumentLabel, _id);
515 VisitInfoSchema
const&
keys = VisitInfoSchema::get();
521 record->set(
keys.tai,
getDate().nsecs(::DateTime::TAI));
526 record->set(
keys.boresightAzAlt_az, boresightAzAlt[0]);
527 record->set(
keys.boresightAzAlt_alt, boresightAzAlt[1]);
532 record->set(
keys.latitude, observatory.getLatitude());
533 record->set(
keys.longitude, observatory.getLongitude());
534 record->set(
keys.elevation, observatory.getElevation());
536 record->set(
keys.airTemperature, weather.getAirTemperature());
537 record->set(
keys.airPressure, weather.getAirPressure());
538 record->set(
keys.humidity, weather.getHumidity());
540 record->set(
keys.version, SERIALIZATION_VERSION);
554 double _parallactic_y, _parallactic_x,
result;
559 result = atan2(_parallactic_y, _parallactic_x);
564 return std::make_unique<VisitInfo>(*
this);
568 return singleClassEquals(*
this, other);
573 buffer <<
"VisitInfo(";
579 buffer <<
"UT1=" <<
getUt1() <<
", ";
580 buffer <<
"ERA=" <<
getEra() <<
", ";
585 buffer <<
"rotType=" <<
static_cast<int>(
getRotType()) <<
", ";
589 buffer <<
"id=" <<
getId();
table::Key< std::string > name
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
table::Key< double > angle
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
#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
table::RecordId getExposureId() const
get exposure ID
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.
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.
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 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).
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.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
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.
FilterProperty & operator=(FilterProperty const &)=default
lsst::geom::SpherePoint SpherePoint
std::int64_t RecordId
Type used for unique IDs for records.
std::size_t hashCombine(std::size_t seed) noexcept
Combine hashes.
constexpr AngleUnit degrees
constant with units of degrees
constexpr AngleUnit radians
constant with units of radians
double sin(Angle const &a)
double tan(Angle const &a)
double cos(Angle const &a)
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