38 #include "muse_instrument.h"
40 #include "muse_astro.h"
41 #include "muse_cplwrappers.h"
42 #include "muse_data_format_z.h"
44 #include "muse_pfits.h"
45 #include "muse_tracing.h"
46 #include "muse_utils.h"
47 #include "muse_wavecalib.h"
91 {
"filename", CPL_TYPE_STRING,
"",
"%s",
92 "(raw) filename from which this measurement originates", CPL_TRUE },
93 {
"image", CPL_TYPE_INT,
"",
"%03d",
"number of the image in the series", CPL_TRUE },
94 {
"POSENC2", CPL_TYPE_INT,
"",
"%d",
95 "x position of the mask in encoder steps", CPL_TRUE },
96 {
"POSPOS2", CPL_TYPE_DOUBLE,
"mm",
"%.3f",
"x position of the mask", CPL_TRUE },
97 {
"POSENC3", CPL_TYPE_INT,
"",
"%d",
98 "y position of the mask in encoder steps", CPL_TRUE },
99 {
"POSPOS3", CPL_TYPE_DOUBLE,
"mm",
"%.3f",
"y position of the mask", CPL_TRUE },
100 {
"POSENC4", CPL_TYPE_INT,
"",
"%d",
101 "z position of the mask in encoder steps", CPL_TRUE },
102 {
"POSPOS4", CPL_TYPE_DOUBLE,
"mm",
"%.3f",
"z position of the mask", CPL_TRUE },
103 {
"VPOS", CPL_TYPE_DOUBLE,
"mm",
"%.3f",
"real vertical position of the mask", CPL_TRUE },
104 {
"ScaleFOV", CPL_TYPE_DOUBLE,
"arcsec/mm",
"%.3f",
105 "focus scale in VLT focal plane (from the FITS header)", CPL_TRUE },
106 {
"SubField", CPL_TYPE_INT,
"",
"%02d",
"sub-field number", CPL_TRUE },
107 {
"SliceCCD", CPL_TYPE_INT,
"",
"%02d",
108 "slice number as counted on the CCD", CPL_TRUE },
109 {
"lambda", CPL_TYPE_DOUBLE,
"Angstrom",
"%.3f",
"wavelength", CPL_TRUE },
110 {
"SpotNo", CPL_TYPE_INT,
"",
"%04d",
111 "number of this spot within the slice (1 is left, 2 is the central one, 3 is right within the slice)", CPL_TRUE },
112 {
"xc", CPL_TYPE_DOUBLE,
"pix",
"%.3f",
"x center of this spot on the CCD", CPL_TRUE },
113 {
"yc", CPL_TYPE_DOUBLE,
"pix",
"%.3f",
"y center of this spot on the CCD", CPL_TRUE },
114 {
"xfwhm", CPL_TYPE_DOUBLE,
"pix",
"%.2f",
"FWHM in x-direction on the CCD", CPL_TRUE },
115 {
"yfwhm", CPL_TYPE_DOUBLE,
"pix",
"%.2f",
"FWHM in y-direction on the CCD", CPL_TRUE },
116 {
"flux", CPL_TYPE_DOUBLE,
"",
"%.1f",
117 "flux of the spot as integrated on the CCD image", CPL_TRUE },
118 {
"bg", CPL_TYPE_DOUBLE,
"",
"%f",
"background level around the spot", CPL_TRUE },
119 {
"dxcen", CPL_TYPE_DOUBLE,
"pix",
"%f",
120 "distance to center of slice at vertical position yc (positive: right of center)", CPL_TRUE },
121 {
"twidth", CPL_TYPE_DOUBLE,
"pix",
"%f",
122 "trace width of the slice at the vertical CCD position of the spot", CPL_TRUE },
123 { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
170 { MUSE_GEOTABLE_FIELD, CPL_TYPE_INT,
"",
"%02d",
171 "sub-field (IFU / channel) number", CPL_TRUE },
172 { MUSE_GEOTABLE_CCD, CPL_TYPE_INT,
"",
"%02d",
173 "the slice number on the CCD, counted from left to right", CPL_TRUE },
174 { MUSE_GEOTABLE_SKY, CPL_TYPE_INT,
"",
"%02d",
175 "the slice number on the sky", CPL_TRUE },
176 { MUSE_GEOTABLE_X, CPL_TYPE_DOUBLE,
"pix",
"%9.4f",
177 "x position within field of view", CPL_TRUE },
178 { MUSE_GEOTABLE_Y, CPL_TYPE_DOUBLE,
"pix",
"%9.4f",
179 "y position within field of view", CPL_TRUE },
180 { MUSE_GEOTABLE_ANGLE, CPL_TYPE_DOUBLE,
"deg",
"%6.3f",
181 "rotation angle of slice", CPL_TRUE },
182 { MUSE_GEOTABLE_WIDTH, CPL_TYPE_DOUBLE,
"pix",
"%.2f",
183 "width of slice within field of view", CPL_TRUE },
184 { MUSE_GEOTABLE_X
"err", CPL_TYPE_DOUBLE,
"pix",
"%8.4f",
185 "error estimated of x position within field of view", CPL_TRUE },
186 { MUSE_GEOTABLE_Y
"err", CPL_TYPE_DOUBLE,
"pix",
"%8.4f",
187 "error estimate of y position within field of view", CPL_TRUE },
188 { MUSE_GEOTABLE_ANGLE
"err", CPL_TYPE_DOUBLE,
"deg",
"%.3f",
189 "error estimate of rotation angle", CPL_TRUE },
190 { MUSE_GEOTABLE_WIDTH
"err", CPL_TYPE_DOUBLE,
"pix",
"%.2f",
191 "error estimate of slice width", CPL_TRUE },
192 {
"stack", CPL_TYPE_INT,
"",
"%02d",
193 "slicer stack that this slice belongs to (optical numbering)", CPL_TRUE },
194 {
"spot", CPL_TYPE_INT,
"",
"%1d",
"spot number in this slice", CPL_TRUE },
195 {
"xrel", CPL_TYPE_DOUBLE,
"mm",
"%7.4f",
196 "x offset of this spot relative to the slice center", CPL_TRUE },
197 {
"xrelerr", CPL_TYPE_DOUBLE,
"mm",
"%6.4f",
198 "error of the relative x offset of this spot", CPL_TRUE },
199 {
"xc", CPL_TYPE_DOUBLE,
"pix",
"%.3f",
"x center of this spot on the CCD", CPL_TRUE },
200 {
"yc", CPL_TYPE_DOUBLE,
"pix",
"%.3f",
"y center of this spot on the CCD", CPL_TRUE },
201 {
"dxl", CPL_TYPE_DOUBLE,
"pix",
"%.3f",
"distance to left edge of slice on the CCD", CPL_TRUE },
202 {
"dxr", CPL_TYPE_DOUBLE,
"pix",
"%.3f",
"distance to right edge of slice on the CCD", CPL_TRUE },
203 {
"dx", CPL_TYPE_DOUBLE,
"pix",
"%.3f",
"pinhole distance in x on the CCD", CPL_TRUE },
204 {
"dxerr", CPL_TYPE_DOUBLE,
"pix",
"%.3f",
205 "error estimate of the pinhole distance in x on the CCD", CPL_TRUE },
206 {
"vpos", CPL_TYPE_DOUBLE,
"mm",
"%.4f",
207 "(averaged) vertical position of the mask", CPL_TRUE },
208 {
"vposerr", CPL_TYPE_DOUBLE,
"mm",
"%.4f",
209 "error estimated of the (averaged) vertical position of the mask", CPL_TRUE },
210 {
"flux", CPL_TYPE_DOUBLE,
"",
"%.1f",
211 "flux of the spot as integrated on the CCD image", CPL_TRUE },
212 {
"lambda", CPL_TYPE_DOUBLE,
"Angstrom",
"%.3f",
"wavelength", CPL_TRUE },
213 { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
237 cpl_ensure(aTable, CPL_ERROR_NULL_INPUT, NULL);
238 cpl_ensure(aIFU >= 1 && aIFU <= kMuseNumIFUs, CPL_ERROR_ILLEGAL_INPUT, NULL);
241 cpl_table *intable = cpl_table_duplicate(aTable);
245 cpl_propertylist *sorting = cpl_propertylist_new();
246 cpl_propertylist_append_bool(sorting, MUSE_GEOTABLE_FIELD, CPL_FALSE);
247 cpl_propertylist_append_bool(sorting, MUSE_GEOTABLE_CCD, CPL_FALSE);
248 cpl_table_sort(intable, sorting);
249 cpl_propertylist_delete(sorting);
251 cpl_table_select_all(intable);
252 cpl_table_and_selected_int(intable, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO, aIFU);
253 cpl_table *subtable = cpl_table_extract_selected(intable);
254 cpl_table_delete(intable);
256 printf(
"table (extracted for IFU %2d)\n", aIFU);
257 cpl_table_dump(subtable, 0, 100000, stdout);
260 int nrow = cpl_table_get_nrow(subtable);
261 if (nrow != kMuseSlicesPerCCD) {
262 cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT,
263 "geometry table contains %d instead of %d slices for "
264 "IFU %d", nrow, kMuseSlicesPerCCD, aIFU);
265 cpl_table_delete(subtable);
300 cpl_ensure(aTable, CPL_ERROR_NULL_INPUT, 0.);
305 cpl_size nrow = cpl_table_get_nrow(table);
306 cpl_ensure(nrow == kMuseSlicesPerCCD, CPL_ERROR_ILLEGAL_INPUT, 0.);
310 cpl_propertylist *sorting = cpl_propertylist_new();
311 cpl_propertylist_append_bool(sorting, MUSE_GEOTABLE_SKY, CPL_FALSE);
312 cpl_table_sort(table, sorting);
313 cpl_propertylist_delete(sorting);
317 double area = 0., areas[4];
319 nperstack = kMuseSlicesPerCCD / 4;
320 for (istack = 0; istack < 4; istack++) {
321 cpl_table *stack = cpl_table_extract(table, 12 * istack, nperstack);
324 double height = fabs(cpl_table_get(stack, MUSE_GEOTABLE_Y, 0, NULL)
325 - cpl_table_get(stack, MUSE_GEOTABLE_Y, nperstack - 1, NULL))
327 / kMuseTypicalCubeSizeY * aScale;
328 areas[istack] = cpl_table_get_column_mean(stack, MUSE_GEOTABLE_WIDTH)
330 / kMuseTypicalCubeSizeX * aScale;
331 cpl_table_delete(stack);
333 cpl_msg_debug(__func__,
"areas[%d] = %f", istack, areas[istack]);
335 area += areas[istack];
337 cpl_table_delete(table);
356 cpl_ensure(aLines, CPL_ERROR_NULL_INPUT, NULL);
359 cpl_table *tlines = cpl_table_duplicate(aLines);
362 cpl_table_cast_column(tlines, MUSE_LINE_CATALOG_LAMBDA, MUSE_LINE_CATALOG_LAMBDA,
364 cpl_table_cast_column(tlines, MUSE_LINE_CATALOG_FLUX, MUSE_LINE_CATALOG_FLUX,
366 cpl_table_unselect_all(tlines);
372 cpl_table_or_selected_string(tlines, MUSE_LINE_CATALOG_ION, CPL_EQUAL_TO,
"XeI");
373 cpl_table_or_selected_double(tlines, MUSE_LINE_CATALOG_FLUX, CPL_LESS_THAN, 5000.);
374 cpl_table_or_selected_double(tlines, MUSE_LINE_CATALOG_LAMBDA, CPL_LESS_THAN,
375 kMuseNominalLambdaMin);
376 cpl_table_or_selected_int(tlines, MUSE_LINE_CATALOG_QUALITY, CPL_LESS_THAN, 1);
377 cpl_table_erase_selected(tlines);
382 cpl_table_or_selected_string(tlines, MUSE_LINE_CATALOG_ION, CPL_EQUAL_TO,
"NeI");
383 cpl_table_and_selected_int(tlines, MUSE_LINE_CATALOG_QUALITY, CPL_LESS_THAN, 2);
384 cpl_table_unselect_row(tlines, cpl_table_get_nrow(tlines) - 1);
385 cpl_table_erase_selected(tlines);
386 cpl_table_or_selected_string(tlines, MUSE_LINE_CATALOG_ION, CPL_EQUAL_TO,
"NeI");
387 cpl_table_and_selected_double(tlines, MUSE_LINE_CATALOG_FLUX, CPL_LESS_THAN, 10000.);
388 cpl_table_unselect_row(tlines, cpl_table_get_nrow(tlines) - 1);
389 cpl_table_erase_selected(tlines);
393 int nlines = cpl_table_get_nrow(tlines);
395 cpl_table_delete(tlines);
396 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND,
397 "Only found %d suitable arc lines!", nlines);
400 cpl_vector *lines = cpl_vector_wrap(nlines,
401 cpl_table_unwrap(tlines, MUSE_LINE_CATALOG_LAMBDA));
402 cpl_table_delete(tlines);
403 cpl_msg_info(__func__,
"Using a list of %d arc lines (from %.1f to %.1f "
404 "Angstrom)", nlines, cpl_vector_get(lines, 0),
405 cpl_vector_get(lines, nlines - 1));
459 const cpl_table *aTrace,
const cpl_table *aWave,
460 const cpl_vector *aLines,
double aSigma,
463 cpl_ensure(aImage && aList && aTrace && aWave && aLines, CPL_ERROR_NULL_INPUT,
465 cpl_ensure(aSigma > 0., CPL_ERROR_ILLEGAL_INPUT, NULL);
467 cpl_ensure(nimages >= 5, CPL_ERROR_ILLEGAL_INPUT, NULL);
468 int nlines = cpl_vector_get_size(aLines);
469 cpl_ensure(nlines >= 3, CPL_ERROR_ILLEGAL_INPUT, NULL);
473 int ny = cpl_image_get_size_y(aImage->
data),
474 nentries = kMuseSlicesPerCCD * kMuseCUmpmSpotsPerSlice * nlines * nimages;
479 for (iline = 0; iline < nlines; iline++) {
480 double lambda = cpl_vector_get(aLines, iline);
481 cpl_msg_info(__func__,
"line %d at %.3f Angstrom", iline + 1, lambda);
483 unsigned short nslice;
484 for (nslice = 1; nslice <= kMuseSlicesPerCCD; nslice++) {
488 if (!ptrace || !pwave) {
490 cpl_polynomial_delete(pwave);
493 double xc = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_CENTER], ny / 2, NULL);
496 cpl_polynomial *pxconst = cpl_polynomial_new(1);
498 cpl_polynomial_set_coeff(pxconst, &p, xc);
499 cpl_polynomial *pywave = cpl_polynomial_extract(pwave, 0, pxconst);
500 cpl_polynomial_delete(pxconst);
502 double yc = 1, lbda = -1;
503 while (fabs(lambda - lbda) > 1.) {
504 lbda = cpl_polynomial_eval_1d(pywave, yc, NULL);
506 if (yc > kMuseOutputYTop) {
510 cpl_polynomial_delete(pywave);
512 cpl_msg_debug(__func__,
"--> %.3f --> %f,%f in slice %hu",
513 lbda, xc, yc, nslice);
515 cpl_polynomial_delete(pwave);
518 if (fabs(lambda - lbda) > 1.) {
519 cpl_msg_warning(__func__,
"Polynomial in slice %hu of IFU %hhu appears "
520 "to be faulty! Skipping measurement of line %d (%.1f "
521 "Angstrom)", nslice, ifu, iline + 1, lambda);
527 #define DETECTION_HALFSIZE 7
528 int xl = lround(cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_LEFT], yc, NULL)),
529 xr = lround(cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_RIGHT], yc, NULL)),
530 yb = lround(yc - DETECTION_HALFSIZE),
531 yt = lround(yc + DETECTION_HALFSIZE);
532 cpl_image *box = cpl_image_extract(aImage->
data, xl, yb, xr, yt);
534 cpl_image *fbox = cpl_image_duplicate(box);
536 cpl_image_filter(fbox, box, gkernel, CPL_FILTER_LINEAR, CPL_BORDER_FILTER);
537 cpl_matrix_delete(gkernel);
538 cpl_stats_mode mode = CPL_STATS_MEDIAN | CPL_STATS_MEDIAN_DEV;
539 cpl_stats *s = cpl_stats_new_from_image(box, mode);
540 double limit = cpl_stats_get_median(s)
541 + aSigma * cpl_stats_get_median_dev(s);
542 cpl_mask *mask = cpl_mask_threshold_image_create(fbox, limit, DBL_MAX);
544 char *fn1 = cpl_sprintf(
"box_%02hu.fits", nslice),
545 *fn2 = cpl_sprintf(
"boxf_%02hu.fits", nslice),
546 *fn3 = cpl_sprintf(
"boxf_%02hu_mask.fits", nslice);
547 cpl_image_save(box, fn1, CPL_TYPE_UNSPECIFIED, NULL, CPL_IO_CREATE);
548 cpl_image_save(fbox, fn2, CPL_TYPE_UNSPECIFIED, NULL, CPL_IO_CREATE);
549 cpl_mask_save(mask, fn3, NULL, CPL_IO_CREATE);
555 cpl_apertures *apertures = cpl_apertures_extract_mask(box, mask);
557 cpl_msg_debug(__func__,
"stats in box [%d:%d,%d:%d] --> limit = "
558 "%f + %.1f * %f = %f, apertures:", xl, xr, yb, yt,
559 cpl_stats_get_median(s), aSigma,
560 cpl_stats_get_median_dev(s), limit);
561 cpl_apertures_dump(apertures, stdout);
564 cpl_mask_delete(mask);
566 cpl_image_delete(fbox);
567 cpl_image_delete(box);
568 cpl_errorstate es = cpl_errorstate_get();
569 int nspots = cpl_apertures_get_size(apertures);
573 if (!apertures || nspots != kMuseCUmpmSpotsPerSlice) {
574 cpl_msg_warning(__func__,
"found %d spot%s (need %hhu) down to the %.1f"
575 "-sigma limit in slice %d for wavelength %.3f Angstrom "
576 "in box [%d:%d,%d:%d]", nspots, nspots == 1 ?
"" :
"s",
577 kMuseCUmpmSpotsPerSlice, aSigma, nslice, lambda,
579 cpl_apertures_delete(apertures);
580 cpl_errorstate_set(es);
583 cpl_msg_debug(__func__,
"found %d spots using the %.1f-sigma limit in "
584 "slice %d for wavelength %.3f Angstrom in box [%d:%d,%d:%d]",
585 nspots, aSigma, nslice, lambda, xl, xr, yb, yt);
587 cpl_apertures_dump(apertures, stdout);
593 cpl_matrix *mspots = cpl_matrix_new(nspots, 2);
595 for (naper = 1; naper <= nspots; naper++) {
596 double xpos = cpl_apertures_get_centroid_x(apertures, naper);
597 cpl_matrix_set(mspots, naper - 1, 0, xpos);
598 cpl_matrix_set(mspots, naper - 1, 1, naper);
600 cpl_matrix_sort_rows(mspots, 1);
602 printf(
"mspots sorted:\n");
603 cpl_matrix_dump(mspots, stdout);
607 int idx, *aperidx = cpl_calloc(nspots,
sizeof(
int));
608 for (naper = 1, idx = nspots - 1; naper <= nspots && idx >= 0; naper++, idx--) {
610 aperidx[naper - 1] = cpl_matrix_get(mspots, idx, 1);
612 cpl_matrix_delete(mspots);
617 for (k = 0; k < nimages; k++) {
623 for (i = 1; i <= 4; i++) {
628 #define MEASUREMENT_HALFSIZE 5
629 #define BACKGROUND_HALFSIZE 7
630 for (idx = 0; idx < nspots; idx++) {
631 naper = aperidx[idx];
632 double xpos = cpl_apertures_get_centroid_x(apertures, naper) + xl,
633 ypos = cpl_apertures_get_centroid_y(apertures, naper) + yb;
635 printf(
"aper %d idx %d xpos %f\n", naper, idx, xpos);
638 cpl_stats_mode mspot = CPL_STATS_FLUX | CPL_STATS_CENTROID,
639 mbg = CPL_STATS_MEAN;
640 cpl_stats *sspot = cpl_stats_new_from_image_window(image->
data, mspot,
641 xpos - MEASUREMENT_HALFSIZE,
642 ypos - MEASUREMENT_HALFSIZE,
643 xpos + MEASUREMENT_HALFSIZE,
644 ypos + MEASUREMENT_HALFSIZE),
645 *sbg = cpl_stats_new_from_image_window(image->
data, mbg,
646 xpos - BACKGROUND_HALFSIZE,
647 ypos - BACKGROUND_HALFSIZE,
648 xpos + BACKGROUND_HALFSIZE,
649 ypos + BACKGROUND_HALFSIZE);
650 int npix = cpl_stats_get_npix(sspot);
651 double bg = cpl_stats_get_mean(sbg),
652 flux = cpl_stats_get_flux(sspot) - bg * npix;
656 cpl_stats_delete(sbg);
657 double xcentroid = cpl_stats_get_centroid_x(sspot),
658 ycentroid = cpl_stats_get_centroid_y(sspot);
659 cpl_stats_delete(sspot);
663 cpl_array *gpars = cpl_array_new(7, CPL_TYPE_DOUBLE);
664 cpl_array_set(gpars, 0, bg);
665 cpl_array_set(gpars, 1, flux);
666 cpl_array_set(gpars, 3, xcentroid);
667 cpl_array_set(gpars, 4, ycentroid);
668 cpl_array_set(gpars, 5, 2.);
669 cpl_array_set(gpars, 6, 2.);
670 cpl_fit_image_gaussian(image->
data, NULL, lround(xcentroid), lround(ycentroid),
671 2 * MEASUREMENT_HALFSIZE + 1, 2 * MEASUREMENT_HALFSIZE + 1,
672 gpars, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
673 xcentroid = cpl_array_get(gpars, 3, NULL);
674 ycentroid = cpl_array_get(gpars, 4, NULL);
675 xfwhm = cpl_array_get(gpars, 5, NULL) * CPL_MATH_FWHM_SIG;
676 yfwhm = cpl_array_get(gpars, 6, NULL) * CPL_MATH_FWHM_SIG;
677 cpl_array_delete(gpars);
679 cpl_image_get_fwhm(image->
data, lround(xcentroid), lround(ycentroid),
682 if (cpl_propertylist_has(image->
header, MUSE_HDR_TMP_FN)) {
683 cpl_table_set_string(measurements,
"filename", irow,
684 cpl_propertylist_get_string(image->
header,
687 cpl_table_set_string(measurements,
"filename", irow,
"unknown");
689 cpl_table_set_int(measurements,
"image", irow, k + 1);
690 cpl_table_set_int(measurements,
"POSENC2", irow, posenc[1]);
691 cpl_table_set(measurements,
"POSPOS2", irow, pospos[1]);
692 cpl_table_set_int(measurements,
"POSENC3", irow, posenc[2]);
693 cpl_table_set(measurements,
"POSPOS3", irow, pospos[2]);
694 cpl_table_set_int(measurements,
"POSENC4", irow, posenc[3]);
695 cpl_table_set(measurements,
"POSPOS4", irow, pospos[3]);
698 cpl_table_set(measurements,
"VPOS", irow,
699 pospos[2] / sin(posang * CPL_MATH_RAD_DEG));
700 cpl_table_set_double(measurements,
"ScaleFOV", irow,
702 cpl_table_set_int(measurements,
"SubField", irow, ifu);
703 cpl_table_set_int(measurements,
"SliceCCD", irow, nslice);
704 cpl_table_set(measurements,
"lambda", irow, lambda);
705 cpl_table_set_int(measurements,
"SpotNo", irow, idx + 1);
706 cpl_table_set(measurements,
"xc", irow, xcentroid);
707 cpl_table_set(measurements,
"yc", irow, ycentroid);
708 cpl_table_set(measurements,
"xfwhm", irow, xfwhm);
709 cpl_table_set(measurements,
"yfwhm", irow, yfwhm);
710 cpl_table_set(measurements,
"flux", irow, flux);
711 cpl_table_set(measurements,
"bg", irow, bg);
713 double xcslice = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_CENTER], ycentroid, NULL);
714 cpl_table_set(measurements,
"dxcen", irow, xcentroid - xcslice);
716 double twidth = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_RIGHT], ycentroid, NULL)
717 - cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_LEFT], ycentroid, NULL);
718 cpl_table_set(measurements,
"twidth", irow, twidth);
721 if (xfwhm < 0 || yfwhm < 0) {
722 cpl_msg_warning(__func__,
"xfwhm and/or yfwhm are invalid: %f, %f:", xfwhm, yfwhm);
724 cpl_table_set_invalid(measurements,
"xfwhm", irow);
727 cpl_table_set_invalid(measurements,
"yfwhm", irow);
729 cpl_table_dump(measurements, irow, 1, stdout);
736 cpl_apertures_delete(apertures);
743 cpl_table_erase_invalid(measurements);
746 cpl_propertylist *order = cpl_propertylist_new();
747 cpl_propertylist_append_bool(order,
"lambda", CPL_FALSE);
748 cpl_propertylist_append_bool(order,
"SliceCCD", CPL_FALSE);
749 cpl_propertylist_append_bool(order,
"SpotNo", CPL_FALSE);
750 cpl_propertylist_append_bool(order,
"VPOS", CPL_FALSE);
751 cpl_table_sort(measurements, order);
752 cpl_propertylist_delete(order);
796 unsigned short aNSlice,
unsigned char aNSpot,
797 double aLambda,
double aVPosRef, cpl_boolean aVerifyDY,
807 cpl_table_unselect_all(aSpots);
808 cpl_size irow, nrow = cpl_table_get_nrow(aSpots);
809 for (irow = 0; irow < nrow; irow++) {
810 if (cpl_table_get_int(aSpots,
"SliceCCD", irow, NULL) == aNSlice &&
811 cpl_table_get_int(aSpots,
"SpotNo", irow, NULL) == aNSpot &&
812 cpl_table_get_double(aSpots,
"lambda", irow, NULL) == aLambda) {
813 cpl_table_select_row(aSpots, irow);
816 cpl_size nextracted = cpl_table_count_selected(aSpots);
817 if (nextracted < 1) {
820 cpl_msg_debug(__func__,
"No detection for spot %1hhu in slice %2hu of IFU "
821 "%hhu at wavelength %.3f", aNSpot, aNSlice, aIFU, aLambda);
824 cpl_table *tspot = cpl_table_extract_selected(aSpots);
827 cpl_table_dump(tspot, 0, 10000, stdout);
831 int nsrow = cpl_table_get_nrow(tspot);
832 cpl_image *imflux = cpl_image_wrap(nsrow, 1,
834 cpl_table_get_data_double(tspot,
"flux"));
837 cpl_stats *s = cpl_stats_new_from_image(imflux,
838 CPL_STATS_MEDIAN | CPL_STATS_MEDIAN_DEV);
839 double limit = cpl_stats_get_median(s) + cpl_stats_get_median_dev(s) * 0.5;
844 cpl_mask *mask = cpl_mask_threshold_image_create(imflux, limit, DBL_MAX);
845 cpl_mask *kernel = cpl_mask_new(3, 1);
846 cpl_mask_not(kernel);
847 cpl_mask *mask2 = cpl_mask_duplicate(mask);
848 cpl_mask_filter(mask, mask2, kernel, CPL_FILTER_DILATION, CPL_BORDER_NOP);
849 cpl_mask_delete(mask2);
850 cpl_mask_delete(kernel);
851 cpl_apertures *aper = cpl_apertures_extract_mask(imflux, mask);
852 cpl_mask_delete(mask);
854 cpl_msg_warning(__func__,
"No detection for spot %1hhu in slice %2hu of IFU "
855 "%2hhu at wavelength %.3f", aNSpot, aNSlice, aIFU, aLambda);
856 cpl_table_delete(tspot);
857 cpl_image_unwrap(imflux);
861 double dcenter = DBL_MAX;
862 int naper, ndcenter = -1;
863 for (naper = 1; naper <= cpl_apertures_get_size(aper); naper++) {
865 int npos = cpl_apertures_get_npix(aper, naper);
866 if (cpl_apertures_get_size(aper) > 1 && npos < 3) {
867 cpl_msg_debug(__func__,
"ifu %2hhu sliceccd %2d spot %1hhu lambda %.3f, "
868 "aperture %d: only %d positions -> skip", aIFU, aNSlice,
869 aNSpot, aLambda, naper, npos);
873 double xref = aVPosRef > 0 ? aVPosRef
874 : cpl_table_get_double(tspot,
"VPOS", (nsrow + 1) / 2, NULL),
875 xcentroid = cpl_apertures_get_centroid_x(aper, naper);
879 while (++irow + 1 < xcentroid) ;
880 double pp1 = cpl_table_get_double(tspot,
"VPOS", irow - 1, NULL),
881 pp2 = cpl_table_get_double(tspot,
"VPOS", irow, NULL),
882 ppfrac = xcentroid - irow;
884 cpl_msg_debug(__func__,
"%"CPL_SIZE_FORMAT
" (%f) --> %f %f ==> %f", irow,
885 xcentroid, pp1, pp2, pp1 * (1 - ppfrac) + pp2 * ppfrac);
887 double ppcentroid = pp1 * (1 - ppfrac) + pp2 * ppfrac;
889 double dc = fabs(ppcentroid - xref);
890 int x1 = cpl_apertures_get_left(aper, naper),
891 x2 = cpl_apertures_get_right(aper, naper);
892 if (dc < dcenter && x1 > 1 && x2 < nsrow) {
899 if (aDY || aVerifyDY) {
900 for (naper = 1; naper < cpl_apertures_get_size(aper); naper++) {
901 int l1 = cpl_apertures_get_left(aper, naper),
902 r1 = cpl_apertures_get_right(aper, naper),
903 l2 = cpl_apertures_get_left(aper, naper + 1),
904 r2 = cpl_apertures_get_right(aper, naper + 1);
905 if (l1 > 1 && r1 < nsrow && l2 > 1 && r2 < nsrow) {
909 for (n2aper = naper; n2aper <= naper + 1; n2aper++) {
910 cpl_size irow1 = cpl_apertures_get_left(aper, n2aper) - 1,
911 irow2 = cpl_apertures_get_right(aper, n2aper) - 1;
912 double vpos = 0., ftot = 0.;
913 for (irow = irow1; irow <= irow2; irow++) {
914 double flux = cpl_table_get(tspot,
"flux", irow, NULL);
916 vpos += cpl_table_get(tspot,
"VPOS", irow, NULL) * flux;
918 peak[n2aper - naper] = vpos / ftot;
920 double xcdiff = fabs(peak[1] - peak[0]);
923 cpl_errorstate state = cpl_errorstate_get();
924 cpl_size idy = 0, ndy = cpl_array_get_size(aDY);
925 while (cpl_array_is_valid(aDY, idy) > 0) {
928 if (cpl_array_get_size(aDY) <= idy) {
929 cpl_array_set_size(aDY, ndy * 1.5);
930 cpl_errorstate_set(state);
933 cpl_msg_debug(__func__,
"xcdiff = %f (%f - %f = %f) / index %"CPL_SIZE_FORMAT,
934 xcdiff, peak[1], peak[0], peak[1] - peak[0], idy);
936 cpl_array_set_double(aDY, idy, xcdiff);
939 printf(
"\"centroids_d_%f.dat\" u 18:16 t \"d %f (%f %f)\" w lp, \\\n",
940 xcdiff, xcdiff, peak[0], peak[1]);
941 char *fn = cpl_sprintf(
"centroids_d_%f.dat", xcdiff);
942 FILE *fp = fopen(fn,
"w");
943 fprintf(fp,
"# good centroids at %f and %f --> d = %f mm\n#", peak[0], peak[1], xcdiff);
944 cpl_table_dump(tspot, 0, 10000, fp);
953 cpl_msg_warning(__func__,
"Motion of spot %1hhu in slice %2hu of IFU "
954 "%2hhu at wavelength %.3f did not result in usable "
955 "coverage", aNSpot, aNSlice, aIFU, aLambda);
956 cpl_table_delete(tspot);
957 cpl_apertures_delete(aper);
958 cpl_image_unwrap(imflux);
961 cpl_size irow1 = cpl_apertures_get_left(aper, ndcenter) - 1,
962 irow2 = cpl_apertures_get_right(aper, ndcenter) - 1;
963 cpl_apertures_delete(aper);
964 cpl_image_unwrap(imflux);
967 cpl_table_unselect_all(tspot);
968 for (irow = irow1; irow <= irow2; irow++) {
969 cpl_table_select_row(tspot, irow);
971 cpl_table *result = cpl_table_extract_selected(tspot);
972 cpl_table_delete(tspot);
1013 cpl_ensure_code(aDY && aSpots, CPL_ERROR_NULL_INPUT);
1014 cpl_ensure_code(cpl_array_get_type(aDY) == CPL_TYPE_DOUBLE,
1015 CPL_ERROR_INCOMPATIBLE_INPUT);
1016 cpl_size nrow = cpl_table_get_nrow(aSpots);
1017 cpl_ensure_code(nrow > 10, CPL_ERROR_ILLEGAL_INPUT);
1019 CPL_ERROR_INCOMPATIBLE_INPUT);
1020 const unsigned char ifu = cpl_table_get_column_min(aSpots,
"SubField"),
1021 ifu2 = cpl_table_get_column_max(aSpots,
"SubField");
1022 cpl_ensure_code(ifu == ifu2 && ifu >= 1 && ifu <= kMuseNumIFUs,
1023 CPL_ERROR_ILLEGAL_INPUT);
1024 cpl_ensure_code(cpl_table_get_column_stdev(aSpots,
"ScaleFOV") < 1e-10,
1025 CPL_ERROR_ILLEGAL_INPUT);
1027 cpl_boolean verifydy = getenv(
"MUSE_DEBUG_GEO_VERIFY_DY")
1028 && atoi(getenv(
"MUSE_DEBUG_GEO_VERIFY_DY")) > 0;
1030 cpl_msg_warning(__func__,
"Running with DY pinhole distance verification on"
1031 " (MUSE_DEBUG_GEO_VERIFY_DY=%s), will produce lots of files "
1032 "\"centroids_d_*.dat\"!", getenv(
"MUSE_DEBUG_GEO_VERIFY_DY"));
1036 double *lbda = cpl_table_get_data_double(aSpots,
"lambda");
1037 cpl_vector *vlbda = cpl_vector_wrap(nrow, lbda);
1039 cpl_vector_unwrap(vlbda);
1040 int nlines = cpl_vector_get_size(lambdas);
1044 cpl_array *dy = cpl_array_new(kMuseSlicesPerCCD * nlines * kMuseCUmpmSpotsPerSlice,
1050 unsigned short nslice;
1051 for (nslice = 1; nslice <= kMuseSlicesPerCCD; nslice++) {
1053 for (iline = 0; iline < nlines; iline++) {
1054 double lambda = cpl_vector_get(lambdas, iline);
1056 unsigned char nspot;
1057 double vposref = -DBL_MAX;
1058 for (nspot = 1; nspot <= kMuseCUmpmSpotsPerSlice; nspot++) {
1060 lambda, vposref, verifydy, dy);
1061 cpl_table_delete(tspot);
1065 cpl_vector_delete(lambdas);
1068 cpl_msg_debug(__func__,
"Median vertical pinhole distance in IFU %02hhu: %f mm",
1069 ifu, cpl_array_get_median(dy));
1070 #pragma omp critical (geo_dy_array_insert)
1071 cpl_array_insert(aDY, dy, cpl_array_get_size(aDY));
1072 cpl_array_delete(dy);
1074 return CPL_ERROR_NONE;
1108 double aMin,
double aMax)
1110 cpl_ensure(aDY, CPL_ERROR_NULL_INPUT, 0.);
1111 cpl_ensure(cpl_array_get_type(aDY) == CPL_TYPE_DOUBLE,
1112 CPL_ERROR_INCOMPATIBLE_INPUT, 0.);
1113 cpl_ensure(cpl_array_count_invalid(aDY) < cpl_array_get_size(aDY),
1114 CPL_ERROR_ILLEGAL_INPUT, 0.);
1119 printf(
"aDY array 1: %f +/- %f (%f)\n", cpl_array_get_mean(aDY),
1120 cpl_array_get_stdev(aDY), cpl_array_get_median(aDY));
1121 cpl_array_dump(aDY, 0, 1000000, stdout);
1122 printf(
"aDY histogram 1:\n");
1123 cpl_plot_bivector(NULL,
"w lp", NULL, histogram);
1124 cpl_bivector_dump(histogram, stdout);
1128 cpl_bivector_delete(histogram);
1129 double mean = cpl_array_get_mean(aDY),
1130 stdev = cpl_array_get_stdev(aDY),
1131 min = mean - 2 * stdev,
1132 max = mean + 2 * stdev,
1133 step = (max - min) / 20.;
1135 double median = cpl_array_get_median(aDY);
1136 printf(
"aDY array 2: %f +/- %f (%f)\n", mean, stdev, median);
1137 cpl_array_dump(aDY, 0, 1000000, stdout);
1142 printf(
"aDY histogram 2:\n");
1143 cpl_plot_bivector(NULL,
"w lp", NULL, histogram);
1144 cpl_bivector_dump(histogram, stdout);
1148 cpl_bivector_delete(histogram);
1149 mean = cpl_array_get_mean(aDY);
1150 stdev = cpl_array_get_stdev(aDY);
1152 double median2 = cpl_array_get_median(aDY);
1153 printf(
"aDY array 3: %f +/- %f (%f)\n", mean, stdev, median2);
1154 cpl_array_dump(aDY, 0, 1000000, stdout);
1160 cpl_msg_info(__func__,
"Computed vertical pinhole distance of %.6f +/- %.6f "
1161 "mm (instead of %.4f)", mean, stdev, kMuseCUmpmDY);
1162 if (getenv(
"MUSE_GEOMETRY_PINHOLE_DY")) {
1163 cpl_msg_warning(__func__,
"Vertical pinhole distance already overridden in the "
1164 "environment (%f mm)", atof(getenv(
"MUSE_GEOMETRY_PINHOLE_DY")));
1166 char *envstring = cpl_sprintf(
"%f", mean);
1167 int err = setenv(
"MUSE_GEOMETRY_PINHOLE_DY", envstring, 1);
1169 cpl_msg_info(__func__,
"Set MUSE_GEOMETRY_PINHOLE_DY=%s in the environment",
1172 cpl_free(envstring);
1214 cpl_ensure(aGeo, CPL_ERROR_NULL_INPUT, NULL);
1216 gt->
table = cpl_table_duplicate(aGeo->
table);
1237 cpl_table_delete(aGeo->
table);
1310 cpl_ensure(aSpots && aTrace, CPL_ERROR_NULL_INPUT, NULL);
1311 cpl_size nrow = cpl_table_get_nrow(aSpots);
1312 cpl_ensure(nrow > 10, CPL_ERROR_ILLEGAL_INPUT, NULL);
1314 CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
1315 const unsigned char ifu = cpl_table_get_column_min(aSpots,
"SubField"),
1316 ifu2 = cpl_table_get_column_max(aSpots,
"SubField");
1317 cpl_ensure(ifu == ifu2 && ifu >= 1 && ifu <= kMuseNumIFUs,
1318 CPL_ERROR_ILLEGAL_INPUT, NULL);
1319 const double kScale = cpl_table_get_column_mean(aSpots,
"ScaleFOV");
1320 cpl_ensure(cpl_table_get_column_stdev(aSpots,
"ScaleFOV") < 1e-10,
1321 CPL_ERROR_ILLEGAL_INPUT, NULL);
1323 cpl_boolean noinvertangle = getenv(
"MUSE_GEOMETRY_NO_INVERT_ANGLE")
1324 && atoi(getenv(
"MUSE_GEOMETRY_NO_INVERT_ANGLE")) > 0;
1325 double maskangle = 0., fmaskrot = 1.;
1326 if (getenv(
"MUSE_GEOMETRY_MASK_ROTATION")) {
1327 maskangle = atof(getenv(
"MUSE_GEOMETRY_MASK_ROTATION"));
1328 fmaskrot = cos(maskangle * CPL_MATH_RAD_DEG);
1329 cpl_msg_warning(__func__,
"Adapting to global mask rotation of %.4f deg "
1330 "(cos = %.4e)", maskangle, fmaskrot);
1332 double pinholedy = kMuseCUmpmDY;
1333 if (getenv(
"MUSE_GEOMETRY_PINHOLE_DY")) {
1334 pinholedy = atof(getenv(
"MUSE_GEOMETRY_PINHOLE_DY"));
1335 cpl_msg_warning(__func__,
"Using pinhole y distance of %f mm (instead of "
1336 "%f mm)", pinholedy, kMuseCUmpmDY);
1340 double *lbda = cpl_table_get_data_double(aSpots,
"lambda");
1341 cpl_vector *vlbda = cpl_vector_wrap(nrow, lbda);
1343 cpl_vector_unwrap(vlbda);
1344 int nlines = cpl_vector_get_size(lambdas);
1346 * kMuseCUmpmSpotsPerSlice * nlines,
1350 unsigned short nslice;
1351 for (nslice = 1; nslice <= kMuseSlicesPerCCD; nslice++) {
1358 for (iline = 0; iline < nlines; iline++) {
1359 double lambda = cpl_vector_get(lambdas, iline);
1361 unsigned char nspot, nslicespot = 0;
1362 double vposref = -DBL_MAX;
1363 for (nspot = 1; nspot <= kMuseCUmpmSpotsPerSlice; nspot++) {
1365 lambda, vposref, CPL_FALSE,
1371 double xcenter = 0., ycenter = 0.,
1372 vpos = 0., ftot = 0.;
1373 nrow = cpl_table_get_nrow(tspot);
1375 for (irow = 0; irow < nrow; irow++) {
1376 double flux = cpl_table_get(tspot,
"flux", irow, NULL);
1378 xcenter += cpl_table_get(tspot,
"xc", irow, NULL) * flux;
1379 ycenter += cpl_table_get(tspot,
"yc", irow, NULL) * flux;
1380 vpos += cpl_table_get(tspot,
"VPOS", irow, NULL) * flux;
1382 cpl_table_delete(tspot);
1384 cpl_msg_warning(__func__,
"Invalid integrated flux of spot %1hhu/%1hhu "
1385 "in slice %2hu of IFU %2hhu at wavelength %.3f: %e",
1386 nspot, nslicespot, nslice, ifu, lambda, ftot);
1396 double xcen = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_CENTER], ycenter,
1398 xl = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_LEFT], ycenter, NULL),
1399 xr = cpl_polynomial_eval_1d(ptrace[MUSE_TRACE_RIGHT], ycenter, NULL),
1401 cpl_msg_info(__func__,
"ifu %2hhu sliceccd %2d spot %1hhu/%1hhu lambda %.3f "
1402 "x/y %8.3f %8.3f (xcen %8.3f xwidth %6.3f) vpos %f flux %e",
1403 ifu, nslice, nspot, nslicespot, lambda, xcenter, ycenter,
1404 xcen, xwidth, vpos, ftot);
1405 cpl_table_set_int(gt->
table, MUSE_GEOTABLE_FIELD, irrow, ifu);
1406 cpl_table_set_int(gt->
table, MUSE_GEOTABLE_SKY, irrow,
1407 kMuseGeoSliceSky[nslice - 1]);
1408 cpl_table_set_int(gt->
table, MUSE_GEOTABLE_CCD, irrow, nslice);
1409 cpl_table_set_int(gt->
table,
"spot", irrow, nslicespot);
1412 unsigned char stack = nslice <= 12 ? 4 : (nslice <= 24 ? 3 : (nslice <= 36 ? 2 : 1));
1413 cpl_table_set_int(gt->
table,
"stack", irrow, stack);
1415 cpl_table_set_double(gt->
table,
"xc", irrow, xcenter);
1416 cpl_table_set_double(gt->
table,
"yc", irrow, ycenter);
1417 cpl_table_set_double(gt->
table,
"dxl", irrow, xcenter - xl);
1418 cpl_table_set_double(gt->
table,
"dxr", irrow, xr - xcenter);
1419 cpl_table_set_double(gt->
table,
"vpos", irrow, vpos);
1420 cpl_table_set_double(gt->
table,
"flux", irrow, ftot);
1421 cpl_table_set_double(gt->
table,
"lambda", irrow, lambda);
1424 cpl_table_set_invalid(gt->
table, MUSE_GEOTABLE_WIDTH, irrow);
1425 cpl_table_set_invalid(gt->
table, MUSE_GEOTABLE_ANGLE, irrow);
1429 if (nslicespot == kMuseCUmpmSpotsPerSlice) {
1431 int err1a, err2a, err1b, err2b;
1432 double xc1 = cpl_table_get_double(gt->
table,
"xc", irrow - 2, &err1a),
1433 xc2 = cpl_table_get_double(gt->
table,
"xc", irrow - 1, &err2a),
1434 vpos1 = cpl_table_get_double(gt->
table,
"vpos", irrow - 2, &err1b),
1435 vpos2 = cpl_table_get_double(gt->
table,
"vpos", irrow - 1, &err2b);
1436 if (!err1a && !err2a) {
1437 if (xcenter < xc2 || xc2 < xc1) {
1438 cpl_msg_warning(__func__,
"spots are not sorted left-to-right on "
1439 "the CCD (%f .. %f .. %f)!", xc1, xc2, xcenter);
1441 double dx = (xcenter - xc1) / 2.;
1444 double dxerr = sqrt((pow(xcenter - xc2 - dx, 2)
1445 + pow(xc2 - xc1 - dx, 2)) / 3.);
1452 double xreloffset = 0.;
1454 const double dxexpected = 26.04644 - 0.05537208 * ifu;
1455 double dx1 = xc2 - xc1,
1456 dx2 = xcenter - xc2,
1457 diff1 = fabs(dx1 - dxexpected),
1458 diff2 = fabs(dx2 - dxexpected),
1460 if (fmin(diff1, diff2) > 1.5) {
1462 }
else if (diff2 > diff1) {
1469 xreloffset = xwidth * kMuseCUmpmDX / fmaskrot
1470 * kMuseTypicalCubeSizeX * kScale / 60.
1471 * fabs(1. / dx - 1 / dxnew) / 2.;
1472 cpl_msg_warning(__func__,
"ifu %2hhu sliceccd %2d spot %1hhu/%1hhu "
1473 "lambda %.3f dx %.3f +/- %.3f (%.3f %.3f): dxerr "
1474 "is large, using a guess of %.3f +/- %.3f "
1475 "(expected was %.3f for this IFU), adding %.3f to"
1476 " xrel!", ifu, nslice, nspot, nslicespot, lambda,
1477 dx, dxerr, xc2-xc1, xcenter-xc2, dxnew, dxerr / 2.,
1478 dxexpected, xreloffset);
1482 cpl_table_fill_column_window_double(gt->
table,
"dx", irrow - 2, 3, dx);
1483 cpl_table_fill_column_window_double(gt->
table,
"dxerr", irrow - 2, 3, dxerr);
1488 double scale = kMuseCUmpmDX / fmaskrot / dx,
1489 width = xwidth * scale * kMuseTypicalCubeSizeX * kScale / 60.,
1490 werr = width * dxerr / dx;
1491 if (width > kMuseSliceLoLikelyWidth && width < kMuseSliceHiLikelyWidth) {
1492 cpl_table_fill_column_window_double(gt->
table, MUSE_GEOTABLE_WIDTH,
1493 irrow - 2, 3, width);
1494 cpl_table_fill_column_window_double(gt->
table, MUSE_GEOTABLE_WIDTH
"err",
1495 irrow - 2, 3, werr);
1497 cpl_msg_warning(__func__,
"ifu %2hhu slice %2d lambda %.3f: computed "
1498 "an unlikely width: %.3f +/- %.3f", ifu, nslice,
1499 lambda, width, werr);
1500 cpl_table_set_column_invalid(gt->
table, MUSE_GEOTABLE_WIDTH,
1502 cpl_table_set_column_invalid(gt->
table, MUSE_GEOTABLE_WIDTH
"err",
1508 double xrel = (xcen - xc1 + xreloffset) * scale,
1509 xrelerr = fabs(xrel * dxerr / dx);
1510 cpl_table_set_double(gt->
table,
"xrel", irrow - 2, xrel);
1511 cpl_table_set_double(gt->
table,
"xrelerr", irrow - 2, xrelerr);
1512 xrel = (xcen - xc2 + xreloffset) * scale;
1513 xrelerr = fabs(xrel * dxerr / dx);
1514 cpl_table_set_double(gt->
table,
"xrel", irrow - 1, xrel);
1515 cpl_table_set_double(gt->
table,
"xrelerr", irrow - 1, xrelerr);
1516 xrel = (xcen - xcenter + xreloffset) * scale;
1517 xrelerr = fabs(xrel * dxerr / dx);
1518 cpl_table_set_double(gt->
table,
"xrel", irrow, xrel);
1519 cpl_table_set_double(gt->
table,
"xrelerr", irrow, xrelerr);
1522 if (!err1b && !err2b) {
1525 double pdiff = fmax(vpos1, fmax(vpos2, vpos))
1526 - fmin(vpos1, fmin(vpos2, vpos));
1528 double pmean = (vpos1 + vpos2 + vpos) / 3.;
1529 if (vpos1 > pmean) {
1530 vpos1 -= pinholedy * fmaskrot;
1532 if (vpos2 > pmean) {
1533 vpos2 -= pinholedy * fmaskrot;
1536 vpos -= pinholedy * fmaskrot;
1538 double pdiff2 = fmax(vpos1, fmax(vpos2, vpos))
1539 - fmin(vpos1, fmin(vpos2, vpos));
1540 if (pdiff2 < pdiff) {
1541 cpl_msg_debug(__func__,
"Fixed max vpos diff from %f down to %f",
1544 double vpos1o = cpl_table_get_double(gt->
table,
"vpos", irrow - 2, NULL),
1545 vpos2o = cpl_table_get_double(gt->
table,
"vpos", irrow - 1, NULL),
1546 vpos3o = cpl_table_get_double(gt->
table,
"vpos", irrow, NULL);
1547 cpl_msg_warning(__func__,
"Large max vpos diff detected but "
1548 "not fixed! (original: %f %f %f, %f -> mean %f "
1549 "-> fixed: %f %f %f, %f)", vpos1o, vpos2o, vpos3o,
1550 pdiff, pmean, vpos1, vpos2, vpos, pdiff2);
1557 double f = 1. / kMuseCUmpmDX / fmaskrot,
1558 angle1 = atan((vpos2 - vpos1) * f) * CPL_MATH_DEG_RAD,
1559 angle2 = atan((vpos - vpos2) * f) * CPL_MATH_DEG_RAD,
1560 angle = (angle1 + angle2) / 2.;
1561 if (!noinvertangle) {
1567 double aerr = sqrt((pow(angle1 - angle, 2) + pow(angle2 - angle, 2)) / 3.);
1568 if (fabs(angle) < kMuseGeoSliceMaxAngle) {
1569 cpl_table_fill_column_window_double(gt->
table, MUSE_GEOTABLE_ANGLE,
1570 irrow - 2, 3, angle);
1571 cpl_table_fill_column_window_double(gt->
table, MUSE_GEOTABLE_ANGLE
"err",
1572 irrow - 2, 3, aerr);
1574 cpl_msg_warning(__func__,
"ifu %2hhu slice %2d lambda %.3f: computed "
1575 "an unlikely angle: %.3f +/- %.3f", ifu, nslice,
1576 lambda, angle, aerr);
1577 cpl_table_set_column_invalid(gt->
table, MUSE_GEOTABLE_ANGLE,
1579 cpl_table_set_column_invalid(gt->
table, MUSE_GEOTABLE_ANGLE
"err",
1586 double dx = cpl_table_get_double(gt->
table,
"dx", irrow - 1, NULL),
1587 dxl = cpl_table_get_double(gt->
table,
"dxl", irrow - 2, NULL),
1588 dxr = cpl_table_get_double(gt->
table,
"dxr", irrow, NULL);
1589 cpl_table_fill_column_window_double(gt->
table,
"dxl", irrow - 2, 3, dxl / dx);
1590 cpl_table_fill_column_window_double(gt->
table,
"dxr", irrow - 2, 3, dxr / dx);
1598 cpl_vector_delete(lambdas);
1599 cpl_table_set_size(gt->
table, irrow);
1601 cpl_table_and_selected_invalid(gt->
table, MUSE_GEOTABLE_WIDTH);
1602 cpl_table_or_selected_invalid(gt->
table, MUSE_GEOTABLE_ANGLE);
1603 cpl_table_erase_selected(gt->
table);
1630 const char *aCol,
const char *aColErr,
1631 double *aValue,
double *aError,
1632 double *aMedian,
double aSigma)
1634 const char func[] =
"muse_geo_determine_horizontal";
1635 if (!aSlice)
return -1;
1636 if (!aCol || !aValue)
return -1;
1639 const double *vv = cpl_table_get_data_double_const(aSlice, aCol),
1642 ve = cpl_table_get_data_double_const(aSlice, aColErr);
1646 cpl_size n = cpl_table_get_nrow(aSlice);
1647 cpl_vector *vtmp = cpl_vector_wrap(n, (
double *)cpl_table_get_data_double_const(aSlice, aCol));
1648 double median = cpl_table_get_column_median(aSlice, aCol),
1650 vlo = median - aSigma * mdev,
1651 vhi = median + aSigma * mdev;
1652 cpl_vector_unwrap(vtmp);
1653 cpl_msg_debug(func,
"%s/%s: median %f +/- %f --> %f...%f", aCol, aColErr,
1654 median, mdev, vlo, vhi);
1658 double value, sigma = mdev,
1660 cpl_vector *vmedian = NULL;
1666 vmedian = cpl_vector_new(n);
1672 for (i = 0; i < n; i++) {
1673 if (vv[i] < vlo || vv[i] > vhi) {
1678 value += vv[i] / ve[i];
1679 weight += 1. / ve[i];
1691 cpl_vector_set(vmedian, im++, vv[i]);
1696 for (i = 0; i < n; i++) {
1697 if (vv[i] < vlo || vv[i] > vhi) {
1701 wmse += pow(vv[i] - value, 2) / ve[i];
1703 wmse += pow(vv[i] - value, 2);
1707 cpl_msg_debug(
"vpos",
"%f %f %f -> %f", vv[i], vv[i] - value,
1708 pow(vv[i] - value, 2), wmse);
1714 cpl_vector_set_size(vmedian, im);
1715 *aMedian = cpl_vector_get_median(vmedian);
1716 cpl_vector_delete(vmedian);
1719 if (isnormal(weight) && isfinite(wmse)) {
1722 sigma = sqrt(ve ? (n - nrejected) / (weight*weight) : 0. + wmse);
1725 cpl_msg_debug(
"vpos",
"%f", sigma);
1730 if (isfinite(value)) {
1731 vlo = value - aSigma * sigma;
1732 vhi = value + aSigma * sigma;
1735 cpl_msg_debug(func,
"%s/%s: %f +/- %f (+/- %f) --> %f...%f (rej. %"
1736 CPL_SIZE_FORMAT
" / %"CPL_SIZE_FORMAT
")", aCol, aColErr,
1737 value, sqrt(wmse), sigma, vlo, vhi, nrejected, n);
1739 }
while (nrejected < n && (max > vhi || min < vlo));
1744 if (nrejected == n) {
1745 value = (vlo + vhi) / 2.;
1746 sigma = (vhi - vlo) / (2. * aSigma);
1748 cpl_msg_debug(func,
"%s/%s: %f +/- %f (ALL rej. %"CPL_SIZE_FORMAT
" / %"
1749 CPL_SIZE_FORMAT
")", aCol, aColErr, value, sigma, nrejected, n);
1784 double *aP,
double *aPE,
double *aPM,
1785 double aDY,
double aF)
1787 const char func[] =
"muse_geo_determine_horizontal";
1788 if (!aSlice)
return;
1789 if (!aP || !aPE || !aPM)
return;
1791 cpl_vector *vvpos = cpl_vector_wrap(cpl_table_get_nrow(aSlice),
1792 (
double *)cpl_table_get_data_double_const(aSlice,
1794 *aP = cpl_vector_get_mean(vvpos);
1795 *aPE = cpl_vector_get_stdev(vvpos);
1796 *aPM = cpl_vector_get_median_const(vvpos);
1800 cpl_vector_unwrap(vvpos);
1804 cpl_size n = cpl_vector_get_size(vvpos);
1805 cpl_vector *vpp = cpl_vector_duplicate(vvpos),
1806 *vres = cpl_vector_duplicate(vvpos),
1807 *vmedian = cpl_vector_new(n);
1808 cpl_vector_unwrap(vvpos);
1809 cpl_vector_fill(vmedian, *aPM);
1810 cpl_vector_subtract(vres, vmedian);
1811 cpl_vector_delete(vmedian);
1812 double min = cpl_vector_get_min(vres),
1813 max = cpl_vector_get_max(vres);
1814 cpl_msg_debug(func,
"vector of vpos values (%.4f +/- %.4f, %.4f) and "
1815 "residuals (%.4f ... %.4f), pinhole distance %.4f",
1816 *aP, *aPE, *aPM, min, max, aDY * aF);
1818 cpl_bivector *biv = cpl_bivector_wrap_vectors(vpp, vres);
1819 cpl_bivector_dump(biv, stdout);
1821 cpl_bivector_unwrap_vectors(biv);
1826 cpl_array *ares = cpl_array_wrap_double(cpl_vector_unwrap(vres), n);
1827 cpl_array_abs(ares);
1828 double amin = cpl_array_get_min(ares),
1829 amax = cpl_array_get_max(ares);
1830 cpl_boolean halfandhalf = fabs(aDY/2. - amin) < 0.01
1831 && fabs(aDY/2. - amax) < 0.01;
1832 cpl_array_delete(ares);
1834 double p2, pe2, pm2;
1836 double limit = fabs(0.9 * aDY * aF);
1838 fabs(min) < limit && fabs(max) < limit) {
1843 &p2, &pe2, &pm2, 2.);
1844 cpl_msg_debug(func,
"values after rejection vector of vpos values (%.4f "
1845 "+/- %.4f, %.4f), %"CPL_SIZE_FORMAT
" rejected", p2, pe2, pm2,
1850 cpl_size nfixed = 0;
1851 for (i = 0; i < n; i++) {
1852 double v = cpl_vector_get(vpp, i);
1854 cpl_vector_set(vpp, i, v - aDY * aF);
1858 p2 = cpl_vector_get_mean(vpp);
1859 pe2 = cpl_vector_get_stdev(vpp);
1860 pm2 = cpl_vector_get_median_const(vpp);
1861 cpl_msg_debug(func,
"fixed vector of vpos values (%.4f +/- %.4f, %.4f), "
1862 "%"CPL_SIZE_FORMAT
" fixed", p2, pe2, pm2, nfixed);
1864 cpl_vector_dump(vpp, stdout);
1868 cpl_vector_delete(vpp);
1930 cpl_ensure(aGeo && aGeo->
table, CPL_ERROR_NULL_INPUT, NULL);
1931 cpl_size nrow = cpl_table_get_nrow(aGeo->
table);
1932 cpl_ensure(nrow >= 50, CPL_ERROR_ILLEGAL_INPUT, NULL);
1934 CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
1935 const unsigned char ifu = cpl_table_get_column_min(aGeo->
table, MUSE_GEOTABLE_FIELD),
1936 ifu2 = cpl_table_get_column_max(aGeo->
table, MUSE_GEOTABLE_FIELD);
1937 cpl_ensure(ifu == ifu2 && ifu >= 1 && ifu <= kMuseNumIFUs,
1938 CPL_ERROR_ILLEGAL_INPUT, NULL);
1940 double maskangle = 0., fmaskrot = 1.;
1941 if (getenv(
"MUSE_GEOMETRY_MASK_ROTATION")) {
1942 maskangle = atof(getenv(
"MUSE_GEOMETRY_MASK_ROTATION"));
1943 fmaskrot = cos(maskangle * CPL_MATH_RAD_DEG);
1944 cpl_msg_warning(__func__,
"Adapting to global mask rotation of %.4f deg "
1945 "(cos = %.4e)", maskangle, fmaskrot);
1947 double pinholedy = kMuseCUmpmDY;
1948 if (getenv(
"MUSE_GEOMETRY_PINHOLE_DY")) {
1949 pinholedy = atof(getenv(
"MUSE_GEOMETRY_PINHOLE_DY"));
1950 cpl_msg_warning(__func__,
"Using pinhole y distance of %f mm (instead of "
1951 "%f mm)", pinholedy, kMuseCUmpmDY);
1953 cpl_boolean stdgap = getenv(
"MUSE_GEOMETRY_STD_GAP")
1954 && atoi(getenv(
"MUSE_GEOMETRY_STD_GAP")) > 0;
1956 cpl_msg_warning(__func__,
"Using old (standard) gap computation");
1958 cpl_msg_info(__func__,
"Using new (alternative) gap computation");
1960 const double kScaleX = kMuseTypicalCubeSizeX * aGeo->
scale / 60.;
1964 cpl_table_and_selected_int(gt->
table,
"spot", CPL_NOT_EQUAL_TO, 2);
1965 cpl_table_erase_selected(gt->
table);
1968 FILE *f = fopen(
"bla_horizontal.dat",
"w");
1970 cpl_table_dump(gt->
table, 0, 100000000, f);
1972 f = fopen(
"bla_lambdas.dat",
"w");
1974 cpl_table_dump(aGeo->
table, 0, 100000000, f);
1977 unsigned short nslice;
1978 for (nslice = 1; nslice <= kMuseSlicesPerCCD; nslice++) {
1980 cpl_table_select_all(gt->
table);
1981 cpl_table_and_selected_int(gt->
table,
"SliceCCD", CPL_EQUAL_TO, nslice);
1982 if (cpl_table_count_selected(gt->
table) < 1) {
1985 cpl_array *asel = cpl_table_where_selected(gt->
table);
1986 cpl_table *slice = cpl_table_extract_selected(gt->
table);
1988 cpl_table_dump(slice, 0, 1000, stdout);
1993 double a, ae, w, we, xr, xre, dxl, dxr;
1995 MUSE_GEOTABLE_ANGLE
"err", &a, &ae,
1998 MUSE_GEOTABLE_WIDTH
"err", &w, &we,
2006 cpl_errorstate ps = cpl_errorstate_get();
2007 double p = NAN, pe = -1, pm = NAN;
2009 pinholedy, fmaskrot);
2010 if (pe < 0 && !cpl_errorstate_is_equal(ps)) {
2012 cpl_errorstate_set(ps);
2015 cpl_msg_debug(__func__,
"IFU %2hhu stack %1d slice %2d / %2d "
2016 "angle %6.3f +/- %.3f deg width %.3f +/- %.3f pix "
2017 "xrel %.4f +/- %.4f vpos %.4f +/- %.4f (%.4f)", ifu,
2018 cpl_table_get_int(slice,
"stack", 0, NULL), nslice,
2019 cpl_table_get_int(slice,
"SliceSky", 0, NULL), a, ae, w, we,
2020 xr, xre, p, pe, pm);
2023 cpl_size irow = cpl_array_get_cplsize(asel, 0, NULL);
2024 cpl_array_delete(asel);
2025 cpl_table_set_double(gt->
table, MUSE_GEOTABLE_ANGLE, irow, a);
2026 cpl_table_set_double(gt->
table, MUSE_GEOTABLE_ANGLE
"err", irow, ae);
2027 cpl_table_set_double(gt->
table, MUSE_GEOTABLE_WIDTH, irow, w);
2028 cpl_table_set_double(gt->
table, MUSE_GEOTABLE_WIDTH
"err", irow, we);
2029 cpl_table_set_double(gt->
table,
"xrel", irow, xr);
2030 cpl_table_set_double(gt->
table,
"xrelerr", irow, xre);
2031 cpl_table_set_double(gt->
table,
"vpos", irow, p);
2032 cpl_table_set_double(gt->
table,
"vposerr", irow, pe);
2033 cpl_table_set_double(gt->
table,
"dxl", irow, dxl);
2034 cpl_table_set_double(gt->
table,
"dxr", irow, dxr);
2037 cpl_table_set_invalid(gt->
table,
"flux", irow);
2038 cpl_table_set_invalid(gt->
table,
"lambda", irow);
2039 cpl_table_set_invalid(gt->
table,
"xc", irow);
2040 cpl_table_set_invalid(gt->
table,
"yc", irow);
2041 cpl_table_unselect_row(gt->
table, irow);
2042 cpl_table_erase_selected(gt->
table);
2043 cpl_table_delete(slice);
2046 printf(
"intermediate result: weighted averages of angle, width, and xrel:\n");
2047 cpl_table_dump(gt->
table, 0, 1000000, stdout);
2057 const unsigned short nsoff = kMuseSlicesPerCCD / 4;
2058 for (nslice = 1 + nsoff; nslice <= 2*nsoff; nslice++) {
2060 cpl_table_unselect_all(gt->
table);
2061 cpl_table_or_selected_int(gt->
table,
"SliceSky", CPL_EQUAL_TO, nslice - nsoff);
2062 cpl_table_or_selected_int(gt->
table,
"SliceSky", CPL_EQUAL_TO, nslice);
2063 cpl_table_or_selected_int(gt->
table,
"SliceSky", CPL_EQUAL_TO, nslice + nsoff);
2064 cpl_table_or_selected_int(gt->
table,
"SliceSky", CPL_EQUAL_TO, nslice + 2*nsoff);
2066 cpl_table *ts = cpl_table_extract_selected(gt->
table);
2068 int irow, nrowts = cpl_table_get_nrow(ts),
2069 i1 = -1, i2 = -1, i3 = -1, i4 = -1;
2070 for (irow = 0; irow < nrowts; irow++) {
2073 switch (cpl_table_get_int(ts,
"stack", irow, NULL)) {
2074 case 1: i1 = irow;
break;
2075 case 2: i2 = irow;
break;
2076 case 3: i3 = irow;
break;
2077 case 4: i4 = irow;
break;
2081 if (i3 < 0 || i2 < 0) {
2082 char *msg = cpl_sprintf(
"For IFU %2hhu / row %2d in the slicer stacks "
2083 "(slice sky numbers %02d, %02d, %02d, %02d), at "
2084 "least one of the two middle stacks (%s/%s) is "
2085 "missing", ifu, nslice - nsoff,
2086 nslice - nsoff, nslice, nslice + nsoff, nslice + 2*nsoff,
2087 i3 < 0 ?
"left" :
"-", i2 < 0 ?
"right" :
"-");
2088 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND,
"%s", msg);
2089 cpl_msg_error(__func__,
"%s", msg);
2091 cpl_table_dump(ts, 0, 10000, stdout);
2092 cpl_table_delete(ts);
2096 double *xrel = cpl_table_get_data_double(ts,
"xrel"),
2097 *xrerr = cpl_table_get_data_double(ts,
"xrelerr"),
2098 *width = cpl_table_get_data_double(ts, MUSE_GEOTABLE_WIDTH),
2099 *werr = cpl_table_get_data_double(ts, MUSE_GEOTABLE_WIDTH
"err"),
2100 *pdxl = cpl_table_get_data_double(ts,
"dxl"),
2101 *pdxr = cpl_table_get_data_double(ts,
"dxr");
2103 cpl_table_duplicate_column(ts,
"width_mm",
2104 ts, MUSE_GEOTABLE_WIDTH);
2105 cpl_table_multiply_scalar(ts,
"width_mm", 1. / kScaleX);
2106 cpl_table_set_column_unit(ts,
"width_mm",
"mm");
2107 cpl_table_duplicate_column(ts,
"widtherr_mm",
2108 ts, MUSE_GEOTABLE_WIDTH
"err");
2109 cpl_table_multiply_scalar(ts,
"widtherr_mm", 1. / kScaleX);
2110 cpl_table_set_column_unit(ts,
"widtherr_mm",
"mm");
2111 double *wmm = cpl_table_get_data_double(ts,
"width_mm"),
2112 *werrmm = cpl_table_get_data_double(ts,
"widtherr_mm");
2114 double cgap1 = (3. * kMuseCUmpmDX / fmaskrot
2115 - (xrel[i3] + wmm[i3] / 2. + wmm[i2] / 2. - xrel[i2]))
2117 cgerr = sqrt(pow(xrerr[i3], 2) + pow(xrerr[i2], 2) +
2118 pow(werrmm[i3] / 2., 2) + pow(werrmm[i2] / 2., 2))
2120 cgap2 = kMuseCUmpmDX * (1. - pdxl[i3] - pdxr[i2])
2122 cgap = stdgap ? cgap1 : cgap2;
2124 cpl_msg_debug(__func__,
"cgap: %f, %f +/- %f", cgap1, cgap2, cgerr);
2128 if (cgap < 0 || cgap > 0.5) {
2129 cpl_msg_warning(__func__,
"For IFU %2hhu / row %2d in the slicer stacks "
2130 "(slice sky numbers %02d, %02d, %02d, %02d), the central "
2131 "gap is unlikely (%f), reset to %.2f pix", ifu,
2132 nslice - nsoff, nslice - nsoff, nslice, nslice + nsoff,
2133 nslice + 2*nsoff, cgap, kMuseGeoMiddleGap);
2134 cgerr += sqrt(fabs(cgap));
2135 cgap = kMuseGeoMiddleGap;
2139 cpl_table_set_double(ts, MUSE_GEOTABLE_X, i3, -(cgap / 2. + width[i3] / 2.));
2140 cpl_table_set_double(ts, MUSE_GEOTABLE_X, i2, cgap / 2. + width[i2] / 2.);
2141 cpl_table_set_double(ts, MUSE_GEOTABLE_X
"err", i3,
2142 sqrt(cgerr*cgerr + werr[i3]*werr[i3]) / 2.);
2143 cpl_table_set_double(ts, MUSE_GEOTABLE_X
"err", i2,
2144 sqrt(cgerr*cgerr + werr[i2]*werr[i2]) / 2.);
2146 double lgap = NAN, lgerr = NAN;
2150 lgerr = sqrt(pow(xrerr[i4], 2) + pow(xrerr[i3], 2) +
2151 pow(werrmm[i4] / 2., 2) + pow(werrmm[i3] / 2., 2))
2153 double lgap1 = (3. * kMuseCUmpmDX / fmaskrot
2154 - (xrel[i4] + wmm[i4] / 2. + wmm[i3] / 2. - xrel[i3]))
2156 lgap2 = kMuseCUmpmDX * (1. - pdxl[i4] - pdxr[i3])
2158 lgap = stdgap ? lgap1 : lgap2;
2160 cpl_msg_debug(__func__,
"lgap: %f, %f +/- %f", lgap1, lgap2, lgerr);
2162 if (lgap < 0 || lgap > 0.5) {
2163 cpl_msg_warning(__func__,
"For IFU %2hhu / row %2d in the slicer stacks"
2164 " (slice sky numbers %02d, %02d, %02d, %02d), the left "
2165 "gap is unlikely (%f), reset to %.2f pix", ifu,
2166 nslice - nsoff, nslice - nsoff, nslice, nslice + nsoff,
2167 nslice + 2*nsoff, lgap, kMuseGeoOuterGap);
2168 lgerr += sqrt(fabs(lgap));
2169 lgap = kMuseGeoOuterGap;
2171 cpl_table_set_double(ts, MUSE_GEOTABLE_X, i4,
2172 -(cgap / 2. + width[i3] + lgap + width[i4] / 2.));
2173 cpl_table_set_double(ts, MUSE_GEOTABLE_X
"err", i4,
2174 sqrt(cgerr*cgerr / 4. + werr[i3]*werr[i3]
2175 + lgerr*lgerr + werr[i4]*werr[i4] / 4.));
2177 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND,
"For IFU %2hhu /"
2178 " row %2d in the slicer stacks (slice sky numbers"
2179 " %02d, %02d, %02d, %02d), the leftmost stack is "
2180 "missing", ifu, nslice - nsoff,
2181 nslice - nsoff, nslice, nslice + nsoff, nslice + 2*nsoff);
2184 double rgap = NAN, rgerr = NAN;
2187 rgerr = sqrt(pow(xrerr[i2], 2) + pow(xrerr[i1], 2) +
2188 pow(werrmm[i2] / 2., 2) + pow(werrmm[i1] / 2., 2))
2190 double rgap1 = (3. * kMuseCUmpmDX / fmaskrot
2191 - (xrel[i2] + wmm[i2] / 2. + wmm[i1] / 2. - xrel[i1]))
2193 rgap2 = kMuseCUmpmDX * (1. - pdxl[i2] - pdxr[i1])
2195 rgap = stdgap ? rgap1 : rgap2;
2197 cpl_msg_debug(__func__,
"rgap: %f, %f +/- %f", rgap1, rgap2, rgerr);
2199 if (rgap < 0 || rgap > 0.5) {
2200 cpl_msg_warning(__func__,
"For IFU %2hhu / row %2d in the slicer stacks"
2201 " (slice sky numbers %02d, %02d, %02d, %02d), the right"
2202 " gap is unlikely (%f), reset to %.2f pix", ifu,
2203 nslice - nsoff, nslice - nsoff, nslice, nslice + nsoff,
2204 nslice + 2*nsoff, rgap, kMuseGeoOuterGap);
2205 rgerr += sqrt(fabs(rgap));
2206 rgap = kMuseGeoOuterGap;
2208 cpl_table_set_double(ts, MUSE_GEOTABLE_X, i1,
2209 cgap / 2. + width[i2] + rgap + width[i1] / 2.);
2210 cpl_table_set_double(ts, MUSE_GEOTABLE_X
"err", i1,
2211 sqrt(cgerr*cgerr / 4. + werr[i2]*werr[i2]
2212 + rgerr*rgerr + werr[i1]*werr[i1] / 4.));
2214 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND,
"For IFU %2hhu /"
2215 " row %2d in the slicer stacks (slice sky numbers"
2216 " %02d, %02d, %02d, %02d), the rightmost stack is"
2217 " missing", ifu, nslice - nsoff,
2218 nslice - nsoff, nslice, nslice + nsoff, nslice + 2*nsoff);
2220 cpl_msg_debug(__func__,
"IFU %2hhu row %2d gaps (slice sky numbers %02d, "
2221 "%02d, %02d, %02d): central %.3f +/- %.3f pix, left %.3f +/- "
2222 "%.3f pix, right %.3f +/- %.3f pix", ifu, nslice - nsoff,
2223 nslice - nsoff, nslice, nslice + nsoff, nslice + 2*nsoff,
2224 cgap, cgerr, lgap, lgerr, rgap, rgerr);
2227 cpl_table_erase_column(ts,
"width_mm");
2228 cpl_table_erase_column(ts,
"widtherr_mm");
2232 cpl_table_erase_selected(gt->
table);
2233 cpl_table_insert(gt->
table, ts, cpl_table_get_nrow(gt->
table));
2234 cpl_table_delete(ts);
2238 cpl_propertylist *order = cpl_propertylist_new();
2239 cpl_propertylist_append_bool(order,
"stack", CPL_TRUE);
2240 cpl_propertylist_append_bool(order,
"SliceSky", CPL_FALSE);
2241 cpl_table_sort(gt->
table, order);
2242 cpl_propertylist_delete(order);
2244 printf(
"intermediate result: only the y position is still missing:\n");
2245 cpl_table_dump(gt->
table, 0, 1000000, stdout);
2266 static unsigned char
2267 muse_geo_select_reference(
const muse_geo_table *aGeo,
unsigned short *aSlice)
2269 unsigned char ifu = 0;
2270 unsigned short slice = 0;
2272 cpl_table *geo = cpl_table_duplicate(aGeo->
table);
2273 cpl_table_unselect_all(geo);
2274 cpl_table_or_selected_int(geo, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO, 12);
2275 cpl_table_and_selected_int(geo, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, 24);
2276 int nsel = cpl_table_count_selected(geo);
2281 unsigned char testifu = 13,
2283 short testoffset = 1;
2284 while (ifu == 0 && slice == 0) {
2285 cpl_table_unselect_all(geo);
2286 cpl_table_or_selected_int(geo, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO, testifu);
2287 cpl_table_and_selected_int(geo, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, testslice);
2288 nsel = cpl_table_count_selected(geo);
2293 testifu += testoffset;
2294 if (testifu > kMuseNumIFUs) {
2299 cpl_table_delete(geo);
2361 cpl_ensure_code(aGeo && aGeo->
table && aSpots, CPL_ERROR_NULL_INPUT);
2362 cpl_size nrow = cpl_table_get_nrow(aGeo->
table);
2363 cpl_ensure_code(nrow >= 50, CPL_ERROR_ILLEGAL_INPUT);
2365 CPL_ERROR_INCOMPATIBLE_INPUT);
2367 CPL_ERROR_INCOMPATIBLE_INPUT);
2368 const unsigned char ifu1 = cpl_table_get_column_min(aGeo->
table, MUSE_GEOTABLE_FIELD),
2369 ifu2 = cpl_table_get_column_max(aGeo->
table, MUSE_GEOTABLE_FIELD);
2370 if (!ifu1 || !ifu2 || ifu1 == ifu2) {
2371 return cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND,
2372 "input geometry table contains data of IFUs "
2373 "%2hhu .. %2hhu", ifu1, ifu2);
2375 cpl_ensure_code(cpl_table_get_column_stdev(aSpots,
"ScaleFOV") < 1e-10,
2376 CPL_ERROR_ILLEGAL_INPUT);
2377 cpl_array *hoffsets = NULL;
2378 if (getenv(
"MUSE_GEOMETRY_HORI_OFFSETS")) {
2380 getenv(
"MUSE_GEOMETRY_HORI_OFFSETS"),
",");
2381 cpl_msg_warning(__func__,
"Overriding horizontal offsets, found %"
2382 CPL_SIZE_FORMAT
" values!", cpl_array_get_size(hoffsets));
2384 const double kScaleX = kMuseTypicalCubeSizeX * aGeo->
scale / 60.;
2387 cpl_table_new_column(aSpots, MUSE_GEOTABLE_SKY, CPL_TYPE_INT);
2388 cpl_table_new_column(aSpots,
"stack", CPL_TYPE_INT);
2389 cpl_size ispot, nspots = cpl_table_get_nrow(aSpots);
2390 for (ispot = 0; ispot < nspots; ispot++) {
2391 int nslice = cpl_table_get_int(aSpots,
"SliceCCD", ispot, NULL);
2392 cpl_table_set(aSpots, MUSE_GEOTABLE_SKY, ispot, kMuseGeoSliceSky[nslice - 1]);
2393 unsigned char stack = nslice <= 12 ? 4 : (nslice <= 24 ? 3 : (nslice <= 36 ? 2 : 1));
2394 cpl_table_set_int(aSpots,
"stack", ispot, stack);
2399 const unsigned short nsoff = kMuseSlicesPerCCD / 4;
2400 cpl_table_unselect_all(aSpots);
2401 cpl_table_or_selected_int(aSpots, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, 24 - nsoff + 1);
2402 cpl_table_or_selected_int(aSpots, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, 24);
2403 cpl_table_or_selected_int(aSpots, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, 24 + 1);
2404 cpl_table_or_selected_int(aSpots, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, 24 + nsoff);
2406 cpl_msg_debug(__func__,
"All top/bottom spots selected: %"
2407 CPL_SIZE_FORMAT, cpl_table_count_selected(aSpots));
2410 cpl_table_and_selected_double(aSpots,
"flux", CPL_NOT_LESS_THAN, 500.);
2412 cpl_msg_debug(__func__,
"All bright top/bottom spots selected: %"
2413 CPL_SIZE_FORMAT, cpl_table_count_selected(aSpots));
2416 cpl_table_and_selected_int(aSpots,
"SpotNo", CPL_EQUAL_TO, 2);
2418 cpl_msg_debug(__func__,
"All bright and central top/bottom spots selected: %"
2419 CPL_SIZE_FORMAT, cpl_table_count_selected(aSpots));
2421 cpl_table *tbspots = cpl_table_extract_selected(aSpots);
2422 cpl_msg_debug(__func__,
"All spots: %"CPL_SIZE_FORMAT
", top/bottom spots to "
2423 "work with: %"CPL_SIZE_FORMAT, nspots, cpl_table_get_nrow(tbspots));
2424 nspots = cpl_table_get_nrow(tbspots);
2427 int *ifus = cpl_table_get_data_int(aGeo->
table, MUSE_GEOTABLE_FIELD),
2428 *slices = cpl_table_get_data_int(aGeo->
table, MUSE_GEOTABLE_SKY);
2429 double *xpos = cpl_table_get_data_double(aGeo->
table, MUSE_GEOTABLE_X),
2430 *xerr = cpl_table_get_data_double(aGeo->
table, MUSE_GEOTABLE_X
"err"),
2431 *xrel = cpl_table_get_data_double(aGeo->
table,
"xrel");
2436 for (nifu = ifu1; nifu < ifu2; nifu++) {
2438 cpl_table_unselect_all(tbspots);
2439 cpl_table_or_selected_int(tbspots, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, 24);
2440 cpl_table_or_selected_int(tbspots, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, 24 + nsoff);
2441 cpl_table_and_selected_int(tbspots, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO, nifu);
2442 cpl_table *xspots = cpl_table_extract_selected(tbspots);
2444 cpl_table_unselect_all(tbspots);
2445 cpl_table_or_selected_int(tbspots, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, 24 - nsoff + 1);
2446 cpl_table_or_selected_int(tbspots, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, 24 + 1);
2447 cpl_table_and_selected_int(tbspots, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO, nifu + 1);
2448 cpl_table *tmp = cpl_table_extract_selected(tbspots);
2449 cpl_table_insert(xspots, tmp, cpl_table_get_nrow(xspots));
2450 cpl_table_delete(tmp);
2451 int nxspots = cpl_table_get_nrow(xspots);
2454 cpl_propertylist *order = cpl_propertylist_new();
2455 cpl_propertylist_append_bool(order,
"lambda", CPL_FALSE);
2456 cpl_propertylist_append_bool(order,
"VPOS", CPL_FALSE);
2457 cpl_propertylist_append_bool(order,
"SliceSky", CPL_FALSE);
2458 cpl_table_sort(xspots, order);
2459 cpl_propertylist_delete(order);
2461 printf(
"ifu %2hhu:\n", nifu);
2462 cpl_table_dump(xspots, 0, nxspots, stdout);
2467 cpl_array *apos2 = cpl_array_new(nxspots, CPL_TYPE_DOUBLE),
2468 *apos3 = cpl_array_new(nxspots, CPL_TYPE_DOUBLE);
2469 int idx2 = 0, idx3 = 0;
2472 cpl_vector *vtmp = cpl_vector_wrap(nxspots,
2473 cpl_table_get_data_double(xspots,
"lambda")),
2475 cpl_vector_unwrap(vtmp);
2476 vtmp = cpl_vector_wrap(nxspots, cpl_table_get_data_double(xspots,
"VPOS"));
2478 cpl_vector_unwrap(vtmp);
2479 int ilambda, nlambda = cpl_vector_get_size(lambdas);
2480 for (ilambda = 0; ilambda < nlambda; ilambda++) {
2481 double lambda = cpl_vector_get(lambdas, ilambda);
2483 int ipos, npos = cpl_vector_get_size(positions);
2484 for (ipos = 0; ipos < npos; ipos++) {
2485 double vpos = cpl_vector_get(positions, ipos);
2488 for (nstack = 3; nstack >= 2; nstack--) {
2489 cpl_table_select_all(xspots);
2490 cpl_table_and_selected_double(xspots,
"lambda", CPL_EQUAL_TO, lambda);
2491 cpl_table_and_selected_double(xspots,
"VPOS", CPL_EQUAL_TO, vpos);
2492 cpl_table_and_selected_int(xspots,
"stack", CPL_EQUAL_TO, nstack);
2493 int nsel = cpl_table_count_selected(xspots);
2496 printf(
"ifu %2hhu, lambda = %f, VPOS = %f, stack = %d: %d selected rows!\n",
2497 nifu, lambda, vpos, nstack, nsel);
2502 cpl_table *common = cpl_table_extract_selected(xspots);
2503 order = cpl_propertylist_new();
2504 cpl_propertylist_append_bool(order,
"SubField", CPL_FALSE);
2505 cpl_propertylist_append_bool(order,
"SliceSky", CPL_FALSE);
2506 cpl_table_sort(common, order);
2507 cpl_propertylist_delete(order);
2508 int nselthis = cpl_table_and_selected_int(common,
"SubField",
2509 CPL_EQUAL_TO, nifu);
2510 cpl_table_select_all(common);
2511 int nselnext = cpl_table_and_selected_int(common,
"SubField",
2512 CPL_EQUAL_TO, nifu + 1);
2513 if (nselthis != 1 || nselnext != 1) {
2515 printf(
"\nifu %2hhu, lambda = %f, VPOS = %f, stack = %d: "
2516 "%d rows of IFU %2hhu, %d rows of IFU %2hhu!\n",
2517 nifu, lambda, vpos, nstack, nselthis, nifu, nselnext, nifu + 1);
2520 cpl_table_delete(common);
2526 double xdiff1 = cpl_table_get(common,
"dxcen", 0, NULL),
2527 xdiff2 = cpl_table_get(common,
"dxcen", 1, NULL),
2528 twidth1 = cpl_table_get(common,
"twidth", 0, NULL),
2529 twidth2 = cpl_table_get(common,
"twidth", 1, NULL);
2530 cpl_table_unselect_all(aGeo->
table);
2531 cpl_table_or_selected_int(aGeo->
table, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO,
2532 cpl_table_get_int(common,
"SubField", 0, NULL));
2533 cpl_table_or_selected_int(aGeo->
table, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO,
2534 cpl_table_get_int(common,
"SliceSky", 0, NULL));
2535 cpl_array *sel = cpl_table_where_selected(aGeo->
table);
2536 double width1 = cpl_table_get_double(aGeo->
table, MUSE_GEOTABLE_WIDTH,
2537 cpl_array_get(sel, 0, NULL), NULL);
2538 cpl_array_delete(sel);
2539 cpl_table_unselect_all(aGeo->
table);
2540 cpl_table_or_selected_int(aGeo->
table, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO,
2541 cpl_table_get_int(common,
"SubField", 1, NULL));
2542 cpl_table_or_selected_int(aGeo->
table, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO,
2543 cpl_table_get_int(common,
"SliceSky", 1, NULL));
2544 sel = cpl_table_where_selected(aGeo->
table);
2545 double width2 = cpl_table_get_double(aGeo->
table, MUSE_GEOTABLE_WIDTH,
2546 cpl_array_get(sel, 0, NULL), NULL);
2547 cpl_array_delete(sel);
2549 printf(
"\nifu %2hhu, lambda = %f, VPOS = %f, stack + %d, twidths: %f / %f, geowidths: %f / %f:\n",
2550 nifu, lambda, vpos, nstack, twidth1, twidth2, width1, width2);
2551 cpl_table_dump(common, 0, 100000, stdout);
2552 printf(
"==> xdiff = %f pix, %f pix (corrected)\n", xdiff1 - xdiff2,
2553 xdiff1 * width1 / twidth1 - xdiff2 * width2 / twidth2);
2556 double xdiff = xdiff1 * width1 / twidth1 - xdiff2 * width2 / twidth2;
2558 cpl_array_set(apos3, idx3++, xdiff);
2561 cpl_array_set(apos2, idx2++, xdiff);
2563 cpl_table_delete(common);
2569 #define HIST_BIN_WIDTH 0.1
2575 cpl_bivector_delete(hist2);
2576 cpl_bivector_delete(hist3);
2577 cpl_array *apos = cpl_array_new(0, cpl_array_get_type(apos2));
2578 cpl_array_insert(apos, apos2, 0);
2579 cpl_array_insert(apos, apos3, cpl_array_get_size(apos));
2581 char *fn = cpl_sprintf(
"apos2_%02hhuto%02hhu.pos", nifu, nifu+1);
2582 FILE *fp = fopen(fn,
"w");
2583 fprintf(fp,
"# apos2 %2hhu to %2hhu:\n#", nifu, nifu+1);
2584 cpl_array_dump(apos2, 0, 1000000, fp);
2587 fn = cpl_sprintf(
"apos3_%02hhuto%02hhu.pos", nifu, nifu+1);
2588 fp = fopen(fn,
"w");
2589 fprintf(fp,
"# apos3 %2hhu to %2hhu:\n#", nifu, nifu+1);
2590 cpl_array_dump(apos3, 0, 1000000, fp);
2593 fn = cpl_sprintf(
"apos_%02hhuto%02hhu.pos", nifu, nifu+1);
2594 fp = fopen(fn,
"w");
2595 fprintf(fp,
"# apos %2hhu to %2hhu:\n#", nifu, nifu+1);
2596 cpl_array_dump(apos, 0, 1000000, fp);
2602 double mean2 = cpl_array_get_mean(apos2),
2603 stdev2 = cpl_array_get_stdev(apos2),
2604 var2 = stdev2 * stdev2,
2605 mean3 = cpl_array_get_mean(apos3),
2606 stdev3 = cpl_array_get_stdev(apos3),
2607 var3 = stdev3 * stdev3,
2608 mean = (mean2 + mean3) / 2.,
2609 wmean = (mean2 / var2 + mean3 / var3) / (1. / var2 + 1. / var3),
2610 wstdev = sqrt(1. / (1. / var2 + 1. / var3));
2611 cpl_array_delete(apos2);
2612 cpl_array_delete(apos3);
2613 cpl_msg_debug(__func__,
"IFU %2hhu to IFU %2hhu: %6.3f +/- %5.3f pix "
2614 "[stack 3: %6.3f +/- %5.3f, stack2: %6.3f +/- %5.3f ==> %6.3f"
2615 " or %6.3f +/- %5.3f (%6.3f)]", nifu, nifu + 1, wmean, wstdev,
2616 mean3, stdev3, mean2, stdev2, mean,
2617 cpl_array_get_mean(apos), cpl_array_get_stdev(apos),
2618 cpl_array_get_median(apos));
2619 cpl_array_delete(apos);
2620 cpl_vector_delete(lambdas);
2621 cpl_vector_delete(positions);
2622 cpl_table_delete(xspots);
2625 for (irow = 0; irow < nrow; irow++) {
2626 if (ifus[irow] >= nifu + 1) {
2627 xpos[irow] -= wmean;
2628 xerr[irow] = sqrt(xerr[irow]*xerr[irow] + wstdev*wstdev);
2629 xrel[irow] += wmean / kScaleX;
2633 cpl_table_delete(tbspots);
2636 for (nifu = ifu1; nifu < ifu2; nifu++) {
2639 if (cpl_array_get_size(hoffsets) >= nifu) {
2640 const char *sdiff = cpl_array_get_string(hoffsets, nifu - 1);
2642 xdiff = atof(sdiff);
2645 cpl_msg_debug(__func__,
"Subtracting extra %7.4f pix from IFU %hhu onwards",
2647 for (irow = 0; irow < nrow; irow++) {
2648 if (ifus[irow] >= nifu + 1) {
2649 xpos[irow] -= xdiff;
2656 printf(
"%s after correcting %hhu:\n", __func__, nifu + 1);
2657 cpl_table_dump(aGeo->
table, 0, 1000000, stdout);
2661 cpl_array_delete(hoffsets);
2666 unsigned short sliceref;
2667 const unsigned char ifuref = muse_geo_select_reference(aGeo, &sliceref);
2668 for (irow = 0; irow < nrow; irow++) {
2669 if (ifus[irow] == ifuref &&
2670 (slices[irow] == sliceref || slices[irow] == sliceref + 12)) {
2675 cpl_msg_debug(__func__,
"Reference point (ifu %hhu, slicesky %hu/%hu) "
2676 "currently centered at %f pix, correcting this offset", ifuref,
2677 sliceref, sliceref + 12, xref);
2678 cpl_table_subtract_scalar(aGeo->
table, MUSE_GEOTABLE_X, xref);
2680 return CPL_ERROR_NONE;
2737 cpl_ensure(aGeo && aGeo->
table, CPL_ERROR_NULL_INPUT, NULL);
2738 int nrow = cpl_table_get_nrow(aGeo->
table);
2739 cpl_ensure(nrow >= 10, CPL_ERROR_ILLEGAL_INPUT, NULL);
2741 CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
2742 int spotmin = cpl_table_get_column_min(aGeo->
table,
"spot"),
2743 spotmax = cpl_table_get_column_max(aGeo->
table,
"spot");
2744 cpl_ensure(spotmin == spotmax, CPL_ERROR_ILLEGAL_INPUT, NULL);
2745 const double kScaleY = 60. / aGeo->
scale / kMuseTypicalCubeSizeY;
2749 cpl_table_erase_column(gt->
table,
"dxerr");
2750 cpl_table_erase_column(gt->
table,
"dxl");
2751 cpl_table_erase_column(gt->
table,
"dxr");
2752 cpl_table_erase_column(gt->
table,
"xc");
2753 cpl_table_erase_column(gt->
table,
"yc");
2754 cpl_table_erase_column(gt->
table,
"dx");
2755 cpl_table_erase_column(gt->
table,
"flux");
2756 cpl_table_erase_column(gt->
table,
"lambda");
2760 cpl_table_fill_column_window_double(gt->
table, MUSE_GEOTABLE_Y, 0, nrow, 0.);
2761 cpl_table_add_columns(gt->
table, MUSE_GEOTABLE_Y,
"vpos");
2762 cpl_table_set_column_unit(gt->
table, MUSE_GEOTABLE_Y,
"mm");
2763 cpl_table_fill_column_window_double(gt->
table, MUSE_GEOTABLE_Y
"err", 0, nrow, 0.);
2764 cpl_table_add_columns(gt->
table, MUSE_GEOTABLE_Y
"err",
"vposerr");
2765 cpl_table_set_column_unit(gt->
table, MUSE_GEOTABLE_Y
"err",
"mm");
2767 printf(
"\nfull geometry table, with absolute \"y\" [mm]:\n");
2768 cpl_table_dump(gt->
table, 0, nrow, stdout);
2772 double maskangle = 0., fmaskrot = 1.;
2773 if (getenv(
"MUSE_GEOMETRY_MASK_ROTATION")) {
2774 maskangle = atof(getenv(
"MUSE_GEOMETRY_MASK_ROTATION"));
2775 fmaskrot = cos(maskangle * CPL_MATH_RAD_DEG);
2776 cpl_msg_warning(__func__,
"Adapting to global mask rotation of %.4f deg "
2777 "(cos = %.4e)", maskangle, fmaskrot);
2779 double pinholedy = kMuseCUmpmDY;
2780 if (getenv(
"MUSE_GEOMETRY_PINHOLE_DY")) {
2781 pinholedy = atof(getenv(
"MUSE_GEOMETRY_PINHOLE_DY"));
2782 cpl_msg_warning(__func__,
"Using pinhole y distance of %f mm (instead of "
2783 "%f mm)", pinholedy, kMuseCUmpmDY);
2785 cpl_boolean printgoing = getenv(
"MUSE_DEBUG_GEO_VERTICAL")
2786 && atoi(getenv(
"MUSE_DEBUG_GEO_VERTICAL")) > 0;
2788 unsigned short middleSlice;
2789 const unsigned char middleIFU = muse_geo_select_reference(aGeo, &middleSlice);
2790 cpl_msg_info(__func__,
"Using IFU %hhu / SliceSky %hu as reference",
2791 middleIFU, middleSlice);
2792 double ycentral = NAN,
2793 ymax = -DBL_MAX, ymin = DBL_MAX;
2795 for (irow = 0; irow < nrow; irow++) {
2796 unsigned char ifu = cpl_table_get_int(gt->
table, MUSE_GEOTABLE_FIELD, irow, NULL);
2797 unsigned short slice = cpl_table_get_int(gt->
table, MUSE_GEOTABLE_SKY, irow, NULL);
2798 double y = cpl_table_get_double(gt->
table, MUSE_GEOTABLE_Y, irow, NULL);
2799 if (ifu == middleIFU && slice == middleSlice) {
2810 if (!isfinite(ycentral)) {
2811 ycentral = (ymin + ymax) / 2.;
2812 cpl_msg_info(__func__,
"Averaged the y range to ycentral = %f pix", ycentral);
2814 cpl_msg_info(__func__,
"Found IFU %hhu, slice %hu, using its y value as "
2815 "ycentral = %f pix", middleIFU, middleSlice, ycentral);
2817 cpl_table_subtract_scalar(gt->
table, MUSE_GEOTABLE_Y, ycentral);
2818 const unsigned short nsoff = kMuseSlicesPerCCD / 4,
2819 nstack[] = { 3, 2, 4, 1, 0 };
2821 for (i = 0; nstack[i] > 0; i++) {
2823 cpl_table_unselect_all(gt->
table);
2824 cpl_table_or_selected_int(gt->
table,
"stack", CPL_EQUAL_TO, nstack[i]);
2825 cpl_table *tstack = cpl_table_extract_selected(gt->
table);
2826 int ntsrow = cpl_table_get_nrow(tstack);
2828 cpl_propertylist *sorting = cpl_propertylist_new();
2829 cpl_propertylist_append_bool(sorting, MUSE_GEOTABLE_FIELD, CPL_FALSE);
2830 cpl_propertylist_append_bool(sorting, MUSE_GEOTABLE_SKY, CPL_FALSE);
2831 cpl_table_sort(tstack, sorting);
2832 cpl_propertylist_delete(sorting);
2834 const unsigned short refslice = middleSlice - (nstack[i] - 3) * nsoff;
2835 cpl_table_select_all(tstack);
2836 cpl_table_and_selected_int(tstack, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO, middleIFU);
2837 cpl_table_and_selected_int(tstack, MUSE_GEOTABLE_SKY, CPL_EQUAL_TO, refslice);
2838 if (cpl_table_count_selected(tstack) <= 0) {
2839 char *msg = cpl_sprintf(
"reference slice %hu of reference IFU %hhu not "
2840 "found in slicer stack %hu!", refslice,
2841 middleIFU, nstack[i]);
2842 cpl_msg_error(__func__,
"%s", msg);
2843 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND,
"%s", msg);
2845 cpl_table_delete(tstack);
2848 cpl_table_erase_selected(gt->
table);
2849 cpl_array *asel = cpl_table_where_selected(tstack);
2850 int iref = cpl_array_get(asel, 0, NULL);
2851 cpl_array_delete(asel);
2852 double yref = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y, iref, NULL);
2853 cpl_msg_info(__func__,
"reference slice %hu of reference IFU %hhu found at row "
2854 "%d in slicer stack %hu!", refslice, middleIFU, iref, nstack[i]);
2858 if (fabs(yref) > pinholedy / 2.) {
2859 cpl_msg_info(__func__,
"%s vertical pinhole distance (%f) to "
2860 "recenter stack %hu", yref < 0. ?
"adding" :
"subtracting",
2861 pinholedy, nstack[i]);
2862 cpl_table_add_scalar(tstack, MUSE_GEOTABLE_Y, yref < 0. ? pinholedy : -pinholedy);
2863 yref = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y, iref, NULL);
2867 cpl_table_new_column(tstack,
"dy", CPL_TYPE_DOUBLE);
2868 cpl_table_set_column_unit(tstack,
"dy",
"mm");
2869 cpl_table_set_column_format(tstack,
"dy",
"%9.5f");
2870 cpl_table_set_double(tstack,
"dy", iref, yref);
2872 cpl_table_duplicate_column(tstack,
"ycopy", tstack, MUSE_GEOTABLE_Y);
2873 cpl_table_set_double(tstack, MUSE_GEOTABLE_Y, iref, yref);
2874 double yprev = cpl_table_get_double(tstack,
"ycopy", iref, NULL),
2875 dyoffset = pinholedy * fmaskrot,
2878 for (irow = iref - 1, iprev = iref; irow >= 0; iprev = irow, irow--) {
2880 int difu = cpl_table_get_int(tstack, MUSE_GEOTABLE_FIELD, iprev, NULL)
2881 - cpl_table_get_int(tstack, MUSE_GEOTABLE_FIELD, irow, NULL),
2882 dslice = cpl_table_get_int(tstack, MUSE_GEOTABLE_SKY, iprev, NULL)
2883 - cpl_table_get_int(tstack, MUSE_GEOTABLE_SKY, irow, NULL);
2887 double y = cpl_table_get_double(tstack,
"ycopy", irow, NULL),
2889 dexpect = dslice * kScaleY,
2890 dratio = dy / dexpect,
2893 dycor = 2 * dyoffset;
2894 }
else if (dratio < -2.) {
2896 }
else if (dratio > 5.) {
2900 cpl_msg_debug(__func__,
"going back: %d %d, %f %f --> diff %9.6f "
2901 "expected %f ratio %9.6f --> dy = %f", difu, dslice,
2902 yprev, y, dy, dexpect, dratio, dy + dycor);
2905 if (abs(difu) > 1) {
2906 double gap = abs(difu) * kMuseGeoIFUVGap;
2908 cpl_msg_warning(__func__,
"%d missing IFUs, subtracted %f from dy",
2909 abs(difu) - 1, gap);
2911 cpl_table_set_double(tstack,
"dy", irow, dy);
2913 cpl_table_set_double(tstack, MUSE_GEOTABLE_Y, irow, ysum);
2918 yprev = cpl_table_get_double(tstack,
"ycopy", iref, NULL);
2920 for (irow = iref + 1, iprev = iref; irow < ntsrow; iprev = irow, irow++) {
2921 int difu = cpl_table_get_int(tstack, MUSE_GEOTABLE_FIELD, iprev, NULL)
2922 - cpl_table_get_int(tstack, MUSE_GEOTABLE_FIELD, irow, NULL),
2923 dslice = cpl_table_get_int(tstack, MUSE_GEOTABLE_SKY, iprev, NULL)
2924 - cpl_table_get_int(tstack, MUSE_GEOTABLE_SKY, irow, NULL);
2928 double y = cpl_table_get_double(tstack,
"ycopy", irow, NULL),
2930 dexpect = -dslice * kScaleY,
2931 dratio = dy / dexpect,
2934 dycor = -2 * dyoffset;
2935 }
else if (dratio > 2.) {
2937 }
else if (dratio < -5.) {
2941 cpl_msg_debug(__func__,
"going forward: %d %d, %f %f --> diff %9.6f "
2942 "expected %f ratio %9.6f --> dy = %f", difu, dslice,
2943 yprev, y, dy, dexpect, dratio, dy + dycor);
2946 if (abs(difu) > 1) {
2947 double gap = abs(difu) * kMuseGeoIFUVGap;
2949 cpl_msg_warning(__func__,
"%d missing IFUs, added %f to dy",
2950 abs(difu) - 1, gap);
2952 cpl_table_set_double(tstack,
"dy", irow, dy);
2954 cpl_table_set_double(tstack, MUSE_GEOTABLE_Y, irow, ysum);
2958 printf(
"\ngeometry table of slicer stack %hu, with \"dy\" [mm]:\n",
2960 cpl_table_dump(tstack, 0, nrow, stdout);
2964 cpl_table_erase_column(tstack,
"ycopy");
2965 cpl_table_erase_column(tstack,
"dy");
2967 cpl_table_insert(gt->
table, tstack, cpl_table_get_nrow(gt->
table));
2968 cpl_table_delete(tstack);
2978 cpl_msg_info(__func__,
"Correcting vertical position of top/bottom slices");
2980 for (ifu = 1; ifu <= kMuseNumIFUs; ifu++) {
2981 for (i = 0; nstack[i] > 0; i++) {
2982 cpl_table_unselect_all(gt->
table);
2983 cpl_table_or_selected_int(gt->
table,
"SubField", CPL_EQUAL_TO, ifu);
2984 cpl_table_and_selected_int(gt->
table,
"stack", CPL_EQUAL_TO, nstack[i]);
2985 if (!cpl_table_count_selected(gt->
table)) {
2988 cpl_table *tstack = cpl_table_extract_selected(gt->
table);
2989 int ntsrow = cpl_table_get_nrow(tstack);
2991 cpl_propertylist *sorting = cpl_propertylist_new();
2992 cpl_propertylist_append_bool(sorting, MUSE_GEOTABLE_SKY, CPL_FALSE);
2993 cpl_table_sort(tstack, sorting);
2994 cpl_propertylist_delete(sorting);
2997 unsigned short nslicetop = cpl_table_get_int(tstack, MUSE_GEOTABLE_SKY, 0, NULL),
2998 nslicebot = cpl_table_get_int(tstack, MUSE_GEOTABLE_SKY,
3000 cpl_boolean hastop = nslicetop % nsoff == 1,
3001 hasbot = nslicebot % nsoff == 0,
3002 haschanged = CPL_FALSE;
3004 printf(
"table of IFU %hhu / SliceSky %hu: %hu to %hu (%s, %s)\n",
3005 ifu, nstack[i], nslicetop, nslicebot,
3006 hastop ?
"has top" :
"does NOT have top",
3007 hasbot ?
"has bottom" :
"does NOT have bottom");
3008 cpl_table_dump(tstack, 0, 1000, stdout);
3012 cpl_vector *vvpos = cpl_vector_new(ntsrow - 3),
3013 *vverr = cpl_vector_new(ntsrow - 3);
3014 for (irow = 1; irow < ntsrow - 2; irow++) {
3015 double dy = fabs(cpl_table_get_double(tstack, MUSE_GEOTABLE_Y, irow + 1, NULL)
3016 - cpl_table_get_double(tstack, MUSE_GEOTABLE_Y, irow, NULL)),
3017 dye1 = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y
"err", irow, NULL),
3018 dye2 = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y
"err", irow + 1, NULL),
3019 dyerr = sqrt(dye1*dye1 + dye2*dye2);
3020 cpl_vector_set(vvpos, irow - 1, dy);
3021 cpl_vector_set(vverr, irow - 1, dyerr);
3025 double mean = cpl_vector_get_mean(vvpos),
3026 median = cpl_vector_get_median_const(vvpos),
3027 stdev = cpl_vector_get_stdev(vvpos),
3028 stdev2 = cpl_vector_get_mean(vverr);
3029 cpl_vector_delete(vvpos);
3030 cpl_vector_delete(vverr);
3031 cpl_msg_debug(__func__,
"dy of IFU %hhu / SliceSky %hu: %f +/- %f (%f) [+/- %f]",
3032 ifu, nstack[i], mean, stdev, median, stdev2);
3035 double ytop = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y, 0, NULL),
3036 dyt = fabs(ytop - cpl_table_get_double(tstack, MUSE_GEOTABLE_Y, 1, NULL)),
3037 dyt1 = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y
"err", 0, NULL),
3038 dyt2 = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y
"err", 1, NULL),
3039 dyterr = sqrt(dyt1*dyt1 + dyt2*dyt2);
3040 cpl_msg_debug(__func__,
"dy of IFU %hhu / SliceSky %hu (top): %f +/- %f",
3041 ifu, nstack[i], dyt, dyterr);
3044 if (mean - dyt > sqrt(dyterr*dyterr + stdev2*stdev2)) {
3047 cpl_table_set_double(tstack, MUSE_GEOTABLE_Y, 0, ytop);
3048 haschanged = CPL_TRUE;
3050 printf(
"new top entry (+%f):\n", mean - dyt);
3051 cpl_table_dump(tstack, 0, 1, stdout);
3058 double ybot = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y, ntsrow - 1, NULL),
3059 dyb = fabs(ybot - cpl_table_get_double(tstack, MUSE_GEOTABLE_Y, ntsrow - 2, NULL)),
3060 dyb1 = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y
"err", ntsrow - 2, NULL),
3061 dyb2 = cpl_table_get_double(tstack, MUSE_GEOTABLE_Y
"err", ntsrow - 1, NULL),
3062 dyberr = sqrt(dyb1*dyb1 + dyb2*dyb2);
3063 cpl_msg_debug(__func__,
"dy of IFU %hhu / SliceSky %hu (bottom): %f +/- %f",
3064 ifu, nstack[i], dyb, dyberr);
3065 if (mean - dyb > sqrt(dyberr*dyberr + stdev2*stdev2)) {
3068 cpl_table_set_double(tstack, MUSE_GEOTABLE_Y, ntsrow - 1, ybot);
3069 haschanged = CPL_TRUE;
3071 printf(
"new bottom entry (-%f):\n", mean - dyb);
3072 cpl_table_dump(tstack, ntsrow - 1, 1, stdout);
3081 cpl_table_erase_selected(gt->
table);
3082 cpl_table_insert(gt->
table, tstack, cpl_table_get_nrow(gt->
table));
3084 cpl_table_delete(tstack);
3089 cpl_table_divide_scalar(gt->
table, MUSE_GEOTABLE_Y, kMuseSpaxelSizeY_WFM / aGeo->
scale);
3090 cpl_table_set_column_unit(gt->
table, MUSE_GEOTABLE_Y,
"pix");
3091 cpl_table_divide_scalar(gt->
table, MUSE_GEOTABLE_Y
"err", kMuseSpaxelSizeY_WFM / aGeo->
scale);
3092 cpl_table_set_column_unit(gt->
table, MUSE_GEOTABLE_Y
"err",
"pix");
3095 cpl_table_erase_column(gt->
table,
"spot");
3096 cpl_table_erase_column(gt->
table,
"xrel");
3097 cpl_table_erase_column(gt->
table,
"xrelerr");
3098 cpl_table_erase_column(gt->
table,
"vpos");
3099 cpl_table_erase_column(gt->
table,
"vposerr");
3100 cpl_table_erase_column(gt->
table,
"stack");
3141 cpl_ensure_code(aGeo && aGeo->
table, CPL_ERROR_NULL_INPUT);
3142 cpl_ensure_code(cpl_table_has_column(aGeo->
table, MUSE_GEOTABLE_FIELD) &&
3143 cpl_table_has_column(aGeo->
table, MUSE_GEOTABLE_CCD) &&
3144 cpl_table_has_column(aGeo->
table, MUSE_GEOTABLE_SKY) &&
3145 cpl_table_has_column(aGeo->
table, MUSE_GEOTABLE_X) &&
3146 cpl_table_has_column(aGeo->
table, MUSE_GEOTABLE_Y) &&
3147 cpl_table_has_column(aGeo->
table, MUSE_GEOTABLE_ANGLE) &&
3148 cpl_table_has_column(aGeo->
table, MUSE_GEOTABLE_WIDTH),
3149 CPL_ERROR_ILLEGAL_INPUT);
3152 if (getenv(
"MUSE_GEOMETRY_PINHOLE_DY")) {
3154 FILE *fn1 = fopen(
"gt1.ascii",
"w");
3155 fprintf(fn1,
"geometry table before scaling\n");
3156 cpl_table_dump(aGeo->
table, 0, 10000, fn1);
3159 double pinholedy = atof(getenv(
"MUSE_GEOMETRY_PINHOLE_DY")),
3160 fdy = kMuseCUmpmDY / pinholedy;
3161 cpl_msg_warning(__func__,
"Pinhole y distance of %f mm was used instead of "
3162 "%f mm; scaling coordinates by %f!", pinholedy, kMuseCUmpmDY,
3164 int irow, nrow = cpl_table_get_nrow(aGeo->
table);
3165 for (irow = 0; irow < nrow; irow++) {
3168 double y = cpl_table_get_double(aGeo->
table, MUSE_GEOTABLE_Y, irow, &err);
3170 cpl_table_set_double(aGeo->
table, MUSE_GEOTABLE_Y, irow, y * fdy);
3175 double angleold = cpl_table_get_double(aGeo->
table, MUSE_GEOTABLE_ANGLE, irow, &err);
3177 double anglenew = atan(fdy * tan(angleold * CPL_MATH_RAD_DEG))
3179 cpl_table_set_double(aGeo->
table, MUSE_GEOTABLE_ANGLE, irow, anglenew);
3183 FILE *fn2 = fopen(
"gt2.ascii",
"w");
3184 fprintf(fn2,
"geometry table after scaling by %f\n", fdy);
3185 cpl_table_dump(aGeo->
table, 0, 10000, fn2);
3192 for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
3193 cpl_table_select_all(aGeo->
table);
3194 cpl_table_and_selected_int(aGeo->
table, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO, nifu);
3195 if (cpl_table_count_selected(aGeo->
table) < 1) {
3199 unsigned short nslice;
3200 for (nslice = 1; nslice <= kMuseSlicesPerCCD; nslice++) {
3201 cpl_table_select_all(aGeo->
table);
3202 cpl_table_and_selected_int(aGeo->
table, MUSE_GEOTABLE_FIELD, CPL_EQUAL_TO, nifu);
3203 cpl_table_and_selected_int(aGeo->
table, MUSE_GEOTABLE_CCD, CPL_EQUAL_TO, nslice);
3204 if (cpl_table_count_selected(aGeo->
table) > 0) {
3208 cpl_table_set_size(aGeo->
table, cpl_table_get_nrow(aGeo->
table) + 1);
3209 int irow = cpl_table_get_nrow(aGeo->
table) - 1;
3210 cpl_table_set_int(aGeo->
table, MUSE_GEOTABLE_FIELD, irow, nifu);
3211 cpl_table_set_int(aGeo->
table, MUSE_GEOTABLE_CCD, irow, nslice);
3212 cpl_table_set_int(aGeo->
table, MUSE_GEOTABLE_SKY, irow,
3213 kMuseGeoSliceSky[nslice - 1]);
3214 cpl_table_set_double(aGeo->
table, MUSE_GEOTABLE_X, irow, NAN);
3215 cpl_table_set_double(aGeo->
table, MUSE_GEOTABLE_Y, irow, NAN);
3216 cpl_table_set_double(aGeo->
table, MUSE_GEOTABLE_ANGLE, irow, 0.);
3217 cpl_table_set_double(aGeo->
table, MUSE_GEOTABLE_WIDTH, irow, 0.);
3221 cpl_boolean needsnoflip = getenv(
"MUSE_GEOMETRY_NO_INVERSION")
3222 && atoi(getenv(
"MUSE_GEOMETRY_NO_INVERSION")) > 0;
3224 cpl_msg_info(__func__,
"Flipping all slices because of data inversion!");
3225 cpl_table_multiply_scalar(aGeo->
table, MUSE_GEOTABLE_Y, -1.);
3226 cpl_table_multiply_scalar(aGeo->
table, MUSE_GEOTABLE_ANGLE, -1.);
3230 cpl_propertylist *sorting = cpl_propertylist_new();
3231 cpl_propertylist_append_bool(sorting, MUSE_GEOTABLE_FIELD, CPL_FALSE);
3232 cpl_propertylist_append_bool(sorting, MUSE_GEOTABLE_SKY, CPL_FALSE);
3233 cpl_table_sort(aGeo->
table, sorting);
3234 cpl_propertylist_delete(sorting);
3237 printf(
"\nfinal geometry table with all slicer stacks [pix]:\n");
3238 cpl_table_dump(aGeo->
table, 0, nrow, stdout);
3242 const char *fn =
"GEOMETRY_TABLE.ascii";
3243 FILE *fp = fopen(fn,
"w");
3244 fprintf(fp,
"# final geometry table with all slicer stacks [pix]:\n");
3245 cpl_table_dump(aGeo->
table, 0, nrow, fp);
3247 cpl_msg_debug(__func__,
"written geometry table in ASCII to \"%s\"", fn);
3250 return CPL_ERROR_NONE;
cpl_error_code muse_cplarray_erase_invalid(cpl_array *aArray)
Erase all invalid values from an array.
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
muse_geo_table * muse_geo_table_duplicate(const muse_geo_table *aGeo)
Make a copy of a MUSE geometry table.
Structure definition for a collection of muse_images.
cpl_error_code muse_geo_refine_horizontal(muse_geo_table *aGeo, cpl_table *aSpots)
Refine relative horizontal positions of adjacent IFUs.
const muse_cpltable_def muse_geo_measurements_def[]
Spots measurement table definition for geometrical calibration.
cpl_polynomial * muse_wave_table_get_poly_for_slice(const cpl_table *aTable, const unsigned short aSlice)
Construct polynomial from the wavelength calibration table entry for the given slice.
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
cpl_image * data
the data extension
static void muse_geo_determine_horizontal_vpos(const cpl_table *aSlice, double *aP, double *aPE, double *aPM, double aDY, double aF)
Compute properly weighted vertical slice position mean from intermediate geometry table columns...
static cpl_size muse_geo_determine_horizontal_wmean(const cpl_table *aSlice, const char *aCol, const char *aColErr, double *aValue, double *aError, double *aMedian, double aSigma)
Interatively reject outliers and compute (weighted) statistics of intermediate geometry table columns...
const muse_cpltable_def muse_geo_table_def[]
Geometry table definition.
double muse_geo_table_ifu_area(const cpl_table *aTable, const unsigned char aIFU, double aScale)
Compute the area of an IFU in the VLT focal plane.
double muse_geo_compute_pinhole_global_distance(cpl_array *aDY, double aWidth, double aMin, double aMax)
Use vertical pinhole distance measurements of all IFUs to compute the effective global value...
double scale
The VLT focal plane scale factor of the data. output file.
muse_geo_table * muse_geo_determine_vertical(const muse_geo_table *aGeo)
Use all properties of the central spot and the horizontal properties in each slice to compute the ver...
cpl_vector * muse_cplvector_get_unique(const cpl_vector *aVector)
Separate out all unique entries in a given vector into a new one.
Structure definition of MUSE three extension FITS file.
double muse_pfits_get_focu_scale(const cpl_propertylist *aHeaders)
find out the scale in the VLT focal plane
static cpl_table * muse_geo_get_spot_peaks(cpl_table *aSpots, unsigned char aIFU, unsigned short aNSlice, unsigned char aNSpot, double aLambda, double aVPosRef, cpl_boolean aVerifyDY, cpl_array *aDY)
Use spot measurements of one IFU to compute vertical pinhole distance.
cpl_propertylist * header
the FITS header
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.
unsigned int muse_imagelist_get_size(muse_imagelist *aList)
Return the number of stored images.
void muse_trace_polys_delete(cpl_polynomial *aPolys[])
Delete the multi-polynomial array created in relation to tracing.
muse_geo_table * muse_geo_determine_horizontal(const muse_geo_table *aGeo)
Use per-spot and per-wavelength partial geometry to determine the horizontal geometrical properties f...
cpl_error_code muse_geo_compute_pinhole_local_distance(cpl_array *aDY, cpl_table *aSpots)
Use spot measurements of one IFU to compute vertical pinhole distance.
cpl_table * muse_cpltable_new(const muse_cpltable_def *aDef, cpl_size aLength)
Create an empty table according to the specified definition.
cpl_table * muse_geo_measure_spots(muse_image *aImage, muse_imagelist *aList, const cpl_table *aTrace, const cpl_table *aWave, const cpl_vector *aLines, double aSigma, muse_geo_centroid_type aCentroid)
Detect spots on a combined image and measure them on the corresponding series of images.
cpl_array * muse_cplarray_new_from_delimited_string(const char *aString, const char *aDelim)
Convert a delimited string into an array of strings.
muse_image * muse_imagelist_get(muse_imagelist *aList, unsigned int aIdx)
Get the muse_image of given list index.
cpl_table * table
The geometry table.
Structure definition of MUSE geometry table.
cpl_error_code muse_geo_finalize(muse_geo_table *aGeo)
Create a final version of a geometry table.
muse_geo_centroid_type
Type of centroiding algorithm to use.
cpl_bivector * muse_cplarray_histogram(const cpl_array *aArray, double aWidth, double aMin, double aMax)
Create a histogram for a numerical array.
int muse_pfits_get_posenc(const cpl_propertylist *aHeaders, unsigned short aEncoder)
query the absolute encoder position of one encoder
void muse_geo_table_delete(muse_geo_table *aGeo)
Deallocate memory associated to a geometry table object.
muse_geo_table * muse_geo_table_new(cpl_size aNRows, double aScale)
Create a new MUSE geometry table.
double muse_astro_posangle(const cpl_propertylist *aHeader)
Derive the position angle of an observation from information in a FITS header.
cpl_size muse_cplarray_erase_outliers(cpl_array *aArray, const cpl_bivector *aHistogram, cpl_size aGap, double aLimit)
Erase outliers from an array using histogram information.
double muse_pfits_get_pospos(const cpl_propertylist *aHeaders, unsigned short aEncoder)
query the position in user units of one encoder
cpl_table * muse_geo_table_extract_ifu(const cpl_table *aTable, const unsigned char aIFU)
Extract the part of a geometry table dealing with a given IFU.
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.
cpl_matrix * muse_matrix_new_gaussian_2d(int aXHalfwidth, int aYHalfwidth, double aSigma)
Create a matrix that contains a normalized 2D Gaussian.
cpl_vector * muse_geo_lines_get(const cpl_table *aLines)
Select lines suitable for geometrical calibration from a line list.
muse_geo_table * muse_geo_determine_initial(cpl_table *aSpots, const cpl_table *aTrace)
Use spot measurements to compute initial geometrical properties.