VISIR Pipeline Reference Manual  4.1.0
visir_inputs.c
1 /* $Id: visir_inputs.c,v 1.238 2013-09-24 10:46:00 jtaylor Exp $
2  *
3  * This file is part of the VISIR Pipeline
4  * Copyright (C) 2002,2003,2013,2014 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 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 /*-----------------------------------------------------------------------------
26  Includes
27  -----------------------------------------------------------------------------*/
28 
29 
30 #include "visir_utils.h"
31 #include "visir_pfits.h"
32 #include "visir_parameter.h"
33 #include "visir_pfits.h"
34 #include "visir_spc_distortion.h"
35 #include "visir_inputs.h"
36 #include "irplib_framelist.h"
37 #include "irplib_wcs.h"
38 #include <cpl.h>
39 
40 #include <string.h>
41 #include <math.h>
42 #include <float.h>
43 #include <assert.h>
44 
45 /*-----------------------------------------------------------------------------
46  Define
47  -----------------------------------------------------------------------------*/
48 
49 #define VISIR_SECS_PER_DAY (24 * 3600)
50 
51 #define FIND_BEAM_MAX_APERTURES_SQR (40 * 40)
52 
53 
54 /*-----------------------------------------------------------------------------
55  Functions prototypes
56  -----------------------------------------------------------------------------*/
57 
58 #include "visir_destripe.h"
59 
60 static cpl_image * visir_load_average(const char *,
61  const cpl_propertylist *);
62 static cpl_imagelist * visir_load_intermint(const irplib_framelist *, int);
63 static cpl_error_code visir_imagelist_unpack_interm(cpl_imagelist *);
64 static cpl_error_code visir_rem_glitch(cpl_image *);
65 static cpl_error_code visir_rem_bad_images(cpl_imagelist *);
66 static cpl_error_code visir_offset_hcycle(cpl_image *);
67 
68 static cpl_image ** visir_img_collapse_beam_four(cpl_propertylist *,
69  const cpl_image *,
70  const cpl_image *,
71  double, double, double,
72  const cpl_propertylist *);
73 
74 static cpl_image ** visir_img_collapse_beam_three(cpl_propertylist *,
75  const cpl_image *,
76  const cpl_image *,
77  double, double,
78  const cpl_propertylist *);
79 
80 
81 static cpl_error_code visir_img_find_beam_three(cpl_propertylist *,
82  const cpl_image *,
83  const cpl_image *,
84  double, double, double,
85  double *,
86  double *);
87 
88 static cpl_error_code visir_img_find_beam_four(cpl_propertylist *,
89  const cpl_image *,
90  const cpl_image *,
91  double, double, double,
92  double *,
93  double *);
94 
95 
96 /*----------------------------------------------------------------------------*/
100 /*----------------------------------------------------------------------------*/
101 
105 /*----------------------------------------------------------------------------*/
118 /*----------------------------------------------------------------------------*/
119 
120 static
121 cpl_error_code visir_load_cube2_split_(cpl_imagelist * alist, cpl_imagelist * blist,
122  cpl_imagelist * packed, cpl_image * prevd)
123 {
124 
125  int naxis3 = cpl_imagelist_get_size(packed);
126  int prevd_insert_pos = cpl_imagelist_get_size(blist);
127 
128  bug_if(alist == NULL);
129  bug_if(blist == NULL);
130 
131 
132  if (prevd)
133  cpl_imagelist_set(blist, prevd, prevd_insert_pos);
134 
135  /* Split the cube */
136  for (int i = 0; i < naxis3/2; i++) {
137  cpl_image * aimage = cpl_imagelist_unset(packed, 0); /* Ai */
138  cpl_image * dimage = cpl_imagelist_unset(packed, 0); /* Ai - Bi mean */
139 
140  cpl_imagelist_set(alist, aimage, cpl_imagelist_get_size(alist));
141  cpl_imagelist_set(blist, dimage, cpl_imagelist_get_size(blist));
142 
143  }
144 
145  skip_if_lt(0, cpl_imagelist_get_size(packed), "Too many packed frames");
146 
147  /* After this call the ith image in the list is Ai - Bi */
148  bug_if(visir_imagelist_unpack_interm(blist));
149 
150  /* remove extra image again */
151  if (prevd)
152  cpl_imagelist_unset(blist, prevd_insert_pos);
153 
154  for (int i = 0; i < naxis3/2; i++) {
155  cpl_image * aimage = cpl_imagelist_get(alist, i);
156  cpl_image * dimage = cpl_imagelist_get(blist, i);
157  /* Compute Bi from Ai - (Ai - Bi). */
158  cpl_image * bimage = cpl_image_subtract_create(aimage, dimage);
159 
160  visir_offset_hcycle(aimage);
161  visir_offset_hcycle(bimage);
162  /* Deallocate the difference image and set Bi in its place */
163  bug_if(cpl_imagelist_set(blist, bimage, i));
164 
165  }
166 
167  skip_if_lt(0, cpl_imagelist_get_size(packed), "Too many packed frames");
168 
169  end_skip;
170 
171  return cpl_error_get_code();
172 
173 }
174 
175 
176 /*----------------------------------------------------------------------------*/
188 /*----------------------------------------------------------------------------*/
189 static cpl_imagelist * load_range(const char * filename,
190  cpl_type im_type,
191  int iext,
192  int pstart,
193  int pend)
194 {
195  int selfsize = 0;
196  cpl_imagelist * self = cpl_imagelist_new();
197 
198  for (int iplane = pstart; iplane < pend; iplane++) {
199  cpl_image * image = cpl_image_load(filename, im_type, iplane, iext);
200 
201  if (image == NULL) break;
202 
203  if (cpl_imagelist_set(self, image, selfsize)) {
204  cpl_image_delete(image);
205  break;
206  }
207 
208  selfsize++;
209  }
210  return self;
211 }
212 
213 
214 /*----------------------------------------------------------------------------*/
237 /*----------------------------------------------------------------------------*/
238 cpl_error_code visir_load_cube2_split(cpl_imagelist * alist, cpl_imagelist * blist,
239  const irplib_framelist * rawframes, int pos,
240  const int planestart, const int planeend)
241 {
242 
243  cpl_imagelist * packed = NULL;
244  const cpl_frame * frame = irplib_framelist_get_const(rawframes, pos);
245  const char * file = cpl_frame_get_filename(frame);
246  int naxis3;
247  visir_data_type data_type;
248  int pend;
249  cpl_image * prevd = NULL;
250  const cpl_propertylist * plist =
252  const int iext = cpl_propertylist_has(plist, "ZNAXIS3") ? 1 : 0;
253  const int nchop = plist ? visir_pfits_get_chop_ncycles(plist) : -1;
254 
255  skip_if(0);
256 
257  skip_if(visir_get_data_type(frame, plist, &data_type, NULL));
258 
259  cpl_ensure_code(data_type == VISIR_DATA_CUBE2, CPL_ERROR_ILLEGAL_INPUT);
260 
261  naxis3 = visir_pfits_get_naxis3(plist);
262 
263  /* both ranges must be even except for the last chunk where it must
264  * be uneven for the average image at the end of the CUBE2 file */
265  error_if(planestart % 2 == 1, CPL_ERROR_ILLEGAL_INPUT,
266  "Plane start %d wrong. It must be even.", planestart);
267  error_if(planeend < naxis3 && planeend % 2 == 1, CPL_ERROR_ILLEGAL_INPUT,
268  "Plane end %d wrong. It must be even if not larger naxis3=%d",
269  planeend, naxis3);
270 
271  any_if("Cannot split non-CUBE2 frame %d/%d in %s", 1+pos,
272  irplib_framelist_get_size(rawframes), file);
273  bug_if(alist == NULL);
274  bug_if(blist == NULL);
275 
276  error_if(data_type != VISIR_DATA_CUBE2, CPL_ERROR_INCOMPATIBLE_INPUT,
277  "Cannot split non-CUBE2 frame %d/%d w. NAXIS3=%d, "
278  "NCYCLES=%d in %s", 1+pos,
279  irplib_framelist_get_size(rawframes), naxis3, nchop, file);
280 
281  if (planeend >= naxis3 || planeend < 0)
282  pend = naxis3 - 1; /* don't load sum */
283  else
284  pend = planeend;
285 
286  if (planestart != 0) {
287  packed = load_range(file, CPL_TYPE_UNSPECIFIED,
288  iext, planestart - 1, pend);
289  prevd = cpl_imagelist_unset(packed, 0);
290  }
291  else
292  packed = load_range(file, CPL_TYPE_UNSPECIFIED, iext, planestart, pend);
293 
294  skip_if(visir_load_cube2_split_(alist, blist, packed, prevd));
295 
296  end_skip;
297 
298  cpl_image_delete(prevd);
299  cpl_imagelist_delete(packed);
300 
301  return cpl_error_get_code();
302 }
303 
304 
305 /*----------------------------------------------------------------------------*/
322 /*----------------------------------------------------------------------------*/
323 static
324 cpl_error_code visir_load_burst_(cpl_imagelist * alist, cpl_imagelist * blist,
325  cpl_imagelist * packed,
326  const int ichopchange, const int ihalfcycle,
327  const int trimlow, int const trimhigh)
328 {
329  cpl_boolean bon = CPL_TRUE;
330  const int offset = 0;
331  const int pend = cpl_imagelist_get_size(packed);
332  int lorej = ihalfcycle - trimlow;
333  int hirej = trimhigh + 1; /* +1 to reject the actual changing frame too */
334 
335  /* get frame where chopper moved from on to off in the _previous_
336  * half exposure */
337  int chpmv = ichopchange - ihalfcycle * 2;
338 
339  cpl_ensure_code(trimhigh >= -1, CPL_ERROR_ILLEGAL_INPUT);
340  cpl_ensure_code(trimlow >= 0, CPL_ERROR_ILLEGAL_INPUT);
341  cpl_ensure_code(ichopchange < ihalfcycle * 2, CPL_ERROR_ILLEGAL_INPUT);
342  cpl_ensure_code(alist != NULL, CPL_ERROR_NULL_INPUT);
343  cpl_ensure_code(blist != NULL, CPL_ERROR_NULL_INPUT);
344 
345  skip_if(0);
346 
347  /* Split the cube and skip the planes where the chopper
348  * changes +- a safety margin
349  * always starts from on -> off movement even if negative */
350  for (int i = chpmv; i < pend; i++) {
351  if ((i + ihalfcycle * 2) % ihalfcycle == ichopchange % ihalfcycle) {
352  bon = !bon;
353  hirej = trimhigh + 1;
354  lorej = ihalfcycle - trimlow;
355  }
356  if (hirej <= 0 && lorej > 0 && i >= 0) {
357  if (bon) {
358  cpl_image * image= cpl_imagelist_unset(packed, offset);
359  cpl_imagelist_set(alist, image, cpl_imagelist_get_size(alist));
360  }
361  else {
362  cpl_image * image= cpl_imagelist_unset(packed, offset);
363  cpl_imagelist_set(blist, image, cpl_imagelist_get_size(blist));
364  }
365  }
366  else if (i >= 0)
367  cpl_image_delete(cpl_imagelist_unset(packed, offset));
368 
369  hirej--;
370  lorej--;
371  }
372 
373  cpl_msg_info(cpl_func, "On: %d, Off %d, Skipped %d",
374  (int)cpl_imagelist_get_size(alist),
375  (int)cpl_imagelist_get_size(blist),
376  (int)(pend - cpl_imagelist_get_size(alist)
377  - cpl_imagelist_get_size(blist)));
378 
379  skip_if_lt(0, cpl_imagelist_get_size(packed), "Too many packed frames");
380 
381  end_skip;
382 
383  return cpl_error_get_code();
384 }
385 
386 
387 /*----------------------------------------------------------------------------*/
396 /*----------------------------------------------------------------------------*/
397 static int get_to_off_plane(int chopchange, const int offset,
398  const int ihalfcycle)
399 {
400  const int ifullcycle = ihalfcycle * 2;
401  /* if offset in second hcycle beginning on/off state is flipped */
402  if (offset -
403  (offset / ifullcycle) * ifullcycle > chopchange)
404  chopchange += ifullcycle - (offset % ifullcycle);
405  else
406  chopchange -= (offset % ifullcycle);
407  return chopchange;
408 }
409 
410 
411 /*----------------------------------------------------------------------------*/
432 /*----------------------------------------------------------------------------*/
433 cpl_error_code visir_load_burst(cpl_imagelist * alist, cpl_imagelist * blist,
434  const cpl_frame * frame, const cpl_propertylist * plist,
435  const int ichopchange, const int ihalfcycle,
436  const int planestart, const int planeend,
437  const int trimlow, const int trimhigh)
438 {
439  const char * file = cpl_frame_get_filename(frame);
440  const int naxis3 = visir_pfits_get_naxis3(plist);
441  const int pend = planeend <= 0 || planeend > naxis3 ? naxis3 : planeend;
442  cpl_imagelist * packed;
443  cpl_boolean chop_on = visir_pfits_get_chop_stat(plist);
444  int to_off = chop_on ? get_to_off_plane(ichopchange, planestart, ihalfcycle) : 0;
445 
446  cpl_msg_info(cpl_func, "Loading planes %d to %d, to off %d",
447  planestart, pend, planestart + to_off);
448 
449  packed = load_range(file, CPL_TYPE_UNSPECIFIED,
450  cpl_propertylist_has(plist, "ZNAXIS3") ? 1 : 0,
451  planestart, pend);
452 
453  skip_if(packed == NULL);
454  /* we don't need the input in the page cache anymore */
455  if (cpl_imagelist_get_size(packed) > 0) {
456  cpl_image * timg = cpl_imagelist_get(packed, 0);
457  size_t nbytes = visir_get_nbytes(timg);
458  visir_drop_cache(file, 0, nbytes * pend);
459  }
460 
461  if (chop_on == CPL_FALSE) {
462  for (cpl_size i = 0; i < cpl_imagelist_get_size(packed); i++)
463  cpl_imagelist_set(alist, cpl_imagelist_get(packed, i), i);
464 
465  for (cpl_size i = cpl_imagelist_get_size(packed); i-- > 0;)
466  cpl_imagelist_unset(packed, i);
467  skip_if(0);
468  }
469  else
470  skip_if(visir_load_burst_(alist, blist, packed, to_off, ihalfcycle,
471  trimlow, trimhigh));
472 
473  end_skip;
474 
475  cpl_imagelist_delete(packed);
476 
477  return cpl_error_get_code();
478 }
479 
480 
481 /*----------------------------------------------------------------------------*/
503 /*----------------------------------------------------------------------------*/
504 cpl_image ** visir_img_collapse_beam(cpl_propertylist * qclist,
505  const cpl_image * self,
506  const cpl_parameterlist * parlist,
507  const char * recipename,
508  visir_chopnod_mode mode,
509  const cpl_propertylist * plist)
510 {
511 
512  cpl_image ** combined = NULL;
513  /* Need to invert the negative beams */
514  cpl_image * inverse = cpl_image_multiply_scalar_create(self, -1.0);
515 
516  const double eccmax = visir_parameterlist_get_double(parlist, recipename,
517  VISIR_PARAM_ECCMAX);
518 
519  /* Get the chopping throw in pixels */
520  const double pscale = visir_pfits_get_pixscale(plist);
521  const double pthrow = pscale > 0.0
522  ? visir_pfits_get_chop_throw(plist) / pscale : 0.0;
523  double angle = visir_pfits_get_chop_posang(plist);
524 
525  skip_if(self == NULL);
526  skip_if(parlist == NULL);
527  skip_if(qclist == NULL);
528  skip_if(plist == NULL);
529 
530  if (mode == VISIR_CHOPNOD_PERPENDICULAR) {
531  /* 4 sources */
532  combined = visir_img_collapse_beam_four(qclist, self, inverse, eccmax,
533  pthrow, angle, plist);
534  } else if (mode == VISIR_CHOPNOD_PARALLEL) {
535  /* 3 sources */
536  combined = visir_img_collapse_beam_three(qclist, self, inverse, eccmax,
537  pthrow, plist);
538  } else if (mode == VISIR_CHOPNOD_AUTO) {
539  cpl_errorstate cleanstate = cpl_errorstate_get();
540 
541  const char * sdir = visir_pfits_get_chopnod_dir(plist);
542 
543  if (sdir != NULL && !strcmp(sdir, "PERPENDICULAR")) {
544  /* 4 sources */
545  combined = visir_img_collapse_beam_four(qclist, self, inverse,
546  eccmax, pthrow, angle, plist);
547  } else if (sdir != NULL && !strcmp(sdir, "PARALLEL")) {
548  /* 3 sources */
549  combined = visir_img_collapse_beam_three(qclist, self, inverse,
550  eccmax, pthrow, plist);
551  } else {
552  if (sdir == NULL) {
553  visir_error_reset("Could not get FITS key");
554  } else {
555  cpl_msg_warning(cpl_func, "Unknown chopping direction: %s",
556  sdir);
557  }
558  cpl_msg_warning(cpl_func, "Proceeding as if FITS card "
559  VISIR_PFITS_STRING_CHOPNOD_DIR " had value: %s",
560  "PERPENDICULAR");
561  combined = visir_img_collapse_beam_four(qclist, self, inverse,
562  eccmax, pthrow, angle, plist);
563  if (combined == NULL) {
564  visir_error_reset("Proceeding as if FITS card "
565  VISIR_PFITS_STRING_CHOPNOD_DIR
566  " had value: %s", "PARALLEL");
567  combined = visir_img_collapse_beam_three(qclist, self, inverse,
568  eccmax, pthrow, plist);
569  }
570  }
571  } else {
572  bug_if(1);
573  }
574 
575  skip_if(combined == NULL);
576 
577  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM THROW",
578  pthrow));
579  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM THROW",
580  "The throw in pixels (TEL CHOP THROW "
581  "divided by INS PFOV)"));
582 
583  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM XPOS",
584  "The X pixel position (centroid) "
585  "of the one-beam object"));
586 
587  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM YPOS",
588  "The Y pixel position (centroid) "
589  "of the one-beam object"));
590  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM ECCENTRICITY",
591  "Eccentricity: 0 for perfect, throw-"
592  "sized square/line"));
593 
594  end_skip;
595 
596  cpl_image_delete(inverse);
597 
598  return combined;
599 }
600 
601 
602 /*----------------------------------------------------------------------------*/
685 /*----------------------------------------------------------------------------*/
686 cpl_imagelist * visir_inputs_combine(const char * recipename,
687  const cpl_parameterlist * parlist,
688  const irplib_framelist * rawframes,
689  const char * badpix,
690  const char * flat,
691  int * nodding_p,
692  cpl_boolean do_spc_fix,
693  double wlen,
694  visir_spc_resol resol)
695 {
696  const char * fnodpos;
697  int nfiles;
698  cpl_imagelist * in = NULL;
699  cpl_image * collapsed = NULL;
700  cpl_image * prev = NULL;
701  cpl_vector * nods_vec = NULL;
702  double * nods_data;
703  int * nod_pos = NULL;
704  cpl_image ** images = NULL;
705  cpl_imagelist * nodded = NULL;
706  int nnod;
707  cpl_image * flat_image = NULL;
708  cpl_image * bpm_im_int = NULL;
709  cpl_mask * bpm_im_bin = NULL;
710  cpl_imagelist * hcycle = NULL;
711  cpl_boolean no_rem;
712  cpl_boolean is_nodding = CPL_FALSE;
713  int i, j;
714  cpl_boolean auto_bpm, rem_glitch, rem_bad;
715  int ndestripe;
716  int naxis1, naxis2;
717  cpl_boolean morpho_destripe;
718  double tstart, tstop;
719  const cpl_propertylist * plist1;
720 #ifdef _OPENMP
721  cpl_errorstate cleanstate = cpl_errorstate_get();
722 #endif
723  cpl_error_code didfail = CPL_ERROR_NONE;
724 
725 
726  skip_if (0);
727  skip_if(recipename == NULL);
728  skip_if(parlist == NULL);
729  skip_if(rawframes == NULL);
730 
731  /* Get the number of files */
732  nfiles = irplib_framelist_get_size(rawframes);
733 
734  /* There should be an even number of files */
735  if (nfiles % 2) {
736  cpl_msg_warning(cpl_func, "Expecting even number of files, "
737  "ignoring the last of %d file(s)", nfiles);
738  error_if (nfiles == 1, CPL_ERROR_DATA_NOT_FOUND,
739  "At least two files are required");
740  nfiles--;
741  }
742 
743  nnod = nfiles/2;
744 
745  skip_if (nnod <= 0);
746 
747  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_FRAME_TYPE,
748  CPL_TYPE_STRING, CPL_FALSE, 0.0));
749  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_DOUBLE_DIT,
750  CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
751  /* Can only combine images of the same size */
752  skip_if (irplib_framelist_contains(rawframes, "NAXIS1",
753  CPL_TYPE_INT, CPL_TRUE, 0.0));
754  skip_if (irplib_framelist_contains(rawframes, "NAXIS2",
755  CPL_TYPE_INT, CPL_TRUE, 0.0));
756  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_NAXIS3,
757  CPL_TYPE_INT, CPL_FALSE, 0.0));
758  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_CHOP_NCYCLES,
759  CPL_TYPE_INT, CPL_FALSE, 0.0));
760 
761  plist1 = irplib_framelist_get_propertylist_const(rawframes, 0);
762  naxis1 = irplib_pfits_get_int(plist1, "NAXIS1");
763  naxis2 = irplib_pfits_get_int(plist1, "NAXIS2");
764  skip_if(0);
765 
766  /* Retrieve input parameters */
767  fnodpos = visir_parameterlist_get_string(parlist, recipename,
768  VISIR_PARAM_NODPOS);
769  skip_if (0);
770 
771  auto_bpm = visir_parameterlist_get_bool(parlist, recipename,
772  VISIR_PARAM_AUTOBPM);
773  skip_if (0);
774 
775  rem_glitch = visir_parameterlist_get_bool(parlist, recipename,
776  VISIR_PARAM_GLITCH);
777  skip_if (0);
778 
779  rem_bad = visir_parameterlist_get_bool(parlist, recipename,
780  VISIR_PARAM_PURGE);
781  skip_if (0);
782 
783  ndestripe = visir_parameterlist_get_int(parlist, recipename,
784  VISIR_PARAM_STRIPITE);
785  bug_if (0);
786 
787  morpho_destripe = ndestripe <= 0 ? CPL_FALSE :
788  visir_parameterlist_get_bool(parlist, recipename,
789  VISIR_PARAM_STRIPMOR);
790  bug_if (0);
791 
792  no_rem = !rem_glitch && !rem_bad;
793 
794  /* Each file corresponds to a nodding position (object=1 or sky=-1) */
795  /* Return nod_pos array if requested */
796  nod_pos = nodding_p ? nodding_p : cpl_malloc(nfiles * sizeof(int));
797  j = 0;
798  if (!visir_str_par_is_empty(fnodpos)) {
799  /* Get the nodding positions from the user-provided ascii file */
800  nods_vec = cpl_vector_read(fnodpos);
801  skip_if (cpl_vector_get_size(nods_vec) != nfiles);
802  nods_data = cpl_vector_get_data(nods_vec);
803  skip_if (0);
804  for (i=0 ; i<nfiles ; i++) {
805  if ((int)nods_data[i] == 0) {
806  nod_pos[i] = 1;
807  j++;
808  } else if ((int)nods_data[i] == 1) {
809  nod_pos[i] = -1;
810  is_nodding = CPL_TRUE;
811  } else {
812  error_if(1, CPL_ERROR_BAD_FILE_FORMAT,
813  "Wrong values in line %d in %s", i+1, fnodpos);
814  }
815  }
816  } else {
817  skip_if (irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_NODPOS,
818  CPL_TYPE_STRING, CPL_FALSE, 0.0));
819  }
820 
821  if (no_rem) cpl_msg_info(cpl_func, "No glitch removal and no purge of bad "
822  "frames requested: Using fast I/O method");
823 
824  /* Initialize the Bad Pixel Map */
825  if (badpix != NULL) {
826  /* The bpm is provided by the user */
827  cpl_msg_info(cpl_func, "Loading bad pixel map from %s", badpix);
828  /* Load the bad pixels image */
829  bpm_im_int = cpl_image_load(badpix, CPL_TYPE_INT, 0, 0);
830  skip_if (0);
831 
832  /* Convert the map from integer to binary */
833  bpm_im_bin = cpl_mask_threshold_image_create(bpm_im_int, -0.5, 0.5);
834  cpl_image_delete(bpm_im_int);
835  bpm_im_int = NULL;
836  skip_if (cpl_mask_not(bpm_im_bin));
837  } else if (auto_bpm) {
838  /* Initialize the Bad Pixel Map using the hcycle */
839 
840  /* i == 0 */
841  hcycle = visir_load_imagelist(rawframes, 0, CPL_FALSE);
842  skip_if(0);
843 
844  bpm_im_bin =
845  cpl_mask_threshold_image_create(cpl_imagelist_get(hcycle,0),
846  VISIR_HCYCLE_BPM_THRESHOLD,
847  DBL_MAX);
848  cpl_imagelist_delete(hcycle);
849  hcycle = NULL;
850  skip_if(0);
851  }
852 
853  /* Initialize the flat field image */
854  if (flat != NULL) {
855  cpl_msg_info(cpl_func, "Divide the nodded images by the flatfield");
856  /* Load the flat image */
857  flat_image = cpl_image_load(flat, CPL_TYPE_FLOAT, 0, 0);
858  any_if ("Cannot load the flat field %s", flat ? flat : "<NULL>");
859  }
860 
861  /* Get nodding position (if needed) and DIT from the header */
862  nodded = cpl_imagelist_new();
863 
864  tstart = cpl_test_get_walltime();
865 
866  /* Fill list with empty images of proper size and type */
867 
868  for (i=0; i < nfiles/2 ; i++) {
869  cpl_image * empty = cpl_image_new(naxis1, naxis2, CPL_TYPE_FLOAT);
870 
871  /* i'th image can only be inserted when i-1'th is there,
872  which prevents parallelism */
873 
874  bug_if (cpl_imagelist_set(nodded, empty, i));
875  }
876 
877 #ifdef _OPENMP
878 #pragma omp parallel for private(i) firstprivate(prev, collapsed) \
879  schedule(static, 2)
880 #endif
881  for (i = 0; i < nfiles ; i++) {
882  cpl_error_code errori = cpl_error_get_code();
883 
884  /* The total number of iterations must be pre-determined for the
885  parallelism to work. In case of an error we can therefore not
886  break, so instead we skip immediately to the next iteration.
887  FIXME: This check on didfail does not guarantee that only one
888  iteration can cause an error to be dumped, but it is not
889  worse than checking on the thread-local state, errori. */
890  if (didfail) continue;
891 
892  do {
893 
894  const char * file =
895  cpl_frame_get_filename(irplib_framelist_get_const(rawframes, i));
896  const cpl_propertylist * plist;
897 
898  double dit;
899  double factor;
900 
901 
902  plist = irplib_framelist_get_propertylist_const(rawframes, i);
903  if (plist == NULL) {
904  errori = cpl_error_set_where(cpl_func);
905  break;
906  }
907 
908  if (nods_vec == NULL) {
909  const char * sval = visir_pfits_get_nodpos(plist);
910  if (sval == NULL) {
911  errori = cpl_error_set_message(cpl_func,
912  CPL_ERROR_DATA_NOT_FOUND,
913  "Cannot get nodding position "
914  "for file %d/%d", i+1, nfiles);
915  break;
916  }
917  if (!strcmp(sval, "A")) {
918  nod_pos[i] = 1;
919 # ifdef _OPENMP
920 # pragma omp atomic
921 # endif
922  j++;
923  } else {
924  nod_pos[i] = -1;
925  is_nodding = CPL_TRUE;
926  }
927  }
928 
929  /* Print the file name with its nodding position */
930  cpl_msg_info(cpl_func, "File %02d: %s (%c)", i+1, file,
931  nod_pos[i]==1 ? '+' : '-');
932 
933  /* With nodding each pair must have exactly one object observation */
934  if (is_nodding && (i & 1) == 1 && nod_pos[i] == nod_pos[i-1]) {
935  cpl_msg_error(cpl_func, "Nodding pair (%d,%d) does not comprise an "
936  "on-object (A) and an off-object (B) image: %s", i-1,
937  i, nod_pos[i] == 1 ? "A" : "B");
938  }
939 
940  /* Compute the normalization factor from the Detector Integration Time */
941  dit = visir_pfits_get_dit(plist);
942  if (cpl_error_get_code()) {
943  errori = cpl_error_set_where(cpl_func);
944  break;
945  }
946 
947  if (dit <= 0) {
948  errori = cpl_error_set_message(cpl_func,
949  CPL_ERROR_ILLEGAL_INPUT,
950  "DIT in file %d/%d is too small: "
951  "%g", i+1, nfiles, dit);
952  break;
953  }
954 
955  factor = dit * nod_pos[i] * 2.0;
956 
957  if (no_rem){
958  collapsed = visir_load_average(file, plist);
959  } else {
960  in = visir_load_intermint(rawframes, i);
961  if (in == NULL) {
962  errori = cpl_error_set_message(cpl_func,
963  CPL_ERROR_ILLEGAL_INPUT,
964  "Could not load image set %d",
965  i+1);
966  break;
967  }
968 
969  /* Convert the image lists from 'INTERM' to A-B' */
970  if (visir_imagelist_unpack_interm(in)) {
971  errori = cpl_error_set_message(cpl_func,
972  cpl_error_get_code(),
973  "Failure for file %d/%d",
974  i+1, nfiles);
975  break;
976  }
977 
978  /* Remove the glitch in each A-B image in each input cube
979  if requested */
980  if (rem_glitch) {
981  int jj;
982  for (jj=0 ; jj < cpl_imagelist_get_size(in); jj++) {
983  if (visir_rem_glitch(cpl_imagelist_get(in, jj))) {
984  errori = cpl_error_set_message(cpl_func,
985  cpl_error_get_code(),
986  "Could not remove "
987  "glitch in image %d in "
988  "set %d", jj+1, i+1);
989  break;
990  }
991  }
992  }
993 
994  /* Remove the bad A-B images in each input file/cube if requested */
995  if (rem_bad) {
996  cpl_msg_info(cpl_func, "Remove the bad A-B input images");
997  if (visir_rem_bad_images(in)) {
998  errori = cpl_error_set_message(cpl_func,
999  cpl_error_get_code(),
1000  "Could not remove bad "
1001  "images in list %d", i+1);
1002  break;
1003  }
1004  }
1005  /* Average each cube */
1006  collapsed = cpl_imagelist_collapse_create(in);
1007 
1008  cpl_imagelist_delete(in);
1009  in = NULL;
1010 
1011  }
1012 
1013  if (cpl_error_get_code()) {
1014  errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1015  "Failure for file %d/%d",
1016  i+1, nfiles);
1017  break;
1018  }
1019  /* Normalise to have ADU/s. */
1020  /* Also divide with 2 to achieve average of image pair */
1021  if (cpl_image_divide_scalar(collapsed, 2*factor)) {
1022  errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1023  "Failure for file %d/%d",
1024  i+1, nfiles);
1025  break;
1026  }
1027 
1028  /* Each pair of input files gives a nodded image in nodded */
1029  if (i & 1) {
1030  if (cpl_image_add(prev, collapsed)) {
1031  errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1032  "Failure for file %d/%d",
1033  i+1, nfiles);
1034  break;
1035  }
1036  cpl_image_delete(collapsed);
1037  collapsed = NULL;
1038 
1039  /* At this point prev is the image to be put into
1040  the list of nodded images */
1041 
1042  if (bpm_im_bin != NULL) {
1043  /* Apply the bad pixels cleaning */
1044  if (cpl_image_reject_from_mask(prev, bpm_im_bin)) {
1045  errori = cpl_error_set_message(cpl_func,
1046  cpl_error_get_code(),
1047  "Failure for file %d/%d",
1048  i+1, nfiles);
1049  break;
1050  }
1051  if (cpl_detector_interpolate_rejected(prev)) {
1052  errori = cpl_error_set_message(cpl_func,
1053  cpl_error_get_code(),
1054  "Failure for file %d/%d",
1055  i+1, nfiles);
1056  break;
1057  }
1058  }
1059 
1060  if (ndestripe > 0)
1061  if(visir_destripe_image(prev, ndestripe,
1062  VISIR_DESTRIPE_DETECT,
1063  VISIR_DESTRIPE_DETECT_THRESHOLD,
1064  morpho_destripe)) {
1065  errori = cpl_error_set_message(cpl_func,
1066  cpl_error_get_code(),
1067  "Failure for file %d/%d",
1068  i+1, nfiles);
1069  break;
1070  }
1071 
1072  if (flat_image != NULL) {
1073  /* Apply the flatfield correction */
1074  if (cpl_image_divide(prev, flat_image)) {
1075  errori = cpl_error_set_message(cpl_func,
1076  cpl_error_get_code(),
1077  "Failure for file %d/%d",
1078  i+1, nfiles);
1079  break;
1080  }
1081  }
1082 
1083  if (cpl_imagelist_set(nodded, prev, i/2)) {
1084  errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1085  "Failure for file %d/%d",
1086  i+1, nfiles);
1087  break;
1088  }
1089  prev = NULL;
1090  } else {
1091  prev = collapsed;
1092  collapsed = NULL;
1093  }
1094  } while (0);
1095 
1096  if (errori) {
1097 #ifdef _OPENMP
1098  /* Cannot access these errors after the join,
1099  so dump them now. :-(((((((((((((((((((( */
1100  cpl_errorstate_dump(cleanstate, CPL_FALSE, NULL);
1101  cpl_errorstate_set(cleanstate);
1102 
1103  /* Need to deallocate the thread-private instances in the loop */
1104  cpl_image_delete(prev); prev = NULL;
1105  cpl_image_delete(collapsed); collapsed = NULL;
1106 
1107 #pragma omp critical(visir_inputs_combine)
1108 #endif
1109  didfail = errori;
1110  }
1111  }
1112 
1113  error_if(didfail, didfail, "Failed to create %d nodded images from %d "
1114  "files", nnod, nfiles);
1115 
1116  tstop = cpl_test_get_walltime();
1117  cpl_msg_info(cpl_func, "Time to create %d nodded images [s]: %g", nnod,
1118  tstop - tstart);
1119 
1120  cpl_vector_delete(nods_vec);
1121  nods_vec = NULL;
1122 
1123  cpl_image_delete(flat_image);
1124  flat_image = NULL;
1125 
1126  cpl_mask_delete(bpm_im_bin);
1127  bpm_im_bin = NULL;
1128 
1129  if (nod_pos != nodding_p) cpl_free(nod_pos);
1130  nod_pos = NULL;
1131 
1132  error_if(is_nodding && j != nnod, CPL_ERROR_INCOMPATIBLE_INPUT,
1133  "With nodding exactly half of the images "
1134  "must be on-object, not %d of %d", j, 2*nnod);
1135 
1136  if (do_spc_fix) {
1137  const double ksi = visir_parameterlist_get_double(parlist, recipename,
1138  VISIR_PARAM_SPECSKEW);
1139  const double eps = visir_parameterlist_get_double(parlist, recipename,
1140  VISIR_PARAM_VERTARC);
1141  const double delta = visir_parameterlist_get_double(parlist, recipename,
1142  VISIR_PARAM_HORIARC);
1143  const double phi = visir_parameterlist_get_double(parlist, recipename,
1144  VISIR_PARAM_SLITSKEW);
1145  const int doplot = visir_parameterlist_get_int(parlist, recipename,
1146  VISIR_PARAM_PLOT);
1147 
1148 
1149  skip_if (0);
1150 
1151  images = cpl_malloc(nnod * sizeof(cpl_image*));
1152 
1153  for (j = 0; j < nnod; j++) images[j] = cpl_imagelist_get(nodded, j);
1154 
1155  skip_if (visir_spc_det_fix(images, nnod, CPL_TRUE, wlen, resol,
1156  phi, ksi, eps, delta, doplot));
1157  }
1158 
1159  end_skip;
1160 
1161  cpl_msg_set_time_off();
1162 
1163  cpl_free(images);
1164  cpl_imagelist_delete(in);
1165 
1166  if (nod_pos != nodding_p) cpl_free(nod_pos);
1167  cpl_vector_delete(nods_vec);
1168  cpl_image_delete(bpm_im_int);
1169  cpl_mask_delete(bpm_im_bin);
1170  cpl_image_delete(collapsed);
1171  cpl_image_delete(prev);
1172  if (cpl_error_get_code() && nodded != NULL) {
1173  cpl_imagelist_delete(nodded);
1174  nodded = NULL;
1175  }
1176 
1177  return nodded;
1178 }
1179 
1180 /*----------------------------------------------------------------------------*/
1189 /*----------------------------------------------------------------------------*/
1190 static double
1191 get_cumoffsets(const cpl_propertylist * plist, double * x, double * y)
1192 {
1193  cpl_errorstate cleanstate = cpl_errorstate_get();
1194 
1195  *x = irplib_pfits_get_double(plist, "ESO DRS CUMOFFSETX");
1196  *y = irplib_pfits_get_double(plist, "ESO DRS CUMOFFSETY");
1197 
1198  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1199  cpl_errorstate_set(cleanstate);
1200  cpl_msg_info(cpl_func, "DRS CUMOFFSET[XY] not found, falling back"
1201  " to SEQ CUMOFFSET[XY]");
1202  *x = visir_pfits_get_cumoffsetx(plist);
1203  *y = visir_pfits_get_cumoffsety(plist);
1204  }
1205  return cpl_error_get_code();
1206 }
1207 
1208 /*----------------------------------------------------------------------------*/
1223 /*----------------------------------------------------------------------------*/
1224 cpl_image ** visir_img_recombine_list(const char * recipename,
1225  const cpl_parameterlist * parlist,
1226  cpl_imagelist * nodded,
1227  const cpl_propertylist ** plists,
1228  cpl_geom_combine combine_mode,
1229  cpl_boolean * pdid_resize)
1230 {
1231  cpl_size nnod;
1232  cpl_bivector * offsets_est = NULL;
1233  cpl_bivector * objs = NULL;
1234  cpl_image ** combined = NULL;
1235  cpl_vector * sigmas = NULL;
1236  cpl_propertylist * qclist = cpl_propertylist_new();
1237 
1238 
1239  bug_if (0);
1240 
1241  bug_if (recipename == NULL);
1242  bug_if (parlist == NULL);
1243  bug_if (pdid_resize == NULL);
1244  bug_if (nodded == NULL);
1245 
1246  nnod = cpl_imagelist_get_size(nodded);
1247 
1248  /* If only one nodded image, the work is finished */
1249  if (nnod == 1) {
1250 
1251  combined = cpl_malloc(2*sizeof(cpl_image*));
1252  combined[1] = NULL; /* In case the unset fails */
1253 
1254  combined[0] = cpl_imagelist_unset(nodded, 0);
1255  bug_if (combined[0] == NULL);
1256 
1257  combined[1] = cpl_image_new(cpl_image_get_size_x(combined[0]),
1258  cpl_image_get_size_y(combined[0]),
1259  CPL_TYPE_INT);
1260  bug_if (combined[1] == NULL);
1261 
1262  /* Set all pixel values to 1 */
1263  bug_if(cpl_image_threshold(combined[1], 1.0, 1.0, 1.0, 1.0));
1264 
1265  *pdid_resize = CPL_FALSE;
1266 
1267  } else {
1268  const double psigmas[] = {5, 2, 1, 0.5};
1269  const char * sval;
1270  const char * offsets;
1271  const char * objects;
1272  int sx, sy, mx, my;
1273  int rej_low, rej_high;
1274  cpl_boolean refine;
1275 
1276 
1277  refine = visir_parameterlist_get_bool(parlist, recipename,
1278  VISIR_PARAM_REFINE);
1279  skip_if (0);
1280 
1281  offsets = visir_parameterlist_get_string(parlist, recipename,
1282  VISIR_PARAM_OFFSETS);
1283  skip_if (0);
1284 
1285  objects = visir_parameterlist_get_string(parlist, recipename,
1286  VISIR_PARAM_OBJECTS);
1287  skip_if (0);
1288 
1289  sval = visir_parameterlist_get_string(parlist, recipename,
1290  VISIR_PARAM_XCORR);
1291  skip_if (0);
1292 
1293  if (sscanf(sval, "%d-%d-%d-%d", &sx, &sy, &mx, &my) != 4)
1294  skip_if (sscanf(sval, "%d %d %d %d", &sx, &sy, &mx, &my) != 4);
1295 
1296 
1297  sval = visir_parameterlist_get_string(parlist, recipename,
1298  VISIR_PARAM_REJECT);
1299  skip_if (0);
1300 
1301  if (sscanf(sval, "%d-%d", &rej_low, &rej_high) !=2 )
1302  skip_if (sscanf(sval, "%d %d", &rej_low, &rej_high) !=2 );
1303 
1304  /* Get the offsets estimation of each input file pair */
1305  cpl_msg_info(cpl_func, "Get the offsets estimation");
1306  if (!visir_str_par_is_empty(offsets)) {
1307  /* A file has been provided on the command line */
1308  offsets_est = cpl_bivector_read(offsets);
1309  skip_if (offsets_est==NULL);
1310 
1311  error_if (cpl_bivector_get_size(offsets_est) != nnod,
1312  CPL_ERROR_BAD_FILE_FORMAT, "The offsets file %s must "
1313  "have %d entries, not %d", offsets, (int)nnod,
1314  (int)cpl_bivector_get_size(offsets_est));
1315  } else {
1316  double * offsets_est_x;
1317  double * offsets_est_y;
1318  double xoff0, yoff0;
1319 
1320  /* Get the offsets from the header */
1321  offsets_est = cpl_bivector_new(nnod);
1322  offsets_est_x = cpl_bivector_get_x_data(offsets_est);
1323  offsets_est_y = cpl_bivector_get_y_data(offsets_est);
1324 
1325  skip_if (0);
1326 
1327  offsets_est_x[0] = 0.0;
1328  offsets_est_y[0] = 0.0;
1329  get_cumoffsets(plists[0], &xoff0, &yoff0);
1330 
1331  for (cpl_size i = 1; i < nnod ; i++) {
1332  double xoff, yoff;
1333 
1334  skip_if(get_cumoffsets(plists[i], &xoff, &yoff));
1335 
1336  /* Subtract the first offset from all offsets */
1337  offsets_est_x[i] = xoff0 - xoff;
1338  offsets_est_y[i] = yoff0 - yoff;
1339  }
1340  }
1341 
1342  /* Read the provided objects file if provided
1343  - if a file has been provided on the command line */
1344  if (!visir_str_par_is_empty(objects)) {
1345  objs = cpl_bivector_read(objects);
1346  any_if ("Could not read objects from %s", objects);
1347  }
1348 
1349  cpl_msg_info(cpl_func, "Recombining the list of nodded images using "
1350  "mode: %d (I=%d:U=%d:F=%d), rej-lo=%d, rej-hi=%d",
1351  combine_mode, CPL_GEOM_INTERSECT, CPL_GEOM_UNION,
1352  CPL_GEOM_FIRST, rej_low, rej_high);
1353 
1354  if (cpl_msg_get_level() <= CPL_MSG_DEBUG) {
1355  cpl_msg_debug(cpl_func, "The offsets for the recombination:");
1356  cpl_bivector_dump(offsets_est, stdout);
1357  }
1358 
1359  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
1360  sigmas = cpl_vector_wrap(4, (double*)psigmas); /* Not changed */
1361  IRPLIB_DIAG_PRAGMA_POP;
1362  combined = cpl_geom_img_offset_combine(nodded, offsets_est, refine,
1363  objs, sigmas, NULL, sx, sy,
1364  mx, my, rej_low, rej_high,
1365  combine_mode);
1366  any_if("Could not recombine the images");
1367 
1368  *pdid_resize = (cpl_boolean)(cpl_image_get_size_x(combined[0])
1369  != cpl_image_get_size_x(cpl_imagelist_get_const(nodded, 0)) ||
1370  cpl_image_get_size_y(combined[0])
1371  != cpl_image_get_size_y(cpl_imagelist_get_const(nodded, 0)));
1372  }
1373 
1374  if (visir_parameterlist_get_int(parlist, recipename, VISIR_PARAM_PLOT) > 0)
1375  visir_image_plot("", "t 'The combined image'", "", combined[0]);
1376 
1377  end_skip;
1378 
1379  cpl_propertylist_delete(qclist);
1380  cpl_bivector_delete(offsets_est);
1381  cpl_bivector_delete(objs);
1382  cpl_vector_unwrap(sigmas);
1383 
1384  return combined;
1385 }
1386 
1387 /*----------------------------------------------------------------------------*/
1434 /*----------------------------------------------------------------------------*/
1435 cpl_image ** visir_img_recombine(const char * recipename,
1436  const cpl_parameterlist * parlist,
1437  const irplib_framelist * rawframes,
1438  const char * badpix,
1439  const char * flat,
1440  cpl_geom_combine combine_mode,
1441  cpl_boolean * pdid_resize,
1442  cpl_boolean do_spc_fix,
1443  double wlen,
1444  visir_spc_resol resol)
1445 {
1446  int nfiles;
1447  int * nod_pos = NULL;
1448  cpl_imagelist * nodded = NULL;
1449  cpl_size nnod;
1450  cpl_bivector * objs = NULL;
1451  cpl_propertylist * qclist = cpl_propertylist_new();
1452  const cpl_propertylist ** plists = NULL;
1453  cpl_image ** rec = NULL;
1454 
1455 
1456  bug_if (0);
1457 
1458  bug_if (recipename == NULL);
1459  bug_if (parlist == NULL);
1460  bug_if (rawframes == NULL);
1461  bug_if (pdid_resize == NULL);
1462 
1463  /* Get the number of files */
1464  nfiles = irplib_framelist_get_size(rawframes);
1465 
1466  /* There should be an even number of files */
1467  if (nfiles % 2) {
1468  cpl_msg_warning(cpl_func, "Expecting even number of files, "
1469  "ignoring the last of %d file(s)", nfiles);
1470  error_if (nfiles == 1, CPL_ERROR_DATA_NOT_FOUND,
1471  "At least two files are required");
1472  nfiles--;
1473  }
1474 
1475  skip_if ( nfiles <= 0);
1476 
1477  /* Each file corresponds to a nodding position (object=1 or sky=-1) */
1478  nod_pos = cpl_malloc(nfiles * sizeof(int));
1479 
1480  /* Combine the input frames into the nodded images */
1481  cpl_msg_info(cpl_func, "Combining the input frames into the nodded images");
1482  nodded = visir_inputs_combine(recipename, parlist, rawframes, badpix, flat,
1483  nod_pos, do_spc_fix, wlen, resol);
1484  skip_if(nodded == NULL);
1485 
1486  nnod = cpl_imagelist_get_size(nodded);
1487  plists = cpl_malloc(nnod * sizeof(cpl_propertylist *));
1488  for (cpl_size i=0; i < nnod ; i++) {
1489  const cpl_size iframe = nod_pos[2*i] == 1 ? 2*i : 2*i+1;
1490 
1491  plists[i] = irplib_framelist_get_propertylist_const(rawframes,
1492  iframe);
1493  }
1494 
1495  rec = visir_img_recombine_list(recipename, parlist, nodded,
1496  plists, combine_mode, pdid_resize);
1497 
1498  end_skip;
1499 
1500  cpl_propertylist_delete(qclist);
1501  cpl_free(nod_pos);
1502  cpl_free(plists);
1503  cpl_imagelist_delete(nodded);
1504  cpl_bivector_delete(objs);
1505 
1506  return rec;
1507 }
1508 
1509 /*----------------------------------------------------------------------------*/
1532 /*----------------------------------------------------------------------------*/
1533 cpl_imagelist * visir_load_hcycle(const irplib_framelist * rawframes, int pos)
1534 {
1535  return visir_load_imagelist(rawframes, pos, CPL_FALSE);
1536 }
1537 
1538 /*----------------------------------------------------------------------------*/
1549 /*----------------------------------------------------------------------------*/
1550 cpl_error_code visir_image_reject_hot(cpl_image * self, const char * bpmfile)
1551 {
1552 
1553  cpl_image * im_bpm = NULL;
1554  cpl_mask * bpm = NULL;
1555  const int upper = VISIR_HCYCLE_BPM_THRESHOLD;
1556 
1557 
1558  skip_if (0);
1559 
1560  skip_if (self == NULL);
1561 
1562  if (bpmfile == NULL) {
1563  bpm = cpl_mask_threshold_image_create(self, upper, DBL_MAX);
1564  skip_if (0);
1565  } else {
1566 
1567  /* The bpm is provided by the user */
1568  cpl_msg_info(cpl_func, "Clean user specified bad pixels");
1569  /* Load the bad pixel image */
1570  im_bpm = cpl_image_load(bpmfile, CPL_TYPE_INT, 0, 0);
1571  any_if ("Could not load the bad pixel map %s",
1572  bpmfile ? bpmfile : "<NULL>");
1573  /* Convert the map from integer to binary */
1574  bpm = cpl_mask_threshold_image_create(im_bpm, -0.5, 0.5);
1575  skip_if (0);
1576  cpl_image_delete(im_bpm);
1577  im_bpm = NULL;
1578 
1579  skip_if (cpl_mask_not(bpm));
1580  }
1581 
1582  skip_if (cpl_image_reject_from_mask(self, bpm));
1583 
1584  end_skip;
1585 
1586  cpl_image_delete(im_bpm);
1587  cpl_mask_delete(bpm);
1588 
1589  return cpl_error_get_code();
1590 
1591 }
1592 
1593 
1594 /*----------------------------------------------------------------------------*/
1601 /*----------------------------------------------------------------------------*/
1602 cpl_imagelist * visir_imagelist_load_last(const irplib_framelist * rawframes)
1603 {
1604  cpl_imagelist * self = NULL;
1605  int naxis3;
1606 
1607  /* Verify that NAXIS3 is the same in all files */
1608  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_NAXIS3,
1609  CPL_TYPE_INT, CPL_TRUE, 0.0));
1610 
1612  rawframes,
1613  0));
1614 
1615  /* Load the image set */
1616  self = irplib_imagelist_load_framelist(rawframes, CPL_TYPE_FLOAT, naxis3-1,
1617  0);
1618 
1619  skip_if (self == NULL);
1620 
1621  end_skip;
1622 
1623  return self;
1624 
1625 }
1626 
1627 
1628 /*----------------------------------------------------------------------------*/
1638 /*----------------------------------------------------------------------------*/
1639 cpl_imagelist * visir_load_imagelist(const irplib_framelist * rawframes,
1640  int pos, cpl_boolean is_interm)
1641 {
1642  cpl_imagelist * self = NULL;
1643  cpl_image * image = NULL;
1644  const cpl_frame * frame = irplib_framelist_get_const(rawframes, pos);
1645  const cpl_propertylist * plist =
1647  const char * file = cpl_frame_get_filename(frame);
1648  const int nchop = plist ? visir_pfits_get_chop_ncycles(plist) : -1;
1649  int naxis3;
1650  visir_data_type data_type;
1651  int plane_step;
1652  int nsize;
1653  int iplane;
1654  int i;
1655 
1656  skip_if (plist == NULL);
1657 
1658  skip_if(visir_get_data_type(frame, plist, &data_type, NULL));
1659 
1660  switch (data_type) {
1661  case VISIR_DATA_AQU_HCYCLE:
1662  case VISIR_DATA_AQU_BURST:
1663  case VISIR_DATA_AQU_BURST_EXT:
1664  error_if(1, CPL_ERROR_UNSUPPORTED_MODE,
1665  "Aquarius data not supported");
1666  default:
1667  naxis3 = visir_pfits_get_naxis3(plist);
1668  break;
1669  }
1670 
1671  bug_if (file == NULL);
1672 
1673  if (data_type == VISIR_DATA_CUBE1) {
1674  /* All INTERM frames are in the first part of the cube */
1675  iplane = is_interm ? 0 : nchop;
1676  /* - followed by a single Half-cycle image */
1677  nsize = is_interm ? nchop : 1;
1678  plane_step = 1;
1679  } else if (naxis3 == 1) {
1680  iplane = 0;
1681  nsize = 1;
1682  plane_step = 1;
1683  } else {
1684  /* Each INTERM frame follows the Half-cycle frame */
1685  iplane = is_interm ? 1 : 0;
1686  nsize = nchop;
1687  plane_step = 2;
1688  }
1689 
1690  self = cpl_imagelist_new();
1691 
1692  for (i=0 ; i < nsize; i++, iplane += plane_step) {
1693 
1694  image = cpl_image_load(file, CPL_TYPE_FLOAT, iplane, 0);
1695  skip_if (image == NULL);
1696 
1697  skip_if (!is_interm && visir_offset_hcycle(image));
1698 
1699  skip_if (cpl_imagelist_set(self, image, i));
1700 
1701  image = NULL;
1702  }
1703 
1704  skip_if (i < nsize);
1705 
1706  end_skip;
1707 
1708  if (cpl_error_get_code()) {
1709  if (file != NULL) cpl_msg_warning(cpl_func, "Could not load the %s "
1710  "frame(s) from: %s",
1711  is_interm ? "INTERM" : "Half-Cycle",
1712  file);
1713  cpl_image_delete(image);
1714  cpl_imagelist_delete(self);
1715  self = NULL;
1716  }
1717 
1718  return self;
1719 }
1720 
1721 
1724 /*----------------------------------------------------------------------------*/
1748 /*----------------------------------------------------------------------------*/
1749 static cpl_imagelist * visir_load_intermint(const irplib_framelist * rawframes,
1750  int pos)
1751 {
1752  return visir_load_imagelist(rawframes, pos, CPL_TRUE);
1753 
1754 }
1755 
1756 /*----------------------------------------------------------------------------*/
1765 /*----------------------------------------------------------------------------*/
1766 static cpl_image * visir_load_average(const char * file,
1767  const cpl_propertylist * plist)
1768 {
1769  cpl_errorstate cleanstate = cpl_errorstate_get();
1770  cpl_image * self = NULL;
1771  int nchop, naxis3;
1772 
1773 
1774  skip_if (0);
1775  skip_if (file == NULL);
1776  skip_if (plist == NULL);
1777 
1778  naxis3 = visir_pfits_get_naxis3(plist);
1779  nchop = visir_pfits_get_chop_ncycles(plist);
1780 
1781  skip_if (0);
1782 
1783  if (nchop == 0 && naxis3 == 1) {
1784  self = cpl_image_load(file, CPL_TYPE_FLOAT, 0, 0);
1785  } else {
1786  const char * sval;
1787  /* If nchop == 1 (and naxis3 == 3) the data unit is both a valid CUBE1
1788  and a valid CUBE2; Set plane_offset as if the frame type is CUBE2 */
1789  const int plane_offset = (naxis3 == 2 * nchop + 1) ? 2 : 3;
1790 
1791 
1792  error_if (nchop <= 0, CPL_ERROR_BAD_FILE_FORMAT, "CHOP NCYCLES in %s "
1793  "is non-positive (and NAXIS3=%d): %d", file, naxis3, nchop);
1794 
1795  error_if (plane_offset == 3 && naxis3 != nchop+2,
1796  CPL_ERROR_BAD_FILE_FORMAT, "NAXIS3=%d and CHOP NCYCLES=%d "
1797  "in %s is not a valid VISIR INTERM+Half-Cycle format", naxis3,
1798  nchop, file);
1799 
1800  if (plane_offset == 3 && nchop > 1)
1801  cpl_msg_debug(cpl_func, "%s has %d INTERM-frames and one Half-"
1802  "Cycle frame (old CUBE1-format)", file, nchop);
1803 
1804  /* Check the data format */
1805  sval = visir_pfits_get_frame_type(plist);
1806  if (sval == NULL) {
1807  /* Has warned about missing frame type card */
1808  visir_error_reset("Could not get FITS key");
1809  /* Don't know whether or not to expect CUBE1 or CUBE2 */
1810  } else if (strlen(sval) == 0) {
1811  /* Don't know whether or not to expect CUBE1 or CUBE2 */
1812  } else if (plane_offset == 3) {
1813  if (strcmp(sval, "CUBE2")==0)
1814  cpl_msg_error(cpl_func, "%s has FRAM TYPE = CUBE2, but NAXIS3="
1815  "%d and CHOP NCYCLES=%d imply a CUBE1. Assuming"
1816  " the frame type is really CUBE1", file,
1817  naxis3, nchop);
1818  } else if (nchop > 1) {
1819  /* if nchop == 1 format can be CUBE1 or CUBE2 */
1820  if (strcmp(sval, "CUBE1")==0)
1821  cpl_msg_error(cpl_func, "%s has FRAM TYPE = CUBE1, but NAXIS3="
1822  "%d and CHOP NCYCLES=%d imply a CUBE2. Assuming"
1823  "the frame type is really CUBE2", file,
1824  naxis3, nchop);
1825  }
1826 
1827  /* Load last INTERM frame */
1828  self = cpl_image_load(file, CPL_TYPE_FLOAT, naxis3-plane_offset, 0);
1829 
1830  }
1831 
1832  skip_if (0);
1833 
1834  end_skip;
1835 
1836  if (cpl_error_get_code()) {
1837  cpl_msg_warning(cpl_func, "Could not load the last INTERM frame from: "
1838  "%s", file ? file : "<NULL>");
1839  cpl_image_delete(self);
1840  self = NULL;
1841  }
1842 
1843  return self;
1844 }
1845 
1846 /*----------------------------------------------------------------------------*/
1869 /*----------------------------------------------------------------------------*/
1870 static cpl_error_code visir_imagelist_unpack_interm(cpl_imagelist * self)
1871 {
1872  cpl_image * iprev;
1873  cpl_image * image;
1874  const int n = cpl_imagelist_get_size(self);
1875  int i;
1876 
1877 
1878  skip_if (0);
1879 
1880  if (n == 1) return CPL_ERROR_NONE;
1881 
1882  iprev = cpl_imagelist_get(self, n - 1);
1883 
1884  skip_if (0);
1885 
1886  skip_if (cpl_image_multiply_scalar(iprev, n));
1887 
1888  /* Loop on the images - with first and last iteration peeled off */
1889  for (i = n-1 ; i > 1 ; i--, iprev = image) {
1890  image = cpl_imagelist_get(self, i-1);
1891 
1892  skip_if (0);
1893 
1894  skip_if (cpl_image_multiply_scalar(image, i));
1895 
1896  skip_if (cpl_image_subtract(iprev, image));
1897 
1898  }
1899 
1900  image = cpl_imagelist_get(self, 0);
1901 
1902  skip_if (0);
1903 
1904  skip_if (cpl_image_subtract(iprev, image));
1905 
1906  end_skip;
1907 
1908  return cpl_error_get_code();
1909 }
1910 
1911 /*----------------------------------------------------------------------------*/
1924 /*----------------------------------------------------------------------------*/
1925 static cpl_error_code visir_rem_glitch(cpl_image * glitchy)
1926 {
1927  cpl_image * med_filt = NULL;
1928  cpl_mask * bpm = NULL;
1929  cpl_mask * kernel = cpl_mask_new(3, 3);
1930  double mean, stdev;
1931  double low_thresh, high_thresh;
1932  const int nx = cpl_image_get_size_x(glitchy);
1933  const int ny = cpl_image_get_size_y(glitchy);
1934  int i;
1935 
1936  /* Some heuristic constants */
1937  double factor1 = 3.0;
1938  double factor2 = 10.0;
1939  const int niterations = 5;
1940  const double median_corr = 1.5;
1941 
1942  bug_if (0);
1943 
1944  /* Create the filtering kernel */
1945  bug_if(cpl_mask_not(kernel));
1946 
1947  /* Apply a 3x3 median filter to the input image */
1948  med_filt = cpl_image_new(cpl_image_get_size_x(glitchy),
1949  cpl_image_get_size_y(glitchy),
1950  cpl_image_get_type(glitchy));
1951  bug_if(med_filt == NULL);
1952  bug_if(cpl_image_filter_mask(med_filt, glitchy, kernel, CPL_FILTER_MEDIAN,
1953  CPL_BORDER_FILTER));
1954  cpl_mask_delete(kernel);
1955  kernel = NULL;
1956 
1957  /* Noise is glitchy - med_filt */
1958  skip_if (cpl_image_subtract(glitchy, med_filt));
1959 
1960  /* niterations iterations */
1961  for (i=0 ; i < niterations ; i++) {
1962  /* Compute mean and stdev */
1963  mean = cpl_image_get_mean(glitchy);
1964  stdev = cpl_image_get_stdev(glitchy);
1965 
1966  skip_if (0);
1967 
1968  /* Set the thresholds */
1969  low_thresh = mean - factor1 * stdev;
1970  high_thresh = mean + factor1 * stdev;
1971 
1972  /* Identify where mean-factor1*stdev < glitchy < mean+factor1*stdev */
1973  bpm = cpl_mask_threshold_image_create(glitchy,low_thresh,high_thresh);
1974  skip_if (cpl_mask_not(bpm));
1975  skip_if (cpl_image_reject_from_mask(glitchy, bpm));
1976  cpl_mask_delete(bpm);
1977  bpm = NULL;
1978 
1979  /* Test the number of bad pixels */
1980  skip_if (cpl_image_count_rejected(glitchy) == nx*ny);
1981  }
1982 
1983  /* Last iteration */
1984  /* Compute mean and stdev */
1985  mean = cpl_image_get_mean(glitchy);
1986  stdev = cpl_image_get_stdev(glitchy) * median_corr;
1987 
1988  skip_if (0);
1989 
1990  low_thresh = mean - factor2 * stdev;
1991  high_thresh = mean + factor2 * stdev;
1992 
1993  bpm = cpl_mask_threshold_image_create(glitchy, low_thresh, high_thresh);
1994  skip_if (cpl_mask_not(bpm));
1995  skip_if (cpl_image_reject_from_mask(glitchy, bpm));
1996  cpl_mask_delete(bpm);
1997  bpm = NULL;
1998 
1999  /* Test the number of bad pixels */
2000  skip_if (cpl_image_count_rejected(glitchy) == nx*ny);
2001 
2002  /* Set the bad pixels to 0 */
2003  skip_if (cpl_image_fill_rejected(glitchy, 0.0));
2004  skip_if (cpl_image_accept_all(glitchy));
2005 
2006  /* Reconstruct the corrected image */
2007  skip_if (cpl_image_add(glitchy, med_filt));
2008 
2009  end_skip;
2010 
2011  cpl_image_delete(med_filt);
2012  cpl_mask_delete(bpm);
2013  cpl_mask_delete(kernel);
2014 
2015  return cpl_error_get_code();
2016 }
2017 
2018 /*----------------------------------------------------------------------------*/
2029 /*----------------------------------------------------------------------------*/
2030 static cpl_error_code visir_rem_bad_images(cpl_imagelist * in)
2031 {
2032  cpl_vector * medians = NULL;
2033  cpl_vector * stdevs = NULL;
2034  cpl_vector * selection = NULL;
2035  double mean_medians, mean_stdevs, stdev_medians, stdev_stdevs;
2036  const double threshold = 3;
2037  const int nima = cpl_imagelist_get_size(in);
2038  int i;
2039 
2040 
2041  /* This will catch a NULL input */
2042  skip_if (0);
2043 
2044  if (nima <= 3) return CPL_ERROR_NONE;
2045 
2046  /* Create medians and stdevs arrays */
2047  medians = cpl_vector_new(nima);
2048  stdevs = cpl_vector_new(nima);
2049 
2050  /* Compute the statistics */
2051  for (i=0 ; i < nima ; i++) {
2052  cpl_stats * stats = cpl_stats_new_from_image(cpl_imagelist_get(in, i),
2053  CPL_STATS_STDEV | CPL_STATS_MEDIAN);
2054 
2055  cpl_vector_set(medians, i, cpl_stats_get_median(stats));
2056  cpl_vector_set(stdevs, i, cpl_stats_get_stdev(stats));
2057  cpl_stats_delete(stats); /* :-( */
2058  }
2059 
2060  skip_if( 0);
2061 
2062  /* Get the stats on these arrays */
2063  mean_medians = cpl_vector_get_mean(medians);
2064  stdev_medians = cpl_vector_get_stdev(medians);
2065  mean_stdevs = cpl_vector_get_mean(stdevs);
2066  stdev_stdevs = cpl_vector_get_stdev(stdevs);
2067 
2068  skip_if (cpl_vector_subtract_scalar(medians, mean_medians));
2069  skip_if (cpl_vector_subtract_scalar(stdevs, mean_stdevs));
2070 
2071  stdev_medians *= threshold;
2072  stdev_stdevs *= threshold;
2073 
2074  /* Create the selection vector */
2075  selection = cpl_vector_new(nima);
2076  skip_if( cpl_vector_fill(selection, 0)); /* Flag all as good */
2077  for (i=0 ; i < nima ; i++) {
2078  if (fabs(cpl_vector_get(medians, i)) <= stdev_medians &&
2079  fabs(cpl_vector_get(stdevs, i)) <= stdev_stdevs) continue;
2080 
2081  cpl_vector_set(selection, i, -1);
2082  cpl_msg_info(cpl_func, "Image %d of %d rejected: median=%g, stdev=%g",
2083  i+1, nima, stdev_medians, stdev_stdevs);
2084  }
2085 
2086  /* Purge the bad images in the images set */
2087  cpl_imagelist_erase(in, selection);
2088 
2089  end_skip;
2090 
2091  cpl_vector_delete(medians);
2092  cpl_vector_delete(stdevs);
2093 
2094  cpl_vector_delete(selection);
2095 
2096  return CPL_ERROR_NONE;
2097 
2098 }
2099 
2100 /*----------------------------------------------------------------------------*/
2109 /*----------------------------------------------------------------------------*/
2110 static cpl_error_code visir_offset_hcycle(cpl_image * hcycle)
2111 {
2112  double minval;
2113 
2114 
2115  skip_if (0);
2116 
2117  skip_if (cpl_image_add_scalar(hcycle, VISIR_HCYCLE_OFFSET));
2118 
2119  minval = cpl_image_get_min(hcycle);
2120 
2121  /* It seems that the offset really is VISIR_HCYCLE_OFFSET-1, warn if not */
2122  if (minval < 1) cpl_msg_warning(cpl_func, "HCycle pixel minval: %g", minval);
2123 
2124  end_skip;
2125 
2126  return CPL_ERROR_NONE;
2127 }
2128 
2129 /*----------------------------------------------------------------------------*/
2141 /*----------------------------------------------------------------------------*/
2142 static
2143 cpl_image ** visir_img_collapse_beam_four(cpl_propertylist * qclist,
2144  const cpl_image * self,
2145  const cpl_image * inverse,
2146  double eccmax,
2147  double pthrow,
2148  double angle,
2149  const cpl_propertylist * plist)
2150 {
2151 
2152  cpl_image ** combined = NULL;
2153  const int nx = cpl_image_get_size_x(self);
2154  const int ny = cpl_image_get_size_y(self);
2155  const cpl_type type = cpl_image_get_type(self);
2156  cpl_imagelist * list4 = cpl_imagelist_new();
2157  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2158  cpl_image * swrap = type == CPL_TYPE_DOUBLE
2159  ? cpl_image_wrap_double(nx, ny, cpl_image_get_data((cpl_image*)self))
2160  : cpl_image_wrap_float(nx, ny, cpl_image_get_data((cpl_image*)self));
2161  cpl_image * iwrap = type == CPL_TYPE_DOUBLE
2162  ? cpl_image_wrap_double(nx, ny, cpl_image_get_data((cpl_image*)inverse))
2163  : cpl_image_wrap_float(nx, ny, cpl_image_get_data((cpl_image*)inverse));
2164  IRPLIB_DIAG_PRAGMA_POP;
2165  cpl_bivector * offs = cpl_bivector_new(4);
2166  double * x4 = cpl_bivector_get_x_data(offs);
2167  double * y4 = cpl_bivector_get_y_data(offs);
2168  double pos_x, pos_y;
2169 
2170  skip_if (0);
2171 
2172  skip_if(plist == NULL);
2173 
2174  skip_if(visir_img_find_beam_four(qclist, self, inverse, eccmax, pthrow,
2175  angle, x4, y4));
2176 
2177  /* Combine the four beams */
2178  for (int i = 1; i < 4; i++) {
2179  x4[i] = x4[0] - x4[i];
2180  y4[i] = y4[0] - y4[i];
2181  }
2182 
2183  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM XPOS",
2184  x4[0]));
2185  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM YPOS",
2186  y4[0]));
2187  x4[0] = y4[0] = 0.0;
2188 
2189  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2190  bug_if(cpl_imagelist_set(list4, (cpl_image*)self, 0));
2191  bug_if(cpl_imagelist_set(list4, swrap, 1));
2192  bug_if(cpl_imagelist_set(list4, (cpl_image*)inverse, 2));
2193  bug_if(cpl_imagelist_set(list4, iwrap, 3));
2194  IRPLIB_DIAG_PRAGMA_POP;
2195 
2196  combined = cpl_geom_img_offset_saa(list4, offs, CPL_KERNEL_DEFAULT, 0, 0,
2197  CPL_GEOM_FIRST, &pos_x, &pos_y);
2198 
2199  skip_if(combined == NULL);
2200 
2201  end_skip;
2202 
2203  cpl_bivector_delete(offs);
2204  visir_imagelist_unwrap(list4);
2205  (void)cpl_image_unwrap(swrap);
2206  (void)cpl_image_unwrap(iwrap);
2207  if (cpl_error_get_code() && combined != NULL) {
2208  cpl_image_delete(combined[0]);
2209  cpl_image_delete(combined[1]);
2210  cpl_free(combined);
2211  }
2212 
2213  return combined;
2214 }
2215 
2216 /*----------------------------------------------------------------------------*/
2230 /*----------------------------------------------------------------------------*/
2231 visir_chopnod_mode visir_img_find_beam(cpl_propertylist * qclist,
2232  const cpl_image * self,
2233  const cpl_image * inverse,
2234  const cpl_propertylist * plist,
2235  const cpl_parameterlist * parlist,
2236  const char * recipename,
2237  double x4[],
2238  double y4[])
2239 {
2240 
2241  cpl_errorstate cleanstate = cpl_errorstate_get();
2242  visir_chopnod_mode mode = VISIR_CHOPNOD_AUTO; /* Assume failure */
2243 
2244  const double eccmax = visir_parameterlist_get_double(parlist, recipename,
2245  VISIR_PARAM_ECCMAX);
2246 
2247  const char * sdir;
2248 
2249  /* Get the chopping throw in pixels */
2250  const double pscale = visir_pfits_get_pixscale(plist);
2251  const double pthrow = pscale > 0.0
2252  ? visir_pfits_get_chop_throw(plist) / pscale : 0.0;
2253  const cpl_boolean chop_on =
2254  cpl_propertylist_get_bool(plist, VISIR_PFITS_BOOL_CHOP_STATUS);
2255  double angle = visir_pfits_get_chop_posang(plist);
2256 
2257  skip_if(x4 == NULL);
2258  skip_if(y4 == NULL);
2259  skip_if(self == NULL);
2260  skip_if(inverse == NULL);
2261  skip_if(parlist == NULL);
2262  skip_if(recipename == NULL);
2263  skip_if(qclist == NULL);
2264 
2265  sdir = visir_pfits_get_chopnod_dir(plist);
2266 
2267  if (sdir != NULL && !strcmp(sdir, "PERPENDICULAR")) {
2268 
2269  /* 4 sources - expected and detected */
2270  mode = VISIR_CHOPNOD_PERPENDICULAR;
2271 
2272  if (chop_on)
2273  skip_if (visir_img_find_beam_four(qclist, self, inverse, eccmax,
2274  pthrow, angle, x4, y4));
2275  else
2276  skip_if (visir_img_find_beam_two(qclist, self, inverse, eccmax,
2277  pthrow, angle, x4, y4, CPL_TRUE));
2278 
2279  } else if (sdir != NULL && !strcmp(sdir, "PARALLEL")) {
2280 
2281  /* 3 sources - expected and detected */
2282  mode = VISIR_CHOPNOD_PARALLEL;
2283 
2284  if (chop_on)
2285  skip_if (visir_img_find_beam_three(qclist, self, inverse, eccmax,
2286  pthrow, angle, x4, y4));
2287  else
2288  skip_if (visir_img_find_beam_two(qclist, self, inverse, eccmax,
2289  pthrow, angle, x4, y4, CPL_FALSE));
2290 
2291  } else {
2292  if (sdir == NULL) {
2293  visir_error_reset("Could not get FITS key");
2294  } else {
2295  cpl_msg_warning(cpl_func, "Unknown chopping direction: %s",
2296  sdir);
2297  }
2298  cpl_msg_warning(cpl_func, "Proceeding as if FITS card "
2299  VISIR_PFITS_STRING_CHOPNOD_DIR " had value: %s",
2300  "PERPENDICULAR");
2301 
2302  if (visir_img_find_beam_four(qclist, self, inverse, eccmax,
2303  pthrow, angle, x4, y4)) {
2304 
2305  visir_error_reset("Proceeding as if FITS card "
2306  VISIR_PFITS_STRING_CHOPNOD_DIR
2307  " had value: %s", "PARALLEL");
2308 
2309  skip_if (visir_img_find_beam_three(qclist, self, inverse,
2310  eccmax, pthrow, angle,
2311  x4, y4));
2312 
2313  /* 3 sources - unexpected, but detected */
2314  mode = VISIR_CHOPNOD_PARALLEL;
2315 
2316  } else {
2317  /* 4 sources - unexpected, but detected */
2318  mode = VISIR_CHOPNOD_PERPENDICULAR;
2319  }
2320  }
2321 
2322  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM THROW",
2323  pthrow));
2324  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM THROW",
2325  "The throw in pixels (TEL CHOP THROW "
2326  "divided by INS PFOV)"));
2327  end_skip;
2328 
2329  return mode;
2330 }
2331 
2332 
2333 /*----------------------------------------------------------------------------*/
2346 /*----------------------------------------------------------------------------*/
2347 static
2348 cpl_error_code visir_img_find_beam_four(cpl_propertylist * qclist,
2349  const cpl_image * self,
2350  const cpl_image * inverse,
2351  double eccmax,
2352  double pthrow,
2353  double angle,
2354  double x4[],
2355  double y4[])
2356 {
2357 
2358  cpl_errorstate cleanstate = cpl_errorstate_get();
2359  cpl_apertures * appos = NULL;
2360  cpl_apertures * apneg = NULL;
2361  const double psigmas[] = {2.0, 1.0, 0.5};
2362  const int nsigmas = sizeof(psigmas)/sizeof(double);
2363  int isigma;
2364  int iappos2[] = {1, 2};
2365  int iapneg2[] = {1, 2};
2366 
2367  bug_if(0);
2368  skip_if(self == NULL);
2369  skip_if(qclist == NULL);
2370  skip_if(pthrow <= 0.0);
2371  skip_if(x4 == NULL);
2372  skip_if(y4 == NULL);
2373 
2374  cpl_msg_info(cpl_func, "Detecting the 4-beam object with %g pixel throw "
2375  "using %d sigma-levels ranging from %g down to %g", pthrow,
2376  nsigmas, psigmas[0], psigmas[nsigmas-1]);
2377 
2378  bug_if(0);
2379  for (isigma = 0; isigma < nsigmas; isigma++) {
2380  int npos = 0;
2381  int nneg = 0;
2382 
2383  /* Detect the (two) POSITIVE objects */
2384  cpl_apertures_delete(appos);
2385  appos = cpl_apertures_extract_sigma(self, psigmas[isigma]);
2386 
2387  if (appos != NULL) {
2388  npos = cpl_apertures_get_size(appos);
2389  }
2390 
2391 
2392  /* Detect the (two) NEGATIVE objects */
2393  cpl_apertures_delete(apneg);
2394  apneg = cpl_apertures_extract_sigma(inverse, psigmas[isigma]);
2395  if (apneg != NULL) {
2396  nneg = cpl_apertures_get_size(apneg);
2397  }
2398 
2399  cpl_msg_info(cpl_func, "Found %d positive (need 2) and %d negative "
2400  "(need 2) object(s) at sigma=%g (%d of %d)", npos, nneg,
2401  psigmas[isigma], 1+isigma, nsigmas);
2402 
2403  /* stop if it would take forever to check the eccentricities */
2404  error_if(npos * nneg > FIND_BEAM_MAX_APERTURES_SQR,
2405  CPL_ERROR_DATA_NOT_FOUND, "Too many objects found, aborting");
2406 
2407  if (eccmax > 0.0) {
2408  int ipos1;
2409  double eccbest = eccmax;
2410  double eccmin = DBL_MAX;
2411  double fluxbest = 0.0;
2412  double fluxecc = DBL_MAX;
2413  cpl_boolean is_first = CPL_TRUE;
2414 
2415 #ifdef _OPENMP
2416 #pragma omp parallel for private(ipos1)
2417 #endif
2418  for (ipos1 = 2; ipos1 < 1 + npos; ipos1++) {
2419  int ipos2, ineg1, ineg2;
2420  for (ipos2 = 1; ipos2 < ipos1; ipos2++) {
2421  for (ineg1 = 2; ineg1 < 1 + nneg; ineg1++) {
2422  for (ineg2 = 1; ineg2 < ineg1; ineg2++) {
2423  cpl_boolean swappos, swapneg;
2424  const double ecc
2425  = visir_img_check_box(appos, ipos1, ipos2,
2426  apneg, ineg1, ineg2,
2427  pthrow, angle, &swappos,
2428  &swapneg);
2429 
2430  const double flux
2431  = cpl_apertures_get_flux(appos, ipos1)
2432  + cpl_apertures_get_flux(appos, ipos2)
2433  + cpl_apertures_get_flux(apneg, ineg1)
2434  + cpl_apertures_get_flux(apneg, ineg2);
2435 
2436 
2437  if (ecc < 0.0 || flux <= 0.0 ||
2438  !cpl_errorstate_is_equal(cleanstate)) {
2439  irplib_error_recover(cleanstate, "Invalid 4-"
2440  "object (%d & %d of %d, "
2441  "%d & %d of %d)",
2442  ipos2, ipos1, npos,
2443  ineg2, ineg1, nneg);
2444  continue;
2445  }
2446 
2447 #ifdef _OPENMP
2448 #pragma omp critical(visir_img_find_beam_four_min)
2449 #endif
2450  if (ecc < eccmin)
2451  {
2452  eccmin = ecc;
2453  fluxecc = flux;
2454  }
2455 
2456  if (eccmax <= ecc) continue;
2457 
2458 #ifdef _OPENMP
2459 #pragma omp critical(visir_img_find_beam_four_ok)
2460 #endif
2461  if (is_first || ecc * fluxbest < eccbest * flux)
2462  {
2463  if (is_first) {
2464  is_first = CPL_FALSE;
2465  cpl_msg_info(cpl_func, "Found 4 object "
2466  "positions with throw-"
2467  "scaled eccentricity %g "
2468  "and flux %g", ecc, flux);
2469  } else {
2470  cpl_msg_info(cpl_func, "Found 4 object "
2471  "positions with throw-"
2472  "scaled eccentricity %g "
2473  "< %g and/or flux %g > %g",
2474  ecc, eccbest, flux, fluxbest);
2475  }
2476  eccbest = ecc;
2477  fluxbest = flux;
2478  iappos2[0] = swappos ? ipos2 : ipos1;
2479  iappos2[1] = swappos ? ipos1 : ipos2;
2480  iapneg2[0] = swapneg ? ineg2 : ineg1;
2481  iapneg2[1] = swapneg ? ineg1 : ineg2;
2482  }
2483  }
2484  }
2485  }
2486  }
2487  if (eccbest < eccmax) {
2488  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM "
2489  "ECCENTRICITY", eccbest));
2490 
2491  break;
2492  }
2493  if (eccmin < DBL_MAX) {
2494  cpl_msg_info(cpl_func, "Found 4 sigma-%g object positions with "
2495  "too large throw-scaled eccentricity %g >= %g and "
2496  "flux %g", psigmas[isigma], eccmin, eccmax,
2497  fluxecc);
2498  }
2499  } else if (npos >= 2 && nneg >= 2) {
2500  cpl_apertures_sort_by_flux(appos);
2501  cpl_apertures_sort_by_flux(apneg);
2502  break;
2503  }
2504 
2505  if (isigma + 1 < nsigmas) {
2506  irplib_error_recover(cleanstate, "4-Beam positions not found among "
2507  "%d postive and %d negative object(s) at "
2508  "sigma=%g, (%d of %d)", npos, nneg,
2509  psigmas[isigma], 1+isigma, nsigmas);
2510  }
2511  }
2512 
2513  error_if (isigma == nsigmas, CPL_ERROR_DATA_NOT_FOUND,
2514  "4-Beam positions not found w. %d sigma(s) down to %g",
2515  nsigmas, psigmas[nsigmas - 1]);
2516 
2517  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
2518  cpl_apertures_dump(appos, stdout);
2519  cpl_apertures_dump(apneg, stdout);
2520  }
2521 
2522  x4[0] = cpl_apertures_get_centroid_x(appos, iappos2[0]);
2523  y4[0] = cpl_apertures_get_centroid_y(appos, iappos2[0]);
2524  x4[1] = cpl_apertures_get_centroid_x(appos, iappos2[1]);
2525  y4[1] = cpl_apertures_get_centroid_y(appos, iappos2[1]);
2526 
2527  x4[2] = cpl_apertures_get_centroid_x(apneg, iapneg2[0]);
2528  y4[2] = cpl_apertures_get_centroid_y(apneg, iapneg2[0]);
2529  x4[3] = cpl_apertures_get_centroid_x(apneg, iapneg2[1]);
2530  y4[3] = cpl_apertures_get_centroid_y(apneg, iapneg2[1]);
2531 
2532  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM FLUX",
2533  cpl_apertures_get_flux(appos,
2534  iappos2[0])));
2535 
2536  cpl_msg_info(cpl_func, "Centroid of positive object 1 [pixel]: %g %g",
2537  x4[0], y4[0]);
2538  cpl_msg_info(cpl_func, "Centroid of positive object 2 [pixel]: %g %g",
2539  x4[1], y4[1]);
2540 
2541  cpl_msg_info(cpl_func, "Centroid of negative object 1 [pixel]: %g %g",
2542  x4[2], y4[2]);
2543  cpl_msg_info(cpl_func, "Centroid of negative object 2 [pixel]: %g %g",
2544  x4[3], y4[3]);
2545 
2546  cpl_msg_info(cpl_func, "Expected object distance (chop throw) [pixel]: %g",
2547  pthrow);
2548  cpl_msg_info(cpl_func, "Object Pos -> Pos x/y-distance [pixel]: %g %g",
2549  x4[1] - x4[0], y4[1] - y4[0]);
2550  cpl_msg_info(cpl_func, "Object Neg -> Neg x/y-distance [pixel]: %g %g",
2551  x4[3] - x4[2], y4[3] - y4[2]);
2552  cpl_msg_info(cpl_func, "Object Pos -> Pos angle [degrees]: %g",
2553  atan2(y4[1] - y4[0], x4[1] - x4[0]) * CPL_MATH_DEG_RAD);
2554  cpl_msg_info(cpl_func, "Object Neg -> Neg angle [degrees]: %g",
2555  atan2(y4[3] - y4[2], x4[3] - x4[2]) * CPL_MATH_DEG_RAD);
2556 
2557  end_skip;
2558 
2559  cpl_apertures_delete(appos);
2560  cpl_apertures_delete(apneg);
2561 
2562  return cpl_error_get_code();
2563 }
2564 
2565 /*----------------------------------------------------------------------------*/
2577 /*----------------------------------------------------------------------------*/
2578 static
2579 cpl_image ** visir_img_collapse_beam_three(cpl_propertylist * qclist,
2580  const cpl_image * self,
2581  const cpl_image * inverse,
2582  double eccmax,
2583  double pthrow,
2584  const cpl_propertylist * plist)
2585 {
2586 
2587  cpl_image ** combined = NULL;
2588  const int nx = cpl_image_get_size_x(self);
2589  const int ny = cpl_image_get_size_y(self);
2590  const cpl_type type = cpl_image_get_type(self);
2591  cpl_imagelist * list3 = cpl_imagelist_new();
2592  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2593  cpl_image * iwrap = type == CPL_TYPE_DOUBLE
2594  ? cpl_image_wrap_double(nx, ny, cpl_image_get_data((cpl_image*)inverse))
2595  : cpl_image_wrap_float(nx, ny, cpl_image_get_data((cpl_image*)inverse));
2596  IRPLIB_DIAG_PRAGMA_POP;
2597  cpl_bivector * offs = cpl_bivector_new(3);
2598  double * x3 = cpl_bivector_get_x_data(offs);
2599  double * y3 = cpl_bivector_get_y_data(offs);
2600  double pos_x, pos_y;
2601  double angle = visir_pfits_get_chop_posang(plist);
2602 
2603  skip_if (0);
2604 
2605  skip_if(plist == NULL);
2606 
2607  skip_if(visir_img_find_beam_three(qclist, self, inverse, eccmax, pthrow,
2608  angle, x3, y3));
2609 
2610  /* Combine the three beams */
2611  for (int i = 1; i < 3; i++) {
2612  x3[i] = x3[0] - x3[i];
2613  y3[i] = y3[0] - y3[i];
2614  }
2615  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM XPOS",
2616  x3[0]));
2617  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM YPOS",
2618  y3[0]));
2619 
2620  x3[0] = y3[0] = 0.0;
2621 
2622  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2623  bug_if(cpl_imagelist_set(list3, (cpl_image*)self, 0));
2624  bug_if(cpl_imagelist_set(list3, (cpl_image*)inverse, 1));
2625  bug_if(cpl_imagelist_set(list3, iwrap, 2));
2626  IRPLIB_DIAG_PRAGMA_POP;
2627 
2628  combined = cpl_geom_img_offset_saa(list3, offs, CPL_KERNEL_DEFAULT, 0, 0,
2629  CPL_GEOM_FIRST, &pos_x, &pos_y);
2630 
2631  skip_if(combined == NULL);
2632 
2633  end_skip;
2634 
2635  cpl_bivector_delete(offs);
2636  visir_imagelist_unwrap(list3);
2637  (void)cpl_image_unwrap(iwrap);
2638  if (cpl_error_get_code() && combined != NULL) {
2639  cpl_image_delete(combined[0]);
2640  cpl_image_delete(combined[1]);
2641  cpl_free(combined);
2642  }
2643 
2644  return combined;
2645 }
2646 
2647 
2648 /*----------------------------------------------------------------------------*/
2662 /*----------------------------------------------------------------------------*/
2663 static
2664 cpl_error_code visir_img_find_beam_three(cpl_propertylist * qclist,
2665  const cpl_image * self,
2666  const cpl_image * inverse,
2667  double eccmax,
2668  double pthrow,
2669  double angle,
2670  double x3[],
2671  double y3[])
2672 {
2673 
2674  cpl_errorstate cleanstate = cpl_errorstate_get();
2675  cpl_apertures * appos = NULL;
2676  cpl_apertures * apneg = NULL;
2677  const double psigmas[] = {2.0, 1.0, 0.5};
2678  const int nsigmas = sizeof(psigmas)/sizeof(double);
2679  int isigma;
2680  int iappos [] = {1};
2681  int iapneg2[] = {1, 2};
2682 
2683  bug_if(0);
2684  skip_if(self == NULL);
2685  skip_if(qclist == NULL);
2686  skip_if(pthrow <= 0.0);
2687  skip_if(eccmax < 0.0);
2688  skip_if(x3 == NULL);
2689  skip_if(y3 == NULL);
2690 
2691 
2692  cpl_msg_info(cpl_func, "Detecting the 3-beam object with %g pixel throw "
2693  "using %d sigma-levels ranging from %g down to %g", pthrow,
2694  nsigmas, psigmas[0], psigmas[nsigmas-1]);
2695 
2696  bug_if(0);
2697  for (isigma = 0; isigma < nsigmas; isigma++) {
2698  int npos = 0;
2699  int nneg = 0;
2700 
2701  /* Detect the (single) POSITIVE object */
2702  cpl_apertures_delete(appos);
2703  appos = cpl_apertures_extract_sigma(self, psigmas[isigma]);
2704 
2705  if (appos != NULL) {
2706  npos = cpl_apertures_get_size(appos);
2707  }
2708 
2709 
2710  /* Detect the (two) NEGATIVE objects */
2711  cpl_apertures_delete(apneg);
2712  apneg = cpl_apertures_extract_sigma(inverse, psigmas[isigma]);
2713  if (apneg != NULL) {
2714  nneg = cpl_apertures_get_size(apneg);
2715  }
2716 
2717  cpl_msg_info(cpl_func, "Found %d positive (need 1) and %d negative "
2718  "(need 2) object(s) at sigma=%g (%d of %d)", npos, nneg,
2719  psigmas[isigma], 1+isigma, nsigmas);
2720 
2721  if (eccmax > 0.0) {
2722  int ipos;
2723  double eccbest = eccmax;
2724  double eccmin = DBL_MAX;
2725  double fluxbest = 0.0;
2726  double fluxecc = DBL_MAX;
2727  cpl_boolean is_first = CPL_TRUE;
2728 
2729 #ifdef _OPENMP
2730 #pragma omp parallel for private(ipos)
2731 #endif
2732  for (ipos = 1; ipos < 1 + npos; ipos++) {
2733  int ineg1, ineg2;
2734  for (ineg1 = 2; ineg1 < 1 + nneg; ineg1++) {
2735  for (ineg2 = 1; ineg2 < ineg1; ineg2++) {
2736  cpl_boolean swapneg;
2737  /* Find best ecc */
2738  const double ecc
2739  = visir_img_check_align(appos, ipos, apneg, ineg1,
2740  ineg2, pthrow, angle,
2741  &swapneg);
2742 
2743  const double flux
2744  = cpl_apertures_get_flux(appos, ipos)
2745  + cpl_apertures_get_flux(apneg, ineg1)
2746  + cpl_apertures_get_flux(apneg, ineg2);
2747 
2748 
2749  if (ecc < 0.0 || flux <= 0.0 ||
2750  !cpl_errorstate_is_equal(cleanstate)) {
2751  irplib_error_recover(cleanstate, "Invalid 3-"
2752  "object (%d of %d, "
2753  "%d & %d of %d)",
2754  ipos, npos,
2755  ineg2, ineg1, nneg);
2756  continue;
2757  }
2758 
2759 #ifdef _OPENMP
2760 #pragma omp critical(visir_img_collapse_beam_three_min)
2761 #endif
2762  if (ecc < eccmin)
2763  {
2764  eccmin = ecc;
2765  fluxecc = flux;
2766  }
2767 
2768  if (eccmax <= ecc) continue;
2769 
2770 #ifdef _OPENMP
2771 #pragma omp critical(visir_img_collapse_beam_three_ok)
2772 #endif
2773  if (is_first || ecc * fluxbest < eccbest * flux)
2774  {
2775  if (is_first) {
2776  is_first = CPL_FALSE;
2777  cpl_msg_info(cpl_func, "Found 3 object posi"
2778  "tions with throw-scaled "
2779  "eccentricity %g and flux %g",
2780  ecc, flux);
2781  } else {
2782  cpl_msg_info(cpl_func, "Found 3 object posi"
2783  "tions with throw-scaled "
2784  "eccentricity %g < %g and/or "
2785  "flux %g > %g", ecc, eccbest,
2786  flux, fluxbest);
2787  }
2788  eccbest = ecc;
2789  fluxbest = flux;
2790  iappos[0] = ipos;
2791  iapneg2[0] = swapneg ? ineg2 : ineg1;
2792  iapneg2[1] = swapneg ? ineg1 : ineg2;
2793  }
2794  }
2795  }
2796  }
2797  if (eccbest < eccmax) {
2798  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM "
2799  "ECCENTRICITY", eccbest));
2800  break;
2801  }
2802  if (eccmin < DBL_MAX) {
2803  cpl_msg_info(cpl_func, "Found 3 sigma-%g object positions with "
2804  "too large throw-scaled eccentricity %g >= %g and "
2805  "flux %g", psigmas[isigma], eccmin, eccmax,
2806  fluxecc);
2807  }
2808  } else if (npos >= 1 && nneg >= 2) {
2809  cpl_apertures_sort_by_flux(appos);
2810  cpl_apertures_sort_by_flux(apneg);
2811  break;
2812  }
2813 
2814  if (isigma + 1 < nsigmas) {
2815  irplib_error_recover(cleanstate, "3-Beam positions not found among "
2816  "%d postive and %d negative object(s) at "
2817  "sigma=%g, (%d of %d)", npos, nneg,
2818  psigmas[isigma], 1+isigma, nsigmas);
2819  }
2820  }
2821 
2822  error_if (isigma == nsigmas, CPL_ERROR_DATA_NOT_FOUND,
2823  "3-Beam positions not found w. %d sigma(s) down to %g",
2824  nsigmas, psigmas[nsigmas - 1]);
2825 
2826  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
2827  cpl_apertures_dump(appos, stdout);
2828  cpl_apertures_dump(apneg, stdout);
2829  }
2830 
2831  x3[0] = cpl_apertures_get_centroid_x(appos, iappos[0]);
2832  y3[0] = cpl_apertures_get_centroid_y(appos, iappos[0]);
2833 
2834  x3[1] = cpl_apertures_get_centroid_x(apneg, iapneg2[0]);
2835  y3[1] = cpl_apertures_get_centroid_y(apneg, iapneg2[0]);
2836  x3[2] = cpl_apertures_get_centroid_x(apneg, iapneg2[1]);
2837  y3[2] = cpl_apertures_get_centroid_y(apneg, iapneg2[1]);
2838 
2839  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM FLUX",
2840  cpl_apertures_get_flux(appos,
2841  iappos[0])));
2842 
2843  cpl_msg_info(cpl_func, "Centroid of positive object [pixel]: %g %g",
2844  x3[0], y3[0]);
2845 
2846  cpl_msg_info(cpl_func, "Centroid of negative object 1 [pixel]: %g %g",
2847  x3[1], y3[1]);
2848  cpl_msg_info(cpl_func, "Centroid of negative object 2 [pixel]: %g %g",
2849  x3[2], y3[2]);
2850 
2851  cpl_msg_info(cpl_func, "Expected object distance (chop throw) [pixel]: %g",
2852  pthrow);
2853  cpl_msg_info(cpl_func, "Object Neg1 -> Pos x/y-distance [pixel]: %g %g",
2854  x3[2] - x3[0], y3[2] - y3[0]);
2855  cpl_msg_info(cpl_func, "Object Pos -> Neg2 x/y-distance [pixel]: %g %g",
2856  x3[0] - x3[1], y3[0] - y3[1]);
2857 
2858  end_skip;
2859 
2860  cpl_apertures_delete(appos);
2861  cpl_apertures_delete(apneg);
2862 
2863  return cpl_error_get_code();
2864 }
2865 
2866 
2867 /*----------------------------------------------------------------------------*/
2883 /*----------------------------------------------------------------------------*/
2884 cpl_error_code visir_img_find_beam_two(cpl_propertylist * qclist,
2885  const cpl_image * self,
2886  const cpl_image * inverse,
2887  double eccmax,
2888  double pthrow,
2889  double angle,
2890  double x2[],
2891  double y2[],
2892  cpl_boolean is_hor)
2893 {
2894 
2895  cpl_errorstate cleanstate = cpl_errorstate_get();
2896  cpl_apertures * appos = NULL;
2897  cpl_apertures * apneg = NULL;
2898  const double psigmas[] = {2.0, 1.0, 0.5};
2899  const int nsigmas = sizeof(psigmas)/sizeof(double);
2900  int isigma;
2901  int iappos[] = {1};
2902  int iapneg[] = {1};
2903 
2904  bug_if(0);
2905  skip_if(self == NULL);
2906  skip_if(qclist == NULL);
2907  skip_if(eccmax < 0.0);
2908  skip_if(x2 == NULL);
2909  skip_if(y2 == NULL);
2910 
2911  if (pthrow > 0.0) {
2912  cpl_msg_info(cpl_func, "Detecting the 2-beam object (Pos -> Neg) with "
2913  "%g pixel throw using %d sigma-levels ranging from %g down"
2914  " to %g", pthrow, nsigmas, psigmas[0], psigmas[nsigmas-1]);
2915  } else if (pthrow < 0.0) {
2916  cpl_msg_info(cpl_func, "Detecting the 2-beam object (Neg -> Pos) with "
2917  "%g pixel throw using %d sigma-levels ranging from %g down"
2918  " to %g", pthrow, nsigmas, psigmas[0], psigmas[nsigmas-1]);
2919  } else {
2920  skip_if(1);
2921  }
2922 
2923  bug_if(0);
2924  for (isigma = 0; isigma < nsigmas; isigma++) {
2925  int npos = 0;
2926  int nneg = 0;
2927 
2928  /* Detect the (single) POSITIVE object */
2929  cpl_apertures_delete(appos);
2930  appos = cpl_apertures_extract_sigma(self, psigmas[isigma]);
2931 
2932  if (appos != NULL) {
2933  npos = cpl_apertures_get_size(appos);
2934  }
2935 
2936 
2937  /* Detect the NEGATIVE object */
2938  cpl_apertures_delete(apneg);
2939  apneg = cpl_apertures_extract_sigma(inverse, psigmas[isigma]);
2940  if (apneg != NULL) {
2941  nneg = cpl_apertures_get_size(apneg);
2942  }
2943 
2944  cpl_msg_info(cpl_func, "Found %d positive (need 1) and %d negative "
2945  "(need 1) object(s) at sigma=%g (%d of %d)", npos, nneg,
2946  psigmas[isigma], 1+isigma, nsigmas);
2947 
2948  if (eccmax > 0.0) {
2949  int ipos;
2950  double eccbest = eccmax;
2951  double eccmin = DBL_MAX;
2952  double fluxbest = 0.0;
2953  double fluxecc = DBL_MAX;
2954  cpl_boolean is_first = CPL_TRUE;
2955 
2956 #ifdef _OPENMP
2957 #pragma omp parallel for private(ipos)
2958 #endif
2959  for (ipos = 1; ipos < 1 + npos; ipos++) {
2960  int ineg;
2961  for (ineg = 1; ineg < 1 + nneg; ineg++) {
2962  const double ecc =
2963  visir_img_check_line(appos, ipos, apneg, ineg,
2964  pthrow, angle);
2965 
2966  const double flux
2967  = cpl_apertures_get_flux(appos, ipos)
2968  + cpl_apertures_get_flux(apneg, ineg);
2969 
2970 
2971  if (ecc < 0.0 || flux <= 0.0 ||
2972  !cpl_errorstate_is_equal(cleanstate)) {
2973  irplib_error_recover(cleanstate, "Invalid 2-"
2974  "object (%d of %d, "
2975  "%d of %d)",
2976  ipos, npos,
2977  ineg, nneg);
2978  continue;
2979  }
2980 
2981 #ifdef _OPENMP
2982 #pragma omp critical(visir_img_collapse_beam_two_min)
2983 #endif
2984  if (ecc < eccmin)
2985  {
2986  eccmin = ecc;
2987  fluxecc = flux;
2988  }
2989 
2990  if (eccmax <= ecc) continue;
2991 
2992 #ifdef _OPENMP
2993 #pragma omp critical(visir_img_collapse_beam_two_ok)
2994 #endif
2995  if (is_first || ecc * fluxbest < eccbest * flux)
2996  {
2997  if (is_first) {
2998  is_first = CPL_FALSE;
2999  cpl_msg_info(cpl_func, "Found 2 object posi"
3000  "tions with throw-scaled eccen"
3001  "tricity %g and flux %g", ecc,
3002  flux);
3003  } else {
3004  cpl_msg_info(cpl_func, "Found 2 object posi"
3005  "tions with throw-scaled eccen"
3006  "tricity %g < %g and/or flux %g "
3007  "> %g", ecc, eccbest, flux,
3008  fluxbest);
3009  }
3010  eccbest = ecc;
3011  fluxbest = flux;
3012  iappos[0] = ipos;
3013  iapneg[0] = ineg;
3014  }
3015  }
3016  }
3017  if (eccbest < eccmax) {
3018  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM "
3019  "ECCENTRICITY", eccbest));
3020  break;
3021  }
3022  if (eccmin < DBL_MAX) {
3023  cpl_msg_info(cpl_func, "Found 2 sigma-%g object positions with "
3024  "too large throw-scaled eccentricity %g >= %g and "
3025  "flux %g", psigmas[isigma], eccmin, eccmax,
3026  fluxecc);
3027  }
3028  } else if (npos >= 1 && nneg >= 2) {
3029  cpl_apertures_sort_by_flux(appos);
3030  cpl_apertures_sort_by_flux(apneg);
3031  break;
3032  }
3033 
3034  if (isigma + 1 < nsigmas) {
3035  irplib_error_recover(cleanstate, "2-Beam positions not found among "
3036  "%d postive and %d negative object(s) at "
3037  "sigma=%g, (%d of %d)", npos, nneg,
3038  psigmas[isigma], 1+isigma, nsigmas);
3039  }
3040  }
3041 
3042  error_if (isigma == nsigmas, CPL_ERROR_DATA_NOT_FOUND,
3043  "2-Beam positions not found w. %d sigma(s) down to %g",
3044  nsigmas, psigmas[nsigmas - 1]);
3045 
3046  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
3047  cpl_apertures_dump(appos, stdout);
3048  cpl_apertures_dump(apneg, stdout);
3049  }
3050 
3051  x2[0] = cpl_apertures_get_centroid_x(appos, iappos[0]);
3052  y2[0] = cpl_apertures_get_centroid_y(appos, iappos[0]);
3053 
3054  x2[1] = cpl_apertures_get_centroid_x(apneg, iapneg[0]);
3055  y2[1] = cpl_apertures_get_centroid_y(apneg, iapneg[0]);
3056 
3057  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM FLUX",
3058  cpl_apertures_get_flux(appos,
3059  iappos[0])));
3060 
3061  cpl_msg_info(cpl_func, "Centroid of positive object [pixel]: %g %g",
3062  x2[0], y2[0]);
3063 
3064  cpl_msg_info(cpl_func, "Centroid of negative object [pixel]: %g %g",
3065  x2[1], y2[1]);
3066 
3067  if (pthrow > 0.0) {
3068  cpl_msg_info(cpl_func, "Expected object distance (chop throw) "
3069  "[pixel]: %g", pthrow);
3070 
3071  cpl_msg_info(cpl_func, "Object Pos -> Neg x/y-distance [pixel]: %g %g",
3072  x2[1] - x2[0], y2[1] - y2[0]);
3073  } else {
3074  cpl_msg_info(cpl_func, "Expected object distance (chop throw) "
3075  "[pixel]: %g", -pthrow);
3076 
3077  cpl_msg_info(cpl_func, "Object Neg -> x/y-distance [pixel]: %g %g",
3078  x2[0] - x2[1], y2[0] - y2[1]);
3079  }
3080 
3081  end_skip;
3082 
3083  cpl_apertures_delete(appos);
3084  cpl_apertures_delete(apneg);
3085 
3086  return cpl_error_get_code();
3087 }
3088 
3089 
3090 
3091 /*----------------------------------------------------------------------------*/
3111 /*----------------------------------------------------------------------------*/
3112 double visir_img_check_box(const cpl_apertures * appos,
3113  int ipos1, int ipos2,
3114  const cpl_apertures * apneg,
3115  int ineg1, int ineg2, double ssize, double angle,
3116  cpl_boolean * pswapp, cpl_boolean * pswapn)
3117 {
3118 
3119  /* NB: Lower left pixel is (1, 1) */
3120 
3121  /* The two positive points */
3122  double xp1 = cpl_apertures_get_centroid_x(appos, ipos1) * cos(angle) -
3123  cpl_apertures_get_centroid_y(appos, ipos1) * sin(angle);
3124  double yp1 = cpl_apertures_get_centroid_x(appos, ipos1) * sin(angle) +
3125  cpl_apertures_get_centroid_y(appos, ipos1) * cos(angle);
3126  double xp2 = cpl_apertures_get_centroid_x(appos, ipos2) * cos(angle) -
3127  cpl_apertures_get_centroid_y(appos, ipos2) * sin(angle);
3128  double yp2 = cpl_apertures_get_centroid_x(appos, ipos2) * sin(angle) +
3129  cpl_apertures_get_centroid_y(appos, ipos2) * cos(angle);
3130 
3131  /* The leftmost positive point */
3132  const double xpl = xp1 < xp2 ? xp1 : xp2;
3133  const double ypl = xp1 < xp2 ? yp1 : yp2;
3134 
3135  /* The rightmost positive point */
3136  const double xpr = xp1 < xp2 ? xp2 : xp1;
3137  const double ypr = xp1 < xp2 ? yp2 : yp1;
3138 
3139  /* The two negative points */
3140  double xn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * cos(angle) -
3141  cpl_apertures_get_centroid_y(apneg, ineg1) * sin(angle);
3142  double yn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * sin(angle) +
3143  cpl_apertures_get_centroid_y(apneg, ineg1) * cos(angle);
3144  double xn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * cos(angle) -
3145  cpl_apertures_get_centroid_y(apneg, ineg2) * sin(angle);
3146  double yn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * sin(angle) +
3147  cpl_apertures_get_centroid_y(apneg, ineg2) * cos(angle);
3148 
3149  /* The leftmost negative point */
3150  const double x_nl = xn1 < xn2 ? xn1 : xn2;
3151  const double y_nl = xn1 < xn2 ? yn1 : yn2;
3152 
3153  /* The rightmost negative point */
3154  const double xnr = xn1 < xn2 ? xn2 : xn1;
3155  const double ynr = xn1 < xn2 ? yn2 : yn1;
3156 
3157  const double lx1 = xnr - xpl; /* The length of the top x-side */
3158  const double lx2 = xpr - x_nl; /* The length of the bottom x-side */
3159  const double ly1 = ypl - y_nl; /* The length of the left y-side */
3160  const double ly2 = ynr - ypr; /* The length of the right y-side */
3161 
3162  const double dx1 = lx1 - ssize;
3163  const double dx2 = lx2 - ssize;
3164  const double dy1 = ly1 - ssize;
3165  const double dy2 = ly2 - ssize;
3166 
3167  const double ey1 = ynr - ypl; /* The displacement in the top x-side */
3168  const double ey2 = ypr - y_nl; /* The displacement in the bottom x-side */
3169  const double ex1 = xpl - x_nl; /* The displacement in the left y-side */
3170  const double ex2 = xpr - xnr; /* The displacement in the right y-side */
3171 
3172  const double ok = sqrt(dx1 * dx1 + dx2 * dx2 + dy1 * dy1 + dy2 * dy2 +
3173  ex1 * ex1 + ex2 * ex2 + ey1 * ey1 + ey2 * ey2);
3174 
3175  double result = -1.0; /* Assume failure */
3176 
3177  skip_if(0); /* Catches NULL apertures and illegal index input */
3178 
3179  skip_if(pswapp == NULL);
3180  skip_if(pswapn == NULL);
3181  skip_if(appos == apneg);
3182  skip_if(ipos1 == ipos2);
3183  skip_if(ineg1 == ineg2);
3184 
3185  skip_if(ssize <= 0.0);
3186 
3187  *pswapp = xp1 < xp2 ? CPL_FALSE : CPL_TRUE;
3188  *pswapn = xn1 < xn2 ? CPL_FALSE : CPL_TRUE;
3189 
3190  result = ok/ssize; /* OK to divide now */
3191 
3192  end_skip;
3193 
3194  return result;
3195 
3196 }
3197 
3198 
3199 /*----------------------------------------------------------------------------*/
3218 /*----------------------------------------------------------------------------*/
3219 double visir_img_check_align(const cpl_apertures * appos, int ipos,
3220  const cpl_apertures * apneg, int ineg1, int ineg2,
3221  double ssize, double angle,
3222  cpl_boolean * pswapn)
3223 {
3224 
3225  /* NB: Lower left pixel is (1, 1) */
3226 
3227  /* The positive point */
3228  double xp = cpl_apertures_get_centroid_x(appos, ipos) * cos(angle) -
3229  cpl_apertures_get_centroid_y(appos, ipos) * sin(angle);
3230  double yp = cpl_apertures_get_centroid_x(appos, ipos) * sin(angle) +
3231  cpl_apertures_get_centroid_y(appos, ipos) * cos(angle);
3232 
3233  /* The two negative points */
3234  double xn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * cos(angle) -
3235  cpl_apertures_get_centroid_y(apneg, ineg1) * sin(angle);
3236  double yn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * sin(angle) +
3237  cpl_apertures_get_centroid_y(apneg, ineg1) * cos(angle);
3238  double xn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * cos(angle) -
3239  cpl_apertures_get_centroid_y(apneg, ineg2) * sin(angle);
3240  double yn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * sin(angle) +
3241  cpl_apertures_get_centroid_y(apneg, ineg2) * cos(angle);
3242 
3243  double result = -1.0; /* Assume failure */
3244 
3245  double ok;
3246 
3247  cpl_boolean swapn;
3248 
3249  /* The lower negative point */
3250  const double x_nl = yn1 < yn2 ? xn1 : xn2;
3251  const double y_nl = yn1 < yn2 ? yn1 : yn2;
3252 
3253  /* The upper negative point */
3254  const double xnr = yn1 < yn2 ? xn2 : xn1;
3255  const double ynr = yn1 < yn2 ? yn2 : yn1;
3256 
3257  const double d1 = ynr - yp - ssize; /* The upper length deviation */
3258  const double d2 = yp - y_nl - ssize; /* The lower length deviation */
3259 
3260  const double e1 = xnr - xp; /* The upper orthogonal deviation */
3261  const double e2 = xp - x_nl; /* The lower orthogonal deviation */
3262 
3263  swapn = yn1 < yn2 ? CPL_FALSE : CPL_TRUE;
3264 
3265  ok = sqrt(d1 * d1 + d2 * d2 + e1 * e1 + e2 * e2);
3266 
3267  skip_if(0); /* Catches NULL apertures and illegal index input */
3268 
3269  skip_if(pswapn == NULL);
3270  skip_if(appos == apneg);
3271  skip_if(ineg1 == ineg2);
3272 
3273  skip_if(ssize <= 0.0);
3274 
3275  *pswapn = swapn;
3276 
3277  result = ok/ssize; /* OK to divide now */
3278 
3279  end_skip;
3280 
3281  return result;
3282 
3283 }
3284 
3285 
3286 /*----------------------------------------------------------------------------*/
3303 /*----------------------------------------------------------------------------*/
3304 double visir_img_check_line(const cpl_apertures * apnear, int inear,
3305  const cpl_apertures * apfar, int ifar,
3306  double ssize, double angle)
3307 {
3308 
3309  /* NB: Lower left pixel is (1, 1) */
3310 
3311  /* The near point */
3312  double x_n = cpl_apertures_get_centroid_x(apnear, inear) * cos(angle) -
3313  cpl_apertures_get_centroid_y(apnear, inear) * sin(angle);
3314  double y_n = cpl_apertures_get_centroid_x(apnear, inear) * sin(angle) +
3315  cpl_apertures_get_centroid_y(apnear, inear) * cos(angle);
3316 
3317  /* The far point */
3318  double xf = cpl_apertures_get_centroid_x(apfar, ifar) * cos(angle) -
3319  cpl_apertures_get_centroid_y(apfar, ifar) * sin(angle);
3320  double yf = cpl_apertures_get_centroid_x(apfar, ifar) * sin(angle) +
3321  cpl_apertures_get_centroid_y(apfar, ifar) * cos(angle);
3322 
3323  double result = -1.0; /* Assume failure */
3324 
3325  double ok;
3326 
3327  const double d = yf - y_n - ssize; /* The length deviation */
3328 
3329  const double e = xf - x_n; /* The orthogonal deviation */
3330 
3331  ok = sqrt(d * d + e * e);
3332 
3333  skip_if(0); /* Catches NULL apertures and illegal index input */
3334 
3335  skip_if(apnear == apfar);
3336 
3337  skip_if(ssize <= 0.0);
3338 
3339  result = ok/ssize; /* OK to divide now */
3340 
3341  end_skip;
3342 
3343  return result;
3344 
3345 }
3346 
3347 
3348 /*----------------------------------------------------------------------------*/
3359 /*----------------------------------------------------------------------------*/
3360 static cpl_error_code
3361 get_aqu_data_type(const cpl_frame * frame, const cpl_propertylist * plist,
3362  const cpl_size next, visir_data_type * ptype)
3363 {
3364  int naxis3 = -1;
3365  const char * format = NULL;
3366  if (cpl_propertylist_has(plist, VISIR_PFITS_INT_NAXIS3))
3367  naxis3 = visir_pfits_get_naxis3(plist);
3368 
3369  if (cpl_propertylist_has(plist, "ESO DET FRAM FORMAT"))
3370  format = cpl_propertylist_get_string(plist, "ESO DET FRAM FORMAT");
3371 
3372  /* HCCYLE data must have at least HCYCLE[12] and optional INT extensions */
3373  if (next >= 2 || (format && !strcmp(format, "extension"))) {
3374  /* if only one extension we only have an INT frame */
3375  if (next == 1) {
3376  *ptype = VISIR_DATA_AQU_INT;
3377  }
3378  else {
3379  *ptype = VISIR_DATA_AQU_HCYCLE;
3380  }
3381  }
3382  else if (next == 1 && (naxis3 == -1 || naxis3 == 0)) {
3383  *ptype = VISIR_DATA_AQU_BURST_EXT;
3384  }
3385  else if (next == 0 && naxis3 > 0) {
3386  *ptype = VISIR_DATA_AQU_BURST;
3387  }
3388  else {
3389  cpl_error_set_message(cpl_func, CPL_ERROR_BAD_FILE_FORMAT,
3390  "Could not determine format of aquarius file %s",
3391  cpl_frame_get_filename(frame));
3392  }
3393 
3394  return cpl_error_get_code();
3395 }
3396 
3397 
3398 /*----------------------------------------------------------------------------*/
3409 /*----------------------------------------------------------------------------*/
3410 static cpl_error_code
3411 get_drs_data_type(const cpl_frame * frame, const cpl_propertylist * plist,
3412  visir_data_type * ptype)
3413 {
3414  cpl_errorstate cleanstate = cpl_errorstate_get();
3415  const char * sval = visir_pfits_get_frame_type(plist);
3416  const int naxis3 = visir_pfits_get_naxis3(plist);
3417  const int nchop = visir_pfits_get_chop_ncycles(plist);
3418  const int ndit = visir_pfits_get_ndit(plist);
3419  const char * file = cpl_frame_get_filename(frame);
3420  cpl_boolean known_frametype = CPL_TRUE;
3421  /* If nchop == 1 (and naxis3 == 3) the data unit is both a valid CUBE1
3422  and a valid CUBE2; Assume the frame type to be CUBE2 */
3423 
3424  cpl_ensure_code(ptype != NULL, CPL_ERROR_NULL_INPUT);
3425 
3426  if (sval == NULL) {
3427  /* Has warned about missing frame type card */
3428  visir_error_reset("Could not get FITS key");
3429  /* Don't know whether or not to expect CUBE1 or CUBE2 */
3430  known_frametype = CPL_FALSE;
3431  } else if (strcmp(sval, "CUBE1")==0) {
3432  *ptype = VISIR_DATA_CUBE1;
3433  } else if (strcmp(sval, "CUBE2")==0) {
3434  *ptype = VISIR_DATA_CUBE2;
3435  }
3436  else
3437  known_frametype = CPL_FALSE;
3438 
3439  /* verification and guessing */
3440  if (known_frametype && *ptype == VISIR_DATA_CUBE2) {
3441  if (naxis3 == 2 * nchop + 1) {
3442  /* OK. FRAME TYPE cannot be verified from naxis3 when nchop = 1 */
3443  } else if (naxis3 == nchop + 2) {
3444  cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3445  "and CHOP NCYCLES=%d imply a CUBE1. Assuming "
3446  "the frame type is really CUBE1", file, sval,
3447  naxis3, nchop);
3448  *ptype = VISIR_DATA_CUBE1;
3449  } else {
3450  cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3451  "and CHOP NCYCLES=%d is not a valid VISIR INTERM+"
3452  "Half-Cycle format", file, sval, naxis3, nchop);
3453  skip_if(1);
3454  }
3455  } else if (known_frametype && *ptype == VISIR_DATA_CUBE1) {
3456  if (naxis3 == nchop + 2) {
3457  /* OK. FRAME TYPE cannot be verified from naxis3 when nchop = 1 */
3458  if (nchop > 1)
3459  cpl_msg_debug(cpl_func, "%s has %d INTERM-frames and one Half-"
3460  "Cycle frame (old CUBE1-format)", file, nchop);
3461 
3462  } else if (naxis3 == 2 * nchop + 1) {
3463  cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3464  "and CHOP NCYCLES=%d imply a CUBE2. Assuming "
3465  "the frame type is really CUBE2", file, sval,
3466  naxis3, nchop);
3467  *ptype = VISIR_DATA_CUBE2;
3468  } else {
3469  cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3470  "and CHOP NCYCLES=%d is not a valid VISIR INTERM+"
3471  "Half-Cycle format", file, sval, naxis3, nchop);
3472  skip_if(1);
3473  }
3474  } else if (naxis3 == 2 * nchop + 1) {
3475  cpl_msg_warning(cpl_func, "%s has FRAM TYPE='%s', but NAXIS3=%d and "
3476  "CHOP NCYCLES=%d imply a CUBE2. Assuming the frame "
3477  "type is CUBE2", file, sval ? sval : "<NULL>", naxis3,
3478  nchop);
3479  *ptype = VISIR_DATA_CUBE2;
3480  } else if (naxis3 == nchop + 2) {
3481  cpl_msg_warning(cpl_func, "%s has FRAM TYPE='%s', but NAXIS3=%d and "
3482  "CHOP NCYCLES=%d imply a CUBE1. Assuming the frame "
3483  "type is CUBE1", file, sval ? sval : "<NULL>", naxis3,
3484  nchop);
3485  *ptype = VISIR_DATA_CUBE1;
3486  }
3487  else if (!known_frametype && nchop * ndit * 2 >= naxis3) {
3488  cpl_msg_info(cpl_func, "%s has FRAM TYPE='%s', NAXIS3=%d and "
3489  "CHOP NCYCLES=%d imply BURST data.",
3490  file, sval ? sval : "<NULL>", naxis3, nchop);
3491  *ptype = VISIR_DATA_BURST;
3492  } else {
3493  return cpl_error_set_message(cpl_func, CPL_ERROR_BAD_FILE_FORMAT,
3494  "%s has FRAM TYPE='%s', NAXIS3 = %d and "
3495  "CHOP NCYCLES = %d", file,
3496  sval ? sval : "<NULL>", naxis3, nchop);
3497  }
3498 
3499  end_skip;
3500 
3501  return cpl_error_get_code();
3502 }
3503 
3504 
3505 /*----------------------------------------------------------------------------*/
3515 /*----------------------------------------------------------------------------*/
3516 cpl_error_code visir_get_data_type(const cpl_frame * frame,
3517  const cpl_propertylist * plist,
3518  visir_data_type * ptype, cpl_size * pnext)
3519 {
3520  const cpl_size next = cpl_frame_get_nextensions(frame);
3521 
3522  cpl_ensure_code(ptype != NULL, CPL_ERROR_NULL_INPUT);
3523 
3524  skip_if(0);
3525 
3526  if (pnext)
3527  *pnext = next;
3528 
3529  if (cpl_propertylist_has(plist, "ESO DRS DTYPE"))
3530  *ptype = cpl_propertylist_get_int(plist, "ESO DRS DTYPE");
3531  else {
3532  if (cpl_propertylist_has(plist, VISIR_PFITS_INT_NAVRG))
3533  skip_if(get_aqu_data_type(frame, plist, next, ptype));
3534  else
3535  skip_if(get_drs_data_type(frame, plist, ptype));
3536  }
3537 
3538  end_skip;
3539 
3540  return cpl_error_get_code();
3541 }
3542 
3543 
3544 /*----------------------------------------------------------------------------*/
3552 /*----------------------------------------------------------------------------*/
3553 cpl_error_code visir_img_burst_find_delta_chop(const cpl_propertylist * self,
3554  int * ichopchange, int * ihalfcycle)
3555 {
3556 
3557  const char * sdateobs =
3558  cpl_propertylist_get_string(self, VISIR_PFITS_STRING_OBS_START);
3559  const char * schopstart =
3560  cpl_propertylist_get_string(self, VISIR_PFITS_STRING_CHOP_START);
3561  const double chop_freq = visir_pfits_get_chop_freq(self);
3562  const int nditskip =
3563  cpl_propertylist_get_int(self, VISIR_PFITS_INT_NDITSKIP);
3564  const double dit = visir_pfits_get_dit(self);
3565  double ddateobs, dchopstart;
3566  double period; /* Number of A+B frames in one full chopping cycle */
3567 
3568  skip_if(0);
3569 
3570  bug_if(irplib_wcs_mjd_from_string(&ddateobs, sdateobs));
3571  bug_if(irplib_wcs_mjd_from_string(&dchopstart, schopstart));
3572 
3573  skip_if(chop_freq <= 0.0);
3574  skip_if(dit <= 0.0);
3575 
3576  /* adapt for skipped exposures after obs start */
3577  ddateobs += dit * nditskip / (double)VISIR_SECS_PER_DAY;
3578 
3579  period = 1.0/(chop_freq * dit);
3580 
3581  /* FIXME: handle this case */
3582  error_if((int)(period + 0.5) % 2 != 0, CPL_ERROR_UNSUPPORTED_MODE,
3583  "Period %g not not an even number, chop frequency %g, dit %g",
3584  period, chop_freq, dit);
3585 
3586  error_if((int)(period + 0.5) <= 1, CPL_ERROR_ILLEGAL_INPUT,
3587  "Period %d < 1", (int)(period + 0.5));
3588 
3589  *ihalfcycle = (int)(period + 0.5)/2;
3590 
3591  cpl_msg_info(cpl_func, "Number of A+B frames in one full chopping cycle: %g",
3592  period);
3593 
3594  if (dchopstart < ddateobs) {
3595  double tchop = (ddateobs - dchopstart) * (double)VISIR_SECS_PER_DAY;
3596  /* Number of chopping cycles before obs+chopper start */
3597  double dprecycle = tchop * chop_freq;
3598  /* Phase to skip to point to first A frame
3599  in first complete chopping cycle */
3600  const double phase = ceil(dprecycle) - dprecycle;
3601 
3602  /* First valid frame is the frame at ddateobs (except for startindex) */
3603  *ichopchange = (int)ceil(phase * period) - 1;
3604 
3605  cpl_msg_info(cpl_func, "Chopping started %gs (%f cycles) before OBS start: "
3606  "%f < %f", tchop, dprecycle, dchopstart, ddateobs);
3607 
3608  } else if (ddateobs < dchopstart) {
3609  /* FIXME: Allowed ? */
3610  /* First valid frame is the frame at dchopstart (except for startindex) */
3611  double tchop = (dchopstart - ddateobs) * (double)VISIR_SECS_PER_DAY;
3612  *ichopchange = (int)ceil(tchop / dit) - 1;
3613  cpl_msg_info(cpl_func, "Chopping started %gs (wasted %g cycles) after OBS "
3614  "start: %f > %f", tchop, tchop * chop_freq, dchopstart,
3615  ddateobs);
3616  } else {
3617  /* FIXME: Allowed ? */
3618  /* First valid frame is the first frame (at both ddateobs and dchopstart)
3619  (except for startindex) */
3620  *ichopchange = 0;
3621  cpl_msg_info(cpl_func, "Chopping started with OBS start: %f == %f",
3622  dchopstart, ddateobs);
3623  }
3624 
3625  /* wrap value to cycle */
3626  *ichopchange = *ichopchange % (*ihalfcycle * 2);
3627 
3628  cpl_msg_info(cpl_func, "Frame of chop change: %d", *ichopchange);
3629 
3630  end_skip;
3631 
3632  return cpl_error_get_code();
3633 }
3634 
3635 #include "visir_destripe.c"
double visir_parameterlist_get_double(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR parameter of type double.
cpl_imagelist * irplib_imagelist_load_framelist(const irplib_framelist *self, cpl_type pixeltype, int planenum, int extnum)
Load an imagelist from a framelist.
double visir_pfits_get_cumoffsetx(const cpl_propertylist *self)
The cumulative offset in X.
Definition: visir_pfits.c:241
cpl_imagelist * visir_imagelist_load_last(const irplib_framelist *rawframes)
Load the last frame of the input files to an image list.
cpl_error_code visir_load_cube2_split(cpl_imagelist *alist, cpl_imagelist *blist, const irplib_framelist *rawframes, int pos, const int planestart, const int planeend)
Load and split the data in a CUBE2 file.
Definition: visir_inputs.c:238
int visir_pfits_get_naxis3(const cpl_propertylist *self)
The NAXIS3 key.
Definition: visir_pfits.c:520
cpl_error_code visir_image_reject_hot(cpl_image *self, const char *bpmfile)
Reject the hot pixels in an image.
cpl_error_code visir_destripe_image(cpl_image *self, int niter, double threshold, double thres_detect, cpl_boolean morpho)
Remove the stripes frome an image using iterations.
int visir_parameterlist_get_int(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR integer parameter.
cpl_image ** visir_img_recombine_list(const char *recipename, const cpl_parameterlist *parlist, cpl_imagelist *nodded, const cpl_propertylist **plists, cpl_geom_combine combine_mode, cpl_boolean *pdid_resize)
The VISIR imaging combination using cross correlation.
const char * visir_pfits_get_frame_type(const cpl_propertylist *self)
The frame type.
Definition: visir_pfits.c:395
cpl_boolean visir_parameterlist_get_bool(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR boolean parameter.
const cpl_propertylist * irplib_framelist_get_propertylist_const(const irplib_framelist *self, int pos)
Get the propertylist of the specified frame in the framelist.
cpl_image ** visir_img_collapse_beam(cpl_propertylist *qclist, const cpl_image *self, const cpl_parameterlist *parlist, const char *recipename, visir_chopnod_mode mode, const cpl_propertylist *plist)
Collapse the 3/4 beams of a combined image.
Definition: visir_inputs.c:504
int visir_pfits_get_chop_ncycles(const cpl_propertylist *self)
The number of chopping cycles.
Definition: visir_pfits.c:229
cpl_imagelist * visir_load_imagelist(const irplib_framelist *rawframes, int pos, cpl_boolean is_interm)
Load either a INTERM or Half-Cycle cube from a VISIR file.
double visir_pfits_get_chop_posang(const cpl_propertylist *self)
The chopping position angle in rad.
Definition: visir_pfits.c:197
const char * visir_parameterlist_get_string(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR string parameter.
cpl_imagelist * visir_inputs_combine(const char *recipename, const cpl_parameterlist *parlist, const irplib_framelist *rawframes, const char *badpix, const char *flat, int *nodding_p, cpl_boolean do_spc_fix, double wlen, visir_spc_resol resol)
The VISIR input data re-combination is performed here.
Definition: visir_inputs.c:686
int visir_pfits_get_ndit(const cpl_propertylist *self)
The NDIT keyword.
Definition: visir_pfits.c:479
double visir_pfits_get_cumoffsety(const cpl_propertylist *self)
The cumulative offset in Y.
Definition: visir_pfits.c:253
double visir_pfits_get_chop_freq(const cpl_propertylist *self)
The chopping frequency.
Definition: visir_pfits.c:162
double visir_pfits_get_chop_throw(const cpl_propertylist *self)
The chopping throw.
Definition: visir_pfits.c:144
const char * visir_pfits_get_nodpos(const cpl_propertylist *self)
The nodding position.
Definition: visir_pfits.c:619
double visir_pfits_get_dit(const cpl_propertylist *self)
The DIT.
Definition: visir_pfits.c:301
const cpl_frame * irplib_framelist_get_const(const irplib_framelist *self, int pos)
Get the specified frame from the framelist.
double visir_pfits_get_pixscale(const cpl_propertylist *self)
The pixel scale.
Definition: visir_pfits.c:631
double visir_pfits_get_chop_stat(const cpl_propertylist *self)
The chopping status.
Definition: visir_pfits.c:179
const char * visir_pfits_get_chopnod_dir(const cpl_propertylist *self)
The chopping direction.
Definition: visir_pfits.c:131
cpl_error_code irplib_framelist_contains(const irplib_framelist *self, const char *key, cpl_type type, cpl_boolean is_equal, double fp_tol)
Verify that a property is present for all frames.
cpl_imagelist * visir_load_hcycle(const irplib_framelist *rawframes, int pos)
Load the HCYCLE images from a VISIR file.
int irplib_framelist_get_size(const irplib_framelist *self)
Get the size of a framelist.
cpl_image ** visir_img_recombine(const char *recipename, const cpl_parameterlist *parlist, const irplib_framelist *rawframes, const char *badpix, const char *flat, cpl_geom_combine combine_mode, cpl_boolean *pdid_resize, cpl_boolean do_spc_fix, double wlen, visir_spc_resol resol)
The VISIR imaging combination using cross correlation.