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