MUSE Pipeline Reference Manual  0.18.1
muse_tracing.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set sw=2 sts=2 et cin: */
3 /*
4  * This file is part of the MUSE Instrument Pipeline
5  * Copyright (C) 2005-2014 European Southern Observatory
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 /*----------------------------------------------------------------------------*
27  * Includes *
28  *----------------------------------------------------------------------------*/
29 #if HAVE_POPEN && HAVE_PCLOSE
30 #define _BSD_SOURCE /* force popen/pclose, mkdtemp definitions from stdio/stdlib */
31 #endif
32 #include <cpl.h>
33 #include <string.h>
34 #undef __USE_MISC /* don't want y1 */
35 #include <math.h>
36 
37 #include "muse_tracing.h"
38 #include "muse_instrument.h"
39 
40 #include "muse_cplwrappers.h"
41 #include "muse_pfits.h"
42 #include "muse_quadrants.h"
43 #include "muse_utils.h"
44 
45 /*----------------------------------------------------------------------------*/
49 /*----------------------------------------------------------------------------*/
50 
53 /* strings corresponding to the muse_trace_poly entries in muse_tracing.h */
54 static const char *muse_trace_poly_strings[] = {
55  "central",
56  "left",
57  "right"
58 };
59 
60 static void muse_trace_plot_located_slices(cpl_vector *, cpl_vector *, double, double, double);
61 
62 /*---------------------------------------------------------------------------*/
76 /*---------------------------------------------------------------------------*/
77 static cpl_vector *
78 muse_trace_horizontal_cut(const muse_image *aImage, unsigned int aNRows)
79 {
80  cpl_ensure(aImage && aImage->data, CPL_ERROR_NULL_INPUT, NULL);
81 
82  /* set the vertical midpoint by looking at the top quadrant boundary, of the *
83  * lower left quadrant but preset to be the vertical center of the image */
84  cpl_size ymid = cpl_image_get_size_y(aImage->data) / 2,
85  *w = muse_quadrants_get_window(aImage, 1);
86  if (w) { /* can fail if a test master flat without headers was passed in */
87  ymid = w[3]; /* top of this bottom quadrant */
88  cpl_free(w);
89  }
90  int y1 = ymid - 1 - aNRows,
91  y2 = ymid - 1,
92  y3 = ymid + 1,
93  y4 = ymid + 1 + aNRows;
94  cpl_msg_debug(__func__, "ymid=%"CPL_SIZE_FORMAT", region=%d..%d, %d..%d",
95  ymid, y1, y2, y3, y4);
96 
97  /* collapse both regions, vertically and separately */
98  int nx = cpl_image_get_size_x(aImage->data);
99  cpl_image *tmp1 = cpl_image_collapse_window_create(aImage->data, 1, y1, nx, y2, 0),
100  *tmp2 = cpl_image_collapse_window_create(aImage->data, 1, y3, nx, y4, 0);
101  /* normalize, to make them comparable despite possible gain differences */
102  cpl_image_normalise(tmp1, CPL_NORM_MEAN);
103  cpl_image_normalise(tmp2, CPL_NORM_MEAN);
104  /* construct max image to exclude bad columns */
105  cpl_image *tmax = cpl_image_new(nx, 1, CPL_TYPE_FLOAT);
106  int i;
107  for (i = 1; i <= nx; i++) {
108  int err;
109  cpl_image_set(tmax, i, 1,
110  fmax(cpl_image_get(tmp1, i, 1, &err),
111  cpl_image_get(tmp2, i, 1, &err)));
112  } /* for i (horizontal pixels) */
113 #if 0 /* DEBUG */
114  cpl_image_save(tmp1, "trace_tmp1.fits", CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE);
115  cpl_image_save(tmp2, "trace_tmp2.fits", CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE);
116  cpl_image_save(tmax, "trace_tmax.fits", CPL_TYPE_FLOAT, NULL, CPL_IO_CREATE);
117  cpl_msg_debug(__func__, "Saved collapsed rows to trace_{tmp1,tmp2,tmax}.fits");
118 #endif
119  cpl_image_delete(tmp1);
120  cpl_image_delete(tmp2);
121 
122  cpl_vector *cut = cpl_vector_new_from_image_row(tmax, 1);
123  cpl_image_delete(tmax);
124  return cut;
125 } /* muse_trace_horizontal_cut() */
126 
127 /*---------------------------------------------------------------------------*/
159 /*---------------------------------------------------------------------------*/
160 cpl_vector *
161 muse_trace_locate_slices(cpl_vector *aRowVec, const unsigned short aNSlices,
162  double aFrac)
163 {
164  cpl_ensure(aRowVec, CPL_ERROR_NULL_INPUT, NULL);
165  cpl_ensure(aFrac > 0. && aFrac < 1., CPL_ERROR_ILLEGAL_INPUT, NULL);
166  cpl_vector *centers = cpl_vector_new(aNSlices),
167  *widths = cpl_vector_new(aNSlices);
168  double f = aFrac;
169 
170  /* Loop through the actual slice location routine until all slices *
171  * monotonically increase in x-direction with a large enough step size. */
172  while (1) {
173  double median = cpl_vector_get_median_const(aRowVec),
174  mdev = muse_cplvector_get_adev_const(aRowVec, median),
175  detlimit = f*median; /* the detection limit */
176 
177  cpl_msg_debug(__func__, "median=%f, mdev=%f, fraction=%f --> edge detection"
178  " limit=%f", median, mdev, f, detlimit);
179 
180  /* find first edge from the left: assume it lies within the first *
181  * kMuseSliceSearchRegion pix */
182  double ledge = 0., redge = 0.;
183  int i;
184  for (i = 0; i <= kMuseSliceSearchRegion; i++) {
185  if (cpl_vector_get(aRowVec, i) >= detlimit) {
186  ledge = i - 0.5; /* the real edge is probably in between... */
187  break;
188  }
189  }
190  if (i == kMuseSliceSearchRegion) {
191  cpl_msg_error(__func__, "Search for first slice (left-edge) failed");
192  cpl_vector_delete(centers);
193  centers = NULL;
194  break;
195  }
196 
197  /* find the corresponding right edge, should be within the next *
198  * kMuseSliceMaxWidth pix */
199  for (i = ledge + 1.5; i <= ledge + kMuseSliceMaxWidth; i++) {
200  if (cpl_vector_get(aRowVec, i) <= detlimit) {
201  redge = i - 0.5;
202  break;
203  }
204  }
205  if (i == ledge + kMuseSliceMaxWidth) {
206  cpl_msg_error(__func__, "Search for first slice (right-edge) failed");
207  cpl_vector_delete(centers);
208  centers = NULL;
209  break;
210  }
211 
212  double width = redge - ledge;
213  if (width < kMuseSliceLoLikelyWidth) {
214  cpl_msg_error(__func__, "Initial slice is too narrow (%.2f pix, %.1f..%.1f)"
215  " -> search failed", width, ledge, redge);
216  cpl_error_set(__func__, CPL_ERROR_ACCESS_OUT_OF_RANGE);
217  cpl_vector_delete(centers);
218  centers = NULL;
219  break;
220  }
221  if (width > kMuseSliceHiLikelyWidth) {
222  cpl_msg_error(__func__, "Initial slice is too wide (%.2f pix, %.1f..%.1f)"
223  " -> search failed", width, ledge, redge);
224  cpl_error_set(__func__, CPL_ERROR_ACCESS_OUT_OF_RANGE);
225  cpl_vector_delete(centers);
226  centers = NULL;
227  break;
228  }
229 
230  /* derive and store the first midpoint (the above are indices!) */
231  cpl_vector_set(centers, 0, round((ledge + redge) / 2.) + 1);
232  cpl_vector_set(widths, 0, width);
233 
234  /* now find centers for the remaining slices in the same way */
235  unsigned short j;
236  for (j = 1; j < aNSlices; j++) {
237  for (i = redge + 1.5; i <= redge + kMuseSliceMaxWidth; i++) {
238  if (cpl_vector_get(aRowVec, i) >= detlimit) {
239  ledge = i - 0.5;
240  break;
241  }
242  }
243  if (i == redge + kMuseSliceMaxWidth) {
244  cpl_msg_error(__func__, "Search for slice %hu (left-edge) failed", j);
245  cpl_vector_delete(centers);
246  cpl_vector_delete(widths);
247  return NULL;
248  }
249 
250  for (i = ledge + 1.5; i <= ledge + kMuseSliceMaxWidth; i++) {
251  if (cpl_vector_get(aRowVec, i) <= detlimit) {
252  redge = i - 0.5;
253  break;
254  }
255  }
256  if (i == ledge + kMuseSliceMaxWidth) {
257  cpl_msg_error(__func__, "Search for slice %hu (right-edge) failed", j);
258  cpl_vector_delete(centers);
259  cpl_vector_delete(widths);
260  return NULL;
261  }
262 
263  /* check that these values make sense, but don't fail */
264  width = redge - ledge;
265  cpl_vector_set(widths, j, width);
266 #if 0
267  cpl_msg_debug(__func__, "slice %hu: left=%f, right=%f --> width %f, center %f",
268  j+1, ledge, redge, width, (ledge + redge) / 2.);
269 #endif
270  /* set output, converting from vector indices to pixel positions */
271  cpl_vector_set(centers, j, round((ledge + redge) / 2.) + 1);
272  } /* for j (all slices after the first one) */
273 
274  /* possibly plot the result */
275  char *doplot = getenv("MUSE_PLOT_TRACE");
276  if (doplot && atoi(doplot) & 0x1) {
277  muse_trace_plot_located_slices(aRowVec, centers, median, mdev, detlimit);
278  }
279 
280  int failures = 0;
281  for (i = 1; i < cpl_vector_get_size(centers); i++) {
282  double step = cpl_vector_get(centers, i) - cpl_vector_get(centers, i-1);
283  if (step < kMuseSliceLoLikelyWidth) {
284  failures++;
285  }
286  }
287 
288  /* if there were no misdetections, we can break the loop */
289  if (!failures) {
290  break;
291  }
292 
293  /* decrease the fraction by a bit and try again */
294  f /= 1.2;
295 
296  if (f < DBL_EPSILON) {
297  cpl_msg_error(__func__, "Still detected %d unlikely slice locations, but "
298  "the cut-off fraction has become unrealistically small "
299  "(initial %f, now %f)", failures, aFrac, f);
300  break;
301  }
302  } /* while 1 */
303 
304  /* if nothing fatal occured, check widths of all slices and output warnings */
305  int i, n = !centers ? -1 : cpl_vector_get_size(widths);
306  for (i = 0; i < n; i++) {
307  float width = cpl_vector_get(widths, i);
308  if (width < kMuseSliceLoLikelyWidth) {
309  cpl_msg_warning(__func__, "From the initial guess, slice %d appears to be"
310  " only %f pix wide, please cross-check!", i+1, width);
311  }
312  if (width > kMuseSliceHiLikelyWidth) {
313  cpl_msg_warning(__func__, "From the initial guess, slice %d appears to be"
314  " very wide (%f pix), please cross-check!", i+1, width);
315  }
316  if (i >= 1) {
317  double step = cpl_vector_get(centers, i) - cpl_vector_get(centers, i-1);
318  if (step < kMuseSliceLoLikelyWidth) {
319  cpl_msg_warning(__func__, "Slice %d is only %.2f pix farther than the "
320  "previous one!", i + 1, step);
321  }
322  }
323  } /* for i (slices) */
324  cpl_vector_delete(widths);
325 
326  return centers;
327 } /* muse_trace_locate_slices() */
328 
329 /*---------------------------------------------------------------------------*/
361 /*---------------------------------------------------------------------------*/
362 double
363 muse_trace_edgefinder(const cpl_vector *aDataVec, double aFrac,
364  double *aLeft, double *aRight, cpl_boolean *aHighSN)
365 {
366  int size = cpl_vector_get_size(aDataVec);
367  /* stuff like i-1 doesn't work if the input vector is too small */
368  cpl_ensure(size > 5, CPL_ERROR_ILLEGAL_INPUT, -3);
369  cpl_ensure(aFrac > 0. && aFrac < 1., CPL_ERROR_ILLEGAL_INPUT, -4);
370  cpl_ensure(aLeft && aRight, CPL_ERROR_NULL_INPUT, -5);
371 
372  /* compute the detection limit from the median and median deviation *
373  * over the one-dimensional cut across the slices */
374  double median = cpl_vector_get_median_const(aDataVec),
375  mdev = muse_cplvector_get_adev_const(aDataVec, median),
376  mean = cpl_vector_get_mean(aDataVec),
377  stdev = cpl_vector_get_stdev(aDataVec),
378  detlimit = aFrac * median;
379  /* the result will be significant, if the flux at this part of *
380  * the slice is larger than the noise; this is used to decide *
381  * whether to output a warning message in case of failure */
382  cpl_boolean significant = median > mdev && mean > stdev;
383  if (aHighSN) {
384  *aHighSN = significant;
385  }
386 #if 0
387  cpl_msg_debug(__func__, "median=%f+/-%f, mean=%f+/-%f, aFrac=%f --> edge "
388  "detection limit=%f (%ssignificant)", median, mdev, mean, stdev,
389  aFrac, detlimit, significant ? "" : "NOT ");
390 #endif
391 
392  /* preset edge positions and return arguments for error cases */
393  *aRight = 0;
394  *aLeft = 0;
395  /* buffer access to vector */
396  const double *ydata = cpl_vector_get_data_const(aDataVec);
397 
398  /* start at the center and search outwards for the right-hand edge */
399  int i;
400  for (i = size/2; i < size; i++) {
401  if (ydata[i] < detlimit) {
402  /* i is the element where we fell below the limit. Use this to *
403  * determine the fractional position where the limit was reached *
404  * using linear interpolation */
405  *aRight = i-1 + (detlimit - ydata[i-1]) / (ydata[i] - ydata[i-1]);
406 #if 0
407  cpl_msg_debug(__func__, "r: %d..._%d_, %f/_%f_ ===> %f", i-1, i,
408  ydata[i-1], ydata[i], *aRight);
409 #endif
410  /* if the found interpolation point is more than 1 pix away *
411  * from the current pixel then the interpolation was wrong! */
412  if (fabs(*aRight - i) > 1.) {
413  /* Only display message and return the fault, if we actually got a few *
414  * pixels away from the position where we started. Otherwise it will *
415  * just be a dark pixel near the slice center, that we can ignore. *
416  * This case would be a real problem and should actually not happen! */
417  if (significant && i - size/2 > 2) {
418  cpl_msg_debug(__func__, "Faulty interpolation of right-hand edge: i=%d"
419  " (start %d), *aRight=%f (%f..%f > %f > %f)", i, size/2,
420  *aRight, ydata[i-2], ydata[i-1], detlimit, ydata[i]);
421  return -11;
422  } /* if real problem */
423  continue;
424  } /* if large offset */
425  break;
426  } /* if data below limit */
427  } /* for i (right-hand part of slice) */
428  /* we arrived at upper limit of vector, so we didn't find the right edge! */
429  if (i == size) {
430  return -1;
431  }
432 
433  /* start again at the center and search outwards for the left-hand edge */
434  for (i = size/2; i >= 0; i--) {
435  if (ydata[i] < detlimit) {
436  /* again, use linear interpolation to find the exact position */
437  *aLeft = i + (detlimit - ydata[i]) / (ydata[i+1] - ydata[i]);
438 #if 0
439  cpl_msg_debug(__func__, "l: %d..._%d_, %f/_%f_ ===> %f", i+1, i,
440  ydata[i+1], ydata[i], *aLeft);
441 #endif
442  if (fabs(*aLeft - i) > 1.) {
443  if (significant && size/2 - i > 2) {
444  /* this would be a real problem, should never happen! */
445  cpl_msg_debug(__func__, "Faulty interpolation of left-hand edge: i=%d"
446  " (start %d), *aLeft=%f (%f < %f < %f..%f)", i, size/2,
447  *aLeft, ydata[i], detlimit, ydata[i+1], ydata[i+2]);
448  return -12;
449  } /* if real problem */
450  continue;
451  } /* if large offset */
452  break;
453  } /* if data below limit */
454  } /* for i (left-hand part of slice) */
455  /* we arrived at lower limit of vector, so we didn't find the left edge! */
456  if (i == -1) {
457  return -2;
458  }
459 
460 #if 0
461  cpl_msg_debug(__func__, "result: %f %f --> %f", *aLeft, *aRight,
462  (*aLeft + *aRight)/2.);
463 #endif
464  return (*aLeft + *aRight)/2.;
465 } /* muse_trace_edgefinder() */
466 
467 /*---------------------------------------------------------------------------*/
494 /*---------------------------------------------------------------------------*/
495 static double
496 muse_trace_refine_edge(cpl_vector *aDiffVec, double *aLeft, double *aRight,
497  int aOffset, double aY)
498 {
499 #define TRACE_REFINE_MAX_SHIFT 0.25 /* more than 1/4 pix shift is too much */
500 #define TRACE_REFINE_RANGE 5
501  int size = cpl_vector_get_size(aDiffVec);
502  cpl_ensure(size > 5, CPL_ERROR_ILLEGAL_INPUT, -3);
503  cpl_ensure(aLeft && aRight, CPL_ERROR_NULL_INPUT, -5);
504  cpl_ensure(*aLeft > 0 && *aLeft < size &&
505  *aRight > 0 && *aRight < size && *aRight > *aLeft,
506  CPL_ERROR_ILLEGAL_INPUT, -6);
507 
508  /* keep initial values around */
509  double left = *aLeft,
510  right = *aRight,
511  mid = (*aLeft + *aRight) / 2.;
512 
513  /* create two vectors for the actual fitting of both edges, *
514  * plus two vectors that contain coordinates for the fit */
515  int nel = 2 * TRACE_REFINE_RANGE + 1; /* number of elements in vectors to fit */
516  cpl_vector *vl = cpl_vector_new(nel), /* diff-data vectors */
517  *vr = cpl_vector_new(nel),
518  *pl = cpl_vector_new(nel), /* position vectors */
519  *pr = cpl_vector_new(nel);
520  /* extraction offsets so that the peaks are in the middle of the vectors */
521  int loffset = (int)(left + 0.5)- TRACE_REFINE_RANGE + 1,
522  roffset = (int)(right + 0.5) - TRACE_REFINE_RANGE + 1;
523 #if 0
524  cpl_msg_debug(__func__, "input: %f/%f -> %d/%d",
525  left, right, loffset, roffset);
526 #endif
527  double *diff = cpl_vector_get_data(aDiffVec);
528  int i;
529  for (i = 0; i < nel; i++ ) {
530  double d = diff[i + loffset - 1];
531 #if 0
532  cpl_msg_debug(__func__, "l i=%d / %d: %f", i, i + loffset, d);
533 #endif
534  cpl_vector_set(pl, i, i + loffset - 1);
535  /* cut off negative values */
536  cpl_vector_set(vl, i, d > 0 ? d : 0);
537  } /* for i (around left start value) */
538  for (i = 0; i < nel; i++ ) {
539  /* need the inverse value for the right-hand edge */
540  double d = -diff[i + roffset - 1];
541 #if 0
542  cpl_msg_debug(__func__, "r i=%d / %d: %f", i, i + roffset, d);
543 #endif
544  cpl_vector_set(pr, i, i + roffset - 1);
545  /* cut off negative values */
546  cpl_vector_set(vr, i, d > 0 ? d : 0);
547  } /* for i (around right start value) */
548 
549  /* get state to possibly reset it */
550  cpl_errorstate state = cpl_errorstate_get();
551  /* background level of zero is a very good estimate on this *
552  * difference data, really no need to fit that parameter */
553  double center, sigma, area, bglevel = 0, mse;
554  cpl_fit_mode fitmode = CPL_FIT_CENTROID | CPL_FIT_STDEV | CPL_FIT_AREA;
555  cpl_error_code rc1 = cpl_vector_fit_gaussian(pl, NULL, vl, NULL, fitmode,
556  &center, &sigma, &area, &bglevel,
557  &mse, NULL, NULL);
558  /* center needs to be corrected for shift introduced by shift-diff procedure */
559  center -= 0.5;
560  if (rc1 == CPL_ERROR_CONTINUE || rc1 == CPL_ERROR_SINGULAR_MATRIX) {
561  /* fit returned error, but parameters are supposed to be valid; use them, *
562  * if they don't differ too much from the input, and reset the error */
563  if (fabs(center - *aLeft) < TRACE_REFINE_MAX_SHIFT) {
564  *aLeft = center;
565  }
566  }
567  if (rc1 != CPL_ERROR_NONE) {
568  /* fit failed, keep the original value */
569  cpl_errorstate_set(state);
570  } else { /* successful, use the center of the fit */
571  *aLeft = center;
572  }
573 #if 0
574  cpl_msg_debug(__func__, "fit l: %f %f %f (%f)", center, sigma, area, sqrt(mse));
575 #endif
576  cpl_error_code rc2 = cpl_vector_fit_gaussian(pr, NULL, vr, NULL, fitmode,
577  &center, &sigma, &area, &bglevel,
578  &mse, NULL, NULL);
579  center -= 0.5;
580  if (rc2 == CPL_ERROR_CONTINUE || rc2 == CPL_ERROR_SINGULAR_MATRIX) {
581  /* fit returned error, but parameters are supposed to be valid; use them, *
582  * if they don't differ too much from the input, and reset the error */
583  if (fabs(center - *aRight) < TRACE_REFINE_MAX_SHIFT) {
584  *aRight = center;
585  }
586  }
587  if (rc2 != CPL_ERROR_NONE) {
588  /* fit failed, keep the original value */
589  cpl_errorstate_set(state);
590  } else { /* successful, use the center of the fit */
591  *aRight = center;
592  }
593 #if 0
594  cpl_msg_debug(__func__, "fit r: %f %f %f (%f)", center, sigma, area, sqrt(mse));
595 #endif
596 
597 #if 0
598  cpl_vector_dump(aDiffVec, stdout);
599  printf("left\n");
600  cpl_bivector *biv = cpl_bivector_wrap_vectors(pl, vl);
601  cpl_bivector_dump(biv, stdout);
602  cpl_bivector_unwrap_vectors(biv);
603  printf("right\n");
604  biv = cpl_bivector_wrap_vectors(pr, vr);
605  cpl_bivector_dump(biv, stdout);
606  cpl_bivector_unwrap_vectors(biv);
607  fflush(stdout);
608 #endif
609  cpl_vector_delete(vl);
610  cpl_vector_delete(vr);
611  cpl_vector_delete(pl);
612  cpl_vector_delete(pr);
613 
614  double midpoint = (*aLeft + *aRight)/2.;
615 #if 0
616  cpl_msg_debug(__func__, "refine: %f %f %f %f %f %f",
617  *aLeft, midpoint, *aRight,
618  left - *aLeft, mid - midpoint, right - *aRight);
619 #endif
620  /* skip points where measured centers don't agree; one could also *
621  * check the edges but for those the shifts change with edgefrac */
622  if (fabs(mid - midpoint) > TRACE_REFINE_MAX_SHIFT) {
623  cpl_msg_debug(__func__, "large refined shift around y=%.1f: %f %f %f "
624  "(%f %f %f) trace point will not be used", aY,
625  left + aOffset, midpoint + aOffset, right + aOffset,
626  left - *aLeft, mid - midpoint, right - *aRight);
627  midpoint = -1.; /* make it fail */
628  } else { /* success, add the offset */
629  midpoint += aOffset;
630  }
631  /* add the offset to left and right in any case */
632  *aLeft += aOffset;
633  *aRight += aOffset;
634  return midpoint;
635 } /* muse_trace_refine_edge() */
636 
637 /*---------------------------------------------------------------------------*/
666 /*---------------------------------------------------------------------------*/
667 static cpl_polynomial **
668 muse_trace_iterate_fit(cpl_matrix *aX, cpl_vector *aY, cpl_vector *aWidths,
669  const unsigned short aSlice, const unsigned int aFitorder,
670  const float aWSigma, const float aRSigma,
671  cpl_vector *aMSE)
672 {
673  cpl_ensure(cpl_vector_get_size(aWidths) >= 3, CPL_ERROR_ILLEGAL_INPUT, NULL);
674  /* not a critical argument but silences the build warning without debug output */
675  cpl_ensure(aSlice >= 1 && aSlice <= kMuseSlicesPerCCD,
676  CPL_ERROR_ILLEGAL_INPUT, NULL);
677 
678  /* throw away trace points with wildly different widths */
679  double wmean = cpl_vector_get_mean(aWidths),
680  wmedian = cpl_vector_get_median_const(aWidths),
681  wstdev = cpl_vector_get_stdev(aWidths),
682  wmdev = muse_cplvector_get_adev_const(aWidths, wmedian);
683 #if 0
684  cpl_msg_debug(__func__, "width (1st): mean %6.3f +/- %5.3f, median %6.3f +/- "
685  "%5.3f (%"CPL_SIZE_FORMAT" points)", wmean, wstdev, wmedian, wmdev,
686  cpl_vector_get_size(aWidths));
687 #endif
688  if ((wmean - wstdev < kMuseSliceLoLikelyWidth ||
689  wmedian - wmdev < kMuseSliceLoLikelyWidth) &&
690  (wmean < kMuseSliceLoLikelyWidth || wmedian < kMuseSliceLoLikelyWidth)) {
691  cpl_msg_debug(__func__, "slice %hu seems to be very narrow initially "
692  "(widths: mean %6.3f +/- %5.3f, median %6.3f +/- %5.3f)!",
693  aSlice, wmean, wstdev, wmedian, wmdev);
694  }
695  if ((wmean + wstdev > kMuseSliceHiLikelyWidth ||
696  wmedian + wmdev > kMuseSliceHiLikelyWidth) &&
697  (wmean > kMuseSliceHiLikelyWidth || wmedian > kMuseSliceHiLikelyWidth)) {
698  cpl_msg_debug(__func__, "slice %hu seems to be very wide initially "
699  "(widths: mean %6.3f +/- %5.3f, median %6.3f +/- %5.3f)!",
700  aSlice, wmean, wstdev, wmedian, wmdev);
701  }
702 
703  /* loop through all trace points and remove grossly deviant points *
704  * where the slice is far too narrow or far too wide to be real */
705  int i;
706  for (i = 0; i < cpl_vector_get_size(aWidths); i++) {
707  double width = cpl_vector_get(aWidths, i);
708  if (width > kMuseSliceLoLikelyWidth && width < kMuseSliceHiLikelyWidth) {
709  /* good widths */
710  continue;
711  }
712  /* guard against removing the last element */
713  if (cpl_vector_get_size(aWidths) == 1) {
714  cpl_msg_warning(__func__, "trying to remove the last vector/matrix "
715  "element in slice %hu when checking widths", aSlice);
716  break;
717  }
718  /* bad width, remove element */
719  cpl_matrix_erase_columns(aX, i, 1);
721  muse_cplvector_erase_element(aWidths, i);
722  i--; /* we stay at this position to see what moved here */
723  } /* for i */
724 
725  wmean = cpl_vector_get_mean(aWidths);
726  wmedian = cpl_vector_get_median_const(aWidths);
727  wstdev = cpl_vector_get_stdev(aWidths);
728  wmdev = muse_cplvector_get_adev_const(aWidths, wmedian);
729 #if 0
730  cpl_msg_debug(__func__, "width (2nd): mean %6.3f+/-%5.3f, median %6.3f+/-%5.3f (%d points)",
731  wmean, wstdev, wmedian, wmdev, cpl_vector_get_size(aWidths));
732 #endif
733  if ((wmean - wstdev < kMuseSliceLoLikelyWidth ||
734  wmedian - wmdev < kMuseSliceLoLikelyWidth) &&
735  (wmean < kMuseSliceLoLikelyWidth || wmedian < kMuseSliceLoLikelyWidth)) {
736  cpl_msg_warning(__func__, "slice %hu seems to be very narrow after iteration"
737  " (widths: mean %6.3f +/- %5.3f, median %6.3f +/- %5.3f)!",
738  aSlice, wmean, wstdev, wmedian, wmdev);
739  }
740  if ((wmean + wstdev > kMuseSliceHiLikelyWidth ||
741  wmedian + wmdev > kMuseSliceHiLikelyWidth) &&
742  (wmean > kMuseSliceHiLikelyWidth || wmedian > kMuseSliceHiLikelyWidth)) {
743  cpl_msg_warning(__func__, "slice %hu seems to be very wide after iteration "
744  "(widths: mean %6.3f +/- %5.3f, median %6.3f +/- %5.3f)!",
745  aSlice, wmean, wstdev, wmedian, wmdev);
746  }
747 
748  /* again loop through all trace points to now reject based on sigma */
749  for (i = 0; i < cpl_vector_get_size(aWidths); i++) {
750  double width = cpl_vector_get(aWidths, i);
751 #if 0
752  cpl_msg_debug(__func__, "i=%d: %f <? %f <? %f", i,
753  wmedian - aWSigma * wmdev, width, wmedian + aWSigma * wmdev);
754 #endif
755  if (width > (wmedian - aWSigma * wmdev) &&
756  width < (wmedian + aWSigma * wmdev)) {
757  /* good widths */
758  continue;
759  }
760  /* guard against removing the last element */
761  if (cpl_vector_get_size(aWidths) == 1) {
762  cpl_msg_warning(__func__, "trying to remove the last vector/matrix "
763  "element in slice %hu when checking fit sigma", aSlice);
764  break;
765  }
766  /* bad width, remove element */
767  cpl_matrix_erase_columns(aX, i, 1);
769  muse_cplvector_erase_element(aWidths, i);
770  i--; /* we stay at this position to see what moved here */
771  } /* for i */
772 
773  /* create table column from vector */
774  cpl_table *wtable = cpl_table_new(cpl_vector_get_size(aWidths));
775  cpl_table_new_column(wtable, "widths", CPL_TYPE_DOUBLE);
776  memcpy(cpl_table_get_data_double(wtable, "widths"),
777  cpl_vector_get_data(aWidths), cpl_vector_get_size(aWidths));
778  double mse, chisq;
779  cpl_polynomial *tracefit = muse_utils_iterate_fit_polynomial(aX, aY, NULL, wtable,
780  aFitorder, aRSigma,
781  &mse, &chisq);
782  /* adapt length of input vector, copy output widths back into it */
783  cpl_vector_set_size(aWidths, cpl_vector_get_size(aY));
784  memcpy(cpl_vector_get_data(aWidths), cpl_table_get_data_double(wtable, "widths"),
785  cpl_vector_get_size(aWidths));
786  cpl_table_delete(wtable);
787  if (!tracefit) { /* if the fit didn't work then something went wrong */
788  cpl_vector_fill(aMSE, FLT_MAX);
789  return NULL;
790  }
791  /* also save in input vector */
792  cpl_vector_set(aMSE, MUSE_TRACE_CENTER, mse);
793 
794  char *dodebug = getenv("MUSE_DEBUG_TRACE");
795  if (dodebug && atoi(dodebug) > 0) {
796  printf("Polynomial trace fit for slice %hu (mse=%g, chi^2=%g):\n", aSlice,
797  mse, chisq);
798  cpl_polynomial_dump(tracefit, stdout);
799  fflush(stdout);
800  }
801 
802  /* Now we have a polynomial defining the center and the array of widths. *
803  * Convert this into separate matrices for left and right edges and redo *
804  * the polynomial fits for both edges. */
805  cpl_vector *edge[MUSE_TRACE_NPOLY - 1] = {
806  cpl_vector_new(cpl_vector_get_size(aY)),
807  cpl_vector_new(cpl_vector_get_size(aY))
808  };
809  for (i = 0; i < cpl_vector_get_size(aWidths); i++) {
810  double x = cpl_vector_get(aY, i),
811  halfwidth = cpl_vector_get(aWidths, i) / 2.;
812 #if 0
813  cpl_msg_debug(__func__, "x=%f (%f...%f)", x, x - halfwidth, x + halfwidth);
814 #endif
815  cpl_vector_set(edge[MUSE_TRACE_LEFT - 1], i, x - halfwidth);
816  cpl_vector_set(edge[MUSE_TRACE_RIGHT - 1], i, x + halfwidth);
817  } /* for i */
818 #if 0
819  printf("left:\n");
820  cpl_vector_dump(edge[0], stdout);
821  printf("right:\n");
822  cpl_vector_dump(edge[1], stdout);
823  fflush(stdout);
824 #endif
825 
826  cpl_polynomial **fit = cpl_calloc(MUSE_TRACE_NPOLY, sizeof(cpl_polynomial *));
827  fit[MUSE_TRACE_CENTER] = tracefit;
828 
829  int ipoly;
830  for (ipoly = 1; ipoly < MUSE_TRACE_NPOLY; ipoly++) { /* from 1, skip center */
831  /* fit again, but do not iterate, i.e. use a very high rejection sigma */
832  fit[ipoly] = muse_utils_iterate_fit_polynomial(aX, edge[ipoly - 1], NULL,
833  NULL, aFitorder, FLT_MAX,
834  &mse, &chisq);
835  cpl_vector_set(aMSE, ipoly, mse);
836  /* we are now done with the extra fit input data, too */
837  cpl_vector_delete(edge[ipoly - 1]);
838  } /* for ipoly */
839 
840 #if 0
841  printf("resulting polynomials (center, left, and right):\n");
842  cpl_polynomial_dump(fit[MUSE_TRACE_CENTER], stdout);
843  cpl_polynomial_dump(fit[MUSE_TRACE_LEFT], stdout);
844  cpl_polynomial_dump(fit[MUSE_TRACE_RIGHT], stdout);
845  printf("MSEs:\n");
846  cpl_vector_dump(aMSE, stdout);
847  fflush(stdout);
848 #endif
849 
850  return fit;
851 } /* muse_trace_iterate_fit() */
852 
853 /*----------------------------------------------------------------------------*/
857 /*----------------------------------------------------------------------------*/
859  { "slice", CPL_TYPE_INT, "", "%02d", "slice number", CPL_TRUE},
860  { "y", CPL_TYPE_FLOAT, "pix", "%6.1f", "y position on CCD", CPL_TRUE},
861  { "mid", CPL_TYPE_FLOAT, "pix", "%8.3f",
862  "midpoint of the slice at this y position", CPL_TRUE},
863  { "left", CPL_TYPE_FLOAT, "pix", "%8.3f",
864  "left edge of the slice at this y position", CPL_TRUE},
865  { "right", CPL_TYPE_FLOAT, "pix", "%8.3f",
866  "right edge of the slice at this y position", CPL_TRUE},
867  { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
868 };
869 
870 /*---------------------------------------------------------------------------*/
916 /*---------------------------------------------------------------------------*/
917 cpl_table *
918 muse_trace(const muse_image *aImage, int aNSum, double aEdgeFrac, int aFitorder,
919  cpl_table **aSamples)
920 {
921  cpl_ensure(aImage && aImage->data, CPL_ERROR_NULL_INPUT, NULL);
922  cpl_ensure(aNSum > 0 && aEdgeFrac > 0. && aEdgeFrac < 1. && aFitorder > 0,
923  CPL_ERROR_ILLEGAL_INPUT, NULL);
924 
925  /* count number of tracepoints */
926  int ny = cpl_image_get_size_y(aImage->data),
927  npoints = (ny - 1) / aNSum;
928  unsigned short nsearchslices = kMuseSlicesPerCCD;
929  cpl_boolean slice_number_hack = getenv("MUSE_AIT_HACK_SLICE_NUMBER")
930  && atoi(getenv("MUSE_AIT_HACK_SLICE_NUMBER")) > 0
931  && atoi(getenv("MUSE_AIT_HACK_SLICE_NUMBER")) < 49;
932  unsigned char ifu = muse_utils_get_ifu(aImage->header);
933  if (slice_number_hack) {
934  nsearchslices = atoi(getenv("MUSE_AIT_HACK_SLICE_NUMBER"));
935  cpl_msg_warning(__func__, "Overriding number of slices to search in IFU "
936  "%hhu to %hu!", ifu, nsearchslices);
937  }
938  cpl_msg_info(__func__, "Working with %hu slices, %d image rows, and %d "
939  "tracepoints in IFU %hhu", nsearchslices, ny, npoints, ifu);
940 
941  cpl_boolean chan24in2014 = (ifu == 24)
942  && !strncmp(muse_pfits_get_dateobs(aImage->header), "2014-", 5);
943  if (chan24in2014) {
944  cpl_msg_warning(__func__, "Using overrides for IFU 24 in 2014: positions of"
945  " slices 37 to 48 may be only approximate!");
946  }
947 
948  /* duplicate input image to be able to interpolate bad pixels */
949  muse_image *image = muse_image_new();
950  image->data = cpl_image_duplicate(aImage->data);
951  if (aImage->dq) {
952  image->dq = cpl_image_duplicate(aImage->dq);
953  } else {
954  image->dq = cpl_image_new(cpl_image_get_size_x(aImage->data), ny, CPL_TYPE_INT);
955  }
956  if (aImage->header) { /* header is used by muse_trace_horizontal_cut() */
957  image->header = cpl_propertylist_duplicate(aImage->header);
958  }
959  /* stat and header are not needed here */
961  cpl_detector_interpolate_rejected(image->data);
962 
963  /* get starting guesses for the midpoint of each slice */
964 #define NROWCOLLAPSE 15
965  cpl_vector *cut = muse_trace_horizontal_cut(image, NROWCOLLAPSE);
966  cpl_vector *centers = muse_trace_locate_slices(cut, nsearchslices, aEdgeFrac);
967  cpl_vector_delete(cut);
968  if (!centers) {
969  cpl_msg_error(__func__, "Could not carry out first guess of slice positions "
970  "in IFU %hhu!", ifu);
971  muse_image_delete(image);
972  return NULL;
973  }
974 
975  /* create the output table */
976  cpl_table *tracetable = cpl_table_new(kMuseSlicesPerCCD);
977  if (!tracetable) {
978  cpl_msg_error(__func__, "Could not create output trace table for IFU %hhu: "
979  "%s", ifu, cpl_error_get_message());
980  muse_image_delete(image);
981  return NULL;
982  }
983 
984  /* prepare output table */
985  cpl_table_new_column(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, CPL_TYPE_INT);
986  cpl_table_set_column_unit(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, "No");
987  cpl_table_set_column_format(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, "%2d");
988  cpl_table_new_column(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, CPL_TYPE_FLOAT);
989  cpl_table_set_column_unit(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, "pix");
990  cpl_table_set_column_format(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, "%6.3f");
991  int ipoly;
992  for (ipoly = 0; ipoly < MUSE_TRACE_NPOLY; ipoly++) {
993  char *colname;
994  int j;
995  for (j = 0; j <= aFitorder; j++) {
996  /* create column name, start coefficient names at 0 */
997  colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_COEFF, ipoly, j);
998  cpl_table_new_column(tracetable, colname, CPL_TYPE_DOUBLE);
999  /* fit coeff are in pixel space */
1000  cpl_table_set_column_unit(tracetable, colname, "pix");
1001  cpl_table_set_column_format(tracetable, colname, "%12.5e");
1002  cpl_free(colname);
1003  }
1004  colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_MSE, ipoly);
1005  cpl_table_new_column(tracetable, colname, CPL_TYPE_DOUBLE);
1006  cpl_table_set_column_unit(tracetable, colname, "pix");
1007  cpl_table_set_column_format(tracetable, colname, "%12.5e");
1008  cpl_free(colname);
1009  } /* for ipoly */
1010 
1011  int isamplesrow = -1; /* track the current row in the samples table */
1012  if (aSamples) {
1013  /* create table of the sampled points for debugging */
1014  *aSamples = muse_cpltable_new(muse_tracesamples_def,
1015  kMuseSlicesPerCCD * npoints);
1016  }
1017 
1018  /* create some kind of image derivative by subtracting a 1 pix *
1019  * shifted version of the input image from the input image */
1020  cpl_image *shiftdiff = cpl_image_duplicate(image->data);
1021  cpl_image_shift(shiftdiff, 1, 0);
1022  /* subtract from original, store in new one */
1023  cpl_image_multiply_scalar(shiftdiff, -1);
1024  cpl_image_add(shiftdiff, image->data);
1025 
1026  /* islice loops over all slices, j over all rows, k over all tracepoints */
1027  unsigned short islice;
1028  for (islice = 0; islice < kMuseSlicesPerCCD; islice++) {
1029  int nfailed = 0; /* count the number of failed tracepoints in this slice */
1030  cpl_matrix *xtrace = cpl_matrix_new(1, npoints);
1031  cpl_vector *ytrace = cpl_vector_new(npoints);
1032  /* track slice width for statistics */
1033  cpl_vector *widths = cpl_vector_new(npoints);
1034 
1035  /* loops over vertical points (j) and tracepoints (k), resp. */
1036  int j, k, knum; /* the latter is just a counter for dianostic output */
1037  for (j = 1, k = 0, knum = 1; j <= ny - aNSum; j += aNSum, k++, knum++) {
1038  /* extract slice section and collapse to 1d cut */
1039  /* (see muse_tracing.h for the definition of TRACE_BINSIZE) */
1040  int noffset = (int)cpl_vector_get(centers, islice) - TRACE_BINSIZE,
1041  ilo = noffset,
1042  ihi = (int)cpl_vector_get(centers, islice) + TRACE_BINSIZE,
1043  jlo = j,
1044  jhi = j + aNSum - 1;
1045 #if 0
1046  cpl_msg_debug(__func__, "slice=%hu, center=%f, cut region: %d,%d,%d,%d",
1047  islice + 1, cpl_vector_get(centers, islice), ilo, ihi, jlo, jhi);
1048 #endif
1049  cpl_image *tmp = cpl_image_collapse_window_create(image->data,
1050  ilo, jlo, ihi, jhi,
1051  0); /* collapse vertically */
1052  cpl_image_divide_scalar(tmp, aNSum);
1053  cut = cpl_vector_new_from_image_row(tmp, 1);
1054  cpl_image_delete(tmp);
1055 
1056  /* find midpoint and edges of the cut */
1057  double left, right;
1058  cpl_boolean highSN = CPL_TRUE;
1059  double midpoint = muse_trace_edgefinder(cut, aEdgeFrac, &left, &right,
1060  &highSN);
1061  cpl_vector_delete(cut);
1062  /* keep left edge of the standard edge detection, in *
1063  * case we are dealing with the slices 37 to 48 in IFU 24 */
1064  double left1 = left + noffset;
1065 
1066  cpl_errorstate state = cpl_errorstate_get();
1067  /* refine edge positions again using difference image *
1068  * and Gaussian fits to the edges */
1069  tmp = cpl_image_collapse_window_create(shiftdiff, ilo, jlo, ihi, jhi,
1070  0); /* collapse along rows */
1071  cpl_image_divide_scalar(tmp, aNSum);
1072  cut = cpl_vector_new_from_image_row(tmp, 1);
1073  cpl_image_delete(tmp);
1074  midpoint = muse_trace_refine_edge(cut, &left, &right, noffset,
1075  (jlo + jhi) / 2.);
1076  cpl_vector_delete(cut);
1077 
1078  /* XXX slices 37 to 48 on the CCD have rounded left edge for IFU 24 */
1079  if (midpoint < 0 && midpoint > -2 && islice+1 >= 37 && chan24in2014) {
1080  cpl_msg_debug(__func__, "IFU24 problem? slice %d, y = %f: refined "
1081  "%f < %f < %f", islice+1, ((double)jlo + jhi) / 2.,
1082  left, midpoint, right);
1083  if (left1 < left) {
1084  /* If the edge detection was left of the Gaussian center, then that *
1085  * fit was affected by the rounded "edge" and needs to be reset. */
1086  left = left1;
1087  cpl_msg_debug(__func__, "IFU24 problem! slice %d y = %f: corrected "
1088  "%f < %f < %f", islice+1, ((double)jlo + jhi) / 2.,
1089  left, (left + right) / 2., right);
1090  }
1091  /* the refined value for the right edge should still be fine, keep it */
1092  midpoint = (left + right) / 2.;
1093  } /* if slice 37-48 problem */
1094 
1095  if (midpoint > 0) { /* edge searching was successful */
1096  cpl_matrix_set(xtrace, 0, k, (jlo + jhi) / 2.);
1097  cpl_vector_set(ytrace, k, midpoint);
1098  cpl_vector_set(widths, k, right - left);
1099 
1100  if (aSamples) {
1101  /* save the points for plotting/debugging */
1102  if (++isamplesrow+1 > cpl_table_get_nrow(*aSamples)) {
1103  cpl_table_set_size(*aSamples, isamplesrow+1);
1104  }
1105  cpl_table_set_int(*aSamples, "slice", isamplesrow, islice + 1);
1106  cpl_table_set_float(*aSamples, "y", isamplesrow, (jlo + jhi) / 2.);
1107  cpl_table_set_float(*aSamples, "mid", isamplesrow, midpoint);
1108  cpl_table_set_float(*aSamples, "left", isamplesrow, left);
1109  cpl_table_set_float(*aSamples, "right", isamplesrow, right);
1110  }
1111 
1112  /* we were successful, so we can go to the next trace point */
1113  continue;
1114  }
1115 
1116  /* Error handling for the failure case follows. Only output warning, *
1117  * if many tracepoints (> 10%) are lost in the current slice. Only *
1118  * count failures with significant flux levels (high S/N). */
1119  cpl_errorstate_set(state);
1120  if (highSN) {
1121  nfailed++;
1122  }
1123 #if 0
1124  cpl_msg_debug(__func__, "slice=%hu, nfailed=%d, tracepoint=%d, midpoint="
1125  "%f, y=%d", islice + 1, nfailed, knum, midpoint, j);
1126 #endif
1127  if (nfailed > 0.1*npoints && midpoint == -1.) {
1128  cpl_msg_warning(__func__, "failure %d in slice %hu of IFU %hhu: lost "
1129  "trace at y=%d (tracepoint %d of %d)", nfailed,
1130  islice + 1, ifu, j, knum, npoints);
1131  }
1132 
1133  /* resize vector and matrix, so that they don't include the failure */
1134  int oldsize = cpl_vector_get_size(ytrace); /* should all have same size */
1135  cpl_vector_set_size(ytrace, oldsize - 1);
1136  cpl_matrix_resize(xtrace, 0, 0, 0, -1);
1137  cpl_vector_set_size(widths, oldsize - 1);
1138  k--; /* set current matrix/vector index backwards */
1139  } /* for j */
1140 #if 0
1141  /* compare number of tracepoints to points in vectors, *
1142  * 1 was already added to k (which started at 0) */
1143  printf("k=%d tracepoints (should be equal to %d)\n", k, npoints);
1144  cpl_matrix_dump(xtrace, stdout), fflush(stdout);
1145  cpl_vector_dump(ytrace, stdout), fflush(stdout);
1146 #endif
1147 
1148  /* iterate through input points and the fit with XXX 5 sigma rejections */
1149  const float kWSigma = 5, kRSigma = 5;
1150  cpl_msg_debug(__func__, "Working on slice %hu (kWSigma=%f, kRSigma=%f)",
1151  islice + 1, kWSigma, kRSigma);
1152  cpl_vector *mse = cpl_vector_new(MUSE_TRACE_NPOLY);
1153  cpl_vector_fill(mse, -1.);
1154  cpl_polynomial **tracefits = muse_trace_iterate_fit(xtrace, ytrace, widths,
1155  islice + 1, aFitorder,
1156  kWSigma, kRSigma, mse);
1157  /* use the final mean width for the trace solution */
1158  double wmean = cpl_vector_get_mean(widths);
1159 
1160  /* reset status after fitting procedures and clean up */
1161  cpl_matrix_delete(xtrace);
1162  cpl_vector_delete(ytrace);
1163  cpl_vector_delete(widths);
1164 
1165  if (!tracefits) {
1166  /* we need to print an error message and skip the table entries */
1167  cpl_msg_error(__func__, "The trace fit in slice %hu of IFU %hhu failed",
1168  islice + 1, ifu);
1169  cpl_vector_delete(mse);
1170  continue;
1171  }
1172 
1173  /* row numbers start at 1 not 0 */
1174  cpl_table_set_int(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, islice,
1175  islice + 1);
1176  cpl_table_set_float(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, islice, wmean);
1177  for (ipoly = 0; ipoly < MUSE_TRACE_NPOLY; ipoly++) {
1178  if (!tracefits[ipoly]) {
1179  cpl_msg_error(__func__, "The fit %d in slice %hu of IFU %hhu failed",
1180  ipoly, islice + 1, ifu);
1181  continue;
1182  }
1183  char *colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_MSE, ipoly);
1184  cpl_table_set_double(tracetable, colname, islice,
1185  cpl_vector_get(mse, ipoly));
1186  cpl_free(colname);
1187  /* j loops over all orders of the polynomial */
1188  for (j = 0; j <= aFitorder; j++) {
1189  cpl_size pows[1] = { j }; /* trick to access the polynomial */
1190 
1191  colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_COEFF, ipoly, j);
1192  cpl_errorstate prestate = cpl_errorstate_get();
1193  double coeff = cpl_polynomial_get_coeff(tracefits[ipoly], pows);
1194 #define SLOPE_WARN_LIMIT 0.015
1195  if (j == 1 && fabs(coeff) > SLOPE_WARN_LIMIT) {
1196  cpl_msg_warning(__func__, "1st order coefficient of the %s tracing "
1197  "polynomial is unexpectedly large in slice %hu of IFU"
1198  " %hhu: |%f| > %f", muse_trace_poly_strings[ipoly],
1199  islice + 1, ifu, coeff, SLOPE_WARN_LIMIT);
1200  }
1201  cpl_table_set_double(tracetable, colname, islice, coeff);
1202  if (!cpl_errorstate_is_equal(prestate)) {
1203  cpl_msg_warning(__func__, "Problem writing to field %s in trace table"
1204  " for IFU %hhu: %s", colname, ifu,
1205  cpl_error_get_message());
1206  }
1207  cpl_free(colname);
1208  } /* for j (all polynomial orders) */
1209  } /* for ipoly */
1210  cpl_vector_delete(mse);
1211  muse_trace_polys_delete(tracefits);
1212  } /* for islice */
1213  cpl_vector_delete(centers);
1214  cpl_image_delete(shiftdiff);
1215  if (aSamples) {
1216  /* cut samples table to the actually used number of rows */
1217  cpl_table_set_size(*aSamples, ++isamplesrow);
1218  }
1219 
1220  if (slice_number_hack) {
1221  cpl_msg_warning(__func__, "Will try to fix the slices, %"CPL_SIZE_FORMAT
1222  " seem to be bad!", cpl_table_count_invalid(tracetable,
1223  MUSE_TRACE_TABLE_COL_WIDTH));
1224  cpl_table_dump(tracetable, 0, 100, stdout); /* be sure to output _all_ slices */
1225  fflush(stdout);
1226  /* add fake slices for those that failed */
1227  for (islice = 0; islice < cpl_table_get_nrow(tracetable); islice++) {
1228  double width = cpl_table_get(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, islice, NULL);
1229  if (width < 40.) {
1230  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT, "slice %hu of "
1231  "IFU %hhu was narrow (%f), erased it", islice + 1,
1232  ifu, width);
1233  cpl_table_erase_window(tracetable, islice--, 1);
1234  } /* if */
1235  } /* for islice */
1236  double last = 0;
1237  for (islice = 0; islice < cpl_table_get_nrow(tracetable); islice++) {
1238  double cen = cpl_table_get(tracetable, "tc0_00", islice, NULL);
1239  if (cen - last > 100) { /* insert a slice */
1240  unsigned short iref = islice - 1;
1241  cpl_table *row = cpl_table_extract(tracetable, iref, 1);
1242  /* the slice numbering is fixed below */
1243  double offset = 84.5; /* typical offset */
1244  cpl_table_add_scalar(row, MUSE_TRACE_TABLE_COL_SLICE_NO, 1);
1245  cpl_table_add_scalar(row, "tc0_00", offset);
1246  cpl_table_add_scalar(row, "tc1_00", offset);
1247  cpl_table_add_scalar(row, "tc2_00", offset);
1248  cpl_table_add_scalar(row, "MSE0", 1.); /* signify by a large MSE that this is fake */
1249  cpl_table_add_scalar(row, "MSE1", 1.);
1250  cpl_table_add_scalar(row, "MSE2", 1.);
1251  cpl_table_insert(tracetable, row, islice);
1252 #if 0
1253  printf("rowtable (islice=%hu):\n", islice);
1254  cpl_table_dump(row, 0, 100, stdout);
1255  fflush(stdout);
1256  printf("tracetable (islice=%hu):\n", islice);
1257  cpl_table_dump(tracetable, islice - 2, 10, stdout);
1258  fflush(stdout);
1259 #endif
1260  cen = cpl_table_get(row, "tc0_00", 0, NULL); /* new center */
1261  cpl_table_delete(row);
1262  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT, "slice was "
1263  "missing before slice %hu of IFU %hhu, copied "
1264  "from slice %hu", islice + 1, ifu, iref + 1);
1265  }
1266  last = cen;
1267  } /* for islice */
1268  for (islice = 0; islice < cpl_table_get_nrow(tracetable); islice++) {
1269  unsigned short sliceno = cpl_table_get_int(tracetable,
1270  MUSE_TRACE_TABLE_COL_SLICE_NO,
1271  islice, NULL);
1272  if (sliceno != islice + 1) {
1273  cpl_msg_warning(__func__, "Resetting entry at table row index %hu to "
1274  "correct slice number (%hu instead of %hu)", islice,
1275  islice + 1, sliceno);
1276  cpl_table_set_int(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, islice,
1277  islice + 1);
1278  }
1279  } /* for islice */
1280  } /* if slice_number_hack */
1281 
1282  /* give the user some useful output to read */
1283  cpl_msg_info(__func__, "Found %"CPL_SIZE_FORMAT" slices of width %4.1f+/-%3.1f"
1284  " pix (%4.1f pix...%4.1f pix) in IFU %hhu",
1285  cpl_table_get_nrow(tracetable),
1286  cpl_table_get_column_mean(tracetable, MUSE_TRACE_TABLE_COL_WIDTH),
1287  cpl_table_get_column_stdev(tracetable, MUSE_TRACE_TABLE_COL_WIDTH),
1288  cpl_table_get_column_min(tracetable, MUSE_TRACE_TABLE_COL_WIDTH),
1289  cpl_table_get_column_max(tracetable, MUSE_TRACE_TABLE_COL_WIDTH),
1290  ifu);
1291  muse_image_delete(image);
1292 
1293  return tracetable;
1294 } /* muse_trace() */
1295 
1296 /*---------------------------------------------------------------------------*/
1305 /*---------------------------------------------------------------------------*/
1306 int
1307 muse_trace_table_get_order(const cpl_table *aTable)
1308 {
1309  /* cpl_table_get_ncol() returns -1 on error so we *
1310  * automatically get a negative value on error here */
1311  /* There are two extra values, the MSEs; the rest of the columns are the *
1312  * coefficients of the MUSE_TRACE_NPOLY polynomials, including zeroth order */
1313  return (cpl_table_get_ncol(aTable) - 2) / MUSE_TRACE_NPOLY - 2;
1314 } /* muse_trace_table_get_order() */
1315 
1316 /*---------------------------------------------------------------------------*/
1335 /*---------------------------------------------------------------------------*/
1336 cpl_polynomial **
1338  const unsigned short aSlice)
1339 {
1340  cpl_ensure(aTable, CPL_ERROR_NULL_INPUT, NULL);
1341  cpl_ensure(aSlice >= 1 && aSlice <= kMuseSlicesPerCCD,
1342  CPL_ERROR_ILLEGAL_INPUT, NULL);
1343  /* search for row containing the requested slice, first to access it *
1344  * in a possibly incomplete table, and second to check its presence! */
1345  int irow, nrow = cpl_table_get_nrow(aTable);
1346  for (irow = 0; irow < nrow; irow++) {
1347  int err;
1348  unsigned short slice = cpl_table_get_int(aTable,
1349  MUSE_TRACE_TABLE_COL_SLICE_NO,
1350  irow, &err);
1351  if (slice == aSlice && !err) {
1352  break;
1353  }
1354  } /* for irow */
1355  cpl_ensure(irow < nrow, CPL_ERROR_DATA_NOT_FOUND, NULL);
1356 
1357  cpl_polynomial **ptrace = cpl_calloc(MUSE_TRACE_NPOLY,
1358  sizeof(cpl_polynomial *));
1359  char colname[7]; /* "tcI_NN" plus null byte */
1360  int ipoly;
1361  for (ipoly = 0; ipoly < MUSE_TRACE_NPOLY; ipoly++) {
1362  int traceorder = muse_trace_table_get_order(aTable);
1363  ptrace[ipoly] = cpl_polynomial_new(1);
1364 
1365  /* fill in the orders of the polynomial */
1366  int k;
1367  for (k = 0; k <= traceorder; k++) {
1368  cpl_size pows[1] = { k }; /* trick to access the polynomial */
1369  sprintf(colname, MUSE_TRACE_TABLE_COL_COEFF, ipoly, k);
1370  int err;
1371  cpl_polynomial_set_coeff(ptrace[ipoly], pows,
1372  cpl_table_get(aTable, colname, irow, &err));
1373  if (err != 0) { /* broken table entry */
1374  cpl_polynomial_delete(ptrace[MUSE_TRACE_CENTER]);
1375  cpl_polynomial_delete(ptrace[MUSE_TRACE_LEFT]);
1376  cpl_polynomial_delete(ptrace[MUSE_TRACE_RIGHT]);
1377  cpl_free(ptrace);
1378  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT, "Trace table "
1379  "broken in slice %hu (row index %d) column %s",
1380  aSlice, irow, colname);
1381  return NULL;
1382  } /* if */
1383  } /* for k */
1384  } /* for ipoly */
1385 
1386  return ptrace;
1387 } /* muse_trace_table_get_polys_for_slice() */
1388 
1389 /*---------------------------------------------------------------------------*/
1394 /*---------------------------------------------------------------------------*/
1395 void
1396 muse_trace_polys_delete(cpl_polynomial *aPolys[])
1397 {
1398  if (!aPolys) {
1399  return;
1400  }
1401  cpl_polynomial_delete(aPolys[MUSE_TRACE_CENTER]);
1402  cpl_polynomial_delete(aPolys[MUSE_TRACE_LEFT]);
1403  cpl_polynomial_delete(aPolys[MUSE_TRACE_RIGHT]);
1404  cpl_free(aPolys);
1405 } /* muse_trace_polys_delete() */
1406 
1407 /* plot the result of muse_trace_locate_slices() */
1408 static void
1409 muse_trace_plot_located_slices(cpl_vector *aRowVec, cpl_vector *aCenters,
1410  double aMedian, double aMDev, double aLimit)
1411 {
1412 #if HAVE_POPEN && HAVE_PCLOSE
1413  FILE *gp = popen("gnuplot -persist", "w");
1414  if (!gp) {
1415  cpl_msg_error(__func__, "could not open gnuplot for plotting");
1416  return;
1417  }
1418 
1419 #if HAVE_MKDTEMP
1420  char dirtemplate[] = "/tmp/muse_trace_plot_located_slices_XXXXXX";
1421  char *dirname = mkdtemp(dirtemplate);
1422  if (!dirname) {
1423  return;
1424  }
1425 #else
1426  char dirname[] = "/tmp";
1427 #endif
1428  char *out1 = cpl_sprintf("%s/row.dat", dirname);
1429  FILE *fp = fopen(out1, "w");
1430  cpl_vector_dump(aRowVec, fp);
1431  fclose(fp);
1432  char *out2 = cpl_sprintf("%s/centers.dat", dirname);
1433  fp = fopen(out2, "w");
1434  cpl_vector_dump(aCenters, fp);
1435  fclose(fp);
1436 
1437  fprintf(gp, "set title \"located slices: median %.2f+/-%.2f, limit %.2f\"\n"
1438  "unset key\nset style fill solid 0.5\n", aMedian, aMDev, aLimit);
1439  fprintf(gp, "median(x)=%e\nlimit(x)=%e\nlo(x)=%e\n",
1440  aMedian, aLimit, aMedian - aMDev);
1441  fprintf(gp, "set xrange [%d:%"CPL_SIZE_FORMAT"]\n", 1, cpl_vector_get_size(aRowVec));
1442  fprintf(gp, "set yrange [%e:%e]\n", aLimit - 0.5*aMDev, aMedian + 1.3*aMDev);
1443  fprintf(gp, "plot lo(x) w filledcu y1=%e, "
1444  " median(x) t \"median\", limit(x) t \"limit\" w l lw 2, "
1445  " \"%s\" w l lt 7, \"%s\" u 2:(%e):1 w p lt -1, "
1446  " \"%s\" u 2:(%e):1 w labels\n",
1447  aMedian+aMDev, out1, out2, aMedian, out2, aMedian+200);
1448 
1449  pclose(gp);
1450  remove(out1);
1451  remove(out2);
1452  cpl_free(out1);
1453  cpl_free(out2);
1454 #if HAVE_MKDTEMP && HAVE_UNISTD_H
1455  int rc = rmdir(dirname);
1456  if (rc < 0) {
1457  cpl_msg_warning(__func__, "Used %s for plotting, please clean it manually!",
1458  dirname);
1459  }
1460 #endif
1461 #endif /* HAVE_POPEN && HAVE_PCLOSE */
1462 } /* muse_trace_plot_located_slices() */
1463 
1464 /*---------------------------------------------------------------------------*/
1485 /*---------------------------------------------------------------------------*/
1486 cpl_error_code
1487 muse_trace_plot_samples(cpl_table *aSamples, cpl_table *aTrace,
1488  unsigned short aSlice1, unsigned short aSlice2,
1489  muse_image *aImage)
1490 {
1491 #if HAVE_POPEN && HAVE_PCLOSE
1492  cpl_ensure_code(aSamples, CPL_ERROR_NULL_INPUT);
1493  cpl_error_code rc = muse_cpltable_check(aSamples, muse_tracesamples_def);
1494  cpl_ensure_code(rc == CPL_ERROR_NONE, rc);
1495  /* if errors occur, only plot the two central slices */
1496  if (aSlice1 < 1 || aSlice1 > kMuseSlicesPerCCD || aSlice1 > aSlice2 ||
1497  aSlice2 < 1 || aSlice2 > kMuseSlicesPerCCD) {
1498  fprintf(stderr, "Warning: resetting slice numbers (%hu to %hu does not make"
1499  " sense)!\n", aSlice1, aSlice2);
1500  aSlice1 = kMuseSlicesPerCCD / 2;
1501  aSlice2 = kMuseSlicesPerCCD / 2 + 1;
1502  }
1503  if (aSlice2 - aSlice1 > 10) {
1504  fprintf(stderr, "Warning: plotting %hu slices may take a long time and "
1505  "RAM/disk space!\n", aSlice2 - aSlice1 + 1);
1506  }
1507  printf("Plotting slices %hu to %hu\n", aSlice1, aSlice2);
1508 
1509  FILE *gp = popen("gnuplot", "w");
1510  if (!gp) {
1511  return CPL_ERROR_ASSIGNING_STREAM;
1512  }
1513 
1514  int nx = -1, ny = kMuseOutputYTop;
1515  const float *data = NULL;
1516  if (aImage) {
1517  nx = cpl_image_get_size_x(aImage->data);
1518  ny = cpl_image_get_size_x(aImage->data);
1519  data = cpl_image_get_data_float_const(aImage->data);
1520  }
1521 
1522  /* fixed output file names for both purposes (plotting of samples and *
1523  * background) within a hopefully random and protected, directory */
1524 #if HAVE_MKDTEMP
1525  char dirtemplate[] = "/tmp/muse_trace_plot_samples_XXXXXX";
1526  char *dirname = mkdtemp(dirtemplate);
1527  if (!dirname) {
1528  return CPL_ERROR_FILE_NOT_CREATED;
1529  }
1530 #else
1531  char dirname[] = "/tmp";
1532 #endif
1533  FILE *tf = NULL, *sf = NULL;
1534  char *t_out = NULL;
1535  if (aImage) {
1536  t_out = cpl_sprintf("%s/muse_trace_plot_flatimage.dat", dirname);
1537  tf = fopen(t_out, "w+");
1538  if (!tf) {
1539  cpl_error_set_message(__func__, CPL_ERROR_FILE_NOT_CREATED, "\"%s\"",
1540  t_out);
1541  cpl_free(t_out); /* fails gracefully on NULL */
1542  return CPL_ERROR_FILE_NOT_CREATED;
1543  }
1544  }
1545  char *s_out = cpl_sprintf("%s/muse_trace_plot_samples.dat", dirname);
1546  sf = fopen(s_out, "w+");
1547  if (!sf) {
1548  cpl_error_set_message(__func__, CPL_ERROR_FILE_NOT_CREATED, "%s", s_out);
1549  cpl_free(t_out); /* fails gracefully on NULL */
1550  cpl_free(s_out);
1551  return CPL_ERROR_FILE_NOT_CREATED;
1552  }
1553 
1554  /* plot all relevant slices at once */
1555  int i, lplot = INT_MAX, rplot = INT_MIN;
1556  unsigned short nslice;
1557  for (nslice = aSlice1; nslice <= aSlice2; nslice++) {
1558  /* write out all relevant sample points */
1559  for (i = 0; i < cpl_table_get_nrow(aSamples); i++) {
1560  if (nslice != cpl_table_get_int(aSamples, "slice", i, NULL)) {
1561  /* not this slice */
1562  continue;
1563  }
1564  float l = cpl_table_get_float(aSamples, "left", i, NULL),
1565  r = cpl_table_get_float(aSamples, "right", i, NULL);
1566  fprintf(sf, "%g %g %g %g\n",
1567  cpl_table_get_float(aSamples, "y", i, NULL),
1568  cpl_table_get_float(aSamples, "mid", i, NULL), l, r);
1569  if ((int)floor(l) < lplot) {
1570  lplot = floor(l);
1571  }
1572  if ((int)floor(r) > rplot) {
1573  rplot = floor(r);
1574  }
1575  }
1576  /* expand the area a bit, to make the edges fully appear on the plot */
1577  lplot -= 5;
1578  rplot += 5;
1579 
1580  if (aTrace) {
1581  int order = muse_trace_table_get_order(aTrace);
1582  double *c = (double *)cpl_calloc(order + 1, sizeof(double));
1583 
1584  int ipoly;
1585  for (ipoly = 0; ipoly < MUSE_TRACE_NPOLY; ipoly++) {
1586  for (i = 0; i <= order; i++) {
1587  char *colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_COEFF, ipoly, i);
1588  c[i] = cpl_table_get_double(aTrace, colname, nslice-1, NULL);
1589  cpl_free(colname);
1590  }
1591 
1592  /* set up the function for the plot */
1593  fprintf(gp, "p%02hu%1d(x) = (%g)", nslice, ipoly, c[0]);
1594  for (i = 1; i <= order; i++) {
1595  fprintf(gp, " + (%g) * x**(%d)", c[i], i);
1596  }
1597  fprintf(gp, "\n");
1598  } /* for ipoly */
1599  cpl_free(c);
1600  } /* if aTrace */
1601  } /* for nslice */
1602  if (aImage) {
1603  /* plot the image data, too */
1604  for (i = lplot - 1; i < rplot; i++) {
1605  int j;
1606  for (j = 0; j < ny; j++) {
1607  if (i < 0 || i >= nx || j < 0 || j >= ny) {
1608  continue;
1609  }
1610  fprintf(tf, "%d %d %f\n", i+1, j+1, data[i + j*nx]);
1611  } /* for j (vertical pixels) */
1612  } /* for i (horizontal pixels) */
1613  printf("Written \"%s\".\n", t_out);
1614  fclose(tf);
1615  }
1616  printf("Written \"%s\".\n", s_out);
1617  fclose(sf); /* close the file now to unlock it for plotting */
1618 
1619  /* plot title */
1620  fprintf(gp, "set title \"trace result, slices %hu to %hu\"\n", aSlice1, aSlice2);
1621  fprintf(gp, "set palette gray\n"); /* alternative palette in greyscale */
1622  fprintf(gp, "unset key\n");
1623 
1624  /* enough sampling points for the vertical dimension */
1625  fprintf(gp, "set samples %d\n", ny);
1626  /* we need parametric mode because we want to plot the slices vertically */
1627  fprintf(gp, "set parametric\n");
1628  /* set ranges for plotting */
1629  fprintf(gp, "set xrange [%d:%d]\n", lplot, rplot);
1630  fprintf(gp, "set yrange [%d:%d]\n", 1, ny);
1631  fprintf(gp, "set trange [%d:%d]\n", 1, ny);
1632  /* we are dealing with normalized master flats here */
1633  fprintf(gp, "set cbrange [1e-4:1.5]\n");
1634 
1635  /* finally create the plot itself */
1636  fprintf(gp, "plot ");
1637  if (aImage) {
1638  fprintf(gp, "\"%s\" w image, ", t_out);
1639  }
1640  fprintf(gp, "\"%s\" u 2:1 t \"center points\" w p pt 2 lt rgb \"blue\" ps 1.2, "
1641  "\"%s\" u 3:1 t \"edge points left\" w p pt 2 lt rgb \"red\" ps 0.8, "
1642  "\"%s\" u 4:1 t \"edge points right\" w p pt 2 lt rgb \"green\" ps 0.8",
1643  s_out, s_out, s_out);
1644  if (aTrace) {
1645  /* show trace polynomials pnni(x) in parametric mode */
1646  for (nslice = aSlice1; nslice <= aSlice2; nslice++) {
1647  fprintf(gp, ", p%02hu0(t),t t \"center\" w l lt rgb \"dark-blue\" lw 2, "
1648  "p%02hu1(t),t t \"left\" w l lt rgb \"dark-red\" lw 1, "
1649  "p%02hu2(t),t t \"right\" w l lt rgb \"forest-green\" lw 1",
1650  nslice, nslice, nslice);
1651  } /* for nslice */
1652  }
1653  fprintf(gp, "\n");
1654  fflush(gp);
1655  /* request keypress, so that working with the mouse keeps *
1656  * working and gnuplot has enough time to actually draw all *
1657  * the stuff before the files get removed */
1658  printf("Press ENTER to end program and close plot\n");
1659  getchar();
1660  remove(s_out);
1661  if (aImage) {
1662  remove(t_out);
1663  cpl_free(t_out);
1664  }
1665  rmdir(dirname);
1666  cpl_free(s_out);
1667  pclose(gp);
1668  return CPL_ERROR_NONE;
1669 #else /* no HAVE_POPEN && HAVE_PCLOSE */
1670  return CPL_ERROR_UNSUPPORTED_MODE;
1671 #endif /* HAVE_POPEN && HAVE_PCLOSE */
1672 } /* muse_trace_plot_samples() */
1673 
1674 /*---------------------------------------------------------------------------*/
1696 /*---------------------------------------------------------------------------*/
1697 cpl_error_code
1698 muse_trace_plot_widths(cpl_table *aSamples, unsigned short aSlice1,
1699  unsigned short aSlice2)
1700 {
1701 #if HAVE_POPEN && HAVE_PCLOSE
1702  cpl_ensure_code(aSamples, CPL_ERROR_NULL_INPUT);
1703  cpl_error_code rc = muse_cpltable_check(aSamples, muse_tracesamples_def);
1704  cpl_ensure_code(rc == CPL_ERROR_NONE, rc);
1705  /* if errors occur, only plot the two central slices */
1706  if (aSlice1 < 1 || aSlice1 > kMuseSlicesPerCCD || aSlice1 > aSlice2 ||
1707  aSlice2 < 1 || aSlice2 > kMuseSlicesPerCCD) {
1708  fprintf(stderr, "Warning: resetting slice numbers (%hu to %hu does not make"
1709  " sense)!\n", aSlice1, aSlice2);
1710  aSlice1 = kMuseSlicesPerCCD / 2;
1711  aSlice2 = kMuseSlicesPerCCD / 2 + 1;
1712  }
1713  printf("Plotting slices %hu to %hu\n", aSlice1, aSlice2);
1714 
1715  FILE *gp = popen("gnuplot", "w");
1716  if (!gp) {
1717  return CPL_ERROR_ASSIGNING_STREAM;
1718  }
1719 
1720  int nrow = cpl_table_get_nrow(aSamples);
1721  const int *sdata = cpl_table_get_data_int_const(aSamples, "slice");
1722  const float *ydata = cpl_table_get_data_float_const(aSamples, "y"),
1723  *ldata = cpl_table_get_data_float_const(aSamples, "left"),
1724  *rdata = cpl_table_get_data_float_const(aSamples, "right");
1725 
1726  /* plot title */
1727  fprintf(gp, "set title \"trace slice widths, slices %hu to %hu\"\n",
1728  aSlice1, aSlice2);
1729  fprintf(gp, "set key outside below\n");
1730  /* set ranges and axes for plotting */
1731  fprintf(gp, "set xrange [%d:%d]\n", 1, kMuseOutputYTop);
1732  fprintf(gp, "set yrange [%f:%f]\n", kMuseSliceLoLikelyWidth,
1733  kMuseSliceHiLikelyWidth);
1734  fprintf(gp, "set xlabel \"y position on CCD [pix]\"\n");
1735  fprintf(gp, "set ylabel \"slice width at y position [pix]\"\n");
1736 
1737  /* distribute slices over 256 color values */
1738  double dslice = (aSlice2 - aSlice1) / 255.;
1739  if (dslice == 0.) { /* take care not to produce NANs below when dividing */
1740  dslice = 1.;
1741  }
1742  /* finally create the plot itself, loop over all slices */
1743  fprintf(gp, "plot ");
1744  unsigned short nslice;
1745  for (nslice = aSlice1; nslice <= aSlice2; nslice++) {
1746  /* change color going from left (red) to right (green), *
1747  * depending on slice number */
1748  fprintf(gp, "\"-\" t \"slice %02hu\" w lp ps 0.8 lt rgb \"#%02x%02x%02x\"",
1749  nslice,
1750  (int)((nslice - aSlice1) / dslice), /* red */
1751  (int)((aSlice2 - nslice) / dslice), /* green */
1752  0); /* blue */
1753  if (nslice == aSlice2) {
1754  fprintf(gp, "\n");
1755  } else {
1756  fprintf(gp, ", ");
1757  }
1758  } /* for nslice */
1759  fflush(gp);
1760  for (nslice = aSlice1; nslice <= aSlice2; nslice++) {
1761  int i;
1762  for (i = 0; i < nrow; i++) {
1763  if (nslice == sdata[i]) {
1764  fprintf(gp, "%f %f\n", ydata[i], rdata[i]-ldata[i]);
1765  }
1766  }
1767  fprintf(gp, "EOF\n");
1768  } /* for nslice */
1769  fprintf(gp, "\n");
1770  fflush(gp);
1771  /* request keypress, so that working with the mouse keeps *
1772  * working and gnuplot has enough time to actually draw all *
1773  * the stuff before the files get removed */
1774  printf("Press ENTER to end program and close plot\n");
1775  getchar();
1776  pclose(gp);
1777  return CPL_ERROR_NONE;
1778 #else /* no HAVE_POPEN && HAVE_PCLOSE */
1779  return CPL_ERROR_UNSUPPORTED_MODE;
1780 #endif /* HAVE_POPEN && HAVE_PCLOSE */
1781 } /* muse_trace_plot_widths() */
1782 
cpl_polynomial ** muse_trace_table_get_polys_for_slice(const cpl_table *aTable, const unsigned short aSlice)
construct polynomial from the trace table entry for the given slice
int muse_trace_table_get_order(const cpl_table *aTable)
determine order of tracing polynomial from table
void muse_image_delete(muse_image *aImage)
Deallocate memory associated to a muse_image object.
Definition: muse_image.c:84
cpl_size * muse_quadrants_get_window(const muse_image *aImage, unsigned char aQuadrant)
Determine the data window of a given quadrant on the CCD.
cpl_error_code muse_trace_plot_samples(cpl_table *aSamples, cpl_table *aTrace, unsigned short aSlice1, unsigned short aSlice2, muse_image *aImage)
Plotting of trace sample points and solution using gnuplot.
cpl_table * muse_trace(const muse_image *aImage, int aNSum, double aEdgeFrac, int aFitorder, cpl_table **aSamples)
carry out the tracing of the slices on CCD, save parameters in table
Definition: muse_tracing.c:918
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
Definition: muse_utils.c:95
cpl_image * data
the data extension
Definition: muse_image.h:47
cpl_vector * muse_trace_locate_slices(cpl_vector *aRowVec, const unsigned short aNSlices, double aFrac)
Find all slice midpoints across a CCD.
Definition: muse_tracing.c:161
static cpl_vector * muse_trace_horizontal_cut(const muse_image *aImage, unsigned int aNRows)
Create a vector containing a representative horizontal image cut.
Definition: muse_tracing.c:78
const char * muse_pfits_get_dateobs(const cpl_propertylist *aHeaders)
find out the exposure time
Definition: muse_pfits.c:260
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:41
cpl_propertylist * header
the FITS header
Definition: muse_image.h:73
cpl_error_code muse_cpltable_check(const cpl_table *aTable, const muse_cpltable_def *aDef)
Check whether the table contains the fields of the definition.
void muse_trace_polys_delete(cpl_polynomial *aPolys[])
Delete the multi-polynomial array created in relation to tracing.
cpl_image * dq
the data quality extension
Definition: muse_image.h:57
cpl_table * muse_cpltable_new(const muse_cpltable_def *aDef, cpl_size aLength)
Create an empty table according to the specified definition.
static cpl_polynomial ** muse_trace_iterate_fit(cpl_matrix *aX, cpl_vector *aY, cpl_vector *aWidths, const unsigned short aSlice, const unsigned int aFitorder, const float aWSigma, const float aRSigma, cpl_vector *aMSE)
iterate the tracing solution to remove outliers
Definition: muse_tracing.c:668
cpl_error_code muse_cplvector_erase_element(cpl_vector *aVector, int aElement)
delete the given element from the input vector
double muse_trace_edgefinder(const cpl_vector *aDataVec, double aFrac, double *aLeft, double *aRight, cpl_boolean *aHighSN)
Find the midpoint and edges of a cut through a slice.
Definition: muse_tracing.c:363
cpl_polynomial * muse_utils_iterate_fit_polynomial(cpl_matrix *aPos, cpl_vector *aVal, cpl_vector *aErr, cpl_table *aExtra, const unsigned int aOrder, const double aRSigma, double *aMSE, double *aChiSq)
Iterate a polynomial fit.
Definition: muse_utils.c:1829
static double muse_trace_refine_edge(cpl_vector *aDiffVec, double *aLeft, double *aRight, int aOffset, double aY)
Find more exact midpoint and edge positions using a difference vector of the input data...
Definition: muse_tracing.c:496
const muse_cpltable_def muse_tracesamples_def[]
MUSE tracing sample points table definition.
Definition: muse_tracing.c:858
cpl_error_code muse_image_reject_from_dq(muse_image *aImage)
Reject pixels of a muse_image depending on its DQ data.
Definition: muse_image.c:848
Definition of a cpl table structure.
double muse_cplvector_get_adev_const(const cpl_vector *aVector, double aCenter)
Compute the average absolute deviation of a (constant) vector.
muse_image * muse_image_new(void)
Allocate memory for a new muse_image object.
Definition: muse_image.c:65
cpl_error_code muse_trace_plot_widths(cpl_table *aSamples, unsigned short aSlice1, unsigned short aSlice2)
Plotting the width from trace sample points using gnuplot.