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