UVES Pipeline Reference Manual  5.4.0
uves_rebin.c
1 /* *
2  * This file is part of the ESO UVES Pipeline *
3  * Copyright (C) 2004,2005 European Southern Observatory *
4  * *
5  * This library 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, 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA *
18  * */
19 
20 /*
21  * $Author: amodigli $
22  * $Date: 2013-04-16 15:45:44 $
23  * $Revision: 1.52 $
24  * $Name: not supported by cvs2svn $
25  * $Log: not supported by cvs2svn $
26  * Revision 1.51 2010/11/09 17:23:13 amodigli
27  * added integrate_noise() and made changes to properly rebin noise spectrum (sum in quadrature over variance, than take sqrt)
28  *
29  * Revision 1.50 2010/09/24 09:32:07 amodigli
30  * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
31  *
32  * Revision 1.48 2010/06/08 11:25:47 amodigli
33  * changed wavestep parmeters range to -1.0,-1.0,DBL_MAX
34  *
35  * Revision 1.47 2010/06/07 09:48:46 amodigli
36  * changed unit: Angstrom->Ang
37  *
38  * Revision 1.46 2010/06/02 13:10:12 amodigli
39  * set wavestep as parameter with range, min allowed is -1, max allowed is 0.4
40  *
41  * Revision 1.45 2010/02/13 12:22:31 amodigli
42  * removed inlines (let's do work to compiler)
43  *
44  * Revision 1.44 2009/01/27 10:11:30 amodigli
45  * fixed problem bue to cpl_image_get_bpm() API change
46  *
47  * Revision 1.43 2007/09/17 08:11:27 amodigli
48  * added support CPL_TYPE_INT
49  *
50  * Revision 1.42 2007/08/21 13:08:26 jmlarsen
51  * Removed irplib_access module, largely deprecated by CPL-4
52  *
53  * Revision 1.41 2007/06/21 07:29:11 jmlarsen
54  * Fixed memory error
55  *
56  * Revision 1.40 2007/06/19 11:59:37 amodigli
57  * added several uves_msg statements to check vs flames_obs_scired
58  *
59  * Revision 1.39 2007/06/18 15:35:27 jmlarsen
60  * Added support for in/out image type=float (used in FLAMES)
61  *
62  * Revision 1.38 2007/06/06 08:17:33 amodigli
63  * replace tab with 4 spaces
64  *
65  * Revision 1.37 2007/05/22 11:38:13 jmlarsen
66  * Removed MIDAS flag for good
67  *
68  * Revision 1.36 2007/05/07 10:18:27 jmlarsen
69  * Added option to enforce positive resulting values (useful for error bars)
70  *
71  * Revision 1.35 2007/05/03 15:21:10 jmlarsen
72  * Decreased output message verbosity
73  *
74  * Revision 1.34 2007/04/27 07:21:15 jmlarsen
75  * Show warning but don't fail if dispersion is ill-formed
76  *
77  * Revision 1.33 2007/04/24 12:50:29 jmlarsen
78  * Replaced cpl_propertylist -> uves_propertylist which is much faster
79  *
80  * Revision 1.32 2007/01/17 13:28:07 jmlarsen
81  * Shortened line
82  *
83  * Revision 1.31 2006/11/15 15:02:15 jmlarsen
84  * Implemented const safe workarounds for CPL functions
85  *
86  * Revision 1.29 2006/11/15 14:04:08 jmlarsen
87  * Removed non-const version of parameterlist_get_first/last/next which is
88  * already in CPL, added const-safe wrapper, unwrapper and deallocator functions
89  *
90  * Revision 1.28 2006/11/13 14:23:55 jmlarsen
91  * Removed workarounds for CPL const bugs
92  *
93  * Revision 1.27 2006/11/06 15:19:41 jmlarsen
94  * Removed unused include directives
95  *
96  * Revision 1.26 2006/10/31 09:15:34 jmlarsen
97  * Fixed buffer overrun
98  *
99  * Revision 1.25 2006/10/10 11:28:19 jmlarsen
100  * Renamed line table columns to match MIDAS
101  *
102  * Revision 1.24 2006/10/10 11:20:11 jmlarsen
103  * Renamed line table columns to match MIDAS
104  *
105  * Revision 1.23 2006/10/02 08:37:35 jmlarsen
106  * Do not avoid reserving space for bad pixels near edge of orders, like MIDAS
107  *
108  * Revision 1.22 2006/09/20 12:53:57 jmlarsen
109  * Replaced stringcat functions with uves_sprintf()
110  *
111  * Revision 1.21 2006/09/11 13:58:41 jmlarsen
112  * Changed CRVAL1 from 1 to 0 in rebinned image
113  *
114  * Revision 1.20 2006/08/17 14:40:06 jmlarsen
115  * Added missing documentation
116  *
117  * Revision 1.19 2006/08/17 13:56:53 jmlarsen
118  * Reduced max line length
119  *
120  * Revision 1.18 2006/08/17 09:17:39 jmlarsen
121  * Removed CPL2 code
122  *
123  * Revision 1.17 2006/08/10 10:52:58 jmlarsen
124  * Removed workaround for cpl_image_get_bpm
125  *
126  * Revision 1.16 2006/08/07 11:35:35 jmlarsen
127  * Disabled parameter environment variable mode
128  *
129  * Revision 1.15 2006/06/01 14:43:17 jmlarsen
130  * Added missing documentation
131  *
132  * Revision 1.14 2006/05/08 11:36:10 jmlarsen
133  * Fixed normalization bug for bins at edge of order
134  *
135  * Revision 1.13 2006/05/05 13:57:01 jmlarsen
136  * Implemented more careful flux interpolation
137  *
138  * Revision 1.12 2006/04/06 08:39:36 jmlarsen
139  * Added void to function prototype
140  *
141  * Revision 1.11 2006/03/03 13:54:11 jmlarsen
142  * Changed syntax of check macro
143  *
144  * Revision 1.10 2006/02/03 07:46:30 jmlarsen
145  * Moved recipe implementations to ./uves directory
146  *
147  * Revision 1.9 2006/01/31 08:24:29 jmlarsen
148  * Wrapper for cpl_image_get_bpm
149  *
150  * Revision 1.8 2006/01/25 16:13:20 jmlarsen
151  * Changed interface of gauss.fitting routine
152  *
153  * Revision 1.7 2005/12/19 16:17:56 jmlarsen
154  * Replaced bool -> int
155  *
156  * Revision 1.6 2005/12/16 14:22:23 jmlarsen
157  * Removed midas test data; Added sof files
158  *
159  * Revision 1.5 2005/12/02 10:41:49 jmlarsen
160  * Minor update
161  *
162  * Revision 1.4 2005/11/28 08:18:12 jmlarsen
163  * Replaced cpl_mask_get_bpm -> cpl_image_get_bpm
164  *
165  * Revision 1.3 2005/11/24 15:09:06 jmlarsen
166  * Implemented 2d extraction/rebinning/merging
167  *
168  * Revision 1.2 2005/11/24 11:54:46 jmlarsen
169  * Added support for CPL 3 interface
170  *
171  * Revision 1.1 2005/11/11 13:18:54 jmlarsen
172  * Reorganized code, renamed source files
173  *
174  */
175 
176 #ifdef HAVE_CONFIG_H
177 # include <config.h>
178 #endif
179 
180 /*----------------------------------------------------------------------------*/
187 /*----------------------------------------------------------------------------*/
190 /*-----------------------------------------------------------------------------
191  Includes
192  -----------------------------------------------------------------------------*/
193 
194 #include <uves_rebin.h>
195 
196 #include <uves_parameters.h>
197 #include <uves.h>
198 #include <uves_pfits.h>
199 #include <uves_dump.h>
200 #include <uves_utils.h>
201 #include <uves_utils_wrappers.h>
202 #include <uves_wavecal_utils.h>
203 #include <uves_error.h>
204 
205 #include <cpl.h>
206 
207 /*-----------------------------------------------------------------------------
208  Functions prototypes
209  -----------------------------------------------------------------------------*/
210 static double
211 integrate_flux(const double *spectrum_data_double,
212  const float *spectrum_data_float,
213  const int *spectrum_data_int,
214  const cpl_binary *spectrum_bad,
215  int spectrum_row,
216  int nx,
217  double x_min, double x_max,
218  bool threshold_to_positive,
219  bool *is_bad);
220 static double
221 integrate_noise(const double *spectrum_data_double,
222  const float *spectrum_data_float,
223  const int *spectrum_data_int,
224  const cpl_binary *spectrum_bad,
225  int spectrum_row,
226  int nx,
227  double x_min, double x_max,
228  bool threshold_to_positive,
229  bool *is_bad);
230 
231 /*-----------------------------------------------------------------------------
232  Implementation
233  -----------------------------------------------------------------------------*/
234 
235 /*----------------------------------------------------------------------------*/
243 /*----------------------------------------------------------------------------*/
244 cpl_parameterlist *
246 {
247  const char *name = "";
248  char *full_name = NULL;
249  cpl_parameter *p = NULL;
250  cpl_parameterlist *parameters = NULL;
251 
252  parameters = cpl_parameterlist_new();
253 
254  {
255  name = "wavestep";
256  full_name = uves_sprintf("%s.%s", UVES_REBIN_ID, name);
257  uves_parameter_new_range(p, full_name,
258  CPL_TYPE_DOUBLE,
259  "The bin size (in w.l.u.) in wavelength space. "
260  "If negative, a step size of "
261  "2/3 * ( average pixel size ) is used.",
262  UVES_REBIN_ID,
263  -1.0,-1.0,DBL_MAX);
264  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
265  cpl_parameterlist_append(parameters, p);
266  cpl_free(full_name);
267 
268  name = "scale";
269  full_name = uves_sprintf("%s.%s", UVES_REBIN_ID, name);
270  uves_parameter_new_value(p, full_name,
271  CPL_TYPE_BOOL,
272  "Whether or not to multiply by the factor "
273  "dx/dlambda (pixels per wavelength) "
274  "during the rebinning. This option is disabled "
275  "as default in concordance with the "
276  "method used in the MIDAS pipeline. This "
277  "option should be set to true "
278  "to convert the observed flux (in pixel-space) "
279  "to a flux per wavelength (in "
280  "wavelength-space).",
281  UVES_REBIN_ID,
282  false);
283  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
284  cpl_parameterlist_append(parameters, p);
285  cpl_free(full_name);
286  }
287 
288  if (cpl_error_get_code() != CPL_ERROR_NONE)
289  {
290  cpl_msg_error(__func__, "Creation of rebinning parameters failed: '%s'",
291  cpl_error_get_where());
292  cpl_parameterlist_delete(parameters);
293  return NULL;
294  }
295  else
296  {
297  return parameters;
298  }
299 }
300 
301 /*----------------------------------------------------------------------------*/
346 /*----------------------------------------------------------------------------*/
347 cpl_image *
348 uves_rebin(const cpl_image *spectrum,
349  const cpl_parameterlist *parameters, const char *context,
350  const cpl_table *linetable, const polynomial *dispersion_relation,
351  int first_abs_order, int last_abs_order,
352  int n_traces,
353  bool threshold_to_positive,
354  bool is_noise,
355  uves_propertylist **rebinned_header)
356 {
357  double wavestep;
358  bool scale;
359  cpl_image *spectrum_local = NULL; /* input */
360  cpl_image *rebinned = NULL; /* Result */
361  double *rebinned_data_double = NULL;
362  float *rebinned_data_float = NULL;
363  int *rebinned_data_int = NULL;
364  cpl_mask *rebinned_badmap = NULL; /* Map of unused bins */
365  cpl_binary *rebinned_bad = NULL;
366 
367  const double *spectrum_data_double = NULL; /* Direct pointer to input data */
368  const float *spectrum_data_float = NULL; /* unused pointer remains NULL */
369  const int *spectrum_data_int = NULL; /* unused pointer remains NULL */
370 
371  const cpl_mask *spectrum_badmap = NULL;
372  const cpl_binary *spectrum_bad = NULL;
373 
374  polynomial *disprel_1d = NULL; /* Dispersion relation for 1 order */
375 
376  int nx, ny, nlambda, norders; /* Image dimensions */
377  int order;
378  bool warning_shown = false;
379 
380 
381  passure( spectrum != NULL, " ");
382  passure( dispersion_relation != NULL, " ");
383  passure( rebinned_header != NULL, " ");
384 
385  assure( cpl_image_get_type(spectrum) == CPL_TYPE_DOUBLE ||
386  cpl_image_get_type(spectrum) == CPL_TYPE_FLOAT ||
387  cpl_image_get_type(spectrum) == CPL_TYPE_INT,
388  CPL_ERROR_TYPE_MISMATCH,
389  "Spectrum must have type double, float or int. It is '%s'",
390  uves_tostring_cpl_type(cpl_image_get_type(spectrum)));
391 
392  /* Get recipe parameters */
393  check( uves_get_parameter(parameters, context, UVES_REBIN_ID, "wavestep",
394  CPL_TYPE_DOUBLE, &wavestep ),
395  "Could not read parameter");
396  check( uves_get_parameter(parameters, context, UVES_REBIN_ID, "scale" ,
397  CPL_TYPE_BOOL, &scale ),
398  "Could not read parameter");
399 
400 
401  /* Set sample bin width if user didn't */
402  if (wavestep < 0)
403  {
404  double pixelsize;
405  check( pixelsize = cpl_table_get_column_mean(linetable, LINETAB_PIXELSIZE),
406  "Error reading mean pixelsize");
407  uves_msg_debug("Average pixelsize = %f w.l.u.", pixelsize);
408 
409  wavestep = pixelsize*2.0/3;
410  }
411 
412  assure( wavestep > 0 , CPL_ERROR_ILLEGAL_INPUT,
413  "Illegal step size: %e wlu", wavestep);
414  assure( n_traces >= 1, CPL_ERROR_ILLEGAL_INPUT,
415  "Illegal number of traces: %d", n_traces);
416 
417  nx = cpl_image_get_size_x(spectrum);
418  ny = cpl_image_get_size_y(spectrum);
419 
420  assure( ny % n_traces == 0, CPL_ERROR_INCOMPATIBLE_INPUT,
421  "Spectrum image height (%d) is not a multiple of "
422  "the number of traces (%d). Confused, bailing out",
423  ny, n_traces);
424 
425  norders = ny / n_traces;
426 
427  spectrum_local=(cpl_image*) spectrum;
428 
429  if(is_noise) {
430  cpl_image_power(spectrum_local,2);
431  }
432 
433  if (cpl_image_get_type(spectrum_local) == CPL_TYPE_DOUBLE) {
434  spectrum_data_double = cpl_image_get_data_double(spectrum_local);
435  }
436  else if (cpl_image_get_type(spectrum_local) == CPL_TYPE_FLOAT) {
437  spectrum_data_float = cpl_image_get_data_float(spectrum_local);
438  } else {
439  spectrum_data_int = cpl_image_get_data_int(spectrum_local);
440  }
441  check_nomsg(spectrum_badmap = cpl_image_get_bpm(spectrum_local));
442  check_nomsg(spectrum_bad = cpl_mask_get_data_const(spectrum_badmap));
443 
444  assure( norders >= 1, CPL_ERROR_ILLEGAL_INPUT, "Empty spectrum");
445  assure( uves_round_double(fabs(first_abs_order - last_abs_order)) + 1 == norders,
446  CPL_ERROR_INCOMPATIBLE_INPUT,
447  "Spectrum contains %d orders, but line table absolute "
448  "order numbering is %d - %d",
449  norders, first_abs_order, last_abs_order);
450 
451 
452  check( *rebinned_header = uves_initialize_image_header(
453  "AWAV", /* CTYPE */
454  (n_traces > 1) ? "PIXEL" : "ORDER",
455  "Angstrom",
456  (n_traces > 1) ? "PIXEL" : NULL,
457  (scale) ? "FLUX PER WAVEL" : "ADU", /* BUNIT */
458  0,
459  0.0, 1.0, /* CRVAL */
460  1.0, 1.0, /* CRPIX */
461  wavestep, 1.0), /* CDELT */
462  "Error setting up rebinned image header");
463  /* CRVAL1 is set to zero. It should really be set to WSTARTi
464  for the i'th row of the image (but obviously that's not possible).
465  CRVAL1 is set to zero so that the true starting position of
466  the i'th image row is simply calculated as CRVAL1 + WSTARTi.
467  */
468 
469  /* Get width of rebinned image and offsets for each order */
470  nlambda = -1; /* Maximum number of bins in any order */
471 
472  for (order = 1; order <= norders; order++)
473  {
474  /*int trace = 1;*/
475  /* In the case where there are more traces in each order
476  (2d extraction), just use the 1st trace to get the
477  wavelength range for the current order. The wavelength
478  range is the same for all traces.
479  */
480  /* int spectrum_row = (order - 1)*n_traces + trace; */
481  int absorder = uves_absolute_order(first_abs_order, last_abs_order, order);
482  double lambda_min, lambda_max;
483  int nbins;
484 
485  int minx, maxx; /* Range of good pixels in current order */
486 
487  minx = 1;
488 /* The following is commented out to get the same
489  alignment as MIDAS
490  while (minx <= nx && cpl_image_is_rejected(spectrum, minx, spectrum_row)) minx++;
491 */
492  maxx = nx;
493 /* while (maxx >= 1 && cpl_image_is_rejected(spectrum, maxx, spectrum_row)) maxx--; */
494  if ( minx > nx )
495  {
496  uves_msg_debug("Nothing extracted in order #%d", order);
497  minx = maxx = nx/2;
498  }
499 
500  lambda_min = uves_polynomial_evaluate_2d(
501  dispersion_relation, minx - 0.5, absorder)/absorder;
502  lambda_max = uves_polynomial_evaluate_2d(
503  dispersion_relation, maxx + 0.5, absorder)/absorder;
504 
505  nbins =
506  uves_round_double(lambda_max / wavestep) -
507  uves_round_double(lambda_min / wavestep) + 1;
508 
509  nlambda = uves_max_int(nlambda, nbins);
510 
512  *rebinned_header, order,
513  wavestep * uves_round_double(lambda_min / wavestep)),
514  "Error writing adding WSTART keyword to header");
515 
517  *rebinned_header, order,
518  wavestep * uves_round_double(lambda_max / wavestep)),
519  "Error writing adding WEND keyword to header");
520 
521  uves_msg_debug("Rebinning abs. order #%d. "
522  "Range = %d - %d pix = %f - %f wlu, %d bins",
523  absorder,
524  minx, maxx,
525  lambda_min,
526  lambda_max,
527  nbins);
528  }
529 
530 
531  uves_msg_debug("Step size = %f wlu (%d orders x %d bins)", wavestep, norders, nlambda);
532 
533  /* Do the rebinning */
534 
535  /* Create empty image */
536  check_nomsg( rebinned = cpl_image_new(nlambda, norders*n_traces, cpl_image_get_type(spectrum_local)));
537  assure_mem( rebinned );
538 
539  if (cpl_image_get_type(spectrum_local) == CPL_TYPE_DOUBLE) {
540  rebinned_data_double = cpl_image_get_data_double(rebinned);
541  }
542  else if (cpl_image_get_type(spectrum_local) == CPL_TYPE_FLOAT) {
543  rebinned_data_float = cpl_image_get_data_float(rebinned);
544  } else {
545  rebinned_data_int = cpl_image_get_data_int(rebinned);
546  }
547  rebinned_badmap = cpl_image_get_bpm(rebinned);
548  rebinned_bad = cpl_mask_get_data(rebinned_badmap);
549 
550  /* Reject all pixels in output image,
551  accept as values are computed */
552  uves_image_reject_all(rebinned);
553 
554  for (order = 1; order <= norders; order++)
555  {
556  int absorder = uves_absolute_order(first_abs_order, last_abs_order, order);
557  double lambda_start; /* Center of first wavel. bin */
558  int trace;
559 
560  /* uves_msg_progress(order - 1, norders, ".."); */
561 
562  check( lambda_start = uves_pfits_get_wstart(*rebinned_header, order),
563  "Error reading product header");
564 
565  /* For efficiency, collapse 2d polynomial to 1d only once per order */
566  uves_polynomial_delete(&disprel_1d);
567  check( disprel_1d = uves_polynomial_collapse(dispersion_relation,
568  2, /* Independent variable number */
569  absorder),
570  "Error getting 1d dispersion relation for absolute order #%d", absorder);
571 
572 
573 
574  for (trace = 1; trace <= n_traces; trace++)
575  {
576  int spectrum_row = (order - 1)*n_traces + trace;
577  int bin;
578 
579  double x = 1;
580  double x_min = 1;
581  double x_max = 1;
582 
583  for (bin = 1; bin <= nlambda && x_min <= nx+0.5; bin++)
584  {
585  double lambda = lambda_start + (bin-1) * wavestep;
586  /* Solve f(x, m) = lambda*m for x */
587  int multiplicity = 1;
588  double x_guess = x;
589 
590 
591 
592  x = uves_polynomial_solve_1d(disprel_1d,
593  lambda * absorder,
594  x_guess,
595  multiplicity);
596 
597  if (cpl_error_get_code() == CPL_ERROR_DIVISION_BY_ZERO) {
599  if (!warning_shown) {
600  uves_msg_warning("Could not invert dispersion relation at "
601  "order = %d, x = %f. This might be caused "
602  "by fitting a too high degree polynomial to "
603  "too few lines. Decrease dispersion "
604  "polynomial degree "
605  "or relax rejection parameters!",
606  absorder, x_guess);
607  warning_shown = true;
608  }
609  x = x_guess;
610  }
611  else {
612  assure( cpl_error_get_code() == CPL_ERROR_NONE,
613  cpl_error_get_code(),
614  "Could not invert dispersion relation");
615  }
616 
617 
618  x_guess = x;
619  x_min = uves_polynomial_solve_1d(
620  disprel_1d,
621  (lambda - 0.5*wavestep) * absorder,
622  x_guess,
623  multiplicity);
624 
625  if (cpl_error_get_code() == CPL_ERROR_DIVISION_BY_ZERO) {
627  if (!warning_shown) {
628  uves_msg_warning("Could not invert dispersion relation at "
629  "order = %d, x = %f. This might be caused "
630  "by fitting a too high degree polynomial to "
631  "too few lines. Decrease dispersion "
632  "polynomial degree "
633  "or relax rejection parameters!",
634  absorder, x_guess);
635  warning_shown = true;
636  }
637  x_min = x_guess;
638  }
639  else {
640  assure( cpl_error_get_code() == CPL_ERROR_NONE,
641  cpl_error_get_code(),
642  "Could not invert dispersion relation");
643  }
644 
645 
646  x_max = uves_polynomial_solve_1d(
647  disprel_1d,
648  (lambda + 0.5*wavestep) * absorder,
649  x_guess,
650  multiplicity);
651 
652  if (cpl_error_get_code() == CPL_ERROR_DIVISION_BY_ZERO) {
654  if (!warning_shown) {
655  uves_msg_warning("Could not invert dispersion relation at "
656  "order = %d, x = %f. This might be caused "
657  "by fitting a too high degree polynomial to "
658  "too few lines. Decrease dispersion "
659  "polynomial degree "
660  "or relax rejection parameters!",
661  absorder, x_guess);
662  warning_shown = true;
663  }
664  x_max = x_guess;
665  }
666  else {
667  assure( cpl_error_get_code() == CPL_ERROR_NONE,
668  cpl_error_get_code(),
669  "Could not invert dispersion relation");
670  }
671 
672  /* If bin overlaps with source image */
673  if (uves_max_double(0.5, uves_min_double(nx+0.5, x_min)) <
674  uves_max_double(0.5, uves_min_double(nx+0.5, x_max)))
675  {
676  /* Measure average flux in range [xmin; xmax]:
677 
678  flux_x = [ int_xmin^xmax f(x) dx ] / (xmax-xmin)
679  */
680 
681  bool pis_rejected;
682  double p_min=uves_max_double(0.5, uves_min_double(nx+0.5, x_min));
683  double p_max=uves_max_double(0.5, uves_min_double(nx+0.5, x_max));
684  double flux_x =0;
685  if(is_noise) {
686  flux_x = integrate_noise(
687  spectrum_data_double,
688  spectrum_data_float,
689  spectrum_data_int,
690  spectrum_bad,
691  spectrum_row,
692  nx,
693  p_min,
694  p_max,
695  threshold_to_positive,
696  &pis_rejected)
697  / (( p_max -p_min) * ( p_max -p_min));
698 
699  } else {
700  flux_x = integrate_flux(
701  spectrum_data_double,
702  spectrum_data_float,
703  spectrum_data_int,
704  spectrum_bad,
705  spectrum_row,
706  nx,
707  p_min,
708  p_max,
709  threshold_to_positive,
710  &pis_rejected)
711  / ( p_max - p_min );
712  }
713  if (!pis_rejected)
714  {
715  /* Convert to flux per wavelength if requested */
716 
717  double dldx;
718 
719 
720  if (scale)
721  {
722  /* For constant m:
723  * dl/dx = d(l*m)/dx / m
724  */
726  dispersion_relation, x, absorder, 1)
727  / absorder;
728 
729  if(is_noise) {
730  dldx *= dldx;
731  }
732  }
733  else
734  {
735  dldx = 1;
736  }
737 
738  /* Density in wavelength space :
739  N_lambda = N_x / |dl/dx| */
740 
741  if (cpl_image_get_type(spectrum_local) == CPL_TYPE_DOUBLE) {
742  rebinned_data_double[(bin-1) +
743  (spectrum_row-1)*nlambda] =
744  flux_x / fabs(dldx);
745  }
746  else if (cpl_image_get_type(spectrum_local) == CPL_TYPE_FLOAT) {
747  rebinned_data_float[(bin-1) +
748  (spectrum_row-1)*nlambda] =
749  flux_x / fabs(dldx);
750  } else {
751  rebinned_data_int[(bin-1) +
752  (spectrum_row-1)*nlambda] =
753  flux_x / fabs(dldx);
754 
755 
756  }
757 
758 
759  rebinned_bad[(bin-1) +
760  (spectrum_row-1)*nlambda] =
761  CPL_BINARY_0;
762  }
763  else
764  {
765  /* Interpolation interval had no good pixels */
766  /* Pixel marked as bad */
767  }
768 
769  }
770  else
771  {
772  /* Current wavelength bin is outside input image */
773  /* Pixel marked as bad */
774  }
775 
776 
777  }/* for each wavelength bin */
778  } /* for trace */
779 
780  } /* for order */
781  /* Done rebinning */
782  if(is_noise) {
783  cpl_image_power(rebinned,0.5);
784  }
785 
786 
787  cleanup:
788  uves_polynomial_delete(&disprel_1d);
789  if (cpl_error_get_code() != CPL_ERROR_NONE)
790  {
791  uves_free_image(&rebinned);
792  uves_free_propertylist(rebinned_header);
793  }
794 
795  return rebinned;
796 }
797 
798 /*----------------------------------------------------------------------------*/
823 /*----------------------------------------------------------------------------*/
824 static double
825 integrate_flux(const double *spectrum_data_double,
826  const float *spectrum_data_float,
827  const int *spectrum_data_int,
828  const cpl_binary *spectrum_bad,
829  int spectrum_row,
830  int nx,
831  double x_min, double x_max,
832  bool threshold_to_positive,
833  bool *is_bad)
834 {
835  double sum = 0; /* Result */
836  double sum_interval = 0; /* The length of the interval defined as the unioun
837  of good pixels */
838  int x;
839  int first_good = 0;
840 
841  *is_bad = true; /* Until at least one good pixel found */
842 
843  for (x = uves_min_int(nx, uves_max_int(1, uves_round_double(x_min)));
844  /* The thresholding is necessary, or nx+0.5 would be rounded to nx+1
845  which would cause a memory error */
846  x <= uves_min_int(nx, uves_max_int(1, uves_round_double(x_max)));
847  x++)
848  {
849 
850  if (spectrum_bad[(x-1) + (spectrum_row-1)*nx] == CPL_BINARY_0)
851  /* If good pixel */
852  {
853  double flux; /* "Raw" flux of current bin */
854  double interval_length;
855  double current_term; /* Integral over current pixel */
856 
857  /* Use a piecewise linear profile like this
858  *
859  * C
860  * interpolant => / \
861  * ---/---\-- <= "raw" flux
862  * | / \|
863  * |/ B
864  * A |________ <= non-continous interpolation
865  * /|
866  * __________|
867  *
868  * The flux levels A and B are midway between the current
869  * pixel flux and its neighbours' levels.
870  * C is chosen so that the integrated flux over the current
871  * pixel equals the observed flux.
872  *
873  * This interpolant is continous as well as flux conserving.
874  */
875 
876  int x_prev = x-1;
877  int x_next = x+1;
878  bool pis_rejected_prev = (x_prev < 1 ) ||
879  (spectrum_bad[(x_prev-1) + (spectrum_row-1)*nx] == CPL_BINARY_1);
880  bool pis_rejected_next = (nx < x_next) ||
881  (spectrum_bad[(x_next-1) + (spectrum_row-1)*nx] == CPL_BINARY_1);
882 
883  if (spectrum_data_double != NULL) {
884  flux = spectrum_data_double[(x-1) + (spectrum_row-1)*nx];
885  }
886  else if (spectrum_data_float != NULL) {
887  flux = spectrum_data_float [(x-1) + (spectrum_row-1)*nx];
888  } else {
889  flux = spectrum_data_int [(x-1) + (spectrum_row-1)*nx];
890  }
891 
892  if (!pis_rejected_prev && !pis_rejected_next)
893  {
894  /* Define flux at pixel borders (A and B) as
895  mean value of this and neighbouring pixel */
896  /* CHANGE in case of noise divide by 4 instead of 2
897  (input has to be variance) */
898  double flux_minus, flux_plus;
899  if (spectrum_data_double != NULL) {
900  flux_minus =
901  (flux + spectrum_data_double[(x_prev-1) + (spectrum_row-1)*nx])
902  / 2.0;
903  flux_plus =
904  (flux + spectrum_data_double[(x_next-1) + (spectrum_row-1)*nx])
905  / 2.0;
906  }
907  else if (spectrum_data_float != NULL) {
908  flux_minus =
909  (flux + spectrum_data_float[(x_prev-1) + (spectrum_row-1)*nx])
910  / 2.0;
911  flux_plus =
912  (flux + spectrum_data_float[(x_next-1) + (spectrum_row-1)*nx])
913  / 2.0;
914  } else {
915  flux_minus =
916  (flux + spectrum_data_int[(x_prev-1) + (spectrum_row-1)*nx])
917  / 2.0;
918  flux_plus =
919  (flux + spectrum_data_int[(x_next-1) + (spectrum_row-1)*nx])
920  / 2.0;
921  }
922 
923  /* Define flux at pixel center, fluxc, so that the average flux is
924  * equal to the "raw" flux:
925  *
926  * ((flux- + fluxc)/2 + (flux+ + fluxc)/2) / 2 = flux
927  * => flux- + flux+ + 2fluxc = 4flux
928  * => fluxc = ...
929  */
930  {
931  double flux_center = 2*flux - (flux_minus + flux_plus) / 2.0;
932  /* CHANGE: 4*flux + (flux_minus + flux_plus) / 4.0 */
933 
934  /* Line slopes */
935  double slope_minus = (flux_center - flux_minus )/ 0.5;
936  double slope_plus = (flux_plus - flux_center) / 0.5;
937  /* CHANGE:
938  (flux_center + flux_minus )/ 0.25;
939  (flux_plus + flux_center) / 0.25;
940  */
941  /* Define overlap between [x_min; x_max] and
942  interval between A-C: [x-0.5; x]) */
943  double lo1 = uves_max_double(x-0.5, uves_min_double(x, x_min));
944  double hi1 = uves_max_double(x-0.5, uves_min_double(x, x_max));
945  double dy1 = hi1-lo1;
946 
947  /* Define overlap between [x_min; x_max] and
948  interval between C-B: [x; x+0.5]) */
949  double lo2 = uves_max_double(x, uves_min_double(x+0.5, x_min));
950  double hi2 = uves_max_double(x, uves_min_double(x+0.5, x_max));
951  double dy2 = hi2-lo2;
952 
953  /* Integrate interpolant over A-C and C-B */
954  /* A-C: interpolant(x) = flux_center + slope_minus *(x-x)
955  C-B: interpolant(x) = flux_center + slope_plus *(x-x)
956  */
957 
958 
959 
960  current_term =
961  dy1 * (flux_center + slope_minus * ((lo1+hi1)/2.0 - x))
962  +
963  dy2 * (flux_center + slope_plus * ((lo2+hi2)/2.0 - x));
964  /* CHANGE
965  current_term =
966  (dy1)^2 * (flux_center + slope_minus * ((lo1+hi1)/2.0 - x)^2)
967  +
968  (dy2)^2 * (flux_center + slope_plus * ((lo2+hi2)/2.0 - x)^2);
969 
970  */
971  interval_length = dy1 + dy2;
972  }
973 
974  }/* Neighbours are good */
975  else
976  {
977  interval_length =
978  uves_min_double(x_max, x+0.5) -
979  uves_max_double(x_min, x-0.5);
980 
981  current_term = interval_length * flux;
982  /* CHANGE
983  current_term = (interval_length)^2 * flux;
984  */
985  }
986 
987  if (*is_bad) {
988  first_good = x;
989  }
990  *is_bad = false;
991 
992  sum += current_term;
993  sum_interval += interval_length;
994  }
995  }
996 
997  if (sum_interval == 0)
998  {
999  *is_bad = true;
1000  return -1;
1001  }
1002  else
1003  {
1004  /* In case of bad pixels, rescale sum to full interval
1005  (If there are only good pixels then sum_interval == x_max-x_min)
1006  */
1007  double result = sum*(x_max-x_min)/sum_interval;
1008  /* CHANGE
1009  double result = sum*[(x_max-x_min)/sum_interval]^2;
1010  */
1011  if (threshold_to_positive) {
1012  if (result == 0) {
1013  /* give up */
1014  *is_bad = true;
1015  return -1;
1016  }
1017  else {
1018  result = fabs(result);
1019  }
1020  }
1021  return result;
1022  }
1023 }
1024 
1025 
1026 /*----------------------------------------------------------------------------*/
1051 /*----------------------------------------------------------------------------*/
1052 static double
1053 integrate_noise(const double *spectrum_data_double,
1054  const float *spectrum_data_float,
1055  const int *spectrum_data_int,
1056  const cpl_binary *spectrum_bad,
1057  int spectrum_row,
1058  int nx,
1059  double x_min, double x_max,
1060  bool threshold_to_positive,
1061  bool *is_bad)
1062 {
1063  double sum = 0; /* Result */
1064  double sum_interval = 0; /* The length of the interval defined as the unioun
1065  of good pixels */
1066  int x;
1067  int first_good = 0;
1068 
1069  *is_bad = true; /* Until at least one good pixel found */
1070 
1071  for (x = uves_min_int(nx, uves_max_int(1, uves_round_double(x_min)));
1072  /* The thresholding is necessary, or nx+0.5 would be rounded to nx+1
1073  which would cause a memory error */
1074  x <= uves_min_int(nx, uves_max_int(1, uves_round_double(x_max)));
1075  x++)
1076  {
1077 
1078  if (spectrum_bad[(x-1) + (spectrum_row-1)*nx] == CPL_BINARY_0)
1079  /* If good pixel */
1080  {
1081  double flux; /* "Raw" flux of current bin */
1082  double interval_length;
1083  double current_term; /* Integral over current pixel */
1084 
1085  /* Use a piecewise linear profile like this
1086  *
1087  * C
1088  * interpolant => / \
1089  * ---/---\-- <= "raw" flux
1090  * | / \|
1091  * |/ B
1092  * A |________ <= non-continous interpolation
1093  * /|
1094  * __________|
1095  *
1096  * The flux levels A and B are midway between the current
1097  * pixel flux and its neighbours' levels.
1098  * C is chosen so that the integrated flux over the current
1099  * pixel equals the observed flux.
1100  *
1101  * This interpolant is continous as well as flux conserving.
1102  */
1103 
1104  int x_prev = x-1;
1105  int x_next = x+1;
1106  bool pis_rejected_prev = (x_prev < 1 ) ||
1107  (spectrum_bad[(x_prev-1) + (spectrum_row-1)*nx] == CPL_BINARY_1);
1108  bool pis_rejected_next = (nx < x_next) ||
1109  (spectrum_bad[(x_next-1) + (spectrum_row-1)*nx] == CPL_BINARY_1);
1110 
1111  if (spectrum_data_double != NULL) {
1112  flux = spectrum_data_double[(x-1) + (spectrum_row-1)*nx];
1113  }
1114  else if (spectrum_data_float != NULL) {
1115  flux = spectrum_data_float [(x-1) + (spectrum_row-1)*nx];
1116  } else {
1117  flux = spectrum_data_int [(x-1) + (spectrum_row-1)*nx];
1118  }
1119 
1120  if (!pis_rejected_prev && !pis_rejected_next)
1121  {
1122  /* Define flux at pixel borders (A and B) as
1123  mean value of this and neighbouring pixel */
1124  /* CHANGED in case of noise divide by 4 instead of 2
1125  (input is to be variance) */
1126  double flux_minus, flux_plus;
1127  if (spectrum_data_double != NULL) {
1128  flux_minus =
1129  (flux + spectrum_data_double[(x_prev-1) + (spectrum_row-1)*nx])
1130  / 4.0;
1131  flux_plus =
1132  (flux + spectrum_data_double[(x_next-1) + (spectrum_row-1)*nx])
1133  / 4.0;
1134  }
1135  else if (spectrum_data_float != NULL) {
1136  flux_minus =
1137  (flux + spectrum_data_float[(x_prev-1) + (spectrum_row-1)*nx])
1138  / 4.0;
1139  flux_plus =
1140  (flux + spectrum_data_float[(x_next-1) + (spectrum_row-1)*nx])
1141  / 4.0;
1142  } else {
1143  flux_minus =
1144  (flux + spectrum_data_int[(x_prev-1) + (spectrum_row-1)*nx])
1145  / 4.0;
1146  flux_plus =
1147  (flux + spectrum_data_int[(x_next-1) + (spectrum_row-1)*nx])
1148  / 4.0;
1149  }
1150 
1151  /* Define flux at pixel center, fluxc, so that the average flux is
1152  * equal to the "raw" flux:
1153  *
1154  * ((flux- + fluxc)/2 + (flux+ + fluxc)/2) / 2 = flux
1155  * => flux- + flux+ + 2fluxc = 4flux
1156  * => fluxc = ...
1157  */
1158  {
1159  double flux_center = 4*flux - (flux_minus + flux_plus) / 4.0;
1160  /* CHANGED: 4*flux + (flux_minus + flux_plus) / 4.0 */
1161 
1162  /* Line slopes */
1163  double slope_minus = 4.0 * (flux_center + flux_minus );
1164  double slope_plus = 4.0 * (flux_plus + flux_center);
1165  /* CHANGED for variance:
1166  (flux_center + flux_minus )/ 0.25;
1167  (flux_plus + flux_center) / 0.25;
1168  */
1169  /* Define overlap between [x_min; x_max] and
1170  interval between A-C: [x-0.5; x]) */
1171  double lo1 = uves_max_double(x-0.5, uves_min_double(x, x_min));
1172  double hi1 = uves_max_double(x-0.5, uves_min_double(x, x_max));
1173  double dy1 = hi1-lo1;
1174 
1175  /* Define overlap between [x_min; x_max] and
1176  interval between C-B: [x; x+0.5]) */
1177  double lo2 = uves_max_double(x, uves_min_double(x+0.5, x_min));
1178  double hi2 = uves_max_double(x, uves_min_double(x+0.5, x_max));
1179  double dy2 = hi2-lo2;
1180 
1181  /* Integrate interpolant over A-C and C-B */
1182  /* A-C: interpolant(x) = flux_center + slope_minus *(x-x)
1183  C-B: interpolant(x) = flux_center + slope_plus *(x-x)
1184  */
1185 
1186 
1187 
1188  current_term =
1189  dy1*dy1 * (flux_center + slope_minus *
1190  ((lo1+hi1)/2.0 - x) * ((lo1+hi1)/2.0 - x) )
1191  +
1192  dy2*dy2 * (flux_center + slope_plus *
1193  ((lo2+hi2)/2.0 - x) * ((lo2+hi2)/2.0 - x) );
1194  /* CHANGED
1195  current_term =
1196  (dy1)^2 * (flux_center + slope_minus * ((lo1+hi1)/2.0 - x)^2)
1197  +
1198  (dy2)^2 * (flux_center + slope_plus * ((lo2+hi2)/2.0 - x)^2);
1199 
1200  */
1201  interval_length = dy1 + dy2;
1202  }
1203 
1204  }/* Neighbours are good */
1205  else
1206  {
1207  interval_length =
1208  uves_min_double(x_max, x+0.5) -
1209  uves_max_double(x_min, x-0.5);
1210 
1211  current_term = interval_length * interval_length * flux;
1212  /* CHANGED
1213  current_term = (interval_length)^2 * flux;
1214  */
1215  }
1216 
1217  if (*is_bad) {
1218  first_good = x;
1219  }
1220  *is_bad = false;
1221 
1222  sum += current_term;
1223  sum_interval += interval_length;
1224  }
1225  }
1226 
1227  if (sum_interval == 0)
1228  {
1229  *is_bad = true;
1230  return -1;
1231  }
1232  else
1233  {
1234  /* In case of bad pixels, rescale sum to full interval
1235  (If there are only good pixels then sum_interval == x_max-x_min)
1236  */
1237  double result = sum*(x_max-x_min)/sum_interval*
1238  (x_max-x_min)/sum_interval;
1239  /* CHANGED
1240  double result = sum*[(x_max-x_min)/sum_interval]^2;
1241  */
1242  if (threshold_to_positive) {
1243  if (result == 0) {
1244  /* give up */
1245  *is_bad = true;
1246  return -1;
1247  }
1248  else {
1249  result = fabs(result);
1250  }
1251  }
1252  return result;
1253  }
1254 }
1255 
1256 
1257