IIINSTRUMENT Pipeline Reference Manual  6.2.2
isaac_spc_jitter.c
1 /* $Id: isaac_spc_jitter.c,v 1.94 2013-03-12 08:06:48 llundin Exp $
2  *
3  * This file is part of the ISAAC Pipeline
4  * Copyright (C) 2002,2003 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19  */
20 
21 /*
22  * $Author: llundin $
23  * $Date: 2013-03-12 08:06:48 $
24  * $Revision: 1.94 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 /*-----------------------------------------------------------------------------
33  Includes
34  -----------------------------------------------------------------------------*/
35 
36 #include <cpl.h>
37 
38 #include "irplib_plugin.h"
39 #include "irplib_utils.h"
40 #include "irplib_stdstar.h"
41 #include "irplib_spectrum.h"
42 
43 #include "isaac_utils.h"
44 #include "isaac_wavelength.h"
45 #include "isaac_physicalmodel.h"
46 #include "isaac_pfits.h"
47 #include "isaac_dfs.h"
48 
49 #include <string.h>
50 #include <math.h>
51 
52 /*-----------------------------------------------------------------------------
53  Define
54  -----------------------------------------------------------------------------*/
55 
56 #define RECIPE_STRING "isaac_spc_jitter"
57 
58 #define ISAAC_SPC_JITTER_OFFSET_ERR 10
59 
60 #define KEYSIZE 512
61 
62 #define CENT_WL_BAND_Z 0.9
63 #define CENT_WL_BAND_SZ 1.06
64 #define CENT_WL_BAND_J 1.25
65 #define CENT_WL_BAND_H 1.65
66 #define CENT_WL_BAND_K 2.2
67 #define CENT_WL_BAND_SL 3.78
68 #define CENT_WL_BAND_M 4.78
69 
70 #define ISAAC_MIN(A,B) ((A) < (B) ? (A) : (B))
71 
72 #define isaac_plot_manpage \
73  "The recipe can produce a number of predefined plots. " \
74  "Zero means that none of the plots are produced, while " \
75  "increasing values (e.g. 1 or 2) increases the number " \
76  "of plots produced. If the plotting fails a warning is " \
77  "produced, and the recipe continues. " \
78  "The default behaviour of the plotting is to use " \
79  "gnuplot (with option -persist). The recipe currently " \
80  "produces 1D-plots using gnuplot commands. The recipe " \
81  "user can control the actual plotting-command used by " \
82  "the recipe to create the plot by setting the " \
83  "environment variable CPL_PLOTTER. Currently, if " \
84  "CPL_PLOTTER " \
85  "is set it must contain the string 'gnuplot'. Setting " \
86  "it to 'cat > my_gnuplot_$$.txt' causes a number of " \
87  "ASCII-files to be created, which each produce a plot " \
88  "when given as standard input to gnuplot (e.g. later " \
89  "or on a different computer). A finer control of the " \
90  "plotting options can be obtained by writing an " \
91  "executable script, e.g. my_gnuplot.pl, that " \
92  "executes gnuplot after setting the desired gnuplot " \
93  "options (e.g. set terminal pslatex color) " \
94  "and then setting CPL_PLOTTER to my_gnuplot.pl. " \
95  "The predefined plots include plotting of images. " \
96  "Images can be plotted not only with gnuplot, but also " \
97  "using the pnm format. This is controlled with the " \
98  "environment variable CPL_IMAGER. If CPL_IMAGER " \
99  "is set to a string that does not contain the word " \
100  "gnuplot, the recipe will generate the plot in pnm " \
101  "format. E.g. setting CPL_IMAGER to " \
102  "'display - &' will produce a gray-scale image " \
103  "using the image viewer display."
104 
105 
106 
107 /*-----------------------------------------------------------------------------
108  Private Function prototypes
109  -----------------------------------------------------------------------------*/
110 
111 static cpl_image ** isaac_spc_jitter_combine(const cpl_frameset *, const char *,
112  const char *, const char *, const char *);
113 static cpl_imagelist * isaac_spc_jitter_load(const cpl_frameset *);
114 static cpl_vector * isaac_spc_jitter_get_offsets(const cpl_frameset *);
115 static int * isaac_spc_jitter_classif(const cpl_vector *, int *);
116 static int off_comp(double, double, double);
117 static cpl_imagelist * isaac_spc_jitter_saa_groups(cpl_imagelist *,
118  cpl_vector *, int *, int, cpl_vector **);
119 static int isaac_spc_jitter_wavecal(const char *, const cpl_image *,
120  const char *, const cpl_frameset *);
121 static cpl_imagelist * isaac_spc_jitter_nodded(cpl_imagelist *, cpl_vector *,
122  cpl_vector **);
123 static cpl_imagelist * isaac_spc_jitter_distor(cpl_imagelist *, const char *,
124  const char *);
125 static double isaac_spc_jitter_refine_offset(const cpl_image *,
126  const cpl_image *);
127 static cpl_table * isaac_spc_jitter_extract(const cpl_image *);
128 static int isaac_spc_jitter_std(const char *, const char *, const cpl_frame *,
129  cpl_table *, double);
130 static cpl_error_code isaac_spc_jitter_flat(cpl_imagelist *, const char *);
131 static cpl_error_code isaac_spc_jitter_save(cpl_frameset *, const cpl_image *,
132  const cpl_image *,
133  const cpl_table *,
134  const cpl_parameterlist *);
135 
136 cpl_recipe_define(isaac_spc_jitter, ISAAC_BINARY_VERSION,
137  "Lars Lundin", PACKAGE_BUGREPORT, "2002,2003, 2008",
138  "ISAAC Spectro jitter recipe",
139  RECIPE_STRING " -- ISAAC spectro jitter recipe\n"
140  "The files listed in the Set Of Frames (sof-file) "
141  "must be tagged:\n"
142  "Observation in nodding mode:\n"
143  "\traw-file.fits "ISAAC_SPC_JITTER_NODOBJ_RAW" or\n"
144  "\traw-file.fits "ISAAC_SPC_JITTER_NODSKY_RAW"\n"
145  "Observation in chopping mode:\n"
146  "\traw-file.fits "ISAAC_SPC_JITTER_CHOP_RAW" or\n"
147  "Calibration (standard star) in nodding mode:\n"
148  "\traw-file.fits "ISAAC_SPC_RESPFUNC_RAW" or\n"
149  "\traw-file.fits "ISAAC_SPC_RESPFUNC_OBJ_RAW" or\n"
150  "\traw-file.fits "ISAAC_SPC_RESPFUNC_SKY_RAW"\n"
151  "\traw-file.fits "ISAAC_SPC_RESPFUNC_FLUX_RAW"\n"
152  "Calibration (standard star) in chopping mode:\n"
153  "\traw-file.fits "ISAAC_SPC_JITTER_CHOP_CAL_RAW"\n"
154  "Calibration files:\n"
155  "\toh.fits "ISAAC_CALPRO_OH_CAT" or\n"
156  "\tflat-file.fits "ISAAC_CALIB_SPFLAT" or\n"
157  "\tflat-file.fits "ISAAC_CALIB_LW_SPFLAT" or\n"
158  "\tarc-file.fits "ISAAC_CALIB_ARC" or\n"
159  "\tarc-file.fits "ISAAC_CALIB_LW_ARC" or\n"
160  "\tstartrace-file.fits "ISAAC_CALIB_STARTRACE" or\n"
161  "\tstartrace-file.fits "ISAAC_CALIB_LW_STARTRACE" or\n"
162  "\tsed-file.fits "ISAAC_CALIB_SED" or\n"
163  "\tstdstars-file.fits "ISAAC_CALIB_STDSTARS"\n");
164 
165 /*-----------------------------------------------------------------------------
166  Static variables
167  -----------------------------------------------------------------------------*/
168 
169 static struct {
170  /* Inputs */
171  int arm;
172  int plot;
173  int oddeven;
174  /* wavecal_in : 0 for physical model, 1 for sky, 2 for arc table */
175  int wavecal_in;
176  int wavecal_rej_bottom;
177  int wavecal_rej_top;
178  int wavecal_rej_left;
179  int wavecal_rej_right;
180  int max_offset;
181  int saa_refine;
182  double saa_rej_high;
183  double saa_rej_low;
184  int extr_spec_pos;
185  int extr_spec_width;
186  int extr_sky_hi_width;
187  int extr_sky_lo_width;
188  int extr_sky_hi_dist;
189  int extr_sky_lo_dist;
190  int std_mode;
191  double std_magnitude;
192  /* Outputs */
193  /* wavecal_out : 0 for physical model, 1 for sky, 2 for arc table */
194  int chopping;
195  cpl_vector * throws;
196  cpl_vector * intensities;
197  char filter[KEYSIZE];
198  char filter_ref[KEYSIZE];
199  char * starname;
200  char * sptype;
201  int wavecal_out;
202  double wavecal_cc;
203  double wavecal_a0;
204  double wavecal_a1;
205  double wavecal_a2;
206  double wavecal_a3;
207 } isaac_spc_jitter_config;
208 
209 
210 /*-----------------------------------------------------------------------------
211  Functions code
212  -----------------------------------------------------------------------------*/
213 
214 /*----------------------------------------------------------------------------*/
222 /*----------------------------------------------------------------------------*/
223 static
224 cpl_error_code isaac_spc_jitter_fill_parameterlist(cpl_parameterlist * self)
225 {
226  const char * context = PACKAGE "." RECIPE_STRING;
227  cpl_error_code err;
228 
229  cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
230 
231  /* Fill the parameters list */
232 
233 
234  /* --oddeven */
235  err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
236  "oddeven", CPL_FALSE, NULL, context,
237  "Flag to correct the oddeven column "
238  "effect");
239  cpl_ensure_code(!err, err);
240 
241  /* --wavecal */
242  err = irplib_parameterlist_set_string(self, PACKAGE, RECIPE_STRING,
243  "wavecal", "sky", NULL, context,
244  "Wavelength calibration method: "
245  "sky, phy or arc");
246  cpl_ensure_code(!err, err);
247 
248  /* --wavecal_rej */
249  err = irplib_parameterlist_set_string(self, PACKAGE, RECIPE_STRING,
250  "wavecal_rej", "-1,-1,50,50",
251  "wc_rej", context,
252  "left right bottom top rejections");
253  cpl_ensure_code(!err, err);
254 
255  /* --max_offset */
256  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
257  "max_offset", 50, NULL, context,
258  "Maximum allowed offset from the "
259  "physical model [pixel]");
260  cpl_ensure_code(!err, err);
261 
262  /* --saa_refine */
263  err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
264  "saa_refine", CPL_TRUE, NULL, context,
265  "Flag to refine the offsets");
266  cpl_ensure_code(!err, err);
267 
268  /* --saa_rej */
269  err = irplib_parameterlist_set_string(self, PACKAGE, RECIPE_STRING,
270  "saa_rej", "0.1,0.1", NULL, context,
271  "Low and high rejection fractions "
272  "(rounded down)");
273  cpl_ensure_code(!err, err);
274 
275  /* --spec_pos */
276  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
277  "spec_pos", -1, NULL, context,
278  "Spectrum position");
279  cpl_ensure_code(!err, err);
280 
281  /* --spec_width */
282  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
283  "spec_width", 10, NULL, context,
284  "Spectrum width");
285  cpl_ensure_code(!err, err);
286 
287  /* --sky_hi_width */
288  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
289  "sky_hi_width", 10, NULL, context,
290  "Sky width above the spectrum");
291  cpl_ensure_code(!err, err);
292 
293  /* --sky_lo_width */
294  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
295  "sky_lo_width", 10, NULL, context,
296  "Sky width below the spectrum");
297  cpl_ensure_code(!err, err);
298 
299  /* --sky_hi_dist */
300  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
301  "sky_hi_dist", -1, NULL, context,
302  "Sky distance above the spectrum");
303  cpl_ensure_code(!err, err);
304 
305  /* --sky_lo_dist */
306  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
307  "sky_lo_dist", -1, NULL, context,
308  "Sky distance below the spectrum");
309  cpl_ensure_code(!err, err);
310 
311  /* --plot */
312  err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
313  "display", CPL_FALSE, NULL, context,
314  "Ignored. Use --plot instead");
315  cpl_ensure_code(!err, err);
316 
317  /* --plot */
318  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
319  "plot", 0, NULL, context,
320  isaac_plot_manpage);
321  cpl_ensure_code(!err, err);
322 
323  /* --std_mode */
324  err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
325  "std_mode", CPL_FALSE, "std", context,
326  "Flag for standard star");
327  cpl_ensure_code(!err, err);
328 
329  /* --std_magnitude */
330  err = irplib_parameterlist_set_double(self, PACKAGE, RECIPE_STRING,
331  "std_magnitude", -1.0, "mag",
332  context, "Standard star magnitude");
333  cpl_ensure_code(!err, err);
334 
335  return CPL_ERROR_NONE;
336 }
337 
338 
339 /*----------------------------------------------------------------------------*/
347 /*----------------------------------------------------------------------------*/
348 static int isaac_spc_jitter(cpl_frameset * framelist,
349  const cpl_parameterlist * parlist)
350 {
351  cpl_errorstate prestate = cpl_errorstate_get();
352  const char * sval;
353  const char * flat;
354  const char * arc;
355  const char * startrace;
356  const char * sed;
357  const char * stdstars;
358  const char * oh;
359  const cpl_frame * cur_frame;
360  cpl_frameset * rawframes = NULL;
361  cpl_propertylist * plist = NULL;
362  cpl_image ** combined = NULL;
363  cpl_table * extracted = NULL;
364 
365  bug_if(0);
366 
367  /* Initialise */
368  isaac_spc_jitter_config.chopping = -1;
369  isaac_spc_jitter_config.wavecal_out = -1;
370  isaac_spc_jitter_config.wavecal_cc = -1.0;
371  isaac_spc_jitter_config.throws = NULL;
372  isaac_spc_jitter_config.intensities = NULL;
373  isaac_spc_jitter_config.filter[0] = (char)0;
374  isaac_spc_jitter_config.filter_ref[0] = (char)0;
375  isaac_spc_jitter_config.starname = NULL;
376  isaac_spc_jitter_config.sptype = NULL;
377  isaac_spc_jitter_config.wavecal_in = -1;
378  isaac_spc_jitter_config.arm = -1;
379 
380  /* Retrieve input parameters */
381  /* --wavecal */
382  sval = irplib_parameterlist_get_string(parlist, PACKAGE, RECIPE_STRING,
383  "wavecal");
384  bug_if(sval == NULL);
385 
386  if (!strcmp(sval, "phy")) isaac_spc_jitter_config.wavecal_in = 0;
387  else if (!strcmp(sval, "sky")) isaac_spc_jitter_config.wavecal_in = 1;
388  else if (!strcmp(sval, "arc")) isaac_spc_jitter_config.wavecal_in = 2;
389 
390  error_if(isaac_spc_jitter_config.wavecal_in < 0,
391  CPL_ERROR_UNSUPPORTED_MODE, "Invalid value for wavecal option: "
392  "%s", sval);
393 
394  /* Rejection parameters for wavelength calibration*/
395  sval = irplib_parameterlist_get_string(parlist, PACKAGE, RECIPE_STRING,
396  "wavecal_rej");
397  bug_if(sval == NULL);
398 
399  skip_if (sscanf(sval, "%d,%d,%d,%d",
400  &isaac_spc_jitter_config.wavecal_rej_left,
401  &isaac_spc_jitter_config.wavecal_rej_right,
402  &isaac_spc_jitter_config.wavecal_rej_bottom,
403  &isaac_spc_jitter_config.wavecal_rej_top) != 4);
404 
405  /* Max offset in pixels */
406  isaac_spc_jitter_config.max_offset
407  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
408  "max_offset");
409 
410  /* Oddeven */
411  isaac_spc_jitter_config.oddeven
412  = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
413  "oddeven");
414 
415  /* Refine of offsets */
416  isaac_spc_jitter_config.saa_refine
417  = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
418  "saa_refine");
419 
420  /* Rejection parameters for SAA */
421  sval = irplib_parameterlist_get_string(parlist, PACKAGE, RECIPE_STRING,
422  "saa_rej");
423  bug_if(sval == NULL);
424 
425  skip_if (sscanf(sval, "%lg,%lg",
426  &isaac_spc_jitter_config.saa_rej_low,
427  &isaac_spc_jitter_config.saa_rej_high) != 2);
428 
429  /* --spec_pos */
430  isaac_spc_jitter_config.extr_spec_pos
431  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
432  "spec_pos");
433  /* --spec_width */
434  isaac_spc_jitter_config.extr_spec_width
435  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
436  "spec_width");
437  /* --sky_hi_width */
438  isaac_spc_jitter_config.extr_sky_hi_width
439  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
440  "sky_hi_width");
441  /* --sky_lo_width */
442  isaac_spc_jitter_config.extr_sky_lo_width
443  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
444  "sky_lo_width");
445  /* --sky_hi_dist */
446  isaac_spc_jitter_config.extr_sky_hi_dist
447  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
448  "sky_hi_dist");
449  /* --sky_lo_dist */
450  isaac_spc_jitter_config.extr_sky_lo_dist
451  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
452  "sky_lo_dist");
453  /* --plot */
454  isaac_spc_jitter_config.plot
455  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
456  "plot");
457  /* --std */
458  isaac_spc_jitter_config.std_mode
459  = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
460  "std_mode");
461  /* --mag */
462  isaac_spc_jitter_config.std_magnitude
463  = irplib_parameterlist_get_double(parlist, PACKAGE, RECIPE_STRING,
464  "std_magnitude");
465 
466  /* Identify the RAW and CALIB frames in the input frameset */
467  skip_if (isaac_dfs_set_groups(framelist));
468 
469  /* Retrieve raw frames */
470  if ((rawframes = isaac_extract_frameset(framelist,
471  ISAAC_SPC_JITTER_CHOP_RAW)) != NULL) {
472  } else if ((rawframes = isaac_extract_frameset(framelist,
473  ISAAC_SPC_JITTER_CHOP_CAL_RAW)) != NULL) {
474  } else if ((rawframes = isaac_extract_frameset(framelist,
475  ISAAC_SPC_JITTER_NODOBJ_RAW)) != NULL) {
476  } else if ((rawframes = isaac_extract_frameset(framelist,
477  ISAAC_SPC_RESPFUNC_RAW)) != NULL) {
478  isaac_spc_jitter_config.std_mode = 1;
479  } else if ((rawframes = isaac_extract_frameset(framelist,
480  ISAAC_SPC_RESPFUNC_OBJ_RAW)) != NULL) {
481  isaac_spc_jitter_config.std_mode = 1;
482  } else if ((rawframes = isaac_extract_frameset(framelist,
483  ISAAC_SPC_RESPFUNC_FLUX_RAW)) != NULL) {
484  isaac_spc_jitter_config.std_mode = 1;
485  } else {
486 
487  /* These frame types alone are not supported */
488 
489  error_if (cpl_frameset_find_const(framelist,
490  ISAAC_SPC_JITTER_NODSKY_RAW),
491  CPL_ERROR_UNSUPPORTED_MODE, "Cannot reduce "
492  ISAAC_SPC_JITTER_NODSKY_RAW "-frames without object frames");
493 
494  error_if (cpl_frameset_find_const(framelist,
495  ISAAC_SPC_RESPFUNC_SKY_RAW),
496  CPL_ERROR_UNSUPPORTED_MODE, "Cannot reduce "
497  ISAAC_SPC_RESPFUNC_SKY_RAW "-frames without object frames");
498 
499  }
500 
501  error_if (rawframes == NULL, CPL_ERROR_DATA_NOT_FOUND,
502  "Cannot find the raw frames in the input list");
503 
504  /* Get the arm used (SW or LW) */
505  cur_frame = cpl_frameset_get_position_const(framelist, 0);
506  skip_if(cur_frame == NULL);
507  plist = cpl_propertylist_load(cpl_frame_get_filename(cur_frame), 0);
508  skip_if(plist == NULL);
509  sval = isaac_pfits_get_arm(plist);
510  error_if (sval == NULL, CPL_ERROR_DATA_NOT_FOUND, "cannot get the arm");
511 
512  if (sval[0] == 'S') isaac_spc_jitter_config.arm = 1;
513  else if (sval[0] == 'L') isaac_spc_jitter_config.arm = 2;
514 
515  error_if (isaac_spc_jitter_config.arm < 0, CPL_ERROR_UNSUPPORTED_MODE,
516  "Unsupported arm: %s", sval);
517 
518  /* Get the filter */
519  sval = isaac_pfits_get_filter(plist);
520  if (sval == NULL) {
521  cpl_error_reset();
522  } else {
523  (void)strncpy(isaac_spc_jitter_config.filter, sval, KEYSIZE);
524  isaac_spc_jitter_config.filter[KEYSIZE-1] = (char)0;
525  }
526  cpl_propertylist_empty(plist);
527 
528  /* Retrieve calibration data */
529  arc = isaac_extract_filename(framelist, ISAAC_CALIB_ARC);
530  if (arc == NULL)
531  arc = isaac_extract_filename(framelist, ISAAC_CALIB_LW_ARC);
532  flat = isaac_extract_filename(framelist, ISAAC_CALIB_SPFLAT);
533  if (flat == NULL)
534  flat = isaac_extract_filename(framelist, ISAAC_CALIB_LW_SPFLAT);
535  startrace = isaac_extract_filename(framelist, ISAAC_CALIB_STARTRACE);
536  if (startrace == NULL)
537  startrace = isaac_extract_filename(framelist, ISAAC_CALIB_LW_STARTRACE);
538  sed = isaac_extract_filename(framelist, ISAAC_CALIB_SED);
539  stdstars = isaac_extract_filename(framelist, ISAAC_CALIB_STDSTARS);
540  oh = isaac_extract_filename(framelist, ISAAC_CALPRO_OH_CAT);
541 
542  /* If arc calibration requested, arc file must be provided */
543  error_if (arc == NULL && isaac_spc_jitter_config.wavecal_in == 2,
544  CPL_ERROR_DATA_NOT_FOUND, "Missing an arc file for the "
545  "arc-based wavelength calibration");
546 
547  cpl_msg_info(cpl_func, "Create the combined image");
548  combined = isaac_spc_jitter_combine(rawframes, oh, flat, arc,
549  startrace);
550  skip_if (combined == NULL);
551 
552  cpl_msg_info(cpl_func, "Extract the spectrum");
553  extracted = isaac_spc_jitter_extract(combined[0]);
554  if (extracted == NULL || !cpl_errorstate_is_equal(prestate)) {
555  cpl_msg_warning(cpl_func, "Cannot extract the spectrum");
556  if (!cpl_errorstate_is_equal(prestate)) {
557  cpl_errorstate_dump(prestate, CPL_FALSE,
558  irplib_errorstate_dump_warning);
559  cpl_errorstate_set(prestate);
560  }
561  } else if (isaac_spc_jitter_config.std_mode) {
562  /* In standard star mode, compute the efficiency and the conversion */
563  cpl_msg_info(cpl_func, "Compute flux calibration");
564  cur_frame = cpl_frameset_get_position_const(framelist, 0);
565  if (isaac_spc_jitter_std(sed, stdstars, cur_frame, extracted,
566  isaac_spc_jitter_config.std_magnitude)) {
567  cpl_msg_warning(cpl_func, "Cannot compute the flux calibration");
568  if (!cpl_errorstate_is_equal(prestate)) {
569  cpl_errorstate_dump(prestate, CPL_FALSE,
570  irplib_errorstate_dump_warning);
571  cpl_errorstate_set(prestate);
572  }
573  }
574  }
575 
576  /* Write the products */
577  cpl_msg_info(cpl_func, "Save the products");
578  skip_if (isaac_spc_jitter_save(framelist, combined[0], combined[1],
579  extracted, parlist));
580 
581  end_skip;
582 
583  if (combined != NULL) {
584  cpl_image_delete(combined[0]);
585  cpl_image_delete(combined[1]);
586  cpl_free(combined);
587  }
588 
589  cpl_table_delete(extracted);
590 
591  cpl_vector_delete(isaac_spc_jitter_config.intensities);
592  cpl_vector_delete(isaac_spc_jitter_config.throws);
593 
594  cpl_free(isaac_spc_jitter_config.starname);
595  cpl_free(isaac_spc_jitter_config.sptype);
596 
597  cpl_propertylist_delete(plist);
598 
599  cpl_frameset_delete(rawframes);
600 
601  return cpl_error_get_code();
602 }
603 
604 /*----------------------------------------------------------------------------*/
615 /*----------------------------------------------------------------------------*/
616 static cpl_image ** isaac_spc_jitter_combine(const cpl_frameset * rawframes,
617  const char * oh,
618  const char * flat,
619  const char * arc,
620  const char * startrace)
621 {
622  int nilist;
623  cpl_imagelist * ilist = NULL;
624  cpl_vector * offsets = NULL;
625  int * groups = NULL;
626  int ngroups = 0; /* Avoid uninit warning */
627  cpl_imagelist * abba = NULL;
628  cpl_vector * abba_off = NULL;
629  cpl_imagelist * nodded = NULL;
630  cpl_vector * nodded_off_y = NULL;
631  double throw;
632  cpl_vector * nodded_off_x = NULL;
633  cpl_bivector * nodded_offsets = NULL;
634  cpl_image ** combined = NULL;
635  int nima;
636  int i;
637 
638  bug_if (rawframes == NULL);
639 
640  /* Load the images */
641  cpl_msg_info(cpl_func, "Load the data");
642  ilist = isaac_spc_jitter_load(rawframes);
643  skip_if (ilist == NULL);
644 
645  nilist = cpl_imagelist_get_size(ilist);
646 
647  /* Apply the odd-even correction */
648  if (isaac_spc_jitter_config.oddeven) {
649  cpl_errorstate prestate = cpl_errorstate_get();
650  cpl_imagelist * corrected = cpl_imagelist_new();
651 
652  cpl_msg_info(cpl_func, "Apply the odd-even effect correction");
653  for (i=0; i < nilist; i++) {
654  cpl_image * cur_im
655  = isaac_oddeven_correct(cpl_imagelist_get(ilist, i));
656 
657  cpl_msg_info(cpl_func, "Correcting frame %d/%d", i+1, nilist);
658 
659  if (cur_im == NULL) break;
660  (void)cpl_imagelist_set(corrected, cur_im, i);
661  }
662 
663  if (i == nilist) {
664  /* Odd/even correction successful */
665  cpl_imagelist_delete(ilist);
666  ilist = corrected;
667  } else {
668  cpl_msg_warning(cpl_func, "Odd/even correction failed for "
669  "image %d/%d", i+1, nilist);
670  cpl_imagelist_delete(corrected);
671  if (!cpl_errorstate_is_equal(prestate)) {
672  cpl_errorstate_dump(prestate, CPL_FALSE,
673  irplib_errorstate_dump_warning);
674  cpl_errorstate_set(prestate);
675  }
676  }
677  }
678 
679 
680  if (isaac_spc_jitter_config.plot > 0) {
681  cpl_plot_image("", "", "", cpl_imagelist_get(ilist, 0));
682  }
683 
684  /* Apply the flatfield */
685  if (flat != NULL) {
686  cpl_errorstate prestate = cpl_errorstate_get();
687  cpl_msg_info(cpl_func, "Apply the flatfield correction");
688  if (isaac_spc_jitter_flat(ilist, flat)) {
689  cpl_msg_warning(cpl_func, "cannot apply the flat field");
690  if (!cpl_errorstate_is_equal(prestate)) {
691  cpl_errorstate_dump(prestate, CPL_FALSE,
692  irplib_errorstate_dump_warning);
693  cpl_errorstate_set(prestate);
694  }
695  }
696  }
697 
698  if (isaac_spc_jitter_config.plot > 0) {
699  cpl_plot_image("", "", "", cpl_imagelist_get(ilist, 0));
700  }
701  /* Get the offsets */
702  cpl_msg_info(cpl_func, "Get the %d offsets", nilist);
703  offsets = isaac_spc_jitter_get_offsets(rawframes);
704 
705  error_if (offsets == NULL, cpl_error_get_code(),
706  "Could not get the offsets");
707 
708  /* Classify in groups the a and b images sequence */
709  cpl_msg_info(cpl_func, "Classify in groups");
710  groups = isaac_spc_jitter_classif(offsets, &ngroups);
711  error_if (groups == NULL, cpl_error_get_code(),
712  "Could not classify the data");
713 
714  /* Shift and add each group to one image */
715  cpl_msg_info(cpl_func, "Shift and add each group to one image");
716  abba = isaac_spc_jitter_saa_groups(ilist, offsets, groups,
717  ngroups, &abba_off);
718  error_if (abba == NULL, cpl_error_get_code(), "Could not shift and add "
719  "groups");
720 
721  /* Compute the wavelength calibration */
722  cpl_msg_info(cpl_func, "Compute the wavelength calibration");
723  error_if (isaac_spc_jitter_wavecal(arc, cpl_imagelist_get(abba, 0), oh,
724  rawframes),
725  cpl_error_get_code(), "Wavelength calibration failed");
726 
727  /* Create the nodded images */
728  cpl_msg_info(cpl_func, "Create the nodded images");
729  nodded = isaac_spc_jitter_nodded(abba, abba_off, &nodded_off_y);
730  cpl_imagelist_delete(abba);
731  abba = NULL;
732 
733  skip_if (nodded == NULL);
734 
735  /* Get the throw offsets from abba_off */
736  nima = cpl_imagelist_get_size(nodded);
737  isaac_spc_jitter_config.throws = cpl_vector_new(nima);
738  if (isaac_spc_jitter_config.chopping == 0) {
739  for (i=0; i<nima/2; i++) {
740  throw = fabs( (cpl_vector_get(abba_off, 2*i))-
741  (cpl_vector_get(abba_off, 2*i+1)));
742  cpl_vector_set(isaac_spc_jitter_config.throws, 2*i, throw);
743  cpl_vector_set(isaac_spc_jitter_config.throws, 2*i+1, throw);
744  }
745  } else {
746  for (i=0; i<nima; i++) {
747  throw = fabs( (cpl_vector_get(abba_off, 2*i))-
748  (cpl_vector_get(abba_off, 2*i+1)));
749  cpl_vector_set(isaac_spc_jitter_config.throws, i, throw);
750  }
751  }
752  cpl_vector_delete(abba_off);
753  abba_off = NULL;
754 
755  /* Get the spectra intensities in the nodded images */
756  cpl_msg_info(cpl_func, "Compute the spectra intensities");
757  cpl_msg_indent_more();
758  isaac_spc_jitter_config.intensities = cpl_vector_new(nima);
759  for (i=0; i<nima; i++) {
760  cpl_errorstate prestate = cpl_errorstate_get();
761  cpl_table * extracted
762  = isaac_spc_jitter_extract(cpl_imagelist_get(nodded, i));
763  double intensity;
764 
765 
766  if (extracted == NULL || !cpl_errorstate_is_equal(prestate)) {
767  cpl_msg_warning(cpl_func,"Cannot extract the spectrum from "
768  "nodded image %d", i+1);
769  cpl_errorstate_dump(prestate, CPL_FALSE,
770  irplib_errorstate_dump_warning);
771  cpl_errorstate_set(prestate);
772  intensity = -1.0;
773  } else {
774  intensity = cpl_table_get_column_mean(extracted,
775  "Extracted_spectrum_value");
776  intensity *= cpl_table_get_nrow(extracted);
777  cpl_table_delete(extracted);
778  cpl_msg_info(cpl_func, "Spectrum intensity %d: %g", i+1,
779  intensity);
780  }
781  cpl_vector_set(isaac_spc_jitter_config.intensities, i, intensity);
782  }
783  cpl_msg_indent_less();
784 
785  if (arc || startrace) {
786  cpl_imagelist * nodded_warped;
787  cpl_msg_info(cpl_func, "Correct the distortion on nodded images");
788  cpl_msg_indent_more();
789  nodded_warped = isaac_spc_jitter_distor(nodded, arc, startrace);
790  cpl_msg_indent_less();
791 
792  skip_if (nodded_warped == NULL);
793 
794  cpl_imagelist_delete(nodded);
795  nodded = nodded_warped;
796  nima = cpl_imagelist_get_size(nodded);
797  }
798 
799 
800  /* Refine the offsets if requested */
801  if (isaac_spc_jitter_config.saa_refine) {
802  double * pnodded_off_y = cpl_vector_get_data(nodded_off_y);
803  cpl_msg_info(cpl_func, "Refine the %d offsets", nima);
804  for (i=0; i < nima; i++) {
805  const double new_offset
806  = isaac_spc_jitter_refine_offset(cpl_imagelist_get(nodded, 0),
807  cpl_imagelist_get(nodded, i));
808  if (new_offset > 5000) {
809  cpl_msg_debug(cpl_func, "cannot refine the offset - keep %g",
810  pnodded_off_y[i]);
811  } else if (fabs(new_offset-pnodded_off_y[i]) <
812  ISAAC_SPC_JITTER_OFFSET_ERR) {
813  cpl_msg_debug(cpl_func, "refined offset : %g (old was %g)",
814  new_offset, pnodded_off_y[i]);
815  pnodded_off_y[i] = new_offset;
816  } else {
817  cpl_msg_debug(cpl_func,
818  "refined offset %g too different - keep %g",
819  new_offset, pnodded_off_y[i]);
820  }
821  }
822  }
823 
824  /* Images Shift and add */
825 
826  /* Get the offsets in a bivector */
827  nodded_off_x = cpl_vector_new(nima);
828  bug_if(cpl_vector_fill(nodded_off_x, 0.0));
829  nodded_offsets = cpl_bivector_wrap_vectors(nodded_off_x, nodded_off_y);
830 
831  if (nima == 1) {
832 
833  combined = cpl_malloc(2 * sizeof(cpl_image*));
834  combined[1] = NULL; /* In case the unset fails */
835 
836  combined[0] = cpl_imagelist_unset(nodded, 0);
837  bug_if (combined[0] == NULL);
838 
839  combined[1] = cpl_image_new(cpl_image_get_size_x(combined[0]),
840  cpl_image_get_size_y(combined[0]),
841  CPL_TYPE_INT);
842  bug_if (combined[1] == NULL);
843 
844  /* Set all pixel values to 1 */
845  bug_if(cpl_image_threshold(combined[1], 1.0, 1.0, 1.0, 1.0));
846 
847  } else {
848  /* FIXME: Round-off instead */
849  const int nrejlo = (int)(nima * isaac_spc_jitter_config.saa_rej_low);
850  const int nrejhi = (int)(nima * isaac_spc_jitter_config.saa_rej_high);
851 
852  cpl_msg_info(cpl_func, "Apply the shift and add on the %d nodded "
853  "frames (Rejecting %d lowermost, %d uppermost)", nima,
854  nrejlo, nrejhi);
855  combined = cpl_geom_img_offset_saa(nodded, nodded_offsets,
856  CPL_KERNEL_DEFAULT, nrejlo, nrejhi,
857  CPL_GEOM_FIRST, NULL, NULL);
858  }
859 
860  skip_if(combined == NULL);
861 
862  end_skip;
863 
864  cpl_free(groups);
865  cpl_imagelist_delete(ilist);
866  cpl_vector_delete(offsets);
867  cpl_imagelist_delete(abba);
868  cpl_vector_delete(abba_off);
869  cpl_imagelist_delete(nodded);
870  cpl_bivector_unwrap_vectors(nodded_offsets);
871  cpl_vector_delete(nodded_off_x);
872  cpl_vector_delete(nodded_off_y);
873 
874  return combined;
875 }
876 
877 /*----------------------------------------------------------------------------*/
885 /*----------------------------------------------------------------------------*/
886 static cpl_error_code isaac_spc_jitter_flat(cpl_imagelist * self,
887  const char * flat)
888 {
889  cpl_image * fim = NULL;
890  cpl_image * big_fim = NULL;
891  const int mx = cpl_image_get_size_x(cpl_imagelist_get(self, 0));
892  const int my = cpl_image_get_size_y(cpl_imagelist_get(self, 0));
893  int nx, ny;
894 
895  bug_if (self == NULL);
896  bug_if (flat == NULL);
897 
898  /* Load the flat */
899  fim = cpl_image_load(flat, CPL_TYPE_UNSPECIFIED, 0, 0);
900  error_if(fim == NULL, cpl_error_get_code(), "Could not load the flat "
901  "field from %s", flat);
902 
903  /* Get flat size */
904  nx = cpl_image_get_size_x(fim);
905  ny = cpl_image_get_size_y(fim);
906 
907  error_if(ny != my, CPL_ERROR_INCOMPATIBLE_INPUT, "Flat field Y-size "
908  "%d is incompatible with image Y-size %d", ny, my);
909 
910  /* Handle the case where nx is smaller */
911  if (nx != mx) {
912  cpl_propertylist * plist
913  = cpl_propertylist_load_regexp(flat, 0, "ESO DET WIN STARTX", 0);
914  const int lx = (int)isaac_pfits_get_win_startx(plist);
915 
916  cpl_propertylist_delete(plist);
917  skip_if (0);
918 
919  cpl_msg_info(cpl_func, "Windowing mode: flat between pixels %d "
920  "and %d", lx, ISAAC_MIN(mx, lx + nx - 1));
921 
922  if (lx > mx || lx < 1) {
923  cpl_msg_warning(cpl_func, "Cannot apply out of bounds flat field, "
924  "lx=%d > mx=%d", lx, mx);
925  cpl_image_delete(fim);
926  fim = NULL;
927  } else {
928 
929  /* Create the big flat image */
930  big_fim = cpl_image_new(mx, my, cpl_image_get_type(fim));
931 
932  /* Set all pixel values to 1 */
933  bug_if(cpl_image_threshold(big_fim, 1.0, 1.0, 1.0, 1.0));
934 
935  /* Copy the small flat in the big one */
936  bug_if (cpl_image_copy(big_fim, fim, lx, 1));
937 
938  cpl_image_delete(fim);
939  fim = big_fim;
940  big_fim = NULL;
941  }
942  }
943 
944  if (fim != NULL) {
945 
946  /* Threshold all pixels below the "mid-point" of FLT_MIN and 1, and
947  all pixels above the "mid-point" of 1 and FLT_MAX. */
948  bug_if(cpl_image_threshold(fim, sqrt(FLT_MIN), sqrt(FLT_MAX),
949  1.0, 1.0));
950 
951  /* Apply the division */
952  error_if (cpl_imagelist_divide_image(self, fim),
953  cpl_error_get_code(), "Could not apply the flat field");
954  }
955 
956  end_skip;
957 
958  cpl_image_delete(fim);
959  cpl_image_delete(big_fim);
960 
961  return 0;
962 }
963 
964 /*----------------------------------------------------------------------------*/
971 /*----------------------------------------------------------------------------*/
972 static cpl_imagelist * isaac_spc_jitter_load(const cpl_frameset * rawframes)
973 {
974  /* Load the propertylist of the first frame to get the data type */
975  const cpl_frame * cur_frame = cpl_frameset_get_position_const(rawframes, 0);
976  cpl_propertylist * plist
977  = cpl_propertylist_load(cpl_frame_get_filename(cur_frame), 0);
978  const char * sval;
979  cpl_imagelist * self = NULL;
980 
981 
982  skip_if (plist == NULL);
983 
984  sval = isaac_pfits_get_frame_type(plist);
985  skip_if (sval == NULL);
986 
987  if (!strcmp(sval, "INT")) isaac_spc_jitter_config.chopping = 0;
988  else if (!strcmp(sval, "CUBE1")) isaac_spc_jitter_config.chopping = 1;
989  else {
990  error_if(1, CPL_ERROR_UNSUPPORTED_MODE, "Unsupported frame type: %s",
991  sval);
992  }
993 
994  cpl_propertylist_empty(plist);
995 
996  /* Load the data */
997  self = cpl_imagelist_load_frameset(rawframes, CPL_TYPE_FLOAT, 1, 0);
998 
999  if (isaac_spc_jitter_config.chopping == 1) {
1000  cpl_imagelist * self_tmp
1001  = cpl_imagelist_load_frameset(rawframes, CPL_TYPE_FLOAT, 2, 0);
1002  const cpl_error_code error = cpl_imagelist_subtract(self, self_tmp);
1003 
1004  cpl_imagelist_delete(self_tmp);
1005  skip_if(error);
1006  }
1007 
1008  end_skip;
1009 
1010  cpl_propertylist_delete(plist);
1011 
1012  if (cpl_error_get_code()) {
1013  cpl_imagelist_delete(self);
1014  self = NULL;
1015  }
1016 
1017  return self;
1018 }
1019 
1020 /*----------------------------------------------------------------------------*/
1027 /*----------------------------------------------------------------------------*/
1028 static
1029 cpl_vector * isaac_spc_jitter_get_offsets(const cpl_frameset * rawframes)
1030 {
1031  const int nraw = cpl_frameset_get_size(rawframes);
1032  cpl_vector * offsets = cpl_vector_new(nraw);
1033  cpl_propertylist * plist = NULL;
1034  int i;
1035 
1036  bug_if (rawframes == NULL);
1037  bug_if (nraw == 0);
1038 
1039 
1040  /* Get the rawframes Y offsets */
1041  for (i=0; i < nraw; i++) {
1042  const cpl_frame * cur_frame
1043  = cpl_frameset_get_position_const(rawframes, i);
1044  const char * filename = cpl_frame_get_filename(cur_frame);
1045  double yoff;
1046 
1047  skip_if(filename == NULL);
1048 
1049  cpl_propertylist_delete(plist);
1050  plist = cpl_propertylist_load_regexp(filename, 0,
1051  "ESO SEQ CUMOFFSETY", 0);
1052 
1053  any_if("Could not load propertylist from %s", filename);
1054 
1055  yoff = cpl_propertylist_get_double(plist, "ESO SEQ CUMOFFSETY");
1056 
1057  any_if("Could not get the offset from %s", filename);
1058 
1059  bug_if(cpl_vector_set(offsets, i, yoff));
1060  }
1061 
1062  end_skip;
1063 
1064  cpl_propertylist_delete(plist);
1065 
1066  if (cpl_error_get_code()) {
1067  cpl_vector_delete(offsets);
1068  offsets = NULL;
1069  }
1070 
1071  return offsets;
1072 }
1073 
1074 /*----------------------------------------------------------------------------*/
1113 /*----------------------------------------------------------------------------*/
1114 static int * isaac_spc_jitter_classif(const cpl_vector * offsets,
1115  int * pngroups)
1116 {
1117  const int nraw = cpl_vector_get_size(offsets);
1118  const double * pvect = cpl_vector_get_data_const(offsets);
1119  const double offmin = cpl_vector_get_min(offsets);
1120  const double offmax = cpl_vector_get_max(offsets);
1121  const double offset_thresh = 0.5 * (offmin + offmax);
1122  int * groups = cpl_calloc(nraw, sizeof(int));
1123  int last_group;
1124  int i = 0;
1125 
1126 
1127  bug_if(offsets == NULL);
1128  bug_if(pngroups == NULL);
1129  *pngroups = 0;
1130 
1131  /* Separate the offsets in 2 categories */
1132  error_if (offmin >= offmax, CPL_ERROR_DATA_NOT_FOUND,
1133  "The %d offset(s) all have the same value: %g", nraw, pvect[0]);
1134 
1135  /* Identify the different A and B groups */
1136 
1137  /* Create a look up table to associate the ith obj with the jth frame */
1138  while (i < nraw) {
1139  int j = 0;
1140  /* Count the number of successive '+' or '-' (j) */
1141  while ((i+j < nraw) &&
1142  (!off_comp(pvect[i], pvect[i+j], offset_thresh))) j++;
1143 
1144  if (i+j >= nraw) i = nraw;
1145  else {
1146  int l, k = 0;
1147  /* Check if there are j '-' or '+' (k) */
1148  while ((i+j+k < nraw)
1149  && (!off_comp(pvect[i+j], pvect[i+j+k], offset_thresh))
1150  && (k<j)) k++;
1151  last_group = 1;
1152  if (i+j+k < nraw) {
1153  for (l=i+j+k; l<nraw; l++) {
1154  if (off_comp(pvect[i+j], pvect[l], offset_thresh)) {
1155  last_group = 0;
1156  break;
1157  }
1158  }
1159  }
1160  if (last_group == 0) {
1161  for (l=0; l<j; l++) groups[i+l] = *pngroups + 1;
1162  for (l=0; l<k; l++) groups[i+j+l] = *pngroups + 2;
1163  *pngroups += 2;
1164  i += j+k;
1165  } else {
1166  for (l=0; l<j; l++) groups[i+l] = *pngroups + 1;
1167  for (l=0; l<nraw - (i+j); l++) groups[i+j+l] = *pngroups + 2;
1168  *pngroups += 2;
1169  i = nraw;
1170  }
1171  }
1172  }
1173 
1174  /* Nb of groups found should be even */
1175  error_if (*pngroups & 1, CPL_ERROR_ILLEGAL_INPUT, "Found an odd number "
1176  "(%d) of groups in the %d offsets", *pngroups, nraw);
1177 
1178  end_skip;
1179 
1180  if (cpl_error_get_code()) {
1181  cpl_free(groups);
1182  groups = NULL;
1183  }
1184 
1185  return groups;
1186 }
1187 
1188 /*----------------------------------------------------------------------------*/
1220 /*----------------------------------------------------------------------------*/
1221 static cpl_imagelist * isaac_spc_jitter_saa_groups(
1222  cpl_imagelist * ilist,
1223  cpl_vector * offsets,
1224  int * groups,
1225  int ngroups,
1226  cpl_vector ** abba_off)
1227 {
1228  cpl_imagelist * abba;
1229  cpl_imagelist * group_list;
1230  cpl_image * tmp_ima;
1231  cpl_image ** combined;
1232  cpl_bivector * group_off;
1233  double * pgroup_off;
1234  double * poffsets;
1235  double * pabba_off;
1236  int nima;
1237  int saa;
1238  int i, j, k;
1239 
1240  /* Test entries */
1241  if ((ilist == NULL) || (offsets == NULL) || (groups == NULL)) return NULL;
1242 
1243  /* Initialise */
1244  nima = cpl_imagelist_get_size(ilist);
1245  poffsets = cpl_vector_get_data(offsets);
1246 
1247  /* Create the output image list */
1248  abba = cpl_imagelist_new();
1249  *abba_off = cpl_vector_new(ngroups);
1250  pabba_off = cpl_vector_get_data(*abba_off);
1251 
1252  /* Loop on the groups */
1253  for (i=0; i<ngroups; i++) {
1254  /* Initialise */
1255  saa = 0;
1256  /* Create the group list of images */
1257  group_list = cpl_imagelist_new();
1258  k = 0;
1259  for (j=0; j<nima; j++) {
1260  if (i+1 == groups[j]) {
1261  /* Get the first offset of the group in abba_off */
1262  if (k==0) pabba_off[i] = poffsets[j];
1263  /* To know if we need the saa (shift and add) */
1264  if (fabs(pabba_off[i]-poffsets[j]) > 1e-3) saa = 1;
1265  /* Copy the images of the group in group_list */
1266  tmp_ima = cpl_image_duplicate(cpl_imagelist_get(ilist, j));
1267  cpl_imagelist_set(group_list, tmp_ima, k);
1268  tmp_ima = NULL;
1269  k++;
1270  }
1271  }
1272 
1273  if (saa) {
1274  /* Get the offsets of the group in group_off */
1275  group_off = cpl_bivector_new(k);
1276  cpl_vector_fill(cpl_bivector_get_x(group_off), 0.0);
1277  pgroup_off = cpl_bivector_get_y_data(group_off);
1278  k = 0;
1279  for (j=0; j<nima; j++) {
1280  if (i+1 == groups[j]) {
1281  pgroup_off[k] = poffsets[j];
1282  k++;
1283  }
1284  }
1285  cpl_vector_subtract_scalar(cpl_bivector_get_y(group_off),
1286  pabba_off[i]);
1287  /* Shift and add */
1288  cpl_msg_debug(cpl_func, "Apply shift-and-add for group %d", i+1);
1289  if ((combined = cpl_geom_img_offset_saa(group_list,
1290  group_off, CPL_KERNEL_DEFAULT, 0, 0,
1291  CPL_GEOM_FIRST, NULL, NULL)) == NULL) {
1292  cpl_msg_error(cpl_func, "Cannot shift and add group nb %d", i+1);
1293  cpl_imagelist_delete(group_list);
1294  cpl_bivector_delete(group_off);
1295  cpl_imagelist_delete(abba);
1296  cpl_vector_delete(*abba_off);
1297  return NULL;
1298  }
1299  cpl_bivector_delete(group_off);
1300  cpl_image_delete(combined[1]);
1301  cpl_imagelist_set(abba, combined[0], i);
1302  cpl_free(combined);
1303  } else {
1304  /* Averaging */
1305  cpl_msg_debug(cpl_func, "Apply averaging for group %d", i+1);
1306  if ((tmp_ima = cpl_imagelist_collapse_create(group_list)) == NULL) {
1307  cpl_msg_error(cpl_func, "Cannot average group nb %d", i+1);
1308  cpl_imagelist_delete(group_list);
1309  cpl_imagelist_delete(abba);
1310  cpl_vector_delete(*abba_off);
1311  return NULL;
1312  }
1313  cpl_imagelist_set(abba, tmp_ima, i);
1314  }
1315  cpl_imagelist_delete(group_list);
1316  }
1317  return abba;
1318 }
1319 
1320 /*----------------------------------------------------------------------------*/
1330 /*----------------------------------------------------------------------------*/
1331 static int isaac_spc_jitter_wavecal(const char * arc,
1332  const cpl_image * ima,
1333  const char * oh,
1334  const cpl_frameset * raw)
1335 {
1336  cpl_table * arc_tab;
1337  double * phdisprel;
1338  const cpl_frame * cur_frame;
1339  const char * cur_fname;
1340  computed_disprel * disprel;
1341  int order;
1342  double slit_width;
1343 
1344  /* Get the wavelength from the arc file */
1345  if (isaac_spc_jitter_config.wavecal_in == 2) {
1346  if (arc == NULL) {
1347  cpl_msg_error(cpl_func,
1348  "Missing arc for the wavelength calibration");
1349  return CPL_ERROR_UNSPECIFIED;
1350  }
1351  cpl_msg_info(cpl_func, "Get the wavelength from the ARC file");
1352  if ((arc_tab = cpl_table_load(arc, 1, 0)) == NULL) {
1353  cpl_msg_error(cpl_func, "Cannot load the arc table");
1354  isaac_spc_jitter_config.wavecal_out = -1;
1355  return CPL_ERROR_UNSPECIFIED;
1356  }
1357  isaac_spc_jitter_config.wavecal_a0 =
1358  cpl_table_get_double(arc_tab, "WL_coefficients", 0, NULL);
1359  isaac_spc_jitter_config.wavecal_a1 =
1360  cpl_table_get_double(arc_tab, "WL_coefficients", 1, NULL);
1361  isaac_spc_jitter_config.wavecal_a2 =
1362  cpl_table_get_double(arc_tab, "WL_coefficients", 2, NULL);
1363  isaac_spc_jitter_config.wavecal_a3 =
1364  cpl_table_get_double(arc_tab, "WL_coefficients", 3, NULL);
1365  cpl_table_delete(arc_tab);
1366  isaac_spc_jitter_config.wavecal_out = 2;
1367  isaac_spc_jitter_config.wavecal_cc = -1.0;
1368  return 0;
1369  }
1370 
1371  /* Get the reference frame */
1372  cur_frame = cpl_frameset_get_position_const(raw, 0);
1373  cur_fname = cpl_frame_get_filename(cur_frame);
1374 
1375  /* Get the physical model */
1376  cpl_msg_info(cpl_func, "Compute the physical model");
1377  cpl_msg_indent_more();
1378  if ((phdisprel = isaac_get_disprel_estimate(cur_fname, 3)) == NULL) {
1379  cpl_msg_error(cpl_func, "cannot compute the physical model");
1380  isaac_spc_jitter_config.wavecal_out = -1;
1381  cpl_msg_indent_less();
1382  return CPL_ERROR_UNSPECIFIED;
1383  }
1384  cpl_msg_info(cpl_func, "f(x)=%g + %g*x + %g*x^2 + %g*x^3",
1385  phdisprel[0], phdisprel[1], phdisprel[2], phdisprel[3]);
1386  isaac_spc_jitter_config.wavecal_a0 = phdisprel[0];
1387  isaac_spc_jitter_config.wavecal_a1 = phdisprel[1];
1388  isaac_spc_jitter_config.wavecal_a2 = phdisprel[2];
1389  isaac_spc_jitter_config.wavecal_a3 = phdisprel[3];
1390  isaac_spc_jitter_config.wavecal_cc = -1.0;
1391  isaac_spc_jitter_config.wavecal_out = 0;
1392  cpl_msg_indent_less();
1393 
1394  /* Compute the wavelength using the sky lines */
1395  if (isaac_spc_jitter_config.wavecal_in == 1) {
1396  /* Compute the slit_width */
1397  if ((slit_width = isaac_get_slitwidth(cur_fname)) == -1) {
1398  cpl_msg_warning(cpl_func, "cannot get the slit width");
1399  cpl_free(phdisprel);
1400  return 0;
1401  }
1402  /* Get the order */
1403  if ((order = isaac_find_order(cur_fname)) == -1) {
1404  cpl_msg_warning(cpl_func, "cannot get the order");
1405  cpl_free(phdisprel);
1406  return 0;
1407  }
1408  /* Compute the wavelength */
1409  cpl_msg_info(cpl_func, "Compute the wavelength with the sky lines");
1410  cpl_msg_indent_more();
1411  if ((disprel = spectro_compute_disprel(ima,
1412  isaac_spc_jitter_config.wavecal_rej_bottom,
1413  isaac_spc_jitter_config.wavecal_rej_top,
1414  isaac_spc_jitter_config.wavecal_rej_left,
1415  isaac_spc_jitter_config.wavecal_rej_right,
1416  isaac_spc_jitter_config.max_offset,
1417  isaac_has_thermal(cur_fname) > 0,
1418  "oh", oh, NULL, NULL, slit_width, order,
1419  (int)(cpl_msg_get_level() == CPL_MSG_DEBUG),
1420  phdisprel)) == NULL) {
1421  cpl_msg_error(cpl_func, "cannot compute the dispersion relation");
1422  cpl_free(phdisprel);
1423  cpl_msg_indent_less();
1424  return 0;
1425  }
1426  cpl_msg_info(cpl_func, "Cross correlation factor: %g", disprel->cc);
1427  cpl_msg_info(cpl_func, "f(x)=%g + %g*x + %g*x^2 + %g*x^3",
1428  disprel->poly[0], disprel->poly[1], disprel->poly[2],
1429  disprel->poly[3]);
1430  isaac_spc_jitter_config.wavecal_a0 = disprel->poly[0];
1431  isaac_spc_jitter_config.wavecal_a1 = disprel->poly[1];
1432  isaac_spc_jitter_config.wavecal_a2 = disprel->poly[2];
1433  isaac_spc_jitter_config.wavecal_a3 = disprel->poly[3];
1434  isaac_spc_jitter_config.wavecal_cc = disprel->cc;
1435  isaac_spc_jitter_config.wavecal_out = 1;
1436  if (disprel->poly != NULL) cpl_free(disprel->poly);
1437  cpl_free(disprel);
1438  cpl_msg_indent_less();
1439  }
1440  cpl_free(phdisprel);
1441  return 0;
1442 }
1443 
1444 /*----------------------------------------------------------------------------*/
1477 /*----------------------------------------------------------------------------*/
1478 static cpl_imagelist * isaac_spc_jitter_nodded(cpl_imagelist * abba,
1479  cpl_vector * abba_off,
1480  cpl_vector ** nodded_off)
1481 {
1482  cpl_imagelist * nodded;
1483  cpl_image * tmp_ima;
1484  int nima;
1485  double * pabba_off;
1486  double * pnodded_off;
1487  double ref_off;
1488  int i;
1489 
1490  /* Test entries */
1491  if ((abba == NULL) || (abba_off == NULL)) return NULL;
1492 
1493  /* Initialise */
1494  nima = cpl_imagelist_get_size(abba);
1495  if (nima % 2) {
1496  cpl_msg_error(cpl_func, "Number of images should be even");
1497  return NULL;
1498  }
1499 
1500  /* Non-chopping case */
1501  if (isaac_spc_jitter_config.chopping == 0) {
1502  /* Create the offsets between the nodded images */
1503  *nodded_off = cpl_vector_duplicate(abba_off);
1504  /* The image list to contain the nodded images */
1505  nodded = cpl_imagelist_new();
1506  for (i=0; i<(nima/2); i++) {
1507  /* a-b */
1508  tmp_ima = cpl_image_duplicate(cpl_imagelist_get(abba, 2*i));
1509  cpl_image_subtract(tmp_ima, cpl_imagelist_get(abba, 2*i+1));
1510  cpl_imagelist_set(nodded, tmp_ima, 2*i);
1511  /* b-a */
1512  tmp_ima = cpl_image_duplicate(cpl_imagelist_get(abba, 2*i+1));
1513  cpl_image_subtract(tmp_ima, cpl_imagelist_get(abba, 2*i));
1514  cpl_imagelist_set(nodded, tmp_ima, 2*i+1);
1515  }
1516  /* Chopping case */
1517  } else if (isaac_spc_jitter_config.chopping == 1) {
1518  /* Create the offsets between the nodded images */
1519  *nodded_off = cpl_vector_new(nima/2);
1520  pnodded_off = cpl_vector_get_data(*nodded_off);
1521  pabba_off = cpl_vector_get_data(abba_off);
1522  /* The image list to contain the nodded images */
1523  nodded = cpl_imagelist_new();
1524  for (i=0; i<nima/2; i++) {
1525  if ((pabba_off[0]-pabba_off[1])*(pabba_off[2*i]-pabba_off[2*i+1])
1526  > 0) {
1527  /* ab sequence */
1528  pnodded_off[i] = pabba_off[2*i];
1529  tmp_ima = cpl_image_duplicate(cpl_imagelist_get(abba, 2*i));
1530  cpl_image_subtract(tmp_ima, cpl_imagelist_get(abba, 2*i+1));
1531  cpl_image_divide_scalar(tmp_ima, 2.0);
1532  cpl_imagelist_set(nodded, tmp_ima, i);
1533  } else {
1534  /* ba sequence */
1535  pnodded_off[i] = pabba_off[2*i+1];
1536  tmp_ima = cpl_image_duplicate(cpl_imagelist_get(abba, 2*i+1));
1537  cpl_image_subtract(tmp_ima, cpl_imagelist_get(abba, 2*i));
1538  cpl_image_divide_scalar(tmp_ima, 2.0);
1539  cpl_imagelist_set(nodded, tmp_ima, i);
1540  }
1541  }
1542  } else {
1543  cpl_msg_error(cpl_func, "Unsupported chopping mode");
1544  return NULL;
1545  }
1546 
1547  /* Subtract the first offset to the others */
1548  ref_off = cpl_vector_get(*nodded_off, 0);
1549  cpl_vector_subtract_scalar(*nodded_off, ref_off);
1550  return nodded;
1551 }
1552 
1553 /*----------------------------------------------------------------------------*/
1562 /*----------------------------------------------------------------------------*/
1563 static cpl_imagelist * isaac_spc_jitter_distor(
1564  cpl_imagelist * ilist,
1565  const char * arc,
1566  const char * startrace)
1567 {
1568  cpl_polynomial * arc_poly;
1569  cpl_polynomial * sttr_poly;
1570  cpl_table * tab;
1571  cpl_size power[2];
1572  cpl_vector * profile;
1573  cpl_imagelist * warped_list;
1574  cpl_image * warped;
1575  int i;
1576 
1577  /* Test entries */
1578  if (ilist == NULL) return NULL;
1579  if ((arc == NULL) && (startrace == NULL)) return NULL;
1580 
1581  /* Get the arc polynomial */
1582  arc_poly = cpl_polynomial_new(2);
1583  if (arc != NULL) {
1584  cpl_msg_info(cpl_func, "Get the arc distortion from the file");
1585  if ((tab = cpl_table_load(arc, 1, 0)) == NULL) {
1586  cpl_msg_error(cpl_func, "cannot load the arc table");
1587  cpl_polynomial_delete(arc_poly);
1588  return NULL;
1589  }
1590  for (i=0; i<cpl_table_get_nrow(tab); i++) {
1591  if (cpl_table_get_column_type(tab, "Degree_of_x") == CPL_TYPE_INT) {
1592  power[0] = cpl_table_get_int(tab, "Degree_of_x", i, NULL);
1593  power[1] = cpl_table_get_int(tab, "Degree_of_y", i, NULL);
1594  } else {
1595  power[0] = cpl_table_get_double(tab, "Degree_of_x", i, NULL);
1596  power[1] = cpl_table_get_double(tab, "Degree_of_y", i, NULL);
1597  }
1598  cpl_polynomial_set_coeff(arc_poly, power,
1599  cpl_table_get_double(tab, "poly2d_coef", i, NULL));
1600  }
1601  cpl_table_delete(tab);
1602  } else {
1603  cpl_msg_info(cpl_func, "Use the ID polynomial for the arc dist");
1604  power[0] = 1;
1605  power[1] = 0;
1606  cpl_polynomial_set_coeff(arc_poly, power, 1.0);
1607  }
1608 
1609  /* Get the startrace polynomial */
1610  sttr_poly = cpl_polynomial_new(2);
1611  if (startrace != NULL) {
1612  cpl_msg_info(cpl_func, "Get the startrace distortion from the file");
1613  if ((tab = cpl_table_load(startrace, 1, 0)) == NULL) {
1614  cpl_msg_error(cpl_func, "cannot load the startrace table");
1615  cpl_polynomial_delete(arc_poly);
1616  cpl_polynomial_delete(sttr_poly);
1617  return NULL;
1618  }
1619  for (i=0; i<cpl_table_get_nrow(tab); i++) {
1620 
1621  if (cpl_table_get_column_type(tab, "Degree_of_x") == CPL_TYPE_INT) {
1622  power[0] = cpl_table_get_int(tab, "Degree_of_x", i, NULL);
1623  power[1] = cpl_table_get_int(tab, "Degree_of_y", i, NULL);
1624  } else {
1625  power[0] = cpl_table_get_double(tab, "Degree_of_x", i, NULL);
1626  power[1] = cpl_table_get_double(tab, "Degree_of_y", i, NULL);
1627  }
1628  cpl_polynomial_set_coeff(sttr_poly, power,
1629  cpl_table_get_double(tab, "poly2d_coef", i, NULL));
1630  }
1631  cpl_table_delete(tab);
1632  } else {
1633  cpl_msg_info(cpl_func, "Use the ID polynomial for the startrace dist");
1634  power[0] = 0;
1635  power[1] = 1;
1636  cpl_polynomial_set_coeff(sttr_poly, power, 1.0);
1637  }
1638 
1639  /* Create the kernel */
1640  profile = cpl_vector_new(CPL_KERNEL_DEF_SAMPLES);
1641  cpl_vector_fill_kernel_profile(profile, CPL_KERNEL_DEFAULT,
1642  CPL_KERNEL_DEF_WIDTH);
1643 
1644  /* Correct the images */
1645  warped_list = cpl_imagelist_new();
1646  for (i=0; i<cpl_imagelist_get_size(ilist); i++) {
1647  warped = cpl_image_duplicate(cpl_imagelist_get(ilist, i));
1648  if (cpl_image_warp_polynomial(warped, cpl_imagelist_get(ilist, i),
1649  arc_poly, sttr_poly, profile, CPL_KERNEL_DEF_WIDTH, profile,
1650  CPL_KERNEL_DEF_WIDTH) != CPL_ERROR_NONE) {
1651  cpl_msg_error(cpl_func, "cannot correct the distortion");
1652  cpl_image_delete(warped);
1653  cpl_polynomial_delete(arc_poly);
1654  cpl_polynomial_delete(sttr_poly);
1655  cpl_vector_delete(profile);
1656  return NULL;
1657  }
1658  cpl_imagelist_set(warped_list, warped, i);
1659  }
1660  cpl_vector_delete(profile);
1661  cpl_polynomial_delete(arc_poly);
1662  cpl_polynomial_delete(sttr_poly);
1663  return warped_list;
1664 }
1665 
1666 /*----------------------------------------------------------------------------*/
1674 /*----------------------------------------------------------------------------*/
1675 static double isaac_spc_jitter_refine_offset(const cpl_image * ima1,
1676  const cpl_image * ima2)
1677 {
1678  double pos1, pos2;
1679 
1680  /* Test entries */
1681  if (ima1 == NULL) return 10000.0;
1682  if (ima2 == NULL) return 10000.0;
1683 
1684  /* Detect the spectra */
1685  if (irplib_spectrum_find_brightest(ima1, 0.0, NO_SHADOW, 0.0, 0,
1686  &pos1) == -1){
1687  return 10000.0;
1688  }
1689  if (irplib_spectrum_find_brightest(ima2, 0.0, NO_SHADOW, 0.0, 0,
1690  &pos2) == -1){
1691  return 10000.0;
1692  }
1693  return pos1-pos2;
1694 }
1695 
1696 /*----------------------------------------------------------------------------*/
1703 /*----------------------------------------------------------------------------*/
1704 static cpl_table * isaac_spc_jitter_extract(const cpl_image * combined)
1705 {
1706  int lo_dist, hi_dist, lo_width, hi_width, spec_pos;
1707  int nx, ny;
1708  double pos;
1709  int low_side, up_side;
1710  int sky_pos[4];
1711  cpl_vector * sky;
1712  cpl_vector * spec;
1713  cpl_vector * wl;
1714  cpl_image * colfluximg;
1715  double * pspec;
1716  double * psky;
1717  double * pwl;
1718  cpl_table * out;
1719  cpl_bivector * toplot;
1720  int i;
1721 
1722  /* Test entries */
1723  if (combined == NULL) return NULL;
1724 
1725  /* Initialise */
1726  nx = cpl_image_get_size_x(combined);
1727  ny = cpl_image_get_size_y(combined);
1728  lo_dist = isaac_spc_jitter_config.extr_sky_lo_dist;
1729  hi_dist = isaac_spc_jitter_config.extr_sky_hi_dist;
1730  lo_width = isaac_spc_jitter_config.extr_sky_lo_width;
1731  hi_width = isaac_spc_jitter_config.extr_sky_hi_width;
1732  spec_pos = isaac_spc_jitter_config.extr_spec_pos;
1733 
1734  /* Detect the spectrum position if not passed */
1735  if (spec_pos < 0) {
1736  const int nthrow = cpl_vector_get_size(isaac_spc_jitter_config.throws);
1737 
1738  if (nthrow < 1) {
1739  cpl_msg_error(cpl_func,
1740  "Need a throw value to detect the spectra !!");
1741  cpl_ensure(0, CPL_ERROR_DATA_NOT_FOUND, NULL);
1742  }
1743 
1744  for (i=0; i < nthrow; i++){
1745  const int throw = (int)cpl_vector_get(isaac_spc_jitter_config.throws,
1746  i);
1747  if (irplib_spectrum_find_brightest(combined, throw, TWO_SHADOWS,
1748  0.0, 0, &pos) == 0) break;
1749  if (irplib_spectrum_find_brightest(combined, throw, ONE_SHADOW,
1750  0.0, 0, &pos) == 0) break;
1751  }
1752  if (i == nthrow) {
1753  cpl_msg_error(cpl_func, "Could not detect the spectrum using %d "
1754  "throw(s):", i);
1755 #if 0
1756  cpl_vector_dump(isaac_spc_jitter_config.throws, stderr);
1757  cpl_plot_image("", "", "", combined);
1758 #endif
1759  cpl_ensure(0, CPL_ERROR_DATA_NOT_FOUND, NULL);
1760  }
1761  spec_pos = (int)pos;
1762  cpl_msg_info(cpl_func, "Spectrum detected at y = %d", spec_pos);
1763  }
1764 
1765  /* Set the parameters for the extraction */
1766 
1767  /* Spectrum position */
1768  low_side = spec_pos - (int)(isaac_spc_jitter_config.extr_spec_width/2);
1769  up_side = low_side + isaac_spc_jitter_config.extr_spec_width;
1770  if (up_side < low_side) {
1771  (void)cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1772  "Spectrum zone invalid: low=%d > up=%d",
1773  low_side, up_side);
1774  return NULL;
1775  }
1776  if (low_side < 1) {
1777  (void)cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1778  "Spectrum zone lower limit too low: "
1779  "%d < 1", low_side);
1780  return NULL;
1781  }
1782  if (up_side > ny) {
1783  (void)cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1784  "Spectrum zone upper limit too high: "
1785  "%d > ny=%d", up_side, ny);
1786  return NULL;
1787  }
1788 
1789  /* Form a 1D single-row-image comprising the column fluxes of the zone */
1790  colfluximg = cpl_image_collapse_window_create(combined, 1, low_side,
1791  nx, up_side, 0);
1792 
1793  if (cpl_image_count_rejected(colfluximg) == up_side - low_side + 1) {
1794  (void)cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
1795  "Combined image has undefined flux "
1796  "from column %d to %d", low_side, up_side);
1797  cpl_image_delete(colfluximg);
1798  return NULL;
1799  }
1800 
1801  /* Residual Sky position */
1802  if (lo_dist < 0) lo_dist = 2*isaac_spc_jitter_config.extr_spec_width;
1803  if (hi_dist < 0) hi_dist = 2*isaac_spc_jitter_config.extr_spec_width;
1804  sky_pos[1] = spec_pos - lo_dist;
1805  sky_pos[0] = sky_pos[1] - lo_width;
1806  sky_pos[2] = spec_pos + hi_dist;
1807  sky_pos[3] = sky_pos[2] + hi_width;
1808 
1809  /* Get the sky */
1810  sky = cpl_vector_new(nx);
1811  psky = cpl_vector_get_data(sky);
1812  if (((sky_pos[0] < 1) || (lo_width == 0)) &&
1813  ((sky_pos[3] <= ny) && (hi_width > 0))) {
1814  for (i=0; i<nx; i++) {
1815  psky[i] = cpl_image_get_median_window(combined, i+1,
1816  sky_pos[2], i+1, sky_pos[3]);
1817  }
1818  } else if (((sky_pos[3] > ny) || (hi_width == 0))
1819  && ((sky_pos[0] > 0) && (lo_width > 0))) {
1820  for (i=0; i<nx; i++) {
1821  psky[i] = cpl_image_get_median_window(combined, i+1,
1822  sky_pos[0], i+1, sky_pos[1]);
1823  }
1824  } else if ((lo_width != 0) && (hi_width != 0)
1825  && (sky_pos[0] > 0) && (sky_pos[3] <= ny)) {
1826  for (i=0; i<nx; i++) {
1827  psky[i] = cpl_image_get_median_window(combined, i+1,
1828  sky_pos[2], i+1, sky_pos[3]);
1829  psky[i] += cpl_image_get_median_window(combined, i+1,
1830  sky_pos[0], i+1, sky_pos[1]);
1831  psky[i] /= 2.0;
1832  }
1833  } else {
1834  for (i=0; i<nx; i++) psky[i] = 0.0;
1835  }
1836 
1837  /* Estimate the spectrum */
1838  spec = cpl_vector_new(nx);
1839  pspec = cpl_vector_get_data(spec);
1840  for (i=0; i<nx; i++) {
1841  int is_bad = 0;
1842  const double colflux = cpl_image_get(colfluximg, i+1, 1, &is_bad);
1843 
1844  if (is_bad) {
1845  pspec[i] = 0.0;
1846  cpl_msg_warning(cpl_func, "Combined image has undefined flux in "
1847  "column %d", i+1);
1848  } else {
1849  pspec[i] = colflux
1850  - psky[i] * isaac_spc_jitter_config.extr_spec_width;
1851  }
1852  }
1853  cpl_image_delete(colfluximg);
1854 
1855  /* Get the wavelength */
1856  wl = cpl_vector_new(nx);
1857  pwl = cpl_vector_get_data(wl);
1858  for (i=0; i<nx; i++) {
1859  pwl[i] = isaac_spc_jitter_config.wavecal_a0 +
1860  isaac_spc_jitter_config.wavecal_a1 * (i+1) +
1861  isaac_spc_jitter_config.wavecal_a2 * (i+1) * (i+1) +
1862  isaac_spc_jitter_config.wavecal_a3 * (i+1) * (i+1) * (i+1);
1863  }
1864 
1865  /* Plot the spectrum if requested */
1866  if (isaac_spc_jitter_config.plot > 0) {
1867  toplot = cpl_bivector_wrap_vectors(wl, spec);
1868  cpl_plot_bivector(NULL, "t 'Spectrum' w lines", NULL, toplot);
1869  cpl_bivector_unwrap_vectors(toplot);
1870  toplot = cpl_bivector_wrap_vectors(wl, sky);
1871  cpl_plot_bivector(NULL, "t 'Sky' w lines", NULL, toplot);
1872  cpl_bivector_unwrap_vectors(toplot);
1873  }
1874 
1875  /* Create and fill the output table */
1876  out = cpl_table_new(nx);
1877  cpl_table_new_column(out, "X_coordinate", CPL_TYPE_DOUBLE);
1878  cpl_table_new_column(out, "Extracted_spectrum_value", CPL_TYPE_DOUBLE);
1879  cpl_table_new_column(out, "Sky_spectrum", CPL_TYPE_DOUBLE);
1880  for (i=0; i<nx; i++) {
1881  cpl_table_set_double(out, "X_coordinate", i, pwl[i]);
1882  cpl_table_set_double(out, "Extracted_spectrum_value", i, pspec[i]);
1883  cpl_table_set_double(out, "Sky_spectrum", i, psky[i]);
1884  }
1885  cpl_vector_delete(wl);
1886  cpl_vector_delete(spec);
1887  cpl_vector_delete(sky);
1888  return out;
1889 }
1890 
1891 /*----------------------------------------------------------------------------*/
1902 /*----------------------------------------------------------------------------*/
1903 static int isaac_spc_jitter_std(const char * seds_file,
1904  const char * stdstars,
1905  const cpl_frame * frame,
1906  cpl_table * tab,
1907  double mag)
1908 {
1909  double surface = CPL_MATH_PI * 400 * 400;
1910  cpl_propertylist * plist;
1911  double dit, ra, dec, magnitude, cent_wl;
1912  isaac_band band;
1913  const int nelem = cpl_table_get_nrow(tab);
1914  cpl_vector * wl;
1915  cpl_vector * spec;
1916  cpl_bivector * spec_biv;
1917  cpl_bivector * sed;
1918  cpl_vector * efficiency;
1919  cpl_vector * mag_zero;
1920  cpl_vector * conversion;
1921  int i;
1922 
1923  /* Test entries */
1924  if (seds_file == NULL) return CPL_ERROR_UNSPECIFIED;
1925  if (stdstars == NULL) return CPL_ERROR_UNSPECIFIED;
1926  if (frame == NULL) return CPL_ERROR_UNSPECIFIED;
1927  if (tab == NULL) return CPL_ERROR_UNSPECIFIED;
1928  if (cpl_error_get_code()) return CPL_ERROR_UNSPECIFIED;
1929 
1930  /* Get the propertylist */
1931  if ((plist=cpl_propertylist_load(cpl_frame_get_filename(frame),0))==NULL) {
1932  cpl_msg_error(cpl_func, "Cannot load the property list");
1933  return CPL_ERROR_UNSPECIFIED;
1934  }
1935 
1936  /* Get DIT */
1937  dit = isaac_pfits_get_dit(plist);
1938  if (cpl_error_get_code()) {
1939  cpl_msg_error(cpl_func, "Cannot get DIT");
1940  cpl_propertylist_delete(plist);
1941  return CPL_ERROR_UNSPECIFIED;
1942  }
1943  cpl_msg_info(cpl_func, "DIT: %g", dit);
1944 
1945  /* Get band */
1946  if ((band = isaac_get_associated_filter(isaac_spc_jitter_config.filter))
1947  == ISAAC_BAND_UNKNOWN) {
1948  cpl_msg_error(cpl_func, "Cannot associate the filter to a BB one");
1949  cpl_propertylist_delete(plist);
1950  return CPL_ERROR_UNSPECIFIED;
1951  }
1952  cpl_msg_info(cpl_func, "Band : %s", isaac_std_band_name(band));
1953 
1954  /* Get RA / DEC */
1955  ra = isaac_pfits_get_ra(plist);
1956  dec = isaac_pfits_get_dec(plist);
1957  if (cpl_error_get_code()) {
1958  cpl_msg_error(cpl_func, "Cannot get star position");
1959  cpl_propertylist_delete(plist);
1960  return CPL_ERROR_UNSPECIFIED;
1961  }
1962  cpl_msg_info(cpl_func, "RA: %g DEC: %g", ra, dec);
1963 
1964  /* Get magnitude */
1965  if (mag < 0) {
1966  /* Reference star */
1967  if (irplib_stdstar_find_star(stdstars, ra, dec,
1968  isaac_std_band_name(isaac_get_bbfilter(isaac_spc_jitter_config.filter)),
1969  "all", &magnitude,
1970  &isaac_spc_jitter_config.starname,
1971  &isaac_spc_jitter_config.sptype,
1972  NULL, NULL, NULL, 2.0)) {
1973  cpl_msg_error(cpl_func, "Cannot find the star in catalogs");
1974  cpl_propertylist_delete(plist);
1975  return CPL_ERROR_UNSPECIFIED;
1976  }
1977  } else {
1978  magnitude = mag;
1979  }
1980  cpl_propertylist_delete(plist);
1981  (void)strncpy(isaac_spc_jitter_config.filter_ref,
1983  (isaac_spc_jitter_config.filter)),
1984  KEYSIZE-1);
1985  cpl_msg_info(cpl_func, "Magnitude: %g", magnitude);
1986  isaac_spc_jitter_config.std_magnitude = magnitude;
1987 
1988  /* Get band center */
1989  switch (band) {
1990  case ISAAC_BAND_Z: cent_wl = CENT_WL_BAND_Z; break;
1991  case ISAAC_BAND_SZ: cent_wl = CENT_WL_BAND_SZ; break;
1992  case ISAAC_BAND_J: cent_wl = CENT_WL_BAND_J; break;
1993  case ISAAC_BAND_JBLOCK: cent_wl = CENT_WL_BAND_J; break;
1994  case ISAAC_BAND_SH: cent_wl = CENT_WL_BAND_H; break;
1995  case ISAAC_BAND_SK: cent_wl = CENT_WL_BAND_K; break;
1996  case ISAAC_BAND_SL: cent_wl = CENT_WL_BAND_SL; break;
1997  case ISAAC_BAND_M: cent_wl = CENT_WL_BAND_M; break;
1998  default:
1999  cpl_msg_error(cpl_func, "Unsupported band: %s",
2000  isaac_std_band_name(band));
2001  return CPL_ERROR_UNSPECIFIED;
2002  }
2003  cpl_msg_info(cpl_func, "Center of band : %g microns", cent_wl);
2004 
2005  /* Get the SED */
2006  sed = irplib_stdstar_get_sed(seds_file, isaac_spc_jitter_config.sptype);
2007  if (sed == NULL) {
2008  cpl_msg_error(cpl_func, "Cannot get the SED");
2009  return CPL_ERROR_UNSPECIFIED;
2010  }
2011 
2012  /* Put the spectrum in a bivector */
2013  wl = cpl_vector_wrap(nelem, cpl_table_get_data_double(tab,
2014  "X_coordinate"));
2015  spec = cpl_vector_wrap(nelem, cpl_table_get_data_double(tab,
2016  "Extracted_spectrum_value"));
2017  spec_biv = cpl_bivector_wrap_vectors(wl, spec);
2018 
2019  /* Get the conversion */
2020  if ((conversion = irplib_stdstar_get_conversion(spec_biv, dit, surface,
2021  4.5, magnitude)) == NULL) {
2022  cpl_msg_error(cpl_func, "Cannot get the conversion");
2023  cpl_bivector_unwrap_vectors(spec_biv);
2024  cpl_vector_unwrap(spec);
2025  cpl_vector_unwrap(wl);
2026  cpl_bivector_delete(sed);
2027  return CPL_ERROR_UNSPECIFIED;
2028  }
2029 
2030  /* Get the 0 magnitude spectrum */
2031  if ((mag_zero = irplib_stdstar_get_mag_zero(sed,
2032  cpl_bivector_get_x(spec_biv), cent_wl)) == NULL) {
2033  cpl_msg_error(cpl_func, "Cannot get the 0 magnitude spectrum");
2034  cpl_bivector_unwrap_vectors(spec_biv);
2035  cpl_vector_unwrap(spec);
2036  cpl_vector_unwrap(wl);
2037  cpl_bivector_delete(sed);
2038  cpl_vector_delete(conversion);
2039  return CPL_ERROR_UNSPECIFIED;
2040  }
2041  cpl_bivector_unwrap_vectors(spec_biv);
2042  cpl_vector_unwrap(spec);
2043  cpl_vector_unwrap(wl);
2044  cpl_bivector_delete(sed);
2045 
2046  /* Set the mag_zero 0 values to 1 */
2047  for (i=0; i<cpl_vector_get_size(mag_zero); i++) {
2048  if (cpl_vector_get(mag_zero, i) < 1e-19) {
2049  cpl_vector_set(mag_zero, i, 1.0);
2050  }
2051  }
2052 
2053  /* Get the efficiency */
2054  efficiency = cpl_vector_duplicate(conversion);
2055  if (cpl_vector_divide(efficiency, mag_zero) != CPL_ERROR_NONE) {
2056  cpl_msg_error(cpl_func, "Missing SED info in this wavelength range");
2057  cpl_vector_delete(conversion);
2058  cpl_vector_delete(mag_zero);
2059  cpl_vector_delete(efficiency);
2060  return CPL_ERROR_UNSPECIFIED;
2061  }
2062 
2063  /* Set the efficiency to 0 where the mag_zero is 1 */
2064  for (i=0; i<cpl_vector_get_size(mag_zero); i++) {
2065  if (cpl_vector_get(mag_zero, i) == 1.0) {
2066  cpl_vector_set(efficiency, i, 0.0);
2067  }
2068  }
2069  cpl_vector_delete(mag_zero);
2070 
2071  /* Create extra columns for the results */
2072  cpl_table_wrap_double(tab, cpl_vector_get_data(efficiency), "Efficiency");
2073  cpl_table_wrap_double(tab, cpl_vector_get_data(conversion), "Conversion");
2074  cpl_vector_unwrap(efficiency);
2075  cpl_vector_unwrap(conversion);
2076 
2077  /* Plot the spectra if requested */
2078  if (isaac_spc_jitter_config.plot > 0) {
2079  wl = cpl_vector_wrap(nelem, cpl_table_get_data_double(tab,
2080  "X_coordinate"));
2081  spec = cpl_vector_wrap(nelem, cpl_table_get_data_double(tab,
2082  "Conversion"));
2083  spec_biv = cpl_bivector_wrap_vectors(wl, spec);
2084  cpl_plot_bivector(NULL, "t 'Conversion' w lines",
2085  NULL, spec_biv);
2086  cpl_bivector_unwrap_vectors(spec_biv);
2087  cpl_vector_unwrap(spec);
2088  spec = cpl_vector_wrap(nelem, cpl_table_get_data_double(tab,
2089  "Efficiency"));
2090  spec_biv = cpl_bivector_wrap_vectors(wl, spec);
2091  cpl_plot_bivector(NULL, "t 'Efficiency' w lines", NULL, spec_biv);
2092  cpl_bivector_unwrap_vectors(spec_biv);
2093  cpl_vector_unwrap(spec);
2094  cpl_vector_unwrap(wl);
2095  }
2096 
2097  return 0;
2098 }
2099 
2100 /*----------------------------------------------------------------------------*/
2111 /*----------------------------------------------------------------------------*/
2112 static
2113 cpl_error_code isaac_spc_jitter_save(cpl_frameset * set,
2114  const cpl_image * ima,
2115  const cpl_image * contrib,
2116  const cpl_table * tab,
2117  const cpl_parameterlist * parlist)
2118 {
2119  /* Get the QC params in qclist */
2120  cpl_propertylist * qclist = cpl_propertylist_new();
2121  cpl_propertylist * paflist = NULL;
2122  const cpl_frame * ref_frame;
2123  char * qc_str = NULL;
2124  const char * procat;
2125  const int nintens
2126  = cpl_vector_get_size(isaac_spc_jitter_config.intensities);
2127  int i;
2128  cpl_propertylist * xtlist = cpl_propertylist_new();
2129 
2130  bug_if(cpl_propertylist_append_string(xtlist, "EXTNAME",
2131  "Contribution Map"));
2132  bug_if(contrib == NULL);
2133 
2134  /* Add QC parameters */
2135  /* FIXME: Fill this list where the numbers are available */
2136  if (isaac_spc_jitter_config.filter[0] != (char)0)
2137  bug_if(cpl_propertylist_append_string(qclist, "ESO QC FILTER OBS",
2138  isaac_spc_jitter_config.filter));
2139 
2140  bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISPCO1",
2141  isaac_spc_jitter_config.wavecal_a0));
2142  cpl_propertylist_append_double(qclist, "ESO QC DISPCO2",
2143  isaac_spc_jitter_config.wavecal_a1);
2144  cpl_propertylist_append_double(qclist, "ESO QC DISPCO3",
2145  isaac_spc_jitter_config.wavecal_a2);
2146  cpl_propertylist_append_double(qclist, "ESO QC DISPCO4",
2147  isaac_spc_jitter_config.wavecal_a3);
2148  cpl_propertylist_append_double(qclist, "ESO QC WLEN",
2149  isaac_spc_jitter_config.wavecal_a0 +
2150  isaac_spc_jitter_config.wavecal_a1 * 512 +
2151  isaac_spc_jitter_config.wavecal_a2 * 512 *
2152  512 +
2153  isaac_spc_jitter_config.wavecal_a3 * 512 *
2154  512 * 512);
2155  bug_if(cpl_propertylist_append_double(qclist, "ESO QC DISP XCORR",
2156  isaac_spc_jitter_config.wavecal_cc));
2157  if (isaac_spc_jitter_config.wavecal_out == 0) {
2158  cpl_propertylist_append_string(qclist, "ESO QC WLMETHOD",
2159  "physical model");
2160  } else if (isaac_spc_jitter_config.wavecal_out == 1) {
2161  cpl_propertylist_append_string(qclist, "ESO QC WLMETHOD",
2162  "sky lines");
2163  } else if (isaac_spc_jitter_config.wavecal_out == 2) {
2164  cpl_propertylist_append_string(qclist, "ESO QC WLMETHOD",
2165  "arc file");
2166  }
2167  for (i = 0; i < nintens; i++) {
2168  const double intens
2169  = cpl_vector_get(isaac_spc_jitter_config.intensities, i);
2170 
2171  qc_str = cpl_sprintf("ESO QC SPEC INTENS%d", i+1);
2172  cpl_propertylist_append_double(qclist, qc_str, intens);
2173  cpl_free(qc_str);
2174  qc_str = NULL;
2175  }
2176 
2177  /* In std mode */
2178  if (isaac_spc_jitter_config.std_mode == 1) {
2179  bug_if(cpl_propertylist_append_string(qclist, "ESO QC STDNAME",
2180  isaac_spc_jitter_config.starname ?
2181  isaac_spc_jitter_config.starname :
2182  ""));
2183  bug_if(cpl_propertylist_append_string(qclist, "ESO QC SPECTYPE",
2184  isaac_spc_jitter_config.sptype ?
2185  isaac_spc_jitter_config.sptype :
2186  ""));
2187  cpl_propertylist_append_double(qclist, "ESO QC STARMAG",
2188  isaac_spc_jitter_config.std_magnitude);
2189  if (isaac_spc_jitter_config.filter_ref[0] != (char)0)
2190  cpl_propertylist_append_string(qclist, "ESO QC FILTER REF",
2191  isaac_spc_jitter_config.filter_ref);
2192  }
2193 
2194  /* Change WCS keywords to the computed wavelength solution */
2195  cpl_propertylist_update_double(qclist, "CRVAL1",
2196  isaac_spc_jitter_config.wavecal_a0);
2197  cpl_propertylist_update_double(qclist, "CRVAL2", 1.0);
2198  cpl_propertylist_update_double(qclist, "CRPIX1", 1.0);
2199  cpl_propertylist_update_double(qclist, "CRPIX2", 1.0);
2200  cpl_propertylist_update_double(qclist, "CDELT1",
2201  isaac_spc_jitter_config.wavecal_a1);
2202  cpl_propertylist_update_double(qclist, "CDELT2", 1.0);
2203  cpl_propertylist_update_string(qclist, "CTYPE1", "LINEAR");
2204  cpl_propertylist_update_string(qclist, "CTYPE2", "LINEAR");
2205  if (cpl_propertylist_has(qclist, "CD1_1")) {
2206  cpl_propertylist_update_double(qclist, "CD1_1",
2207  isaac_spc_jitter_config.wavecal_a1);
2208  } else {
2209  cpl_propertylist_insert_after_double(qclist, "CTYPE2", "CD1_1",
2210  isaac_spc_jitter_config.wavecal_a1);
2211  }
2212  if (cpl_propertylist_has(qclist, "CD2_2")) {
2213  cpl_propertylist_update_double(qclist, "CD2_2", 1.0);
2214  } else {
2215  cpl_propertylist_insert_after_double(qclist, "CD1_1", "CD2_2", 1.0);
2216  }
2217 
2218  /* Write the image */
2219  procat = isaac_spc_jitter_config.std_mode == 1 ?
2220  ISAAC_SPC_JITTER_COMB_STD : ISAAC_SPC_JITTER_COMB;
2221 
2222  irplib_dfs_save_image(set, parlist, set, ima, CPL_BPP_IEEE_FLOAT,
2223  RECIPE_STRING, procat, qclist, NULL,
2224  PACKAGE "/" PACKAGE_VERSION,
2225  RECIPE_STRING "_combined" CPL_DFS_FITS);
2226 
2227  /* Append the contribution map */
2228  skip_if (cpl_image_save(contrib, RECIPE_STRING "_combined" CPL_DFS_FITS,
2229  CPL_BPP_16_UNSIGNED, xtlist, CPL_IO_EXTEND));
2230 
2231  /* Write the table */
2232  if (tab != NULL) {
2233  const char * procatt = isaac_spc_jitter_config.std_mode == 1 ?
2234  ISAAC_SPC_JITTER_EXTR_STD : ISAAC_SPC_JITTER_EXTR;
2235 
2236  irplib_dfs_save_table(set, parlist, set, tab, NULL, RECIPE_STRING,
2237  procatt, qclist, NULL,
2238  PACKAGE "/" PACKAGE_VERSION,
2239  RECIPE_STRING "_extracted" CPL_DFS_FITS);
2240  }
2241 
2242  /* Get the reference frame */
2243  ref_frame = irplib_frameset_get_first_from_group(set, CPL_FRAME_GROUP_RAW);
2244 
2245  /* Get the keywords for the paf file from the reference file */
2246  paflist = cpl_propertylist_load_regexp(cpl_frame_get_filename(ref_frame),
2247  0, "^(ARCFILE|MJD-OBS|INSTRUME"
2248  "|ESO TPL ID|ESO TPL NEXP"
2249  "|ESO DPR CATG|ESO DPR TECH"
2250  "|ESO DPR TYPE|DATE-OBS"
2251  "|ESO INS GRAT NAME"
2252  "|ESO INS GRAT WLEN"
2253  "|ESO INS OPTI1 ID|ESO OBS ID"
2254  "|ESO OBS TARG NAME)$", 0);
2255  skip_if (paflist == NULL);
2256 
2257  /* Copy the QC in paflist */
2258  bug_if(cpl_propertylist_append(paflist, qclist));
2259  cpl_propertylist_empty(qclist);
2260 
2261  /* PRO.CATG */
2262  cpl_propertylist_update_string(paflist, CPL_DFS_PRO_CATG, procat);
2263 
2264  /* Save the PAF file */
2265  skip_if(cpl_dfs_save_paf("ISAAC", RECIPE_STRING, paflist,
2266  RECIPE_STRING CPL_DFS_PAF));
2267 
2268  end_skip;
2269 
2270  cpl_propertylist_delete(xtlist);
2271  cpl_propertylist_delete(paflist);
2272  cpl_propertylist_delete(qclist);
2273  cpl_free(qc_str);
2274 
2275  return CPL_ERROR_NONE;
2276 }
2277 
2278 /*----------------------------------------------------------------------------*/
2287 /*----------------------------------------------------------------------------*/
2288 static int off_comp(double off1, double off2, double thresh)
2289 {
2290  return (off1 > thresh && off2 < thresh) || (off1 < thresh && off2 > thresh)
2291  ? 1 : 0;
2292 }
2293 
int isaac_dfs_set_groups(cpl_frameset *set)
Set the group as RAW or CALIB in a frameset.
Definition: isaac_dfs.c:60
double isaac_pfits_get_win_startx(const cpl_propertylist *plist)
find out the WIN STARTX keyword
Definition: isaac_pfits.c:552
const char * isaac_pfits_get_frame_type(const cpl_propertylist *plist)
find out the frame type
Definition: isaac_pfits.c:286
double isaac_pfits_get_ra(const cpl_propertylist *plist)
find out the RA
Definition: isaac_pfits.c:760
double isaac_pfits_get_dit(const cpl_propertylist *plist)
find out the DIT value
Definition: isaac_pfits.c:305
double isaac_pfits_get_dec(const cpl_propertylist *plist)
find out the DEC
Definition: isaac_pfits.c:271
const char * isaac_pfits_get_arm(const cpl_propertylist *plist)
find out the arm which is active
Definition: isaac_pfits.c:106
const char * isaac_pfits_get_filter(const cpl_propertylist *plist)
find out the filter
Definition: isaac_pfits.c:880
double * isaac_get_disprel_estimate(const char *filename, cpl_size poly_deg)
Estimate the instrument wavelength range.
const char * isaac_extract_filename(const cpl_frameset *self, const char *tag)
Extract the filename of the first frame of the given tag.
Definition: isaac_utils.c:397
const char * isaac_std_band_name(isaac_band band)
Return a band name.
Definition: isaac_utils.c:243
cpl_image * isaac_oddeven_correct(const cpl_image *in)
Correct the odd/even in an image.
Definition: isaac_utils.c:275
isaac_band isaac_get_bbfilter(const char *f)
Get the broad band filter.
Definition: isaac_utils.c:144
cpl_frameset * isaac_extract_frameset(const cpl_frameset *self, const char *tag)
Extract the frames with the given tag from a frameset.
Definition: isaac_utils.c:356
isaac_band isaac_get_associated_filter(const char *f)
Get the broad band filter.
Definition: isaac_utils.c:195