Loading [MathJax]/extensions/tex2jax.js
LSST Applications g0fba68d861+aa97b6e50c,g1ec0fe41b4+f536777771,g1fd858c14a+a9301854fb,g35bb328faa+fcb1d3bbc8,g4af146b050+a5c07d5b1d,g4d2262a081+78f4f01b60,g53246c7159+fcb1d3bbc8,g56a49b3a55+9c12191793,g5a012ec0e7+3632fc3ff3,g60b5630c4e+ded28b650d,g67b6fd64d1+ed4b5058f4,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g8352419a5c+fcb1d3bbc8,g87b7deb4dc+7b42cf88bf,g8852436030+e5453db6e6,g89139ef638+ed4b5058f4,g8e3bb8577d+d38d73bdbd,g9125e01d80+fcb1d3bbc8,g94187f82dc+ded28b650d,g989de1cb63+ed4b5058f4,g9d31334357+ded28b650d,g9f33ca652e+50a8019d8c,gabe3b4be73+1e0a283bba,gabf8522325+fa80ff7197,gb1101e3267+d9fb1f8026,gb58c049af0+f03b321e39,gb89ab40317+ed4b5058f4,gcf25f946ba+e5453db6e6,gcf6002c91b+2a0c9e9e84,gd6cbbdb0b4+bb83cc51f8,gdd1046aedd+ded28b650d,gde0f65d7ad+66b3a48cb7,ge278dab8ac+d65b3c2b70,ge410e46f29+ed4b5058f4,gf23fb2af72+b7cae620c0,gf5e32f922b+fcb1d3bbc8,gf67bdafdda+ed4b5058f4,w.2025.16
LSST Data Management Base Package
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
VisitInfo.cc
Go to the documentation of this file.
1// -*- LSST-C++ -*- // fixed format comment for emacs
2/*
3 * LSST Data Management System
4 * Copyright 2016 LSST Corporation.
5 *
6 * This product includes software developed by the
7 * LSST Project (http://www.lsst.org/).
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the LSST License Statement and
20 * the GNU General Public License along with this program. If not,
21 * see <http://www.lsstcorp.org/LegalNotices/>.
22 */
23#include <cmath>
24#include <limits>
25#include <sstream>
26
27#include "boost/algorithm/string/trim.hpp"
28
30#include "lsst/pex/exceptions.h"
31#include "lsst/geom/Angle.h"
33#include "lsst/afw/table/Key.h"
34#include "lsst/afw/table/aggregates.h" // for CoordKey
36#include "lsst/afw/table/misc.h" // for RecordId
39#include "lsst/afw/table/io/CatalogVector.h" // needed, but why?
42
44
45namespace lsst {
46namespace afw {
47
48template std::shared_ptr<image::VisitInfo> table::io::PersistableFacade<image::VisitInfo>::dynamicCast(
49 std::shared_ptr<table::io::Persistable> const&);
50
51namespace image {
52
53// the following persistence-related code emulates that in Calib.cc
54
55namespace {
56
57// Version history:
58// unversioned: original VisitInfo schema
59// 1: file versioning, instrument label
60// 2: id field
61// 3: focusZ field
62// 4: observationType, scienceProgram, observationReason, object, hasSimulatedContent fields
63// 5: remove exposureId field
64int constexpr SERIALIZATION_VERSION = 5;
65
67
76double getDouble(daf::base::PropertySet const& metadata, std::string const& key) {
77 try {
78 return metadata.exists(key) && !metadata.isUndefined(key) ? metadata.getAsDouble(key) : nan;
79 } catch (pex::exceptions::TypeError& err) {
80 // If an exposure has an invalid float here (often NaN)
81 // it should put NaN in the visitInfo because it's the same
82 // as it not being a valid key.
83 return nan;
84 }
85}
86
95lsst::geom::Angle getAngle(daf::base::PropertySet const& metadata, std::string const& key) {
96 return getDouble(metadata, key) * lsst::geom::degrees;
97}
98
107std::string getString(daf::base::PropertySet const& metadata, std::string const& key) {
108 return metadata.exists(key) && !metadata.isUndefined(key) ? metadata.getAsString(key) : "";
109}
110
119bool setDouble(daf::base::PropertySet& metadata, std::string const& key, double value,
120 std::string const& comment) {
121 if (std::isfinite(value)) {
122 metadata.set(key, value);
123 return true;
124 }
125 return false;
126}
127
136bool setAngle(daf::base::PropertySet& metadata, std::string const& key, lsst::geom::Angle const& angle,
137 std::string const& comment) {
138 return setDouble(metadata, key, angle.asDegrees(), comment);
139}
140
149bool setString(daf::base::PropertySet& metadata, std::string const& key, std::string value,
150 std::string const& comment) {
151 if (!value.empty()) {
152 metadata.set(key, value);
153 return true;
154 }
155 return false;
156}
157
163std::string rotTypeStrFromEnum(RotType rotType) {
164 switch (rotType) {
165 case RotType::UNKNOWN:
166 return "UNKNOWN";
167 case RotType::SKY:
168 return "SKY";
169 case RotType::HORIZON:
170 return "HORIZON";
171 case RotType::MOUNT:
172 return "MOUNT";
173 }
174 std::ostringstream os;
175 os << "Unknown RotType enum: " << static_cast<int>(rotType);
176 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, os.str());
177}
178
184RotType rotTypeEnumFromStr(std::string const& rotTypeName) {
185 if (rotTypeName == "UNKNOWN") {
186 return RotType::UNKNOWN;
187 } else if (rotTypeName == "SKY") {
188 return RotType::SKY;
189 } else if (rotTypeName == "HORIZON") {
190 return RotType::HORIZON;
191 } else if (rotTypeName == "MOUNT") {
192 return RotType::MOUNT;
193 }
194 std::ostringstream os;
195 os << "Unknown RotType name: \"" << rotTypeName << "\"";
196 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, os.str());
197}
198
199class VisitInfoSchema {
200public:
202 table::Key<double> exposureTime;
203 table::Key<double> darkTime;
207 table::CoordKey boresightRaDec;
208 table::Key<lsst::geom::Angle> boresightAzAlt_az;
209 table::Key<lsst::geom::Angle> boresightAzAlt_alt;
210 table::Key<double> boresightAirmass;
211 table::Key<lsst::geom::Angle> boresightRotAngle;
212 table::Key<int> rotType;
213 // observatory data
216 table::Key<double> elevation;
217 // weather data
218 table::Key<double> airTemperature;
219 table::Key<double> airPressure;
220 table::Key<double> humidity;
221
222 table::Key<std::string> instrumentLabel;
224
226
227 table::Key<double> focusZ;
228
229 table::Key<std::string> observationType;
230 table::Key<std::string> scienceProgram;
231 table::Key<std::string> observationReason;
233 table::Key<afw::table::Flag> hasSimulatedContent;
234
235 static std::string const VERSION_KEY;
236
237 static VisitInfoSchema const& get(int version = SERIALIZATION_VERSION) {
238 // This is always the latest version.
239 static VisitInfoSchema instanceLatest(SERIALIZATION_VERSION);
240 // Versions that need to be preserved (e.g., a removed field) should have a separate instance.
241 static VisitInfoSchema instanceWithExposureId(4);
242 if (version < 5) {
243 return instanceWithExposureId;
244 } else {
245 return instanceLatest;
246 }
247 }
248
249 // No copying
250 VisitInfoSchema(const VisitInfoSchema&) = delete;
251 VisitInfoSchema& operator=(const VisitInfoSchema&) = delete;
252
253 // No moving
254 VisitInfoSchema(VisitInfoSchema&&) = delete;
255 VisitInfoSchema& operator=(VisitInfoSchema&&) = delete;
256
257private:
258 VisitInfoSchema(int _version) : schema() {
259 if (_version < 5) {
260 // exposureId was removed in version 5, but we need to reserve space in the schema for old files.
261 // We don't save the field record, as we don't need to read into it.
262 schema.addField<table::RecordId>("exposureid", "exposure ID", "");
263 }
264 exposureTime = schema.addField<double>("exposuretime", "exposure duration", "s");
265 darkTime = schema.addField<double>("darktime", "time from CCD flush to readout", "s");
266 tai = schema.addField<std::int64_t>(
267 "tai", "TAI date and time at middle of exposure as nsec from unix epoch", "nsec");
268 ut1 = schema.addField<double>("ut1", "UT1 date and time at middle of exposure", "MJD");
269 era = schema.addField<lsst::geom::Angle>("era", "earth rotation angle at middle of exposure", "");
270 boresightRaDec = table::CoordKey::addFields(schema, "boresightradec",
271 "sky position of boresight at middle of exposure");
272 // CoordKey is intended for ICRS coordinates, so use a pair of lsst::geom::Angle fields
273 // to save boresightAzAlt
274 boresightAzAlt_az = schema.addField<lsst::geom::Angle>(
275 "boresightazalt_az",
276 "refracted apparent topocentric position of boresight at middle of exposure", "");
277 boresightAzAlt_alt = schema.addField<lsst::geom::Angle>(
278 "boresightazalt_alt",
279 "refracted apparent topocentric position of boresight at middle of exposure", "");
280 boresightAirmass = schema.addField<double>(
281 "boresightairmass", "airmass at boresight, relative to zenith at sea level", "");
282 boresightRotAngle = schema.addField<lsst::geom::Angle>(
283 "boresightrotangle", "rotation angle at boresight at middle of exposure", "");
284 rotType =
285 schema.addField<int>("rottype", "rotation type; see VisitInfo.getRotType for details", "MJD");
286
287 // observatory data
288 latitude = schema.addField<lsst::geom::Angle>("latitude",
289 "latitude of telescope (+ is east of Greenwich)", "");
290 longitude = schema.addField<lsst::geom::Angle>("longitude", "longitude of telescope", "");
291 elevation = schema.addField<double>("elevation", "elevation of telescope", "");
292
293 // weather data
294 airTemperature = schema.addField<double>("airtemperature", "air temperature", "C");
295 airPressure = schema.addField<double>("airpressure", "air pressure", "Pascal");
296 humidity = schema.addField<double>("humidity", "humidity (%)", "");
297
298 instrumentLabel = schema.addField<std::string>(
299 "instrumentlabel", "Short name of the instrument that took this data", "", 0);
300
301 // for internal support
302 version = schema.addField<int>(VERSION_KEY, "version of this VisitInfo");
303
304 idnum = schema.addField<table::RecordId>("idnum", "identifier of this full focal plane exposure", "");
305
306 focusZ = schema.addField<double>("focusz", "defocal distance", "mm");
307
308 observationType = schema.addField<std::string>(
309 "observationType", "type of this observation (e.g. science, flat, bias)", "", 0);
310 scienceProgram = schema.addField<std::string>(
311 "scienceProgram", "observing program (survey or proposal) identifier", "", 0);
312 observationReason = schema.addField<std::string>(
313 "observationReason", "reason this observation was taken, or its purpose", "", 0);
314 object = schema.addField<std::string>("object", "object of interest or field name", "", 0);
315 hasSimulatedContent = schema.addField<afw::table::Flag>(
316 "hasSimulatedContent", "Was any part of this observation simulated?");
317 }
318};
319std::string const VisitInfoSchema::VERSION_KEY = "version";
320
321class VisitInfoFactory : public table::io::PersistableFactory {
322public:
323 std::shared_ptr<table::io::Persistable> read(InputArchive const& archive,
324 CatalogVector const& catalogs) const override {
325 LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
326 LSST_ARCHIVE_ASSERT(catalogs.front().size() == 1u);
327 table::BaseRecord const& record = catalogs.front().front();
328 int version = getVersion(record);
329 if (version > SERIALIZATION_VERSION) {
330 throw LSST_EXCEPT(pex::exceptions::TypeError, "Cannot read VisitInfo FITS version > " +
331 std::to_string(SERIALIZATION_VERSION) +
332 ", found version " +
334 }
335
336 VisitInfoSchema const& keys = VisitInfoSchema::get(version);
337 // Version-dependent fields
338 std::string instrumentLabel = version >= 1 ? record.get(keys.instrumentLabel) : "";
339 table::RecordId id = version >= 2 ? record.get(keys.idnum) : 0;
340 double focusZ = version >= 3 ? record.get(keys.focusZ) : nan;
341
342 std::string observationType = version >= 4 ? record.get(keys.observationType) : "";
343 std::string scienceProgram = version >= 4 ? record.get(keys.scienceProgram) : "";
344 std::string observationReason = version >= 4 ? record.get(keys.observationReason) : "";
345 std::string object = version >= 4 ? record.get(keys.object) : "";
346 // default false for backwards compatibility
347 bool hasSimulatedContent = version >= 4 ? record.get(keys.hasSimulatedContent) : false;
348
349 std::shared_ptr<VisitInfo> result(
350 new VisitInfo(record.get(keys.exposureTime), record.get(keys.darkTime),
351 ::DateTime(record.get(keys.tai), ::DateTime::TAI), record.get(keys.ut1),
352 record.get(keys.era), record.get(keys.boresightRaDec),
353 lsst::geom::SpherePoint(record.get(keys.boresightAzAlt_az),
354 record.get(keys.boresightAzAlt_alt)),
355 record.get(keys.boresightAirmass), record.get(keys.boresightRotAngle),
356 static_cast<RotType>(record.get(keys.rotType)),
357 coord::Observatory(record.get(keys.longitude), record.get(keys.latitude),
358 record.get(keys.elevation)),
359 coord::Weather(record.get(keys.airTemperature), record.get(keys.airPressure),
360 record.get(keys.humidity)),
361 instrumentLabel, id, focusZ, observationType, scienceProgram, observationReason,
362 object, hasSimulatedContent));
363 return result;
364 }
365
366 explicit VisitInfoFactory(std::string const& name) : table::io::PersistableFactory(name) {}
367
368private:
369 int getVersion(table::BaseRecord const& record) const {
370 try {
371 // Don't assume version is at same index as in VisitInfoSchema
372 auto versionKey = record.getSchema().find<int>(VisitInfoSchema::VERSION_KEY);
373 return record.get(versionKey.key);
374 } catch (pex::exceptions::NotFoundError const&) {
375 // un-versioned files are implicitly version 0
376 return 0;
377 }
378 }
379};
380
381std::string getVisitInfoPersistenceName() { return "VisitInfo"; }
382
383VisitInfoFactory registration(getVisitInfoPersistenceName());
384
385} // namespace
386
387namespace detail {
388
390 int nstripped = 0;
391
392 std::vector<std::string> keyList = {"EXPTIME", "DARKTIME", "DATE-AVG", "TIMESYS",
393 "TIME-MID", "MJD-AVG-UT1", "AVG-ERA", "BORE-RA",
394 "BORE-DEC", "BORE-AZ", "BORE-ALT", "BORE-AIRMASS",
395 "BORE-ROTANG", "ROTTYPE", "OBS-LONG", "OBS-LAT",
396 "OBS-ELEV", "AIRTEMP", "AIRPRESS", "HUMIDITY",
397 "INSTRUMENT", "IDNUM", "FOCUSZ", "OBSTYPE",
398 "PROGRAM", "REASON", "OBJECT", "HAS-SIMULATED-CONTENT"};
399 for (auto&& key : keyList) {
400 if (metadata.exists(key)) {
401 metadata.remove(key);
402 nstripped++;
403 }
404 }
405 return nstripped;
406}
407
408void setVisitInfoMetadata(daf::base::PropertyList& metadata, VisitInfo const& visitInfo) {
409 setDouble(metadata, "EXPTIME", visitInfo.getExposureTime(), "Exposure time (sec)");
410 setDouble(metadata, "DARKTIME", visitInfo.getDarkTime(), "Time from CCD flush to readout (sec)");
411 if (visitInfo.getDate().isValid()) {
412 metadata.set("DATE-AVG", visitInfo.getDate().toString(::DateTime::TAI),
413 "TAI date at middle of observation");
414 metadata.set("TIMESYS", "TAI");
415 }
416 setDouble(metadata, "MJD-AVG-UT1", visitInfo.getUt1(), "UT1 MJD date at ctr of obs");
417 setAngle(metadata, "AVG-ERA", visitInfo.getEra(), "Earth rot ang at ctr of obs (deg)");
418 auto boresightRaDec = visitInfo.getBoresightRaDec();
419 setAngle(metadata, "BORE-RA", boresightRaDec[0], "ICRS RA (deg) at boresight");
420 setAngle(metadata, "BORE-DEC", boresightRaDec[1], "ICRS Dec (deg) at boresight");
421 auto boresightAzAlt = visitInfo.getBoresightAzAlt();
422 setAngle(metadata, "BORE-AZ", boresightAzAlt[0], "Refr app topo az (deg) at bore");
423 setAngle(metadata, "BORE-ALT", boresightAzAlt[1], "Refr app topo alt (deg) at bore");
424 setDouble(metadata, "BORE-AIRMASS", visitInfo.getBoresightAirmass(), "Airmass at boresight");
425 setAngle(metadata, "BORE-ROTANG", visitInfo.getBoresightRotAngle(), "Rotation angle (deg) at boresight");
426 metadata.set("ROTTYPE", rotTypeStrFromEnum(visitInfo.getRotType()), "Type of rotation angle");
427 auto observatory = visitInfo.getObservatory();
428 setAngle(metadata, "OBS-LONG", observatory.getLongitude(), "Telescope longitude (+E, deg)");
429 setAngle(metadata, "OBS-LAT", observatory.getLatitude(), "Telescope latitude (deg)");
430 setDouble(metadata, "OBS-ELEV", observatory.getElevation(), "Telescope elevation (m)");
431 auto weather = visitInfo.getWeather();
432 setDouble(metadata, "AIRTEMP", weather.getAirTemperature(), "Outside air temperature (C)");
433 setDouble(metadata, "AIRPRESS", weather.getAirPressure(), "Outdoor air pressure (P)");
434 setDouble(metadata, "HUMIDITY", weather.getHumidity(), "Relative humidity (%)");
435 setString(metadata, "INSTRUMENT", visitInfo.getInstrumentLabel(),
436 "Short name of the instrument that took this data");
437 if (visitInfo.getId() != 0) {
438 metadata.set("IDNUM", visitInfo.getId(), "identifier of this full focal plane exposure");
439 }
440 setDouble(metadata, "FOCUSZ", visitInfo.getFocusZ(), "Defocal distance (mm)");
441 setString(metadata, "OBSTYPE", visitInfo.getObservationType(), "Type of this observation");
442 setString(metadata, "PROGRAM", visitInfo.getScienceProgram(),
443 "observing program (survey or proposal) identifier");
444 setString(metadata, "REASON", visitInfo.getObservationReason(),
445 "reason this observation was taken, or its purpose");
446 setString(metadata, "OBJECT", visitInfo.getObject(), "object of interest or field name");
447 metadata.set("HAS-SIMULATED-CONTENT", visitInfo.getHasSimulatedContent(),
448 "Was any part of this observation simulated?");
449}
450
451} // namespace detail
452
454 : _exposureTime(nan), // don't use getDouble because str values are also accepted
455 _darkTime(getDouble(metadata, "DARKTIME")),
456 _date(),
457 _ut1(getDouble(metadata, "MJD-AVG-UT1")),
458 _era(getAngle(metadata, "AVG-ERA")),
459 _boresightRaDec(
460 lsst::geom::SpherePoint(getAngle(metadata, "BORE-RA"), getAngle(metadata, "BORE-DEC"))),
461 _boresightAzAlt(
462 lsst::geom::SpherePoint(getAngle(metadata, "BORE-AZ"), getAngle(metadata, "BORE-ALT"))),
463 _boresightAirmass(getDouble(metadata, "BORE-AIRMASS")),
464 _boresightRotAngle(getAngle(metadata, "BORE-ROTANG")),
465 _rotType(RotType::UNKNOWN),
466 _observatory(getAngle(metadata, "OBS-LONG"), getAngle(metadata, "OBS-LAT"),
467 getDouble(metadata, "OBS-ELEV")),
468 _weather(getDouble(metadata, "AIRTEMP"), getDouble(metadata, "AIRPRESS"),
469 getDouble(metadata, "HUMIDITY")),
470 _instrumentLabel(getString(metadata, "INSTRUMENT")),
471 _id(0),
472 _focusZ(getDouble(metadata, "FOCUSZ")),
473 _observationType(getString(metadata, "OBSTYPE")),
474 _scienceProgram(getString(metadata, "PROGRAM")),
475 _observationReason(getString(metadata, "REASON")),
476 _object(getString(metadata, "OBJECT")),
477 // default false for backwards compatibility
478 _hasSimulatedContent(false) {
479 auto key = "IDNUM";
480 if (metadata.exists(key) && !metadata.isUndefined(key)) {
481 _id = metadata.getAsInt64(key);
482 }
483
484 key = "EXPTIME";
485 if (metadata.exists(key) && !metadata.isUndefined(key)) {
486 try {
487 _exposureTime = metadata.getAsDouble(key);
488 } catch (lsst::pex::exceptions::TypeError& err) {
489 // some old exposures have EXPTIME stored as a string
490 std::string exptimeStr = metadata.getAsString(key);
491 _exposureTime = std::stod(exptimeStr);
492 }
493 }
494
495 key = "DATE-AVG";
496 if (metadata.exists(key) && !metadata.isUndefined(key)) {
497 if (metadata.exists("TIMESYS")) {
498 auto timesysName = boost::algorithm::trim_right_copy(metadata.getAsString("TIMESYS"));
499 if (timesysName != "TAI") {
500 // rather than try to deal with all the possible choices, which requires
501 // appending or deleting a "Z", depending on the time system, just give up.
502 // VisitInfo should be used on FITS headers that have been sanitized!
503 std::ostringstream os;
504 os << "TIMESYS = \"" << timesysName
505 << "\"; VisitInfo requires TIMESYS to exist and to equal \"TAI\"";
506 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError, os.str());
507 }
508 } else {
509 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
510 "TIMESYS not found; VistitInfo requires TIMESYS to exist and to equal \"TAI\"");
511 }
512 _date = ::DateTime(boost::algorithm::trim_right_copy(metadata.getAsString(key)), ::DateTime::TAI);
513 } else {
514 // DATE-AVG not found. For backwards compatibility look for TIME-MID, an outdated LSST keyword
515 // whose time system was UTC, despite a FITS comment claiming it was TAI. Ignore TIMESYS.
516 key = "TIME-MID";
517 if (metadata.exists(key) && !metadata.isUndefined(key)) {
518 _date = ::DateTime(boost::algorithm::trim_right_copy(metadata.getAsString(key)), ::DateTime::UTC);
519 }
520 }
521
522 key = "ROTTYPE";
523 if (metadata.exists(key) && !metadata.isUndefined(key)) {
524 _rotType = rotTypeEnumFromStr(metadata.getAsString(key));
525 }
526
527 key = "HAS-SIMULATED-CONTENT";
528 if (metadata.exists(key) && !metadata.isUndefined(key)) {
529 _hasSimulatedContent = metadata.getAsBool(key);
530 }
531}
532
539bool _eqOrNan(double lhs, double rhs) noexcept { return (std::isnan(lhs) && std::isnan(rhs)) || lhs == rhs; }
540
548 return (!lhs.isFinite() && !rhs.isFinite()) || lhs == rhs;
549}
550
551bool VisitInfo::operator==(VisitInfo const& other) const {
552 return _eqOrNan(_exposureTime, other.getExposureTime()) && _eqOrNan(_darkTime, other.getDarkTime()) &&
553 _date == other.getDate() && _eqOrNan(_ut1, other.getUt1()) && _eqOrNan(_era, other.getEra()) &&
554 _eqOrNonFinite(_boresightRaDec, other.getBoresightRaDec()) &&
555 _eqOrNonFinite(_boresightAzAlt, other.getBoresightAzAlt()) &&
556 _eqOrNan(_boresightAirmass, other.getBoresightAirmass()) &&
557 _eqOrNan(_boresightRotAngle, other.getBoresightRotAngle()) && _rotType == other.getRotType() &&
558 _observatory == other.getObservatory() && _weather == other.getWeather() &&
559 _instrumentLabel == other.getInstrumentLabel() && _id == other.getId() &&
560 _eqOrNan(_focusZ, other.getFocusZ()) && _observationType == other.getObservationType() &&
561 _scienceProgram == other.getScienceProgram() &&
562 _observationReason == other.getObservationReason() && _object == other.getObject() &&
563 _hasSimulatedContent == other.getHasSimulatedContent();
564}
565
567 // Completely arbitrary seed
568 return cpputils::hashCombine(17, _exposureTime, _darkTime, _date, _ut1, _era, _boresightRaDec,
569 _boresightAzAlt, _boresightAirmass, _boresightRotAngle, _rotType, _observatory,
570 _weather, _instrumentLabel, _id, _focusZ, _observationType, _scienceProgram,
571 _observationReason, _object, _hasSimulatedContent);
572}
573
574std::string VisitInfo::getPersistenceName() const { return getVisitInfoPersistenceName(); }
575
577 VisitInfoSchema const& keys = VisitInfoSchema::get();
578 table::BaseCatalog cat = handle.makeCatalog(keys.schema);
580 record->set(keys.exposureTime, getExposureTime());
581 record->set(keys.darkTime, getDarkTime());
582 record->set(keys.tai, getDate().nsecs(::DateTime::TAI));
583 record->set(keys.ut1, getUt1());
584 record->set(keys.era, getEra());
585 record->set(keys.boresightRaDec, getBoresightRaDec());
586 auto boresightAzAlt = getBoresightAzAlt();
587 record->set(keys.boresightAzAlt_az, boresightAzAlt[0]);
588 record->set(keys.boresightAzAlt_alt, boresightAzAlt[1]);
589 record->set(keys.boresightAirmass, getBoresightAirmass());
590 record->set(keys.boresightRotAngle, getBoresightRotAngle());
591 record->set(keys.rotType, static_cast<int>(getRotType()));
592 auto observatory = getObservatory();
593 record->set(keys.latitude, observatory.getLatitude());
594 record->set(keys.longitude, observatory.getLongitude());
595 record->set(keys.elevation, observatory.getElevation());
596 auto weather = getWeather();
597 record->set(keys.airTemperature, weather.getAirTemperature());
598 record->set(keys.airPressure, weather.getAirPressure());
599 record->set(keys.humidity, weather.getHumidity());
600 record->set(keys.instrumentLabel, getInstrumentLabel());
601 record->set(keys.version, SERIALIZATION_VERSION);
602 record->set(keys.idnum, getId());
603 record->set(keys.focusZ, getFocusZ());
604 record->set(keys.observationType, getObservationType());
605 record->set(keys.scienceProgram, getScienceProgram());
606 record->set(keys.observationReason, getObservationReason());
607 record->set(keys.object, getObject());
608 record->set(keys.hasSimulatedContent, getHasSimulatedContent());
609 handle.saveCatalog(cat);
610}
611
612lsst::geom::Angle VisitInfo::getLocalEra() const { return getEra() + getObservatory().getLongitude(); }
613
615
621 double _parallactic_y, _parallactic_x, result;
622 _parallactic_y = sin(getBoresightHourAngle().asRadians());
623 _parallactic_x =
624 cos((getBoresightRaDec()[1]).asRadians()) * tan(getObservatory().getLatitude().asRadians()) -
625 sin((getBoresightRaDec()[1]).asRadians()) * cos(getBoresightHourAngle().asRadians());
626 result = atan2(_parallactic_y, _parallactic_x);
627 return result * lsst::geom::radians;
628}
629
631 return std::make_unique<VisitInfo>(*this);
632}
633
634bool VisitInfo::equals(typehandling::Storable const& other) const noexcept {
635 return singleClassEquals(*this, other);
636}
637
639 std::stringstream buffer;
640 buffer << "VisitInfo(";
641 buffer << "exposureTime=" << getExposureTime() << ", ";
642 buffer << "darkTime=" << getDarkTime() << ", ";
643 buffer << "date=" << (getDate().isValid() ? getDate().toString(daf::base::DateTime::TAI) : "<invalid>")
644 << ", ";
645 buffer << "UT1=" << getUt1() << ", ";
646 buffer << "ERA=" << getEra() << ", ";
647 buffer << "boresightRaDec=" << getBoresightRaDec() << ", ";
648 buffer << "boresightAzAlt=" << getBoresightAzAlt() << ", ";
649 buffer << "boresightAirmass=" << getBoresightAirmass() << ", ";
650 buffer << "boresightRotAngle=" << getBoresightRotAngle() << ", ";
651 buffer << "rotType=" << static_cast<int>(getRotType()) << ", ";
652 buffer << "observatory=" << getObservatory() << ", ";
653 buffer << "weather=" << getWeather() << ", ";
654 buffer << "instrumentLabel='" << getInstrumentLabel() << "', ";
655 buffer << "id=" << getId() << ", ";
656 buffer << "focusZ=" << getFocusZ() << ", ";
657 buffer << "observationType='" << getObservationType() << "', ";
658 buffer << "scienceProgram='" << getScienceProgram() << "', ";
659 buffer << "observationReason='" << getObservationReason() << "', ";
660 buffer << "object='" << getObject() << "', ";
661 buffer << "hasSimulatedContent=" << std::boolalpha << getHasSimulatedContent();
662 buffer << ")";
663 return buffer.str();
664}
665
667 os << visitInfo.toString();
668 return os;
669}
670
671} // namespace image
672} // namespace afw
673} // namespace lsst
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
Definition Persistable.h:48
T boolalpha(T... args)
Hold the location of an observatory.
Definition Observatory.h:43
Basic weather information sufficient for a simple model for air mass or refraction.
Definition Weather.h:38
Information about a single exposure of an imaging camera.
Definition VisitInfo.h:68
std::string getPersistenceName() const override
Return the unique name used to persist this object and look up its factory.
Definition VisitInfo.cc:574
daf::base::DateTime getDate() const
get uniform date and time at middle of exposure
Definition VisitInfo.h:150
coord::Weather getWeather() const
get basic weather information
Definition VisitInfo.h:185
VisitInfo(double exposureTime, double darkTime, daf::base::DateTime const &date, double ut1, lsst::geom::Angle const &era, lsst::geom::SpherePoint const &boresightRaDec, lsst::geom::SpherePoint const &boresightAzAlt, double boresightAirmass, lsst::geom::Angle const &boresightRotAngle, RotType const &rotType, coord::Observatory const &observatory, coord::Weather const &weather, std::string const &instrumentLabel, table::RecordId const &id, double focusZ, std::string const &observationType, std::string const &scienceProgram, std::string const &observationReason, std::string const &object, bool hasSimulatedContent)
Construct a VisitInfo.
Definition VisitInfo.h:98
lsst::geom::Angle getLocalEra() const
Definition VisitInfo.cc:612
double getUt1() const
get UT1 (universal time) MJD date at middle of exposure
Definition VisitInfo.h:153
double getBoresightAirmass() const
get airmass at the boresight, relative to zenith at sea level (and at the middle of the exposure,...
Definition VisitInfo.h:168
std::string getScienceProgram() const
Definition VisitInfo.h:203
lsst::geom::Angle getBoresightHourAngle() const
Definition VisitInfo.cc:614
lsst::geom::Angle getBoresightRotAngle() const
Get rotation angle at boresight at middle of exposure.
Definition VisitInfo.h:176
std::size_t hash_value() const noexcept override
Return a hash of this object.
Definition VisitInfo.cc:566
bool operator==(VisitInfo const &other) const
Definition VisitInfo.cc:551
lsst::geom::Angle getEra() const
get earth rotation angle at middle of exposure
Definition VisitInfo.h:156
table::RecordId getId() const
Definition VisitInfo.h:197
std::string getObservationReason() const
Definition VisitInfo.h:204
std::string toString() const override
Create a string representation of this object.
Definition VisitInfo.cc:638
double getExposureTime() const
get exposure duration (shutter open time); (sec)
Definition VisitInfo.h:144
RotType getRotType() const
get rotation type of boresightRotAngle
Definition VisitInfo.h:179
bool equals(typehandling::Storable const &other) const noexcept override
Compare this object to another Storable.
Definition VisitInfo.cc:634
lsst::geom::SpherePoint getBoresightAzAlt() const
get refracted apparent topocentric Az/Alt position at the boresight (and at the middle of the exposur...
Definition VisitInfo.h:164
std::string getObject() const
Definition VisitInfo.h:205
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)
Definition VisitInfo.h:160
std::shared_ptr< typehandling::Storable > cloneStorable() const override
Create a new VisitInfo that is a copy of this one.
Definition VisitInfo.cc:630
std::string getInstrumentLabel() const
Definition VisitInfo.h:195
bool getHasSimulatedContent() const
Definition VisitInfo.h:206
double getDarkTime() const
get time from CCD flush to exposure readout, including shutter open time (despite the name); (sec)
Definition VisitInfo.h:147
coord::Observatory getObservatory() const
get observatory longitude, latitude and elevation
Definition VisitInfo.h:182
lsst::geom::Angle getBoresightParAngle() const
Get parallactic angle at the boresight.
Definition VisitInfo.cc:616
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
Definition VisitInfo.cc:576
std::string getObservationType() const
Definition VisitInfo.h:202
Base class for all records.
Definition BaseRecord.h:31
std::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
Definition Catalog.h:489
A FunctorKey used to get or set celestial coordinates from a pair of lsst::geom::Angle keys.
Definition aggregates.h:292
static CoordKey addFields(afw::table::Schema &schema, std::string const &name, std::string const &doc)
Add a pair of _ra, _dec fields to a Schema, and return a CoordKey that points to them.
A class used as a handle to a particular field in a table.
Definition Key.h:53
Defines the fields and offsets for a table.
Definition Schema.h:51
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.
A base class for factory classes used to reconstruct objects from records.
io::OutputArchiveHandle OutputArchiveHandle
Interface supporting iteration over heterogenous containers.
Definition Storable.h:58
static bool singleClassEquals(T const &lhs, Storable const &rhs)
Test if a Storable is of a particular class and equal to another object.
Definition Storable.h:151
Class for handling dates/times, including MJD, UTC, and TAI.
Definition DateTime.h:64
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.
Definition PropertySet.h:67
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).
bool isUndefined(std::string const &name) const
Determine if a name (possibly hierarchical) has a defined value.
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
A class representing an angle.
Definition Angle.h:128
constexpr double asDegrees() const noexcept
Return an Angle's value in degrees.
Definition Angle.h:176
Point in an unspecified spherical coordinate system.
Definition SpherePoint.h:57
Reports errors from accepting an object of an unexpected or inappropriate type.
Definition Runtime.h:167
T isfinite(T... args)
T isnan(T... args)
int stripVisitInfoKeywords(daf::base::PropertySet &metadata)
Remove VisitInfo-related keywords from the metadata.
Definition VisitInfo.cc:389
void setVisitInfoMetadata(daf::base::PropertyList &metadata, VisitInfo const &visitInfo)
Set FITS metadata from a VisitInfo.
Definition VisitInfo.cc:408
bool _eqOrNonFinite(lsst::geom::SpherePoint const &lhs, lsst::geom::SpherePoint const &rhs) noexcept
Test whether two SpherePoints are exactly equal or invalid.
Definition VisitInfo.cc:547
std::ostream & operator<<(std::ostream &os, Measurement const &measurement)
Definition PhotoCalib.cc:48
RotType
Type of rotation.
Definition VisitInfo.h:45
bool _eqOrNan(double lhs, double rhs) noexcept
Test whether two numbers are exactly equal or both NaN.
Definition VisitInfo.cc:539
CatalogT< BaseRecord > BaseCatalog
Definition fwd.h:72
std::int64_t RecordId
Type used for unique IDs for records.
Definition misc.h:21
std::size_t hashCombine(std::size_t seed) noexcept
Combine hashes.
Definition hashCombine.h:35
AngleUnit constexpr degrees
constant with units of degrees
Definition Angle.h:110
AngleUnit constexpr radians
constant with units of radians
Definition Angle.h:109
T nan(T... args)
T quiet_NaN(T... args)
T stod(T... args)
T str(T... args)
T to_string(T... args)
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override