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