UVES Pipeline Reference Manual  5.4.6
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  cpl_table_set_column_unit(tracetable,"Order","");
918  cpl_table_set_column_unit(tracetable,"X","pix");
919  cpl_table_set_column_unit(tracetable,"Y","pix");
920  cpl_table_set_column_unit(tracetable,"dY","pix");
921  cpl_table_set_column_unit(tracetable,"Residual_Square","pix*pix");
922 
923  cpl_table_set_column_unit(tracetable,"Yfit","pix");
924  cpl_table_set_column_unit(tracetable,"dYfit_Square","pix");
925  cpl_table_set_column_unit(tracetable,"Residual","pix");
926 
927 
928  /* Plot results */
929  check( uves_plot_table(tracetable, "X", "Yfit", "%d orders detected", *orders_traced),
930  "Plotting failed");
931  check( uves_plot_table(tracetable, "X", "Residual",
932  "Residual of fit (RMS = %.3f pixels; red.chi^2 = %f)",
933  sqrt(mse), red_chisq), "Plotting failed");
934  check( uves_plot_table(tracetable, "Y", "Residual",
935  "Residual of fit (RMS = %.3f pixels; red.chi^2 = %f)",
936  sqrt(mse), red_chisq), "Plotting failed");
937 
938  cleanup:
939  uves_free_table(&temp);
940  uves_free_table(&singletrace);
941  if (cpl_error_get_code() != CPL_ERROR_NONE)
942  {
943  uves_free_table(&tracetable);
944  }
945 
946  return tracetable;
947 }
948 
949 
950 /*----------------------------------------------------------------------------*/
958 static int
959 count_orders(const cpl_table *tracetable)
960 {
961  int number = 0;
962  int previous = -1;
963  int row;
964 
965  passure( tracetable != NULL, " ");
966  passure( cpl_table_has_column(tracetable, "Order"), " ");
967 
968  for (row = 0; row < cpl_table_get_nrow(tracetable); row++)
969  {
970  int current;
971  current = cpl_table_get_int(tracetable, "Order", row, NULL);
972  if (current != previous)
973  {
974  number++;
975  }
976  previous = current;
977  }
978 
979  cleanup:
980  return number;
981 
982 }
983 
984 
985 /*----------------------------------------------------------------------------*/
1000 /*----------------------------------------------------------------------------*/
1001 
1002 static double
1003 fit_order_linear(cpl_table *singletrace,
1004  int order,
1005  double KAPPA,
1006  double *slope)
1007 {
1008  double mse = 0; /* mean square error of the fit */
1009  double intersept;
1010  cpl_table *temp = NULL; /* Don't remove rows from the input table */
1011  polynomial *pol = NULL; /* The 1d polynomial */
1012 
1013  passure( slope != NULL, " ");
1014  passure( cpl_table_get_ncol(singletrace) == 3, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(singletrace));
1015  passure( cpl_table_has_column(singletrace, "X"), " ");
1016  passure( cpl_table_has_column(singletrace, "Y"), " ");
1017  passure( cpl_table_has_column(singletrace, "dY")," ");
1018 
1019  check( temp = cpl_table_duplicate(singletrace),
1020  "Error cloning table");
1021 
1022  if (cpl_table_get_nrow(temp) == 1)
1023  {
1024  /* Only one point: create another point at next table row (1) to
1025  make linear fitting is possible. */
1026  check(( cpl_table_set_size(temp, 2),
1027  cpl_table_set_int (temp, "X", 1, uves_max_int(
1028  cpl_table_get_int (temp, "X", 0, NULL) - 1, 1)),
1029  cpl_table_set_double(temp, "Y", 1,
1030  cpl_table_get_double(temp, "Y", 0, NULL)),
1031  cpl_table_set_double(temp, "dY", 1,
1032  cpl_table_get_double(temp, "dY",0, NULL))),
1033  "Could not add point");
1034  }
1035 
1036  /* Make the linear fit. When kappa-sigma clipping, rows
1037  are removed. Therefore, use a copy of the input table */
1039  "X", "Y", NULL,/* Unweighted fit
1040  for robustness */
1041  1, /* Degree */
1042  NULL, NULL, /* Fit, residual */
1043  &mse,
1044  KAPPA),
1045  "Fitting of order %d failed. You may have to increase value of kappa",
1046  order);
1047 
1048  intersept = uves_polynomial_get_coeff_1d(pol, 0);
1049  *slope = uves_polynomial_get_coeff_1d(pol, 1);
1050 
1051  uves_msg_debug("The RMS error of order #%d is %.2f pixels; "
1052  "slope = %f; intersept = %f",
1053  order, sqrt(mse),
1054  *slope, intersept);
1055 
1056  /* Write results of fit to input table */
1057  {
1058  int i;
1059 
1060  check(( cpl_table_new_column(singletrace, "Linear fit", CPL_TYPE_DOUBLE),
1061  cpl_table_new_column(singletrace, "Residual_Square", CPL_TYPE_DOUBLE)),
1062  "Error adding table columns");
1063 
1064  for (i = 0; i < cpl_table_get_nrow(singletrace); i++)
1065  {
1066  int x = cpl_table_get_int (singletrace, "X", i, NULL);
1067  double y = cpl_table_get_double(singletrace, "Y", i, NULL);
1068 
1069  double linear_fit, residual;
1070 
1071  check (linear_fit = uves_polynomial_evaluate_1d(pol, x),
1072  "Error evaluating polynomial");
1073 
1074  residual = y - linear_fit;
1075 
1076  check(( cpl_table_set_double(singletrace, "Linear fit", i, linear_fit),
1077  cpl_table_set_double(singletrace, "Residual_Square",
1078  i, residual*residual)),
1079  "Error updating table");
1080  }
1081  }
1082 
1083  /* Add info about the order's RMS+slope for each point */
1084  check(( cpl_table_new_column(singletrace, "OrderRMS", CPL_TYPE_DOUBLE),
1085  cpl_table_new_column(singletrace, "OrderSlope", CPL_TYPE_DOUBLE),
1086  cpl_table_fill_column_window_double(
1087  singletrace, "OrderRMS", 0, cpl_table_get_nrow(singletrace), sqrt(mse)),
1088  cpl_table_fill_column_window_double(
1089  singletrace, "OrderSlope", 0, cpl_table_get_nrow(singletrace), *slope)),
1090  "Could not create columns OrderRMS and OrderSlope");
1091 
1092  passure( cpl_table_get_ncol(singletrace) == 7, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(singletrace));
1093  passure( cpl_table_has_column(singletrace, "X"), " ");
1094  passure( cpl_table_has_column(singletrace, "Y"), " ");
1095  passure( cpl_table_has_column(singletrace, "dY")," ");
1096  passure( cpl_table_has_column(singletrace, "Linear fit"), " ");
1097  passure( cpl_table_has_column(singletrace, "Residual_Square"), " ");
1098  passure( cpl_table_has_column(singletrace, "OrderRMS"), " ");
1099  passure( cpl_table_has_column(singletrace, "OrderSlope"), " ");
1100 
1101  cleanup:
1102  uves_free_table(&temp);
1103  uves_polynomial_delete(&pol);
1104  return sqrt(mse);
1105 
1106 }
1107 
1108 /*----------------------------------------------------------------------------*/
1135 /*----------------------------------------------------------------------------*/
1136 
1137 static cpl_table *
1138 trace_order(const cpl_table *ordertable, int order,
1139  const cpl_image *inputimage, const cpl_image *noise,
1140  const cpl_binary *image_bad,
1141  int TRACESTEP,
1142  double MAXGAP)
1143 {
1144  cpl_table *singletrace = NULL;
1145  int tracerow; /* pointing to the next empty row in the tracetable */
1146  int DIRECTION;
1147  double slope;
1148  double threshold;
1149  double minthreshold;
1150  int nx;
1151  int xcenter;
1152  int ycenter;
1153  int orderlength; /* x-distance between endpoints */
1154  int order_spacing; /* approximate distance to next order(s) */
1155  int xmax, xmin;
1156 
1157  nx = cpl_image_get_size_x(inputimage);
1158  /* Initialize result */
1159  check(( singletrace =
1160  cpl_table_new(nx/TRACESTEP + 2),
1161  cpl_table_new_column(singletrace, "X", CPL_TYPE_INT),
1162  cpl_table_new_column(singletrace, "Y", CPL_TYPE_DOUBLE),
1163  cpl_table_new_column(singletrace, "dY",CPL_TYPE_DOUBLE),
1164  tracerow = 0),
1165  "Could not initialize tracetable");
1166 
1167  /* Trace the order */
1168  /* While less than TRACEITER of the order is traced
1169  lower threshold, and try again
1170  But don't try more than three times */
1171 
1172  /* Order number n is in ordertable row n-1 */
1173  check((xcenter = cpl_table_get_int (ordertable, "Xcenter" , order - 1, NULL),
1174  ycenter = cpl_table_get_int (ordertable, "Ycenter" , order - 1, NULL),
1175  orderlength = cpl_table_get_int (ordertable, "OrderLength" , order - 1, NULL),
1176  order_spacing= cpl_table_get_int (ordertable, "Spacing" , order - 1, NULL),
1177  threshold = cpl_table_get_double(ordertable, "Threshold" , order - 1, NULL),
1178  minthreshold = cpl_table_get_double(ordertable, "MinThreshold", order - 1, NULL)),
1179  "Reading order table failed");
1180 
1181  /* Trace once using the minimum threshold */
1182  threshold = minthreshold;
1183 
1184 
1185  /* Clear the trace table */
1186  tracerow = 0;
1187 
1188  xmax = xmin = xcenter;
1189 
1190  /* Trace it to the left, trace it to the right */
1191  for (DIRECTION = -1; DIRECTION <= 1; DIRECTION += 2) {
1192  /* Start tracing at this position */
1193  int x = xcenter;
1194  double y = (double) ycenter;
1195  double dy = 0;
1196  int gap_size = 0; /* gap size (for jumping) in pixels */
1197 
1198  check( slope = cpl_table_get_double(
1199  ordertable, "Slope", order - 1, NULL),
1200  "Could not read slope from table");
1201 
1202  if (xcenter < nx/10 || xcenter > (nx*99)/100) {
1203  /* Order at very edge of chip. Give up */
1204  x = 0;
1205 
1206  /* The numbers chosen here: 10% left and 1% right
1207  are finetuned to the blaze-function of UVES */
1208  }
1209 
1210  while(1 <= x && x <= nx && gap_size < MAXGAP*nx) {
1211  bool found;
1212 
1213  check( found = find_centroid(
1214  inputimage, noise, image_bad, threshold,
1215  order_spacing, x, &y, &dy),
1216  "Could not get order line position");
1217 
1218  /* If found and if
1219  new slope when including this detection is
1220  inside [-1;1] */
1221  if (found &&
1222  (y - ycenter)/(x - xcenter) > -1 &&
1223  (y - ycenter)/(x - xcenter) < 1) {
1224 
1225  /* Update xmax, xmin */
1226  xmax = uves_max_int(xmax, x);
1227  xmin = uves_min_int(xmin, x);
1228 
1229  uves_msg_debug("(Order, x, y, dy, threshold) = "
1230  "(%d, %d, %f, %f, %f)",
1231  order, x, y, dy, threshold);
1232 
1233  if (!(x == xcenter && DIRECTION == 1))
1234  /* Update table */
1235  /* When tracing right, don't insert the
1236  center point again */
1237 
1238  {
1239  cpl_table_set_int (
1240  singletrace, "X", tracerow, x);
1241  cpl_table_set_double(
1242  singletrace, "Y", tracerow, y);
1243  if (dy > 0) {
1244  cpl_table_set_double(
1245  singletrace, "dY", tracerow, dy);
1246  }
1247  else {
1248  cpl_table_set_invalid(
1249  singletrace, "dY", tracerow);
1250  }
1251  tracerow++;
1252  }
1253 
1254  gap_size = 0;
1255 
1256  }/* If order found */
1257  else {
1258  gap_size += TRACESTEP;
1259  }
1260 
1261  /* Initial 'slope' will be the Hough slope */
1262  x = x + DIRECTION * TRACESTEP;
1263  y = y + slope*DIRECTION * TRACESTEP;
1264 
1265  slope = (y - ycenter)/(x - xcenter);
1266 
1267  }/* while */
1268 
1269  }/* for... DIRECTION */
1270 
1271  /* Now width of the trace is (xmax - xmin + 1) */
1272 
1273  uves_msg_debug("%d points were traced in order %d", tracerow, order);
1274 
1275  /* Remove the last part of the table (garbage) */
1276  check( cpl_table_set_size(singletrace, tracerow), "Could not resize tracetable");
1277 
1278  /* Set the undetermined 'dY' column values to some value that is not completely off
1279  (such as the median of all other dY). If there are no other points, set dY
1280  to 1.0 pixel which effectively excludes the point from later fits. */
1281  {
1282  double dy_median;
1283 
1284  if (cpl_table_has_valid(singletrace, "dY"))
1285  {
1286  /* Invalid column values are excluded from the computation */
1287  dy_median = cpl_table_get_column_median(singletrace, "dY");
1288  }
1289  else
1290  {
1291  dy_median = 1.0;
1292  }
1293 
1294  /* Write median value to all invalid rows */
1295  cpl_table_select_all(singletrace);
1296  cpl_table_and_selected_invalid(singletrace, "dY");
1297  {
1298  int i;
1299  for (i = 0; i < cpl_table_get_nrow(singletrace); i++)
1300  {
1301  if (cpl_table_is_selected(singletrace, i))
1302  {
1303  cpl_table_set_double(singletrace, "dY", i, dy_median);
1304  }
1305  }
1306  }
1307  }
1308 
1309  /* Finally, sort the single order table by X */
1310  check( uves_sort_table_1(singletrace, "X", false), "Could not sort order table");
1311 
1312  cleanup:
1313  if (cpl_error_get_code() != CPL_ERROR_NONE)
1314  {
1315  uves_free_table(&singletrace);
1316  }
1317 
1318  return singletrace;
1319 }
1320 
1321 /*----------------------------------------------------------------------------*/
1334 /*----------------------------------------------------------------------------*/
1335 static int
1336 get_orderlength(int nx, int ny, cpl_table *ordertable, int row)
1337 {
1338  int x0 = 0, y_0, x1 = 0, y_1;
1339  double intersept, slope;
1340 
1341  check(( intersept = cpl_table_get_double(ordertable, "Intersept", row, NULL),
1342  slope = cpl_table_get_double(ordertable, "Slope", row, NULL)),
1343  "Could not read line from ordertable");
1344 
1345  /* The left endpoint of the order line is... */
1346  x0 = 1;
1347  y_0 = uves_round_double(intersept + slope*x0);
1348 
1349  /* However, if... */
1350  if (y_0 < 1)
1351  {
1352  y_0 = 1;
1353  x0 = uves_round_double((y_0 - intersept)/slope); /* y = intersept + slope*x */
1354  }
1355 
1356  /* The right endpoint */
1357  x1 = nx;
1358  y_1 = uves_round_double(intersept + slope*nx);
1359  if (y_1 > ny)
1360  {
1361  y_1 = ny;
1362  x1 = uves_round_double((y_1 - intersept)/slope);
1363  }
1364 
1365  cleanup:
1366  return (x1 - x0);
1367 }
1368 
1369 
1370 /*----------------------------------------------------------------------------*/
1383 /*----------------------------------------------------------------------------*/
1384 static int
1385 get_xcenter(int nx, int ny, cpl_table *ordertable, int row)
1386 {
1387  int x0, y_0, x1, y_1, xc = 0;
1388  double intersept, slope;
1389  check(( intersept = cpl_table_get_double(ordertable, "Intersept", row, NULL),
1390  slope = cpl_table_get_double(ordertable, "Slope", row, NULL)),
1391  "Could not read line from ordertable");
1392 
1393  /* The left endpoint of the order line */
1394  x0 = 1;
1395  y_0 = uves_round_double(intersept + slope*x0);
1396 
1397  /* However, if... */
1398  if (y_0 < 1)
1399  {
1400  y_0 = 1;
1401  x0 = uves_round_double((y_0 - intersept)/slope);
1402  /* y = intersept + slope*x */
1403  }
1404 
1405 
1406  /* The right endpoint */
1407  x1 = nx;
1408  y_1 = uves_round_double(intersept + slope*nx);
1409 
1410  /* However, if ... */
1411  if (y_1 > ny)
1412  {
1413  y_1 = ny;
1414  x1 = uves_round_double((y_1 - intersept)/slope);
1415  }
1416 
1417  xc = (x0 + x1)/2;
1418 
1419  cleanup:
1420  return xc;
1421 }
1422 
1423 /*----------------------------------------------------------------------------*/
1435 /*----------------------------------------------------------------------------*/
1436 static int
1437 get_ycenter(int nx, int ny, cpl_table *ordertable, int row)
1438 {
1439  int xc = 0;
1440  int yc = 0;
1441  check( xc = get_xcenter(nx, ny, ordertable, row), "Could not find x-center of order");
1442 
1443  check( yc = uves_round_double(
1444  cpl_table_get_double(ordertable, "Slope" , row, NULL)*xc +
1445  cpl_table_get_double(ordertable, "Intersept", row, NULL)
1446  ), "Could not read line from ordertable");
1447 
1448  cleanup:
1449  return yc;
1450 }
1451 
1452 /*----------------------------------------------------------------------------*/
1468 /*----------------------------------------------------------------------------*/
1469 static double
1470 estimate_threshold(const cpl_image *inputimage, const cpl_image *noise,
1471  cpl_table *ordertable, int row, double relative_threshold)
1472 {
1473  int yupper = 0;
1474  int ylower = 0;
1475  int xc, yc;
1476  int N;
1477  int ny;
1478  double returnvalue = 0;
1479  cpl_stats *stats = NULL;
1480 
1481  passure( inputimage != NULL, " ");
1482  passure( ordertable != NULL, " ");
1483  passure( cpl_table_get_int(ordertable, "Order", row, NULL) == row+1, "%d %d",
1484  cpl_table_get_int(ordertable, "Order", row, NULL), row);
1485 
1486  check( ny = cpl_image_get_size_y(inputimage), "Could not read input image dimension");
1487 
1488  check( N = cpl_table_get_nrow(ordertable), "Could not read size of ordertable");
1489  assure(N > 1, CPL_ERROR_ILLEGAL_INPUT,
1490  "Cannot calculate orderspacing with less than 2 (i.e. %d) orders.", N);
1491  check( xc = cpl_table_get_int(ordertable, "Xcenter", row, NULL),
1492  "Could not read x-center of order #%d", row+1);
1493  check( yc = cpl_table_get_int(ordertable, "Ycenter", row, NULL),
1494  "Could not find y-center of order #%d", row+1);
1495 
1496 
1497  /* Set yupper and ylower midway between this and the adjacent orders
1498  * The y-location of the surrounding orders must be calculated at the center,
1499  * xc, of the current order
1500  */
1501  if (row < N - 1)
1502  {
1503  double ynext;
1504  check(ynext =
1505  cpl_table_get_double(ordertable, "Slope" , row + 1, NULL)*xc +
1506  cpl_table_get_double(ordertable, "Intersept", row + 1, NULL),
1507  "Could not read line from ordertable row %d", row + 1);
1508 
1509  yupper = (int)((yc + (uves_round_double(ynext)-1))/2);
1510  /* Midway between this and the next order */
1511  }
1512 
1513  if (row > 0)
1514  {
1515  double yprev;
1516  check( yprev =
1517  cpl_table_get_double(ordertable, "Slope" , row - 1, NULL)*xc +
1518  cpl_table_get_double(ordertable, "Intersept", row - 1, NULL),
1519  "Could not read line from ordertable row %d", row - 1);
1520 
1521  ylower = (int)((yc + uves_round_double(yprev)-1)/2);
1522  /* Midway between this and the previous order */
1523  }
1524 
1525  /* We need to manually set yupper for the highest order
1526  and ylower for the lowest order */
1527  if (row == N-1)
1528  {
1529  yupper = yc + (yc - ylower);
1530  }
1531  if (row == 0)
1532  {
1533  ylower = yc - (yupper - yc);
1534  }
1535  yupper = uves_min_int(uves_max_int(yupper, 1), ny);
1536  ylower = uves_min_int(uves_max_int(ylower, 1), ny);
1537 
1538  /* Order lines were originally sorted with respect to intersept. This does not
1539  necessarily mean that their centers are also sorted (if the Hough algorithm
1540  detected wrong slopes (which happens if trying to detect too many lines)).
1541  So check this. */
1542  assure(yupper > ylower, CPL_ERROR_ILLEGAL_INPUT,
1543  "Initially detected order lines intersept!");
1544 
1545  /* Find max and min pixel values between ylower and yupper, then calculate threshold */
1546  {
1547  double minval = 0;
1548  double maxval = 0;
1549  double noise_level = 0;
1550 
1551  /* Find maximum and minimum pixels along center column */
1552  check( stats = cpl_stats_new_from_image_window(
1553  inputimage,
1554  CPL_STATS_MIN | CPL_STATS_MAX | CPL_STATS_MINPOS,
1555  xc, ylower, /* Corners of window (FITS convention) (included) */
1556  xc, yupper),
1557  "Could not get statistics on image sub-window (%d,%d)-(%d,%d)",
1558  xc, ylower, xc, yupper);
1559 
1560  check(( minval = cpl_stats_get_min(stats),
1561  maxval = cpl_stats_get_max(stats)),
1562  "Could not get minimum and maximum pixel values");
1563 
1564  /* Get noise level at the location of 'minval' */
1565  {
1566  int xpos, ypos, pis_rejected;
1567  xpos = cpl_stats_get_min_x(stats);
1568  ypos = cpl_stats_get_min_y(stats);
1569  noise_level = cpl_image_get(noise, xpos, ypos, &pis_rejected);
1570  }
1571 
1572  /* Calculate threshold */
1573  returnvalue = uves_max_double(minval + relative_threshold * (maxval - minval),
1574  (minval + noise_level) + noise_level);
1575 
1576  uves_msg_debug("Order: %d \tThreshold: %f \tMinimum: %f \tMaximum: %f"
1577  " \tNoise: %f \tWindow: (%d, %d)-(%d, %d)",
1578  row+1, returnvalue, minval, maxval, noise_level, xc, ylower, xc, yupper);
1579  }
1580 
1581  cleanup:
1582  uves_free_stats(&stats);
1583  return returnvalue;
1584 }
1585 
1586 /*----------------------------------------------------------------------------*/
1609 /*----------------------------------------------------------------------------*/
1610 static bool
1611 find_centroid(const cpl_image *inputimage, const cpl_image *noise,
1612  const cpl_binary *image_bad,
1613  double threshold, int spacing, int x, double *yguess, double *dY)
1614 {
1615  bool returnvalue = true;
1616  int nx;
1617  int ny;
1618  int y;
1619  double thisvalue = 0;
1620  int pis_rejected;
1621  int ylow = 0;
1622  int yhigh = 0;
1623  cpl_matrix *covariance = NULL;
1624 
1625  passure( inputimage != NULL, " ");
1626 
1627  nx = cpl_image_get_size_x(inputimage);
1628  ny = cpl_image_get_size_y(inputimage);
1629 
1630  passure( 1 <= x && x <= nx, "%d %d", x, nx);
1631 
1632  uves_msg_debug("Order location estimate = (%d, %f)", x, *yguess);
1633 
1634  /* Start at yguess and move to a local max */
1635 
1636  y = uves_round_double(*yguess);
1637  if (y < 1 || y > ny)
1638  {
1639  returnvalue = false;
1640  }
1641  else {
1642  bool cont; /* continue? */
1643 
1644  do {
1645  cont = false;
1646  thisvalue = cpl_image_get(inputimage, x, y , &pis_rejected);
1647  /* Move up? */
1648  if (y < ny) {
1649  double uppervalue = cpl_image_get(inputimage, x, y + 1, &pis_rejected);
1650  if (!pis_rejected && uppervalue > thisvalue)
1651  {
1652  y += 1;
1653  cont = true;
1654  }
1655  }
1656 
1657  /* Move down? */
1658  if (y > 1) {
1659  double lowervalue = cpl_image_get(inputimage, x, y - 1, &pis_rejected);
1660  if (!pis_rejected && lowervalue > thisvalue)
1661  {
1662  y -= 1;
1663  cont = true;
1664  }
1665  }
1666 
1667  } while (cont);
1668 
1669  /* Now 'thisvalue' is the local maximum */
1670 
1671  uves_msg_debug("Local maximum at (%d, %d) (value = %f)\tthreshold = %f",
1672  x, y, thisvalue, threshold);
1673 
1674  /* Return false if no value above threshold was found */
1675  if (thisvalue < threshold)
1676  {
1677  uves_msg_debug("Order not traced at (%d, %d) (value = %f)\tthreshold = %f",
1678  x, y, thisvalue, threshold);
1679  returnvalue = false;
1680  }
1681  else
1682  {
1683  /* Find and use pixels that are above half max */
1684  double minvalue;
1685  double sigmaY; /* Width of peak */
1686 
1687  double mse, rms, chi_sq;
1688  double background;
1689  double norm;
1690 
1691  /* Threshold is half of max value at this x */
1692  minvalue = 0.5*thisvalue;
1693 
1694  /* Move to the lowest y above 'minvalue' */
1695  while(y > 1 && cpl_image_get(inputimage, x, y - 1, &pis_rejected) >= minvalue)
1696  {
1697  y--;
1698  }
1699 
1700  assure( cpl_error_get_code() == CPL_ERROR_NONE,
1701  cpl_error_get_code(), "Could not read pixel from input image" );
1702 
1703  /* Remember this place */
1704  ylow = y;
1705 
1706  /* Move to the highest y above 'minvalue' */
1707  while(y < ny && cpl_image_get(inputimage, x, y + 1, &pis_rejected) >= minvalue)
1708  {
1709  y++;
1710  }
1711 
1712  assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
1713  "Could not read pixel from input image" );
1714 
1715  /* Also remember this place */
1716  yhigh = y;
1717 
1718  /* Update the order line's location to centroid of
1719  strip from ylow to yhigh w.r.t. minvalue*/
1720  {
1721  double sum = 0;
1722  double sumy = 0;
1723  double sumy2= 0;
1724  for (y = ylow; y <= yhigh; y++)
1725  {
1726  double flux;
1727  flux = cpl_image_get(inputimage, x, y, &pis_rejected) - minvalue;
1728  if (!pis_rejected && flux > 0)
1729  {
1730  sum += flux;
1731  sumy += flux * (y - *yguess*0);
1732  sumy2 += flux * (y - *yguess*0) * (y - *yguess*0);
1733  }
1734  }
1735  if (sum > 0)
1736  {
1737  *yguess = *yguess*0 + sumy / sum;
1738  sigmaY = sqrt( sumy2 / sum - sumy*sumy/(sum*sum) );
1739 
1740  if ( sumy2 / sum - sumy*sumy/(sum*sum) < 0 ||
1741  sigmaY < sqrt(1.0/12) )
1742  {
1743  /* If the sum is over one pixel, sigma will be zero
1744  (or less than zero because of numerical error),
1745  so set sigma to stddev of one pixel = 1/sqrt(12)
1746  in that case */
1747  sigmaY = sqrt(1.0/12);
1748  }
1749 
1750  /* Uncertainty, dY, of mean value (yguess) is sigma/sqrt(N)
1751  where N is the total count, i.e. area under curve */
1752  *dY = sigmaY/sqrt(sum);
1753 
1754  }
1755  else
1756  {
1757  /* If all pixels were bad, don't update '*yguess' */
1758  sigmaY = 1.0;
1759  *dY = .1;
1760 
1761  }
1762  }
1763 
1764  /* This is a better method. Get centroid
1765  position by making a Gaussian fit. */
1766 
1767  /* Use a wide fitting window to get a well defined background level */
1768  ylow = uves_max_int(1 , uves_round_double(*yguess - spacing/3));
1769  yhigh = uves_min_int(ny, uves_round_double(*yguess + spacing/3));
1770 
1771  assure( yhigh - ylow >= 1, CPL_ERROR_ILLEGAL_INPUT,
1772  "Estimated spacing too small: %d pixel(s)", spacing);
1773 
1774  /* Fit. Save the result in 'yguess' */
1775  uves_fit_1d_image(inputimage, noise,
1776  image_bad,
1777  false, false, false,
1778  ylow, yhigh, x,
1779  yguess, &sigmaY, &norm, &background, NULL,
1780  &mse, &chi_sq, &covariance,
1782 
1783  /* Recover from specific fitting errors */
1784  if (cpl_error_get_code() == CPL_ERROR_NONE)
1785  {
1786  /* Variance is guaranteed to be positive */
1787  *dY = sqrt(cpl_matrix_get(covariance, 0, 0));
1788  }
1789  else if (cpl_error_get_code() == CPL_ERROR_CONTINUE)
1790  {
1791  /* Fitting failed */
1792  uves_error_reset();
1793  uves_msg_debug("Fitting failed at (x,y) = (%d, %e), "
1794  "using centroid", x, *yguess);
1795  *dY = sigmaY / sqrt(norm);
1796  }
1797  else if (cpl_error_get_code() == CPL_ERROR_SINGULAR_MATRIX)
1798  {
1799  uves_error_reset();
1800 
1801  /* Fitting succeeded but covariance computation failed */
1802  uves_msg_debug("Covariance matrix computation failed");
1803  *dY = sigmaY / sqrt(norm);
1804  }
1805 
1806  assure(cpl_error_get_code() == CPL_ERROR_NONE,
1807  cpl_error_get_code(), "Gaussian fitting failed");
1808 
1809  rms = sqrt(mse);
1810 
1811  uves_msg_debug("dy = %f sigma/sqrt(N) = %f", *dY, sigmaY/(norm));
1812 
1813  /* If the peak is definitely there or definitely not there,
1814  set the returnvalue appropriately */
1815  if ( norm > 10 * rms)
1816  {
1817  returnvalue = true;
1818  }
1819  if ( norm < 2 * rms)
1820  {
1821  returnvalue = false;
1822  }
1823 
1824  } /* signal was above threshold at this x */
1825 
1826  }/* If yguess was inside image */
1827 
1828  cleanup:
1829  cpl_matrix_delete(covariance);
1830  return returnvalue;
1831 }
void uves_polynomial_delete(polynomial **p)
Delete a polynomial.
#define uves_msg_warning(...)
Print an warning message.
Definition: uves_msg.h:87
#define check_nomsg(CMD)
Definition: uves_error.h:204
#define passure(BOOL,...)
Definition: uves_error.h:207
int uves_gauss_derivative(const double x[], const double a[], double result[])
Evaluate the derivatives of a gaussian.
Definition: uves_utils.c:4346
cpl_table * uves_locate_orders(const cpl_image *inputimage, const cpl_image *noise, cpl_table *ordertable, int TRACESTEP, double MINTHRESH, double MAXGAP, double MAXRMS, int *DEFPOL1, int *DEFPOL2, double KAPPA, polynomial **bivariate_fit, int *orders_traced)
Trace all orders.
#define uves_msg(...)
Print a message on 'info' or 'debug' level.
Definition: uves_msg.h:119
int uves_gauss(const double x[], const double a[], double *result)
Evaluate a gaussian.
Definition: uves_utils.c:4291
polynomial * uves_polynomial_regression_2d(cpl_table *t, const char *X1, const char *X2, const char *Y, const char *sigmaY, int degree1, int degree2, const char *polynomial_fit, const char *residual_square, const char *variance_fit, double *mse, double *red_chisq, polynomial **variance, double kappa, double min_reject)
Fit a 2d polynomial to three table columns.
Definition: uves_utils.c:2869
static double xcenter(const cpl_image *image, const cpl_image *noise, int xlo, int xhi, int row, centering_method CENTERING_METHOD, int bin_disp, double *sigma, double *intensity, double *dx0, double *slope, double *background)
Refine the center position of an initially detected emission line.
double uves_polynomial_evaluate_1d(const polynomial *p, double x)
Evaluate a 1d polynomial.
double uves_polynomial_get_coeff_1d(const polynomial *p, int degree)
Get a coefficient of a 1D polynomial.
polynomial * uves_polynomial_regression_1d(cpl_table *t, const char *X, const char *Y, const char *sigmaY, int degree, const char *polynomial_fit, const char *residual_square, double *mean_squared_error, double kappa)
Fit a 1d polynomial to two table columns.
Definition: uves_utils.c:2590
#define uves_error_reset()
Definition: uves_error.h:215
#define uves_msg_low(...)
Print a message on a lower message level.
Definition: uves_msg.h:105
#define uves_msg_debug(...)
Print a debug message.
Definition: uves_msg.h:97
#define assure_nomsg(BOOL, CODE)
Definition: uves_error.h:177
#define check(CMD,...)
Definition: uves_error.h:198