VISIR Pipeline Reference Manual  4.1.7
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  int to_off = get_to_off_plane(ichopchange, planestart, ihalfcycle);
444 
445  cpl_msg_info(cpl_func, "Loading planes %d to %d, to off %d",
446  planestart, pend, planestart + to_off);
447 
448  packed = load_range(file, CPL_TYPE_UNSPECIFIED,
449  cpl_propertylist_has(plist, "ZNAXIS3") ? 1 : 0,
450  planestart, pend);
451 
452  skip_if(packed == NULL);
453  /* we don't need the input in the page cache anymore */
454  if (cpl_imagelist_get_size(packed) > 0) {
455  cpl_image * timg = cpl_imagelist_get(packed, 0);
456  size_t nbytes = visir_get_nbytes(timg);
457  visir_drop_cache(file, 0, nbytes * pend);
458  }
459 
460  skip_if(visir_load_burst_(alist, blist, packed, to_off, ihalfcycle,
461  trimlow, trimhigh));
462 
463  end_skip;
464 
465  cpl_imagelist_delete(packed);
466 
467  return cpl_error_get_code();
468 }
469 
470 
471 cpl_error_code
472 visir_load_burst_aqu(cpl_imagelist * alist, cpl_imagelist * blist,
473  const cpl_frame * frame, const cpl_propertylist * plist,
474  const int ihalfcycle,
475  const int planestart, const int planeend)
476 {
477  const char * file = cpl_frame_get_filename(frame);
478  const int naxis3 = visir_pfits_get_naxis3(plist);
479  const int pend = planeend <= 0 || planeend > naxis3 ? naxis3 : planeend;
480  cpl_imagelist * packed;
481 
482  cpl_msg_info(cpl_func, "Loading planes %d to %d",
483  planestart, pend);
484 
485  packed = load_range(file, CPL_TYPE_UNSPECIFIED,
486  cpl_propertylist_has(plist, "ZNAXIS3") ? 1 : 0,
487  planestart, pend);
488 
489  skip_if(packed == NULL);
490  /* we don't need the input in the page cache anymore */
491  if (cpl_imagelist_get_size(packed) > 0) {
492  cpl_image * timg = cpl_imagelist_get(packed, 0);
493  size_t nbytes = visir_get_nbytes(timg);
494  visir_drop_cache(file, 0, nbytes * pend);
495  }
496 
497  cpl_boolean bon = CPL_FALSE;
498  int it = 0;
499  for (size_t i = planestart; i < pend; i++) {
500  if (bon) {
501  cpl_image * image = cpl_imagelist_unset(packed, 0);
502  cpl_imagelist_set(alist, image, cpl_imagelist_get_size(alist));
503  }
504  else {
505  cpl_image * image = cpl_imagelist_unset(packed, 0);
506  cpl_imagelist_set(blist, image, cpl_imagelist_get_size(blist));
507  }
508  it++;
509  if (it == ihalfcycle){
510  bon = !bon;
511  it = 0;
512  }
513  }
514 
515  end_skip;
516 
517  cpl_imagelist_delete(packed);
518 
519  return cpl_error_get_code();
520 }
521 
522 
523 /*----------------------------------------------------------------------------*/
545 /*----------------------------------------------------------------------------*/
546 cpl_image ** visir_img_collapse_beam(cpl_propertylist * qclist,
547  const cpl_image * self,
548  const cpl_parameterlist * parlist,
549  const char * recipename,
550  visir_chopnod_mode mode,
551  const cpl_propertylist * plist)
552 {
553 
554  cpl_image ** combined = NULL;
555  /* Need to invert the negative beams */
556  cpl_image * inverse = cpl_image_multiply_scalar_create(self, -1.0);
557 
558  const double eccmax = visir_parameterlist_get_double(parlist, recipename,
559  VISIR_PARAM_ECCMAX);
560 
561  /* Get the chopping throw in pixels */
562  const double pscale = visir_pfits_get_pixscale(plist);
563  const double pthrow = pscale > 0.0
564  ? visir_pfits_get_chop_throw(plist) / pscale : 0.0;
565  double angle = visir_pfits_get_chop_posang(plist);
566 
567  skip_if(self == NULL);
568  skip_if(parlist == NULL);
569  skip_if(qclist == NULL);
570  skip_if(plist == NULL);
571 
572  if (mode == VISIR_CHOPNOD_PERPENDICULAR) {
573  /* 4 sources */
574  combined = visir_img_collapse_beam_four(qclist, self, inverse, eccmax,
575  pthrow, angle, plist);
576  } else if (mode == VISIR_CHOPNOD_PARALLEL) {
577  /* 3 sources */
578  combined = visir_img_collapse_beam_three(qclist, self, inverse, eccmax,
579  pthrow, plist);
580  } else if (mode == VISIR_CHOPNOD_AUTO) {
581  cpl_errorstate cleanstate = cpl_errorstate_get();
582 
583  const char * sdir = visir_pfits_get_chopnod_dir(plist);
584 
585  if (sdir != NULL && !strcmp(sdir, "PERPENDICULAR")) {
586  /* 4 sources */
587  combined = visir_img_collapse_beam_four(qclist, self, inverse,
588  eccmax, pthrow, angle, plist);
589  } else if (sdir != NULL && !strcmp(sdir, "PARALLEL")) {
590  /* 3 sources */
591  combined = visir_img_collapse_beam_three(qclist, self, inverse,
592  eccmax, pthrow, plist);
593  } else {
594  if (sdir == NULL) {
595  visir_error_reset("Could not get FITS key");
596  } else {
597  cpl_msg_warning(cpl_func, "Unknown chopping direction: %s",
598  sdir);
599  }
600  cpl_msg_warning(cpl_func, "Proceeding as if FITS card "
601  VISIR_PFITS_STRING_CHOPNOD_DIR " had value: %s",
602  "PERPENDICULAR");
603  combined = visir_img_collapse_beam_four(qclist, self, inverse,
604  eccmax, pthrow, angle, plist);
605  if (combined == NULL) {
606  visir_error_reset("Proceeding as if FITS card "
607  VISIR_PFITS_STRING_CHOPNOD_DIR
608  " had value: %s", "PARALLEL");
609  combined = visir_img_collapse_beam_three(qclist, self, inverse,
610  eccmax, pthrow, plist);
611  }
612  }
613  } else {
614  bug_if(1);
615  }
616 
617  skip_if(combined == NULL);
618 
619  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM THROW",
620  pthrow));
621  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM THROW",
622  "The throw in pixels (TEL CHOP THROW "
623  "divided by INS PFOV)"));
624 
625  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM XPOS",
626  "The X pixel position (centroid) "
627  "of the one-beam object"));
628 
629  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM YPOS",
630  "The Y pixel position (centroid) "
631  "of the one-beam object"));
632  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM ECCENTRICITY",
633  "Eccentricity: 0 for perfect, throw-"
634  "sized square/line"));
635 
636  end_skip;
637 
638  cpl_image_delete(inverse);
639 
640  return combined;
641 }
642 
643 
644 /*----------------------------------------------------------------------------*/
727 /*----------------------------------------------------------------------------*/
728 cpl_imagelist * visir_inputs_combine(const char * recipename,
729  const cpl_parameterlist * parlist,
730  const irplib_framelist * rawframes,
731  const char * badpix,
732  const char * flat,
733  int * nodding_p,
734  cpl_boolean do_spc_fix,
735  double wlen,
736  visir_spc_resol resol)
737 {
738  const char * fnodpos;
739  int nfiles;
740  cpl_imagelist * in = NULL;
741  cpl_image * collapsed = NULL;
742  cpl_image * prev = NULL;
743  cpl_vector * nods_vec = NULL;
744  double * nods_data;
745  int * nod_pos = NULL;
746  cpl_image ** images = NULL;
747  cpl_imagelist * nodded = NULL;
748  int nnod;
749  cpl_image * flat_image = NULL;
750  cpl_image * bpm_im_int = NULL;
751  cpl_mask * bpm_im_bin = NULL;
752  cpl_imagelist * hcycle = NULL;
753  cpl_boolean no_rem;
754  cpl_boolean is_nodding = CPL_FALSE;
755  int i, j;
756  cpl_boolean auto_bpm, rem_glitch, rem_bad;
757  int ndestripe;
758  int naxis1, naxis2;
759  cpl_boolean morpho_destripe;
760  double tstart, tstop;
761  const cpl_propertylist * plist1;
762 #ifdef _OPENMP
763  cpl_errorstate cleanstate = cpl_errorstate_get();
764 #endif
765  cpl_error_code didfail = CPL_ERROR_NONE;
766 
767 
768  skip_if (0);
769  skip_if(recipename == NULL);
770  skip_if(parlist == NULL);
771  skip_if(rawframes == NULL);
772 
773  /* Get the number of files */
774  nfiles = irplib_framelist_get_size(rawframes);
775 
776  /* There should be an even number of files */
777  if (nfiles % 2) {
778  cpl_msg_warning(cpl_func, "Expecting even number of files, "
779  "ignoring the last of %d file(s)", nfiles);
780  error_if (nfiles == 1, CPL_ERROR_DATA_NOT_FOUND,
781  "At least two files are required");
782  nfiles--;
783  }
784 
785  nnod = nfiles/2;
786 
787  skip_if (nnod <= 0);
788 
789  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_FRAME_TYPE,
790  CPL_TYPE_STRING, CPL_FALSE, 0.0));
791  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_DOUBLE_DIT,
792  CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
793  /* Can only combine images of the same size */
794  skip_if (irplib_framelist_contains(rawframes, "NAXIS1",
795  CPL_TYPE_INT, CPL_TRUE, 0.0));
796  skip_if (irplib_framelist_contains(rawframes, "NAXIS2",
797  CPL_TYPE_INT, CPL_TRUE, 0.0));
798  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_NAXIS3,
799  CPL_TYPE_INT, CPL_FALSE, 0.0));
800  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_CHOP_NCYCLES,
801  CPL_TYPE_INT, CPL_FALSE, 0.0));
802 
803  plist1 = irplib_framelist_get_propertylist_const(rawframes, 0);
804  naxis1 = irplib_pfits_get_int(plist1, "NAXIS1");
805  naxis2 = irplib_pfits_get_int(plist1, "NAXIS2");
806  skip_if(0);
807 
808  /* Retrieve input parameters */
809  fnodpos = visir_parameterlist_get_string(parlist, recipename,
810  VISIR_PARAM_NODPOS);
811  skip_if (0);
812 
813  auto_bpm = visir_parameterlist_get_bool(parlist, recipename,
814  VISIR_PARAM_AUTOBPM);
815  skip_if (0);
816 
817  rem_glitch = visir_parameterlist_get_bool(parlist, recipename,
818  VISIR_PARAM_GLITCH);
819  skip_if (0);
820 
821  rem_bad = visir_parameterlist_get_bool(parlist, recipename,
822  VISIR_PARAM_PURGE);
823  skip_if (0);
824 
825  ndestripe = visir_parameterlist_get_int(parlist, recipename,
826  VISIR_PARAM_STRIPITE);
827  bug_if (0);
828 
829  morpho_destripe = ndestripe <= 0 ? CPL_FALSE :
830  visir_parameterlist_get_bool(parlist, recipename,
831  VISIR_PARAM_STRIPMOR);
832  bug_if (0);
833 
834  no_rem = !rem_glitch && !rem_bad;
835 
836  /* Each file corresponds to a nodding position (object=1 or sky=-1) */
837  /* Return nod_pos array if requested */
838  nod_pos = nodding_p ? nodding_p : cpl_malloc(nfiles * sizeof(int));
839  j = 0;
840  if (!visir_str_par_is_empty(fnodpos)) {
841  /* Get the nodding positions from the user-provided ascii file */
842  nods_vec = cpl_vector_read(fnodpos);
843  skip_if (cpl_vector_get_size(nods_vec) != nfiles);
844  nods_data = cpl_vector_get_data(nods_vec);
845  skip_if (0);
846  for (i=0 ; i<nfiles ; i++) {
847  if ((int)nods_data[i] == 0) {
848  nod_pos[i] = 1;
849  j++;
850  } else if ((int)nods_data[i] == 1) {
851  nod_pos[i] = -1;
852  is_nodding = CPL_TRUE;
853  } else {
854  error_if(1, CPL_ERROR_BAD_FILE_FORMAT,
855  "Wrong values in line %d in %s", i+1, fnodpos);
856  }
857  }
858  } else {
859  skip_if (irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_NODPOS,
860  CPL_TYPE_STRING, CPL_FALSE, 0.0));
861  }
862 
863  if (no_rem) cpl_msg_info(cpl_func, "No glitch removal and no purge of bad "
864  "frames requested: Using fast I/O method");
865 
866  /* Initialize the Bad Pixel Map */
867  if (badpix != NULL) {
868  /* The bpm is provided by the user */
869  cpl_msg_info(cpl_func, "Loading bad pixel map from %s", badpix);
870  /* Load the bad pixels image */
871  bpm_im_int = cpl_image_load(badpix, CPL_TYPE_INT, 0, 0);
872  skip_if (0);
873 
874  /* Convert the map from integer to binary */
875  bpm_im_bin = cpl_mask_threshold_image_create(bpm_im_int, -0.5, 0.5);
876  cpl_image_delete(bpm_im_int);
877  bpm_im_int = NULL;
878  skip_if (cpl_mask_not(bpm_im_bin));
879  } else if (auto_bpm) {
880  /* Initialize the Bad Pixel Map using the hcycle */
881 
882  /* i == 0 */
883  hcycle = visir_load_imagelist(rawframes, 0, CPL_FALSE);
884  skip_if(0);
885 
886  bpm_im_bin =
887  cpl_mask_threshold_image_create(cpl_imagelist_get(hcycle,0),
888  VISIR_HCYCLE_BPM_THRESHOLD,
889  DBL_MAX);
890  cpl_imagelist_delete(hcycle);
891  hcycle = NULL;
892  skip_if(0);
893  }
894 
895  /* Initialize the flat field image */
896  if (flat != NULL) {
897  cpl_msg_info(cpl_func, "Divide the nodded images by the flatfield");
898  /* Load the flat image */
899  flat_image = cpl_image_load(flat, CPL_TYPE_FLOAT, 0, 0);
900  any_if ("Cannot load the flat field %s", flat ? flat : "<NULL>");
901  }
902 
903  /* Get nodding position (if needed) and DIT from the header */
904  nodded = cpl_imagelist_new();
905 
906  tstart = cpl_test_get_walltime();
907 
908  /* Fill list with empty images of proper size and type */
909 
910  for (i=0; i < nfiles/2 ; i++) {
911  cpl_image * empty = cpl_image_new(naxis1, naxis2, CPL_TYPE_FLOAT);
912 
913  /* i'th image can only be inserted when i-1'th is there,
914  which prevents parallelism */
915 
916  bug_if (cpl_imagelist_set(nodded, empty, i));
917  }
918 
919 #ifdef _OPENMP
920 #pragma omp parallel for private(i) firstprivate(prev, collapsed) \
921  schedule(static, 2)
922 #endif
923  for (i = 0; i < nfiles ; i++) {
924  cpl_error_code errori = cpl_error_get_code();
925 
926  /* The total number of iterations must be pre-determined for the
927  parallelism to work. In case of an error we can therefore not
928  break, so instead we skip immediately to the next iteration.
929  FIXME: This check on didfail does not guarantee that only one
930  iteration can cause an error to be dumped, but it is not
931  worse than checking on the thread-local state, errori. */
932  if (didfail) continue;
933 
934  do {
935 
936  const char * file =
937  cpl_frame_get_filename(irplib_framelist_get_const(rawframes, i));
938  const cpl_propertylist * plist;
939 
940  double dit;
941  double factor;
942 
943 
944  plist = irplib_framelist_get_propertylist_const(rawframes, i);
945  if (plist == NULL) {
946  errori = cpl_error_set_where(cpl_func);
947  break;
948  }
949 
950  if (nods_vec == NULL) {
951  const char * sval = visir_pfits_get_nodpos(plist);
952  if (sval == NULL) {
953  errori = cpl_error_set_message(cpl_func,
954  CPL_ERROR_DATA_NOT_FOUND,
955  "Cannot get nodding position "
956  "for file %d/%d", i+1, nfiles);
957  break;
958  }
959  if (!strcmp(sval, "A")) {
960  nod_pos[i] = 1;
961 # ifdef _OPENMP
962 # pragma omp atomic
963 # endif
964  j++;
965  } else {
966  nod_pos[i] = -1;
967  is_nodding = CPL_TRUE;
968  }
969  }
970 
971  /* Print the file name with its nodding position */
972  cpl_msg_info(cpl_func, "File %02d: %s (%c)", i+1, file,
973  nod_pos[i]==1 ? '+' : '-');
974 
975  /* With nodding each pair must have exactly one object observation */
976  if (is_nodding && (i & 1) == 1 && nod_pos[i] == nod_pos[i-1]) {
977  cpl_msg_error(cpl_func, "Nodding pair (%d,%d) does not comprise an "
978  "on-object (A) and an off-object (B) image: %s", i-1,
979  i, nod_pos[i] == 1 ? "A" : "B");
980  }
981 
982  /* Compute the normalization factor from the Detector Integration Time */
983  dit = visir_pfits_get_dit(plist);
984  if (cpl_error_get_code()) {
985  errori = cpl_error_set_where(cpl_func);
986  break;
987  }
988 
989  if (dit <= 0) {
990  errori = cpl_error_set_message(cpl_func,
991  CPL_ERROR_ILLEGAL_INPUT,
992  "DIT in file %d/%d is too small: "
993  "%g", i+1, nfiles, dit);
994  break;
995  }
996 
997  factor = dit * nod_pos[i] * 2.0;
998 
999  if (no_rem){
1000  collapsed = visir_load_average(file, plist);
1001  } else {
1002  in = visir_load_intermint(rawframes, i);
1003  if (in == NULL) {
1004  errori = cpl_error_set_message(cpl_func,
1005  CPL_ERROR_ILLEGAL_INPUT,
1006  "Could not load image set %d",
1007  i+1);
1008  break;
1009  }
1010 
1011  /* Convert the image lists from 'INTERM' to A-B' */
1012  if (visir_imagelist_unpack_interm(in)) {
1013  errori = cpl_error_set_message(cpl_func,
1014  cpl_error_get_code(),
1015  "Failure for file %d/%d",
1016  i+1, nfiles);
1017  break;
1018  }
1019 
1020  /* Remove the glitch in each A-B image in each input cube
1021  if requested */
1022  if (rem_glitch) {
1023  int jj;
1024  for (jj=0 ; jj < cpl_imagelist_get_size(in); jj++) {
1025  if (visir_rem_glitch(cpl_imagelist_get(in, jj))) {
1026  errori = cpl_error_set_message(cpl_func,
1027  cpl_error_get_code(),
1028  "Could not remove "
1029  "glitch in image %d in "
1030  "set %d", jj+1, i+1);
1031  break;
1032  }
1033  }
1034  }
1035 
1036  /* Remove the bad A-B images in each input file/cube if requested */
1037  if (rem_bad) {
1038  cpl_msg_info(cpl_func, "Remove the bad A-B input images");
1039  if (visir_rem_bad_images(in)) {
1040  errori = cpl_error_set_message(cpl_func,
1041  cpl_error_get_code(),
1042  "Could not remove bad "
1043  "images in list %d", i+1);
1044  break;
1045  }
1046  }
1047  /* Average each cube */
1048  collapsed = cpl_imagelist_collapse_create(in);
1049 
1050  cpl_imagelist_delete(in);
1051  in = NULL;
1052 
1053  }
1054 
1055  if (cpl_error_get_code()) {
1056  errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1057  "Failure for file %d/%d",
1058  i+1, nfiles);
1059  break;
1060  }
1061  /* Normalise to have ADU/s. */
1062  /* Also divide with 2 to achieve average of image pair */
1063  if (cpl_image_divide_scalar(collapsed, 2*factor)) {
1064  errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1065  "Failure for file %d/%d",
1066  i+1, nfiles);
1067  break;
1068  }
1069 
1070  /* Each pair of input files gives a nodded image in nodded */
1071  if (i & 1) {
1072  if (cpl_image_add(prev, collapsed)) {
1073  errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1074  "Failure for file %d/%d",
1075  i+1, nfiles);
1076  break;
1077  }
1078  cpl_image_delete(collapsed);
1079  collapsed = NULL;
1080 
1081  /* At this point prev is the image to be put into
1082  the list of nodded images */
1083 
1084  if (bpm_im_bin != NULL) {
1085  /* Apply the bad pixels cleaning */
1086  if (cpl_image_reject_from_mask(prev, bpm_im_bin)) {
1087  errori = cpl_error_set_message(cpl_func,
1088  cpl_error_get_code(),
1089  "Failure for file %d/%d",
1090  i+1, nfiles);
1091  break;
1092  }
1093  if (cpl_detector_interpolate_rejected(prev)) {
1094  errori = cpl_error_set_message(cpl_func,
1095  cpl_error_get_code(),
1096  "Failure for file %d/%d",
1097  i+1, nfiles);
1098  break;
1099  }
1100  }
1101 
1102  if (ndestripe > 0)
1103  if(visir_destripe_image(prev, ndestripe,
1104  VISIR_DESTRIPE_DETECT,
1105  VISIR_DESTRIPE_DETECT_THRESHOLD,
1106  morpho_destripe)) {
1107  errori = cpl_error_set_message(cpl_func,
1108  cpl_error_get_code(),
1109  "Failure for file %d/%d",
1110  i+1, nfiles);
1111  break;
1112  }
1113 
1114  if (flat_image != NULL) {
1115  /* Apply the flatfield correction */
1116  if (cpl_image_divide(prev, flat_image)) {
1117  errori = cpl_error_set_message(cpl_func,
1118  cpl_error_get_code(),
1119  "Failure for file %d/%d",
1120  i+1, nfiles);
1121  break;
1122  }
1123  }
1124 
1125  if (cpl_imagelist_set(nodded, prev, i/2)) {
1126  errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1127  "Failure for file %d/%d",
1128  i+1, nfiles);
1129  break;
1130  }
1131  prev = NULL;
1132  } else {
1133  prev = collapsed;
1134  collapsed = NULL;
1135  }
1136  } while (0);
1137 
1138  if (errori) {
1139 #ifdef _OPENMP
1140  /* Cannot access these errors after the join,
1141  so dump them now. :-(((((((((((((((((((( */
1142  cpl_errorstate_dump(cleanstate, CPL_FALSE, NULL);
1143  cpl_errorstate_set(cleanstate);
1144 
1145  /* Need to deallocate the thread-private instances in the loop */
1146  cpl_image_delete(prev); prev = NULL;
1147  cpl_image_delete(collapsed); collapsed = NULL;
1148 
1149 #pragma omp critical(visir_inputs_combine)
1150 #endif
1151  didfail = errori;
1152  }
1153  }
1154 
1155  error_if(didfail, didfail, "Failed to create %d nodded images from %d "
1156  "files", nnod, nfiles);
1157 
1158  tstop = cpl_test_get_walltime();
1159  cpl_msg_info(cpl_func, "Time to create %d nodded images [s]: %g", nnod,
1160  tstop - tstart);
1161 
1162  cpl_vector_delete(nods_vec);
1163  nods_vec = NULL;
1164 
1165  cpl_image_delete(flat_image);
1166  flat_image = NULL;
1167 
1168  cpl_mask_delete(bpm_im_bin);
1169  bpm_im_bin = NULL;
1170 
1171  if (nod_pos != nodding_p) cpl_free(nod_pos);
1172  nod_pos = NULL;
1173 
1174  error_if(is_nodding && j != nnod, CPL_ERROR_INCOMPATIBLE_INPUT,
1175  "With nodding exactly half of the images "
1176  "must be on-object, not %d of %d", j, 2*nnod);
1177 
1178  if (do_spc_fix) {
1179  const double ksi = visir_parameterlist_get_double(parlist, recipename,
1180  VISIR_PARAM_SPECSKEW);
1181  const double eps = visir_parameterlist_get_double(parlist, recipename,
1182  VISIR_PARAM_VERTARC);
1183  const double delta = visir_parameterlist_get_double(parlist, recipename,
1184  VISIR_PARAM_HORIARC);
1185  const double phi = visir_parameterlist_get_double(parlist, recipename,
1186  VISIR_PARAM_SLITSKEW);
1187  const int doplot = visir_parameterlist_get_int(parlist, recipename,
1188  VISIR_PARAM_PLOT);
1189 
1190 
1191  skip_if (0);
1192 
1193  images = cpl_malloc(nnod * sizeof(cpl_image*));
1194 
1195  for (j = 0; j < nnod; j++) images[j] = cpl_imagelist_get(nodded, j);
1196 
1197  skip_if (visir_spc_det_fix(images, nnod, CPL_TRUE, wlen, resol,
1198  phi, ksi, eps, delta, doplot));
1199  }
1200 
1201  end_skip;
1202 
1203  cpl_msg_set_time_off();
1204 
1205  cpl_free(images);
1206  cpl_imagelist_delete(in);
1207 
1208  if (nod_pos != nodding_p) cpl_free(nod_pos);
1209  cpl_vector_delete(nods_vec);
1210  cpl_image_delete(bpm_im_int);
1211  cpl_mask_delete(bpm_im_bin);
1212  cpl_image_delete(collapsed);
1213  cpl_image_delete(prev);
1214  if (cpl_error_get_code() && nodded != NULL) {
1215  cpl_imagelist_delete(nodded);
1216  nodded = NULL;
1217  }
1218 
1219  return nodded;
1220 }
1221 
1222 /*----------------------------------------------------------------------------*/
1231 /*----------------------------------------------------------------------------*/
1232 static double
1233 get_cumoffsets(const cpl_propertylist * plist, double * x, double * y)
1234 {
1235  cpl_errorstate cleanstate = cpl_errorstate_get();
1236 
1237  *x = irplib_pfits_get_double(plist, "ESO DRS CUMOFFSETX");
1238  *y = irplib_pfits_get_double(plist, "ESO DRS CUMOFFSETY");
1239 
1240  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1241  cpl_errorstate_set(cleanstate);
1242  cpl_msg_info(cpl_func, "DRS CUMOFFSET[XY] not found, falling back"
1243  " to SEQ CUMOFFSET[XY]");
1244  *x = visir_pfits_get_cumoffsetx(plist);
1245  *y = visir_pfits_get_cumoffsety(plist);
1246  }
1247  return cpl_error_get_code();
1248 }
1249 
1250 /*----------------------------------------------------------------------------*/
1265 /*----------------------------------------------------------------------------*/
1266 cpl_image ** visir_img_recombine_list(const char * recipename,
1267  const cpl_parameterlist * parlist,
1268  cpl_imagelist * nodded,
1269  const cpl_propertylist ** plists,
1270  cpl_geom_combine combine_mode,
1271  cpl_boolean * pdid_resize)
1272 {
1273  cpl_size nnod;
1274  cpl_bivector * offsets_est = NULL;
1275  cpl_bivector * objs = NULL;
1276  cpl_image ** combined = NULL;
1277  cpl_vector * sigmas = NULL;
1278  cpl_propertylist * qclist = cpl_propertylist_new();
1279 
1280 
1281  bug_if (0);
1282 
1283  bug_if (recipename == NULL);
1284  bug_if (parlist == NULL);
1285  bug_if (pdid_resize == NULL);
1286  bug_if (nodded == NULL);
1287 
1288  nnod = cpl_imagelist_get_size(nodded);
1289 
1290  /* If only one nodded image, the work is finished */
1291  if (nnod == 1) {
1292 
1293  combined = cpl_malloc(2*sizeof(cpl_image*));
1294  combined[1] = NULL; /* In case the unset fails */
1295 
1296  combined[0] = cpl_imagelist_unset(nodded, 0);
1297  bug_if (combined[0] == NULL);
1298 
1299  combined[1] = cpl_image_new(cpl_image_get_size_x(combined[0]),
1300  cpl_image_get_size_y(combined[0]),
1301  CPL_TYPE_INT);
1302  bug_if (combined[1] == NULL);
1303 
1304  /* Set all pixel values to 1 */
1305  bug_if(cpl_image_threshold(combined[1], 1.0, 1.0, 1.0, 1.0));
1306 
1307  *pdid_resize = CPL_FALSE;
1308 
1309  } else {
1310  const double psigmas[] = {5, 2, 1, 0.5};
1311  const char * sval;
1312  const char * offsets;
1313  const char * objects;
1314  int sx, sy, mx, my;
1315  int rej_low, rej_high;
1316  cpl_boolean refine;
1317 
1318 
1319  refine = visir_parameterlist_get_bool(parlist, recipename,
1320  VISIR_PARAM_REFINE);
1321  skip_if (0);
1322 
1323  offsets = visir_parameterlist_get_string(parlist, recipename,
1324  VISIR_PARAM_OFFSETS);
1325  skip_if (0);
1326 
1327  objects = visir_parameterlist_get_string(parlist, recipename,
1328  VISIR_PARAM_OBJECTS);
1329  skip_if (0);
1330 
1331  sval = visir_parameterlist_get_string(parlist, recipename,
1332  VISIR_PARAM_XCORR);
1333  skip_if (0);
1334 
1335  if (sscanf(sval, "%d-%d-%d-%d", &sx, &sy, &mx, &my) != 4)
1336  skip_if (sscanf(sval, "%d %d %d %d", &sx, &sy, &mx, &my) != 4);
1337 
1338 
1339  sval = visir_parameterlist_get_string(parlist, recipename,
1340  VISIR_PARAM_REJECT);
1341  skip_if (0);
1342 
1343  if (sscanf(sval, "%d-%d", &rej_low, &rej_high) !=2 )
1344  skip_if (sscanf(sval, "%d %d", &rej_low, &rej_high) !=2 );
1345 
1346  /* Get the offsets estimation of each input file pair */
1347  cpl_msg_info(cpl_func, "Get the offsets estimation");
1348  if (!visir_str_par_is_empty(offsets)) {
1349  /* A file has been provided on the command line */
1350  offsets_est = cpl_bivector_read(offsets);
1351  skip_if (offsets_est==NULL);
1352 
1353  error_if (cpl_bivector_get_size(offsets_est) != nnod,
1354  CPL_ERROR_BAD_FILE_FORMAT, "The offsets file %s must "
1355  "have %d entries, not %d", offsets, (int)nnod,
1356  (int)cpl_bivector_get_size(offsets_est));
1357  } else {
1358  double * offsets_est_x;
1359  double * offsets_est_y;
1360  double xoff0, yoff0;
1361 
1362  /* Get the offsets from the header */
1363  offsets_est = cpl_bivector_new(nnod);
1364  offsets_est_x = cpl_bivector_get_x_data(offsets_est);
1365  offsets_est_y = cpl_bivector_get_y_data(offsets_est);
1366 
1367  skip_if (0);
1368 
1369  offsets_est_x[0] = 0.0;
1370  offsets_est_y[0] = 0.0;
1371  get_cumoffsets(plists[0], &xoff0, &yoff0);
1372 
1373  for (cpl_size i = 1; i < nnod ; i++) {
1374  double xoff, yoff;
1375 
1376  skip_if(get_cumoffsets(plists[i], &xoff, &yoff));
1377 
1378  /* Subtract the first offset from all offsets */
1379  offsets_est_x[i] = xoff0 - xoff;
1380  offsets_est_y[i] = yoff0 - yoff;
1381  }
1382  }
1383 
1384  /* Read the provided objects file if provided
1385  - if a file has been provided on the command line */
1386  if (!visir_str_par_is_empty(objects)) {
1387  objs = cpl_bivector_read(objects);
1388  any_if ("Could not read objects from %s", objects);
1389  }
1390 
1391  cpl_msg_info(cpl_func, "Recombining the list of nodded images using "
1392  "mode: %d (I=%d:U=%d:F=%d), rej-lo=%d, rej-hi=%d",
1393  combine_mode, CPL_GEOM_INTERSECT, CPL_GEOM_UNION,
1394  CPL_GEOM_FIRST, rej_low, rej_high);
1395 
1396  if (cpl_msg_get_level() <= CPL_MSG_DEBUG) {
1397  cpl_msg_debug(cpl_func, "The offsets for the recombination:");
1398  cpl_bivector_dump(offsets_est, stdout);
1399  }
1400 
1401  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
1402  sigmas = cpl_vector_wrap(4, (double*)psigmas); /* Not changed */
1403  IRPLIB_DIAG_PRAGMA_POP;
1404  combined = cpl_geom_img_offset_combine(nodded, offsets_est, refine,
1405  objs, sigmas, NULL, sx, sy,
1406  mx, my, rej_low, rej_high,
1407  combine_mode);
1408  any_if("Could not recombine the images");
1409 
1410  *pdid_resize = (cpl_boolean)(cpl_image_get_size_x(combined[0])
1411  != cpl_image_get_size_x(cpl_imagelist_get_const(nodded, 0)) ||
1412  cpl_image_get_size_y(combined[0])
1413  != cpl_image_get_size_y(cpl_imagelist_get_const(nodded, 0)));
1414  }
1415 
1416  if (visir_parameterlist_get_int(parlist, recipename, VISIR_PARAM_PLOT) > 0)
1417  visir_image_plot("", "t 'The combined image'", "", combined[0]);
1418 
1419  end_skip;
1420 
1421  cpl_propertylist_delete(qclist);
1422  cpl_bivector_delete(offsets_est);
1423  cpl_bivector_delete(objs);
1424  cpl_vector_unwrap(sigmas);
1425 
1426  return combined;
1427 }
1428 
1429 /*----------------------------------------------------------------------------*/
1476 /*----------------------------------------------------------------------------*/
1477 cpl_image ** visir_img_recombine(const char * recipename,
1478  const cpl_parameterlist * parlist,
1479  const irplib_framelist * rawframes,
1480  const char * badpix,
1481  const char * flat,
1482  cpl_geom_combine combine_mode,
1483  cpl_boolean * pdid_resize,
1484  cpl_boolean do_spc_fix,
1485  double wlen,
1486  visir_spc_resol resol)
1487 {
1488  int nfiles;
1489  int * nod_pos = NULL;
1490  cpl_imagelist * nodded = NULL;
1491  cpl_size nnod;
1492  cpl_bivector * objs = NULL;
1493  cpl_propertylist * qclist = cpl_propertylist_new();
1494  const cpl_propertylist ** plists = NULL;
1495  cpl_image ** rec = NULL;
1496 
1497 
1498  bug_if (0);
1499 
1500  bug_if (recipename == NULL);
1501  bug_if (parlist == NULL);
1502  bug_if (rawframes == NULL);
1503  bug_if (pdid_resize == NULL);
1504 
1505  /* Get the number of files */
1506  nfiles = irplib_framelist_get_size(rawframes);
1507 
1508  /* There should be an even number of files */
1509  if (nfiles % 2) {
1510  cpl_msg_warning(cpl_func, "Expecting even number of files, "
1511  "ignoring the last of %d file(s)", nfiles);
1512  error_if (nfiles == 1, CPL_ERROR_DATA_NOT_FOUND,
1513  "At least two files are required");
1514  nfiles--;
1515  }
1516 
1517  skip_if ( nfiles <= 0);
1518 
1519  /* Each file corresponds to a nodding position (object=1 or sky=-1) */
1520  nod_pos = cpl_malloc(nfiles * sizeof(int));
1521 
1522  /* Combine the input frames into the nodded images */
1523  cpl_msg_info(cpl_func, "Combining the input frames into the nodded images");
1524  nodded = visir_inputs_combine(recipename, parlist, rawframes, badpix, flat,
1525  nod_pos, do_spc_fix, wlen, resol);
1526  skip_if(nodded == NULL);
1527 
1528  nnod = cpl_imagelist_get_size(nodded);
1529  plists = cpl_malloc(nnod * sizeof(cpl_propertylist *));
1530  for (cpl_size i=0; i < nnod ; i++) {
1531  const cpl_size iframe = nod_pos[2*i] == 1 ? 2*i : 2*i+1;
1532 
1533  plists[i] = irplib_framelist_get_propertylist_const(rawframes,
1534  iframe);
1535  }
1536 
1537  rec = visir_img_recombine_list(recipename, parlist, nodded,
1538  plists, combine_mode, pdid_resize);
1539 
1540  end_skip;
1541 
1542  cpl_propertylist_delete(qclist);
1543  cpl_free(nod_pos);
1544  cpl_free(plists);
1545  cpl_imagelist_delete(nodded);
1546  cpl_bivector_delete(objs);
1547 
1548  return rec;
1549 }
1550 
1551 /*----------------------------------------------------------------------------*/
1574 /*----------------------------------------------------------------------------*/
1575 cpl_imagelist * visir_load_hcycle(const irplib_framelist * rawframes, int pos)
1576 {
1577  return visir_load_imagelist(rawframes, pos, CPL_FALSE);
1578 }
1579 
1580 /*----------------------------------------------------------------------------*/
1591 /*----------------------------------------------------------------------------*/
1592 cpl_error_code visir_image_reject_hot(cpl_image * self, const char * bpmfile)
1593 {
1594 
1595  cpl_image * im_bpm = NULL;
1596  cpl_mask * bpm = NULL;
1597  const int upper = VISIR_HCYCLE_BPM_THRESHOLD;
1598 
1599 
1600  skip_if (0);
1601 
1602  skip_if (self == NULL);
1603 
1604  if (bpmfile == NULL) {
1605  bpm = cpl_mask_threshold_image_create(self, upper, DBL_MAX);
1606  skip_if (0);
1607  } else {
1608 
1609  /* The bpm is provided by the user */
1610  cpl_msg_info(cpl_func, "Clean user specified bad pixels");
1611  /* Load the bad pixel image */
1612  im_bpm = cpl_image_load(bpmfile, CPL_TYPE_INT, 0, 0);
1613  any_if ("Could not load the bad pixel map %s",
1614  bpmfile ? bpmfile : "<NULL>");
1615  /* Convert the map from integer to binary */
1616  bpm = cpl_mask_threshold_image_create(im_bpm, -0.5, 0.5);
1617  skip_if (0);
1618  cpl_image_delete(im_bpm);
1619  im_bpm = NULL;
1620 
1621  skip_if (cpl_mask_not(bpm));
1622  }
1623 
1624  skip_if (cpl_image_reject_from_mask(self, bpm));
1625 
1626  end_skip;
1627 
1628  cpl_image_delete(im_bpm);
1629  cpl_mask_delete(bpm);
1630 
1631  return cpl_error_get_code();
1632 
1633 }
1634 
1635 
1636 /*----------------------------------------------------------------------------*/
1643 /*----------------------------------------------------------------------------*/
1644 cpl_imagelist * visir_imagelist_load_last(const irplib_framelist * rawframes)
1645 {
1646  cpl_imagelist * self = NULL;
1647  int naxis3;
1648 
1649  /* Verify that NAXIS3 is the same in all files */
1650  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_NAXIS3,
1651  CPL_TYPE_INT, CPL_TRUE, 0.0));
1652 
1654  rawframes,
1655  0));
1656 
1657  /* Load the image set */
1658  self = irplib_imagelist_load_framelist(rawframes, CPL_TYPE_FLOAT, naxis3-1,
1659  0);
1660 
1661  skip_if (self == NULL);
1662 
1663  end_skip;
1664 
1665  return self;
1666 
1667 }
1668 
1669 
1670 /*----------------------------------------------------------------------------*/
1680 /*----------------------------------------------------------------------------*/
1681 cpl_imagelist * visir_load_imagelist(const irplib_framelist * rawframes,
1682  int pos, cpl_boolean is_interm)
1683 {
1684  cpl_imagelist * self = NULL;
1685  cpl_image * image = NULL;
1686  const cpl_frame * frame = irplib_framelist_get_const(rawframes, pos);
1687  const cpl_propertylist * plist =
1689  const char * file = cpl_frame_get_filename(frame);
1690  const int nchop = plist ? visir_pfits_get_chop_ncycles(plist) : -1;
1691  int naxis3;
1692  visir_data_type data_type;
1693  int plane_step;
1694  int nsize;
1695  int iplane;
1696  int i;
1697 
1698  skip_if (plist == NULL);
1699 
1700  skip_if(visir_get_data_type(frame, plist, &data_type, NULL));
1701 
1702  switch (data_type) {
1703  case VISIR_DATA_AQU_HCYCLE:
1704  case VISIR_DATA_AQU_BURST:
1705  case VISIR_DATA_AQU_BURST_EXT:
1706  error_if(1, CPL_ERROR_UNSUPPORTED_MODE,
1707  "Aquarius data not supported");
1708  default:
1709  naxis3 = visir_pfits_get_naxis3(plist);
1710  break;
1711  }
1712 
1713  bug_if (file == NULL);
1714 
1715  if (data_type == VISIR_DATA_CUBE1) {
1716  /* All INTERM frames are in the first part of the cube */
1717  iplane = is_interm ? 0 : nchop;
1718  /* - followed by a single Half-cycle image */
1719  nsize = is_interm ? nchop : 1;
1720  plane_step = 1;
1721  } else if (naxis3 == 1) {
1722  iplane = 0;
1723  nsize = 1;
1724  plane_step = 1;
1725  } else {
1726  /* Each INTERM frame follows the Half-cycle frame */
1727  iplane = is_interm ? 1 : 0;
1728  nsize = nchop;
1729  plane_step = 2;
1730  }
1731 
1732  self = cpl_imagelist_new();
1733 
1734  for (i=0 ; i < nsize; i++, iplane += plane_step) {
1735 
1736  image = cpl_image_load(file, CPL_TYPE_FLOAT, iplane, 0);
1737  skip_if (image == NULL);
1738 
1739  skip_if (!is_interm && visir_offset_hcycle(image));
1740 
1741  skip_if (cpl_imagelist_set(self, image, i));
1742 
1743  image = NULL;
1744  }
1745 
1746  skip_if (i < nsize);
1747 
1748  end_skip;
1749 
1750  if (cpl_error_get_code()) {
1751  if (file != NULL) cpl_msg_warning(cpl_func, "Could not load the %s "
1752  "frame(s) from: %s",
1753  is_interm ? "INTERM" : "Half-Cycle",
1754  file);
1755  cpl_image_delete(image);
1756  cpl_imagelist_delete(self);
1757  self = NULL;
1758  }
1759 
1760  return self;
1761 }
1762 
1763 
1766 /*----------------------------------------------------------------------------*/
1790 /*----------------------------------------------------------------------------*/
1791 static cpl_imagelist * visir_load_intermint(const irplib_framelist * rawframes,
1792  int pos)
1793 {
1794  return visir_load_imagelist(rawframes, pos, CPL_TRUE);
1795 
1796 }
1797 
1798 /*----------------------------------------------------------------------------*/
1807 /*----------------------------------------------------------------------------*/
1808 static cpl_image * visir_load_average(const char * file,
1809  const cpl_propertylist * plist)
1810 {
1811  cpl_errorstate cleanstate = cpl_errorstate_get();
1812  cpl_image * self = NULL;
1813  int nchop, naxis3;
1814 
1815 
1816  skip_if (0);
1817  skip_if (file == NULL);
1818  skip_if (plist == NULL);
1819 
1820  naxis3 = visir_pfits_get_naxis3(plist);
1821  nchop = visir_pfits_get_chop_ncycles(plist);
1822 
1823  skip_if (0);
1824 
1825  if (nchop == 0 && naxis3 == 1) {
1826  self = cpl_image_load(file, CPL_TYPE_FLOAT, 0, 0);
1827  } else {
1828  const char * sval;
1829  /* If nchop == 1 (and naxis3 == 3) the data unit is both a valid CUBE1
1830  and a valid CUBE2; Set plane_offset as if the frame type is CUBE2 */
1831  const int plane_offset = (naxis3 == 2 * nchop + 1) ? 2 : 3;
1832 
1833 
1834  error_if (nchop <= 0, CPL_ERROR_BAD_FILE_FORMAT, "CHOP NCYCLES in %s "
1835  "is non-positive (and NAXIS3=%d): %d", file, naxis3, nchop);
1836 
1837  error_if (plane_offset == 3 && naxis3 != nchop+2,
1838  CPL_ERROR_BAD_FILE_FORMAT, "NAXIS3=%d and CHOP NCYCLES=%d "
1839  "in %s is not a valid VISIR INTERM+Half-Cycle format", naxis3,
1840  nchop, file);
1841 
1842  if (plane_offset == 3 && nchop > 1)
1843  cpl_msg_debug(cpl_func, "%s has %d INTERM-frames and one Half-"
1844  "Cycle frame (old CUBE1-format)", file, nchop);
1845 
1846  /* Check the data format */
1847  sval = visir_pfits_get_frame_type(plist);
1848  if (sval == NULL) {
1849  /* Has warned about missing frame type card */
1850  visir_error_reset("Could not get FITS key");
1851  /* Don't know whether or not to expect CUBE1 or CUBE2 */
1852  } else if (strlen(sval) == 0) {
1853  /* Don't know whether or not to expect CUBE1 or CUBE2 */
1854  } else if (plane_offset == 3) {
1855  if (strcmp(sval, "CUBE2")==0)
1856  cpl_msg_error(cpl_func, "%s has FRAM TYPE = CUBE2, but NAXIS3="
1857  "%d and CHOP NCYCLES=%d imply a CUBE1. Assuming"
1858  " the frame type is really CUBE1", file,
1859  naxis3, nchop);
1860  } else if (nchop > 1) {
1861  /* if nchop == 1 format can be CUBE1 or CUBE2 */
1862  if (strcmp(sval, "CUBE1")==0)
1863  cpl_msg_error(cpl_func, "%s has FRAM TYPE = CUBE1, but NAXIS3="
1864  "%d and CHOP NCYCLES=%d imply a CUBE2. Assuming"
1865  "the frame type is really CUBE2", file,
1866  naxis3, nchop);
1867  }
1868 
1869  /* Load last INTERM frame */
1870  self = cpl_image_load(file, CPL_TYPE_FLOAT, naxis3-plane_offset, 0);
1871 
1872  }
1873 
1874  skip_if (0);
1875 
1876  end_skip;
1877 
1878  if (cpl_error_get_code()) {
1879  cpl_msg_warning(cpl_func, "Could not load the last INTERM frame from: "
1880  "%s", file ? file : "<NULL>");
1881  cpl_image_delete(self);
1882  self = NULL;
1883  }
1884 
1885  return self;
1886 }
1887 
1888 /*----------------------------------------------------------------------------*/
1911 /*----------------------------------------------------------------------------*/
1912 static cpl_error_code visir_imagelist_unpack_interm(cpl_imagelist * self)
1913 {
1914  cpl_image * iprev;
1915  cpl_image * image;
1916  const int n = cpl_imagelist_get_size(self);
1917  int i;
1918 
1919 
1920  skip_if (0);
1921 
1922  if (n == 1) return CPL_ERROR_NONE;
1923 
1924  iprev = cpl_imagelist_get(self, n - 1);
1925 
1926  skip_if (0);
1927 
1928  skip_if (cpl_image_multiply_scalar(iprev, n));
1929 
1930  /* Loop on the images - with first and last iteration peeled off */
1931  for (i = n-1 ; i > 1 ; i--, iprev = image) {
1932  image = cpl_imagelist_get(self, i-1);
1933 
1934  skip_if (0);
1935 
1936  skip_if (cpl_image_multiply_scalar(image, i));
1937 
1938  skip_if (cpl_image_subtract(iprev, image));
1939 
1940  }
1941 
1942  image = cpl_imagelist_get(self, 0);
1943 
1944  skip_if (0);
1945 
1946  skip_if (cpl_image_subtract(iprev, image));
1947 
1948  end_skip;
1949 
1950  return cpl_error_get_code();
1951 }
1952 
1953 /*----------------------------------------------------------------------------*/
1966 /*----------------------------------------------------------------------------*/
1967 static cpl_error_code visir_rem_glitch(cpl_image * glitchy)
1968 {
1969  cpl_image * med_filt = NULL;
1970  cpl_mask * bpm = NULL;
1971  cpl_mask * kernel = cpl_mask_new(3, 3);
1972  double mean, stdev;
1973  double low_thresh, high_thresh;
1974  const int nx = cpl_image_get_size_x(glitchy);
1975  const int ny = cpl_image_get_size_y(glitchy);
1976  int i;
1977 
1978  /* Some heuristic constants */
1979  double factor1 = 3.0;
1980  double factor2 = 10.0;
1981  const int niterations = 5;
1982  const double median_corr = 1.5;
1983 
1984  bug_if (0);
1985 
1986  /* Create the filtering kernel */
1987  bug_if(cpl_mask_not(kernel));
1988 
1989  /* Apply a 3x3 median filter to the input image */
1990  med_filt = cpl_image_new(cpl_image_get_size_x(glitchy),
1991  cpl_image_get_size_y(glitchy),
1992  cpl_image_get_type(glitchy));
1993  bug_if(med_filt == NULL);
1994  bug_if(cpl_image_filter_mask(med_filt, glitchy, kernel, CPL_FILTER_MEDIAN,
1995  CPL_BORDER_FILTER));
1996  cpl_mask_delete(kernel);
1997  kernel = NULL;
1998 
1999  /* Noise is glitchy - med_filt */
2000  skip_if (cpl_image_subtract(glitchy, med_filt));
2001 
2002  /* niterations iterations */
2003  for (i=0 ; i < niterations ; i++) {
2004  /* Compute mean and stdev */
2005  mean = cpl_image_get_mean(glitchy);
2006  stdev = cpl_image_get_stdev(glitchy);
2007 
2008  skip_if (0);
2009 
2010  /* Set the thresholds */
2011  low_thresh = mean - factor1 * stdev;
2012  high_thresh = mean + factor1 * stdev;
2013 
2014  /* Identify where mean-factor1*stdev < glitchy < mean+factor1*stdev */
2015  bpm = cpl_mask_threshold_image_create(glitchy,low_thresh,high_thresh);
2016  skip_if (cpl_mask_not(bpm));
2017  skip_if (cpl_image_reject_from_mask(glitchy, bpm));
2018  cpl_mask_delete(bpm);
2019  bpm = NULL;
2020 
2021  /* Test the number of bad pixels */
2022  skip_if (cpl_image_count_rejected(glitchy) == nx*ny);
2023  }
2024 
2025  /* Last iteration */
2026  /* Compute mean and stdev */
2027  mean = cpl_image_get_mean(glitchy);
2028  stdev = cpl_image_get_stdev(glitchy) * median_corr;
2029 
2030  skip_if (0);
2031 
2032  low_thresh = mean - factor2 * stdev;
2033  high_thresh = mean + factor2 * stdev;
2034 
2035  bpm = cpl_mask_threshold_image_create(glitchy, low_thresh, high_thresh);
2036  skip_if (cpl_mask_not(bpm));
2037  skip_if (cpl_image_reject_from_mask(glitchy, bpm));
2038  cpl_mask_delete(bpm);
2039  bpm = NULL;
2040 
2041  /* Test the number of bad pixels */
2042  skip_if (cpl_image_count_rejected(glitchy) == nx*ny);
2043 
2044  /* Set the bad pixels to 0 */
2045  skip_if (cpl_image_fill_rejected(glitchy, 0.0));
2046  skip_if (cpl_image_accept_all(glitchy));
2047 
2048  /* Reconstruct the corrected image */
2049  skip_if (cpl_image_add(glitchy, med_filt));
2050 
2051  end_skip;
2052 
2053  cpl_image_delete(med_filt);
2054  cpl_mask_delete(bpm);
2055  cpl_mask_delete(kernel);
2056 
2057  return cpl_error_get_code();
2058 }
2059 
2060 /*----------------------------------------------------------------------------*/
2071 /*----------------------------------------------------------------------------*/
2072 static cpl_error_code visir_rem_bad_images(cpl_imagelist * in)
2073 {
2074  cpl_vector * medians = NULL;
2075  cpl_vector * stdevs = NULL;
2076  cpl_vector * selection = NULL;
2077  double mean_medians, mean_stdevs, stdev_medians, stdev_stdevs;
2078  const double threshold = 3;
2079  const int nima = cpl_imagelist_get_size(in);
2080  int i;
2081 
2082 
2083  /* This will catch a NULL input */
2084  skip_if (0);
2085 
2086  if (nima <= 3) return CPL_ERROR_NONE;
2087 
2088  /* Create medians and stdevs arrays */
2089  medians = cpl_vector_new(nima);
2090  stdevs = cpl_vector_new(nima);
2091 
2092  /* Compute the statistics */
2093  for (i=0 ; i < nima ; i++) {
2094  cpl_stats * stats = cpl_stats_new_from_image(cpl_imagelist_get(in, i),
2095  CPL_STATS_STDEV | CPL_STATS_MEDIAN);
2096 
2097  cpl_vector_set(medians, i, cpl_stats_get_median(stats));
2098  cpl_vector_set(stdevs, i, cpl_stats_get_stdev(stats));
2099  cpl_stats_delete(stats); /* :-( */
2100  }
2101 
2102  skip_if( 0);
2103 
2104  /* Get the stats on these arrays */
2105  mean_medians = cpl_vector_get_mean(medians);
2106  stdev_medians = cpl_vector_get_stdev(medians);
2107  mean_stdevs = cpl_vector_get_mean(stdevs);
2108  stdev_stdevs = cpl_vector_get_stdev(stdevs);
2109 
2110  skip_if (cpl_vector_subtract_scalar(medians, mean_medians));
2111  skip_if (cpl_vector_subtract_scalar(stdevs, mean_stdevs));
2112 
2113  stdev_medians *= threshold;
2114  stdev_stdevs *= threshold;
2115 
2116  /* Create the selection vector */
2117  selection = cpl_vector_new(nima);
2118  skip_if( cpl_vector_fill(selection, 0)); /* Flag all as good */
2119  for (i=0 ; i < nima ; i++) {
2120  if (fabs(cpl_vector_get(medians, i)) <= stdev_medians &&
2121  fabs(cpl_vector_get(stdevs, i)) <= stdev_stdevs) continue;
2122 
2123  cpl_vector_set(selection, i, -1);
2124  cpl_msg_info(cpl_func, "Image %d of %d rejected: median=%g, stdev=%g",
2125  i+1, nima, stdev_medians, stdev_stdevs);
2126  }
2127 
2128  /* Purge the bad images in the images set */
2129  cpl_imagelist_erase(in, selection);
2130 
2131  end_skip;
2132 
2133  cpl_vector_delete(medians);
2134  cpl_vector_delete(stdevs);
2135 
2136  cpl_vector_delete(selection);
2137 
2138  return CPL_ERROR_NONE;
2139 
2140 }
2141 
2142 /*----------------------------------------------------------------------------*/
2151 /*----------------------------------------------------------------------------*/
2152 static cpl_error_code visir_offset_hcycle(cpl_image * hcycle)
2153 {
2154  double minval;
2155 
2156 
2157  skip_if (0);
2158 
2159  skip_if (cpl_image_add_scalar(hcycle, VISIR_HCYCLE_OFFSET));
2160 
2161  minval = cpl_image_get_min(hcycle);
2162 
2163  /* It seems that the offset really is VISIR_HCYCLE_OFFSET-1, warn if not */
2164  if (minval < 1) cpl_msg_warning(cpl_func, "HCycle pixel minval: %g", minval);
2165 
2166  end_skip;
2167 
2168  return CPL_ERROR_NONE;
2169 }
2170 
2171 /*----------------------------------------------------------------------------*/
2183 /*----------------------------------------------------------------------------*/
2184 static
2185 cpl_image ** visir_img_collapse_beam_four(cpl_propertylist * qclist,
2186  const cpl_image * self,
2187  const cpl_image * inverse,
2188  double eccmax,
2189  double pthrow,
2190  double angle,
2191  const cpl_propertylist * plist)
2192 {
2193 
2194  cpl_image ** combined = NULL;
2195  const int nx = cpl_image_get_size_x(self);
2196  const int ny = cpl_image_get_size_y(self);
2197  const cpl_type type = cpl_image_get_type(self);
2198  cpl_imagelist * list4 = cpl_imagelist_new();
2199  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2200  cpl_image * swrap = type == CPL_TYPE_DOUBLE
2201  ? cpl_image_wrap_double(nx, ny, cpl_image_get_data((cpl_image*)self))
2202  : cpl_image_wrap_float(nx, ny, cpl_image_get_data((cpl_image*)self));
2203  cpl_image * iwrap = type == CPL_TYPE_DOUBLE
2204  ? cpl_image_wrap_double(nx, ny, cpl_image_get_data((cpl_image*)inverse))
2205  : cpl_image_wrap_float(nx, ny, cpl_image_get_data((cpl_image*)inverse));
2206  IRPLIB_DIAG_PRAGMA_POP;
2207  cpl_bivector * offs = cpl_bivector_new(4);
2208  double * x4 = cpl_bivector_get_x_data(offs);
2209  double * y4 = cpl_bivector_get_y_data(offs);
2210  double pos_x, pos_y;
2211 
2212  skip_if (0);
2213 
2214  skip_if(plist == NULL);
2215 
2216  skip_if(visir_img_find_beam_four(qclist, self, inverse, eccmax, pthrow,
2217  angle, x4, y4));
2218 
2219  /* Combine the four beams */
2220  for (int i = 1; i < 4; i++) {
2221  x4[i] = x4[0] - x4[i];
2222  y4[i] = y4[0] - y4[i];
2223  }
2224 
2225  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM XPOS",
2226  x4[0]));
2227  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM YPOS",
2228  y4[0]));
2229  x4[0] = y4[0] = 0.0;
2230 
2231  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2232  bug_if(cpl_imagelist_set(list4, (cpl_image*)self, 0));
2233  bug_if(cpl_imagelist_set(list4, swrap, 1));
2234  bug_if(cpl_imagelist_set(list4, (cpl_image*)inverse, 2));
2235  bug_if(cpl_imagelist_set(list4, iwrap, 3));
2236  IRPLIB_DIAG_PRAGMA_POP;
2237 
2238  combined = cpl_geom_img_offset_saa(list4, offs, CPL_KERNEL_DEFAULT, 0, 0,
2239  CPL_GEOM_FIRST, &pos_x, &pos_y);
2240 
2241  skip_if(combined == NULL);
2242 
2243  end_skip;
2244 
2245  cpl_bivector_delete(offs);
2246  visir_imagelist_unwrap(list4);
2247  (void)cpl_image_unwrap(swrap);
2248  (void)cpl_image_unwrap(iwrap);
2249  if (cpl_error_get_code() && combined != NULL) {
2250  cpl_image_delete(combined[0]);
2251  cpl_image_delete(combined[1]);
2252  cpl_free(combined);
2253  }
2254 
2255  return combined;
2256 }
2257 
2258 /*----------------------------------------------------------------------------*/
2272 /*----------------------------------------------------------------------------*/
2273 visir_chopnod_mode visir_img_find_beam(cpl_propertylist * qclist,
2274  const cpl_image * self,
2275  const cpl_image * inverse,
2276  const cpl_propertylist * plist,
2277  const cpl_parameterlist * parlist,
2278  const char * recipename,
2279  double x4[],
2280  double y4[])
2281 {
2282 
2283  cpl_errorstate cleanstate = cpl_errorstate_get();
2284  visir_chopnod_mode mode = VISIR_CHOPNOD_AUTO; /* Assume failure */
2285 
2286  const double eccmax = visir_parameterlist_get_double(parlist, recipename,
2287  VISIR_PARAM_ECCMAX);
2288 
2289  const char * sdir;
2290 
2291  /* Get the chopping throw in pixels */
2292  const double pscale = visir_pfits_get_pixscale(plist);
2293  const double pthrow = pscale > 0.0
2294  ? visir_pfits_get_chop_throw(plist) / pscale : 0.0;
2295  double angle = visir_pfits_get_chop_posang(plist);
2296 
2297  skip_if(x4 == NULL);
2298  skip_if(y4 == NULL);
2299  skip_if(self == NULL);
2300  skip_if(inverse == NULL);
2301  skip_if(parlist == NULL);
2302  skip_if(recipename == NULL);
2303  skip_if(qclist == NULL);
2304 
2305  sdir = visir_pfits_get_chopnod_dir(plist);
2306 
2307  if (sdir != NULL && !strcmp(sdir, "PERPENDICULAR")) {
2308 
2309  /* 4 sources - expected and detected */
2310  mode = VISIR_CHOPNOD_PERPENDICULAR;
2311 
2312  skip_if (visir_img_find_beam_four(qclist, self, inverse, eccmax,
2313  pthrow, angle, x4, y4));
2314 
2315  } else if (sdir != NULL && !strcmp(sdir, "PARALLEL")) {
2316 
2317  /* 3 sources - expected and detected */
2318  mode = VISIR_CHOPNOD_PARALLEL;
2319 
2320  skip_if (visir_img_find_beam_three(qclist, self, inverse, eccmax,
2321  pthrow, angle, x4, y4));
2322 
2323  } else {
2324  if (sdir == NULL) {
2325  visir_error_reset("Could not get FITS key");
2326  } else {
2327  cpl_msg_warning(cpl_func, "Unknown chopping direction: %s",
2328  sdir);
2329  }
2330  cpl_msg_warning(cpl_func, "Proceeding as if FITS card "
2331  VISIR_PFITS_STRING_CHOPNOD_DIR " had value: %s",
2332  "PERPENDICULAR");
2333 
2334  if (visir_img_find_beam_four(qclist, self, inverse, eccmax,
2335  pthrow, angle, x4, y4)) {
2336 
2337  visir_error_reset("Proceeding as if FITS card "
2338  VISIR_PFITS_STRING_CHOPNOD_DIR
2339  " had value: %s", "PARALLEL");
2340 
2341  skip_if (visir_img_find_beam_three(qclist, self, inverse,
2342  eccmax, pthrow, angle,
2343  x4, y4));
2344 
2345  /* 3 sources - unexpected, but detected */
2346  mode = VISIR_CHOPNOD_PARALLEL;
2347 
2348  } else {
2349  /* 4 sources - unexpected, but detected */
2350  mode = VISIR_CHOPNOD_PERPENDICULAR;
2351  }
2352  }
2353 
2354  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM THROW",
2355  pthrow));
2356  bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM THROW",
2357  "The throw in pixels (TEL CHOP THROW "
2358  "divided by INS PFOV)"));
2359  end_skip;
2360 
2361  return mode;
2362 }
2363 
2364 
2365 /*----------------------------------------------------------------------------*/
2378 /*----------------------------------------------------------------------------*/
2379 static
2380 cpl_error_code visir_img_find_beam_four(cpl_propertylist * qclist,
2381  const cpl_image * self,
2382  const cpl_image * inverse,
2383  double eccmax,
2384  double pthrow,
2385  double angle,
2386  double x4[],
2387  double y4[])
2388 {
2389 
2390  cpl_errorstate cleanstate = cpl_errorstate_get();
2391  cpl_apertures * appos = NULL;
2392  cpl_apertures * apneg = NULL;
2393  const double psigmas[] = {2.0, 1.0, 0.5};
2394  const int nsigmas = sizeof(psigmas)/sizeof(double);
2395  int isigma;
2396  int iappos2[] = {1, 2};
2397  int iapneg2[] = {1, 2};
2398 
2399  bug_if(0);
2400  skip_if(self == NULL);
2401  skip_if(qclist == NULL);
2402  skip_if(pthrow <= 0.0);
2403  skip_if(x4 == NULL);
2404  skip_if(y4 == NULL);
2405 
2406  cpl_msg_info(cpl_func, "Detecting the 4-beam object with %g pixel throw "
2407  "using %d sigma-levels ranging from %g down to %g", pthrow,
2408  nsigmas, psigmas[0], psigmas[nsigmas-1]);
2409 
2410  bug_if(0);
2411  for (isigma = 0; isigma < nsigmas; isigma++) {
2412  int npos = 0;
2413  int nneg = 0;
2414 
2415  /* Detect the (two) POSITIVE objects */
2416  cpl_apertures_delete(appos);
2417  appos = cpl_apertures_extract_sigma(self, psigmas[isigma]);
2418 
2419  if (appos != NULL) {
2420  npos = cpl_apertures_get_size(appos);
2421  }
2422 
2423 
2424  /* Detect the (two) NEGATIVE objects */
2425  cpl_apertures_delete(apneg);
2426  apneg = cpl_apertures_extract_sigma(inverse, psigmas[isigma]);
2427  if (apneg != NULL) {
2428  nneg = cpl_apertures_get_size(apneg);
2429  }
2430 
2431  cpl_msg_info(cpl_func, "Found %d positive (need 2) and %d negative "
2432  "(need 2) object(s) at sigma=%g (%d of %d)", npos, nneg,
2433  psigmas[isigma], 1+isigma, nsigmas);
2434 
2435  /* stop if it would take forever to check the eccentricities */
2436  error_if(npos * nneg > FIND_BEAM_MAX_APERTURES_SQR,
2437  CPL_ERROR_DATA_NOT_FOUND, "Too many objects found, aborting");
2438 
2439  if (eccmax > 0.0) {
2440  int ipos1;
2441  double eccbest = eccmax;
2442  double eccmin = DBL_MAX;
2443  double fluxbest = 0.0;
2444  double fluxecc = DBL_MAX;
2445  cpl_boolean is_first = CPL_TRUE;
2446 
2447 #ifdef _OPENMP
2448 #pragma omp parallel for private(ipos1)
2449 #endif
2450  for (ipos1 = 2; ipos1 < 1 + npos; ipos1++) {
2451  int ipos2, ineg1, ineg2;
2452  for (ipos2 = 1; ipos2 < ipos1; ipos2++) {
2453  for (ineg1 = 2; ineg1 < 1 + nneg; ineg1++) {
2454  for (ineg2 = 1; ineg2 < ineg1; ineg2++) {
2455  cpl_boolean swappos, swapneg;
2456  const double ecc
2457  = visir_img_check_box(appos, ipos1, ipos2,
2458  apneg, ineg1, ineg2,
2459  pthrow, angle, &swappos,
2460  &swapneg);
2461 
2462  const double flux
2463  = cpl_apertures_get_flux(appos, ipos1)
2464  + cpl_apertures_get_flux(appos, ipos2)
2465  + cpl_apertures_get_flux(apneg, ineg1)
2466  + cpl_apertures_get_flux(apneg, ineg2);
2467 
2468 
2469  if (ecc < 0.0 || flux <= 0.0 ||
2470  !cpl_errorstate_is_equal(cleanstate)) {
2471  irplib_error_recover(cleanstate, "Invalid 4-"
2472  "object (%d & %d of %d, "
2473  "%d & %d of %d)",
2474  ipos2, ipos1, npos,
2475  ineg2, ineg1, nneg);
2476  continue;
2477  }
2478 
2479 #ifdef _OPENMP
2480 #pragma omp critical(visir_img_find_beam_four_min)
2481 #endif
2482  if (ecc < eccmin)
2483  {
2484  eccmin = ecc;
2485  fluxecc = flux;
2486  }
2487 
2488  if (eccmax <= ecc) continue;
2489 
2490 #ifdef _OPENMP
2491 #pragma omp critical(visir_img_find_beam_four_ok)
2492 #endif
2493  if (is_first || ecc * fluxbest < eccbest * flux)
2494  {
2495  if (is_first) {
2496  is_first = CPL_FALSE;
2497  cpl_msg_info(cpl_func, "Found 4 object "
2498  "positions with throw-"
2499  "scaled eccentricity %g "
2500  "and flux %g", ecc, flux);
2501  } else {
2502  cpl_msg_info(cpl_func, "Found 4 object "
2503  "positions with throw-"
2504  "scaled eccentricity %g "
2505  "< %g and/or flux %g > %g",
2506  ecc, eccbest, flux, fluxbest);
2507  }
2508  eccbest = ecc;
2509  fluxbest = flux;
2510  iappos2[0] = swappos ? ipos2 : ipos1;
2511  iappos2[1] = swappos ? ipos1 : ipos2;
2512  iapneg2[0] = swapneg ? ineg2 : ineg1;
2513  iapneg2[1] = swapneg ? ineg1 : ineg2;
2514  }
2515  }
2516  }
2517  }
2518  }
2519  if (eccbest < eccmax) {
2520  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM "
2521  "ECCENTRICITY", eccbest));
2522 
2523  break;
2524  }
2525  if (eccmin < DBL_MAX) {
2526  cpl_msg_info(cpl_func, "Found 4 sigma-%g object positions with "
2527  "too large throw-scaled eccentricity %g >= %g and "
2528  "flux %g", psigmas[isigma], eccmin, eccmax,
2529  fluxecc);
2530  }
2531  } else if (npos >= 2 && nneg >= 2) {
2532  cpl_apertures_sort_by_flux(appos);
2533  cpl_apertures_sort_by_flux(apneg);
2534  break;
2535  }
2536 
2537  if (isigma + 1 < nsigmas) {
2538  irplib_error_recover(cleanstate, "4-Beam positions not found among "
2539  "%d postive and %d negative object(s) at "
2540  "sigma=%g, (%d of %d)", npos, nneg,
2541  psigmas[isigma], 1+isigma, nsigmas);
2542  }
2543  }
2544 
2545  error_if (isigma == nsigmas, CPL_ERROR_DATA_NOT_FOUND,
2546  "4-Beam positions not found w. %d sigma(s) down to %g",
2547  nsigmas, psigmas[nsigmas - 1]);
2548 
2549  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
2550  cpl_apertures_dump(appos, stdout);
2551  cpl_apertures_dump(apneg, stdout);
2552  }
2553 
2554  x4[0] = cpl_apertures_get_centroid_x(appos, iappos2[0]);
2555  y4[0] = cpl_apertures_get_centroid_y(appos, iappos2[0]);
2556  x4[1] = cpl_apertures_get_centroid_x(appos, iappos2[1]);
2557  y4[1] = cpl_apertures_get_centroid_y(appos, iappos2[1]);
2558 
2559  x4[2] = cpl_apertures_get_centroid_x(apneg, iapneg2[0]);
2560  y4[2] = cpl_apertures_get_centroid_y(apneg, iapneg2[0]);
2561  x4[3] = cpl_apertures_get_centroid_x(apneg, iapneg2[1]);
2562  y4[3] = cpl_apertures_get_centroid_y(apneg, iapneg2[1]);
2563 
2564  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM FLUX",
2565  cpl_apertures_get_flux(appos,
2566  iappos2[0])));
2567 
2568  cpl_msg_info(cpl_func, "Centroid of positive object 1 [pixel]: %g %g",
2569  x4[0], y4[0]);
2570  cpl_msg_info(cpl_func, "Centroid of positive object 2 [pixel]: %g %g",
2571  x4[1], y4[1]);
2572 
2573  cpl_msg_info(cpl_func, "Centroid of negative object 1 [pixel]: %g %g",
2574  x4[2], y4[2]);
2575  cpl_msg_info(cpl_func, "Centroid of negative object 2 [pixel]: %g %g",
2576  x4[3], y4[3]);
2577 
2578  cpl_msg_info(cpl_func, "Expected object distance (chop throw) [pixel]: %g",
2579  pthrow);
2580  cpl_msg_info(cpl_func, "Object Pos -> Pos x/y-distance [pixel]: %g %g",
2581  x4[1] - x4[0], y4[1] - y4[0]);
2582  cpl_msg_info(cpl_func, "Object Neg -> Neg x/y-distance [pixel]: %g %g",
2583  x4[3] - x4[2], y4[3] - y4[2]);
2584  cpl_msg_info(cpl_func, "Object Pos -> Pos angle [degrees]: %g",
2585  atan2(y4[1] - y4[0], x4[1] - x4[0]) * CPL_MATH_DEG_RAD);
2586  cpl_msg_info(cpl_func, "Object Neg -> Neg angle [degrees]: %g",
2587  atan2(y4[3] - y4[2], x4[3] - x4[2]) * CPL_MATH_DEG_RAD);
2588 
2589  end_skip;
2590 
2591  cpl_apertures_delete(appos);
2592  cpl_apertures_delete(apneg);
2593 
2594  return cpl_error_get_code();
2595 }
2596 
2597 /*----------------------------------------------------------------------------*/
2609 /*----------------------------------------------------------------------------*/
2610 static
2611 cpl_image ** visir_img_collapse_beam_three(cpl_propertylist * qclist,
2612  const cpl_image * self,
2613  const cpl_image * inverse,
2614  double eccmax,
2615  double pthrow,
2616  const cpl_propertylist * plist)
2617 {
2618 
2619  cpl_image ** combined = NULL;
2620  const int nx = cpl_image_get_size_x(self);
2621  const int ny = cpl_image_get_size_y(self);
2622  const cpl_type type = cpl_image_get_type(self);
2623  cpl_imagelist * list3 = cpl_imagelist_new();
2624  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2625  cpl_image * iwrap = type == CPL_TYPE_DOUBLE
2626  ? cpl_image_wrap_double(nx, ny, cpl_image_get_data((cpl_image*)inverse))
2627  : cpl_image_wrap_float(nx, ny, cpl_image_get_data((cpl_image*)inverse));
2628  IRPLIB_DIAG_PRAGMA_POP;
2629  cpl_bivector * offs = cpl_bivector_new(3);
2630  double * x3 = cpl_bivector_get_x_data(offs);
2631  double * y3 = cpl_bivector_get_y_data(offs);
2632  double pos_x, pos_y;
2633  double angle = visir_pfits_get_chop_posang(plist);
2634 
2635  skip_if (0);
2636 
2637  skip_if(plist == NULL);
2638 
2639  skip_if(visir_img_find_beam_three(qclist, self, inverse, eccmax, pthrow,
2640  angle, x3, y3));
2641 
2642  /* Combine the three beams */
2643  for (int i = 1; i < 3; i++) {
2644  x3[i] = x3[0] - x3[i];
2645  y3[i] = y3[0] - y3[i];
2646  }
2647  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM XPOS",
2648  x3[0]));
2649  bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM YPOS",
2650  y3[0]));
2651 
2652  x3[0] = y3[0] = 0.0;
2653 
2654  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2655  bug_if(cpl_imagelist_set(list3, (cpl_image*)self, 0));
2656  bug_if(cpl_imagelist_set(list3, (cpl_image*)inverse, 1));
2657  bug_if(cpl_imagelist_set(list3, iwrap, 2));
2658  IRPLIB_DIAG_PRAGMA_POP;
2659 
2660  combined = cpl_geom_img_offset_saa(list3, offs, CPL_KERNEL_DEFAULT, 0, 0,
2661  CPL_GEOM_FIRST, &pos_x, &pos_y);
2662 
2663  skip_if(combined == NULL);
2664 
2665  end_skip;
2666 
2667  cpl_bivector_delete(offs);
2668  visir_imagelist_unwrap(list3);
2669  (void)cpl_image_unwrap(iwrap);
2670  if (cpl_error_get_code() && combined != NULL) {
2671  cpl_image_delete(combined[0]);
2672  cpl_image_delete(combined[1]);
2673  cpl_free(combined);
2674  }
2675 
2676  return combined;
2677 }
2678 
2679 
2680 /*----------------------------------------------------------------------------*/
2694 /*----------------------------------------------------------------------------*/
2695 static
2696 cpl_error_code visir_img_find_beam_three(cpl_propertylist * qclist,
2697  const cpl_image * self,
2698  const cpl_image * inverse,
2699  double eccmax,
2700  double pthrow,
2701  double angle,
2702  double x3[],
2703  double y3[])
2704 {
2705 
2706  cpl_errorstate cleanstate = cpl_errorstate_get();
2707  cpl_apertures * appos = NULL;
2708  cpl_apertures * apneg = NULL;
2709  const double psigmas[] = {2.0, 1.0, 0.5};
2710  const int nsigmas = sizeof(psigmas)/sizeof(double);
2711  int isigma;
2712  int iappos [] = {1};
2713  int iapneg2[] = {1, 2};
2714 
2715  bug_if(0);
2716  skip_if(self == NULL);
2717  skip_if(qclist == NULL);
2718  skip_if(pthrow <= 0.0);
2719  skip_if(eccmax < 0.0);
2720  skip_if(x3 == NULL);
2721  skip_if(y3 == NULL);
2722 
2723 
2724  cpl_msg_info(cpl_func, "Detecting the 3-beam object with %g pixel throw "
2725  "using %d sigma-levels ranging from %g down to %g", pthrow,
2726  nsigmas, psigmas[0], psigmas[nsigmas-1]);
2727 
2728  bug_if(0);
2729  for (isigma = 0; isigma < nsigmas; isigma++) {
2730  int npos = 0;
2731  int nneg = 0;
2732 
2733  /* Detect the (single) POSITIVE object */
2734  cpl_apertures_delete(appos);
2735  appos = cpl_apertures_extract_sigma(self, psigmas[isigma]);
2736 
2737  if (appos != NULL) {
2738  npos = cpl_apertures_get_size(appos);
2739  }
2740 
2741 
2742  /* Detect the (two) NEGATIVE objects */
2743  cpl_apertures_delete(apneg);
2744  apneg = cpl_apertures_extract_sigma(inverse, psigmas[isigma]);
2745  if (apneg != NULL) {
2746  nneg = cpl_apertures_get_size(apneg);
2747  }
2748 
2749  cpl_msg_info(cpl_func, "Found %d positive (need 1) and %d negative "
2750  "(need 2) object(s) at sigma=%g (%d of %d)", npos, nneg,
2751  psigmas[isigma], 1+isigma, nsigmas);
2752 
2753  /* stop if it would take forever to check the eccentricities */
2754  error_if(npos * nneg > FIND_BEAM_MAX_APERTURES_SQR,
2755  CPL_ERROR_DATA_NOT_FOUND, "Too many objects found, aborting");
2756 
2757  if (eccmax > 0.0) {
2758  int ipos;
2759  double eccbest = eccmax;
2760  double eccmin = DBL_MAX;
2761  double fluxbest = 0.0;
2762  double fluxecc = DBL_MAX;
2763  cpl_boolean is_first = CPL_TRUE;
2764 
2765 #ifdef _OPENMP
2766 #pragma omp parallel for private(ipos)
2767 #endif
2768  for (ipos = 1; ipos < 1 + npos; ipos++) {
2769  int ineg1, ineg2;
2770  for (ineg1 = 2; ineg1 < 1 + nneg; ineg1++) {
2771  for (ineg2 = 1; ineg2 < ineg1; ineg2++) {
2772  cpl_boolean swapneg;
2773  /* Find best ecc */
2774  const double ecc
2775  = visir_img_check_align(appos, ipos, apneg, ineg1,
2776  ineg2, pthrow, angle,
2777  &swapneg);
2778 
2779  const double flux
2780  = cpl_apertures_get_flux(appos, ipos)
2781  + cpl_apertures_get_flux(apneg, ineg1)
2782  + cpl_apertures_get_flux(apneg, ineg2);
2783 
2784 
2785  if (ecc < 0.0 || flux <= 0.0 ||
2786  !cpl_errorstate_is_equal(cleanstate)) {
2787  irplib_error_recover(cleanstate, "Invalid 3-"
2788  "object (%d of %d, "
2789  "%d & %d of %d)",
2790  ipos, npos,
2791  ineg2, ineg1, nneg);
2792  continue;
2793  }
2794 
2795 #ifdef _OPENMP
2796 #pragma omp critical(visir_img_collapse_beam_three_min)
2797 #endif
2798  if (ecc < eccmin)
2799  {
2800  eccmin = ecc;
2801  fluxecc = flux;
2802  }
2803 
2804  if (eccmax <= ecc) continue;
2805 
2806 #ifdef _OPENMP
2807 #pragma omp critical(visir_img_collapse_beam_three_ok)
2808 #endif
2809  if (is_first || ecc * fluxbest < eccbest * flux)
2810  {
2811  if (is_first) {
2812  is_first = CPL_FALSE;
2813  cpl_msg_info(cpl_func, "Found 3 object posi"
2814  "tions with throw-scaled "
2815  "eccentricity %g and flux %g",
2816  ecc, flux);
2817  } else {
2818  cpl_msg_info(cpl_func, "Found 3 object posi"
2819  "tions with throw-scaled "
2820  "eccentricity %g < %g and/or "
2821  "flux %g > %g", ecc, eccbest,
2822  flux, fluxbest);
2823  }
2824  eccbest = ecc;
2825  fluxbest = flux;
2826  iappos[0] = ipos;
2827  iapneg2[0] = swapneg ? ineg2 : ineg1;
2828  iapneg2[1] = swapneg ? ineg1 : ineg2;
2829  }
2830  }
2831  }
2832  }
2833  if (eccbest < eccmax) {
2834  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM "
2835  "ECCENTRICITY", eccbest));
2836  break;
2837  }
2838  if (eccmin < DBL_MAX) {
2839  cpl_msg_info(cpl_func, "Found 3 sigma-%g object positions with "
2840  "too large throw-scaled eccentricity %g >= %g and "
2841  "flux %g", psigmas[isigma], eccmin, eccmax,
2842  fluxecc);
2843  }
2844  } else if (npos >= 1 && nneg >= 2) {
2845  cpl_apertures_sort_by_flux(appos);
2846  cpl_apertures_sort_by_flux(apneg);
2847  break;
2848  }
2849 
2850  if (isigma + 1 < nsigmas) {
2851  irplib_error_recover(cleanstate, "3-Beam positions not found among "
2852  "%d postive and %d negative object(s) at "
2853  "sigma=%g, (%d of %d)", npos, nneg,
2854  psigmas[isigma], 1+isigma, nsigmas);
2855  }
2856  }
2857 
2858  error_if (isigma == nsigmas, CPL_ERROR_DATA_NOT_FOUND,
2859  "3-Beam positions not found w. %d sigma(s) down to %g",
2860  nsigmas, psigmas[nsigmas - 1]);
2861 
2862  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
2863  cpl_apertures_dump(appos, stdout);
2864  cpl_apertures_dump(apneg, stdout);
2865  }
2866 
2867  x3[0] = cpl_apertures_get_centroid_x(appos, iappos[0]);
2868  y3[0] = cpl_apertures_get_centroid_y(appos, iappos[0]);
2869 
2870  x3[1] = cpl_apertures_get_centroid_x(apneg, iapneg2[0]);
2871  y3[1] = cpl_apertures_get_centroid_y(apneg, iapneg2[0]);
2872  x3[2] = cpl_apertures_get_centroid_x(apneg, iapneg2[1]);
2873  y3[2] = cpl_apertures_get_centroid_y(apneg, iapneg2[1]);
2874 
2875  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM FLUX",
2876  cpl_apertures_get_flux(appos,
2877  iappos[0])));
2878 
2879  cpl_msg_info(cpl_func, "Centroid of positive object [pixel]: %g %g",
2880  x3[0], y3[0]);
2881 
2882  cpl_msg_info(cpl_func, "Centroid of negative object 1 [pixel]: %g %g",
2883  x3[1], y3[1]);
2884  cpl_msg_info(cpl_func, "Centroid of negative object 2 [pixel]: %g %g",
2885  x3[2], y3[2]);
2886 
2887  cpl_msg_info(cpl_func, "Expected object distance (chop throw) [pixel]: %g",
2888  pthrow);
2889  cpl_msg_info(cpl_func, "Object Neg1 -> Pos x/y-distance [pixel]: %g %g",
2890  x3[2] - x3[0], y3[2] - y3[0]);
2891  cpl_msg_info(cpl_func, "Object Pos -> Neg2 x/y-distance [pixel]: %g %g",
2892  x3[0] - x3[1], y3[0] - y3[1]);
2893 
2894  end_skip;
2895 
2896  cpl_apertures_delete(appos);
2897  cpl_apertures_delete(apneg);
2898 
2899  return cpl_error_get_code();
2900 }
2901 
2902 
2903 /*----------------------------------------------------------------------------*/
2919 /*----------------------------------------------------------------------------*/
2920 cpl_error_code visir_img_find_beam_two(cpl_propertylist * qclist,
2921  const cpl_image * self,
2922  const cpl_image * inverse,
2923  double eccmax,
2924  double pthrow,
2925  double angle,
2926  double x2[],
2927  double y2[],
2928  cpl_boolean is_hor)
2929 {
2930 
2931  cpl_errorstate cleanstate = cpl_errorstate_get();
2932  cpl_apertures * appos = NULL;
2933  cpl_apertures * apneg = NULL;
2934  const double psigmas[] = {2.0, 1.0, 0.5};
2935  const int nsigmas = sizeof(psigmas)/sizeof(double);
2936  int isigma;
2937  int iappos[] = {1};
2938  int iapneg[] = {1};
2939 
2940  bug_if(0);
2941  skip_if(self == NULL);
2942  skip_if(qclist == NULL);
2943  skip_if(eccmax < 0.0);
2944  skip_if(x2 == NULL);
2945  skip_if(y2 == NULL);
2946 
2947  if (pthrow > 0.0) {
2948  cpl_msg_info(cpl_func, "Detecting the 2-beam object (Pos -> Neg) with "
2949  "%g pixel throw using %d sigma-levels ranging from %g down"
2950  " to %g", pthrow, nsigmas, psigmas[0], psigmas[nsigmas-1]);
2951  } else if (pthrow < 0.0) {
2952  cpl_msg_info(cpl_func, "Detecting the 2-beam object (Neg -> Pos) with "
2953  "%g pixel throw using %d sigma-levels ranging from %g down"
2954  " to %g", pthrow, nsigmas, psigmas[0], psigmas[nsigmas-1]);
2955  } else {
2956  skip_if(1);
2957  }
2958 
2959  bug_if(0);
2960  for (isigma = 0; isigma < nsigmas; isigma++) {
2961  int npos = 0;
2962  int nneg = 0;
2963 
2964  /* Detect the (single) POSITIVE object */
2965  cpl_apertures_delete(appos);
2966  appos = cpl_apertures_extract_sigma(self, psigmas[isigma]);
2967 
2968  if (appos != NULL) {
2969  npos = cpl_apertures_get_size(appos);
2970  }
2971 
2972 
2973  /* Detect the NEGATIVE object */
2974  cpl_apertures_delete(apneg);
2975  apneg = cpl_apertures_extract_sigma(inverse, psigmas[isigma]);
2976  if (apneg != NULL) {
2977  nneg = cpl_apertures_get_size(apneg);
2978  }
2979 
2980  cpl_msg_info(cpl_func, "Found %d positive (need 1) and %d negative "
2981  "(need 1) object(s) at sigma=%g (%d of %d)", npos, nneg,
2982  psigmas[isigma], 1+isigma, nsigmas);
2983 
2984  if (eccmax > 0.0) {
2985  int ipos;
2986  double eccbest = eccmax;
2987  double eccmin = DBL_MAX;
2988  double fluxbest = 0.0;
2989  double fluxecc = DBL_MAX;
2990  cpl_boolean is_first = CPL_TRUE;
2991 
2992 #ifdef _OPENMP
2993 #pragma omp parallel for private(ipos)
2994 #endif
2995  for (ipos = 1; ipos < 1 + npos; ipos++) {
2996  int ineg;
2997  for (ineg = 1; ineg < 1 + nneg; ineg++) {
2998  const double ecc =
2999  visir_img_check_line(appos, ipos, apneg, ineg,
3000  pthrow, angle);
3001 
3002  const double flux
3003  = cpl_apertures_get_flux(appos, ipos)
3004  + cpl_apertures_get_flux(apneg, ineg);
3005 
3006 
3007  if (ecc < 0.0 || flux <= 0.0 ||
3008  !cpl_errorstate_is_equal(cleanstate)) {
3009  irplib_error_recover(cleanstate, "Invalid 2-"
3010  "object (%d of %d, "
3011  "%d of %d)",
3012  ipos, npos,
3013  ineg, nneg);
3014  continue;
3015  }
3016 
3017 #ifdef _OPENMP
3018 #pragma omp critical(visir_img_collapse_beam_two_min)
3019 #endif
3020  if (ecc < eccmin)
3021  {
3022  eccmin = ecc;
3023  fluxecc = flux;
3024  }
3025 
3026  if (eccmax <= ecc) continue;
3027 
3028 #ifdef _OPENMP
3029 #pragma omp critical(visir_img_collapse_beam_two_ok)
3030 #endif
3031  if (is_first || ecc * fluxbest < eccbest * flux)
3032  {
3033  if (is_first) {
3034  is_first = CPL_FALSE;
3035  cpl_msg_info(cpl_func, "Found 2 object posi"
3036  "tions with throw-scaled eccen"
3037  "tricity %g and flux %g", ecc,
3038  flux);
3039  } else {
3040  cpl_msg_info(cpl_func, "Found 2 object posi"
3041  "tions with throw-scaled eccen"
3042  "tricity %g < %g and/or flux %g "
3043  "> %g", ecc, eccbest, flux,
3044  fluxbest);
3045  }
3046  eccbest = ecc;
3047  fluxbest = flux;
3048  iappos[0] = ipos;
3049  iapneg[0] = ineg;
3050  }
3051  }
3052  }
3053  if (eccbest < eccmax) {
3054  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM "
3055  "ECCENTRICITY", eccbest));
3056  break;
3057  }
3058  if (eccmin < DBL_MAX) {
3059  cpl_msg_info(cpl_func, "Found 2 sigma-%g object positions with "
3060  "too large throw-scaled eccentricity %g >= %g and "
3061  "flux %g", psigmas[isigma], eccmin, eccmax,
3062  fluxecc);
3063  }
3064  } else if (npos >= 1 && nneg >= 2) {
3065  cpl_apertures_sort_by_flux(appos);
3066  cpl_apertures_sort_by_flux(apneg);
3067  break;
3068  }
3069 
3070  if (isigma + 1 < nsigmas) {
3071  irplib_error_recover(cleanstate, "2-Beam positions not found among "
3072  "%d postive and %d negative object(s) at "
3073  "sigma=%g, (%d of %d)", npos, nneg,
3074  psigmas[isigma], 1+isigma, nsigmas);
3075  }
3076  }
3077 
3078  error_if (isigma == nsigmas, CPL_ERROR_DATA_NOT_FOUND,
3079  "2-Beam positions not found w. %d sigma(s) down to %g",
3080  nsigmas, psigmas[nsigmas - 1]);
3081 
3082  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
3083  cpl_apertures_dump(appos, stdout);
3084  cpl_apertures_dump(apneg, stdout);
3085  }
3086 
3087  x2[0] = cpl_apertures_get_centroid_x(appos, iappos[0]);
3088  y2[0] = cpl_apertures_get_centroid_y(appos, iappos[0]);
3089 
3090  x2[1] = cpl_apertures_get_centroid_x(apneg, iapneg[0]);
3091  y2[1] = cpl_apertures_get_centroid_y(apneg, iapneg[0]);
3092 
3093  bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM FLUX",
3094  cpl_apertures_get_flux(appos,
3095  iappos[0])));
3096 
3097  cpl_msg_info(cpl_func, "Centroid of positive object [pixel]: %g %g",
3098  x2[0], y2[0]);
3099 
3100  cpl_msg_info(cpl_func, "Centroid of negative object [pixel]: %g %g",
3101  x2[1], y2[1]);
3102 
3103  if (pthrow > 0.0) {
3104  cpl_msg_info(cpl_func, "Expected object distance (chop throw) "
3105  "[pixel]: %g", pthrow);
3106 
3107  cpl_msg_info(cpl_func, "Object Pos -> Neg x/y-distance [pixel]: %g %g",
3108  x2[1] - x2[0], y2[1] - y2[0]);
3109  } else {
3110  cpl_msg_info(cpl_func, "Expected object distance (chop throw) "
3111  "[pixel]: %g", -pthrow);
3112 
3113  cpl_msg_info(cpl_func, "Object Neg -> x/y-distance [pixel]: %g %g",
3114  x2[0] - x2[1], y2[0] - y2[1]);
3115  }
3116 
3117  end_skip;
3118 
3119  cpl_apertures_delete(appos);
3120  cpl_apertures_delete(apneg);
3121 
3122  return cpl_error_get_code();
3123 }
3124 
3125 
3126 
3127 /*----------------------------------------------------------------------------*/
3147 /*----------------------------------------------------------------------------*/
3148 double visir_img_check_box(const cpl_apertures * appos,
3149  int ipos1, int ipos2,
3150  const cpl_apertures * apneg,
3151  int ineg1, int ineg2, double ssize, double angle,
3152  cpl_boolean * pswapp, cpl_boolean * pswapn)
3153 {
3154 
3155  /* NB: Lower left pixel is (1, 1) */
3156 
3157  /* The two positive points */
3158  double xp1 = cpl_apertures_get_centroid_x(appos, ipos1) * cos(angle) -
3159  cpl_apertures_get_centroid_y(appos, ipos1) * sin(angle);
3160  double yp1 = cpl_apertures_get_centroid_x(appos, ipos1) * sin(angle) +
3161  cpl_apertures_get_centroid_y(appos, ipos1) * cos(angle);
3162  double xp2 = cpl_apertures_get_centroid_x(appos, ipos2) * cos(angle) -
3163  cpl_apertures_get_centroid_y(appos, ipos2) * sin(angle);
3164  double yp2 = cpl_apertures_get_centroid_x(appos, ipos2) * sin(angle) +
3165  cpl_apertures_get_centroid_y(appos, ipos2) * cos(angle);
3166 
3167  /* The leftmost positive point */
3168  const double xpl = xp1 < xp2 ? xp1 : xp2;
3169  const double ypl = xp1 < xp2 ? yp1 : yp2;
3170 
3171  /* The rightmost positive point */
3172  const double xpr = xp1 < xp2 ? xp2 : xp1;
3173  const double ypr = xp1 < xp2 ? yp2 : yp1;
3174 
3175  /* The two negative points */
3176  double xn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * cos(angle) -
3177  cpl_apertures_get_centroid_y(apneg, ineg1) * sin(angle);
3178  double yn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * sin(angle) +
3179  cpl_apertures_get_centroid_y(apneg, ineg1) * cos(angle);
3180  double xn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * cos(angle) -
3181  cpl_apertures_get_centroid_y(apneg, ineg2) * sin(angle);
3182  double yn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * sin(angle) +
3183  cpl_apertures_get_centroid_y(apneg, ineg2) * cos(angle);
3184 
3185  /* The leftmost negative point */
3186  const double x_nl = xn1 < xn2 ? xn1 : xn2;
3187  const double y_nl = xn1 < xn2 ? yn1 : yn2;
3188 
3189  /* The rightmost negative point */
3190  const double xnr = xn1 < xn2 ? xn2 : xn1;
3191  const double ynr = xn1 < xn2 ? yn2 : yn1;
3192 
3193  const double lx1 = xnr - xpl; /* The length of the top x-side */
3194  const double lx2 = xpr - x_nl; /* The length of the bottom x-side */
3195  const double ly1 = ypl - y_nl; /* The length of the left y-side */
3196  const double ly2 = ynr - ypr; /* The length of the right y-side */
3197 
3198  const double dx1 = lx1 - ssize;
3199  const double dx2 = lx2 - ssize;
3200  const double dy1 = ly1 - ssize;
3201  const double dy2 = ly2 - ssize;
3202 
3203  const double ey1 = ynr - ypl; /* The displacement in the top x-side */
3204  const double ey2 = ypr - y_nl; /* The displacement in the bottom x-side */
3205  const double ex1 = xpl - x_nl; /* The displacement in the left y-side */
3206  const double ex2 = xpr - xnr; /* The displacement in the right y-side */
3207 
3208  const double ok = sqrt(dx1 * dx1 + dx2 * dx2 + dy1 * dy1 + dy2 * dy2 +
3209  ex1 * ex1 + ex2 * ex2 + ey1 * ey1 + ey2 * ey2);
3210 
3211  double result = -1.0; /* Assume failure */
3212 
3213  skip_if(0); /* Catches NULL apertures and illegal index input */
3214 
3215  skip_if(pswapp == NULL);
3216  skip_if(pswapn == NULL);
3217  skip_if(appos == apneg);
3218  skip_if(ipos1 == ipos2);
3219  skip_if(ineg1 == ineg2);
3220 
3221  skip_if(ssize <= 0.0);
3222 
3223  *pswapp = xp1 < xp2 ? CPL_FALSE : CPL_TRUE;
3224  *pswapn = xn1 < xn2 ? CPL_FALSE : CPL_TRUE;
3225 
3226  result = ok/ssize; /* OK to divide now */
3227 
3228  end_skip;
3229 
3230  return result;
3231 
3232 }
3233 
3234 
3235 /*----------------------------------------------------------------------------*/
3254 /*----------------------------------------------------------------------------*/
3255 double visir_img_check_align(const cpl_apertures * appos, int ipos,
3256  const cpl_apertures * apneg, int ineg1, int ineg2,
3257  double ssize, double angle,
3258  cpl_boolean * pswapn)
3259 {
3260 
3261  /* NB: Lower left pixel is (1, 1) */
3262 
3263  /* The positive point */
3264  double xp = cpl_apertures_get_centroid_x(appos, ipos) * cos(angle) -
3265  cpl_apertures_get_centroid_y(appos, ipos) * sin(angle);
3266  double yp = cpl_apertures_get_centroid_x(appos, ipos) * sin(angle) +
3267  cpl_apertures_get_centroid_y(appos, ipos) * cos(angle);
3268 
3269  /* The two negative points */
3270  double xn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * cos(angle) -
3271  cpl_apertures_get_centroid_y(apneg, ineg1) * sin(angle);
3272  double yn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * sin(angle) +
3273  cpl_apertures_get_centroid_y(apneg, ineg1) * cos(angle);
3274  double xn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * cos(angle) -
3275  cpl_apertures_get_centroid_y(apneg, ineg2) * sin(angle);
3276  double yn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * sin(angle) +
3277  cpl_apertures_get_centroid_y(apneg, ineg2) * cos(angle);
3278 
3279  double result = -1.0; /* Assume failure */
3280 
3281  double ok;
3282 
3283  cpl_boolean swapn;
3284 
3285  /* The lower negative point */
3286  const double x_nl = yn1 < yn2 ? xn1 : xn2;
3287  const double y_nl = yn1 < yn2 ? yn1 : yn2;
3288 
3289  /* The upper negative point */
3290  const double xnr = yn1 < yn2 ? xn2 : xn1;
3291  const double ynr = yn1 < yn2 ? yn2 : yn1;
3292 
3293  const double d1 = ynr - yp - ssize; /* The upper length deviation */
3294  const double d2 = yp - y_nl - ssize; /* The lower length deviation */
3295 
3296  const double e1 = xnr - xp; /* The upper orthogonal deviation */
3297  const double e2 = xp - x_nl; /* The lower orthogonal deviation */
3298 
3299  swapn = yn1 < yn2 ? CPL_FALSE : CPL_TRUE;
3300 
3301  ok = sqrt(d1 * d1 + d2 * d2 + e1 * e1 + e2 * e2);
3302 
3303  skip_if(0); /* Catches NULL apertures and illegal index input */
3304 
3305  skip_if(pswapn == NULL);
3306  skip_if(appos == apneg);
3307  skip_if(ineg1 == ineg2);
3308 
3309  skip_if(ssize <= 0.0);
3310 
3311  *pswapn = swapn;
3312 
3313  result = ok/ssize; /* OK to divide now */
3314 
3315  end_skip;
3316 
3317  return result;
3318 
3319 }
3320 
3321 
3322 /*----------------------------------------------------------------------------*/
3339 /*----------------------------------------------------------------------------*/
3340 double visir_img_check_line(const cpl_apertures * apnear, int inear,
3341  const cpl_apertures * apfar, int ifar,
3342  double ssize, double angle)
3343 {
3344 
3345  /* NB: Lower left pixel is (1, 1) */
3346 
3347  /* The near point */
3348  double x_n = cpl_apertures_get_centroid_x(apnear, inear) * cos(angle) -
3349  cpl_apertures_get_centroid_y(apnear, inear) * sin(angle);
3350  double y_n = cpl_apertures_get_centroid_x(apnear, inear) * sin(angle) +
3351  cpl_apertures_get_centroid_y(apnear, inear) * cos(angle);
3352 
3353  /* The far point */
3354  double xf = cpl_apertures_get_centroid_x(apfar, ifar) * cos(angle) -
3355  cpl_apertures_get_centroid_y(apfar, ifar) * sin(angle);
3356  double yf = cpl_apertures_get_centroid_x(apfar, ifar) * sin(angle) +
3357  cpl_apertures_get_centroid_y(apfar, ifar) * cos(angle);
3358 
3359  double result = -1.0; /* Assume failure */
3360 
3361  double ok;
3362 
3363  const double d = yf - y_n - ssize; /* The length deviation */
3364 
3365  const double e = xf - x_n; /* The orthogonal deviation */
3366 
3367  ok = sqrt(d * d + e * e);
3368 
3369  skip_if(0); /* Catches NULL apertures and illegal index input */
3370 
3371  skip_if(apnear == apfar);
3372 
3373  skip_if(ssize <= 0.0);
3374 
3375  result = ok/ssize; /* OK to divide now */
3376 
3377  end_skip;
3378 
3379  return result;
3380 
3381 }
3382 
3383 
3384 /*----------------------------------------------------------------------------*/
3395 /*----------------------------------------------------------------------------*/
3396 static cpl_error_code
3397 get_aqu_data_type(const cpl_frame * frame, const cpl_propertylist * plist,
3398  const cpl_size next, visir_data_type * ptype)
3399 {
3400  int naxis3 = -1;
3401  const char * format = NULL;
3402  if (cpl_propertylist_has(plist, VISIR_PFITS_INT_NAXIS3))
3403  naxis3 = visir_pfits_get_naxis3(plist);
3404 
3405  if (cpl_propertylist_has(plist, "ESO DET FRAM FORMAT"))
3406  format = cpl_propertylist_get_string(plist, "ESO DET FRAM FORMAT");
3407 
3408  /* HCCYLE data must have at least HCYCLE[12] and optional INT extensions */
3409  if (next >= 2 || (format && !strcmp(format, "extension"))) {
3410  /* if only one extension we only have an INT frame */
3411  if (next == 1) {
3412  *ptype = VISIR_DATA_AQU_INT;
3413  }
3414  else {
3415  *ptype = VISIR_DATA_AQU_HCYCLE;
3416  }
3417  }
3418  else if (next == 1 && (naxis3 == -1 || naxis3 == 0)) {
3419  *ptype = VISIR_DATA_AQU_BURST_EXT;
3420  }
3421  else if (next == 0 && naxis3 > 0) {
3422  *ptype = VISIR_DATA_AQU_BURST;
3423  }
3424  else {
3425  cpl_error_set_message(cpl_func, CPL_ERROR_BAD_FILE_FORMAT,
3426  "Could not determine format of aquarius file %s",
3427  cpl_frame_get_filename(frame));
3428  }
3429 
3430  return cpl_error_get_code();
3431 }
3432 
3433 
3434 /*----------------------------------------------------------------------------*/
3445 /*----------------------------------------------------------------------------*/
3446 static cpl_error_code
3447 get_drs_data_type(const cpl_frame * frame, const cpl_propertylist * plist,
3448  visir_data_type * ptype)
3449 {
3450  cpl_errorstate cleanstate = cpl_errorstate_get();
3451  const char * sval = visir_pfits_get_frame_type(plist);
3452  const int naxis3 = visir_pfits_get_naxis3(plist);
3453  const int nchop = visir_pfits_get_chop_ncycles(plist);
3454  const int ndit = visir_pfits_get_ndit(plist);
3455  const char * file = cpl_frame_get_filename(frame);
3456  cpl_boolean known_frametype = CPL_TRUE;
3457  /* If nchop == 1 (and naxis3 == 3) the data unit is both a valid CUBE1
3458  and a valid CUBE2; Assume the frame type to be CUBE2 */
3459 
3460  cpl_ensure_code(ptype != NULL, CPL_ERROR_NULL_INPUT);
3461 
3462  if (sval == NULL) {
3463  /* Has warned about missing frame type card */
3464  visir_error_reset("Could not get FITS key");
3465  /* Don't know whether or not to expect CUBE1 or CUBE2 */
3466  known_frametype = CPL_FALSE;
3467  } else if (strcmp(sval, "CUBE1")==0) {
3468  *ptype = VISIR_DATA_CUBE1;
3469  } else if (strcmp(sval, "CUBE2")==0) {
3470  *ptype = VISIR_DATA_CUBE2;
3471  }
3472  else
3473  known_frametype = CPL_FALSE;
3474 
3475  /* verification and guessing */
3476  if (known_frametype && *ptype == VISIR_DATA_CUBE2) {
3477  if (naxis3 == 2 * nchop + 1) {
3478  /* OK. FRAME TYPE cannot be verified from naxis3 when nchop = 1 */
3479  } else if (naxis3 == nchop + 2) {
3480  cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3481  "and CHOP NCYCLES=%d imply a CUBE1. Assuming "
3482  "the frame type is really CUBE1", file, sval,
3483  naxis3, nchop);
3484  *ptype = VISIR_DATA_CUBE1;
3485  } else {
3486  cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3487  "and CHOP NCYCLES=%d is not a valid VISIR INTERM+"
3488  "Half-Cycle format", file, sval, naxis3, nchop);
3489  skip_if(1);
3490  }
3491  } else if (known_frametype && *ptype == VISIR_DATA_CUBE1) {
3492  if (naxis3 == nchop + 2) {
3493  /* OK. FRAME TYPE cannot be verified from naxis3 when nchop = 1 */
3494  if (nchop > 1)
3495  cpl_msg_debug(cpl_func, "%s has %d INTERM-frames and one Half-"
3496  "Cycle frame (old CUBE1-format)", file, nchop);
3497 
3498  } else if (naxis3 == 2 * nchop + 1) {
3499  cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3500  "and CHOP NCYCLES=%d imply a CUBE2. Assuming "
3501  "the frame type is really CUBE2", file, sval,
3502  naxis3, nchop);
3503  *ptype = VISIR_DATA_CUBE2;
3504  } else {
3505  cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3506  "and CHOP NCYCLES=%d is not a valid VISIR INTERM+"
3507  "Half-Cycle format", file, sval, naxis3, nchop);
3508  skip_if(1);
3509  }
3510  } else if (naxis3 == 2 * nchop + 1) {
3511  cpl_msg_warning(cpl_func, "%s has FRAM TYPE='%s', but NAXIS3=%d and "
3512  "CHOP NCYCLES=%d imply a CUBE2. Assuming the frame "
3513  "type is CUBE2", file, sval ? sval : "<NULL>", naxis3,
3514  nchop);
3515  *ptype = VISIR_DATA_CUBE2;
3516  } else if (naxis3 == nchop + 2) {
3517  cpl_msg_warning(cpl_func, "%s has FRAM TYPE='%s', but NAXIS3=%d and "
3518  "CHOP NCYCLES=%d imply a CUBE1. Assuming the frame "
3519  "type is CUBE1", file, sval ? sval : "<NULL>", naxis3,
3520  nchop);
3521  *ptype = VISIR_DATA_CUBE1;
3522  }
3523  else if (!known_frametype && nchop * ndit * 2 >= naxis3) {
3524  cpl_msg_info(cpl_func, "%s has FRAM TYPE='%s', NAXIS3=%d and "
3525  "CHOP NCYCLES=%d imply BURST data.",
3526  file, sval ? sval : "<NULL>", naxis3, nchop);
3527  *ptype = VISIR_DATA_BURST;
3528  } else {
3529  return cpl_error_set_message(cpl_func, CPL_ERROR_BAD_FILE_FORMAT,
3530  "%s has FRAM TYPE='%s', NAXIS3 = %d and "
3531  "CHOP NCYCLES = %d", file,
3532  sval ? sval : "<NULL>", naxis3, nchop);
3533  }
3534 
3535  end_skip;
3536 
3537  return cpl_error_get_code();
3538 }
3539 
3540 
3541 /*----------------------------------------------------------------------------*/
3551 /*----------------------------------------------------------------------------*/
3552 cpl_error_code visir_get_data_type(const cpl_frame * frame,
3553  const cpl_propertylist * plist,
3554  visir_data_type * ptype, cpl_size * pnext)
3555 {
3556  const cpl_size next = cpl_frame_get_nextensions(frame);
3557 
3558  cpl_ensure_code(ptype != NULL, CPL_ERROR_NULL_INPUT);
3559 
3560  skip_if(0);
3561 
3562  if (pnext)
3563  *pnext = next;
3564 
3565  if (cpl_propertylist_has(plist, "ESO DRS DTYPE"))
3566  *ptype = cpl_propertylist_get_int(plist, "ESO DRS DTYPE");
3567  else {
3568  if (cpl_propertylist_has(plist, VISIR_PFITS_INT_NAVRG))
3569  skip_if(get_aqu_data_type(frame, plist, next, ptype));
3570  else
3571  skip_if(get_drs_data_type(frame, plist, ptype));
3572  }
3573 
3574  end_skip;
3575 
3576  return cpl_error_get_code();
3577 }
3578 
3579 
3580 /*----------------------------------------------------------------------------*/
3588 /*----------------------------------------------------------------------------*/
3589 cpl_error_code visir_img_burst_find_delta_chop(const cpl_propertylist * self,
3590  int * ichopchange, int * ihalfcycle)
3591 {
3592 
3593  const char * sdateobs =
3594  cpl_propertylist_get_string(self, VISIR_PFITS_STRING_OBS_START);
3595  const char * schopstart =
3596  cpl_propertylist_get_string(self, VISIR_PFITS_STRING_CHOP_START);
3597  const double chop_freq = visir_pfits_get_chop_freq(self);
3598  const int nditskip =
3599  cpl_propertylist_get_int(self, VISIR_PFITS_INT_NDITSKIP);
3600  const double dit = visir_pfits_get_dit(self) *
3601  visir_pfits_get_navrg(self);
3602  double ddateobs, dchopstart;
3603  double period; /* Number of A+B frames in one full chopping cycle */
3604 
3605  skip_if(0);
3606 
3607  bug_if(irplib_wcs_mjd_from_string(&ddateobs, sdateobs));
3608  bug_if(irplib_wcs_mjd_from_string(&dchopstart, schopstart));
3609 
3610  skip_if(chop_freq <= 0.0);
3611  skip_if(dit <= 0.0);
3612 
3613  /* adapt for skipped exposures after obs start */
3614  /* TODO seems to work better without in aqu */
3615  if (!cpl_propertylist_has(self, VISIR_PFITS_INT_NAVRG)) {
3616  ddateobs += dit * nditskip / (double)VISIR_SECS_PER_DAY;
3617  }
3618 
3619  period = 1.0/(chop_freq * dit);
3620 
3621  /* FIXME: handle this case */
3622  error_if((int)(period + 0.5) % 2 != 0, CPL_ERROR_UNSUPPORTED_MODE,
3623  "Period %g not not an even number, chop frequency %g, dit %g",
3624  period, chop_freq, dit);
3625 
3626  error_if((int)(period + 0.5) <= 1, CPL_ERROR_ILLEGAL_INPUT,
3627  "Period %d < 1", (int)(period + 0.5));
3628 
3629  *ihalfcycle = (int)(period + 0.5)/2;
3630 
3631  cpl_msg_info(cpl_func, "Number of A+B frames in one full chopping cycle: %g",
3632  period);
3633 
3634  if (dchopstart < ddateobs) {
3635  double tchop = (ddateobs - dchopstart) * (double)VISIR_SECS_PER_DAY;
3636  /* Number of chopping cycles before obs+chopper start */
3637  double dprecycle = tchop * chop_freq;
3638  /* Phase to skip to point to first A frame
3639  in first complete chopping cycle */
3640  const double phase = ceil(dprecycle) - dprecycle;
3641 
3642  /* First valid frame is the frame at ddateobs (except for startindex) */
3643  *ichopchange = (int)ceil(phase * period) - 1;
3644 
3645  cpl_msg_info(cpl_func, "Chopping started %gs (%f cycles) before OBS start: "
3646  "%f < %f", tchop, dprecycle, dchopstart, ddateobs);
3647 
3648  } else if (ddateobs < dchopstart) {
3649  /* FIXME: Allowed ? */
3650  /* First valid frame is the frame at dchopstart (except for startindex) */
3651  double tchop = (dchopstart - ddateobs) * (double)VISIR_SECS_PER_DAY;
3652  *ichopchange = (int)ceil(tchop / dit) - 1;
3653  cpl_msg_info(cpl_func, "Chopping started %gs (wasted %g cycles) after OBS "
3654  "start: %f > %f", tchop, tchop * chop_freq, dchopstart,
3655  ddateobs);
3656  } else {
3657  /* FIXME: Allowed ? */
3658  /* First valid frame is the first frame (at both ddateobs and dchopstart)
3659  (except for startindex) */
3660  *ichopchange = 0;
3661  cpl_msg_info(cpl_func, "Chopping started with OBS start: %f == %f",
3662  dchopstart, ddateobs);
3663  }
3664 
3665  /* wrap value to cycle */
3666  *ichopchange = *ichopchange % (*ihalfcycle * 2);
3667 
3668  cpl_msg_info(cpl_func, "Frame of chop change: %d", *ichopchange);
3669 
3670  end_skip;
3671 
3672  return cpl_error_get_code();
3673 }
3674 
3675 #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:249
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:543
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_pfits_get_navrg(const cpl_propertylist *self)
The NAVRG.
Definition: visir_pfits.c:323
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:418
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:546
int visir_pfits_get_chop_ncycles(const cpl_propertylist *self)
The number of chopping cycles.
Definition: visir_pfits.c:237
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:205
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:728
int visir_pfits_get_ndit(const cpl_propertylist *self)
The NDIT keyword.
Definition: visir_pfits.c:502
double visir_pfits_get_cumoffsety(const cpl_propertylist *self)
The cumulative offset in Y.
Definition: visir_pfits.c:261
double visir_pfits_get_chop_freq(const cpl_propertylist *self)
The chopping frequency.
Definition: visir_pfits.c:170
double visir_pfits_get_chop_throw(const cpl_propertylist *self)
The chopping throw.
Definition: visir_pfits.c:152
const char * visir_pfits_get_nodpos(const cpl_propertylist *self)
The nodding position.
Definition: visir_pfits.c:666
double visir_pfits_get_dit(const cpl_propertylist *self)
The DIT.
Definition: visir_pfits.c:309
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:678
const char * visir_pfits_get_chopnod_dir(const cpl_propertylist *self)
The chopping direction.
Definition: visir_pfits.c:139
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.