VISIR Pipeline Reference Manual  4.1.7
visir_spectro.c
1 /* $Id: visir_spectro.c,v 1.254 2013-09-24 10:46:00 jtaylor Exp $
2  *
3  * This file is part of the VISIR Pipeline
4  * Copyright (C) 2002,2003 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 /*
22  * $Author: jtaylor $
23  * $Date: 2013-09-24 10:46:00 $
24  * $Revision: 1.254 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 /*-----------------------------------------------------------------------------
33  Includes
34  -----------------------------------------------------------------------------*/
35 
36 #include "irplib_wavecal.h"
37 
38 #include "visir_spectro.h"
39 
40 #include "visir_utils.h"
41 #include "visir_pfits.h"
42 #include "visir_inputs.h"
43 #include "visir_parameter.h"
44 #include "visir_spc_distortion.h"
45 
46 #include "irplib_framelist.h"
47 
48 #include <cpl.h>
49 
50 #include <string.h>
51 #include <math.h>
52 #include <float.h>
53 #include <assert.h>
54 
55 /*----------------------------------------------------------------------------*/
61 /*----------------------------------------------------------------------------*/
62 
63 /*-----------------------------------------------------------------------------
64  Private structs
65  -----------------------------------------------------------------------------*/
66 
67 typedef struct {
68  cpl_size cost; /* May be incremented for cost counting */
69  cpl_size xcost; /* Ditto (can exclude failed fills) */
70  cpl_size ulines; /* May be set to number of lines used */
71 
72  double temp; /* Temperature of dominant black-body (M1) */
73  const cpl_vector * vsymm; /* Symmetric convolution vector from slit
74  width, FWHM of transfer function and
75  truncation width */
76 
77  const cpl_bivector * lines; /* Sky spectrum, with
78  increasing X-vector elements */
79  const cpl_bivector * tqeff; /* Spectrum of detector quantum efficiency with
80  increasing X-vector elements */
82 
83 /*-----------------------------------------------------------------------------
84  Private Function Prototypes
85  -----------------------------------------------------------------------------*/
86 
87 
88 static cpl_error_code
89 visir_polynomial_shift_1d_from_correlation(cpl_polynomial *,
90  const cpl_vector *,
92  cpl_error_code (*)
93  (cpl_vector *,
94  const cpl_polynomial *,
96  int, int, cpl_boolean,
97  double *, double *);
98 
99 static cpl_error_code visir_spectro_refine(cpl_polynomial *,
100  const cpl_vector *,
102  const cpl_polynomial *,
103  int, cpl_boolean, double *,
104  cpl_boolean *, double *);
105 
106 static cpl_error_code visir_spectro_fill(cpl_vector *, const cpl_polynomial *,
108 
109 static cpl_bivector * visir_spc_extract(cpl_image *, cpl_propertylist *,
110  cpl_image **, int, int);
111 
112 static cpl_error_code visir_spc_emission(cpl_bivector *, const cpl_vector *,
113  const cpl_bivector *,
114  const cpl_bivector *,
115  const cpl_vector *, double);
116 
117 static cpl_polynomial * visir_spc_phys_disp(int, double, visir_spc_resol, int,
118  int);
119 static cpl_polynomial * visir_spc_phys_lrp(void);
120 static double visir_spc_get_dispersion(const cpl_polynomial *, double);
121 static cpl_error_code visir_vector_convolve_symm(cpl_vector *,
122  const cpl_vector *);
123 static cpl_vector * cpl_spc_convolve_init(int, double, double, int);
124 
125 static cpl_error_code visir_spectro_qclist_wcal(cpl_propertylist *,
126  int, double,
127  cpl_boolean, double,
128  const cpl_polynomial *,
129  const cpl_polynomial *);
130 
131 static cpl_error_code visir_spectro_qclist_obs(cpl_propertylist *,
132  double, double);
133 
134 static const double N_upper = 13.4e-6; /* Upper limit of N-band */
135 static const double whechelle = 35.8/2; /* Half the echelle width */
136 
137 #ifndef VISIR_XC_LEN
138 #define VISIR_XC_LEN 50
139 #endif
140 #ifndef VISIR_XC_SUBSEARCH
141 #define VISIR_XC_SUBSEARCH 100
142 #endif
143 
144 #ifndef VISIR_SPECTRO_SIGMA
145 #define VISIR_SPECTRO_SIGMA 3.0
146 #endif
147 
150 /*-----------------------------------------------------------------------------
151  Function code
152  -----------------------------------------------------------------------------*/
153 
154 
155 cpl_error_code
156 visir_spc_extract_order(cpl_image ** order,
157  cpl_image ** comorder,
158  const cpl_image * combined,
159  const cpl_image * imhcycle,
160  const double wlen,
161  const visir_spc_config * pconfig,
162  const cpl_boolean do_ech,
163  int is_aqu)
164 {
165  int icol1, icol2;
166  int jcol1, jcol2;
167 
168  jcol1 = visir_parameterlist_get_int(pconfig->parlist, pconfig->recipename,
169  VISIR_PARAM_REJLEFT);
170  jcol2 = visir_parameterlist_get_int(pconfig->parlist, pconfig->recipename,
171  VISIR_PARAM_REJRIGHT);
172 
173 
174  if (do_ech) {
175  skip_if (visir_spc_echelle_limit(&icol1, &icol2, wlen,
176  pconfig->orderoffset,
177  1, cpl_image_get_size_y(combined),
178  is_aqu));
179  } else {
180  icol1 = 1;
181  icol2 = cpl_image_get_size_x(imhcycle);
182  }
183 
184  if (do_ech) {
185  if (jcol1 != 0) {
186  cpl_msg_info(cpl_func, "Ignoring %d leftmost columns from %d to %d",
187  jcol1, icol1, icol1 + jcol1);
188  icol1 += jcol1;
189  }
190  if (jcol2 != 0) {
191  cpl_msg_info(cpl_func, "Ignoring %d rightmost columns from %d to %d",
192  jcol2, icol2 - jcol2, icol2);
193  icol2 -= jcol2;
194  }
195  } else {
196  if (jcol1 != 0) {
197  cpl_msg_info(cpl_func, "Ignoring %d leftmost columns", jcol1);
198  icol1 += jcol1;
199  }
200  if (jcol2 != 0) {
201  cpl_msg_info(cpl_func, "Ignoring %d rightmost columns", jcol2);
202  icol2 -= jcol2;
203  }
204  }
205 
206  if (icol1 != 1 || icol2 != cpl_image_get_size_x(imhcycle)) {
207  *order = visir_spc_column_extract(imhcycle, icol1, icol2,
208  pconfig->plot);
209  skip_if (0);
210 
211  *comorder = visir_spc_column_extract(combined, icol1, icol2,
212  pconfig->plot);
213  skip_if (0);
214 
215  } else {
216  *order = cpl_image_duplicate(imhcycle);
217  *comorder = cpl_image_duplicate(combined);
218  }
219 
220  end_skip;
221 
222  return cpl_error_get_code();
223 }
224 
225 /*----------------------------------------------------------------------------*/
241 /*----------------------------------------------------------------------------*/
242 visir_spc_resol visir_spc_get_res_wl(const irplib_framelist * rawframes,
243  double * pwlen, double * pslitw,
244  double * ptemp, double * pfwhm,
245  int is_aqu)
246 {
247  cpl_errorstate cleanstate = cpl_errorstate_get();
248  /* Avoid (false) uninit warning */
249  visir_spc_resol resol = VISIR_SPC_R_ERR;
250  char ptmp[IRPLIB_FITS_STRLEN+1];
251  double wl, spx, pfov = 0.127; /* Avoid (false) uninit warning */
252  double sl = 0.0; /* Avoid (false) uninit warning */
253  cpl_boolean need_temp = ptemp != NULL;
254  int n;
255 
256  /* Check entries */
257  cpl_ensure(rawframes != NULL, CPL_ERROR_NULL_INPUT, VISIR_SPC_R_ERR);
258  cpl_ensure(pwlen != NULL, CPL_ERROR_NULL_INPUT, VISIR_SPC_R_ERR);
259  cpl_ensure(pslitw != NULL, CPL_ERROR_NULL_INPUT, VISIR_SPC_R_ERR);
260  cpl_ensure(pfwhm != NULL, CPL_ERROR_NULL_INPUT, VISIR_SPC_R_ERR);
261 
262  n = irplib_framelist_get_size(rawframes);
263 
264  cpl_ensure(n > 0, CPL_ERROR_DATA_NOT_FOUND, VISIR_SPC_R_ERR);
265 
266  /* Allow 1 micron difference */
267  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_DOUBLE_PIXSPACE,
268  CPL_TYPE_DOUBLE, CPL_TRUE, 1e-6));
269 
270  /* The actual value depends on the age of the file :-( */
271  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_DOUBLE_SLITWIDTH,
272  CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
273 
274  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_RESOL,
275  CPL_TYPE_STRING, CPL_TRUE, 0.0));
276 
277  skip_if(irplib_framelist_contains(rawframes, VISIR_PFITS_STRING_SLITNAME,
278  CPL_TYPE_STRING, CPL_TRUE, 0.0));
279 
280  for (int i = 0; i < n; i++) {
281  const cpl_propertylist * plist;
282  const char * filename =
283  cpl_frame_get_filename(irplib_framelist_get_const(rawframes, i));
284  const char * pfits;
285  double wl_tmp, sl_tmp, spx_tmp, pfov_tmp;
286 
287 
288  cpl_ensure(!cpl_error_get_code(), CPL_ERROR_DATA_NOT_FOUND,
289  VISIR_SPC_R_ERR);
290 
291  cpl_ensure(filename != NULL, CPL_ERROR_DATA_NOT_FOUND,
292  VISIR_SPC_R_ERR);
293 
294  plist = irplib_framelist_get_propertylist_const(rawframes, i);
295 
296  cpl_ensure(plist != NULL, CPL_ERROR_DATA_NOT_FOUND, VISIR_SPC_R_ERR);
297 
298  wl_tmp = visir_pfits_get_wlen(plist);
299  if (wl_tmp <= 0.0 || !cpl_errorstate_is_equal(cleanstate)) {
300  irplib_error_recover(cleanstate, "Missing or invalid FITS card");
301  wl_tmp = VISIR_SPC_LRP_CWLEN;
302  }
303  pfits = visir_pfits_get_resol(plist);
304  if (pfits == NULL || !cpl_errorstate_is_equal(cleanstate)) {
305  irplib_error_recover(cleanstate, "Missing or invalid FITS card");
306  pfits = VISIR_SPC_LRP_NAME;
307  }
308  sl_tmp = visir_pfits_get_slitwidth(plist);
309  spx_tmp = visir_pfits_get_pixspace(plist);
310 
311  /* FIXME: catch error 0.127, NULL, 0.127, ... */
312  {
313  pfov_tmp = visir_pfits_get_pixscale(plist);
314  if (pfov_tmp <= 0.) {
315  cpl_errorstate_set(cleanstate);
316  cpl_msg_warning(cpl_func, VISIR_PFITS_STRING_PIXSCALE
317  " not set, falling back to 0.127");
318  pfov_tmp = 0.127;
319  }
320  }
321 
322  cpl_ensure(!cpl_error_get_code(), CPL_ERROR_DATA_NOT_FOUND,
323  VISIR_SPC_R_ERR);
324 
325  if (i == 0) {
326 
327  visir_optmod ins_settings;
328 
329  sl = sl_tmp;
330  spx = spx_tmp;
331  wl = wl_tmp;
332  pfov = pfov_tmp;
333 
334  /* Divide the slit width with the
335  Spectral PFOV = 0.127 Arcseconds/pixel */
336  *pslitw = sl / pfov; /* Convert Slit width from Arcseconds to pixel */
337 
338  *pwlen = wl * 1e-6; /* Convert from micron to m */
339 
340  strncpy(ptmp, pfits, IRPLIB_FITS_STRLEN);
341  ptmp[IRPLIB_FITS_STRLEN] = '\0';
342 
343  cpl_msg_info(cpl_func, "RESOL [" VISIR_SPC_LRP_NAME "|LR|MR|HRS|HRG]"
344  " and WLEN [m] (%d frames): %s %g", n, ptmp, *pwlen);
345 
346  if (spx <= 0) {
347  cpl_msg_error(cpl_func,"Pixel Spacing (%g) in %s is non-"
348  "positive", spx, filename);
349  cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, VISIR_SPC_R_ERR);
350  }
351 
352  if (*pslitw <= 0) {
353  cpl_msg_error(cpl_func,"Slit Width (%g) in %s is non-positive",
354  sl, filename);
355  cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, VISIR_SPC_R_ERR);
356  }
357 
358  cpl_msg_info(cpl_func, "Slit Width [pixel] and Pixel Spacing [m]: "
359  "%g %g", *pslitw, spx);
360 
361  if (!strcmp(VISIR_SPC_LRP_NAME, ptmp)) {
362  resol = VISIR_SPC_R_LRP;
363  } else if (!strcmp("LR", ptmp)) {
364  resol = VISIR_SPC_R_LR;
365  } else if (!strcmp("MR", ptmp)) {
366  resol = VISIR_SPC_R_MR;
367  } else if (!strcmp("HRS", ptmp)) {
368  resol = VISIR_SPC_R_HR;
369  } else if (!strcmp("HRG", ptmp)) {
370  resol = VISIR_SPC_R_GHR;
371  } else {
372  cpl_msg_error(cpl_func,"Unsupported resolution (%s) in %s",
373  ptmp, filename);
374  cpl_ensure(0, CPL_ERROR_UNSUPPORTED_MODE, VISIR_SPC_R_ERR);
375  }
376 
377  if (resol != VISIR_SPC_R_LRP) {
378  /* Allow 1 nm difference */
379  skip_if(irplib_framelist_contains(rawframes,
380  VISIR_PFITS_DOUBLE_WLEN,
381  CPL_TYPE_DOUBLE, CPL_TRUE,
382  1e-3));
383  }
384 
385  if (visir_spc_optmod_init(resol, *pwlen, &ins_settings, is_aqu)) {
386  cpl_msg_error(cpl_func, "Resolution %s does not support "
387  "Central Wavelength [m]: %g", ptmp, *pwlen);
388  cpl_ensure(0, CPL_ERROR_INCOMPATIBLE_INPUT, VISIR_SPC_R_ERR);
389  }
390 
391  cpl_msg_info(cpl_func, "The %s-Spectral Resolution at %gm: %g",
392  ptmp, *pwlen,
393  visir_spc_optmod_resolution(&ins_settings));
394  cpl_msg_info(cpl_func, "The %s-Linear Dispersion at %gm [pixel/m]: "
395  "%g", ptmp, *pwlen,
396  visir_spc_optmod_dispersion(&ins_settings));
397 
398  *pfwhm = *pwlen * visir_spc_optmod_dispersion(&ins_settings)
399  / visir_spc_optmod_resolution(&ins_settings);
400 
401  cpl_msg_info(cpl_func, "The %s-FWHM at %gm [pixel]: %g",
402  ptmp, *pwlen, *pfwhm);
403  } else {
404  if (fabs(sl-sl_tmp) > 1e-3) { /* Allow 1 micron difference */
405  cpl_msg_error(cpl_func, "Inconsistent slit width (%g <=>"
406  " %g) in %s (%d of %d)",
407  sl, sl_tmp, filename, i+1, n);
408  cpl_ensure(0, CPL_ERROR_INCOMPATIBLE_INPUT, VISIR_SPC_R_ERR);
409  }
410  if (fabs(pfov-pfov_tmp) > 1e-4) { /* Allow 1 micron difference */
411  cpl_msg_error(cpl_func, "Inconsistent pfov (%g <=>"
412  " %g) in %s (%d of %d)",
413  pfov, pfov_tmp, filename, i+1, n);
414  cpl_ensure(0, CPL_ERROR_INCOMPATIBLE_INPUT, VISIR_SPC_R_ERR);
415  }
416  }
417  if (need_temp) {
418  /* Temperature [Celcius] not yet found */
419  const double temp = visir_pfits_get_temp(plist);
420  if (cpl_error_get_code()) {
421  visir_error_reset("Could not get FITS key");
422  } else if ((-20 < temp) && (temp < 60)) {
423  /* Only accept a non-extreme temperature */
424  need_temp = CPL_FALSE;
425  *ptemp = temp;
426  }
427  }
428 
429  }
430 
431  if (need_temp) {
432  cpl_msg_warning(cpl_func, "No FITS-files specify the M1 temperature, "
433  "using default");
434  *ptemp = 10; /* Default is 10 Celcius */
435  }
436 
437 
438  if (ptemp != NULL) {
439  *ptemp += 273.15; /* Convert to Kelvin */
440  cpl_msg_info(cpl_func, "The M1 temperature [Kelvin]: %g", *ptemp);
441  }
442 
443  end_skip;
444 
445  return resol;
446 
447 }
448 
449 /*----------------------------------------------------------------------------*/
470 /*----------------------------------------------------------------------------*/
471 cpl_error_code visir_vector_resample(cpl_vector * self,
472  const cpl_vector * xbounds,
473  const cpl_bivector * source)
474 {
475 
476  const cpl_vector * xsource = cpl_bivector_get_x_const(source);
477  const cpl_vector * ysource = cpl_bivector_get_y_const(source);
478 
479  const double * pxsource = cpl_vector_get_data_const(xsource);
480  const double * pysource = cpl_vector_get_data_const(ysource);
481  const double * pxbounds = cpl_vector_get_data_const(xbounds);
482 
483 
484  cpl_vector * ybounds = cpl_vector_new(cpl_vector_get_size(xbounds));
485  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual)
486  cpl_bivector * boundary = cpl_bivector_wrap_vectors((cpl_vector*)xbounds,
487  ybounds);
488  IRPLIB_DIAG_PRAGMA_POP
489  double * pybounds = cpl_vector_get_data(ybounds);
490 
491  double * pself = cpl_vector_get_data(self);
492  const int npix = cpl_vector_get_size(self);
493  int i;
494  int itt;
495 
496 
497  cpl_ensure_code(cpl_bivector_get_size(boundary) == npix + 1,
498  CPL_ERROR_ILLEGAL_INPUT);
499 
500  skip_if (0);
501 
502  itt = cpl_vector_find(xsource, pxbounds[0]);
503 
504  skip_if (0);
505 
506  skip_if (cpl_bivector_interpolate_linear(boundary, source));
507 
508  /* At this point itt most likely points to element just below
509  pxbounds[0] */
510  while (pxsource[itt] < pxbounds[0]) itt++;
511 
512  for (i=0; i < npix; i++) {
513 
514  /* The i'th value is the weighted average of the two interpolated
515  values at the boundaries and the source values in between */
516 
517  double xlow = pxbounds[i];
518  double x = pxsource[itt];
519 
520  if (x > pxbounds[i+1]) x = pxbounds[i+1];
521  /* Contribution from interpolated value at lower boundary */
522  pself[i] = pybounds[i] * (x - xlow);
523 
524  /* Contribution from table values in between boundaries */
525  while (pxsource[itt] < pxbounds[i+1]) {
526  const double xprev = x;
527  x = pxsource[itt+1];
528  if (x > pxbounds[i+1]) x = pxbounds[i+1];
529  pself[i] += pysource[itt] * (x - xlow);
530  xlow = xprev;
531  itt++;
532  }
533 
534  /* Contribution from interpolated value at upper boundary */
535  pself[i] += pybounds[i+1] * (pxbounds[i+1] - xlow);
536 
537  /* Compute average by dividing integral by length of sampling interval
538  (the factor 2 comes from the contributions) */
539  pself[i] /= 2 * (pxbounds[i+1] - pxbounds[i]);
540 
541  }
542 
543 
544  end_skip;
545 
546  cpl_vector_delete(ybounds);
547  cpl_bivector_unwrap_vectors(boundary);
548 
549  return cpl_error_get_code();
550 }
551 
552 
553 
554 /*----------------------------------------------------------------------------*/
578 /*----------------------------------------------------------------------------*/
579 cpl_error_code visir_spc_extract_wcal(const cpl_image * combined,
580  const cpl_image * hcycle,
581  double wlen, double slitw,
582  double temp, double fwhm,
583  visir_spc_resol resol,
584  int ioffset,
585  const char * spc_cal_lines,
586  const char * spc_cal_qeff,
587  cpl_table ** pspc_table,
588  cpl_image ** pweight2d,
589  cpl_propertylist * qclist,
590  int doplot, int bkgcorrect,
591  int is_aqu)
592 {
593 
594  /* Both spectrum and error */
595  cpl_bivector * spc_n_err = NULL;
596  cpl_image * flipped = NULL;
597  const int npix = cpl_image_get_size_y(combined);
598 
599 
600  cpl_ensure_code(pweight2d != NULL, CPL_ERROR_NULL_INPUT);
601 
602  *pweight2d = NULL;
603 
604  cpl_ensure_code(npix > 0, CPL_ERROR_ILLEGAL_INPUT);
605  error_if(npix != cpl_image_get_size_y(hcycle), CPL_ERROR_ILLEGAL_INPUT,
606  "Sky frame does not have same size as the object frame."
607  " %d vs %d pixels", (int)cpl_image_get_size_y(hcycle), npix);
608 
609 
610  skip_if (0);
611 
612  skip_if (visir_spc_wavecal(hcycle, qclist, wlen, slitw, temp, fwhm, resol,
613  ioffset, spc_cal_lines, spc_cal_qeff,
614  pspc_table, doplot, is_aqu));
615 
616  /* Convert the combined image */
617  flipped = cpl_image_cast(combined, CPL_TYPE_DOUBLE);
618  skip_if (0);
619 
620  /* Extract spectrum with error from the combined image */
621  /* FIXME: Move inside */
622  spc_n_err = visir_spc_extract(flipped, qclist, pweight2d,
623  doplot, bkgcorrect);
624  skip_if (0);
625 
626  cpl_image_delete(flipped);
627  flipped = NULL;
628 
629  skip_if (*pspc_table == NULL);
630 
631  skip_if (cpl_table_new_column(*pspc_table, "SPC_EXTRACTED", CPL_TYPE_DOUBLE));
632  skip_if (cpl_table_new_column(*pspc_table, "SPC_ERROR", CPL_TYPE_DOUBLE));
633 
634  skip_if (cpl_table_set_column_unit(*pspc_table, "SPC_EXTRACTED", "ADU/s"));
635  skip_if (cpl_table_set_column_unit(*pspc_table, "SPC_ERROR", "ADU/s"));
636 
637  skip_if (cpl_table_copy_data_double(*pspc_table, "SPC_EXTRACTED",
638  cpl_bivector_get_x_data(spc_n_err)));
639  skip_if (cpl_table_copy_data_double(*pspc_table, "SPC_ERROR",
640  cpl_bivector_get_y_data(spc_n_err)));
641 
642  if (doplot) {
643  visir_table_plot("set grid;set xlabel 'Wavelength [m]';",
644  "t 'Extracted Spectrum' w linespoints",
645  "", *pspc_table, "WLEN", "SPC_EXTRACTED");
646  visir_table_plot("set grid;set xlabel 'Wavelength [m]';",
647  "t 'Error on Extracted Spectrum' w linespoints",
648  "", *pspc_table, "WLEN", "SPC_ERROR");
649  }
650 
651  end_skip;
652 
653  cpl_image_delete(flipped);
654  cpl_bivector_delete(spc_n_err);
655 
656  return cpl_error_get_code();
657 }
658 
659 
660 /*----------------------------------------------------------------------------*/
686 /*----------------------------------------------------------------------------*/
687 cpl_error_code visir_spc_wavecal(const cpl_image * hcycle,
688  cpl_propertylist * qclist,
689  double wlen, double slitw,
690  double temp, double fwhm,
691  visir_spc_resol resol,
692  int ioffset,
693  const char * linefile,
694  const char * qefffile,
695  cpl_table ** pspc_table, int doplot,
696  int is_aqu)
697 {
698 
699  /* Dispersion relation from physical model */
700  cpl_polynomial * phdisp = NULL;
701  /* Dispersion relation corrected by cross-correlation */
702  cpl_polynomial * xcdisp = NULL;
703 
704  visir_spectrum_model mymodel;
705  cpl_vector * wlvals = NULL;
706  cpl_vector * spmodel = NULL;
707 
708  cpl_bivector * emission = NULL;
709  cpl_vector * boundary = NULL;
710 
711  cpl_bivector * temiss = NULL;
712  cpl_bivector * tqeff = NULL;
713 
714  cpl_image * corrected = NULL;
715 
716  cpl_image * xc_image = NULL;
717  cpl_vector * xc_vector = NULL;
718 
719  cpl_vector * vsymm = NULL;
720  cpl_vector * vxc = NULL;
721 
722  const int npix = cpl_image_get_size_y(hcycle);
723 #if 0
724  double xc0;
725 #endif
726  double qcxc = -1.0, qcsubdelta = 0.; /* avoid false unint warning */
727  double hc_min;
728  const cpl_size i0 = 0;
729  const cpl_size i1 = 1;
730  cpl_boolean didshift = CPL_FALSE;
731 
732 
733  cpl_ensure_code(!cpl_error_get_code(), cpl_error_get_code());
734  cpl_ensure_code(pspc_table, CPL_ERROR_NULL_INPUT);
735  cpl_ensure_code(npix > 0, CPL_ERROR_ILLEGAL_INPUT);
736 
737 
738  /* Make sure the corrected image is of type double */
739  corrected = cpl_image_cast(hcycle, CPL_TYPE_DOUBLE);
740  skip_if (0);
741 
742  hc_min = cpl_image_get_min(corrected);
743  skip_if (0);
744  cpl_msg_info(cpl_func,"Half-cycle image [%d X %d] has minimum intensity: %g",
745  (int)cpl_image_get_size_x(hcycle), npix, hc_min);
746  if (hc_min < 0) {
747  cpl_msg_warning(cpl_func, "Thresholding negative intensities in half-"
748  "cycle image: %g", hc_min);
749  skip_if (cpl_image_threshold(corrected, 0.0, DBL_MAX, 0.0, DBL_MAX));
750  } else if (hc_min > 0) {
751  skip_if (cpl_image_subtract_scalar(corrected, hc_min));
752  }
753 
754  xc_image = cpl_image_duplicate(corrected);
755 
756  /* Average the spatial dimension - into a cpl_vector */
757  cpl_image_delete(corrected);
758  corrected = cpl_image_collapse_create(xc_image, 1);
759  cpl_image_delete(xc_image);
760  xc_image = corrected;
761  corrected = NULL;
762 
763  skip_if(cpl_image_divide_scalar(xc_image, npix));
764 
765  xc_vector = cpl_vector_wrap(npix, cpl_image_get_data(xc_image));
766 
767  skip_if (0);
768 
769 #ifdef VISIR_SPC_LRP
770  phdisp = visir_spc_phys_lrp();
771  cpl_msg_info(cpl_func, "Central Dispersion (physical model) [pixel/m]: %g",
772  1.0/visir_spc_get_dispersion(phdisp, npix/2.0 + 0.5));
773  cpl_msg_info(cpl_func, "Central Wavelength (physical model) [m]: %g",
774  cpl_polynomial_eval_1d(phdisp, npix/2.0 + 0.5, NULL));
775  cpl_msg_info(cpl_func, "First Wavelength (physical model) [m]: %g",
776  cpl_polynomial_eval_1d(phdisp, 1.0, NULL));
777  cpl_msg_info(cpl_func, "Last Wavelength (physical model) [m]: %g",
778  cpl_polynomial_eval_1d(phdisp, 1024, NULL));
779  cpl_polynomial_dump(phdisp, stdout);
780  cpl_polynomial_delete(phdisp);
781 #endif
782 
783  phdisp = visir_spc_phys_disp(npix, wlen, resol, ioffset, is_aqu);
784  skip_if (0);
785 
786  if (cpl_polynomial_get_degree(phdisp) == 2) {
787  const cpl_size i2 = 2;
788  cpl_msg_info(cpl_func, "Dispersion polynomial of physical model:"
789  " %gmum + ipix * %gmum/pixel + ipix^2 * (%g)mum/pixel^2 "
790  "[ipix = 1, 2, ..., %d]",
791  cpl_polynomial_get_coeff(phdisp, &i0) * 1e6,
792  cpl_polynomial_get_coeff(phdisp, &i1) * 1e6,
793  cpl_polynomial_get_coeff(phdisp, &i2) * 1e6,
794  npix);
795  }
796  else {
797  cpl_msg_info(cpl_func, "Dispersion polynomial of physical model:"
798  " %gmum + ipix * %gmum/pixel [ipix = 1, 2, ..., %d]",
799  cpl_polynomial_get_coeff(phdisp, &i0) * 1e6,
800  cpl_polynomial_get_coeff(phdisp, &i1) * 1e6, npix);
801  }
802 
803  temiss = visir_bivector_load_fits(linefile, "Wavelength", "Emission", 1);
804  any_if ("Could not load file with Emission Lines");
805 
806  tqeff = visir_bivector_load_fits(qefffile, "Wavelength", "Efficiency",
807  npix > 256 ? 2 : 1);
808  any_if("Could not load file with Quantum-Efficiencies");
809 
810  if (doplot) {
811  visir_bivector_plot("set grid;set xlabel 'Wavelength [m]';", "t '"
812  "Quantum Efficiency' w linespoints", "", tqeff);
813  }
814 
815  vsymm = cpl_spc_convolve_init(npix, slitw, fwhm, doplot);
816 
817  skip_if (vsymm == NULL);
818 
819  vxc = cpl_vector_new(1);
820  xcdisp = cpl_polynomial_new(1);
821 
822  mymodel.lines = temiss;
823  mymodel.tqeff = tqeff;
824  mymodel.vsymm = vsymm;
825  mymodel.temp = temp;
826  mymodel.ulines = 0;
827  mymodel.cost = 0;
828  mymodel.xcost = 0;
829 
830  skip_if(visir_spectro_refine(xcdisp, xc_vector, &mymodel, phdisp,
831  VISIR_XC_LEN, doplot,
832  &qcxc, &didshift, &qcsubdelta));
833 
834  if (didshift) {
835  if (fabs(qcsubdelta) >= VISIR_XC_LEN) {
836  cpl_msg_warning(cpl_func, "Cross-correlation (%g pixel shift): %g",
837  qcsubdelta, qcxc);
838  } else {
839  cpl_msg_info(cpl_func,"Cross-correlation (%g pixel shift): %g",
840  qcsubdelta, qcxc);
841  }
842  }
843 
844  cpl_msg_info(cpl_func, "Dispersion polynomial from cross-correlation: "
845  "%gm + ipix * %gm/pixel [ipix = 1, 2, ..., %d]",
846  cpl_polynomial_get_coeff(xcdisp, &i0),
847  cpl_polynomial_get_coeff(xcdisp, &i1), npix);
848 
849  cpl_msg_info(cpl_func, "New Central Wavelength [m]: %g",
850  cpl_polynomial_eval_1d(xcdisp, 0.5*npix+0.5, NULL));
851 
852  *pspc_table = cpl_table_new(npix);
853  skip_if (0);
854 
855  /* Generate the new wavelengths based on the cross-correlation shift */
856  wlvals = cpl_vector_new(npix);
857  bug_if (cpl_table_wrap_double(*pspc_table, cpl_vector_get_data(wlvals),
858  "WLEN"));
859 
860  skip_if (cpl_vector_fill_polynomial(wlvals, xcdisp, 1.0, 1.0));
861 
862  /* Dump the unshifted model spectrum to the table */
863  spmodel = cpl_vector_new(npix);
864  bug_if (cpl_table_wrap_double(*pspc_table, cpl_vector_get_data(spmodel),
865  "SPC_MODEL_PH"));
866  skip_if (visir_spectro_fill(spmodel, phdisp,
867  (irplib_base_spectrum_model*)&mymodel));
868 
869  /* - and the wavelength calibrated model spectrum */
870  (void)cpl_vector_unwrap(spmodel);
871  spmodel = cpl_vector_new(npix);
872  bug_if (cpl_table_wrap_double(*pspc_table, cpl_vector_get_data(spmodel),
873  "SPC_MODEL_XC"));
874 
875  skip_if (visir_spectro_fill(spmodel, xcdisp,
876  (irplib_base_spectrum_model*)&mymodel));
877 
878  bug_if (cpl_table_wrap_double(*pspc_table,
879  cpl_image_get_data_double(xc_image),
880  "SPC_SKY"));
881  (void)cpl_image_unwrap(xc_image);
882  xc_image = NULL;
883 
884  /* Get the emissivity (range 0 to 1) for the calibrated wavelengths */
885  (void)cpl_vector_unwrap(spmodel);
886  spmodel = cpl_vector_new(npix);
887  bug_if (cpl_table_wrap_double(*pspc_table, cpl_vector_get_data(spmodel),
888  "SPC_EMISSIVITY"));
889 
890  boundary = cpl_vector_new(npix + 1);
891  skip_if (cpl_vector_fill_polynomial(boundary, xcdisp, 0.5, 1.0));
892  skip_if (visir_vector_resample(spmodel, boundary, temiss));
893 
894  bug_if (cpl_table_set_column_unit(*pspc_table, "WLEN", "m"));
895  bug_if (cpl_table_set_column_unit(*pspc_table, "SPC_MODEL_PH",
896  "J*radian/m^3/s"));
897  bug_if (cpl_table_set_column_unit(*pspc_table, "SPC_MODEL_XC",
898  "J*radian/m^3/s"));
899  bug_if (cpl_table_set_column_unit(*pspc_table, "SPC_SKY", "ADU/s"));
900 
901  /* If the spectrum goes into N-band the sky spectrum may have variable
902  atmospheric features, that are not in the model used for the model
903  spectrum. This can cause the wavelength calibration to yield completely
904  wrong results */
905  if (cpl_vector_get(wlvals, 0) < N_upper &&
906  N_upper < cpl_vector_get(wlvals, npix-1))
907  cpl_msg_warning(cpl_func, "Spectrum goes above N-band (%gm). Wavelength"
908  " Calibration may be entirely inaccurate", N_upper);
909 
910  bug_if(visir_spectro_qclist_wcal(qclist, npix, qcxc, didshift, qcsubdelta,
911  phdisp, xcdisp));
912 
913  if (doplot) {
914  cpl_bivector * plot = cpl_bivector_wrap_vectors(wlvals, xc_vector);
915 
916  visir_bivector_plot("set grid;set xlabel 'Wavelength [m]';", "t 'Spec"
917  "trum from Half-cycle' w linespoints", "", plot);
918  cpl_bivector_unwrap_vectors(plot);
919 
920  visir_table_plot("set grid;set xlabel 'Wavelength [m]';",
921  "t 'Calibrated Model Spectrum' w linespoints",
922  "", *pspc_table, "WLEN", "SPC_MODEL_XC");
923 
924  /* The unshifted model spectrum */
925  visir_table_plot("set grid;set xlabel 'Wavelength [m]';",
926  "t 'Physical Model Spectrum' w linespoints",
927  "", *pspc_table, "WLEN", "SPC_MODEL_PH");
928 
929  if (resol != VISIR_SPC_R_LRP) {
930 
931  /* Create an model spectrum of twice the npix length */
932  emission = cpl_bivector_new(2 * npix);
933 
934  cpl_vector_delete(boundary);
935  boundary = cpl_vector_new(2 * npix + 1);
936 
937  cpl_vector_fill_polynomial(cpl_bivector_get_x(emission),
938  phdisp, -0.5*npix, 1);
939  cpl_vector_fill_polynomial(boundary, phdisp, -0.5*(npix+1), 1);
940 
941  /* Get the emission at those wavelengths */
942  visir_spc_emission(emission, boundary, temiss, tqeff, vsymm, temp);
943  cpl_vector_delete(boundary);
944  boundary = NULL;
945 
946  visir_bivector_plot("set grid;set xlabel 'Wavelength [m]';",
947  "t 'Extended Model Spectrum' w linespoints",
948  "", emission);
949  }
950  }
951 
952  end_skip;
953 
954  (void)cpl_vector_unwrap(wlvals);
955  (void)cpl_vector_unwrap(spmodel);
956  cpl_polynomial_delete(phdisp);
957  cpl_polynomial_delete(xcdisp);
958  cpl_image_delete(xc_image);
959  cpl_vector_delete(vsymm);
960  cpl_image_delete(corrected);
961  cpl_bivector_delete(temiss);
962  cpl_bivector_delete(tqeff);
963  cpl_vector_delete(boundary);
964  cpl_bivector_delete(emission);
965  (void)cpl_vector_unwrap(xc_vector);
966  cpl_vector_delete(vxc);
967 
968  return cpl_error_get_code();
969 }
970 
971 
972 /*----------------------------------------------------------------------------*/
989 /*----------------------------------------------------------------------------*/
990 cpl_error_code visir_spc_echelle_limit(int * pcol1, int * pcol2, double wlen,
991  int ioffset, int icolmin, int icolmax,
992  int is_aqu)
993 {
994 
995  visir_optmod ins_settings;
996  double echpos;
997  double wleni; /* The central wavelength at order offset ioffset */
998  int order;
999  int error;
1000 
1001 
1002  cpl_ensure_code(wlen > 0, CPL_ERROR_ILLEGAL_INPUT);
1003  cpl_ensure_code(pcol1, CPL_ERROR_NULL_INPUT);
1004  cpl_ensure_code(pcol2, CPL_ERROR_NULL_INPUT);
1005  cpl_ensure_code(icolmin > 0, CPL_ERROR_ILLEGAL_INPUT);
1006  cpl_ensure_code(icolmax >= icolmin, CPL_ERROR_ILLEGAL_INPUT);
1007  /* There are up to 5 spectra in the imaage */
1008  cpl_ensure_code(ioffset >= -4, CPL_ERROR_ILLEGAL_INPUT);
1009  cpl_ensure_code(ioffset <= 4, CPL_ERROR_ILLEGAL_INPUT);
1010 
1011  error = visir_spc_optmod_init(VISIR_SPC_R_GHR, wlen, &ins_settings, is_aqu);
1012  if (error) {
1013  cpl_msg_error(cpl_func, "HRG Optical model initialization (%p) failed: %d "
1014  "(%g)", (void*)&ins_settings, error, wlen);
1015  cpl_ensure_code(0, CPL_ERROR_ILLEGAL_INPUT);
1016  }
1017  order = ioffset + visir_spc_optmod_get_echelle_order(&ins_settings);
1018 
1019  /* There are 18 echelle orders */
1020  cpl_ensure_code(order > 0, CPL_ERROR_ILLEGAL_INPUT);
1021  cpl_ensure_code(order <= 18, CPL_ERROR_ILLEGAL_INPUT);
1022 
1023  wleni = visir_spc_optmod_echelle(&ins_settings, wlen, ioffset );
1024 
1025  echpos = visir_spc_optmod_cross_dispersion(&ins_settings, wleni);
1026  if (echpos <= whechelle || echpos >= icolmax-whechelle) {
1027  cpl_msg_error(cpl_func, "Echelle (%d) location out of range [%d;%d]: %g",
1028  order, icolmin, icolmax, echpos);
1029  cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
1030  }
1031 
1032  *pcol1 = ceil(echpos - whechelle); /* Round up */
1033  *pcol2 = echpos + whechelle; /* Round down */
1034 
1035  if (*pcol1 < icolmin) *pcol1 = icolmin;
1036  if (*pcol2 > icolmax) *pcol2 = icolmax;
1037 
1038  cpl_msg_info(cpl_func, "Echelle order %d at col %g [%d; %d]", order, echpos,
1039  *pcol1, *pcol2);
1040 
1041  return cpl_error_get_code();
1042 
1043 }
1044 
1045 /*----------------------------------------------------------------------------*/
1058 /*----------------------------------------------------------------------------*/
1059 cpl_image * visir_spc_column_extract(const cpl_image * self, int icol1,
1060  int icol2, int doplot)
1061 {
1062 
1063  cpl_image * band = NULL;
1064  cpl_image * spatial = NULL;
1065  const int nrow = cpl_image_get_size_y(self);
1066  const int ncol = cpl_image_get_size_x(self);
1067 
1068  cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, NULL);
1069  cpl_ensure(icol1 > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1070  cpl_ensure(icol2 >= icol1, CPL_ERROR_ILLEGAL_INPUT, NULL);
1071 
1072  cpl_ensure(ncol >= icol2, CPL_ERROR_ILLEGAL_INPUT, NULL);
1073 
1074  band = cpl_image_extract(self, icol1, 1, icol2, nrow);
1075  skip_if (0);
1076 
1077  if (doplot > 0) {
1078  visir_image_plot("", "t 'The full-width image'", "", self);
1079 
1080  if (doplot > 1) {
1081  /* Average the spectral dimension */
1082  spatial = cpl_image_collapse_create(self, 0);
1083  skip_if (0);
1084  skip_if (cpl_image_divide_scalar(spatial, nrow));
1085 
1086  visir_image_row_plot("set grid;", "t 'Spectral direction "
1087  "collapsed' w linespoints", "",
1088  spatial, 1, 1, 1);
1089  }
1090  }
1091 
1092  end_skip;
1093 
1094  cpl_image_delete(spatial);
1095  if (cpl_error_get_code() && band != NULL) {
1096  cpl_image_delete(band);
1097  band = NULL;
1098  }
1099 
1100  return band;
1101 
1102 }
1103 
1104 
1105 /*----------------------------------------------------------------------------*/
1118 /*----------------------------------------------------------------------------*/
1119 cpl_error_code visir_spectro_qc(cpl_propertylist * qclist,
1120  cpl_propertylist * paflist,
1121  cpl_boolean drop_wcs,
1122  const irplib_framelist * rawframes,
1123  const char * regcopy,
1124  const char * regcopypaf)
1125 {
1126 
1127  const cpl_propertylist * reflist
1129 
1130  bug_if (0);
1131 
1132  bug_if (visir_qc_append_capa(qclist, rawframes));
1133 
1134  if (regcopy != NULL)
1135  bug_if (cpl_propertylist_copy_property_regexp(qclist, reflist,
1136  regcopy, 0));
1137 
1138  if (regcopypaf != NULL)
1139  bug_if (cpl_propertylist_copy_property_regexp(paflist, reflist,
1140  regcopypaf, 0));
1141 
1142  bug_if (cpl_propertylist_append(paflist, qclist));
1143 
1144  if (drop_wcs) {
1145  cpl_propertylist * pcopy = cpl_propertylist_new();
1146  const cpl_error_code error
1147  = cpl_propertylist_copy_property_regexp(pcopy, reflist, "^("
1148  IRPLIB_PFITS_WCS_REGEXP
1149  ")$", 0);
1150  if (!error && cpl_propertylist_get_size(pcopy) > 0) {
1151  cpl_msg_warning(cpl_func, "Combined image will have no WCS "
1152  "coordinates");
1153  }
1154  cpl_propertylist_delete(pcopy);
1155  bug_if(0);
1156  } else {
1157  bug_if(cpl_propertylist_copy_property_regexp(qclist, reflist, "^("
1158  IRPLIB_PFITS_WCS_REGEXP
1159  ")$", 0));
1160  }
1161 
1162  end_skip;
1163 
1164  return cpl_error_get_code();
1165 
1166 }
1167 
1168 
1172 /*----------------------------------------------------------------------------*/
1185 /*----------------------------------------------------------------------------*/
1186 static cpl_error_code visir_spectro_qclist_wcal(cpl_propertylist * self,
1187  int npix, double xc,
1188  cpl_boolean didshift,
1189  double subdelta,
1190  const cpl_polynomial * phdisp,
1191  const cpl_polynomial * xcdisp)
1192 {
1193 
1194  const cpl_size phdegree = cpl_polynomial_get_degree(phdisp);
1195  const cpl_size xcdegree = cpl_polynomial_get_degree(xcdisp);
1196 
1197  const double phdisp0 = cpl_polynomial_eval_1d(phdisp, 1.0, NULL);
1198  const double xcdisp0 = cpl_polynomial_eval_1d(xcdisp, 1.0, NULL);
1199 
1200  const double xcwlen = cpl_polynomial_eval_1d(xcdisp, 0.5*(double)npix+0.5,
1201  NULL);
1202  const double phcdisp = visir_spc_get_dispersion(phdisp, npix/2.0 + 0.5);
1203  const double xccdisp = visir_spc_get_dispersion(xcdisp, npix/2.0 + 0.5);
1204  cpl_size i;
1205 
1206 
1207  bug_if (0);
1208  skip_if (phdegree < 1);
1209  skip_if (xcdegree < 1);
1210 
1211  cpl_msg_info(cpl_func, "Central Dispersion (physical model) [m/pixel]: %g",
1212  phcdisp);
1213  cpl_msg_info(cpl_func, "Central Dispersion (calibrated) [m/pixel]: %g",
1214  xccdisp);
1215 
1216  bug_if (cpl_propertylist_append_double(self, "ESO QC XC", xc));
1217 
1218  if (didshift)
1219  bug_if (cpl_propertylist_append_double(self, "ESO QC XCSHIFT",
1220  subdelta));;
1221 
1222  bug_if (cpl_propertylist_append_int(self, "ESO QC PHDEGREE", phdegree));
1223  bug_if (cpl_propertylist_append_double(self, "ESO QC PHDISPX0", phdisp0));
1224  for (i = 1; i <= phdegree; i++) {
1225  const double coeff = cpl_polynomial_get_coeff(phdisp, &i);
1226  char * label = cpl_sprintf("ESO QC PHDISPX%d", (int)i);
1227 
1228  bug_if (cpl_propertylist_append_double(self, label, coeff));
1229  cpl_free(label);
1230  }
1231 
1232  bug_if (cpl_propertylist_append_double(self, "ESO QC XCWLEN", xcwlen));
1233 
1234  bug_if (cpl_propertylist_append_int(self, "ESO QC XCDEGREE", xcdegree));
1235  bug_if (cpl_propertylist_append_double(self, "ESO QC XCDISPX0", xcdisp0));
1236 
1237  for (i = 1; i <= xcdegree; i++) {
1238  const double coeff = cpl_polynomial_get_coeff(xcdisp, &i);
1239  char * label = cpl_sprintf("ESO QC XCDISPX%d", (int)i);
1240 
1241  bug_if (cpl_propertylist_append_double(self, label, coeff));
1242  cpl_free(label);
1243  }
1244 
1245  end_skip;
1246 
1247  return cpl_error_get_code();
1248 
1249 }
1250 
1251 
1252 
1253 /*----------------------------------------------------------------------------*/
1265 /*----------------------------------------------------------------------------*/
1266 static cpl_error_code visir_spectro_qclist_obs(cpl_propertylist * self,
1267  double xfwhm, double xcentro)
1268 {
1269 
1270 
1271  bug_if (0);
1272 
1273  bug_if (cpl_propertylist_append_double(self, "ESO QC XFWHM", xfwhm));
1274  bug_if (cpl_propertylist_append_double(self, "ESO QC XCENTROI", xcentro));
1275 
1276  end_skip;
1277 
1278  return cpl_error_get_code();
1279 
1280 }
1281 
1282 
1283 /*----------------------------------------------------------------------------*/
1295 /*----------------------------------------------------------------------------*/
1296 static cpl_error_code visir_vector_convolve_symm(cpl_vector * self,
1297  const cpl_vector * vsymm)
1298 {
1299 
1300  const int npix = cpl_vector_get_size(self);
1301  const int ihwidth = cpl_vector_get_size(vsymm) - 1;
1302  cpl_vector * raw = cpl_vector_duplicate(self);
1303  double * pself= cpl_vector_get_data(self);
1304  double * praw = cpl_vector_get_data(raw);
1305  const double * psymm = cpl_vector_get_data_const(vsymm);
1306 
1307  int i, j;
1308 
1309 
1310  skip_if (0);
1311 
1312  /* The convolution does not support this */
1313  skip_if (ihwidth >= npix);
1314 
1315  /* Convolve with the symmetric function */
1316  for (i = 0; i < ihwidth; i++) {
1317  pself[i] = praw[i] * psymm[0];
1318  for (j = 1; j <= ihwidth; j++) {
1319  const int k = i-j < 0 ? 0 : i-j;
1320  pself[i] += (praw[k]+praw[i+j]) * psymm[j];
1321  }
1322 
1323  }
1324 
1325  for (i = ihwidth; i < npix-ihwidth; i++) {
1326  pself[i] = praw[i] * psymm[0];
1327  for (j = 1; j <= ihwidth; j++)
1328  pself[i] += (praw[i-j]+praw[i+j]) * psymm[j];
1329 
1330  }
1331  for (i = npix-ihwidth; i < npix; i++) {
1332  pself[i] = praw[i] * psymm[0];
1333  for (j = 1; j <= ihwidth; j++) {
1334  const int k = i+j > npix-1 ? npix - 1 : i+j;
1335  pself[i] += (praw[k]+praw[i-j]) * psymm[j];
1336  }
1337 
1338  }
1339 
1340  end_skip;
1341 
1342  cpl_vector_delete(raw);
1343 
1344  return cpl_error_get_code();
1345 }
1346 
1347 /*----------------------------------------------------------------------------*/
1368 /*----------------------------------------------------------------------------*/
1369 cpl_image * visir_spc_flip(const cpl_image * image, double wlen,
1370  visir_spc_resol resol, visir_data_type dtype)
1371 {
1372  cpl_image * flipped = cpl_image_cast(image, CPL_TYPE_DOUBLE);
1373  visir_optmod ins_settings;
1374 
1375 
1376  skip_if (0);
1377 
1378  if ((resol == VISIR_SPC_R_HR || resol == VISIR_SPC_R_GHR) &&
1379  visir_spc_optmod_init(resol, wlen, &ins_settings,
1380  visir_data_is_aqu(dtype))) {
1381  visir_error_set(CPL_ERROR_ILLEGAL_INPUT);
1382  skip_if (1);
1383  }
1384 
1385  /* The dispersion relation goes from the top of the image to the bottom
1386  - except aquarius , where the detector is rotated 90 degrees
1387  - except using the B-side (in high resolution) */
1388  if (visir_data_is_aqu(dtype)) {
1389  skip_if (cpl_image_turn(flipped, 1));
1390  if ((resol == VISIR_SPC_R_HR || resol == VISIR_SPC_R_GHR) &&
1391  visir_spc_optmod_side_is_A(&ins_settings) == 0) {
1392  skip_if (cpl_image_flip(flipped, 0));
1393  }
1394  }
1395 
1396  else if ((resol != VISIR_SPC_R_HR && resol != VISIR_SPC_R_GHR) ||
1397  visir_spc_optmod_side_is_A(&ins_settings) > 0) {
1398 
1399  cpl_msg_info(cpl_func, "Flipping image");
1400 
1401  skip_if (cpl_image_flip(flipped, 0));
1402  }
1403 
1404  end_skip;
1405 
1406  if (cpl_error_get_code() && flipped) {
1407  cpl_image_delete(flipped);
1408  flipped = NULL;
1409  }
1410 
1411  return flipped;
1412 
1413 }
1414 
1415 /*----------------------------------------------------------------------------*/
1431 /*----------------------------------------------------------------------------*/
1432 static cpl_polynomial * visir_spc_phys_disp(int npix, double wlen,
1433  visir_spc_resol resol, int ioffset,
1434  int is_aqu)
1435 {
1436 
1437  cpl_polynomial * phdisp = NULL;
1438  visir_optmod ins_settings;
1439 
1440  double dwl;
1441  double wlen0;
1442  double wlen1;
1443  double disp;
1444  const cpl_size i1 = 1;
1445  const cpl_size i0 = 0;
1446 
1447 
1448  cpl_ensure(resol, CPL_ERROR_ILLEGAL_INPUT, NULL);
1449  cpl_ensure(wlen > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1450  cpl_ensure(npix > 1, CPL_ERROR_ILLEGAL_INPUT, NULL);
1451 
1452  /* Initialize instrument-specific settings
1453  - the resolution is not needed hereafter
1454  visir_spc_optmod_init() does itself not use the CPL-error system
1455  because it is also used in a non-CPL scope */
1456 
1457  cpl_ensure(!visir_spc_optmod_init(resol, wlen, &ins_settings, is_aqu),
1458  CPL_ERROR_ILLEGAL_INPUT, NULL);
1459 
1460  /* Get wavelength range (and corresponding central-wavelength)
1461  visir_spc_optmod_wlen() does not use the CPL-error system
1462  because it is also used in a non-CPL scope */
1463  dwl = visir_spc_optmod_wlen(&ins_settings, &wlen0, &wlen1);
1464 
1465  cpl_ensure(dwl >= 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1466 
1467  /* Central-wavelength residual on Scan-Angle determination */
1468  dwl -= wlen;
1469  /* Warn if the residual exceeds twice the machine-precision */
1470  if (fabs(dwl) > 2*wlen*DBL_EPSILON) cpl_msg_warning(cpl_func, "Too large res"
1471  "idual in Scan-Angle determination [meps]: %g", dwl/DBL_EPSILON/wlen);
1472 
1473  if ((resol == VISIR_SPC_R_HR || resol == VISIR_SPC_R_GHR) &&
1474  !visir_spc_optmod_side_is_A(&ins_settings)) {
1475  const double swap = wlen1;
1476  wlen1 = wlen0;
1477  wlen0 = swap;
1478  }
1479  cpl_ensure(wlen1 > wlen0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1480 
1481  if (resol == VISIR_SPC_R_LRP) {
1482  phdisp = visir_spc_phys_lrp();
1483  } else {
1484  /* Construct the 1st degree dispersion relation
1485  based on the physical model */
1486  phdisp = cpl_polynomial_new(1);
1487 
1488  /* The dispersion */
1489  disp = (wlen1-wlen0)/(npix-1);
1490 
1491  skip_if (0);
1492 
1493  skip_if (cpl_polynomial_set_coeff(phdisp, &i1, disp));
1494 
1495  skip_if (cpl_polynomial_set_coeff(phdisp, &i0, wlen0-disp));
1496  }
1497 
1498  if ((resol == VISIR_SPC_R_HR || resol == VISIR_SPC_R_GHR) &&
1499  !visir_spc_optmod_side_is_A(&ins_settings)) {
1500  cpl_msg_info(cpl_func,"HR B-side WLMin, WLMax, Disp: %g %g %g", wlen0,
1501  wlen1, cpl_polynomial_get_coeff(phdisp, &i1));
1502  } else {
1503  cpl_msg_info(cpl_func,"WLMin, WLMax, Disp: %g %g %g", wlen0, wlen1,
1504  cpl_polynomial_get_coeff(phdisp, &i1));
1505  }
1506 
1507  if (resol == VISIR_SPC_R_GHR && ioffset != 0) {
1508  /* Another HRG Echelle order is requested
1509  - shift the 1st degree polynomial */
1510  const double dispi = visir_spc_optmod_echelle(&ins_settings,
1511  cpl_polynomial_get_coeff(phdisp, &i1), ioffset);
1512  const double wlen0i= visir_spc_optmod_echelle(&ins_settings,
1513  cpl_polynomial_get_coeff(phdisp, &i0), ioffset);
1514 
1515  skip_if (cpl_polynomial_set_coeff(phdisp, &i1, dispi));
1516 
1517  skip_if (cpl_polynomial_set_coeff(phdisp, &i0, wlen0i));
1518 
1519  cpl_msg_info(cpl_func, "WLc relative error(%d): %g", ioffset,
1520  (wlen0i - cpl_polynomial_eval_1d(phdisp, 1, NULL))/wlen0i);
1521  }
1522 
1523 
1524  end_skip;
1525 
1526  if (cpl_error_get_code() && phdisp != NULL) {
1527  cpl_polynomial_delete(phdisp);
1528  phdisp = NULL;
1529  }
1530 
1531  return phdisp;
1532 
1533 }
1534 
1535 
1536 /*----------------------------------------------------------------------------*/
1550 /*----------------------------------------------------------------------------*/
1551 
1552 cpl_bivector * visir_bivector_load_fits(const char * file,
1553  const char * labelx,
1554  const char * labely,
1555  int extnum)
1556 {
1557 
1558  cpl_bivector * result = NULL;
1559  cpl_table * table = NULL;
1560  cpl_propertylist * extlist = NULL;
1561  cpl_vector * xwrapper;
1562  cpl_vector * ywrapper;
1563  char * sext = NULL;
1564  double * prowx;
1565  double * prowy;
1566  int next;
1567  int nlines;
1568 
1569 
1570  bug_if (extnum < 1);
1571 
1572  next = cpl_fits_count_extensions(file);
1573  any_if("Could not load FITS table from (extension %d in) file: %s",
1574  extnum, file ? file : "<NULL>");
1575 
1576  skip_if_lt(next, extnum, "extensions in file: %s", file);
1577 
1578  table = cpl_table_load(file, extnum, 0);
1579  any_if ("Could not load FITS table from extension %d of %d in file: %s",
1580  extnum, next, file ? file : "<NULL>");
1581 
1582  extlist = cpl_propertylist_load_regexp(file, extnum, "EXTNAME", 0);
1583  if (cpl_propertylist_has(extlist, "EXTNAME")) {
1584  const char * extname = cpl_propertylist_get_string(extlist, "EXTNAME");
1585  sext = cpl_sprintf(" (EXTNAME=%s)", extname);
1586  }
1587 
1588  nlines = cpl_table_get_nrow(table);
1589  skip_if_lt(nlines, 2, "rows in table from extension %d%s of %d "
1590  "in %s", extnum, sext, next, file);
1591 
1592  prowx = cpl_table_get_data_double(table, labelx);
1593  any_if("Table from extension %d%s of %d in %s has no column %s",
1594  extnum, sext, next, file, labelx);
1595 
1596  prowy = cpl_table_get_data_double(table, labely);
1597  any_if("Table from extension %d%s of %d in %s has no column %s",
1598  extnum, sext, next, file, labely);
1599 
1600  xwrapper = cpl_vector_wrap(nlines, prowx);
1601  ywrapper = cpl_vector_wrap(nlines, prowy);
1602 
1603  result = cpl_bivector_wrap_vectors(xwrapper, ywrapper);
1604  cpl_table_unwrap(table, labelx);
1605  cpl_table_unwrap(table, labely);
1606 
1607  cpl_msg_info(cpl_func, "Read %d rows from extension %d%s of %d "
1608  "in %s [%g;%g]", nlines, extnum, sext, next, file,
1609  cpl_vector_get(xwrapper, 0),
1610  cpl_vector_get(ywrapper, nlines-1));
1611 
1612  end_skip;
1613 
1614  cpl_free(sext);
1615  cpl_table_delete(table);
1616  cpl_propertylist_delete(extlist);
1617 
1618  if (result && cpl_error_get_code()) {
1619  cpl_bivector_delete(result);
1620  result = NULL;
1621  }
1622 
1623  return result;
1624 
1625 }
1626 
1627 
1628 /*----------------------------------------------------------------------------*/
1655 /*----------------------------------------------------------------------------*/
1656 static cpl_error_code visir_spc_emission(cpl_bivector * emission,
1657  const cpl_vector * boundary,
1658  const cpl_bivector * temiss,
1659  const cpl_bivector * tqeff,
1660  const cpl_vector * vsymm,
1661  double temp)
1662 {
1663  cpl_bivector * tqeffi = NULL;
1664  cpl_vector * planck = NULL;
1665  const int npix = cpl_bivector_get_size(emission);
1666 
1667 
1668  bug_if(emission == NULL);
1669  bug_if(boundary == NULL);
1670  bug_if(temiss == NULL);
1671  bug_if(tqeff == NULL);
1672 
1673  /* npix is currently 256 */
1674  skip_if(npix <= 1);
1675 
1676  skip_if(cpl_vector_get_size(boundary) != npix + 1);
1677 
1678  planck = cpl_vector_new(npix);
1679  skip_if (0);
1680 
1681  /* The atmospheric emission is assumed to be equivalent to that of
1682  a Black Body at 253 K */
1683  cpl_photom_fill_blackbody(planck, CPL_UNIT_ENERGYRADIANCE,
1684  cpl_bivector_get_x(emission),
1685  CPL_UNIT_LENGTH, 253);
1686 
1687  skip_if (visir_vector_resample(cpl_bivector_get_y(emission),
1688  boundary, temiss));
1689 
1690  /* Convolve to reflect the instrument resolution */
1691  skip_if (visir_vector_convolve_symm(cpl_bivector_get_y(emission),
1692  vsymm));
1693 
1694  skip_if (cpl_vector_multiply(cpl_bivector_get_y(emission), planck));
1695 
1696  /* The telescope emission is assumed to be equivalent to that of
1697  a Black Body */
1698  cpl_photom_fill_blackbody(planck, CPL_UNIT_ENERGYRADIANCE,
1699  cpl_bivector_get_x(emission),
1700  CPL_UNIT_LENGTH, temp);
1701 
1702  /* The telescope emissivity is assumed to be uniform at 0.12 */
1703  skip_if (cpl_vector_multiply_scalar(planck, 0.12));
1704 
1705  /* Add the telescope emission to the atmospheric */
1706  skip_if (cpl_vector_add(cpl_bivector_get_y(emission), planck));
1707 
1708  /* Multiply by the detector quantum efficiency */
1709  tqeffi = cpl_bivector_duplicate(emission);
1710  skip_if (cpl_bivector_interpolate_linear(tqeffi, tqeff));
1711 
1712  skip_if (cpl_vector_multiply(cpl_bivector_get_y(emission),
1713  cpl_bivector_get_y(tqeffi)));
1714 
1715  end_skip;
1716 
1717  cpl_bivector_delete(tqeffi);
1718  cpl_vector_delete(planck);
1719 
1720  return cpl_error_get_code();
1721 }
1722 
1723 
1724 /*----------------------------------------------------------------------------*/
1747 /*----------------------------------------------------------------------------*/
1748 static cpl_vector * cpl_spc_convolve_init(int maxlen, double slitw,
1749  double fwhm, int doplot)
1750 {
1751 
1752  const double sigma = fwhm * CPL_MATH_SIG_FWHM;
1753  const int ihtophat = (int)slitw/2;
1754  const int gausshlen = 1 + 5 * sigma + ihtophat < maxlen/2
1755  ? 1 + 5 * sigma + ihtophat : maxlen/2 - 1;
1756  /* convolen must be at least twice the gausshlen */
1757  const int convolen = 1 + 10 * sigma + 8*ihtophat;
1758  cpl_vector * self = cpl_vector_new(gausshlen);
1759  cpl_vector * tophat = cpl_vector_new(convolen);
1760  int i;
1761 
1762  /* Easiest way to fill with a Gaussian is via a CPL image */
1763  cpl_image * iself = cpl_image_wrap_double(gausshlen, 1,
1764  cpl_vector_get_data(self));
1765 
1766 
1767  skip_if (0);
1768 
1769  skip_if( slitw <= 0.0);
1770  skip_if( fwhm <= 0.0);
1771  skip_if( convolen < 2 * gausshlen); /* This would indicate a bug */
1772 
1773  /* Place the top point of the Gaussian on left-most pixel */
1774  skip_if (cpl_image_fill_gaussian(iself, 1.0, 1.0, CPL_MATH_SQRT2PI,
1775  sigma, 1.0));
1776 
1777  if (doplot > 2) visir_vector_plot("set grid;", "t 'Right Half of Gaussian' "
1778  "w linespoints", "", self);
1779 
1780  /* The number of non-zero elements is 1+2*ihtophat */
1781  skip_if( cpl_vector_fill(tophat, 0.0));
1782 
1783  for (i = convolen/2-ihtophat; i < 1+convolen/2+ihtophat; i++)
1784  skip_if (cpl_vector_set(tophat, i, 1.0/(1.0+2.0*ihtophat)));
1785 
1786  /* Convolve the Top-hat with the Gaussian */
1787  skip_if (visir_vector_convolve_symm(tophat, self));
1788 
1789  if (doplot > 2) visir_vector_plot("set grid;","t 'Full Width Convolution' "
1790  "w linespoints", "", tophat);
1791 
1792  /* Overwrite the Gaussian with the Right Half of the convolution of the
1793  Top-hat + Gausssian */
1794 #if 1
1795  memcpy(cpl_vector_get_data(self),
1796  cpl_vector_get_data(tophat) + convolen/2,
1797  sizeof(double)*gausshlen);
1798 #else
1799  /* Equivalent, but slower */
1800  for (i = 0 ; i < gausshlen; i++)
1801  skip_if (cpl_vector_set(self, i, cpl_vector_get(tophat,
1802  i + convolen/2)));
1803 #endif
1804 
1805  skip_if (0);
1806 
1807  cpl_msg_info(cpl_func, "Convolving Model Spectrum, Gauss-sigma=%g, "
1808  "Tophat-width=%d, Truncation-Error=%g with width=%d", sigma,
1809  1+2*ihtophat,
1810  cpl_vector_get(self,gausshlen-1)/cpl_vector_get(self,0),
1811  2*gausshlen-1);
1812 
1813  if (doplot > 1) visir_vector_plot("set grid;","t 'Right Half of Convolution"
1814  "' w linespoints", "", self);
1815 
1816  end_skip;
1817 
1818  cpl_vector_delete(tophat);
1819  cpl_image_unwrap(iself);
1820 
1821  if (cpl_error_get_code()) {
1822  cpl_vector_delete(self);
1823  self = NULL;
1824  }
1825 
1826  return self;
1827 
1828 }
1829 
1830 
1831 static cpl_error_code
1832 fit_gaussians(const cpl_image * flipped, const cpl_vector * error,
1833  cpl_size icollo, cpl_size icolhi,
1834  cpl_propertylist * qclist)
1835 {
1836  cpl_size nrow = cpl_image_get_size_y(flipped);
1837  cpl_size ncol = cpl_image_get_size_x(flipped);
1838  icollo = CX_MAX(1, icollo);
1839  icolhi = CX_MIN(ncol, icolhi);
1840  cpl_errorstate cleanstate = cpl_errorstate_get();
1841  double sigs[nrow];
1842  double sigs_err = 0.;
1843  double peaks[nrow];
1844  double peaks_err = 0.;
1845  size_t nmeas = 0;
1846  for (cpl_size row = 0; row < nrow; row++) {
1847  const cpl_binary * dmask = cpl_image_get_bpm_const(flipped) ?
1848  cpl_mask_get_data_const(cpl_image_get_bpm_const(flipped)) : NULL;
1849  const double *dflipped = cpl_image_get_data_double_const(flipped);
1850  double * dx = cpl_malloc(ncol * sizeof(*dx));
1851  double * dy = cpl_malloc(ncol * sizeof(*dy));
1852  double * dye = cpl_malloc(ncol * sizeof(*dye));
1853  cpl_vector * x;
1854  cpl_vector * y;
1855  cpl_vector * ye;
1856  size_t n = 0;
1857  for (cpl_size i = icollo; i <= icolhi; i++) {
1858  if (dmask == NULL || !dmask[row * ncol + i]) {
1859  dx[n] = i;
1860  dy[n] = dflipped[row * ncol + (i - 1)];
1861  dye[n] = cpl_vector_get(error, (i - 1));
1862  n++;
1863  }
1864  }
1865  if (n > 0) {
1866  x = cpl_vector_wrap(n, dx);
1867  y = cpl_vector_wrap(n, dy);
1868  ye = cpl_vector_wrap(n, dye);
1869  double x0, sigma, sigma_err, peak, peak_err;
1870  fit_1d_gauss(x, y, ye, &x0, NULL, &peak, &peak_err, &sigma, &sigma_err);
1871  if (cpl_error_get_code() != CPL_ERROR_NONE) {
1872  cpl_msg_debug(cpl_func, "FIT row %lld failed", row);
1873  cpl_errorstate_set(cleanstate);
1874  }
1875  else {
1876  sigs[nmeas] = sigma;
1877  peaks[nmeas] = peak;
1878  sigs_err += sigma * sigma;
1879  peaks_err += peak * peak;
1880  nmeas++;
1881  cpl_msg_debug(cpl_func, "FIT row %lld x %g sig %g +- %g "
1882  "peak %g +- %g",
1883  row, x0, sigma, sigma_err, peak, peak_err);
1884  }
1885  cpl_vector_delete(x);
1886  cpl_vector_delete(y);
1887  cpl_vector_delete(ye);
1888  }
1889  else {
1890  cpl_free(dx);
1891  cpl_free(dy);
1892  cpl_free(dye);
1893  }
1894  }
1895  cpl_vector * sigv = cpl_vector_wrap(nmeas, sigs);
1896  cpl_vector * peakv = cpl_vector_wrap(nmeas, peaks);
1897  double medsigma = cpl_vector_get_median(sigv);
1898  double medsigma_err = sqrt(sigs_err) * sqrt(CPL_MATH_PI_2) / nmeas;
1899  double medpeak = cpl_vector_get_median(peakv);
1900  double medpeak_err = sqrt(peaks_err) * sqrt(CPL_MATH_PI_2) / nmeas;
1901  cpl_msg_info(cpl_func, "Median FWHM of spectrum: %g +- %g, Peak %g +- %g",
1902  medsigma, medsigma_err, medpeak, medpeak_err);
1903  cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT FWHM",
1904  medsigma * 2.355);
1905  cpl_propertylist_set_comment(qclist, "ESO QC GAUSSFIT FWHM", "[pix]");
1906  cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT FWHM_ERR",
1907  medsigma_err * 2.355);
1908  cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT PEAK", medpeak);
1909  cpl_propertylist_set_comment(qclist, "ESO QC GAUSSFIT PEAK", "[adu/s]");
1910  cpl_propertylist_append_double(qclist, "ESO QC GAUSSFIT PEAK_ERR",
1911  medpeak_err);
1912  cpl_vector_unwrap(sigv);
1913  cpl_vector_unwrap(peakv);
1914 
1915  return cpl_error_get_code();
1916 }
1917 
1918 /*----------------------------------------------------------------------------*/
1935 /*----------------------------------------------------------------------------*/
1936 static cpl_bivector * visir_spc_extract(cpl_image * flipped,
1937  cpl_propertylist * qclist,
1938  cpl_image ** pweight2d,
1939  int doplot, int bkgcorrect)
1940 {
1941  const int ncol = cpl_image_get_size_x(flipped);
1942  const int npix = cpl_image_get_size_y(flipped);
1943 
1944  cpl_bivector * result = NULL;
1945  cpl_vector * spectrum = NULL;
1946  cpl_vector * error = NULL;
1947  cpl_vector * col = NULL;
1948 
1949  cpl_image * spatial = NULL;
1950  cpl_image * iweight = NULL;
1951  cpl_vector * row = NULL;
1952  cpl_image * imrow = NULL;
1953 
1954  double * pweight = NULL;
1955 
1956  cpl_apertures * objects = NULL;
1957  cpl_mask * binary = NULL;
1958  cpl_image * locnoise = NULL;
1959 
1960  double xfwhm; /* FWHM of brightest object */
1961  double xcentro; /* X-Centroid of brightest object */
1962 
1963  int i, j;
1964  int is_rejected;
1965 
1966  const double sigma = VISIR_SPECTRO_SIGMA; /* Assume signal at this level */
1967  double sp_median;
1968  double stdev2d, min, max, yfwhm;
1969  double weight_2norm;
1970  /* Position of the widest signal region */
1971  cpl_size ifwhm, jfwhm;
1972  int mspix;
1973  /* Low and High pixel of the widest signal-less region */
1974  int ilnoise, ihnoise;
1975  const int is_echelle = ncol <= 2 * (whechelle + 1);
1976 
1977 
1978  cpl_ensure(pweight2d != NULL, CPL_ERROR_NULL_INPUT, NULL);
1979 
1980  cpl_ensure(sigma > 0.0, CPL_ERROR_UNSUPPORTED_MODE, NULL);
1981 
1982  *pweight2d = NULL;
1983 
1984  skip_if (0);
1985 
1986  /* Compute spatial weights:
1987  mean-subtract each row and average + normalize */
1988 
1989  if (bkgcorrect) {
1990  cpl_image * imean = cpl_image_duplicate(flipped);
1991  for (int h = 0; h < cpl_image_get_size_y(flipped); h++) {
1992  cpl_vector * n = cpl_vector_new_from_image_row(flipped, h+1);
1993  double mean = cpl_vector_get_median(n);
1994  cpl_vector_delete(n);
1995  for (int g = 0; g < cpl_image_get_size_x(flipped); g++) {
1996  cpl_image_set(imean, g+1, h + 1, mean);
1997  }
1998  }
1999  cpl_image_subtract(flipped, imean);
2000  cpl_image_delete(imean);
2001  }
2002 
2003  if (!is_echelle) {
2004  /* All but HR Grism has a negative signal equal to the positive
2005  i.e. the mean is zero */
2006  /* FIXME: Not true for large offsets (or very extended objects) */
2007  cpl_msg_info(cpl_func, "Combined image has mean: %g",
2008  cpl_image_get_mean(flipped));
2009 
2010  col = cpl_vector_new(npix);
2011  skip_if (0);
2012 
2013  /* Subtract the mean from each row/wavelength */
2014  pweight = cpl_image_get_data_double(flipped);
2015  for (j=0; j < npix; j++, pweight += ncol) {
2016  double mean;
2017 
2018  imrow = cpl_image_wrap_double(1, ncol, pweight);
2019  skip_if (0);
2020 
2021  mean = cpl_image_get_mean(imrow);
2022  skip_if (0);
2023 
2024  skip_if (cpl_vector_set(col, j, mean));
2025 
2026  skip_if (cpl_image_subtract_scalar(imrow, mean));
2027 
2028  (void)cpl_image_unwrap(imrow);
2029  imrow = NULL;
2030 
2031  }
2032 
2033  if (doplot > 1) visir_vector_plot("set grid;","t 'Estimated Background'"
2034  " w linespoints", "", col);
2035  cpl_vector_delete(col);
2036  col = NULL;
2037  }
2038 
2039  /* The st.dev. of the noise */
2040  stdev2d = visir_img_phot_sigma_clip(flipped)/sqrt(npix);
2041  skip_if (0);
2042 
2043  cpl_msg_info(cpl_func, "St.Dev. on noise in 2D-combined image: %g",
2044  stdev2d);
2045 
2046  /* Average the spectral dimension */
2047  spatial = cpl_image_collapse_create(flipped, 0);
2048  skip_if (0);
2049  skip_if (cpl_image_divide_scalar(spatial, npix));
2050 
2051  iweight = cpl_image_duplicate(spatial);
2052 
2053  /* Reject noise from spatial */
2054  sp_median = cpl_image_get_median(spatial);
2055  binary = cpl_mask_threshold_image_create(spatial, sp_median - sigma * stdev2d,
2056  sp_median + sigma * stdev2d);
2057 
2058  if (cpl_mask_count(binary) == ncol) {
2059  (void)cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
2060  "%d spatial weights too noisy. sigma=%g. "
2061  "stdev2d=%g. Spatial median=%g", ncol,
2062  sigma, stdev2d, sp_median);
2063  skip_if (1);
2064  }
2065 
2066 
2067  bug_if (cpl_image_reject_from_mask(spatial, binary));
2068 
2069  bug_if (cpl_image_get_maxpos(spatial, &ifwhm, &jfwhm));
2070 
2071  if (doplot > 1) {
2072  visir_image_col_plot("","t 'Most intense column' w linespoints",
2073  "", flipped, ifwhm, ifwhm, 1);
2074  visir_image_row_plot("set grid;", "t 'Combined image with "
2075  "spectral direction collapsed' w linespoints",
2076  "", spatial, 1, 1, 1);
2077  }
2078 
2079  max = cpl_image_get(spatial, ifwhm, 1, &is_rejected);
2080  bug_if(is_rejected);
2081  if (max <= 0.0) {
2082  (void)cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
2083  "Cannot compute FWHM on a collapsed "
2084  "spectrum with a non-positive maximum: %g "
2085  "(at i=%d)", max, (int)ifwhm);
2086  skip_if (1);
2087  }
2088 
2089  skip_if (cpl_image_get_fwhm(spatial, ifwhm, 1, &xfwhm, &yfwhm));
2090 
2091  /* Find centroid in spatial */
2092  for (ilnoise = ifwhm; ilnoise > 0 &&
2093  !cpl_image_is_rejected(spatial, ilnoise, 1); ilnoise--);
2094  bug_if (0);
2095  for (ihnoise = ifwhm; ihnoise <= ncol &&
2096  !cpl_image_is_rejected(spatial, ihnoise, 1); ihnoise++);
2097  bug_if (0);
2098  /* There may be no negative weights at all */
2099  if (!ilnoise) ilnoise = 1;
2100  if (ihnoise > ncol) ihnoise = ncol;
2101 
2102  xcentro = cpl_image_get_centroid_x_window(spatial, ilnoise, 1, ihnoise, 1);
2103 
2104  cpl_msg_info(cpl_func, "Spatial FWHM(%d:%d:%d:%g): %g", (int)ilnoise,
2105  (int)ifwhm, (int)ihnoise, xcentro, xfwhm);
2106 
2107  /* Create weights that have an absolute sum of 1 - as an image */
2108  skip_if (cpl_image_normalise(iweight, CPL_NORM_ABSFLUX));
2109 
2110  if (doplot > 1) visir_image_row_plot("set grid;", "t 'Cleaned, normalized "
2111  "combined image with spectral direction"
2112  " averaged' w linespoints", "",
2113  iweight, 1, 1, 1);
2114 
2115  weight_2norm = sqrt(cpl_image_get_sqflux(iweight));
2116 
2117  cpl_msg_info(cpl_func, "2-norm of weights: %g", weight_2norm);
2118 
2119 
2120 
2121  /* Determine st.dev. on noise at signal-less pixels */
2122  if (is_echelle) {
2123  int ileft = 5;
2124  int iright = ncol - 5;
2125  cpl_binary * pbin;
2126 
2127 
2128  if (ileft > xcentro - xfwhm * 2)
2129  ileft = xcentro - xfwhm * 2;
2130  if (iright < xcentro + xfwhm * 2)
2131  iright = xcentro + xfwhm * 2;
2132 
2133  cpl_msg_info(cpl_func, "HRG pixels of noise: [1 %d] [%d %d]", ileft,
2134  iright, ncol);
2135 
2136  bug_if(cpl_mask_xor(binary, binary));
2137 
2138  pbin = cpl_mask_get_data(binary);
2139  bug_if (0);
2140 
2141  for (i = 0; i < ncol; i++) pbin[i] = CPL_BINARY_0;
2142  for (i = 0; i < ileft; i++) pbin[i] = CPL_BINARY_1;
2143  for (i = iright; i < ncol; i++) pbin[i] = CPL_BINARY_1;
2144 
2145  }
2146  skip_if (0);
2147 
2148  mspix = cpl_mask_count(binary);
2149  cpl_msg_info(cpl_func, "Pixels of noise(%g +/- %g*%g): %d",
2150  sp_median, stdev2d, sigma, mspix);
2151  skip_if (0);
2152 
2153  if (mspix < 2) {
2154  /* No noise pixels found */
2155  cpl_msg_error(cpl_func, "Cannot estimate spectrum noise with just %d "
2156  "pixels of noise", mspix);
2157  visir_error_set(CPL_ERROR_DATA_NOT_FOUND);
2158  skip_if (1);
2159  }
2160 
2161  locnoise = cpl_image_new_from_mask(binary);
2162  cpl_mask_delete(binary);
2163  binary = NULL;
2164 
2165  skip_if (0);
2166 
2167  error = cpl_vector_new(npix);
2168  skip_if (0);
2169 
2170 
2171  /* Compute for each wavelength the noise */
2172  for (j=0; j < npix; j++) {
2173 
2174  double npp, stdev1d;
2175 
2176 
2177  imrow = cpl_image_extract(flipped, 1, j+1, ncol, j+1);
2178 
2179  skip_if (0);
2180 
2181  objects = cpl_apertures_new_from_image(imrow, locnoise);
2182  cpl_image_delete(imrow);
2183  imrow = NULL;
2184 
2185  skip_if (0);
2186 
2187  stdev1d = cpl_apertures_get_stdev(objects, 1);
2188  cpl_apertures_delete(objects);
2189  objects = NULL;
2190 
2191  /* The noise per pixel is defined as the Standard Deviation
2192  on the noise (computed from the part of the signal that
2193  has no object signal) multiplied by the 2-norm of the
2194  noise-thresholded spatial weights */
2195 
2196  npp = weight_2norm * stdev1d;
2197 
2198  skip_if (cpl_vector_set(error, j, npp));
2199  }
2200 
2201  /* Spectrum noise computation done */
2202 
2203 
2204  /* Iterate through the spatial dimension - sum up the weighted column */
2205  for (i=1; i <= ncol; i++) {
2206  const double weight = cpl_image_get(iweight, i, 1, &is_rejected);
2207 
2208  skip_if (0);
2209  if (is_rejected) {
2210  /* This would require a whole column to be rejected */
2211  visir_error_set(CPL_ERROR_DATA_NOT_FOUND);
2212  skip_if (1);
2213  }
2214 
2215  /* The sigma-clipping may cause many columns to be zero */
2216  if (weight == 0) continue;
2217 
2218  col = cpl_vector_new_from_image_column(flipped, i); /* or medcorr */
2219  skip_if (0);
2220 
2221  skip_if (cpl_vector_multiply_scalar(col, weight));
2222 
2223  if (spectrum == NULL) {
2224  spectrum = col;
2225  } else {
2226  skip_if (cpl_vector_add(spectrum, col));
2227  cpl_vector_delete(col);
2228  }
2229  col = NULL;
2230  }
2231 
2232  fit_gaussians(flipped, error, ifwhm - 20, ifwhm + 20, qclist);
2233 
2234  /* assert( spectrum ); */
2235 
2236  min = cpl_vector_get_min(spectrum);
2237  if (min <0) cpl_msg_warning(cpl_func, "Extracted spectrum has negative "
2238  "intensity: %g", min);
2239 
2240  /* Create 2D-weight map by replicating the 1D-weights over the
2241  wavelengths */
2242 
2243  *pweight2d = cpl_image_new(ncol, npix, CPL_TYPE_DOUBLE);
2244 
2245  for (j=1; j <= npix; j++)
2246  skip_if (cpl_image_copy(*pweight2d, iweight, 1, j));
2247 
2248  if (doplot > 0) visir_image_plot("", "t 'The weight map'", "", *pweight2d);
2249 
2250  bug_if(visir_spectro_qclist_obs(qclist, xfwhm, xcentro));
2251 
2252  end_skip;
2253 
2254  cpl_image_delete(locnoise);
2255  cpl_mask_delete(binary);
2256  cpl_image_delete(spatial);
2257  cpl_apertures_delete(objects);
2258  cpl_vector_delete(col);
2259  cpl_vector_delete(row);
2260  cpl_image_delete(imrow);
2261  cpl_image_delete(iweight);
2262 
2263  if (cpl_error_get_code()) {
2264  cpl_vector_delete(spectrum);
2265  cpl_vector_delete(error);
2266  } else {
2267 
2268  result = cpl_bivector_wrap_vectors(spectrum, error);
2269 
2270  if (doplot > 2) visir_bivector_plot("", "t 'error versus spectrum'",
2271  "", result);
2272  }
2273 
2274  return result;
2275 }
2276 
2277 /*----------------------------------------------------------------------------*/
2299 /*----------------------------------------------------------------------------*/
2300 static cpl_error_code visir_spectro_fill(cpl_vector * self,
2301  const cpl_polynomial * disp,
2303 {
2304 
2305  visir_spectrum_model * mymodel = (visir_spectrum_model*)model;
2306  const cpl_size npix = cpl_vector_get_size(self);
2307 
2308  cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
2309  cpl_ensure_code(model, CPL_ERROR_NULL_INPUT);
2310  cpl_ensure_code(disp, CPL_ERROR_NULL_INPUT);
2311 
2312  cpl_vector * wavelength = cpl_vector_new(npix);
2313  cpl_bivector * emission = cpl_bivector_wrap_vectors(wavelength, self);
2314  cpl_vector * boundary = cpl_vector_new(npix + 1);
2315 
2316  /* Compute the wavelengths of the spectrum
2317  according to the physical model */
2318  skip_if (cpl_vector_fill_polynomial(cpl_bivector_get_x(emission),
2319  disp, 1, 1));
2320  skip_if (cpl_vector_fill_polynomial(boundary, disp, 0.5, 1));
2321 
2322  /* Get the emission at those wavelengths */
2323  skip_if (visir_spc_emission(emission, boundary, mymodel->lines,
2324  mymodel->tqeff, mymodel->vsymm,
2325  mymodel->temp));
2326  end_skip;
2327 
2328  cpl_bivector_unwrap_vectors(emission);
2329  cpl_vector_delete(wavelength);
2330  cpl_vector_delete(boundary);
2331 
2332  return cpl_error_get_code();
2333 }
2334 
2335 
2336 
2337 /*----------------------------------------------------------------------------*/
2352 /*----------------------------------------------------------------------------*/
2353 static cpl_error_code visir_spectro_refine(cpl_polynomial * self,
2354  const cpl_vector * xc_vector,
2355  visir_spectrum_model * pmymodel,
2356  const cpl_polynomial * phdisp,
2357  int hsize, cpl_boolean doplot,
2358  double * pxc,
2359  cpl_boolean * pdidshift,
2360  double * pdelta)
2361 {
2362  const int subres = VISIR_XC_SUBSEARCH;
2363  cpl_polynomial * shifted = NULL;
2364 #ifdef VISIR_SPC_CAL_HIGH
2365  const int fitdeg = 2;
2366  double pixstep = 0.5;
2367  double pixtol = 1e-5;
2368  const int maxite = fitdeg * 200;
2369  int maxfail = 3;
2370  int maxcont = 3;
2371  const int clines = (int)(cpl_bivector_get_size(pmymodel->lines) *
2372  cpl_vector_get_size(xc_vector));
2373  cpl_errorstate prestate = cpl_errorstate_get();
2374 #endif
2375 
2376  cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
2377  cpl_ensure_code(xc_vector, CPL_ERROR_NULL_INPUT);
2378  cpl_ensure_code(pmymodel, CPL_ERROR_NULL_INPUT);
2379  cpl_ensure_code(phdisp, CPL_ERROR_NULL_INPUT);
2380  cpl_ensure_code(pxc, CPL_ERROR_NULL_INPUT);
2381  cpl_ensure_code(pdidshift, CPL_ERROR_NULL_INPUT);
2382  cpl_ensure_code(pdelta, CPL_ERROR_NULL_INPUT);
2383 
2384  skip_if(cpl_polynomial_copy(self, phdisp));
2385 
2386 #ifdef VISIR_SPC_CAL_HIGH
2388  (self, fitdeg, xc_vector, 1, clines,
2389  (irplib_base_spectrum_model*)pmymodel,
2390  visir_spectro_fill, pixtol, pixstep,
2391  hsize, maxite, maxfail, maxcont, doplot, pxc) || *pxc <= 0.0) {
2392 
2393  irplib_error_recover(prestate, "Could not optimize %d "
2394  "coefficients, trying shifting", fitdeg);
2395  skip_if(cpl_polynomial_copy(self, phdisp));
2396 
2397  skip_if(visir_polynomial_shift_1d_from_correlation
2398  (self, xc_vector, (irplib_base_spectrum_model*) pmymodel,
2399  visir_spectro_fill, hsize, subres, doplot, pxc, pdelta));
2400  *pdidshift = CPL_TRUE;
2401 
2402  /* Retry optimization */
2403  shifted = cpl_polynomial_duplicate(self);
2404 
2406  (self, fitdeg, xc_vector, 1, clines,
2407  (irplib_base_spectrum_model*)pmymodel,
2408  visir_spectro_fill, pixtol, pixstep,
2409  hsize, maxite, maxfail, maxcont, doplot, pxc) || *pxc <= 0.0) {
2410 
2411  irplib_error_recover(prestate, "Could not re-optimize %d "
2412  "coefficients, keeping shifted", fitdeg);
2413  skip_if(cpl_polynomial_copy(self, shifted));
2414  }
2415  }
2416 
2417 #else
2418  skip_if(visir_polynomial_shift_1d_from_correlation
2419  (self, xc_vector, (irplib_base_spectrum_model*) pmymodel,
2420  visir_spectro_fill, hsize, subres, doplot, pxc, pdelta));
2421  *pdidshift = CPL_TRUE;
2422 #endif
2423 
2424  error_if (*pxc <= 0.0, CPL_ERROR_DATA_NOT_FOUND, "Atmospheric and Model "
2425  "Spectra have non-positive cross-correlation (%g pixel shift): "
2426  "%g", *pdelta, *pxc);
2427 
2428  end_skip;
2429 
2430  cpl_polynomial_delete(shifted);
2431 
2432  return cpl_error_get_code();
2433 
2434 }
2435 
2436 /*----------------------------------------------------------------------------*/
2459 /*----------------------------------------------------------------------------*/
2460 static cpl_error_code
2461 visir_polynomial_shift_1d_from_correlation(cpl_polynomial * self,
2462  const cpl_vector * obs,
2464  cpl_error_code (*filler)
2465  (cpl_vector *,
2466  const cpl_polynomial *,
2468  int hsize,
2469  int subres,
2470  cpl_boolean doplot,
2471  double * pxc, double *pshift)
2472 {
2473  const int nobs = cpl_vector_get_size(obs);
2474  cpl_polynomial * cand = NULL;
2475  cpl_bivector * xcplot = NULL;
2476  double * xcplotx = NULL;
2477  double * xcploty = NULL;
2478  cpl_vector * mspec1d = NULL;
2479  cpl_vector * vxc;
2480  double bestxc = -1.0;
2481  double bestdelta = -1.0; /* avoid false unint warning */
2482  int bestxxc = -1; /* avoid false unint warning */
2483  int i;
2484 
2485  cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
2486  cpl_ensure_code(obs != NULL, CPL_ERROR_NULL_INPUT);
2487  cpl_ensure_code(model != NULL, CPL_ERROR_NULL_INPUT);
2488  cpl_ensure_code(filler != NULL, CPL_ERROR_NULL_INPUT);
2489  cpl_ensure_code(subres > 0, CPL_ERROR_ILLEGAL_INPUT);
2490  cpl_ensure_code(hsize > 0, CPL_ERROR_ILLEGAL_INPUT);
2491 
2492  cand = cpl_polynomial_new(1);
2493  mspec1d = cpl_vector_new(2 * hsize + nobs);
2494  vxc = cpl_vector_new(2 * hsize + 1);
2495  if (doplot) {
2496  xcplot = cpl_bivector_new(subres * (2 * hsize + 1));
2497  xcplotx = cpl_bivector_get_x_data(xcplot);
2498  xcploty = cpl_bivector_get_y_data(xcplot);
2499  }
2500 
2501  /* subdelta search is in the range [0; 1[ */
2502  for (i = 0; i < subres; i++) {
2503  const double delta = i / (double)subres;
2504  double xc;
2505  int ixc;
2506 
2507  bug_if (cpl_polynomial_copy(cand, self));
2508  bug_if (cpl_polynomial_shift_1d(cand, 0, delta - hsize));
2509 
2510  skip_if (filler(mspec1d, cand, model));
2511 
2512  ixc = cpl_vector_correlate(vxc, mspec1d, obs);
2513  xc = cpl_vector_get(vxc, ixc);
2514 
2515  if (xc > bestxc) {
2516  bestxc = xc;
2517  bestxxc = ixc - hsize;
2518  bestdelta = delta + bestxxc;
2519  cpl_msg_debug(cpl_func, "Shifting %g = %d + %g pixels (XC=%g)",
2520  bestdelta, bestxxc, delta, bestxc);
2521  }
2522  if (doplot) {
2523  int j;
2524  for (j = 0; j <= 2 * hsize; j++) {
2525  const double xcj = cpl_vector_get(vxc, j);
2526  xcplotx[i + j * subres] = (double)(j - hsize) + delta;
2527  xcploty[i + j * subres] = xcj;
2528  }
2529  }
2530  }
2531 
2532 #ifdef IRPLIB_SPC_DUMP
2533  /* Need irplib_wavecal.c rev. 1.12 through 1.15 */
2534  irplib_polynomial_dump_corr_step(self, vxc, "Shift");
2535 #endif
2536 
2537  skip_if(cpl_polynomial_shift_1d(self, 0, bestdelta));
2538 
2539  /* Verify correctness of shift, at hsize = 0 */
2540  cpl_vector_set_size(vxc, 1);
2541  cpl_vector_set_size(mspec1d, nobs);
2542  skip_if (filler(mspec1d, self, model));
2543  bug_if(cpl_vector_correlate(vxc, mspec1d, obs));
2544 
2545  if (doplot) {
2546  char * title = cpl_sprintf("t 'Cross-correlation of %d-pixel spectrum "
2547  "(max=%.4g at %g pixel)' w points", nobs,
2548  cpl_vector_get(vxc, 0), bestdelta);
2549 
2550  cpl_plot_bivector("set grid;set xlabel 'Offset [pixel]';set ylabel "
2551  "'Cross-correlation';", title, "", xcplot);
2552  cpl_free(title);
2553 
2554  irplib_plot_spectrum_and_model(obs, self, model, filler);
2555  }
2556 
2557  cpl_msg_info(cpl_func, "Shifting %g = %d + %g pixels (XC: %g <=> %g)",
2558  bestdelta, bestxxc, bestdelta - (double)bestxxc,
2559  cpl_vector_get(vxc, 0), bestxc);
2560 
2561  if (pxc != NULL) *pxc = cpl_vector_get(vxc, 0);
2562  if (pshift != NULL) *pshift = bestdelta;
2563 
2564  end_skip;
2565 
2566  cpl_vector_delete(mspec1d);
2567  cpl_polynomial_delete(cand);
2568  cpl_vector_delete(vxc);
2569  cpl_bivector_delete(xcplot);
2570 
2571  return cpl_error_get_code();
2572 
2573 }
2574 
2575 /*----------------------------------------------------------------------------*/
2580 /*----------------------------------------------------------------------------*/
2581 static cpl_polynomial * visir_spc_phys_lrp(void)
2582 {
2583  /* commissioning mar2015 values */
2584  const double xval[] = {146, 191, 223, 355, 470, 649, 669, 747, 792};
2585  const double yval[] = {8.200e-06, 8.3e-06, 9.50e-06,
2586  9.660e-06, 11.e-06, 11.7e-06,
2587  12.54e-06, 12.76e-06, 13.02e-06 };
2588  /* Values provided by ELagadec com 2012, RMS is 15.7144 nm */
2589  //const double xval[] = {32, 227, 355, 529, 555, 661, 671, 695, 719, 795};
2590  //const double yval[] = { 7.999e-6, 9.568e-6, 10.546e-6, 11.726e-6,
2591  // 11.905e-6, 12.523e-6, 12.629e-6, 12.748e-6,
2592  // 12.877e-6, 13.261e-6};
2593 
2594  const cpl_size maxdeg1d = 2; /* The polynomial degree */
2595 
2596  cpl_polynomial * self = cpl_polynomial_new(1);
2597  const cpl_boolean sampsym = CPL_FALSE;
2598  const size_t nvals = sizeof(xval)/sizeof(*xval);
2599 
2600  IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual)
2601  cpl_matrix * xmatrix = cpl_matrix_wrap(1, nvals, (double*)xval);
2602  cpl_vector * yvector = cpl_vector_wrap(nvals, (double*)yval);
2603  IRPLIB_DIAG_PRAGMA_POP;
2604  cpl_vector * fitres = cpl_vector_new(nvals);
2605 
2606  const cpl_error_code error = cpl_polynomial_fit(self, xmatrix, &sampsym,
2607  yvector, NULL,
2608  CPL_FALSE, NULL, &maxdeg1d)
2609  || cpl_vector_fill_polynomial_fit_residual(fitres, yvector, NULL, self,
2610  xmatrix, NULL);
2611 
2612  const double mse = cpl_vector_product(fitres, fitres) / (double)nvals;
2613 
2614  (void)cpl_matrix_unwrap(xmatrix);
2615  (void)cpl_vector_unwrap(yvector);
2616  cpl_vector_delete(fitres);
2617 
2618  if (error) {
2619  cpl_error_set_where(cpl_func);
2620  cpl_polynomial_delete(self);
2621  self = NULL;
2622  } else {
2623  cpl_msg_info(cpl_func, "Fitted %d degree 1D-polynomial to %u "
2624  "wavelengths with a root mean square error [m]: %g",
2625  (int)maxdeg1d, (unsigned)nvals, sqrt(mse));
2626  }
2627 
2628  return self;
2629 }
2630 
2631 /*----------------------------------------------------------------------------*/
2638 /*----------------------------------------------------------------------------*/
2639 static double visir_spc_get_dispersion(const cpl_polynomial * self, double xval)
2640 {
2641 
2642  cpl_errorstate prestate = cpl_errorstate_get();
2643  double disp;
2644 
2645  (void)cpl_polynomial_eval_1d(self, xval, &disp);
2646 
2647  if (!cpl_errorstate_is_equal(prestate)) {
2648  (void)cpl_error_set_where(cpl_func);
2649  }
2650 
2651  return disp;
2652 }
double visir_pfits_get_slitwidth(const cpl_propertylist *self)
The slit width in Arcseconds.
Definition: visir_pfits.c:721
const char * visir_pfits_get_resol(const cpl_propertylist *self)
The spectral resolution.
Definition: visir_pfits.c:787
int visir_parameterlist_get_int(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR integer parameter.
const cpl_propertylist * irplib_framelist_get_propertylist_const(const irplib_framelist *self, int pos)
Get the propertylist of the specified frame in the framelist.
cpl_error_code irplib_plot_spectrum_and_model(const cpl_vector *self, const cpl_polynomial *disp1d, irplib_base_spectrum_model *model, cpl_error_code(*filler)(cpl_vector *, const cpl_polynomial *, irplib_base_spectrum_model *))
Plot a 1D spectrum and one from a model.
double visir_pfits_get_pixspace(const cpl_propertylist *self)
The pixel spacing.
Definition: visir_pfits.c:694
cpl_error_code irplib_polynomial_find_1d_from_correlation_all(cpl_polynomial *self, int maxdeg, const cpl_vector *obs, int nmaxima, int linelim, irplib_base_spectrum_model *model, cpl_error_code(*filler)(cpl_vector *, const cpl_polynomial *, irplib_base_spectrum_model *), double pixtol, double pixstep, int hsize, int maxite, int maxfail, int maxcont, cpl_boolean doplot, double *pxc)
Modify self by maximizing the cross-correlation across all maxima.
double visir_pfits_get_wlen(const cpl_propertylist *self)
The central wavelength.
Definition: visir_pfits.c:859
const cpl_frame * irplib_framelist_get_const(const irplib_framelist *self, int pos)
Get the specified frame from the framelist.
double visir_pfits_get_pixscale(const cpl_propertylist *self)
The pixel scale.
Definition: visir_pfits.c:678
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.
double visir_pfits_get_temp(const cpl_propertylist *self)
The telescope (M1) temperature [Celcius].
Definition: visir_pfits.c:799
int irplib_framelist_get_size(const irplib_framelist *self)
Get the size of a framelist.