IIINSTRUMENT Pipeline Reference Manual 4.5.1
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 <stdio.h>
41#include <string.h>
42#include <math.h>
43#include <float.h>
44#include <assert.h>
45
46/*-----------------------------------------------------------------------------
47 Define
48 -----------------------------------------------------------------------------*/
49
50#define VISIR_SECS_PER_DAY (24 * 3600)
51
52#define FIND_BEAM_MAX_APERTURES_SQR (40 * 40)
53
54/*-----------------------------------------------------------------------------
55 Functions prototypes
56 -----------------------------------------------------------------------------*/
57
58#include "visir_destripe.h"
59
60static cpl_image * visir_load_average(const char *,
61 const cpl_propertylist *);
62static cpl_imagelist * visir_load_intermint(const irplib_framelist *, int);
63static cpl_error_code visir_imagelist_unpack_interm(cpl_imagelist *);
64static cpl_error_code visir_rem_glitch(cpl_image *);
65static cpl_error_code visir_rem_bad_images(cpl_imagelist *);
66static cpl_error_code visir_offset_hcycle(cpl_image *);
67
68static 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
74static 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
81static 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
88static 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
104void visir_aplist_destroy(visir_aplist * list)
105{
106 cx_list_destroy((list), (cx_free_func)visir_apdefs_delete);
107}
108
109/*----------------------------------------------------------------------------*/
122/*----------------------------------------------------------------------------*/
123
124static
125cpl_error_code visir_load_cube2_split_(cpl_imagelist * alist, cpl_imagelist * blist,
126 cpl_imagelist * packed, cpl_image * prevd)
127{
128
129 int naxis3 = cpl_imagelist_get_size(packed);
130 int prevd_insert_pos = cpl_imagelist_get_size(blist);
131
132 bug_if(alist == NULL);
133 bug_if(blist == NULL);
134
135
136 if (prevd)
137 cpl_imagelist_set(blist, prevd, prevd_insert_pos);
138
139 /* Split the cube */
140 for (int i = 0; i < naxis3/2; i++) {
141 cpl_image * aimage = cpl_imagelist_unset(packed, 0); /* Ai */
142 cpl_image * dimage = cpl_imagelist_unset(packed, 0); /* Ai - Bi mean */
143
144 cpl_imagelist_set(alist, aimage, cpl_imagelist_get_size(alist));
145 cpl_imagelist_set(blist, dimage, cpl_imagelist_get_size(blist));
146
147 }
148
149 skip_if_lt(0, cpl_imagelist_get_size(packed), "Too many packed frames");
150
151 /* After this call the ith image in the list is Ai - Bi */
152 bug_if(visir_imagelist_unpack_interm(blist));
153
154 /* remove extra image again */
155 if (prevd)
156 (void)cpl_imagelist_unset(blist, prevd_insert_pos);
157
158 for (int i = 0; i < naxis3/2; i++) {
159 cpl_image * aimage = cpl_imagelist_get(alist, i);
160 cpl_image * dimage = cpl_imagelist_get(blist, i);
161 /* Compute Bi from Ai - (Ai - Bi). */
162 cpl_image * bimage = cpl_image_subtract_create(aimage, dimage);
163
164 visir_offset_hcycle(aimage);
165 visir_offset_hcycle(bimage);
166 /* Deallocate the difference image and set Bi in its place */
167 bug_if(cpl_imagelist_set(blist, bimage, i));
168
169 }
170
171 skip_if_lt(0, cpl_imagelist_get_size(packed), "Too many packed frames");
172
173 end_skip;
174
175 return cpl_error_get_code();
176
177}
178
179
180/*----------------------------------------------------------------------------*/
192/*----------------------------------------------------------------------------*/
193static cpl_imagelist * load_range(const char * filename,
194 cpl_type im_type,
195 int iext,
196 int pstart,
197 int pend)
198{
199 int selfsize = 0;
200 cpl_imagelist * self = cpl_imagelist_new();
201
202 for (int iplane = pstart; iplane < pend; iplane++) {
203 cpl_image * image = cpl_image_load(filename, im_type, iplane, iext);
204
205 if (image == NULL) break;
206
207 if (cpl_imagelist_set(self, image, selfsize)) {
208 cpl_image_delete(image);
209 break;
210 }
211
212 selfsize++;
213 }
214 return self;
215}
216
217
218/*----------------------------------------------------------------------------*/
241/*----------------------------------------------------------------------------*/
242cpl_error_code visir_load_cube2_split(cpl_imagelist * alist, cpl_imagelist * blist,
243 const irplib_framelist * rawframes, int pos,
244 const int planestart, const int planeend)
245{
246
247 cpl_imagelist * packed = NULL;
248 const cpl_frame * frame = irplib_framelist_get_const(rawframes, pos);
249 const char * file = cpl_frame_get_filename(frame);
250 int naxis3;
251 visir_data_type data_type;
252 int pend;
253 cpl_image * prevd = NULL;
254 const cpl_propertylist * plist =
255 irplib_framelist_get_propertylist_const(rawframes, pos);
256 const int iext = cpl_propertylist_has(plist, "ZNAXIS3") ? 1 : 0;
257 const int nchop = plist ? visir_pfits_get_chop_ncycles(plist) : -1;
258
259 skip_if(0);
260
261 skip_if(visir_get_data_type(frame, plist, &data_type, NULL));
262
263 cpl_ensure_code(data_type == VISIR_DATA_CUBE2, CPL_ERROR_ILLEGAL_INPUT);
264
265 naxis3 = visir_pfits_get_naxis3(plist);
266
267 /* both ranges must be even except for the last chunk where it must
268 * be uneven for the average image at the end of the CUBE2 file */
269 error_if(planestart % 2 == 1, CPL_ERROR_ILLEGAL_INPUT,
270 "Plane start %d wrong. It must be even.", planestart);
271 error_if(planeend < naxis3 && planeend % 2 == 1, CPL_ERROR_ILLEGAL_INPUT,
272 "Plane end %d wrong. It must be even if not larger naxis3=%d",
273 planeend, naxis3);
274
275 any_if("Cannot split non-CUBE2 frame %d/%d in %s", 1+pos,
276 irplib_framelist_get_size(rawframes), file);
277 bug_if(alist == NULL);
278 bug_if(blist == NULL);
279
280 error_if(data_type != VISIR_DATA_CUBE2, CPL_ERROR_INCOMPATIBLE_INPUT,
281 "Cannot split non-CUBE2 frame %d/%d w. NAXIS3=%d, "
282 "NCYCLES=%d in %s", 1+pos,
283 irplib_framelist_get_size(rawframes), naxis3, nchop, file);
284
285 if (planeend >= naxis3 || planeend < 0)
286 pend = naxis3 - 1; /* don't load sum */
287 else
288 pend = planeend;
289
290 if (planestart != 0) {
291 packed = load_range(file, CPL_TYPE_UNSPECIFIED,
292 iext, planestart - 1, pend);
293 prevd = cpl_imagelist_unset(packed, 0);
294 }
295 else
296 packed = load_range(file, CPL_TYPE_UNSPECIFIED, iext, planestart, pend);
297
298 skip_if(visir_load_cube2_split_(alist, blist, packed, prevd));
299
300 end_skip;
301
302 cpl_image_delete(prevd);
303 cpl_imagelist_delete(packed);
304
305 return cpl_error_get_code();
306}
307
308
309/*----------------------------------------------------------------------------*/
326/*----------------------------------------------------------------------------*/
327static
328cpl_error_code visir_load_burst_(cpl_imagelist * alist, cpl_imagelist * blist,
329 cpl_imagelist * packed,
330 const int ichopchange, const int ihalfcycle,
331 const int trimlow, int const trimhigh)
332{
333 cpl_boolean bon = CPL_TRUE;
334 const int offset = 0;
335 const int pend = cpl_imagelist_get_size(packed);
336 int lorej = ihalfcycle - trimlow;
337 int hirej = trimhigh + 1; /* +1 to reject the actual changing frame too */
338
339 /* get frame where chopper moved from on to off in the _previous_
340 * half exposure */
341 int chpmv = ichopchange - ihalfcycle * 2;
342
343 cpl_ensure_code(trimhigh >= -1, CPL_ERROR_ILLEGAL_INPUT);
344 cpl_ensure_code(trimlow >= 0, CPL_ERROR_ILLEGAL_INPUT);
345 cpl_ensure_code(ichopchange < ihalfcycle * 2, CPL_ERROR_ILLEGAL_INPUT);
346 cpl_ensure_code(alist != NULL, CPL_ERROR_NULL_INPUT);
347 cpl_ensure_code(blist != NULL, CPL_ERROR_NULL_INPUT);
348
349 skip_if(0);
350
351 /* Split the cube and skip the planes where the chopper
352 * changes +- a safety margin
353 * always starts from on -> off movement even if negative */
354 for (int i = chpmv; i < pend; i++) {
355 if ((i + ihalfcycle * 2) % ihalfcycle == ichopchange % ihalfcycle) {
356 bon = !bon;
357 hirej = trimhigh + 1;
358 lorej = ihalfcycle - trimlow;
359 }
360 if (hirej <= 0 && lorej > 0 && i >= 0) {
361 if (bon) {
362 cpl_image * image= cpl_imagelist_unset(packed, offset);
363 cpl_imagelist_set(alist, image, cpl_imagelist_get_size(alist));
364 }
365 else {
366 cpl_image * image= cpl_imagelist_unset(packed, offset);
367 cpl_imagelist_set(blist, image, cpl_imagelist_get_size(blist));
368 }
369 }
370 else if (i >= 0)
371 cpl_image_delete(cpl_imagelist_unset(packed, offset));
372
373 hirej--;
374 lorej--;
375 }
376
377 cpl_msg_info(cpl_func, "On: %d, Off %d, Skipped %d",
378 (int)cpl_imagelist_get_size(alist),
379 (int)cpl_imagelist_get_size(blist),
380 (int)(pend - cpl_imagelist_get_size(alist)
381 - cpl_imagelist_get_size(blist)));
382
383 skip_if_lt(0, cpl_imagelist_get_size(packed), "Too many packed frames");
384
385 end_skip;
386
387 return cpl_error_get_code();
388}
389
390
391/*----------------------------------------------------------------------------*/
400/*----------------------------------------------------------------------------*/
401static int get_to_off_plane(int chopchange, const int offset,
402 const int ihalfcycle)
403{
404 const int ifullcycle = ihalfcycle * 2;
405 /* if offset in second hcycle beginning on/off state is flipped */
406 if (offset -
407 (offset / ifullcycle) * ifullcycle > chopchange)
408 chopchange += ifullcycle - (offset % ifullcycle);
409 else
410 chopchange -= (offset % ifullcycle);
411 return chopchange;
412}
413
414
415/*----------------------------------------------------------------------------*/
436/*----------------------------------------------------------------------------*/
437cpl_error_code visir_load_burst(cpl_imagelist * alist, cpl_imagelist * blist,
438 const cpl_frame * frame, const cpl_propertylist * plist,
439 const int ichopchange, const int ihalfcycle,
440 const int planestart, const int planeend,
441 const int trimlow, const int trimhigh)
442{
443 const char * file = cpl_frame_get_filename(frame);
444 const int naxis3 = visir_pfits_get_naxis3(plist);
445 const int pend = planeend <= 0 || planeend > naxis3 ? naxis3 : planeend;
446 cpl_imagelist * packed;
447 int to_off = get_to_off_plane(ichopchange, planestart, ihalfcycle);
448
449 cpl_msg_info(cpl_func, "Loading planes %d to %d, to off %d",
450 planestart, pend, planestart + to_off);
451
452 packed = load_range(file, CPL_TYPE_UNSPECIFIED,
453 cpl_propertylist_has(plist, "ZNAXIS3") ? 1 : 0,
454 planestart, pend);
455
456 skip_if(packed == NULL);
457 /* we don't need the input in the page cache anymore */
458 if (cpl_imagelist_get_size(packed) > 0) {
459 cpl_image * timg = cpl_imagelist_get(packed, 0);
460 size_t nbytes = visir_get_nbytes(timg);
461 visir_drop_cache(file, 0, nbytes * pend);
462 }
463
464 skip_if(visir_load_burst_(alist, blist, packed, to_off, ihalfcycle,
465 trimlow, trimhigh));
466
467 end_skip;
468
469 cpl_imagelist_delete(packed);
470
471 return cpl_error_get_code();
472}
473
474
475cpl_error_code
476visir_load_burst_aqu(cpl_imagelist * alist, cpl_imagelist * blist,
477 const cpl_frame * frame, const cpl_propertylist * plist,
478 const int ihalfcycle,
479 const int planestart, const int planeend)
480{
481 const char * file = cpl_frame_get_filename(frame);
482 const int naxis3 = visir_pfits_get_naxis3(plist);
483 const size_t pend = planeend <= 0 || planeend > naxis3 ? naxis3 : planeend;
484 cpl_imagelist * packed;
485
486 cpl_msg_info(cpl_func, "Loading planes %d to %zu",
487 planestart, pend);
488
489 packed = load_range(file, CPL_TYPE_UNSPECIFIED,
490 cpl_propertylist_has(plist, "ZNAXIS3") ? 1 : 0,
491 planestart, pend);
492
493 skip_if(packed == NULL);
494 /* we don't need the input in the page cache anymore */
495 if (cpl_imagelist_get_size(packed) > 0) {
496 cpl_image * timg = cpl_imagelist_get(packed, 0);
497 size_t nbytes = visir_get_nbytes(timg);
498 visir_drop_cache(file, 0, nbytes * pend);
499 }
500
501 cpl_boolean bon = CPL_FALSE;
502 int it = 0;
503 for (size_t i = planestart; i < pend; i++) {
504 if (bon) {
505 cpl_image * image = cpl_imagelist_unset(packed, 0);
506 cpl_imagelist_set(alist, image, cpl_imagelist_get_size(alist));
507 }
508 else {
509 cpl_image * image = cpl_imagelist_unset(packed, 0);
510 cpl_imagelist_set(blist, image, cpl_imagelist_get_size(blist));
511 }
512 it++;
513 if (it == ihalfcycle){
514 bon = !bon;
515 it = 0;
516 }
517 }
518
519 end_skip;
520
521 cpl_imagelist_delete(packed);
522
523 return cpl_error_get_code();
524}
525
526/* ---------------------------------------------------------------------------*/
535/* ---------------------------------------------------------------------------*/
536cpl_bivector * visir_load_lintable(cpl_frame * linframe, cpl_boolean is_spec)
537{
538 cpl_bivector * lintable = NULL;
539 const char * extname;
540 cpl_ensure(linframe, CPL_ERROR_NULL_INPUT, NULL);
541 if (is_spec) {
542 extname = "SPEC_LIN";
543 }
544 else {
545 extname = "IMAGE_LIN";
546 }
547 const char * fn = cpl_frame_get_filename(linframe);
548 cpl_size iext = cpl_fits_find_extension(fn, extname);
549 error_if(iext < 0, CPL_ERROR_ILLEGAL_INPUT,
550 "Linearity correction extension %s not found in %s", extname, fn);
551
552 cpl_table * tab = cpl_table_load(fn, iext, 0);
553 const size_t nrow = cpl_table_get_nrow(tab);
554 lintable = cpl_bivector_new(nrow);
555 memcpy(cpl_bivector_get_x_data(lintable),
556 cpl_table_get_data_double(tab, "dc_level"),
557 sizeof(double) * nrow);
558 memcpy(cpl_bivector_get_y_data(lintable),
559 cpl_table_get_data_double(tab, "conv_gain"),
560 sizeof(double) * nrow);
561 cpl_table_delete(tab);
562
563 /* normalize by average */
564 cpl_vector_divide_scalar(cpl_bivector_get_y(lintable),
565 cpl_vector_get_mean(cpl_bivector_get_y(lintable)));
566cleanup:
567 return lintable;
568}
569
570
571cpl_image *
572visir_load_bpm(cpl_frame * frm, visir_data_type dtype, cpl_boolean is_spec)
573{
574 const char * fn = cpl_frame_get_filename(frm);
575 const char * extname;
576 if (visir_data_is_aqu(dtype)) {
577 extname = is_spec ? "BPM_AQU_SPC" : "BPM_AQU_IMG";
578 }
579 else {
580 extname = is_spec ? "BPM_DRS_SPC" : "BPM_DRS_IMG";
581 }
582 cpl_size iext = cpl_fits_find_extension(fn, extname);
583 cpl_msg_info(cpl_func, "Loading BPM extension %s from %s", extname, fn);
584 if (iext < 0) {
585 return NULL;
586 }
587 return cpl_image_load(fn, CPL_TYPE_UNSPECIFIED, 0, iext);
588}
589
590/*----------------------------------------------------------------------------*/
612/*----------------------------------------------------------------------------*/
613cpl_image ** visir_img_collapse_beam(cpl_propertylist * qclist,
614 const cpl_image * self,
615 const cpl_parameterlist * parlist,
616 const char * recipename,
617 visir_chopnod_mode mode,
618 const cpl_propertylist * plist)
619{
620
621 cpl_image ** combined = NULL;
622 /* Need to invert the negative beams */
623 cpl_image * inverse = cpl_image_multiply_scalar_create(self, -1.0);
624
625 const double eccmax = visir_parameterlist_get_double(parlist, recipename,
626 VISIR_PARAM_ECCMAX);
627
628 /* Get the chopping throw in pixels */
629 const double pscale = visir_pfits_get_pixscale(plist);
630 const double pthrow = pscale > 0.0
631 ? visir_pfits_get_chop_throw(plist) / pscale : 0.0;
632 double angle = visir_pfits_get_chop_posang(plist);
633
634 skip_if(self == NULL);
635 skip_if(parlist == NULL);
636 skip_if(qclist == NULL);
637 skip_if(plist == NULL);
638
639 if (mode == VISIR_CHOPNOD_PERPENDICULAR) {
640 /* 4 sources */
641 combined = visir_img_collapse_beam_four(qclist, self, inverse, eccmax,
642 pthrow, angle, plist);
643 } else if (mode == VISIR_CHOPNOD_PARALLEL) {
644 /* 3 sources */
645 combined = visir_img_collapse_beam_three(qclist, self, inverse, eccmax,
646 pthrow, plist);
647 } else if (mode == VISIR_CHOPNOD_AUTO) {
648 cpl_errorstate cleanstate = cpl_errorstate_get();
649
650 const char * sdir = visir_pfits_get_chopnod_dir(plist);
651
652 if (sdir != NULL && !strcmp(sdir, "PERPENDICULAR")) {
653 /* 4 sources */
654 combined = visir_img_collapse_beam_four(qclist, self, inverse,
655 eccmax, pthrow, angle, plist);
656 } else if (sdir != NULL && !strcmp(sdir, "PARALLEL")) {
657 /* 3 sources */
658 combined = visir_img_collapse_beam_three(qclist, self, inverse,
659 eccmax, pthrow, plist);
660 } else {
661 if (sdir == NULL) {
662 visir_error_reset("Could not get FITS key");
663 } else {
664 cpl_msg_warning(cpl_func, "Unknown chopping direction: %s",
665 sdir);
666 }
667 cpl_msg_warning(cpl_func, "Proceeding as if FITS card "
668 VISIR_PFITS_STRING_CHOPNOD_DIR " had value: %s",
669 "PERPENDICULAR");
670 combined = visir_img_collapse_beam_four(qclist, self, inverse,
671 eccmax, pthrow, angle, plist);
672 if (combined == NULL) {
673 visir_error_reset("Proceeding as if FITS card "
674 VISIR_PFITS_STRING_CHOPNOD_DIR
675 " had value: %s", "PARALLEL");
676 combined = visir_img_collapse_beam_three(qclist, self, inverse,
677 eccmax, pthrow, plist);
678 }
679 }
680 } else {
681 bug_if(1);
682 }
683
684 skip_if(combined == NULL);
685
686 bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM THROW",
687 pthrow));
688 bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM THROW",
689 "The throw in pixels (TEL CHOP THROW "
690 "divided by INS PFOV)"));
691
692 bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM XPOS",
693 "The X pixel position (centroid) "
694 "of the one-beam object"));
695
696 bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM YPOS",
697 "The Y pixel position (centroid) "
698 "of the one-beam object"));
699 bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM ECCENTRICITY",
700 "Eccentricity: 0 for perfect, throw-"
701 "sized square/line"));
702
703 end_skip;
704
705 cpl_image_delete(inverse);
706
707 return combined;
708}
709
710
711/*----------------------------------------------------------------------------*/
794/*----------------------------------------------------------------------------*/
795cpl_imagelist * visir_inputs_combine(const char * recipename,
796 const cpl_parameterlist * parlist,
797 const irplib_framelist * rawframes,
798 const char * badpix,
799 const char * flat,
800 int * nodding_p,
801 cpl_boolean do_spc_fix,
802 double wlen,
803 visir_spc_resol resol)
804{
805 const char * fnodpos;
806 int nfiles;
807 cpl_imagelist * in = NULL;
808 cpl_image * collapsed = NULL;
809 cpl_image * prev = NULL;
810 cpl_vector * nods_vec = NULL;
811 double * nods_data;
812 int * nod_pos = NULL;
813 cpl_image ** images = NULL;
814 cpl_imagelist * nodded = NULL;
815 int nnod;
816 cpl_image * flat_image = NULL;
817 cpl_image * bpm_im_int = NULL;
818 cpl_mask * bpm_im_bin = NULL;
819 cpl_imagelist * hcycle = NULL;
820 cpl_boolean no_rem;
821 cpl_boolean is_nodding = CPL_FALSE;
822 int i, j;
823 cpl_boolean auto_bpm, rem_glitch, rem_bad;
824 int ndestripe;
825 int naxis1, naxis2;
826 cpl_boolean morpho_destripe;
827 double tstart, tstop;
828 const cpl_propertylist * plist1;
829#ifdef _OPENMP
830 cpl_errorstate cleanstate = cpl_errorstate_get();
831#endif
832 cpl_error_code didfail = CPL_ERROR_NONE;
833
834
835 skip_if (0);
836 skip_if(recipename == NULL);
837 skip_if(parlist == NULL);
838 skip_if(rawframes == NULL);
839
840 /* Get the number of files */
841 nfiles = irplib_framelist_get_size(rawframes);
842
843 /* There should be an even number of files */
844 if (nfiles % 2) {
845 cpl_msg_warning(cpl_func, "Expecting even number of files, "
846 "ignoring the last of %d file(s)", nfiles);
847 error_if (nfiles == 1, CPL_ERROR_DATA_NOT_FOUND,
848 "At least two files are required");
849 nfiles--;
850 }
851
852 nnod = nfiles/2;
853
854 skip_if (nnod <= 0);
855
856 skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_FRAME_TYPE,
857 CPL_TYPE_STRING, CPL_FALSE, 0.0));
858 skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_DOUBLE_DIT,
859 CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
860 /* Can only combine images of the same size */
861 skip_if (irplib_framelist_contains(rawframes, "NAXIS1",
862 CPL_TYPE_INT, CPL_TRUE, 0.0));
863 skip_if (irplib_framelist_contains(rawframes, "NAXIS2",
864 CPL_TYPE_INT, CPL_TRUE, 0.0));
865 skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_NAXIS3,
866 CPL_TYPE_INT, CPL_FALSE, 0.0));
867 skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_CHOP_NCYCLES,
868 CPL_TYPE_INT, CPL_FALSE, 0.0));
869
870 plist1 = irplib_framelist_get_propertylist_const(rawframes, 0);
871 naxis1 = irplib_pfits_get_int(plist1, "NAXIS1");
872 naxis2 = irplib_pfits_get_int(plist1, "NAXIS2");
873 skip_if(0);
874
875 /* Retrieve input parameters */
876 fnodpos = visir_parameterlist_get_string(parlist, recipename,
877 VISIR_PARAM_NODPOS);
878 skip_if (0);
879
880 auto_bpm = visir_parameterlist_get_bool(parlist, recipename,
881 VISIR_PARAM_AUTOBPM);
882 skip_if (0);
883
884 rem_glitch = visir_parameterlist_get_bool(parlist, recipename,
885 VISIR_PARAM_GLITCH);
886 skip_if (0);
887
888 rem_bad = visir_parameterlist_get_bool(parlist, recipename,
889 VISIR_PARAM_PURGE);
890 skip_if (0);
891
892 ndestripe = visir_parameterlist_get_int(parlist, recipename,
893 VISIR_PARAM_STRIPITE);
894 bug_if (0);
895
896 morpho_destripe = ndestripe <= 0 ? CPL_FALSE :
897 visir_parameterlist_get_bool(parlist, recipename,
898 VISIR_PARAM_STRIPMOR);
899 bug_if (0);
900
901 no_rem = !rem_glitch && !rem_bad;
902
903 /* Each file corresponds to a nodding position (object=1 or sky=-1) */
904 /* Return nod_pos array if requested */
905 nod_pos = nodding_p ? nodding_p : cpl_malloc(nfiles * sizeof(int));
906 j = 0;
907 if (!visir_str_par_is_empty(fnodpos)) {
908 /* Get the nodding positions from the user-provided ascii file */
909 nods_vec = cpl_vector_read(fnodpos);
910 skip_if (cpl_vector_get_size(nods_vec) != nfiles);
911 nods_data = cpl_vector_get_data(nods_vec);
912 skip_if (0);
913 for (i=0 ; i<nfiles ; i++) {
914 if ((int)nods_data[i] == 0) {
915 nod_pos[i] = 1;
916 j++;
917 } else if ((int)nods_data[i] == 1) {
918 nod_pos[i] = -1;
919 is_nodding = CPL_TRUE;
920 } else {
921 error_if(1, CPL_ERROR_BAD_FILE_FORMAT,
922 "Wrong values in line %d in %s", i+1, fnodpos);
923 }
924 }
925 } else {
926 skip_if (irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_NODPOS,
927 CPL_TYPE_STRING, CPL_FALSE, 0.0));
928 }
929
930 if (no_rem) cpl_msg_info(cpl_func, "No glitch removal and no purge of bad "
931 "frames requested: Using fast I/O method");
932
933 /* Initialize the Bad Pixel Map */
934 if (badpix != NULL) {
935 /* The bpm is provided by the user */
936 cpl_msg_info(cpl_func, "Loading bad pixel map from %s", badpix);
937 /* Load the bad pixels image */
938 bpm_im_int = cpl_image_load(badpix, CPL_TYPE_INT, 0, 0);
939 skip_if (0);
940
941 /* Convert the map from integer to binary */
942 bpm_im_bin = cpl_mask_threshold_image_create(bpm_im_int, -0.5, 0.5);
943 cpl_image_delete(bpm_im_int);
944 bpm_im_int = NULL;
945 skip_if (cpl_mask_not(bpm_im_bin));
946 } else if (auto_bpm) {
947 /* Initialize the Bad Pixel Map using the hcycle */
948
949 /* i == 0 */
950 hcycle = visir_load_imagelist(rawframes, 0, CPL_FALSE);
951 skip_if(0);
952
953 bpm_im_bin =
954 cpl_mask_threshold_image_create(cpl_imagelist_get(hcycle,0),
955 VISIR_HCYCLE_BPM_THRESHOLD,
956 DBL_MAX);
957 cpl_imagelist_delete(hcycle);
958 hcycle = NULL;
959 skip_if(0);
960 }
961
962 /* Initialize the flat field image */
963 if (flat != NULL) {
964 cpl_msg_info(cpl_func, "Divide the nodded images by the flatfield");
965 /* Load the flat image */
966 flat_image = cpl_image_load(flat, CPL_TYPE_FLOAT, 0, 0);
967 any_if ("Cannot load the flat field %s", flat ? flat : "<NULL>");
968 }
969
970 /* Get nodding position (if needed) and DIT from the header */
971 nodded = cpl_imagelist_new();
972
973 tstart = cpl_test_get_walltime();
974
975 /* Fill list with empty images of proper size and type */
976
977 for (i=0; i < nfiles/2 ; i++) {
978 cpl_image * empty = cpl_image_new(naxis1, naxis2, CPL_TYPE_FLOAT);
979
980 /* i'th image can only be inserted when i-1'th is there,
981 which prevents parallelism */
982
983 bug_if (cpl_imagelist_set(nodded, empty, i));
984 }
985
986#ifdef _OPENMP
987#pragma omp parallel for private(i) firstprivate(prev, collapsed) \
988 schedule(static, 2)
989#endif
990 for (i = 0; i < nfiles ; i++) {
991 cpl_error_code errori = cpl_error_get_code();
992
993 /* The total number of iterations must be pre-determined for the
994 parallelism to work. In case of an error we can therefore not
995 break, so instead we skip immediately to the next iteration.
996 FIXME: This check on didfail does not guarantee that only one
997 iteration can cause an error to be dumped, but it is not
998 worse than checking on the thread-local state, errori. */
999 if (didfail) continue;
1000
1001 do {
1002
1003 const char * file =
1004 cpl_frame_get_filename(irplib_framelist_get_const(rawframes, i));
1005 const cpl_propertylist * plist;
1006
1007 double dit;
1008 double factor;
1009
1010
1011 plist = irplib_framelist_get_propertylist_const(rawframes, i);
1012 if (plist == NULL) {
1013 errori = cpl_error_set_where(cpl_func);
1014 break;
1015 }
1016
1017 if (nods_vec == NULL) {
1018 const char * sval = visir_pfits_get_nodpos(plist);
1019 if (sval == NULL) {
1020 errori = cpl_error_set_message(cpl_func,
1021 CPL_ERROR_DATA_NOT_FOUND,
1022 "Cannot get nodding position "
1023 "for file %d/%d", i+1, nfiles);
1024 break;
1025 }
1026 if (!strcmp(sval, "A")) {
1027 nod_pos[i] = 1;
1028# ifdef _OPENMP
1029# pragma omp atomic
1030# endif
1031 j++;
1032 } else {
1033 nod_pos[i] = -1;
1034 is_nodding = CPL_TRUE;
1035 }
1036 }
1037
1038 /* Print the file name with its nodding position */
1039 cpl_msg_info(cpl_func, "File %02d: %s (%c)", i+1, file,
1040 nod_pos[i]==1 ? '+' : '-');
1041
1042 /* With nodding each pair must have exactly one object observation */
1043 if (is_nodding && (i & 1) == 1 && nod_pos[i] == nod_pos[i-1]) {
1044 cpl_msg_error(cpl_func, "Nodding pair (%d,%d) does not comprise an "
1045 "on-object (A) and an off-object (B) image: %s", i-1,
1046 i, nod_pos[i] == 1 ? "A" : "B");
1047 }
1048
1049 /* Compute the normalization factor from the Detector Integration Time */
1050 dit = visir_pfits_get_dit(plist);
1051 if (cpl_error_get_code()) {
1052 errori = cpl_error_set_where(cpl_func);
1053 break;
1054 }
1055
1056 if (dit <= 0) {
1057 errori = cpl_error_set_message(cpl_func,
1058 CPL_ERROR_ILLEGAL_INPUT,
1059 "DIT in file %d/%d is too small: "
1060 "%g", i+1, nfiles, dit);
1061 break;
1062 }
1063
1064 factor = dit * nod_pos[i] * 2.0;
1065
1066 if (no_rem){
1067 collapsed = visir_load_average(file, plist);
1068 } else {
1069 in = visir_load_intermint(rawframes, i);
1070 if (in == NULL) {
1071 errori = cpl_error_set_message(cpl_func,
1072 CPL_ERROR_ILLEGAL_INPUT,
1073 "Could not load image set %d",
1074 i+1);
1075 break;
1076 }
1077
1078 /* Convert the image lists from 'INTERM' to A-B' */
1079 if (visir_imagelist_unpack_interm(in)) {
1080 errori = cpl_error_set_message(cpl_func,
1081 cpl_error_get_code(),
1082 "Failure for file %d/%d",
1083 i+1, nfiles);
1084 break;
1085 }
1086
1087 /* Remove the glitch in each A-B image in each input cube
1088 if requested */
1089 if (rem_glitch) {
1090 int jj;
1091 for (jj=0 ; jj < cpl_imagelist_get_size(in); jj++) {
1092 if (visir_rem_glitch(cpl_imagelist_get(in, jj))) {
1093 errori = cpl_error_set_message(cpl_func,
1094 cpl_error_get_code(),
1095 "Could not remove "
1096 "glitch in image %d in "
1097 "set %d", jj+1, i+1);
1098 break;
1099 }
1100 }
1101 }
1102
1103 /* Remove the bad A-B images in each input file/cube if requested */
1104 if (rem_bad) {
1105 cpl_msg_info(cpl_func, "Remove the bad A-B input images");
1106 if (visir_rem_bad_images(in)) {
1107 errori = cpl_error_set_message(cpl_func,
1108 cpl_error_get_code(),
1109 "Could not remove bad "
1110 "images in list %d", i+1);
1111 break;
1112 }
1113 }
1114 /* Average each cube */
1115 collapsed = cpl_imagelist_collapse_create(in);
1116
1117 cpl_imagelist_delete(in);
1118 in = NULL;
1119
1120 }
1121
1122 if (cpl_error_get_code()) {
1123 errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1124 "Failure for file %d/%d",
1125 i+1, nfiles);
1126 break;
1127 }
1128 /* Normalise to have ADU/s. */
1129 /* Also divide with 2 to achieve average of image pair */
1130 if (cpl_image_divide_scalar(collapsed, 2*factor)) {
1131 errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1132 "Failure for file %d/%d",
1133 i+1, nfiles);
1134 break;
1135 }
1136
1137 /* Each pair of input files gives a nodded image in nodded */
1138 if (i & 1) {
1139 if (cpl_image_add(prev, collapsed)) {
1140 errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1141 "Failure for file %d/%d",
1142 i+1, nfiles);
1143 break;
1144 }
1145 cpl_image_delete(collapsed);
1146 collapsed = NULL;
1147
1148 /* At this point prev is the image to be put into
1149 the list of nodded images */
1150
1151 if (bpm_im_bin != NULL) {
1152 /* Apply the bad pixels cleaning */
1153 if (cpl_image_reject_from_mask(prev, bpm_im_bin)) {
1154 errori = cpl_error_set_message(cpl_func,
1155 cpl_error_get_code(),
1156 "Failure for file %d/%d",
1157 i+1, nfiles);
1158 break;
1159 }
1160 if (cpl_detector_interpolate_rejected(prev)) {
1161 errori = cpl_error_set_message(cpl_func,
1162 cpl_error_get_code(),
1163 "Failure for file %d/%d",
1164 i+1, nfiles);
1165 break;
1166 }
1167 }
1168
1169 if (ndestripe > 0)
1170 if(visir_destripe_image(prev, ndestripe,
1171 VISIR_DESTRIPE_DETECT,
1172 VISIR_DESTRIPE_DETECT_THRESHOLD,
1173 morpho_destripe)) {
1174 errori = cpl_error_set_message(cpl_func,
1175 cpl_error_get_code(),
1176 "Failure for file %d/%d",
1177 i+1, nfiles);
1178 break;
1179 }
1180
1181 if (flat_image != NULL) {
1182 /* Apply the flatfield correction */
1183 if (cpl_image_divide(prev, flat_image)) {
1184 errori = cpl_error_set_message(cpl_func,
1185 cpl_error_get_code(),
1186 "Failure for file %d/%d",
1187 i+1, nfiles);
1188 break;
1189 }
1190 }
1191
1192 if (cpl_imagelist_set(nodded, prev, i/2)) {
1193 errori = cpl_error_set_message(cpl_func, cpl_error_get_code(),
1194 "Failure for file %d/%d",
1195 i+1, nfiles);
1196 break;
1197 }
1198 prev = NULL;
1199 } else {
1200 prev = collapsed;
1201 collapsed = NULL;
1202 }
1203 } while (0);
1204
1205 if (errori) {
1206#ifdef _OPENMP
1207 /* Cannot access these errors after the join,
1208 so dump them now. :-(((((((((((((((((((( */
1209 cpl_errorstate_dump(cleanstate, CPL_FALSE, NULL);
1210 cpl_errorstate_set(cleanstate);
1211
1212 /* Need to deallocate the thread-private instances in the loop */
1213 cpl_image_delete(prev); prev = NULL;
1214 cpl_image_delete(collapsed); collapsed = NULL;
1215
1216#pragma omp critical(visir_inputs_combine)
1217#endif
1218 didfail = errori;
1219 }
1220 }
1221
1222 error_if(didfail, didfail, "Failed to create %d nodded images from %d "
1223 "files", nnod, nfiles);
1224
1225 tstop = cpl_test_get_walltime();
1226 cpl_msg_info(cpl_func, "Time to create %d nodded images [s]: %g", nnod,
1227 tstop - tstart);
1228
1229 cpl_vector_delete(nods_vec);
1230 nods_vec = NULL;
1231
1232 cpl_image_delete(flat_image);
1233 flat_image = NULL;
1234
1235 cpl_mask_delete(bpm_im_bin);
1236 bpm_im_bin = NULL;
1237
1238 if (nod_pos != nodding_p) cpl_free(nod_pos);
1239 nod_pos = NULL;
1240
1241 error_if(is_nodding && j != nnod, CPL_ERROR_INCOMPATIBLE_INPUT,
1242 "With nodding exactly half of the images "
1243 "must be on-object, not %d of %d", j, 2*nnod);
1244
1245 if (do_spc_fix) {
1246 const double ksi = visir_parameterlist_get_double(
1247 parlist, recipename, VISIR_PARAM_SPECSKEW);
1248 const double eps = visir_parameterlist_get_double(
1249 parlist, recipename, VISIR_PARAM_VERTARC);
1250 const double delta = visir_parameterlist_get_double(
1251 parlist, recipename, VISIR_PARAM_HORIARC);
1252 const double phi = visir_parameterlist_get_double(
1253 parlist, recipename, VISIR_PARAM_SLITSKEW);
1254 const int doplot = visir_parameterlist_get_int(
1255 parlist, recipename, VISIR_PARAM_PLOT);
1256
1257 skip_if (0);
1258
1259 images = cpl_malloc(nnod * sizeof(cpl_image*));
1260
1261 for (j = 0; j < nnod; j++) images[j] = cpl_imagelist_get(nodded, j);
1262
1263 skip_if (visir_spc_det_fix(images, nnod, CPL_TRUE, wlen, resol,
1264 phi, ksi, eps, delta, doplot));
1265 }
1266
1267 end_skip;
1268
1269 cpl_msg_set_time_off();
1270
1271 cpl_free(images);
1272 cpl_imagelist_delete(in);
1273
1274 if (nod_pos != nodding_p) cpl_free(nod_pos);
1275 cpl_vector_delete(nods_vec);
1276 cpl_image_delete(bpm_im_int);
1277 cpl_mask_delete(bpm_im_bin);
1278 cpl_image_delete(collapsed);
1279 cpl_image_delete(prev);
1280 if (cpl_error_get_code() && nodded != NULL) {
1281 cpl_imagelist_delete(nodded);
1282 nodded = NULL;
1283 }
1284
1285 return nodded;
1286}
1287
1288/*----------------------------------------------------------------------------*/
1297/*----------------------------------------------------------------------------*/
1298static double
1299get_cumoffsets(const cpl_propertylist * plist, double * x, double * y)
1300{
1301 cpl_errorstate cleanstate = cpl_errorstate_get();
1302
1303 *x = irplib_pfits_get_double(plist, "ESO DRS CUMOFFSETX");
1304 *y = irplib_pfits_get_double(plist, "ESO DRS CUMOFFSETY");
1305
1306 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1307 cpl_errorstate_set(cleanstate);
1308 cpl_msg_info(cpl_func, "DRS CUMOFFSET[XY] not found, falling back"
1309 " to SEQ CUMOFFSET[XY]");
1310 *x = visir_pfits_get_cumoffsetx(plist);
1311 *y = visir_pfits_get_cumoffsety(plist);
1312 }
1313 return cpl_error_get_code();
1314}
1315
1316/*----------------------------------------------------------------------------*/
1331/*----------------------------------------------------------------------------*/
1332cpl_image ** visir_img_recombine_list(const char * recipename,
1333 const cpl_parameterlist * parlist,
1334 cpl_imagelist * nodded,
1335 const cpl_propertylist ** plists,
1336 cpl_geom_combine combine_mode,
1337 cpl_boolean * pdid_resize)
1338{
1339 cpl_size nnod;
1340 cpl_bivector * offsets_est = NULL;
1341 cpl_bivector * objs = NULL;
1342 cpl_image ** combined = NULL;
1343 cpl_vector * sigmas = NULL;
1344 cpl_propertylist * qclist = cpl_propertylist_new();
1345
1346
1347 bug_if (0);
1348
1349 bug_if (recipename == NULL);
1350 bug_if (parlist == NULL);
1351 bug_if (pdid_resize == NULL);
1352 bug_if (nodded == NULL);
1353
1354 nnod = cpl_imagelist_get_size(nodded);
1355 cpl_msg_debug(cpl_func, "nnod = %lld", nnod);
1356
1357 /* If only one nodded image, the work is finished */
1358 if (nnod == 1) {
1359 combined = cpl_malloc(2*sizeof(cpl_image*));
1360 combined[1] = NULL; /* In case the unset fails */
1361
1362 combined[0] = cpl_imagelist_unset(nodded, 0);
1363 bug_if (combined[0] == NULL);
1364
1365 combined[1] = cpl_image_new(cpl_image_get_size_x(combined[0]),
1366 cpl_image_get_size_y(combined[0]),
1367 CPL_TYPE_INT);
1368 bug_if (combined[1] == NULL);
1369
1370 /* Set all pixel values to 1 */
1371 bug_if(cpl_image_threshold(combined[1], 1.0, 1.0, 1.0, 1.0));
1372
1373 *pdid_resize = CPL_FALSE;
1374
1375 } else {
1376 const double psigmas[] = {5, 2, 1, 0.5};
1377 const char * sval;
1378 const char * offsets;
1379 const char * objects;
1380 int sx, sy, mx, my;
1381 int rej_low, rej_high;
1382 cpl_boolean refine;
1383
1384
1385 refine = visir_parameterlist_get_bool(parlist, recipename,
1386 VISIR_PARAM_REFINE);
1387 skip_if (0);
1388
1389 offsets = visir_parameterlist_get_string(parlist, recipename,
1390 VISIR_PARAM_OFFSETS);
1391 skip_if (0);
1392
1393 objects = visir_parameterlist_get_string(parlist, recipename,
1394 VISIR_PARAM_OBJECTS);
1395 skip_if (0);
1396
1397 sval = visir_parameterlist_get_string(parlist, recipename,
1398 VISIR_PARAM_XCORR);
1399 skip_if (0);
1400
1401 if (sscanf(sval, "%d-%d-%d-%d", &sx, &sy, &mx, &my) != 4)
1402 skip_if (sscanf(sval, "%d %d %d %d", &sx, &sy, &mx, &my) != 4);
1403
1404
1405 sval = visir_parameterlist_get_string(parlist, recipename,
1406 VISIR_PARAM_REJECT);
1407 skip_if (0);
1408
1409 if (sscanf(sval, "%d-%d", &rej_low, &rej_high) !=2 )
1410 skip_if (sscanf(sval, "%d %d", &rej_low, &rej_high) !=2 );
1411
1412 /* Get the offsets estimation of each input file pair */
1413 cpl_msg_info(cpl_func, "Get the offsets estimation");
1414 if (!visir_str_par_is_empty(offsets)) {
1415 /* A file has been provided on the command line */
1416 offsets_est = cpl_bivector_read(offsets);
1417 skip_if (offsets_est==NULL);
1418
1419 error_if (cpl_bivector_get_size(offsets_est) != nnod,
1420 CPL_ERROR_BAD_FILE_FORMAT, "The offsets file %s must "
1421 "have %d entries, not %d", offsets, (int)nnod,
1422 (int)cpl_bivector_get_size(offsets_est));
1423 } else {
1424 double * offsets_est_x;
1425 double * offsets_est_y;
1426 double xoff0, yoff0;
1427
1428 /* Get the offsets from the header */
1429 offsets_est = cpl_bivector_new(nnod);
1430 offsets_est_x = cpl_bivector_get_x_data(offsets_est);
1431 offsets_est_y = cpl_bivector_get_y_data(offsets_est);
1432
1433 skip_if (0);
1434
1435 offsets_est_x[0] = 0.0;
1436 offsets_est_y[0] = 0.0;
1437 get_cumoffsets(plists[0], &xoff0, &yoff0);
1438
1439 for (cpl_size i = 1; i < nnod ; i++) {
1440 double xoff, yoff;
1441
1442 skip_if(get_cumoffsets(plists[i], &xoff, &yoff));
1443
1444 /* Subtract the first offset from all offsets */
1445 offsets_est_x[i] = xoff0 - xoff;
1446 offsets_est_y[i] = yoff0 - yoff;
1447 }
1448 }
1449
1450 /* Read the provided objects file if provided
1451 - if a file has been provided on the command line */
1452 if (!visir_str_par_is_empty(objects)) {
1453 objs = cpl_bivector_read(objects);
1454 any_if ("Could not read objects from %s", objects);
1455 }
1456
1457 cpl_msg_info(cpl_func, "Recombining the list of nodded images using "
1458 "mode: %d (I=%d:U=%d:F=%d), rej-lo=%d, rej-hi=%d",
1459 combine_mode, CPL_GEOM_INTERSECT, CPL_GEOM_UNION,
1460 CPL_GEOM_FIRST, rej_low, rej_high);
1461
1462 if (cpl_msg_get_level() <= CPL_MSG_DEBUG) {
1463 cpl_msg_debug(cpl_func, "The offsets for the recombination:");
1464 cpl_bivector_dump(offsets_est, stdout);
1465 }
1466
1467 IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
1468 sigmas = cpl_vector_wrap(4, (double*)psigmas); /* Not changed */
1469 IRPLIB_DIAG_PRAGMA_POP;
1470 combined = cpl_geom_img_offset_combine(nodded, offsets_est, refine,
1471 objs, sigmas, NULL, sx, sy,
1472 mx, my, rej_low, rej_high,
1473 combine_mode);
1474 any_if("Could not recombine the images");
1475
1476 *pdid_resize = (cpl_boolean)(cpl_image_get_size_x(combined[0])
1477 != cpl_image_get_size_x(cpl_imagelist_get_const(nodded, 0)) ||
1478 cpl_image_get_size_y(combined[0])
1479 != cpl_image_get_size_y(cpl_imagelist_get_const(nodded, 0)));
1480 }
1481
1482 if (visir_parameterlist_get_int(parlist, recipename, VISIR_PARAM_PLOT) > 0)
1483 visir_image_plot("", "t 'The combined image'", "", combined[0]);
1484
1485 end_skip;
1486
1487 cpl_propertylist_delete(qclist);
1488 cpl_bivector_delete(offsets_est);
1489 cpl_bivector_delete(objs);
1490 cpl_vector_unwrap(sigmas);
1491
1492 return combined;
1493}
1494
1495/*----------------------------------------------------------------------------*/
1542/*----------------------------------------------------------------------------*/
1543cpl_image ** visir_img_recombine(const char * recipename,
1544 const cpl_parameterlist * parlist,
1545 const irplib_framelist * rawframes,
1546 const char * badpix,
1547 const char * flat,
1548 cpl_geom_combine combine_mode,
1549 cpl_boolean * pdid_resize,
1550 cpl_boolean do_spc_fix,
1551 double wlen,
1552 visir_spc_resol resol)
1553{
1554 int nfiles;
1555 int * nod_pos = NULL;
1556 cpl_imagelist * nodded = NULL;
1557 cpl_size nnod;
1558 cpl_bivector * objs = NULL;
1559 cpl_propertylist * qclist = cpl_propertylist_new();
1560 const cpl_propertylist ** plists = NULL;
1561 cpl_image ** rec = NULL;
1562
1563
1564 bug_if (0);
1565
1566 bug_if (recipename == NULL);
1567 bug_if (parlist == NULL);
1568 bug_if (rawframes == NULL);
1569 bug_if (pdid_resize == NULL);
1570
1571 /* Get the number of files */
1572 nfiles = irplib_framelist_get_size(rawframes);
1573 cpl_msg_debug(cpl_func, "nfiles = %d", nfiles);
1574
1575 /* There should be an even number of files */
1576 if (nfiles % 2) {
1577 cpl_msg_warning(cpl_func, "Expecting even number of files, "
1578 "ignoring the last of %d file(s)", nfiles);
1579 error_if (nfiles == 1, CPL_ERROR_DATA_NOT_FOUND,
1580 "At least two files are required");
1581 nfiles--;
1582 }
1583
1584 skip_if ( nfiles <= 0);
1585
1586 /* Each file corresponds to a nodding position (object=1 or sky=-1) */
1587 nod_pos = cpl_malloc(nfiles * sizeof(int));
1588
1589 /* Combine the input frames into the nodded images */
1590 cpl_msg_info(cpl_func, "Combining the input frames into the nodded images");
1591 nodded = visir_inputs_combine(recipename, parlist, rawframes, badpix, flat,
1592 nod_pos, do_spc_fix, wlen, resol);
1593 skip_if(nodded == NULL);
1594
1595 nnod = cpl_imagelist_get_size(nodded);
1596 cpl_msg_debug(cpl_func, "nnod = %lld", nnod);
1597 plists = cpl_malloc(nnod * sizeof(cpl_propertylist *));
1598 for (cpl_size i=0; i < nnod ; i++) {
1599 const cpl_size iframe = nod_pos[2*i] == 1 ? 2*i : 2*i+1;
1600
1601 plists[i] = irplib_framelist_get_propertylist_const(rawframes,
1602 iframe);
1603 }
1604
1605 rec = visir_img_recombine_list(recipename, parlist, nodded,
1606 plists, combine_mode, pdid_resize);
1607
1608 end_skip;
1609
1610 cpl_propertylist_delete(qclist);
1611 cpl_free(nod_pos);
1612 cpl_free(plists);
1613 cpl_imagelist_delete(nodded);
1614 cpl_bivector_delete(objs);
1615
1616 return rec;
1617}
1618
1619/*----------------------------------------------------------------------------*/
1642/*----------------------------------------------------------------------------*/
1643cpl_imagelist * visir_load_hcycle(const irplib_framelist * rawframes, int pos)
1644{
1645 return visir_load_imagelist(rawframes, pos, CPL_FALSE);
1646}
1647
1648/*----------------------------------------------------------------------------*/
1654/*----------------------------------------------------------------------------*/
1655visir_aplist * visir_aplist_new_from_file(const char * apfile)
1656{
1657 FILE* fp = fopen(apfile, "r");
1658 if (!fp) {
1659 cpl_msg_error(cpl_func, "Cannot open apfile (supply full path): %s",
1660 apfile);
1661 cpl_error_set(cpl_func, CPL_ERROR_FILE_NOT_FOUND);
1662 return NULL;
1663 }
1664
1665 int ident = 1;
1666 char *line = NULL;
1667 size_t len = 0;
1668 visir_aplist* list = visir_aplist_new();
1669 while (getline(&line, &len, fp) != -1) {
1670 visir_apdefs* ap = visir_apdefs_new_from_line(line, ident);
1671 free(line);
1672 line = NULL;
1673 if (!ap) continue; // blank/empty line
1674 visir_aplist_push_back(list, ap);
1675 ++ident;
1676 }
1677 if (line) {
1678 free(line);
1679 }
1680 fclose(fp);
1681
1682 if (visir_aplist_size(list) < 1) {
1683 cpl_msg_error(cpl_func, "File has no usable entries: %s", apfile);
1684 cpl_error_set(cpl_func, CPL_ERROR_BAD_FILE_FORMAT);
1685 return NULL;
1686 }
1687
1688 return list;
1689}
1690
1691/*----------------------------------------------------------------------------*/
1697/*----------------------------------------------------------------------------*/
1698char * visir_apdefs_dump(const visir_apdefs * ap) {
1699 char buf[80];
1700 int offs = snprintf(buf, sizeof(buf), "%c %d %d", ap->extract_method,
1701 ap->limits[0].l, ap->limits[0].r);
1702 if (ap->nlimits > 1) {
1703 offs += snprintf(buf+offs, sizeof(buf)-offs, " %c", ap->sky_method);
1704 for (size_t i = 1; i < ap->nlimits; ++i)
1705 offs += snprintf(buf+offs, sizeof(buf)-offs, " %d %d",
1706 ap->limits[i].l, ap->limits[i].r);
1707 }
1708 return cpl_sprintf("%s", buf);
1709}
1710
1711/*----------------------------------------------------------------------------*/
1719/*----------------------------------------------------------------------------*/
1720visir_apdefs * visir_apdefs_new_from_line(char * line, int ident)
1721{
1722 // strip comments starting from 1st '#' char, if any
1723 char * term = strchr(line, '#');
1724 if (term) *term = '\0';
1725
1726 char const * const intro = "Error parsing apfile: ";
1727 char* outro = cpl_sprintf(" on apfile line: %s", line);
1728
1729 // tokenise the line and count the tokens
1730 int ntoks = 0;
1731 char max = 40, * toks[max];
1732 char * state, * tok = strtok_r(line, "\n\t ", &state);
1733 for (; tok; tok = strtok_r(NULL, "\n\t ", &state)) {
1734 toks[ntoks++] = tok;
1735 if (ntoks > max) {
1736 cpl_msg_error(cpl_func, "%sToo many tokens%s", intro, outro);
1737 cpl_error_set(cpl_func, CPL_ERROR_BAD_FILE_FORMAT);
1738 cpl_free(outro);
1739 return NULL;
1740 }
1741 }
1742
1743 // ensure ntoks is either 0, 3 or one of (6, 8, 10, 12, etc...)
1744 if (ntoks != 0 && ntoks != 3 && (ntoks < 6 || ntoks % 2)) {
1745 cpl_msg_error(cpl_func, "%sWrong token count%s", intro, outro);
1746 cpl_error_set(cpl_func, CPL_ERROR_BAD_FILE_FORMAT);
1747 cpl_free(outro);
1748 return NULL;
1749 }
1750 const int nlimits = (ntoks == 3 ? 2 : ntoks-2);
1751
1752 // return NULL (without any CPL error set) for blank/empty lines
1753 if (!ntoks) {
1754 cpl_free(outro);
1755 return NULL;
1756 }
1757
1758 // ensure extraction & sky method are properly indicated
1759 if (ntoks == 3) { // optimal extraction requested
1760 if (toks[0][0] != 'O') {
1761 cpl_msg_error(cpl_func, "%sMethod & token count mismatch%s",
1762 intro, outro);
1763 cpl_error_set(cpl_func, CPL_ERROR_BAD_FILE_FORMAT);
1764 cpl_free(outro);
1765 return NULL;
1766 }
1767 } else { // normal aperture extraction requested
1768 if (toks[0][0] != 'A' || !strchr("AFM", toks[3][0])) {
1769 cpl_msg_error(cpl_func, "%sMethod & token count mismatch%s",
1770 intro, outro);
1771 cpl_error_set(cpl_func, CPL_ERROR_BAD_FILE_FORMAT);
1772 cpl_free(outro);
1773 return NULL;
1774 }
1775 }
1776
1777 // ensure all aperture limits will properly scan
1778 int nscans = nlimits;
1779 for (int i = 1; i < ntoks; i += (i==2 ? 2 : 1)) // i skips over 3
1780 nscans -= sscanf(toks[i], "%f", &(float){0});
1781 if (nscans) {
1782 cpl_msg_error(cpl_func, "%sToken scan failure%s", intro, outro);
1783 cpl_error_set(cpl_func, CPL_ERROR_BAD_FILE_FORMAT);
1784 cpl_free(outro);
1785 return NULL;
1786 }
1787
1788 // everything looks okay: create the new object
1789 visir_apdefs* new = visir_apdefs_new(nlimits/2, ident, toks[0][0],
1790 ntoks > 3 ? toks[3][0] : 0);
1791 for (int i = 1, j = 0; i < ntoks; i += (i==2 ? 2 : 1), ++j) {
1792 float temp;
1793 sscanf(toks[i], "%f", &temp);
1794 new->limits[j].l = (int)floor(temp);
1795 sscanf(toks[++i], "%f", &temp);
1796 new->limits[j].r = (int)ceil(temp);
1797 if (new->limits[j].l > new->limits[j].r) {
1798 cpl_msg_error(cpl_func,
1799 "%sLeft aperture limit greater than right%s",
1800 intro, outro);
1801 cpl_error_set(cpl_func, CPL_ERROR_BAD_FILE_FORMAT);
1802 cpl_free(outro);
1803 visir_apdefs_delete(new);
1804 return NULL;
1805 }
1806 }
1807
1808 // if there are multiple sky apertures, sort & ensure no overlaps
1809 if (new->nlimits > 2) {
1810
1811 // selection sort: O(n-squared)
1812 for (size_t i = 1; i < new->nlimits - 1; ++i) {
1813 int mini = i; // index of smallest elem in unsorted part of array
1814 for (size_t j = i + 1; j < new->nlimits; ++j)
1815 if (new->limits[j].l < new->limits[mini].l)
1816 mini = j;
1817 // swap the smallest element with the first
1818 visir_aplimits tmp = new->limits[mini];
1819 new->limits[mini] = new->limits[i];
1820 new->limits[i] = tmp;
1821 }
1822
1823 // ensure no overlaps exist
1824 int nskys = new->nlimits - 1;
1825 int niterations = nskys - 1;
1826 for (int i = 0; i < niterations; ++i)
1827 if (new->limits[i+1].r > new->limits[i+2].l) {
1828 cpl_msg_error(cpl_func, "%sOverlapping apertures found%s",
1829 intro, outro);
1830 cpl_error_set(cpl_func, CPL_ERROR_BAD_FILE_FORMAT);
1831 cpl_free(outro);
1832 visir_apdefs_delete(new);
1833 return NULL;
1834 }
1835 }
1836 cpl_free(outro);
1837 return new;
1838}
1839
1840/*----------------------------------------------------------------------------*/
1851/*----------------------------------------------------------------------------*/
1852cpl_error_code visir_image_reject_hot(cpl_image * self, const char * bpmfile)
1853{
1854
1855 cpl_image * im_bpm = NULL;
1856 cpl_mask * bpm = NULL;
1857 const int upper = VISIR_HCYCLE_BPM_THRESHOLD;
1858
1859
1860 skip_if (0);
1861
1862 skip_if (self == NULL);
1863
1864 if (bpmfile == NULL) {
1865 bpm = cpl_mask_threshold_image_create(self, upper, DBL_MAX);
1866 skip_if (0);
1867 } else {
1868
1869 /* The bpm is provided by the user */
1870 cpl_msg_info(cpl_func, "Clean user specified bad pixels");
1871 /* Load the bad pixel image */
1872 im_bpm = cpl_image_load(bpmfile, CPL_TYPE_INT, 0, 0);
1873 any_if ("Could not load the bad pixel map %s",
1874 bpmfile ? bpmfile : "<NULL>");
1875 /* Convert the map from integer to binary */
1876 bpm = cpl_mask_threshold_image_create(im_bpm, -0.5, 0.5);
1877 skip_if (0);
1878 cpl_image_delete(im_bpm);
1879 im_bpm = NULL;
1880
1881 skip_if (cpl_mask_not(bpm));
1882 }
1883
1884 skip_if (cpl_image_reject_from_mask(self, bpm));
1885
1886 end_skip;
1887
1888 cpl_image_delete(im_bpm);
1889 cpl_mask_delete(bpm);
1890
1891 return cpl_error_get_code();
1892
1893}
1894
1895
1896/*----------------------------------------------------------------------------*/
1903/*----------------------------------------------------------------------------*/
1904cpl_imagelist * visir_imagelist_load_last(const irplib_framelist * rawframes)
1905{
1906 cpl_imagelist * self = NULL;
1907 int naxis3;
1908
1909 /* Verify that NAXIS3 is the same in all files */
1910 skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_NAXIS3,
1911 CPL_TYPE_INT, CPL_TRUE, 0.0));
1912
1913 naxis3 = visir_pfits_get_naxis3(irplib_framelist_get_propertylist_const(
1914 rawframes,
1915 0));
1916
1917 /* Load the image set */
1918 self = irplib_imagelist_load_framelist(rawframes, CPL_TYPE_FLOAT, naxis3-1,
1919 0);
1920
1921 skip_if (self == NULL);
1922
1923 end_skip;
1924
1925 return self;
1926
1927}
1928
1929
1930/*----------------------------------------------------------------------------*/
1940/*----------------------------------------------------------------------------*/
1941cpl_imagelist * visir_load_imagelist(const irplib_framelist * rawframes,
1942 int pos, cpl_boolean is_interm)
1943{
1944 cpl_imagelist * self = NULL;
1945 cpl_image * image = NULL;
1946 const cpl_frame * frame = irplib_framelist_get_const(rawframes, pos);
1947 const cpl_propertylist * plist =
1948 irplib_framelist_get_propertylist_const(rawframes, pos);
1949 const char * file = cpl_frame_get_filename(frame);
1950 const int nchop = plist ? visir_pfits_get_chop_ncycles(plist) : -1;
1951 int naxis3;
1952 visir_data_type data_type;
1953 int plane_step;
1954 int nsize;
1955 int iplane;
1956 int i;
1957
1958 skip_if (plist == NULL);
1959
1960 skip_if(visir_get_data_type(frame, plist, &data_type, NULL));
1961
1962 switch (data_type) {
1963 case VISIR_DATA_AQU_HCYCLE:
1964 case VISIR_DATA_AQU_BURST:
1965 case VISIR_DATA_AQU_BURST_EXT:
1966 error_if(1, CPL_ERROR_UNSUPPORTED_MODE,
1967 "Aquarius data not supported");
1968 default:
1969 naxis3 = visir_pfits_get_naxis3(plist);
1970 break;
1971 }
1972
1973 bug_if (file == NULL);
1974
1975 if (data_type == VISIR_DATA_CUBE1) {
1976 /* All INTERM frames are in the first part of the cube */
1977 iplane = is_interm ? 0 : nchop;
1978 /* - followed by a single Half-cycle image */
1979 nsize = is_interm ? nchop : 1;
1980 plane_step = 1;
1981 } else if (naxis3 == 1) {
1982 iplane = 0;
1983 nsize = 1;
1984 plane_step = 1;
1985 } else {
1986 /* Each INTERM frame follows the Half-cycle frame */
1987 iplane = is_interm ? 1 : 0;
1988 nsize = nchop;
1989 plane_step = 2;
1990 }
1991
1992 self = cpl_imagelist_new();
1993
1994 for (i=0 ; i < nsize; i++, iplane += plane_step) {
1995
1996 image = cpl_image_load(file, CPL_TYPE_FLOAT, iplane, 0);
1997 skip_if (image == NULL);
1998
1999 skip_if (!is_interm && visir_offset_hcycle(image));
2000
2001 skip_if (cpl_imagelist_set(self, image, i));
2002
2003 image = NULL;
2004 }
2005
2006 skip_if (i < nsize);
2007
2008 end_skip;
2009
2010 if (cpl_error_get_code()) {
2011 if (file != NULL) cpl_msg_warning(cpl_func, "Could not load the %s "
2012 "frame(s) from: %s",
2013 is_interm ? "INTERM" : "Half-Cycle",
2014 file);
2015 cpl_image_delete(image);
2016 cpl_imagelist_delete(self);
2017 self = NULL;
2018 }
2019
2020 return self;
2021}
2022
2023
2026/*----------------------------------------------------------------------------*/
2050/*----------------------------------------------------------------------------*/
2051static cpl_imagelist * visir_load_intermint(const irplib_framelist * rawframes,
2052 int pos)
2053{
2054 return visir_load_imagelist(rawframes, pos, CPL_TRUE);
2055
2056}
2057
2058/*----------------------------------------------------------------------------*/
2067/*----------------------------------------------------------------------------*/
2068static cpl_image * visir_load_average(const char * file,
2069 const cpl_propertylist * plist)
2070{
2071 cpl_errorstate cleanstate = cpl_errorstate_get();
2072 cpl_image * self = NULL;
2073 int nchop, naxis3;
2074
2075
2076 skip_if (0);
2077 skip_if (file == NULL);
2078 skip_if (plist == NULL);
2079
2080 naxis3 = visir_pfits_get_naxis3(plist);
2081 nchop = visir_pfits_get_chop_ncycles(plist);
2082
2083 skip_if (0);
2084
2085 if (nchop == 0 && naxis3 == 1) {
2086 self = cpl_image_load(file, CPL_TYPE_FLOAT, 0, 0);
2087 } else {
2088 const char * sval;
2089 /* If nchop == 1 (and naxis3 == 3) the data unit is both a valid CUBE1
2090 and a valid CUBE2; Set plane_offset as if the frame type is CUBE2 */
2091 const int plane_offset = (naxis3 == 2 * nchop + 1) ? 2 : 3;
2092
2093
2094 error_if (nchop <= 0, CPL_ERROR_BAD_FILE_FORMAT, "CHOP NCYCLES in %s "
2095 "is non-positive (and NAXIS3=%d): %d", file, naxis3, nchop);
2096
2097 error_if (plane_offset == 3 && naxis3 != nchop+2,
2098 CPL_ERROR_BAD_FILE_FORMAT, "NAXIS3=%d and CHOP NCYCLES=%d "
2099 "in %s is not a valid VISIR INTERM+Half-Cycle format", naxis3,
2100 nchop, file);
2101
2102 if (plane_offset == 3 && nchop > 1)
2103 cpl_msg_debug(cpl_func, "%s has %d INTERM-frames and one Half-"
2104 "Cycle frame (old CUBE1-format)", file, nchop);
2105
2106 /* Check the data format */
2107 sval = visir_pfits_get_frame_type(plist);
2108 if (sval == NULL) {
2109 /* Has warned about missing frame type card */
2110 visir_error_reset("Could not get FITS key");
2111 /* Don't know whether or not to expect CUBE1 or CUBE2 */
2112 } else if (strlen(sval) == 0) {
2113 /* Don't know whether or not to expect CUBE1 or CUBE2 */
2114 } else if (plane_offset == 3) {
2115 if (strcmp(sval, "CUBE2")==0)
2116 cpl_msg_error(cpl_func, "%s has FRAM TYPE = CUBE2, but NAXIS3="
2117 "%d and CHOP NCYCLES=%d imply a CUBE1. Assuming"
2118 " the frame type is really CUBE1", file,
2119 naxis3, nchop);
2120 } else if (nchop > 1) {
2121 /* if nchop == 1 format can be CUBE1 or CUBE2 */
2122 if (strcmp(sval, "CUBE1")==0)
2123 cpl_msg_error(cpl_func, "%s has FRAM TYPE = CUBE1, but NAXIS3="
2124 "%d and CHOP NCYCLES=%d imply a CUBE2. Assuming"
2125 "the frame type is really CUBE2", file,
2126 naxis3, nchop);
2127 }
2128
2129 /* Load last INTERM frame */
2130 self = cpl_image_load(file, CPL_TYPE_FLOAT, naxis3-plane_offset, 0);
2131
2132 }
2133
2134 skip_if (0);
2135
2136 end_skip;
2137
2138 if (cpl_error_get_code()) {
2139 cpl_msg_warning(cpl_func, "Could not load the last INTERM frame from: "
2140 "%s", file ? file : "<NULL>");
2141 cpl_image_delete(self);
2142 self = NULL;
2143 }
2144
2145 return self;
2146}
2147
2148/*----------------------------------------------------------------------------*/
2171/*----------------------------------------------------------------------------*/
2172static cpl_error_code visir_imagelist_unpack_interm(cpl_imagelist * self)
2173{
2174 cpl_image * iprev;
2175 cpl_image * image;
2176 const int n = cpl_imagelist_get_size(self);
2177 int i;
2178
2179 skip_if (0);
2180
2181 if (n == 1) return CPL_ERROR_NONE;
2182
2183 iprev = cpl_imagelist_get(self, n - 1);
2184
2185 skip_if (0);
2186
2187 skip_if (cpl_image_multiply_scalar(iprev, n));
2188
2189 /* Loop on the images - with first and last iteration peeled off */
2190 for (i = n-1 ; i > 1 ; i--, iprev = image) {
2191 image = cpl_imagelist_get(self, i-1);
2192
2193 skip_if (0);
2194
2195 skip_if (cpl_image_multiply_scalar(image, i));
2196
2197 skip_if (cpl_image_subtract(iprev, image));
2198
2199 }
2200
2201 image = cpl_imagelist_get(self, 0);
2202
2203 skip_if (0);
2204
2205 skip_if (cpl_image_subtract(iprev, image));
2206
2207 end_skip;
2208
2209 return cpl_error_get_code();
2210}
2211
2212/*----------------------------------------------------------------------------*/
2225/*----------------------------------------------------------------------------*/
2226static cpl_error_code visir_rem_glitch(cpl_image * glitchy)
2227{
2228 cpl_image * med_filt = NULL;
2229 cpl_mask * bpm = NULL;
2230 cpl_mask * kernel = cpl_mask_new(3, 3);
2231 double mean, stdev;
2232 double low_thresh, high_thresh;
2233 const int nx = cpl_image_get_size_x(glitchy);
2234 const int ny = cpl_image_get_size_y(glitchy);
2235 int i;
2236
2237 /* Some heuristic constants */
2238 double factor1 = 3.0;
2239 double factor2 = 10.0;
2240 const int niterations = 5;
2241 const double median_corr = 1.5;
2242
2243 bug_if (0);
2244
2245 /* Create the filtering kernel */
2246 bug_if(cpl_mask_not(kernel));
2247
2248 /* Apply a 3x3 median filter to the input image */
2249 med_filt = cpl_image_new(cpl_image_get_size_x(glitchy),
2250 cpl_image_get_size_y(glitchy),
2251 cpl_image_get_type(glitchy));
2252 bug_if(med_filt == NULL);
2253 bug_if(cpl_image_filter_mask(med_filt, glitchy, kernel, CPL_FILTER_MEDIAN,
2254 CPL_BORDER_FILTER));
2255 cpl_mask_delete(kernel);
2256 kernel = NULL;
2257
2258 /* Noise is glitchy - med_filt */
2259 skip_if (cpl_image_subtract(glitchy, med_filt));
2260
2261 /* niterations iterations */
2262 for (i=0 ; i < niterations ; i++) {
2263 /* Compute mean and stdev */
2264 mean = cpl_image_get_mean(glitchy);
2265 stdev = cpl_image_get_stdev(glitchy);
2266
2267 skip_if (0);
2268
2269 /* Set the thresholds */
2270 low_thresh = mean - factor1 * stdev;
2271 high_thresh = mean + factor1 * stdev;
2272
2273 /* Identify where mean-factor1*stdev < glitchy < mean+factor1*stdev */
2274 bpm = cpl_mask_threshold_image_create(glitchy,low_thresh,high_thresh);
2275 skip_if (cpl_mask_not(bpm));
2276 skip_if (cpl_image_reject_from_mask(glitchy, bpm));
2277 cpl_mask_delete(bpm);
2278 bpm = NULL;
2279
2280 /* Test the number of bad pixels */
2281 skip_if (cpl_image_count_rejected(glitchy) == nx*ny);
2282 }
2283
2284 /* Last iteration */
2285 /* Compute mean and stdev */
2286 mean = cpl_image_get_mean(glitchy);
2287 stdev = cpl_image_get_stdev(glitchy) * median_corr;
2288
2289 skip_if (0);
2290
2291 low_thresh = mean - factor2 * stdev;
2292 high_thresh = mean + factor2 * stdev;
2293
2294 bpm = cpl_mask_threshold_image_create(glitchy, low_thresh, high_thresh);
2295 skip_if (cpl_mask_not(bpm));
2296 skip_if (cpl_image_reject_from_mask(glitchy, bpm));
2297 cpl_mask_delete(bpm);
2298 bpm = NULL;
2299
2300 /* Test the number of bad pixels */
2301 skip_if (cpl_image_count_rejected(glitchy) == nx*ny);
2302
2303 /* Set the bad pixels to 0 */
2304 skip_if (cpl_image_fill_rejected(glitchy, 0.0));
2305 skip_if (cpl_image_accept_all(glitchy));
2306
2307 /* Reconstruct the corrected image */
2308 skip_if (cpl_image_add(glitchy, med_filt));
2309
2310 end_skip;
2311
2312 cpl_image_delete(med_filt);
2313 cpl_mask_delete(bpm);
2314 cpl_mask_delete(kernel);
2315
2316 return cpl_error_get_code();
2317}
2318
2319/*----------------------------------------------------------------------------*/
2330/*----------------------------------------------------------------------------*/
2331static cpl_error_code visir_rem_bad_images(cpl_imagelist * in)
2332{
2333 cpl_vector * medians = NULL;
2334 cpl_vector * stdevs = NULL;
2335 cpl_vector * selection = NULL;
2336 double mean_medians, mean_stdevs, stdev_medians, stdev_stdevs;
2337 const double threshold = 3;
2338 const int nima = cpl_imagelist_get_size(in);
2339 int i;
2340
2341 /* This will catch a NULL input */
2342 skip_if (0);
2343
2344 if (nima <= 3) return CPL_ERROR_NONE;
2345
2346 /* Create medians and stdevs arrays */
2347 medians = cpl_vector_new(nima);
2348 stdevs = cpl_vector_new(nima);
2349
2350 /* Compute the statistics */
2351 for (i=0 ; i < nima ; i++) {
2352 cpl_stats * stats = cpl_stats_new_from_image(cpl_imagelist_get(in, i),
2353 CPL_STATS_STDEV | CPL_STATS_MEDIAN);
2354
2355 cpl_vector_set(medians, i, cpl_stats_get_median(stats));
2356 cpl_vector_set(stdevs, i, cpl_stats_get_stdev(stats));
2357 cpl_stats_delete(stats); /* :-( */
2358 }
2359
2360 skip_if( 0);
2361
2362 /* Get the stats on these arrays */
2363 mean_medians = cpl_vector_get_mean(medians);
2364 stdev_medians = cpl_vector_get_stdev(medians);
2365 mean_stdevs = cpl_vector_get_mean(stdevs);
2366 stdev_stdevs = cpl_vector_get_stdev(stdevs);
2367
2368 skip_if (cpl_vector_subtract_scalar(medians, mean_medians));
2369 skip_if (cpl_vector_subtract_scalar(stdevs, mean_stdevs));
2370
2371 stdev_medians *= threshold;
2372 stdev_stdevs *= threshold;
2373
2374 /* Create the selection vector */
2375 selection = cpl_vector_new(nima);
2376 skip_if( cpl_vector_fill(selection, 0)); /* Flag all as good */
2377 for (i=0 ; i < nima ; i++) {
2378 if (fabs(cpl_vector_get(medians, i)) <= stdev_medians &&
2379 fabs(cpl_vector_get(stdevs, i)) <= stdev_stdevs) continue;
2380
2381 cpl_vector_set(selection, i, -1);
2382 cpl_msg_info(cpl_func, "Image %d of %d rejected: median=%g, stdev=%g",
2383 i+1, nima, stdev_medians, stdev_stdevs);
2384 }
2385
2386 /* Purge the bad images in the images set */
2387 cpl_imagelist_erase(in, selection);
2388
2389 end_skip;
2390
2391 cpl_vector_delete(medians);
2392 cpl_vector_delete(stdevs);
2393
2394 cpl_vector_delete(selection);
2395
2396 return CPL_ERROR_NONE;
2397}
2398
2399/*----------------------------------------------------------------------------*/
2408/*----------------------------------------------------------------------------*/
2409static cpl_error_code visir_offset_hcycle(cpl_image * hcycle)
2410{
2411 double minval;
2412
2413 skip_if (0);
2414
2415 skip_if (cpl_image_add_scalar(hcycle, VISIR_HCYCLE_OFFSET));
2416
2417 minval = cpl_image_get_min(hcycle);
2418
2419 /* It seems that the offset really is VISIR_HCYCLE_OFFSET-1, warn if not */
2420 if (minval < 1) cpl_msg_warning(cpl_func, "HCycle pixel minval: %g", minval);
2421
2422 end_skip;
2423
2424 return CPL_ERROR_NONE;
2425}
2426
2427/*----------------------------------------------------------------------------*/
2439/*----------------------------------------------------------------------------*/
2440static
2441cpl_image ** visir_img_collapse_beam_four(cpl_propertylist * qclist,
2442 const cpl_image * self,
2443 const cpl_image * inverse,
2444 double eccmax,
2445 double pthrow,
2446 double angle,
2447 const cpl_propertylist * plist)
2448{
2449
2450 cpl_image ** combined = NULL;
2451 const int nx = cpl_image_get_size_x(self);
2452 const int ny = cpl_image_get_size_y(self);
2453 const cpl_type type = cpl_image_get_type(self);
2454 cpl_imagelist * list4 = cpl_imagelist_new();
2455 IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2456 cpl_image * swrap = type == CPL_TYPE_DOUBLE
2457 ? cpl_image_wrap_double(nx, ny, cpl_image_get_data((cpl_image*)self))
2458 : cpl_image_wrap_float(nx, ny, cpl_image_get_data((cpl_image*)self));
2459 cpl_image * iwrap = type == CPL_TYPE_DOUBLE
2460 ? cpl_image_wrap_double(nx, ny, cpl_image_get_data((cpl_image*)inverse))
2461 : cpl_image_wrap_float(nx, ny, cpl_image_get_data((cpl_image*)inverse));
2462 IRPLIB_DIAG_PRAGMA_POP;
2463 cpl_bivector * offs = cpl_bivector_new(4);
2464 double * x4 = cpl_bivector_get_x_data(offs);
2465 double * y4 = cpl_bivector_get_y_data(offs);
2466 double pos_x, pos_y;
2467
2468 skip_if (0);
2469
2470 skip_if(plist == NULL);
2471
2472 skip_if(visir_img_find_beam_four(qclist, self, inverse, eccmax, pthrow,
2473 angle, x4, y4));
2474
2475 /* Combine the four beams */
2476 for (int i = 1; i < 4; i++) {
2477 x4[i] = x4[0] - x4[i];
2478 y4[i] = y4[0] - y4[i];
2479 }
2480
2481 bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM XPOS",
2482 x4[0]));
2483 bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM YPOS",
2484 y4[0]));
2485 x4[0] = y4[0] = 0.0;
2486
2487 IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2488 bug_if(cpl_imagelist_set(list4, (cpl_image*)self, 0));
2489 bug_if(cpl_imagelist_set(list4, swrap, 1));
2490 bug_if(cpl_imagelist_set(list4, (cpl_image*)inverse, 2));
2491 bug_if(cpl_imagelist_set(list4, iwrap, 3));
2492 IRPLIB_DIAG_PRAGMA_POP;
2493
2494 combined = cpl_geom_img_offset_saa(list4, offs, CPL_KERNEL_DEFAULT, 0, 0,
2495 CPL_GEOM_FIRST, &pos_x, &pos_y);
2496
2497 skip_if(combined == NULL);
2498
2499 end_skip;
2500
2501 cpl_bivector_delete(offs);
2502 visir_imagelist_unwrap(list4);
2503 (void)cpl_image_unwrap(swrap);
2504 (void)cpl_image_unwrap(iwrap);
2505 if (cpl_error_get_code() && combined != NULL) {
2506 cpl_image_delete(combined[0]);
2507 cpl_image_delete(combined[1]);
2508 cpl_free(combined);
2509 }
2510
2511 return combined;
2512}
2513
2514/*----------------------------------------------------------------------------*/
2528/*----------------------------------------------------------------------------*/
2529visir_chopnod_mode visir_img_find_beam(cpl_propertylist * qclist,
2530 const cpl_image * self,
2531 const cpl_image * inverse,
2532 const cpl_propertylist * plist,
2533 const cpl_parameterlist * parlist,
2534 const char * recipename,
2535 double x4[],
2536 double y4[])
2537{
2538
2539 cpl_errorstate cleanstate = cpl_errorstate_get();
2540 visir_chopnod_mode mode = VISIR_CHOPNOD_AUTO; /* Assume failure */
2541
2542 const double eccmax = visir_parameterlist_get_double(parlist, recipename,
2543 VISIR_PARAM_ECCMAX);
2544
2545 const char * sdir;
2546
2547 /* Get the chopping throw in pixels */
2548 const double pscale = visir_pfits_get_pixscale(plist);
2549 const double pthrow = pscale > 0.0
2550 ? visir_pfits_get_chop_throw(plist) / pscale : 0.0;
2551 double angle = visir_pfits_get_chop_posang(plist);
2552
2553 skip_if(x4 == NULL);
2554 skip_if(y4 == NULL);
2555 skip_if(self == NULL);
2556 skip_if(inverse == NULL);
2557 skip_if(parlist == NULL);
2558 skip_if(recipename == NULL);
2559 skip_if(qclist == NULL);
2560
2561 sdir = visir_pfits_get_chopnod_dir(plist);
2562
2563 if (sdir != NULL && !strcmp(sdir, "PERPENDICULAR")) {
2564
2565 /* 4 sources - expected and detected */
2566 mode = VISIR_CHOPNOD_PERPENDICULAR;
2567
2568 skip_if (visir_img_find_beam_four(qclist, self, inverse, eccmax,
2569 pthrow, angle, x4, y4));
2570
2571 } else if (sdir != NULL && !strcmp(sdir, "PARALLEL")) {
2572
2573 /* 3 sources - expected and detected */
2574 mode = VISIR_CHOPNOD_PARALLEL;
2575
2576 skip_if (visir_img_find_beam_three(qclist, self, inverse, eccmax,
2577 pthrow, angle, x4, y4));
2578
2579 } else {
2580 if (sdir == NULL) {
2581 visir_error_reset("Could not get FITS key");
2582 } else {
2583 cpl_msg_warning(cpl_func, "Unknown chopping direction: %s",
2584 sdir);
2585 }
2586 cpl_msg_warning(cpl_func, "Proceeding as if FITS card "
2587 VISIR_PFITS_STRING_CHOPNOD_DIR " had value: %s",
2588 "PERPENDICULAR");
2589
2590 if (visir_img_find_beam_four(qclist, self, inverse, eccmax,
2591 pthrow, angle, x4, y4)) {
2592
2593 visir_error_reset("Proceeding as if FITS card "
2594 VISIR_PFITS_STRING_CHOPNOD_DIR
2595 " had value: %s", "PARALLEL");
2596
2597 skip_if (visir_img_find_beam_three(qclist, self, inverse,
2598 eccmax, pthrow, angle,
2599 x4, y4));
2600
2601 /* 3 sources - unexpected, but detected */
2602 mode = VISIR_CHOPNOD_PARALLEL;
2603
2604 } else {
2605 /* 4 sources - unexpected, but detected */
2606 mode = VISIR_CHOPNOD_PERPENDICULAR;
2607 }
2608 }
2609
2610 bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM THROW",
2611 pthrow));
2612 bug_if (cpl_propertylist_set_comment(qclist, "ESO QC ONEBEAM THROW",
2613 "The throw in pixels (TEL CHOP THROW "
2614 "divided by INS PFOV)"));
2615 end_skip;
2616
2617 return mode;
2618}
2619
2620
2621/*----------------------------------------------------------------------------*/
2634/*----------------------------------------------------------------------------*/
2635static
2636cpl_error_code visir_img_find_beam_four(cpl_propertylist * qclist,
2637 const cpl_image * self,
2638 const cpl_image * inverse,
2639 double eccmax,
2640 double pthrow,
2641 double angle,
2642 double x4[],
2643 double y4[])
2644{
2645
2646 cpl_errorstate cleanstate = cpl_errorstate_get();
2647 cpl_apertures * appos = NULL;
2648 cpl_apertures * apneg = NULL;
2649 const double psigmas[] = {2.0, 1.0, 0.5};
2650 const int nsigmas = sizeof(psigmas)/sizeof(double);
2651 int isigma;
2652 int iappos2[] = {1, 2};
2653 int iapneg2[] = {1, 2};
2654
2655 bug_if(0);
2656 skip_if(self == NULL);
2657 skip_if(qclist == NULL);
2658 skip_if(pthrow <= 0.0);
2659 skip_if(x4 == NULL);
2660 skip_if(y4 == NULL);
2661
2662 cpl_msg_info(cpl_func, "Detecting the 4-beam object with %g pixel throw "
2663 "using %d sigma-levels ranging from %g down to %g", pthrow,
2664 nsigmas, psigmas[0], psigmas[nsigmas-1]);
2665
2666 bug_if(0);
2667 for (isigma = 0; isigma < nsigmas; isigma++) {
2668 int npos = 0;
2669 int nneg = 0;
2670
2671 /* Detect the (two) POSITIVE objects */
2672 cpl_apertures_delete(appos);
2673 appos = cpl_apertures_extract_sigma(self, psigmas[isigma]);
2674
2675 if (appos != NULL) {
2676 npos = cpl_apertures_get_size(appos);
2677 }
2678
2679
2680 /* Detect the (two) NEGATIVE objects */
2681 cpl_apertures_delete(apneg);
2682 apneg = cpl_apertures_extract_sigma(inverse, psigmas[isigma]);
2683 if (apneg != NULL) {
2684 nneg = cpl_apertures_get_size(apneg);
2685 }
2686
2687 cpl_msg_info(cpl_func, "Found %d positive (need 2) and %d negative "
2688 "(need 2) object(s) at sigma=%g (%d of %d)", npos, nneg,
2689 psigmas[isigma], 1+isigma, nsigmas);
2690
2691 /* stop if it would take forever to check the eccentricities */
2692 error_if(npos * nneg > FIND_BEAM_MAX_APERTURES_SQR,
2693 CPL_ERROR_DATA_NOT_FOUND, "Too many objects found, aborting");
2694
2695 if (eccmax > 0.0) {
2696 int ipos1;
2697 double eccbest = eccmax;
2698 double eccmin = DBL_MAX;
2699 double fluxbest = 0.0;
2700 double fluxecc = DBL_MAX;
2701 cpl_boolean is_first = CPL_TRUE;
2702
2703#ifdef _OPENMP
2704#pragma omp parallel for private(ipos1)
2705#endif
2706 for (ipos1 = 2; ipos1 < 1 + npos; ipos1++) {
2707 int ipos2, ineg1, ineg2;
2708 for (ipos2 = 1; ipos2 < ipos1; ipos2++) {
2709 for (ineg1 = 2; ineg1 < 1 + nneg; ineg1++) {
2710 for (ineg2 = 1; ineg2 < ineg1; ineg2++) {
2711 cpl_boolean swappos, swapneg;
2712 const double ecc
2713 = visir_img_check_box(appos, ipos1, ipos2,
2714 apneg, ineg1, ineg2,
2715 pthrow, angle, &swappos,
2716 &swapneg);
2717
2718 const double flux
2719 = cpl_apertures_get_flux(appos, ipos1)
2720 + cpl_apertures_get_flux(appos, ipos2)
2721 + cpl_apertures_get_flux(apneg, ineg1)
2722 + cpl_apertures_get_flux(apneg, ineg2);
2723
2724
2725 if (ecc < 0.0 || flux <= 0.0 ||
2726 !cpl_errorstate_is_equal(cleanstate)) {
2727 irplib_error_recover(cleanstate, "Invalid 4-"
2728 "object (%d & %d of %d, "
2729 "%d & %d of %d)",
2730 ipos2, ipos1, npos,
2731 ineg2, ineg1, nneg);
2732 continue;
2733 }
2734
2735#ifdef _OPENMP
2736#pragma omp critical(visir_img_find_beam_four_min)
2737#endif
2738 if (ecc < eccmin)
2739 {
2740 eccmin = ecc;
2741 fluxecc = flux;
2742 }
2743
2744 if (eccmax <= ecc) continue;
2745
2746#ifdef _OPENMP
2747#pragma omp critical(visir_img_find_beam_four_ok)
2748#endif
2749 if (is_first || ecc * fluxbest < eccbest * flux)
2750 {
2751 if (is_first) {
2752 is_first = CPL_FALSE;
2753 cpl_msg_info(cpl_func, "Found 4 object "
2754 "positions with throw-"
2755 "scaled eccentricity %g "
2756 "and flux %g", ecc, flux);
2757 } else {
2758 cpl_msg_info(cpl_func, "Found 4 object "
2759 "positions with throw-"
2760 "scaled eccentricity %g "
2761 "< %g and/or flux %g > %g",
2762 ecc, eccbest, flux, fluxbest);
2763 }
2764 eccbest = ecc;
2765 fluxbest = flux;
2766 iappos2[0] = swappos ? ipos2 : ipos1;
2767 iappos2[1] = swappos ? ipos1 : ipos2;
2768 iapneg2[0] = swapneg ? ineg2 : ineg1;
2769 iapneg2[1] = swapneg ? ineg1 : ineg2;
2770 }
2771 }
2772 }
2773 }
2774 }
2775 if (eccbest < eccmax) {
2776 bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM "
2777 "ECCENTRICITY", eccbest));
2778
2779 break;
2780 }
2781 if (eccmin < DBL_MAX) {
2782 cpl_msg_info(cpl_func, "Found 4 sigma-%g object positions with "
2783 "too large throw-scaled eccentricity %g >= %g and "
2784 "flux %g", psigmas[isigma], eccmin, eccmax,
2785 fluxecc);
2786 }
2787 } else if (npos >= 2 && nneg >= 2) {
2788 cpl_apertures_sort_by_flux(appos);
2789 cpl_apertures_sort_by_flux(apneg);
2790 break;
2791 }
2792
2793 if (isigma + 1 < nsigmas) {
2794 irplib_error_recover(cleanstate, "4-Beam positions not found among "
2795 "%d postive and %d negative object(s) at "
2796 "sigma=%g, (%d of %d)", npos, nneg,
2797 psigmas[isigma], 1+isigma, nsigmas);
2798 }
2799 }
2800
2801 error_if (isigma == nsigmas, CPL_ERROR_DATA_NOT_FOUND,
2802 "4-Beam positions not found w. %d sigma(s) down to %g",
2803 nsigmas, psigmas[nsigmas - 1]);
2804
2805 if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
2806 cpl_apertures_dump(appos, stdout);
2807 cpl_apertures_dump(apneg, stdout);
2808 }
2809
2810 x4[0] = cpl_apertures_get_centroid_x(appos, iappos2[0]);
2811 y4[0] = cpl_apertures_get_centroid_y(appos, iappos2[0]);
2812 x4[1] = cpl_apertures_get_centroid_x(appos, iappos2[1]);
2813 y4[1] = cpl_apertures_get_centroid_y(appos, iappos2[1]);
2814
2815 x4[2] = cpl_apertures_get_centroid_x(apneg, iapneg2[0]);
2816 y4[2] = cpl_apertures_get_centroid_y(apneg, iapneg2[0]);
2817 x4[3] = cpl_apertures_get_centroid_x(apneg, iapneg2[1]);
2818 y4[3] = cpl_apertures_get_centroid_y(apneg, iapneg2[1]);
2819
2820 bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM FLUX",
2821 cpl_apertures_get_flux(appos,
2822 iappos2[0])));
2823
2824 cpl_msg_info(cpl_func, "Centroid of positive object 1 [pixel]: %g %g",
2825 x4[0], y4[0]);
2826 cpl_msg_info(cpl_func, "Centroid of positive object 2 [pixel]: %g %g",
2827 x4[1], y4[1]);
2828
2829 cpl_msg_info(cpl_func, "Centroid of negative object 1 [pixel]: %g %g",
2830 x4[2], y4[2]);
2831 cpl_msg_info(cpl_func, "Centroid of negative object 2 [pixel]: %g %g",
2832 x4[3], y4[3]);
2833
2834 cpl_msg_info(cpl_func, "Expected object distance (chop throw) [pixel]: %g",
2835 pthrow);
2836 cpl_msg_info(cpl_func, "Object Pos -> Pos x/y-distance [pixel]: %g %g",
2837 x4[1] - x4[0], y4[1] - y4[0]);
2838 cpl_msg_info(cpl_func, "Object Neg -> Neg x/y-distance [pixel]: %g %g",
2839 x4[3] - x4[2], y4[3] - y4[2]);
2840 cpl_msg_info(cpl_func, "Object Pos -> Pos angle [degrees]: %g",
2841 atan2(y4[1] - y4[0], x4[1] - x4[0]) * CPL_MATH_DEG_RAD);
2842 cpl_msg_info(cpl_func, "Object Neg -> Neg angle [degrees]: %g",
2843 atan2(y4[3] - y4[2], x4[3] - x4[2]) * CPL_MATH_DEG_RAD);
2844
2845 end_skip;
2846
2847 cpl_apertures_delete(appos);
2848 cpl_apertures_delete(apneg);
2849
2850 return cpl_error_get_code();
2851}
2852
2853/*----------------------------------------------------------------------------*/
2865/*----------------------------------------------------------------------------*/
2866static
2867cpl_image ** visir_img_collapse_beam_three(cpl_propertylist * qclist,
2868 const cpl_image * self,
2869 const cpl_image * inverse,
2870 double eccmax,
2871 double pthrow,
2872 const cpl_propertylist * plist)
2873{
2874
2875 cpl_image ** combined = NULL;
2876 const int nx = cpl_image_get_size_x(self);
2877 const int ny = cpl_image_get_size_y(self);
2878 const cpl_type type = cpl_image_get_type(self);
2879 cpl_imagelist * list3 = cpl_imagelist_new();
2880 IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2881 cpl_image * iwrap = type == CPL_TYPE_DOUBLE
2882 ? cpl_image_wrap_double(nx, ny, cpl_image_get_data((cpl_image*)inverse))
2883 : cpl_image_wrap_float(nx, ny, cpl_image_get_data((cpl_image*)inverse));
2884 IRPLIB_DIAG_PRAGMA_POP;
2885 cpl_bivector * offs = cpl_bivector_new(3);
2886 double * x3 = cpl_bivector_get_x_data(offs);
2887 double * y3 = cpl_bivector_get_y_data(offs);
2888 double pos_x, pos_y;
2889 double angle = visir_pfits_get_chop_posang(plist);
2890
2891 skip_if (0);
2892
2893 skip_if(plist == NULL);
2894
2895 skip_if(visir_img_find_beam_three(qclist, self, inverse, eccmax, pthrow,
2896 angle, x3, y3));
2897
2898 /* Combine the three beams */
2899 for (int i = 1; i < 3; i++) {
2900 x3[i] = x3[0] - x3[i];
2901 y3[i] = y3[0] - y3[i];
2902 }
2903 bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM XPOS",
2904 x3[0]));
2905 bug_if (cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM YPOS",
2906 y3[0]));
2907
2908 x3[0] = y3[0] = 0.0;
2909
2910 IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2911 bug_if(cpl_imagelist_set(list3, (cpl_image*)self, 0));
2912 bug_if(cpl_imagelist_set(list3, (cpl_image*)inverse, 1));
2913 bug_if(cpl_imagelist_set(list3, iwrap, 2));
2914 IRPLIB_DIAG_PRAGMA_POP;
2915
2916 combined = cpl_geom_img_offset_saa(list3, offs, CPL_KERNEL_DEFAULT, 0, 0,
2917 CPL_GEOM_FIRST, &pos_x, &pos_y);
2918
2919 skip_if(combined == NULL);
2920
2921 end_skip;
2922
2923 cpl_bivector_delete(offs);
2924 visir_imagelist_unwrap(list3);
2925 (void)cpl_image_unwrap(iwrap);
2926 if (cpl_error_get_code() && combined != NULL) {
2927 cpl_image_delete(combined[0]);
2928 cpl_image_delete(combined[1]);
2929 cpl_free(combined);
2930 }
2931
2932 return combined;
2933}
2934
2935
2936/*----------------------------------------------------------------------------*/
2950/*----------------------------------------------------------------------------*/
2951static
2952cpl_error_code visir_img_find_beam_three(cpl_propertylist * qclist,
2953 const cpl_image * self,
2954 const cpl_image * inverse,
2955 double eccmax,
2956 double pthrow,
2957 double angle,
2958 double x3[],
2959 double y3[])
2960{
2961
2962 cpl_errorstate cleanstate = cpl_errorstate_get();
2963 cpl_apertures * appos = NULL;
2964 cpl_apertures * apneg = NULL;
2965 const double psigmas[] = {2.0, 1.0, 0.5};
2966 const int nsigmas = sizeof(psigmas)/sizeof(double);
2967 int isigma;
2968 int iappos [] = {1};
2969 int iapneg2[] = {1, 2};
2970
2971 bug_if(0);
2972 skip_if(self == NULL);
2973 skip_if(qclist == NULL);
2974 skip_if(pthrow <= 0.0);
2975 skip_if(eccmax < 0.0);
2976 skip_if(x3 == NULL);
2977 skip_if(y3 == NULL);
2978
2979
2980 cpl_msg_info(cpl_func, "Detecting the 3-beam object with %g pixel throw "
2981 "using %d sigma-levels ranging from %g down to %g", pthrow,
2982 nsigmas, psigmas[0], psigmas[nsigmas-1]);
2983
2984 bug_if(0);
2985 for (isigma = 0; isigma < nsigmas; isigma++) {
2986 int npos = 0;
2987 int nneg = 0;
2988
2989 /* Detect the (single) POSITIVE object */
2990 cpl_apertures_delete(appos);
2991 appos = cpl_apertures_extract_sigma(self, psigmas[isigma]);
2992
2993 if (appos != NULL) {
2994 npos = cpl_apertures_get_size(appos);
2995 }
2996
2997
2998 /* Detect the (two) NEGATIVE objects */
2999 cpl_apertures_delete(apneg);
3000 apneg = cpl_apertures_extract_sigma(inverse, psigmas[isigma]);
3001 if (apneg != NULL) {
3002 nneg = cpl_apertures_get_size(apneg);
3003 }
3004
3005 cpl_msg_info(cpl_func, "Found %d positive (need 1) and %d negative "
3006 "(need 2) object(s) at sigma=%g (%d of %d)", npos, nneg,
3007 psigmas[isigma], 1+isigma, nsigmas);
3008
3009 /* stop if it would take forever to check the eccentricities */
3010 error_if(npos * nneg > FIND_BEAM_MAX_APERTURES_SQR,
3011 CPL_ERROR_DATA_NOT_FOUND, "Too many objects found, aborting");
3012
3013 if (eccmax > 0.0) {
3014 int ipos;
3015 double eccbest = eccmax;
3016 double eccmin = DBL_MAX;
3017 double fluxbest = 0.0;
3018 double fluxecc = DBL_MAX;
3019 cpl_boolean is_first = CPL_TRUE;
3020
3021#ifdef _OPENMP
3022#pragma omp parallel for private(ipos)
3023#endif
3024 for (ipos = 1; ipos < 1 + npos; ipos++) {
3025 int ineg1, ineg2;
3026 for (ineg1 = 2; ineg1 < 1 + nneg; ineg1++) {
3027 for (ineg2 = 1; ineg2 < ineg1; ineg2++) {
3028 cpl_boolean swapneg;
3029 /* Find best ecc */
3030 const double ecc
3031 = visir_img_check_align(appos, ipos, apneg, ineg1,
3032 ineg2, pthrow, angle,
3033 &swapneg);
3034
3035 const double flux
3036 = cpl_apertures_get_flux(appos, ipos)
3037 + cpl_apertures_get_flux(apneg, ineg1)
3038 + cpl_apertures_get_flux(apneg, ineg2);
3039
3040
3041 if (ecc < 0.0 || flux <= 0.0 ||
3042 !cpl_errorstate_is_equal(cleanstate)) {
3043 irplib_error_recover(cleanstate, "Invalid 3-"
3044 "object (%d of %d, "
3045 "%d & %d of %d)",
3046 ipos, npos,
3047 ineg2, ineg1, nneg);
3048 continue;
3049 }
3050
3051#ifdef _OPENMP
3052#pragma omp critical(visir_img_collapse_beam_three_min)
3053#endif
3054 if (ecc < eccmin)
3055 {
3056 eccmin = ecc;
3057 fluxecc = flux;
3058 }
3059
3060 if (eccmax <= ecc) continue;
3061
3062#ifdef _OPENMP
3063#pragma omp critical(visir_img_collapse_beam_three_ok)
3064#endif
3065 if (is_first || ecc * fluxbest < eccbest * flux)
3066 {
3067 if (is_first) {
3068 is_first = CPL_FALSE;
3069 cpl_msg_info(cpl_func, "Found 3 object posi"
3070 "tions with throw-scaled "
3071 "eccentricity %g and flux %g",
3072 ecc, flux);
3073 } else {
3074 cpl_msg_info(cpl_func, "Found 3 object posi"
3075 "tions with throw-scaled "
3076 "eccentricity %g < %g and/or "
3077 "flux %g > %g", ecc, eccbest,
3078 flux, fluxbest);
3079 }
3080 eccbest = ecc;
3081 fluxbest = flux;
3082 iappos[0] = ipos;
3083 iapneg2[0] = swapneg ? ineg2 : ineg1;
3084 iapneg2[1] = swapneg ? ineg1 : ineg2;
3085 }
3086 }
3087 }
3088 }
3089 if (eccbest < eccmax) {
3090 bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM "
3091 "ECCENTRICITY", eccbest));
3092 break;
3093 }
3094 if (eccmin < DBL_MAX) {
3095 cpl_msg_info(cpl_func, "Found 3 sigma-%g object positions with "
3096 "too large throw-scaled eccentricity %g >= %g and "
3097 "flux %g", psigmas[isigma], eccmin, eccmax,
3098 fluxecc);
3099 }
3100 } else if (npos >= 1 && nneg >= 2) {
3101 cpl_apertures_sort_by_flux(appos);
3102 cpl_apertures_sort_by_flux(apneg);
3103 break;
3104 }
3105
3106 if (isigma + 1 < nsigmas) {
3107 irplib_error_recover(cleanstate, "3-Beam positions not found among "
3108 "%d postive and %d negative object(s) at "
3109 "sigma=%g, (%d of %d)", npos, nneg,
3110 psigmas[isigma], 1+isigma, nsigmas);
3111 }
3112 }
3113
3114 error_if (isigma == nsigmas, CPL_ERROR_DATA_NOT_FOUND,
3115 "3-Beam positions not found w. %d sigma(s) down to %g",
3116 nsigmas, psigmas[nsigmas - 1]);
3117
3118 if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
3119 cpl_apertures_dump(appos, stdout);
3120 cpl_apertures_dump(apneg, stdout);
3121 }
3122
3123 x3[0] = cpl_apertures_get_centroid_x(appos, iappos[0]);
3124 y3[0] = cpl_apertures_get_centroid_y(appos, iappos[0]);
3125
3126 x3[1] = cpl_apertures_get_centroid_x(apneg, iapneg2[0]);
3127 y3[1] = cpl_apertures_get_centroid_y(apneg, iapneg2[0]);
3128 x3[2] = cpl_apertures_get_centroid_x(apneg, iapneg2[1]);
3129 y3[2] = cpl_apertures_get_centroid_y(apneg, iapneg2[1]);
3130
3131 bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM FLUX",
3132 cpl_apertures_get_flux(appos,
3133 iappos[0])));
3134
3135 cpl_msg_info(cpl_func, "Centroid of positive object [pixel]: %g %g",
3136 x3[0], y3[0]);
3137
3138 cpl_msg_info(cpl_func, "Centroid of negative object 1 [pixel]: %g %g",
3139 x3[1], y3[1]);
3140 cpl_msg_info(cpl_func, "Centroid of negative object 2 [pixel]: %g %g",
3141 x3[2], y3[2]);
3142
3143 cpl_msg_info(cpl_func, "Expected object distance (chop throw) [pixel]: %g",
3144 pthrow);
3145 cpl_msg_info(cpl_func, "Object Neg1 -> Pos x/y-distance [pixel]: %g %g",
3146 x3[2] - x3[0], y3[2] - y3[0]);
3147 cpl_msg_info(cpl_func, "Object Pos -> Neg2 x/y-distance [pixel]: %g %g",
3148 x3[0] - x3[1], y3[0] - y3[1]);
3149
3150 end_skip;
3151
3152 cpl_apertures_delete(appos);
3153 cpl_apertures_delete(apneg);
3154
3155 return cpl_error_get_code();
3156}
3157
3158
3159/*----------------------------------------------------------------------------*/
3174/*----------------------------------------------------------------------------*/
3175cpl_error_code visir_img_find_beam_two(cpl_propertylist * qclist,
3176 const cpl_image * self,
3177 const cpl_image * inverse,
3178 double eccmax,
3179 double pthrow,
3180 double angle,
3181 double x2[],
3182 double y2[])
3183{
3184
3185 cpl_errorstate cleanstate = cpl_errorstate_get();
3186 cpl_apertures * appos = NULL;
3187 cpl_apertures * apneg = NULL;
3188 const double psigmas[] = {2.0, 1.0, 0.5};
3189 const int nsigmas = sizeof(psigmas)/sizeof(double);
3190 int isigma;
3191 int iappos[] = {1};
3192 int iapneg[] = {1};
3193
3194 bug_if(0);
3195 skip_if(self == NULL);
3196 skip_if(qclist == NULL);
3197 skip_if(eccmax < 0.0);
3198 skip_if(x2 == NULL);
3199 skip_if(y2 == NULL);
3200
3201 if (pthrow > 0.0) {
3202 cpl_msg_info(cpl_func, "Detecting the 2-beam object (Pos -> Neg) with "
3203 "%g pixel throw using %d sigma-levels ranging from %g down"
3204 " to %g", pthrow, nsigmas, psigmas[0], psigmas[nsigmas-1]);
3205 } else if (pthrow < 0.0) {
3206 cpl_msg_info(cpl_func, "Detecting the 2-beam object (Neg -> Pos) with "
3207 "%g pixel throw using %d sigma-levels ranging from %g down"
3208 " to %g", pthrow, nsigmas, psigmas[0], psigmas[nsigmas-1]);
3209 } else {
3210 skip_if(1);
3211 }
3212
3213 bug_if(0);
3214 for (isigma = 0; isigma < nsigmas; isigma++) {
3215 int npos = 0;
3216 int nneg = 0;
3217
3218 /* Detect the (single) POSITIVE object */
3219 cpl_apertures_delete(appos);
3220 appos = cpl_apertures_extract_sigma(self, psigmas[isigma]);
3221
3222 if (appos != NULL) {
3223 npos = cpl_apertures_get_size(appos);
3224 }
3225
3226
3227 /* Detect the NEGATIVE object */
3228 cpl_apertures_delete(apneg);
3229 apneg = cpl_apertures_extract_sigma(inverse, psigmas[isigma]);
3230 if (apneg != NULL) {
3231 nneg = cpl_apertures_get_size(apneg);
3232 }
3233
3234 cpl_msg_info(cpl_func, "Found %d positive (need 1) and %d negative "
3235 "(need 1) object(s) at sigma=%g (%d of %d)", npos, nneg,
3236 psigmas[isigma], 1+isigma, nsigmas);
3237
3238 if (eccmax > 0.0) {
3239 int ipos;
3240 double eccbest = eccmax;
3241 double eccmin = DBL_MAX;
3242 double fluxbest = 0.0;
3243 double fluxecc = DBL_MAX;
3244 cpl_boolean is_first = CPL_TRUE;
3245
3246#ifdef _OPENMP
3247#pragma omp parallel for private(ipos)
3248#endif
3249 for (ipos = 1; ipos < 1 + npos; ipos++) {
3250 int ineg;
3251 for (ineg = 1; ineg < 1 + nneg; ineg++) {
3252 const double ecc =
3253 visir_img_check_line(appos, ipos, apneg, ineg,
3254 pthrow, angle);
3255
3256 const double flux
3257 = cpl_apertures_get_flux(appos, ipos)
3258 + cpl_apertures_get_flux(apneg, ineg);
3259
3260
3261 if (ecc < 0.0 || flux <= 0.0 ||
3262 !cpl_errorstate_is_equal(cleanstate)) {
3263 irplib_error_recover(cleanstate, "Invalid 2-"
3264 "object (%d of %d, "
3265 "%d of %d)",
3266 ipos, npos,
3267 ineg, nneg);
3268 continue;
3269 }
3270
3271#ifdef _OPENMP
3272#pragma omp critical(visir_img_collapse_beam_two_min)
3273#endif
3274 if (ecc < eccmin)
3275 {
3276 eccmin = ecc;
3277 fluxecc = flux;
3278 }
3279
3280 if (eccmax <= ecc) continue;
3281
3282#ifdef _OPENMP
3283#pragma omp critical(visir_img_collapse_beam_two_ok)
3284#endif
3285 if (is_first || ecc * fluxbest < eccbest * flux)
3286 {
3287 if (is_first) {
3288 is_first = CPL_FALSE;
3289 cpl_msg_info(cpl_func, "Found 2 object posi"
3290 "tions with throw-scaled eccen"
3291 "tricity %g and flux %g", ecc,
3292 flux);
3293 } else {
3294 cpl_msg_info(cpl_func, "Found 2 object posi"
3295 "tions with throw-scaled eccen"
3296 "tricity %g < %g and/or flux %g "
3297 "> %g", ecc, eccbest, flux,
3298 fluxbest);
3299 }
3300 eccbest = ecc;
3301 fluxbest = flux;
3302 iappos[0] = ipos;
3303 iapneg[0] = ineg;
3304 }
3305 }
3306 }
3307 if (eccbest < eccmax) {
3308 bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM "
3309 "ECCENTRICITY", eccbest));
3310 break;
3311 }
3312 if (eccmin < DBL_MAX) {
3313 cpl_msg_info(cpl_func, "Found 2 sigma-%g object positions with "
3314 "too large throw-scaled eccentricity %g >= %g and "
3315 "flux %g", psigmas[isigma], eccmin, eccmax,
3316 fluxecc);
3317 }
3318 } else if (npos >= 1 && nneg >= 2) {
3319 cpl_apertures_sort_by_flux(appos);
3320 cpl_apertures_sort_by_flux(apneg);
3321 break;
3322 }
3323
3324 if (isigma + 1 < nsigmas) {
3325 irplib_error_recover(cleanstate, "2-Beam positions not found among "
3326 "%d postive and %d negative object(s) at "
3327 "sigma=%g, (%d of %d)", npos, nneg,
3328 psigmas[isigma], 1+isigma, nsigmas);
3329 }
3330 }
3331
3332 error_if (isigma == nsigmas, CPL_ERROR_DATA_NOT_FOUND,
3333 "2-Beam positions not found w. %d sigma(s) down to %g",
3334 nsigmas, psigmas[nsigmas - 1]);
3335
3336 if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
3337 cpl_apertures_dump(appos, stdout);
3338 cpl_apertures_dump(apneg, stdout);
3339 }
3340
3341 x2[0] = cpl_apertures_get_centroid_x(appos, iappos[0]);
3342 y2[0] = cpl_apertures_get_centroid_y(appos, iappos[0]);
3343
3344 x2[1] = cpl_apertures_get_centroid_x(apneg, iapneg[0]);
3345 y2[1] = cpl_apertures_get_centroid_y(apneg, iapneg[0]);
3346
3347 bug_if(cpl_propertylist_append_double(qclist, "ESO QC ONEBEAM FLUX",
3348 cpl_apertures_get_flux(appos,
3349 iappos[0])));
3350
3351 cpl_msg_info(cpl_func, "Centroid of positive object [pixel]: %g %g",
3352 x2[0], y2[0]);
3353
3354 cpl_msg_info(cpl_func, "Centroid of negative object [pixel]: %g %g",
3355 x2[1], y2[1]);
3356
3357 if (pthrow > 0.0) {
3358 cpl_msg_info(cpl_func, "Expected object distance (chop throw) "
3359 "[pixel]: %g", pthrow);
3360
3361 cpl_msg_info(cpl_func, "Object Pos -> Neg x/y-distance [pixel]: %g %g",
3362 x2[1] - x2[0], y2[1] - y2[0]);
3363 } else {
3364 cpl_msg_info(cpl_func, "Expected object distance (chop throw) "
3365 "[pixel]: %g", -pthrow);
3366
3367 cpl_msg_info(cpl_func, "Object Neg -> x/y-distance [pixel]: %g %g",
3368 x2[0] - x2[1], y2[0] - y2[1]);
3369 }
3370
3371 end_skip;
3372
3373 cpl_apertures_delete(appos);
3374 cpl_apertures_delete(apneg);
3375
3376 return cpl_error_get_code();
3377}
3378
3379
3380
3381/*----------------------------------------------------------------------------*/
3401/*----------------------------------------------------------------------------*/
3402double visir_img_check_box(const cpl_apertures * appos,
3403 int ipos1, int ipos2,
3404 const cpl_apertures * apneg,
3405 int ineg1, int ineg2, double ssize, double angle,
3406 cpl_boolean * pswapp, cpl_boolean * pswapn)
3407{
3408
3409 /* NB: Lower left pixel is (1, 1) */
3410
3411 /* The two positive points */
3412 double xp1 = cpl_apertures_get_centroid_x(appos, ipos1) * cos(angle) -
3413 cpl_apertures_get_centroid_y(appos, ipos1) * sin(angle);
3414 double yp1 = cpl_apertures_get_centroid_x(appos, ipos1) * sin(angle) +
3415 cpl_apertures_get_centroid_y(appos, ipos1) * cos(angle);
3416 double xp2 = cpl_apertures_get_centroid_x(appos, ipos2) * cos(angle) -
3417 cpl_apertures_get_centroid_y(appos, ipos2) * sin(angle);
3418 double yp2 = cpl_apertures_get_centroid_x(appos, ipos2) * sin(angle) +
3419 cpl_apertures_get_centroid_y(appos, ipos2) * cos(angle);
3420
3421 /* The leftmost positive point */
3422 const double xpl = xp1 < xp2 ? xp1 : xp2;
3423 const double ypl = xp1 < xp2 ? yp1 : yp2;
3424
3425 /* The rightmost positive point */
3426 const double xpr = xp1 < xp2 ? xp2 : xp1;
3427 const double ypr = xp1 < xp2 ? yp2 : yp1;
3428
3429 /* The two negative points */
3430 double xn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * cos(angle) -
3431 cpl_apertures_get_centroid_y(apneg, ineg1) * sin(angle);
3432 double yn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * sin(angle) +
3433 cpl_apertures_get_centroid_y(apneg, ineg1) * cos(angle);
3434 double xn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * cos(angle) -
3435 cpl_apertures_get_centroid_y(apneg, ineg2) * sin(angle);
3436 double yn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * sin(angle) +
3437 cpl_apertures_get_centroid_y(apneg, ineg2) * cos(angle);
3438
3439 /* The leftmost negative point */
3440 const double x_nl = xn1 < xn2 ? xn1 : xn2;
3441 const double y_nl = xn1 < xn2 ? yn1 : yn2;
3442
3443 /* The rightmost negative point */
3444 const double xnr = xn1 < xn2 ? xn2 : xn1;
3445 const double ynr = xn1 < xn2 ? yn2 : yn1;
3446
3447 const double lx1 = xnr - xpl; /* The length of the top x-side */
3448 const double lx2 = xpr - x_nl; /* The length of the bottom x-side */
3449 const double ly1 = ypl - y_nl; /* The length of the left y-side */
3450 const double ly2 = ynr - ypr; /* The length of the right y-side */
3451
3452 const double dx1 = lx1 - ssize;
3453 const double dx2 = lx2 - ssize;
3454 const double dy1 = ly1 - ssize;
3455 const double dy2 = ly2 - ssize;
3456
3457 const double ey1 = ynr - ypl; /* The displacement in the top x-side */
3458 const double ey2 = ypr - y_nl; /* The displacement in the bottom x-side */
3459 const double ex1 = xpl - x_nl; /* The displacement in the left y-side */
3460 const double ex2 = xpr - xnr; /* The displacement in the right y-side */
3461
3462 const double ok = sqrt(dx1 * dx1 + dx2 * dx2 + dy1 * dy1 + dy2 * dy2 +
3463 ex1 * ex1 + ex2 * ex2 + ey1 * ey1 + ey2 * ey2);
3464
3465 double result = -1.0; /* Assume failure */
3466
3467 skip_if(0); /* Catches NULL apertures and illegal index input */
3468
3469 skip_if(pswapp == NULL);
3470 skip_if(pswapn == NULL);
3471 skip_if(appos == apneg);
3472 skip_if(ipos1 == ipos2);
3473 skip_if(ineg1 == ineg2);
3474
3475 skip_if(ssize <= 0.0);
3476
3477 *pswapp = xp1 < xp2 ? CPL_FALSE : CPL_TRUE;
3478 *pswapn = xn1 < xn2 ? CPL_FALSE : CPL_TRUE;
3479
3480 result = ok/ssize; /* OK to divide now */
3481
3482 end_skip;
3483
3484 return result;
3485}
3486
3487
3488/*----------------------------------------------------------------------------*/
3507/*----------------------------------------------------------------------------*/
3508double visir_img_check_align(const cpl_apertures * appos, int ipos,
3509 const cpl_apertures * apneg, int ineg1, int ineg2,
3510 double ssize, double angle,
3511 cpl_boolean * pswapn)
3512{
3513
3514 /* NB: Lower left pixel is (1, 1) */
3515
3516 /* The positive point */
3517 double xp = cpl_apertures_get_centroid_x(appos, ipos) * cos(angle) -
3518 cpl_apertures_get_centroid_y(appos, ipos) * sin(angle);
3519 double yp = cpl_apertures_get_centroid_x(appos, ipos) * sin(angle) +
3520 cpl_apertures_get_centroid_y(appos, ipos) * cos(angle);
3521
3522 /* The two negative points */
3523 double xn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * cos(angle) -
3524 cpl_apertures_get_centroid_y(apneg, ineg1) * sin(angle);
3525 double yn1 = cpl_apertures_get_centroid_x(apneg, ineg1) * sin(angle) +
3526 cpl_apertures_get_centroid_y(apneg, ineg1) * cos(angle);
3527 double xn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * cos(angle) -
3528 cpl_apertures_get_centroid_y(apneg, ineg2) * sin(angle);
3529 double yn2 = cpl_apertures_get_centroid_x(apneg, ineg2) * sin(angle) +
3530 cpl_apertures_get_centroid_y(apneg, ineg2) * cos(angle);
3531
3532 double result = -1.0; /* Assume failure */
3533
3534 double ok;
3535
3536 cpl_boolean swapn;
3537
3538 /* The lower negative point */
3539 const double x_nl = yn1 < yn2 ? xn1 : xn2;
3540 const double y_nl = yn1 < yn2 ? yn1 : yn2;
3541
3542 /* The upper negative point */
3543 const double xnr = yn1 < yn2 ? xn2 : xn1;
3544 const double ynr = yn1 < yn2 ? yn2 : yn1;
3545
3546 const double d1 = ynr - yp - ssize; /* The upper length deviation */
3547 const double d2 = yp - y_nl - ssize; /* The lower length deviation */
3548
3549 const double e1 = xnr - xp; /* The upper orthogonal deviation */
3550 const double e2 = xp - x_nl; /* The lower orthogonal deviation */
3551
3552 swapn = yn1 < yn2 ? CPL_FALSE : CPL_TRUE;
3553
3554 ok = sqrt(d1 * d1 + d2 * d2 + e1 * e1 + e2 * e2);
3555
3556 skip_if(0); /* Catches NULL apertures and illegal index input */
3557
3558 skip_if(pswapn == NULL);
3559 skip_if(appos == apneg);
3560 skip_if(ineg1 == ineg2);
3561
3562 skip_if(ssize <= 0.0);
3563
3564 *pswapn = swapn;
3565
3566 result = ok/ssize; /* OK to divide now */
3567
3568 end_skip;
3569
3570 return result;
3571}
3572
3573
3574/*----------------------------------------------------------------------------*/
3591/*----------------------------------------------------------------------------*/
3592double visir_img_check_line(const cpl_apertures * apnear, int inear,
3593 const cpl_apertures * apfar, int ifar,
3594 double ssize, double angle)
3595{
3596
3597 /* NB: Lower left pixel is (1, 1) */
3598
3599 /* The near point */
3600 double x_n = cpl_apertures_get_centroid_x(apnear, inear) * cos(angle) -
3601 cpl_apertures_get_centroid_y(apnear, inear) * sin(angle);
3602 double y_n = cpl_apertures_get_centroid_x(apnear, inear) * sin(angle) +
3603 cpl_apertures_get_centroid_y(apnear, inear) * cos(angle);
3604
3605 /* The far point */
3606 double xf = cpl_apertures_get_centroid_x(apfar, ifar) * cos(angle) -
3607 cpl_apertures_get_centroid_y(apfar, ifar) * sin(angle);
3608 double yf = cpl_apertures_get_centroid_x(apfar, ifar) * sin(angle) +
3609 cpl_apertures_get_centroid_y(apfar, ifar) * cos(angle);
3610
3611 double result = -1.0; /* Assume failure */
3612
3613 double ok;
3614
3615 const double d = yf - y_n - ssize; /* The length deviation */
3616
3617 const double e = xf - x_n; /* The orthogonal deviation */
3618
3619 ok = sqrt(d * d + e * e);
3620
3621 skip_if(0); /* Catches NULL apertures and illegal index input */
3622
3623 skip_if(apnear == apfar);
3624
3625 skip_if(ssize <= 0.0);
3626
3627 result = ok/ssize; /* OK to divide now */
3628
3629 end_skip;
3630
3631 return result;
3632}
3633
3634
3635/*----------------------------------------------------------------------------*/
3646/*----------------------------------------------------------------------------*/
3647static cpl_error_code
3648get_aqu_data_type(const cpl_frame * frame, const cpl_propertylist * plist,
3649 const cpl_size next, visir_data_type * ptype)
3650{
3651 int naxis3 = -1;
3652 const char * format = NULL;
3653 if (cpl_propertylist_has(plist, VISIR_PFITS_INT_NAXIS3))
3654 naxis3 = visir_pfits_get_naxis3(plist);
3655
3656 if (cpl_propertylist_has(plist, "ESO DET FRAM FORMAT"))
3657 format = cpl_propertylist_get_string(plist, "ESO DET FRAM FORMAT");
3658
3659 /* HCCYLE data must have at least HCYCLE[12] and optional INT extensions */
3660 if (next >= 2 || (format && !strcmp(format, "extension"))) {
3661 /* if only one extension we only have an INT frame */
3662 if (next == 1) {
3663 *ptype = VISIR_DATA_AQU_INT;
3664 }
3665 else {
3666 *ptype = VISIR_DATA_AQU_HCYCLE;
3667 }
3668 }
3669 else if (next == 1 && (naxis3 == -1 || naxis3 == 0)) {
3670 *ptype = VISIR_DATA_AQU_BURST_EXT;
3671 }
3672 else if (next == 0 && naxis3 > 0) {
3673 *ptype = VISIR_DATA_AQU_BURST;
3674 }
3675 else {
3676 cpl_error_set_message(cpl_func, CPL_ERROR_BAD_FILE_FORMAT,
3677 "Could not determine format of aquarius file %s",
3678 cpl_frame_get_filename(frame));
3679 }
3680
3681 return cpl_error_get_code();
3682}
3683
3684
3685/*----------------------------------------------------------------------------*/
3696/*----------------------------------------------------------------------------*/
3697static cpl_error_code
3698get_drs_data_type(const cpl_frame * frame, const cpl_propertylist * plist,
3699 visir_data_type * ptype)
3700{
3701 cpl_errorstate cleanstate = cpl_errorstate_get();
3702 const char * sval = visir_pfits_get_frame_type(plist);
3703 const int naxis3 = visir_pfits_get_naxis3(plist);
3704 const int nchop = visir_pfits_get_chop_ncycles(plist);
3705 const int ndit = visir_pfits_get_ndit(plist);
3706 const char * file = cpl_frame_get_filename(frame);
3707 cpl_boolean known_frametype = CPL_TRUE;
3708 /* If nchop == 1 (and naxis3 == 3) the data unit is both a valid CUBE1
3709 and a valid CUBE2; Assume the frame type to be CUBE2 */
3710
3711 cpl_ensure_code(ptype != NULL, CPL_ERROR_NULL_INPUT);
3712
3713 if (sval == NULL) {
3714 /* Has warned about missing frame type card */
3715 visir_error_reset("Could not get FITS key");
3716 /* Don't know whether or not to expect CUBE1 or CUBE2 */
3717 known_frametype = CPL_FALSE;
3718 } else if (strcmp(sval, "CUBE1")==0) {
3719 *ptype = VISIR_DATA_CUBE1;
3720 } else if (strcmp(sval, "CUBE2")==0) {
3721 *ptype = VISIR_DATA_CUBE2;
3722 }
3723 else
3724 known_frametype = CPL_FALSE;
3725
3726 /* verification and guessing */
3727 if (known_frametype && *ptype == VISIR_DATA_CUBE2) {
3728 if (naxis3 == 2 * nchop + 1) {
3729 /* OK. FRAME TYPE cannot be verified from naxis3 when nchop = 1 */
3730 } else if (naxis3 == nchop + 2) {
3731 cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3732 "and CHOP NCYCLES=%d imply a CUBE1. Assuming "
3733 "the frame type is really CUBE1", file, sval,
3734 naxis3, nchop);
3735 *ptype = VISIR_DATA_CUBE1;
3736 } else {
3737 cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3738 "and CHOP NCYCLES=%d is not a valid VISIR INTERM+"
3739 "Half-Cycle format", file, sval, naxis3, nchop);
3740 skip_if(1);
3741 }
3742 } else if (known_frametype && *ptype == VISIR_DATA_CUBE1) {
3743 if (naxis3 == nchop + 2) {
3744 /* OK. FRAME TYPE cannot be verified from naxis3 when nchop = 1 */
3745 if (nchop > 1)
3746 cpl_msg_debug(cpl_func, "%s has %d INTERM-frames and one Half-"
3747 "Cycle frame (old CUBE1-format)", file, nchop);
3748
3749 } else if (naxis3 == 2 * nchop + 1) {
3750 cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3751 "and CHOP NCYCLES=%d imply a CUBE2. Assuming "
3752 "the frame type is really CUBE2", file, sval,
3753 naxis3, nchop);
3754 *ptype = VISIR_DATA_CUBE2;
3755 } else {
3756 cpl_msg_warning(cpl_func, "%s has FRAM TYPE = '%s', but NAXIS3=%d "
3757 "and CHOP NCYCLES=%d is not a valid VISIR INTERM+"
3758 "Half-Cycle format", file, sval, naxis3, nchop);
3759 skip_if(1);
3760 }
3761 } else if (naxis3 == 2 * nchop + 1) {
3762 cpl_msg_warning(cpl_func, "%s has FRAM TYPE='%s', but NAXIS3=%d and "
3763 "CHOP NCYCLES=%d imply a CUBE2. Assuming the frame "
3764 "type is CUBE2", file, sval ? sval : "<NULL>", naxis3,
3765 nchop);
3766 *ptype = VISIR_DATA_CUBE2;
3767 } else if (naxis3 == nchop + 2) {
3768 cpl_msg_warning(cpl_func, "%s has FRAM TYPE='%s', but NAXIS3=%d and "
3769 "CHOP NCYCLES=%d imply a CUBE1. Assuming the frame "
3770 "type is CUBE1", file, sval ? sval : "<NULL>", naxis3,
3771 nchop);
3772 *ptype = VISIR_DATA_CUBE1;
3773 }
3774 else if (!known_frametype && nchop * ndit * 2 >= naxis3) {
3775 cpl_msg_info(cpl_func, "%s has FRAM TYPE='%s', NAXIS3=%d and "
3776 "CHOP NCYCLES=%d imply BURST data.",
3777 file, sval ? sval : "<NULL>", naxis3, nchop);
3778 *ptype = VISIR_DATA_BURST;
3779 } else {
3780 return cpl_error_set_message(cpl_func, CPL_ERROR_BAD_FILE_FORMAT,
3781 "%s has FRAM TYPE='%s', NAXIS3 = %d and "
3782 "CHOP NCYCLES = %d", file,
3783 sval ? sval : "<NULL>", naxis3, nchop);
3784 }
3785
3786 end_skip;
3787
3788 return cpl_error_get_code();
3789}
3790
3791
3792/*----------------------------------------------------------------------------*/
3802/*----------------------------------------------------------------------------*/
3803cpl_error_code visir_get_data_type(const cpl_frame * frame,
3804 const cpl_propertylist * plist,
3805 visir_data_type * ptype, cpl_size * pnext)
3806{
3807 const cpl_size next = cpl_frame_get_nextensions(frame);
3808
3809 cpl_ensure_code(ptype != NULL, CPL_ERROR_NULL_INPUT);
3810
3811 skip_if(0);
3812
3813 if (pnext)
3814 *pnext = next;
3815
3816 if (cpl_propertylist_has(plist, "ESO DRS DTYPE"))
3817 *ptype = cpl_propertylist_get_int(plist, "ESO DRS DTYPE");
3818 else {
3819 if (cpl_propertylist_has(plist, VISIR_PFITS_INT_NAVRG))
3820 skip_if(get_aqu_data_type(frame, plist, next, ptype));
3821 else
3822 skip_if(get_drs_data_type(frame, plist, ptype));
3823 }
3824
3825 end_skip;
3826
3827 return cpl_error_get_code();
3828}
3829
3830
3831/*----------------------------------------------------------------------------*/
3839/*----------------------------------------------------------------------------*/
3840cpl_error_code visir_img_burst_find_delta_chop(const cpl_propertylist * self,
3841 int * ichopchange, int * ihalfcycle)
3842{
3843
3844 const char * sdateobs =
3845 cpl_propertylist_get_string(self, VISIR_PFITS_STRING_OBS_START);
3846 const char * schopstart =
3847 cpl_propertylist_get_string(self, VISIR_PFITS_STRING_CHOP_START);
3848 const double chop_freq = visir_pfits_get_chop_freq(self);
3849 const int nditskip =
3850 cpl_propertylist_get_int(self, VISIR_PFITS_INT_NDITSKIP);
3851 const double dit = visir_pfits_get_dit(self) *
3853 double ddateobs, dchopstart;
3854 double period; /* Number of A+B frames in one full chopping cycle */
3855
3856 skip_if(0);
3857
3858 bug_if(irplib_wcs_mjd_from_string(&ddateobs, sdateobs));
3859 bug_if(irplib_wcs_mjd_from_string(&dchopstart, schopstart));
3860
3861 skip_if(chop_freq <= 0.0);
3862 skip_if(dit <= 0.0);
3863
3864 /* adapt for skipped exposures after obs start */
3865 /* TODO seems to work better without in aqu */
3866 if (!cpl_propertylist_has(self, VISIR_PFITS_INT_NAVRG)) {
3867 ddateobs += dit * nditskip / (double)VISIR_SECS_PER_DAY;
3868 }
3869
3870 period = 1.0/(chop_freq * dit);
3871
3872 /* FIXME: handle this case */
3873 error_if((int)(period + 0.5) % 2 != 0, CPL_ERROR_UNSUPPORTED_MODE,
3874 "Period %g not not an even number, chop frequency %g, dit %g",
3875 period, chop_freq, dit);
3876
3877 error_if((int)(period + 0.5) <= 1, CPL_ERROR_ILLEGAL_INPUT,
3878 "Period %d < 1", (int)(period + 0.5));
3879
3880 *ihalfcycle = (int)(period + 0.5)/2;
3881
3882 cpl_msg_info(cpl_func, "Number of A+B frames in one full chopping cycle: %g",
3883 period);
3884
3885 if (dchopstart < ddateobs) {
3886 double tchop = (ddateobs - dchopstart) * (double)VISIR_SECS_PER_DAY;
3887 /* Number of chopping cycles before obs+chopper start */
3888 double dprecycle = tchop * chop_freq;
3889 /* Phase to skip to point to first A frame
3890 in first complete chopping cycle */
3891 const double phase = ceil(dprecycle) - dprecycle;
3892
3893 /* First valid frame is the frame at ddateobs (except for startindex) */
3894 *ichopchange = (int)ceil(phase * period) - 1;
3895
3896 cpl_msg_info(cpl_func, "Chopping started %gs (%f cycles) before OBS start: "
3897 "%f < %f", tchop, dprecycle, dchopstart, ddateobs);
3898
3899 } else if (ddateobs < dchopstart) {
3900 /* FIXME: Allowed ? */
3901 /* First valid frame is the frame at dchopstart (except for startindex) */
3902 double tchop = (dchopstart - ddateobs) * (double)VISIR_SECS_PER_DAY;
3903 *ichopchange = (int)ceil(tchop / dit) - 1;
3904 cpl_msg_info(cpl_func, "Chopping started %gs (wasted %g cycles) after OBS "
3905 "start: %f > %f", tchop, tchop * chop_freq, dchopstart,
3906 ddateobs);
3907 } else {
3908 /* FIXME: Allowed ? */
3909 /* First valid frame is the first frame (at both ddateobs and dchopstart)
3910 (except for startindex) */
3911 *ichopchange = 0;
3912 cpl_msg_info(cpl_func, "Chopping started with OBS start: %f == %f",
3913 dchopstart, ddateobs);
3914 }
3915
3916 /* wrap value to cycle */
3917 *ichopchange = *ichopchange % (*ihalfcycle * 2);
3918
3919 cpl_msg_info(cpl_func, "Frame of chop change: %d", *ichopchange);
3920
3921 end_skip;
3922
3923 return cpl_error_get_code();
3924}
3925
3926#include "visir_destripe.c"
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.
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.
cpl_imagelist * visir_load_hcycle(const irplib_framelist *rawframes, int pos)
Load the HCYCLE images from a VISIR file.
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:613
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:242
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:795
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.
visir_apdefs * visir_apdefs_new_from_line(char *line, int ident)
Parse a line from the user-supplied apertures definition file, creating a new visir_apdefs object.
char * visir_apdefs_dump(const visir_apdefs *ap)
Convert an aperture definition into its string representation.
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.
visir_aplist * visir_aplist_new_from_file(const char *apfile)
Load the user-supplied apertures definition file.
cpl_bivector * visir_load_lintable(cpl_frame *linframe, cpl_boolean is_spec)
load and normalize linearity table into a bivector
Definition: visir_inputs.c:536
cpl_error_code visir_image_reject_hot(cpl_image *self, const char *bpmfile)
Reject the hot pixels in an image.
int visir_parameterlist_get_int(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR integer parameter.
const char * visir_parameterlist_get_string(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR string parameter.
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_boolean visir_parameterlist_get_bool(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR boolean parameter.
int visir_pfits_get_navrg(const cpl_propertylist *self)
The NAVRG.
Definition: visir_pfits.c:339
double visir_pfits_get_dit(const cpl_propertylist *self)
The DIT.
Definition: visir_pfits.c:325
int visir_pfits_get_chop_ncycles(const cpl_propertylist *self)
The number of chopping cycles.
Definition: visir_pfits.c:237
double visir_pfits_get_pixscale(const cpl_propertylist *self)
The pixel scale.
Definition: visir_pfits.c:711
double visir_pfits_get_chop_posang(const cpl_propertylist *self)
The chopping position angle in rad.
Definition: visir_pfits.c:205
double visir_pfits_get_cumoffsety(const cpl_propertylist *self)
The cumulative offset in Y.
Definition: visir_pfits.c:277
const char * visir_pfits_get_chopnod_dir(const cpl_propertylist *self)
The chopping direction.
Definition: visir_pfits.c:139
int visir_pfits_get_ndit(const cpl_propertylist *self)
The NDIT keyword.
Definition: visir_pfits.c:535
int visir_pfits_get_naxis3(const cpl_propertylist *self)
The NAXIS3 key.
Definition: visir_pfits.c:576
double visir_pfits_get_chop_throw(const cpl_propertylist *self)
The chopping throw.
Definition: visir_pfits.c:152
double visir_pfits_get_chop_freq(const cpl_propertylist *self)
The chopping frequency.
Definition: visir_pfits.c:170
const char * visir_pfits_get_frame_type(const cpl_propertylist *self)
The frame type.
Definition: visir_pfits.c:434
double visir_pfits_get_cumoffsetx(const cpl_propertylist *self)
The cumulative offset in X.
Definition: visir_pfits.c:265
const char * visir_pfits_get_nodpos(const cpl_propertylist *self)
The nodding position.
Definition: visir_pfits.c:699