IIINSTRUMENT Pipeline Reference Manual  1.5.13
sofi_spc_jitter.c
1 /* $Id: sofi_spc_jitter.c,v 1.45 2013-03-12 08:04:50 llundin Exp $
2  *
3  * This file is part of the SOFI 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:04:50 $
24  * $Revision: 1.45 $
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 <string.h>
37 #include <math.h>
38 #include <cpl.h>
39 
40 #include "irplib_plugin.h"
41 #include "irplib_utils.h"
42 #include "irplib_spectrum.h"
43 
44 #include "sofi_utils.h"
45 #include "sofi_wavelength.h"
46 #include "sofi_pfits.h"
47 #include "sofi_dfs.h"
48 
49 /*-----------------------------------------------------------------------------
50  Defines
51  -----------------------------------------------------------------------------*/
52 
53 #define RECIPE_STRING "sofi_spc_jitter"
54 
55 #define SOFI_SPC_JITTER_OFFSET_ERR 10
56 
57 /*-----------------------------------------------------------------------------
58  Functions prototypes
59  -----------------------------------------------------------------------------*/
60 
61 static cpl_image ** sofi_spc_jitter_combine(cpl_frameset *, const char *,
62  const char *, const char *);
63 static cpl_vector * sofi_spc_jitter_get_offsets(cpl_frameset *);
64 static int * sofi_spc_jitter_classif(cpl_vector *, int *);
65 static int off_comp(double, double, double);
66 static cpl_imagelist * sofi_spc_jitter_saa_groups(cpl_imagelist *,
67  cpl_vector *, int *, int, cpl_vector **);
68 static int sofi_spc_jitter_wavecal(const char *, const char *, cpl_image *,
69  cpl_frameset *);
70 static cpl_imagelist * sofi_spc_jitter_nodded(cpl_imagelist *, cpl_vector *,
71  cpl_vector **);
72 static cpl_imagelist * sofi_spc_jitter_distor(cpl_imagelist *, const char *);
73 static double sofi_spc_jitter_refine_offset(cpl_image *, cpl_image *);
74 static cpl_table * sofi_spc_jitter_extract(cpl_image *);
75 static cpl_error_code sofi_spc_jitter_save(cpl_frameset *, const cpl_image *,
76  const cpl_table *,
77  const cpl_parameterlist *);
78 
79 cpl_recipe_define(sofi_spc_jitter, SOFI_BINARY_VERSION,
80  "Lars Lundin", PACKAGE_BUGREPORT, "2002,2003,2009",
81  "SOFI Spectro jitter recipe",
82  RECIPE_STRING " -- SOFI Spectro jitter recipe"
83  "The files listed in the Set Of Frames (sof-file) "
84  "must be tagged:\n"
85  "raw-file.fits "SOFI_SPC_JITTER_NODOBJ_RAW" or\n"
86  "raw-file.fits "SOFI_SPC_JITTER_NODSKY_RAW"\n"
87  "Calibration files:\n"
88  "oh-cat.fits "SOFI_CALPRO_OH_CAT" or\n"
89  "flat-file.fits "SOFI_CALIB_SPFLAT" or\n"
90  "arc-file.fits "SOFI_CALIB_ARC"\n");
91 
92 /*-----------------------------------------------------------------------------
93  Static variables
94  -----------------------------------------------------------------------------*/
95 
96 static struct {
97  /* Inputs */
98  int display;
99  int crosstalk;
100  /* wavecal_in : 0 for physical model, 1 for sky, 2 for arc table */
101  int wavecal_in;
102  int wavecal_degree;
103  double wavecal_err;
104  int wavecal_nsamples;
105  int wavecal_ppm;
106  int saa_refine;
107  double saa_rej_high;
108  double saa_rej_low;
109  int extr_spec_pos;
110  int extr_spec_width;
111  int extr_sky_le_width;
112  int extr_sky_ri_width;
113  int extr_sky_le_dist;
114  int extr_sky_ri_dist;
115  /* Outputs */
116  /* wavecal_out : 0 for physical model, 1 for sky, 2 for arc table */
117  cpl_vector * throws;
118  cpl_vector * intensities;
119  char filter[512];
120  int wavecal_out;
121  double wavecal_cc;
122  double wavecal_a0;
123  double wavecal_a1;
124  double wavecal_a2;
125  double wavecal_a3;
126  double wavecal_a4;
127 } sofi_spc_jitter_config;
128 
129 
130 /*-----------------------------------------------------------------------------
131  Functions code
132  -----------------------------------------------------------------------------*/
133 
134 /*----------------------------------------------------------------------------*/
142 /*----------------------------------------------------------------------------*/
143 static
144 cpl_error_code sofi_spc_jitter_fill_parameterlist(cpl_parameterlist * self)
145 {
146  const char * context = PACKAGE "." RECIPE_STRING;
147  cpl_error_code err;
148 
149  cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
150 
151  /* Fill the parameters list */
152 
153 
154  /* --crosstalk */
155  err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
156  "crosstalk", CPL_TRUE, NULL, context,
157  "Enable removal of the crosstalk "
158  "effect");
159  cpl_ensure_code(!err, err);
160 
161  /* --wavecal */
162  err = irplib_parameterlist_set_string(self, PACKAGE, RECIPE_STRING,
163  "wavecal", "sky", NULL, context,
164  "Wavelength method: "
165  "phy or sky or arc");
166  cpl_ensure_code(!err, err);
167 
168  /* --wavecal_ppm */
169  err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
170  "wavecal_ppm", CPL_FALSE, NULL, context,
171  "Enable Point Pattern Matching in the "
172  "wavelength calibration");
173  cpl_ensure_code(!err, err);
174 
175  /* --wavecal_degree */
176  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
177  "wavecal_degree", 2, NULL, context,
178  "Degree of the wavelength dispersion "
179  "polynomial");
180  cpl_ensure_code(!err, err);
181 
182  /* --wavecal_nsamples */
183  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
184  "wavecal_nsamples", 100, NULL, context,
185  "Number of samples for the wavelength "
186  "calibration");
187  cpl_ensure_code(!err, err);
188 
189  /* --wavecal_err */
190  err = irplib_parameterlist_set_double(self, PACKAGE, RECIPE_STRING,
191  "wavecal_err", 1000.0, NULL, context,
192  "The wavelength error [Angstrom]");
193  cpl_ensure_code(!err, err);
194 
195  /* --saa_refine */
196  err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
197  "saa_refine", CPL_TRUE, NULL, context,
198  "Enable refinement of the offsets");
199  cpl_ensure_code(!err, err);
200 
201  /* --saa_rej */
202  err = irplib_parameterlist_set_string(self, PACKAGE, RECIPE_STRING,
203  "saa_rej", "0.1,0.1", NULL, context,
204  "Low, high SAA rejection [%,%]");
205  cpl_ensure_code(!err, err);
206 
207  /* --spec_pos */
208  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
209  "spec_pos", -1, NULL, context,
210  "Spectrum position");
211  cpl_ensure_code(!err, err);
212 
213  /* --spec_width */
214  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
215  "spec_width", 10, NULL, context,
216  "Spectrum width");
217  cpl_ensure_code(!err, err);
218 
219  /* --sky_le_width */
220  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
221  "sky_le_width", 10, NULL, context,
222  "Sky width left of the spectrum");
223  cpl_ensure_code(!err, err);
224 
225  /* --sky_ri_width */
226  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
227  "sky_ri_width", 10, NULL, context,
228  "Sky width right of the spectrum");
229  cpl_ensure_code(!err, err);
230 
231  /* --sky_le_dist */
232  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
233  "sky_le_dist", -1, NULL, context,
234  "Sky distance left of the spectrum");
235  cpl_ensure_code(!err, err);
236 
237  /* --sky_ri_dist */
238  err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
239  "sky_ri_dist", -1, NULL, context,
240  "Sky distance right of the spectrum");
241  cpl_ensure_code(!err, err);
242 
243  /* --display */
244  err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING, "display",
245  CPL_FALSE, NULL, context,
246  "Enable plotting");
247  cpl_ensure_code(!err, err);
248 
249  return CPL_ERROR_NONE;
250 }
251 
252 /*----------------------------------------------------------------------------*/
259 /*----------------------------------------------------------------------------*/
260 static int sofi_spc_jitter(cpl_frameset * framelist,
261  const cpl_parameterlist * parlist)
262 {
263  cpl_propertylist * plist;
264  const char * sval;
265  cpl_frameset * rawframes = NULL;
266  const cpl_frame * cur_frame;
267  const char * flat;
268  const char * oh;
269  const char * arc;
270  cpl_image ** combined = NULL;
271  cpl_table * extracted = NULL;
272 
273  /* Initialise */
274  sofi_spc_jitter_config.wavecal_out = -1;
275  sofi_spc_jitter_config.wavecal_cc = -1.0;
276  sofi_spc_jitter_config.throws = NULL;
277  sofi_spc_jitter_config.intensities = NULL;
278  sofi_spc_jitter_config.filter[0] = (char)0;
279 
280  /* Retrieve input parameters */
281 
282  /* Cross-talk */
283  sofi_spc_jitter_config.crosstalk
284  = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
285  "crosstalk");
286 
287  /* --wavecal */
288  sval = irplib_parameterlist_get_string(parlist, PACKAGE, RECIPE_STRING,
289  "wavecal");
290  bug_if(sval == NULL);
291  if (!strcmp(sval, "phy")) sofi_spc_jitter_config.wavecal_in = 0;
292  else if (!strcmp(sval, "sky")) sofi_spc_jitter_config.wavecal_in = 1;
293  else if (!strcmp(sval, "arc")) sofi_spc_jitter_config.wavecal_in = 2;
294  else {
295  error_if(1, CPL_ERROR_UNSUPPORTED_MODE,
296  "Invalid value for wavecal option: %s", sval);
297  }
298 
299  /* --wavecal_degree */
300  sofi_spc_jitter_config.wavecal_degree
301  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
302  "wavecal_degree");
303 
304  /* --wavecal_nsamples */
305  sofi_spc_jitter_config.wavecal_nsamples
306  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
307  "wavecal_nsamples");
308 
309  /* --wavecal_err */
310  sofi_spc_jitter_config.wavecal_err
311  = irplib_parameterlist_get_double(parlist, PACKAGE, RECIPE_STRING,
312  "wavecal_err");
313 
314  /* --wavecal_ppm */
315  sofi_spc_jitter_config.wavecal_ppm
316  = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
317  "wavecal_ppm");
318 
319  /* Refine of offsets */
320  sofi_spc_jitter_config.saa_refine
321  = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
322  "saa_refine");
323 
324  /* Rejection parameters for SAA */
325  sval = irplib_parameterlist_get_string(parlist, PACKAGE, RECIPE_STRING,
326  "saa_rej");
327  skip_if (sscanf(sval, "%lg,%lg",
328  &sofi_spc_jitter_config.saa_rej_low,
329  &sofi_spc_jitter_config.saa_rej_high) != 2);
330 
331  /* --spec_pos */
332  sofi_spc_jitter_config.extr_spec_pos
333  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
334  "spec_pos");
335 
336  /* --spec_width */
337  sofi_spc_jitter_config.extr_spec_width
338  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
339  "spec_width");
340 
341  /* --sky_le_width */
342  sofi_spc_jitter_config.extr_sky_le_width
343  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
344  "sky_le_width");
345  /* --sky_ri_width */
346  sofi_spc_jitter_config.extr_sky_ri_width
347  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
348  "sky_ri_width");
349 
350  /* --sky_le_dist */
351  sofi_spc_jitter_config.extr_sky_le_dist
352  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
353  "sky_le_dist");
354 
355  /* --sky_ri_dist */
356  sofi_spc_jitter_config.extr_sky_ri_dist
357  = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
358  "sky_ri_dist");
359  /* Display */
360  sofi_spc_jitter_config.display
361  = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
362  "display");
363 
364  /* Identify the RAW and CALIB frames in the input frameset */
365  skip_if (sofi_dfs_set_groups(framelist));
366 
367  /* Get the filter */
368  cur_frame = cpl_frameset_get_position(framelist, 0);
369  plist = cpl_propertylist_load(cpl_frame_get_filename(cur_frame), 0);
370  skip_if(plist == NULL);
371 
372  if ((sval = sofi_pfits_get_filter(plist)) != NULL) {
373  strcpy(sofi_spc_jitter_config.filter, sval); /* FIXME: Replace */
374  } else {
375  cpl_error_reset();
376  }
377  cpl_propertylist_delete(plist);
378 
379  /* Retrieve calibration data */
380  arc = sofi_extract_filename(framelist, SOFI_CALIB_ARC);
381  flat = sofi_extract_filename(framelist, SOFI_CALIB_SPFLAT);
382  oh = sofi_extract_filename(framelist, SOFI_CALPRO_OH_CAT);
383 
384  /* Retrieve raw frames */
385  rawframes = sofi_extract_frameset(framelist, SOFI_SPC_JITTER_NODOBJ_RAW);
386  error_if (rawframes == NULL, CPL_ERROR_DATA_NOT_FOUND,
387  "Cannot find the raw frames in the input list");
388 
389  /* If arc calibration requested, arc file must be provided */
390  error_if (arc == NULL && sofi_spc_jitter_config.wavecal_in == 2,
391  CPL_ERROR_ILLEGAL_INPUT,
392  "You must provide an ARC file for the wl cal");
393 
394  /* Create the combined image */
395  cpl_msg_info(cpl_func, "Create the combined image");
396  cpl_msg_indent_more();
397  combined = sofi_spc_jitter_combine(rawframes, oh, flat, arc);
398  cpl_msg_indent_less();
399 
400  error_if (combined==NULL, CPL_ERROR_ILLEGAL_INPUT,
401  "Cannot combine the images");
402 
403  /* Extract the spectrum */
404  cpl_msg_info(cpl_func, "Extract the spectrum");
405  cpl_msg_indent_more();
406  extracted = sofi_spc_jitter_extract(combined[0]);
407  cpl_msg_indent_less();
408 
409  error_if (extracted == NULL, CPL_ERROR_DATA_NOT_FOUND,
410  "Cannot extract the spectrum");
411 
412  /* Write the products */
413  cpl_msg_info(cpl_func, "Save the products");
414  skip_if(sofi_spc_jitter_save(framelist, combined[0], extracted, parlist));
415 
416  end_skip;
417 
418  if (combined != NULL) {
419  cpl_image_delete(combined[0]);
420  cpl_image_delete(combined[1]);
421  cpl_free(combined);
422  }
423 
424  cpl_vector_delete(sofi_spc_jitter_config.intensities);
425  cpl_vector_delete(sofi_spc_jitter_config.throws);
426 
427  cpl_frameset_delete(rawframes);
428 
429  cpl_table_delete(extracted);
430 
431  return cpl_error_get_code();
432 }
433 
434 /*----------------------------------------------------------------------------*/
443 /*----------------------------------------------------------------------------*/
444 static cpl_image ** sofi_spc_jitter_combine(
445  cpl_frameset * rawframes,
446  const char * oh,
447  const char * flat,
448  const char * arc)
449 {
450  cpl_imagelist * ilist;
451  cpl_image * tmp_im;
452  cpl_vector * offsets;
453  int * groups;
454  int ngroups;
455  cpl_imagelist * abba;
456  cpl_vector * abba_off;
457  cpl_imagelist * nodded;
458  cpl_vector * nodded_off_x;
459  cpl_vector * nodded_off_y;
460  double throw;
461  cpl_table * extracted;
462  double intensity;
463  double * pnodded_off_x;
464  cpl_imagelist * nodded_warped;
465  cpl_bivector * nodded_offsets;
466  cpl_image ** combined;
467  int nima;
468  double new_offset;
469  int i;
470 
471  /* Test entries */
472  if (rawframes == NULL) return NULL;
473 
474  /* Load the images */
475  cpl_msg_info(cpl_func, "Load the data");
476  cpl_msg_indent_more();
477  if ((ilist = cpl_imagelist_load_frameset(rawframes, CPL_TYPE_FLOAT, 1,
478  0)) == NULL) {
479  cpl_msg_error(cpl_func, "cannot load the data");
480  cpl_msg_indent_less();
481  return NULL;
482  }
483  cpl_msg_indent_less();
484 
485  /* Apply the cross-talk correction */
486  if (sofi_spc_jitter_config.crosstalk) {
487  cpl_msg_info(cpl_func, "Apply the cross-talk correction");
488  cpl_msg_indent_more();
489  if (sofi_correct_crosstalk_list(ilist) == -1) {
490  cpl_msg_error(cpl_func, "Cannot correct for Cross-talk");
491  cpl_imagelist_delete(ilist);
492  cpl_msg_indent_less();
493  return NULL;
494  }
495  cpl_msg_indent_less();
496  }
497 
498  /* Apply the flafield */
499  if (flat != NULL) {
500  cpl_msg_info(cpl_func, "Apply the flatfield correction");
501  if ((tmp_im = cpl_image_load(flat, CPL_TYPE_FLOAT, 0, 0)) == NULL) {
502  cpl_msg_warning(cpl_func, "cannot load the flat field");
503  } else {
504  if (cpl_imagelist_divide_image(ilist, tmp_im) != CPL_ERROR_NONE) {
505  cpl_msg_warning(cpl_func, "cannot apply the flat field");
506  }
507  cpl_image_delete(tmp_im);
508  }
509  }
510 
511  /* Get the offsets */
512  cpl_msg_info(cpl_func, "Get the offsets");
513  if ((offsets = sofi_spc_jitter_get_offsets(rawframes)) == NULL) {
514  cpl_msg_error(cpl_func, "cannot get the offsets");
515  cpl_imagelist_delete(ilist);
516  return NULL;
517  }
518 
519  /* Classify in groups the a and b images sequence */
520  cpl_msg_info(cpl_func, "Classify in groups");
521  cpl_msg_indent_more();
522  if ((groups = sofi_spc_jitter_classif(offsets, &ngroups)) == NULL) {
523  cpl_msg_error(cpl_func, "cannot classify the data");
524  cpl_imagelist_delete(ilist);
525  cpl_vector_delete(offsets);
526  cpl_msg_indent_less();
527  return NULL;
528  }
529  cpl_msg_indent_less();
530 
531  /* Shift and add each group to one image */
532  cpl_msg_info(cpl_func, "Shift and add each group to one image");
533  cpl_msg_indent_more();
534  if ((abba = sofi_spc_jitter_saa_groups(ilist, offsets, groups,
535  ngroups, &abba_off)) == NULL) {
536  cpl_msg_error(cpl_func, "cannot shift and add groups");
537  cpl_imagelist_delete(ilist);
538  cpl_vector_delete(offsets);
539  cpl_free(groups);
540  cpl_msg_indent_less();
541  return NULL;
542  }
543  cpl_imagelist_delete(ilist);
544  cpl_free(groups);
545  cpl_vector_delete(offsets);
546  cpl_msg_indent_less();
547 
548  /* Compute the wavelength calibration */
549  cpl_msg_info(cpl_func, "Compute the wavelength calibration");
550  cpl_msg_indent_more();
551  if (sofi_spc_jitter_wavecal(arc, oh, cpl_imagelist_get(abba, 0),
552  rawframes) == -1) {
553  cpl_msg_error(cpl_func, "cannot compute the wavelength");
554  cpl_imagelist_delete(abba);
555  cpl_vector_delete(abba_off);
556  cpl_msg_indent_less();
557  return NULL;
558  }
559  cpl_msg_indent_less();
560 
561  /* Create the nodded images */
562  cpl_msg_info(cpl_func, "Create the nodded images");
563  cpl_msg_indent_more();
564  if ((nodded = sofi_spc_jitter_nodded(abba, abba_off,
565  &nodded_off_x))==NULL) {
566  cpl_msg_error(cpl_func, "cannot create the nodded images");
567  cpl_imagelist_delete(abba);
568  cpl_vector_delete(abba_off);
569  cpl_msg_indent_less();
570  return NULL;
571  }
572  cpl_imagelist_delete(abba);
573  cpl_msg_indent_less();
574 
575  /* Get the throw offsets from abba_off */
576  nima = cpl_imagelist_get_size(nodded);
577  sofi_spc_jitter_config.throws = cpl_vector_new(nima);
578  for (i=0; i<nima/2; i++) {
579  throw = fabs( (cpl_vector_get(abba_off, 2*i))-
580  (cpl_vector_get(abba_off, 2*i+1)));
581  cpl_vector_set(sofi_spc_jitter_config.throws, 2*i, throw);
582  cpl_vector_set(sofi_spc_jitter_config.throws, 2*i+1, throw);
583  }
584  cpl_vector_delete(abba_off);
585 
586  /* Get the spectra intensities in the nodded images */
587  cpl_msg_info(cpl_func, "Compute the spectra intensities");
588  cpl_msg_indent_more();
589  nima = cpl_imagelist_get_size(nodded);
590  sofi_spc_jitter_config.intensities = cpl_vector_new(nima);
591  for (i=0; i<nima; i++) {
592  if ((extracted = sofi_spc_jitter_extract(
593  cpl_imagelist_get(nodded, i))) == NULL) {
594  cpl_msg_warning(cpl_func,"Cannot extract the spectrum from nodded %d",
595  i+1);
596  intensity = -1.0;
597  } else {
598  intensity = cpl_table_get_column_mean(extracted,
599  "Extracted_spectrum_value");
600  intensity *= cpl_table_get_nrow(extracted);
601  cpl_table_delete(extracted);
602  }
603  cpl_msg_info(cpl_func, "Spectrum intensity nb %d: %g", i+1,
604  intensity);
605  cpl_vector_set(sofi_spc_jitter_config.intensities, i, intensity);
606  }
607  cpl_msg_indent_less();
608 
609  /* Distortion correction */
610  if (arc) {
611  cpl_msg_info(cpl_func, "Correct the distortion on nodded images");
612  cpl_msg_indent_more();
613  if ((nodded_warped = sofi_spc_jitter_distor(nodded, arc)) == NULL) {
614  cpl_msg_error(cpl_func, "cannot correct the distortion");
615  cpl_imagelist_delete(nodded);
616  cpl_vector_delete(nodded_off_x);
617  cpl_msg_indent_less();
618  return NULL;
619  }
620  cpl_imagelist_delete(nodded);
621  nodded = nodded_warped;
622  cpl_msg_indent_less();
623  }
624 
625  /* Refine the offsets if requested */
626  if (sofi_spc_jitter_config.saa_refine) {
627  cpl_msg_info(cpl_func, "Refine the offsets");
628  pnodded_off_x = cpl_vector_get_data(nodded_off_x);
629  for (i=0; i<cpl_imagelist_get_size(nodded); i++) {
630  new_offset = sofi_spc_jitter_refine_offset(
631  cpl_imagelist_get(nodded, 0),
632  cpl_imagelist_get(nodded, i));
633  if (new_offset > 5000) {
634  cpl_msg_debug(cpl_func, "cannot refine the offset - keep %g",
635  pnodded_off_x[i]);
636  } else {
637  if (fabs(new_offset-pnodded_off_x[i]) <
638  SOFI_SPC_JITTER_OFFSET_ERR) {
639  cpl_msg_debug(cpl_func, "refined offset : %g (old was %g)",
640  new_offset, pnodded_off_x[i]);
641  pnodded_off_x[i] = new_offset;
642  } else {
643  cpl_msg_debug(cpl_func,
644  "refined offset %g too different - keep %g",
645  new_offset, pnodded_off_x[i]);
646  }
647  }
648  }
649  }
650 
651  /* Images combination */
652  /* Get the offsets in a bivector */
653  nodded_off_y = cpl_vector_duplicate(nodded_off_x);
654  cpl_vector_fill(nodded_off_y, 0.0);
655  nodded_offsets = cpl_bivector_wrap_vectors(nodded_off_x, nodded_off_y);
656  /* Shift and add */
657  cpl_msg_info(cpl_func, "Apply the shift and add on the nodded frames");
658  nima = cpl_imagelist_get_size(nodded);
659  if ((combined = cpl_geom_img_offset_saa(nodded, nodded_offsets,
660  CPL_KERNEL_DEFAULT,
661  (int)(sofi_spc_jitter_config.saa_rej_low * nima),
662  (int)(sofi_spc_jitter_config.saa_rej_high * nima),
663  CPL_GEOM_FIRST, NULL, NULL)) == NULL) {
664  cpl_msg_error(cpl_func, "Cannot shift and add group");
665  cpl_imagelist_delete(nodded);
666  cpl_bivector_unwrap_vectors(nodded_offsets);
667  cpl_vector_delete(nodded_off_x);
668  cpl_vector_delete(nodded_off_y);
669  return NULL;
670  }
671  cpl_imagelist_delete(nodded);
672  cpl_bivector_unwrap_vectors(nodded_offsets);
673  cpl_vector_delete(nodded_off_x);
674  cpl_vector_delete(nodded_off_y);
675  return combined;
676 }
677 
678 /*----------------------------------------------------------------------------*/
684 /*----------------------------------------------------------------------------*/
685 static cpl_vector * sofi_spc_jitter_get_offsets(cpl_frameset * rawframes)
686 {
687  cpl_vector * offsets;
688  double * pvect;
689  int nraw;
690  cpl_frame * cur_frame;
691  cpl_propertylist * plist;
692  int i;
693 
694  /* Test entries */
695  if (rawframes == NULL) return NULL;
696 
697  /* Initialise */
698  nraw = cpl_frameset_get_size(rawframes);
699 
700  /* Get the rawframes X offsets */
701  offsets = cpl_vector_new(nraw);
702  pvect = cpl_vector_get_data(offsets);
703  for (i=0; i<nraw; i++) {
704  cur_frame = cpl_frameset_get_position(rawframes, i);
705  if ((plist=cpl_propertylist_load(cpl_frame_get_filename(cur_frame),
706  0)) == NULL) {
707  cpl_msg_error(cpl_func, "cannot get property list");
708  cpl_vector_delete(offsets);
709  return NULL;
710  }
711  pvect[i] = sofi_pfits_get_cumoffsetx(plist);
712  if (cpl_error_get_code()) {
713  cpl_msg_error(cpl_func, "cannot get the offset from the header");
714  cpl_vector_delete(offsets);
715  cpl_propertylist_delete(plist);
716  return NULL;
717  }
718  cpl_propertylist_delete(plist);
719  pvect[i] *= -1.0;
720  }
721  return offsets;
722 }
723 
724 /*----------------------------------------------------------------------------*/
762 /*----------------------------------------------------------------------------*/
763 static int * sofi_spc_jitter_classif(
764  cpl_vector * offsets,
765  int * ngroups)
766 {
767  double * pvect;
768  int nraw;
769  double offset_thresh;
770  cpl_vector * tmp_vec;
771  int * groups;
772  int last_group;
773  int i, j, k, l;
774 
775  /* Test entries */
776  if (offsets == NULL) return NULL;
777 
778  /* Initialise */
779  nraw = cpl_vector_get_size(offsets);
780 
781  /* Separate the offsets in 2 categories */
782  tmp_vec = cpl_vector_duplicate(offsets);
783  cpl_vector_sort(tmp_vec, 1);
784  pvect = cpl_vector_get_data(tmp_vec);
785  if (pvect[0] == pvect[nraw-1]) {
786  cpl_msg_error(cpl_func, "Only one offset in the list - abort");
787  cpl_vector_delete(tmp_vec);
788  return NULL;
789  }
790  offset_thresh = (pvect[0] + pvect[nraw-1]) / 2.0;
791  cpl_vector_delete(tmp_vec);
792 
793  /* Identify the different A and B groups */
794  pvect = cpl_vector_get_data(offsets);
795  *ngroups = 0;
796  groups = cpl_calloc(nraw, sizeof(int));
797 
798  /* Create a look up table to associate the ith obj with the jth frame */
799  i = 0;
800  while (i < nraw) {
801  j = 0;
802  /* Count the number of successive '+' or '-' (j) */
803  while ((i+j<nraw) &&
804  (!off_comp(pvect[i], pvect[i+j], offset_thresh))) j++;
805 
806  if (i+j >= nraw) i = nraw;
807  else {
808  k = 0;
809  /* Check if there are j '-' or '+' (k) */
810  while ((i+j+k < nraw)
811  && (!off_comp(pvect[i+j], pvect[i+j+k], offset_thresh))
812  && (k<j)) k++;
813  last_group = 1;
814  if (i+j+k < nraw) {
815  for (l=i+j+k; l<nraw; l++) {
816  if (off_comp(pvect[i+j], pvect[l], offset_thresh)) {
817  last_group = 0;
818  break;
819  }
820  }
821  }
822  if (last_group == 0) {
823  for (l=0; l<j; l++) groups[i+l] = *ngroups + 1;
824  for (l=0; l<k; l++) groups[i+j+l] = *ngroups + 2;
825  *ngroups += 2;
826  i += j+k;
827  } else {
828  for (l=0; l<j; l++) groups[i+l] = *ngroups + 1;
829  for (l=0; l<nraw - (i+j); l++) groups[i+j+l] =*ngroups + 2;
830  *ngroups += 2;
831  i = nraw;
832  }
833  }
834  }
835 
836  /* Nb of groups found should be even */
837  if (*ngroups % 2) {
838  cpl_msg_error(cpl_func, "Odd number of groups found");
839  cpl_free(groups);
840  return NULL;
841  }
842 
843  return groups;
844 }
845 
846 /*----------------------------------------------------------------------------*/
877 /*----------------------------------------------------------------------------*/
878 static cpl_imagelist * sofi_spc_jitter_saa_groups(
879  cpl_imagelist * ilist,
880  cpl_vector * offsets,
881  int * groups,
882  int ngroups,
883  cpl_vector ** abba_off)
884 {
885  cpl_imagelist * abba;
886  cpl_imagelist * group_list;
887  cpl_image * tmp_ima;
888  cpl_image ** combined;
889  cpl_bivector * group_off;
890  double * pgroup_off;
891  double * poffsets;
892  double * pabba_off;
893  int nima;
894  int saa;
895  int i, j, k;
896 
897  /* Test entries */
898  if ((ilist == NULL) || (offsets == NULL) || (groups == NULL)) return NULL;
899 
900  /* Initialise */
901  nima = cpl_imagelist_get_size(ilist);
902  poffsets = cpl_vector_get_data(offsets);
903 
904  /* Create the output image list */
905  abba = cpl_imagelist_new();
906  *abba_off = cpl_vector_new(ngroups);
907  pabba_off = cpl_vector_get_data(*abba_off);
908 
909  /* Loop on the groups */
910  for (i=0; i<ngroups; i++) {
911  /* Initialise */
912  saa = 0;
913  /* Create the group list of images */
914  group_list = cpl_imagelist_new();
915  k = 0;
916  for (j=0; j<nima; j++) {
917  if (i+1 == groups[j]) {
918  /* Get the first offset of the group in abba_off */
919  if (k==0) pabba_off[i] = poffsets[j];
920  /* To know if we need the saa (shift and add) */
921  if (fabs(pabba_off[i]-poffsets[j]) > 1e-3) saa = 1;
922  /* Copy the images of the group in group_list */
923  tmp_ima = cpl_image_duplicate(cpl_imagelist_get(ilist, j));
924  cpl_imagelist_set(group_list, tmp_ima, k);
925  tmp_ima = NULL;
926  k++;
927  }
928  }
929 
930  if (saa) {
931  /* Get the offsets of the group in group_off */
932  group_off = cpl_bivector_new(k);
933  cpl_vector_fill(cpl_bivector_get_y(group_off), 0.0);
934  pgroup_off = cpl_bivector_get_x_data(group_off);
935  k = 0;
936  for (j=0; j<nima; j++) {
937  if (i+1 == groups[j]) {
938  pgroup_off[k] = poffsets[j];
939  k++;
940  }
941  }
942  cpl_vector_subtract_scalar(cpl_bivector_get_x(group_off),
943  pabba_off[i]);
944  /* Shift and add */
945  cpl_msg_debug(cpl_func, "Apply shift-and-add for group %d", i+1);
946  if ((combined = cpl_geom_img_offset_saa(group_list,
947  group_off, CPL_KERNEL_DEFAULT, 0, 0,
948  CPL_GEOM_FIRST, NULL, NULL)) == NULL) {
949  cpl_msg_error(cpl_func,"Cannot shift and add group nb %d", i+1);
950  cpl_imagelist_delete(group_list);
951  cpl_bivector_delete(group_off);
952  cpl_imagelist_delete(abba);
953  cpl_vector_delete(*abba_off);
954  return NULL;
955  }
956  cpl_bivector_delete(group_off);
957  cpl_image_delete(combined[1]);
958  cpl_imagelist_set(abba, combined[0], i);
959  cpl_free(combined);
960  } else {
961  /* Averaging */
962  cpl_msg_debug(cpl_func, "Apply averaging for group %d", i+1);
963  if ((tmp_ima = cpl_imagelist_collapse_create(group_list)) == NULL) {
964  cpl_msg_error(cpl_func, "Cannot average group nb %d", i+1);
965  cpl_imagelist_delete(group_list);
966  cpl_imagelist_delete(abba);
967  cpl_vector_delete(*abba_off);
968  return NULL;
969  }
970  cpl_imagelist_set(abba, tmp_ima, i);
971  }
972  cpl_imagelist_delete(group_list);
973  }
974  return abba;
975 }
976 
977 /*----------------------------------------------------------------------------*/
986 /*----------------------------------------------------------------------------*/
987 static int sofi_spc_jitter_wavecal(
988  const char * arc,
989  const char * oh,
990  cpl_image * ima,
991  cpl_frameset * raw)
992 {
993  cpl_table * arc_tab;
994  cpl_polynomial * phdisprel;
995  cpl_frame * cur_frame;
996  const char * cur_fname;
997  cpl_polynomial * disprel;
998  double slit_width;
999  cpl_size p[5];
1000  double xc, a, b, c, d, e;
1001  int degree;
1002 
1003  /* Get the wavelength from the arc file */
1004  if (sofi_spc_jitter_config.wavecal_in == 2) {
1005  if (arc == NULL) {
1006  cpl_msg_error(cpl_func, "Missing arc for the wavelength calib");
1007  return CPL_ERROR_UNSPECIFIED;
1008  }
1009  cpl_msg_info(cpl_func, "Get the wavelength from the ARC file");
1010  if ((arc_tab = cpl_table_load(arc, 1, 0)) == NULL) {
1011  cpl_msg_error(cpl_func, "Cannot load the arc table");
1012  sofi_spc_jitter_config.wavecal_out = -1;
1013  return CPL_ERROR_UNSPECIFIED;
1014  }
1015  sofi_spc_jitter_config.wavecal_a0 =
1016  cpl_table_get_double(arc_tab, "WL_coefficients", 0, NULL);
1017  sofi_spc_jitter_config.wavecal_a1 =
1018  cpl_table_get_double(arc_tab, "WL_coefficients", 1, NULL);
1019  sofi_spc_jitter_config.wavecal_a2 =
1020  cpl_table_get_double(arc_tab, "WL_coefficients", 2, NULL);
1021  sofi_spc_jitter_config.wavecal_a3 =
1022  cpl_table_get_double(arc_tab, "WL_coefficients", 3, NULL);
1023  sofi_spc_jitter_config.wavecal_a4 =
1024  cpl_table_get_double(arc_tab, "WL_coefficients", 4, NULL);
1025  cpl_table_delete(arc_tab);
1026  sofi_spc_jitter_config.wavecal_out = 2;
1027  sofi_spc_jitter_config.wavecal_cc = -1.0;
1028  return 0;
1029  }
1030 
1031  /* Get the reference frame */
1032  cur_frame = cpl_frameset_get_position(raw, 0);
1033  cur_fname = cpl_frame_get_filename(cur_frame);
1034 
1035  /* Get the physical model */
1036  cpl_msg_info(cpl_func, "Compute the physical model");
1037  cpl_msg_indent_more();
1038  if ((phdisprel = sofi_get_disprel_estimate(cur_fname, 1)) == NULL) {
1039  cpl_msg_error(cpl_func, "cannot compute the physical model");
1040  sofi_spc_jitter_config.wavecal_out = -1;
1041  cpl_msg_indent_less();
1042  return CPL_ERROR_UNSPECIFIED;
1043  }
1044  p[0] = 0; p[1] = 1;
1045  a = cpl_polynomial_get_coeff(phdisprel, p);
1046  b = cpl_polynomial_get_coeff(phdisprel, p + 1);
1047  cpl_msg_info(cpl_func, "f(x)=%g + %g*x", a, b);
1048  sofi_spc_jitter_config.wavecal_a0 = a;
1049  sofi_spc_jitter_config.wavecal_a1 = b;
1050  sofi_spc_jitter_config.wavecal_a2 = 0.0;
1051  sofi_spc_jitter_config.wavecal_a3 = 0.0;
1052  sofi_spc_jitter_config.wavecal_a4 = 0.0;
1053  sofi_spc_jitter_config.wavecal_cc = -1.0;
1054  sofi_spc_jitter_config.wavecal_out = 0;
1055  cpl_msg_indent_less();
1056 
1057  /* Compute the wavelength using the sky lines */
1058  if (sofi_spc_jitter_config.wavecal_in == 1) {
1059  /* Compute the slit_width */
1060  if ((slit_width = sofi_get_slitwidth(cur_fname)) == -1) {
1061  cpl_msg_warning(cpl_func, "cannot get the slit width");
1062  cpl_polynomial_delete(phdisprel);
1063  return 0;
1064  }
1065  /* Compute the wavelength */
1066  cpl_msg_info(cpl_func, "Compute the wavelength with the sky lines");
1067  cpl_msg_indent_more();
1068  if ((disprel = sofi_wavelength_engine(ima, "oh", oh, NULL, NULL,
1069  phdisprel, slit_width,
1070  sofi_spc_jitter_config.wavecal_degree,
1071  sofi_spc_jitter_config.wavecal_err,
1072  sofi_spc_jitter_config.wavecal_nsamples,
1073  sofi_spc_jitter_config.wavecal_ppm,
1074  sofi_spc_jitter_config.display, &xc)) == NULL) {
1075  cpl_msg_error(cpl_func, "cannot compute the dispersion relation");
1076  cpl_polynomial_delete(phdisprel);
1077  cpl_msg_indent_less();
1078  return 0;
1079  }
1080  cpl_msg_info(cpl_func, "Cross correlation factor: %g", xc);
1081  p[0] = 0; p[1] = 1; p[2] = 2; p[3] = 3; p[4] = 4;
1082  a = b = c = d = e = 0.0;
1083  degree = cpl_polynomial_get_degree(disprel);
1084  a = cpl_polynomial_get_coeff(disprel, p);
1085  if (degree > 0) b = cpl_polynomial_get_coeff(disprel, p + 1);
1086  if (degree > 1) c = cpl_polynomial_get_coeff(disprel, p + 2);
1087  if (degree > 2) d = cpl_polynomial_get_coeff(disprel, p + 3);
1088  if (degree > 3) e = cpl_polynomial_get_coeff(disprel, p + 4);
1089  if (degree == 0)
1090  cpl_msg_info(cpl_func, "f(x)=%g", a);
1091  if (degree == 1)
1092  cpl_msg_info(cpl_func, "f(x)=%g + %g*x", a, b);
1093  if (degree == 2)
1094  cpl_msg_info(cpl_func, "f(x)=%g + %g*x + %g*x^2", a, b, c);
1095  if (degree == 3)
1096  cpl_msg_info(cpl_func, "f(x)=%g + %g*x + %g*x^2 + %g*x^3",
1097  a, b, c, d);
1098  if (degree == 4)
1099  cpl_msg_info(cpl_func, "f(x)=%g + %g*x + %g*x^2 + %g*x^3 + %g*x^4",
1100  a, b, c, d, e);
1101  sofi_spc_jitter_config.wavecal_a0 = a;
1102  sofi_spc_jitter_config.wavecal_a1 = b;
1103  sofi_spc_jitter_config.wavecal_a2 = c;
1104  sofi_spc_jitter_config.wavecal_a3 = d;
1105  sofi_spc_jitter_config.wavecal_a4 = e;
1106  sofi_spc_jitter_config.wavecal_cc = xc;
1107  sofi_spc_jitter_config.wavecal_out = 1;
1108  cpl_polynomial_delete(disprel);
1109  cpl_msg_indent_less();
1110  }
1111  cpl_polynomial_delete(phdisprel);
1112  return 0;
1113 }
1114 
1115 /*----------------------------------------------------------------------------*/
1136 /*----------------------------------------------------------------------------*/
1137 static cpl_imagelist * sofi_spc_jitter_nodded(
1138  cpl_imagelist * abba,
1139  cpl_vector * abba_off,
1140  cpl_vector ** nodded_off)
1141 {
1142  cpl_imagelist * nodded;
1143  cpl_image * tmp_ima;
1144  int nima;
1145  double ref_off;
1146  int i;
1147 
1148  /* Test entries */
1149  if ((abba == NULL) || (abba_off == NULL)) return NULL;
1150 
1151  /* Initialise */
1152  nima = cpl_imagelist_get_size(abba);
1153  if (nima % 2) {
1154  cpl_msg_error(cpl_func, "Number of images should be even");
1155  return NULL;
1156  }
1157 
1158  /* Create the offsets between the nodded images */
1159  *nodded_off = cpl_vector_duplicate(abba_off);
1160  /* The image list to contain the nodded images */
1161  nodded = cpl_imagelist_new();
1162  for (i=0; i<(nima/2); i++) {
1163  /* a-b */
1164  tmp_ima = cpl_image_duplicate(cpl_imagelist_get(abba, 2*i));
1165  cpl_image_subtract(tmp_ima, cpl_imagelist_get(abba, 2*i+1));
1166  cpl_imagelist_set(nodded, tmp_ima, 2*i);
1167  /* b-a */
1168  tmp_ima = cpl_image_duplicate(cpl_imagelist_get(abba, 2*i+1));
1169  cpl_image_subtract(tmp_ima, cpl_imagelist_get(abba, 2*i));
1170  cpl_imagelist_set(nodded, tmp_ima, 2*i+1);
1171  }
1172 
1173  /* Subtract the first offset to the others */
1174  ref_off = cpl_vector_get(*nodded_off, 0);
1175  cpl_vector_subtract_scalar(*nodded_off, ref_off);
1176  return nodded;
1177 }
1178 
1179 /*----------------------------------------------------------------------------*/
1186 /*----------------------------------------------------------------------------*/
1187 static cpl_imagelist * sofi_spc_jitter_distor(
1188  cpl_imagelist * ilist,
1189  const char * arc)
1190 {
1191  cpl_polynomial * arc_poly;
1192  cpl_polynomial * id_poly;
1193  cpl_table * tab;
1194  cpl_size power[2];
1195  cpl_vector * profile;
1196  cpl_imagelist * warped_list;
1197  cpl_image * warped;
1198  int i;
1199 
1200  /* Test entries */
1201  if (ilist == NULL) return NULL;
1202  if (arc == NULL) return NULL;
1203 
1204  /* Get the arc polynomial */
1205  arc_poly = cpl_polynomial_new(2);
1206  cpl_msg_info(cpl_func, "Get the arc distortion from the file");
1207  if ((tab = cpl_table_load(arc, 1, 0)) == NULL) {
1208  cpl_msg_error(cpl_func, "cannot load the arc table");
1209  cpl_polynomial_delete(arc_poly);
1210  return NULL;
1211  }
1212  for (i=0; i<cpl_table_get_nrow(tab); i++) {
1213  power[0] = cpl_table_get_int(tab, "Degree_of_x", i, NULL);
1214  power[1] = cpl_table_get_int(tab, "Degree_of_y", i, NULL);
1215  cpl_polynomial_set_coeff(arc_poly, power,
1216  cpl_table_get_double(tab, "poly2d_coef", i, NULL));
1217  }
1218  cpl_table_delete(tab);
1219 
1220  /* Create the ID polynomial for the other direction */
1221  id_poly = cpl_polynomial_new(2);
1222  power[0] = 1;
1223  power[1] = 0;
1224  cpl_polynomial_set_coeff(id_poly, power, 1.0);
1225 
1226  /* Create the kernel */
1227  profile = cpl_vector_new(CPL_KERNEL_DEF_SAMPLES);
1228  cpl_vector_fill_kernel_profile(profile, CPL_KERNEL_DEFAULT,
1229  CPL_KERNEL_DEF_WIDTH);
1230 
1231  /* Correct the images */
1232  warped_list = cpl_imagelist_new();
1233  for (i=0; i<cpl_imagelist_get_size(ilist); i++) {
1234  warped = cpl_image_duplicate(cpl_imagelist_get(ilist, i));
1235  if (cpl_image_warp_polynomial(warped, cpl_imagelist_get(ilist, i),
1236  id_poly, arc_poly, profile, CPL_KERNEL_DEF_WIDTH, profile,
1237  CPL_KERNEL_DEF_WIDTH) != CPL_ERROR_NONE) {
1238  cpl_msg_error(cpl_func, "cannot correct the distortion");
1239  cpl_image_delete(warped);
1240  cpl_polynomial_delete(arc_poly);
1241  cpl_polynomial_delete(id_poly);
1242  cpl_vector_delete(profile);
1243  return NULL;
1244  }
1245  cpl_imagelist_set(warped_list, warped, i);
1246  }
1247  cpl_vector_delete(profile);
1248  cpl_polynomial_delete(arc_poly);
1249  cpl_polynomial_delete(id_poly);
1250  return warped_list;
1251 }
1252 
1253 /*----------------------------------------------------------------------------*/
1260 /*----------------------------------------------------------------------------*/
1261 static double sofi_spc_jitter_refine_offset(
1262  cpl_image * ima1,
1263  cpl_image * ima2)
1264 {
1265  double pos1, pos2;
1266 
1267  /* Test entries */
1268  if (ima1 == NULL) return 10000.0;
1269  if (ima2 == NULL) return 10000.0;
1270 
1271  /* Detect the spectra */
1272  if (irplib_spectrum_find_brightest(ima1, 0.0, NO_SHADOW, 0.0, 1,
1273  &pos1) == -1){
1274  return 10000.0;
1275  }
1276  if (irplib_spectrum_find_brightest(ima2, 0.0, NO_SHADOW, 0.0, 1,
1277  &pos2) == -1){
1278  return 10000.0;
1279  }
1280  return pos1-pos2;
1281 }
1282 
1283 /*----------------------------------------------------------------------------*/
1289 /*----------------------------------------------------------------------------*/
1290 static cpl_table * sofi_spc_jitter_extract(cpl_image * combined)
1291 {
1292  int ri_dist, le_dist, ri_width, le_width, spec_pos;
1293  int nx, ny;
1294  double pos;
1295  int right_side, left_side;
1296  int sky_pos[4];
1297  cpl_vector * sky;
1298  cpl_vector * spec;
1299  cpl_vector * wl;
1300  double * pspec;
1301  double * psky;
1302  double * pwl;
1303  cpl_table * out;
1304  cpl_bivector * toplot;
1305  int throw;
1306  int res;
1307  int i;
1308 
1309  /* Test entries */
1310  if (combined == NULL) return NULL;
1311 
1312  /* Initialise */
1313  nx = cpl_image_get_size_x(combined);
1314  ny = cpl_image_get_size_y(combined);
1315  ri_dist = sofi_spc_jitter_config.extr_sky_ri_dist;
1316  le_dist = sofi_spc_jitter_config.extr_sky_le_dist;
1317  ri_width = sofi_spc_jitter_config.extr_sky_ri_width;
1318  le_width = sofi_spc_jitter_config.extr_sky_le_width;
1319  spec_pos = sofi_spc_jitter_config.extr_spec_pos;
1320  res = -1;
1321 
1322  /* Detect the spectrum position if not passed */
1323  if (spec_pos < 0) {
1324  if (sofi_spc_jitter_config.throws == NULL) {
1325  cpl_msg_error(cpl_func,
1326  "Need a throw value to detect the spectra !!");
1327  return NULL;
1328  }
1329  for (i=0; i<cpl_vector_get_size(sofi_spc_jitter_config.throws); i++){
1330  throw = (int)cpl_vector_get(sofi_spc_jitter_config.throws, i);
1331  if ((res = irplib_spectrum_find_brightest(combined, throw,
1332  TWO_SHADOWS, 0.0, 1, &pos)) == 0) break;
1333  if ((res = irplib_spectrum_find_brightest(combined, throw,
1334  ONE_SHADOW, 0.0, 1, &pos)) == 0) break;
1335  }
1336  if (res != 0) {
1337  cpl_msg_error(cpl_func, "Cannot detect the spectrum");
1338  return NULL;
1339  }
1340  spec_pos = (int)pos;
1341  cpl_msg_info(cpl_func, "Spectrum detected at x = %d", spec_pos);
1342  }
1343 
1344  /* Set the parameters for the extraction */
1345 
1346  /* Spectrum position */
1347  left_side = spec_pos - (int)(sofi_spc_jitter_config.extr_spec_width/2);
1348  right_side = left_side + sofi_spc_jitter_config.extr_spec_width;
1349  if ((left_side < 1) || (right_side > ny)) {
1350  cpl_msg_error(cpl_func, "Spectrum zone falls outside the image");
1351  return NULL;
1352  }
1353  /* Residual Sky position */
1354  if (ri_dist < 0) ri_dist = 2*sofi_spc_jitter_config.extr_spec_width;
1355  if (le_dist < 0) le_dist = 2*sofi_spc_jitter_config.extr_spec_width;
1356  sky_pos[1] = spec_pos - le_dist;
1357  sky_pos[0] = sky_pos[1] - le_width;
1358  sky_pos[2] = spec_pos + ri_dist;
1359  sky_pos[3] = sky_pos[2] + ri_width;
1360 
1361  /* Get the sky */
1362  sky = cpl_vector_new(ny);
1363  psky = cpl_vector_get_data(sky);
1364  if (((sky_pos[0] < 1) || (le_width == 0)) &&
1365  ((sky_pos[3] <= nx) && (ri_width > 0))) {
1366  for (i=0; i<ny; i++) {
1367  psky[i] = cpl_image_get_median_window(combined, sky_pos[2],
1368  ny-i, sky_pos[3], ny-i);
1369  }
1370  } else if (((sky_pos[3] > nx) || (ri_width == 0))
1371  && ((sky_pos[0] > 0) && (le_width > 0))) {
1372  for (i=0; i<ny; i++) {
1373  psky[i] = cpl_image_get_median_window(combined, sky_pos[0],
1374  ny-i, sky_pos[1], ny-i);
1375  }
1376  } else if ((ri_width != 0) && (le_width != 0)
1377  && (sky_pos[0] > 0) && (sky_pos[3] <= nx)) {
1378  for (i=0; i<ny; i++) {
1379  psky[i] = cpl_image_get_median_window(combined, sky_pos[2],
1380  ny-i, sky_pos[3], ny-i);
1381  psky[i] += cpl_image_get_median_window(combined, sky_pos[0],
1382  ny-i, sky_pos[1], ny-i);
1383  psky[i] /= 2.0;
1384  }
1385  } else {
1386  for (i=0; i<ny; i++) psky[i] = 0.0;
1387  }
1388 
1389  /* Estimate the spectrum */
1390  spec = cpl_vector_new(ny);
1391  pspec = cpl_vector_get_data(spec);
1392  for (i=0; i<ny; i++) {
1393  pspec[i] = cpl_image_get_flux_window(combined, left_side, ny-i,
1394  right_side, ny-i);
1395  pspec[i] -= psky[i] * sofi_spc_jitter_config.extr_spec_width;
1396  }
1397 
1398  /* Get the wavelength */
1399  wl = cpl_vector_new(ny);
1400  pwl = cpl_vector_get_data(wl);
1401  for (i=0; i<ny; i++) {
1402  pwl[i] = sofi_spc_jitter_config.wavecal_a0 +
1403  sofi_spc_jitter_config.wavecal_a1 * (i+1) +
1404  sofi_spc_jitter_config.wavecal_a2 * (i+1) * (i+1) +
1405  sofi_spc_jitter_config.wavecal_a3 * (i+1) * (i+1) * (i+1) +
1406  sofi_spc_jitter_config.wavecal_a4 * (i+1) * (i+1) * (i+1) + (i+1);
1407  }
1408 
1409  /* Plot the spectrum if requested */
1410  if (sofi_spc_jitter_config.display) {
1411  toplot = cpl_bivector_wrap_vectors(wl, spec);
1412  cpl_plot_bivector(NULL, "t 'Spectrum' w lines", NULL, toplot);
1413  cpl_bivector_unwrap_vectors(toplot);
1414  toplot = cpl_bivector_wrap_vectors(wl, sky);
1415  cpl_plot_bivector(NULL, "t 'Sky' w lines", NULL, toplot);
1416  cpl_bivector_unwrap_vectors(toplot);
1417  }
1418 
1419  /* Create and fill the output table */
1420  out = cpl_table_new(nx);
1421  cpl_table_new_column(out, "Y_coordinate", CPL_TYPE_DOUBLE);
1422  cpl_table_new_column(out, "Extracted_spectrum_value", CPL_TYPE_DOUBLE);
1423  cpl_table_new_column(out, "Sky_spectrum", CPL_TYPE_DOUBLE);
1424  for (i=0; i<nx; i++) {
1425  cpl_table_set_double(out, "Y_coordinate", i, pwl[i]);
1426  cpl_table_set_double(out, "Extracted_spectrum_value", i, pspec[i]);
1427  cpl_table_set_double(out, "Sky_spectrum", i, psky[i]);
1428  }
1429  cpl_vector_delete(wl);
1430  cpl_vector_delete(spec);
1431  cpl_vector_delete(sky);
1432  return out;
1433 }
1434 
1435 /*----------------------------------------------------------------------------*/
1444 /*----------------------------------------------------------------------------*/
1445 static cpl_error_code sofi_spc_jitter_save(cpl_frameset * set,
1446  const cpl_image * ima,
1447  const cpl_table * tab,
1448  const cpl_parameterlist * parlist)
1449 {
1450  cpl_propertylist * plist;
1451  /* Get the QC params in qclist */
1452  cpl_propertylist * qclist = cpl_propertylist_new();
1453  cpl_propertylist * paflist;
1454  /* Get the reference frame */
1455  const cpl_frame * ref_frame
1456  = irplib_frameset_get_first_from_group(set, CPL_FRAME_GROUP_RAW);
1457  char qc_str[128];
1458  int i;
1459 
1460 
1461  /* Add QC parameters */
1462  if (sofi_spc_jitter_config.filter[0] != (char)0)
1463  cpl_propertylist_append_string(qclist, "ESO QC FILTER OBS",
1464  sofi_spc_jitter_config.filter);
1465  cpl_propertylist_append_double(qclist, "ESO QC DISPCO1",
1466  sofi_spc_jitter_config.wavecal_a0);
1467  cpl_propertylist_append_double(qclist, "ESO QC DISPCO2",
1468  sofi_spc_jitter_config.wavecal_a1);
1469  cpl_propertylist_append_double(qclist, "ESO QC DISPCO3",
1470  sofi_spc_jitter_config.wavecal_a2);
1471  cpl_propertylist_append_double(qclist, "ESO QC DISPCO4",
1472  sofi_spc_jitter_config.wavecal_a3);
1473  cpl_propertylist_append_double(qclist, "ESO QC DISPCO5",
1474  sofi_spc_jitter_config.wavecal_a4);
1475  cpl_propertylist_append_double(qclist, "ESO QC WLEN",
1476  sofi_spc_jitter_config.wavecal_a0 +
1477  sofi_spc_jitter_config.wavecal_a1 * 512 +
1478  sofi_spc_jitter_config.wavecal_a2 * 512 * 512 +
1479  sofi_spc_jitter_config.wavecal_a3 * 512 * 512 * 512 +
1480  sofi_spc_jitter_config.wavecal_a4 * 512 * 512 * 512 * 512);
1481  cpl_propertylist_append_double(qclist, "ESO QC DISP XCORR",
1482  sofi_spc_jitter_config.wavecal_cc);
1483  if (sofi_spc_jitter_config.wavecal_out == 0) {
1484  cpl_propertylist_append_string(qclist, "ESO QC WLMETHOD",
1485  "physical model");
1486  } else if (sofi_spc_jitter_config.wavecal_out == 1) {
1487  cpl_propertylist_append_string(qclist, "ESO QC WLMETHOD",
1488  "sky lines");
1489  } else if (sofi_spc_jitter_config.wavecal_out == 2) {
1490  cpl_propertylist_append_string(qclist, "ESO QC WLMETHOD",
1491  "arc file");
1492  }
1493  for (i=0; i<cpl_vector_get_size(sofi_spc_jitter_config.intensities);i++) {
1494  sprintf(qc_str, "ESO QC SPEC INTENS%d", i+1);
1495  cpl_propertylist_append_double(qclist, qc_str,
1496  cpl_vector_get(sofi_spc_jitter_config.intensities, i));
1497  }
1498 
1499  /* Change WCS keywords to the computed wavelength solution */
1500  cpl_propertylist_update_double(qclist, "CRVAL1",
1501  sofi_spc_jitter_config.wavecal_a0);
1502  cpl_propertylist_update_double(qclist, "CRVAL2", 1.0);
1503  cpl_propertylist_update_double(qclist, "CRPIX1", 1.0);
1504  cpl_propertylist_update_double(qclist, "CRPIX2", 1.0);
1505  cpl_propertylist_update_double(qclist, "CDELT1",
1506  sofi_spc_jitter_config.wavecal_a1);
1507  cpl_propertylist_update_double(qclist, "CDELT2", 1.0);
1508  cpl_propertylist_update_string(qclist, "CTYPE1", "LINEAR");
1509  cpl_propertylist_update_string(qclist, "CTYPE2", "LINEAR");
1510  if (cpl_propertylist_has(qclist, "CD1_1")) {
1511  cpl_propertylist_update_double(qclist, "CD1_1",
1512  sofi_spc_jitter_config.wavecal_a1);
1513  } else {
1514  cpl_propertylist_insert_after_double(qclist, "CTYPE2", "CD1_1",
1515  sofi_spc_jitter_config.wavecal_a1);
1516  }
1517  if (cpl_propertylist_has(qclist, "CD2_2")) {
1518  cpl_propertylist_update_double(qclist, "CD2_2", 1.0);
1519  } else {
1520  cpl_propertylist_insert_after_double(qclist, "CD1_1", "CD2_2", 1.0);
1521  }
1522 
1523  /* Write the image */
1524  irplib_dfs_save_image(set, parlist, set, ima, CPL_BPP_IEEE_FLOAT,
1525  RECIPE_STRING, SOFI_SPC_JITTER_COMB, qclist,
1526  NULL, PACKAGE "/" PACKAGE_VERSION,
1527  RECIPE_STRING "_combined" CPL_DFS_FITS);
1528 
1529  /* Write the table */
1530  if (tab != NULL) {
1531  irplib_dfs_save_table(set, parlist, set, tab, NULL, RECIPE_STRING,
1532  SOFI_SPC_JITTER_EXTR, qclist, NULL, PACKAGE "/" PACKAGE_VERSION,
1533  RECIPE_STRING "_extracted" CPL_DFS_FITS);
1534  }
1535 
1536 
1537  /* Get FITS header from reference file */
1538  plist = cpl_propertylist_load(cpl_frame_get_filename(ref_frame), 0);
1539  if (plist == NULL) {
1540  cpl_propertylist_delete(qclist);
1541  return cpl_error_set_message(cpl_func, cpl_error_get_code(), "Could "
1542  "not read header from reference frame");
1543  }
1544 
1545  /* Get the keywords for the paf file */
1546  paflist = cpl_propertylist_new();
1547  cpl_propertylist_copy_property_regexp(paflist, plist,
1548  "^(ARCFILE|MJD-OBS|INSTRUME|ESO TPL ID|ESO TPL NEXP|ESO DPR CATG|"
1549  "ESO DPR TECH|ESO DPR TYPE|DATE-OBS|ESO INS GRAT NAME|"
1550  "ESO INS GRAT WLEN|ESO INS OPTI1 ID|ESO OBS ID|ESO OBS TARG NAME)$", 0);
1551  cpl_propertylist_delete(plist);
1552 
1553  /* Copy the QC in paflist */
1554  cpl_propertylist_copy_property_regexp(paflist, qclist, ".", 0);
1555  cpl_propertylist_delete(qclist);
1556 
1557  /* Save the PAF file */
1558  cpl_dfs_save_paf("SOFI", RECIPE_STRING, paflist,
1559  RECIPE_STRING CPL_DFS_PAF);
1560  cpl_propertylist_delete(paflist);
1561 
1562  return cpl_error_get_code();
1563 }
1564 
1565 /*----------------------------------------------------------------------------*/
1573 /*----------------------------------------------------------------------------*/
1574 static int off_comp(double off1, double off2, double thresh)
1575 {
1576  if (((off1>thresh) && (off2<thresh)) || ((off1<thresh) && (off2>thresh)))
1577  return 1;
1578  else return 0;
1579 }
1580 
sofi_correct_crosstalk_list
int sofi_correct_crosstalk_list(cpl_imagelist *ilist)
Remove the Cross-talk effect from an image list.
Definition: sofi_utils.c:91
sofi_extract_frameset
cpl_frameset * sofi_extract_frameset(const cpl_frameset *in, const char *tag)
Extract the frames with the given tag from a frameset.
Definition: sofi_utils.c:298
sofi_dfs_set_groups
int sofi_dfs_set_groups(cpl_frameset *set)
Set the group as RAW or CALIB in a frameset.
Definition: sofi_dfs.c:60
sofi_wavelength_engine
cpl_polynomial * sofi_wavelength_engine(const cpl_image *in, const char *table_name, const char *oh, const char *xe, const char *ne, const cpl_polynomial *phdisprel, double slit_width, int degree, double wl_err, int nsamples, int use_ppm, int plot, double *xcorr)
Compute a dispersion relation.
Definition: sofi_wavelength.c:150
sofi_get_disprel_estimate
cpl_polynomial * sofi_get_disprel_estimate(const char *filename, int poly_deg)
Estimate the instrument dispersion relation.
Definition: sofi_wavelength.c:313
sofi_extract_filename
const char * sofi_extract_filename(const cpl_frameset *in, const char *tag)
Extract the filename ffor the first frame of the given tag.
Definition: sofi_utils.c:342
sofi_pfits_get_cumoffsetx
double sofi_pfits_get_cumoffsetx(const cpl_propertylist *plist)
find out the cumulative offset in X
Definition: sofi_pfits.c:110
sofi_get_slitwidth
double sofi_get_slitwidth(const char *filename)
Find out the slit width.
Definition: sofi_wavelength.c:263
sofi_pfits_get_filter
const char * sofi_pfits_get_filter(const cpl_propertylist *plist)
find out which wave band is active in short wavelength
Definition: sofi_pfits.c:243