UVES Pipeline Reference Manual  5.4.0
uves_orderpos_follow.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: 2011-12-08 14:03:53 $
23  * $Revision: 1.44 $
24  * $Name: not supported by cvs2svn $
25  * $Log: not supported by cvs2svn $
26  * Revision 1.43 2010/09/24 09:32:04 amodigli
27  * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
28  *
29  * Revision 1.41 2010/05/06 14:55:29 amodigli
30  * clearer error message
31  *
32  * Revision 1.40 2007/08/30 07:56:54 amodigli
33  * fixed some doxygen warnings
34  *
35  * Revision 1.39 2007/08/23 08:16:40 jmlarsen
36  * Indentation change
37  *
38  * Revision 1.38 2007/08/21 13:08:26 jmlarsen
39  * Removed irplib_access module, largely deprecated by CPL-4
40  *
41  * Revision 1.37 2007/06/28 09:18:01 jmlarsen
42  * Return actualy polynomial degree used
43  *
44  * Revision 1.36 2007/06/06 08:17:33 amodigli
45  * replace tab with 4 spaces
46  *
47  * Revision 1.35 2007/05/22 14:09:56 amodigli
48  * removed compilation warnings
49  *
50  * Revision 1.34 2007/05/14 15:57:15 jmlarsen
51  * Avoid tracing orders at very edge of chip
52  *
53  * Revision 1.33 2007/04/12 14:02:24 jmlarsen
54  * Made robust against input orders outside image
55  *
56  * Revision 1.32 2007/04/12 12:02:09 jmlarsen
57  * Decreased verbosity
58  *
59  * Revision 1.31 2007/04/10 07:07:25 jmlarsen
60  * Changed interface of polynomial_regression_2d()
61  *
62  * Revision 1.30 2007/03/30 07:07:28 jmlarsen
63  * Fixed mixed code and variable definitions
64  *
65  * Revision 1.29 2007/03/28 14:02:21 jmlarsen
66  * Removed unused parameter
67  *
68  * Revision 1.28 2007/03/28 11:39:09 jmlarsen
69  * Killed MIDAS flag, removed dead code
70  *
71  * Revision 1.27 2007/03/05 10:17:03 jmlarsen
72  * Support slope parameter in 1d fitting
73  *
74  * Revision 1.26 2007/02/26 11:56:39 jmlarsen
75  * Made fitting (even) more robust against points with low sigma
76  *
77  * Revision 1.25 2007/01/17 13:26:18 jmlarsen
78  * Added comment
79  *
80  * Revision 1.24 2007/01/15 08:46:25 jmlarsen
81  * More robust polynomial fitting
82  *
83  * Revision 1.23 2006/11/23 10:04:31 jmlarsen
84  * Minor message change
85  *
86  * Revision 1.22 2006/11/15 15:02:14 jmlarsen
87  * Implemented const safe workarounds for CPL functions
88  *
89  * Revision 1.20 2006/11/15 14:04:08 jmlarsen
90  * Removed non-const version of parameterlist_get_first/last/next which is already
91  * in CPL, added const-safe wrapper, unwrapper and deallocator functions
92  *
93  * Revision 1.19 2006/11/13 14:23:55 jmlarsen
94  * Removed workarounds for CPL const bugs
95  *
96  * Revision 1.18 2006/11/06 15:19:41 jmlarsen
97  * Removed unused include directives
98  *
99  * Revision 1.17 2006/08/23 09:33:03 jmlarsen
100  * Renamed local variables shadowing POSIX reserved names
101  *
102  * Revision 1.16 2006/08/17 14:40:06 jmlarsen
103  * Added missing documentation
104  *
105  * Revision 1.15 2006/08/17 14:33:28 jmlarsen
106  * Added missing opening bracket
107  *
108  * Revision 1.14 2006/08/17 13:56:53 jmlarsen
109  * Reduced max line length
110  *
111  * Revision 1.13 2006/08/17 09:18:27 jmlarsen
112  * Removed CPL2 code
113  *
114  * Revision 1.12 2006/08/10 10:52:41 jmlarsen
115  * Removed workaround for cpl_image_get_bpm
116  *
117  * Revision 1.11 2006/08/08 11:27:18 amodigli
118  * upgrade to CPL3
119  *
120  * Revision 1.10 2006/07/14 12:22:17 jmlarsen
121  * Do not use uncertainties in linear fit
122  *
123  * Revision 1.9 2006/07/03 14:20:39 jmlarsen
124  * Exclude bad pixels from order tracing
125  *
126  * Revision 1.8 2006/05/12 15:05:49 jmlarsen
127  * Pass image bpm as extra parameter to fitting routine for efficiency reasons
128  *
129  * Revision 1.7 2006/04/24 09:34:26 jmlarsen
130  * Adapted to new interface of gaussian fitting routine
131  *
132  * Revision 1.6 2006/04/10 12:38:43 jmlarsen
133  * Minor layout change
134  *
135  * Revision 1.5 2006/04/06 08:44:16 jmlarsen
136  * Renamed shadowing variables
137  *
138  * Revision 1.4 2006/03/24 14:12:18 jmlarsen
139  * Use MIDAS default values for polynomial degree if MIDAS flag is set
140  *
141  * Revision 1.3 2006/03/03 13:54:11 jmlarsen
142  * Changed syntax of check macro
143  *
144  * Revision 1.2 2006/02/15 13:19:15 jmlarsen
145  * Reduced source code max. line length
146  *
147  * Revision 1.1 2006/02/03 07:46:30 jmlarsen
148  * Moved recipe implementations to ./uves directory
149  *
150  * Revision 1.42 2006/01/25 16:15:59 jmlarsen
151  * Changed interface of gauss.fitting routine
152  *
153  * Revision 1.41 2006/01/19 08:47:24 jmlarsen
154  * Inserted missing doxygen end tag
155  *
156  * Revision 1.40 2006/01/12 15:41:14 jmlarsen
157  * Moved gauss. fitting to irplib
158  *
159  * Revision 1.39 2005/12/19 16:17:55 jmlarsen
160  * Replaced bool -> int
161  *
162  */
163 
164 /*----------------------------------------------------------------------------*/
168 /*----------------------------------------------------------------------------*/
171 #ifdef HAVE_CONFIG_H
172 # include <config.h>
173 #endif
174 
175 #include <uves_orderpos_follow.h>
176 
177 #include <uves_plot.h>
178 #include <uves_utils.h>
179 #include <uves_utils_wrappers.h>
180 #include <uves_error.h>
181 #include <uves_msg.h>
182 
183 #include <cpl.h>
184 #include <math.h>
185 #include <float.h>
186 
187 static cpl_table * trace_order(const cpl_table *ordertable, int order,
188  const cpl_image *inputimage, const cpl_image *noise,
189  const cpl_binary *image_bad,
190  int TRACESTEP,
191  double MAXGAP);
192 static int count_orders(const cpl_table *tracetable);
193 static double fit_order_linear(cpl_table *singletrace, int order, double KAPPA,
194  double *slope);
195 static int get_xcenter(int nx, int ny, cpl_table *ordertab, int row);
196 static int get_ycenter(int nx, int ny, cpl_table *ordertab, int row);
197 static int get_orderlength(int nx, int ny, cpl_table *ordertab, int row);
198 static double estimate_threshold(const cpl_image *inputimage,
199  const cpl_image *nosie,
200  cpl_table *ordertable,
201  int row, double relative_threshold);
202 static bool find_centroid(const cpl_image *inputimage,
203  const cpl_image *noise,
204  const cpl_binary *image_bad,
205  double threshold, int spacing, int x, double *yguess,
206  double *dY);
207 
208 /*----------------------------------------------------------------------------*/
248 /*----------------------------------------------------------------------------*/
249 cpl_table *
250 uves_locate_orders(const cpl_image *inputimage,
251  const cpl_image *noise,
252  cpl_table *ordertable,
253  int TRACESTEP,
254  double MINTHRESH,
255  double MAXGAP,
256  double MAXRMS,
257  int *DEFPOL1,
258  int *DEFPOL2,
259  double KAPPA,
260  polynomial **bivariate_fit,
261  int *orders_traced)
262 {
263  cpl_table *tracetable = NULL; /* The result */
264  cpl_table *singletrace = NULL; /* Location of one order */
265  cpl_table *temp = NULL; /* Temporary */
266  const cpl_mask *image_badmap = NULL;
267  const cpl_binary *image_bad = NULL;
268  int N; /* Initial number of orders detected */
269 
270  double mse, red_chisq;
271  int order;
272 
273  /* Check input */
274  assure_nomsg( inputimage != NULL, CPL_ERROR_NULL_INPUT);
275  assure_nomsg( noise != NULL, CPL_ERROR_NULL_INPUT);
276  assure( cpl_image_get_size_x(inputimage) == cpl_image_get_size_x(noise) &&
277  cpl_image_get_size_y(inputimage) == cpl_image_get_size_y(noise),
278  CPL_ERROR_INCOMPATIBLE_INPUT,
279  "Image sizes are %" CPL_SIZE_FORMAT "x%" CPL_SIZE_FORMAT " and %" CPL_SIZE_FORMAT "x%" CPL_SIZE_FORMAT "",
280  cpl_image_get_size_x(inputimage), cpl_image_get_size_x(noise),
281  cpl_image_get_size_y(inputimage), cpl_image_get_size_y(noise));
282 
283  assure_nomsg( ordertable != NULL, CPL_ERROR_NULL_INPUT);
284  assure( cpl_table_get_ncol(ordertable) == 4,
285  CPL_ERROR_ILLEGAL_INPUT,
286  "%" CPL_SIZE_FORMAT " columns found. 4 expected",
287  cpl_table_get_ncol(ordertable));
288  assure( cpl_table_has_column(ordertable, "Intersept"),
289  CPL_ERROR_DATA_NOT_FOUND,
290  "Missing column Intersept");
291  assure( cpl_table_has_column(ordertable, "Slope"),
292  CPL_ERROR_DATA_NOT_FOUND,
293  "Missing column Slope");
294  assure( cpl_table_has_column(ordertable, "Order"),
295  CPL_ERROR_DATA_NOT_FOUND,
296  "Missing column Order");
297  assure( cpl_table_has_column(ordertable, "Spacing"),
298  CPL_ERROR_DATA_NOT_FOUND,
299  "Missing column Spacing");
300  assure_nomsg( DEFPOL1 != NULL, CPL_ERROR_NULL_INPUT );
301  assure_nomsg( DEFPOL2 != NULL, CPL_ERROR_NULL_INPUT );
302 
303  image_badmap = cpl_image_get_bpm_const(inputimage);
304  image_bad = cpl_mask_get_data_const(image_badmap);
305 
306  N = cpl_table_get_nrow(ordertable);
307 
308  *bivariate_fit = NULL;
309 
310  /* Initialise result table */
311  check(( tracetable = cpl_table_new(0),
312  cpl_table_new_column(tracetable, "Order" , CPL_TYPE_INT),
313  cpl_table_new_column(tracetable, "X" , CPL_TYPE_INT),
314  cpl_table_new_column(tracetable, "Y" , CPL_TYPE_DOUBLE),
315  cpl_table_new_column(tracetable, "dY" , CPL_TYPE_DOUBLE),
316  cpl_table_new_column(tracetable, "Residual_Square", CPL_TYPE_DOUBLE),
317  cpl_table_new_column(tracetable, "OrderRMS" , CPL_TYPE_DOUBLE),
318  cpl_table_new_column(tracetable, "OrderSlope" , CPL_TYPE_DOUBLE)),
319  /* The order's RMS (from linear fit) */
320  "Could not initialize order trace table");
321 
322  /* Info about the order */
323  check(( cpl_table_new_column(ordertable, "Xcenter", CPL_TYPE_INT),
324  cpl_table_new_column(ordertable, "Ycenter", CPL_TYPE_INT),
325  cpl_table_new_column(ordertable, "OrderLength", CPL_TYPE_INT),
326  cpl_table_new_column(ordertable, "Threshold", CPL_TYPE_DOUBLE),
327  cpl_table_new_column(ordertable, "MinThreshold", CPL_TYPE_DOUBLE),
328  cpl_table_new_column(ordertable, "RMS", CPL_TYPE_DOUBLE),
329  cpl_table_new_column(ordertable, "TraceSlope", CPL_TYPE_DOUBLE)),
330  "Could not add columns to order table");
331 
332  *orders_traced = 0;
333 
334  /* Trace all orders and make a linear fit */
335  for (order = 1; order <= N; order++)
336  {
337  /* Calculate parameters used for tracing */
338  int nx = cpl_image_get_size_x(inputimage);
339  int ny = cpl_image_get_size_y(inputimage);
340  int points_traced = 0;
341  int xc = get_xcenter (nx, ny, ordertable, order - 1);
342  int yc = get_ycenter (nx, ny, ordertable, order - 1);
343 
344  check(( cpl_table_set_int(ordertable, "Xcenter" , order - 1, xc),
345  /* Order n is at row n-1 */
346  cpl_table_set_int(ordertable, "Ycenter" , order - 1, yc),
347  cpl_table_set_int(ordertable, "OrderLength" , order - 1,
348  get_orderlength (nx, ny, ordertable, order - 1))),
349  "Could not calculate order line geometry");
350 
351  if (!(1 <= xc && xc <= nx && 1 <= yc && yc <= ny))
352  {
353  uves_msg_warning("Order %d: Center of order (%d, %d) is outside image "
354  "(intersept = %.2f, slope = %f)",
355  order, xc, yc,
356  cpl_table_get_double(ordertable, "Intersept", order-1, NULL),
357  cpl_table_get_double(ordertable, "Slope", order-1, NULL));
358  }
359  else
360  {
361  check( cpl_table_set_double(
362  ordertable, "Threshold" , order - 1,
363  estimate_threshold(inputimage, noise, ordertable, order - 1, -1/*not used*/)),
364  "Could not calculate max. threshold");
365  check( cpl_table_set_double(
366  ordertable, "MinThreshold", order - 1,
367  estimate_threshold(inputimage, noise, ordertable, order - 1, MINTHRESH)),
368  "Could not calculate min. threshold");
369  }
370 
371  /* Trace this order */
372  uves_free_table(&singletrace);
373  check( singletrace = trace_order(ordertable,
374  order,
375  inputimage,
376  noise,
377  image_bad,
378  TRACESTEP,
379  MAXGAP),
380  "Error occured while tracing order #%d", order);
381 
382  check( points_traced = cpl_table_get_nrow(singletrace), "Could not read table size");
383 
384  passure( cpl_table_get_ncol(singletrace) == 3, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(singletrace));
385  passure( cpl_table_has_column(singletrace, "X"), " ");
386  passure( cpl_table_has_column(singletrace, "Y"), " ");
387  passure( cpl_table_has_column(singletrace, "dY"), " ");
388 
389  /* If no points could be located, issue a warning
390  and continue with the next order */
391  if (points_traced == 0)
392  {
393  uves_msg_warning("Could not trace order #%d", order);
394  check( cpl_table_set_invalid(ordertable, "RMS", order - 1),
395  "Could not flag order %d RMS as invalid", order);
396  }
397  else
398  { /* At least one x-position of this order was traced */
399  double rms=0;
400  double slope=0;
401 
402  /* Fit order (linear) and write RMS to trace
403  table and to (hough) order table */
404  check( rms = fit_order_linear(singletrace, order, KAPPA, &slope),
405  "Creating linear fit of order #%d failed", order);
406 
407  check(( cpl_table_set_double(ordertable, "RMS", order - 1, rms),
408  cpl_table_fill_column_window_double(singletrace, "OrderRMS",
409  0, points_traced, rms)),
410  "Could not write RMS of order #%d to tables", order);
411 
412  check(( cpl_table_set_double(ordertable, "TraceSlope", order - 1, slope),
413  cpl_table_fill_column_window_double(singletrace, "OrderSlope",
414  0, points_traced, slope)),
415  "Could not write slope of order #%d to tables", order);
416 
417 
418  passure( cpl_table_get_ncol(singletrace) == 7, "%" CPL_SIZE_FORMAT "",
419  cpl_table_get_ncol(singletrace));
420  passure( cpl_table_has_column(singletrace, "X"), " ");
421  passure( cpl_table_has_column(singletrace, "Y"), " ");
422  passure( cpl_table_has_column(singletrace, "dY"), " ");
423  passure( cpl_table_has_column(singletrace, "Linear fit"), " ");
424  passure( cpl_table_has_column(singletrace, "Residual_Square"), " ");
425  passure( cpl_table_has_column(singletrace, "OrderRMS"), " ");
426  passure( cpl_table_has_column(singletrace, "OrderSlope"), " ");
427 
428  /* Remove unnecessary column before appending */
429  check( cpl_table_erase_column(singletrace, "Linear fit"),
430  "Could not delete column 'Linear fit'");
431 
432  /* Write current order number to single order table */
433  check(( cpl_table_new_column(singletrace, "Order", CPL_TYPE_INT),
434  cpl_table_fill_column_window_int(
435  singletrace, "Order",
436  0, cpl_table_get_nrow(singletrace), order)
437  ),
438  "Could not create new column 'Order'");
439 
440  /* The two tables now contain the same columns */
441  passure( cpl_table_compare_structure(singletrace, tracetable) == 0, " ");
442 
443  /* Append to 'tracetable' */
444  check( cpl_table_insert(tracetable, singletrace,
445  cpl_table_get_nrow(tracetable)),
446  "Could not append single order #%d to trace table", order);
447 
448  *orders_traced += 1;
449  }
450 
451  }/* for ... order */
452 
453  /* Plot initial (before rejection) order tracing */
454  check( uves_plot_table(tracetable, "X", "Y",
455  "Initial trace (%d orders)", *orders_traced),
456  "Plotting failed");
457 
458  /* The trace table now contains these columns */
459  passure( cpl_table_get_ncol(tracetable) == 7, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(tracetable));
460  passure( cpl_table_has_column(tracetable, "X"), " ");
461  passure( cpl_table_has_column(tracetable, "Order"), " ");
462  passure( cpl_table_has_column(tracetable, "Y"), " ");
463  passure( cpl_table_has_column(tracetable, "dY"), " ");
464  passure( cpl_table_has_column(tracetable, "Residual_Square"), " ");
465  passure( cpl_table_has_column(tracetable, "OrderRMS"), " ");
466  passure( cpl_table_has_column(tracetable, "OrderSlope"), " ");
467 
468  assure(*orders_traced >= 1, CPL_ERROR_ILLEGAL_OUTPUT, "No orders could be traced");
469 
470  /* Remove badly traced orders from 'tracetable' */
471  {
472  double maxrms;
473  int orders_rejected;
474  check( maxrms =
475  uves_max_double(0.05, MAXRMS * cpl_table_get_column_median(ordertable, "RMS")),
476  "Could not read median RMS");
477 
478  uves_msg_debug("Maximum admissible RMS is %.2f pixels", maxrms);
479 
480  /* Select orders with RMS > maxrms */
481  check( orders_rejected = uves_select_table_rows(
482  ordertable, "RMS", CPL_GREATER_THAN, maxrms),
483  "Could not select rows in order table");
484 
485  /* Delete rows from trace table */
486  if (orders_rejected > 0)
487  {
488  uves_msg_warning("%d order(s) rejected because RMS "
489  "(from linear fit) was too large", orders_rejected);
490 
491  /* Delete rejected orders from 'tracetable' */
492  check( uves_erase_table_rows(tracetable, "OrderRMS",
493  CPL_GREATER_THAN, maxrms),
494  "Could not erase bad orders from trace table");
495 
496  /* Don't remove from 'ordertable' */
497  }
498  else
499  {
500  uves_msg_debug("All RMSs are less than %.2f", maxrms);
501  }
502 
503 
504  /* Reject based on line slope
505  (this is not the slope from a Hough transform
506  but the slope measured after tracing the order)
507  */
508  check_nomsg( orders_rejected =
509  uves_select_table_rows(
510  ordertable, "TraceSlope", CPL_GREATER_THAN, 0.5) +
511  uves_select_table_rows(
512  ordertable, "TraceSlope", CPL_LESS_THAN, -0.5));
513 
514  if (orders_rejected > 0) {
515  uves_msg_warning("%d order(s) rejected because slope was outside [-0.5 ; 0.5]",
516  orders_rejected);
517 
518  check_nomsg( uves_erase_table_rows(tracetable, "OrderSlope",
519  CPL_GREATER_THAN, 0.5));
520  check_nomsg( uves_erase_table_rows(tracetable, "OrderSlope",
521  CPL_LESS_THAN, -0.5));
522  }
523  else {
524  uves_msg_debug("All line slopes are within [-0.5 ; 0.5]");
525  }
526  }
527 
528  /* Remove points with too low 'dY',
529  * they would have too much weight in fit.
530  */
531  {
532  double dy_median = cpl_table_get_column_median(tracetable, "dY");
533  double threshold = 0.40*dy_median;
534  int nreject;
535 
536  check_nomsg( nreject = uves_erase_table_rows(tracetable, "dY", CPL_LESS_THAN,
537  threshold) );
538 
539  uves_msg_debug("Rejected %d points with dY less than %f pixels (median = %f pixels)",
540  nreject, threshold, dy_median);
541  }
542 
543  /* Auto-detect optimal pol. degree if it is negative
544  * (i.e. not specified)
545  */
546  if (*DEFPOL1 < 0 || *DEFPOL2 < 0)
547  {
548  int deg1, deg2; /* Current degrees */
549  int new_deg1, new_deg2; /* New degrees */
550  double red_chisq1, mse1; /* Reduced chi^sq, mse for (DEG1+1, DEG2 ) */
551  double red_chisq2, mse2; /* Reduced chi^sq, mse for (DEG1 , DEG2+1) */
552  double red_chisq3, mse3; /* Reduced chi^sq, mse for (DEG1+1, DEG2+1) */
553  bool adjust1 = (*DEFPOL1 < 0); /* Flags indicating if DEFPOL1/DEFPOL2
554  should be adjusted */
555  bool adjust2 = (*DEFPOL2 < 0); /* (or is held constant) */
556  int finished; /* 0 = finished,
557  1 = moved to (DEG1+1, DEG2 )
558  2 = moved to (DEG1 , DEG2+1)
559  3 = moved to (DEG1+1, DEG2+1) */
560  int number_of_orders = 0; /* The number of order lines left after
561  kappa-sigma clipping */
562  int number_of_orders1 = 0;
563  int number_of_orders2 = 0;
564  int number_of_orders3 = 0;
565 
566  if (adjust1)
567  {
568  /* Initialize */
569  *DEFPOL1 = 1;
570  deg1 = 1;
571  }
572  else
573  {
574  /* Don't modify */
575  deg1 = *DEFPOL1;
576  }
577  if (adjust2)
578  {
579  /* Initialize */
580  *DEFPOL2 = 1;
581  deg2 = 1;
582  }
583  else
584  {
585  /* Don't modify */
586  deg2 = *DEFPOL2;
587  }
588 
589  uves_free_table(&temp);
590  temp = cpl_table_duplicate(tracetable);
591  uves_polynomial_delete(bivariate_fit);
592  check( *bivariate_fit = uves_polynomial_regression_2d(
593  temp,
594  "X", "Order", "Y", "dY",
595  deg1,
596  deg2,
597  NULL, NULL, NULL, /* No extra columns */
598  &mse, &red_chisq,
599  NULL, /* No variance polynomial */
600  KAPPA, -1),
601  "Error fitting orders");
602 
603  check( number_of_orders = count_orders(temp),
604  "Error counting orders");
605 
606  uves_msg_low("(%d, %d)-degree: RMS = %.3f pixels. "
607  "Red.chi^2 = %.2f (%d orders) *",
608  deg1,
609  deg2,
610  sqrt(mse),
611  red_chisq,
612  number_of_orders);
613 
614  /* Find best values of deg1, deg2 less than or equal to 8,8
615  (the fitting algorithm is unstable after this point, anyway)
616 
617  */
618  do
619  {
620  int maxdegree = 6;
621  finished = 0;
622 
623  adjust1 = adjust1 && (deg1 + 1 <= maxdegree);
624  adjust2 = adjust2 && (deg2 + 1 <= maxdegree);
625 
626  /* Try (deg1+1, deg2) */
627  if (adjust1)
628  {
629  uves_free_table(&temp);
630  temp = cpl_table_duplicate(tracetable);
631  uves_polynomial_delete(bivariate_fit);
632  *bivariate_fit = uves_polynomial_regression_2d(
633  temp,
634  "X", "Order", "Y", "dY",
635  deg1 + 1,
636  deg2,
637  NULL, NULL, NULL, /* extra columns */
638  &mse1, &red_chisq1,
639  NULL, /* variance polynomial */
640  KAPPA, -1);
641 
642  if (cpl_error_get_code() == CPL_ERROR_SINGULAR_MATRIX)
643  {
645  mse1 = -1;
646  red_chisq1 = DBL_MAX/2;
647  }
648  else
649  {
650  assure( cpl_error_get_code() == CPL_ERROR_NONE,
651  cpl_error_get_code(),
652  "Error fitting orders");
653 
654  check( number_of_orders1 = count_orders(temp),
655  "Error counting orders");
656  }
657  }
658 
659  /* Try (deg1, deg2+1) */
660  if (adjust2)
661  {
662  uves_free_table(&temp);
663  temp = cpl_table_duplicate(tracetable);
664  uves_polynomial_delete(bivariate_fit);
665  *bivariate_fit = uves_polynomial_regression_2d(
666  temp,
667  "X", "Order", "Y", "dY",
668  deg1,
669  deg2 + 1,
670  NULL, NULL, NULL, /* No extra columns */
671  &mse2, &red_chisq2,
672  NULL, /* No variance polynomial */
673  KAPPA, -1);
674 
675  if (cpl_error_get_code() == CPL_ERROR_SINGULAR_MATRIX)
676  {
678  mse2 = -1;
679  red_chisq2 = DBL_MAX/2;
680  }
681  else
682  {
683  assure( cpl_error_get_code() == CPL_ERROR_NONE,
684  cpl_error_get_code(),
685  "Error fitting orders");
686 
687  check( number_of_orders2 = count_orders(temp),
688  "Error counting orders");
689  }
690  }
691 
692  /* Try (deg1+1, deg2+1) */
693  if (adjust1 && adjust2)
694  {
695  uves_free_table(&temp);
696  temp = cpl_table_duplicate(tracetable);
697  uves_polynomial_delete(bivariate_fit);
698  *bivariate_fit = uves_polynomial_regression_2d(
699  temp,
700  "X", "Order", "Y", "dY",
701  deg1 + 1,
702  deg2 + 1,
703  NULL, NULL, NULL, /* extra columns */
704  &mse3, &red_chisq3,
705  NULL, /* variance polynomial */
706  KAPPA, -1);
707 
708  if (cpl_error_get_code() == CPL_ERROR_SINGULAR_MATRIX)
709  {
711  mse3 = -1;
712  red_chisq3 = DBL_MAX/2;
713  }
714  else
715  {
716  assure( cpl_error_get_code() == CPL_ERROR_NONE,
717  cpl_error_get_code(),
718  "Error fitting orders");
719 
720  check( number_of_orders3 = count_orders(temp),
721  "Error counting orders");
722  }
723  }
724 
725  /* If fit is significantly better (say, 10% improvement
726  * in chi^2) in either direction, (in (degree,degree)-space)
727  * then move in that direction.
728  *
729  * First try to move one step horizontal/vertical,
730  * otherwise try to move
731  * diagonally (i.e. increase both degrees)
732  *
733  * Assign to DEFPOL1/2 only if enough orders were detected.
734  */
735 
736  new_deg1 = deg1;
737  new_deg2 = deg2;
738  if (adjust1 && mse1 >= 0 && (red_chisq - red_chisq1)/red_chisq > 0.1 &&
739  red_chisq1 <= red_chisq2)
740  {
741  new_deg1++;
742  mse = mse1;
743  red_chisq = red_chisq1;
744  finished = 1;
745 
746  if (number_of_orders1 >= number_of_orders)
747  {
748  *DEFPOL1 = new_deg1;
749  *DEFPOL2 = new_deg2;
750  number_of_orders = number_of_orders1;
751  }
752  }
753  else if (adjust2 && mse2 >= 0 && (red_chisq - red_chisq2)/red_chisq > 0.1 &&
754  red_chisq2 < red_chisq1)
755  {
756  new_deg2++;
757  mse = mse2;
758  red_chisq = red_chisq2;
759  finished = 2;
760 
761  if (number_of_orders2 >= number_of_orders)
762  {
763  *DEFPOL1 = new_deg1;
764  *DEFPOL2 = new_deg2;
765  number_of_orders = number_of_orders2;
766  }
767  }
768  else if (adjust1 && adjust2 &&
769  mse3 >= 0 && (red_chisq - red_chisq3)/red_chisq > 0.1)
770  {
771  new_deg1++;
772  new_deg2++;
773  mse = mse3;
774  red_chisq = red_chisq3;
775  finished = 3;
776 
777  if (number_of_orders3 >= number_of_orders)
778  {
779  *DEFPOL1 = new_deg1;
780  *DEFPOL2 = new_deg2;
781  number_of_orders = number_of_orders3;
782  }
783  }
784 
785  /* Print mse, chi^2, ...
786  * Add a star '*' at the better solution (if any).
787  */
788  if (adjust1)
789  {
790  if (mse1 >= 0)
791  {
792  uves_msg_low("(%d, %d)-degree: RMS = %.3f pixels. "
793  "Red.chi^2 = %.3f (%d orders)%s",
794  deg1 + 1,
795  deg2,
796  sqrt(mse1),
797  red_chisq1,
798  number_of_orders1,
799  (finished == 1) ? " *" : "");
800  }
801  else
802  {
803  uves_msg_low("(%d, %d)-degree: Singular matrix",
804  deg1 + 1,
805  deg2);
806  }
807  }
808 
809  if (adjust2)
810  {
811  if (mse2 >= 0)
812  {
813  uves_msg_low("(%d, %d)-degree: RMS = %.3f pixels. "
814  "Red.chi^2 = %.3f (%d orders)%s",
815  deg1,
816  deg2 + 1,
817  sqrt(mse2),
818  red_chisq2,
819  number_of_orders2,
820  (finished == 2) ? " *" : "");
821  }
822  else
823  {
824  uves_msg_low("(%d, %d)-degree: Singular matrix",
825  deg1,
826  deg2 + 1);
827  }
828  }
829 
830  if (adjust1 && adjust2)
831  {
832  if (mse3 >= 0)
833  {
834  uves_msg_low("(%d, %d)-degree: RMS = %.3f pixels. "
835  "Red.chi^2 = %.3f (%d orders)%s",
836  deg1 + 1,
837  deg2 + 1,
838  sqrt(mse3),
839  red_chisq3,
840  number_of_orders3,
841  (finished == 3) ? " *" : "");
842  }
843  else
844  {
845  uves_msg_low("(%d, %d)-degree: Singular matrix",
846  deg1 + 1,
847  deg2 + 1);
848  }
849  }
850 
851  if (finished != 0)
852  {
853  uves_msg_debug("Moved to degree (%d, %d), finished = %d, "
854  "DEFPOL = %d, %d",
855  new_deg1, new_deg2, finished, *DEFPOL1, *DEFPOL2);
856  }
857 
858  deg1 = new_deg1;
859  deg2 = new_deg2;
860 
861  } while (finished != 0);
862 
863  uves_msg_low("Using degree (%d, %d)", *DEFPOL1, *DEFPOL2);
864 
865  }/* endif auto degree */
866 
867  /* Make the final fit */
868  uves_polynomial_delete(bivariate_fit);
869  check( *bivariate_fit = uves_polynomial_regression_2d(tracetable,
870  "X", "Order", "Y", "dY",
871  *DEFPOL1,
872  *DEFPOL2,
873  "Yfit", NULL, "dYfit_Square",
874  &mse, &red_chisq,
875  NULL, /* variance polynomial */
876  KAPPA, -1),
877  "Error fitting orders");
878 
879  uves_msg("RMS error of (%d, %d)-degree fit is %.3f pixels. Reduced chi^2 is %.3f",
880  *DEFPOL1,
881  *DEFPOL2,
882  sqrt(mse),
883  red_chisq);
884 
885  /* Warn about bad fit */
886  if (sqrt(mse) > 0.3)
887  {
888  uves_msg_warning("RMS of bivariate fit (%.2f pixels) "
889  "is larger than 0.3 pixels", sqrt(mse));
890  }
891  if (red_chisq < .01)
892  {
893  uves_msg_warning("Reduced chi^2 of fit is less than 1/100: %f", red_chisq);
894  }
895  if (red_chisq > 100)
896  {
897  uves_msg_warning("Reduced chi^2 of fit is greater than 100: %f", red_chisq);
898  }
899 
900  /* Create residual column 'Residual' := 'Y' - 'Yfit' */
901  check(( cpl_table_duplicate_column(tracetable, "Residual", tracetable, "Y"),
902  cpl_table_subtract_columns(tracetable, "Residual", "Yfit")),
903  "Error calculating residuals of fit");
904 
905  /* Show how many orders were traced */
906  {
907  check( *orders_traced = count_orders(tracetable),
908  "Error counting orders");
909 
910  uves_msg("%d order(s) were traced", *orders_traced);
911  if (*orders_traced < N)
912  {
913  uves_msg_warning("Rejected %d order(s)", N - *orders_traced);
914  }
915  }
916 
917  /* Plot results */
918  check( uves_plot_table(tracetable, "X", "Yfit", "%d orders detected", *orders_traced),
919  "Plotting failed");
920  check( uves_plot_table(tracetable, "X", "Residual",
921  "Residual of fit (RMS = %.3f pixels; red.chi^2 = %f)",
922  sqrt(mse), red_chisq), "Plotting failed");
923  check( uves_plot_table(tracetable, "Y", "Residual",
924  "Residual of fit (RMS = %.3f pixels; red.chi^2 = %f)",
925  sqrt(mse), red_chisq), "Plotting failed");
926 
927  cleanup:
928  uves_free_table(&temp);
929  uves_free_table(&singletrace);
930  if (cpl_error_get_code() != CPL_ERROR_NONE)
931  {
932  uves_free_table(&tracetable);
933  }
934 
935  return tracetable;
936 }
937 
938 
939 /*----------------------------------------------------------------------------*/
947 static int
948 count_orders(const cpl_table *tracetable)
949 {
950  int number = 0;
951  int previous = -1;
952  int row;
953 
954  passure( tracetable != NULL, " ");
955  passure( cpl_table_has_column(tracetable, "Order"), " ");
956 
957  for (row = 0; row < cpl_table_get_nrow(tracetable); row++)
958  {
959  int current;
960  current = cpl_table_get_int(tracetable, "Order", row, NULL);
961  if (current != previous)
962  {
963  number++;
964  }
965  previous = current;
966  }
967 
968  cleanup:
969  return number;
970 
971 }
972 
973 
974 /*----------------------------------------------------------------------------*/
989 /*----------------------------------------------------------------------------*/
990 
991 static double
992 fit_order_linear(cpl_table *singletrace,
993  int order,
994  double KAPPA,
995  double *slope)
996 {
997  double mse = 0; /* mean square error of the fit */
998  double intersept;
999  cpl_table *temp = NULL; /* Don't remove rows from the input table */
1000  polynomial *pol = NULL; /* The 1d polynomial */
1001 
1002  passure( slope != NULL, " ");
1003  passure( cpl_table_get_ncol(singletrace) == 3, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(singletrace));
1004  passure( cpl_table_has_column(singletrace, "X"), " ");
1005  passure( cpl_table_has_column(singletrace, "Y"), " ");
1006  passure( cpl_table_has_column(singletrace, "dY")," ");
1007 
1008  check( temp = cpl_table_duplicate(singletrace),
1009  "Error cloning table");
1010 
1011  if (cpl_table_get_nrow(temp) == 1)
1012  {
1013  /* Only one point: create another point at next table row (1) to
1014  make linear fitting is possible. */
1015  check(( cpl_table_set_size(temp, 2),
1016  cpl_table_set_int (temp, "X", 1, uves_max_int(
1017  cpl_table_get_int (temp, "X", 0, NULL) - 1, 1)),
1018  cpl_table_set_double(temp, "Y", 1,
1019  cpl_table_get_double(temp, "Y", 0, NULL)),
1020  cpl_table_set_double(temp, "dY", 1,
1021  cpl_table_get_double(temp, "dY",0, NULL))),
1022  "Could not add point");
1023  }
1024 
1025  /* Make the linear fit. When kappa-sigma clipping, rows
1026  are removed. Therefore, use a copy of the input table */
1028  "X", "Y", NULL,/* Unweighted fit
1029  for robustness */
1030  1, /* Degree */
1031  NULL, NULL, /* Fit, residual */
1032  &mse,
1033  KAPPA),
1034  "Fitting of order %d failed. You may have to increase value of kappa",
1035  order);
1036 
1037  intersept = uves_polynomial_get_coeff_1d(pol, 0);
1038  *slope = uves_polynomial_get_coeff_1d(pol, 1);
1039 
1040  uves_msg_debug("The RMS error of order #%d is %.2f pixels; "
1041  "slope = %f; intersept = %f",
1042  order, sqrt(mse),
1043  *slope, intersept);
1044 
1045  /* Write results of fit to input table */
1046  {
1047  int i;
1048 
1049  check(( cpl_table_new_column(singletrace, "Linear fit", CPL_TYPE_DOUBLE),
1050  cpl_table_new_column(singletrace, "Residual_Square", CPL_TYPE_DOUBLE)),
1051  "Error adding table columns");
1052 
1053  for (i = 0; i < cpl_table_get_nrow(singletrace); i++)
1054  {
1055  int x = cpl_table_get_int (singletrace, "X", i, NULL);
1056  double y = cpl_table_get_double(singletrace, "Y", i, NULL);
1057 
1058  double linear_fit, residual;
1059 
1060  check (linear_fit = uves_polynomial_evaluate_1d(pol, x),
1061  "Error evaluating polynomial");
1062 
1063  residual = y - linear_fit;
1064 
1065  check(( cpl_table_set_double(singletrace, "Linear fit", i, linear_fit),
1066  cpl_table_set_double(singletrace, "Residual_Square",
1067  i, residual*residual)),
1068  "Error updating table");
1069  }
1070  }
1071 
1072  /* Add info about the order's RMS+slope for each point */
1073  check(( cpl_table_new_column(singletrace, "OrderRMS", CPL_TYPE_DOUBLE),
1074  cpl_table_new_column(singletrace, "OrderSlope", CPL_TYPE_DOUBLE),
1075  cpl_table_fill_column_window_double(
1076  singletrace, "OrderRMS", 0, cpl_table_get_nrow(singletrace), sqrt(mse)),
1077  cpl_table_fill_column_window_double(
1078  singletrace, "OrderSlope", 0, cpl_table_get_nrow(singletrace), *slope)),
1079  "Could not create columns OrderRMS and OrderSlope");
1080 
1081  passure( cpl_table_get_ncol(singletrace) == 7, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(singletrace));
1082  passure( cpl_table_has_column(singletrace, "X"), " ");
1083  passure( cpl_table_has_column(singletrace, "Y"), " ");
1084  passure( cpl_table_has_column(singletrace, "dY")," ");
1085  passure( cpl_table_has_column(singletrace, "Linear fit"), " ");
1086  passure( cpl_table_has_column(singletrace, "Residual_Square"), " ");
1087  passure( cpl_table_has_column(singletrace, "OrderRMS"), " ");
1088  passure( cpl_table_has_column(singletrace, "OrderSlope"), " ");
1089 
1090  cleanup:
1091  uves_free_table(&temp);
1092  uves_polynomial_delete(&pol);
1093  return sqrt(mse);
1094 
1095 }
1096 
1097 /*----------------------------------------------------------------------------*/
1124 /*----------------------------------------------------------------------------*/
1125 
1126 static cpl_table *
1127 trace_order(const cpl_table *ordertable, int order,
1128  const cpl_image *inputimage, const cpl_image *noise,
1129  const cpl_binary *image_bad,
1130  int TRACESTEP,
1131  double MAXGAP)
1132 {
1133  cpl_table *singletrace = NULL;
1134  int tracerow; /* pointing to the next empty row in the tracetable */
1135  int DIRECTION;
1136  double slope;
1137  double threshold;
1138  double minthreshold;
1139  int nx;
1140  int xcenter;
1141  int ycenter;
1142  int orderlength; /* x-distance between endpoints */
1143  int order_spacing; /* approximate distance to next order(s) */
1144  int xmax, xmin;
1145 
1146  nx = cpl_image_get_size_x(inputimage);
1147  /* Initialize result */
1148  check(( singletrace =
1149  cpl_table_new(nx/TRACESTEP + 2),
1150  cpl_table_new_column(singletrace, "X", CPL_TYPE_INT),
1151  cpl_table_new_column(singletrace, "Y", CPL_TYPE_DOUBLE),
1152  cpl_table_new_column(singletrace, "dY",CPL_TYPE_DOUBLE),
1153  tracerow = 0),
1154  "Could not initialize tracetable");
1155 
1156  /* Trace the order */
1157  /* While less than TRACEITER of the order is traced
1158  lower threshold, and try again
1159  But don't try more than three times */
1160 
1161  /* Order number n is in ordertable row n-1 */
1162  check((xcenter = cpl_table_get_int (ordertable, "Xcenter" , order - 1, NULL),
1163  ycenter = cpl_table_get_int (ordertable, "Ycenter" , order - 1, NULL),
1164  orderlength = cpl_table_get_int (ordertable, "OrderLength" , order - 1, NULL),
1165  order_spacing= cpl_table_get_int (ordertable, "Spacing" , order - 1, NULL),
1166  threshold = cpl_table_get_double(ordertable, "Threshold" , order - 1, NULL),
1167  minthreshold = cpl_table_get_double(ordertable, "MinThreshold", order - 1, NULL)),
1168  "Reading order table failed");
1169 
1170  /* Trace once using the minimum threshold */
1171  threshold = minthreshold;
1172 
1173 
1174  /* Clear the trace table */
1175  tracerow = 0;
1176 
1177  xmax = xmin = xcenter;
1178 
1179  /* Trace it to the left, trace it to the right */
1180  for (DIRECTION = -1; DIRECTION <= 1; DIRECTION += 2) {
1181  /* Start tracing at this position */
1182  int x = xcenter;
1183  double y = (double) ycenter;
1184  double dy = 0;
1185  int gap_size = 0; /* gap size (for jumping) in pixels */
1186 
1187  check( slope = cpl_table_get_double(
1188  ordertable, "Slope", order - 1, NULL),
1189  "Could not read slope from table");
1190 
1191  if (xcenter < nx/10 || xcenter > (nx*99)/100) {
1192  /* Order at very edge of chip. Give up */
1193  x = 0;
1194 
1195  /* The numbers chosen here: 10% left and 1% right
1196  are finetuned to the blaze-function of UVES */
1197  }
1198 
1199  while(1 <= x && x <= nx && gap_size < MAXGAP*nx) {
1200  bool found;
1201 
1202  check( found = find_centroid(
1203  inputimage, noise, image_bad, threshold,
1204  order_spacing, x, &y, &dy),
1205  "Could not get order line position");
1206 
1207  /* If found and if
1208  new slope when including this detection is
1209  inside [-1;1] */
1210  if (found &&
1211  (y - ycenter)/(x - xcenter) > -1 &&
1212  (y - ycenter)/(x - xcenter) < 1) {
1213 
1214  /* Update xmax, xmin */
1215  xmax = uves_max_int(xmax, x);
1216  xmin = uves_min_int(xmin, x);
1217 
1218  uves_msg_debug("(Order, x, y, dy, threshold) = "
1219  "(%d, %d, %f, %f, %f)",
1220  order, x, y, dy, threshold);
1221 
1222  if (!(x == xcenter && DIRECTION == 1))
1223  /* Update table */
1224  /* When tracing right, don't insert the
1225  center point again */
1226 
1227  {
1228  cpl_table_set_int (
1229  singletrace, "X", tracerow, x);
1230  cpl_table_set_double(
1231  singletrace, "Y", tracerow, y);
1232  if (dy > 0) {
1233  cpl_table_set_double(
1234  singletrace, "dY", tracerow, dy);
1235  }
1236  else {
1237  cpl_table_set_invalid(
1238  singletrace, "dY", tracerow);
1239  }
1240  tracerow++;
1241  }
1242 
1243  gap_size = 0;
1244 
1245  }/* If order found */
1246  else {
1247  gap_size += TRACESTEP;
1248  }
1249 
1250  /* Initial 'slope' will be the Hough slope */
1251  x = x + DIRECTION * TRACESTEP;
1252  y = y + slope*DIRECTION * TRACESTEP;
1253 
1254  slope = (y - ycenter)/(x - xcenter);
1255 
1256  }/* while */
1257 
1258  }/* for... DIRECTION */
1259 
1260  /* Now width of the trace is (xmax - xmin + 1) */
1261 
1262  uves_msg_debug("%d points were traced in order %d", tracerow, order);
1263 
1264  /* Remove the last part of the table (garbage) */
1265  check( cpl_table_set_size(singletrace, tracerow), "Could not resize tracetable");
1266 
1267  /* Set the undetermined 'dY' column values to some value that is not completely off
1268  (such as the median of all other dY). If there are no other points, set dY
1269  to 1.0 pixel which effectively excludes the point from later fits. */
1270  {
1271  double dy_median;
1272 
1273  if (cpl_table_has_valid(singletrace, "dY"))
1274  {
1275  /* Invalid column values are excluded from the computation */
1276  dy_median = cpl_table_get_column_median(singletrace, "dY");
1277  }
1278  else
1279  {
1280  dy_median = 1.0;
1281  }
1282 
1283  /* Write median value to all invalid rows */
1284  cpl_table_select_all(singletrace);
1285  cpl_table_and_selected_invalid(singletrace, "dY");
1286  {
1287  int i;
1288  for (i = 0; i < cpl_table_get_nrow(singletrace); i++)
1289  {
1290  if (cpl_table_is_selected(singletrace, i))
1291  {
1292  cpl_table_set_double(singletrace, "dY", i, dy_median);
1293  }
1294  }
1295  }
1296  }
1297 
1298  /* Finally, sort the single order table by X */
1299  check( uves_sort_table_1(singletrace, "X", false), "Could not sort order table");
1300 
1301  cleanup:
1302  if (cpl_error_get_code() != CPL_ERROR_NONE)
1303  {
1304  uves_free_table(&singletrace);
1305  }
1306 
1307  return singletrace;
1308 }
1309 
1310 /*----------------------------------------------------------------------------*/
1323 /*----------------------------------------------------------------------------*/
1324 static int
1325 get_orderlength(int nx, int ny, cpl_table *ordertable, int row)
1326 {
1327  int x0 = 0, y_0, x1 = 0, y_1;
1328  double intersept, slope;
1329 
1330  check(( intersept = cpl_table_get_double(ordertable, "Intersept", row, NULL),
1331  slope = cpl_table_get_double(ordertable, "Slope", row, NULL)),
1332  "Could not read line from ordertable");
1333 
1334  /* The left endpoint of the order line is... */
1335  x0 = 1;
1336  y_0 = uves_round_double(intersept + slope*x0);
1337 
1338  /* However, if... */
1339  if (y_0 < 1)
1340  {
1341  y_0 = 1;
1342  x0 = uves_round_double((y_0 - intersept)/slope); /* y = intersept + slope*x */
1343  }
1344 
1345  /* The right endpoint */
1346  x1 = nx;
1347  y_1 = uves_round_double(intersept + slope*nx);
1348  if (y_1 > ny)
1349  {
1350  y_1 = ny;
1351  x1 = uves_round_double((y_1 - intersept)/slope);
1352  }
1353 
1354  cleanup:
1355  return (x1 - x0);
1356 }
1357 
1358 
1359 /*----------------------------------------------------------------------------*/
1372 /*----------------------------------------------------------------------------*/
1373 static int
1374 get_xcenter(int nx, int ny, cpl_table *ordertable, int row)
1375 {
1376  int x0, y_0, x1, y_1, xc = 0;
1377  double intersept, slope;
1378  check(( intersept = cpl_table_get_double(ordertable, "Intersept", row, NULL),
1379  slope = cpl_table_get_double(ordertable, "Slope", row, NULL)),
1380  "Could not read line from ordertable");
1381 
1382  /* The left endpoint of the order line */
1383  x0 = 1;
1384  y_0 = uves_round_double(intersept + slope*x0);
1385 
1386  /* However, if... */
1387  if (y_0 < 1)
1388  {
1389  y_0 = 1;
1390  x0 = uves_round_double((y_0 - intersept)/slope);
1391  /* y = intersept + slope*x */
1392  }
1393 
1394 
1395  /* The right endpoint */
1396  x1 = nx;
1397  y_1 = uves_round_double(intersept + slope*nx);
1398 
1399  /* However, if ... */
1400  if (y_1 > ny)
1401  {
1402  y_1 = ny;
1403  x1 = uves_round_double((y_1 - intersept)/slope);
1404  }
1405 
1406  xc = (x0 + x1)/2;
1407 
1408  cleanup:
1409  return xc;
1410 }
1411 
1412 /*----------------------------------------------------------------------------*/
1424 /*----------------------------------------------------------------------------*/
1425 static int
1426 get_ycenter(int nx, int ny, cpl_table *ordertable, int row)
1427 {
1428  int xc = 0;
1429  int yc = 0;
1430  check( xc = get_xcenter(nx, ny, ordertable, row), "Could not find x-center of order");
1431 
1432  check( yc = uves_round_double(
1433  cpl_table_get_double(ordertable, "Slope" , row, NULL)*xc +
1434  cpl_table_get_double(ordertable, "Intersept", row, NULL)
1435  ), "Could not read line from ordertable");
1436 
1437  cleanup:
1438  return yc;
1439 }
1440 
1441 /*----------------------------------------------------------------------------*/
1457 /*----------------------------------------------------------------------------*/
1458 static double
1459 estimate_threshold(const cpl_image *inputimage, const cpl_image *noise,
1460  cpl_table *ordertable, int row, double relative_threshold)
1461 {
1462  int yupper = 0;
1463  int ylower = 0;
1464  int xc, yc;
1465  int N;
1466  int ny;
1467  double returnvalue = 0;
1468  cpl_stats *stats = NULL;
1469 
1470  passure( inputimage != NULL, " ");
1471  passure( ordertable != NULL, " ");
1472  passure( cpl_table_get_int(ordertable, "Order", row, NULL) == row+1, "%d %d",
1473  cpl_table_get_int(ordertable, "Order", row, NULL), row);
1474 
1475  check( ny = cpl_image_get_size_y(inputimage), "Could not read input image dimension");
1476 
1477  check( N = cpl_table_get_nrow(ordertable), "Could not read size of ordertable");
1478  assure(N > 1, CPL_ERROR_ILLEGAL_INPUT,
1479  "Cannot calculate orderspacing with less than 2 (i.e. %d) orders.", N);
1480  check( xc = cpl_table_get_int(ordertable, "Xcenter", row, NULL),
1481  "Could not read x-center of order #%d", row+1);
1482  check( yc = cpl_table_get_int(ordertable, "Ycenter", row, NULL),
1483  "Could not find y-center of order #%d", row+1);
1484 
1485 
1486  /* Set yupper and ylower midway between this and the adjacent orders
1487  * The y-location of the surrounding orders must be calculated at the center,
1488  * xc, of the current order
1489  */
1490  if (row < N - 1)
1491  {
1492  double ynext;
1493  check(ynext =
1494  cpl_table_get_double(ordertable, "Slope" , row + 1, NULL)*xc +
1495  cpl_table_get_double(ordertable, "Intersept", row + 1, NULL),
1496  "Could not read line from ordertable row %d", row + 1);
1497 
1498  yupper = (int)((yc + (uves_round_double(ynext)-1))/2);
1499  /* Midway between this and the next order */
1500  }
1501 
1502  if (row > 0)
1503  {
1504  double yprev;
1505  check( yprev =
1506  cpl_table_get_double(ordertable, "Slope" , row - 1, NULL)*xc +
1507  cpl_table_get_double(ordertable, "Intersept", row - 1, NULL),
1508  "Could not read line from ordertable row %d", row - 1);
1509 
1510  ylower = (int)((yc + uves_round_double(yprev)-1)/2);
1511  /* Midway between this and the previous order */
1512  }
1513 
1514  /* We need to manually set yupper for the highest order
1515  and ylower for the lowest order */
1516  if (row == N-1)
1517  {
1518  yupper = yc + (yc - ylower);
1519  }
1520  if (row == 0)
1521  {
1522  ylower = yc - (yupper - yc);
1523  }
1524  yupper = uves_min_int(uves_max_int(yupper, 1), ny);
1525  ylower = uves_min_int(uves_max_int(ylower, 1), ny);
1526 
1527  /* Order lines were originally sorted with respect to intersept. This does not
1528  necessarily mean that their centers are also sorted (if the Hough algorithm
1529  detected wrong slopes (which happens if trying to detect too many lines)).
1530  So check this. */
1531  assure(yupper > ylower, CPL_ERROR_ILLEGAL_INPUT,
1532  "Initially detected order lines intersept!");
1533 
1534  /* Find max and min pixel values between ylower and yupper, then calculate threshold */
1535  {
1536  double minval = 0;
1537  double maxval = 0;
1538  double noise_level = 0;
1539 
1540  /* Find maximum and minimum pixels along center column */
1541  check( stats = cpl_stats_new_from_image_window(
1542  inputimage,
1543  CPL_STATS_MIN | CPL_STATS_MAX | CPL_STATS_MINPOS,
1544  xc, ylower, /* Corners of window (FITS convention) (included) */
1545  xc, yupper),
1546  "Could not get statistics on image sub-window (%d,%d)-(%d,%d)",
1547  xc, ylower, xc, yupper);
1548 
1549  check(( minval = cpl_stats_get_min(stats),
1550  maxval = cpl_stats_get_max(stats)),
1551  "Could not get minimum and maximum pixel values");
1552 
1553  /* Get noise level at the location of 'minval' */
1554  {
1555  int xpos, ypos, pis_rejected;
1556  xpos = cpl_stats_get_min_x(stats);
1557  ypos = cpl_stats_get_min_y(stats);
1558  noise_level = cpl_image_get(noise, xpos, ypos, &pis_rejected);
1559  }
1560 
1561  /* Calculate threshold */
1562  returnvalue = uves_max_double(minval + relative_threshold * (maxval - minval),
1563  (minval + noise_level) + noise_level);
1564 
1565  uves_msg_debug("Order: %d \tThreshold: %f \tMinimum: %f \tMaximum: %f"
1566  " \tNoise: %f \tWindow: (%d, %d)-(%d, %d)",
1567  row+1, returnvalue, minval, maxval, noise_level, xc, ylower, xc, yupper);
1568  }
1569 
1570  cleanup:
1571  uves_free_stats(&stats);
1572  return returnvalue;
1573 }
1574 
1575 /*----------------------------------------------------------------------------*/
1598 /*----------------------------------------------------------------------------*/
1599 static bool
1600 find_centroid(const cpl_image *inputimage, const cpl_image *noise,
1601  const cpl_binary *image_bad,
1602  double threshold, int spacing, int x, double *yguess, double *dY)
1603 {
1604  bool returnvalue = true;
1605  int nx;
1606  int ny;
1607  int y;
1608  double thisvalue = 0;
1609  int pis_rejected;
1610  int ylow = 0;
1611  int yhigh = 0;
1612  cpl_matrix *covariance = NULL;
1613 
1614  passure( inputimage != NULL, " ");
1615 
1616  nx = cpl_image_get_size_x(inputimage);
1617  ny = cpl_image_get_size_y(inputimage);
1618 
1619  passure( 1 <= x && x <= nx, "%d %d", x, nx);
1620 
1621  uves_msg_debug("Order location estimate = (%d, %f)", x, *yguess);
1622 
1623  /* Start at yguess and move to a local max */
1624 
1625  y = uves_round_double(*yguess);
1626  if (y < 1 || y > ny)
1627  {
1628  returnvalue = false;
1629  }
1630  else {
1631  bool cont; /* continue? */
1632 
1633  do {
1634  cont = false;
1635  thisvalue = cpl_image_get(inputimage, x, y , &pis_rejected);
1636  /* Move up? */
1637  if (y < ny) {
1638  double uppervalue = cpl_image_get(inputimage, x, y + 1, &pis_rejected);
1639  if (!pis_rejected && uppervalue > thisvalue)
1640  {
1641  y += 1;
1642  cont = true;
1643  }
1644  }
1645 
1646  /* Move down? */
1647  if (y > 1) {
1648  double lowervalue = cpl_image_get(inputimage, x, y - 1, &pis_rejected);
1649  if (!pis_rejected && lowervalue > thisvalue)
1650  {
1651  y -= 1;
1652  cont = true;
1653  }
1654  }
1655 
1656  } while (cont);
1657 
1658  /* Now 'thisvalue' is the local maximum */
1659 
1660  uves_msg_debug("Local maximum at (%d, %d) (value = %f)\tthreshold = %f",
1661  x, y, thisvalue, threshold);
1662 
1663  /* Return false if no value above threshold was found */
1664  if (thisvalue < threshold)
1665  {
1666  uves_msg_debug("Order not traced at (%d, %d) (value = %f)\tthreshold = %f",
1667  x, y, thisvalue, threshold);
1668  returnvalue = false;
1669  }
1670  else
1671  {
1672  /* Find and use pixels that are above half max */
1673  double minvalue;
1674  double sigmaY; /* Width of peak */
1675 
1676  double mse, rms, chi_sq;
1677  double background;
1678  double norm;
1679 
1680  /* Threshold is half of max value at this x */
1681  minvalue = 0.5*thisvalue;
1682 
1683  /* Move to the lowest y above 'minvalue' */
1684  while(y > 1 && cpl_image_get(inputimage, x, y - 1, &pis_rejected) >= minvalue)
1685  {
1686  y--;
1687  }
1688 
1689  assure( cpl_error_get_code() == CPL_ERROR_NONE,
1690  cpl_error_get_code(), "Could not read pixel from input image" );
1691 
1692  /* Remember this place */
1693  ylow = y;
1694 
1695  /* Move to the highest y above 'minvalue' */
1696  while(y < ny && cpl_image_get(inputimage, x, y + 1, &pis_rejected) >= minvalue)
1697  {
1698  y++;
1699  }
1700 
1701  assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
1702  "Could not read pixel from input image" );
1703 
1704  /* Also remember this place */
1705  yhigh = y;
1706 
1707  /* Update the order line's location to centroid of
1708  strip from ylow to yhigh w.r.t. minvalue*/
1709  {
1710  double sum = 0;
1711  double sumy = 0;
1712  double sumy2= 0;
1713  for (y = ylow; y <= yhigh; y++)
1714  {
1715  double flux;
1716  flux = cpl_image_get(inputimage, x, y, &pis_rejected) - minvalue;
1717  if (!pis_rejected && flux > 0)
1718  {
1719  sum += flux;
1720  sumy += flux * (y - *yguess*0);
1721  sumy2 += flux * (y - *yguess*0) * (y - *yguess*0);
1722  }
1723  }
1724  if (sum > 0)
1725  {
1726  *yguess = *yguess*0 + sumy / sum;
1727  sigmaY = sqrt( sumy2 / sum - sumy*sumy/(sum*sum) );
1728 
1729  if ( sumy2 / sum - sumy*sumy/(sum*sum) < 0 ||
1730  sigmaY < sqrt(1.0/12) )
1731  {
1732  /* If the sum is over one pixel, sigma will be zero
1733  (or less than zero because of numerical error),
1734  so set sigma to stddev of one pixel = 1/sqrt(12)
1735  in that case */
1736  sigmaY = sqrt(1.0/12);
1737  }
1738 
1739  /* Uncertainty, dY, of mean value (yguess) is sigma/sqrt(N)
1740  where N is the total count, i.e. area under curve */
1741  *dY = sigmaY/sqrt(sum);
1742 
1743  }
1744  else
1745  {
1746  /* If all pixels were bad, don't update '*yguess' */
1747  sigmaY = 1.0;
1748  *dY = .1;
1749 
1750  }
1751  }
1752 
1753  /* This is a better method. Get centroid
1754  position by making a Gaussian fit. */
1755 
1756  /* Use a wide fitting window to get a well defined background level */
1757  ylow = uves_max_int(1 , uves_round_double(*yguess - spacing/3));
1758  yhigh = uves_min_int(ny, uves_round_double(*yguess + spacing/3));
1759 
1760  assure( yhigh - ylow >= 1, CPL_ERROR_ILLEGAL_INPUT,
1761  "Estimated spacing too small: %d pixel(s)", spacing);
1762 
1763  /* Fit. Save the result in 'yguess' */
1764  uves_fit_1d_image(inputimage, noise,
1765  image_bad,
1766  false, false, false,
1767  ylow, yhigh, x,
1768  yguess, &sigmaY, &norm, &background, NULL,
1769  &mse, &chi_sq, &covariance,
1771 
1772  /* Recover from specific fitting errors */
1773  if (cpl_error_get_code() == CPL_ERROR_NONE)
1774  {
1775  /* Variance is guaranteed to be positive */
1776  *dY = sqrt(cpl_matrix_get(covariance, 0, 0));
1777  }
1778  else if (cpl_error_get_code() == CPL_ERROR_CONTINUE)
1779  {
1780  /* Fitting failed */
1781  uves_error_reset();
1782  uves_msg_debug("Fitting failed at (x,y) = (%d, %e), "
1783  "using centroid", x, *yguess);
1784  *dY = sigmaY / sqrt(norm);
1785  }
1786  else if (cpl_error_get_code() == CPL_ERROR_SINGULAR_MATRIX)
1787  {
1788  uves_error_reset();
1789 
1790  /* Fitting succeeded but covariance computation failed */
1791  uves_msg_debug("Covariance matrix computation failed");
1792  *dY = sigmaY / sqrt(norm);
1793  }
1794 
1795  assure(cpl_error_get_code() == CPL_ERROR_NONE,
1796  cpl_error_get_code(), "Gaussian fitting failed");
1797 
1798  rms = sqrt(mse);
1799 
1800  uves_msg_debug("dy = %f sigma/sqrt(N) = %f", *dY, sigmaY/(norm));
1801 
1802  /* If the peak is definitely there or definitely not there,
1803  set the returnvalue appropriately */
1804  if ( norm > 10 * rms)
1805  {
1806  returnvalue = true;
1807  }
1808  if ( norm < 2 * rms)
1809  {
1810  returnvalue = false;
1811  }
1812 
1813  } /* signal was above threshold at this x */
1814 
1815  }/* If yguess was inside image */
1816 
1817  cleanup:
1818  cpl_matrix_delete(covariance);
1819  return returnvalue;
1820 }