VISIR Pipeline Reference Manual  4.1.7
visir_img_phot.c
1 /*
2  * This file is part of the VISIR Pipeline
3  * Copyright (C) 2002,2003,2013,2014 European Southern Observatory
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 
25 /*-----------------------------------------------------------------------------
26  Includes
27  -----------------------------------------------------------------------------*/
28 
29 #include "visir_recipe.h"
30 #include "visir_pfits.h"
31 #include "irplib_strehl.h"
32 #include "irplib_framelist.h"
33 
34 #include <string.h>
35 #include <float.h>
36 
37 #define SQR(a) ((a) * (a))
38 
39 /*-----------------------------------------------------------------------------
40  Define
41  -----------------------------------------------------------------------------*/
42 
43 #define VISIR_IMG_PHOT_POS_WARN 10.0
44 #define VISIR_IMG_PHOT_POS_ERROR 25.0
45 #define VISIR_IMG_PHOT_POS_UNCERTAINTY 10.0
46 #define STREHL_M1 8.0
47 #define STREHL_M2 1.1
48 #define STREHL_BOX_SIZE 64
49 #define STREHL_STAR_RADIUS 2.0
50 #define STREHL_BG_R1 2.0
51 #define STREHL_BG_R2 3.0
52 
53 #define RECIPE_STRING "visir_old_img_phot"
54 /* still used by chain recipe internally, so keep name for output */
55 #define RECIPE_SAVE_STRING "visir_img_phot"
56 
57 
58 /* FITS keys to be loaded for all raw files */
59 #define RECIPE_KEYS_REGEXP_ALL \
60  VISIR_PFITS_REGEXP_IMG_RECOMBINE \
61  "|" VISIR_PFITS_REGEXP_IMG_SENSIT
62 
63 /* FITS keys to be loaded for first raw file */
64 #define RECIPE_KEYS_REGEXP \
65  RECIPE_KEYS_REGEXP_ALL \
66  "|" VISIR_PFITS_IMG_PHOT_COPY \
67  "|" VISIR_PFITS_REGEXP_IMG_PHOT_PAF
68 
69 /* FITS keys to be loaded for first raw file, in case WCS is used */
70 #define RECIPE_KEYS_REGEXP_WCS \
71  RECIPE_KEYS_REGEXP \
72  "|" IRPLIB_PFITS_WCS_REGEXP
73 
74 
75 /*-----------------------------------------------------------------------------
76  Private Functions prototypes
77  -----------------------------------------------------------------------------*/
78 
79 typedef struct beamshape_ {
80  /* simple fwhm in x and y from cpl, very inaccurate */
81  double fwhm_x, fwhm_y;
82  /* fwhm from gauss fit */
83  double peak, peak_err;
84  double major, major_err;
85  double minor, minor_err;
86  double angle, angle_err;
87 } beamshape_t;
88 
89 
90 static double visir_img_phot_eccent_four(const cpl_apertures *, int, int,
91  const cpl_apertures *, int, int);
92 
93 static int visir_img_phot_flux_one(const cpl_image *, const cpl_image *,
94  const cpl_image *, const cpl_propertylist *);
95 #ifdef VISIR_IMG_PHOT_USE_ECCENT_THREE
96 static double visir_img_phot_eccent_three(const cpl_apertures *, int,
97  const cpl_apertures *, int, int);
98 #endif
99 
100 static double visir_img_phot_jy_from_cat(const char *, const char *, double,
101  double, const char *);
102 static int visir_img_phot_sensit(const cpl_image *, const cpl_image *,
103  const cpl_image *, const irplib_framelist *,
104  const char *, cpl_boolean, cpl_propertylist *);
105 static int visir_img_phot_flux_three(const cpl_image *);
106 static int visir_img_phot_flux_four(const cpl_image *);
107 static int visir_img_phot_flux(const cpl_image *, const cpl_image *,
108  const cpl_image *, double,
109  double, int, int, int, double *,
110  double *, int *, double *, beamshape_t *);
111 
112 static cpl_error_code visir_get_filter_infos(const char *, double *, double *);
113 
114 static cpl_error_code visir_img_phot_qc(cpl_propertylist *,
115  cpl_boolean,
116  const irplib_framelist *);
117 
118 static cpl_error_code visir_img_phot_save(cpl_frameset *,
119  const cpl_parameterlist *,
120  const cpl_propertylist *,
121  const cpl_image *,
122  const cpl_image *,
123  const cpl_image *,
124  const cpl_image *,
125  const cpl_image *);
126 
127 static char * visir_img_phot_filter2label(const char *);
128 
129 #ifdef VISIR_CHAIN
130 #define cpl_plugin_get_info visir_old_img_phot_get_info
131 #endif
132 VISIR_RECIPE_DEFINE(visir_old_img_phot,
133  VISIR_PARAM_RADII | VISIR_PARAM_JYVAL |
134  VISIR_PARAM_REFINE | VISIR_PARAM_XCORR |
135  VISIR_PARAM_OFFSETS | VISIR_PARAM_OBJECTS |
136  VISIR_PARAM_NODPOS | VISIR_PARAM_AUTOBPM |
137  VISIR_PARAM_GLITCH | VISIR_PARAM_PURGE |
138  VISIR_PARAM_UNION | VISIR_PARAM_REJECT |
139  VISIR_PARAM_COMBINE | VISIR_PARAM_ECCMAX |
140  VISIR_PARAM_STRIPITE | VISIR_PARAM_STRIPMOR |
141  VISIR_PARAM_PLOT,
142  "Old DRS detector: Sensitivity computation",
143  "This recipe computes the conversion factor and the "
144  "sentitivity of a\n"
145  "standard star. It requires a standard star catalog.\n"
146  "The files listed in the Set Of Frames (sof-file) "
147  "must be tagged:\n"
148  "VISIR-std-star-observation.fits " VISIR_IMG_PHOT_RAW
149  "\n"
150  "VISIR-Imaging-Standard-Star-Catalog.fits "
151  VISIR_IMA_STD_CAT_PROCATG "\n"
152  "\n"
153  "The primary product will have a FITS card\n"
154  "'HIERARCH " CPL_DFS_PRO_CATG "' with a string value of\n"
155  "'" VISIR_IMG_PHOT_COMBINED_PROCATG "'\n"
156  " and the corresponding beam-collapsed product will "
157  "have a FITS card\n"
158  "'HIERARCH " CPL_DFS_PRO_CATG "' with a value of\n"
159  VISIR_IMG_PHOT_ONEBEAM_PROCATG
160  "\n"
161  MAN_VISIR_CALIB_BPM_IMG);
162 
163 /*-----------------------------------------------------------------------------
164  Static variables
165  -----------------------------------------------------------------------------*/
166 
167 static const int fits_strlen = IRPLIB_FITS_STRLEN;
168 
169 static struct {
170  /* Inputs */
171  double jy_val;
172  int r0_max;
173  int r1;
174  int r2;
175 
176  /* Outputs */
177  double exptime;
178  double pscale;
179  char star_name[IRPLIB_FITS_STRLEN+1];
180  char filter[IRPLIB_FITS_STRLEN+1];
181  double bg_sigma;
182  double flux_snr;
183  double flux_snr_noise;
184  int flux_snr_radius[4];
185  double flux_tot;
186  double nelec;
187  double nphot;
188  beamshape_t beamshape;
189  double fwhm_x_pos1;
190  double fwhm_y_pos1;
191  double fwhm_x_pos2;
192  double fwhm_y_pos2;
193  double fwhm_x_neg1;
194  double fwhm_y_neg1;
195  double fwhm_x_neg2;
196  double fwhm_y_neg2;
197  double sensitivity;
198  double area_sensit;
199  double conversion;
200  double strehl;
201  double strehl_err;
202 } visir_img_phot_config;
203 
204 
205 /*----------------------------------------------------------------------------*/
209 /*----------------------------------------------------------------------------*/
210 
211 /*-----------------------------------------------------------------------------
212  Functions code
213  -----------------------------------------------------------------------------*/
214 
215 
216 static cpl_error_code
217 propagate_qc_header(cpl_propertylist * qclist, const cpl_propertylist * ppqc)
218 {
219  double d;
220  int i;
221  const char * s;
222 
223  s = cpl_propertylist_get_string(ppqc, "BUNIT");
224  cpl_propertylist_append_string(qclist, "BUNIT", s);
225 
226  s = cpl_propertylist_get_string(ppqc, "ESO DRS CATG");
227  cpl_propertylist_append_string(qclist, "ESO DRS CATG", s);
228 
229  d = cpl_propertylist_get_double(ppqc, "ESO QC BACKGD MEAN");
230  cpl_propertylist_append_double(qclist, "ESO QC BACKGD MEAN", d);
231 
232  d = cpl_propertylist_get_double(ppqc, "ESO QC EXPTIME EFFECTIVE");
233  cpl_propertylist_append_double(qclist, "ESO QC EXPTIME EFFECTIVE", d);
234 
235  if (cpl_propertylist_has(ppqc, "ESO QC EXECTIME")) {
236  d = cpl_propertylist_get_double(ppqc, "ESO QC EXECTIME");
237  cpl_propertylist_append_double(qclist, "ESO QC EXECTIME", d);
238  }
239 
240  i = cpl_propertylist_get_int(ppqc, "ESO QC NIMAGES TOTAL");
241  cpl_propertylist_append_int(qclist, "ESO QC NIMAGES TOTAL", i);
242 
243  i = cpl_propertylist_get_int(ppqc, "ESO QC NIMAGES EFFECTIVE");
244  cpl_propertylist_append_int(qclist, "ESO QC NIMAGES EFFECTIVE", i);
245 
246  s = cpl_propertylist_get_string(ppqc, "ESO QC BEAMID");
247  cpl_propertylist_append_string(qclist, "ESO QC BEAMID", s);
248 
249  error_if(strcmp(s, "UNDEFINED") == 0, CPL_ERROR_ILLEGAL_INPUT,
250  "Object detection was not successful, "
251  "please provide visir_util_detect_position with the object "
252  "positions to allow photometry");
253  if (strcmp(s, "COMBINED") != 0) {
254  d = cpl_propertylist_get_double(ppqc, "ESO QC BEAMX");
255  cpl_propertylist_append_double(qclist, "ESO QC BEAMX", d);
256 
257  d = cpl_propertylist_get_double(ppqc, "ESO QC BEAMY");
258  cpl_propertylist_append_double(qclist, "ESO QC BEAMY", d);
259  }
260 
261  end_skip;
262 
263  return cpl_error_get_code();
264 }
265 
266 
267 /*----------------------------------------------------------------------------*/
274 /*----------------------------------------------------------------------------*/
275 static int visir_old_img_phot(cpl_frameset * framelist,
276  const cpl_parameterlist * parlist)
277 {
278  cpl_errorstate cleanstate = cpl_errorstate_get();
279  irplib_framelist * allframes = NULL;
280  irplib_framelist * rawframes = NULL;
281  cpl_propertylist * ppqc = NULL;
282  cpl_propertylist * qclist = NULL;
283  const char * badpix;
284  const char * star_cat;
285  const char * flat;
286  cpl_image * weights = NULL;
287  cpl_image * contrib = NULL;
288  cpl_image ** combined = NULL;
289  cpl_image ** beam1 = NULL;
290  const char * sval;
291  cpl_boolean drop_wcs = CPL_FALSE;
292  const char * keys_regexp = "^(" RECIPE_KEYS_REGEXP_WCS
293  "|"VISIR_PFITS_REGEXP_DIT
294  "|ESO OBS.*)$";
295  const char * combine_string;
296  cpl_geom_combine combine_mode;
297  cpl_boolean one_beam = CPL_FALSE;
298  const char * dit_key = VISIR_PFITS_DOUBLE_DIT;
299 
300 
301 
302  visir_img_phot_config.exptime = -1.0;
303  visir_img_phot_config.pscale = -1.0;
304  visir_img_phot_config.star_name[0] = (char)0;
305  visir_img_phot_config.filter[0] = (char)0;
306  visir_img_phot_config.bg_sigma = -1.0;
307  visir_img_phot_config.flux_snr = -1.0;
308  visir_img_phot_config.flux_snr_noise = -1.0;
309  visir_img_phot_config.flux_snr_radius[0] = -1;
310  visir_img_phot_config.flux_snr_radius[1] = -1;
311  visir_img_phot_config.flux_snr_radius[2] = -1;
312  visir_img_phot_config.flux_snr_radius[3] = -1;
313  visir_img_phot_config.flux_tot = -1.0;
314  visir_img_phot_config.nelec = -1.0;
315  visir_img_phot_config.nphot = -1.0;
316  visir_img_phot_config.beamshape =
317  (beamshape_t){.fwhm_x = -1., .fwhm_y = -1.,
318  .peak = -1., .peak_err = 0.,
319  .major = -1., .major_err = 0.,
320  .minor = -1., .minor_err = 0.,
321  .angle = -1., .angle_err = 0.};
322  visir_img_phot_config.fwhm_x_pos1 = -1.0;
323  visir_img_phot_config.fwhm_y_pos1 = -1.0;
324  visir_img_phot_config.fwhm_x_pos2 = -1.0;
325  visir_img_phot_config.fwhm_y_pos2 = -1.0;
326  visir_img_phot_config.fwhm_x_neg1 = -1.0;
327  visir_img_phot_config.fwhm_y_neg1 = -1.0;
328  visir_img_phot_config.fwhm_x_neg2 = -1.0;
329  visir_img_phot_config.fwhm_y_neg2 = -1.0;
330  visir_img_phot_config.sensitivity = -1.0;
331  visir_img_phot_config.area_sensit = -1.0;
332  visir_img_phot_config.conversion = -1.0;
333  visir_img_phot_config.strehl = -1.0;
334  visir_img_phot_config.strehl_err = -1.0;
335 
336  /* Retrieve input parameters */
337  combine_string = visir_parameterlist_get_string(parlist, RECIPE_STRING,
338  VISIR_PARAM_COMBINE);
339 
340  bug_if (combine_string == NULL);
341 
342  if (combine_string[0] == 'u')
343  combine_mode = CPL_GEOM_UNION;
344  else if (combine_string[0] == 'f')
345  combine_mode = CPL_GEOM_FIRST;
346  else if (combine_string[0] == 'i')
347  combine_mode = CPL_GEOM_INTERSECT;
348  else
349  skip_if(1);
350 
351 
352  visir_img_phot_config.jy_val =
353  visir_parameterlist_get_double(parlist, RECIPE_STRING, VISIR_PARAM_JYVAL);
354  skip_if (0);
355 
356  sval = visir_parameterlist_get_string(parlist, RECIPE_STRING, VISIR_PARAM_RADII);
357  skip_if (sval == NULL);
358 
359  skip_if (sscanf(sval, "%d%*c%d%*c%d",
360  &visir_img_phot_config.r0_max,
361  &visir_img_phot_config.r1,
362  &visir_img_phot_config.r2) != 3);
363 
364  /* Identify the RAW and CALIB frames in the input frameset */
365  skip_if (visir_dfs_set_groups(framelist));
366 
367  /* Objects observation */
368  allframes = irplib_framelist_cast(framelist);
369  skip_if(allframes == NULL);
370  {
371  rawframes = irplib_framelist_extract(allframes, VISIR_IMG_PHOT_RAW);
372  if (rawframes == NULL) {
373  cpl_errorstate_set(cleanstate);
374  rawframes = irplib_framelist_extract(allframes,
375  "IM_CAL_PHOT_PREPROCESSED");
376  skip_if (rawframes == NULL);
377  const char * fn =
378  irplib_frameset_find_file(framelist, VISIR_UTIL_QC_PROCATG);
379  if(fn == NULL) {
380  cpl_msg_error(cpl_func, "The input files has no header tagged "
381  "%s", VISIR_UTIL_QC_PROCATG);
382  visir_error_set(CPL_ERROR_DATA_NOT_FOUND);
383  skip_if(1);
384  }
385  ppqc = cpl_propertylist_load(fn, 0);
386  one_beam = CPL_TRUE;
387  }
388  }
389 
390  skip_if (rawframes == NULL);
391 
392  irplib_framelist_empty(allframes);
393 
394  skip_if(irplib_framelist_load_propertylist(rawframes, 0, 0, keys_regexp,
395  CPL_FALSE));
396  qclist = cpl_propertylist_duplicate(irplib_framelist_get_propertylist(rawframes, 0));
397  if (ppqc)
398  skip_if(propagate_qc_header(qclist, ppqc));
399 
400  skip_if(irplib_framelist_load_propertylist_all(rawframes, 0, "^("
401  RECIPE_KEYS_REGEXP_ALL
402  "|"VISIR_PFITS_REGEXP_DIT
403  ")$", CPL_FALSE));
404 
405  skip_if(visir_dfs_check_framelist_tag(rawframes));
406 
407  if (cpl_propertylist_has(irplib_framelist_get_propertylist(rawframes, 0),
408  VISIR_PFITS_DOUBLE_SEQ1_DIT))
409  dit_key = VISIR_PFITS_DOUBLE_SEQ1_DIT;
410  skip_if(0);
411 
412  /* Standard star catalog */
413  star_cat = irplib_frameset_find_file(framelist, VISIR_CALIB_STDSTAR_IMG);
414  if (star_cat == NULL) {
415  cpl_msg_error(cpl_func, "The input files has no star catalog tagged "
416  "%s", VISIR_CALIB_STDSTAR_IMG);
417  visir_error_set(CPL_ERROR_DATA_NOT_FOUND);
418  skip_if(1);
419  }
420 
421  /* Bad pixels calibration file */
422  badpix = irplib_frameset_find_file(framelist, VISIR_CALIB_BPM);
423 
424  /* Flatfield calibration file */
425  flat = irplib_frameset_find_file(framelist, VISIR_CALIB_FLAT);
426 
427 
428  /* DIT must be present every where */
429  skip_if(irplib_framelist_contains(rawframes, dit_key,
430  CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
431  if(irplib_framelist_contains(rawframes, dit_key,
432  CPL_TYPE_DOUBLE, CPL_TRUE, 1e-5)) {
433  /* Allow 0.1 ms difference - or warn */
434  /* FIXME: The recipe does not properly handle non-uniform DITSs */
435  visir_error_reset("DIT differs by more than %g", 1e-5);
436  }
437 
438  /* FIXME: Verify the angular distance */
439  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_DOUBLE_RA,
440  CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
441 
442  /* FIXME: Allow 1 degree difference */
443  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_DOUBLE_DEC,
444  CPL_TYPE_DOUBLE, CPL_TRUE, 1.0));
445 
446  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_CHOP_NCYCLES,
447  CPL_TYPE_INT, CPL_TRUE, 0.0));
448 
449  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_INT_NDIT,
450  CPL_TYPE_INT, CPL_TRUE, 0.0));
451 
452  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_STARNAME,
453  CPL_TYPE_STRING, CPL_TRUE, 0.0));
454 
455  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_INSMODE,
456  CPL_TYPE_STRING, CPL_TRUE, 0.0));
457 
458  if (strncmp("IMG", visir_pfits_get_insmode(
460  6) == 0) {
461  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_FILTER1,
462  CPL_TYPE_STRING, CPL_TRUE, 0.0));
463  } else {
464  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_FILTER2,
465  CPL_TYPE_STRING, CPL_TRUE, 0.0));
466  }
467 
468  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_PIXSCALE,
469  CPL_TYPE_STRING, CPL_TRUE, 0.0));
470 
471 
472  {
473  const char * prew =
474  irplib_frameset_find_file(framelist, "WEIGHT_MAP");
475  if (prew) {
476  cpl_msg_info(cpl_func, "Loading weight map %s", prew);
477  weights = cpl_image_load(prew, CPL_TYPE_UNSPECIFIED, 0, 0);
478  }
479  }
480 
481  {
482  const char * prec =
483  irplib_frameset_find_file(framelist, "CONTRIBUTION_MAP");
484  if (prec) {
485  cpl_msg_info(cpl_func, "Loading contribution map %s", prec);
486  contrib = cpl_image_load(prec, CPL_TYPE_INT, 0, 0);
487  }
488  }
489 
490  skip_if(0);
491  /* Combine the frames */
492  cpl_msg_info(cpl_func, "Combining the images");
493  {
494  const char * pre =
495  irplib_frameset_find_file(framelist, "IM_CAL_PHOT_PREPROCESSED");
496  if (pre) {
497  combined = cpl_calloc(2, sizeof(combined[0]));
498  combined[0] = cpl_image_load(pre, CPL_TYPE_UNSPECIFIED, 0, 0);
499  combined[1] = contrib ? cpl_image_duplicate(contrib) : NULL;
500  if (combined[0] == NULL) {
501  cpl_msg_error(cpl_func, "Could not load the input frames");
502  skip_if(1);
503  }
504  if (weights) {
505  cpl_mask * m = cpl_mask_threshold_image_create(weights, -1,
506  FLT_EPSILON);
507  cpl_image_reject_from_mask(combined[0], m);
508  cpl_mask_delete(m);
509  }
510  }
511  else {
512  combined = visir_img_recombine(RECIPE_STRING, parlist, rawframes,
513  badpix, flat, combine_mode,
514  &drop_wcs, CPL_FALSE, 0.0, 0);
515  if (combined == NULL) {
516  cpl_msg_error(cpl_func, "Could not combine the input frames");
517  skip_if(1);
518  }
519  beam1 = visir_img_collapse_beam(qclist, combined[0], parlist, RECIPE_STRING,
520  VISIR_CHOPNOD_AUTO,
522  (rawframes, 0));
523  if (beam1 == NULL)
524  irplib_error_recover(cleanstate, "Could not collapse the "
525  "beams of the combined image");
526 
527  /* Compute the background values of the HCYCLE frames
528  * done by repack in preprocessed mode */
529  skip_if(visir_qc_append_background(qclist, rawframes, 0, 0));
530  }
531  }
532 
533  /* Compute here the sensitivity */
534  cpl_msg_info(cpl_func, "Computing the sensitivity");
535  if (visir_img_phot_sensit(combined[0], weights, contrib, rawframes,
536  star_cat, one_beam, ppqc)) {
537  cpl_msg_error(cpl_func, "Could not compute sensitivity: '%s' in %s",
538  cpl_error_get_message(), cpl_error_get_where());
539  skip_if(1);
540  }
541 
542  skip_if (visir_img_phot_qc(qclist, drop_wcs, rawframes));
543  irplib_framelist_empty(rawframes);
544 
545  /* Save the combined image and contribution map */
546  if (beam1 == NULL) {
547  cpl_msg_info(cpl_func, "Saving the combined image with its contribution "
548  "map");
549  skip_if (visir_img_phot_save(framelist, parlist, qclist, combined[0],
550  combined[1], NULL, NULL, weights));
551  } else {
552  cpl_msg_info(cpl_func, "Saving the combined and the beam-combined "
553  "images with their contribution maps");
554  skip_if (visir_img_phot_save(framelist, parlist, qclist, combined[0],
555  combined[1], beam1[0], beam1[1], weights));
556  }
557 
558  end_skip;
559 
560  cpl_propertylist_delete(qclist);
561  irplib_framelist_delete(allframes);
562  irplib_framelist_delete(rawframes);
563  cpl_propertylist_delete(ppqc);
564  cpl_image_delete(weights);
565  cpl_image_delete(contrib);
566 
567  if (combined) {
568  cpl_image_delete(combined[0]);
569  cpl_image_delete(combined[1]);
570  cpl_free(combined);
571  }
572 
573  if (beam1) {
574  cpl_image_delete(beam1[0]);
575  cpl_image_delete(beam1[1]);
576  cpl_free(beam1);
577  }
578 
579  return cpl_error_get_code();
580 
581 }
582 
583 /*----------------------------------------------------------------------------*/
608 /*----------------------------------------------------------------------------*/
609 static int visir_img_phot_sensit(const cpl_image * combined,
610  const cpl_image * weights,
611  const cpl_image * contrib,
612  const irplib_framelist * rawframes,
613  const char * star_cat,
614  cpl_boolean one_beam,
615  cpl_propertylist * ppqc)
616 {
617  cpl_errorstate cleanstate = cpl_errorstate_get();
618  const cpl_propertylist * plist = NULL;
619  double ra, dec;
620  const char * sval;
621  double f2;
622 
623 
624  skip_if (0);
625 
626  plist = irplib_framelist_get_propertylist_const(rawframes, 0);
627  skip_if (0);
628 
629  /* Get the total exposure time */
630  {
631  double exptime;
632 
633  if (ppqc) /* FIXME: use effective exptime? */
634  exptime = cpl_propertylist_get_double(ppqc, "ESO QC EXPTIME TOTAL");
635  else {
636  const int nnod = irplib_framelist_get_size(rawframes);
637  exptime = visir_utils_get_exptime(nnod, plist);
638  }
639  visir_img_phot_config.exptime = exptime;
640 
641  if (exptime <= 0 || cpl_error_get_code()) {
642  cpl_msg_error(cpl_func, "Illegal exposure time: %g", exptime);
643  skip_if(1);
644  }
645  }
646 
647  /* Copy the standard star name */
648  skip_if ((sval = visir_pfits_get_starname(plist)) == NULL);
649  (void) strncpy(visir_img_phot_config.star_name, sval, fits_strlen);
650 
651  /* Copy the filter name */
652  skip_if ((sval = visir_pfits_get_filter(plist)) == NULL);
653  (void)strncpy(visir_img_phot_config.filter, sval, fits_strlen);
654 
655  /* Get RA / DEC */
656  ra = visir_pfits_get_ra(plist);
657  skip_if (0);
658  dec = visir_pfits_get_dec(plist);
659  skip_if (0);
660 
661  /* Get the pixel scale */
662  visir_img_phot_config.pscale = visir_pfits_get_pixscale(plist);
663 
664  /* Get the JY value from the catalog if not user provided */
665  if (visir_img_phot_config.jy_val < -998) {
666  visir_img_phot_config.jy_val =
667  visir_img_phot_jy_from_cat(star_cat, visir_img_phot_config.filter,
668  ra,dec, visir_img_phot_config.star_name);
669  skip_if (visir_img_phot_config.jy_val < -998);
670  }
671 
672  /* Display the result */
673  cpl_msg_info(cpl_func, "Star %s with filter %s : %g Jy",
674  visir_img_phot_config.star_name,
675  visir_img_phot_config.filter,
676  visir_img_phot_config.jy_val);
677 
678  /* Compute the background signa */
679  visir_img_phot_config.bg_sigma = visir_img_phot_sigma_clip(combined);
680  skip_if (0);
681 
682  /* Get the flux and flux noise and fwhm */
683  cpl_msg_info(cpl_func, "Compute the star flux");
684  sval = visir_pfits_get_chopnod_dir(plist);
685 #if 1
686  if (one_beam) {
687  skip_if (visir_img_phot_flux_one(combined, weights, contrib, plist));
688  }
689  else if (sval != NULL && !strcmp(sval, "PERPENDICULAR")) {
690  /* 4 sources */
691  skip_if (visir_img_phot_flux_four(combined));
692  } else if (sval != NULL && !strcmp(sval, "PARALLEL")) {
693  /* 3 sources */
694  skip_if (visir_img_phot_flux_three(combined));
695  } else {
696  if (sval == NULL) {
697  visir_error_reset("Could not get FITS key");
698  } else {
699  cpl_msg_warning(cpl_func, "Unknown chopping direction: %s", sval);
700  }
701  cpl_msg_warning(cpl_func, "Proceeding as if FITS card %s had value %s",
702  "ESO SEQ CHOPNOD DIR", "PERPENDICULAR");
703  if (visir_img_phot_flux_four(combined)) {
704  visir_error_reset("Proceeding as if FITS card %s had value %s",
705  "ESO SEQ CHOPNOD DIR", "PARALLEL");
706  skip_if (visir_img_phot_flux_three(combined));
707  }
708  }
709 #else
710  if (sval == NULL) {
711  visir_error_reset("Could not get chopping direction");
712  if (visir_img_phot_flux_three(combined)) {
713  cpl_msg_error(cpl_func, "Could not compute the flux");
714  skip_if(1);
715  }
716  } else if (!strcmp(sval, "PARALLEL")) {
717  /* 3 sources */
718  if (visir_img_phot_flux_three(combined)) {
719  cpl_msg_error(cpl_func, "Could not compute the flux");
720  skip_if(1);
721  }
722  } else {
723  /* Default is 4 sources */
724  if (strcmp(sval, "PERPENDICULAR"))
725  cpl_msg_warning(cpl_func, "Unknown chopping direction: %s", sval);
726  if (visir_img_phot_flux_four(combined)) {
727  cpl_msg_error(cpl_func, "Could not compute the flux");
728  skip_if(1);
729  }
730  }
731 #endif
732 
733 
734  /* compute number of photons and electrons for the efficiency */
735  {
736  double fluxSI = visir_img_phot_config.jy_val * 1e-26;
737  double dit = visir_pfits_get_dit(plist);;
738  /* area of telescope [m^2] */
739  double area = (8.2/2.) * (8.2/2.) * CPL_MATH_PI;
740  const char * filter = visir_pfits_get_filter(plist);
741  double wl;
742  /* filter bandwidth delta lambda [m] */
743  double bandwidth;
744  double gain = 20.;
745 
746  if (ppqc && cpl_propertylist_has(ppqc, "ESO DET CHIP1 GAIN"))
747  gain = cpl_propertylist_get_double(ppqc, "ESO DET CHIP1 GAIN");
748  else if (ppqc && cpl_propertylist_has(ppqc, "ESO DET CHIP GAIN"))
749  gain = cpl_propertylist_get_double(ppqc, "ESO DET CHIP GAIN");
750  else
751  cpl_msg_warning(cpl_func, "ESO DET CHIP1 GAIN not found, "
752  "assuming %g", gain);
753 
754  if (filter && !visir_get_filter_infos(filter, &wl, &bandwidth)) {
755  /* filter bandwidth deltanue [Hz] */
756  double bwh, ptot, ephot;
757  wl *= 1e-6; /* [micron] -> [m] */
758  bandwidth *= 1e-6; /* [micron] -> [m] */
759 
760  bwh = CPL_PHYS_C / (wl * wl) * bandwidth;
761  /* total power [W] */
762  ptot = fluxSI * area * bwh;
763  /* photon energy [Ws] */
764  ephot = CPL_PHYS_H * CPL_PHYS_C / wl;
765 
766  visir_img_phot_config.nelec = visir_img_phot_config.flux_tot * gain;
767  visir_img_phot_config.nphot = ptot / ephot * dit;
768  }
769  else
770  cpl_msg_warning(cpl_func, "Unknown filter: %s", filter);
771  }
772 
773  /* Compute the sensitivity and the conversion factor */
774 
775  skip_if ( visir_img_phot_config.flux_snr == 0 );
776  skip_if ( visir_img_phot_config.flux_snr_noise == 0 );
777  skip_if ( visir_img_phot_config.jy_val == 0 );
778 
779  f2 = visir_img_phot_config.flux_snr / visir_img_phot_config.flux_snr_noise;
780  f2 *= sqrt(3600/visir_img_phot_config.exptime);
781  visir_img_phot_config.sensitivity = visir_img_phot_config.jy_val*1000*10/f2;
782  /* compute conversion, accounting for double signal in parallel mode */
783  visir_img_phot_config.conversion = visir_img_phot_config.flux_tot /
784  visir_img_phot_config.jy_val /
785  (ppqc ? visir_pfits_get_img_weight(ppqc) : 1.);
786 
787  /* sensitivity using the noise under 1as circle */
788  visir_img_phot_config.area_sensit =
789  visir_img_phot_config.sensitivity *
790  sqrt((1. / SQR(visir_img_phot_config.pscale)) /
791  SQR(visir_img_phot_config.flux_snr_radius[0]));
792 
793  end_skip;
794 
795  cpl_msg_info(cpl_func, "Sensitivity : %g", visir_img_phot_config.sensitivity);
796  cpl_msg_info(cpl_func, "Conversion : %g", visir_img_phot_config.conversion);
797  cpl_msg_info(cpl_func, "Strehl : %g", visir_img_phot_config.strehl);
798  cpl_msg_info(cpl_func, "Strehl error: %g", visir_img_phot_config.strehl_err);
799  cpl_msg_info(cpl_func, "Exposure : %g s", visir_img_phot_config.exptime);
800 
801  return cpl_error_get_code();
802 }
803 
804 /*----------------------------------------------------------------------------*/
814 /*----------------------------------------------------------------------------*/
815 static double visir_img_phot_jy_from_cat(
816  const char * star_cat,
817  const char * filter,
818  double ra,
819  double dec,
820  const char * star_name)
821 {
822  cpl_table * tab = NULL;
823  cpl_vector * v_ra = NULL;
824  cpl_vector * v_dec = NULL;
825  const char * stdstar;
826  char * label = NULL;
827  int nb_stars;
828  const double max_radius = VISIR_STAR_MAX_RADIUS;
829  double value = -999;
830  double jy;
831  double dist;
832  int min_dist_ind;
833 
834 
835  skip_if( 0 );
836  skip_if( star_cat == NULL );
837  skip_if( star_name == NULL );
838  skip_if( filter == NULL );
839 
840  label = visir_img_phot_filter2label(filter);
841  skip_if(label == NULL);
842 
843  /* Open the star catalog */
844  if ((tab = cpl_table_load(star_cat, 1, 1)) == NULL) {
845  cpl_error_set_message(cpl_func, cpl_error_get_code(),
846  "Could not load the star catalog: %s",
847  star_cat);
848  skip_if(1);
849  }
850 
851  /* Check that the filter is in the table */
852  if (!cpl_table_has_column(tab, label)) {
853  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
854  "Catalog %s has no column for filter: %s",
855  star_cat, filter);
856  skip_if(1);
857  }
858 
859  if (!cpl_table_has_column(tab, "STARS")) {
860  cpl_error_set_message(cpl_func, CPL_ERROR_BAD_FILE_FORMAT,
861  "Catalog %s does not have a column labeled %s",
862  star_cat, "STARS");
863  skip_if(1);
864  }
865 
866  nb_stars = cpl_table_get_nrow(tab);
867  skip_if (nb_stars < 1);
868 
869  /* Get the RA and DEC columns */
870  v_ra = cpl_vector_wrap(nb_stars, cpl_table_get_data_double(tab, "RA"));
871  skip_if( v_ra == NULL);
872 
873  v_dec = cpl_vector_wrap(nb_stars, cpl_table_get_data_double(tab, "DEC"));
874  skip_if( v_dec == NULL);
875 
876  min_dist_ind = visir_star_find(v_ra, v_dec, ra, dec, max_radius, &dist);
877 
878  if (min_dist_ind < 0) {
879  cpl_error_set_message(cpl_func, cpl_error_get_code(),
880  "Observation target '%s' was not found among "
881  "the %d entries in the standard star catalog %s",
882  star_name, nb_stars, star_cat);
883  skip_if(1);
884  }
885 
886  stdstar = cpl_table_get_string(tab, "STARS", min_dist_ind);
887  skip_if ( stdstar == NULL );
888 
889  int null;
890  jy = cpl_table_get(tab, label, min_dist_ind, &null);
891  skip_if( 0 );
892 
893  if (null || isnan(jy)) {
894  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
895  "Catalog does not contain a value for %s in "
896  "filter %s", star_name, filter);
897  skip_if(1);
898  }
899 
900  value = jy;
901 
902  if (strcmp(stdstar, star_name) != 0 && dist > 0.0) {
903  /* The names do not match, nor does the location (exactly) */
904  cpl_msg_warning(cpl_func, "The standard star '%s' at (RA,DEC)=(%g,%g) "
905  "in the FITS header is closest to the catalog star "
906  "'%s' at (RA,DEC)=(%g,%g) at distance %g degrees",
907  star_name, ra, dec, stdstar,
908  cpl_vector_get(v_ra, min_dist_ind),
909  cpl_vector_get(v_dec, min_dist_ind), dist);
910  } else if (dist > 0.0) {
911  /* The names match, but the location does not (exactly) */
912  cpl_msg_warning(cpl_func, "The location of the standard star '%s' in "
913  "the FITS header (RA,DEC)=(%g,%g) and the catalog "
914  "(RA,DEC)=(%g,%g) differ by %g degrees", stdstar, ra,
915  dec, cpl_vector_get(v_ra, min_dist_ind),
916  cpl_vector_get(v_dec, min_dist_ind), dist);
917  } else if (strcmp(stdstar, star_name) != 0) {
918  /* The names do not match, but the location does */
919  cpl_msg_warning(cpl_func, "The name of the standard star at (RA,DEC)="
920  "(%g,%g) in the FITS header '%s' and the catalog '%s' "
921  "are different", ra, dec, star_name, stdstar);
922  } else {
923  cpl_msg_info(cpl_func, "Standard star is '%s' at (RA, DEC)=(%g,%g)",
924  stdstar, ra, dec);
925 
926  if (cpl_msg_get_level() <= CPL_MSG_DEBUG)
927  cpl_table_dump(tab, min_dist_ind, 1, stdout);
928  }
929 
930  cpl_msg_info(cpl_func, "The standard star '%s' has %g Jy through filter %s",
931  stdstar, value, filter);
932 
933  end_skip;
934 
935  cpl_free(label);
936  cpl_table_delete(tab);
937  cpl_vector_unwrap(v_ra);
938  cpl_vector_unwrap(v_dec);
939 
940  return value;
941 }
942 
943 /*----------------------------------------------------------------------------*/
952 /*----------------------------------------------------------------------------*/
953 static int visir_img_phot_flux_three(const cpl_image * combined)
954 {
955  cpl_errorstate cleanstate = cpl_errorstate_get();
956  cpl_image * min_combined = NULL;
957  cpl_apertures * appos = NULL;
958  cpl_apertures * apneg = NULL;
959  cpl_vector * sigmas = NULL;
960  double psigmas[] = {5, 2, 1, 0.5};
961  double x[3];
962  double y[3];
963  double flux_snr[3];
964  double flux_snr_noise[3];
965  int flux_snr_radius[3];
966  double flux_tot[3];
967  beamshape_t beamshapes[3];
968  double dist1, dist2;
969  int ngood_fwhm;
970  double lam, dlam;
971  double star_bg,star_peak,star_flux,psf_peak,psf_flux,bg_noise;
972  double eccmin = DBL_MAX;
973  const int nsigmas = sizeof(psigmas)/sizeof(double);
974  int isigma;
975 #ifdef VISIR_IMG_PHOT_USE_ECCENT_THREE
976  int ipos, ineg1, ineg2;
977 #endif
978  int iappos, iapneg2[2];
979  int i;
980 
981 
982  if (cpl_error_get_code()) return cpl_error_get_code();
983 
984  cpl_ensure_code(combined != NULL, CPL_ERROR_NULL_INPUT);
985 
986  cpl_msg_info(cpl_func, "Detecting the 3-source star using %d sigma-levels "
987  "ranging from %g down to %g", nsigmas, psigmas[0],
988  psigmas[nsigmas-1]);
989 
990  sigmas = cpl_vector_new(1);
991  min_combined = cpl_image_multiply_scalar_create(combined, -1.0);
992  bug_if(0);
993  for (isigma = 0; isigma < nsigmas; isigma++) {
994 
995  /* FIXME: Why ?! */
996  irplib_error_recover(cleanstate, "Resetting error (why?)");
997 
998  bug_if(cpl_vector_set(sigmas, 0, psigmas[isigma]));
999 
1000  /* Detect where the POSITIVE star is */
1001  cpl_apertures_delete(appos);
1002  appos = cpl_apertures_extract(combined, sigmas, NULL);
1003  if (appos == NULL) {
1004  cpl_msg_warning(cpl_func, "Found no positive star at sigma=%g",
1005  psigmas[isigma]);
1006  continue;
1007  }
1008 
1009  /* Detect where the NEGATIVE stars are */
1010  cpl_apertures_delete(apneg);
1011  apneg = cpl_apertures_extract(min_combined, sigmas, NULL);
1012  if (apneg == NULL) {
1013  cpl_msg_warning(cpl_func, "Found no negative stars at sigma=%g",
1014  psigmas[isigma]);
1015  continue;
1016  }
1017  if (cpl_apertures_get_size(apneg) < 2) {
1018  cpl_msg_warning(cpl_func, "Found just 1 negative star at sigma=%g, "
1019  "need two", psigmas[isigma]);
1020  continue;
1021  }
1022 
1023  cpl_msg_info(cpl_func, "Found positive and negative stars at sigma=%g",
1024  psigmas[isigma]);
1025  break;
1026  }
1027 
1028  skip_if(appos == NULL);
1029  skip_if(apneg == NULL);
1030  skip_if (cpl_apertures_get_size(apneg) < 2);
1031 
1032  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
1033  cpl_apertures_dump(appos, stdout);
1034  cpl_apertures_dump(apneg, stdout);
1035  }
1036 
1037 #ifndef VISIR_IMG_PHOT_USE_ECCENT_THREE
1038  bug_if(irplib_apertures_find_max_flux(appos, &iappos, 1));
1039  bug_if(irplib_apertures_find_max_flux(apneg, iapneg2, 2));
1040 #else
1041  if (cpl_apertures_get_size(appos) > 1 || cpl_apertures_get_size(apneg) > 2)
1042  cpl_msg_info(cpl_func, "Selecting from %d positive and %d negative "
1043  "stars 3 stars on a vertical line",
1044  cpl_apertures_get_size(appos),
1045  cpl_apertures_get_size(apneg));
1046 
1047  for (ipos = 1; ipos <= cpl_apertures_get_size(appos); ipos++) {
1048  for (ineg1 = 2; ineg1 <= cpl_apertures_get_size(apneg); ineg1++) {
1049  for (ineg2 = 1; ineg2 < ineg1; ineg2++) {
1050  const double ecc = visir_img_phot_eccent_three(appos, ipos,
1051  apneg,
1052  ineg1, ineg2);
1053  if (ecc < eccmin) {
1054  if (eccmin < DBL_MAX)
1055  cpl_msg_debug(cpl_func, "Found star positions with "
1056  "reduced mis-alignment [pixel]: "
1057  "%g < %g", ecc, eccmin);
1058  eccmin = ecc;
1059  iappos = ipos;
1060  iapneg2[0] = ineg1;
1061  iapneg2[1] = ineg2;
1062  }
1063  }
1064  }
1065  }
1066 
1067  /* Star 1 should have largest flux */
1068  if (cpl_apertures_get_flux(apneg, iapneg2[0]) <
1069  cpl_apertures_get_flux(apneg, iapneg2[1])) {
1070  const int tmp = iapneg2[0];
1071  iapneg2[0] = iapneg2[1];
1072  iapneg2[1] = tmp;
1073  }
1074 
1075 #endif
1076 
1077  x[0] = cpl_apertures_get_centroid_x(appos, iappos);
1078  y[0] = cpl_apertures_get_centroid_y(appos, iappos);
1079 
1080  x[1] = cpl_apertures_get_centroid_x(apneg, iapneg2[0]);
1081  y[1] = cpl_apertures_get_centroid_y(apneg, iapneg2[0]);
1082  x[2] = cpl_apertures_get_centroid_x(apneg, iapneg2[1]);
1083  y[2] = cpl_apertures_get_centroid_y(apneg, iapneg2[1]);
1084 
1085  cpl_apertures_delete(appos);
1086  cpl_apertures_delete(apneg);
1087  appos = NULL;
1088  apneg = NULL;
1089 
1090  cpl_msg_info(cpl_func, "Positive star at position %g %g", x[0], y[0]);
1091  cpl_msg_info(cpl_func, "Negative star 1 at position %g %g", x[1], y[1]);
1092  cpl_msg_info(cpl_func, "Negative star 2 at position %g %g", x[2], y[2]);
1093 
1094  dist1 = sqrt((x[1]-x[0])*(x[1]-x[0])+(y[1]-y[0])*(y[1]-y[0]));
1095  dist2 = sqrt((x[2]-x[0])*(x[2]-x[0])+(y[2]-y[0])*(y[2]-y[0]));
1096  cpl_msg_info(cpl_func, "Star 1 Pos/Neg Distance: %g", dist1);
1097  cpl_msg_info(cpl_func, "Star 2 Pos/Neg Distance: %g", dist2);
1098 
1099 
1100  if (eccmin < DBL_MAX) {
1101  cpl_msg_info(cpl_func, "The deviation from a vertical line by "
1102  "the three stars [pixel]: %g", eccmin);
1103  if (eccmin > VISIR_IMG_PHOT_POS_WARN) {
1104  if (eccmin > VISIR_IMG_PHOT_POS_ERROR) {
1105  cpl_msg_error(cpl_func, "The deviation from a vertical line"
1106  " by the three stars exceed %g, the detected "
1107  "objects are wrong", VISIR_IMG_PHOT_POS_ERROR);
1108  skip_if(1);
1109  }
1110  cpl_msg_warning(cpl_func, "The deviation from a vertical line "
1111  "by the three stars exceed %g, the detected "
1112  "objects may be wrong", VISIR_IMG_PHOT_POS_WARN);
1113  }
1114  }
1115 
1116  /* FIXME: Pick other object instead, and lower sigma if needed */
1117  if ((int)x[0]-IRPLIB_STREHL_BORDER <= 0 ||
1118  (int)y[0]-IRPLIB_STREHL_BORDER <= 0 ||
1119  (int)x[0]+IRPLIB_STREHL_BORDER > cpl_image_get_size_x(combined) ||
1120  (int)y[0]+IRPLIB_STREHL_BORDER > cpl_image_get_size_y(combined)) {
1121  cpl_msg_error(cpl_func, "Positive star at (%g,%g) is less than %d "
1122  "pixels from the image border", x[0], y[0],
1123  1+IRPLIB_STREHL_BORDER);
1124  skip_if(1);
1125  }
1126 
1127  /* FIXME: Useful ? */
1128  /* Verify the stars positions */
1129  if (fabs(dist1-dist2) > VISIR_IMG_PHOT_POS_UNCERTAINTY) {
1130  cpl_msg_error(cpl_func, "Too large Pos/Neg Distance between the two "
1131  "stars: %g > %g", fabs(dist1-dist2),
1132  VISIR_IMG_PHOT_POS_UNCERTAINTY);
1133  skip_if(1);
1134  }
1135 
1136  /* Photometry on positive stars */
1137  skip_if (visir_img_phot_flux(combined, NULL, NULL,
1138  x[0], y[0],
1139  visir_img_phot_config.r0_max,
1140  visir_img_phot_config.r1,
1141  visir_img_phot_config.r2,
1142  &(flux_snr[0]),
1143  &(flux_snr_noise[0]),
1144  &(flux_snr_radius[0]),
1145  &(flux_tot[0]),
1146  &(beamshapes[0])));
1147 
1148  /* Photometry on negative stars */
1149  for (i=1 ; i<3 ; i++)
1150  skip_if (visir_img_phot_flux(min_combined, NULL, NULL,
1151  x[i], y[i],
1152  visir_img_phot_config.r0_max,
1153  visir_img_phot_config.r1,
1154  visir_img_phot_config.r2,
1155  &(flux_snr[i]),
1156  &(flux_snr_noise[i]),
1157  &(flux_snr_radius[i]),
1158  &(flux_tot[i]),
1159  &(beamshapes[i])));
1160 
1161  cpl_image_delete(min_combined);
1162  min_combined = NULL;
1163 
1164  /* Compute the results */
1165  /* Flux */
1166  visir_img_phot_config.flux_snr = 0.0;
1167  for (i=0 ; i<3 ; i++) visir_img_phot_config.flux_snr += flux_snr[i];
1168 
1169  /* Flux noise */
1170  visir_img_phot_config.flux_snr_noise = 0.0;
1171  for (i=0 ; i<3 ; i++) visir_img_phot_config.flux_snr_noise +=
1172  flux_snr_noise[i]*flux_snr_noise[i];
1173  visir_img_phot_config.flux_snr_noise =
1174  sqrt(visir_img_phot_config.flux_snr_noise);
1175 
1176  for (i=0 ; i<3 ; i++)
1177  visir_img_phot_config.flux_snr_radius[i] = flux_snr_radius[i];
1178 
1179  /* Total flux */
1180  visir_img_phot_config.flux_tot = 0.0;
1181  for (i=0 ; i<3 ; i++) visir_img_phot_config.flux_tot += flux_tot[i];
1182 
1183  /* FWHM */
1184  ngood_fwhm = 0;
1185  visir_img_phot_config.beamshape.fwhm_x = 0.0;
1186  for (i=0 ; i<3 ; i++) {
1187  if (beamshapes[i].fwhm_x > 0.0) {
1188  visir_img_phot_config.beamshape.fwhm_x += beamshapes[i].fwhm_x;
1189  ngood_fwhm ++;
1190  }
1191  }
1192  if (ngood_fwhm > 0) visir_img_phot_config.beamshape.fwhm_x /= ngood_fwhm;
1193  else visir_img_phot_config.beamshape.fwhm_x = -1.0;
1194  ngood_fwhm = 0;
1195  visir_img_phot_config.beamshape.fwhm_y = 0.0;
1196  for (i=0 ; i<3 ; i++) {
1197  if (beamshapes[i].fwhm_y > 0.0) {
1198  visir_img_phot_config.beamshape.fwhm_y += beamshapes[i].fwhm_y;
1199  ngood_fwhm ++;
1200  }
1201  }
1202  if (ngood_fwhm > 0) visir_img_phot_config.beamshape.fwhm_y /= ngood_fwhm;
1203  else visir_img_phot_config.beamshape.fwhm_y = -1.0;
1204  visir_img_phot_config.fwhm_x_pos1 = beamshapes[0].fwhm_x;
1205  visir_img_phot_config.fwhm_y_pos1 = beamshapes[0].fwhm_y;
1206  visir_img_phot_config.fwhm_x_neg1 = beamshapes[1].fwhm_x;
1207  visir_img_phot_config.fwhm_y_neg1 = beamshapes[1].fwhm_y;
1208  visir_img_phot_config.fwhm_x_neg2 = beamshapes[2].fwhm_x;
1209  visir_img_phot_config.fwhm_y_neg2 = beamshapes[2].fwhm_y;
1210 
1211  /* Get lam and dlam from the filter name for the Strehl computation */
1212  if (visir_get_filter_infos(visir_img_phot_config.filter, &lam, &dlam)) {
1213  cpl_msg_error(cpl_func, "Could not get info for filter: %s",
1214  visir_img_phot_config.filter);
1215  skip_if(1);
1216  }
1217 
1218  /* Strehl computation */
1219  cpl_errorstate errstate = cpl_errorstate_get();
1220  if (irplib_strehl_compute(combined, STREHL_M1, STREHL_M2, lam, dlam,
1221  visir_img_phot_config.pscale,
1222  STREHL_BOX_SIZE, x[0], y[0], STREHL_STAR_RADIUS,
1223  STREHL_BG_R1, STREHL_BG_R2, -1, -1,
1224  &(visir_img_phot_config.strehl),
1225  &(visir_img_phot_config.strehl_err),
1226  &star_bg, &star_peak, &star_flux, &psf_peak, &psf_flux,
1227  &bg_noise)) {
1228  cpl_msg_error(cpl_func, "Could not compute the strehl: '%s' in %s",
1229  cpl_error_get_message(), cpl_error_get_where());
1230  cpl_errorstate_set(errstate);
1231  }
1232 
1233 
1234  end_skip;
1235 
1236  cpl_apertures_delete(appos);
1237  cpl_apertures_delete(apneg);
1238  cpl_vector_delete(sigmas);
1239  cpl_image_delete(min_combined);
1240 
1241  return cpl_error_get_code();
1242 }
1243 
1244 static int visir_img_phot_flux_one(const cpl_image * combined,
1245  const cpl_image * weights,
1246  const cpl_image * contrib,
1247  const cpl_propertylist * plist)
1248 {
1249  double x;
1250  double y;
1251  double flux_snr;
1252  double flux_snr_noise;
1253  int flux_snr_radius;
1254  double flux_tot;
1255  beamshape_t beamshape;
1256  double lam, dlam;
1257  double star_bg,star_peak,star_flux,psf_peak,psf_flux,bg_noise;
1258 
1259  if (cpl_error_get_code()) return cpl_error_get_code();
1260 
1261  cpl_ensure_code(combined != NULL, CPL_ERROR_NULL_INPUT);
1262 
1263  x = cpl_propertylist_get_double(plist, "CRPIX1");
1264  y = cpl_propertylist_get_double(plist, "CRPIX2");
1265  {
1266  /* CRPIX can be a few pixels off center due to slicing of
1267  * the images around the beam done in detect_shift
1268  * only the int cast is used later, so pixel precision of
1269  * maxpos is ok */
1270  cpl_size yp, xp;
1271  cpl_image_get_maxpos_window(combined,
1272  CX_MAX(1, x - 5), CX_MAX(1, y - 5),
1273  CX_MIN(cpl_image_get_size_x(combined), x + 5),
1274  CX_MIN(cpl_image_get_size_y(combined), y + 5), &xp, &yp);
1275  x = xp;
1276  y = yp;
1277  }
1278 
1279  cpl_msg_info(cpl_func, "Positive star 1 at position %g %g", x, y);
1280 
1281  if ((int)x-IRPLIB_STREHL_BORDER <= 0 ||
1282  (int)y-IRPLIB_STREHL_BORDER <= 0) {
1283  cpl_msg_error(cpl_func, "Positive star 1 at (%g,%g) is less than %d "
1284  "pixels from the image border", x, y,
1285  1+IRPLIB_STREHL_BORDER);
1286  skip_if(1);
1287  }
1288 
1289  /* Photometry on positive stars */
1290  skip_if (visir_img_phot_flux(combined, weights, contrib,
1291  x, y,
1292  visir_img_phot_config.r0_max,
1293  visir_img_phot_config.r1,
1294  visir_img_phot_config.r2,
1295  &(flux_snr),
1296  &(flux_snr_noise),
1297  &(flux_snr_radius),
1298  &(flux_tot),
1299  &(beamshape)));
1300 
1301 
1302  /* Compute the results */
1303  /* Flux */
1304  visir_img_phot_config.flux_snr = flux_snr;
1305 
1306  /* Flux noise */
1307  visir_img_phot_config.flux_snr_noise = flux_snr_noise;
1308 
1309  visir_img_phot_config.flux_snr_radius[0] = flux_snr_radius;
1310 
1311  /* Total flux */
1312  visir_img_phot_config.flux_tot = flux_tot;
1313 
1314  /* FWHM */
1315  visir_img_phot_config.beamshape = beamshape;
1316  visir_img_phot_config.fwhm_x_pos1 = beamshape.fwhm_x;
1317  visir_img_phot_config.fwhm_y_pos1 = beamshape.fwhm_y;
1318 
1319  /* Get lam and dlam from the filter name for the Strehl computation */
1320  if (visir_get_filter_infos(visir_img_phot_config.filter, &lam, &dlam)) {
1321  cpl_msg_error(cpl_func, "Central wavelength and width is missing for "
1322  "filter: %s", visir_img_phot_config.filter);
1323  skip_if(1);
1324  }
1325 
1326  /* Strehl computation */
1327  cpl_errorstate errstate = cpl_errorstate_get();
1328  if (irplib_strehl_compute(combined, STREHL_M1, STREHL_M2, lam, dlam,
1329  visir_img_phot_config.pscale,
1330  STREHL_BOX_SIZE, x, y, STREHL_STAR_RADIUS,
1331  STREHL_BG_R1, STREHL_BG_R2, -1, -1,
1332  &(visir_img_phot_config.strehl),
1333  &(visir_img_phot_config.strehl_err),
1334  &star_bg, &star_peak, &star_flux, &psf_peak, &psf_flux,
1335  &bg_noise)) {
1336  cpl_msg_error(cpl_func, "Could not compute the strehl: '%s' in %s",
1337  cpl_error_get_message(), cpl_error_get_where());
1338  cpl_errorstate_set(errstate);
1339  }
1340 
1341 
1342  end_skip;
1343 
1344  return cpl_error_get_code();
1345 
1346 }
1347 
1348 /*----------------------------------------------------------------------------*/
1357 /*----------------------------------------------------------------------------*/
1358 static int visir_img_phot_flux_four(const cpl_image * combined)
1359 {
1360  cpl_errorstate cleanstate = cpl_errorstate_get();
1361  cpl_image * min_combined = NULL;
1362  cpl_apertures * appos = NULL;
1363  cpl_apertures * apneg = NULL;
1364  cpl_vector * sigmas = NULL;
1365  double psigmas[] = {5, 2, 1, 0.5};
1366  double x[4];
1367  double y[4];
1368  double flux_snr[4];
1369  double flux_snr_noise[4];
1370  int flux_snr_radius[4];
1371  double flux_tot[4];
1372  beamshape_t beamshapes[4];
1373  double dist1, dist2;
1374  int ngood_fwhm;
1375  double lam, dlam;
1376  double star_bg,star_peak,star_flux,psf_peak,psf_flux,bg_noise;
1377  double eccmin = DBL_MAX;
1378  const int nsigmas = sizeof(psigmas)/sizeof(double);
1379  int isigma;
1380  int ipos1, ipos2, ineg1, ineg2;
1381  int i;
1382  int iappos2[] = {0, 0}; /* Avoid (false) uninit warning */
1383  int iapneg2[] = {0, 0}; /* Avoid (false) uninit warning */
1384 
1385 
1386  if (cpl_error_get_code()) return cpl_error_get_code();
1387 
1388  cpl_ensure_code(combined != NULL, CPL_ERROR_NULL_INPUT);
1389 
1390  cpl_msg_info(cpl_func, "Detecting the 4-source star using %d sigma-levels "
1391  "ranging from %g down to %g", nsigmas, psigmas[0],
1392  psigmas[nsigmas-1]);
1393 
1394  sigmas = cpl_vector_new(1);
1395  min_combined = cpl_image_multiply_scalar_create(combined, -1.0);
1396  bug_if(0);
1397  for (isigma = 0; isigma < nsigmas; isigma++) {
1398 
1399  /* FIXME: Why ?! */
1400  irplib_error_recover(cleanstate, "Resetting error (why?)");
1401 
1402  bug_if(cpl_vector_set(sigmas, 0, psigmas[isigma]));
1403 
1404  /* Detect where the POSITIVE stars are */
1405  cpl_apertures_delete(appos);
1406  appos = cpl_apertures_extract(combined, sigmas, NULL);
1407  if (appos == NULL) {
1408  cpl_msg_warning(cpl_func, "Found no positive stars at sigma=%g",
1409  psigmas[isigma]);
1410  continue;
1411  }
1412  if (cpl_apertures_get_size(appos) < 2) {
1413  cpl_msg_warning(cpl_func, "Found just 1 positive star at sigma=%g, "
1414  "need two", psigmas[isigma]);
1415  continue;
1416  }
1417 
1418  /* Detect where the NEGATIVE stars are */
1419  cpl_apertures_delete(apneg);
1420  apneg = cpl_apertures_extract(min_combined, sigmas, NULL);
1421  if (apneg == NULL) {
1422  cpl_msg_warning(cpl_func, "Found no negative stars at sigma=%g",
1423  psigmas[isigma]);
1424  continue;
1425  }
1426  if (cpl_apertures_get_size(apneg) < 2) {
1427  cpl_msg_warning(cpl_func, "Found just 1 negative star at sigma=%g, "
1428  "need two", psigmas[isigma]);
1429  continue;
1430  }
1431 
1432  cpl_msg_info(cpl_func, "Found positive and negative stars at sigma=%g",
1433  psigmas[isigma]);
1434  break;
1435  }
1436 
1437  skip_if(appos == NULL);
1438  skip_if(apneg == NULL);
1439  skip_if (cpl_apertures_get_size(appos) < 2);
1440  skip_if (cpl_apertures_get_size(apneg) < 2);
1441 
1442  if (cpl_msg_get_level() == CPL_MSG_DEBUG) {
1443  cpl_apertures_dump(appos, stdout);
1444  cpl_apertures_dump(apneg, stdout);
1445  }
1446 
1447  if (cpl_apertures_get_size(appos) > 2 || cpl_apertures_get_size(apneg) > 2)
1448  cpl_msg_info(cpl_func, "Selecting from %d positive and %d negative "
1449  "stars two pairs that outline a square",
1450  (int)cpl_apertures_get_size(appos),
1451  (int)cpl_apertures_get_size(apneg));
1452 
1453  for (ipos1 = 2; ipos1 <= cpl_apertures_get_size(appos); ipos1++) {
1454  for (ipos2 = 1; ipos2 < ipos1; ipos2++) {
1455  for (ineg1 = 2; ineg1 <= cpl_apertures_get_size(apneg); ineg1++) {
1456  for (ineg2 = 1; ineg2 < ineg1; ineg2++) {
1457  const double ecc = visir_img_phot_eccent_four(appos, ipos1,
1458  ipos2, apneg,
1459  ineg1, ineg2);
1460 
1461  if (ecc < eccmin) {
1462  if (eccmin < DBL_MAX)
1463  cpl_msg_debug(cpl_func, "Found star positions with "
1464  "reduced non-square-ness [pixel]: "
1465  "%g < %g", ecc, eccmin);
1466  eccmin = ecc;
1467  iappos2[0] = ipos1;
1468  iappos2[1] = ipos2;
1469  iapneg2[0] = ineg1;
1470  iapneg2[1] = ineg2;
1471  }
1472  }
1473  }
1474  }
1475  }
1476 
1477  /* Star 1 should have largest flux */
1478  if (cpl_apertures_get_flux(appos, iappos2[0]) <
1479  cpl_apertures_get_flux(appos, iappos2[1])) {
1480  const int tmp = iappos2[0];
1481  iappos2[0] = iappos2[1];
1482  iappos2[1] = tmp;
1483  }
1484  if (cpl_apertures_get_flux(apneg, iapneg2[0]) <
1485  cpl_apertures_get_flux(apneg, iapneg2[1])) {
1486  const int tmp = iapneg2[0];
1487  iapneg2[0] = iapneg2[1];
1488  iapneg2[1] = tmp;
1489  }
1490 
1491 
1492  x[0] = cpl_apertures_get_centroid_x(appos, iappos2[0]);
1493  y[0] = cpl_apertures_get_centroid_y(appos, iappos2[0]);
1494  x[1] = cpl_apertures_get_centroid_x(appos, iappos2[1]);
1495  y[1] = cpl_apertures_get_centroid_y(appos, iappos2[1]);
1496 
1497  x[2] = cpl_apertures_get_centroid_x(apneg, iapneg2[0]);
1498  y[2] = cpl_apertures_get_centroid_y(apneg, iapneg2[0]);
1499  x[3] = cpl_apertures_get_centroid_x(apneg, iapneg2[1]);
1500  y[3] = cpl_apertures_get_centroid_y(apneg, iapneg2[1]);
1501 
1502  cpl_apertures_delete(appos);
1503  cpl_apertures_delete(apneg);
1504  appos = NULL;
1505  apneg = NULL;
1506 
1507  cpl_msg_info(cpl_func, "Positive star 1 at position %g %g", x[0], y[0]);
1508  cpl_msg_info(cpl_func, "Positive star 2 at position %g %g", x[1], y[1]);
1509 
1510  cpl_msg_info(cpl_func, "Negative star 1 at position %g %g", x[2], y[2]);
1511  cpl_msg_info(cpl_func, "Negative star 2 at position %g %g", x[3], y[3]);
1512 
1513  /* FIXME: Useful ? - change to 1D-distances - and move into sigma loop */
1514  dist1 = sqrt((x[2]-x[0])*(x[2]-x[0])+(y[2]-y[0])*(y[2]-y[0]));
1515  dist2 = sqrt((x[3]-x[1])*(x[3]-x[1])+(y[3]-y[1])*(y[3]-y[1]));
1516  cpl_msg_info(cpl_func, "Star 1 Pos/Neg Distance: %g", dist1);
1517  cpl_msg_info(cpl_func, "Star 2 Pos/Neg Distance: %g", dist2);
1518 
1519  if (eccmin < DBL_MAX) {
1520  cpl_msg_info(cpl_func, "The deviation from a horizontal square by "
1521  "the two star pairs [pixel]: %g", eccmin);
1522  if (eccmin > VISIR_IMG_PHOT_POS_WARN) {
1523  if (eccmin > VISIR_IMG_PHOT_POS_ERROR) {
1524  cpl_msg_error(cpl_func, "The deviation from a horizontal square"
1525  " by the two star pairs exceed %g, the detected "
1526  "objects are wrong", VISIR_IMG_PHOT_POS_ERROR);
1527  skip_if(1);
1528  }
1529  cpl_msg_warning(cpl_func, "The deviation from a horizontal square "
1530  "by the two star pairs exceed %g, the detected "
1531  "objects may be wrong", VISIR_IMG_PHOT_POS_WARN);
1532  }
1533  }
1534 
1535  if ((int)x[0]-IRPLIB_STREHL_BORDER <= 0 ||
1536  (int)y[0]-IRPLIB_STREHL_BORDER <= 0 ||
1537  (int)x[0]+IRPLIB_STREHL_BORDER > cpl_image_get_size_x(combined) ||
1538  (int)y[0]+IRPLIB_STREHL_BORDER > cpl_image_get_size_y(combined)) {
1539  cpl_msg_error(cpl_func, "Positive star 1 at (%g,%g) is less than %d "
1540  "pixels from the image border", x[0], y[0],
1541  1+IRPLIB_STREHL_BORDER);
1542  skip_if(1);
1543  }
1544 
1545  /* Verify the stars positions */
1546  if (fabs(dist1-dist2) > VISIR_IMG_PHOT_POS_UNCERTAINTY) {
1547  cpl_msg_error(cpl_func, "Too large Pos/Neg Distance between the two "
1548  "stars: %g > %g", fabs(dist1-dist2),
1549  VISIR_IMG_PHOT_POS_UNCERTAINTY);
1550  skip_if(1);
1551  }
1552 
1553  /* Photometry on positive stars */
1554  for (i=0 ; i<2 ; i++)
1555  skip_if (visir_img_phot_flux(combined, NULL, NULL,
1556  x[i], y[i],
1557  visir_img_phot_config.r0_max,
1558  visir_img_phot_config.r1,
1559  visir_img_phot_config.r2,
1560  &(flux_snr[i]),
1561  &(flux_snr_noise[i]),
1562  &(flux_snr_radius[i]),
1563  &(flux_tot[i]),
1564  &(beamshapes[i])));
1565 
1566  /* Photometry on negative stars */
1567  for (i=2 ; i<4 ; i++)
1568  skip_if (visir_img_phot_flux(min_combined, NULL, NULL,
1569  x[i], y[i],
1570  visir_img_phot_config.r0_max,
1571  visir_img_phot_config.r1,
1572  visir_img_phot_config.r2,
1573  &(flux_snr[i]),
1574  &(flux_snr_noise[i]),
1575  &(flux_snr_radius[i]),
1576  &(flux_tot[i]),
1577  &(beamshapes[i])));
1578 
1579  cpl_image_delete(min_combined);
1580  min_combined = NULL;
1581 
1582  /* Compute the results */
1583  /* Flux */
1584  visir_img_phot_config.flux_snr = 0.0;
1585  for (i=0 ; i<4 ; i++) visir_img_phot_config.flux_snr += flux_snr[i];
1586 
1587  /* Flux noise */
1588  visir_img_phot_config.flux_snr_noise = 0.0;
1589  for (i=0 ; i<4 ; i++) visir_img_phot_config.flux_snr_noise +=
1590  flux_snr_noise[i]*flux_snr_noise[i];
1591  visir_img_phot_config.flux_snr_noise =
1592  sqrt(visir_img_phot_config.flux_snr_noise);
1593 
1594  for (i=0 ; i<4 ; i++)
1595  visir_img_phot_config.flux_snr_radius[i] = flux_snr_radius[i];
1596 
1597  /* Total flux */
1598  visir_img_phot_config.flux_tot = 0.0;
1599  for (i=0 ; i<4 ; i++) visir_img_phot_config.flux_tot += flux_tot[i];
1600 
1601  /* FWHM */
1602  ngood_fwhm = 0;
1603  visir_img_phot_config.beamshape.fwhm_x = 0.0;
1604  for (i=0 ; i<4 ; i++) {
1605  if (beamshapes[i].fwhm_x > 0.0) {
1606  visir_img_phot_config.beamshape.fwhm_x += beamshapes[i].fwhm_x;
1607  ngood_fwhm ++;
1608  }
1609  }
1610  if (ngood_fwhm > 0) visir_img_phot_config.beamshape.fwhm_x /= ngood_fwhm;
1611  else visir_img_phot_config.beamshape.fwhm_x = -1.0;
1612  ngood_fwhm = 0;
1613  visir_img_phot_config.beamshape.fwhm_y = 0.0;
1614  for (i=0 ; i<4 ; i++) {
1615  if (beamshapes[i].fwhm_y > 0.0) {
1616  visir_img_phot_config.beamshape.fwhm_y += beamshapes[i].fwhm_y;
1617  ngood_fwhm ++;
1618  }
1619  }
1620  if (ngood_fwhm > 0) visir_img_phot_config.beamshape.fwhm_y /= ngood_fwhm;
1621  else visir_img_phot_config.beamshape.fwhm_y = -1.0;
1622  visir_img_phot_config.fwhm_x_pos1 = beamshapes[0].fwhm_x;
1623  visir_img_phot_config.fwhm_y_pos1 = beamshapes[0].fwhm_y;
1624  visir_img_phot_config.fwhm_x_pos2 = beamshapes[1].fwhm_x;
1625  visir_img_phot_config.fwhm_y_pos2 = beamshapes[1].fwhm_y;
1626  visir_img_phot_config.fwhm_x_neg1 = beamshapes[2].fwhm_x;
1627  visir_img_phot_config.fwhm_y_neg1 = beamshapes[2].fwhm_y;
1628  visir_img_phot_config.fwhm_x_neg2 = beamshapes[3].fwhm_x;
1629  visir_img_phot_config.fwhm_y_neg2 = beamshapes[3].fwhm_y;
1630 
1631  /* Get lam and dlam from the filter name for the Strehl computation */
1632  if (visir_get_filter_infos(visir_img_phot_config.filter, &lam, &dlam)) {
1633  cpl_msg_error(cpl_func, "Central wavelength and width is missing for "
1634  "filter: %s", visir_img_phot_config.filter);
1635  skip_if(1);
1636  }
1637 
1638  /* Strehl computation */
1639  cpl_errorstate errstate = cpl_errorstate_get();
1640  if (irplib_strehl_compute(combined, STREHL_M1, STREHL_M2, lam, dlam,
1641  visir_img_phot_config.pscale,
1642  STREHL_BOX_SIZE, x[0], y[0], STREHL_STAR_RADIUS,
1643  STREHL_BG_R1, STREHL_BG_R2, -1, -1,
1644  &(visir_img_phot_config.strehl),
1645  &(visir_img_phot_config.strehl_err),
1646  &star_bg, &star_peak, &star_flux, &psf_peak, &psf_flux,
1647  &bg_noise)) {
1648  cpl_msg_error(cpl_func, "Could not compute the strehl: '%s' in %s",
1649  cpl_error_get_message(), cpl_error_get_where());
1650  cpl_errorstate_set(errstate);
1651  }
1652 
1653 
1654  end_skip;
1655 
1656  cpl_apertures_delete(appos);
1657  cpl_apertures_delete(apneg);
1658  cpl_vector_delete(sigmas);
1659  cpl_image_delete(min_combined);
1660 
1661  return cpl_error_get_code();
1662 
1663 }
1664 
1665 
1666 /*----------------------------------------------------------------------------*/
1682 /*----------------------------------------------------------------------------*/
1683 static double visir_img_phot_eccent_four(const cpl_apertures * appos,
1684  int ipos1, int ipos2,
1685  const cpl_apertures * apneg,
1686  int ineg1, int ineg2)
1687 {
1688 
1689  /* NB: Lower left pixel is (1, 1) */
1690 
1691  /* The two positive points */
1692  const double xp1 = cpl_apertures_get_centroid_x(appos, ipos1);
1693  const double xp2 = cpl_apertures_get_centroid_x(appos, ipos2);
1694  const double yp1 = cpl_apertures_get_centroid_y(appos, ipos1);
1695  const double yp2 = cpl_apertures_get_centroid_y(appos, ipos2);
1696 
1697  /* The leftmost positive point */
1698  const double xpl = xp1 < xp2 ? xp1 : xp2;
1699  const double ypl = xp1 < xp2 ? yp1 : yp2;
1700 
1701  /* The rightmost positive point */
1702  const double xpr = xp1 < xp2 ? xp2 : xp1;
1703  const double ypr = xp1 < xp2 ? yp2 : yp1;
1704 
1705  /* The two negative points */
1706  const double xn1 = cpl_apertures_get_centroid_x(apneg, ineg1);
1707  const double yn1 = cpl_apertures_get_centroid_y(apneg, ineg1);
1708  const double xn2 = cpl_apertures_get_centroid_x(apneg, ineg2);
1709  const double yn2 = cpl_apertures_get_centroid_y(apneg, ineg2);
1710 
1711  /* The leftmost negative point */
1712  const double xln = xn1 < xn2 ? xn1 : xn2;
1713  const double yln = xn1 < xn2 ? yn1 : yn2;
1714 
1715  /* The rightmost engative point */
1716  const double xrn = xn1 < xn2 ? xn2 : xn1;
1717  const double yrn = xn1 < xn2 ? yn2 : yn1;
1718 
1719  const double lx1 = xrn - xpl; /* The length of the top x-side */
1720  const double lx2 = xpr - xln; /* The length of the bottom x-side */
1721  const double ly1 = ypl - yln; /* The length of the left y-side */
1722  const double ly2 = yrn - ypr; /* The length of the right y-side */
1723 
1724  const double lmean = 0.25 * ( lx1 + lx2 + ly1 + ly2);
1725 
1726  const double dx1 = lx1 - lmean;
1727  const double dx2 = lx2 - lmean;
1728  const double dy1 = ly1 - lmean;
1729  const double dy2 = ly2 - lmean;
1730 
1731  const double ey1 = yrn - ypl; /* The displacement in the top x-side */
1732  const double ey2 = ypr - yln; /* The displacement in the bottom x-side */
1733  const double ex1 = xpl - xln; /* The displacement in the left y-side */
1734  const double ex2 = xpr - xrn; /* The displacement in the right y-side */
1735 
1736  const double result = sqrt(dx1 * dx1 + dx2 * dx2 + dy1 * dy1 + dy2 * dy2 +
1737  ex1 * ex1 + ex2 * ex2 + ey1 * ey1 + ey2 * ey2);
1738 
1739 
1740  bug_if(0);
1741 
1742  bug_if(appos == apneg);
1743  bug_if(ipos1 == ipos2);
1744  bug_if(ineg1 == ineg2);
1745 
1746  end_skip;
1747 
1748  return result;
1749 
1750 
1751 }
1752 
1753 #ifdef VISIR_IMG_PHOT_USE_ECCENT_THREE
1754 /*----------------------------------------------------------------------------*/
1769 /*----------------------------------------------------------------------------*/
1770 static double visir_img_phot_eccent_three(const cpl_apertures * appos,
1771  int ipos,
1772  const cpl_apertures * apneg,
1773  int ineg1, int ineg2)
1774 {
1775 
1776  /* NB: Lower left pixel is (1, 1) */
1777 
1778  /* The positive point */
1779  const double xp = cpl_apertures_get_centroid_x(appos, ipos);
1780  const double yp = cpl_apertures_get_centroid_y(appos, ipos);
1781 
1782  /* The two negative points */
1783  const double xn1 = cpl_apertures_get_centroid_x(apneg, ineg1);
1784  const double yn1 = cpl_apertures_get_centroid_y(apneg, ineg1);
1785  const double xn2 = cpl_apertures_get_centroid_x(apneg, ineg2);
1786  const double yn2 = cpl_apertures_get_centroid_y(apneg, ineg2);
1787 
1788  /* The bottom negative point */
1789  const double xnb = xn1 < xn2 ? xn1 : xn2;
1790  const double ynb = xn1 < xn2 ? yn1 : yn2;
1791 
1792  /* The top engative point */
1793  const double xnt = xn1 < xn2 ? xn2 : xn1;
1794  const double ynt = xn1 < xn2 ? yn2 : yn1;
1795 
1796  const double l1 = ynt - yp; /* The length of the top line */
1797  const double l2 = yp - ynb; /* The length of the bottom line */
1798  const double ln = ynt - ynb; /* The length of the top to bottom line */
1799 
1800  const double lmean = 0.25 * ( l1 + l2 + ln);
1801 
1802  const double d1 = l1 - lmean;
1803  const double d2 = l2 - lmean;
1804  const double dn = 0.5 * ln - lmean;
1805 
1806  const double e1 = xp - xnt; /* The displacement in the top line */
1807  const double e2 = xp - xnb; /* The displacement in the bottom line */
1808 
1809  const double result = sqrt(d1 * d1 + d2 * d2 + dn * dn + e1 * e1 + e2 * e2);
1810 
1811 
1812  bug_if(0);
1813 
1814  bug_if(appos == apneg);
1815  bug_if(ineg1 == ineg2);
1816 
1817  end_skip;
1818 
1819  return result;
1820 
1821 
1822 }
1823 
1824 #endif
1825 
1826 /*----------------------------------------------------------------------------*/
1848 /*----------------------------------------------------------------------------*/
1849 static int visir_img_phot_flux(
1850  const cpl_image * combined,
1851  const cpl_image * weights_,
1852  const cpl_image * contrib,
1853  double x_pos,
1854  double y_pos,
1855  int r0_max,
1856  int r1,
1857  int r2,
1858  double * flux_snr,
1859  double * flux_snr_noise,
1860  int * flux_snr_radius,
1861  double * flux_tot,
1862  beamshape_t * beamshape)
1863 {
1864  cpl_apertures * apert = NULL;
1865  cpl_image * labels;
1866  cpl_image * bg_subtracted = NULL;
1867  cpl_vector * r0 = NULL;
1868  cpl_vector * fl = NULL;
1869  cpl_vector * fl_noise = NULL;
1870  cpl_vector * snr = NULL;
1871  double bg;
1872  double max_val = 0.0; /* Avoid (false) uninit warning */
1873  int max_ind = 0; /* Avoid (false) uninit warning */
1874  int i;
1875  cpl_size nx, ny;
1876  int max_contrib = 1;
1877  cpl_image * weights = weights_ == NULL ?
1878  NULL : cpl_image_duplicate(weights_);
1879 
1880 
1881  if (cpl_error_get_code()) return cpl_error_get_code();
1882 
1883  cpl_ensure_code(combined != NULL, CPL_ERROR_NULL_INPUT);
1884  cpl_ensure_code(r0_max > 0, CPL_ERROR_ILLEGAL_INPUT);
1885 
1886  nx = cpl_image_get_size_x(combined);
1887  ny = cpl_image_get_size_y(combined);
1888 
1889  if (weights) {
1890  cpl_ensure_code(nx == cpl_image_get_size_x(weights) &&
1891  ny == cpl_image_get_size_y(weights),
1892  CPL_ERROR_ILLEGAL_INPUT);
1893  }
1894 
1895  if (contrib) {
1896  cpl_ensure_code(nx == cpl_image_get_size_x(contrib) &&
1897  ny == cpl_image_get_size_y(contrib),
1898  CPL_ERROR_ILLEGAL_INPUT);
1899  max_contrib = cpl_image_get_max(contrib);
1900  cpl_ensure_code(max_contrib >= 1, CPL_ERROR_ILLEGAL_INPUT);
1901  }
1902 
1903  /* Create the label image defining the background ring */
1904  if ((labels = visir_create_ring_intimage(nx, ny,
1905  (int)x_pos, (int)y_pos, r1, r2)) == NULL) {
1906  cpl_msg_error(cpl_func, "Could not create a ring image");
1907  skip_if(1);
1908  }
1909  /* Compute the background */
1910  apert = cpl_apertures_new_from_image(combined, labels);
1911  cpl_image_delete(labels);
1912  labels = NULL;
1913  bg = cpl_apertures_get_median(apert, 1);
1914  cpl_apertures_delete(apert);
1915  apert = NULL;
1916  cpl_msg_info(cpl_func, "Background : %g", bg);
1917 
1918  /* Create the label image defining the total star disk */
1919  if ((labels = visir_create_disk_intimage(nx, ny,
1920  (int)x_pos, (int)y_pos, r0_max)) == NULL) {
1921  cpl_msg_error(cpl_func, "Could not create a disk image");
1922  skip_if(1);
1923  }
1924 
1925  /* Compute the total flux and the associated error */
1926 
1927  bg_subtracted = cpl_image_subtract_scalar_create(combined, bg);
1928 
1929  /* due to bad pixels rescale the sum to equal contribution */
1930  if (contrib) {
1931  cpl_image * scale = cpl_image_cast(contrib, CPL_TYPE_DOUBLE);
1932  cpl_image_power(scale, -1);
1933  cpl_image_multiply_scalar(scale, max_contrib);
1934  cpl_image_multiply(bg_subtracted, scale);
1935  if (weights)
1936  cpl_image_multiply(weights, scale);
1937  cpl_image_delete(scale);
1938  }
1939 
1940  apert = cpl_apertures_new_from_image(bg_subtracted, labels);
1941  cpl_image_delete(labels);
1942  labels = NULL;
1943  *flux_tot = cpl_apertures_get_flux(apert, 1);
1944  cpl_apertures_delete(apert);
1945  apert = NULL;
1946  cpl_msg_info(cpl_func, "Star total flux (error): %g", *flux_tot);
1947 
1948  /* Create and fill r0 */
1949  r0 = cpl_vector_new(r0_max);
1950  for (i=0 ; i<r0_max ; i++) cpl_vector_set(r0, i, i+1);
1951 
1952  /* Create fl, fl_noise */
1953  fl = cpl_vector_new(r0_max);
1954  fl_noise = cpl_vector_new(r0_max);
1955 
1956  /* For each radius, compute fl and fl_noise */
1957  for (i=0 ; i<r0_max ; i++) {
1958  float fl_val = 0.f, fl_noise_val = 0.f;
1959  /* Create the label image defining the current star disk */
1960  if ((labels = visir_create_disk_intimage(nx, ny,
1961  (int)x_pos, (int)y_pos, (int)cpl_vector_get(r0, i))) == NULL) {
1962  cpl_msg_error(cpl_func, "Could not create a disk image: %d", i);
1963  break;
1964  }
1965 
1966  if (weights) {
1967  double sum = 0, esum = 0;
1968  int d;
1969  for (int ix = 1; ix < 1 + nx; ix++) {
1970  for (int iy = 1; iy < 1 + ny; iy++) {
1971  if (!cpl_image_get(labels, ix, iy, &d) || d)
1972  continue;
1973 
1974  sum += cpl_image_get(bg_subtracted, ix, iy, &d);
1975  /* assume gaussian error propagation, e = sqrt(1/w)
1976  * => e = sqrt(sum_i(1/w_i)) */
1977  esum += (1. / cpl_image_get(weights, ix, iy, &d));
1978  }
1979  }
1980  fl_val = sum;
1981  fl_noise_val = sqrt(esum);
1982  }
1983  else {
1984  cpl_apertures * pert =
1985  cpl_apertures_new_from_image(bg_subtracted, labels);
1986  /* FIXME: pert == NULL ? */
1987  fl_val = cpl_apertures_get_flux(pert, 1);
1988  fl_noise_val = visir_img_phot_config.bg_sigma *
1989  sqrt((double)cpl_apertures_get_npix(pert, 1));
1990  cpl_apertures_delete(pert);
1991  }
1992 
1993  /* Compute the statistics on the zone defined by the labels */
1994  cpl_image_delete(labels);
1995  labels = NULL;
1996  cpl_vector_set(fl, i, fl_val);
1997  cpl_vector_set(fl_noise, i, fl_noise_val);
1998  }
1999  skip_if( 0 );
2000 
2001  /* Compute the flux (and error) for the best signal to noise */
2002  snr = cpl_vector_duplicate(fl);
2003  cpl_vector_divide(snr, fl_noise);
2004  for (i=0 ; i<r0_max ; i++) {
2005  if (i == 0 || max_val < cpl_vector_get(snr, i)) {
2006  max_val = cpl_vector_get(snr, i);
2007  max_ind = i;
2008  }
2009  }
2010  *flux_snr = cpl_vector_get(fl, max_ind);
2011  *flux_snr_noise = cpl_vector_get(fl_noise, max_ind);
2012  *flux_snr_radius = (int)cpl_vector_get(r0, max_ind);
2013  cpl_msg_info(cpl_func, "Best SNR star flux : %g (%g) for radius %d <= %d "
2014  " [pixel]", *flux_snr, *flux_snr_noise,
2015  (int)cpl_vector_get(r0, max_ind),
2016  (int)cpl_vector_get(r0, r0_max-1));
2017 
2018  if (cpl_msg_get_level() <= CPL_MSG_DEBUG) {
2019  cpl_matrix * mdump = cpl_matrix_new(1+r0_max, 3);
2020  for (i = 0; i < r0_max; i++) {
2021  cpl_matrix_set(mdump, i+1, 0, cpl_vector_get(fl, i));
2022  cpl_matrix_set(mdump, i+1, 1, cpl_vector_get(fl_noise, i));
2023  cpl_matrix_set(mdump, i+1, 2, cpl_vector_get(snr, i));
2024  }
2025  cpl_msg_debug(cpl_func, "Flux, flux-noise and S/N for radii out to %d",
2026  (int)cpl_vector_get(r0, r0_max-1));
2027 
2028  cpl_matrix_dump(mdump, stdout);
2029  cpl_matrix_delete(mdump);
2030  }
2031 
2032  *beamshape = (beamshape_t){.fwhm_x = -1., .fwhm_y = -1.,
2033  .peak = -1., .peak_err = 0.,
2034  .major = -1., .major_err = 0.,
2035  .minor = -1., .minor_err = 0.,
2036  .angle = -1., .angle_err = 0.};
2037 
2038  /* Compute the FWHM */
2039  skip_if (cpl_image_get_fwhm(bg_subtracted, (int)x_pos, (int)y_pos, &beamshape->fwhm_x,
2040  &beamshape->fwhm_y));
2041 
2042  skip_if ( beamshape->fwhm_x <= 0.0 );
2043  skip_if ( beamshape->fwhm_y <= 0.0 );
2044 
2045  cpl_errorstate cleanstate = cpl_errorstate_get();
2046 
2047  if (fit_2d_gauss(bg_subtracted, weights, (cpl_size)x_pos, (cpl_size)y_pos,
2048  beamshape->fwhm_x, beamshape->fwhm_y,
2049  &beamshape->peak, &beamshape->peak_err,
2050  &beamshape->major, &beamshape->major_err,
2051  &beamshape->minor, &beamshape->minor_err,
2052  &beamshape->angle, &beamshape->angle_err) == CPL_ERROR_NONE) {
2053  cpl_msg_info(cpl_func, "Peak: %g +- %g, FWHM : %g +- %g major ; %g +- %g minor, "
2054  "angle %g +- %g", beamshape->peak, beamshape->peak_err,
2055  beamshape->major, beamshape->major_err,
2056  beamshape->minor, beamshape->minor_err, beamshape->angle *
2057  CPL_MATH_DEG_RAD, beamshape->angle_err *
2058  CPL_MATH_DEG_RAD);
2059  }
2060  else {
2061  cpl_msg_warning(cpl_func, "2D gauss fit failed, approximate FWHM : %g"
2062  "in x ; %g in y ", beamshape->fwhm_x, beamshape->fwhm_y);
2063  cpl_errorstate_set(cleanstate);
2064  }
2065 
2066  end_skip;
2067 
2068  cpl_apertures_delete(apert);
2069  cpl_image_delete(labels);
2070  cpl_image_delete(bg_subtracted);
2071  cpl_image_delete(weights);
2072 
2073  cpl_vector_delete(r0);
2074  cpl_vector_delete(fl);
2075  cpl_vector_delete(snr);
2076  cpl_vector_delete(fl_noise);
2077 
2078  return cpl_error_get_code();
2079 }
2080 
2081 
2082 /*----------------------------------------------------------------------------*/
2093 /*----------------------------------------------------------------------------*/
2094 static cpl_error_code visir_get_filter_infos(
2095  const char * f,
2096  double * pcwlen,
2097  double * pdwlen)
2098 {
2099 
2100  double cwlen = -1;
2101  double dwlen = -1;
2102 
2103  cpl_ensure_code(f, CPL_ERROR_NULL_INPUT);
2104  cpl_ensure_code(pcwlen, CPL_ERROR_NULL_INPUT);
2105  cpl_ensure_code(pdwlen, CPL_ERROR_NULL_INPUT);
2106 
2107  skip_if (!strcmp(f, "MV"));
2108 
2109  if (!strcmp(f, "N-BAND") || !strcmp(f, "N_BAND"))
2110  { cwlen = 10.56; dwlen = 5.37;}
2111  else if (!strcmp(f, "SIC")) { cwlen = 11.848;dwlen = 2.34;}
2112  else if (!strcmp(f, "PAH1_1")) { cwlen = 8.19;dwlen = 0.15;}
2113  else if (!strcmp(f, "PAH1")) { cwlen = 8.586;dwlen = 0.421;}
2114  else if (!strcmp(f, "ARIII")) { cwlen = 8.992;dwlen = 0.138;}
2115  else if (!strcmp(f, "SIV_1")) { cwlen = 10.02;dwlen = 0.18;}
2116  else if (!strcmp(f, "SIV")) { cwlen = 10.485;dwlen = 0.159;}
2117  else if (!strcmp(f, "PAH2_1")) { cwlen = 10.76;dwlen = 0.69;}
2118  else if (!strcmp(f, "SIV_2")) { cwlen = 11.1;dwlen = 0.19;}
2119  else if (!strcmp(f, "PAH2")) { cwlen = 11.254;dwlen = 0.594;}
2120  else if (!strcmp(f, "PAH2_2")) { cwlen = 12.13;dwlen = 0.37;}
2121  else if (!strcmp(f, "NEII_1")) { cwlen = 12.51;dwlen = 0.18;}
2122  else if (!strcmp(f, "NEII")) { cwlen = 12.805;dwlen = 0.21;}
2123  else if (!strcmp(f, "NEII_2")) { cwlen = 13.036;dwlen = 0.219;}
2124  else if (!strcmp(f, "Q0")) { cwlen = 16.554;dwlen = 0.398;}
2125  else if (!strcmp(f, "QH2")) { cwlen = 17.11;dwlen = 0.398;}
2126  else if (!strcmp(f, "Q1")) { cwlen = 17.653;dwlen = 0.83;}
2127  else if (!strcmp(f, "Q2")) { cwlen = 18.718;dwlen = 0.878;}
2128  else if (!strcmp(f, "Q3")) { cwlen = 19.5;dwlen = 0.4;}
2129  else if (!strcmp(f, "Q4")) { cwlen = 20.5;dwlen = 1.0;}
2130  else if (!strcmp(f, "Q7")) { cwlen = 23.1;dwlen = 0.8;}
2131  else if (!strcmp(f, "Q8")) { cwlen = 24.5;dwlen = 0.8;}
2132  /* TODO missing bandwidth for coro filters not determined yet */
2133  else if (!strcmp(f, "10_5_4QP")) { cwlen = 10.5;dwlen =0.1;}
2134  else if (!strcmp(f, "11_3_4QP")) { cwlen = 11.3;dwlen =0.1;}
2135  else if (!strcmp(f, "12_4_AGP")) { cwlen = 12.4;dwlen =0.1;}
2136  else if (!strcmp(f, "N-SW-spec") || !strcmp(f, "N_SW_spec"))
2137  { cwlen = 8.85; dwlen = 2.7;}
2138  else if (!strcmp(f, "H2S4-spec") || !strcmp(f, "H2S4_spec"))
2139  { cwlen = 8.12; dwlen = 0.3;}
2140  else if (!strcmp(f, "ARIII-spec") || !strcmp(f, "ARIII_spec"))
2141  { cwlen = 8.44; dwlen = 0.78;}
2142  else if (!strcmp(f, "NEII_2-spec") || !strcmp(f, "NEII_2_spec"))
2143  { cwlen = 12.805; dwlen = 0.2;}
2144  else if (!strcmp(f, "H2S3-spec") || !strcmp(f, "H2S3_spec"))
2145  { cwlen = 9.62; dwlen = 0.2;}
2146  else if (!strcmp(f, "H2S1-spec") || !strcmp(f, "H2S1_spec"))
2147  { cwlen = 17.0;dwlen = 0.4;}
2148  else if (!strcmp(f, "M-BAND") || !strcmp(f, "M_BAND"))
2149  { cwlen = 4.82;dwlen = 0.35;}
2150 
2151  /* The width of each new, below filter is the Full Width at Half Maximum */
2152 
2153  else if (!strcmp(f, "J7.9") || !strcmp(f, "J7_9"))
2154  { cwlen = (7.483 + 8.035)/2.0;
2155  dwlen = 8.035 - 7.483;}
2156  else if (!strcmp(f, "J8.9") || !strcmp(f, "J8_9"))
2157  { cwlen = (8.338 + 9.068)/2.0;
2158  dwlen = 9.068 - 8.338;}
2159  else if (!strcmp(f, "J9.8") || !strcmp(f, "J9_8"))
2160  { cwlen = (9.123 + 10.059)/2.0;
2161  dwlen = 10.059 - 9.123;}
2162  else if (!strcmp(f, "J12.2") || !strcmp(f, "J12_2"))
2163  { cwlen = (11.700 + 12.216)/2.0;
2164  dwlen = 12.216 - 11.700;}
2165  else if (!strcmp(f, "B8.7") || !strcmp(f, "B8_7"))
2166  { cwlen = (8.436 + 9.410)/2.0;
2167  dwlen = 9.410 - 8.436;}
2168  else if (!strcmp(f, "B9.7") || !strcmp(f, "B9_7"))
2169  { cwlen = (9.402 + 10.242)/2.0;
2170  dwlen = 10.242 - 9.402;}
2171  else if (!strcmp(f, "B10.7") || !strcmp(f, "B10_7"))
2172  { cwlen = (9.970 + 11.338)/2.0;
2173  dwlen = 11.338 - 9.970;}
2174  else if (!strcmp(f, "B11.7") || !strcmp(f, "B11_7"))
2175  { cwlen = (11.098 + 11.950)/2.0;
2176  dwlen = 11.950 - 11.098;}
2177  else if (!strcmp(f, "B12.4") || !strcmp(f, "B12_4"))
2178  { cwlen = (11.971 + 12.961)/2.0;
2179  dwlen = 12.961 - 11.971;}
2180 
2181  *pcwlen = cwlen;
2182  *pdwlen = dwlen;
2183 
2184  cpl_ensure_code(cwlen > 0, CPL_ERROR_DATA_NOT_FOUND);
2185  cpl_ensure_code(dwlen > 0, CPL_ERROR_DATA_NOT_FOUND);
2186 
2187  end_skip;
2188 
2189  return cpl_error_get_code();
2190 }
2191 
2192 /*----------------------------------------------------------------------------*/
2200 /*----------------------------------------------------------------------------*/
2201 static cpl_error_code visir_img_phot_qc(cpl_propertylist * qclist,
2202  cpl_boolean drop_wcs,
2203  const irplib_framelist * rawframes)
2204 {
2205 
2206  const cpl_propertylist * reflist
2208 
2209 
2210  /* QC.EXPTIME */
2211  bug_if (cpl_propertylist_append_double(qclist, "ESO QC EXPTIME",
2212  visir_img_phot_config.exptime));
2213  /* QC.JYVAL */
2214  bug_if (cpl_propertylist_append_double(qclist, "ESO QC JYVAL",
2215  visir_img_phot_config.jy_val));
2216  /* QC.STARNAME */
2217  bug_if (cpl_propertylist_append_string(qclist, "ESO QC STARNAME",
2218  visir_img_phot_config.star_name));
2219  /* QC.FILTER */
2220  bug_if (cpl_propertylist_append_string(qclist, "ESO QC FILTER",
2221  visir_img_phot_config.filter));
2222 
2223  /* QC.NELEC */
2224  bug_if (cpl_propertylist_append_double(qclist, "ESO QC NELEC",
2225  visir_img_phot_config.nelec));
2226  /* QC.NPHOT */
2227  bug_if (cpl_propertylist_append_double(qclist, "ESO QC NPHOT",
2228  visir_img_phot_config.nphot));
2229  /* QC.BACKGD.SIGMA */
2230  bug_if (cpl_propertylist_append_double(qclist, "ESO QC BACKGD SIGMA",
2231  visir_img_phot_config.bg_sigma));
2232  /* QC.FLUXTOT */
2233  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FLUXTOT",
2234  visir_img_phot_config.flux_tot));
2235  /* QC.FLUXSNR */
2236  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FLUXSNR",
2237  visir_img_phot_config.flux_snr));
2238  /* QC.FLUXSNR.NOISE */
2239  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FLUXSNR NOISE",
2240  visir_img_phot_config.flux_snr_noise));
2241  /* QC.FLUXSNR.RADIUS */
2242  for (int i = 0; i < 4; i++) {
2243  const int r = visir_img_phot_config.flux_snr_radius[i];
2244  char buffer[100];
2245  sprintf(buffer, "ESO QC FLUXSNR RADIUS%d", i + 1);
2246  bug_if (cpl_propertylist_append_int(qclist, buffer, r));
2247  }
2248  /* QC.FWHMX */
2249  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMX",
2250  visir_img_phot_config.beamshape.fwhm_x));
2251  /* QC.FWHMY */
2252  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMY",
2253  visir_img_phot_config.beamshape.fwhm_y));
2254  bug_if (cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT FWHM_MAX",
2255  visir_img_phot_config.beamshape.major));
2256  cpl_propertylist_set_comment(qclist, "ESO QC GAUSSFIT FWHM_MAX",
2257  "major axis [pix]");
2258  bug_if (cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT FWHM_MAX_ERR",
2259  visir_img_phot_config.beamshape.major_err));
2260  bug_if (cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT FWHM_MIN",
2261  visir_img_phot_config.beamshape.minor));
2262  cpl_propertylist_set_comment(qclist, "ESO QC GAUSSFIT FWHM_MIN",
2263  "minor axis [pix]");
2264  bug_if (cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT FWHM_MIN_ERR",
2265  visir_img_phot_config.beamshape.minor_err));
2266  bug_if (cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT ANGLE",
2267  visir_img_phot_config.beamshape.angle * CPL_MATH_DEG_RAD));
2268  cpl_propertylist_set_comment(qclist, "ESO QC GAUSSFIT ANGLE",
2269  "[deg]");
2270  bug_if (cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT ANGLE_ERR",
2271  visir_img_phot_config.beamshape.angle_err * CPL_MATH_DEG_RAD));
2272  bug_if (cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT PEAK",
2273  visir_img_phot_config.beamshape.peak));
2274  bug_if (cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT PEAK_ERR",
2275  visir_img_phot_config.beamshape.peak_err));
2276  /* QC.FWHMX.POS1 */
2277  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMX POS1",
2278  visir_img_phot_config.fwhm_x_pos1));
2279  /* QC.FWHMY.POS1 */
2280  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMY POS1",
2281  visir_img_phot_config.fwhm_y_pos1));
2282  /* QC.FWHMX.POS2 */
2283  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMX POS2",
2284  visir_img_phot_config.fwhm_x_pos2));
2285  /* QC.FWHMY.POS2 */
2286  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMY POS2",
2287  visir_img_phot_config.fwhm_y_pos2));
2288  /* QC.FWHMX.NEG1 */
2289  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMX NEG1",
2290  visir_img_phot_config.fwhm_x_neg1));
2291  /* QC.FWHMY.NEG1 */
2292  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMY NEG1",
2293  visir_img_phot_config.fwhm_y_neg1));
2294  /* QC.FWHMX.NEG2 */
2295  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMX NEG2",
2296  visir_img_phot_config.fwhm_x_neg2));
2297  /* QC.FWHMY.NEG2 */
2298  bug_if (cpl_propertylist_append_double(qclist, "ESO QC FWHMY NEG2",
2299  visir_img_phot_config.fwhm_y_neg2));
2300  /* QC.SENSIT */
2301  bug_if (cpl_propertylist_append_double(qclist, "ESO QC SENSIT",
2302  visir_img_phot_config.sensitivity));
2303  /* QC.AREASENSIT */
2304  bug_if (cpl_propertylist_append_double(qclist, "ESO QC AREA SENSIT",
2305  visir_img_phot_config.area_sensit));
2306  cpl_propertylist_set_comment(qclist, "ESO QC AREA SENSIT",
2307  "scaled to noise of 1 arcsecond");
2308  /* QC.CONVER */
2309  bug_if (cpl_propertylist_append_double(qclist, "ESO QC CONVER",
2310  visir_img_phot_config.conversion));
2311  /* QC.STREHL */
2312  bug_if (cpl_propertylist_append_double(qclist, "ESO QC STREHL",
2313  visir_img_phot_config.strehl));
2314  /* QC.STREHL.ERROR */
2315  bug_if (cpl_propertylist_append_double(qclist, "ESO QC STREHL ERROR",
2316  visir_img_phot_config.strehl_err));
2317 
2318  /* QC.CAPA */
2319  skip_if (visir_qc_append_capa(qclist, rawframes));
2320 
2321  if (drop_wcs) {
2322  cpl_propertylist * pcopy = cpl_propertylist_new();
2323  const cpl_error_code error
2324  = cpl_propertylist_copy_property_regexp(pcopy, reflist, "^("
2325  IRPLIB_PFITS_WCS_REGEXP
2326  ")$", 0);
2327  if (!error && cpl_propertylist_get_size(pcopy) > 0) {
2328  cpl_msg_warning(cpl_func, "Combined image will have no WCS "
2329  "coordinates");
2330  }
2331  cpl_propertylist_delete(pcopy);
2332  bug_if(0);
2333 
2334  bug_if(cpl_propertylist_copy_property_regexp(qclist, reflist, "^("
2335  VISIR_PFITS_IMG_PHOT_COPY
2336  ")$", 0));
2337  } else {
2338  bug_if(cpl_propertylist_copy_property_regexp(qclist, reflist, "^("
2339  VISIR_PFITS_IMG_PHOT_COPY
2340  "|" IRPLIB_PFITS_WCS_REGEXP
2341  ")$", 0));
2342  }
2343 
2344  bug_if (irplib_pfits_set_airmass(qclist, rawframes));
2345 
2346  end_skip;
2347 
2348  return cpl_error_get_code();
2349 
2350 }
2351 
2352 /*----------------------------------------------------------------------------*/
2364 /*----------------------------------------------------------------------------*/
2365 static cpl_error_code visir_img_phot_save(cpl_frameset * self,
2366  const cpl_parameterlist * parlist,
2367  const cpl_propertylist * qclist,
2368  const cpl_image * combined,
2369  const cpl_image * contrib,
2370  const cpl_image * beam1,
2371  const cpl_image * beam1i,
2372  const cpl_image * weights)
2373 {
2374 
2375  cpl_propertylist * xtlist = cpl_propertylist_new();
2376  const char * procatg = VISIR_IMG_PHOT_COMBINED_PROCATG;
2377 
2378  bug_if (0);
2379 
2380  if (weights) {
2381  const char * s = cpl_propertylist_get_string(qclist, "ESO QC BEAMID");
2382  bug_if(s == NULL);
2383 
2384  if (strcmp(s, "COMBINED") == 0)
2385  procatg = VISIR_IMG_PHOT_COMBINED_PROCATG;
2386  else
2387  procatg = VISIR_IMG_PHOT_ONEBEAM_PROCATG;
2388  }
2389 
2390  /* SAVE THE COMBINED IMAGE */
2391  skip_if (irplib_dfs_save_image(self, parlist, self, combined,
2392  CPL_BPP_IEEE_FLOAT, RECIPE_SAVE_STRING,
2393  procatg, qclist, NULL, visir_pipe_id,
2394  RECIPE_SAVE_STRING CPL_DFS_FITS));
2395 
2396  if (!weights) {
2397  /* THE CONTRIBUTION MAP */
2398  bug_if(cpl_propertylist_append_string(xtlist, "EXTNAME",
2399  "Contribution Map"));
2400  skip_if (cpl_image_save(contrib, RECIPE_SAVE_STRING CPL_DFS_FITS,
2401  CPL_BPP_16_UNSIGNED, xtlist, CPL_IO_EXTEND));
2402  }
2403 
2404  if (beam1 != NULL) {
2405  bug_if(weights);
2406  /* THE BEAM COLLAPSED IMAGE */
2407  skip_if (irplib_dfs_save_image(self, parlist, self, beam1,
2408  CPL_BPP_IEEE_FLOAT, RECIPE_SAVE_STRING,
2409  VISIR_IMG_PHOT_ONEBEAM_PROCATG, qclist,
2410  NULL, visir_pipe_id,
2411  RECIPE_SAVE_STRING "_onebeam" CPL_DFS_FITS));
2412 
2413  /* THE BEAM COLLAPSED CONTRIBUTION MAP */
2414  skip_if (cpl_image_save(beam1i, RECIPE_SAVE_STRING "_onebeam" CPL_DFS_FITS,
2415  CPL_BPP_8_UNSIGNED, xtlist, CPL_IO_EXTEND));
2416  }
2417 
2418  end_skip;
2419 
2420  cpl_propertylist_delete(xtlist);
2421 
2422  return cpl_error_get_code();
2423 
2424 }
2425 
2426 /*----------------------------------------------------------------------------*/
2436 /*----------------------------------------------------------------------------*/
2437 static char * visir_img_phot_filter2label(const char * self)
2438 {
2439 
2440  char * label = NULL;
2441  char * p;
2442 
2443  bug_if(self == NULL);
2444 
2445  label = cpl_strdup(self);
2446 
2447  for (p = label; *p != (char)0; p++) {
2448  if (*p == '.' || *p == '-') *p = '_';
2449  }
2450 
2451  end_skip;
2452 
2453  return label;
2454 
2455 }
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.
double visir_pfits_get_dec(const cpl_propertylist *self)
The DEC.
Definition: visir_pfits.c:297
cpl_error_code visir_dfs_check_framelist_tag(const irplib_framelist *self)
Check the tags in a frameset (group raw only)
Definition: visir_dfs.c:218
cpl_error_code irplib_apertures_find_max_flux(const cpl_apertures *self, int *ind, int nfind)
Find the aperture(s) with the greatest flux.
const char * visir_pfits_get_starname(const cpl_propertylist *self)
The std star name.
Definition: visir_pfits.c:775
cpl_error_code irplib_pfits_set_airmass(cpl_propertylist *self, const irplib_framelist *rawframes)
Update/Set the AIRMASS property.
Definition: irplib_pfits.c:338
const char * visir_pfits_get_filter(const cpl_propertylist *self)
The filter.
Definition: visir_pfits.c:349
const cpl_propertylist * irplib_framelist_get_propertylist_const(const irplib_framelist *self, int pos)
Get the propertylist of the specified frame in the framelist.
void irplib_framelist_empty(irplib_framelist *self)
Erase all frames from a framelist.
cpl_image ** visir_img_collapse_beam(cpl_propertylist *qclist, const cpl_image *self, const cpl_parameterlist *parlist, const char *recipename, visir_chopnod_mode mode, const cpl_propertylist *plist)
Collapse the 3/4 beams of a combined image.
Definition: visir_inputs.c:546
cpl_error_code irplib_dfs_save_image(cpl_frameset *allframes, const cpl_parameterlist *parlist, const cpl_frameset *usedframes, const cpl_image *image, cpl_type_bpp bpp, const char *recipe, const char *procat, const cpl_propertylist *applist, const char *remregexp, const char *pipe_id, const char *filename)
Save an image as a DFS-compliant pipeline product.
Definition: irplib_utils.c:192
cpl_error_code irplib_framelist_load_propertylist_all(irplib_framelist *self, int ind, const char *regexp, cpl_boolean invert)
Load the propertylists of all frames in the framelist.
const char * visir_parameterlist_get_string(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR string parameter.
const char * visir_pfits_get_insmode(const cpl_propertylist *self)
The mode.
Definition: visir_pfits.c:442
double visir_pfits_get_img_weight(const cpl_propertylist *self)
The relative weight of the image compared to the other images.
Definition: visir_pfits.c:877
irplib_framelist * irplib_framelist_extract(const irplib_framelist *self, const char *tag)
Extract the frames with the given tag from a framelist.
int visir_dfs_set_groups(cpl_frameset *set)
Set the group as RAW or CALIB in a frameset.
Definition: visir_dfs.c:72
double visir_pfits_get_dit(const cpl_propertylist *self)
The DIT.
Definition: visir_pfits.c:309
double visir_pfits_get_pixscale(const cpl_propertylist *self)
The pixel scale.
Definition: visir_pfits.c:678
void irplib_framelist_delete(irplib_framelist *self)
Deallocate an irplib_framelist with its frames and properties.
const char * visir_pfits_get_chopnod_dir(const cpl_propertylist *self)
The chopping direction.
Definition: visir_pfits.c:139
cpl_error_code irplib_framelist_contains(const irplib_framelist *self, const char *key, cpl_type type, cpl_boolean is_equal, double fp_tol)
Verify that a property is present for all frames.
const char * irplib_frameset_find_file(const cpl_frameset *self, const char *tag)
Find the filename with the given tag in a frame set.
cpl_propertylist * irplib_framelist_get_propertylist(irplib_framelist *self, int pos)
Get the propertylist of the specified frame in the framelist.
cpl_error_code irplib_framelist_load_propertylist(irplib_framelist *self, int pos, int ind, const char *regexp, cpl_boolean invert)
Load the propertylist of the specified frame in the framelist.
irplib_framelist * irplib_framelist_cast(const cpl_frameset *frameset)
Create an irplib_framelist from a cpl_framelist.
double visir_pfits_get_ra(const cpl_propertylist *self)
The RA.
Definition: visir_pfits.c:709
int irplib_framelist_get_size(const irplib_framelist *self)
Get the size of a framelist.
cpl_image ** visir_img_recombine(const char *recipename, const cpl_parameterlist *parlist, const irplib_framelist *rawframes, const char *badpix, const char *flat, cpl_geom_combine combine_mode, cpl_boolean *pdid_resize, cpl_boolean do_spc_fix, double wlen, visir_spc_resol resol)
The VISIR imaging combination using cross correlation.