35 #include "muse_astro.h"
37 #include "muse_pfits.h"
43 #define MUSE_ASTRO_AIRMASS_APPROX 2
79 muse_astro_get_zenith_distance(
double aHourAngle,
double aDelta,
82 double p0 = sin(aLatitude) * sin(aDelta),
83 p1 = cos(aLatitude) * cos(aDelta),
84 z = p0 + cos(aHourAngle) * p1;
85 return fabs(z) < FLT_EPSILON ? 0. : z;
88 #if MUSE_ASTRO_AIRMASS_APPROX == 0
103 muse_astro_get_airmass_youngirvine(
double aSecZ)
105 return aSecZ * (1. - 0.0012 * (pow(aSecZ, 2) - 1.));
109 #if MUSE_ASTRO_AIRMASS_APPROX == 1
126 muse_astro_get_airmass_young(
double aCosZt)
128 return (1.002432 * aCosZt*aCosZt + 0.148386 * aCosZt + 0.0096467)
129 / (aCosZt*aCosZt*aCosZt + 0.149864 * aCosZt*aCosZt + 0.0102963 * aCosZt
134 #if MUSE_ASTRO_AIRMASS_APPROX == 2
150 muse_astro_get_airmass_hardie(
double aSecZ)
152 double secm1 = aSecZ - 1;
153 return aSecZ - 0.0018167 * secm1 - 0.002875 * secm1*secm1
154 - 0.0008083 * secm1*secm1*secm1;
199 double aExptime,
double aLatitude)
201 cpl_ensure(aRA >= 0. && aRA < 360. && aDEC >= -90. && aDEC <= 90. &&
202 aLST >= 0. && aLST < 86400. && aLatitude >= -90. && aLatitude <= 90.,
203 CPL_ERROR_ILLEGAL_INPUT, -1.);
204 cpl_ensure(aExptime >= 0., CPL_ERROR_ILLEGAL_INPUT, -1.);
207 double HA = aLST * 15./3600. - aRA;
217 double delta = aDEC * CPL_MATH_RAD_DEG,
218 latitude = aLatitude * CPL_MATH_RAD_DEG,
219 hourangle = HA * CPL_MATH_RAD_DEG;
224 double cosz = muse_astro_get_zenith_distance(hourangle, delta, latitude);
225 #if MUSE_ASTRO_AIRMASS_APPROX == 2
226 double z = acos(cosz) * CPL_MATH_DEG_RAD;
227 const double zlimit = 80.;
229 cpl_msg_warning(__func__,
"Zenith angle %f > %f!", z, zlimit);
232 if (cosz == 0. || fabs(1. / cosz) < FLT_EPSILON ||
233 acos(cosz) > CPL_MATH_PI_2) {
234 cpl_msg_error(__func__,
"Airmass computation unsuccessful. Object is below "
235 "the horizon at start (z = %f).", acos(cosz) * CPL_MATH_DEG_RAD);
236 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
241 #if MUSE_ASTRO_AIRMASS_APPROX == 0
242 airmass = muse_astro_get_airmass_youngirvine(1. / cosz);
244 #if MUSE_ASTRO_AIRMASS_APPROX == 1
245 airmass = muse_astro_get_airmass_young(cosz);
247 #if MUSE_ASTRO_AIRMASS_APPROX == 2
248 airmass = muse_astro_get_airmass_hardie(1. / cosz);
250 #if MUSE_ASTRO_AIRMASS_APPROX > 2
251 #error set MUSE_ASTRO_AIRMASS_APPROX to 0, 1, 2
258 const double weights[] = {1./6., 2./3., 1./6.};
259 const int nweights =
sizeof(weights) /
sizeof(
double);
261 double timeStep = aExptime / (nweights - 1) * 15./3600. * CPL_MATH_RAD_DEG;
262 airmass *= weights[0];
265 for (i = 1; i < nweights; i++) {
266 cosz = muse_astro_get_zenith_distance(hourangle + i * timeStep, delta, latitude);
267 #if MUSE_ASTRO_AIRMASS_APPROX == 2
268 z = acos(cosz) * CPL_MATH_DEG_RAD;
270 cpl_msg_warning(__func__,
"Zenith angle %f > %f!", z, zlimit);
273 if (cosz == 0. || fabs(1. / cosz) < FLT_EPSILON ||
274 acos(cosz) > CPL_MATH_PI_2) {
275 cpl_msg_error(__func__,
"Airmass computation unsuccessful at timeStep. "
276 "Object is below the horizon at %s exposure (z=%f).",
277 i == 1 ?
"mid" :
"end", acos(cosz) * CPL_MATH_DEG_RAD);
278 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
282 #if MUSE_ASTRO_AIRMASS_APPROX == 0
283 airmass += weights[i] * muse_astro_get_airmass_youngirvine(1. / cosz);
285 #if MUSE_ASTRO_AIRMASS_APPROX == 1
286 airmass += weights[i] * muse_astro_get_airmass_young(cosz);
288 #if MUSE_ASTRO_AIRMASS_APPROX == 2
289 airmass += weights[i] * muse_astro_get_airmass_hardie(1. / cosz);
294 #if MUSE_ASTRO_AIRMASS_APPROX == 0
296 const double airmasslimit = 4.;
297 if (airmass > airmasslimit) {
298 cpl_msg_warning(__func__,
"Airmass larger than %f", airmasslimit);
324 cpl_ensure(aHeader, CPL_ERROR_NULL_INPUT, -1.);
326 cpl_errorstate prestate = cpl_errorstate_get();
329 cpl_errorstate_set(prestate);
335 prestate = cpl_errorstate_get();
337 if (!cpl_errorstate_is_equal(prestate)) {
340 prestate = cpl_errorstate_get();
342 if (!cpl_errorstate_is_equal(prestate)) {
345 prestate = cpl_errorstate_get();
347 if (!cpl_errorstate_is_equal(prestate)) {
350 prestate = cpl_errorstate_get();
352 if (!cpl_errorstate_is_equal(prestate)) {
360 if (airmass < 0. && airm1 != 0. && airm2 != 0.) {
362 double average = (airm1 + airm2) / 2.;
363 cpl_msg_warning(__func__,
"airmass computation unsuccessful (%s), using simple "
364 "average of start and end values (%f)", cpl_error_get_message(),
370 cpl_msg_debug(__func__,
"airmass=%f (header %f, %f)", airmass, airm1, airm2);
371 if (airm1 != 0. && airm2 != 0.) {
372 cpl_boolean check = airmass > fmin(airm1, airm2) - 0.005
373 && airmass < fmax(airm1, airm2) + 0.005;
375 cpl_msg_warning(__func__,
"Computed airmass %.3f is NOT in the range "
376 "recorded in the FITS header (%f, %f)", airmass,
406 cpl_ensure(aHeader, CPL_ERROR_NULL_INPUT, 0.);
409 if (mode && !strncmp(mode,
"SKY", 4)) {
411 }
else if (mode && strncmp(mode,
"STAT", 5)) {
412 cpl_msg_warning(__func__,
"Derotator mode is neither SKY nor STAT! "
413 "Effective position angle may be wrong!");
415 cpl_msg_warning(__func__,
"Derotator mode is not given! Effective position "
416 "angle may be wrong!");
442 cpl_ensure(aHeader, CPL_ERROR_NULL_INPUT, 0.);
443 cpl_errorstate es = cpl_errorstate_get();
446 if (!cpl_errorstate_is_equal(es)) {
447 cpl_msg_warning(__func__,
"One or both TEL.PARANG keywords are missing!");
450 double parang = (p1 + p2) / 2.;
451 if (fabs(p1 - p2) < 90.) {
462 double d1 = copysign(180. - fabs(p1), p1),
463 d2 = copysign(180. - fabs(p2), p2);
464 parang = 180. - fabs((d1 + d2) / 2.);
465 if (fabs(d1) > fabs(d2)) {
466 parang = copysign(parang, p1);
468 parang = copysign(parang, p2);
490 double ra1 = aRA1 * CPL_MATH_RAD_DEG,
491 dec1 = aDEC1 * CPL_MATH_RAD_DEG,
492 ra2 = aRA2 * CPL_MATH_RAD_DEG,
493 dec2 = aDEC2 * CPL_MATH_RAD_DEG;
495 double dra = ra2 - ra1,
502 double t1 = cosdec2 * sin(dra),
503 t2 = cosdec1 * sindec2 - sindec1 * cosdec2 * cosdra,
504 dsigma = atan2(sqrt(t1*t1 + t2*t2),
505 sindec1 * sindec2 + cosdec1 * cosdec2 * cosdra);
506 return dsigma * CPL_MATH_DEG_RAD;
double muse_pfits_get_ra(const cpl_propertylist *aHeaders)
find out the right ascension
double muse_pfits_get_airmass_start(const cpl_propertylist *aHeaders)
find out the airmass at start of exposure
double muse_astro_parangle(const cpl_propertylist *aHeader)
Properly average parallactic angle values of one exposure.
double muse_pfits_get_drot_posang(const cpl_propertylist *aHeaders)
find out the MUSE derotator position angle (in degrees)
double muse_pfits_get_geolat(const cpl_propertylist *aHeaders)
find out the telescope's latitude
const char * muse_pfits_get_drot_mode(const cpl_propertylist *aHeaders)
find out the MUSE derotator mode
double muse_astro_compute_airmass(double aRA, double aDEC, double aLST, double aExptime, double aLatitude)
Compute the effective airmass of an observation.
double muse_pfits_get_dec(const cpl_propertylist *aHeaders)
find out the declination
double muse_pfits_get_parang_start(const cpl_propertylist *aHeaders)
find out the parallactic angle at start of exposure (in degrees)
double muse_astro_posangle(const cpl_propertylist *aHeader)
Derive the position angle of an observation from information in a FITS header.
double muse_pfits_get_airmass_end(const cpl_propertylist *aHeaders)
find out the airmass at end of exposure
double muse_pfits_get_exptime(const cpl_propertylist *aHeaders)
find out the exposure time
double muse_pfits_get_lst(const cpl_propertylist *aHeaders)
find out the local siderial time
double muse_astro_angular_distance(double aRA1, double aDEC1, double aRA2, double aDEC2)
Compute angular distance in the sky between two positions.
double muse_pfits_get_parang_end(const cpl_propertylist *aHeaders)
find out the parallactic angle at end of exposure (in degrees)
double muse_astro_airmass(cpl_propertylist *aHeader)
Derive the effective airmass of an observation from information in a FITS header. ...