DETMON Pipeline Reference Manual  1.2.7
detmon_lg_mr.c
1 /*
2  * This file is part of the DETMON Pipeline
3  * Copyright (C) 2013 European Southern Observatory
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
18  */
19 
20 /*
21  * $Author: jtaylor $
22  * $Date: 2013-08-07 09:32:50 $
23  * $Revision: 1.6 $
24  * $Name: not supported by cvs2svn $
25  */
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 
30 /*----------------------------------------------------------------------------
31  Includes
32  ----------------------------------------------------------------------------*/
33 
34 
35 #include "detmon.h"
36 #include "detmon_lg.h"
37 #include "irplib_plugin.h"
38 #include <cpl.h>
39 
40 #include <string.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <assert.h>
44 #include <unistd.h> /* for chdir */
45 #include <sys/stat.h> /* for mkdir */
46 #include <sys/types.h> /* for mkdir */
47 #include <fcntl.h> /* for O_RDWR | S_IRWXU */
48 
49 /*----------------------------------------------------------------------------
50  Functions prototypes
51  ----------------------------------------------------------------------------*/
52 int
53 detmon_lg_dfs_set_groups(cpl_frameset * set,
54  const char *tag_on, const char *tag_off);
55 
58 static const cpl_parameter *
59 get_parameter_const(const cpl_parameterlist * parlist,
60  const char * recipe, const char * name)
61 {
62  char * parname = cpl_sprintf("detmon.%s.%s", recipe, name);
63  const cpl_parameter * p = cpl_parameterlist_find_const(parlist, parname);
64  cpl_free(parname);
65  return p;
66 }
67 
68 static cpl_parameter *
69 get_parameter(cpl_parameterlist * parlist,
70  const char * recipe, const char * name)
71 {
72  char * parname = cpl_sprintf("detmon.%s.%s", recipe, name);
73  cpl_parameter * p = cpl_parameterlist_find(parlist, parname);
74  cpl_free(parname);
75  return p;
76 }
77 
78 int compare_tags(const cpl_frame * a, const cpl_frame * b)
79 {
80  return strcmp(cpl_frame_get_tag(a), cpl_frame_get_tag(b)) == 0;
81 }
82 
83 
84 /* ---------------------------------------------------------------------------*/
95 /* ---------------------------------------------------------------------------*/
96 static cpl_error_code merge_keys(cpl_propertylist * mplist,
97  const cpl_propertylist * plist_,
98  const int pos,
99  const char * rmregexp)
100 {
101  char buffer[256];
102  /* no copy_regex, so duplicate and use erase_regex on the copy */
103  cpl_propertylist * plist = cpl_propertylist_duplicate(plist_);
104 
105  if (rmregexp) {
106  cpl_propertylist_erase_regexp(plist, rmregexp, 0);
107  cpl_propertylist_erase_regexp(mplist, rmregexp, 0);
108  }
109 
110  for (cpl_size i = 0; i < cpl_propertylist_get_size(plist); i++) {
111  cpl_property * p = cpl_propertylist_get(plist, i);
112  const char * key = cpl_property_get_name(p);
113  /* extend all qc keywords by a region prefix */
114  if (!strncmp(key, "ESO QC ", 7)) {
115  sprintf(buffer, "ESO QC REG%d %s", pos, key + 7);
116  cpl_msg_debug(cpl_func, "merging %s to %s", key, buffer);
117  if (cpl_property_get_type(p) == CPL_TYPE_INT) {
118  int val = cpl_property_get_int(p);
119  cpl_propertylist_append_int(mplist, buffer, val);
120  } else if (cpl_property_get_type(p) == CPL_TYPE_DOUBLE) {
121  double val = cpl_property_get_double(p);
122  cpl_propertylist_append_double(mplist, buffer, val);
123  } else if (cpl_property_get_type(p) == CPL_TYPE_STRING) {
124  const char * val = cpl_property_get_string(p);
125  cpl_propertylist_append_string(mplist, buffer, val);
126  }
127  else
128  bug_if(1);
129  const char * com = cpl_property_get_comment(p);
130  cpl_propertylist_set_comment(mplist, buffer, com);
131 
132  /* remove unprefixed key */
133  if (cpl_propertylist_has(mplist, key))
134  cpl_propertylist_erase(mplist, key);
135  }
136  }
137  skip_if(0);
138 
139  end_skip;
140  cpl_propertylist_delete(plist);
141 
142  return cpl_error_get_code();
143 }
144 
145 
146 /* ---------------------------------------------------------------------------*/
154 /* ---------------------------------------------------------------------------*/
155 static cpl_error_code
156 merge_images(cpl_imagelist * mimgl, const cpl_imagelist * img)
157 {
158  cpl_ensure_code(cpl_imagelist_get_size(img) ==
159  cpl_imagelist_get_size(mimgl), CPL_ERROR_ILLEGAL_INPUT);
160  for (int i = 0; i < cpl_imagelist_get_size(img); i++) {
161  cpl_image * mimg = cpl_imagelist_get(mimgl, i);
162  const cpl_image * part = cpl_imagelist_get_const(img, i);
163  cpl_image_add(mimg, part);
164  }
165 
166  return cpl_error_get_code();
167 }
168 
169 
170 /* ---------------------------------------------------------------------------*/
178 /* ---------------------------------------------------------------------------*/
179 static cpl_error_code
180 merge_table(cpl_table * mtab, cpl_table * tab, const int pos)
181 {
182  cpl_table_new_column(tab, "REGION", CPL_TYPE_INT);
183  cpl_table_fill_column_window(tab, "REGION", 0, cpl_table_get_nrow(tab), pos);
184  skip_if(cpl_table_insert(mtab, tab, cpl_table_get_nrow(mtab)));
185 
186  end_skip;
187 
188  return cpl_error_get_code();
189 }
190 
191 
192 static cpl_error_code
193 parse_token(const char * token, cpl_size * llx, cpl_size * lly,
194  cpl_size * urx, cpl_size * ury)
195 {
196  assert(sizeof(*llx) == 8);
197  if (sscanf(token, " %lld , %lld , %lld , %lld ", llx, lly, urx, ury) == 4) {
198  return CPL_ERROR_NONE;
199  }
200  return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
201  "Data not formated correctly: %s; "
202  "expecting: name,llx,lly,urx,ury",
203  token);
204 }
205 
206 
207 /* ---------------------------------------------------------------------------*/
220 /* ---------------------------------------------------------------------------*/
221 static cpl_error_code
222 read_regions_file(const char * filename, cpl_size ** rllx,
223  cpl_size ** rlly, cpl_size ** rurx, cpl_size ** rury,
224  cpl_size * n)
225 {
226  FILE * f = fopen(filename, "r");
227  char line[1024];
228  cpl_size i = 0;
229  cpl_size nmax = 100;
230  if (f == NULL) {
231  return cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
232  "Could not open file %s: %s", filename,
233  strerror(errno));
234  }
235  cpl_msg_info(cpl_func, "Reading %s", filename);
236 
237  *n = 0;
238  *rllx = cpl_malloc(sizeof(**rllx) * nmax);
239  *rlly = cpl_malloc(sizeof(**rllx) * nmax);
240  *rurx = cpl_malloc(sizeof(**rllx) * nmax);
241  *rury = cpl_malloc(sizeof(**rllx) * nmax);
242 
243  while (fgets(line, 1024, f) != NULL) {
244  const size_t nline = strlen(line);
245  if (line[nline - 1] != '\n') {
246  cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
247  "File %s contained too long line",
248  filename);
249  skip_if(0);
250  }
251 
252  if (line[0] == '#')
253  continue;
254 
255  skip_if(parse_token(line, &(*rllx)[i], &(*rlly)[i],
256  &(*rurx)[i], &(*rury)[i]));
257  *n = ++i;
258  if (i >= nmax) {
259  nmax *= 2;
260  *rllx = cpl_realloc(*rllx, sizeof(**rllx) * nmax);
261  *rlly = cpl_realloc(*rlly, sizeof(**rllx) * nmax);
262  *rurx = cpl_realloc(*rurx, sizeof(**rllx) * nmax);
263  *rury = cpl_realloc(*rury, sizeof(**rllx) * nmax);
264  skip_if(0);
265  }
266  }
267 
268  end_skip;
269  if (f)
270  fclose(f);
271  return CPL_ERROR_NONE;
272 }
273 
274 
275 /* ---------------------------------------------------------------------------*/
288 /* ---------------------------------------------------------------------------*/
289 static cpl_error_code
290 read_regions_str(const char * istr, cpl_size ** rllx,
291  cpl_size ** rlly, cpl_size ** rurx, cpl_size ** rury,
292  cpl_size * n)
293 {
294  cpl_size nmax = 100;
295  char * str_ = cpl_strdup(istr);
296  char * str = str_;
297  *n = 0;
298  *rllx = cpl_malloc(sizeof(**rllx) * nmax);
299  *rlly = cpl_malloc(sizeof(**rllx) * nmax);
300  *rurx = cpl_malloc(sizeof(**rllx) * nmax);
301  *rury = cpl_malloc(sizeof(**rllx) * nmax);
302 
303  for (int i = 0; ;i++) {
304  char * token = strtok(str, ":");
305  if (token == NULL)
306  break;
307 
308  skip_if(parse_token(token, &(*rllx)[i], &(*rlly)[i],
309  &(*rurx)[i], &(*rury)[i]));
310  (*n)++;
311  if (i >= nmax - 1) {
312  nmax *= 2;
313  *rllx = cpl_realloc(*rllx, sizeof(**rllx) * nmax);
314  *rlly = cpl_realloc(*rlly, sizeof(**rllx) * nmax);
315  *rurx = cpl_realloc(*rurx, sizeof(**rllx) * nmax);
316  *rury = cpl_realloc(*rury, sizeof(**rllx) * nmax);
317  skip_if(0);
318  }
319  str = NULL;
320  }
321 
322  end_skip;
323  cpl_free(str_);
324 
325  return CPL_ERROR_NONE;
326 }
327 
328 static int check_regions(cpl_size * llx, cpl_size * lly,
329  cpl_size * urx, cpl_size * ury,
330  cpl_size nregions)
331 {
332  for (cpl_size i = 0; i < nregions; i++) {
333  if (llx[i] < 1 || lly < 1 ||
334  urx[i] < 1 || ury[i] < 1) {
335  return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
336  "Region %lld (%lld,%lld,%lld,%lld) has entry smaller one",
337  i, llx[i], lly[i], urx[i], ury[i]);
338  }
339  if ((urx[i] - llx[i] < 1) || (ury[i] - lly[i] < 1)) {
340  return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
341  "Region %lld (%lld,%lld,%lld,%lld) has size zero",
342  i, llx[i], lly[i], urx[i], ury[i]);
343  }
344  for (cpl_size j = i + 1; j < nregions; j++) {
345  if (!(llx[i] > urx[j] || urx[i] < llx[j] ||
346  lly[i] > ury[j] || ury[i] < lly[j])) {
347  return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
348  "Regions %lld (%lld,%lld,%lld,%lld) and "
349  "%lld (%lld,%lld,%lld,%lld) overlap (llx,lly,urx,ury)",
350  i, llx[i], lly[i], urx[i], ury[i],
351  j, llx[j], lly[j], urx[j], ury[j]);
352  }
353  }
354  }
355 
356  return CPL_ERROR_NONE;
357 }
358 
359 
360 /* ---------------------------------------------------------------------------*/
373 /* ---------------------------------------------------------------------------*/
374 static cpl_error_code
375 merge_lingain(cpl_propertylist * mplist[], cpl_table * mtab[],
376  const char * fn, const cpl_size next,
377  const char * rmregexp, int pos)
378 {
379  cpl_errorstate cleanstate = cpl_errorstate_get();
380 
381  {
382  cpl_propertylist * plist = cpl_propertylist_load(fn, 0);
383  skip_if(plist == NULL);
384  if (mplist[0] == NULL) {
385  mplist[0] = plist;
386  merge_keys(mplist[0], plist, pos, rmregexp);
387  }
388  else {
389  merge_keys(mplist[0], plist, pos, rmregexp);
390  cpl_propertylist_delete(plist);
391  }
392  }
393  skip_if(0);
394 
395  for (int e = 1; e < next + 1; e++) {
396  cpl_propertylist * plist = cpl_propertylist_load(fn, e);
397  cpl_table * tab = cpl_table_load(fn, e, 0);
398  /* empty extensions may exist */
399  if (tab == NULL) {
400  cpl_errorstate_set(cleanstate);
401  cpl_propertylist_delete(plist);
402  continue;
403  }
404  skip_if(plist == NULL);
405 
406  if (mplist[e] == NULL) {
407  merge_keys(plist, plist, pos, rmregexp);
408  cpl_table_new_column(tab, "REGION", CPL_TYPE_INT);
409  cpl_table_fill_column_window(tab, "REGION", 0,
410  cpl_table_get_nrow(tab), 1);
411  skip_if(0);
412  mplist[e] = plist;
413  mtab[e] = tab;
414  }
415  else {
416  merge_keys(mplist[e], plist, pos, rmregexp);
417  merge_table(mtab[e], tab, pos);
418  skip_if(0);
419  cpl_propertylist_delete(plist);
420  cpl_table_delete(tab);
421  }
422  skip_if(0);
423  }
424 
425  end_skip;
426 
427  return cpl_error_get_code();
428 }
429 
430 
431 /* ---------------------------------------------------------------------------*/
444 /* ---------------------------------------------------------------------------*/
445 static cpl_error_code
446 merge_bpmcoeff(cpl_propertylist * mplist[], cpl_imagelist * mimg[],
447  const char * fn, const cpl_size next,
448  const char * rmregexp, int pos)
449 {
450  cpl_errorstate cleanstate = cpl_errorstate_get();
451 
452  for (int e = 0; e < next + 1; e++) {
453  cpl_propertylist * plist = cpl_propertylist_load(fn, e);
454  cpl_imagelist * img = cpl_imagelist_load(fn, CPL_TYPE_UNSPECIFIED, e);
455  /* empty extensions may exist */
456  if (img == NULL) {
457  cpl_errorstate_set(cleanstate);
458  cpl_propertylist_delete(plist);
459  continue;
460  }
461  skip_if(plist == NULL);
462 
463  if (mimg[e] == NULL) {
464  merge_keys(plist, plist, pos, rmregexp);
465  mplist[e] = plist;
466  mimg[e] = img;
467  }
468  else {
469  merge_keys(mplist[e], plist, pos, rmregexp);
470  merge_images(mimg[e], img);
471  skip_if(0);
472  cpl_propertylist_delete(plist);
473  cpl_imagelist_delete(img);
474  }
475  skip_if(0);
476  }
477 
478  end_skip;
479 
480  return cpl_error_get_code();
481 }
482 
483 typedef enum {
484  DETMON_BP_MAP_NL_IDX = 0,
485  DETMON_LIN_INFO_IDX = 1,
486  DETMON_GAIN_INFO_IDX = 2,
487  DETMON_BPM_IDX = 3,
488  DETMON_COEFF_IDX = 4,
489  DETMON_MAX_IDX,
490 } detmon_idx;
491 
492 static cpl_error_code disable_contamination(cpl_parameterlist * parlist,
493  const char * recipe)
494 {
495  /* "disable" by setting it to a region of size 1 */
496  cpl_parameter * par;
497  for (int i = 1; i < 6; i++) {
498  char buffer[100];
499  sprintf(buffer, "detmon.%s.llx%d", recipe, i);
500  par = cpl_parameterlist_find(parlist, buffer);
501  cpl_parameter_set_int(par, 1);
502  sprintf(buffer, "detmon.%s.lly%d", recipe, i);
503  par = cpl_parameterlist_find(parlist, buffer);
504  cpl_parameter_set_int(par, 1);
505  sprintf(buffer, "detmon.%s.urx%d", recipe, i);
506  par = cpl_parameterlist_find(parlist, buffer);
507  cpl_parameter_set_int(par, 2);
508  sprintf(buffer, "detmon.%s.ury%d", recipe, i);
509  par = cpl_parameterlist_find(parlist, buffer);
510  cpl_parameter_set_int(par, 2);
511  }
512 
513  return cpl_error_get_code();
514 }
515 
516 /*---------------------------------------------------------------------------*/
517 /*
518  @brief Interpret the command line options and execute the data processing
519  @param frameset the frames list
520  @param parlist the parameters list
521  @return 0 if everything is ok
522  */
523 /*---------------------------------------------------------------------------*/
524 
525 int detmon_lg_mr(cpl_frameset * frameset,
526  const cpl_parameterlist * parlist_,
527  const char * recipe,
528  cpl_boolean is_nir)
529 {
530  const char * tag_on=NULL;
531  const char * tag_off=NULL;
532  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
533  cpl_parameterlist * parlist = (cpl_parameterlist *)parlist_;
534  IRPLIB_DIAG_PRAGMA_POP;
535  cpl_propertylist ** mplist[DETMON_MAX_IDX] = {NULL};
536  cpl_imagelist ** mimg[DETMON_MAX_IDX] = {NULL};
537  cpl_table ** mtab[DETMON_MAX_IDX] = {NULL};
538  cpl_size next[DETMON_MAX_IDX] = {0};
539  char * outname[DETMON_MAX_IDX] = {NULL};
540  char * outtag[DETMON_MAX_IDX] = {NULL};
541 
542  cpl_frameset * frameset_copy = NULL;
543 
544  cpl_propertylist * lintbl =
545  detmon_fill_prolist("DET_LIN_INFO", "REDUCED", "TECH", CPL_FALSE);
546 
547  cpl_propertylist * gaintbl =
548  detmon_fill_prolist("GAIN_INFO", "REDUCED", "TECH", CPL_FALSE);
549 
550  cpl_propertylist * coeffscube =
551  detmon_fill_prolist("COEFFS_CUBE", "REDUCED", "TECH", CPL_FALSE);
552 
553  cpl_propertylist * bpm =
554  detmon_fill_prolist("BP_MAP_NL", "REDUCED", "TECH", CPL_FALSE);
555 
556  cpl_propertylist * corr =
557  detmon_fill_prolist("AUTOCORR", "REDUCED", "TECH", CPL_FALSE);
558 
559  cpl_propertylist * diff_flat =
560  detmon_fill_prolist("DIFF_FLAT", "REDUCED", "TECH", CPL_FALSE);
561  const char * regions_file = NULL;
562  const char * regions_str = NULL;
563  cpl_error_code error = CPL_ERROR_NONE;
564  cpl_size * llx = NULL, * lly = NULL, * urx = NULL, * ury = NULL;
565  const cpl_parameter * p =
566  get_parameter_const(parlist, recipe, "regions-file");
567  if (p) {
568  regions_file = cpl_parameter_get_string(p);
569  if (strlen(regions_file) == 0)
570  regions_file = NULL;
571  }
572 
573  p = get_parameter_const(parlist, recipe, "regions");
574  if (p) {
575  regions_str = cpl_parameter_get_string(p);
576  if (strlen(regions_str) == 0)
577  regions_str = NULL;
578  else {
579  if (regions_file) {
580  return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
581  "Please only provide either "
582  "--regions or "
583  "--regions-file, not both");
584  }
585  }
586  }
587 
588  p = get_parameter(parlist, recipe, "intermediate");
589  if (p) {
590  error_if(cpl_parameter_get_bool(p), CPL_ERROR_UNSUPPORTED_MODE,
591  "--intermediate not supported by multi region recipe");
592  }
593 
594  skip_if (detmon_lg_set_tag(frameset, &tag_on, &tag_off));
595  skip_if (detmon_lg_dfs_set_groups(frameset, tag_on, tag_off));
596 
597  cpl_size nregions = 0;
598  if (regions_file) {
599  read_regions_file(regions_file, &llx, &lly, &urx, &ury,
600  &nregions);
601  }
602  else if (regions_str) {
603  read_regions_str(regions_str, &llx, &lly, &urx, &ury,
604  &nregions);
605  }
606  else {
607  return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
608  "Empty value in --regions or "
609  "--regions-file");
610  }
611  skip_if(0);
612 
613  skip_if(check_regions(llx, lly, urx, ury, nregions));
614 
615  if (!is_nir) {
616  /* contamination not supported */
617  skip_if(disable_contamination(parlist, recipe));
618  }
619 
620  /* additionally iterating on extensions would improve IO caching and
621  * reduce memory usage for pix2pix but requires checks if detmon really
622  * gives the same result in this case */
623  for (int i = 0; i < nregions; i++) {
624  cpl_parameter * par;
625  par = get_parameter(parlist, recipe, "llx");
626  cpl_parameter_set_int(par, llx[i]);
627  par = get_parameter(parlist, recipe, "lly");
628  cpl_parameter_set_int(par, lly[i]);
629  par = get_parameter(parlist, recipe, "urx");
630  cpl_parameter_set_int(par, urx[i]);
631  par = get_parameter(parlist, recipe, "ury");
632  cpl_parameter_set_int(par, ury[i]);
633  cpl_msg_info(cpl_func, "working on region %d/%d: %lld %lld, "
634  "%lld %lld", i + 1, (int)nregions,
635  llx[i], lly[i], urx[i], ury[i]);
636 
637  /* copy and throw away so we don't accumulate products
638  * for each call */
639  cpl_frameset_delete(frameset_copy);
640  frameset_copy = cpl_frameset_duplicate(frameset);
641  error = detmon_lg(frameset_copy,
642  parlist,
643  tag_on,
644  tag_off,
645  recipe,
646  PACKAGE_TARNAME,
647  REGEXP,
648  lintbl, gaintbl, coeffscube,
649  bpm, corr, diff_flat,
650  PACKAGE "/" PACKAGE_VERSION,
651  NULL, NULL, is_nir);
652  skip_if(error);
653 
654  cpl_size nlabels;
655  cpl_size * selection = cpl_frameset_labelise(frameset_copy,
656  compare_tags, &nlabels);
657 
658  for (cpl_size j = 0; j < nlabels; j++) {
659  cpl_frameset * a = cpl_frameset_extract(frameset_copy, selection, j);
660  cpl_frame * frm = cpl_frameset_get_first(a);
661  const char * fn = cpl_frame_get_filename(frm);
662  int idx;
663 
664  if (strcmp(cpl_frame_get_tag(frm), "DET_LIN_INFO") == 0)
665  idx = DETMON_LIN_INFO_IDX;
666  else if (strcmp(cpl_frame_get_tag(frm), "GAIN_INFO") == 0)
667  idx = DETMON_GAIN_INFO_IDX;
668  else if (strcmp(cpl_frame_get_tag(frm), "BP_MAP_NL") == 0)
669  idx = DETMON_BPM_IDX;
670  else if (strcmp(cpl_frame_get_tag(frm), "COEFFS_CUBE") == 0)
671  idx = DETMON_COEFF_IDX;
672  else {
673  cpl_frameset_delete(a);
674  continue;
675  }
676 
677  /* prepare storage */
678  if (mplist[idx] == NULL) {
679  next[idx] = cpl_frame_get_nextensions(frm);
680  outname[idx] = cpl_strdup(cpl_frame_get_filename(frm));
681  outtag[idx] = cpl_strdup(cpl_frame_get_tag(frm));
682  mplist[idx] = cpl_calloc(sizeof(cpl_propertylist*), next[idx] + 1);
683  mimg[idx] = cpl_calloc(sizeof(cpl_imagelist*), next[idx] + 1);
684  mtab[idx] = cpl_calloc(sizeof(cpl_table*), next[idx] + 1);
685  }
686 
687  if (idx == DETMON_LIN_INFO_IDX) {
688  const char * rmregexp = "ESO QC CONTAM.*";
689  merge_lingain(mplist[idx], mtab[idx], fn, next[idx],
690  rmregexp, i + 1);
691  }
692  else if (idx == DETMON_GAIN_INFO_IDX) {
693  merge_lingain(mplist[idx], mtab[idx], fn, next[idx],
694  NULL, i + 1);
695  }
696  else if (idx == DETMON_BPM_IDX) {
697  merge_bpmcoeff(mplist[idx], mimg[idx], fn, next[idx],
698  NULL, i + 1);
699  }
700  else if (idx == DETMON_COEFF_IDX) {
701  merge_bpmcoeff(mplist[idx], mimg[idx], fn, next[idx],
702  NULL, i + 1);
703  }
704  skip_if(0);
705 
706  for (int e = 0; e < next[idx] + 1; e++) {
707  char buffer[300];
708  cpl_propertylist * copy_plist;
709  cpl_size len;
710  if (!mplist[idx][e])
711  continue;
712 
713  /* don't add region if extension has no QC parameters */
714  copy_plist = cpl_propertylist_duplicate(mplist[idx][e]);
715  cpl_propertylist_erase_regexp(copy_plist, "ESO QC.*", 1);
716  len = cpl_propertylist_get_size(copy_plist);
717  cpl_propertylist_delete(copy_plist);
718  if (len == 0) {
719  continue;
720  }
721 
722  sprintf(buffer, "ESO QC REG%d LLX", i + 1);
723  cpl_propertylist_append_int(mplist[idx][e], buffer, llx[i]);
724  cpl_propertylist_set_comment(mplist[idx][e], buffer,
725  "Lower left X of region");
726  sprintf(buffer, "ESO QC REG%d LLY", i + 1);
727  cpl_propertylist_append_int(mplist[idx][e], buffer, lly[i]);
728  cpl_propertylist_set_comment(mplist[idx][e], buffer,
729  "Lower left Y of region");
730  sprintf(buffer, "ESO QC REG%d URX", i + 1);
731  cpl_propertylist_append_int(mplist[idx][e], buffer, urx[i]);
732  cpl_propertylist_set_comment(mplist[idx][e], buffer,
733  "Upper right X of region");
734  sprintf(buffer, "ESO QC REG%d URY", i + 1);
735  cpl_propertylist_append_int(mplist[idx][e], buffer, ury[i]);
736  cpl_propertylist_set_comment(mplist[idx][e], buffer,
737  "Upper right Y of region");
738  }
739 
740  cpl_frameset_delete(a);
741  skip_if(0);
742  }
743  cpl_free(selection);
744  }
745 
746  {
747  cpl_frameset * usedframes = cpl_frameset_new();
748 
749  for (cpl_frame *frm = cpl_frameset_get_first(frameset); frm != NULL;
750  frm = cpl_frameset_get_next(frameset)) {
751  if (cpl_frame_get_group(frm) == CPL_FRAME_GROUP_RAW ||
752  cpl_frame_get_group(frm) == CPL_FRAME_GROUP_CALIB) {
753  cpl_frameset_insert(usedframes, cpl_frame_duplicate(frm));
754  }
755  }
756  for (int idx = 0; idx < DETMON_MAX_IDX; idx++) {
757  if (!mplist[idx])
758  continue;
759  if (idx == DETMON_LIN_INFO_IDX || idx == DETMON_GAIN_INFO_IDX) {
760  irplib_dfs_save_propertylist(frameset, parlist, usedframes,
761  recipe, outtag[idx],
762  mplist[idx][0], NULL, PACKAGE "/"
763  PACKAGE_VERSION, outname[idx]);
764  skip_if(0);
765  for (cpl_size e = 1; e < next[idx] + 1; e++) {
766  if (mtab[idx][e]) {
767  cpl_table_save(mtab[idx][e], NULL, mplist[idx][e],
768  outname[idx], CPL_IO_EXTEND);
769  }
770  else {
771  cpl_propertylist_save(mplist[idx][e], outname[idx],
772  CPL_IO_EXTEND);
773  }
774  skip_if(0);
775  }
776  }
777  else if (idx == DETMON_BPM_IDX) {
778  if (!mimg[idx][0]) {
779  irplib_dfs_save_propertylist(frameset, parlist, usedframes,
780  recipe, outtag[idx],
781  mplist[idx][0], NULL, PACKAGE
782  "/" PACKAGE_VERSION,
783  outname[idx]);
784  }
785  else {
786  irplib_dfs_save_image(frameset, parlist, usedframes,
787  cpl_imagelist_get(mimg[idx][0], 0),
788  CPL_TYPE_UNSPECIFIED, recipe,
789  outtag[idx], mplist[idx][0], NULL,
790  PACKAGE "/" PACKAGE_VERSION,
791  outname[idx]);
792  cpl_imagelist_delete(mimg[idx][0]);
793  mimg[idx][0] = NULL;
794  }
795  skip_if(0);
796  for (cpl_size e = 1; e < next[idx] + 1; e++) {
797  if (mimg[idx][e]) {
798  cpl_image * img = cpl_imagelist_get(mimg[idx][e], 0);
799  cpl_image_save(img, outname[idx], CPL_TYPE_UNSPECIFIED,
800  mplist[idx][e],
801  CPL_IO_EXTEND);
802  cpl_imagelist_delete(mimg[idx][e]);
803  mimg[idx][e] = NULL;
804  }
805  else {
806  cpl_propertylist_save(mplist[idx][e], outname[idx],
807  CPL_IO_EXTEND);
808  }
809  skip_if(0);
810  }
811  }
812  else if (idx == DETMON_COEFF_IDX) {
813  if (!mimg[idx][0]) {
814  irplib_dfs_save_propertylist(frameset, parlist, usedframes,
815  recipe, outtag[idx],
816  mplist[idx][0], NULL, PACKAGE
817  "/" PACKAGE_VERSION,
818  outname[idx]);
819  }
820  else {
821  irplib_dfs_save_imagelist(frameset, parlist, usedframes,
822  mimg[idx][0],
823  CPL_TYPE_UNSPECIFIED,
824  recipe, outtag[idx],
825  mplist[idx][0], NULL, PACKAGE "/"
826  PACKAGE_VERSION, outname[idx]);
827  cpl_imagelist_delete(mimg[idx][0]);
828  mimg[idx][0] = NULL;
829  }
830  skip_if(0);
831  for (cpl_size e = 1; e < next[idx] + 1; e++) {
832  if (mimg[idx][e]) {
833  cpl_imagelist_save(mimg[idx][e], outname[idx],
834  CPL_TYPE_UNSPECIFIED,
835  mplist[idx][e], CPL_IO_EXTEND);
836  cpl_imagelist_delete(mimg[idx][e]);
837  mimg[idx][e] = NULL;
838  }
839  else {
840  cpl_propertylist_save(mplist[idx][e], outname[idx],
841  CPL_IO_EXTEND);
842  }
843  skip_if(0);
844  }
845  }
846  }
847 
848  cpl_frameset_delete(usedframes);
849  skip_if(0);
850  }
851 
852  end_skip;
853  cpl_frameset_delete(frameset_copy);
854  cpl_propertylist_delete(lintbl);
855  cpl_propertylist_delete(gaintbl);
856  cpl_propertylist_delete(coeffscube);
857  cpl_propertylist_delete(bpm);
858  cpl_propertylist_delete(corr);
859  cpl_propertylist_delete(diff_flat);
860  for (int i = 0; i < DETMON_MAX_IDX; i++) {
861  for (int e = 0; e < next[i] + 1; e++) {
862  if (mplist[i])
863  cpl_propertylist_delete(mplist[i][e]);
864  if (mimg[i])
865  cpl_imagelist_delete(mimg[i][e]);
866  if (mtab[i])
867  cpl_table_delete(mtab[i][e]);
868  }
869  cpl_free(mplist[i]);
870  cpl_free(mimg[i]);
871  cpl_free(mtab[i]);
872  cpl_free(outname[i]);
873  cpl_free(outtag[i]);
874  }
875  cpl_free(llx);
876  cpl_free(lly);
877  cpl_free(urx);
878  cpl_free(ury);
879 
880  return cpl_error_get_code();
881 }