MUSE Pipeline Reference Manual  2.1.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-2017 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  /* run the followingn at least once, iterate if the width is too small */
409  int offset = 0; /* do the first iteration starting at the center *
410  * of the extracted buffer */
411  do {
412 #if 0
413  if (offset > 0) {
414  cpl_msg_debug(__func__, "Iterating edge search with offset = %d pix (IFU "
415  "%hhu)", offset, aIFU);
416  }
417 #endif
418 
419  /* start at the center and search outwards for the right-hand edge */
420  int i;
421  for (i = size/2 + offset; i < size; i++) {
422  if (ydata[i] < detlimit) {
423  /* i is the element where we fell below the limit. Use this to *
424  * determine the fractional position where the limit was reached *
425  * using linear interpolation */
426  *aRight = i-1 + (detlimit - ydata[i-1]) / (ydata[i] - ydata[i-1]);
427 #if 0
428  cpl_msg_debug(__func__, "r: %d..._%d_, %f/_%f_ ===> %f", i-1, i,
429  ydata[i-1], ydata[i], *aRight);
430 #endif
431  /* if the found interpolation point is more than 1 pix away *
432  * from the current pixel then the interpolation was wrong! */
433  if (fabs(*aRight - i) > 1.) {
434  /* Only display message and return the fault, if we actually got a few *
435  * pixels away from the position where we started. Otherwise it will *
436  * just be a dark pixel near the slice center, that we can ignore. *
437  * This case would be a real problem and should actually not happen! */
438  if (significant && (i - (size/2 + offset)) > 2) {
439  cpl_msg_debug(__func__, "Faulty interpolation of right-hand edge in "
440  "IFU %hhu: i=%d (start %d), *aRight=%f (%f..%f > %f > "
441  "%f)", aIFU, i, size/2 + offset, *aRight, ydata[i-2],
442  ydata[i-1], detlimit, ydata[i]);
443  return -11;
444  } /* if real problem */
445  continue;
446  } /* if large offset */
447  break;
448  } /* if data below limit */
449  } /* for i (right-hand part of slice) */
450  /* we arrived at upper limit of vector, so we didn't find the right edge! */
451  if (i == size) {
452  return -1;
453  }
454 
455  /* start again at the center and search outwards for the left-hand edge */
456  for (i = size/2 - offset; i >= 0; i--) {
457  if (ydata[i] < detlimit) {
458  /* again, use linear interpolation to find the exact position */
459  *aLeft = i + (detlimit - ydata[i]) / (ydata[i+1] - ydata[i]);
460 #if 0
461  cpl_msg_debug(__func__, "l: %d..._%d_, %f/_%f_ ===> %f", i+1, i,
462  ydata[i+1], ydata[i], *aLeft);
463 #endif
464  if (fabs(*aLeft - i) > 1.) {
465  if (significant && (size/2 - offset - i) > 2) {
466  /* this would be a real problem, should never happen! */
467  cpl_msg_debug(__func__, "Faulty interpolation of left-hand edge in "
468  "IFU %hhu: i=%d (start %d), *aLeft=%f (%f < %f < %f..%f"
469  ")", aIFU, i, size/2 - offset, *aLeft, ydata[i],
470  detlimit, ydata[i+1], ydata[i+2]);
471  return -12;
472  } /* if real problem */
473  continue;
474  } /* if large offset */
475  break;
476  } /* if data below limit */
477  } /* for i (left-hand part of slice) */
478  /* we arrived at lower limit of vector, so we didn't find the left edge! */
479  if (i == -1) {
480  return -2;
481  }
482 
483  /* If we need to iterate, do so with an offset from the center that is 1 *
484  * pixel larger than the smaller of the two distances that we found last. */
485  int offsetold = offset;
486  if ((size/2 - *aLeft) > (*aRight - size/2)) {
487  offset = *aRight - size/2 + 2;
488  } else {
489  offset = size/2 - *aLeft + 2;
490  }
491  if (offset <= offsetold) {
492  /* Ensure that the next iteration starts at least one *
493  * pixel further, otherwise we might not stop iterating! */
494  offset++;
495  }
496  if (offset > size/2) {
497  /* if the offset is already half the extent of the buffer, *
498  * we won't find the edge(s) any more... */
499  break;
500  }
501  } while ((*aRight - *aLeft + 1) < kMuseSliceLoLikelyWidth || (offset > size/2));
502 
503 #if 0
504  cpl_msg_debug(__func__, "result in IFU %hhu: %f %f --> %f", aIFU, *aLeft,
505  *aRight, (*aLeft + *aRight)/2.);
506 #endif
507  return (*aLeft + *aRight)/2.;
508 } /* muse_trace_edgefinder() */
509 
510 /*---------------------------------------------------------------------------*/
539 /*---------------------------------------------------------------------------*/
540 static double
541 muse_trace_refine_edge(cpl_vector *aDiffVec, double *aLeft, double *aRight,
542  int aOffset, double aY, const unsigned short aSlice,
543  const unsigned char aIFU)
544 {
545 #define TRACE_REFINE_MAX_SHIFT 0.25 /* more than 1/4 pix shift is too much */
546 #define TRACE_REFINE_RANGE 5
547  int size = cpl_vector_get_size(aDiffVec);
548  cpl_ensure(size > 5, CPL_ERROR_ILLEGAL_INPUT, -3);
549  cpl_ensure(aLeft && aRight, CPL_ERROR_NULL_INPUT, -5);
550  cpl_ensure(*aLeft > 0 && *aLeft < size &&
551  *aRight > 0 && *aRight < size && *aRight > *aLeft,
552  CPL_ERROR_ILLEGAL_INPUT, -6);
553 
554  /* keep initial values around */
555  double left = *aLeft,
556  right = *aRight,
557  mid = (*aLeft + *aRight) / 2.;
558 
559  /* create two vectors for the actual fitting of both edges, *
560  * plus two vectors that contain coordinates for the fit */
561  int nel = 2 * TRACE_REFINE_RANGE + 1; /* number of elements in vectors to fit */
562  cpl_vector *vl = cpl_vector_new(nel), /* diff-data vectors */
563  *vr = cpl_vector_new(nel),
564  *pl = cpl_vector_new(nel), /* position vectors */
565  *pr = cpl_vector_new(nel);
566  /* extraction offsets so that the peaks are in the middle of the vectors */
567  int loffset = (int)(left + 0.5)- TRACE_REFINE_RANGE + 1,
568  roffset = (int)(right + 0.5) - TRACE_REFINE_RANGE + 1;
569 #if 0
570  cpl_msg_debug(__func__, "input: %f/%f -> %d/%d",
571  left, right, loffset, roffset);
572 #endif
573  double *diff = cpl_vector_get_data(aDiffVec);
574  int i;
575  for (i = 0; i < nel; i++ ) {
576  double d = diff[i + loffset - 1];
577 #if 0
578  cpl_msg_debug(__func__, "l i=%d / %d: %f", i, i + loffset, d);
579 #endif
580  cpl_vector_set(pl, i, i + loffset - 1);
581  /* cut off negative values */
582  cpl_vector_set(vl, i, d > 0 ? d : 0);
583  } /* for i (around left start value) */
584  for (i = 0; i < nel; i++ ) {
585  /* need the inverse value for the right-hand edge */
586  double d = -diff[i + roffset - 1];
587 #if 0
588  cpl_msg_debug(__func__, "r i=%d / %d: %f", i, i + roffset, d);
589 #endif
590  cpl_vector_set(pr, i, i + roffset - 1);
591  /* cut off negative values */
592  cpl_vector_set(vr, i, d > 0 ? d : 0);
593  } /* for i (around right start value) */
594 
595  /* get state to possibly reset it */
596  cpl_errorstate state = cpl_errorstate_get();
597  /* background level of zero is a very good estimate on this *
598  * difference data, really no need to fit that parameter */
599  double center, sigma, area, bglevel = 0, mse;
600  cpl_fit_mode fitmode = CPL_FIT_CENTROID | CPL_FIT_STDEV | CPL_FIT_AREA;
601  cpl_error_code rc1 = cpl_vector_fit_gaussian(pl, NULL, vl, NULL, fitmode,
602  &center, &sigma, &area, &bglevel,
603  &mse, NULL, NULL);
604  /* center needs to be corrected for shift introduced by shift-diff procedure */
605  center -= 0.5;
606  if (rc1 == CPL_ERROR_CONTINUE || rc1 == CPL_ERROR_SINGULAR_MATRIX) {
607  /* fit returned error, but parameters are supposed to be valid; use them, *
608  * if they don't differ too much from the input, and reset the error */
609  if (fabs(center - *aLeft) < TRACE_REFINE_MAX_SHIFT) {
610  *aLeft = center;
611  }
612  }
613  if (rc1 != CPL_ERROR_NONE) {
614  /* fit failed, keep the original value */
615  cpl_errorstate_set(state);
616  } else { /* successful, use the center of the fit */
617  *aLeft = center;
618  }
619 #if 0
620  cpl_msg_debug(__func__, "fit l: %f %f %f (%f)", center, sigma, area, sqrt(mse));
621 #endif
622  cpl_error_code rc2 = cpl_vector_fit_gaussian(pr, NULL, vr, NULL, fitmode,
623  &center, &sigma, &area, &bglevel,
624  &mse, NULL, NULL);
625  center -= 0.5;
626  if (rc2 == CPL_ERROR_CONTINUE || rc2 == CPL_ERROR_SINGULAR_MATRIX) {
627  /* fit returned error, but parameters are supposed to be valid; use them, *
628  * if they don't differ too much from the input, and reset the error */
629  if (fabs(center - *aRight) < TRACE_REFINE_MAX_SHIFT) {
630  *aRight = center;
631  }
632  }
633  if (rc2 != CPL_ERROR_NONE) {
634  /* fit failed, keep the original value */
635  cpl_errorstate_set(state);
636  } else { /* successful, use the center of the fit */
637  *aRight = center;
638  }
639 #if 0
640  cpl_msg_debug(__func__, "fit r: %f %f %f (%f)", center, sigma, area, sqrt(mse));
641 #endif
642 
643 #if 0
644  cpl_vector_dump(aDiffVec, stdout);
645  printf("left\n");
646  cpl_bivector *biv = cpl_bivector_wrap_vectors(pl, vl);
647  cpl_bivector_dump(biv, stdout);
648  cpl_bivector_unwrap_vectors(biv);
649  printf("right\n");
650  biv = cpl_bivector_wrap_vectors(pr, vr);
651  cpl_bivector_dump(biv, stdout);
652  cpl_bivector_unwrap_vectors(biv);
653  fflush(stdout);
654 #endif
655  cpl_vector_delete(vl);
656  cpl_vector_delete(vr);
657  cpl_vector_delete(pl);
658  cpl_vector_delete(pr);
659 
660  double midpoint = (*aLeft + *aRight)/2.;
661 #if 0
662  cpl_msg_debug(__func__, "refine: %f %f %f %f %f %f",
663  *aLeft, midpoint, *aRight,
664  left - *aLeft, mid - midpoint, right - *aRight);
665 #endif
666  /* skip points where measured centers don't agree; one could also *
667  * check the edges but for those the shifts change with edgefrac */
668  if (fabs(mid - midpoint) > TRACE_REFINE_MAX_SHIFT) {
669  cpl_msg_debug(__func__, "large refined shift around y=%.1f in slice %hu of "
670  "IFU %hhu: %f %f %f (%f %f %f) trace point will not be used",
671  aY, aSlice, aIFU, left + aOffset, midpoint + aOffset,
672  right + aOffset, left - *aLeft, mid - midpoint, right - *aRight);
673  midpoint = -1.; /* make it fail */
674  } else { /* success, add the offset */
675  midpoint += aOffset;
676  }
677  /* add the offset to left and right in any case */
678  *aLeft += aOffset;
679  *aRight += aOffset;
680  return midpoint;
681 } /* muse_trace_refine_edge() */
682 
683 /*---------------------------------------------------------------------------*/
713 /*---------------------------------------------------------------------------*/
714 static cpl_polynomial **
715 muse_trace_iterate_fit(cpl_matrix *aX, cpl_vector *aY, cpl_vector *aWidths,
716  const unsigned short aSlice, const unsigned char aIFU,
717  const unsigned int aFitorder, const float aWSigma,
718  const float aRSigma, cpl_vector *aMSE)
719 {
720  cpl_ensure(cpl_vector_get_size(aWidths) >= 3, CPL_ERROR_ILLEGAL_INPUT, NULL);
721  /* not a critical argument but silences the build warning without debug output */
722  cpl_ensure(aSlice >= 1 && aSlice <= kMuseSlicesPerCCD,
723  CPL_ERROR_ILLEGAL_INPUT, NULL);
724 
725  /* throw away trace points with wildly different widths */
726  double wmean = cpl_vector_get_mean(aWidths),
727  wmedian = cpl_vector_get_median_const(aWidths),
728  wstdev = cpl_vector_get_stdev(aWidths),
729  wmdev = muse_cplvector_get_adev_const(aWidths, wmedian);
730 #if 0
731  cpl_msg_debug(__func__, "width (1st): mean %6.3f +/- %5.3f, median %6.3f +/- "
732  "%5.3f (%"CPL_SIZE_FORMAT" points)", wmean, wstdev, wmedian, wmdev,
733  cpl_vector_get_size(aWidths));
734 #endif
735  if ((wmean - wstdev < kMuseSliceLoLikelyWidth ||
736  wmedian - wmdev < kMuseSliceLoLikelyWidth) &&
737  (wmean < kMuseSliceLoLikelyWidth || wmedian < kMuseSliceLoLikelyWidth)) {
738  cpl_msg_debug(__func__, "slice %hu of IFU %hhu seems to be very narrow "
739  "initially (widths: mean %6.3f +/- %5.3f, median %6.3f +/- "
740  "%5.3f)!", aSlice, aIFU, wmean, wstdev, wmedian, wmdev);
741  }
742  if ((wmean + wstdev > kMuseSliceHiLikelyWidth ||
743  wmedian + wmdev > kMuseSliceHiLikelyWidth) &&
744  (wmean > kMuseSliceHiLikelyWidth || wmedian > kMuseSliceHiLikelyWidth)) {
745  cpl_msg_debug(__func__, "slice %hu of IFU %hhu seems to be very wide "
746  "initially (widths: mean %6.3f +/- %5.3f, median %6.3f +/- "
747  "%5.3f)!", aSlice, aIFU, wmean, wstdev, wmedian, wmdev);
748  }
749 
750  /* loop through all trace points and remove grossly deviant points *
751  * where the slice is far too narrow or far too wide to be real */
752  int i;
753  for (i = 0; i < cpl_vector_get_size(aWidths); i++) {
754  double width = cpl_vector_get(aWidths, i);
755  if (width > kMuseSliceLoLikelyWidth && width < kMuseSliceHiLikelyWidth) {
756  /* good widths */
757  continue;
758  }
759  /* guard against removing the last element */
760  if (cpl_vector_get_size(aWidths) == 1) {
761  cpl_msg_warning(__func__, "trying to remove the last vector/matrix "
762  "element in slice %hu of IFU %hhu when checking widths",
763  aSlice, aIFU);
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  wmean = cpl_vector_get_mean(aWidths);
774  wmedian = cpl_vector_get_median_const(aWidths);
775  wstdev = cpl_vector_get_stdev(aWidths);
776  wmdev = muse_cplvector_get_adev_const(aWidths, wmedian);
777 #if 0
778  cpl_msg_debug(__func__, "width (2nd): mean %6.3f+/-%5.3f, median %6.3f+/-%5.3f (%d points)",
779  wmean, wstdev, wmedian, wmdev, cpl_vector_get_size(aWidths));
780 #endif
781  if ((wmean - wstdev < kMuseSliceLoLikelyWidth ||
782  wmedian - wmdev < kMuseSliceLoLikelyWidth) &&
783  (wmean < kMuseSliceLoLikelyWidth || wmedian < kMuseSliceLoLikelyWidth)) {
784  cpl_msg_warning(__func__, "slice %hu of IFU %hhu seems to be very narrow "
785  "after iteration (widths: mean %6.3f +/- %5.3f, median %6.3f"
786  " +/- %5.3f)!", aSlice, aIFU, wmean, wstdev, wmedian, wmdev);
787  }
788  if ((wmean + wstdev > kMuseSliceHiLikelyWidth ||
789  wmedian + wmdev > kMuseSliceHiLikelyWidth) &&
790  (wmean > kMuseSliceHiLikelyWidth || wmedian > kMuseSliceHiLikelyWidth)) {
791  cpl_msg_warning(__func__, "slice %hu of IFU %hhu seems to be very wide "
792  "after iteration (widths: mean %6.3f +/- %5.3f, median %6.3f"
793  " +/- %5.3f)!", aSlice, aIFU, wmean, wstdev, wmedian, wmdev);
794  }
795 
796  /* again loop through all trace points to now reject based on sigma */
797  for (i = 0; i < cpl_vector_get_size(aWidths); i++) {
798  double width = cpl_vector_get(aWidths, i);
799 #if 0
800  cpl_msg_debug(__func__, "i=%d: %f <? %f <? %f", i,
801  wmedian - aWSigma * wmdev, width, wmedian + aWSigma * wmdev);
802 #endif
803  if (width > (wmedian - aWSigma * wmdev) &&
804  width < (wmedian + aWSigma * wmdev)) {
805  /* good widths */
806  continue;
807  }
808  /* guard against removing the last element */
809  if (cpl_vector_get_size(aWidths) == 1) {
810  cpl_msg_warning(__func__, "trying to remove the last vector/matrix "
811  "element in slice %hu of IFU %hhu when checking fit "
812  "sigma", aSlice, aIFU);
813  break;
814  }
815  /* bad width, remove element */
816  cpl_matrix_erase_columns(aX, i, 1);
818  muse_cplvector_erase_element(aWidths, i);
819  i--; /* we stay at this position to see what moved here */
820  } /* for i */
821 
822  /* create table column from vector */
823  cpl_table *wtable = cpl_table_new(cpl_vector_get_size(aWidths));
824  cpl_table_new_column(wtable, "widths", CPL_TYPE_DOUBLE);
825  memcpy(cpl_table_get_data_double(wtable, "widths"),
826  cpl_vector_get_data(aWidths), cpl_vector_get_size(aWidths));
827  double mse, chisq;
828  cpl_polynomial *tracefit = muse_utils_iterate_fit_polynomial(aX, aY, NULL, wtable,
829  aFitorder, aRSigma,
830  &mse, &chisq);
831  /* adapt length of input vector, copy output widths back into it */
832  cpl_vector_set_size(aWidths, cpl_vector_get_size(aY));
833  memcpy(cpl_vector_get_data(aWidths), cpl_table_get_data_double(wtable, "widths"),
834  cpl_vector_get_size(aWidths));
835  cpl_table_delete(wtable);
836  if (!tracefit) { /* if the fit didn't work then something went wrong */
837  cpl_vector_fill(aMSE, FLT_MAX);
838  return NULL;
839  }
840  /* also save in input vector */
841  cpl_vector_set(aMSE, MUSE_TRACE_CENTER, mse);
842 
843  char *dodebug = getenv("MUSE_DEBUG_TRACE");
844  if (dodebug && atoi(dodebug) > 0) {
845  printf("Polynomial trace fit for slice %hu of IFU %hhu (mse=%g, "
846  "chi**2=%g):\n", aSlice, aIFU, mse, chisq);
847  cpl_polynomial_dump(tracefit, stdout);
848  fflush(stdout);
849  }
850 
851  /* Now we have a polynomial defining the center and the array of widths. *
852  * Convert this into separate matrices for left and right edges and redo *
853  * the polynomial fits for both edges. */
854  cpl_vector *edge[MUSE_TRACE_NPOLY - 1] = {
855  cpl_vector_new(cpl_vector_get_size(aY)),
856  cpl_vector_new(cpl_vector_get_size(aY))
857  };
858  for (i = 0; i < cpl_vector_get_size(aWidths); i++) {
859  double x = cpl_vector_get(aY, i),
860  halfwidth = cpl_vector_get(aWidths, i) / 2.;
861 #if 0
862  cpl_msg_debug(__func__, "x=%f (%f...%f)", x, x - halfwidth, x + halfwidth);
863 #endif
864  cpl_vector_set(edge[MUSE_TRACE_LEFT - 1], i, x - halfwidth);
865  cpl_vector_set(edge[MUSE_TRACE_RIGHT - 1], i, x + halfwidth);
866  } /* for i */
867 #if 0
868  printf("left:\n");
869  cpl_vector_dump(edge[0], stdout);
870  printf("right:\n");
871  cpl_vector_dump(edge[1], stdout);
872  fflush(stdout);
873 #endif
874 
875  cpl_polynomial **fit = cpl_calloc(MUSE_TRACE_NPOLY, sizeof(cpl_polynomial *));
876  fit[MUSE_TRACE_CENTER] = tracefit;
877 
878  int ipoly;
879  for (ipoly = 1; ipoly < MUSE_TRACE_NPOLY; ipoly++) { /* from 1, skip center */
880  /* fit again, but do not iterate, i.e. use a very high rejection sigma */
881  fit[ipoly] = muse_utils_iterate_fit_polynomial(aX, edge[ipoly - 1], NULL,
882  NULL, aFitorder, FLT_MAX,
883  &mse, &chisq);
884  cpl_vector_set(aMSE, ipoly, mse);
885  /* we are now done with the extra fit input data, too */
886  cpl_vector_delete(edge[ipoly - 1]);
887  } /* for ipoly */
888 
889 #if 0
890  printf("resulting polynomials (center, left, and right):\n");
891  cpl_polynomial_dump(fit[MUSE_TRACE_CENTER], stdout);
892  cpl_polynomial_dump(fit[MUSE_TRACE_LEFT], stdout);
893  cpl_polynomial_dump(fit[MUSE_TRACE_RIGHT], stdout);
894  printf("MSEs:\n");
895  cpl_vector_dump(aMSE, stdout);
896  fflush(stdout);
897 #endif
898 
899  return fit;
900 } /* muse_trace_iterate_fit() */
901 
902 /*----------------------------------------------------------------------------*/
906 /*----------------------------------------------------------------------------*/
908  { "slice", CPL_TYPE_INT, "", "%02d", "slice number", CPL_TRUE},
909  { "y", CPL_TYPE_FLOAT, "pix", "%6.1f", "y position on CCD", CPL_TRUE},
910  { "mid", CPL_TYPE_FLOAT, "pix", "%8.3f",
911  "midpoint of the slice at this y position", CPL_TRUE},
912  { "left", CPL_TYPE_FLOAT, "pix", "%8.3f",
913  "left edge of the slice at this y position", CPL_TRUE},
914  { "right", CPL_TYPE_FLOAT, "pix", "%8.3f",
915  "right edge of the slice at this y position", CPL_TRUE},
916  { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
917 };
918 
919 /*---------------------------------------------------------------------------*/
965 /*---------------------------------------------------------------------------*/
966 cpl_table *
967 muse_trace(const muse_image *aImage, int aNSum, double aEdgeFrac, int aFitorder,
968  cpl_table **aSamples)
969 {
970  cpl_ensure(aImage && aImage->data, CPL_ERROR_NULL_INPUT, NULL);
971  cpl_ensure(aNSum > 0 && aEdgeFrac > 0. && aEdgeFrac < 1. && aFitorder > 0,
972  CPL_ERROR_ILLEGAL_INPUT, NULL);
973 
974  /* count number of tracepoints */
975  int ny = cpl_image_get_size_y(aImage->data),
976  npoints = (ny - 1) / aNSum;
977  unsigned short nsearchslices = kMuseSlicesPerCCD;
978  cpl_boolean slice_number_hack = getenv("MUSE_AIT_HACK_SLICE_NUMBER")
979  && atoi(getenv("MUSE_AIT_HACK_SLICE_NUMBER")) > 0
980  && atoi(getenv("MUSE_AIT_HACK_SLICE_NUMBER")) < 49;
981  unsigned char ifu = muse_utils_get_ifu(aImage->header);
982  if (slice_number_hack) {
983  nsearchslices = atoi(getenv("MUSE_AIT_HACK_SLICE_NUMBER"));
984  cpl_msg_warning(__func__, "Overriding number of slices to search in IFU "
985  "%hhu to %hu!", ifu, nsearchslices);
986  }
987  cpl_msg_info(__func__, "Working with %hu slices, %d image rows, and %d "
988  "tracepoints in IFU %hhu", nsearchslices, ny, npoints, ifu);
989 
990  /* check if this is IFU 24 and data taken since it was installed at Paranal *
991  * in January 2014, because that has the strange vignetting in channel 24 */
992  cpl_boolean chan24paranal = (ifu == 24);
993  const char *dateobs = muse_pfits_get_dateobs(aImage->header);
994  if (chan24paranal && dateobs) { /* it is IFU 24 and we do have a DATE-OBS */
995  chan24paranal = atoi(dateobs) >= 2014;
996  }
997  if (chan24paranal) {
998  cpl_msg_info(__func__, "Using overrides for IFU 24 since 2014: due to field"
999  " vignetting, left-hand edges (as seen on the CCD) of slices "
1000  "37 to 48 may be only approximate!");
1001  }
1002 
1003  /* duplicate input image to be able to interpolate bad pixels */
1004  muse_image *image = muse_image_new();
1005  image->data = cpl_image_duplicate(aImage->data);
1006  if (aImage->dq) {
1007  image->dq = cpl_image_duplicate(aImage->dq);
1008  } else {
1009  image->dq = cpl_image_new(cpl_image_get_size_x(aImage->data), ny, CPL_TYPE_INT);
1010  }
1011  if (aImage->header) { /* header is used by muse_trace_horizontal_cut() */
1012  image->header = cpl_propertylist_duplicate(aImage->header);
1013  }
1014  /* stat and header are not needed here */
1016  cpl_detector_interpolate_rejected(image->data);
1017 
1018  /* get starting guesses for the midpoint of each slice */
1019 #define NROWCOLLAPSE 15
1020  cpl_vector *cut = muse_trace_horizontal_cut(image, NROWCOLLAPSE);
1021  cpl_vector *centers = muse_trace_locate_slices(cut, nsearchslices, aEdgeFrac,
1022  ifu);
1023  cpl_vector_delete(cut);
1024  if (!centers) {
1025  cpl_msg_error(__func__, "Could not carry out first guess of slice positions "
1026  "in IFU %hhu!", ifu);
1027  muse_image_delete(image);
1028  return NULL;
1029  }
1030 
1031  /* create the output table */
1032  cpl_table *tracetable = cpl_table_new(kMuseSlicesPerCCD);
1033  if (!tracetable) {
1034  cpl_msg_error(__func__, "Could not create output trace table for IFU %hhu: "
1035  "%s", ifu, cpl_error_get_message());
1036  muse_image_delete(image);
1037  return NULL;
1038  }
1039 
1040  /* prepare output table */
1041  cpl_table_new_column(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, CPL_TYPE_INT);
1042  cpl_table_set_column_unit(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, "No");
1043  cpl_table_set_column_format(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, "%2d");
1044  cpl_table_new_column(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, CPL_TYPE_FLOAT);
1045  cpl_table_set_column_unit(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, "pix");
1046  cpl_table_set_column_format(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, "%6.3f");
1047  int ipoly;
1048  for (ipoly = 0; ipoly < MUSE_TRACE_NPOLY; ipoly++) {
1049  char *colname;
1050  int j;
1051  for (j = 0; j <= aFitorder; j++) {
1052  /* create column name, start coefficient names at 0 */
1053  colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_COEFF, ipoly, j);
1054  cpl_table_new_column(tracetable, colname, CPL_TYPE_DOUBLE);
1055  /* fit coeff are in pixel space */
1056  cpl_table_set_column_unit(tracetable, colname, "pix");
1057  cpl_table_set_column_format(tracetable, colname, "%12.5e");
1058  cpl_free(colname);
1059  }
1060  colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_MSE, ipoly);
1061  cpl_table_new_column(tracetable, colname, CPL_TYPE_DOUBLE);
1062  cpl_table_set_column_unit(tracetable, colname, "pix");
1063  cpl_table_set_column_format(tracetable, colname, "%12.5e");
1064  cpl_free(colname);
1065  } /* for ipoly */
1066 
1067  int isamplesrow = -1; /* track the current row in the samples table */
1068  if (aSamples) {
1069  /* create table of the sampled points for debugging */
1070  *aSamples = muse_cpltable_new(muse_tracesamples_def,
1071  kMuseSlicesPerCCD * npoints);
1072  }
1073 
1074  /* create some kind of image derivative by subtracting a 1 pix *
1075  * shifted version of the input image from the input image */
1076  cpl_image *shiftdiff = cpl_image_duplicate(image->data);
1077  cpl_image_shift(shiftdiff, 1, 0);
1078  /* subtract from original, store in new one */
1079  cpl_image_multiply_scalar(shiftdiff, -1);
1080  cpl_image_add(shiftdiff, image->data);
1081 
1082  /* islice loops over all slices, j over all rows, k over all tracepoints */
1083  unsigned short islice;
1084  for (islice = 0; islice < kMuseSlicesPerCCD; islice++) {
1085  int nfailed = 0; /* count the number of failed tracepoints in this slice */
1086  cpl_matrix *xtrace = cpl_matrix_new(1, npoints);
1087  cpl_vector *ytrace = cpl_vector_new(npoints);
1088  /* track slice width for statistics */
1089  cpl_vector *widths = cpl_vector_new(npoints);
1090 
1091  /* loops over vertical points (j) and tracepoints (k), resp. */
1092  int j, k, knum; /* the latter is just a counter for dianostic output */
1093  for (j = 1, k = 0, knum = 1; j <= ny - aNSum; j += aNSum, k++, knum++) {
1094  /* extract slice section and collapse to 1d cut */
1095  /* (see muse_tracing.h for the definition of TRACE_BINSIZE) */
1096  int noffset = (int)cpl_vector_get(centers, islice) - TRACE_BINSIZE,
1097  ilo = noffset,
1098  ihi = (int)cpl_vector_get(centers, islice) + TRACE_BINSIZE,
1099  jlo = j,
1100  jhi = j + aNSum - 1;
1101 #if 0
1102  cpl_msg_debug(__func__, "slice=%d, center=%f, cut region: %d,%d,%d,%d",
1103  (int)islice + 1, cpl_vector_get(centers, islice), ilo, ihi, jlo, jhi);
1104 #endif
1105  cpl_image *tmp = cpl_image_collapse_window_create(image->data,
1106  ilo, jlo, ihi, jhi,
1107  0); /* collapse vertically */
1108  cpl_image_divide_scalar(tmp, aNSum);
1109  cut = cpl_vector_new_from_image_row(tmp, 1);
1110  cpl_image_delete(tmp);
1111 
1112  /* find midpoint and edges of the cut */
1113  double left, right;
1114  cpl_boolean highSN = CPL_TRUE;
1115  double midpoint = muse_trace_edgefinder(cut, aEdgeFrac, &left, &right,
1116  &highSN, ifu);
1117  cpl_vector_delete(cut);
1118  /* keep left edge of the standard edge detection, in *
1119  * case we are dealing with the slices 37 to 48 in IFU 24 */
1120  double left1 = left + noffset;
1121 
1122  cpl_errorstate state = cpl_errorstate_get();
1123  /* refine edge positions again using difference image *
1124  * and Gaussian fits to the edges */
1125  tmp = cpl_image_collapse_window_create(shiftdiff, ilo, jlo, ihi, jhi,
1126  0); /* collapse along rows */
1127  cpl_image_divide_scalar(tmp, aNSum);
1128  cut = cpl_vector_new_from_image_row(tmp, 1);
1129  cpl_image_delete(tmp);
1130  midpoint = muse_trace_refine_edge(cut, &left, &right, noffset,
1131  (jlo + jhi) / 2., islice + 1, ifu);
1132  cpl_vector_delete(cut);
1133 
1134  /* slices 37 to 48 on the CCD have rounded left edge for IFU 24 */
1135  if (midpoint < 0 && midpoint > -2 && islice+1 >= 37 && chan24paranal) {
1136  cpl_msg_debug(__func__, "IFU24 problem? slice %d, y = %f: refined "
1137  "%f < %f < %f", islice+1, ((double)jlo + jhi) / 2.,
1138  left, midpoint, right);
1139  if (left1 < left) {
1140  /* If the edge detection was left of the Gaussian center, then that *
1141  * fit was affected by the rounded "edge" and needs to be reset. */
1142  left = left1;
1143  cpl_msg_debug(__func__, "IFU24 problem! slice %d y = %f: corrected "
1144  "%f < %f < %f", islice+1, ((double)jlo + jhi) / 2.,
1145  left, (left + right) / 2., right);
1146  }
1147  /* the refined value for the right edge should still be fine, keep it */
1148  midpoint = (left + right) / 2.;
1149  } /* if slice 37-48 problem */
1150 
1151  if (midpoint > 0) { /* edge searching was successful */
1152  cpl_matrix_set(xtrace, 0, k, (jlo + jhi) / 2.);
1153  cpl_vector_set(ytrace, k, midpoint);
1154  cpl_vector_set(widths, k, right - left);
1155 
1156  if (aSamples) {
1157  /* save the points for plotting/debugging */
1158  if (++isamplesrow+1 > cpl_table_get_nrow(*aSamples)) {
1159  cpl_table_set_size(*aSamples, isamplesrow+1);
1160  }
1161  cpl_table_set_int(*aSamples, "slice", isamplesrow, islice + 1);
1162  cpl_table_set_float(*aSamples, "y", isamplesrow, (jlo + jhi) / 2.);
1163  cpl_table_set_float(*aSamples, "mid", isamplesrow, midpoint);
1164  cpl_table_set_float(*aSamples, "left", isamplesrow, left);
1165  cpl_table_set_float(*aSamples, "right", isamplesrow, right);
1166  }
1167 
1168  /* we were successful, so we can go to the next trace point */
1169  continue;
1170  }
1171 
1172  /* Error handling for the failure case follows. Only output warning, *
1173  * if many tracepoints (> 10%) are lost in the current slice. Only *
1174  * count failures with significant flux levels (high S/N). */
1175  cpl_errorstate_set(state);
1176  if (highSN) {
1177  nfailed++;
1178  }
1179 #if 0
1180  cpl_msg_debug(__func__, "slice=%d, nfailed=%d, tracepoint=%d, midpoint="
1181  "%f, y=%d", (int)islice + 1, nfailed, knum, midpoint, j);
1182 #endif
1183  if (nfailed > 0.1*npoints && midpoint == -1.) {
1184  cpl_msg_warning(__func__, "failure %d in slice %d of IFU %hhu: lost "
1185  "trace at y=%d (tracepoint %d of %d)", nfailed,
1186  (int)islice + 1, ifu, j, knum, npoints);
1187  }
1188 
1189  /* resize vector and matrix, so that they don't include the failure */
1190  int oldsize = cpl_vector_get_size(ytrace); /* should all have same size */
1191  cpl_vector_set_size(ytrace, oldsize - 1);
1192  cpl_matrix_resize(xtrace, 0, 0, 0, -1);
1193  cpl_vector_set_size(widths, oldsize - 1);
1194  k--; /* set current matrix/vector index backwards */
1195  } /* for j */
1196 #if 0
1197  /* compare number of tracepoints to points in vectors, *
1198  * 1 was already added to k (which started at 0) */
1199  printf("k=%d tracepoints (should be equal to %d)\n", k, npoints);
1200  cpl_matrix_dump(xtrace, stdout), fflush(stdout);
1201  cpl_vector_dump(ytrace, stdout), fflush(stdout);
1202 #endif
1203 
1204  /* iterate through input points and the fit with XXX 5 sigma rejections */
1205  const float kWSigma = 5, kRSigma = 5;
1206  cpl_msg_debug(__func__, "Working on slice %d of IFU %hhu (kWSigma=%f, "
1207  "kRSigma=%f)", (int)islice + 1, ifu, kWSigma, kRSigma);
1208  cpl_vector *mse = cpl_vector_new(MUSE_TRACE_NPOLY);
1209  cpl_vector_fill(mse, -1.);
1210  cpl_polynomial **tracefits = muse_trace_iterate_fit(xtrace, ytrace, widths,
1211  islice + 1, ifu, aFitorder,
1212  kWSigma, kRSigma, mse);
1213  /* use the final mean width for the trace solution */
1214  double wmean = cpl_vector_get_mean(widths);
1215 
1216  /* reset status after fitting procedures and clean up */
1217  cpl_matrix_delete(xtrace);
1218  cpl_vector_delete(ytrace);
1219  cpl_vector_delete(widths);
1220 
1221  if (!tracefits) {
1222  /* we need to print an error message and skip the table entries */
1223  cpl_msg_error(__func__, "The trace fit in slice %d of IFU %hhu failed",
1224  (int)islice + 1, ifu);
1225  cpl_vector_delete(mse);
1226  continue;
1227  }
1228 
1229  /* row numbers start at 1 not 0 */
1230  cpl_table_set_int(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, islice,
1231  islice + 1);
1232  cpl_table_set_float(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, islice, wmean);
1233  for (ipoly = 0; ipoly < MUSE_TRACE_NPOLY; ipoly++) {
1234  if (!tracefits[ipoly]) {
1235  cpl_msg_error(__func__, "The fit %d in slice %d of IFU %hhu failed",
1236  ipoly, (int)islice + 1, ifu);
1237  continue;
1238  }
1239  char *colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_MSE, ipoly);
1240  cpl_table_set_double(tracetable, colname, islice,
1241  cpl_vector_get(mse, ipoly));
1242  cpl_free(colname);
1243  /* j loops over all orders of the polynomial */
1244  for (j = 0; j <= aFitorder; j++) {
1245  cpl_size pows[1] = { j }; /* trick to access the polynomial */
1246 
1247  colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_COEFF, ipoly, j);
1248  cpl_errorstate prestate = cpl_errorstate_get();
1249  double coeff = cpl_polynomial_get_coeff(tracefits[ipoly], pows);
1250 #define SLOPE_WARN_LIMIT 0.015
1251  if (j == 1 && fabs(coeff) > SLOPE_WARN_LIMIT) {
1252  cpl_msg_warning(__func__, "1st order coefficient of the %s tracing "
1253  "polynomial is unexpectedly large in slice %d of IFU"
1254  " %hhu: |%f| > %f", muse_trace_poly_strings[ipoly],
1255  (int)islice + 1, ifu, coeff, SLOPE_WARN_LIMIT);
1256  }
1257  cpl_table_set_double(tracetable, colname, islice, coeff);
1258  if (!cpl_errorstate_is_equal(prestate)) {
1259  cpl_msg_warning(__func__, "Problem writing to field %s in trace table"
1260  " for IFU %hhu: %s", colname, ifu,
1261  cpl_error_get_message());
1262  }
1263  cpl_free(colname);
1264  } /* for j (all polynomial orders) */
1265  } /* for ipoly */
1266  cpl_vector_delete(mse);
1267  muse_trace_polys_delete(tracefits);
1268  } /* for islice */
1269  cpl_vector_delete(centers);
1270  cpl_image_delete(shiftdiff);
1271  if (aSamples) {
1272  /* cut samples table to the actually used number of rows */
1273  cpl_table_set_size(*aSamples, ++isamplesrow);
1274  }
1275 
1276  if (slice_number_hack) {
1277  cpl_msg_warning(__func__, "Will try to fix the slices in IFU %hhu, %"
1278  CPL_SIZE_FORMAT" seem to be bad!", ifu,
1279  cpl_table_count_invalid(tracetable, MUSE_TRACE_TABLE_COL_WIDTH));
1280  cpl_table_dump(tracetable, 0, 100, stdout); /* be sure to output _all_ slices */
1281  fflush(stdout);
1282  /* add fake slices for those that failed */
1283  for (islice = 0; islice < cpl_table_get_nrow(tracetable); islice++) {
1284  double width = cpl_table_get(tracetable, MUSE_TRACE_TABLE_COL_WIDTH, islice, NULL);
1285  if (width < 40.) {
1286  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT, "slice %d of "
1287  "IFU %hhu was narrow (%f), erased it", (int)islice + 1,
1288  ifu, width);
1289  cpl_table_erase_window(tracetable, islice--, 1);
1290  } /* if */
1291  } /* for islice */
1292  double last = 0;
1293  for (islice = 0; islice < cpl_table_get_nrow(tracetable); islice++) {
1294  double cen = cpl_table_get(tracetable, "tc0_00", islice, NULL);
1295  if (cen - last > 100) { /* insert a slice */
1296  unsigned short iref = islice - 1;
1297  cpl_table *row = cpl_table_extract(tracetable, iref, 1);
1298  /* the slice numbering is fixed below */
1299  double offset = 84.5; /* typical offset */
1300  cpl_table_add_scalar(row, MUSE_TRACE_TABLE_COL_SLICE_NO, 1);
1301  cpl_table_add_scalar(row, "tc0_00", offset);
1302  cpl_table_add_scalar(row, "tc1_00", offset);
1303  cpl_table_add_scalar(row, "tc2_00", offset);
1304  cpl_table_add_scalar(row, "MSE0", 1.); /* signify by a large MSE that this is fake */
1305  cpl_table_add_scalar(row, "MSE1", 1.);
1306  cpl_table_add_scalar(row, "MSE2", 1.);
1307  cpl_table_insert(tracetable, row, islice);
1308 #if 0
1309  printf("rowtable (islice=%hu):\n", islice);
1310  cpl_table_dump(row, 0, 100, stdout);
1311  fflush(stdout);
1312  printf("tracetable (islice=%hu):\n", islice);
1313  cpl_table_dump(tracetable, islice - 2, 10, stdout);
1314  fflush(stdout);
1315 #endif
1316  cen = cpl_table_get(row, "tc0_00", 0, NULL); /* new center */
1317  cpl_table_delete(row);
1318  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT, "slice was "
1319  "missing before slice %d of IFU %hhu, copied "
1320  "from slice %d", (int)islice + 1, ifu, (int)iref + 1);
1321  }
1322  last = cen;
1323  } /* for islice */
1324  for (islice = 0; islice < cpl_table_get_nrow(tracetable); islice++) {
1325  unsigned short sliceno = cpl_table_get_int(tracetable,
1326  MUSE_TRACE_TABLE_COL_SLICE_NO,
1327  islice, NULL);
1328  if (sliceno != islice + 1) {
1329  cpl_msg_warning(__func__, "Resetting entry at table row index %hu to "
1330  "correct slice number in IFU %hhu (%d instead of %hu)",
1331  islice, ifu, (int)islice + 1, sliceno);
1332  cpl_table_set_int(tracetable, MUSE_TRACE_TABLE_COL_SLICE_NO, islice,
1333  islice + 1);
1334  }
1335  } /* for islice */
1336  } /* if slice_number_hack */
1337 
1338  /* give the user some useful output to read */
1339  cpl_msg_info(__func__, "Found %"CPL_SIZE_FORMAT" slices of width %4.1f+/-%3.1f"
1340  " pix (%4.1f pix...%4.1f pix) in IFU %hhu",
1341  cpl_table_get_nrow(tracetable),
1342  cpl_table_get_column_mean(tracetable, MUSE_TRACE_TABLE_COL_WIDTH),
1343  cpl_table_get_column_stdev(tracetable, MUSE_TRACE_TABLE_COL_WIDTH),
1344  cpl_table_get_column_min(tracetable, MUSE_TRACE_TABLE_COL_WIDTH),
1345  cpl_table_get_column_max(tracetable, MUSE_TRACE_TABLE_COL_WIDTH),
1346  ifu);
1347  muse_image_delete(image);
1348 
1349  return tracetable;
1350 } /* muse_trace() */
1351 
1352 /*---------------------------------------------------------------------------*/
1361 /*---------------------------------------------------------------------------*/
1362 int
1363 muse_trace_table_get_order(const cpl_table *aTable)
1364 {
1365  /* cpl_table_get_ncol() returns -1 on error so we *
1366  * automatically get a negative value on error here */
1367  /* There are two extra values, the MSEs; the rest of the columns are the *
1368  * coefficients of the MUSE_TRACE_NPOLY polynomials, including zeroth order */
1369  return (cpl_table_get_ncol(aTable) - 2) / MUSE_TRACE_NPOLY - 2;
1370 } /* muse_trace_table_get_order() */
1371 
1372 /*---------------------------------------------------------------------------*/
1391 /*---------------------------------------------------------------------------*/
1392 cpl_polynomial **
1394  const unsigned short aSlice)
1395 {
1396  cpl_ensure(aTable, CPL_ERROR_NULL_INPUT, NULL);
1397  cpl_ensure(aSlice >= 1 && aSlice <= kMuseSlicesPerCCD,
1398  CPL_ERROR_ILLEGAL_INPUT, NULL);
1399  /* search for row containing the requested slice, first to access it *
1400  * in a possibly incomplete table, and second to check its presence! */
1401  int irow, nrow = cpl_table_get_nrow(aTable);
1402  for (irow = 0; irow < nrow; irow++) {
1403  int err;
1404  unsigned short slice = cpl_table_get_int(aTable,
1405  MUSE_TRACE_TABLE_COL_SLICE_NO,
1406  irow, &err);
1407  if (slice == aSlice && !err) {
1408  break;
1409  }
1410  } /* for irow */
1411  cpl_ensure(irow < nrow, CPL_ERROR_DATA_NOT_FOUND, NULL);
1412 
1413  cpl_polynomial **ptrace = cpl_calloc(MUSE_TRACE_NPOLY,
1414  sizeof(cpl_polynomial *));
1415  char colname[7]; /* "tcI_NN" plus null byte */
1416  int ipoly;
1417  for (ipoly = 0; ipoly < MUSE_TRACE_NPOLY; ipoly++) {
1418  int traceorder = muse_trace_table_get_order(aTable);
1419  ptrace[ipoly] = cpl_polynomial_new(1);
1420 
1421  /* fill in the orders of the polynomial */
1422  int k;
1423  for (k = 0; k <= traceorder; k++) {
1424  cpl_size pows[1] = { k }; /* trick to access the polynomial */
1425  sprintf(colname, MUSE_TRACE_TABLE_COL_COEFF, ipoly, k);
1426  int err;
1427  cpl_polynomial_set_coeff(ptrace[ipoly], pows,
1428  cpl_table_get(aTable, colname, irow, &err));
1429  if (err != 0) { /* broken table entry */
1430  cpl_polynomial_delete(ptrace[MUSE_TRACE_CENTER]);
1431  cpl_polynomial_delete(ptrace[MUSE_TRACE_LEFT]);
1432  cpl_polynomial_delete(ptrace[MUSE_TRACE_RIGHT]);
1433  cpl_free(ptrace);
1434  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT, "Trace table "
1435  "broken in slice %hu (row index %d) column %s",
1436  aSlice, irow, colname);
1437  return NULL;
1438  } /* if */
1439  } /* for k */
1440  } /* for ipoly */
1441 
1442  return ptrace;
1443 } /* muse_trace_table_get_polys_for_slice() */
1444 
1445 /*---------------------------------------------------------------------------*/
1450 /*---------------------------------------------------------------------------*/
1451 void
1452 muse_trace_polys_delete(cpl_polynomial *aPolys[])
1453 {
1454  if (!aPolys) {
1455  return;
1456  }
1457  cpl_polynomial_delete(aPolys[MUSE_TRACE_CENTER]);
1458  cpl_polynomial_delete(aPolys[MUSE_TRACE_LEFT]);
1459  cpl_polynomial_delete(aPolys[MUSE_TRACE_RIGHT]);
1460  cpl_free(aPolys);
1461 } /* muse_trace_polys_delete() */
1462 
1463 /* plot the result of muse_trace_locate_slices() */
1464 static void
1465 muse_trace_plot_located_slices(cpl_vector *aRowVec, cpl_vector *aCenters,
1466  double aMedian, double aMDev, double aLimit,
1467  const unsigned char aIFU)
1468 {
1469 #if HAVE_POPEN && HAVE_PCLOSE
1470  FILE *gp = popen("gnuplot -persist", "w");
1471  if (!gp) {
1472  cpl_msg_error(__func__, "could not open gnuplot for plotting");
1473  return;
1474  }
1475 
1476 #if HAVE_MKDTEMP
1477  char dirtemplate[] = "/tmp/muse_trace_plot_located_slices_XXXXXX";
1478  char *dirname = mkdtemp(dirtemplate);
1479  if (!dirname) {
1480  return;
1481  }
1482 #else
1483  char dirname[] = "/tmp";
1484 #endif
1485  char *out1 = cpl_sprintf("%s/row.dat", dirname);
1486  FILE *fp = fopen(out1, "w");
1487  cpl_vector_dump(aRowVec, fp);
1488  fclose(fp);
1489  char *out2 = cpl_sprintf("%s/centers.dat", dirname);
1490  fp = fopen(out2, "w");
1491  cpl_vector_dump(aCenters, fp);
1492  fclose(fp);
1493 
1494  fprintf(gp, "set title \"located slices (IFU %hhu): median %.2f+/-%.2f, limit"
1495  " %.2f\"\nunset key\nset style fill solid 0.5\n", aIFU, aMedian, aMDev,
1496  aLimit);
1497  fprintf(gp, "median(x)=%e\nlimit(x)=%e\nlo(x)=%e\n",
1498  aMedian, aLimit, aMedian - aMDev);
1499  fprintf(gp, "set xrange [%d:%"CPL_SIZE_FORMAT"]\n", 1, cpl_vector_get_size(aRowVec));
1500  fprintf(gp, "set yrange [%e:%e]\n", aLimit - 0.5*aMDev, aMedian + 1.3*aMDev);
1501  fprintf(gp, "plot lo(x) w filledcu y1=%e, "
1502  " median(x) t \"median\", limit(x) t \"limit\" w l lw 2, "
1503  " \"%s\" w l lt 7, \"%s\" u 2:(%e):1 w p lt -1, "
1504  " \"%s\" u 2:(%e):1 w labels\n",
1505  aMedian+aMDev, out1, out2, aMedian, out2, aMedian+200);
1506 
1507  pclose(gp);
1508  remove(out1);
1509  remove(out2);
1510  cpl_free(out1);
1511  cpl_free(out2);
1512 #if HAVE_MKDTEMP && HAVE_UNISTD_H
1513  int rc = rmdir(dirname);
1514  if (rc < 0) {
1515  cpl_msg_warning(__func__, "Used %s for plotting, please clean it manually!",
1516  dirname);
1517  }
1518 #endif
1519 #endif /* HAVE_POPEN && HAVE_PCLOSE */
1520 } /* muse_trace_plot_located_slices() */
1521 
1522 /*---------------------------------------------------------------------------*/
1544 /*---------------------------------------------------------------------------*/
1545 cpl_error_code
1546 muse_trace_plot_samples(cpl_table *aSamples, cpl_table *aTrace,
1547  unsigned short aSlice1, unsigned short aSlice2,
1548  unsigned char aIFU, muse_image *aImage)
1549 {
1550 #if HAVE_POPEN && HAVE_PCLOSE
1551  cpl_ensure_code(aSamples, CPL_ERROR_NULL_INPUT);
1552  cpl_error_code rc = muse_cpltable_check(aSamples, muse_tracesamples_def);
1553  cpl_ensure_code(rc == CPL_ERROR_NONE, rc);
1554  /* if errors occur, only plot the two central slices */
1555  if (aSlice1 < 1 || aSlice1 > kMuseSlicesPerCCD || aSlice1 > aSlice2 ||
1556  aSlice2 < 1 || aSlice2 > kMuseSlicesPerCCD) {
1557  fprintf(stderr, "Warning: resetting slice numbers (%hu to %hu does not make"
1558  " sense)!\n", aSlice1, aSlice2);
1559  aSlice1 = kMuseSlicesPerCCD / 2;
1560  aSlice2 = kMuseSlicesPerCCD / 2 + 1;
1561  }
1562  if (aSlice2 - aSlice1 > 10) {
1563  fprintf(stderr, "Warning: plotting %d slices may take a long time and "
1564  "RAM/disk space!\n", (int)aSlice2 - (int)aSlice1 + 1);
1565  }
1566  printf("Plotting ");;
1567  if (aIFU > 0) {
1568  printf("IFU %hhu, ", aIFU);
1569  }
1570  printf("slices %hu to %hu\n", aSlice1, aSlice2);
1571 
1572  FILE *gp = popen("gnuplot", "w");
1573  if (!gp) {
1574  return CPL_ERROR_ASSIGNING_STREAM;
1575  }
1576 
1577  int nx = -1, ny = kMuseOutputYTop;
1578  const float *data = NULL;
1579  if (aImage) {
1580  nx = cpl_image_get_size_x(aImage->data);
1581  ny = cpl_image_get_size_x(aImage->data);
1582  data = cpl_image_get_data_float_const(aImage->data);
1583  }
1584 
1585  /* fixed output file names for both purposes (plotting of samples and *
1586  * background) within a hopefully random and protected, directory */
1587 #if HAVE_MKDTEMP
1588  char dirtemplate[] = "/tmp/muse_trace_plot_samples_XXXXXX";
1589  char *dirname = mkdtemp(dirtemplate);
1590  if (!dirname) {
1591  return CPL_ERROR_FILE_NOT_CREATED;
1592  }
1593 #else
1594  char dirname[] = "/tmp";
1595 #endif
1596  FILE *tf = NULL, *sf = NULL;
1597  char *t_out = NULL;
1598  if (aImage) {
1599  t_out = cpl_sprintf("%s/muse_trace_plot_flatimage.dat", dirname);
1600  tf = fopen(t_out, "w+");
1601  if (!tf) {
1602  cpl_error_set_message(__func__, CPL_ERROR_FILE_NOT_CREATED, "\"%s\"",
1603  t_out);
1604  cpl_free(t_out); /* fails gracefully on NULL */
1605  return CPL_ERROR_FILE_NOT_CREATED;
1606  }
1607  }
1608  char *s_out = cpl_sprintf("%s/muse_trace_plot_samples.dat", dirname);
1609  sf = fopen(s_out, "w+");
1610  if (!sf) {
1611  cpl_error_set_message(__func__, CPL_ERROR_FILE_NOT_CREATED, "%s", s_out);
1612  cpl_free(t_out); /* fails gracefully on NULL */
1613  cpl_free(s_out);
1614  return CPL_ERROR_FILE_NOT_CREATED;
1615  }
1616 
1617  /* plot all relevant slices at once */
1618  int i, lplot = INT_MAX, rplot = INT_MIN;
1619  unsigned short nslice;
1620  for (nslice = aSlice1; nslice <= aSlice2; nslice++) {
1621  /* write out all relevant sample points */
1622  for (i = 0; i < cpl_table_get_nrow(aSamples); i++) {
1623  if (nslice != cpl_table_get_int(aSamples, "slice", i, NULL)) {
1624  /* not this slice */
1625  continue;
1626  }
1627  float l = cpl_table_get_float(aSamples, "left", i, NULL),
1628  r = cpl_table_get_float(aSamples, "right", i, NULL);
1629  fprintf(sf, "%g %g %g %g\n",
1630  cpl_table_get_float(aSamples, "y", i, NULL),
1631  cpl_table_get_float(aSamples, "mid", i, NULL), l, r);
1632  if ((int)floor(l) < lplot) {
1633  lplot = floor(l);
1634  }
1635  if ((int)floor(r) > rplot) {
1636  rplot = floor(r);
1637  }
1638  }
1639  /* expand the area a bit, to make the edges fully appear on the plot */
1640  lplot -= 5;
1641  rplot += 5;
1642 
1643  if (aTrace) {
1644  int order = muse_trace_table_get_order(aTrace);
1645  double *c = (double *)cpl_calloc(order + 1, sizeof(double));
1646 
1647  int ipoly;
1648  for (ipoly = 0; ipoly < MUSE_TRACE_NPOLY; ipoly++) {
1649  for (i = 0; i <= order; i++) {
1650  char *colname = cpl_sprintf(MUSE_TRACE_TABLE_COL_COEFF, ipoly, i);
1651  c[i] = cpl_table_get_double(aTrace, colname, nslice-1, NULL);
1652  cpl_free(colname);
1653  }
1654 
1655  /* set up the function for the plot */
1656  fprintf(gp, "p%02hu%1d(x) = (%g)", nslice, ipoly, c[0]);
1657  for (i = 1; i <= order; i++) {
1658  fprintf(gp, " + (%g) * x**(%d)", c[i], i);
1659  }
1660  fprintf(gp, "\n");
1661  } /* for ipoly */
1662  cpl_free(c);
1663  } /* if aTrace */
1664  } /* for nslice */
1665  if (aImage) {
1666  /* plot the image data, too */
1667  for (i = lplot - 1; i < rplot; i++) {
1668  int j;
1669  for (j = 0; j < ny; j++) {
1670  if (i < 0 || i >= nx || j < 0 || j >= ny) {
1671  continue;
1672  }
1673  fprintf(tf, "%d %d %f\n", i+1, j+1, data[i + j*nx]);
1674  } /* for j (vertical pixels) */
1675  } /* for i (horizontal pixels) */
1676  printf("Written \"%s\".\n", t_out);
1677  fclose(tf);
1678  }
1679  printf("Written \"%s\".\n", s_out);
1680  fclose(sf); /* close the file now to unlock it for plotting */
1681 
1682  /* plot title */
1683  fprintf(gp, "set title \"trace result, ");
1684  if (aIFU > 0) {
1685  fprintf(gp, "IFU %hhu, ", aIFU);
1686  }
1687  fprintf(gp, "slices %hu to %hu\"\n", aSlice1, aSlice2);
1688  fprintf(gp, "set palette gray\n"); /* alternative palette in greyscale */
1689  fprintf(gp, "unset key\n");
1690 
1691  /* enough sampling points for the vertical dimension */
1692  fprintf(gp, "set samples %d\n", ny);
1693  /* we need parametric mode because we want to plot the slices vertically */
1694  fprintf(gp, "set parametric\n");
1695  /* set ranges for plotting */
1696  fprintf(gp, "set xrange [%d:%d]\n", lplot, rplot);
1697  fprintf(gp, "set yrange [%d:%d]\n", 1, ny);
1698  fprintf(gp, "set trange [%d:%d]\n", 1, ny);
1699  /* we are dealing with normalized master flats here */
1700  fprintf(gp, "set cbrange [1e-4:1.5]\n");
1701 
1702  /* finally create the plot itself */
1703  fprintf(gp, "plot ");
1704  if (aImage) {
1705  fprintf(gp, "\"%s\" w image, ", t_out);
1706  }
1707  fprintf(gp, "\"%s\" u 2:1 t \"center points\" w p pt 2 lt rgb \"blue\" ps 1.2, "
1708  "\"%s\" u 3:1 t \"edge points left\" w p pt 2 lt rgb \"red\" ps 0.8, "
1709  "\"%s\" u 4:1 t \"edge points right\" w p pt 2 lt rgb \"green\" ps 0.8",
1710  s_out, s_out, s_out);
1711  if (aTrace) {
1712  /* show trace polynomials pnni(x) in parametric mode */
1713  for (nslice = aSlice1; nslice <= aSlice2; nslice++) {
1714  fprintf(gp, ", p%02hu0(t),t t \"center\" w l lt rgb \"dark-blue\" lw 2, "
1715  "p%02hu1(t),t t \"left\" w l lt rgb \"dark-red\" lw 1, "
1716  "p%02hu2(t),t t \"right\" w l lt rgb \"forest-green\" lw 1",
1717  nslice, nslice, nslice);
1718  } /* for nslice */
1719  }
1720  fprintf(gp, "\n");
1721  fflush(gp);
1722  /* request keypress, so that working with the mouse keeps *
1723  * working and gnuplot has enough time to actually draw all *
1724  * the stuff before the files get removed */
1725  printf("Press ENTER to end program and close plot\n");
1726  getchar();
1727  remove(s_out);
1728  if (aImage) {
1729  remove(t_out);
1730  cpl_free(t_out);
1731  }
1732  rmdir(dirname);
1733  cpl_free(s_out);
1734  pclose(gp);
1735  return CPL_ERROR_NONE;
1736 #else /* no HAVE_POPEN && HAVE_PCLOSE */
1737  return CPL_ERROR_UNSUPPORTED_MODE;
1738 #endif /* HAVE_POPEN && HAVE_PCLOSE */
1739 } /* muse_trace_plot_samples() */
1740 
1741 /*---------------------------------------------------------------------------*/
1764 /*---------------------------------------------------------------------------*/
1765 cpl_error_code
1766 muse_trace_plot_widths(cpl_table *aSamples, unsigned short aSlice1,
1767  unsigned short aSlice2, unsigned char aIFU)
1768 {
1769 #if HAVE_POPEN && HAVE_PCLOSE
1770  cpl_ensure_code(aSamples, CPL_ERROR_NULL_INPUT);
1771  cpl_error_code rc = muse_cpltable_check(aSamples, muse_tracesamples_def);
1772  cpl_ensure_code(rc == CPL_ERROR_NONE, rc);
1773  /* if errors occur, only plot the two central slices */
1774  if (aSlice1 < 1 || aSlice1 > kMuseSlicesPerCCD || aSlice1 > aSlice2 ||
1775  aSlice2 < 1 || aSlice2 > kMuseSlicesPerCCD) {
1776  fprintf(stderr, "Warning: resetting slice numbers (%hu to %hu does not make"
1777  " sense)!\n", aSlice1, aSlice2);
1778  aSlice1 = kMuseSlicesPerCCD / 2;
1779  aSlice2 = kMuseSlicesPerCCD / 2 + 1;
1780  }
1781  printf("Plotting ");;
1782  if (aIFU > 0) {
1783  printf("IFU %hhu, ", aIFU);
1784  }
1785  printf("slices %hu to %hu\n", aSlice1, aSlice2);
1786 
1787  FILE *gp = popen("gnuplot", "w");
1788  if (!gp) {
1789  return CPL_ERROR_ASSIGNING_STREAM;
1790  }
1791 
1792  int nrow = cpl_table_get_nrow(aSamples);
1793  const int *sdata = cpl_table_get_data_int_const(aSamples, "slice");
1794  const float *ydata = cpl_table_get_data_float_const(aSamples, "y"),
1795  *ldata = cpl_table_get_data_float_const(aSamples, "left"),
1796  *rdata = cpl_table_get_data_float_const(aSamples, "right");
1797 
1798  /* plot title */
1799  fprintf(gp, "set title \"trace slice widths, ");
1800  if (aIFU > 0) {
1801  fprintf(gp, "IFU %hhu, ", aIFU);
1802  }
1803  fprintf(gp, "slices %hu to %hu\"\n", aSlice1, aSlice2);
1804  fprintf(gp, "set key outside below\n");
1805  /* set ranges and axes for plotting */
1806  fprintf(gp, "set xrange [%d:%d]\n", 1, kMuseOutputYTop);
1807  fprintf(gp, "set yrange [%f:%f]\n", kMuseSliceLoLikelyWidth,
1808  kMuseSliceHiLikelyWidth);
1809  fprintf(gp, "set xlabel \"y position on CCD [pix]\"\n");
1810  fprintf(gp, "set ylabel \"slice width at y position [pix]\"\n");
1811 
1812  /* distribute slices over 256 color values */
1813  double dslice = (aSlice2 - aSlice1) / 255.;
1814  if (dslice == 0.) { /* take care not to produce NANs below when dividing */
1815  dslice = 1.;
1816  }
1817  /* finally create the plot itself, loop over all slices */
1818  fprintf(gp, "plot ");
1819  unsigned short nslice;
1820  for (nslice = aSlice1; nslice <= aSlice2; nslice++) {
1821  /* change color going from left (red) to right (green), *
1822  * depending on slice number */
1823  fprintf(gp, "\"-\" t \"slice %02hu\" w lp ps 0.8 lt rgb \"#%02x%02x%02x\"",
1824  nslice,
1825  (int)((nslice - aSlice1) / dslice), /* red */
1826  (int)((aSlice2 - nslice) / dslice), /* green */
1827  0); /* blue */
1828  if (nslice == aSlice2) {
1829  fprintf(gp, "\n");
1830  } else {
1831  fprintf(gp, ", ");
1832  }
1833  } /* for nslice */
1834  fflush(gp);
1835  for (nslice = aSlice1; nslice <= aSlice2; nslice++) {
1836  int i;
1837  for (i = 0; i < nrow; i++) {
1838  if (nslice == sdata[i]) {
1839  fprintf(gp, "%f %f\n", ydata[i], rdata[i]-ldata[i]);
1840  }
1841  }
1842  fprintf(gp, "EOF\n");
1843  } /* for nslice */
1844  fprintf(gp, "\n");
1845  fflush(gp);
1846  /* request keypress, so that working with the mouse keeps *
1847  * working and gnuplot has enough time to actually draw all *
1848  * the stuff before the files get removed */
1849  printf("Press ENTER to end program and close plot\n");
1850  getchar();
1851  pclose(gp);
1852  return CPL_ERROR_NONE;
1853 #else /* no HAVE_POPEN && HAVE_PCLOSE */
1854  return CPL_ERROR_UNSUPPORTED_MODE;
1855 #endif /* HAVE_POPEN && HAVE_PCLOSE */
1856 } /* muse_trace_plot_widths() */
1857 
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:715
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_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:967
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
Definition: muse_utils.c:98
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:364
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:541
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_error_code muse_trace_plot_samples(cpl_table *aSamples, cpl_table *aTrace, unsigned short aSlice1, unsigned short aSlice2, unsigned char aIFU, muse_image *aImage)
Plotting of trace sample points and solution using gnuplot.
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:2234
const muse_cpltable_def muse_tracesamples_def[]
MUSE tracing sample points table definition.
Definition: muse_tracing.c:907
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:863
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_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
cpl_error_code muse_trace_plot_widths(cpl_table *aSamples, unsigned short aSlice1, unsigned short aSlice2, unsigned char aIFU)
Plotting the width from trace sample points using gnuplot.