DETMON Pipeline Reference Manual  1.3.0
detmon.c
1 /* $Id: detmon.c,v 1.11 2013-07-19 12:00:24 jtaylor Exp $
2  *
3  * This file is part of the irplib package
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: jtaylor $
23  * $Date: 2013-07-19 12:00:24 $
24  * $Revision: 1.11 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <complex.h>
33 
34 /*---------------------------------------------------------------------------
35  Includes
36  ---------------------------------------------------------------------------*/
37 
38 #include <math.h>
39 #include <string.h>
40 #include <assert.h>
41 #include <float.h>
42 
43 #include <cpl.h>
44 
45 #include "detmon.h"
46 
47 #include "irplib_ksigma_clip.h"
48 #include "irplib_hist.h"
49 #include "irplib_utils.h"
50 
51 
52 /* Computes the square of an euclidean distance bet. 2 points */
53 #define pdist(x1,y1,x2,y2) (((x1-x2)*(x1-x2))+((y1-y2)*(y1-y2)))
54 
55 #define cpl_drand() ((double)rand()/(double)RAND_MAX)
56 
57 /*--------------------------------------------------------------------------*/
58 
59 /*
60  * @defgroup detmon Detector monitoring functions
61  */
62 
63 /*--------------------------------------------------------------------------*/
64 
65 /*---------------------------------------------------------------------------
66  Defines
67  ---------------------------------------------------------------------------*/
68 
69 
70 #define HIST_FACT 2.354820045
71 
72 enum pixeltypes
73 {
74  HOT = 0,
75  DEAD = 1,
76  NOISY = 2
77 };
78 
79 enum stackingtypes
80 {
81  MINMAX = 0,
82  MEAN = 1,
83  MEDIAN = 2,
84  KSIGMA = 3
85 };
86 
87 enum readouts
88 {
89  HORIZONTAL = 1,
90  VERTICAL = 2
91 };
92 
93 
94 static struct
95 {
96  const char * ron_method;
97  const char * dsnu_method;
98  int exts;
99  int nb_extensions;
100  cpl_boolean opt_nir;
101 } detmon_dark_config;
102 
103 #define NIR TRUE
104 #define OPT FALSE
105 
106 /*---------------------------------------------------------------------------
107  Private function prototypes
108  ---------------------------------------------------------------------------*/
109 
110 /* Functions for RON/Bias recipe, detmon_ronbias() */
111 
112 cpl_error_code
113 detmon_rm_bpixs(cpl_image **,
114  const double,
115  int ,
116  int );
117 
118 /* The following 2 functions are duplicated from cpl_det */
119 
120 
121 
122 static cpl_bivector *
123 irplib_bivector_gen_rect_poisson(const int *r,
124  const int np,
125  const int homog);
126 
127 static cpl_error_code
128 detmon_dark_save(const cpl_parameterlist *,
129  cpl_frameset *,
130  const char *,
131  const char *,
132  const char *,
133  const char *,
134  const char *,
135  const char *,
136  cpl_imagelist **,
137  cpl_table **,
138  cpl_imagelist **,
139  cpl_propertylist **,
140  const int,
141  const int,
142  const cpl_frameset *);
143 
144 
145 
146 static cpl_error_code
147 detmon_retrieve_dark_params(const char *,
148  const char *,
149  const cpl_parameterlist *);
150 
151 
152 /*---------------------------------------------------------------------------*/
153 
154 /*
155  * @brief Fill input parameters values
156  * @param parlist parameters list
157  * @param recipe_name recipe name
158  * @param pipeline_name pipeline name
159  * @param npars number of parameters
160 
161  * @return CPL_ERROR_NONE on success.
162  */
163 
164 /*---------------------------------------------------------------------------*/
165 cpl_error_code
166 detmon_fill_parlist(cpl_parameterlist * parlist,
167  const char *recipe_name,
168  const char *pipeline_name,
169  int npars, ...)
170 {
171 
172  va_list ap;
173 
174  char *group_name;
175 
176  int pars_counter = 0;
177 
178  group_name = cpl_sprintf("%s.%s", pipeline_name, recipe_name);
179  assert(group_name != NULL);
180 
181 #define insert_par(PARNAME, PARDESC, PARVALUE, PARTYPE) \
182  do { \
183  char * par_name = cpl_sprintf("%s.%s", group_name, PARNAME); \
184  cpl_parameter * p; \
185  assert(par_name != NULL); \
186  p = cpl_parameter_new_value(par_name, PARTYPE, \
187  PARDESC, group_name, PARVALUE); \
188  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, PARNAME); \
189  cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); \
190  cpl_parameterlist_append(parlist, p); \
191  cpl_free(par_name); \
192  } while(0);
193 
194 
195  va_start(ap, npars);
196 
197  while(pars_counter < npars) {
198  char *name = va_arg(ap, char *);
199  char *desc = va_arg(ap, char *);
200  char *type = va_arg(ap, char *);
201 
202  if(!strcmp(type, "CPL_TYPE_INT")) {
203  int v1 = va_arg(ap, int);
204 
205  insert_par(name, desc, v1, CPL_TYPE_INT);
206  } else if(!strcmp(type, "CPL_TYPE_BOOL")) {
207  char *v2 = va_arg(ap, char *);
208 
209  if(!strcmp(v2, "CPL_FALSE"))
210  insert_par(name, desc, CPL_FALSE, CPL_TYPE_BOOL);
211  if(!strcmp(v2, "CPL_TRUE"))
212  insert_par(name, desc, CPL_TRUE, CPL_TYPE_BOOL);
213  } else if(!strcmp(type, "CPL_TYPE_STRING")) {
214  char *v2 = va_arg(ap, char *);
215 
216  insert_par(name, desc, v2, CPL_TYPE_STRING);
217  } else if(!strcmp(type, "CPL_TYPE_DOUBLE")) {
218  double v3 = va_arg(ap, double);
219  insert_par(name, desc, v3, CPL_TYPE_DOUBLE);
220  }
221 
222  pars_counter++;
223  }
224 
225  va_end(ap);
226 
227  cpl_free(group_name);
228 
229 #undef insert_par
230  return 0;
231 }
232 
233 /*---------------------------------------------------------------------------*/
234 
235 /*
236  * @brief Retrieve input int parameter
237  * @param pipeline_name Input image
238  * @param recipe_name Input image
239  * @param parlist Shift to apply on the x-axis
240  * @return CPL_ERROR_NONE on success.
241  */
242 
243 /*---------------------------------------------------------------------------*/
244 int
245 detmon_retrieve_par_int(const char *parn,
246  const char *pipeline_name,
247  const char *recipe_name,
248  const cpl_parameterlist * parlist)
249 {
250  char *par_name;
251  cpl_parameter *par;
252  int value;
253 
254  par_name = cpl_sprintf("%s.%s.%s", pipeline_name, recipe_name, parn);
255  assert(par_name != NULL);
256  par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
257  value = cpl_parameter_get_int(par);
258  cpl_free(par_name);
259 
260  return value;
261 }
262 
263 /*---------------------------------------------------------------------------*/
264 
265 /*
266  * @brief Retrieve input double parameter
267  * @param pipeline_name Input image
268  * @param recipe_name Input image
269  * @param parlist Shift to apply on the x-axis
270  * @return CPL_ERROR_NONE on success.
271  */
272 
273 /*---------------------------------------------------------------------------*/
274 double
275 detmon_retrieve_par_double(const char *parn,
276  const char *pipeline_name,
277  const char *recipe_name,
278  const cpl_parameterlist * parlist)
279 {
280  char *par_name;
281  cpl_parameter *par;
282  double value;
283 
284  par_name = cpl_sprintf("%s.%s.%s", pipeline_name, recipe_name, parn);
285  assert(par_name != NULL);
286  par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
287  value = cpl_parameter_get_double(par);
288  cpl_free(par_name);
289 
290  return value;
291 }
292 
293 
294 /*---------------------------------------------------------------------------*/
295 
296 /*
297  * @brief Retrieve exposure time
298  * @param plist parameter list
299  * @return "EXPTIME" keyword value.
300  */
301 
302 /*---------------------------------------------------------------------------*/
303 
304 double
305 irplib_pfits_get_exptime(const cpl_propertylist * plist)
306 {
307  double exptime;
308 
309  exptime = cpl_propertylist_get_double(plist, "EXPTIME");
310 
311  return exptime;
312 }
313 
314 /*---------------------------------------------------------------------------*/
315 /*
316  * @brief Generate propertylist with product category, type, technique
317  * @param procatg product category
318  * @param protype product type
319  * @param protech product technique
320  * @param proscience switch to identify a science product
321  * @return filled cpl_propertylist (to be deallocated)
322  */
323 /*---------------------------------------------------------------------------*/
324 cpl_propertylist *
325 detmon_fill_prolist(const char * procatg,
326  const char * protype,
327  const char * protech,
328  cpl_boolean proscience)
329 {
330  cpl_propertylist * prolist = cpl_propertylist_new();
331 
332  cpl_propertylist_append_string(prolist, CPL_DFS_PRO_CATG, procatg);
333  cpl_propertylist_append_bool(prolist, CPL_DFS_PRO_SCIENCE, proscience);
334  if (protype) {
335  cpl_propertylist_append_string(prolist, CPL_DFS_PRO_TYPE, protype);
336  }
337  if (protech) {
338  cpl_propertylist_append_string(prolist, CPL_DFS_PRO_TECH, protech);
339  }
340 
341  return prolist;
342 }
343 
344 /*---------------------------------------------------------------------------*/
345 
346 /*
347  * @brief Remove bad pixels
348  * @param image input/output image
349  * @param kappa kappa for kappa-sigma clip
350  * @param nffts number of fft
351  * @param nsamples number of sampling areas
352  * @return CPL_ERROR_NONE on success.
353  */
354 
355 /*---------------------------------------------------------------------------*/
356 
357 
358 cpl_error_code
359 detmon_rm_bpixs(cpl_image ** image,
360  const double kappa, int nffts, int nsamples)
361 {
362  int i, j;
363 
364  float *data = cpl_image_get_data_float(*image);
365  int k = 0;
366  for(i = 0; i < nffts; i++) {
367  for(j = 0; j < nsamples; j++) {
368  float neighbours = 0;
369  int nneighs = 0;
370  float average = 0;
371 
372  /*
373  * Look for the way to optimize this:
374  * Some of the points added to neighbours coincide
375  * in one iteration and the following
376  */
377  if(i > 0) {
378  neighbours += *(data + (i - 1) * nsamples + j);
379  nneighs++;
380  }
381  if(i < nffts - 1) {
382  neighbours += *(data + (i + 1) * nsamples + j);
383  nneighs++;
384  }
385  if(j > 0) {
386  neighbours += *(data + i * nsamples + (j - 1));
387  nneighs++;
388  }
389  if(j < nsamples - 1) {
390  neighbours += *(data + i * nsamples + (j + 1));
391  nneighs++;
392  }
393  average = neighbours / nneighs;
394  if(average > 0) {
395  if(*(data + i * nsamples + j) < average * (-1 * kappa) ||
396  *(data + i * nsamples + j) > average * (kappa)) {
397  k++;
398  *(data + i * nsamples + j) = average;
399  }
400  }
401  if(average < 0) {
402  if(*(data + i * nsamples + j) > average * (-1 * kappa) ||
403  *(data + i * nsamples + j) < average * (kappa)) {
404  k++;
405  *(data + i * nsamples + j) = average;
406  }
407  }
408 
409  }
410  }
411 
412 
413  return cpl_error_get_code();
414 
415 }
416 
417 /*---------------------------------------------------------------------------*/
418 
419 /*
420  * @brief Generates Poisson distributed values
421  * @param r Input values
422  * @param np number of points
423  * @param homog homogeneity factor
424  * @return CPL_ERROR_NONE on success.
425  */
426 
427 /*---------------------------------------------------------------------------*/
428 static cpl_bivector *
429 irplib_bivector_gen_rect_poisson(const int *r, const int np, const int homog)
430 {
431  double min_dist;
432  int i;
433  int gnp;
434  cpl_bivector *list;
435  double cand_x, cand_y;
436  int ok;
437  int start_ndx;
438  int xmin, xmax, ymin, ymax;
439 
440  /* Corrected Homogeneity factor */
441  const int homogc = 0 < homog && homog < np ? homog : np;
442  double *px;
443  double *py;
444 
445  /* error handling: test arguments are correct */
446  cpl_ensure(r, CPL_ERROR_NULL_INPUT, NULL);
447  cpl_ensure(np > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
448 
449  list = cpl_bivector_new(np);
450  cpl_ensure(list, CPL_ERROR_NULL_INPUT, NULL);
451  px = cpl_bivector_get_x_data(list);
452  py = cpl_bivector_get_y_data(list);
453 
454  xmin = r[0];
455  xmax = r[1];
456  ymin = r[2];
457  ymax = r[3];
458 
459  min_dist =
460  CPL_MATH_SQRT1_2 * ((xmax - xmin) * (ymax - ymin) / (double) (homogc + 1));
461  gnp = 1;
462  px[0] = 0;
463  py[0] = 0;
464 
465  /* First: generate <homog> points */
466  while(gnp < homogc) {
467  /* Pick a random point within requested range */
468  cand_x = cpl_drand() * (xmax - xmin) + xmin;
469  cand_y = cpl_drand() * (ymax - ymin) + ymin;
470 
471  /* Check the candidate obeys the minimal Poisson distance */
472  ok = 1;
473  for(i = 0; i < gnp; i++) {
474  if(pdist(cand_x, cand_y, px[i], py[i]) < min_dist) {
475  /* does not check Poisson law: reject point */
476  ok = 0;
477  break;
478  }
479  }
480  if(ok) {
481  /* obeys Poisson law: register the point as valid */
482  px[gnp] = cand_x;
483  py[gnp] = cand_y;
484  gnp++;
485  }
486  }
487 
488  /* Iterative process: */
489  /* Pick points out of Poisson distance of the last <homogc-1> points. */
490  start_ndx = 0;
491  while(gnp < np) {
492  /* Pick a random point within requested range */
493  cand_x = cpl_drand() * (xmax - xmin) + xmin;
494  cand_y = cpl_drand() * (ymax - ymin) + ymin;
495 
496  /* Check the candidate obeys the minimal Poisson distance */
497  ok = 1;
498  for(i = 0; i < homogc; i++) {
499  if(pdist(cand_x,
500  cand_y,
501  px[start_ndx + i], py[start_ndx + i]) < min_dist) {
502  /* does not check Poisson law: reject point */
503  ok = 0;
504  break;
505  }
506  }
507  if(ok) {
508  /* obeys Poisson law: register the point as valid */
509  px[gnp] = cand_x;
510  py[gnp] = cand_y;
511  gnp++;
512  }
513  }
514 
515  /* Iterative process: */
516  /* Pick points out of Poisson distance of the last <homogc-1> points. */
517  start_ndx = 0;
518  while(gnp < np) {
519  /* Pick a random point within requested range */
520  cand_x = cpl_drand() * (xmax - xmin) + xmin;
521  cand_y = cpl_drand() * (ymax - ymin) + ymin;
522 
523  /* Check the candidate obeys the minimal Poisson distance */
524  ok = 1;
525  for(i = 0; i < homogc; i++) {
526  if(pdist(cand_x,
527  cand_y,
528  px[start_ndx + i], py[start_ndx + i]) < min_dist) {
529  /* does not check Poisson law: reject point */
530  ok = 0;
531  break;
532  }
533  }
534  if(ok) {
535  /* obeys Poisson law: register the point as valid */
536  px[gnp] = cand_x;
537  py[gnp] = cand_y;
538  gnp++;
539  start_ndx++;
540  }
541  }
542  return list;
543 }
544 
545 
546 /*---------------------------------------------------------------------------*/
547 
548 /*
549  * @brief Retrieve input parameters
550  * @param pipeline_name Input pipeline name
551  * @param recipe_name Input recipe name
552  * @param parlist Input parameter list
553  * @return CPL_ERROR_NONE on success.
554  */
555 
556 /*---------------------------------------------------------------------------*/
557 static cpl_error_code
558 detmon_retrieve_dark_params(const char *pipeline_name,
559  const char *recipe_name,
560  const cpl_parameterlist * parlist)
561 {
562  char *par_name;
563  cpl_parameter *par;
564 
565  /* --ron.method */
566  par_name = cpl_sprintf("%s.%s.ron.method", pipeline_name, recipe_name);
567  assert(par_name != NULL);
568  par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
569  detmon_dark_config.ron_method = cpl_parameter_get_string(par);
570  cpl_free(par_name);
571 
572  /* --dsnu.method */
573  par_name = cpl_sprintf("%s.%s.dsnu.method", pipeline_name, recipe_name);
574  assert(par_name != NULL);
575  par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
576  detmon_dark_config.dsnu_method = cpl_parameter_get_string(par);
577  cpl_free(par_name);
578 
579  /* --opt_nir */
580  par_name = cpl_sprintf("%s.%s.opt_nir", pipeline_name, recipe_name);
581  assert(par_name != NULL);
582  par = cpl_parameterlist_find((cpl_parameterlist *) parlist, par_name);
583  detmon_dark_config.opt_nir = cpl_parameter_get_bool(par);
584  cpl_free(par_name);
585 
586  /* --exts */
587  detmon_dark_config.exts =
588  detmon_retrieve_par_int("exts", pipeline_name, recipe_name,
589  parlist);
590 
591  if(cpl_error_get_code()) {
592  cpl_msg_error(cpl_func, "Failed to retrieve the input parameters");
593  cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
594  }
595 
596 
597  return CPL_ERROR_NONE;
598 }
599 
600 
601 /*---------------------------------------------------------------------------*/
602 
603 /*
604  * @brief Save dark recipe products
605  * @param parlist input parameter list
606  * @param frameset input frameset
607  * @param recipe_name input recipe name
608  * @param pipeline_name input pipeline name
609  * @param procatg_master input procatg of master product
610  * @param procatg_tbl input procatg table
611  * @param procatg_dsnu input procatg dsnu product
612  * @param package input package name
613  * @param masters list of masters products
614  * @param dsnu_table dsnu table product
615  * @param dsnu list of dsnu images products
616  * @param qclist qc parameters
617  * @param flag_sets switch
618  * @param which_set specifier
619  * @param usedframes used frames
620  * @return CPL_ERROR_NONE on success.
621  */
622 
623 /*---------------------------------------------------------------------------*/
624 static cpl_error_code
625 detmon_dark_save(const cpl_parameterlist * parlist,
626  cpl_frameset * frameset,
627  const char *recipe_name,
628  const char *pipeline_name,
629  const char *procatg_master,
630  const char *procatg_tbl,
631  const char *procatg_dsnu,
632  const char *package,
633  cpl_imagelist ** masters,
634  cpl_table ** dsnu_table,
635  cpl_imagelist ** dsnu,
636  cpl_propertylist ** qclist,
637  const int flag_sets,
638  const int which_set,
639  const cpl_frameset * usedframes)
640 {
641 
642  cpl_frame *ref_frame;
643  cpl_propertylist *plist;
644  char *name_o = NULL; /* Avoid (false) uninit warning */
645  int i, j;
646  cpl_propertylist *paflist;
647  cpl_error_code error;
648  int nb_images;
649 
650  /***************************/
651  /* Write the MASTER FITS */
652  /***************************/
653 
654  nb_images = cpl_imagelist_get_size(masters[0]);
655  cpl_ensure_code(nb_images > 0, CPL_ERROR_DATA_NOT_FOUND);
656 
657 
658  for(i = 0; i < nb_images; i++) {
659  /* Set the file name for each image */
660  if(!flag_sets) {
661  name_o =
662  cpl_sprintf("%s_master_dit_%d.fits", recipe_name, i+1);
663  assert(name_o != NULL);
664  } else {
665  name_o =
666  cpl_sprintf("%s_master_dit_%d_set%02d.fits",
667  recipe_name, i, which_set);
668  assert(name_o != NULL);
669  }
670 
671 
672  /* Save the image */
673  if(detmon_dark_config.exts >= 0) {
674  cpl_propertylist * pro_master = cpl_propertylist_new();
675 
676  cpl_propertylist_append_string(pro_master,
677  CPL_DFS_PRO_CATG, procatg_master);
678 
679  cpl_propertylist_append(pro_master, qclist[0]);
680 
681  if(cpl_dfs_save_image
682  (frameset, NULL, parlist, usedframes, NULL,
683  cpl_imagelist_get(*masters, i), CPL_BPP_IEEE_FLOAT,
684  recipe_name, pro_master, NULL, package,
685  name_o)) {
686  cpl_msg_error(cpl_func, "Cannot save the product: %s",
687  name_o);
688  cpl_free(name_o);
689  cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
690 
691  }
692 
693  cpl_propertylist_delete(pro_master);
694  } else {
695  cpl_propertylist * pro_master = cpl_propertylist_new();
696 
697  cpl_propertylist_append_string(pro_master,
698  CPL_DFS_PRO_CATG, procatg_master);
699 
700  cpl_propertylist_append(pro_master, qclist[0]);
701 
702  if(cpl_dfs_save_image(frameset, NULL, parlist, usedframes, NULL,
703  NULL, CPL_BPP_IEEE_FLOAT, recipe_name,
704  pro_master, NULL,
705  package, name_o)) {
706  cpl_msg_error(cpl_func, "Cannot save the product: %s",
707  name_o);
708  cpl_free(name_o);
709  cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
710  }
711 
712  cpl_propertylist_delete(pro_master);
713  for(j = 0; j < detmon_dark_config.nb_extensions; j++) {
714  error =
715  cpl_image_save(cpl_imagelist_get(masters[j], i),
716  name_o, CPL_BPP_IEEE_FLOAT, qclist[j],
717  CPL_IO_EXTEND);
718  cpl_ensure_code(!error, error);
719  }
720  }
721  cpl_free(name_o);
722  }
723 
724  if (detmon_dark_config.opt_nir == OPT) {
725  cpl_propertylist * pro_tbl = cpl_propertylist_new();
726 
727  cpl_propertylist_append_string(pro_tbl,
728  CPL_DFS_PRO_CATG, procatg_tbl);
729 
730  cpl_propertylist_append(pro_tbl, qclist[0]);
731  /*******************************/
732  /* Write the LINEARITY TABLE */
733  /*******************************/
734 
735  /* Set the file name for the table */
736  if(!flag_sets) {
737  name_o = cpl_sprintf("%s_dsnu_table.fits", recipe_name);
738  assert(name_o != NULL);
739  } else {
740  name_o =
741  cpl_sprintf("%s_dsnu_table_set%02d.fits", recipe_name,
742  which_set);
743  assert(name_o != NULL);
744  }
745  /* Save the table */
746  if(cpl_dfs_save_table(frameset, NULL, parlist, usedframes, NULL,
747  dsnu_table[0], NULL, recipe_name, pro_tbl, NULL,
748  package, name_o)) {
749  cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
750  cpl_free(name_o);
751  cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
752  }
753 
754  cpl_propertylist_delete(pro_tbl);
755 
756  if(detmon_dark_config.exts < 0) {
757 
758  for(i = 1; i < detmon_dark_config.nb_extensions; i++) {
759  error =
760  cpl_table_save(dsnu_table[i], NULL, qclist[i], name_o,
761  CPL_IO_EXTEND);
762  cpl_ensure_code(!error, error);
763  }
764  }
765 
766  /* Free */
767  cpl_free(name_o);
768 
769  /***************************/
770  /* Write the DSNU_MAP FITS */
771  /***************************/
772 
773  for(i = 0; i < nb_images; i++) {
774  /* Set the file name for each image */
775  if(!flag_sets) {
776  name_o =
777  cpl_sprintf("%s_dsnu_map_dit_%d.fits", recipe_name, i+1);
778  assert(name_o != NULL);
779  } else {
780  name_o =
781  cpl_sprintf("%s_dsnu_map_dit_%d_set%02d.fits",
782  recipe_name, i, which_set);
783  assert(name_o != NULL);
784  }
785 
786 
787  /* Save the image */
788  if(detmon_dark_config.exts >= 0) {
789  cpl_propertylist * pro_dsnu = cpl_propertylist_new();
790 
791  cpl_propertylist_append_string(pro_dsnu,
792  CPL_DFS_PRO_CATG, procatg_dsnu);
793 
794  cpl_propertylist_append(pro_dsnu, qclist[0]);
795 
796  if(cpl_dfs_save_image
797  (frameset, NULL, parlist, usedframes, NULL,
798  cpl_imagelist_get(*dsnu, i), CPL_BPP_IEEE_FLOAT,
799  recipe_name, pro_dsnu, NULL, package,
800  name_o)) {
801  cpl_msg_error(cpl_func, "Cannot save the product: %s",
802  name_o);
803  cpl_free(name_o);
804  cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
805 
806  }
807 
808  cpl_propertylist_delete(pro_dsnu);
809  } else {
810  cpl_propertylist * pro_dsnu = cpl_propertylist_new();
811 
812  cpl_propertylist_append_string(pro_dsnu,
813  CPL_DFS_PRO_CATG, procatg_dsnu);
814 
815  cpl_propertylist_append(pro_dsnu, qclist[0]);
816 
817  if(cpl_dfs_save_image(frameset, NULL, parlist, usedframes,
818  NULL, NULL,
819  CPL_BPP_IEEE_FLOAT, recipe_name,
820  pro_dsnu, NULL,
821  package, name_o)) {
822  cpl_msg_error(cpl_func, "Cannot save the product: %s",
823  name_o);
824  cpl_free(name_o);
825  cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
826  }
827 
828  cpl_propertylist_delete(pro_dsnu);
829  for(j = 0; j < detmon_dark_config.nb_extensions; j++) {
830  error =
831  cpl_image_save(cpl_imagelist_get(dsnu[j], i),
832  name_o, CPL_BPP_IEEE_FLOAT, qclist[j],
833  CPL_IO_EXTEND);
834  cpl_ensure_code(!error, error);
835  }
836  }
837  cpl_free(name_o);
838  }
839 
840 
841 
842  } /* End of if(OPT) */
843 
844  /*******************************/
845  /* Write the PAF file(s) */
846  /*******************************/
847 
848  /* Get FITS header from reference file */
849  ref_frame = cpl_frameset_get_first(frameset);
850  if((plist = cpl_propertylist_load(cpl_frame_get_filename(ref_frame),
851  0)) == NULL) {
852  cpl_msg_error(cpl_func, "getting header from reference frame");
853  cpl_ensure_code(0, cpl_error_get_code());
854  }
855 
856  /* Get the keywords for the paf file */
857  paflist = cpl_propertylist_new();
858  cpl_propertylist_copy_property_regexp(paflist, plist,
859  "^(ARCFILE|MJD-OBS|ESO TPL ID|"
860  "DATE-OBS|ESO DET DIT|ESO DET NDIT|"
861  "ESO DET NCORRS|"
862  "ESO DET MODE NAME)$", 0);
863 
864  for(i = 0; i < detmon_dark_config.nb_extensions; i++) {
865  cpl_propertylist * c_paflist = cpl_propertylist_duplicate(paflist);
866  error = cpl_propertylist_append(c_paflist, qclist[i]);
867  cpl_ensure_code(!error, error);
868 
869  /* Set the file name for the bpm */
870  if(detmon_dark_config.exts >= 0) {
871  if(!flag_sets) {
872  name_o = cpl_sprintf("%s.paf", recipe_name);
873  assert(name_o != NULL);
874  } else {
875  name_o = cpl_sprintf("%s_set%02d.paf", recipe_name, which_set);
876  assert(name_o != NULL);
877  }
878  } else {
879  if(!flag_sets) {
880  name_o = cpl_sprintf("%s_ext%02d.paf", recipe_name, i+1);
881  assert(name_o != NULL);
882  } else {
883  name_o = cpl_sprintf("%s_set%02d_ext%02d.paf", recipe_name, which_set, i+1);
884  assert(name_o != NULL);
885  }
886  }
887  /* Save the PAF */
888  if(cpl_dfs_save_paf(pipeline_name, recipe_name, c_paflist, name_o)) {
889  cpl_msg_error(cpl_func, "Cannot save the product: %s", name_o);
890  cpl_free(name_o);
891  cpl_propertylist_delete(paflist);
892  cpl_propertylist_delete(plist);
893  cpl_free(name_o);
894  cpl_ensure_code(0, CPL_ERROR_FILE_NOT_CREATED);
895  }
896  cpl_propertylist_delete(c_paflist);
897  cpl_free(name_o);
898  }
899 
900  cpl_propertylist_delete(plist);
901  cpl_propertylist_delete(paflist);
902 
903  return cpl_error_get_code();
904 }
905 
906 void
907 detmon_print_rec_status(void) {
908  if(cpl_error_get_code() != CPL_ERROR_NONE) {
909  cpl_msg_error(cpl_func,"%s",(const char*) cpl_error_get_message());
910  cpl_msg_error(cpl_func,"%s",(const char*) cpl_error_get_where());
911  return;
912  }
913  return;
914 }
915