CR2RE Pipeline Reference Manual 1.6.7
hdrl_barycorr.c
1/*
2 * hdrl_barycorr.c
3 *
4 * Created on: Jan 31, 2022
5 * Author: agabasch
6 */
7
8/*
9 * This file is part of the ESO Toolkit
10 * Copyright (C) 2022 European Southern Observatory
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 */
26
27/*-----------------------------------------------------------------------------
28 Includes
29 -----------------------------------------------------------------------------*/
30
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35#include "hdrl_barycorr.h"
36#include <erfa.h>
37
38/*----------------------------------------------------------------------------*/
53/*----------------------------------------------------------------------------*/
54
57/* ---------------------------------------------------------------------------*/
75/* ---------------------------------------------------------------------------*/
76
77cpl_error_code
78hdrl_eop_interpolate(double mjd, const cpl_table * eop_table, hdrl_parameter *
79 resample_par, double *pmx, double *pmy, double *dut)
80{
81 cpl_ensure_code (eop_table, CPL_ERROR_NULL_INPUT);
82 cpl_ensure_code (resample_par, CPL_ERROR_NULL_INPUT);
83 cpl_ensure_code (pmx, CPL_ERROR_NULL_INPUT);
84 cpl_ensure_code (pmy, CPL_ERROR_NULL_INPUT);
85 cpl_ensure_code (dut, CPL_ERROR_NULL_INPUT);
86
87 cpl_table * eop_table_loc = cpl_table_duplicate(eop_table);
88
89 /* Doing some baisc checks on the local eop table */
90
91 if (!cpl_table_has_column(eop_table_loc, "MJD") ||
92 !cpl_table_has_column(eop_table_loc, "PMX") ||
93 !cpl_table_has_column(eop_table_loc, "PMY") ||
94 !cpl_table_has_column(eop_table_loc, "DUT")){
95 cpl_table_delete(eop_table_loc);
96 return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
97 "The EOP table does not have all required "
98 "columns, i.e. MJD, PMX, PMY, DUT");
99 }
100
101 cpl_table_unselect_all (eop_table_loc);
102 cpl_table_or_selected_invalid (eop_table_loc, "MJD");
103 cpl_table_or_selected_invalid (eop_table_loc, "PMX");
104 cpl_table_or_selected_invalid (eop_table_loc, "PMY");
105 cpl_table_or_selected_invalid (eop_table_loc, "DUT");
106 cpl_table_erase_selected (eop_table_loc);
107
108 if (cpl_table_get_nrow(eop_table_loc) < 1){
109 cpl_table_delete(eop_table_loc);
110 return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
111 "The EOP table does not have entries after "
112 "removing all invalid elements");
113 }
114
115 cpl_size mjd_max = cpl_table_get_column_max(eop_table_loc, "MJD");
116 cpl_size mjd_min = cpl_table_get_column_min(eop_table_loc, "MJD");
117
118 if (mjd < mjd_min || mjd > mjd_max) {
119 *pmx = cpl_table_get_column_median(eop_table_loc, "PMX");
120 *pmy = cpl_table_get_column_median(eop_table_loc, "PMY");
121 *dut = cpl_table_get_column_median(eop_table_loc, "DUT");
122 cpl_msg_warning(cpl_func,"The exposure MJD-OBS is outside the validity "
123 "range of the EOP calibration. Using median values instead of "
124 "interpolated values. Please provide a more up to date EOP file for "
125 "a higher accuracy.");
126 cpl_table_delete(eop_table_loc);
127 return cpl_error_get_code();
128 }
129
130 hdrl_spectrum1D * pmx_spec =
131 hdrl_spectrum1D_convert_from_table(eop_table_loc, "PMX", "MJD", NULL, NULL,
132 hdrl_spectrum1D_wave_scale_linear);
133 hdrl_spectrum1D * pmy_spec =
134 hdrl_spectrum1D_convert_from_table(eop_table_loc, "PMY", "MJD", NULL, NULL,
135 hdrl_spectrum1D_wave_scale_linear);
136 hdrl_spectrum1D * dut_spec =
137 hdrl_spectrum1D_convert_from_table(eop_table_loc, "DUT", "MJD", NULL, NULL,
138 hdrl_spectrum1D_wave_scale_linear);
139
140 cpl_array * mjd_out = cpl_array_new(1, CPL_TYPE_DOUBLE);
141 cpl_array_set_double(mjd_out, 0, mjd);
142
143 hdrl_spectrum1D * pmx_spec_resampled =
144 hdrl_spectrum1D_resample_on_array(pmx_spec, mjd_out, resample_par);
145 hdrl_spectrum1D * pmy_spec_resampled =
146 hdrl_spectrum1D_resample_on_array(pmy_spec, mjd_out, resample_par);
147 hdrl_spectrum1D * dut_spec_resampled =
148 hdrl_spectrum1D_resample_on_array(dut_spec, mjd_out, resample_par);
149
150 cpl_array_delete(mjd_out);
151 hdrl_spectrum1D_delete(&pmx_spec);
152 hdrl_spectrum1D_delete(&pmy_spec);
153 hdrl_spectrum1D_delete(&dut_spec);
154
155 int rej_pmx = 0, rej_pmy = 0, rej_dut = 0;
156
157 hdrl_value hvpmx = hdrl_spectrum1D_get_flux_value(pmx_spec_resampled, 0, &rej_pmx);
158 hdrl_value hvpmy = hdrl_spectrum1D_get_flux_value(pmy_spec_resampled, 0, &rej_pmy);
159 hdrl_value hvdut = hdrl_spectrum1D_get_flux_value(dut_spec_resampled, 0, &rej_dut);
160
161 hdrl_spectrum1D_delete(&pmx_spec_resampled);
162 hdrl_spectrum1D_delete(&pmy_spec_resampled);
163 hdrl_spectrum1D_delete(&dut_spec_resampled);
164
165 *pmx = hvpmx.data;
166 *pmy = hvpmy.data;
167 *dut = hvdut.data;
168
169 cpl_table_delete(eop_table_loc);
170 return cpl_error_get_code();
171}
172
173/* ---------------------------------------------------------------------------*/
203/* ---------------------------------------------------------------------------*/
204cpl_error_code
205hdrl_barycorr_compute(double ra, double dec, const cpl_table * eop_table,
206 double mjdobs, double time_to_mid_exposure,
207 double longitude, double latitude, double elevation,
208 double pressure, double temperature,
209 double humidity, double wavelength , double *barycorr){
210
211 cpl_ensure_code(ra >= 0. && ra < 360., CPL_ERROR_ILLEGAL_INPUT);
212 cpl_ensure_code(dec >= -90. && dec <= 90., CPL_ERROR_ILLEGAL_INPUT);
213 /* Todo check range for longitude and latidude !*/
214 cpl_ensure_code(longitude >= -180. && longitude <= 180., CPL_ERROR_ILLEGAL_INPUT);
215 cpl_ensure_code(latitude >= -90. && latitude <= 90., CPL_ERROR_ILLEGAL_INPUT);
216 cpl_ensure_code(eop_table != NULL, CPL_ERROR_NULL_INPUT);
217
218
219 cpl_error_code err = CPL_ERROR_NONE;
220 ra *= CPL_MATH_RAD_DEG;
221 dec *= CPL_MATH_RAD_DEG;
222 longitude *= CPL_MATH_RAD_DEG; // Lon in [rad], East positive
223 latitude *= CPL_MATH_RAD_DEG; // Lat in [rad]
224
225 /* Mean mjdobs from the middle of the exposure time */
226 double mean_mjd = mjdobs + (time_to_mid_exposure/3600./24.);
227 cpl_msg_info(cpl_func, "Mean MJD-OBS used to derive barycorr: %g", mean_mjd);
228
229
230 /* Compute Earth Orientation Parameters for the mean MJD */
231 double dut1 = 0, pmx = 0, pmy = 0;
232
233 // ToDo Expose it in the API?
234 // hdrl_parameter * resample_par = hdrl_spectrum1D_resample_interpolate_parameter_create(hdrl_spectrum1D_interp_akima);
235 // hdrl_parameter * resample_par = hdrl_spectrum1D_resample_interpolate_parameter_create(hdrl_spectrum1D_interp_cspline);
236
237 hdrl_parameter * resample_par =
238 hdrl_spectrum1D_resample_interpolate_parameter_create(hdrl_spectrum1D_interp_linear);
239 err =
240 hdrl_eop_interpolate(mean_mjd, eop_table, resample_par, &pmx, &pmy, &dut1) ;
241 hdrl_parameter_delete(resample_par);
242
243 /* Check output result */
244 if (err) {
245 return cpl_error_set_message(cpl_func, err, "Could not interpolate the "
246 "Earth Orientation Parameter table");
247 }
248
249 cpl_msg_debug(cpl_func, "Using the following Earth Orientation Parameter for "
250 "MJD-OBS %g: pmx: %g, pmy: %g, dut1: %g", mean_mjd, pmx, pmy, dut1);
251
252 pmx = pmx / 3600.0 * CPL_MATH_RAD_DEG;
253 pmy = pmy / 3600.0 * CPL_MATH_RAD_DEG;
254
255 cpl_msg_debug(cpl_func, "Input to the erfa function eraApco13():");
256 cpl_msg_indent_more();
257 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "utc1 :", "UTC as a 2-part... ", 2400000.5);
258 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "utc2 :", "...quasi Julian Date (Notes 1,2) ", mean_mjd);
259 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "dut1 :", "UT1-UTC (seconds, Note 3) ", dut1);
260 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "elong :", "longitude (radians, east +ve, Note 4) ", longitude);
261 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "phi :", "latitude (geodetic, radians, Note 4) ", latitude);
262 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "hm :", "height above ellipsoid (m, geodetic, Notes 4,6) ", elevation);
263 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "xp :", "polar motion coordinates (radians, Note 5) ", pmx);
264 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "yp :", "polar motion coordinates (radians, Note 5) ", pmy);
265 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "phpa :", "pressure at the observer (hPa = mB, Note 6) ", pressure);
266 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "tc :", "ambient temperature at the observer (deg C) ", temperature);
267 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "rh :", "relative humidity at the observer (range 0-1) ", humidity);
268 cpl_msg_debug (cpl_func, "%12s %50s: %20.20g", "wl :", "wavelength (micrometers, Note 7) ", wavelength);
269 cpl_msg_indent_less();
270 /* Allocate memory for the tmp computations */
271 eraASTROM astrom;
272 double eo;
273 int eraApco13_status = 0;
274 cpl_msg_info(cpl_func, "Calling erfa function eraApco13() ...");
275 eraApco13_status = eraApco13 (2400000.5, mean_mjd, dut1,
276 longitude, latitude, elevation,
277 pmx, pmy,
278 pressure, temperature, humidity, wavelength,
279 &astrom, &eo);
280 if (eraApco13_status < 0) {
281 *barycorr = NAN;
282 return cpl_error_set_message(cpl_func, CPL_ERROR_UNSPECIFIED,
283 "Erfa function eraApco13() did not succeed in"
284 " computing the barycentric correction");
285 }
286
287 cpl_msg_debug(cpl_func, "Output of the erfa function eraApco13():");
288 cpl_msg_indent_more();
289
290 cpl_msg_debug (cpl_func, " pmt : /* PM time interval (SSB, Julian years) */ : %20.20g ", astrom.pmt);
291 cpl_msg_debug (cpl_func, " eb[0] : /* SSB to observer (vector, au) */ : %20.20g ", astrom.eb[0]);
292 cpl_msg_debug (cpl_func, " eb[1] : /* SSB to observer (vector, au) */ : %20.20g ", astrom.eb[1]);
293 cpl_msg_debug (cpl_func, " eb[2] : /* SSB to observer (vector, au) */ : %20.20g ", astrom.eb[2]);
294 cpl_msg_debug (cpl_func, " eh[0] : /* Sun to observer (unit vector) */ : %20.20g ", astrom.eh[0]);
295 cpl_msg_debug (cpl_func, " eh[1] : /* Sun to observer (unit vector) */ : %20.20g ", astrom.eh[1]);
296 cpl_msg_debug (cpl_func, " eh[2] : /* Sun to observer (unit vector) */ : %20.20g ", astrom.eh[2]);
297 cpl_msg_debug (cpl_func, " em : /* distance from Sun to observer (au) */ : %20.20g ", astrom.em);
298 cpl_msg_debug (cpl_func, " v[0] : /* barycentric observer velocity (vector, c) */ : %20.20g ", astrom.v[0]);
299 cpl_msg_debug (cpl_func, " v[1] : /* barycentric observer velocity (vector, c) */ : %20.20g ", astrom.v[1]);
300 cpl_msg_debug (cpl_func, " v[2] : /* barycentric observer velocity (vector, c) */ : %20.20g ", astrom.v[2]);
301 cpl_msg_debug (cpl_func, " bm1 : /* sqrt(1-|v|^2): reciprocal of Lorenz factor */ : %20.20g ", astrom.bm1);
302 cpl_msg_debug (cpl_func, " bpn[0][0] : /* bias-precession-nutation matrix */ : %20.20g ", astrom.bpn[0][0]);
303 cpl_msg_debug (cpl_func, " along: : /* longitude + s' + dERA(DUT) (radians) */ : %20.20g ", astrom.along);
304 // phi may be un-initialized according to valgrind - not printing it at this stage
305 // cpl_msg_debug (cpl_func, " phi: : /* geodetic latitude (radians) */ : %20.20g ", astrom.phi);
306 cpl_msg_debug (cpl_func, " xpl : /* polar motion xp wrt local meridian (radians) */ : %20.20g ", astrom.xpl);
307 cpl_msg_debug (cpl_func, " ypl : /* polar motion yp wrt local meridian (radians) */ : %20.20g ", astrom.ypl);
308 cpl_msg_debug (cpl_func, " sphi : /* sine of geodetic latitude */ : %20.20g ", astrom.sphi);
309 cpl_msg_debug (cpl_func, " cphi : /* cosine of geodetic latitude */ : %20.20g ", astrom.cphi);
310 cpl_msg_debug (cpl_func, " diurab : /* magnitude of diurnal aberration vector */ : %20.20g ", astrom.diurab);
311 cpl_msg_debug (cpl_func, " eral : /* local Earth rotation angle (radians) */ : %20.20g ", astrom.eral);
312 cpl_msg_debug (cpl_func, " refa : /* refraction constant A (radians) */ : %20.20g ", astrom.refa);
313 cpl_msg_debug (cpl_func, " refb : /* refraction constant B (radians) */ : %20.20g ", astrom.refb);
314 cpl_msg_debug (cpl_func, " eo : /* equation of the origins (ERA-GST) */ : %20.20g ", astrom.refb);
315 cpl_msg_indent_less();
316
317
318 /* From UVES Pipeline:
319 * ... REFERENCE: THE ASTRONOMICAL ALMANAC 1982 PAGE:B17 */
320
321 *barycorr =
322 astrom.v[0]*cos(ra)*cos(dec)+
323 astrom.v[1]*sin(ra)*cos(dec)+
324 astrom.v[2]*sin(dec);
325
326 *barycorr *= CPL_PHYS_C; // m/s
327
328 return cpl_error_get_code();
329
330}
331
cpl_error_code hdrl_barycorr_compute(double ra, double dec, const cpl_table *eop_table, double mjdobs, double time_to_mid_exposure, double longitude, double latitude, double elevation, double pressure, double temperature, double humidity, double wavelength, double *barycorr)
Derives the barycentric correction using the erfa function eraApco13(). The latter For a terrestrial ...
void hdrl_parameter_delete(hdrl_parameter *obj)
shallow delete of a parameter
hdrl_parameter * hdrl_spectrum1D_resample_interpolate_parameter_create(const hdrl_spectrum1D_interpolation_method method)
constructor for the hdrl_parameter in the case of interpolation
void hdrl_spectrum1D_delete(hdrl_spectrum1D **p_self)
hdrl_spectrum1D destructor
hdrl_spectrum1D * hdrl_spectrum1D_convert_from_table(const cpl_table *self, const char *flux_col_name, const char *wavelength_col_name, const char *flux_e_col_name, const char *flux_bpm_col_name, hdrl_spectrum1D_wave_scale scale)
convert a table to a spectrum
hdrl_spectrum1D * hdrl_spectrum1D_resample_on_array(const hdrl_spectrum1D *self, const cpl_array *waves, const hdrl_parameter *par)
resample a hdrl_spectrum1D on the wavelengths contained in waves
hdrl_value hdrl_spectrum1D_get_flux_value(const hdrl_spectrum1D *self, int idx, int *rej)
hdrl_spectrum1D getter for a flux value