00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027
00028
00029
00030
00031 #include <cpl.h>
00032 #include <math.h>
00033 #include <string.h>
00034 #include <ctype.h>
00035
00036 #include "muse_astro.h"
00037
00038 #include "muse_pfits.h"
00039
00040
00041
00042
00043
00044 #define MUSE_ASTRO_AIRMASS_APPROX 2
00045
00046
00047
00048
00049
00050 static const double kT0 = 2415020.;
00051 static const double kEpoch1900 = 1900.;
00052 static const double kJulianCentury = 36525.;
00053 static const double kTropYear = 365.242198781;
00054 static const double kBesselOffset = 0.31352;
00055 static const double kAU = 149597870.7;
00056
00057
00066
00067
00070
00086
00087 static double
00088 muse_astro_get_zenith_distance(double aHourAngle, double aDelta,
00089 double aLatitude)
00090 {
00091 double p0 = sin(aLatitude) * sin(aDelta),
00092 p1 = cos(aLatitude) * cos(aDelta),
00093 z = p0 + cos(aHourAngle) * p1;
00094 return fabs(z) < FLT_EPSILON ? 0. : z;
00095 }
00096
00097 #if MUSE_ASTRO_AIRMASS_APPROX == 0
00098
00110
00111 static double
00112 muse_astro_get_airmass_youngirvine(double aSecZ)
00113 {
00114 return aSecZ * (1. - 0.0012 * (pow(aSecZ, 2) - 1.));
00115 }
00116 #endif
00117
00118 #if MUSE_ASTRO_AIRMASS_APPROX == 1
00119
00133
00134 static double
00135 muse_astro_get_airmass_young(double aCosZt)
00136 {
00137 return (1.002432 * aCosZt*aCosZt + 0.148386 * aCosZt + 0.0096467)
00138 / (aCosZt*aCosZt*aCosZt + 0.149864 * aCosZt*aCosZt + 0.0102963 * aCosZt
00139 + 0.000303978);
00140 }
00141 #endif
00142
00143 #if MUSE_ASTRO_AIRMASS_APPROX == 2
00144
00157
00158 static double
00159 muse_astro_get_airmass_hardie(double aSecZ)
00160 {
00161 double secm1 = aSecZ - 1;
00162 return aSecZ - 0.0018167 * secm1 - 0.002875 * secm1*secm1
00163 - 0.0008083 * secm1*secm1*secm1;
00164 }
00165 #endif
00166
00167
00205
00206 double
00207 muse_astro_compute_airmass(double aRA, double aDEC, double aLST,
00208 double aExptime, double aLatitude)
00209 {
00210 cpl_ensure(aRA >= 0. && aRA < 360. && aDEC >= -90. && aDEC <= 90. &&
00211 aLST >= 0. && aLST < 86400. && aLatitude >= -90. && aLatitude <= 90.,
00212 CPL_ERROR_ILLEGAL_INPUT, -1.);
00213 cpl_ensure(aExptime >= 0., CPL_ERROR_ILLEGAL_INPUT, -1.);
00214
00215
00216 double HA = aLST * 15./3600. - aRA;
00217
00218 if (HA < -180.) {
00219 HA += 360.;
00220 }
00221 if (HA > 180.) {
00222 HA -= 360.;
00223 }
00224
00225
00226 double delta = aDEC * CPL_MATH_RAD_DEG,
00227 latitude = aLatitude * CPL_MATH_RAD_DEG,
00228 hourangle = HA * CPL_MATH_RAD_DEG;
00229
00230
00231
00232
00233 double cosz = muse_astro_get_zenith_distance(hourangle, delta, latitude);
00234 #if MUSE_ASTRO_AIRMASS_APPROX == 2
00235 double z = acos(cosz) * CPL_MATH_DEG_RAD;
00236 const double zlimit = 80.;
00237 if (z > zlimit) {
00238 cpl_msg_warning(__func__, "Zenith angle %f > %f!", z, zlimit);
00239 }
00240 #endif
00241 if (cosz == 0. || fabs(1. / cosz) < FLT_EPSILON ||
00242 acos(cosz) > CPL_MATH_PI_2) {
00243 cpl_msg_error(__func__, "Airmass computation unsuccessful. Object is below "
00244 "the horizon at start (z = %f).", acos(cosz) * CPL_MATH_DEG_RAD);
00245 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
00246 return -1.;
00247 }
00248
00249 double airmass = 0.;
00250 #if MUSE_ASTRO_AIRMASS_APPROX == 0
00251 airmass = muse_astro_get_airmass_youngirvine(1. / cosz);
00252 #endif
00253 #if MUSE_ASTRO_AIRMASS_APPROX == 1
00254 airmass = muse_astro_get_airmass_young(cosz);
00255 #endif
00256 #if MUSE_ASTRO_AIRMASS_APPROX == 2
00257 airmass = muse_astro_get_airmass_hardie(1. / cosz);
00258 #endif
00259 #if MUSE_ASTRO_AIRMASS_APPROX > 2
00260 #error set MUSE_ASTRO_AIRMASS_APPROX to 0, 1, 2
00261 #endif
00262
00263
00264
00265
00266 if (aExptime > 0.) {
00267 const double weights[] = {1./6., 2./3., 1./6.};
00268 const int nweights = sizeof(weights) / sizeof(double);
00269
00270 double timeStep = aExptime / (nweights - 1) * 15./3600. * CPL_MATH_RAD_DEG;
00271 airmass *= weights[0];
00272
00273 int i;
00274 for (i = 1; i < nweights; i++) {
00275 cosz = muse_astro_get_zenith_distance(hourangle + i * timeStep, delta, latitude);
00276 #if MUSE_ASTRO_AIRMASS_APPROX == 2
00277 z = acos(cosz) * CPL_MATH_DEG_RAD;
00278 if (z > zlimit) {
00279 cpl_msg_warning(__func__, "Zenith angle %f > %f!", z, zlimit);
00280 }
00281 #endif
00282 if (cosz == 0. || fabs(1. / cosz) < FLT_EPSILON ||
00283 acos(cosz) > CPL_MATH_PI_2) {
00284 cpl_msg_error(__func__, "Airmass computation unsuccessful at timeStep. "
00285 "Object is below the horizon at %s exposure (z=%f).",
00286 i == 1 ? "mid" : "end", acos(cosz) * CPL_MATH_DEG_RAD);
00287 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_OUTPUT);
00288 return -1.;
00289 }
00290
00291 #if MUSE_ASTRO_AIRMASS_APPROX == 0
00292 airmass += weights[i] * muse_astro_get_airmass_youngirvine(1. / cosz);
00293 #endif
00294 #if MUSE_ASTRO_AIRMASS_APPROX == 1
00295 airmass += weights[i] * muse_astro_get_airmass_young(cosz);
00296 #endif
00297 #if MUSE_ASTRO_AIRMASS_APPROX == 2
00298 airmass += weights[i] * muse_astro_get_airmass_hardie(1. / cosz);
00299 #endif
00300 }
00301 }
00302
00303 #if MUSE_ASTRO_AIRMASS_APPROX == 0
00304
00305 const double airmasslimit = 4.;
00306 if (airmass > airmasslimit) {
00307 cpl_msg_warning(__func__, "Airmass larger than %f", airmasslimit);
00308 }
00309 #endif
00310
00311 return airmass;
00312 }
00313
00314
00329
00330 double
00331 muse_astro_airmass(cpl_propertylist *aHeader)
00332 {
00333 cpl_ensure(aHeader, CPL_ERROR_NULL_INPUT, -1.);
00334
00335 cpl_errorstate prestate = cpl_errorstate_get();
00336 double airm1 = muse_pfits_get_airmass_start(aHeader),
00337 airm2 = muse_pfits_get_airmass_end(aHeader);
00338 cpl_errorstate_set(prestate);
00339
00340
00341
00342
00343
00344 prestate = cpl_errorstate_get();
00345 double ra = muse_pfits_get_ra(aHeader);
00346 if (!cpl_errorstate_is_equal(prestate)) {
00347 ra = -1000.;
00348 }
00349 prestate = cpl_errorstate_get();
00350 double dec = muse_pfits_get_dec(aHeader);
00351 if (!cpl_errorstate_is_equal(prestate)) {
00352 dec = -1000.;
00353 }
00354 prestate = cpl_errorstate_get();
00355 double lst = muse_pfits_get_lst(aHeader);
00356 if (!cpl_errorstate_is_equal(prestate)) {
00357 lst = -1000.;
00358 }
00359 prestate = cpl_errorstate_get();
00360 double exptime = muse_pfits_get_exptime(aHeader);
00361 if (!cpl_errorstate_is_equal(prestate)) {
00362 exptime = -1.;
00363 }
00364
00365 double airmass = muse_astro_compute_airmass(ra, dec, lst, exptime,
00366 muse_pfits_get_geolat(aHeader));
00367
00368
00369 if (airmass < 0. && airm1 != 0. && airm2 != 0.) {
00370
00371 double average = (airm1 + airm2) / 2.;
00372 cpl_msg_warning(__func__, "airmass computation unsuccessful (%s), using simple "
00373 "average of start and end values (%f)", cpl_error_get_message(),
00374 average);
00375 return average;
00376 }
00377
00378
00379 cpl_msg_debug(__func__, "airmass=%f (header %f, %f)", airmass, airm1, airm2);
00380 if (airm1 != 0. && airm2 != 0.) {
00381 cpl_boolean check = airmass > fmin(airm1, airm2) - 0.005
00382 && airmass < fmax(airm1, airm2) + 0.005;
00383 if (!check) {
00384 cpl_msg_warning(__func__, "Computed airmass %.3f is NOT in the range "
00385 "recorded in the FITS header (%f, %f)", airmass,
00386 airm1, airm2);
00387 }
00388 }
00389
00390 return airmass;
00391 }
00392
00393
00411
00412 double
00413 muse_astro_posangle(const cpl_propertylist *aHeader)
00414 {
00415 cpl_ensure(aHeader, CPL_ERROR_NULL_INPUT, 0.);
00416 double posang = muse_pfits_get_drot_posang(aHeader);
00417 const char *mode = muse_pfits_get_drot_mode(aHeader);
00418 if (mode && !strncmp(mode, "SKY", 4)) {
00419 posang *= -1.;
00420 } else if (mode && strncmp(mode, "STAT", 5)) {
00421 cpl_msg_warning(__func__, "Derotator mode is neither SKY nor STAT! "
00422 "Effective position angle may be wrong!");
00423 } else if (!mode) {
00424 cpl_msg_warning(__func__, "Derotator mode is not given! Effective position "
00425 "angle may be wrong!");
00426 }
00427 return posang;
00428 }
00429
00430
00447
00448 double
00449 muse_astro_parangle(const cpl_propertylist *aHeader)
00450 {
00451 cpl_ensure(aHeader, CPL_ERROR_NULL_INPUT, 0.);
00452 cpl_errorstate es = cpl_errorstate_get();
00453 double p1 = muse_pfits_get_parang_start(aHeader),
00454 p2 = muse_pfits_get_parang_end(aHeader);
00455 if (!cpl_errorstate_is_equal(es)) {
00456 cpl_msg_warning(__func__, "One or both TEL.PARANG keywords are missing!");
00457 }
00458
00459 double parang = (p1 + p2) / 2.;
00460 if (fabs(p1 - p2) < 90.) {
00461
00462
00463 return parang;
00464 }
00465
00466
00467
00468
00469
00470
00471 double d1 = copysign(180. - fabs(p1), p1),
00472 d2 = copysign(180. - fabs(p2), p2);
00473 parang = 180. - fabs((d1 + d2) / 2.);
00474 if (fabs(d1) > fabs(d2)) {
00475 parang = copysign(parang, p1);
00476 } else {
00477 parang = copysign(parang, p2);
00478 }
00479 return parang;
00480 }
00481
00482
00494
00495 double
00496 muse_astro_angular_distance(double aRA1, double aDEC1, double aRA2, double aDEC2)
00497 {
00498
00499 double ra1 = aRA1 * CPL_MATH_RAD_DEG,
00500 dec1 = aDEC1 * CPL_MATH_RAD_DEG,
00501 ra2 = aRA2 * CPL_MATH_RAD_DEG,
00502 dec2 = aDEC2 * CPL_MATH_RAD_DEG;
00503
00504 double dra = ra2 - ra1,
00505 cosdra = cos(dra),
00506 sindec1 = sin(dec1),
00507 cosdec1 = cos(dec1),
00508 sindec2 = sin(dec2),
00509 cosdec2 = cos(dec2);
00510
00511 double t1 = cosdec2 * sin(dra),
00512 t2 = cosdec1 * sindec2 - sindec1 * cosdec2 * cosdra,
00513 dsigma = atan2(sqrt(t1*t1 + t2*t2),
00514 sindec1 * sindec2 + cosdec1 * cosdec2 * cosdra);
00515 return dsigma * CPL_MATH_DEG_RAD;
00516 }
00517
00518
00532
00533 double
00534 muse_astro_wavelength_vacuum_to_air(double aVac)
00535 {
00536 return aVac / (1. + 2.735182e-4 + 131.4182 / pow(aVac, 2)
00537 + 2.76249e8 / pow(aVac, 4));
00538 }
00539
00540
00567
00568 static cpl_matrix *
00569 muse_astro_create_rotation(const char *aMode, double aPhi, double aTheta,
00570 double aPsi)
00571 {
00572 int nrotations = strlen(aMode);
00573 nrotations = nrotations <= 3 ? nrotations : 3;
00574 cpl_matrix *self = cpl_matrix_new(3, 3);
00575
00576
00577 cpl_matrix_fill_diagonal(self, 1., 0);
00578
00579 if (!nrotations) {
00580 return self;
00581 }
00582
00583
00584
00585 cpl_matrix *R = cpl_matrix_new(3, 3);
00586 double angle[3] = { aPhi, aTheta, aPsi },
00587 *_R = cpl_matrix_get_data(R);
00588 int i;
00589 for (i = 0; i < nrotations; ++i) {
00590 double sa = sin(angle[i]),
00591 ca = cos(angle[i]);
00592 cpl_matrix_fill(R, 0.);
00593 switch (tolower(aMode[i])) {
00594 case 'x':
00595 _R[0] = 1.;
00596 _R[4] = ca;
00597 _R[5] = sa;
00598 _R[7] = -sa;
00599 _R[8] = ca;
00600 break;
00601 case 'y':
00602 _R[0] = ca;
00603 _R[2] = -sa;
00604 _R[4] = 1.;
00605 _R[6] = sa;
00606 _R[8] = ca;
00607 break;
00608 case 'z':
00609 _R[0] = ca;
00610 _R[1] = sa;
00611 _R[3] = -sa;
00612 _R[4] = ca;
00613 _R[8] = 1.;
00614 break;
00615 default:
00616
00617 cpl_matrix_delete(R);
00618 cpl_matrix_delete(self);
00619 return NULL;
00620 }
00621 cpl_matrix *mt = cpl_matrix_product_create(R, self);
00622 cpl_matrix_delete(self);
00623 self = mt;
00624 }
00625 cpl_matrix_delete(R);
00626 return self;
00627 }
00628
00629
00645
00646 static cpl_matrix *
00647 muse_astro_precession_matrix(double aEpoch1, double aEpoch2)
00648 {
00649
00650 double sigma0 = 2000.,
00651 sigmaF = aEpoch1,
00652 sigmaD = aEpoch2;
00653
00654 double T = (sigmaF - sigma0) / 1000.,
00655 t = (sigmaD - sigmaF) / 1000.;
00656
00657 double thetaA = (20042.0207 - 85.3131 * T - 0.2111 * T*T
00658 + 0.3642 * T*T*T + 0.0008 * T*T*T*T - 0.0005 * T*T*T*T*T) * t
00659 + (-42.6566 - 0.2111 * T + 0.5463 * T*T
00660 + 0.0017 * T*T*T - 0.0012 * T*T*T*T) * t*t
00661 + (-41.8238 + 0.0359 * T + 0.0027 *T*T - 0.0001 * T*T*T) * t*t*t
00662 + (-0.0731 + 0.0019 * T + 0.0009 *T*T) * t*t*t*t
00663 + (-0.0127 + 0.0011 * T) * t*t*t*t*t
00664 + (0.0004) * t*t*t*t*t*t,
00665 zetaA = (23060.9097 + 139.7495 * T - 0.0038 * T*T - 0.5918 * T*T*T
00666 - 0.0037 * T*T*T*T + 0.0007 * T*T*T*T*T) * t
00667 + (30.2226 - 0.2523 * T - 0.3840 * T*T - 0.0014 *T*T*T
00668 + 0.0007 * T*T*T*T) * t*t
00669 + (18.0183 - 0.1326 * T + 0.0006 * T*T + 0.0005 * T*T*T) * t*t*t
00670 + (-0.0583 - 0.0001 * T + 0.0007 * T*T) * t*t*t*t
00671 + (-0.0285) * t*t*t*t*t
00672 + (-0.0002) * t*t*t*t*t*t,
00673 zA = (23060.9097 +139.7495 * T -0.0038 * T*T -0.5918 * T*T*T
00674 - 0.0037 * T*T*T*T + 0.0007 * T*T*T*T*T) * t
00675 + (109.5270 + 0.2446 * T - 1.3913 * T*T - 0.0134 * T*T*T
00676 + 0.0026 * T*T*T*T) * t*t
00677 + (18.2667 - 1.1400 * T - 0.0173 * T*T + 0.0044 * T*T*T) * t*t*t
00678 + (-0.2821 - 0.0093 * T + 0.0032 * T*T) * t*t*t*t
00679 + (-0.0301 + 0.0006 * T) * t*t*t*t*t
00680 + (-0.0001) * t*t*t*t*t*t;
00681
00682 thetaA *= CPL_MATH_RAD_DEG / 3600.;
00683 zetaA *= CPL_MATH_RAD_DEG / 3600.;
00684 zA *= CPL_MATH_RAD_DEG / 3600.;
00685
00686
00687 return muse_astro_create_rotation("zyz", -zetaA, thetaA, -zA);
00688 }
00689
00690
00703
00704 static double
00705 muse_astro_sidereal_time(double aJD, double aLong)
00706 {
00707
00708
00709 const double d1 = 1.739935934667999,
00710 d2 = 6.283319509909095e02,
00711 d3 = 6.755878646261384e-06,
00712 df = 1.00273790934;
00713 double djd0 = floor(aJD) + 0.5;
00714 if (djd0 > aJD) {
00715 djd0 -= 1.;
00716 }
00717 double dut = (aJD - djd0) * CPL_MATH_2PI,
00718 dt = (djd0 - kT0) / kJulianCentury,
00719 dst0 = d1 + d2 * dt + d3 * dt * dt;
00720 dst0 = fmod(dst0, CPL_MATH_2PI);
00721 double dst = df * dut + dst0 - aLong;
00722 dst = fmod(dst + 2.*CPL_MATH_2PI, CPL_MATH_2PI);
00723 return dst;
00724 }
00725
00726
00754
00755 static double
00756 muse_astro_geo_correction(double aLat, double aElev, double aDEC, double aHA)
00757 {
00758
00759 const double da = 6378.137;
00760
00761
00762 const double df = 1. / 298.257222;
00763
00764
00765 const double dw = CPL_MATH_2PI / 86164.;
00766 const double de2 = df * (2.0 - df),
00767 dsdlats = sin(aLat) * sin(aLat);
00768
00769
00770 double d1 = 1.0 - de2 * (2.0 - de2) * dsdlats,
00771 d2 = 1.0 - de2 * dsdlats,
00772 dr0 = da * sqrt(d1 / d2);
00773
00774
00775 d1 = de2 * sin(2.0 * aLat);
00776 d2 = 2.0 * d2;
00777 double dlatg = aLat - atan(d1 / d2);
00778
00779
00780 double drh = dr0 * cos(dlatg) + (aElev / 1000.) * cos(aLat);
00781
00782
00783
00784 return dw * drh * cos(aDEC) * sin(aHA);
00785 }
00786
00787
00814
00815 static void
00816 muse_astro_earth_velocity(double aDJE, double aDEqu, double* const aHVel,
00817 double* const aBVel)
00818 {
00819
00820
00821 const double dcfel[][3] = {
00822 { 1.7400353e+00, 6.2833195099091e+02, 5.2796e-06 },
00823 { 6.2565836e+00, 6.2830194572674e+02, -2.6180e-06 },
00824 { 4.7199666e+00, 8.3997091449254e+03, -1.9780e-05 },
00825 { 1.9636505e-01, 8.4334662911720e+03, -5.6044e-05 },
00826 { 4.1547339e+00, 5.2993466764997e+01, 5.8845e-06 },
00827 { 4.6524223e+00, 2.1354275911213e+01, 5.6797e-06 },
00828 { 4.2620486e+00, 7.5025342197656e+00, 5.5317e-06 },
00829 { 1.4740694e+00, 3.8377331909193e+00, 5.6093e-06 }
00830 };
00831
00832
00833
00834 const double dceps[3] = {
00835 4.093198e-01, -2.271110e-04, -2.860401e-08
00836 };
00837 const double ccsel[][3] = {
00838 { 1.675104e-02, -4.179579e-05, -1.260516e-07 },
00839 { 2.220221e-01, 2.809917e-02, 1.852532e-05 },
00840 { 1.589963e+00, 3.418075e-02, 1.430200e-05 },
00841 { 2.994089e+00, 2.590824e-02, 4.155840e-06 },
00842 { 8.155457e-01, 2.486352e-02, 6.836840e-06 },
00843 { 1.735614e+00, 1.763719e-02, 6.370440e-06 },
00844 { 1.968564e+00, 1.524020e-02, -2.517152e-06 },
00845 { 1.282417e+00, 8.703393e-03, 2.289292e-05 },
00846 { 2.280820e+00, 1.918010e-02, 4.484520e-06 },
00847 { 4.833473e-02, 1.641773e-04, -4.654200e-07 },
00848 { 5.589232e-02, -3.455092e-04, -7.388560e-07 },
00849 { 4.634443e-02, -2.658234e-05, 7.757000e-08 },
00850 { 8.997041e-03, 6.329728e-06, -1.939256e-09 },
00851 { 2.284178e-02, -9.941590e-05, 6.787400e-08 },
00852 { 4.350267e-02, -6.839749e-05, -2.714956e-07 },
00853 { 1.348204e-02, 1.091504e-05, 6.903760e-07 },
00854 { 3.106570e-02, -1.665665e-04, -1.590188e-07 }
00855 };
00856
00857
00858
00859
00860 const double dcargs[][2] = {
00861 { 5.0974222e+00, -7.8604195454652e+02 },
00862 { 3.9584962e+00, -5.7533848094674e+02 },
00863 { 1.6338070e+00, -1.1506769618935e+03 },
00864 { 2.5487111e+00, -3.9302097727326e+02 },
00865 { 4.9255514e+00, -5.8849265665348e+02 },
00866 { 1.3363463e+00, -5.5076098609303e+02 },
00867 { 1.6072053e+00, -5.2237501616674e+02 },
00868 { 1.3629480e+00, -1.1790629318198e+03 },
00869 { 5.5657014e+00, -1.0977134971135e+03 },
00870 { 5.0708205e+00, -1.5774000881978e+02 },
00871 { 3.9318944e+00, 5.2963464780000e+01 },
00872 { 4.8989497e+00, 3.9809289073258e+01 },
00873 { 1.3097446e+00, 7.7540959633708e+01 },
00874 { 3.5147141e+00, 7.9618578146517e+01 },
00875 { 3.5413158e+00, -5.4868336758022e+02 }
00876 };
00877
00878
00879
00880 const double ccamps[][5] = {
00881 { -2.279594e-5, 1.407414e-5, 8.273188e-6, 1.340565e-5, -2.490817e-7 },
00882 { -3.494537e-5, 2.860401e-7, 1.289448e-7, 1.627237e-5, -1.823138e-7 },
00883 { 6.593466e-7, 1.322572e-5, 9.258695e-6, -4.674248e-7, -3.646275e-7 },
00884 { 1.140767e-5, -2.049792e-5, -4.747930e-6, -2.638763e-6, -1.245408e-7 },
00885 { 9.516893e-6, -2.748894e-6, -1.319381e-6, -4.549908e-6, -1.864821e-7 },
00886 { 7.310990e-6, -1.924710e-6, -8.772849e-7, -3.334143e-6, -1.745256e-7 },
00887 { -2.603449e-6, 7.359472e-6, 3.168357e-6, 1.119056e-6, -1.655307e-7 },
00888 { -3.228859e-6, 1.308997e-7, 1.013137e-7, 2.403899e-6, -3.736225e-7 },
00889 { 3.442177e-7, 2.671323e-6, 1.832858e-6, -2.394688e-7, -3.478444e-7 },
00890 { 8.702406e-6, -8.421214e-6, -1.372341e-6, -1.455234e-6, -4.998479e-8 },
00891 { -1.488378e-6, -1.251789e-5, 5.226868e-7, -2.049301e-7, 0.0e0 },
00892 { -8.043059e-6, -2.991300e-6, 1.473654e-7, -3.154542e-7, 0.0e0 },
00893 { 3.699128e-6, -3.316126e-6, 2.901257e-7, 3.407826e-7, 0.0e0 },
00894 { 2.550120e-6, -1.241123e-6, 9.901116e-8, 2.210482e-7, 0.0e0 },
00895 { -6.351059e-7, 2.341650e-6, 1.061492e-6, 2.878231e-7, 0.0e0 }
00896 };
00897
00898
00899
00900
00901 const double ccsec3 = -7.757020e-08,
00902 ccsec[][3] = {
00903 { 1.289600e-06, 5.550147e-01, 2.076942e+00 },
00904 { 3.102810e-05, 4.035027e+00, 3.525565e-01 },
00905 { 9.124190e-06, 9.990265e-01, 2.622706e+00 },
00906 { 9.793240e-07, 5.508259e+00, 1.559103e+01 }
00907 };
00908
00909
00910 const double dcsld = 1.990987e-07,
00911 ccsgd = 1.990969e-07;
00912
00913
00914
00915 const double cckm = 3.122140e-05,
00916 ccmld = 2.661699e-06,
00917 ccfdi = 2.399485e-07;
00918
00919
00920
00921
00922 const double dcargm[][2] = {
00923 { 5.1679830e+00, 8.3286911095275e+03 },
00924 { 5.4913150e+00, -7.2140632838100e+03 },
00925 { 5.9598530e+00, 1.5542754389685e+04 }
00926 };
00927
00928
00929
00930 const double ccampm[][4] = {
00931 { 1.097594e-01, 2.896773e-07, 5.450474e-02, 1.438491e-07 },
00932 {-2.223581e-02, 5.083103e-08, 1.002548e-02, -2.291823e-08 },
00933 { 1.148966e-02, 5.658888e-08, 8.249439e-03, 4.063015e-08 }
00934 };
00935
00936
00937 const double ccpamv[4] = { 8.326827e-11, 1.843484e-11,
00938 1.988712e-12, 1.881276e-12 };
00939
00940
00941 const double dc1mme = 0.99999696;
00942
00943
00944 int ideq = (int)aDEqu;
00945 double dt = (aDJE - kT0) / kJulianCentury,
00946 t = dt,
00947 dtsq = dt * dt,
00948 tsq = dtsq;
00949
00950
00951 double dml = 0.,
00952 forbel[7] = { 0., 0., 0., 0., 0., 0., 0. };
00953 int k;
00954 for (k = 0; k < 8; k++) {
00955 double dlocal = fmod(dcfel[k][0] + dt * dcfel[k][1] + dtsq * dcfel[k][2],
00956 CPL_MATH_2PI);
00957 if (k == 0) {
00958 dml = dlocal;
00959 }
00960 if (k != 0) {
00961 forbel[k - 1] = dlocal;
00962 }
00963 }
00964 double deps = fmod(dceps[0] + dt * dceps[1] + dtsq * dceps[2], CPL_MATH_2PI),
00965 sorbel[17];
00966 for (k = 0; k < 17; k++) {
00967 sorbel[k] = fmod(ccsel[k][0] + t * ccsel[k][1] + tsq * ccsel[k][2],
00968 CPL_MATH_2PI);
00969 }
00970
00971
00972 double sn[4];
00973 for (k = 0; k < 4; k++) {
00974 double a = fmod(ccsec[k][1] + t * ccsec[k][2], CPL_MATH_2PI);
00975 sn[k] = sin(a);
00976 }
00977
00978
00979 double pertl = ccsec[0][0] * sn[0] + ccsec[1][0] * sn[1]
00980 + (ccsec[2][0] + t * ccsec3) * sn[2] + ccsec[3][0] * sn[3],
00981 pertld = 0.,
00982 pertr = 0.,
00983 pertrd = 0.;
00984 for (k = 0; k < 15; k++) {
00985 double a = fmod(dcargs[k][0] + dt * dcargs[k][1], CPL_MATH_2PI);
00986 pertl += (ccamps[k][0] * cos(a) + ccamps[k][1] * sin(a));
00987 pertr += (ccamps[k][2] * cos(a) + ccamps[k][3] * sin(a));
00988 if (k >= 10) {
00989 continue;
00990 }
00991 pertld += ((ccamps[k][1] * cos(a) - ccamps[k][0] * sin(a)) * ccamps[k][4]);
00992 pertrd += ((ccamps[k][3] * cos(a) - ccamps[k][2] * sin(a)) * ccamps[k][4]);
00993 }
00994
00995
00996 double esq = sorbel[0] * sorbel[0],
00997 dparam = 1. - esq,
00998 param = dparam,
00999 twoe = sorbel[0] + sorbel[0],
01000 twog = forbel[0] + forbel[0],
01001 phi = twoe * ((1.0 - esq * (1.0 / 8.0)) * sin(forbel[0])
01002 + sorbel[0] * (5.0 / 8.0) * sin(twog)
01003 + esq * 0.5416667 * sin(forbel[0] + twog)),
01004 f = forbel[0] + phi,
01005 dpsi = dparam / (1. + sorbel[0] * cos(f)),
01006 phid = twoe * ccsgd * ((1.0 + esq * 1.50) * cos(f)
01007 + sorbel[0] * (1.250 - sin(f) * sin(f) * 0.50)),
01008 psid = ccsgd * sorbel[0] * sin(f) / sqrt(param);
01009
01010
01011 double d1pdro = 1. + pertr,
01012 drd = d1pdro * (psid + dpsi * pertrd),
01013 drld = d1pdro * dpsi * (dcsld + phid + pertld),
01014 dtl = fmod(dml + phi + pertl, CPL_MATH_2PI),
01015 dxhd = drd * cos(dtl) - drld * sin(dtl),
01016 dyhd = drd * sin(dtl) + drld * cos(dtl);
01017
01018
01019
01020 pertl = 0.;
01021 pertld = 0.;
01022 double pertp = 0.,
01023 pertpd = 0.;
01024 for (k = 0; k < 3; k++) {
01025 double a = fmod(dcargm[k][0] + dt * dcargm[k][1], CPL_MATH_2PI);
01026 pertl += ccampm[k][0] * sin(a);
01027 pertld += ccampm[k][1] * cos(a);
01028 pertp += ccampm[k][2] * cos(a);
01029 pertpd -= ccampm[k][3] * sin(a);
01030 }
01031
01032
01033 double tl = forbel[1] + pertl,
01034 sigma = cckm / (1. + pertp),
01035 a = sigma * (ccmld + pertld),
01036 b = sigma * pertpd;
01037 dxhd = dxhd + a * sin(tl) + b * cos(tl);
01038 dyhd = dyhd - a * cos(tl) + b * sin(tl);
01039 double dzhd = -sigma * ccfdi * cos(forbel[2]);
01040
01041
01042 double dxbd = dxhd * dc1mme,
01043 dybd = dyhd * dc1mme,
01044 dzbd = dzhd * dc1mme;
01045 for (k = 0; k < 4; k++) {
01046 double plon = forbel[k + 3],
01047 pomg = sorbel[k + 1],
01048 pecc = sorbel[k + 9];
01049 tl = fmod(plon + 2.0 * pecc * sin(plon - pomg), CPL_MATH_2PI);
01050 dxbd = dxbd + ccpamv[k] * (sin(tl) + pecc * sin(pomg));
01051 dybd = dybd - ccpamv[k] * (cos(tl) + pecc * cos(pomg));
01052 dzbd = dzbd - ccpamv[k] * sorbel[k + 13] * cos(plon - sorbel[k + 5]);
01053 }
01054
01055
01056 double dyahd = cos(deps) * dyhd - sin(deps) * dzhd,
01057 dzahd = sin(deps) * dyhd + cos(deps) * dzhd,
01058 dyabd = cos(deps) * dybd - sin(deps) * dzbd,
01059 dzabd = sin(deps) * dybd + cos(deps) * dzbd;
01060 if (ideq == 0) {
01061 aHVel[0] = dxhd;
01062 aHVel[1] = dyahd;
01063 aHVel[2] = dzahd;
01064 aBVel[0] = dxbd;
01065 aBVel[1] = dyabd;
01066 aBVel[2] = dzabd;
01067 } else {
01068
01069 double deqdat = (aDJE - kT0 - kBesselOffset) / kTropYear + kEpoch1900;
01070 cpl_matrix* prec = muse_astro_precession_matrix(deqdat, aDEqu);
01071 int n;
01072 for (n = 0; n < 3; n++) {
01073 aHVel[n] = dxhd * cpl_matrix_get(prec, 0, n)
01074 + dyahd * cpl_matrix_get(prec, 1, n)
01075 + dzahd * cpl_matrix_get(prec, 2, n);
01076 aBVel[n] = dxbd * cpl_matrix_get(prec, 0, n)
01077 + dyabd * cpl_matrix_get(prec, 1, n)
01078 + dzabd * cpl_matrix_get(prec, 2, n);
01079 }
01080 cpl_matrix_delete(prec);
01081 }
01082 }
01083
01084
01109
01110 static void
01111 muse_rvcorrection_compute(muse_astro_rvcorr *aRV, double aJD, double aLong,
01112 double aLat, double aElev, double aRA, double aDEC,
01113 double aEqui)
01114 {
01115
01116 double eqt = (aJD - kT0 - kBesselOffset) / kTropYear + kEpoch1900,
01117 dc[3] = { cos(aRA * 15.0 * CPL_MATH_RAD_DEG) * cos(aDEC * CPL_MATH_RAD_DEG),
01118 sin(aRA * 15.0 * CPL_MATH_RAD_DEG) * cos(aDEC * CPL_MATH_RAD_DEG),
01119 sin(aDEC * CPL_MATH_RAD_DEG) };
01120 cpl_matrix *precession = muse_astro_precession_matrix(aEqui, eqt);
01121 double dcc[3];
01122 int i;
01123 for (i = 0; i < 3; ++i) {
01124 dcc[i] = dc[0] * cpl_matrix_get(precession, i, 0)
01125 + dc[1] * cpl_matrix_get(precession, i, 1)
01126 + dc[2] * cpl_matrix_get(precession, i, 2);
01127 }
01128 cpl_matrix_delete(precession);
01129 precession = NULL;
01130
01131 double ra2 = 0.,
01132 dec2 = asin(dcc[2]);
01133 if (dcc[0] != 0.) {
01134 ra2 = atan(dcc[1] / dcc[0]);
01135 if (dcc[0] < 0.) {
01136 ra2 += CPL_MATH_PI;
01137 } else {
01138 if (dcc[1] < 0.) {
01139 ra2 += CPL_MATH_2PI;
01140 }
01141 }
01142 } else {
01143 if (dcc[1] > 0.) {
01144 ra2 = CPL_MATH_PI_2;
01145 } else {
01146 ra2 = 1.5 * CPL_MATH_PI;
01147 }
01148 }
01149
01150
01151
01152 double st = muse_astro_sidereal_time(aJD, aLong * CPL_MATH_RAD_DEG),
01153 ha = st - ra2;
01154
01155
01156
01157 aRV->geo = muse_astro_geo_correction(aLat * CPL_MATH_RAD_DEG, aElev, dec2, -ha);
01158
01159
01160
01161 double hv[3] = { 0., 0., 0. },
01162 bv[3] = { 0., 0., 0. };
01163 muse_astro_earth_velocity(aJD, eqt, hv, bv);
01164
01165
01166
01167 aRV->bary = 0.;
01168 aRV->helio = 0.;
01169 for (i = 0; i < 3; ++i) {
01170 aRV->bary += bv[i] * dcc[i] * kAU;
01171 aRV->helio += hv[i] * dcc[i] * kAU;
01172 }
01173 }
01174
01175
01195
01196 muse_astro_rvcorr
01197 muse_astro_rvcorr_compute(const cpl_propertylist *aHeader)
01198 {
01199 muse_astro_rvcorr rvcorr = { 0., 0., 0. };
01200 cpl_ensure(aHeader, CPL_ERROR_NULL_INPUT, rvcorr);
01201
01202
01203 cpl_errorstate state = cpl_errorstate_get();
01204 double exptime = muse_pfits_get_exptime(aHeader),
01205 jd = muse_pfits_get_mjdobs(aHeader),
01206 equinox = muse_pfits_get_equinox(aHeader),
01207 ra = muse_pfits_get_ra(aHeader) / 15.,
01208 dec = muse_pfits_get_dec(aHeader);
01209
01210
01211 jd += 2400000.5 + 0.5 * exptime / (24. * 3600.);
01212 if (!cpl_errorstate_is_equal(state)) {
01213 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND, "Could not find all"
01214 " properties necessary for radial velocity computation!");
01215 return rvcorr;
01216 }
01217
01218 double lon = muse_pfits_get_geolon(aHeader),
01219 lat = muse_pfits_get_geolat(aHeader),
01220 elev = muse_pfits_get_geoelev(aHeader);
01221 if (!cpl_errorstate_is_equal(state)) {
01222 cpl_errorstate_set(state);
01223 }
01224
01225
01226 muse_rvcorrection_compute(&rvcorr, jd, lon, lat, elev, ra, dec, equinox);
01227
01228 rvcorr.bary = rvcorr.bary + rvcorr.geo;
01229 rvcorr.helio = rvcorr.helio + rvcorr.geo;
01230 return rvcorr;
01231 }
01232