FORS Pipeline Reference Manual  5.0.14
moses.c
1 /* $Id: moses.c,v 1.116 2013-10-15 09:27:38 cgarcia Exp $
2  *
3  * This file is part of the MOSES library
4  * Copyright (C) 2002-2010 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * $Author: cgarcia $
23  * $Date: 2013-10-15 09:27:38 $
24  * $Revision: 1.116 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <math.h>
37 #include <time.h>
38 
39 #include <fors_utils.h>
40 #include <moses.h>
41 
42 /* Prototypes */
43 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row);
44 
45 
46 /* Cheating: moving here the cpl_tools_get_median_float() prototype,
47  * even if cpl_tool.h is not public. It should be removed as soon as
48  * an image median filtering with generic kernel will be implemented
49  * in the CPL, or as soon as this module will be moved into the CPL. */
50 /* FIXME: This has to be removed!! */
51 
52 float cpl_tools_get_median_float(float *, cpl_size);
53 
54 #define MAX_COLNAME (80)
55 #define STRETCH_FACTOR (1.20)
56 
57 // Related to mos_identify_peaks(), used in multiplex mode
58 
59 static int mos_multiplex = -1;
60 static int mos_region_size = 800;
61 
62 static double default_lines_hi[] = { /* Default sky line catalog */
63  5577.338, /* for high res data */
64  5889.953,
65  5895.923,
66  5915.301,
67  5932.862,
68  5953.420,
69  6257.961,
70  6287.434,
71  6300.304,
72  6306.869,
73  6363.780,
74  6498.729,
75  6533.044,
76  6553.617,
77  6841.945,
78  6863.955,
79  6870.994,
80  6889.288,
81  6900.833,
82  6912.623,
83  6923.220,
84  6939.521,
85  6969.930,
86  7003.858,
87  7244.907,
88  7276.405,
89  7284.439,
90  7316.282,
91  7329.148,
92  7340.885,
93  7358.659,
94  7571.746,
95  7750.640,
96  7759.996,
97  7794.112,
98  7808.467,
99  7821.503,
100  7841.266,
101  7913.708,
102  7949.204,
103  7964.650,
104  7993.332,
105  8014.059,
106  8310.719,
107  8344.602,
108  8382.392,
109  8399.170,
110  8415.231,
111  8430.174,
112  8452.250,
113  8493.389,
114  8791.186,
115  8827.096,
116  8885.850,
117  8903.114,
118  8943.395,
119  8988.366
120  };
121 
122 static double default_lines_lo[] = { /* Default sky line catalog */
123  5577.338, /* for low res data */
124  6300.304,
125  6863.955,
126  7571.746,
127  7964.650,
128  7993.332
129  };
130 
131 
141 /*
142  * The following macros and function for finding the k-th smallest
143  * value on a float array will be accessible from cpl_tools once
144  * this module will be moved into the CPL.
145  */
146 
147 /****
148 
149 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
150 
151 static float kthSmallest(float a[], int n, int k)
152 {
153  register int i,j,l,m;
154  register float x;
155 
156  l = 0;
157  m = n-1;
158  while (l < m) {
159  x = a[k];
160  i = l;
161  j = m;
162  do {
163  while (a[i] < x) {
164  i++;
165  }
166  while (x < a[j]) {
167  j--;
168  }
169  if (i <= j) {
170  PIX_SWAP(a[i],a[j]);
171  i++;
172  j--;
173  }
174  } while (i <= j);
175 
176  if (j < k) {
177  l = i;
178  }
179  if (k < i) {
180  m = j;
181  }
182 
183  }
184  return(a[k]);
185 }
186 
187 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1)))
188 
189 ****/
190 
191 /*
192  * Return random number with gaussian distribution (mean = 0, variance = 1)
193  * (Box-Mueller method). The mos_randg() argument is either true or false,
194  * indicating whether to "seed" or not the sequence of generated random
195  * numbers. The "seeding" is performed just at the first mos_randg(1) call,
196  * and at further calls the input argument is ignored. This function
197  * generates two random numbers at each call, returning the first one
198  * at odd calls, and the second one at even calls.
199  */
200 
201 static void mos_seed(void)
202 {
203  srand((unsigned int)time((time_t *)0));
204 }
205 
206 static double mos_randg(int seme)
207 {
208  static int doit = 1;
209  static int gotit = 1;
210  double x1, x2, w, y1;
211  static double y2;
212 
213  if (gotit && seme) {
214  mos_seed();
215  gotit = 0;
216  }
217 
218  if (doit) {
219  doit = 0;
220  do {
221  x1 = 2.0 * (double)rand() / RAND_MAX - 1.0;
222  x2 = 2.0 * (double)rand() / RAND_MAX - 1.0;
223  w = x1 * x1 + x2 * x2;
224  } while (w >= 1.0 || w == 0.0);
225 
226  w = sqrt( (-2.0 * log(w)) / w);
227 
228  y1 = x1 * w;
229  y2 = x2 * w;
230  return y1;
231  }
232 
233  doit = 1;
234  return y2;
235 }
236 
237 /*
238  * This function contained a dependency on the VIMOS library
239  * (function medianPixelvalue()): it should be removed as soon as an
240  * image median filtering with generic kernel will be implemented
241  * in the CPL. Currently it has been solved by a direct call to
242  * a cpl_tool function.
243  */
244 
245 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in,
246  int filtsizey, int refrow,
247  int above, int below, int step)
248 {
249 
250  const char *func = "mos_image_general_median_filter";
251 
252  cpl_image *filt_img = NULL;
253  int col, row;
254  float *buf = NULL;
255  float *data;
256  float *fdata;
257  int upright_y, loleft_y;
258  int j;
259  int yIsEven = !(filtsizey - (filtsizey/2)*2);
260  int f2y;
261  int nx = cpl_image_get_size_x(ima_in);
262  int ny = cpl_image_get_size_y(ima_in);
263  int firstRow;
264 
265 
266  if (yIsEven) filtsizey++;
267 
268  if (ny <= filtsizey) {
269  cpl_msg_error(func,
270  "Median filter size: %d, image size: %d", filtsizey, ny);
271  return NULL;
272  }
273 
274  f2y = filtsizey / 2;
275 
276  filt_img = cpl_image_duplicate(ima_in);
277  buf = cpl_malloc(filtsizey * sizeof(float));
278  data = cpl_image_get_data(ima_in);
279  fdata = cpl_image_get_data(filt_img);
280 
281  firstRow = refrow - step * (below / step);
282  if (firstRow < f2y)
283  firstRow += step;
284 
285  for (col = 0; col < nx; col++) {
286  for (row = firstRow; row < refrow + above; row += step) {
287  if (row >= ny - f2y)
288  break;
289  loleft_y = row - f2y;
290  upright_y = row + f2y + 1;
291  for (j = loleft_y; j < upright_y; j++)
292  buf[j - loleft_y] = data[col + j * nx];
293 
294  fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey);
295  }
296  }
297 
298  cpl_free(buf);
299 
300  return filt_img;
301 
302 }
303 
304 
305 /*
306  * The following static function is used to find an accurate position
307  * of a peak within a short interval (however at least 5 pixels long).
308  * The basic idea is to find the baricenter of all the pixel values
309  * that pass a threshold level between the median value and the maximum
310  * value within the examined interval (in case such levels are equal,
311  * the input is considered flat and no position is returned). At least
312  * minPoints must pass this threshold, or no position is computed. To
313  * evaluate the significance of the computed baricenter, the variance
314  * of the contributing positions (relative to the found baricenter) is
315  * also evaluated, and compared with the expected variance for a uniform
316  * distribution of positions. If the observed variance is greater than
317  * 80% of the variance of the uniform distribution, the found position
318  * is rejected.
319  */
320 
321 static int peakPosition(const float *data, int size, float *position,
322  int minPoints)
323 {
324  int i;
325  int count = 0;
326  float *copy;
327  float max, median, level, pos, variance, uniformVariance;
328  double sum, weights;
329 
330 
331  if (data == NULL)
332  return 1;
333 
334  if (size < 5) /* Hardcoded, I know... */
335  return 1;
336 
337 
338  /*
339  * Find median level
340  */
341 
342  copy = (float *) cpl_malloc(size*sizeof(float));
343  for (i = 0; i < size; i++)
344  copy[i] = data[i];
345  median = cpl_tools_get_median_float(copy, size);
346  cpl_free(copy);
347 
348 
349  /*
350  * Find max
351  */
352 
353  max = data[0];
354  for (i = 1; i < size; i++)
355  if (data[i] > max)
356  max = data[i];
357 
358 
359  /*
360  * If the max equals the median we have a flat input, therefore
361  * no peak is found.
362  */
363 
364  if (max-median < 0.00001)
365  return 1;
366 
367 
368  /*
369  * Discrimination level: only pixels with values above this
370  * level are considered in baricenter calculation.
371  */
372 
373  level = (max + median) / 2;
374 
375 
376  /*
377  * Of the values above this level compute the baricenter and
378  * then the variance of the positions used. Note that the weights
379  * are taken as the difference between the pixels values and
380  * the median level (supposedly the background).
381  */
382 
383  count = 0;
384  for (i = 0, sum = 0., weights = 0.; i < size; i++) {
385  if (data[i] > level) {
386  count++;
387  weights += (data[i] - median);
388  sum += i * (data[i] - median);
389  }
390  }
391 
392 
393  /*
394  * If too few values are above threshold, refuse the position
395  * as insignificant
396  */
397 
398  if (count < minPoints)
399  return 1;
400 
401  pos = sum / weights;
402  for (i = 0, sum = 0., weights = 0.; i < size; i++) {
403  if (data[i] > level) {
404  weights++;
405  sum += (i - pos) * (i - pos);
406  }
407  }
408  variance = sqrt(sum / weights);
409 
410 
411  /*
412  * The "uniform variance" is the variance that should be obtained
413  * in the case of uniform distribution of the points positions in
414  * the selected interval. If the real variance is comparable with
415  * this value, the peak is considered not found.
416  */
417 
418  uniformVariance = sqrt(size*size/3 - pos*size + pos*pos);
419 
420  if (variance > 0.8 * uniformVariance)
421  return 1;
422 
423  *position = pos + 0.5;
424 
425  return 0;
426 }
427 
428 
429 /*
430  * The following static function determines the quantity dx to be
431  * added to the position of the highest pixel of a fiber profile,
432  * to get the true position of the profile maximum. All is needed
433  * is the maximum observed value v2 in the profile, and the observed
434  * values v1 and v3 of the previous and the next pixels in the profile.
435  *
436  * The following ratio is defined:
437  *
438  * R = 0.5 (v3 - v1) / (2*v2 - v3 - v1)
439  *
440  * This is a conventional ratio that wouldn't diverge for any set of
441  * pixel values, and that would not depend on the presence of background
442  * (with the assumption that the background level is the same for the
443  * three pixels). R has also been chosen in such a way that its value
444  * is already quite close to the real dx. It should be noted that the
445  * following condition should be fulfilled:
446  *
447  * v1 <= v2 and v3 < v2
448  * or
449  * v1 < v2 and v3 <= v2
450  *
451  * This implies that dx varies between -0.5 and 0.5 pixels. In such
452  * boundary cases, one has:
453  *
454  * v2 = v1 and R = dx = -0.5
455  * v2 = v3 and R = dx = 0.5
456  *
457  * Another special case is when the observed pixel values are perfectly
458  * symmetrical:
459  *
460  * v1 = v3 and R = dx = 0.0
461  *
462  * In all the intermediate cases the relation between R and dx depends
463  * on the shape of the fiber profile, that has been determined elsewhere.
464  * Using the accurate reconstruction of the fiber profile obtained by
465  * the * functions ifuProfile() and rebinProfile(), it can be shown
466  * that R differs from dx always less than 0.01 pixels. If the condition
467  *
468  * v1 <= v2 and v3 < v2
469  * or
470  * v1 < v2 and v3 <= v2
471  *
472  * is not fulfilled, then this function returns the value 2.0.
473  */
474 
475 static double values_to_dx(double v1, double v2, double v3)
476 {
477 
478  static double epsilon = 0.00000001;
479  double r = 2.0;
480 
481 
482  if (v1 > v2 || v3 > v2)
483  return r;
484 
485  if (2 * v2 - v1 - v3 < epsilon)
486  return r;
487 
488  r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1);
489 
490  return r;
491 
492 }
493 
494 
495 /*
496  * The following static function passes a min filter of given box
497  * size on the data buffer. The box size must be a positive odd integer.
498  */
499 
500 static float *min_filter(float *buffer, int length, int size)
501 {
502  float *minf = cpl_calloc(length, sizeof(float));
503  float min;
504  int start = size / 2;
505  int end = length - size / 2;
506  int i, j;
507 
508 
509  for (i = start; i < end; i++) {
510  min = buffer[i-start];
511  for (j = i - start + 1; j <= i + start; j++)
512  if (min > buffer[j])
513  min = buffer[j];
514  minf[i] = min;
515  }
516 
517  for (i = 0; i < start; i++)
518  minf[i] = minf[start];
519 
520  for (i = end; i < length; i++)
521  minf[i] = minf[end-1];
522 
523  return minf;
524 }
525 
526 
527 /*
528  * The following static function passes a max filter of given box
529  * size on the data buffer. The box size must be a positive odd integer.
530  */
531 
532 static float *max_filter(float *buffer, int length, int size)
533 {
534  float *maxf = cpl_calloc(length, sizeof(float));
535  float max;
536  int start = size / 2;
537  int end = length - size / 2;
538  int i, j;
539 
540 
541  for (i = start; i < end; i++) {
542  max = buffer[i-start];
543  for (j = i - start + 1; j <= i + start; j++)
544  if (max < buffer[j])
545  max = buffer[j];
546  maxf[i] = max;
547  }
548 
549  for (i = 0; i < start; i++)
550  maxf[i] = maxf[start];
551 
552  for (i = end; i < length; i++)
553  maxf[i] = maxf[end-1];
554 
555  return maxf;
556 }
557 
558 
559 /*
560  * The following static function passes a running average of given box
561  * size on the data buffer. The box size must be a positive odd integer.
562  */
563 
564 static float *smo_filter(float *buffer, int length, int size)
565 {
566  float *smof = cpl_calloc(length, sizeof(float));
567  double sum;
568  int start = size / 2;
569  int end = length - size / 2;
570  int i, j;
571 
572 
573  for (i = start; i < end; i++) {
574  sum = 0.0;
575  for (j = i - start; j <= i + start; j++)
576  sum += buffer[j];
577  smof[i] = sum / size;
578  }
579 
580  for (i = 0; i < start; i++)
581  smof[i] = smof[start];
582 
583  for (i = end; i < length; i++)
584  smof[i] = smof[end-1];
585 
586  return smof;
587 }
588 
589 /*
590  * The following two static functions are used to read and write from the
591  * global distortion table the different model components. Conventionally
592  * the table consists of 6 columns and 10 rows. Each row is just ordered
593  * storage for model coefficients, and these functions guarantee that the
594  * coefficients are read in and written out correctly, independent on their
595  * physical meaning. The first 6 table rows are a description of the IDS
596  * coefficients, followed by a row containing only the used reference
597  * wavelength. The remaining 3 are a description of the spectral curvature.
598  * The first row is a description of coefficient c0, the second of coefficient
599  * c1, etc., of the IDS. The 8th row is a description of coefficient c0,
600  * the 9th of coefficient c1, etc., of the spectral curvature. All are
601  * bivariate polynomialx on x,y mask coordinates. If the input table
602  * to the write routine is NULL, it is allocated and initialised. Also
603  * the input polynomial could be NULL, and nothing would be written to
604  * the table. If both pointers are NULL the function is basically a
605  * constructor of the global distortion table.
606  */
607 
608 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row)
609 {
610  cpl_polynomial *poly = NULL;
611  cpl_size p[2];
612  cpl_size degree = 2;
613  int null;
614  double coeff;
615 
616  char name[MAX_COLNAME];
617 
618 
619  for (p[0] = 0; p[0] <= degree; p[0]++) {
620  for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
621  snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
622  coeff = cpl_table_get_double(global, name, row, &null);
623  if (null)
624  continue;
625  if (poly == NULL)
626  poly = cpl_polynomial_new(2);
627  cpl_polynomial_set_coeff(poly, p, coeff);
628  }
629  }
630 
631  return poly;
632 }
633 
634 static cpl_table *write_global_distortion(cpl_table *global, int row,
635  cpl_polynomial *poly)
636 {
637  cpl_table *table;
638  cpl_size p[2];
639  cpl_size degree = 2;
640  int nrow = 13;
641 
642  char name[MAX_COLNAME];
643 
644 
645  if (global) {
646  table = global;
647  }
648  else {
649  table = cpl_table_new(nrow);
650  for (p[0] = 0; p[0] <= degree; p[0]++) {
651  for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
652  snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
653  cpl_table_new_column(table, name, CPL_TYPE_DOUBLE);
654  }
655  }
656  }
657 
658  if (poly) {
659  for (p[0] = 0; p[0] <= degree; p[0]++) {
660  for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
661  snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
662  cpl_table_set_double(table, name, row,
663  cpl_polynomial_get_coeff(poly, p));
664  }
665  }
666  }
667 
668  return table;
669 }
670 
671 
672 /*
673  * The following static function is performing a robust linear fit
674  * (drawn from the VIMOS library, and originally from ESO-Eclipse).
675  *
676  * ----> y = a + b * x
677  *
678  * This function return 0 on success.
679  */
680 
681 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
682 static int robustLinearFit(cpl_bivector *list, double *a, double *b,
683  double *abdev)
684 {
685  cpl_vector *vx;
686  cpl_vector *vy;
687  cpl_vector *va;
688 
689  double aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum;
690  double sx, sy, sxy, sxx, chisq;
691  double *arr;
692  double aa_ls, bb_ls;
693  double *x;
694  double *y;
695  int np;
696  int iter;
697  int max_iterate = 30;
698  int i;
699 
700 
701  np = cpl_bivector_get_size(list);
702  vx = cpl_bivector_get_x(list);
703  vy = cpl_bivector_get_y(list);
704  x = cpl_vector_get_data(vx);
705  y = cpl_vector_get_data(vy);
706 
707  sx = sy = sxx = sxy = 0.00;
708  for (i = 0; i < np; i++) {
709  sx += x[i];
710  sy += y[i];
711  sxy += x[i] * y[i];
712  sxx += x[i] * x[i];
713  }
714 
715  del = np * sxx - sx * sx;
716  aa_ls = aa = (sxx * sy - sx * sxy) / del;
717  bb_ls = bb = (np * sxy - sx * sy) / del;
718 
719  chisq = 0.00;
720  for (i = 0; i < np; i++) {
721  temp = y[i] - (aa+bb*x[i]);
722  temp *= temp;
723  chisq += temp;
724  }
725 
726  va = cpl_vector_new(np);
727  arr = cpl_vector_get_data(va);
728  sigb = sqrt(chisq/del);
729  b1 = bb;
730 
731  bcomp = b1;
732  sum = 0.00;
733  for (i = 0; i < np; i++) {
734  arr[i] = y[i] - bcomp * x[i];
735  }
736  aa = cpl_vector_get_median_const(va);
737  abdevt = 0.0;
738  for (i = 0; i < np; i++) {
739  d = y[i] - (bcomp * x[i] + aa);
740  abdevt += fabs(d);
741  if (y[i] != 0.0)
742  d /= fabs(y[i]);
743  if (fabs(d) > 1e-7)
744  sum += (d >= 0.0 ? x[i] : -x[i]);
745  }
746  f1 = sum;
747 
748  b2 = bb + SEGNO(3.0 * sigb, f1);
749 
750  bcomp = b2;
751  sum = 0.00;
752  for (i = 0; i < np; i++) {
753  arr[i] = y[i] - bcomp * x[i];
754  }
755  aa = cpl_vector_get_median_const(va);
756  abdevt = 0.0;
757  for (i = 0; i < np; i++) {
758  d = y[i] - (bcomp * x[i] + aa);
759  abdevt += fabs(d);
760  if (y[i] != 0.0)
761  d /= fabs(y[i]);
762  if (fabs(d) > 1e-7)
763  sum += (d >= 0.0 ? x[i] : -x[i]);
764  }
765  f2 = sum;
766 
767  if (fabs(b2-b1)<1e-7) {
768  *a = aa;
769  *b = bb;
770  *abdev = abdevt / (double)np;
771  cpl_vector_delete(va);
772  return 0;
773  }
774 
775  iter = 0;
776  while (f1*f2 > 0.0) {
777  bb = 2.0*b2-b1;
778  b1 = b2;
779  f1 = f2;
780  b2 = bb;
781 
782  bcomp = b2;
783  sum = 0.00;
784  for (i = 0; i < np; i++) {
785  arr[i] = y[i] - bcomp * x[i];
786  }
787  aa = cpl_vector_get_median_const(va);
788  abdevt = 0.0;
789  for (i = 0; i < np; i++) {
790  d = y[i] - (bcomp * x[i] + aa);
791  abdevt += fabs(d);
792  if (y[i] != 0.0)
793  d /= fabs(y[i]);
794  if (fabs(d) > 1e-7)
795  sum += (d >= 0.0 ? x[i] : -x[i]);
796  }
797  f2 = sum;
798  iter++;
799  if (iter >= max_iterate)
800  break;
801  }
802  if (iter >= max_iterate) {
803  *a = aa_ls;
804  *b = bb_ls;
805  *abdev = -1.0;
806  cpl_vector_delete(va);
807  return 1;
808  }
809 
810  sigb = 0.01 * sigb;
811  while (fabs(b2-b1) > sigb) {
812  bb = 0.5 * (b1 + b2);
813  if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7))
814  break;
815  bcomp = bb;
816  sum = 0.0;
817  for (i = 0; i < np; i++) {
818  arr[i] = y[i] - bcomp * x[i];
819  }
820  aa = cpl_vector_get_median_const(va);
821  abdevt = 0.0;
822  for (i = 0; i < np; i++) {
823  d = y[i] - (bcomp * x[i] + aa);
824  abdevt += fabs(d);
825  if (y[i] != 0.0)
826  d /= fabs(y[i]);
827  if (fabs(d) > 1e-7)
828  sum += (d >= 0.0 ? x[i] : -x[i]);
829  }
830  f = sum;
831 
832  if (f*f1 >= 0.0) {
833  f1=f;
834  b1=bb;
835  }
836  else {
837  f2=f;
838  b2=bb;
839  }
840  }
841  cpl_vector_delete(va);
842  *a = aa;
843  *b = bb;
844  *abdev = abdevt / np;
845  return 0;
846 }
847 #undef SEGNO
848 
849 /*
850  * The following static function applies the Hough transform from a table
851  * of points to another table of points. Given the points p_i = (x_i,y_i)
852  * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j)
853  * and Y = y_i - X*x_i is computed and added to the output table for each
854  * p_i, p_j pair. This means that if the input table has N points, the
855  * output table has N*(N-1)/2 points.
856  */
857 
858 /* static */
859 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y)
860 {
861  cpl_table *output;
862  double *xdata;
863  double *ydata;
864  double *xodata;
865  double *yodata;
866  int npoints;
867  int opoints;
868  int i, j, k;
869 
870 
871  npoints = cpl_table_get_nrow(table);
872  opoints = npoints*(npoints-1)/2;
873 
874  output = cpl_table_new(opoints);
875  cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE);
876  cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE);
877  cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0);
878  cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0);
879 
880  xodata = cpl_table_get_data_double(output, "m");
881  yodata = cpl_table_get_data_double(output, "q");
882 
883  cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE);
884  cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE);
885 
886  xdata = cpl_table_get_data_double(table, "x");
887  ydata = cpl_table_get_data_double(table, "y");
888 
889  k = 0;
890  for (i = 0; i < npoints; i++) {
891  for (j = i+1; j < npoints; j++) {
892  xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]);
893  yodata[k] = ydata[i] - xodata[k] * xdata[i];
894  k++;
895  }
896  }
897 
898  if (k != opoints)
899  printf("Assert k = %d, expected %d\n", k, opoints);
900 
901  cpl_table_erase_column(table, "x");
902  cpl_table_erase_column(table, "y");
903 
904  return output;
905 }
906 
907 
908 /*
909  * The following static function is performing the spectral
910  * extraction for the function mos_extract_objects()
911  */
912 
913 static void mos_extraction(cpl_image *sciwin, cpl_image *sci_var_win,
914  cpl_image *skywin,
915  cpl_image *extracted, cpl_image *sky,
916  cpl_image *error, int nobjects, int extraction,
917  double ron, double conad, int ncomb)
918 {
919 
920  cpl_vector *vprofile;
921  cpl_image *smowin;
922 
923  int i, j;
924  int specLen;
925  int numRows;
926  int index;
927  int iter;
928  int maxIter = 2; /* Not less than 2 !!! */
929  int smoothBox = 31; /* Not less than 5 !!! */
930  double nsigma = 5.0;
931 
932  double sumWeight, sum, sumSky, sumProf, sumVar, variance, weight;
933  double *profile;
934  double *buffer;
935  float *edata;
936  float *ekdata;
937  float *endata;
938  float *sdata;
939  float *kdata;
940  float *fdata;
941  float *vardata;
942 
943  double value;
944 
945 
946  specLen = cpl_image_get_size_x(sciwin);
947  numRows = cpl_image_get_size_y(sciwin);
948 
949  edata = cpl_image_get_data(extracted);
950  edata += nobjects * specLen;
951 
952  ekdata = cpl_image_get_data(sky);
953  ekdata += nobjects * specLen;
954 
955  endata = cpl_image_get_data(error);
956  endata += nobjects * specLen;
957 
958  sdata = cpl_image_get_data(sciwin);
959  kdata = cpl_image_get_data(skywin);
960  if(sci_var_win != NULL)
961  vardata = cpl_image_get_data(sci_var_win);
962 
963  /*
964  * Initial spectrum estimate
965  if (sdata[i + j * specLen] > 0.0)
966  */
967 
968  if (extraction && numRows > 5) {
969  smowin = mos_image_filter_median(sciwin, 3, 3);
970  fdata = cpl_image_get_data(smowin);
971  for (i = 0; i < specLen; i++)
972  for (j = 0, edata[i] = 0.0; j < numRows; j++)
973  edata[i] += fdata[i + j * specLen];
974  cpl_image_delete(smowin);
975  }
976  else {
977  for (i = 0; i < specLen; i++)
978  for (j = 0, edata[i] = 0.0; j < numRows; j++)
979  edata[i] += sdata[i + j * specLen];
980  }
981 
982  if (extraction) {
983 
984  profile = cpl_calloc(specLen * numRows, sizeof(double));
985  buffer = cpl_calloc(specLen, sizeof(double));
986 
987  for (iter = 0; iter < maxIter; iter++) {
988 
989  /*
990  * Normalised spatial profile
991  */
992 
993  for (i = 0; i < specLen; i++) {
994  for (j = 0; j < numRows; j++) {
995  index = i + j * specLen;
996 /* if (sdata[index] > 0.0 && edata[i] > 0.00001) */
997  if (fabs(edata[i]) > 0.00001)
998  profile[index] = sdata[index] / edata[i];
999  else
1000  profile[index] = 0.0;
1001  }
1002  }
1003 
1004  for (j = 0; j < numRows; j++) {
1005 
1006  /*
1007  * Smooth each row in the dispersion direction, and enforce positivity
1008  */
1009 
1010  for (i = 0; i < specLen - smoothBox; i++) {
1011  vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen);
1012  value = cpl_vector_get_median_const(vprofile);
1013  cpl_vector_unwrap(vprofile);
1014  if (value < 0)
1015  value = 0.0;
1016  buffer[i + smoothBox / 2] = value;
1017  }
1018 
1019  /*
1020  * Replace the end portions (i.e., not median filtered) with a mean
1021  */
1022 
1023  vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen);
1024  value = cpl_vector_get_mean(vprofile);
1025  cpl_vector_unwrap(vprofile);
1026 
1027  if (value < 0)
1028  value = 0.0;
1029 
1030  for (i = 0; i < smoothBox / 2; i++)
1031  buffer[i] = value;
1032 
1033  vprofile = cpl_vector_wrap(smoothBox / 2,
1034  profile + specLen - smoothBox/2 + j*specLen);
1035  value = cpl_vector_get_mean(vprofile);
1036  cpl_vector_unwrap(vprofile);
1037 
1038  if (value < 0)
1039  value = 0.0;
1040 
1041  for (i = 0; i < smoothBox / 2; i++)
1042  buffer[i + specLen - smoothBox / 2] = value;
1043 
1044  for (i = 0; i < specLen; i++)
1045  profile[i + j * specLen] = buffer[i];
1046 
1047  }
1048 
1049  /*
1050  * Enforce normalization of spatial profile after smoothing
1051  */
1052 
1053  for (i = 0; i < specLen; i++) {
1054  for (j = 0, value = 0.0; j < numRows; j++)
1055  value += profile[i + j * specLen];
1056  if (value > 0.00001)
1057  for (j = 0; j < numRows; j++)
1058  profile[i + j * specLen] /= value;
1059  else
1060  for (j = 0; j < numRows; j++)
1061  profile[i + j * specLen] = 0.0;
1062  }
1063 
1064 
1065  /*
1066  * Optimal extraction
1067  */
1068 
1069  for (i = 0; i < specLen; i++) {
1070  sum = 0.0;
1071  sumSky = 0.0;
1072  sumWeight = 0.0;
1073  sumProf = 0.0;
1074  sumVar = 0;
1075  for (j = 0; j < numRows; j++) {
1076  index = i + j * specLen;
1077  /*
1078  if (sdata[index] > 0.0) {
1079  */
1080  //This is the theoretical estimated variance. In principle, since we
1081  //have the propagated variance, we could use that one, but I leave
1082  //this as this is the original algorithm (cgarcia)
1083  variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index])
1084  / conad;
1085  variance /= ncomb; /* If input dataset is sum of ncomb images */
1086  value = sdata[index] - edata[i] * profile[index];
1087  if (fabs(value) / sqrt(variance) < nsigma) {
1088  weight = 1000000 * profile[index] / variance;
1089  sum += weight * sdata[index];
1090  sumSky += weight * kdata[index];
1091  sumWeight += weight * profile[index];
1092  sumProf += profile[index];
1093  //This is how we propagated the variance. We assume that the
1094  //weigth has no error, although in has been computed from the
1095  //profile and the theoretical variance (which also includes the data)
1096  if(sci_var_win != NULL)
1097  sumVar += weight * weight * vardata[index];
1098  }
1099  }
1100 
1101  if (sumWeight > 0.00001) {
1102  edata[i] = sum / sumWeight;
1103  ekdata[i] = sumSky / sumWeight;
1104  if(sci_var_win != NULL)
1105  endata[i] = sqrt(sumVar / sumWeight / sumWeight); //This is the error, not the variance.
1106  else
1107  endata[i] = 1000 * sqrt(sumProf / sumWeight); //This was the old formula, which is not a real error propagation
1108  }
1109  else {
1110 /*
1111  edata[i] = 0.0;
1112  ekdata[i] = 0.0;
1113  endata[i] = 0.0;
1114 */
1115  //endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
1116  }
1117  }
1118  }
1119  cpl_free(profile);
1120  cpl_free(buffer);
1121  }
1122  else {
1123 
1124  /*
1125  * Add sky estimation for the simple aperture extraction.
1126  if (kdata[i + j * specLen] > 0.0)
1127  */
1128 
1129  for (i = 0; i < specLen; i++)
1130  for (j = 0, ekdata[i] = 0.0; j < numRows; j++)
1131  ekdata[i] += kdata[i + j * specLen];
1132 
1133  /*
1134  * Add error estimation for the simple aperture extraction.
1135  */
1136  for (i = 0; i < specLen; i++)
1137  {
1138  if(sci_var_win != NULL)
1139  {
1140  //We propagate the variance of a simple addition
1141  for (j = 0, endata[i] = 0.0; j < numRows; j++)
1142  endata[i] += vardata[i + j * specLen];
1143  endata[i] = sqrt(endata[i]); //We return the error, not the variance
1144  }
1145  else
1146  endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
1147 
1148  }
1149  }
1150 
1151 }
1152 
1153 
1200 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits,
1201  cpl_table *ids, cpl_table *crv,
1202  double reference)
1203 {
1204  const char *func = "mos_global_distortion";
1205 
1206  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
1207 
1208  cpl_table *global = NULL;
1209  cpl_table *coeff;
1210  cpl_table *dummy;
1211  cpl_vector *ci;
1212  cpl_vector *xmask;
1213  cpl_vector *ymask;
1214  cpl_bivector *mask;
1215  cpl_vector *xccd;
1216  cpl_vector *yccd;
1217  cpl_bivector *ccd;
1218  cpl_polynomial *poly;
1219  double *xtop;
1220  double *ytop;
1221  double *xbottom;
1222  double *ybottom;
1223  double *mxtop;
1224  double *mytop;
1225  double *mxbottom;
1226  double *mybottom;
1227  int *position;
1228  int *length;
1229  int *slit_id;
1230  int *mslit_id;
1231  int nslits, nmaskslits, npoints;
1232  int order;
1233  int i, j;
1234  int minslit = 6; // 12;
1235 
1236 
1237  if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) {
1238  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
1239  return NULL;
1240  }
1241 
1242  nslits = cpl_table_get_nrow(slits);
1243 
1244  if (nslits < minslit) {
1245  cpl_msg_warning(func, "Too few slits (%d < %d) for global "
1246  "distortion model determination", nslits, minslit);
1247  return NULL;
1248  }
1249 
1250  nmaskslits = cpl_table_get_nrow(maskslits);
1251 
1252  length = cpl_table_get_data_int(slits, "length");
1253  position = cpl_table_get_data_int(slits, "position");
1254  slit_id = cpl_table_get_data_int(slits, "slit_id");
1255  mslit_id = cpl_table_get_data_int(maskslits, "slit_id");
1256  xtop = cpl_table_get_data_double(slits, "xtop");
1257  ytop = cpl_table_get_data_double(slits, "ytop");
1258  xbottom = cpl_table_get_data_double(slits, "xbottom");
1259  ybottom = cpl_table_get_data_double(slits, "ybottom");
1260  mxtop = cpl_table_get_data_double(maskslits, "xtop");
1261  mytop = cpl_table_get_data_double(maskslits, "ytop");
1262  mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
1263  mybottom = cpl_table_get_data_double(maskslits, "ybottom");
1264 
1265 
1266  /*
1267  * Global IDS
1268  */
1269 
1270  coeff = cpl_table_new(nslits);
1271  cpl_table_copy_structure(coeff, ids);
1272  cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
1273  cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
1274  cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
1275  cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
1276 
1277  for (i = 0; i < nslits; i++) {
1278  for (j = 0; j < nmaskslits; j++) {
1279  if (slit_id[i] == mslit_id[j]) {
1280  cpl_table_set_double(coeff, "xmask", i,
1281  (mxtop[j] + mxbottom[j]) / 2);
1282  cpl_table_set_double(coeff, "ymask", i,
1283  (mytop[j] + mybottom[j]) / 2);
1284  }
1285  }
1286  }
1287 
1288  if (cpl_table_has_invalid(coeff, "xmask")) {
1289  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
1290  cpl_table_delete(coeff);
1291  return NULL;
1292  }
1293 
1294  for (i = 0; i < nslits; i++) {
1295  cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2);
1296  cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2);
1297  }
1298 
1299  for (i = 0; i < nslits; i++) {
1300 
1301  if (length[i] == 0)
1302  continue;
1303 
1304  cpl_table_and_selected_window(ids, position[i], length[i]);
1305  dummy = cpl_table_extract_selected(ids);
1306  for (j = 0; j < 6; j++) {
1307  if (cpl_table_has_column(dummy, clab[j])) {
1308  if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) {
1309  cpl_table_set_double(coeff, clab[j], i,
1310  cpl_table_get_column_median(dummy, clab[j]));
1311  }
1312  }
1313  }
1314 
1315  cpl_table_delete(dummy);
1316  cpl_table_select_all(ids);
1317 
1318  }
1319 
1320  for (j = 0; j < 6; j++) {
1321  if (cpl_table_has_column(coeff, clab[j])) {
1322  cpl_table_and_selected_invalid(coeff, clab[j]);
1323 
1324  if (cpl_table_not_selected(coeff))
1325  dummy = cpl_table_extract_selected(coeff);
1326  else
1327  break;
1328 
1329  npoints = cpl_table_get_nrow(dummy);
1330 
1331  if (npoints >= 6) {
1332 
1333  if (npoints >= 12)
1334  order = 2;
1335  else
1336  order = 1;
1337 
1338  ci = cpl_vector_wrap(npoints,
1339  cpl_table_get_data_double(dummy, clab[j]));
1340  if (j) {
1341  xccd = cpl_vector_wrap(npoints,
1342  cpl_table_get_data_double(dummy, "xccd"));
1343  yccd = cpl_vector_wrap(npoints,
1344  cpl_table_get_data_double(dummy, "yccd"));
1345  ccd = cpl_bivector_wrap_vectors(xccd, yccd);
1346 
1347 /* %%% */
1348  poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
1349 
1350  cpl_bivector_unwrap_vectors(ccd);
1351  cpl_vector_unwrap(xccd);
1352  cpl_vector_unwrap(yccd);
1353  cpl_vector_unwrap(ci);
1354  }
1355  else {
1356  xmask = cpl_vector_wrap(npoints,
1357  cpl_table_get_data_double(dummy, "xmask"));
1358  ymask = cpl_vector_wrap(npoints,
1359  cpl_table_get_data_double(dummy, "ymask"));
1360  mask = cpl_bivector_wrap_vectors(xmask, ymask);
1361 
1362 /* %%% */
1363  poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
1364 
1365  cpl_bivector_unwrap_vectors(mask);
1366  cpl_vector_unwrap(xmask);
1367  cpl_vector_unwrap(ymask);
1368  cpl_vector_unwrap(ci);
1369  }
1370  }
1371  else {
1372  cpl_size p[2] = {0, 0};
1373  poly = cpl_polynomial_new(2);
1374  cpl_polynomial_set_coeff(poly, p,
1375  cpl_table_get_column_median(dummy, clab[j]));
1376  }
1377 
1378  cpl_table_delete(dummy);
1379 
1380  global = write_global_distortion(global, j, poly);
1381 
1382  cpl_polynomial_delete(poly);
1383 
1384  cpl_table_select_all(coeff);
1385  }
1386  }
1387 
1388  cpl_table_delete(coeff);
1389 
1390 
1391  /*
1392  * Add model's reference wavelength
1393  */
1394 
1395  cpl_table_set_double(global, "a00", 6, reference);
1396 
1397 
1398  /*
1399  * Global curvature model
1400  */
1401 
1402  coeff = cpl_table_duplicate(crv);
1403  cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
1404  cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
1405  cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
1406  cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
1407  slit_id = cpl_table_get_data_int(coeff, "slit_id");
1408  npoints = cpl_table_get_nrow(coeff);
1409 
1410  for (i = 0; i < npoints; i++) {
1411  for (j = 0; j < nmaskslits; j++) {
1412  if (slit_id[i] == mslit_id[j]) {
1413  if (i%2) {
1414  cpl_table_set_double(coeff, "xmask", i, mxbottom[j]);
1415  cpl_table_set_double(coeff, "ymask", i, mybottom[j]);
1416  }
1417  else {
1418  cpl_table_set_double(coeff, "xmask", i, mxtop[j]);
1419  cpl_table_set_double(coeff, "ymask", i, mytop[j]);
1420  }
1421  }
1422  }
1423  if (i%2)
1424  {
1425  cpl_table_set_double(coeff, "xccd", i, xtop[(i-1)/2]);
1426  cpl_table_set_double(coeff, "yccd", i, ytop[(i-1)/2]);
1427  }
1428  else
1429  {
1430  cpl_table_set_double(coeff, "xccd", i, xbottom[i/2]);
1431  cpl_table_set_double(coeff, "yccd", i, ybottom[i/2]);
1432  }
1433  }
1434 
1435  if (cpl_table_has_invalid(coeff, "xmask")) {
1436  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
1437  cpl_table_delete(coeff);
1438  return NULL;
1439  }
1440 
1441  for (j = 0; j < 3; j++) {
1442  cpl_polynomial * poly_ccd;
1443  if (cpl_table_has_column(coeff, clab[j])) {
1444  cpl_table_and_selected_invalid(coeff, clab[j]);
1445 
1446  if (cpl_table_not_selected(coeff))
1447  dummy = cpl_table_extract_selected(coeff);
1448  else
1449  break;
1450 
1451  npoints = cpl_table_get_nrow(dummy);
1452 
1453  if (npoints >= 6) {
1454 
1455  if (npoints >= 12)
1456  order = 2;
1457  else
1458  order = 1;
1459 
1460  ci = cpl_vector_wrap(npoints,
1461  cpl_table_get_data_double(dummy, clab[j]));
1462  xmask = cpl_vector_wrap(npoints,
1463  cpl_table_get_data_double(dummy, "xmask"));
1464  ymask = cpl_vector_wrap(npoints,
1465  cpl_table_get_data_double(dummy, "ymask"));
1466  mask = cpl_bivector_wrap_vectors(xmask, ymask);
1467 
1468  poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
1469 
1470  xccd = cpl_vector_wrap(npoints,
1471  cpl_table_get_data_double(dummy, "xccd"));
1472  yccd = cpl_vector_wrap(npoints,
1473  cpl_table_get_data_double(dummy, "yccd"));
1474  ccd = cpl_bivector_wrap_vectors(xccd, yccd);
1475 
1476  poly_ccd = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
1477 
1478 
1479  cpl_bivector_unwrap_vectors(mask);
1480  cpl_vector_unwrap(ci);
1481  cpl_vector_unwrap(xmask);
1482  cpl_vector_unwrap(ymask);
1483  }
1484  else {
1485  cpl_size p[2] = {0, 0};
1486  poly = cpl_polynomial_new(2);
1487  cpl_polynomial_set_coeff(poly, p,
1488  cpl_table_get_column_median(dummy, clab[j]));
1489  }
1490 
1491  cpl_table_delete(dummy);
1492 
1493  global = write_global_distortion(global, j + 7, poly);
1494  global = write_global_distortion(global, j + 10, poly_ccd);
1495 
1496  cpl_polynomial_delete(poly);
1497  cpl_polynomial_delete(poly_ccd);
1498  cpl_table_select_all(coeff);
1499  }
1500  }
1501 
1502  cpl_table_delete(coeff);
1503 
1504  return global;
1505 
1506 }
1507 
1508 
1546 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits,
1547  int ysize)
1548 {
1549  const char *func = "mos_build_slit_location";
1550 
1551  cpl_propertylist *sort_col;
1552  cpl_polynomial *ids0;
1553  cpl_polynomial *crv[3];
1554  cpl_polynomial *loc_crv;
1555  cpl_vector *point;
1556  cpl_table *slits;
1557  cpl_size nslits;
1558  int *slit_id;
1559  double *dpoint;
1560  double *xtop;
1561  double *ytop;
1562  double *xbottom;
1563  double *ybottom;
1564  double *mxtop;
1565  double *mytop;
1566  double *mxbottom;
1567  double *mybottom;
1568  cpl_size i;
1569  cpl_size j;
1570 
1571 
1572  if (global == NULL || maskslits == NULL) {
1573  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
1574  return NULL;
1575  }
1576 
1577  nslits = cpl_table_get_nrow(maskslits);
1578  slit_id = cpl_table_get_data_int(maskslits, "slit_id");
1579  mxtop = cpl_table_get_data_double(maskslits, "xtop");
1580  mytop = cpl_table_get_data_double(maskslits, "ytop");
1581  mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
1582  mybottom = cpl_table_get_data_double(maskslits, "ybottom");
1583 
1584  slits = cpl_table_duplicate(maskslits);
1585 
1586  xtop = cpl_table_get_data_double(slits, "xtop");
1587  ytop = cpl_table_get_data_double(slits, "ytop");
1588  xbottom = cpl_table_get_data_double(slits, "xbottom");
1589  ybottom = cpl_table_get_data_double(slits, "ybottom");
1590 
1591  ids0 = read_global_distortion(global, 0);
1592  crv[0] = read_global_distortion(global, 7);
1593  crv[1] = read_global_distortion(global, 8);
1594  crv[2] = read_global_distortion(global, 9);
1595 
1596  loc_crv = cpl_polynomial_new(1);
1597 
1598  point = cpl_vector_new(2);
1599  dpoint = cpl_vector_get_data(point);
1600 
1601  for (i = 0; i < nslits; i++) {
1602  dpoint[0] = mxtop[i];
1603  dpoint[1] = mytop[i];
1604 
1605  xtop[i] = cpl_polynomial_eval(ids0, point);
1606 
1607  for (j = 0; j < 3; j++)
1608  if (crv[j])
1609  cpl_polynomial_set_coeff(loc_crv, &j,
1610  cpl_polynomial_eval(crv[j], point));
1611 
1612  ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL);
1613 
1614  dpoint[0] = mxbottom[i];
1615  dpoint[1] = mybottom[i];
1616  xbottom[i] = cpl_polynomial_eval(ids0, point);
1617 
1618  for (j = 0; j < 3; j++)
1619  if (crv[j])
1620  cpl_polynomial_set_coeff(loc_crv, &j,
1621  cpl_polynomial_eval(crv[j], point));
1622 
1623  ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL);
1624  }
1625 
1626  cpl_vector_delete(point);
1627  cpl_polynomial_delete(ids0);
1628  cpl_polynomial_delete(loc_crv);
1629  for (j = 0; j < 3; j++)
1630  cpl_polynomial_delete(crv[j]);
1631 
1632  sort_col = cpl_propertylist_new();
1633  cpl_propertylist_append_bool(sort_col, "ytop", 1);
1634  cpl_table_sort(slits, sort_col);
1635  cpl_table_sort(maskslits, sort_col);
1636  cpl_propertylist_delete(sort_col);
1637 
1638  /*
1639  * Eliminate slits which are _entirely_ outside the CCD
1640  */
1641 
1642  cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1);
1643  cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0);
1644  cpl_table_erase_selected(slits);
1645 
1646  nslits = cpl_table_get_nrow(slits);
1647 
1648  if (nslits == 0) {
1649  cpl_msg_warning(func, "No slits found on the CCD");
1650  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
1651  cpl_table_delete(slits);
1652  return NULL;
1653  }
1654 
1655  if (nslits > 1)
1656  cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slits are entirely or partially "
1657  "contained in CCD", nslits);
1658  else
1659  cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slit is entirely or partially "
1660  "contained in CCD", nslits);
1661 
1662  return slits;
1663 
1664 }
1665 
1666 
1693 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits,
1694  cpl_table *slits)
1695 {
1696  const char *func = "mos_build_curv_coeff";
1697 
1698  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
1699  /* Max order is 5 */
1700 
1701  cpl_polynomial *crv[3];
1702  cpl_vector *point;
1703  cpl_table *polytraces;
1704  double *dpoint;
1705  double *xtop;
1706  double *ytop;
1707  double *xbottom;
1708  double *ybottom;
1709  int *slit_id;
1710  int *valid_id;
1711  int nslits, nvalid;
1712  int found;
1713  int i, j, k;
1714 
1715 
1716  if (global == NULL || slits == NULL || maskslits == NULL) {
1717  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
1718  return NULL;
1719  }
1720 
1721  nslits = cpl_table_get_nrow(maskslits);
1722  slit_id = cpl_table_get_data_int(maskslits, "slit_id");
1723  xtop = cpl_table_get_data_double(maskslits, "xtop");
1724  ytop = cpl_table_get_data_double(maskslits, "ytop");
1725  xbottom = cpl_table_get_data_double(maskslits, "xbottom");
1726  ybottom = cpl_table_get_data_double(maskslits, "ybottom");
1727 
1728  polytraces = cpl_table_new(2*nslits);
1729  cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
1730  for (i = 0; i < 3; i++)
1731  cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
1732 
1733  crv[0] = read_global_distortion(global, 7);
1734  crv[1] = read_global_distortion(global, 8);
1735  crv[2] = read_global_distortion(global, 9);
1736 
1737  point = cpl_vector_new(2);
1738  dpoint = cpl_vector_get_data(point);
1739 
1740  for (i = 0; i < nslits; i++) {
1741  for (j = 0; j < 2; j++) { /* For top and bottom trace of each slit */
1742 
1743  cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
1744 
1745  if (j) {
1746  dpoint[0] = xbottom[i];
1747  dpoint[1] = ybottom[i];
1748  }
1749  else {
1750  dpoint[0] = xtop[i];
1751  dpoint[1] = ytop[i];
1752  }
1753 
1754  for (k = 0; k < 3; k++)
1755  if (crv[j])
1756  cpl_table_set_double(polytraces, clab[k], 2*i+j,
1757  cpl_polynomial_eval(crv[k], point));
1758  }
1759  }
1760 
1761  cpl_vector_delete(point);
1762  for (j = 0; j < 3; j++)
1763  cpl_polynomial_delete(crv[j]);
1764 
1765  /*
1766  * Eliminate slits which are _entirely_ outside the CCD
1767  */
1768 
1769  nvalid = cpl_table_get_nrow(slits);
1770  valid_id = cpl_table_get_data_int(slits, "slit_id");
1771  cpl_table_unselect_all(polytraces);
1772  for (i = 0; i < nslits; i++) {
1773  found = 0;
1774  for (j = 0; j < nvalid; j++) {
1775  if (slit_id[i] == valid_id[j]) {
1776  found = 1;
1777  break;
1778  }
1779  }
1780  if (!found) {
1781  cpl_table_select_row(polytraces, 2*i);
1782  cpl_table_select_row(polytraces, 2*i + 1);
1783  }
1784  }
1785  cpl_table_erase_selected(polytraces);
1786 
1787  nslits = cpl_table_get_nrow(polytraces);
1788 
1789  if (nslits == 0) {
1790  cpl_msg_warning(func, "No slits found on the CCD");
1791  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
1792  cpl_table_delete(polytraces);
1793  return NULL;
1794  }
1795 
1796  if (nslits > 2)
1797  cpl_msg_info(func, "Curvature model: %d slits are entirely or "
1798  "partially contained in CCD", nslits / 2);
1799  else
1800  cpl_msg_info(func, "Curvature model: %d slit is entirely or "
1801  "partially contained in CCD", nslits / 2);
1802 
1803  return polytraces;
1804 }
1805 
1806 
1848 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
1849 {
1850  const char *func = "mos_build_disp_coeff";
1851 
1852  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
1853 
1854  cpl_polynomial *ids[6];
1855  cpl_vector *point;
1856  cpl_table *idscoeff;
1857  double *dpoint;
1858  double *xtop;
1859  double *ytop;
1860  double *xbottom;
1861  double *ybottom;
1862  int *position;
1863  int *length;
1864  int nslits;
1865  int nrows;
1866  int order;
1867  int ylow, yhig;
1868  int i, j, k;
1869 
1870 
1871  if (global == NULL || slits == NULL) {
1872  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
1873  return NULL;
1874  }
1875 
1876  nslits = cpl_table_get_nrow(slits);
1877  position = cpl_table_get_data_int(slits, "position");
1878  length = cpl_table_get_data_int(slits, "length");
1879  xtop = cpl_table_get_data_double(slits, "xtop");
1880  ytop = cpl_table_get_data_double(slits, "ytop");
1881  xbottom = cpl_table_get_data_double(slits, "xbottom");
1882  ybottom = cpl_table_get_data_double(slits, "ybottom");
1883 
1884  for (i = 0; i < 6; i++)
1885  ids[i] = read_global_distortion(global, i);
1886 
1887  for (i = 0; i < 6; i++)
1888  if (ids[i] == NULL)
1889  break;
1890 
1891  order = i - 1;
1892 
1893  if (order < 1) {
1894  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
1895  return NULL;
1896  }
1897 
1898  nrows = 0;
1899  for (i = 0; i < nslits; i++)
1900  nrows += length[i];
1901 
1902  idscoeff = cpl_table_new(nrows);
1903 
1904  for (j = 0; j <= order; j++)
1905  cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
1906 
1907  cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE);
1908  cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0);
1909  cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT);
1910  cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0);
1911 
1912  point = cpl_vector_new(2);
1913  dpoint = cpl_vector_get_data(point);
1914 
1915  for (i = 0; i < nslits; i++) {
1916 
1917  if (length[i] == 0)
1918  continue;
1919 
1920  ylow = position[i];
1921  yhig = ylow + length[i];
1922 
1923  for (j = 0; j <= order; j++) {
1924  if (j) {
1925  for (k = 0; k < length[i]; k++) {
1926  dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i];
1927  dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i];
1928  cpl_table_set_double(idscoeff, clab[j], ylow + k,
1929  cpl_polynomial_eval(ids[j], point));
1930  }
1931  }
1932  else {
1933  for (k = 0; k < length[i]; k++) {
1934  cpl_table_set_double(idscoeff, clab[0], ylow + k,
1935  xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]);
1936  }
1937  }
1938  }
1939  }
1940 
1941  cpl_vector_delete(point);
1942  for (j = 0; j < 6; j++)
1943  cpl_polynomial_delete(ids[j]);
1944 
1945  return idscoeff;
1946 
1947 }
1948 
1949 
1972 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits,
1973  cpl_table *polytraces, double reference,
1974  double blue, double red, double dispersion)
1975 {
1976  const char *func = "mos_subtract_sky";
1977 
1978  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
1979  /* Max order is 5 */
1980 
1981  cpl_image *sky;
1982  cpl_bivector *list;
1983  cpl_vector *listx;
1984  cpl_vector *listy;
1985  cpl_polynomial *polytop;
1986  cpl_polynomial *polybot;
1987  cpl_polynomial *trend;
1988 
1989  int *slit_id;
1990  double *dlistx;
1991  double *dlisty;
1992  float *sdata;
1993  float *kdata;
1994  double top, bot;
1995  int itop, ibot;
1996  double coeff;
1997  double ytop, ybot;
1998  double m, q, err;
1999  int npix;
2000 
2001  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
2002  int nx, ny;
2003  int nslits;
2004  int *length;
2005  int missing_top, missing_bot;
2006  int order;
2007  int null;
2008  int window = 50; /* Longer slits have polynomial sky model */
2009  int count;
2010  int i, j;
2011  cpl_size k;
2012 
2013 
2014  if (science == NULL || slits == NULL || polytraces == NULL) {
2015  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
2016  return NULL;
2017  }
2018 
2019  if (dispersion <= 0.0) {
2020  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2021  return NULL;
2022  }
2023 
2024  if (red - blue < dispersion) {
2025  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2026  return NULL;
2027  }
2028 
2029  nx = cpl_image_get_size_x(science);
2030  ny = cpl_image_get_size_y(science);
2031 
2032  sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
2033 
2034  sdata = cpl_image_get_data(science);
2035  kdata = cpl_image_get_data(sky);
2036 
2037  nslits = cpl_table_get_nrow(slits);
2038  order = cpl_table_get_ncol(polytraces) - 2;
2039  length = cpl_table_get_data_int(slits, "length");
2040  slit_id = cpl_table_get_data_int(slits, "slit_id");
2041 
2042  /*
2043  * The spatial resampling is performed for a certain number of
2044  * pixels above and below the position of the reference wavelength:
2045  */
2046 
2047  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
2048  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
2049 
2050  for (i = 0; i < nslits; i++) {
2051 
2052  if (length[i] == 0)
2053  continue;
2054 
2055 
2056  /*
2057  * Recover from the table of spectral curvature coefficients
2058  * the curvature polynomials.
2059  */
2060 
2061  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
2062 
2063  start_pixel = refpixel - pixel_below;
2064  if (start_pixel < 0)
2065  start_pixel = 0;
2066 
2067  end_pixel = refpixel + pixel_above;
2068  if (end_pixel > nx)
2069  end_pixel = nx;
2070 
2071  missing_top = 0;
2072  polytop = cpl_polynomial_new(1);
2073  for (k = 0; k <= order; k++) {
2074  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
2075  if (null) {
2076  cpl_polynomial_delete(polytop);
2077  missing_top = 1;
2078  break;
2079  }
2080  cpl_polynomial_set_coeff(polytop, &k, coeff);
2081  }
2082 
2083  missing_bot = 0;
2084  polybot = cpl_polynomial_new(1);
2085  for (k = 0; k <= order; k++) {
2086  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
2087  if (null) {
2088  cpl_polynomial_delete(polybot);
2089  missing_bot = 1;
2090  break;
2091  }
2092  cpl_polynomial_set_coeff(polybot, &k, coeff);
2093  }
2094 
2095  if (missing_top && missing_bot) {
2096  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
2097  slit_id[i]);
2098  continue;
2099  }
2100 
2101  /*
2102  * In case just one of the two edges was not traced, the other
2103  * edge curvature model is duplicated and shifted to the other
2104  * end of the slit: better than nothing!
2105  */
2106 
2107  if (missing_top) {
2108  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
2109  "the spectral curvature of the lower edge "
2110  "is used instead.", slit_id[i]);
2111  polytop = cpl_polynomial_duplicate(polybot);
2112  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2113  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2114  k = 0;
2115  coeff = cpl_polynomial_get_coeff(polybot, &k);
2116  coeff += ytop - ybot;
2117  cpl_polynomial_set_coeff(polytop, &k, coeff);
2118  }
2119 
2120  if (missing_bot) {
2121  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
2122  "the spectral curvature of the upper edge "
2123  "is used instead.", slit_id[i]);
2124  polybot = cpl_polynomial_duplicate(polytop);
2125  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2126  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2127  k = 0;
2128  coeff = cpl_polynomial_get_coeff(polytop, &k);
2129  coeff -= ytop - ybot;
2130  cpl_polynomial_set_coeff(polybot, &k, coeff);
2131  }
2132 
2133 
2134  /*
2135  * Now read pixel values along spatial direction, and fit them.
2136  */
2137 
2138  for (j = start_pixel; j < end_pixel; j++) {
2139  top = cpl_polynomial_eval_1d(polytop, j, NULL);
2140  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
2141  itop = floor(top + 0.5) + 1;
2142  ibot = floor(bot + 0.5);
2143  if (itop > ny)
2144  itop = ny;
2145  if (ibot < 0)
2146  ibot = 0;
2147  npix = itop - ibot;
2148  if (npix < 5)
2149  break;
2150 
2151  list = cpl_bivector_new(npix);
2152  listx = cpl_bivector_get_x(list);
2153  listy = cpl_bivector_get_y(list);
2154  dlistx = cpl_vector_get_data(listx);
2155  dlisty = cpl_vector_get_data(listy);
2156 
2157  for (k = 0; k < npix; k++) {
2158  dlistx[k] = k;
2159  dlisty[k] = sdata[j + (ibot + k)*nx];
2160  }
2161 
2162  if (robustLinearFit(list, &q, &m, &err)) {
2163  cpl_bivector_delete(list);
2164  continue;
2165  }
2166 
2167  cpl_bivector_delete(list);
2168 
2169  for (k = 0; k < npix; k++) {
2170  kdata[j + (ibot + k)*nx] = m*k + q;
2171  }
2172 
2173  if (npix > window) {
2174 
2175  /*
2176  * Polynomial iteration
2177  */
2178 
2179  err = 3*sqrt(err);
2180 
2181  count = 0;
2182  for (k = 0; k < npix; k++)
2183  if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err)
2184  count++;
2185 
2186  if (count < 10)
2187  continue;
2188 
2189  list = cpl_bivector_new(count);
2190  listx = cpl_bivector_get_x(list);
2191  listy = cpl_bivector_get_y(list);
2192  dlistx = cpl_vector_get_data(listx);
2193  dlisty = cpl_vector_get_data(listy);
2194 
2195  count = 0;
2196  for (k = 0; k < npix; k++) {
2197  if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) {
2198  dlistx[count] = k;
2199  dlisty[count] = sdata[j + (ibot + k)*nx];
2200  count++;
2201  }
2202  }
2203 
2204  trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err);
2205 
2206  cpl_bivector_delete(list);
2207 
2208  err = 3*sqrt(err);
2209 
2210  count = 0;
2211  for (k = 0; k < npix; k++)
2212  if (fabs(sdata[j + (ibot + k)*nx]
2213  - cpl_polynomial_eval_1d(trend, k, NULL)) < err)
2214  count++;
2215 
2216  if (count < 10) {
2217  cpl_polynomial_delete(trend);
2218  continue;
2219  }
2220 
2221  list = cpl_bivector_new(count);
2222  listx = cpl_bivector_get_x(list);
2223  listy = cpl_bivector_get_y(list);
2224  dlistx = cpl_vector_get_data(listx);
2225  dlisty = cpl_vector_get_data(listy);
2226 
2227  count = 0;
2228  for (k = 0; k < npix; k++) {
2229  if (fabs(sdata[j + (ibot + k)*nx]
2230  - cpl_polynomial_eval_1d(trend, k, NULL)) < err) {
2231  dlistx[count] = k;
2232  dlisty[count] = sdata[j + (ibot + k)*nx];
2233  count++;
2234  }
2235  }
2236 
2237  cpl_polynomial_delete(trend);
2238 
2239  trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err);
2240 
2241  cpl_bivector_delete(list);
2242 
2243  for (k = 0; k < npix; k++) {
2244  kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend,
2245  k, NULL);
2246  }
2247 
2248  cpl_polynomial_delete(trend);
2249  }
2250  }
2251  cpl_polynomial_delete(polytop);
2252  cpl_polynomial_delete(polybot);
2253  }
2254 
2255  cpl_image_subtract(science, sky);
2256 
2257  return sky;
2258 }
2259 
2260 
2293 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial,
2294  cpl_table *slits, cpl_table *polytraces,
2295  double reference, double blue, double red,
2296  double dispersion, int sradius, int polyorder)
2297 {
2298  const char *func = "mos_normalise_flat";
2299 
2300  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
2301  /* Max order is 5 */
2302 
2303  cpl_image *rectified;
2304  cpl_image *smo_flat;
2305  cpl_image *exslit;
2306  cpl_vector *positions;
2307  cpl_vector *flux;
2308  cpl_vector *smo_flux;
2309  cpl_polynomial *trend;
2310  cpl_polynomial *polytop;
2311  cpl_polynomial *polybot;
2312 
2313  int *slit_id;
2314  float *p;
2315  float *data;
2316  double *fdata;
2317  double *pdata;
2318  float *sdata;
2319  float *xdata;
2320  float *wdata;
2321  double vtop, vbot, value;
2322  double top, bot;
2323  double coeff;
2324  double ytop, ybot;
2325  double ypos;
2326  double fvalue;
2327  int ivalue;
2328  int yint, yprev;
2329  int npseudo;
2330 
2331  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
2332  int nx, ny, nsubx, nsuby;
2333  int xlow, ylow, xhig, yhig;
2334  int nslits;
2335  int *position;
2336  int *length;
2337  int missing_top, missing_bot;
2338  int order;
2339  int npoints;
2340  int uradius;
2341  int null;
2342  int i, j;
2343  cpl_size k;
2344 
2345 /* int exclude = 5; Number of excluded pixels at edges */
2346 
2347  /* For debug puposes only: cpl_image *smo_rectified; */
2348 
2349 
2350  if (flat == NULL || slits == NULL || polytraces == NULL) {
2351  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
2352  return NULL;
2353  }
2354 
2355  if (dispersion <= 0.0) {
2356  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2357  return NULL;
2358  }
2359 
2360  if (red - blue < dispersion) {
2361  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2362  return NULL;
2363  }
2364 
2365  rectified = mos_spatial_calibration(flat, slits, polytraces, reference,
2366  blue, red, dispersion, 0, NULL);
2367 
2368  nx = cpl_image_get_size_x(rectified);
2369  ny = cpl_image_get_size_y(rectified);
2370 
2371  smo_flat = cpl_image_new(cpl_image_get_size_x(spatial),
2372  cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT);
2373  wdata = cpl_image_get_data(smo_flat);
2374 
2375  nslits = cpl_table_get_nrow(slits);
2376  order = cpl_table_get_ncol(polytraces) - 2;
2377  position = cpl_table_get_data_int(slits, "position");
2378  length = cpl_table_get_data_int(slits, "length");
2379  slit_id = cpl_table_get_data_int(slits, "slit_id");
2380 
2381  /*
2382  * The spatial resampling is performed for a certain number of
2383  * pixels above and below the position of the reference wavelength:
2384  */
2385 
2386  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
2387  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
2388 
2389  xlow = 1;
2390  xhig = nx;
2391  for (i = 0; i < nslits; i++) {
2392 
2393  if (length[i] == 0)
2394  continue;
2395 
2396  /*
2397  * We DON'T write:
2398  *
2399  * ylow = position[i];
2400  * yhig = ylow + length[i];
2401  *
2402  * because the cpl_image pixels are counted from 1, and because in
2403  * cpl_image_extract() the coordinates of the last pixel are inclusive.
2404  */
2405 
2406  ylow = position[i] + 1;
2407  yhig = ylow + length[i] - 1;
2408 
2409  exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig);
2410 
2411  if (polyorder < 0) {
2412 
2413  cpl_image_turn(exslit, -1); /* For faster memory access */
2414 
2415  nsubx = cpl_image_get_size_x(exslit);
2416  nsuby = cpl_image_get_size_y(exslit);
2417  data = cpl_image_get_data(exslit);
2418  flux = cpl_vector_new(nsubx);
2419 
2420  uradius = nsubx / 2;
2421  if (uradius > sradius)
2422  uradius = sradius;
2423 
2424  for (j = 0; j < nsuby; j++) {
2425  fdata = cpl_vector_get_data(flux);
2426  p = data;
2427  for (k = 0; k < nsubx; k++)
2428  *fdata++ = *p++;
2429  smo_flux = cpl_vector_filter_median_create(flux, uradius);
2430  fdata = cpl_vector_get_data(smo_flux);
2431  p = data;
2432  for (k = 0; k < nsubx; k++)
2433  *p++ = *fdata++;
2434  cpl_vector_delete(smo_flux);
2435  data += nsubx;
2436  }
2437 
2438  cpl_vector_delete(flux);
2439 
2440 
2441  /*
2442  * First fit fluxes along the spatial direction with a low-degree
2443  * polynomial (excluding the first and the last pixels, close to
2444  * the edges)
2445  */
2446 /*
2447  if (nsubx-2*exclude > 10) {
2448  flux = cpl_vector_new(nsubx-2*exclude);
2449  fdata = cpl_vector_get_data(flux);
2450  positions = cpl_vector_new(nsubx-2*exclude);
2451  for (j = 0; j < nsubx-2*exclude; j++)
2452  cpl_vector_set(positions, j, j+exclude);
2453 
2454  for (k = 0; k < nsuby; k++) {
2455  for (j = 0; j < nsubx-2*exclude; j++)
2456  fdata[j] = data[j+exclude];
2457  trend = cpl_polynomial_fit_1d_create(positions, flux,
2458  1, NULL);
2459  for (j = 0; j < nsubx; j++)
2460  data[j] = cpl_polynomial_eval_1d(trend, j, NULL);
2461  cpl_polynomial_delete(trend);
2462  data += nsubx;
2463  }
2464 
2465  cpl_vector_delete(flux);
2466  cpl_vector_delete(positions);
2467  }
2468 */
2469 
2470  /*
2471  * Now smooth along the dispersion direction
2472  */
2473 
2474  cpl_image_turn(exslit, 1); /* For faster memory access */
2475  nsubx = cpl_image_get_size_x(exslit);
2476  nsuby = cpl_image_get_size_y(exslit);
2477  data = cpl_image_get_data(exslit);
2478 
2479  for (j = 0; j < nsuby; j++) {
2480  flux = cpl_vector_new(nsubx);
2481  fdata = cpl_vector_get_data(flux);
2482  p = data;
2483  for (k = 0; k < nsubx; k++)
2484  *fdata++ = *p++;
2485  smo_flux = cpl_vector_filter_median_create(flux, sradius);
2486  cpl_vector_delete(flux);
2487  fdata = cpl_vector_get_data(smo_flux);
2488  p = data;
2489  for (k = 0; k < nsubx; k++)
2490  *p++ = *fdata++;
2491  cpl_vector_delete(smo_flux);
2492  data += nsubx;
2493  }
2494  }
2495  else {
2496 
2497  /*
2498  * Fit with a polynomial the flat field trend row by row.
2499  */
2500 
2501  nsubx = cpl_image_get_size_x(exslit);
2502  nsuby = cpl_image_get_size_y(exslit);
2503  data = cpl_image_get_data(exslit);
2504 
2505  for (j = 0; j < nsuby; j++) {
2506 
2507  /*
2508  * First get the size of the vectors to allocate:
2509  */
2510 
2511  npoints = 0;
2512  p = data + j*nsubx;
2513  for (k = 0; k < nsubx; k++)
2514  if (p[k] > 1.0)
2515  npoints++;
2516 
2517  if (npoints > polyorder + 1) {
2518 
2519  /*
2520  * Fill the vectors for the fitting
2521  */
2522 
2523  flux = cpl_vector_new(npoints);
2524  fdata = cpl_vector_get_data(flux);
2525  positions = cpl_vector_new(npoints);
2526  pdata = cpl_vector_get_data(positions);
2527 
2528  npoints = 0;
2529  p = data + j*nsubx;
2530  for (k = 0; k < nsubx; k++) {
2531  if (p[k] > 1.0) {
2532  fdata[npoints] = p[k];
2533  pdata[npoints] = k;
2534  npoints++;
2535  }
2536  }
2537 
2538  trend = cpl_polynomial_fit_1d_create(positions, flux,
2539  polyorder, NULL);
2540 
2541  cpl_vector_delete(flux);
2542  cpl_vector_delete(positions);
2543 
2544  if (trend) {
2545  p = data + j*nsubx;
2546  for (k = 0; k < nsubx; k++)
2547  if (p[k] > 1.0)
2548  p[k] = cpl_polynomial_eval_1d(trend, k, NULL);
2549  cpl_polynomial_delete(trend);
2550  }
2551  else {
2552  cpl_msg_warning(func, "Invalid flat field flux fit "
2553  "(ignored)");
2554  }
2555  }
2556  }
2557  }
2558 
2559 
2560  /*
2561  * Recover from the table of spectral curvature coefficients
2562  * the curvature polynomials.
2563  */
2564 
2565  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
2566 
2567  start_pixel = refpixel - pixel_below;
2568  if (start_pixel < 0)
2569  start_pixel = 0;
2570 
2571  end_pixel = refpixel + pixel_above;
2572  if (end_pixel > nx)
2573  end_pixel = nx;
2574 
2575  missing_top = 0;
2576  polytop = cpl_polynomial_new(1);
2577  for (k = 0; k <= order; k++) {
2578  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
2579  if (null) {
2580  cpl_polynomial_delete(polytop);
2581  missing_top = 1;
2582  break;
2583  }
2584  cpl_polynomial_set_coeff(polytop, &k, coeff);
2585  }
2586 
2587  missing_bot = 0;
2588  polybot = cpl_polynomial_new(1);
2589  for (k = 0; k <= order; k++) {
2590  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
2591  if (null) {
2592  cpl_polynomial_delete(polybot);
2593  missing_bot = 1;
2594  break;
2595  }
2596  cpl_polynomial_set_coeff(polybot, &k, coeff);
2597  }
2598 
2599  if (missing_top && missing_bot) {
2600  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
2601  slit_id[i]);
2602  continue;
2603  }
2604 
2605  /*
2606  * In case just one of the two edges was not traced, the other
2607  * edge curvature model is duplicated and shifted to the other
2608  * end of the slit: better than nothing!
2609  */
2610 
2611  if (missing_top) {
2612  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
2613  "the spectral curvature of the lower edge "
2614  "is used instead.", slit_id[i]);
2615  polytop = cpl_polynomial_duplicate(polybot);
2616  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2617  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2618  k = 0;
2619  coeff = cpl_polynomial_get_coeff(polybot, &k);
2620  coeff += ytop - ybot;
2621  cpl_polynomial_set_coeff(polytop, &k, coeff);
2622  }
2623 
2624  if (missing_bot) {
2625  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
2626  "the spectral curvature of the upper edge "
2627  "is used instead.", slit_id[i]);
2628  polybot = cpl_polynomial_duplicate(polytop);
2629  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2630  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2631  k = 0;
2632  coeff = cpl_polynomial_get_coeff(polytop, &k);
2633  coeff -= ytop - ybot;
2634  cpl_polynomial_set_coeff(polybot, &k, coeff);
2635  }
2636 
2637 
2638  /*
2639  * Now map smoothed image to CCD.
2640  * Note that the npseudo value related to this slit is equal
2641  * to the number of spatial pseudo-pixels decreased by 1
2642  * (compare with function mos_spatial_calibration()).
2643  */
2644 
2645  nx = cpl_image_get_size_x(flat);
2646  ny = cpl_image_get_size_y(flat);
2647 
2648  sdata = cpl_image_get_data(spatial);
2649  xdata = cpl_image_get_data(exslit);
2650  npseudo = cpl_image_get_size_y(exslit) - 1;
2651 
2652  /*
2653  * Write interpolated smoothed values to CCD image
2654  */
2655 
2656  for (j = start_pixel; j < end_pixel; j++) {
2657  top = cpl_polynomial_eval_1d(polytop, j, NULL);
2658  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
2659  for (k = 0; k <= npseudo; k++) {
2660  ypos = top - k*(top-bot)/npseudo;
2661  yint = ypos;
2662 
2663  /*
2664  * The line:
2665  * value = sdata[j + nx*yint];
2666  * should be equivalent to:
2667  * value = npseudo*(top-yint)/(top-bot);
2668  */
2669 
2670  if (yint < 0 || yint >= ny-1) {
2671  yprev = yint;
2672  continue;
2673  }
2674 
2675  value = sdata[j + nx*yint]; /* Spatial coordinate on CCD */
2676  ivalue = value; /* Nearest spatial pixels: */
2677  fvalue = value - ivalue; /* ivalue and ivalue+1 */
2678  if (ivalue < npseudo && ivalue >= 0) {
2679  vtop = xdata[j + nx*(npseudo-ivalue)];
2680  vbot = xdata[j + nx*(npseudo-ivalue-1)];
2681  wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue;
2682 
2683  if (k) {
2684 
2685  /*
2686  * This is added to recover lost pixels on
2687  * the CCD image (pixels are lost because
2688  * the CCD pixels are less than npseudo+1).
2689  */
2690 
2691  if (yprev - yint > 1) {
2692  value = sdata[j + nx*(yint+1)];
2693  ivalue = value;
2694  fvalue = value - ivalue;
2695  if (ivalue < npseudo && ivalue >= 0) {
2696  vtop = xdata[j + nx*(npseudo-ivalue)];
2697  vbot = xdata[j + nx*(npseudo-ivalue-1)];
2698  wdata[j + nx*(yint+1)] = vtop*(1-fvalue)
2699  + vbot*fvalue;
2700  }
2701  }
2702  }
2703  }
2704  yprev = yint;
2705  }
2706  }
2707  cpl_polynomial_delete(polytop);
2708  cpl_polynomial_delete(polybot);
2709  cpl_image_delete(exslit);
2710  }
2711 
2712  cpl_image_delete(rectified);
2713 
2714  cpl_image_divide(flat, smo_flat);
2715 
2716  return smo_flat;
2717 }
2718 
2719 
2744 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius,
2745  int polyorder)
2746 {
2747  const char *func = "mos_normalise_longflat";
2748 
2749  cpl_image *smo_flat;
2750  cpl_image *profile;
2751  cpl_vector *flux;
2752  cpl_vector *smo_flux;
2753  cpl_vector *positions;
2754  cpl_polynomial *trend;
2755 
2756  float *level;
2757  float *p;
2758  float *data;
2759  double *fdata;
2760  double *pdata;
2761 
2762  int nx, ny;
2763  int npoints;
2764  int i, j;
2765 
2766 
2767  if (flat == NULL) {
2768  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
2769  return NULL;
2770  }
2771 
2772  if (sradius < 1 || dradius < 1) {
2773  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2774  return NULL;
2775  }
2776 
2777  smo_flat = cpl_image_duplicate(flat);
2778 
2779  if (polyorder < 0) {
2780 
2781  /*
2782  * First smooth along the spatial direction
2783  */
2784 
2785  cpl_image_turn(smo_flat, -1); /* For faster memory access */
2786 
2787  nx = cpl_image_get_size_x(smo_flat);
2788  ny = cpl_image_get_size_y(smo_flat);
2789  data = cpl_image_get_data(smo_flat);
2790 
2791  for (i = 0; i < ny; i++) {
2792  flux = cpl_vector_new(nx);
2793  fdata = cpl_vector_get_data(flux);
2794  p = data;
2795  for (j = 0; j < nx; j++)
2796  *fdata++ = *p++;
2797  smo_flux = cpl_vector_filter_median_create(flux, sradius);
2798  cpl_vector_delete(flux);
2799  fdata = cpl_vector_get_data(smo_flux);
2800  p = data;
2801  for (j = 0; j < nx; j++)
2802  *p++ = *fdata++;
2803  cpl_vector_delete(smo_flux);
2804  data += nx;
2805  }
2806 
2807  /*
2808  * Second smooth along the dispersion direction
2809  */
2810 
2811  cpl_image_turn(smo_flat, 1); /* For faster memory access */
2812 
2813  nx = cpl_image_get_size_x(smo_flat);
2814  ny = cpl_image_get_size_y(smo_flat);
2815  data = cpl_image_get_data(smo_flat);
2816 
2817  for (i = 0; i < ny; i++) {
2818  flux = cpl_vector_new(nx);
2819  fdata = cpl_vector_get_data(flux);
2820  p = data;
2821  for (j = 0; j < nx; j++)
2822  *fdata++ = *p++;
2823  smo_flux = cpl_vector_filter_median_create(flux, sradius);
2824  cpl_vector_delete(flux);
2825  fdata = cpl_vector_get_data(smo_flux);
2826  p = data;
2827  for (j = 0; j < nx; j++)
2828  *p++ = *fdata++;
2829  cpl_vector_delete(smo_flux);
2830  data += nx;
2831  }
2832  }
2833  else {
2834 
2835  /*
2836  * Fit with a polynomial the flat field trend column by column.
2837  */
2838 
2839  cpl_image_turn(smo_flat, -1); /* For faster memory access */
2840 
2841  nx = cpl_image_get_size_x(smo_flat);
2842  ny = cpl_image_get_size_y(smo_flat);
2843  data = cpl_image_get_data(smo_flat);
2844 
2845  profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0);
2846  level = cpl_image_get_data(profile);
2847 
2848  for (i = 0; i < ny; i++) {
2849 
2850  /*
2851  * First get the size of the vectors to allocate:
2852  * eliminate from fit any value more than 20% away
2853  * from median level in current column.
2854  */
2855 
2856  npoints = 0;
2857  p = data + i*nx;
2858  for (j = 0; j < nx; j++)
2859  if (fabs(p[j]/level[i] - 1) < 0.20)
2860  npoints++;
2861 
2862  if (npoints > polyorder + 1) {
2863 
2864  /*
2865  * Fill the vectors for the fitting
2866  */
2867 
2868  flux = cpl_vector_new(npoints);
2869  fdata = cpl_vector_get_data(flux);
2870  positions = cpl_vector_new(npoints);
2871  pdata = cpl_vector_get_data(positions);
2872 
2873  npoints = 0;
2874  p = data + i*nx;
2875  for (j = 0; j < nx; j++) {
2876  if (fabs(p[j]/level[i] - 1) < 0.20) {
2877  fdata[npoints] = p[j];
2878  pdata[npoints] = j;
2879  npoints++;
2880  }
2881  }
2882 
2883  trend = cpl_polynomial_fit_1d_create(positions, flux,
2884  polyorder, NULL);
2885 
2886  cpl_vector_delete(flux);
2887  cpl_vector_delete(positions);
2888 
2889  if (trend) {
2890  p = data + i*nx;
2891  for (j = 0; j < nx; j++)
2892  p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
2893  cpl_polynomial_delete(trend);
2894  }
2895  else {
2896  cpl_msg_warning(func,
2897  "Invalid flat field flux fit (ignored)");
2898  }
2899  }
2900  }
2901 
2902  cpl_image_delete(profile);
2903  cpl_image_turn(smo_flat, 1);
2904 
2905  }
2906 
2907  cpl_image_divide(flat, smo_flat);
2908 
2909  return smo_flat;
2910 }
2911 
2912 
2935 cpl_error_code mos_interpolate_wavecalib_slit(cpl_table *idscoeff,
2936  cpl_table *slits,
2937  int order, int global)
2938 {
2939  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
2940  /* Max order is 5 */
2941  int nrow = cpl_table_get_nrow(slits);
2942  int i, j;
2943 
2944 
2945  if (order < 0)
2946  return CPL_ERROR_NONE;
2947 
2948  cpl_table_new_column(idscoeff, "x", CPL_TYPE_DOUBLE);
2949  cpl_table_new_column(idscoeff, "y", CPL_TYPE_DOUBLE);
2950 
2951  for (i = 0; i < nrow; i++) {
2952  int position = cpl_table_get_int (slits, "position", i, NULL);
2953  int length = cpl_table_get_int (slits, "length", i, NULL);
2954  double xtop = cpl_table_get_double(slits, "xtop", i, NULL);
2955  double xbot = cpl_table_get_double(slits, "xbottom", i, NULL);
2956  double ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2957  double ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2958  double dx = xtop - xbot;
2959  double dy = ytop - ybot;
2960  cpl_table *table = cpl_table_extract(idscoeff, position, length);
2961 
2962  if (mos_interpolate_wavecalib(table, NULL, 2, order))
2963  continue;
2964 
2965  cpl_table_erase_window(idscoeff, position, length);
2966  cpl_table_insert(idscoeff, table, position);
2967 
2968  cpl_table_delete(table);
2969 
2970  for (j = 0; j < length; j++) {
2971  cpl_table_set_double(idscoeff, "x", j + position,
2972  xbot + j*(dx/length));
2973  cpl_table_set_double(idscoeff, "y", j + position,
2974  ybot + j*(dy/length));
2975  }
2976  }
2977 
2978  if (global) {
2979 
2980  /*
2981  * Now fit a global solution
2982  */
2983 
2984  nrow = cpl_table_get_nrow(idscoeff);
2985 
2986  for (i = 0; i < 6; i++) {
2987  cpl_table *dummy;
2988  cpl_vector *x;
2989  cpl_vector *y;
2990  cpl_bivector *z;
2991  cpl_vector *c;
2992  cpl_polynomial *p;
2993  cpl_vector *point;
2994  double *dpoint;
2995  int npoints;
2996 
2997  if (!cpl_table_has_column(idscoeff, clab[i]))
2998  break;
2999 
3000  npoints = nrow - cpl_table_count_invalid(idscoeff, clab[i]);
3001  if (npoints < 18)
3002  break;
3003 
3004  dummy = cpl_table_new(nrow);
3005  cpl_table_duplicate_column(dummy, "x", idscoeff, "x");
3006  cpl_table_duplicate_column(dummy, "y", idscoeff, "y");
3007  cpl_table_duplicate_column(dummy, clab[i], idscoeff, clab[i]);
3008  cpl_table_erase_invalid(dummy);
3009 
3010  x = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "x"));
3011  y = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "y"));
3012  z = cpl_bivector_wrap_vectors(x, y);
3013  c = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy,
3014  clab[i]));
3015  p = cpl_polynomial_fit_2d_create(z, c, 2, NULL);
3016  cpl_bivector_unwrap_vectors(z);
3017  cpl_vector_unwrap(x);
3018  cpl_vector_unwrap(y);
3019  cpl_vector_unwrap(c);
3020  cpl_table_delete(dummy);
3021 
3022  point = cpl_vector_new(2);
3023  dpoint = cpl_vector_get_data(point);
3024  for (j = 0; j < nrow; j++) {
3025  dpoint[0] = cpl_table_get_double(idscoeff, "x", j, NULL);
3026  dpoint[1] = cpl_table_get_double(idscoeff, "y", j, NULL);
3027  cpl_table_set_double(idscoeff, clab[i], j,
3028  cpl_polynomial_eval(p, point));
3029  }
3030  cpl_vector_delete(point);
3031  cpl_polynomial_delete(p);
3032  }
3033  }
3034 
3035  return CPL_ERROR_NONE;
3036 }
3037 
3038 
3064 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff,
3065  cpl_image *wavemap, int mode,
3066  int degree)
3067 {
3068  const char *func = "mos_interpolate_wavecalib";
3069 
3070  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
3071  /* Max order is 5 */
3072 
3073  cpl_vector *wave;
3074  cpl_vector *positions;
3075  cpl_polynomial *trend;
3076 
3077  float *p;
3078  float *data;
3079  double *wdata;
3080  double *pdata;
3081 
3082  double c;
3083  double mse, ksigma;
3084 
3085  int order;
3086  int nrows, first_row, last_row;
3087  int nx, ny;
3088  int npoints, rpoints;
3089  int null;
3090  int i, j, k;
3091 
3092  int polyorder = 4; /* Candidate input argument */
3093 
3094 
3095  if (idscoeff == NULL)
3096  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3097 
3098  if (mode < 0 || mode > 2)
3099  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
3100 
3101  if (mode == 0 || degree < 0)
3102  return CPL_ERROR_NONE;
3103 
3104  if (wavemap) {
3105 
3106  /*
3107  * Fit with a polynomial the wavelength trend column by column.
3108  */
3109 
3110  cpl_image_turn(wavemap, -1); /* For faster memory access */
3111 
3112  nx = cpl_image_get_size_x(wavemap);
3113  ny = cpl_image_get_size_y(wavemap);
3114  data = cpl_image_get_data(wavemap);
3115 
3116  for (i = 0; i < ny; i++) {
3117 
3118  /*
3119  * First get the size of the vectors to allocate:
3120  * eliminate from fit any value with "impossible" wavelength.
3121  */
3122 
3123  npoints = 0;
3124  p = data + i*nx;
3125  for (j = 0; j < nx; j++)
3126  if (p[j] > 1.0)
3127  npoints++;
3128 
3129  if (npoints > polyorder + 1) {
3130 
3131  /*
3132  * Fill the vectors for the fitting
3133  */
3134 
3135  wave = cpl_vector_new(npoints);
3136  wdata = cpl_vector_get_data(wave);
3137  positions = cpl_vector_new(npoints);
3138  pdata = cpl_vector_get_data(positions);
3139 
3140  npoints = 0;
3141  p = data + i*nx;
3142  for (j = 0; j < nx; j++) {
3143  if (p[j] > 1.0) {
3144  wdata[npoints] = p[j];
3145  pdata[npoints] = j;
3146  npoints++;
3147  }
3148  }
3149 
3150  trend = cpl_polynomial_fit_1d_create(positions, wave,
3151  polyorder, &mse);
3152 
3153  ksigma = 3*sqrt(mse);
3154 
3155  cpl_vector_delete(wave);
3156  cpl_vector_delete(positions);
3157 
3158  if (trend) {
3159 
3160  /*
3161  * Apply 3-sigma rejection
3162  */
3163 
3164  rpoints = 0;
3165  p = data + i*nx;
3166  for (j = 0; j < nx; j++)
3167  if (p[j] > 1.0)
3168  if (fabs(cpl_polynomial_eval_1d(trend, j, NULL)
3169  - p[j]) < ksigma)
3170  rpoints++;
3171 
3172  if (rpoints < npoints && rpoints > polyorder + 1) {
3173 
3174  wave = cpl_vector_new(rpoints);
3175  wdata = cpl_vector_get_data(wave);
3176  positions = cpl_vector_new(rpoints);
3177  pdata = cpl_vector_get_data(positions);
3178 
3179  npoints = 0;
3180  p = data + i*nx;
3181  for (j = 0; j < nx; j++) {
3182  if (p[j] > 1.0) {
3183  if (fabs(cpl_polynomial_eval_1d(trend,
3184  j, NULL) - p[j])
3185  < ksigma) {
3186  wdata[npoints] = p[j];
3187  pdata[npoints] = j;
3188  npoints++;
3189  }
3190  }
3191  }
3192 
3193  cpl_polynomial_delete(trend);
3194  trend = cpl_polynomial_fit_1d_create(positions, wave,
3195  polyorder, NULL);
3196 
3197  cpl_vector_delete(wave);
3198  cpl_vector_delete(positions);
3199  }
3200  }
3201 
3202  if (trend) {
3203  p = data + i*nx;
3204  if (mode == 1) {
3205  for (j = 0; j < nx; j++)
3206  if (p[j] < 1.0)
3207  p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
3208  }
3209  else if (mode == 2) {
3210  for (j = 0; j < nx; j++)
3211  p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
3212  }
3213  cpl_polynomial_delete(trend);
3214  }
3215  else {
3216  cpl_msg_warning(func,
3217  "Invalid wavelength field fit (ignored)");
3218  }
3219  }
3220 
3221  }
3222 
3223  cpl_image_turn(wavemap, 1);
3224 
3225  }
3226 
3227 
3228  /*
3229  * Interpolating the IDS coefficients
3230  */
3231 
3232  nrows = cpl_table_get_nrow(idscoeff);
3233 
3234  order = 0;
3235  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
3236  ++order;
3237  --order;
3238 
3239  if (degree == 0) {
3240  for (k = 0; k <= order; k++) {
3241  double m;
3242  if (cpl_table_has_column(idscoeff, clab[k])) {
3243  m = cpl_table_get_column_median(idscoeff, clab[k]);
3244  cpl_table_fill_column_window_double(idscoeff, clab[k],
3245  0, nrows, m);
3246  }
3247  }
3248 
3249  return CPL_ERROR_NONE;
3250  }
3251 
3252  first_row = 0;
3253  while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
3254  first_row++;
3255 
3256  last_row = nrows - 1;
3257  while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
3258  last_row--;
3259 
3260  for (k = 0; k <= order; k++) {
3261 
3262  npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]);
3263  wave = cpl_vector_new(npoints);
3264  wdata = cpl_vector_get_data(wave);
3265  positions = cpl_vector_new(npoints);
3266  pdata = cpl_vector_get_data(positions);
3267 
3268  npoints = 0;
3269  for (i = first_row; i <= last_row; i++) {
3270  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
3271  if (null == 0) {
3272  wdata[npoints] = c;
3273  pdata[npoints] = i;
3274  npoints++;
3275  }
3276  }
3277 
3278 // This doesn't seem to provide good results, I have not understood why.
3279 // Restore for robust linear fitting.
3280 //
3281  if (degree == 1) {
3282  cpl_vector *p;
3283  cpl_vector *w;
3284  cpl_bivector *list;
3285  double q, m;
3286 
3287  if (npoints > 4) {
3288  p = cpl_vector_extract(positions, 2, npoints - 2, 1);
3289  w = cpl_vector_extract(wave, 2, npoints - 2, 1);
3290  }
3291  else {
3292  p = positions;
3293  w = wave;
3294  }
3295 
3296  list = cpl_bivector_wrap_vectors(p, w);
3297 
3298  robustLinearFit(list, &q, &m, &mse);
3299  cpl_bivector_unwrap_vectors(list);
3300  for (i = first_row; i <= last_row; i++)
3301  cpl_table_set_double(idscoeff, clab[k], i, q + m*i);
3302 
3303  if (npoints > 4) {
3304  cpl_vector_delete(p);
3305  cpl_vector_delete(w);
3306  }
3307 
3308  cpl_vector_delete(wave);
3309  cpl_vector_delete(positions);
3310 
3311  continue;
3312  }
3313 
3314 // End robust linear fitting
3315 
3316  trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse);
3317 
3318  ksigma = 3*sqrt(mse);
3319 
3320  cpl_vector_delete(wave);
3321  cpl_vector_delete(positions);
3322 
3323  /*
3324  * Iteration
3325  */
3326 
3327  if (trend) {
3328  rpoints = 0;
3329  for (i = first_row; i <= last_row; i++) {
3330  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
3331  if (null == 0) {
3332  if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
3333  < ksigma) {
3334  rpoints++;
3335  }
3336  }
3337  }
3338 
3339  if (rpoints > 0 && rpoints < npoints) {
3340  cpl_msg_debug(func, "%d points rejected from "
3341  "wavelength calibration fit",
3342  npoints - rpoints);
3343 
3344  wave = cpl_vector_new(rpoints);
3345  wdata = cpl_vector_get_data(wave);
3346  positions = cpl_vector_new(rpoints);
3347  pdata = cpl_vector_get_data(positions);
3348 
3349  npoints = 0;
3350  for (i = first_row; i <= last_row; i++) {
3351  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
3352  if (null == 0) {
3353  if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
3354  < ksigma) {
3355  wdata[npoints] = c;
3356  pdata[npoints] = i;
3357  npoints++;
3358  }
3359  }
3360  }
3361 
3362  if (npoints) {
3363  cpl_polynomial_delete(trend);
3364  trend = cpl_polynomial_fit_1d_create(positions,
3365  wave, degree, NULL);
3366  }
3367 
3368  cpl_vector_delete(wave);
3369  cpl_vector_delete(positions);
3370 
3371  }
3372  }
3373 
3374  if (trend) {
3375  for (i = first_row; i <= last_row; i++) {
3376  if (mode == 1) {
3377  if (!cpl_table_is_valid(idscoeff, clab[k], i)) {
3378  cpl_table_set_double(idscoeff, clab[k], i,
3379  cpl_polynomial_eval_1d(trend, i,
3380  NULL));
3381  }
3382  }
3383  else if (mode == 2) {
3384  cpl_table_set_double(idscoeff, clab[k], i,
3385  cpl_polynomial_eval_1d(trend, i, NULL));
3386  }
3387  }
3388  cpl_polynomial_delete(trend);
3389  }
3390  else {
3391  cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
3392  }
3393 
3394  }
3395 
3396  return CPL_ERROR_NONE;
3397 }
3398 
3416 cpl_error_code mos_interpolate_wavecalib_mos(cpl_table *idscoeff,
3417  int mode,
3418  int degree)
3419 {
3420  const char *func = "mos_interpolate_wavecalib";
3421 
3422  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
3423  /* Max order is 5 */
3424 
3425  cpl_vector *wave;
3426  cpl_vector *positions;
3427  cpl_polynomial *trend;
3428 
3429  double *wdata;
3430  double *pdata;
3431 
3432  double c;
3433  double mse, ksigma;
3434 
3435  int order;
3436  int nrows, first_row, last_row;
3437  int npoints, rpoints;
3438  int null;
3439  int i;
3440 
3441 
3442  if (idscoeff == NULL)
3443  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3444 
3445  if (mode < 0 || mode > 2)
3446  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
3447 
3448  if (mode == 0 || degree < 0)
3449  return CPL_ERROR_NONE;
3450 
3451  /*
3452  * Interpolating the IDS coefficients
3453  */
3454 
3455  nrows = cpl_table_get_nrow(idscoeff);
3456 
3457  order = 0;
3458  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
3459  ++order;
3460  --order;
3461 
3462  //For coefficients > 0 always do a median
3463 
3464  int kinit = 1;
3465  if (degree == 0)
3466  kinit = 0;
3467  for (int k = kinit; k <= order; k++) {
3468  double m;
3469  if (cpl_table_has_column(idscoeff, clab[k])) {
3470  m = cpl_table_get_column_median(idscoeff, clab[k]);
3471  cpl_table_fill_column_window_double(idscoeff, clab[k],
3472  0, nrows, m);
3473  }
3474  }
3475 
3476  if(degree > 0)
3477  {
3478  first_row = 0;
3479  while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
3480  first_row++;
3481 
3482  last_row = nrows - 1;
3483  while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
3484  last_row--;
3485 
3486  int korder = 0;
3487 
3488  npoints = nrows - cpl_table_count_invalid(idscoeff, clab[korder]);
3489  wave = cpl_vector_new(npoints);
3490  wdata = cpl_vector_get_data(wave);
3491  positions = cpl_vector_new(npoints);
3492  pdata = cpl_vector_get_data(positions);
3493 
3494  npoints = 0;
3495  for (i = first_row; i <= last_row; i++) {
3496  c = cpl_table_get_double(idscoeff, clab[korder], i, &null);
3497  if (null == 0) {
3498  wdata[npoints] = c;
3499  pdata[npoints] = i;
3500  npoints++;
3501  }
3502  }
3503 
3504  // This doesn't seem to provide good results, I have not understood why.
3505  // Restore for robust linear fitting.
3506  //
3507  if (degree == 1) {
3508  cpl_vector *p;
3509  cpl_vector *w;
3510  cpl_bivector *list;
3511  double q, m;
3512 
3513  if (npoints > 4) {
3514  p = cpl_vector_extract(positions, 2, npoints - 2, 1);
3515  w = cpl_vector_extract(wave, 2, npoints - 2, 1);
3516  }
3517  else {
3518  p = positions;
3519  w = wave;
3520  }
3521 
3522  list = cpl_bivector_wrap_vectors(p, w);
3523 
3524  robustLinearFit(list, &q, &m, &mse);
3525  cpl_bivector_unwrap_vectors(list);
3526  for (i = first_row; i <= last_row; i++)
3527  cpl_table_set_double(idscoeff, clab[korder], i, q + m*i);
3528 
3529  if (npoints > 4) {
3530  cpl_vector_delete(p);
3531  cpl_vector_delete(w);
3532  }
3533 
3534  cpl_vector_delete(wave);
3535  cpl_vector_delete(positions);
3536 
3537  }
3538  else
3539  {
3540 
3541  // End robust linear fitting
3542 
3543  trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse);
3544 
3545  ksigma = 3*sqrt(mse);
3546 
3547  cpl_vector_delete(wave);
3548  cpl_vector_delete(positions);
3549 
3550  /*
3551  * Iteration
3552  */
3553 
3554  if (trend) {
3555  rpoints = 0;
3556  for (i = first_row; i <= last_row; i++) {
3557  c = cpl_table_get_double(idscoeff, clab[korder], i, &null);
3558  if (null == 0) {
3559  if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
3560  < ksigma) {
3561  rpoints++;
3562  }
3563  }
3564  }
3565 
3566  if (rpoints > 0 && rpoints < npoints) {
3567  cpl_msg_debug(func, "%d points rejected from "
3568  "wavelength calibration fit",
3569  npoints - rpoints);
3570 
3571  wave = cpl_vector_new(rpoints);
3572  wdata = cpl_vector_get_data(wave);
3573  positions = cpl_vector_new(rpoints);
3574  pdata = cpl_vector_get_data(positions);
3575 
3576  npoints = 0;
3577  for (i = first_row; i <= last_row; i++) {
3578  c = cpl_table_get_double(idscoeff, clab[korder], i, &null);
3579  if (null == 0) {
3580  if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
3581  < ksigma) {
3582  wdata[npoints] = c;
3583  pdata[npoints] = i;
3584  npoints++;
3585  }
3586  }
3587  }
3588 
3589  if (npoints) {
3590  cpl_polynomial_delete(trend);
3591  trend = cpl_polynomial_fit_1d_create(positions,
3592  wave, degree, NULL);
3593  }
3594 
3595  cpl_vector_delete(wave);
3596  cpl_vector_delete(positions);
3597 
3598  }
3599  }
3600 
3601  if (trend) {
3602  for (i = first_row; i <= last_row; i++) {
3603  if (mode == 1) {
3604  if (!cpl_table_is_valid(idscoeff, clab[korder], i)) {
3605  cpl_table_set_double(idscoeff, clab[korder], i,
3606  cpl_polynomial_eval_1d(trend, i,
3607  NULL));
3608  }
3609  }
3610  else if (mode == 2) {
3611  cpl_table_set_double(idscoeff, clab[korder], i,
3612  cpl_polynomial_eval_1d(trend, i, NULL));
3613  }
3614  }
3615  cpl_polynomial_delete(trend);
3616  }
3617  else {
3618  cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
3619  }
3620  }
3621  }
3622 
3623  return CPL_ERROR_NONE;
3624 }
3625 
3626 
3651 //TODO:Deprecate this function. Change all the recipes which still use it.
3652 //Use the new functionality to remove overscan, trimm and bias.
3653 
3654 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias,
3655  cpl_table *overscans)
3656 {
3657  const char *func = "mos_remove_bias";
3658 
3659  cpl_image *unbiased;
3660  cpl_image *overscan;
3661  double mean_bias_level;
3662  double mean_overscans_level;
3663  int count;
3664  int nrows;
3665  int xlow, ylow, xhig, yhig;
3666  int i;
3667 
3668 
3669  if (image == NULL || overscans == NULL) {
3670  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3671  return NULL;
3672  }
3673 
3674  nrows = cpl_table_get_nrow(overscans);
3675 
3676  if (nrows == 0) {
3677  cpl_msg_error(func, "Empty overscan table");
3678  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
3679  return NULL;
3680  }
3681 
3682  if (bias) {
3683  if (nrows == 1) {
3684  unbiased = cpl_image_subtract_create(image, bias);
3685  if (unbiased == NULL) {
3686  cpl_msg_error(func, "Incompatible master bias");
3687  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
3688  }
3689  return unbiased;
3690  }
3691  mean_bias_level = cpl_image_get_mean(bias);
3692  }
3693  else {
3694  if (nrows == 1) {
3695  cpl_msg_error(func, "No master bias in input, and no overscan "
3696  "regions in input image: bias subtraction "
3697  "cannot be performed!");
3698  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
3699  return NULL;
3700  }
3701  mean_bias_level = 0.0;
3702  }
3703 
3704  mean_overscans_level = 0.0;
3705  count = 0;
3706  for (i = 0; i < nrows; i++) {
3707  xlow = cpl_table_get_int(overscans, "xlow", i, NULL);
3708  ylow = cpl_table_get_int(overscans, "ylow", i, NULL);
3709  xhig = cpl_table_get_int(overscans, "xhig", i, NULL);
3710  yhig = cpl_table_get_int(overscans, "yhig", i, NULL);
3711 
3712  if (i == 0) {
3713  unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
3714  if (unbiased == NULL) {
3715  cpl_msg_error(func, "Incompatible overscan table");
3716  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
3717  return NULL;
3718  }
3719  if (bias) {
3720  if (cpl_image_subtract(unbiased, bias)) {
3721  cpl_msg_error(func, "Incompatible master bias");
3722  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
3723  cpl_image_delete(unbiased);
3724  return NULL;
3725  }
3726  }
3727  }
3728  else {
3729  overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
3730  if (overscan == NULL) {
3731  cpl_msg_error(func, "Incompatible overscan table");
3732  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
3733  cpl_image_delete(unbiased);
3734  return NULL;
3735  }
3736 
3737  mean_overscans_level += cpl_image_get_median(overscan);
3738  count++;
3739 
3740 /***
3741  * Here the mean level was used: not very robust...
3742 
3743  mean_overscans_level += cpl_image_get_flux(overscan);
3744  count += cpl_image_get_size_x(overscan)
3745  * cpl_image_get_size_y(overscan);
3746 ***/
3747  cpl_image_delete(overscan);
3748  }
3749  }
3750 
3751  /*
3752  * Overscan correction
3753  */
3754 
3755  mean_overscans_level /= count;
3756 
3757  cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level);
3758 
3759  cpl_msg_info(cpl_func,
3760  "Difference between mean overscans level "
3761  "and mean bias level: %.2f",
3762  mean_overscans_level - mean_bias_level);
3763 
3764  return unbiased;
3765 
3766 }
3767 
3768 
3827 cpl_error_code mos_arc_background_1D(float *spectrum, float *back,
3828  int length, int msize, int fsize)
3829 {
3830  const char *func = "mos_arc_background_1D";
3831 
3832  float *minf;
3833  float *maxf;
3834  float *smof;
3835  int i;
3836 
3837 
3838  if (spectrum == NULL || back == NULL)
3839  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3840 
3841  if (msize % 2 == 0)
3842  msize++;
3843 
3844  if (fsize % 2 == 0)
3845  fsize++;
3846 
3847  if (msize < 3 || fsize < msize || length < 2*fsize)
3848  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
3849 
3850 
3851  minf = min_filter(spectrum, length, msize);
3852  smof = smo_filter(minf, length, fsize);
3853  cpl_free(minf);
3854  maxf = max_filter(smof, length, 2*msize+1);
3855  cpl_free(smof);
3856  smof = smo_filter(maxf, length, 2*fsize+1);
3857  cpl_free(maxf);
3858  minf = min_filter(smof, length, 2*msize+1);
3859  cpl_free(smof);
3860  smof = smo_filter(minf, length, 2*fsize+1);
3861  cpl_free(minf);
3862 
3863  for (i = 0; i < length; i++)
3864  back[i] = smof[i];
3865 
3866  cpl_free(smof);
3867 
3868  return CPL_ERROR_NONE;
3869 
3870 }
3871 
3872 
3929 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize)
3930 {
3931  const char *func = "mos_arc_background";
3932 
3933  cpl_image *fimage;
3934  cpl_image *bimage;
3935  float *data;
3936  float *bdata;
3937  float *row;
3938  float *brow;
3939  int nx, ny;
3940  int i;
3941 
3942 
3943  if (image == NULL) {
3944  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3945  return NULL;
3946  }
3947 
3948  if (msize % 2 == 0)
3949  msize++;
3950 
3951  if (fsize % 2 == 0)
3952  fsize++;
3953 
3954  nx = cpl_image_get_size_x(image);
3955  ny = cpl_image_get_size_y(image);
3956 
3957  bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
3958 
3959  fimage = mos_image_filter_median(image, 3, 3);
3960 
3961  data = cpl_image_get_data_float(fimage);
3962  bdata = cpl_image_get_data_float(bimage);
3963 
3964  for (i = 0; i < ny; i++) {
3965  row = data + i * nx;
3966  brow = bdata + i * nx;
3967  if (mos_arc_background_1D(row, brow, nx, msize, fsize)) {
3968  cpl_error_set_where(func);
3969  cpl_image_delete(fimage);
3970  cpl_image_delete(bimage);
3971  return NULL;
3972  }
3973  }
3974 
3975  cpl_image_delete(fimage);
3976 
3977  return bimage;
3978 }
3979 
3980 
4001 int mos_lines_width(const float *spectrum, int length)
4002 {
4003 
4004  const char *func = "mos_lines_width";
4005 
4006  double *profile1 = cpl_calloc(length - 1, sizeof(double));
4007  double *profile2 = cpl_calloc(length - 1, sizeof(double));
4008 
4009  double norm, value, max;
4010  int radius = 20;
4011  int short_length = length - 2*radius - 1;
4012  int width;
4013  int i, j, k;
4014 
4015 
4016  /*
4017  * Derivative, and separation of positive and negative derivatives
4018  */
4019 
4020  for (j = 0, i = 1; i < length; j++, i++) {
4021  profile1[j] = profile2[j] = spectrum[i] - spectrum[j];
4022  if (profile1[j] < 0)
4023  profile1[j] = 0;
4024  if (profile2[j] > 0)
4025  profile2[j] = 0;
4026  else
4027  profile2[j] = -profile2[j];
4028  }
4029 
4030 
4031  /*
4032  * Profiles normalisation
4033  */
4034 
4035  length--;
4036 
4037  norm = 0;
4038  for (i = 0; i < length; i++)
4039  if (norm < profile1[i])
4040  norm = profile1[i];
4041 
4042  for (i = 0; i < length; i++) {
4043  profile1[i] /= norm;
4044  profile2[i] /= norm;
4045  }
4046 
4047 
4048  /*
4049  * Cross-correlation
4050  */
4051 
4052  max = -1;
4053  for (i = 0; i <= radius; i++) {
4054  value = 0;
4055  for (j = 0; j < short_length; j++) {
4056  k = radius+j;
4057  value += profile1[k] * profile2[k+i];
4058  }
4059  if (max < value) {
4060  max = value;
4061  width = i;
4062  }
4063  }
4064 
4065  cpl_free(profile1);
4066  cpl_free(profile2);
4067 
4068  if (max < 0.0) {
4069  cpl_msg_debug(func, "Cannot estimate line width");
4070  width = 1;
4071  }
4072 
4073  return width;
4074 
4075 }
4076 
4077 
4104 cpl_vector *mos_peak_candidates(const float *spectrum,
4105  int length, float level,
4106  float exp_width)
4107 {
4108 
4109  const char *func = "mos_peak_candidates";
4110 
4111  int i, j;
4112  int nint = length - 1;
4113  int n = 0;
4114  int width = 2 * ceil(exp_width / 2) + 1;
4115  int start = width / 2;
4116  int end = length - width / 2;
4117  int step;
4118  float *smo;
4119  double *data = cpl_calloc(length/2, sizeof(double));
4120 
4121 
4122  if (spectrum == NULL) {
4123  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
4124  return NULL;
4125  }
4126 
4127 
4128  /*
4129  * If lines have a flat top (as in the case of broad slit), smooth
4130  * before determining the max.
4131  */
4132 
4133  if (width > 7) {
4134  smo = cpl_calloc(length, sizeof(float));
4135  start = width / 2;
4136  end = length - width / 2;
4137  for (i = 0; i < start; i++)
4138  smo[i] = spectrum[i];
4139  for (i = start; i < end; i++) {
4140  for (j = i - start; j <= i + start; j++)
4141  smo[i] += spectrum[j];
4142  smo[i] /= width;
4143  }
4144  for (i = end; i < length; i++)
4145  smo[i] = spectrum[i];
4146  }
4147  else {
4148  smo = (float *)spectrum;
4149  }
4150 
4151  /*
4152  * Collect all relative maxima along spectrum, that are higher than the
4153  * specified level.
4154  */
4155 
4156  if (width > 20)
4157  step = width / 2;
4158  else
4159  step = 1;
4160 
4161  for (i = step; i < nint - step + 1; i += step) {
4162  if (smo[i] > level) {
4163  if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) {
4164  if (smo[i-step] != 0.0 && smo[i+step] != 0.0) {
4165  data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]);
4166  ++n;
4167  }
4168  }
4169  }
4170  }
4171 
4172  if (width > 7) {
4173  cpl_free(smo);
4174  }
4175 
4176  if (n == 0) {
4177  cpl_free(data);
4178  return NULL;
4179  }
4180 
4181  return cpl_vector_wrap(n, data);
4182 
4183 }
4184 
4185 
4207 cpl_vector *mos_refine_peaks(const float *spectrum, int length,
4208  cpl_vector *peaks, int sradius)
4209 {
4210 
4211  const char *func = "mos_refine_peaks";
4212 
4213  double *data;
4214  float pos;
4215  int npeaks;
4216  int startPos, endPos;
4217  int window = 2*sradius+1;
4218  int i, j;
4219 
4220 
4221  if (peaks == NULL || spectrum == NULL) {
4222  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
4223  return NULL;
4224  }
4225 
4226  npeaks = cpl_vector_get_size(peaks);
4227  data = cpl_vector_unwrap(peaks);
4228 
4229  for (i = 0; i < npeaks; i++) {
4230  startPos = data[i] - window/2;
4231  endPos = startPos + window;
4232  if (startPos < 0 || endPos >= length)
4233  continue;
4234 
4235  if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) {
4236  pos += startPos;
4237  data[i] = pos;
4238  }
4239  }
4240 
4241  for (i = 1; i < npeaks; i++)
4242  if (data[i] - data[i-1] < 0.5)
4243  data[i-1] = -1.0;
4244 
4245  for (i = 0, j = 0; i < npeaks; i++) {
4246  if (data[i] > 0.0) {
4247  if (i != j)
4248  data[j] = data[i];
4249  j++;
4250  }
4251  }
4252 
4253  return cpl_vector_wrap(j, data);
4254 
4255 }
4256 
4257 
4258 void mos_set_multiplex(int multiplex)
4259 {
4260  mos_multiplex = multiplex;
4261 }
4262 
4316 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines,
4317  double min_disp, double max_disp,
4318  double tolerance)
4319 {
4320 
4321  int i, j, k, l;
4322  int nlint, npint;
4323  int minpos;
4324  float min;
4325  double lratio, pratio;
4326  double lo_start, lo_end, hi_start, hi_end, denom;
4327  double disp, variation, prev_variation;
4328  int max, maxpos, minl, mink;
4329  int ambiguous;
4330  int npeaks_lo, npeaks_hi;
4331  int *peak_lo;
4332  int *peak_hi;
4333  int **ident;
4334  int *nident;
4335  int *lident;
4336 
4337  double *peak;
4338  double *line;
4339  int npeaks, nlines;
4340 
4341  double *xpos;
4342  double *lambda;
4343  int *ilambda;
4344  double *tmp_xpos;
4345  double *tmp_lambda;
4346  int *tmp_ilambda;
4347  int *flag;
4348  int n = 0;
4349  int nn;
4350  int nseq = 0;
4351  int gap;
4352  int *seq_length;
4353  int found;
4354 
4355  peak = cpl_vector_get_data(peaks);
4356  npeaks = cpl_vector_get_size(peaks);
4357  line = cpl_vector_get_data(lines);
4358  nlines = cpl_vector_get_size(lines);
4359 
4360  if (npeaks < 4)
4361  return NULL;
4362 
4363  peak_lo = cpl_malloc(npeaks * sizeof(int));
4364  peak_hi = cpl_malloc(npeaks * sizeof(int));
4365  nident = cpl_calloc(npeaks, sizeof(int));
4366  lident = cpl_calloc(nlines, sizeof(int));
4367  xpos = cpl_calloc(npeaks, sizeof(double));
4368  lambda = cpl_calloc(npeaks, sizeof(double));
4369  ilambda = cpl_calloc(npeaks, sizeof(int));
4370  tmp_xpos = cpl_calloc(npeaks, sizeof(double));
4371  tmp_lambda = cpl_calloc(npeaks, sizeof(double));
4372  tmp_ilambda = cpl_calloc(npeaks, sizeof(int));
4373  flag = cpl_calloc(npeaks, sizeof(int));
4374  seq_length = cpl_calloc(npeaks, sizeof(int));
4375  ident = cpl_malloc(npeaks * sizeof(int *));
4376  for (i = 0; i < npeaks; i++)
4377  ident[i] = cpl_malloc(3 * npeaks * sizeof(int));
4378 
4379  /*
4380  * This is just the number of intervals - one less than the number
4381  * of points (catalog wavelengths, or detected peaks).
4382  */
4383 
4384  nlint = nlines - 1;
4385  npint = npeaks - 1;
4386 
4387 
4388  /*
4389  * Here the big loops on catalog lines begins.
4390  */
4391 
4392  for (i = 1; i < nlint; i++) {
4393 
4394 
4395  /*
4396  * For each catalog wavelength I take the previous and the next one,
4397  * and compute the ratio of the corresponding wavelength intervals.
4398  * This ratio will be compared to all the ratios obtained doing the
4399  * same with all the detected peaks positions.
4400  */
4401 
4402  lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
4403 
4404 
4405  /*
4406  * Here the loop on detected peaks positions begins.
4407  */
4408 
4409  for (j = 1; j < npint; j++) {
4410 
4411  /*
4412  * Not all peaks are used for computing ratios: just the ones
4413  * that are compatible with the expected spectral dispersion
4414  * are taken into consideration. Therefore, I define the pixel
4415  * intervals before and after any peak that are compatible with
4416  * the specified dispersion interval, and select just the peaks
4417  * within such intervals. If either of the two intervals doesn't
4418  * contain any peak, then I skip the current peak and continue
4419  * with the next.
4420  */
4421 
4422  lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
4423  lo_end = peak[j] - (line[i] - line[i-1]) / max_disp;
4424  hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
4425  hi_end = peak[j] + (line[i+1] - line[i]) / min_disp;
4426 
4427  for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
4428  if (peak[k] > lo_end)
4429  break;
4430  if (peak[k] > lo_start) {
4431  peak_lo[npeaks_lo] = k;
4432  ++npeaks_lo;
4433  }
4434  }
4435 
4436  if (npeaks_lo == 0)
4437  continue;
4438 
4439  for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
4440  if (peak[k] > hi_end)
4441  break;
4442  if (peak[k] > hi_start) {
4443  peak_hi[npeaks_hi] = k;
4444  ++npeaks_hi;
4445  }
4446  }
4447 
4448  if (npeaks_hi == 0)
4449  continue;
4450 
4451 
4452  /*
4453  * Now I have all peaks that may help for a local identification.
4454  * peak_lo[k] is the sequence number of the k-th peak of the lower
4455  * interval; peak_hi[l] is the sequence number of the l-th peak of
4456  * the higher interval. j is, of course, the sequence number of the
4457  * current peak (second big loop).
4458  */
4459 
4460  prev_variation = 1000.0;
4461  minl = mink = 0;
4462 
4463  for (k = 0; k < npeaks_lo; k++) {
4464  denom = peak[j] - peak[peak_lo[k]];
4465  for (l = 0; l < npeaks_hi; l++) {
4466 
4467  /*
4468  * For any pair of peaks - one from the lower and the other
4469  * from the higher interval - I compute the same ratio that
4470  * was computed with the current line catalog wavelength.
4471  */
4472 
4473  pratio = (peak[peak_hi[l]] - peak[j]) / denom;
4474 
4475  /*
4476  * If the two ratios are compatible within the specified
4477  * tolerance, we have a preliminary identification. This
4478  * will be marked in the matrix ident[][], where the first
4479  * index corresponds to a peak sequence number, and the second
4480  * index is the counter of the identifications made during
4481  * this whole process. The array of counters is nident[].
4482  * If more than one interval pair fulfills the specified
4483  * tolerance, the closest to the expected ratio is selected.
4484  */
4485 
4486  variation = fabs(lratio-pratio) / pratio;
4487 
4488  if (variation < tolerance) {
4489  if (variation < prev_variation) {
4490  prev_variation = variation;
4491  minl = l;
4492  mink = k;
4493  }
4494  }
4495  }
4496  }
4497  if (prev_variation < tolerance) {
4498  ident[j][nident[j]] = i;
4499  ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
4500  ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
4501  ++nident[j];
4502  ++nident[peak_hi[minl]];
4503  ++nident[peak_lo[mink]];
4504  }
4505  } /* End loop on positions */
4506  } /* End loop on lines */
4507 
4508 
4509  /*
4510  * At this point I have filled the ident matrix with all my preliminary
4511  * identifications. Ambiguous identifications must be eliminated.
4512  */
4513 
4514 
4515  for (i = 0; i < npeaks; i++) {
4516 
4517 
4518  /*
4519  * I don't take into consideration peaks that were never identified.
4520  * They are likely contaminations, or emission lines that were not
4521  * listed in the input wavelength catalog.
4522  */
4523 
4524  if (nident[i] > 1) {
4525 
4526 
4527  /*
4528  * Initialise the histogram of wavelengths assigned to the i-th peak.
4529  */
4530 
4531  for (j = 0; j < nlines; j++)
4532  lident[j] = 0;
4533 
4534 
4535  /*
4536  * Count how many times each catalog wavelength was assigned
4537  * to the i-th peak.
4538  */
4539 
4540  for (j = 0; j < nident[i]; j++)
4541  ++lident[ident[i][j]];
4542 
4543 
4544  /*
4545  * What wavelength was most frequently assigned to the i-th peak?
4546  */
4547 
4548  max = 0;
4549  maxpos = 0;
4550  for (j = 0; j < nlines; j++) {
4551  if (max < lident[j]) {
4552  max = lident[j];
4553  maxpos = j;
4554  }
4555  }
4556 
4557 
4558  /*
4559  * Were there other wavelengths assigned with the same frequency?
4560  * This would be the case of an ambiguous identification. It is
4561  * safer to reject this peak...
4562  */
4563 
4564  ambiguous = 0;
4565 
4566  for (k = maxpos + 1; k < nlines; k++) {
4567  if (lident[k] == max) {
4568  ambiguous = 1;
4569  break;
4570  }
4571  }
4572 
4573  if (ambiguous)
4574  continue;
4575 
4576 
4577  /*
4578  * Otherwise, I assign to the i-th peak the wavelength that was
4579  * most often assigned to it.
4580  */
4581 
4582  tmp_xpos[n] = peak[i];
4583  tmp_lambda[n] = line[maxpos];
4584  tmp_ilambda[n] = maxpos;
4585 
4586  ++n;
4587 
4588  }
4589 
4590  }
4591 
4592 
4593  /*
4594  * Check on identified peaks. Contaminations from other spectra might
4595  * be present and should be excluded: this type of contamination
4596  * consists of peaks that have been _correctly_ identified! The non-
4597  * spectral type of light contamination should have been almost all
4598  * removed already in the previous steps, but it may still be present.
4599  * Here, the self-consistent sequences of identified peaks are
4600  * separated one from the other. At the moment, just the longest of
4601  * such sequences is selected (in other words, spectral multiplexing
4602  * is ignored).
4603  */
4604 
4605  if (n > 1) {
4606  nn = 0; /* Number of peaks in the list of sequences */
4607  nseq = 0; /* Current sequence */
4608  for (k = 0; k < n; k++) {
4609  if (flag[k] == 0) { /* Was peak k already assigned to a sequence? */
4610  flag[k] = 1;
4611  xpos[nn] = tmp_xpos[k]; /* Begin the nseq-th sequence */
4612  lambda[nn] = tmp_lambda[k];
4613  ilambda[nn] = tmp_ilambda[k];
4614  ++seq_length[nseq];
4615  ++nn;
4616 
4617  /*
4618  * Now look for all the following peaks that are compatible
4619  * with the expected spectral dispersion, and add them in
4620  * sequence to xpos. Note that missing peaks are not a problem...
4621  */
4622 
4623  i = k;
4624  while (i < n - 1) {
4625  found = 0;
4626  for (j = i + 1; j < n; j++) {
4627  if (flag[j] == 0) {
4628  disp = (tmp_lambda[j] - tmp_lambda[i])
4629  / (tmp_xpos[j] - tmp_xpos[i]);
4630  if (disp >= min_disp && disp <= max_disp) {
4631  flag[j] = 1;
4632  xpos[nn] = tmp_xpos[j];
4633  lambda[nn] = tmp_lambda[j];
4634  ilambda[nn] = tmp_ilambda[j];
4635  ++seq_length[nseq];
4636  ++nn;
4637  i = j;
4638  found = 1;
4639  break;
4640  }
4641  }
4642  }
4643  if (!found)
4644  break;
4645  }
4646 
4647  /*
4648  * Current sequence is completed: begin new sequence on the
4649  * excluded peaks, starting the loop on peaks again.
4650  */
4651 
4652  ++nseq;
4653  k = 0;
4654  }
4655  }
4656 
4657 
4658  /*
4659  * Find the longest sequence of self-consistent peaks.
4660  */
4661 
4662  maxpos = max = 0;
4663 
4664  if (mos_multiplex < 0) {
4665  for (i = 0; i < nseq; i++) {
4666  if (seq_length[i] > max) {
4667  max = seq_length[i];
4668  maxpos = i;
4669  }
4670  }
4671  }
4672  else {
4673 
4674  /*
4675  * Now consider the sequence which lays in the specified
4676  * CCD region (indicated by mos_multiplex): that is, _most_
4677  * of its lines (more than half) must be in that region...
4678  */
4679 
4680  nn = 0;
4681  found = 0;
4682 
4683  for (i = 0; i < nseq; i++) {
4684  n = seq_length[i];
4685  if (n > 5) {
4686  cpl_array *regions = cpl_array_new(n, CPL_TYPE_INT);
4687  int region;
4688 
4689  for (j = 0; j < n; j++)
4690  cpl_array_set_int(regions, j,
4691  ((int)floor(xpos[nn + j])) / mos_region_size);
4692 
4693  region = (int)cpl_array_get_median(regions);
4694  cpl_array_delete(regions);
4695 
4696  if (mos_multiplex == region) {
4697  if (found) {
4698  cpl_msg_debug(cpl_func, "More than one spectrum found in "
4699  "region %d (only the first one is extracted)",
4700  mos_multiplex);
4701  break;
4702  }
4703  found = 1;
4704  max = seq_length[i];
4705  maxpos = i;
4706  }
4707  }
4708  nn += seq_length[i];
4709  }
4710  }
4711 
4712  /*
4713  * Find where this sequence starts in the whole peak position
4714  * storage.
4715  */
4716 
4717  nn = 0;
4718  for (i = 0; i < maxpos; i++)
4719  nn += seq_length[i];
4720 
4721  /*
4722  * Move the longest sequence at the beginning of the returned lists
4723  */
4724 
4725  n = max;
4726  for (i = 0; i < n; i++, nn++) {
4727  xpos[i] = xpos[nn];
4728  lambda[i] = lambda[nn];
4729  ilambda[i] = ilambda[nn];
4730  }
4731 
4732 
4733  /*
4734  * Are some wavelengths missing? Recover them.
4735  */
4736 
4737  for (i = 1; i < n; i++) {
4738  gap = ilambda[i] - ilambda[i-1];
4739  for (j = 1; j < gap; j++) {
4740 
4741  if (j == 1) {
4742 
4743  /*
4744  * Determine the local dispersion from the current pair of peaks
4745  */
4746 
4747  disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
4748  }
4749 
4750  /*
4751  * With this, find the expected position of the missing
4752  * peak by linear interpolation.
4753  */
4754 
4755  hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
4756 
4757  /*
4758  * Is there a peak at that position? Here a peak from the
4759  * original list is searched, that is closer than 2 pixels
4760  * to the expected position. If it is found, insert it at
4761  * the current position on the list of identified peaks,
4762  * and leave immediately the loop (taking the new position
4763  * for the following linear interpolation, in case more
4764  * than one peak is missing in the current interval).
4765  * If it is not found, stay in the loop, looking for
4766  * the following missing peaks in this interval.
4767  */
4768 
4769  found = 0;
4770  for (k = 0; k < npeaks; k++) {
4771  if (fabs(peak[k] - hi_start) < 2) {
4772  for (l = n; l > i; l--) {
4773  xpos[l] = xpos[l-1];
4774  lambda[l] = lambda[l-1];
4775  ilambda[l] = ilambda[l-1];
4776  }
4777  xpos[i] = peak[k];
4778  lambda[i] = line[ilambda[i-1] + j];
4779  ilambda[i] = ilambda[i-1] + j;
4780  ++n;
4781  found = 1;
4782  break;
4783  }
4784  }
4785  if (found)
4786  break;
4787  }
4788  }
4789 
4790 
4791  /*
4792  * Try to extrapolate forward
4793  */
4794 
4795  found = 1;
4796  while (ilambda[n-1] < nlines - 1 && found) {
4797 
4798  /*
4799  * Determine the local dispersion from the last pair of
4800  * identified peaks
4801  */
4802 
4803  if (n > 1)
4804  disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
4805  else
4806  disp = 0.0;
4807 
4808  if (disp > max_disp || disp < min_disp)
4809  break;
4810 
4811 
4812  /*
4813  * With this, find the expected position of the missing
4814  * peak by linear interpolation.
4815  */
4816 
4817  hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
4818 
4819  /*
4820  * Is there a peak at that position? Here a peak from the
4821  * original list is searched, that is closer than 6 pixels
4822  * to the expected position. If it is found, insert it at
4823  * the end of the list of identified peaks. If it is not
4824  * found, leave the loop.
4825  */
4826 
4827  found = 0;
4828  min = fabs(peak[0] - hi_start);
4829  minpos = 0;
4830  for (k = 1; k < npeaks; k++) {
4831  if (min > fabs(peak[k] - hi_start)) {
4832  min = fabs(peak[k] - hi_start);
4833  minpos = k;
4834  }
4835  }
4836  if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
4837  xpos[n] = peak[minpos];
4838  lambda[n] = line[ilambda[n-1] + 1];
4839  ilambda[n] = ilambda[n-1] + 1;
4840  ++n;
4841  found = 1;
4842  }
4843  }
4844 
4845 
4846  /*
4847  * Try to extrapolate backward
4848  */
4849 
4850  found = 1;
4851  while (ilambda[0] > 0 && found) {
4852 
4853  /*
4854  * Determine the local dispersion from the first pair of
4855  * identified peaks
4856  */
4857 
4858  disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
4859 
4860  if (disp > max_disp || disp < min_disp)
4861  break;
4862 
4863 
4864  /*
4865  * With this, find the expected position of the missing
4866  * peak by linear interpolation.
4867  */
4868 
4869  hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
4870 
4871 
4872  /*
4873  * Is there a peak at that position? Here a peak from the
4874  * original list is searched, that is closer than 6 pixels
4875  * to the expected position. If it is found, insert it at
4876  * the beginning of the list of identified peaks. If it is not
4877  * found, leave the loop.
4878  */
4879 
4880  found = 0;
4881  min = fabs(peak[0] - hi_start);
4882  minpos = 0;
4883  for (k = 1; k < npeaks; k++) {
4884  if (min > fabs(peak[k] - hi_start)) {
4885  min = fabs(peak[k] - hi_start);
4886  minpos = k;
4887  }
4888  }
4889  if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
4890  for (j = n; j > 0; j--) {
4891  xpos[j] = xpos[j-1];
4892  lambda[j] = lambda[j-1];
4893  ilambda[j] = ilambda[j-1];
4894  }
4895  xpos[0] = peak[minpos];
4896  lambda[0] = line[ilambda[0] - 1];
4897  ilambda[0] = ilambda[0] - 1;
4898  ++n;
4899  found = 1;
4900  }
4901  }
4902  }
4903 
4904 
4905  /*
4906  * At this point all peaks are processed. Free memory, and return
4907  * the result.
4908  */
4909 
4910 /************************************************+
4911  for (i = 0; i < npeaks; i++) {
4912  printf("Peak %d:\n ", i);
4913  for (j = 0; j < nident[i]; j++)
4914  printf("%.2f, ", line[ident[i][j]]);
4915  printf("\n");
4916  }
4917 
4918  printf("\n");
4919 
4920  for (i = 0; i < n; i++)
4921  printf("%.2f, %.2f\n", xpos[i], lambda[i]);
4922 +************************************************/
4923  for (i = 0; i < npeaks; i++)
4924  cpl_free(ident[i]);
4925  cpl_free(ident);
4926  cpl_free(nident);
4927  cpl_free(lident);
4928  cpl_free(ilambda);
4929  cpl_free(tmp_xpos);
4930  cpl_free(tmp_lambda);
4931  cpl_free(tmp_ilambda);
4932  cpl_free(peak_lo);
4933  cpl_free(flag);
4934  cpl_free(seq_length);
4935  cpl_free(peak_hi);
4936 
4937  if (n == 0) {
4938  cpl_free(xpos);
4939  cpl_free(lambda);
4940  return NULL;
4941  }
4942 
4943  return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos),
4944  cpl_vector_wrap(n, lambda));
4945 }
4946 
4947 
4965 /*
4966 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
4967  double refwave, double pixel)
4968 {
4969  double yellow;
4970  double cpixel;
4971  double tolerance = 0.02;
4972  int max_iter = 20;
4973  int iter = 0;
4974 
4975 
4976  if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
4977  return 0.0;
4978 
4979  if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
4980  return 0.0;
4981 
4982  yellow = (blue + red) / 2;
4983 
4984  cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
4985 
4986  while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
4987 
4988  if (cpixel > pixel)
4989  red = yellow;
4990  else
4991  blue = yellow;
4992 
4993  yellow = (blue + red) / 2;
4994  cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
4995 
4996  iter++;
4997 
4998  }
4999 
5000  return yellow;
5001 
5002 }
5003 */
5004 
5005 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
5006  double refwave, double pixel)
5007 {
5008  double yellow;
5009  double coeff;
5010  cpl_size zero = 0;
5011 
5012  if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
5013  return 0.0;
5014 
5015  if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
5016  return 0.0;
5017 
5018  yellow = (blue + red) / 2 - refwave;
5019 
5020  coeff = cpl_polynomial_get_coeff(ids, &zero);
5021  cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
5022 
5023  cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
5024  if (cpl_error_get_code() != CPL_ERROR_NONE) {
5025  cpl_error_reset();
5026  return 0.0;
5027  }
5028 
5029  cpl_polynomial_set_coeff(ids, &zero, coeff);
5030 
5031  return yellow + refwave;
5032 
5033 }
5034 
5060 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order,
5061  double reject, int minlines,
5062  int *nlines, double *err,
5063  cpl_bivector **pixwav_used)
5064 {
5065  const char *func = "mos_poly_wav2pix";
5066 
5067  cpl_bivector *pixwav2;
5068  cpl_vector *wavel;
5069  cpl_vector *pixel;
5070  double *d_wavel;
5071  double *d_pixel;
5072  double pixpos;
5073  int fitlines;
5074  int rejection = 0;
5075  int i, j;
5076 
5077  cpl_polynomial *ids;
5078 
5079 
5080  *nlines = 0;
5081  *err = 0;
5082 
5083  if (pixwav == NULL) {
5084  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
5085  return NULL;
5086  }
5087 
5088  fitlines = cpl_bivector_get_size(pixwav);
5089 
5090  if (fitlines < minlines) {
5091  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5092  return NULL;
5093  }
5094 
5095 
5096  /*
5097  * If outliers rejection was requested, allocate a working
5098  * vector (that can be modified as soon as outliers are removed)
5099  */
5100 
5101  if (reject > 0.0)
5102  rejection = 1;
5103 
5104  if (rejection)
5105  pixwav2 = cpl_bivector_duplicate(pixwav);
5106  else
5107  pixwav2 = pixwav;
5108 
5109 
5110  /*
5111  * The single vectors are extracted just because the fitting routine
5112  * requires it
5113  */
5114 
5115  pixel = cpl_bivector_get_x(pixwav2);
5116  wavel = cpl_bivector_get_y(pixwav2);
5117 
5118 
5119  /*
5120  * Get rid of the wrapper, in case of duplication
5121  */
5122 
5123  if (rejection)
5124  cpl_bivector_unwrap_vectors(pixwav2);
5125 
5126 
5127  /*
5128  * Here begins the iterative fit of identified lines
5129  */
5130 
5131  while (fitlines >= minlines) {
5132 
5133  ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
5134  *err = sqrt(*err);
5135 
5136  if (ids == NULL) {
5137  cpl_msg_debug(cpl_error_get_where(), "%s", cpl_error_get_message());
5138  cpl_msg_debug(func, "Fitting IDS");
5139  cpl_error_set_where(func);
5140  if (rejection) {
5141  cpl_vector_delete(wavel);
5142  cpl_vector_delete(pixel);
5143  }
5144  return NULL;
5145  }
5146 
5147  if (rejection) {
5148  cpl_vector * wavel_used = cpl_vector_duplicate(wavel);
5149  cpl_vector * pixel_used = cpl_vector_duplicate(pixel);
5150 
5151 
5152  /*
5153  * Now work directly with the vector data buffers...
5154  */
5155 
5156  d_pixel = cpl_vector_unwrap(pixel);
5157  d_wavel = cpl_vector_unwrap(wavel);
5158 
5159  for (i = 0, j = 0; i < fitlines; i++) {
5160  pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
5161  if (fabs(pixpos - d_pixel[i]) < reject) {
5162  d_pixel[j] = d_pixel[i];
5163  d_wavel[j] = d_wavel[i];
5164  j++;
5165  }
5166  }
5167 
5168  if (j == fitlines) { /* No rejection in last iteration */
5169  cpl_bivector * pixwav_used_temp =
5170  cpl_bivector_wrap_vectors(pixel_used, wavel_used);
5171  *pixwav_used = cpl_bivector_duplicate(pixwav_used_temp);
5172  cpl_bivector_unwrap_vectors(pixwav_used_temp);
5173  cpl_vector_delete(wavel_used);
5174  cpl_vector_delete(pixel_used);
5175  cpl_free(d_wavel);
5176  cpl_free(d_pixel);
5177  *nlines = fitlines;
5178  return ids;
5179  }
5180  else { /* Some lines were rejected */
5181  fitlines = j;
5182  cpl_polynomial_delete(ids);
5183  if (fitlines >= minlines) {
5184  pixel = cpl_vector_wrap(fitlines, d_pixel);
5185  wavel = cpl_vector_wrap(fitlines, d_wavel);
5186  }
5187  else { /* Too few lines: failure */
5188  cpl_free(d_wavel);
5189  cpl_free(d_pixel);
5190  cpl_error_set(func, CPL_ERROR_CONTINUE);
5191  return NULL;
5192  }
5193  }
5194  cpl_vector_delete(wavel_used);
5195  cpl_vector_delete(pixel_used);
5196  }
5197  else {
5198  *nlines = fitlines;
5199  *pixwav_used = cpl_bivector_duplicate(pixwav2);
5200  return ids; /* Exit at first iteration if no rejection */
5201  }
5202  }
5203 
5204  return ids; /* To avoid compiler warnings */
5205 }
5206 
5207 
5232 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
5233  double reject, int minlines,
5234  int *nlines, double *err)
5235 {
5236 
5237  cpl_bivector *wavpix;
5238  cpl_vector *wavel;
5239  cpl_vector *pixel;
5240 
5241  cpl_polynomial *dds;
5242 
5243  cpl_bivector *wavepix_used;
5244 
5245 
5246  /*
5247  * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
5248  */
5249 
5250  pixel = cpl_bivector_get_x(pixwav);
5251  wavel = cpl_bivector_get_y(pixwav);
5252 
5253  wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
5254 
5255  dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err,
5256  wavepix_used);
5257 
5258  cpl_bivector_unwrap_vectors(wavpix);
5259 
5260  cpl_bivector_delete(wavepix_used);
5261 
5262  return dds;
5263 
5264 }
5265 
5266 
5289 cpl_bivector *mos_find_peaks(const float *spectrum, int length,
5290  cpl_vector *lines, cpl_polynomial *ids,
5291  double refwave, int sradius)
5292 {
5293  const char *func = "mos_find_peaks";
5294 
5295  double *data;
5296  double *d_pixel;
5297  double *d_wavel;
5298  float pos;
5299  int nlines;
5300  int pixel;
5301  int i, j;
5302 
5303 
5304  if (spectrum == NULL || lines == NULL || ids == NULL) {
5305  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
5306  return NULL;
5307  }
5308 
5309  nlines = cpl_vector_get_size(lines);
5310 
5311  if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
5312  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5313  return NULL;
5314  }
5315 
5316  d_wavel = cpl_malloc(nlines * sizeof(double));
5317  d_pixel = cpl_malloc(nlines * sizeof(double));
5318 
5319  data = cpl_vector_get_data(lines);
5320 
5321  for (i = 0, j = 0; i < nlines; i++) {
5322  pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
5323  if (pixel < 0 || pixel - sradius < 0 || pixel + sradius >= length)
5324  continue;
5325  if (peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1) == 0) {
5326  pos += pixel - sradius;
5327  d_pixel[j] = pos;
5328  d_wavel[j] = data[i];
5329  j++;
5330  }
5331  }
5332 
5333  if (j > 0) {
5334  return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
5335  cpl_vector_wrap(j, d_wavel));
5336  }
5337  else {
5338  cpl_free(d_wavel);
5339  cpl_free(d_pixel);
5340  cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
5341  return NULL;
5342  }
5343 }
5344 
5345 
5469 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image,
5470  cpl_vector *lines,
5471  double dispersion, float level,
5472  int sradius, int order,
5473  double reject, double refwave,
5474  double *wavestart, double *waveend,
5475  int *nlines, double *error,
5476  cpl_table *idscoeff,
5477  cpl_image *calibration,
5478  cpl_image *residuals,
5479  cpl_table *restable,
5480  cpl_mask *refmask,
5481  cpl_table *detected_lines)
5482 {
5483 
5484  const char *func = "mos_wavelength_calibration_raw";
5485 
5486  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
5487  /* Max order is 5 */
5488 
5489  double tolerance = 20.0; /* Probably forever... */
5490  int step = 10; /* Compute restable every "step" rows */
5491 
5492  char name[MAX_COLNAME];
5493  cpl_image *resampled;
5494  cpl_bivector *output;
5495  cpl_bivector *new_output;
5496  cpl_vector *peaks;
5497  cpl_vector *wavel;
5498  cpl_polynomial *ids;
5499  cpl_polynomial *lin;
5500  cpl_matrix *kernel;
5501  double ids_err;
5502  double max_disp, min_disp;
5503  double *line;
5504  double firstLambda, lastLambda, lambda;
5505  double value, wave, pixe;
5506  cpl_binary *mdata;
5507  const float *sdata;
5508  float *rdata;
5509  float *idata;
5510  float *ddata;
5511  float v1, v2, vi;
5512  float fpixel;
5513  int *have_it;
5514  int pixstart, pixend;
5515  int extrapolation;
5516  int nref;
5517  int nl, nx, ny, pixel;
5518  int countLines, usedLines;
5519  int uorder;
5520  int in, first, last;
5521  int width, uradius;
5522  int i, j;
5523  int null;
5524  cpl_size k;
5525 
5526 
5527  if (dispersion == 0.0) {
5528  cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
5529  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5530  return NULL;
5531  }
5532 
5533  if (dispersion < 0.0) {
5534  cpl_msg_error(func, "The expected dispersion must be positive");
5535  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5536  return NULL;
5537  }
5538 
5539  max_disp = dispersion + dispersion * tolerance / 100;
5540  min_disp = dispersion - dispersion * tolerance / 100;
5541 
5542  if (order < 1) {
5543  cpl_msg_error(func, "The order of the fitting polynomial "
5544  "must be at least 1");
5545  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5546  return NULL;
5547  }
5548 
5549  if (image == NULL || lines == NULL) {
5550  cpl_msg_error(func, "Both spectral exposure and reference line "
5551  "catalog are required in input");
5552  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
5553  return NULL;
5554  }
5555 
5556  nx = cpl_image_get_size_x(image);
5557  ny = cpl_image_get_size_y(image);
5558  sdata = cpl_image_get_data_float_const(image);
5559 
5560  nref = cpl_vector_get_size(lines);
5561  line = cpl_vector_get_data(lines);
5562 
5563  if (*wavestart < 1.0 && *waveend < 1.0) {
5564  firstLambda = line[0];
5565  lastLambda = line[nref-1];
5566  extrapolation = (lastLambda - firstLambda) / 10;
5567  firstLambda -= extrapolation;
5568  lastLambda += extrapolation;
5569  *wavestart = firstLambda;
5570  *waveend = lastLambda;
5571  }
5572  else {
5573  firstLambda = *wavestart;
5574  lastLambda = *waveend;
5575  }
5576 
5577  nl = (lastLambda - firstLambda) / dispersion;
5578  resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
5579  rdata = cpl_image_get_data_float(resampled);
5580 
5581  if (calibration)
5582  idata = cpl_image_get_data_float(calibration);
5583 
5584  if (residuals)
5585  ddata = cpl_image_get_data_float(residuals);
5586 
5587  if (idscoeff)
5588  for (j = 0; j <= order; j++)
5589  cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
5590 
5591  if (restable) {
5592  cpl_table_set_size(restable, nref);
5593  cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
5594  cpl_table_copy_data_double(restable, "wavelength", line);
5595  for (i = 0; i < ny; i += step) {
5596  snprintf(name, MAX_COLNAME, "r%d", i);
5597  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
5598  snprintf(name, MAX_COLNAME, "d%d", i);
5599  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
5600  snprintf(name, MAX_COLNAME, "p%d", i);
5601  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
5602  }
5603  }
5604 
5605  if (detected_lines) {
5606  cpl_table_set_size(detected_lines, 0);
5607  cpl_table_new_column(detected_lines, "xpos", CPL_TYPE_DOUBLE);
5608  cpl_table_new_column(detected_lines, "ypos", CPL_TYPE_DOUBLE);
5609  cpl_table_new_column(detected_lines, "xpos_iter", CPL_TYPE_DOUBLE);
5610  cpl_table_new_column(detected_lines, "ypos_iter", CPL_TYPE_DOUBLE);
5611  cpl_table_new_column(detected_lines, "peak_flux", CPL_TYPE_DOUBLE);
5612  cpl_table_new_column(detected_lines, "wave_ident", CPL_TYPE_DOUBLE);
5613  cpl_table_new_column(detected_lines, "wave_ident_iter", CPL_TYPE_DOUBLE);
5614  cpl_table_new_column(detected_lines, "xpos_fit_rect_wavecal", CPL_TYPE_DOUBLE);
5615  cpl_table_new_column(detected_lines, "res_xpos", CPL_TYPE_DOUBLE);
5616  }
5617 
5618  /*
5619  * Here is the real thing: detecting and identifying peaks,
5620  * and then fit the transformation from wavelength to pixel
5621  * and from pixel to wavelength.
5622  */
5623 
5624  for (i = 0; i < ny; i++) {
5625  width = mos_lines_width(sdata + i*nx, nx);
5626  if (sradius > 0) {
5627  if (width > sradius) {
5628  uradius = width;
5629  }
5630  else {
5631  uradius = sradius;
5632  }
5633  }
5634  if (width < 5)
5635  width = 5;
5636  peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
5637  if (peaks) {
5638  peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
5639  }
5640  if (peaks) {
5641  output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05);
5642  if (output) {
5643  cpl_bivector * peaks_ident_used_fit;
5644  countLines = cpl_bivector_get_size(output);
5645  if (countLines < 4) {
5646  cpl_bivector_delete(output);
5647  cpl_vector_delete(peaks);
5648  if (nlines)
5649  nlines[i] = 0;
5650  if (error)
5651  error[i] = 0.0;
5652  continue;
5653  }
5654 
5655  /*
5656  * Set reference wavelength as zero point
5657  */
5658 
5659  wavel = cpl_bivector_get_y(output);
5660  cpl_vector_subtract_scalar(wavel, refwave);
5661 
5662  uorder = countLines / 2 - 1;
5663  if (uorder > order)
5664  uorder = order;
5665 
5666 /* This part is now commented out. In case the first-guess iteration
5667  * was requested, the first fit was made with a lower polynomial degree:
5668  * more robust, and still accurate enough to be used as a first-guess.
5669 
5670  if (sradius > 0 && uorder > 2)
5671  --uorder;
5672 
5673  * End of commented part */
5674 
5675  ids = mos_poly_wav2pix(output, uorder, reject,
5676  2 * (uorder + 1), &usedLines,
5677  &ids_err, &peaks_ident_used_fit);
5678 
5679  if (ids == NULL) {
5680  cpl_bivector_delete(output);
5681  cpl_vector_delete(peaks);
5682  if (nlines)
5683  nlines[i] = 0;
5684  if (error)
5685  error[i] = 0.0;
5686  cpl_error_reset();
5687  continue;
5688  }
5689 
5690  if (idscoeff) {
5691 
5692  /*
5693  * Write it anyway, even in case a first-guess based
5694  * solution will be searched afterwards: in case of
5695  * failure, the "blind" solution is kept.
5696  */
5697 
5698  for (k = 0; k <= order; k++) {
5699  if (k > uorder) {
5700  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
5701  }
5702  else {
5703  cpl_table_set_double(idscoeff, clab[k], i,
5704  cpl_polynomial_get_coeff(ids, &k));
5705  }
5706  }
5707  }
5708 
5709  if(detected_lines)
5710  {
5711  cpl_size newlines = cpl_vector_get_size(peaks);
5712  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
5713  cpl_table_set_size(detected_lines, oldsize + newlines);
5714  for(cpl_size iline = 0; iline < newlines; ++iline)
5715  {
5716  cpl_table_set_double(detected_lines, "xpos",
5717  oldsize + iline, cpl_vector_get(peaks, iline) + 1);
5718  cpl_table_set_double(detected_lines, "ypos",
5719  oldsize + iline, (double)i + 1);
5720  cpl_table_set_double(detected_lines, "peak_flux",
5721  oldsize + iline,
5722  sdata[i*nx+(int)(cpl_vector_get(peaks, iline)+0.5)]);
5723  }
5724  }
5725 
5726  //Fill the line identification information in
5727  //the detected_lines table
5728  if(detected_lines)
5729  {
5730  cpl_size nidentlines = cpl_bivector_get_size(output);
5731  cpl_size ndetectlines = cpl_vector_get_size(peaks);
5732  cpl_size totalsize = cpl_table_get_nrow(detected_lines);
5733  for(cpl_size idline = 0; idline < nidentlines; ++idline)
5734  {
5735  for(cpl_size detline = 0; detline < ndetectlines; ++detline)
5736  {
5737  if(cpl_vector_get(peaks, detline) ==
5738  cpl_bivector_get_x_data(output)[idline])
5739  {
5740  cpl_size table_pos = totalsize - ndetectlines + detline;
5741  double wave_ident = cpl_bivector_get_y_data(output)[idline] + refwave;
5742  double xpix_fit = cpl_polynomial_eval_1d(ids,
5743  wave_ident - refwave, NULL);
5744  double xpos_det = cpl_table_get_double(detected_lines,
5745  "xpos",
5746  table_pos, &null);
5747  cpl_table_set_double(detected_lines,
5748  "wave_ident",
5749  table_pos,
5750  wave_ident);
5751  cpl_table_set_double(detected_lines,
5752  "xpos_fit_rect_wavecal",
5753  table_pos,
5754  xpix_fit + 1);
5755  cpl_table_set_double(detected_lines,
5756  "res_xpos",
5757  table_pos,
5758  xpos_det - xpix_fit - 1);
5759 
5760  }
5761  }
5762  }
5763  }
5764 
5765  if (sradius > 0) {
5766  cpl_bivector * peaks_ident_used_fit;
5767 
5768  /*
5769  * Use ids as a first-guess
5770  */
5771 
5772  new_output = mos_find_peaks(sdata + i*nx, nx, lines,
5773  ids, refwave, uradius);
5774 
5775  if (new_output) {
5776  cpl_bivector_delete(output);
5777  output = new_output;
5778  }
5779  else
5780  cpl_error_reset();
5781 
5782 
5783  cpl_polynomial_delete(ids);
5784 
5785  countLines = cpl_bivector_get_size(output);
5786 
5787  if (countLines < 4) {
5788  cpl_bivector_delete(output);
5789  cpl_vector_delete(peaks);
5790 
5791  /*
5792  * With the following code a decision is taken:
5793  * if using the first-guess gives no results,
5794  * then also the "blind" solution is rejected.
5795  */
5796 
5797  if (nlines)
5798  nlines[i] = 0;
5799  if (error)
5800  error[i] = 0.0;
5801  if (idscoeff)
5802  for (k = 0; k <= order; k++)
5803  cpl_table_set_invalid(idscoeff, clab[k], i);
5804  continue;
5805  }
5806 
5807  wavel = cpl_bivector_get_y(output);
5808  cpl_vector_subtract_scalar(wavel, refwave);
5809 
5810  uorder = countLines / 2 - 1;
5811  if (uorder > order)
5812  uorder = order;
5813 
5814  ids = mos_poly_wav2pix(output, uorder, reject,
5815  2 * (uorder + 1), &usedLines,
5816  &ids_err, &peaks_ident_used_fit);
5817 
5818  if (ids == NULL) {
5819  cpl_bivector_delete(output);
5820  cpl_vector_delete(peaks);
5821 
5822  /*
5823  * With the following code a decision is taken:
5824  * if using the first-guess gives no results,
5825  * then also the "blind" solution is rejected.
5826  */
5827 
5828  if (nlines)
5829  nlines[i] = 0;
5830  if (error)
5831  error[i] = 0.0;
5832  if (idscoeff)
5833  for (k = 0; k <= order; k++)
5834  cpl_table_set_invalid(idscoeff, clab[k], i);
5835  cpl_error_reset();
5836  continue;
5837  }
5838 
5839  if (idscoeff) {
5840  for (k = 0; k <= order; k++) {
5841  if (k > uorder) {
5842  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
5843  }
5844  else {
5845  cpl_table_set_double(idscoeff, clab[k], i,
5846  cpl_polynomial_get_coeff(ids, &k));
5847  }
5848  }
5849  }
5850 
5851 
5852  if(detected_lines)
5853  {
5854  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
5855  cpl_size nidentlines = cpl_bivector_get_size(output);
5856  cpl_table_set_size(detected_lines, oldsize + nidentlines);
5857  for(cpl_size idline = 0; idline < nidentlines ; ++idline)
5858  {
5859  double wave_ident = cpl_bivector_get_y_data(output)[idline] + refwave;
5860  double xpix_fit = cpl_polynomial_eval_1d(ids,
5861  wave_ident - refwave, NULL);
5862  cpl_table_set_double(detected_lines, "xpos_iter",
5863  oldsize + idline, cpl_bivector_get_x_data(output)[idline] + 1);
5864  cpl_table_set_double(detected_lines, "ypos_iter",
5865  oldsize + idline, (double)i + 1);
5866  cpl_table_set_double(detected_lines, "peak_flux",
5867  oldsize + idline,
5868  sdata[i*nx+(int)(cpl_bivector_get_x_data(output)[idline]+0.5)]);
5869  cpl_table_set_double(detected_lines, "wave_ident_iter",
5870  oldsize + idline, wave_ident);
5871  cpl_table_set_double(detected_lines, "xpos_fit_rect_wavecal",
5872  oldsize + idline, xpix_fit + 1);
5873  }
5874  }
5875 
5876  } /* End of "use ids as a first-guess" */
5877 
5878  if (nlines)
5879  nlines[i] = usedLines;
5880  if (error)
5881  error[i] = ids_err / sqrt(usedLines/(uorder + 1));
5882 
5883  pixstart = cpl_polynomial_eval_1d(ids,
5884  cpl_bivector_get_y_data(output)[0], NULL);
5885  pixend = cpl_polynomial_eval_1d(ids,
5886  cpl_bivector_get_y_data(output)[countLines-1], NULL);
5887  extrapolation = (pixend - pixstart) / 5;
5888  pixstart -= extrapolation;
5889  pixend += extrapolation;
5890  if (pixstart < 0)
5891  pixstart = 0;
5892  if (pixend > nx)
5893  pixend = nx;
5894 
5895  /*
5896  * Wavelength calibrated image (if requested):
5897  */
5898 
5899  if (calibration) {
5900  for (j = pixstart; j < pixend; j++) {
5901  (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda,
5902  lastLambda, refwave,
5903  j);
5904  }
5905  }
5906 
5907  /*
5908  * Resampled image:
5909  */
5910 
5911  for (j = 0; j < nl; j++) {
5912  lambda = firstLambda + j * dispersion;
5913  fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave,
5914  NULL);
5915  pixel = fpixel;
5916  if (pixel >= 0 && pixel < nx-1) {
5917  v1 = (sdata + i*nx)[pixel];
5918  v2 = (sdata + i*nx)[pixel+1];
5919  vi = v1 + (v2-v1)*(fpixel-pixel);
5920  (rdata + i*nl)[j] = vi;
5921  }
5922  }
5923 
5924  /*
5925  * Residuals image
5926  */
5927 
5928  if (residuals || (restable && !(i%step))) {
5929  if (restable && !(i%step)) {
5930  lin = cpl_polynomial_new(1);
5931  for (k = 0; k < 2; k++)
5932  cpl_polynomial_set_coeff(lin, &k,
5933  cpl_polynomial_get_coeff(ids, &k));
5934  }
5935  for (j = 0; j < countLines; j++) {
5936  pixe = cpl_bivector_get_x_data(output)[j];
5937  wave = cpl_bivector_get_y_data(output)[j];
5938  value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
5939  if (residuals) {
5940  pixel = pixe + 0.5;
5941  (ddata + i*nx)[pixel] = value;
5942  }
5943  if (restable && !(i%step)) {
5944  for (k = 0; k < nref; k++) {
5945  if (fabs(line[k] - refwave - wave) < 0.1) {
5946  snprintf(name, MAX_COLNAME, "r%d", i);
5947  cpl_table_set_double(restable, name,
5948  k, value);
5949  value = pixe
5950  - cpl_polynomial_eval_1d(lin, wave,
5951  NULL);
5952  snprintf(name, MAX_COLNAME, "d%d", i);
5953  cpl_table_set_double(restable, name,
5954  k, value);
5955  snprintf(name, MAX_COLNAME, "p%d", i);
5956  cpl_table_set_double(restable, name,
5957  k, pixe);
5958  break;
5959  }
5960  }
5961  }
5962  }
5963  if (restable && !(i%step)) {
5964  cpl_polynomial_delete(lin);
5965  }
5966  }
5967 
5968  /*
5969  * Mask at reference wavelength
5970  */
5971 
5972  if (refmask) {
5973  mdata = cpl_mask_get_data(refmask);
5974  pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
5975  if (pixel - 1 >= 0 && pixel + 1 < nx) {
5976  mdata[pixel-1 + i*nx] = CPL_BINARY_1;
5977  mdata[pixel + i*nx] = CPL_BINARY_1;
5978  mdata[pixel+1 + i*nx] = CPL_BINARY_1;
5979  }
5980  }
5981 
5982  cpl_polynomial_delete(ids);
5983  cpl_bivector_delete(output);
5984  }
5985  cpl_vector_delete(peaks);
5986  }
5987  }
5988 
5989  if (refmask) {
5990  kernel = cpl_matrix_new(3, 3);
5991  cpl_matrix_set(kernel, 0, 1, 1.0);
5992  cpl_matrix_set(kernel, 1, 1, 1.0);
5993  cpl_matrix_set(kernel, 2, 1, 1.0);
5994 
5995  cpl_mask_dilation(refmask, kernel);
5996  cpl_mask_erosion(refmask, kernel);
5997  cpl_mask_erosion(refmask, kernel);
5998  cpl_mask_dilation(refmask, kernel);
5999 
6000  cpl_matrix_delete(kernel);
6001 
6002  /*
6003  * Fill possible gaps
6004  */
6005 
6006  mdata = cpl_mask_get_data(refmask);
6007  have_it = cpl_calloc(ny, sizeof(int));
6008 
6009  for (i = 0; i < ny; i++, mdata += nx) {
6010  for (j = 0; j < nx; j++) {
6011  if (mdata[j] == CPL_BINARY_1) {
6012  have_it[i] = j;
6013  break;
6014  }
6015  }
6016  }
6017 
6018  mdata = cpl_mask_get_data(refmask);
6019  in = 0;
6020  first = last = 0;
6021 
6022  for (i = 0; i < ny; i++) {
6023  if (have_it[i]) {
6024  if (!in) {
6025  in = 1;
6026  if (first) {
6027  last = i;
6028  if (abs(have_it[first] - have_it[last]) < 3) {
6029  for (j = first; j < last; j++) {
6030  mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
6031  mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
6032  mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
6033  }
6034  }
6035  }
6036  }
6037  }
6038  else {
6039  if (in) {
6040  in = 0;
6041  first = i - 1;
6042  }
6043  }
6044  }
6045 
6046  cpl_free(have_it);
6047 
6048  }
6049 
6050 /*
6051  for (i = 0; i < ny; i++) {
6052  if (nlines[i] == 0) {
6053  for (k = 0; k <= order; k++) {
6054  cpl_table_set_invalid(idscoeff, clab[k], i);
6055  }
6056  }
6057  }
6058 */
6059 
6060  return resampled;
6061 }
6062 
6063 
6085 /*
6086 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
6087 {
6088  const char *func = "mos_locate_spectra_bis";
6089 
6090  cpl_apertures *slits;
6091  cpl_image *labimage;
6092  cpl_image *refimage;
6093  cpl_binary *mdata;
6094  cpl_table *slitpos;
6095  cpl_propertylist *sort_col;
6096  int nslits;
6097  int *have_it;
6098  int in, first, last;
6099  int i, j;
6100 
6101 
6102  if (mask == NULL) {
6103  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
6104  return NULL;
6105  }
6106 
6107  nx = cpl_mask_get_size_x(mask);
6108  ny = cpl_mask_get_size_y(mask);
6109 
6110  mdata = cpl_mask_get_data(refmask);
6111  have_it = cpl_calloc(ny, sizeof(int));
6112 
6113  for (i = 0; i < ny; i++, mdata += nx) {
6114  for (j = 0; j < nx; j++) {
6115  if (mdata[j] == CPL_BINARY_1) {
6116  have_it[i] = j + 1;
6117  break;
6118  }
6119  }
6120  }
6121 
6122  mdata = cpl_mask_get_data(refmask);
6123  in = 0;
6124  first = last = 0;
6125  nslits = 0;
6126 
6127  for (i = 0; i < ny; i++) {
6128  if (have_it[i]) {
6129  if (in) {
6130  if (i) {
6131  if (abs(have_it[i] - have_it[i-1]) > 3) {
6132  nslits++;
6133  }
6134  }
6135  }
6136  else {
6137  in = 1;
6138  nslits++;
6139  }
6140  }
6141  else {
6142  if (in) {
6143  in = 0;
6144  }
6145  }
6146  }
6147 }
6148 */
6149 
6150 
6172 cpl_table *mos_locate_spectra(cpl_mask *mask)
6173 {
6174  const char *func = "mos_locate_spectra";
6175 
6176  cpl_apertures *slits;
6177  cpl_image *labimage;
6178  cpl_image *refimage;
6179  cpl_table *slitpos;
6180  cpl_propertylist *sort_col;
6181  cpl_size nslits;
6182  int i;
6183 
6184 
6185  if (mask == NULL) {
6186  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
6187  return NULL;
6188  }
6189 
6190  labimage = cpl_image_labelise_mask_create(mask, &nslits);
6191 
6192  if (nslits < 1) {
6193  cpl_image_delete(labimage);
6194  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6195  return NULL;
6196  }
6197 
6198  refimage = cpl_image_new_from_mask(mask);
6199 
6200  slits = cpl_apertures_new_from_image(refimage, labimage);
6201 
6202  cpl_image_delete(labimage);
6203  cpl_image_delete(refimage);
6204 
6205  nslits = cpl_apertures_get_size(slits); /* Overwriting nslits - safer! */
6206  if (nslits < 1) {
6207  cpl_apertures_delete(slits);
6208  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6209  return NULL;
6210  }
6211 
6212  slitpos = cpl_table_new(nslits);
6213  cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
6214  cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
6215  cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
6216  cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
6217  cpl_table_set_column_unit(slitpos, "xtop", "pixel");
6218  cpl_table_set_column_unit(slitpos, "ytop", "pixel");
6219  cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
6220  cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
6221 
6222  for (i = 0; i < nslits; i++) {
6223  cpl_table_set_double(slitpos, "xtop", i,
6224  cpl_apertures_get_top_x(slits, i+1) - 1);
6225  cpl_table_set_double(slitpos, "ytop", i,
6226  cpl_apertures_get_top(slits, i+1));
6227  cpl_table_set_double(slitpos, "xbottom", i,
6228  cpl_apertures_get_bottom_x(slits, i+1) - 1);
6229  cpl_table_set_double(slitpos, "ybottom", i,
6230  cpl_apertures_get_bottom(slits, i+1));
6231  }
6232 
6233  cpl_apertures_delete(slits);
6234 
6235  sort_col = cpl_propertylist_new();
6236  cpl_propertylist_append_bool(sort_col, "ytop", 1);
6237  cpl_table_sort(slitpos, sort_col);
6238  cpl_propertylist_delete(sort_col);
6239 
6240  return slitpos;
6241 
6242 }
6243 
6244 
6260 cpl_error_code mos_validate_slits(cpl_table *slits)
6261 {
6262  const char *func = "mos_validate_slits";
6263 
6264 
6265  if (slits == NULL)
6266  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
6267 
6268  if (1 != cpl_table_has_column(slits, "xtop"))
6269  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6270 
6271  if (1 != cpl_table_has_column(slits, "ytop"))
6272  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6273 
6274  if (1 != cpl_table_has_column(slits, "xbottom"))
6275  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6276 
6277  if (1 != cpl_table_has_column(slits, "ybottom"))
6278  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6279 
6280  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
6281  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6282 
6283  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
6284  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6285 
6286  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
6287  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6288 
6289  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
6290  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6291 
6292  return CPL_ERROR_NONE;
6293 }
6294 
6295 
6324 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
6325 {
6326  const char *func = "mos_rotate_slits";
6327 
6328  cpl_error_code error;
6329  char aux_name[] = "_0";
6330  int i;
6331 
6332 
6333  rotation %= 4;
6334  if (rotation < 0)
6335  rotation += 4;
6336 
6337  if (rotation == 0)
6338  return CPL_ERROR_NONE;
6339 
6340  error = mos_validate_slits(slits);
6341  if (error)
6342  return cpl_error_set(func, error);
6343 
6344  if (rotation == 1 || rotation == 3) {
6345 
6346  /*
6347  * Swap x and y column names
6348  */
6349 
6350  for (i = 0; i < 77; i++)
6351  if (1 == cpl_table_has_column(slits, aux_name))
6352  aux_name[1]++;
6353  if (1 == cpl_table_has_column(slits, aux_name))
6354  return cpl_error_set(func, CPL_ERROR_CONTINUE);
6355  cpl_table_name_column(slits, "xtop", aux_name);
6356  cpl_table_name_column(slits, "ytop", "xtop");
6357  cpl_table_name_column(slits, aux_name, "ytop");
6358  cpl_table_name_column(slits, "xbottom", aux_name);
6359  cpl_table_name_column(slits, "ybottom", "xbottom");
6360  cpl_table_name_column(slits, aux_name, "ybottom");
6361  }
6362 
6363  if (rotation == 1 || rotation == 2) {
6364  cpl_table_multiply_scalar(slits, "xtop", -1.0);
6365  cpl_table_multiply_scalar(slits, "xbottom", -1.0);
6366  cpl_table_add_scalar(slits, "xtop", nx);
6367  cpl_table_add_scalar(slits, "xbottom", nx);
6368  }
6369 
6370  if (rotation == 3 || rotation == 2) {
6371  cpl_table_multiply_scalar(slits, "ytop", -1.0);
6372  cpl_table_multiply_scalar(slits, "ybottom", -1.0);
6373  cpl_table_add_scalar(slits, "ytop", ny);
6374  cpl_table_add_scalar(slits, "ybottom", ny);
6375  }
6376 
6377  return CPL_ERROR_NONE;
6378 }
6379 
6380 
6438 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
6439  cpl_table *global)
6440 {
6441  cpl_array *top_ident = NULL;;
6442  cpl_array *bot_ident = NULL;;
6443  cpl_matrix *mdata;
6444  cpl_matrix *mpattern;
6445  cpl_matrix *top_data;
6446  cpl_matrix *top_pattern;
6447  cpl_matrix *top_mdata;
6448  cpl_matrix *top_mpattern;
6449  cpl_matrix *bot_data;
6450  cpl_matrix *bot_pattern;
6451  cpl_matrix *bot_mdata;
6452  cpl_matrix *bot_mpattern;
6453  cpl_propertylist *sort_col;
6454  double *xtop;
6455  double *ytop;
6456  double *xmtop;
6457  double *ymtop;
6458  double *xbot;
6459  double *ybot;
6460  double *xmbot;
6461  double *ymbot;
6462  double top_scale, bot_scale;
6463  double angle, top_angle, bot_angle;
6464  double xmse, ymse;
6465  double xrms, top_xrms, bot_xrms;
6466  double yrms, top_yrms, bot_yrms;
6467  int nslits;
6468  int nmaskslits, use_pattern;
6469  int found_slits, found_slits_top, found_slits_bot;
6470  int i;
6471  cpl_table *positions;
6472  cpl_error_code error;
6473 
6474  cpl_vector *point;
6475  double *dpoint;
6476  cpl_vector *xpos;
6477  cpl_vector *ypos;
6478  cpl_vector *xmpos;
6479  cpl_vector *ympos;
6480  cpl_bivector *mpos;
6481  cpl_polynomial *xpoly = NULL;
6482  cpl_polynomial *ypoly = NULL;
6483  cpl_polynomial *top_xpoly = NULL;
6484  cpl_polynomial *top_ypoly = NULL;
6485  cpl_polynomial *bot_xpoly = NULL;
6486  cpl_polynomial *bot_ypoly = NULL;
6487 
6488  char *msg_multiplex = " ";
6489 
6490 
6491  error = mos_validate_slits(slits);
6492  if (error) {
6493  cpl_msg_error(cpl_func, "CCD slits table validation: %s",
6494  cpl_error_get_message());
6495  cpl_error_set(cpl_func, error);
6496  return NULL;
6497  }
6498 
6499  error = mos_validate_slits(maskslits);
6500  if (error) {
6501  cpl_msg_error(cpl_func, "Mask slits table validation: %s",
6502  cpl_error_get_message());
6503  cpl_error_set(cpl_func, error);
6504  return NULL;
6505  }
6506 
6507  if (1 != cpl_table_has_column(maskslits, "slit_id")) {
6508  cpl_msg_error(cpl_func, "Missing slits identifiers");
6509  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
6510  return NULL;
6511  }
6512 
6513  if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
6514  cpl_msg_error(cpl_func, "Wrong type used for slits identifiers");
6515  cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE);
6516  return NULL;
6517  }
6518 
6519  nslits = cpl_table_get_nrow(slits);
6520  nmaskslits = cpl_table_get_nrow(maskslits);
6521 
6522  if (nslits == 0 || nmaskslits == 0) {
6523  cpl_msg_error(cpl_func, "Empty slits table");
6524  cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
6525  return NULL;
6526  }
6527 
6528  if (nslits > 100 && mos_multiplex < 0) {
6529  cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching...");
6530  positions = mos_identify_slits_fast(slits, maskslits, global);
6531  if (positions == NULL)
6532  cpl_error_set_where(cpl_func);
6533  return positions;
6534  }
6535 
6536  /*
6537  * Guarantee that both input tables are sorted in the same way
6538  */
6539 
6540  sort_col = cpl_propertylist_new();
6541  cpl_propertylist_append_bool(sort_col, "ytop", 1);
6542  cpl_table_sort(slits, sort_col);
6543  cpl_table_sort(maskslits, sort_col);
6544  cpl_propertylist_delete(sort_col);
6545 
6546  /*
6547  * First we handle all the special cases (too few slits...)
6548  */
6549 
6550  if (nslits < 3 && nmaskslits > nslits) {
6551 
6552  /*
6553  * If there are just 1 or 2 slits on the CCD, and more on the
6554  * mask, the ambiguity cannot be solved, and an error is returned.
6555  * This is a case that must be solved with a first-guess relation
6556  * between mask and CCD.
6557  */
6558 
6559  if (nslits > 1)
6560  cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits "
6561  "with the %d mask slits: process will continue "
6562  "using the detected CCD slits positions", nslits,
6563  nmaskslits);
6564  else
6565  cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with "
6566  "the %d mask slits: process will continue using "
6567  "the detected CCD slit position", nmaskslits);
6568  return NULL;
6569  }
6570 
6571  if (nmaskslits < 3 && nslits > nmaskslits) {
6572 
6573  /*
6574  * If there are less than 3 slits on the mask the ambiguity cannot
6575  * be solved, and an error is returned. This is a case that must
6576  * be solved with a first-guess relation between mask and CCD.
6577  */
6578 
6579  cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with "
6580  "the %d mask slits: process will continue using "
6581  "the detected CCD slits positions", nslits,
6582  nmaskslits);
6583  return NULL;
6584  }
6585 
6586  /*
6587  * Pattern matching related operations begin here. Two pattern
6588  * matching will be run, one based on the "top" and another one
6589  * based on the "bottom" slit coordinates. The one with the
6590  * smallest rms in the Y coordinate will be chosen.
6591  */
6592 
6593  xtop = cpl_table_get_data_double(slits, "xtop");
6594  ytop = cpl_table_get_data_double(slits, "ytop");
6595  xmtop = cpl_table_get_data_double(maskslits, "xtop");
6596  ymtop = cpl_table_get_data_double(maskslits, "ytop");
6597 
6598  xbot = cpl_table_get_data_double(slits, "xbottom");
6599  ybot = cpl_table_get_data_double(slits, "ybottom");
6600  xmbot = cpl_table_get_data_double(maskslits, "xbottom");
6601  ymbot = cpl_table_get_data_double(maskslits, "ybottom");
6602 
6603  top_data = cpl_matrix_new(2, nslits);
6604  top_pattern = cpl_matrix_new(2, nmaskslits);
6605  bot_data = cpl_matrix_new(2, nslits);
6606  bot_pattern = cpl_matrix_new(2, nmaskslits);
6607 
6608  for (i = 0; i < nslits; i++)
6609  cpl_matrix_set(top_data, 0, i, xtop[i]);
6610 
6611  for (i = 0; i < nslits; i++)
6612  cpl_matrix_set(top_data, 1, i, ytop[i]);
6613 
6614  for (i = 0; i < nmaskslits; i++)
6615  cpl_matrix_set(top_pattern, 0, i, xmtop[i]);
6616 
6617  for (i = 0; i < nmaskslits; i++)
6618  cpl_matrix_set(top_pattern, 1, i, ymtop[i]);
6619 
6620  for (i = 0; i < nslits; i++)
6621  cpl_matrix_set(bot_data, 0, i, xbot[i]);
6622 
6623  for (i = 0; i < nslits; i++)
6624  cpl_matrix_set(bot_data, 1, i, ybot[i]);
6625 
6626  for (i = 0; i < nmaskslits; i++)
6627  cpl_matrix_set(bot_pattern, 0, i, xmbot[i]);
6628 
6629  for (i = 0; i < nmaskslits; i++)
6630  cpl_matrix_set(bot_pattern, 1, i, ymbot[i]);
6631 
6632  if (nmaskslits > nslits)
6633  use_pattern = nslits;
6634  else
6635  use_pattern = nmaskslits;
6636 
6637  top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern,
6638  use_pattern, 0.0, 0.1, 5, &top_mdata,
6639  &top_mpattern, &top_scale, &top_angle);
6640 
6641  bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern,
6642  use_pattern, 0.0, 0.1, 5, &bot_mdata,
6643  &bot_mpattern, &bot_scale, &bot_angle);
6644  cpl_matrix_delete(top_data);
6645  cpl_matrix_delete(top_pattern);
6646  cpl_matrix_delete(bot_data);
6647  cpl_matrix_delete(bot_pattern);
6648 
6649  if (top_ident == NULL && bot_ident == NULL) {
6650  cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match "
6651  "the %d found CCD slits with the %d mask slits: "
6652  "process will continue using the detected CCD "
6653  "slits positions", nslits, nmaskslits);
6654  return NULL;
6655  }
6656 
6657  found_slits_top = 0;
6658  found_slits_bot = 0;
6659  if (top_ident && bot_ident) {
6660  cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm",
6661  (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale));
6662  cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees",
6663  (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle));
6664  if (fabs(top_angle) < fabs(bot_angle))
6665  angle = fabs(top_angle);
6666  else
6667  angle = fabs(bot_angle);
6668  found_slits_top = cpl_matrix_get_ncol(top_mdata);
6669  found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
6670  }
6671  else if (top_ident) {
6672  cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale);
6673  cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle);
6674  angle = fabs(top_angle);
6675  found_slits_top = cpl_matrix_get_ncol(top_mdata);
6676  }
6677  else {
6678  cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale);
6679  cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle);
6680  angle = fabs(bot_angle);
6681  found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
6682  }
6683 
6684  cpl_array_delete(top_ident);
6685  cpl_array_delete(bot_ident);
6686 
6687  if (angle > 4.0) {
6688  cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation "
6689  "angle is expected to be around zero. This match is "
6690  "rejected: the process will continue using the %d "
6691  "detected CCD slits positions", nslits);
6692  return NULL;
6693  }
6694 
6695  found_slits = found_slits_top;
6696  if (found_slits < found_slits_bot)
6697  found_slits = found_slits_bot; /* Max value */
6698 
6699  if (found_slits < 4) {
6700  cpl_msg_warning(cpl_func,
6701  "Too few safely identified slits: %d out of %d "
6702  "candidates (%d expected). Process will continue "
6703  "using the detected CCD slits positions", found_slits,
6704  nslits, nmaskslits);
6705  return NULL;
6706  }
6707 
6708  cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d "
6709  "candidates\n(%d expected)", found_slits, nslits,
6710  nmaskslits);
6711 
6712  if (found_slits_top < 4)
6713  found_slits_top = 0;
6714 
6715  if (found_slits_bot < 4)
6716  found_slits_bot = 0;
6717 
6718  /*
6719  * Now for each set select the points of the identified slits, and fit
6720  * two bivariate polynomials to determine a first approximate relation
6721  * between positions on the mask and positions on the CCD.
6722  */
6723 
6724  for (i = 0; i < 2; i++) {
6725  cpl_size mindeg2d[] = {0, 0};
6726  cpl_size maxdeg2d[2];
6727  cpl_vector * fitresidual;
6728  if (i) {
6729  found_slits = found_slits_top;
6730  mdata = top_mdata;
6731  mpattern = top_mpattern;
6732  }
6733  else {
6734  found_slits = found_slits_bot;
6735  mdata = bot_mdata;
6736  mpattern = bot_mpattern;
6737  }
6738 
6739  if (found_slits == 0)
6740  continue;
6741  else if (found_slits < 10)
6742  maxdeg2d[0] = maxdeg2d[1] = 1;
6743  else
6744  maxdeg2d[0] = maxdeg2d[1] = 2;
6745 
6746  xpos = cpl_vector_wrap(found_slits,
6747  cpl_matrix_get_data(mdata) );
6748  ypos = cpl_vector_wrap(found_slits,
6749  cpl_matrix_get_data(mdata) + found_slits);
6750  xmpos = cpl_vector_wrap(found_slits,
6751  cpl_matrix_get_data(mpattern) );
6752  ympos = cpl_vector_wrap(found_slits,
6753  cpl_matrix_get_data(mpattern) + found_slits);
6754  mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
6755  fitresidual = cpl_vector_new(cpl_vector_get_size(xpos));
6756  xpoly = cpl_polynomial_new(2);
6757  cpl_polynomial_fit(xpoly, mpattern, NULL, xpos, NULL, CPL_FALSE, mindeg2d, maxdeg2d);
6758  cpl_vector_fill_polynomial_fit_residual(fitresidual, xpos, NULL, xpoly, mpattern, NULL);
6759  xmse = cpl_vector_product(fitresidual, fitresidual)
6760  / cpl_vector_get_size(fitresidual);
6761  ypoly = cpl_polynomial_new(2);
6762  cpl_polynomial_fit(ypoly, mpattern, NULL, ypos, NULL, CPL_FALSE, mindeg2d, maxdeg2d);
6763  cpl_vector_fill_polynomial_fit_residual(fitresidual, ypos, NULL, ypoly, mpattern, NULL);
6764  ymse = cpl_vector_product(fitresidual, fitresidual)
6765  / cpl_vector_get_size(fitresidual);
6766 
6767  cpl_bivector_unwrap_vectors(mpos);
6768  cpl_vector_unwrap(xpos);
6769  cpl_vector_unwrap(ypos);
6770  cpl_vector_unwrap(xmpos);
6771  cpl_vector_unwrap(ympos);
6772  cpl_matrix_delete(mdata);
6773  cpl_matrix_delete(mpattern);
6774  cpl_vector_delete(fitresidual);
6775 
6776  if (i) {
6777  top_xpoly = xpoly;
6778  top_ypoly = ypoly;
6779  top_xrms = sqrt(xmse*2*maxdeg2d[0]/(found_slits - 1));
6780  top_yrms = sqrt(ymse*2*maxdeg2d[0]/(found_slits - 1));
6781  }
6782  else {
6783  bot_xpoly = xpoly;
6784  bot_ypoly = ypoly;
6785  bot_xrms = sqrt(xmse*2*maxdeg2d[0]/(found_slits - 1));
6786  bot_yrms = sqrt(ymse*2*maxdeg2d[0]/(found_slits - 1));
6787  }
6788  }
6789 
6790  if (top_xpoly && bot_xpoly) {
6791  if (top_xrms < bot_xrms) { /* top X solution wins... */
6792  xrms = top_xrms;
6793  xpoly = top_xpoly;
6794  cpl_polynomial_delete(bot_xpoly);
6795  }
6796  else { /* bottom X solution wins... */
6797  xrms = bot_xrms;
6798  xpoly = bot_xpoly;
6799  cpl_polynomial_delete(top_xpoly);
6800  }
6801  }
6802  else if (top_xpoly) {
6803  xrms = top_xrms;
6804  xpoly = top_xpoly;
6805  }
6806  else {
6807  xrms = bot_xrms;
6808  xpoly = bot_xpoly;
6809  }
6810 
6811  if (top_ypoly && bot_ypoly) {
6812  if (top_yrms < bot_yrms) { /* top Y solution wins... */
6813  yrms = top_yrms;
6814  ypoly = top_ypoly;
6815  cpl_polynomial_delete(bot_ypoly);
6816  }
6817  else { /* bottom Y solution wins... */
6818  yrms = bot_yrms;
6819  ypoly = bot_ypoly;
6820  cpl_polynomial_delete(top_ypoly);
6821  }
6822  }
6823  else if (top_ypoly) {
6824  yrms = top_yrms;
6825  ypoly = top_ypoly;
6826  }
6827  else {
6828  yrms = bot_yrms;
6829  ypoly = bot_ypoly;
6830  }
6831 
6832  if (xpoly == NULL || ypoly == NULL) {
6833  cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the "
6834  "identified slits positions cannot be improved.");
6835  cpl_polynomial_delete(xpoly);
6836  cpl_polynomial_delete(ypoly);
6837  cpl_error_reset();
6838  return NULL;
6839  }
6840 
6841  cpl_msg_info(cpl_func,
6842  "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
6843  xrms, yrms);
6844 
6845  if (global) {
6846  write_global_distortion(global, 0, xpoly);
6847  write_global_distortion(global, 7, ypoly);
6848  }
6849 
6850  /*
6851  * The fit was successful: use the polynomials to obtain a new
6852  * position table with the improved positions of the slits
6853  */
6854 
6855  positions = cpl_table_duplicate(maskslits);
6856  cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
6857  cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
6858  cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
6859  cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
6860 
6861  point = cpl_vector_new(2);
6862  dpoint = cpl_vector_get_data(point);
6863 
6864  for (i = 0; i < nmaskslits; i++) {
6865  double position_x;
6866  double position_y;
6867 
6868  dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
6869  dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
6870  position_x = cpl_polynomial_eval(xpoly, point);
6871 // if (mos_multiplex >= 0) {
6872 // if (mos_multiplex != ((int)floor(position)) / mos_region_size) {
6873 // cpl_table_unselect_row(positions, i);
6874 // continue;
6875 // }
6876 // }
6877  cpl_table_set_double(positions, "xtop", i, position_x);
6878  position_y = cpl_polynomial_eval(ypoly, point);
6879  cpl_table_set_double(positions, "ytop", i, position_y);
6880  dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
6881  dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
6882  position_x = cpl_polynomial_eval(xpoly, point);
6883  cpl_table_set_double(positions, "xbottom", i, position_x);
6884  position_y = cpl_polynomial_eval(ypoly, point);
6885  cpl_table_set_double(positions, "ybottom", i, position_y);
6886  }
6887 
6888 // if (mos_multiplex >= 0) {
6889 // cpl_table_not_selected(positions);
6890 // cpl_table_erase_selected(positions);
6891 // nmaskslits = cpl_table_get_nrow(positions);
6892 // }
6893 
6894  cpl_vector_delete(point);
6895  cpl_polynomial_delete(xpoly);
6896  cpl_polynomial_delete(ypoly);
6897 
6898  cpl_table_erase_column(positions, "xmtop");
6899  cpl_table_erase_column(positions, "ymtop");
6900  cpl_table_erase_column(positions, "xmbottom");
6901  cpl_table_erase_column(positions, "ymbottom");
6902 
6903  if (mos_multiplex >= 0) {
6904  msg_multiplex =
6905  cpl_sprintf("in the CCD section between %d and %d pixel",
6906  mos_multiplex * mos_region_size,
6907  (mos_multiplex + 1) * mos_region_size);
6908  }
6909 
6910  if (nmaskslits > nslits)
6911  cpl_msg_info(cpl_func,
6912  "Finally identified slits: %d out of %d expected %s\n"
6913  "(%d recovered)", nmaskslits, nmaskslits, msg_multiplex,
6914  nmaskslits - nslits);
6915  else if (nmaskslits < nslits)
6916  cpl_msg_info(cpl_func,
6917  "Finally identified slits: %d out of %d expected %s\n"
6918  "(%d rejected)", nmaskslits, nmaskslits, msg_multiplex,
6919  nslits - nmaskslits);
6920  else
6921  cpl_msg_info(cpl_func,
6922  "Finally identified slits: %d out of %d expected %s",
6923  nmaskslits, nmaskslits, msg_multiplex);
6924 
6925  if (mos_multiplex >= 0) {
6926  cpl_free(msg_multiplex);
6927  }
6928 
6929  return positions;
6930 
6931 }
6932 
6933 
6934 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits,
6935  cpl_table *global)
6936 {
6937  const char *func = "mos_identify_slits_fast";
6938 
6939  cpl_propertylist *sort_col;
6940  cpl_table *positions;
6941  cpl_vector *scales;
6942  cpl_vector *angles;
6943  cpl_vector *point;
6944  cpl_vector *xpos;
6945  cpl_vector *ypos;
6946  cpl_vector *xmpos;
6947  cpl_vector *ympos;
6948  cpl_bivector *mpos;
6949  cpl_polynomial *xpoly = NULL;
6950  cpl_polynomial *ypoly = NULL;
6951  cpl_error_code error;
6952  int nslits;
6953  int nmaskslits;
6954  int found_slits;
6955  int i, j, k;
6956 
6957  double dist1, dist2, dist3, dist, mindist;
6958  double scale, minscale, maxscale;
6959  double angle, minangle, maxangle;
6960  double *dscale;
6961  double *dangle;
6962  double *dpoint;
6963  double *xtop;
6964  double *ytop;
6965  double *xbottom;
6966  double *ybottom;
6967  double *xcenter;
6968  double *ycenter;
6969  double *xpseudo;
6970  double *ypseudo;
6971  int *slit_id;
6972  double *xmtop;
6973  double *ymtop;
6974  double *xmbottom;
6975  double *ymbottom;
6976  double *xmcenter;
6977  double *ymcenter;
6978  double *xmpseudo;
6979  double *ympseudo;
6980  double xmse, ymse;
6981  int *mslit_id;
6982  int *good;
6983  int minpos;
6984  int degree;
6985 
6986  double sradius = 0.01; /* Candidate input argument... */
6987  int in_sradius;
6988 
6989  double pi = 3.14159265358979323846;
6990 
6991 
6992  error = mos_validate_slits(slits);
6993  if (error) {
6994  cpl_msg_error(func, "CCD slits table validation: %s",
6995  cpl_error_get_message());
6996  cpl_error_set(func, error);
6997  return NULL;
6998  }
6999 
7000  error = mos_validate_slits(maskslits);
7001  if (error) {
7002  cpl_msg_error(func, "Mask slits table validation: %s",
7003  cpl_error_get_message());
7004  cpl_error_set(func, error);
7005  return NULL;
7006  }
7007 
7008  if (1 != cpl_table_has_column(maskslits, "slit_id")) {
7009  cpl_msg_error(func, "Missing slits identifiers");
7010  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
7011  return NULL;
7012  }
7013 
7014  if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
7015  cpl_msg_error(func, "Wrong type used for slits identifiers");
7016  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
7017  return NULL;
7018  }
7019 
7020  nslits = cpl_table_get_nrow(slits);
7021  nmaskslits = cpl_table_get_nrow(maskslits);
7022 
7023  if (nslits == 0 || nmaskslits == 0) {
7024  cpl_msg_error(func, "Empty slits table");
7025  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
7026  return NULL;
7027  }
7028 
7029 
7030  /*
7031  * Compute middle point coordinates for each slit listed in both
7032  * input tables.
7033  */
7034 
7035  if (cpl_table_has_column(slits, "xcenter"))
7036  cpl_table_erase_column(slits, "xcenter");
7037 
7038  if (cpl_table_has_column(slits, "ycenter"))
7039  cpl_table_erase_column(slits, "ycenter");
7040 
7041  if (cpl_table_has_column(maskslits, "xcenter"))
7042  cpl_table_erase_column(maskslits, "xcenter");
7043 
7044  if (cpl_table_has_column(maskslits, "ycenter"))
7045  cpl_table_erase_column(maskslits, "ycenter");
7046 
7047  cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
7048  cpl_table_add_columns(slits, "xcenter", "xbottom");
7049  cpl_table_divide_scalar(slits, "xcenter", 2.0);
7050  cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
7051  cpl_table_add_columns(slits, "ycenter", "ybottom");
7052  cpl_table_divide_scalar(slits, "ycenter", 2.0);
7053 
7054  cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
7055  cpl_table_add_columns(maskslits, "xcenter", "xbottom");
7056  cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
7057  cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
7058  cpl_table_add_columns(maskslits, "ycenter", "ybottom");
7059  cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
7060 
7061 
7062  /*
7063  * Guarantee that both input tables are sorted in the same way
7064  */
7065 
7066  sort_col = cpl_propertylist_new();
7067  cpl_propertylist_append_bool(sort_col, "ycenter", 1);
7068  cpl_table_sort(slits, sort_col);
7069  cpl_table_sort(maskslits, sort_col);
7070  cpl_propertylist_delete(sort_col);
7071 
7072 
7073  /*
7074  * First we handle all the special cases (too few slits...)
7075  */
7076 
7077  if (nslits < 3 && nmaskslits > nslits) {
7078 
7079  /*
7080  * If there are just 1 or 2 slits on the CCD, and more on the
7081  * mask, the ambiguity cannot be solved, and an error is returned.
7082  * This is a case that must be solved with a first-guess relation
7083  * between mask and CCD.
7084  */
7085 
7086  if (nslits > 1)
7087  cpl_msg_warning(func, "Cannot match the found CCD slit with the "
7088  "%d mask slits: process will continue using the "
7089  "detected CCD slit position", nmaskslits);
7090  else
7091  cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
7092  "the %d mask slits: process will continue using "
7093  "the detected CCD slits positions", nslits,
7094  nmaskslits);
7095  return NULL;
7096  }
7097 
7098  if (nslits <= 3 && nslits == nmaskslits) {
7099 
7100  cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
7101  cpl_msg_warning(func, "Their detected positions are left unchanged");
7102 
7103  /*
7104  * If there are just up to 3 slits, both on the mask and on the CCD,
7105  * we can reasonably hope that those slits were found, and accept
7106  * that their positions on the CCD cannot be improved. We prepare
7107  * therefore an output position table containing the slits with
7108  * their original positions. We can however give an estimate of
7109  * the platescale if there is more than one slit.
7110  */
7111 
7112  positions = cpl_table_duplicate(slits);
7113  cpl_table_erase_column(slits, "xcenter");
7114  cpl_table_erase_column(slits, "ycenter");
7115  cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
7116  cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
7117  cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
7118  cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
7119  cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
7120  cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
7121  cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
7122  cpl_table_erase_column(maskslits, "xcenter");
7123  cpl_table_erase_column(maskslits, "ycenter");
7124 
7125  if (nslits > 1) {
7126  xcenter = cpl_table_get_data_double(positions, "xcenter");
7127  ycenter = cpl_table_get_data_double(positions, "ycenter");
7128  xmcenter = cpl_table_get_data_double(positions, "xmcenter");
7129  ymcenter = cpl_table_get_data_double(positions, "ymcenter");
7130 
7131  dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
7132  + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
7133  dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
7134  + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
7135  scale = sqrt(dist1/dist2);
7136 
7137  if (nslits == 3) {
7138  dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
7139  + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
7140  dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
7141  + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
7142  scale += sqrt(dist1/dist2);
7143  scale /= 2;
7144  }
7145 
7146  cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
7147  }
7148 
7149  return positions;
7150  }
7151 
7152  if (nmaskslits < 3 && nslits > nmaskslits) {
7153 
7154  /*
7155  * If there are less than 3 slits on the mask the ambiguity cannot
7156  * be solved, and an error is returned. This is a case that must
7157  * be solved with a first-guess relation between mask and CCD.
7158  */
7159 
7160  cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
7161  "the %d mask slits: process will continue using "
7162  "the detected CCD slits positions", nslits,
7163  nmaskslits);
7164  return NULL;
7165  }
7166 
7167 
7168  /*
7169  * At this point of the program all the region of the plane
7170  * (nslits, nmaskslits) where either or both mask and CCD display
7171  * less than 3 slits are handled in some way. It would be better
7172  * to add in this place a special handling for identifying slits
7173  * in case of a very reduced number of slits (say, below 6).
7174  * It is also clear that if there are many more slits on the
7175  * mask than on the CCD, or many more on the CCD than on the
7176  * mask, something went deeply wrong with the preliminary
7177  * wavelength calibration. Such cases should be handled with
7178  * a _complete_ pattern-recognition algorithm based on the
7179  * construction of all possible triangles. For the moment,
7180  * we go directly to the limited pattern-recognition applied
7181  * below, based on triangles build only for consecutive slits.
7182  * This is reasonably safe, since the preliminary wavelength
7183  * calibration performed by mos_identify_peaks() is generally
7184  * robust.
7185  */
7186 
7187 
7188  /*
7189  * Compute (X, Y) coordinates on pseudo-plane describing the
7190  * different position ratios of successive slits, in both
7191  * input tables.
7192  */
7193 
7194  if (cpl_table_has_column(slits, "xpseudo"))
7195  cpl_table_erase_column(slits, "xpseudo");
7196 
7197  if (cpl_table_has_column(slits, "ypseudo"))
7198  cpl_table_erase_column(slits, "ypseudo");
7199 
7200  if (cpl_table_has_column(maskslits, "xpseudo"))
7201  cpl_table_erase_column(maskslits, "xpseudo");
7202 
7203  if (cpl_table_has_column(maskslits, "ypseudo"))
7204  cpl_table_erase_column(maskslits, "ypseudo");
7205 
7206  cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
7207  cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
7208 
7209  xcenter = cpl_table_get_data_double(slits, "xcenter");
7210  ycenter = cpl_table_get_data_double(slits, "ycenter");
7211  xpseudo = cpl_table_get_data_double(slits, "xpseudo");
7212  ypseudo = cpl_table_get_data_double(slits, "ypseudo");
7213 
7214  for (i = 1; i < nslits - 1; i++) {
7215  dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
7216  + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
7217  dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
7218  + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
7219  dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
7220  + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
7221  xpseudo[i] = sqrt(dist1/dist2);
7222  ypseudo[i] = sqrt(dist3/dist2);
7223  }
7224 
7225  cpl_table_set_invalid(slits, "xpseudo", 0);
7226  cpl_table_set_invalid(slits, "xpseudo", nslits-1);
7227  cpl_table_set_invalid(slits, "ypseudo", 0);
7228  cpl_table_set_invalid(slits, "ypseudo", nslits-1);
7229 
7230  cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
7231  cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
7232 
7233  xcenter = cpl_table_get_data_double(maskslits, "xcenter");
7234  ycenter = cpl_table_get_data_double(maskslits, "ycenter");
7235  xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
7236  ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
7237 
7238  for (i = 1; i < nmaskslits - 1; i++) {
7239  dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
7240  + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
7241  dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
7242  + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
7243  dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
7244  + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
7245  xmpseudo[i] = sqrt(dist1/dist2);
7246  ympseudo[i] = sqrt(dist3/dist2);
7247  }
7248 
7249  cpl_table_set_invalid(maskslits, "xpseudo", 0);
7250  cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
7251  cpl_table_set_invalid(maskslits, "ypseudo", 0);
7252  cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
7253 
7254 
7255  /*
7256  * For each (X, Y) on the pseudo-plane related to the mask positions,
7257  * find the closest (X, Y) on the pseudo-plane related to the CCD
7258  * positions. If the closest point is closer than a given search
7259  * radius, a triangle has been found, and 3 slits are identified.
7260  * However, if more than one point is found within the search
7261  * radius, we have an ambiguity and this is rejected.
7262  */
7263 
7264  if (cpl_table_has_column(slits, "slit_id"))
7265  cpl_table_erase_column(slits, "slit_id");
7266  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
7267  slit_id = cpl_table_get_data_int(maskslits, "slit_id");
7268 
7269  for (i = 1; i < nmaskslits - 1; i++) {
7270  in_sradius = 0;
7271  mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
7272  + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
7273  minpos = 1;
7274  if (mindist < sradius*sradius)
7275  in_sradius++;
7276  for (j = 2; j < nslits - 1; j++) {
7277  dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
7278  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
7279  if (dist < sradius*sradius)
7280  in_sradius++;
7281  if (in_sradius > 1) /* More than one triangle within radius */
7282  break;
7283  if (mindist > dist) {
7284  mindist = dist;
7285  minpos = j;
7286  }
7287  }
7288 
7289  mindist = sqrt(mindist);
7290 
7291  if (mindist < sradius && in_sradius == 1) {
7292  cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
7293  cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
7294  cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
7295  }
7296  }
7297 
7298 
7299  /*
7300  * At this point, the slit_id column contains invalid elements
7301  * corresponding to unidentified slits.
7302  */
7303 
7304  found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
7305 
7306  if (found_slits < 3) {
7307  cpl_msg_warning(func, "Too few preliminarily identified slits: "
7308  "%d out of %d", found_slits, nslits);
7309  if (nslits == nmaskslits) {
7310  cpl_msg_warning(func, "(this is not an error, it could be caused "
7311  "by a mask with regularly located slits)");
7312  cpl_msg_warning(func, "The detected slits positions are left "
7313  "unchanged");
7314 
7315  /*
7316  * If there are less than 3 identified slits, this is probably
7317  * a mask with regularly spaced slits (leading to an ambiguous
7318  * pattern). Only in the case all expected slits appear to have
7319  * been found on the CCD we can proceed...
7320  */
7321 
7322  cpl_table_erase_column(slits, "slit_id");
7323  cpl_table_erase_column(slits, "xpseudo");
7324  cpl_table_erase_column(slits, "ypseudo");
7325  positions = cpl_table_duplicate(slits);
7326  cpl_table_erase_column(slits, "xcenter");
7327  cpl_table_erase_column(slits, "ycenter");
7328 
7329  cpl_table_erase_column(maskslits, "xpseudo");
7330  cpl_table_erase_column(maskslits, "ypseudo");
7331  cpl_table_duplicate_column(positions, "xmtop",
7332  maskslits, "xtop");
7333  cpl_table_duplicate_column(positions, "ymtop",
7334  maskslits, "ytop");
7335  cpl_table_duplicate_column(positions, "xmbottom",
7336  maskslits, "xbottom");
7337  cpl_table_duplicate_column(positions, "ymbottom",
7338  maskslits, "ybottom");
7339  cpl_table_duplicate_column(positions, "xmcenter",
7340  maskslits, "xcenter");
7341  cpl_table_duplicate_column(positions, "ymcenter",
7342  maskslits, "ycenter");
7343  cpl_table_duplicate_column(positions, "slit_id",
7344  maskslits, "slit_id");
7345  cpl_table_erase_column(maskslits, "xcenter");
7346  cpl_table_erase_column(maskslits, "ycenter");
7347  return positions;
7348  }
7349  else {
7350  cpl_table_erase_column(slits, "slit_id");
7351  cpl_table_erase_column(slits, "xpseudo");
7352  cpl_table_erase_column(slits, "ypseudo");
7353  positions = cpl_table_duplicate(slits);
7354  cpl_table_erase_column(slits, "xcenter");
7355  cpl_table_erase_column(slits, "ycenter");
7356  cpl_msg_warning(func, "(the failure could be caused "
7357  "by a mask with regularly located slits)");
7358  return NULL;
7359  }
7360  }
7361  else {
7362  cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
7363  "candidates (%d expected)", found_slits, nslits,
7364  nmaskslits);
7365  }
7366 
7367 
7368  /*
7369  * Create a table with the coordinates of the preliminarily identified
7370  * slits, both on CCD and mask. The original order of the slits positions
7371  * is preserved.
7372  */
7373 
7374  positions = cpl_table_new(found_slits);
7375  cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
7376  cpl_table_new_column(positions, "xtop", CPL_TYPE_DOUBLE);
7377  cpl_table_new_column(positions, "ytop", CPL_TYPE_DOUBLE);
7378  cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
7379  cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
7380  cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
7381  cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
7382  cpl_table_new_column(positions, "xmtop", CPL_TYPE_DOUBLE);
7383  cpl_table_new_column(positions, "ymtop", CPL_TYPE_DOUBLE);
7384  cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
7385  cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
7386  cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
7387  cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
7388  cpl_table_new_column(positions, "good", CPL_TYPE_INT);
7389  cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
7390 
7391  slit_id = cpl_table_get_data_int (slits, "slit_id");
7392  xtop = cpl_table_get_data_double(slits, "xtop");
7393  ytop = cpl_table_get_data_double(slits, "ytop");
7394  xbottom = cpl_table_get_data_double(slits, "xbottom");
7395  ybottom = cpl_table_get_data_double(slits, "ybottom");
7396  xcenter = cpl_table_get_data_double(slits, "xcenter");
7397  ycenter = cpl_table_get_data_double(slits, "ycenter");
7398 
7399  mslit_id = cpl_table_get_data_int (maskslits, "slit_id");
7400  xmtop = cpl_table_get_data_double(maskslits, "xtop");
7401  ymtop = cpl_table_get_data_double(maskslits, "ytop");
7402  xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
7403  ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
7404  xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
7405  ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
7406 
7407 
7408  /*
7409  * Transferring the valid slits information to the new table.
7410  * Note that invalid elements are coded as 0 in the internal
7411  * buffer, and this is the way they are recognised and excluded.
7412  */
7413 
7414  k = 0;
7415  cpl_table_fill_invalid_int(slits, "slit_id", 0);
7416  for (i = 0; i < nmaskslits; i++) {
7417  for (j = 0; j < nslits; j++) {
7418  if (slit_id[j] == 0)
7419  continue; /* Skip invalid slit */
7420  if (mslit_id[i] == slit_id[j]) {
7421  cpl_table_set_int (positions, "slit_id", k, slit_id[j]);
7422 
7423  cpl_table_set_double(positions, "xtop", k, xtop[j]);
7424  cpl_table_set_double(positions, "ytop", k, ytop[j]);
7425  cpl_table_set_double(positions, "xbottom", k, xbottom[j]);
7426  cpl_table_set_double(positions, "ybottom", k, ybottom[j]);
7427  cpl_table_set_double(positions, "xcenter", k, xcenter[j]);
7428  cpl_table_set_double(positions, "ycenter", k, ycenter[j]);
7429 
7430  cpl_table_set_double(positions, "xmtop", k, xmtop[i]);
7431  cpl_table_set_double(positions, "ymtop", k, ymtop[i]);
7432  cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
7433  cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
7434  cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
7435  cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
7436 
7437  k++;
7438 
7439  break;
7440  }
7441  }
7442  }
7443 
7444  found_slits = k;
7445 
7446  cpl_table_erase_column(slits, "slit_id");
7447  cpl_table_erase_column(slits, "xpseudo");
7448  cpl_table_erase_column(slits, "ypseudo");
7449  cpl_table_erase_column(slits, "xcenter");
7450  cpl_table_erase_column(slits, "ycenter");
7451  cpl_table_erase_column(maskslits, "xpseudo");
7452  cpl_table_erase_column(maskslits, "ypseudo");
7453  cpl_table_erase_column(maskslits, "xcenter");
7454  cpl_table_erase_column(maskslits, "ycenter");
7455 
7456 
7457  /*
7458  * Find the median platescale and rotation angle from the identified
7459  * slits, and then exclude slits outlaying more than 10% from the
7460  * median platescale, and more than 2 degrees from the median
7461  * rotation angle.
7462  */
7463 
7464  ytop = cpl_table_get_data_double(positions, "ytop");
7465  ybottom = cpl_table_get_data_double(positions, "ybottom");
7466  xcenter = cpl_table_get_data_double(positions, "xcenter");
7467  ycenter = cpl_table_get_data_double(positions, "ycenter");
7468  xmcenter = cpl_table_get_data_double(positions, "xmcenter");
7469  ymcenter = cpl_table_get_data_double(positions, "ymcenter");
7470 
7471  scales = cpl_vector_new(found_slits - 1);
7472  dscale = cpl_vector_get_data(scales);
7473  angles = cpl_vector_new(found_slits - 1);
7474  dangle = cpl_vector_get_data(angles);
7475 
7476  for (i = 1; i < found_slits; i++) {
7477  dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
7478  + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
7479  dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
7480  + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
7481  dscale[i-1] = sqrt(dist1/dist2);
7482  dangle[i-1] = atan2(ycenter[i-1] - ycenter[i],
7483  xcenter[i-1] - xcenter[i])
7484  - atan2(ymcenter[i-1] - ymcenter[i],
7485  xmcenter[i-1] - xmcenter[i]);
7486  dangle[i-1] *= 180;
7487  dangle[i-1] /= pi;
7488  }
7489 
7490  minscale = cpl_vector_get_min(scales);
7491  scale = cpl_vector_get_median_const(scales);
7492  maxscale = cpl_vector_get_max(scales);
7493 
7494  minangle = cpl_vector_get_min(angles);
7495  angle = cpl_vector_get_median_const(angles);
7496  maxangle = cpl_vector_get_max(angles);
7497 
7498  cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
7499  cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm",
7500  minscale, maxscale);
7501 
7502  cpl_msg_info(func, "Median rotation: %f degrees", angle);
7503  cpl_msg_info(func, "Minmax rotation: %f, %f degrees",
7504  minangle, maxangle);
7505 
7506  good = cpl_table_get_data_int(positions, "good");
7507 
7508  good[0] = good[found_slits - 1] = 1;
7509  for (i = 1; i < found_slits; i++) {
7510  if (fabs((dscale[i-1] - scale)/scale) < 0.10
7511  && fabs(dangle[i-1] - angle) < 2) {
7512  good[i-1]++;
7513  good[i]++;
7514  }
7515  }
7516 
7517  for (i = 0; i < found_slits; i++) {
7518  if (good[i] < 2)
7519  good[i] = 0;
7520  else
7521  good[i] = 1;
7522  }
7523 
7524 /*
7525  for (i = 1; i < found_slits; i++)
7526  if (fabs((dscale[i-1] - scale)/scale) < 0.10)
7527  good[i-1] = good[i] = 1;
7528 */
7529 
7530 /* DEBUG ************+
7531  for (i = 0; i < found_slits; i++) {
7532  if (good[i]) {
7533  if (i == found_slits - 1)
7534  printf("include slit %d, prev = %f, %f\n",
7535  i, dscale[i-1], dangle[i-1]);
7536  else if (i == 0)
7537  printf("include slit %d, next %f, %f\n",
7538  i, dscale[i], dangle[i]);
7539  else
7540  printf("include slit %d, prev = %f, %f, next %f, %f\n", i,
7541  dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
7542  }
7543  else {
7544  if (i == found_slits - 1)
7545  printf("EXclude slit %d, prev = %f, %f\n",
7546  i, dscale[i-1], dangle[i-1]);
7547  else if (i == 0)
7548  printf("EXclude slit %d, next %f, %f\n",
7549  i, dscale[i], dangle[i]);
7550  else
7551  printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,
7552  dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
7553  }
7554  }
7555 +*********** DEBUG */
7556 
7557  cpl_vector_delete(scales);
7558  cpl_vector_delete(angles);
7559 
7560  cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
7561  cpl_table_erase_selected(positions);
7562  cpl_table_erase_column(positions, "good");
7563  found_slits = cpl_table_get_nrow(positions);
7564 
7565  if (found_slits < 4) {
7566 
7567  /*
7568  * If the self-consistency check gives such a poor result,
7569  * something must have gone really wrong in the preliminary
7570  * wavelength calibration... Nothing can be done.
7571  */
7572 
7573  cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
7574  "candidates (%d expected). Process will continue "
7575  "using the detected CCD slits positions", found_slits,
7576  nslits, nmaskslits);
7577  cpl_table_delete(positions);
7578  return NULL;
7579  }
7580  else {
7581  cpl_msg_info(func, "Safely identified slits: %d out of %d "
7582  "candidates\n(%d expected)", found_slits, nslits,
7583  nmaskslits);
7584  }
7585 
7586 
7587  /*
7588  * Now select the central points of the identified slits, and
7589  * fit two bivariate polynomials to determine a first approximate
7590  * relation between positions on the mask and positions on the CCD.
7591  */
7592 
7593  xpos = cpl_vector_wrap(found_slits,
7594  cpl_table_get_data_double(positions, "xcenter"));
7595  ypos = cpl_vector_wrap(found_slits,
7596  cpl_table_get_data_double(positions, "ycenter"));
7597  xmpos = cpl_vector_wrap(found_slits,
7598  cpl_table_get_data_double(positions, "xmcenter"));
7599  ympos = cpl_vector_wrap(found_slits,
7600  cpl_table_get_data_double(positions, "ymcenter"));
7601  mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
7602 
7603  if (found_slits < 10)
7604  degree = 1;
7605  else
7606  degree = 2;
7607 
7608  xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
7609  if (xpoly != NULL)
7610  ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
7611  cpl_bivector_unwrap_vectors(mpos);
7612  cpl_vector_unwrap(xpos);
7613  cpl_vector_unwrap(ypos);
7614  cpl_vector_unwrap(xmpos);
7615  cpl_vector_unwrap(ympos);
7616  if (ypoly == NULL) {
7617  if (found_slits == nmaskslits) {
7618  cpl_msg_warning(func, "Fit failure: the accuracy of the "
7619  "identified slits positions is not improved.");
7620 
7621  /*
7622  * The determination of the transformation from mask to CCD
7623  * failed, but since all slits have been already identified
7624  * this is not a problem: data can be reduced also without
7625  * such transformation. Simply the accuracy of the slits
7626  * positions cannot be improved.
7627  */
7628 
7629  } else {
7630  cpl_msg_info(func, "Fit failure: not all slits have been "
7631  "identified. Process will continue using "
7632  "the detected CCD slits positions");
7633  }
7634 
7635  cpl_polynomial_delete(xpoly);
7636  return positions;
7637  }
7638 
7639  cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
7640  sqrt(xmse), sqrt(ymse));
7641 
7642  if (global) {
7643  write_global_distortion(global, 0, xpoly);
7644  write_global_distortion(global, 7, ypoly);
7645  }
7646 
7647  /*
7648  * The fit was successful: use the polynomials to obtain a new
7649  * position table with the improved positions of the slits
7650  */
7651 
7652  cpl_table_delete(positions);
7653 
7654  positions = cpl_table_duplicate(maskslits);
7655  cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
7656  cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
7657  cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
7658  cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
7659 
7660  point = cpl_vector_new(2);
7661  dpoint = cpl_vector_get_data(point);
7662 
7663  for (i = 0; i < nmaskslits; i++) {
7664  dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
7665  dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
7666  cpl_table_set_double(positions, "xtop", i,
7667  cpl_polynomial_eval(xpoly, point));
7668  cpl_table_set_double(positions, "ytop", i,
7669  cpl_polynomial_eval(ypoly, point));
7670  dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
7671  dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
7672  cpl_table_set_double(positions, "xbottom", i,
7673  cpl_polynomial_eval(xpoly, point));
7674  cpl_table_set_double(positions, "ybottom", i,
7675  cpl_polynomial_eval(ypoly, point));
7676  }
7677 
7678  cpl_vector_delete(point);
7679  cpl_polynomial_delete(xpoly);
7680  cpl_polynomial_delete(ypoly);
7681 
7682  cpl_table_erase_column(positions, "xmtop");
7683  cpl_table_erase_column(positions, "ymtop");
7684  cpl_table_erase_column(positions, "xmbottom");
7685  cpl_table_erase_column(positions, "ymbottom");
7686 
7687  if (nmaskslits > nslits)
7688  cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
7689  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
7690  else if (nmaskslits < nslits)
7691  cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
7692  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
7693  else
7694  cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
7695  nmaskslits, nmaskslits);
7696 
7697  return positions;
7698 }
7699 
7700 
7742 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
7743  double blue, double red, double dispersion)
7744 {
7745 
7746  const char *func = "mos_trace_flat";
7747 
7748  cpl_image *gradient;
7749  cpl_image *sgradient;
7750  float *dgradient;
7751  float level = 500; /* It was 250... */
7752  cpl_vector *row;
7753  cpl_vector *srow;
7754  cpl_vector **peaks;
7755  double *peak;
7756  int *slit_id;
7757  float *g;
7758  double *r;
7759  double *xtop;
7760  double *ytop;
7761  double *xbottom;
7762  double *ybottom;
7763  double min, dist;
7764  double sradius;
7765  double tolerance;
7766  double start_y, prev_y;
7767  int minpos;
7768  int nslits;
7769  int nrows;
7770  int step = 10;
7771  int filtbox = 15;
7772  int nx, ny, npix;
7773  int pos, ypos;
7774  int npeaks;
7775  int pixel_above, pixel_below;
7776  int i, j, k, l;
7777  char trace_id[MAX_COLNAME];
7778 
7779  cpl_table *traces;
7780 
7781 
7782  if (flat == NULL || slits == NULL) {
7783  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
7784  return NULL;
7785  }
7786 
7787  if (dispersion <= 0.0) {
7788  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
7789  return NULL;
7790  }
7791 
7792  if (red - blue < dispersion) {
7793  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
7794  return NULL;
7795  }
7796 
7797  /*
7798  * Create a dummy slit_id column if it is missing in the
7799  * input slits table
7800  */
7801 
7802  nslits = cpl_table_get_nrow(slits);
7803  if (1 != cpl_table_has_column(slits, "slit_id")) {
7804  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
7805  for (i = 0; i < nslits; i++)
7806  cpl_table_set_int(slits, "slit_id", i, -(i+1)); /* it was (i+1) */
7807  }
7808 
7809  slit_id = cpl_table_get_data_int(slits, "slit_id");
7810 
7811  nx = cpl_image_get_size_x(flat);
7812  ny = cpl_image_get_size_y(flat);
7813  npix = nx * ny;
7814 
7815  gradient = cpl_image_duplicate(flat);
7816  dgradient = cpl_image_get_data_float(gradient);
7817 
7818  for (i = 0; i < ny - 1; i++) {
7819  k = i * nx;
7820  for (j = 0; j < nx; j++) {
7821  l = k + j;
7822  dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
7823  }
7824  }
7825 
7826  npix--;
7827  for (j = 0; j < nx; j++)
7828  dgradient[npix - j] = 0.0;
7829 
7830  cpl_image_turn(gradient, -1);
7831  nx = cpl_image_get_size_x(gradient);
7832  ny = cpl_image_get_size_y(gradient);
7833  sgradient = mos_image_vertical_median_filter(gradient,
7834  filtbox, 0, ny, 0, step);
7835  cpl_image_delete(gradient);
7836 
7837 
7838  /*
7839  * Remove background from processed image rows
7840  */
7841 
7842  dgradient = cpl_image_get_data_float(sgradient);
7843 
7844  for (i = 1; i <= ny; i += step) {
7845  row = cpl_vector_new_from_image_row(sgradient, i);
7846  srow = cpl_vector_filter_median_create(row, filtbox);
7847  cpl_vector_subtract(row, srow);
7848  cpl_vector_delete(srow);
7849  g = dgradient + (i-1)*nx;
7850  r = cpl_vector_get_data(row);
7851  for (j = 0; j < nx; j++)
7852  g[j] = r[j];
7853  cpl_vector_delete(row);
7854  }
7855 
7856 
7857  /*
7858  * Rotate (temporarily) the input slits table, to get coordinates
7859  * compatible with the rotated gradient image.
7860  */
7861 
7862  mos_rotate_slits(slits, 1, nx, ny);
7863  xtop = cpl_table_get_data_double(slits, "xtop");
7864  ytop = cpl_table_get_data_double(slits, "ytop");
7865  xbottom = cpl_table_get_data_double(slits, "xbottom");
7866  ybottom = cpl_table_get_data_double(slits, "ybottom");
7867 
7868 
7869  /*
7870  * Get positions of peaks candidates for each processed gradient
7871  * image row
7872  */
7873 
7874  peaks = cpl_calloc(ny, sizeof(cpl_vector *));
7875 
7876  for (i = 0; i < ny; i += step) {
7877  g = dgradient + i*nx;
7878  peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
7879 
7880  /* I thought this would be required, but apparently I was wrong.
7881  * Check twice... */
7882  if (peaks[i])
7883  cpl_vector_subtract_scalar(peaks[i], 0.5);
7884 
7885  }
7886 
7887  cpl_image_delete(sgradient);
7888 
7889 
7890  /*
7891  * Tracing the flat field spectra edges, starting from the
7892  * slits positions obtained at reference wavelength. The
7893  * gradient maximum closest to each slits ends is found and
7894  * accepted only within a given search radius:
7895  */
7896 
7897  sradius = 5.0; /* Pixel */
7898 
7899  /*
7900  * The tracing proceeds along the processed gradient image rows,
7901  * above and below the start position, finding the closest peak
7902  * to the previous obtained position, accepting the new position
7903  * only if it is closer than a given tolerance:
7904  */
7905 
7906  tolerance = 0.9; /* Pixel */
7907 
7908  /*
7909  * The trace is attempted for a certain number of pixels above
7910  * and below the position of the reference wavelength:
7911  */
7912 
7913  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
7914  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
7915 
7916 
7917  /*
7918  * Prepare the structure of the output table:
7919  */
7920 
7921  nrows = (ny-1)/step + 1;
7922  traces = cpl_table_new(nrows);
7923  cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
7924  cpl_table_set_column_unit(traces, "x", "pixel");
7925  for (i = 0, j = 0; i < ny; i += step, j++)
7926  cpl_table_set(traces, "x", j, i);
7927 
7928  for (i = 0; i < nslits; i++) {
7929 
7930  /*
7931  * Find the closest processed gradient image row
7932  */
7933 
7934  ypos = ytop[i];
7935 
7936  if (ypos < 0)
7937  ypos = 0;
7938  if (ypos >= ny)
7939  ypos = ny - 1;
7940 
7941  pos = ypos / step;
7942  pos *= step;
7943 
7944  /*
7945  * Find the peak in that row that is closest to xtop[i]
7946  */
7947 
7948  if (peaks[pos]) {
7949  peak = cpl_vector_get_data(peaks[pos]);
7950  npeaks = cpl_vector_get_size(peaks[pos]);
7951 
7952  min = fabs(peak[0] - xtop[i]);
7953  minpos = 0;
7954  for (j = 1; j < npeaks; j++) {
7955  dist = fabs(peak[j] - xtop[i]);
7956  if (min > dist) {
7957  min = dist;
7958  minpos = j;
7959  }
7960  }
7961  }
7962  else {
7963  npeaks = 0;
7964  }
7965 
7966  snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
7967  cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
7968 
7969  if (min > sradius || npeaks == 0) {
7970  cpl_msg_warning(func, "Cannot find spectrum edge for "
7971  "top (or left) end of slit %d", slit_id[i]);
7972  }
7973  else {
7974 
7975  /*
7976  * Add to output table the start y position. Note that
7977  * y positions are written in coordinates of the unrotated
7978  * image, i.e., with horizontal dispersion. Currently nx
7979  * is the x size of the temporarily rotated image (for
7980  * faster memory access), but it has the sense of a y size.
7981  */
7982 
7983  cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
7984  start_y = peak[minpos];
7985 
7986  /*
7987  * Perform the tracing of current edge. Above:
7988  */
7989 
7990  prev_y = start_y;
7991 
7992  for (j = pos + step; j < ny; j += step) {
7993  if (j - pos > pixel_above)
7994  break;
7995  if (peaks[j]) {
7996  peak = cpl_vector_get_data(peaks[j]);
7997  npeaks = cpl_vector_get_size(peaks[j]);
7998  min = fabs(peak[0] - prev_y);
7999  minpos = 0;
8000  for (k = 1; k < npeaks; k++) {
8001  dist = fabs(peak[k] - prev_y);
8002  if (min > dist) {
8003  min = dist;
8004  minpos = k;
8005  }
8006  }
8007  if (min < tolerance) {
8008  cpl_table_set(traces, trace_id, j/step,
8009  nx - peak[minpos]);
8010  prev_y = peak[minpos];
8011  }
8012  }
8013  }
8014 
8015  /*
8016  * Perform the tracing of current edge. Below:
8017  */
8018 
8019  prev_y = start_y;
8020 
8021  for (j = pos - step; j >= 0; j -= step) {
8022  if (pos - j > pixel_below)
8023  break;
8024  if (peaks[j]) {
8025  peak = cpl_vector_get_data(peaks[j]);
8026  npeaks = cpl_vector_get_size(peaks[j]);
8027  min = fabs(peak[0] - prev_y);
8028  minpos = 0;
8029  for (k = 1; k < npeaks; k++) {
8030  dist = fabs(peak[k] - prev_y);
8031  if (min > dist) {
8032  min = dist;
8033  minpos = k;
8034  }
8035  }
8036  if (min < tolerance) {
8037  cpl_table_set(traces, trace_id, j/step,
8038  nx - peak[minpos]);
8039  prev_y = peak[minpos];
8040  }
8041  }
8042  }
8043  }
8044 
8045 
8046  /*
8047  * Find the peak in that row that is closest to xbottom[i]
8048  */
8049 
8050  if (peaks[pos]) {
8051  peak = cpl_vector_get_data(peaks[pos]);
8052  npeaks = cpl_vector_get_size(peaks[pos]);
8053 
8054  min = fabs(peak[0] - xbottom[i]);
8055  minpos = 0;
8056  for (j = 1; j < npeaks; j++) {
8057  dist = fabs(peak[j] - xbottom[i]);
8058  if (min > dist) {
8059  min = dist;
8060  minpos = j;
8061  }
8062  }
8063  }
8064  else {
8065  npeaks = 0;
8066  }
8067 
8068  snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
8069  cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
8070 
8071  if (min > sradius || npeaks == 0) {
8072  cpl_msg_warning(func, "Cannot find spectrum edge for "
8073  "bottom (or right) end of slit %d", slit_id[i]);
8074  }
8075  else {
8076 
8077  cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
8078  start_y = peak[minpos];
8079 
8080  /*
8081  * Perform the tracing of current edge. Above:
8082  */
8083 
8084  prev_y = start_y;
8085 
8086  for (j = pos + step; j < ny; j += step) {
8087  if (j - pos > pixel_above)
8088  break;
8089  if (peaks[j]) {
8090  peak = cpl_vector_get_data(peaks[j]);
8091  npeaks = cpl_vector_get_size(peaks[j]);
8092  min = fabs(peak[0] - prev_y);
8093  minpos = 0;
8094  for (k = 1; k < npeaks; k++) {
8095  dist = fabs(peak[k] - prev_y);
8096  if (min > dist) {
8097  min = dist;
8098  minpos = k;
8099  }
8100  }
8101  if (min < tolerance) {
8102  cpl_table_set(traces, trace_id, j/step,
8103  nx - peak[minpos]);
8104  prev_y = peak[minpos];
8105  }
8106  }
8107  }
8108 
8109  /*
8110  * Perform the tracing of current edge. Below:
8111  */
8112 
8113  prev_y = start_y;
8114 
8115  for (j = pos - step; j >= 0; j -= step) {
8116  if (pos - j > pixel_below)
8117  break;
8118  if (peaks[j]) {
8119  peak = cpl_vector_get_data(peaks[j]);
8120  npeaks = cpl_vector_get_size(peaks[j]);
8121  min = fabs(peak[0] - prev_y);
8122  minpos = 0;
8123  for (k = 1; k < npeaks; k++) {
8124  dist = fabs(peak[k] - prev_y);
8125  if (min > dist) {
8126  min = dist;
8127  minpos = k;
8128  }
8129  }
8130  if (min < tolerance) {
8131  cpl_table_set(traces, trace_id, j/step,
8132  nx - peak[minpos]);
8133  prev_y = peak[minpos];
8134  }
8135  }
8136  }
8137  }
8138 
8139  } /* End of loop on slits */
8140 
8141  for (i = 0; i < ny; i += step)
8142  cpl_vector_delete(peaks[i]);
8143  cpl_free(peaks);
8144 
8145  /*
8146  * Restore original orientation of slits positions table
8147  */
8148 
8149  mos_rotate_slits(slits, -1, ny, nx);
8150 
8151  return traces;
8152 
8153 }
8154 
8155 
8176 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
8177 {
8178  const char *func = "mos_poly_trace";
8179 
8180  cpl_table *polytraces;
8181  cpl_table *dummy;
8182  cpl_vector *x;
8183  cpl_vector *trace;
8184  cpl_polynomial *polytrace;
8185  char trace_id[MAX_COLNAME];
8186  char trace_res[MAX_COLNAME];
8187  char trace_mod[MAX_COLNAME];
8188  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8189  /* Max order is 5 */
8190  double *xdata;
8191  int *slit_id;
8192  int nslits;
8193  int nrows;
8194  int npoints;
8195  int i, j;
8196  cpl_size k;
8197 
8198 
8199  if (traces == NULL || slits == NULL) {
8200  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8201  return NULL;
8202  }
8203 
8204  if (order > 5) {
8205  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8206  return NULL;
8207  }
8208 
8209  nrows = cpl_table_get_nrow(traces);
8210  xdata = cpl_table_get_data_double(traces, "x");
8211  nslits = cpl_table_get_nrow(slits);
8212  slit_id = cpl_table_get_data_int(slits, "slit_id");
8213 
8214  polytraces = cpl_table_new(2*nslits);
8215  cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
8216  for (i = 0; i <= order; i++)
8217  cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
8218 
8219  for (i = 0; i < nslits; i++) {
8220  for (j = 0; j < 2; j++) { /* For top and bottom trace of each slit */
8221 
8222  if (j) {
8223  snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
8224  snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
8225  snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
8226  }
8227  else {
8228  snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
8229  snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
8230  snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
8231  }
8232 
8233  cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
8234 
8235  /*
8236  * The "dummy" table is just a tool for eliminating invalid
8237  * points from the vectors to be fitted.
8238  */
8239 
8240  dummy = cpl_table_new(nrows);
8241  cpl_table_duplicate_column(dummy, "x", traces, "x");
8242  cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
8243  npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
8244  if (npoints < 2 * order) {
8245  cpl_table_delete(dummy);
8246  continue;
8247  }
8248  cpl_table_erase_invalid(dummy);
8249  x = cpl_vector_wrap(npoints,
8250  cpl_table_get_data_double(dummy, "x"));
8251  trace = cpl_vector_wrap(npoints,
8252  cpl_table_get_data_double(dummy, trace_id));
8253  polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
8254  cpl_vector_unwrap(x);
8255  cpl_vector_unwrap(trace);
8256  cpl_table_delete(dummy);
8257 
8258  /*
8259  * Screen bad solutions. At the moment, a primitive screening
8260  * consists in excluding solutions displaying excessive
8261  * curvature (larger than 1E-5 / pixel)
8262  */
8263 
8264  k = 2;
8265  if (fabs(cpl_polynomial_get_coeff(polytrace, &k)) > 1.E-4) {
8266  cpl_polynomial_delete(polytrace);
8267  cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
8268  cpl_table_duplicate_column(traces, trace_res, traces,
8269  trace_mod);
8270  if (j)
8271  cpl_msg_warning(func, "Exclude bad curvature solution "
8272  "for bottom (right) edge of slit %d", slit_id[i]);
8273  else
8274  cpl_msg_warning(func, "Exclude bad curvature solution "
8275  "for top (left) edge of slit %d", slit_id[i]);
8276  continue;
8277  }
8278 
8279  /*
8280  * Write polynomial coefficients to the output table,
8281  * tagged with the appropriate slit_id
8282  */
8283 
8284  for (k = 0; k <= order; k++)
8285  cpl_table_set_double(polytraces, clab[k], 2*i+j,
8286  cpl_polynomial_get_coeff(polytrace, &k));
8287 
8288  /*
8289  * Add column of residuals to input traces table
8290  */
8291 
8292  cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
8293  cpl_table_set_column_unit(traces, trace_mod, "pixel");
8294 
8295  for (k = 0; k < nrows; k++) {
8296  cpl_table_set_double(traces, trace_mod, k,
8297  cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
8298  }
8299 
8300  cpl_polynomial_delete(polytrace);
8301 
8302  cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
8303  cpl_table_subtract_columns(traces, trace_res, trace_id);
8304  cpl_table_multiply_scalar(traces, trace_res, -1.0);
8305 
8306  }
8307  }
8308 
8309  return polytraces;
8310 
8311 }
8312 
8313 
8337 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
8338  int mode)
8339 {
8340  const char *func = "mos_global_trace";
8341 
8342  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8343  /* Max order is 5 */
8344  cpl_table *table;
8345  cpl_vector *c0;
8346  cpl_vector *cn;
8347  cpl_bivector *list;
8348 /* alternative (not robust)
8349  cpl_polynomial *poly;
8350 */
8351 
8352  double *offset;
8353  double rms, q, m;
8354 
8355  int order, nrows, nslits;
8356  int i, j;
8357 
8358 
8359  if (polytraces == NULL) {
8360  cpl_msg_error(func, "Missing spectral curvature table");
8361  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8362  }
8363 
8364  if (slits == NULL) {
8365  cpl_msg_error(func, "Missing slits positions table");
8366  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8367  }
8368 
8369  nslits = cpl_table_get_nrow(slits);
8370 
8371  table = cpl_table_duplicate(polytraces);
8372  cpl_table_erase_invalid(table);
8373 
8374  nrows = cpl_table_get_nrow(table);
8375 
8376  if (nrows < 4) {
8377  cpl_msg_warning(func, "Too few successful spectral curvature tracings "
8378  "(%d): the determination of a global curvature model "
8379  "failed", nrows);
8380  return CPL_ERROR_NONE;
8381  }
8382 
8383  order = cpl_table_get_ncol(polytraces) - 2;
8384 
8385  for (i = 0; i <= order; i++) {
8386  if (!cpl_table_has_column(table, clab[i])) {
8387  cpl_msg_error(func, "Wrong spectral curvature table");
8388  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8389  }
8390  }
8391 
8392 
8393  /*
8394  * Fill in advance the missing offset terms
8395  */
8396 
8397  for (i = 0; i < nslits; i++) {
8398  if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
8399  cpl_table_set_double(polytraces, clab[0], 2*i,
8400  cpl_table_get_double(slits, "ytop", i, NULL));
8401  }
8402  if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
8403  cpl_table_set_double(polytraces, clab[0], 2*i+1,
8404  cpl_table_get_double(slits, "ybottom", i, NULL));
8405  }
8406  }
8407 
8408  offset = cpl_table_get_data_double(polytraces, clab[0]);
8409 
8410 
8411  /*
8412  * Fit the global model and modify polytraces table accordingly
8413  */
8414 
8415  c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
8416 
8417  for (i = 1; i <= order; i++) {
8418  cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
8419  list = cpl_bivector_wrap_vectors(c0, cn);
8420  robustLinearFit(list, &q, &m, &rms);
8421 /* alternative (not robust)
8422  poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
8423 */
8424  for (j = 0; j < 2*nslits; j++) {
8425  if (mode == 1)
8426  if (cpl_table_is_valid(polytraces, clab[i], j))
8427  continue;
8428  cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
8429 /* alternative (not robust)
8430  cpl_table_set_double(polytraces, clab[i], j,
8431  cpl_polynomial_eval_1d(poly, offset[j], NULL));
8432 */
8433  }
8434  cpl_bivector_unwrap_vectors(list);
8435 /* alternative (not robust)
8436  cpl_polynomial_delete(poly);
8437 */
8438  cpl_vector_unwrap(cn);
8439  }
8440 
8441  cpl_vector_unwrap(c0);
8442  cpl_table_delete(table);
8443 
8444  return CPL_ERROR_NONE;
8445 
8446 }
8447 
8448 
8518 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
8519  cpl_table *polytraces, double reference,
8520  double blue, double red, double dispersion,
8521  int flux, cpl_image *calibration)
8522 {
8523  const char *func = "mos_spatial_calibration";
8524 
8525  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8526  /* Max order is 5 */
8527  cpl_polynomial *polytop;
8528  cpl_polynomial *polybot;
8529  cpl_image **exslit;
8530  cpl_image *resampled;
8531  float *data;
8532  float *sdata;
8533  float *xdata;
8534  double vtop, vbot, value;
8535  double top, bot;
8536  double coeff;
8537  double ytop, ybot;
8538  double ypos, yfra;
8539  double factor;
8540  int yint, ysize, yprev;
8541  int nslits;
8542  int npseudo;
8543  int *slit_id;
8544  int *length;
8545  int nx, ny;
8546  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
8547  int missing_top, missing_bot;
8548  int null;
8549  int order;
8550  int i, j;
8551  cpl_size k;
8552 
8553  int create_position = 1;
8554 
8555 
8556  if (spectra == NULL || slits == NULL || polytraces == NULL) {
8557  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8558  return NULL;
8559  }
8560 
8561  if (dispersion <= 0.0) {
8562  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8563  return NULL;
8564  }
8565 
8566  if (red - blue < dispersion) {
8567  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8568  return NULL;
8569  }
8570 
8571  nx = cpl_image_get_size_x(spectra);
8572  ny = cpl_image_get_size_y(spectra);
8573  sdata = cpl_image_get_data(spectra);
8574  if (calibration)
8575  data = cpl_image_get_data(calibration);
8576 
8577  if (cpl_table_has_column(slits, "position"))
8578  create_position = 0;
8579 
8580  if (create_position) {
8581  cpl_table_new_column(slits, "position", CPL_TYPE_INT);
8582  cpl_table_new_column(slits, "length", CPL_TYPE_INT);
8583  cpl_table_set_column_unit(slits, "position", "pixel");
8584  cpl_table_set_column_unit(slits, "length", "pixel");
8585  }
8586  else
8587  length = cpl_table_get_data_int(slits, "length");
8588 
8589  nslits = cpl_table_get_nrow(slits);
8590  slit_id = cpl_table_get_data_int(slits, "slit_id");
8591  order = cpl_table_get_ncol(polytraces) - 2;
8592 
8593  /*
8594  * The spatial resampling is performed for a certain number of
8595  * pixels above and below the position of the reference wavelength:
8596  */
8597 
8598  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
8599  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
8600 
8601  exslit = cpl_calloc(nslits, sizeof(cpl_image *));
8602 
8603  for (i = 0; i < nslits; i++) {
8604 
8605  if (create_position == 0)
8606  if (length[i] == 0)
8607  continue;
8608 
8609  /*
8610  * Note that the x coordinate of the reference pixels on the CCD
8611  * is taken arbitrarily at the top end of each slit. This wouldn't
8612  * be entirely correct in case of curved slits, or in presence of
8613  * heavy distortions: in such cases the spatial resampling is
8614  * really performed across a wide range of wavelengths. But
8615  * the lag between top and bottom spectral curvature models
8616  * would introduce even in such cases negligible effects on
8617  * the spectral spatial resampling.
8618  */
8619 
8620  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
8621 
8622  start_pixel = refpixel - pixel_below;
8623  if (start_pixel < 0)
8624  start_pixel = 0;
8625 
8626  end_pixel = refpixel + pixel_above;
8627  if (end_pixel > nx)
8628  end_pixel = nx;
8629 
8630  /*
8631  * Recover from the table of spectral curvature coefficients
8632  * the curvature polynomials.
8633  */
8634 
8635  missing_top = 0;
8636  polytop = cpl_polynomial_new(1);
8637  for (k = 0; k <= order; k++) {
8638  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
8639  if (null) {
8640  cpl_polynomial_delete(polytop);
8641  missing_top = 1;
8642  break;
8643  }
8644  cpl_polynomial_set_coeff(polytop, &k, coeff);
8645  }
8646 
8647  missing_bot = 0;
8648  polybot = cpl_polynomial_new(1);
8649  for (k = 0; k <= order; k++) {
8650  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
8651  if (null) {
8652  cpl_polynomial_delete(polybot);
8653  missing_bot = 1;
8654  break;
8655  }
8656  cpl_polynomial_set_coeff(polybot, &k, coeff);
8657  }
8658 
8659  if (missing_top && missing_bot) {
8660  cpl_msg_warning(func, "Spatial calibration, slit %d was not "
8661  "traced: no extraction!",
8662  slit_id[i]);
8663  continue;
8664  }
8665 
8666  /*
8667  * In case just one of the two edges was not traced, the other
8668  * edge curvature model is duplicated and shifted to the other
8669  * end of the slit: better than nothing!
8670  */
8671 
8672  if (missing_top) {
8673  cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
8674  "the spectral curvature of the lower edge "
8675  "is used instead.", slit_id[i]);
8676  polytop = cpl_polynomial_duplicate(polybot);
8677  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
8678  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
8679  k = 0;
8680  coeff = cpl_polynomial_get_coeff(polybot, &k);
8681  coeff += ytop - ybot;
8682  cpl_polynomial_set_coeff(polytop, &k, coeff);
8683  }
8684 
8685  if (missing_bot) {
8686  cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
8687  "the spectral curvature of the upper edge "
8688  "is used instead.", slit_id[i]);
8689  polybot = cpl_polynomial_duplicate(polytop);
8690  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
8691  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
8692  k = 0;
8693  coeff = cpl_polynomial_get_coeff(polytop, &k);
8694  coeff -= ytop - ybot;
8695  cpl_polynomial_set_coeff(polybot, &k, coeff);
8696  }
8697 
8698  /*
8699  * Allocate image for current extracted slit
8700  */
8701 
8702  top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
8703  bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
8704  npseudo = ceil(top-bot) + 1;
8705 
8706  if (npseudo < 1) {
8707  cpl_polynomial_delete(polytop);
8708  cpl_polynomial_delete(polybot);
8709  cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
8710  slit_id[i]);
8711  continue;
8712  }
8713 
8714  exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
8715  xdata = cpl_image_get_data(exslit[i]);
8716 
8717  /*
8718  * Write interpolated values to slit image.
8719  */
8720 
8721  for (j = start_pixel; j < end_pixel; j++) {
8722  top = cpl_polynomial_eval_1d(polytop, j, NULL);
8723  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
8724  factor = (top-bot)/npseudo;
8725  for (k = 0; k <= npseudo; k++) {
8726  ypos = top - k*factor;
8727  yint = floor(ypos);
8728  yfra = ypos - yint;
8729  if (yint >= 0 && yint < ny-1) {
8730  vtop = sdata[j + nx*yint];
8731  vbot = sdata[j + nx*(yint+1)];
8732 
8733  //This means that the top and bottom traces are crossing,
8734  //which is physically impossible, so let's set it to 0.
8735  if(factor <= 0 )
8736  value = 0;
8737  else if(vtop == FLT_MAX || vbot == FLT_MAX)
8738  value = FLT_MAX;
8739  else
8740  {
8741  value = vtop*(1-yfra) + vbot*yfra;
8742  if (flux)
8743  value *= factor;
8744  }
8745  xdata[j + nx*(npseudo-k)] = value;
8746  if (calibration) {
8747  data[j + nx*yint] = (top-yint)/factor;
8748  if (k) {
8749 
8750  /*
8751  * This is added to recover lost pixels on
8752  * the CCD image (pixels are lost because
8753  * the CCD pixels are less than npseudo+1).
8754  */
8755 
8756  if (yprev - yint > 1) {
8757  data[j + nx*(yint+1)] = (top-yint-1)/factor;
8758  }
8759  }
8760  }
8761  }
8762  yprev = yint;
8763  }
8764  }
8765  cpl_polynomial_delete(polytop);
8766  cpl_polynomial_delete(polybot);
8767  }
8768 
8769  /*
8770  * Now all the slits images are copied to a single image
8771  */
8772 
8773  ysize = 0;
8774  for (i = 0; i < nslits; i++)
8775  if (exslit[i])
8776  ysize += cpl_image_get_size_y(exslit[i]);
8777 
8778  if(ysize == 0)
8779  return NULL;
8780 
8781  resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
8782 
8783  yint = -1;
8784  for (i = 0; i < nslits; i++) {
8785  if (exslit[i]) {
8786  yint += cpl_image_get_size_y(exslit[i]);
8787  cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
8788  if (create_position) {
8789  cpl_table_set_int(slits, "position", i, ysize - yint - 1);
8790  cpl_table_set_int(slits, "length", i,
8791  cpl_image_get_size_y(exslit[i]));
8792  }
8793  cpl_image_delete(exslit[i]);
8794  }
8795  else if (create_position) {
8796  cpl_table_set_int(slits, "position", i, -1);
8797  cpl_table_set_int(slits, "length", i, 0);
8798  }
8799  }
8800 
8801 
8802  /*
8803  * Elimination of non-traced slits from slit position table: we cannot do
8804  * it because we would lose sync with polytraces and other slit-oriented
8805  * tables. COMMENTED OUT.
8806  *
8807 
8808  if (create_position) {
8809 
8810  if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
8811  cpl_table_erase_selected(slits);
8812 
8813  }
8814 
8815  */
8816 
8817  cpl_free(exslit);
8818 
8819  return resampled;
8820 
8821 }
8822 
8823 
8936 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
8937  cpl_vector *lines,
8938  double dispersion, float level,
8939  int sradius, int order,
8940  double reject, double refwave,
8941  double *wavestart, double *waveend,
8942  int *nlines, double *error,
8943  cpl_table *idscoeff,
8944  cpl_image *calibration,
8945  cpl_image *residuals,
8946  cpl_table *restable,
8947  cpl_table *detected_lines)
8948 {
8949 
8950  const char *func = "mos_wavelength_calibration_final";
8951 
8952  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8953  /* Max order is 5 */
8954 
8955  double tolerance = 20.0; /* Probably forever... */
8956  int step = 10; /* Compute restable every "step" rows */
8957 
8958  char name[MAX_COLNAME];
8959 
8960  cpl_image *resampled;
8961  cpl_bivector *peaks_ident;
8962  cpl_vector *wavel;
8963  cpl_vector *peaks;
8964  cpl_polynomial *ids;
8965  cpl_polynomial *lin;
8966  cpl_polynomial *fguess;
8967  cpl_table *coeff;
8968  double ids_err;
8969  double max_disp, min_disp;
8970  double *line;
8971  double firstLambda, lastLambda, lambda;
8972  double wave, pixe, value;
8973  double c;
8974  float *sdata;
8975  float *rdata;
8976  float *idata;
8977  float *ddata;
8978  float v1, v2, vi;
8979  float fpixel;
8980  int *length;
8981  int pixstart, pixend;
8982  int row_top, row_bot;
8983  int extrapolation;
8984  int nref;
8985  int nslits;
8986  int nfits;
8987  int nl, nx, ny, pixel;
8988  int countLines, usedLines;
8989  int uorder;
8990  int missing;
8991  int null;
8992  int width, uradius;
8993  int i, j, s;
8994  cpl_size k;
8995 
8996 
8997  if (dispersion == 0.0) {
8998  cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
8999  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9000  return NULL;
9001  }
9002 
9003  if (dispersion < 0.0) {
9004  cpl_msg_error(func, "The expected dispersion must be positive");
9005  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9006  return NULL;
9007  }
9008 
9009  if (idscoeff == NULL) {
9010  cpl_msg_error(func, "A preallocated IDS coeff table must be given");
9011  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9012  return NULL;
9013  }
9014 
9015  max_disp = dispersion + dispersion * tolerance / 100;
9016  min_disp = dispersion - dispersion * tolerance / 100;
9017 
9018  if (order < 1) {
9019  cpl_msg_error(func, "The order of the fitting polynomial "
9020  "must be at least 1");
9021  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9022  return NULL;
9023  }
9024 
9025  if (image == NULL || lines == NULL) {
9026  cpl_msg_error(func, "Both spectral exposure and reference line "
9027  "catalog are required in input");
9028  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9029  return NULL;
9030  }
9031 
9032  nx = cpl_image_get_size_x(image);
9033  ny = cpl_image_get_size_y(image);
9034  sdata = cpl_image_get_data_float(image);
9035 
9036  nref = cpl_vector_get_size(lines);
9037  line = cpl_vector_get_data(lines);
9038 
9039  if (*wavestart < 1.0 && *waveend < 1.0) {
9040  firstLambda = line[0];
9041  lastLambda = line[nref-1];
9042  extrapolation = (lastLambda - firstLambda) / 10;
9043  firstLambda -= extrapolation;
9044  lastLambda += extrapolation;
9045  *wavestart = firstLambda;
9046  *waveend = lastLambda;
9047  }
9048  else {
9049  firstLambda = *wavestart;
9050  lastLambda = *waveend;
9051  }
9052 
9053  nl = (lastLambda - firstLambda) / dispersion;
9054  resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
9055  rdata = cpl_image_get_data_float(resampled);
9056 
9057  /*
9058  * Allocate total output table of IDS coefficients
9059  */
9060 
9061  for (j = 0; j <= order; j++)
9062  cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
9063 
9064  if (calibration)
9065  idata = cpl_image_get_data_float(calibration);
9066 
9067  if (residuals)
9068  ddata = cpl_image_get_data_float(residuals);
9069 
9070  if (restable) {
9071  cpl_table_set_size(restable, nref);
9072  cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
9073  cpl_table_copy_data_double(restable, "wavelength", line);
9074  for (i = 0; i < ny; i += step) {
9075  snprintf(name, MAX_COLNAME, "r%d", i);
9076  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
9077  snprintf(name, MAX_COLNAME, "d%d", i);
9078  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
9079  snprintf(name, MAX_COLNAME, "p%d", i);
9080  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
9081  }
9082  }
9083 
9084  if (detected_lines) {
9085  cpl_table_set_size(detected_lines, 0);
9086  cpl_table_new_column(detected_lines, "slit_id", CPL_TYPE_INT);
9087  cpl_table_new_column(detected_lines, "xpos_rectified", CPL_TYPE_DOUBLE);
9088  cpl_table_new_column(detected_lines, "ypos_rectified", CPL_TYPE_DOUBLE);
9089  cpl_table_new_column(detected_lines, "xpos_rectified_iter", CPL_TYPE_DOUBLE);
9090  cpl_table_new_column(detected_lines, "ypos_rectified_iter", CPL_TYPE_DOUBLE);
9091  cpl_table_new_column(detected_lines, "peak_flux", CPL_TYPE_DOUBLE);
9092  cpl_table_new_column(detected_lines, "wave_ident", CPL_TYPE_DOUBLE);
9093  cpl_table_new_column(detected_lines, "wave_ident_iter", CPL_TYPE_DOUBLE);
9094  cpl_table_new_column(detected_lines, "xpos_fit_rect_wavecal", CPL_TYPE_DOUBLE);
9095  cpl_table_new_column(detected_lines, "res_xpos", CPL_TYPE_DOUBLE);
9096  cpl_table_new_column(detected_lines, "fit_used", CPL_TYPE_INT);
9097  }
9098 
9099 
9100  /*
9101  * Process all slits separately.
9102  */
9103 
9104  nslits = cpl_table_get_nrow(slits);
9105  length = cpl_table_get_data_int(slits, "length");
9106 
9107  row_top = ny;
9108  for (s = 0; s < nslits; s++) {
9109 
9110  int slit_id;
9111  slit_id = cpl_table_get_int(slits, "slit_id", s, NULL);
9112 
9113  if (length[s] == 0)
9114  continue;
9115 
9116  /*
9117  * row_top and row_bot define the boundaries of the current slit.
9118  * Here we begin (arbitrarily...) from the top slit.
9119  */
9120 
9121  row_bot = cpl_table_get_int(slits, "position", s, NULL);
9122 
9123  if (sradius > 0) {
9124 
9125  /*
9126  * If a search radius was defined, allocate the table of
9127  * the fitting polynomials coefficients. This table is
9128  * just used to generate the first-guess polynomial made
9129  * of the median coefficients of all polynomials found
9130  * for this slit.
9131  */
9132 
9133  coeff = cpl_table_new(row_top - row_bot);
9134  for (j = 0; j <= order; j++)
9135  cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
9136  }
9137 
9138  /*
9139  * Here is the loop on all rows of the current slit. They are
9140  * wavelength calibrated one by one.
9141  */
9142 
9143  for (i = row_bot; i < row_top; i++) {
9144  width = mos_lines_width(sdata + i*nx, nx);
9145  if (width < 5)
9146  width = 5;
9147  peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
9148  if (peaks) {
9149  peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
9150  }
9151  if (peaks) {
9152  int keep_multiplex = mos_multiplex;
9153  mos_multiplex = -1;
9154  if(detected_lines)
9155  {
9156  cpl_size newlines = cpl_vector_get_size(peaks);
9157  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
9158  cpl_table_set_size(detected_lines, oldsize + newlines);
9159  for(cpl_size iline = 0; iline < newlines; ++iline)
9160  {
9161  cpl_table_set_int(detected_lines, "slit_id",
9162  oldsize + iline, slit_id);
9163  cpl_table_set_double(detected_lines, "xpos_rectified",
9164  oldsize + iline, cpl_vector_get(peaks, iline) + 1);
9165  cpl_table_set_double(detected_lines, "ypos_rectified",
9166  oldsize + iline, (double)i + 1);
9167  cpl_table_set_double(detected_lines, "peak_flux",
9168  oldsize + iline,
9169  sdata[i*nx+(int)(cpl_vector_get(peaks, iline)+0.5)]);
9170  cpl_table_set_int(detected_lines,
9171  "fit_used",
9172  oldsize + iline, 0);
9173  }
9174  }
9175  peaks_ident = mos_identify_peaks(peaks, lines,
9176  min_disp, max_disp, 0.05);
9177  mos_multiplex = keep_multiplex;
9178  if (peaks_ident) {
9179  cpl_bivector * peaks_ident_used_fit;
9180  countLines = cpl_bivector_get_size(peaks_ident);
9181  if (countLines < 4) {
9182  cpl_bivector_delete(peaks_ident);
9183  cpl_vector_delete(peaks);
9184  if (nlines)
9185  nlines[i] = 0;
9186  if (error)
9187  error[i] = 0.0;
9188  continue;
9189  }
9190 
9191  /*
9192  * Set reference wavelength as zero point
9193  */
9194 
9195  wavel = cpl_bivector_get_y(peaks_ident);
9196  cpl_vector_subtract_scalar(wavel, refwave);
9197 
9198  uorder = countLines / 2 - 1;
9199  if (uorder > order)
9200  uorder = order;
9201 
9202  ids = mos_poly_wav2pix(peaks_ident, uorder, reject,
9203  2 * (uorder + 1), &usedLines,
9204  &ids_err, &peaks_ident_used_fit);
9205 
9206  if (ids == NULL) {
9207  cpl_bivector_delete(peaks_ident);
9208  cpl_vector_delete(peaks);
9209  if (nlines)
9210  nlines[i] = 0;
9211  if (error)
9212  error[i] = 0.0;
9213  cpl_error_reset();
9214  continue;
9215  }
9216 
9217  if (sradius > 0) {
9218  for (k = 0; k <= order; k++) {
9219  if (k > uorder) {
9220  cpl_table_set_double(coeff, clab[k],
9221  i - row_bot, 0.0);
9222  }
9223  else {
9224  cpl_table_set_double(coeff, clab[k],
9225  i - row_bot, cpl_polynomial_get_coeff(ids, &k));
9226  }
9227  }
9228  }
9229  /* else { */
9230  if (calibration) {
9231  pixstart = cpl_polynomial_eval_1d(ids,
9232  cpl_bivector_get_y_data(peaks_ident)[0],
9233  NULL);
9234  pixend = cpl_polynomial_eval_1d(ids,
9235  cpl_bivector_get_y_data(peaks_ident)[countLines-1],
9236  NULL);
9237  extrapolation = (pixend - pixstart) / 5;
9238  pixstart -= extrapolation;
9239  pixend += extrapolation;
9240  if (pixstart < 0)
9241  pixstart = 0;
9242  if (pixend > nx)
9243  pixend = nx;
9244 
9245  for (j = pixstart; j < pixend; j++) {
9246  (idata + i*nx)[j] = mos_eval_dds(ids,
9247  firstLambda, lastLambda, refwave, j);
9248  }
9249  }
9250 
9251  /*
9252  * Residuals image
9253  */
9254 
9255  if (residuals || (restable && !(i%step))) {
9256  if (restable && !(i%step)) {
9257  lin = cpl_polynomial_new(1);
9258  for (k = 0; k < 2; k++)
9259  cpl_polynomial_set_coeff(lin, &k,
9260  cpl_polynomial_get_coeff(ids, &k));
9261  }
9262  for (j = 0; j < countLines; j++) {
9263  pixe = cpl_bivector_get_x_data(peaks_ident)[j];
9264  wave = cpl_bivector_get_y_data(peaks_ident)[j];
9265  value = pixe
9266  - cpl_polynomial_eval_1d(ids, wave, NULL);
9267  if (residuals) {
9268  pixel = pixe + 0.5;
9269  (ddata + i*nx)[pixel] = value;
9270  }
9271  if (restable && !(i%step)) {
9272  for (k = 0; k < nref; k++) {
9273  if (fabs(line[k]-refwave-wave) < 0.1) {
9274  snprintf(name, MAX_COLNAME,
9275  "r%d", i);
9276  cpl_table_set_double(restable, name,
9277  k, value);
9278  value = pixe
9279  - cpl_polynomial_eval_1d(lin,
9280  wave, NULL);
9281  snprintf(name, MAX_COLNAME,
9282  "d%d", i);
9283  cpl_table_set_double(restable, name,
9284  k, value);
9285  snprintf(name, MAX_COLNAME,
9286  "p%d", i);
9287  cpl_table_set_double(restable, name,
9288  k, pixe);
9289  break;
9290  }
9291  }
9292  }
9293  }
9294  if (restable && !(i%step)) {
9295  cpl_polynomial_delete(lin);
9296  }
9297 /***
9298  for (j = 0; j < countLines; j++) {
9299  pixel = cpl_bivector_get_x_data(output)[j]
9300  + 0.5;
9301  (ddata + i*nx)[pixel] =
9302  cpl_bivector_get_x_data(output)[j]
9303  - cpl_polynomial_eval_1d(ids,
9304  cpl_bivector_get_y_data(output)[j],
9305  NULL);
9306  }
9307 ***/
9308  //Fill the line identification information in
9309  //the detected_lines table
9310  if(detected_lines)
9311  {
9312  cpl_size nidentlines = cpl_bivector_get_size(peaks_ident);
9313  cpl_size ndetectlines = cpl_vector_get_size(peaks);
9314  cpl_size totalsize = cpl_table_get_nrow(detected_lines);
9315  for(cpl_size idline = 0; idline < nidentlines; ++idline)
9316  {
9317  for(cpl_size detline = 0; detline < ndetectlines; ++detline)
9318  {
9319  if(cpl_vector_get(peaks, detline) ==
9320  cpl_bivector_get_x_data(peaks_ident)[idline])
9321  {
9322  cpl_size table_pos = totalsize - ndetectlines + detline;
9323  double wave_ident = cpl_bivector_get_y_data(peaks_ident)[idline] + refwave;
9324  double xpix_fit = cpl_polynomial_eval_1d(ids,
9325  wave_ident - refwave, NULL);
9326  double xpos_det = cpl_table_get_double(detected_lines,
9327  "xpos_rectified",
9328  table_pos, &null);
9329  cpl_table_set_double(detected_lines,
9330  "wave_ident",
9331  table_pos,
9332  wave_ident);
9333  cpl_table_set_double(detected_lines,
9334  "xpos_fit_rect_wavecal",
9335  table_pos,
9336  xpix_fit + 1);
9337  cpl_table_set_double(detected_lines,
9338  "res_xpos",
9339  table_pos,
9340  xpos_det - xpix_fit - 1);
9341  cpl_table_set_int(detected_lines,
9342  "fit_used",
9343  table_pos, 0);
9344  for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
9345  {
9346  if(cpl_bivector_get_x_data(peaks_ident)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
9347  cpl_table_set_int(detected_lines,
9348  "fit_used",
9349  table_pos, 1);
9350  }
9351  }
9352  }
9353  }
9354  }
9355 
9356  }
9357  /* } */
9358 
9359  /*
9360  * Write it anyway, even in case a first-guess based
9361  * solution will be searched afterwards: in case of
9362  * failure, the "blind" solution is kept.
9363  */
9364 
9365  if (nlines)
9366  nlines[i] = usedLines;
9367  if (error)
9368  error[i] = ids_err / sqrt(usedLines/(uorder + 1));
9369 
9370  for (k = 0; k <= order; k++) {
9371  if (k > uorder) {
9372  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
9373  }
9374  else {
9375  cpl_table_set_double(idscoeff, clab[k], i,
9376  cpl_polynomial_get_coeff(ids, &k));
9377  }
9378  }
9379 
9380  cpl_polynomial_delete(ids);
9381  cpl_bivector_delete(peaks_ident);
9382  }
9383  cpl_vector_delete(peaks);
9384  }
9385  } /* End of loop on current slit's rows */
9386 
9387 
9388  if (sradius > 0) {
9389 
9390  /*
9391  * See whether there are valid fits at all...
9392  */
9393 
9394  nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
9395 
9396  if (nfits) {
9397  int slope = 0;
9398 
9399  fguess = cpl_polynomial_new(1);
9400 
9401  if (mos_interpolate_wavecalib_mos(coeff, 2, 1)) {
9402 
9403  slope = 0;
9404 
9405  /*
9406  * Compute a median IDS polynomial for the current slit
9407  */
9408 
9409  for (k = 0; k <= order; k++) {
9410  c = cpl_table_get_column_median(coeff, clab[k]);
9411  cpl_polynomial_set_coeff(fguess, &k, c);
9412  }
9413  }
9414  else {
9415  slope = 1;
9416  }
9417 
9418  for (i = row_bot; i < row_top; i++) {
9419  cpl_bivector * peaks_ident_used_fit;
9420 
9421  /*
9422  * Use first-guess to find the reference lines again
9423  */
9424 
9425  width = mos_lines_width(sdata + i*nx, nx);
9426  if (width > sradius) {
9427  uradius = width;
9428  }
9429  else {
9430  uradius = sradius;
9431  }
9432 
9433  if (slope) {
9434  for (k = 0; k <= order; k++) {
9435  c = cpl_table_get_double(coeff, clab[k],
9436  i - row_bot, NULL);
9437  cpl_polynomial_set_coeff(fguess, &k, c);
9438  }
9439  }
9440 
9441  peaks_ident = mos_find_peaks(sdata + i*nx, nx, lines,
9442  fguess, refwave, uradius);
9443 
9444  if (peaks_ident == NULL) {
9445  cpl_error_reset();
9446  continue;
9447  }
9448 
9449  countLines = cpl_bivector_get_size(peaks_ident);
9450 
9451  if (countLines < 4) {
9452  cpl_bivector_delete(peaks_ident);
9453  continue;
9454  }
9455 
9456  /*
9457  * Set reference wavelength as zero point
9458  */
9459 
9460  wavel = cpl_bivector_get_y(peaks_ident);
9461  cpl_vector_subtract_scalar(wavel, refwave);
9462 
9463  uorder = countLines / 2 - 1;
9464  if (uorder > order)
9465  uorder = order;
9466 
9467  ids = mos_poly_wav2pix(peaks_ident, uorder, reject,
9468  2 * (uorder + 1), &usedLines,
9469  &ids_err, &peaks_ident_used_fit);
9470 
9471  if (ids == NULL) {
9472  cpl_error_reset();
9473  cpl_bivector_delete(peaks_ident);
9474  continue;
9475  }
9476 
9477  if (nlines)
9478  nlines[i] = usedLines;
9479  if (error)
9480  error[i] = ids_err / sqrt(usedLines/(uorder + 1));
9481 
9482  if (calibration) {
9483  pixstart = cpl_polynomial_eval_1d(ids,
9484  cpl_bivector_get_y_data(peaks_ident)[0],
9485  NULL);
9486  pixend = cpl_polynomial_eval_1d(ids,
9487  cpl_bivector_get_y_data(peaks_ident)[countLines-1],
9488  NULL);
9489  extrapolation = (pixend - pixstart) / 5;
9490  pixstart -= extrapolation;
9491  pixend += extrapolation;
9492  if (pixstart < 0)
9493  pixstart = 0;
9494  if (pixend > nx)
9495  pixend = nx;
9496 
9497  for (j = pixstart; j < pixend; j++) {
9498  (idata + i*nx)[j] = mos_eval_dds(ids,
9499  firstLambda, lastLambda, refwave, j);
9500  }
9501  }
9502 
9503  /*
9504  * Residuals image
9505  */
9506 
9507  if (residuals || (restable && !(i%step))) {
9508  if (restable && !(i%step)) {
9509  lin = cpl_polynomial_new(1);
9510  for (k = 0; k < 2; k++)
9511  cpl_polynomial_set_coeff(lin, &k,
9512  cpl_polynomial_get_coeff(ids, &k));
9513  }
9514  for (j = 0; j < countLines; j++) {
9515  pixe = cpl_bivector_get_x_data(peaks_ident)[j];
9516  wave = cpl_bivector_get_y_data(peaks_ident)[j];
9517  value = pixe
9518  - cpl_polynomial_eval_1d(ids, wave, NULL);
9519  if (residuals) {
9520  pixel = pixe + 0.5;
9521  (ddata + i*nx)[pixel] = value;
9522  }
9523  if (restable && !(i%step)) {
9524  for (k = 0; k < nref; k++) {
9525  if (fabs(line[k]-refwave-wave) < 0.1) {
9526  snprintf(name, MAX_COLNAME,
9527  "r%d", i);
9528  cpl_table_set_double(restable, name,
9529  k, value);
9530  value = pixe
9531  - cpl_polynomial_eval_1d(lin,
9532  wave, NULL);
9533  snprintf(name, MAX_COLNAME,
9534  "d%d", i);
9535  cpl_table_set_double(restable, name,
9536  k, value);
9537  snprintf(name, MAX_COLNAME,
9538  "p%d", i);
9539  cpl_table_set_double(restable, name,
9540  k, pixe);
9541  break;
9542  }
9543  }
9544  }
9545  }
9546  if (restable && !(i%step)) {
9547  cpl_polynomial_delete(lin);
9548  }
9549 /***
9550  for (j = 0; j < countLines; j++) {
9551  pixel = cpl_bivector_get_x_data(output)[j]
9552  + 0.5;
9553  (ddata + i*nx)[pixel] =
9554  cpl_bivector_get_x_data(output)[j]
9555  - cpl_polynomial_eval_1d(ids,
9556  cpl_bivector_get_y_data(output)[j],
9557  NULL);
9558  }
9559 ***/
9560  }
9561 
9562  if(detected_lines)
9563  {
9564  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
9565  cpl_size nidentlines = cpl_bivector_get_size(peaks_ident);
9566  cpl_table_set_size(detected_lines, oldsize + nidentlines);
9567  for(cpl_size idline = 0; idline < nidentlines ; ++idline)
9568  {
9569  double wave_ident = cpl_bivector_get_y_data(peaks_ident)[idline] + refwave;
9570  double xpix_fit = cpl_polynomial_eval_1d(ids,
9571  wave_ident - refwave, NULL);
9572  cpl_table_set_int(detected_lines, "slit_id",
9573  oldsize + idline, slit_id);
9574  cpl_table_set_double(detected_lines, "xpos_rectified_iter",
9575  oldsize + idline, cpl_bivector_get_x_data(peaks_ident)[idline] + 1);
9576  cpl_table_set_double(detected_lines, "ypos_rectified_iter",
9577  oldsize + idline, (double)i + 1);
9578  cpl_table_set_double(detected_lines, "peak_flux",
9579  oldsize + idline,
9580  sdata[i*nx+(int)(cpl_bivector_get_x_data(peaks_ident)[idline]+0.5)]);
9581  cpl_table_set_double(detected_lines, "wave_ident_iter",
9582  oldsize + idline, wave_ident);
9583  cpl_table_set_double(detected_lines, "xpos_fit_rect_wavecal",
9584  oldsize + idline, xpix_fit + 1);
9585  cpl_table_set_int(detected_lines,
9586  "fit_used",
9587  oldsize + idline, 0);
9588  for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
9589  {
9590  if(cpl_bivector_get_x_data(peaks_ident)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
9591  cpl_table_set_int(detected_lines,
9592  "fit_used",
9593  oldsize + idline, 1);
9594  }
9595  }
9596  }
9597 
9598 
9599  for (k = 0; k <= order; k++) {
9600  if (k > uorder) {
9601  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
9602  }
9603  else {
9604  cpl_table_set_double(idscoeff, clab[k], i,
9605  cpl_polynomial_get_coeff(ids, &k));
9606  }
9607  }
9608 
9609  cpl_bivector_delete(peaks_ident);
9610  cpl_polynomial_delete(ids);
9611 
9612  } /* End of loop "use ids as a first-guess" */
9613 
9614  cpl_polynomial_delete(fguess);
9615  }
9616 
9617  cpl_table_delete(coeff);
9618 
9619  }
9620 
9621  row_top = row_bot;
9622 
9623  } /* End of loop on slits */
9624 
9625 
9626  /*
9627  * At this point the idscoeff table has been filled with all the
9628  * fits coefficients obtained for all the rows of the input image.
9629  * Now we apply these coefficients to resample the input image
9630  * at constant wavelength step.
9631  */
9632 
9633  for (i = 0; i < ny; i++) {
9634 
9635  missing = 0;
9636  ids = cpl_polynomial_new(1);
9637  for (k = 0; k <= order; k++) {
9638  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
9639  if (null) {
9640  cpl_polynomial_delete(ids);
9641  missing = 1;
9642  break;
9643  }
9644  cpl_polynomial_set_coeff(ids, &k, c);
9645  }
9646  if (missing)
9647  continue;
9648 
9649  pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
9650  pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
9651  if (pixstart < 0)
9652  pixstart = 0;
9653  if (pixend > nx)
9654  pixend = nx;
9655 
9656  /*
9657  * Resampled image:
9658  */
9659 
9660  for (j = 0; j < nl; j++) {
9661  lambda = firstLambda + j * dispersion;
9662  fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
9663  pixel = fpixel;
9664  if (pixel >= 0 && pixel < nx-1) {
9665  v1 = (sdata + i*nx)[pixel];
9666  v2 = (sdata + i*nx)[pixel+1];
9667  vi = v1 + (v2-v1)*(fpixel-pixel);
9668  (rdata + i*nl)[j] = vi;
9669  }
9670  }
9671 
9672  cpl_polynomial_delete(ids);
9673  }
9674 
9675  return resampled;
9676 }
9677 
9678 
9705 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave,
9706  double firstLambda, double lastLambda,
9707  double dispersion, cpl_table *idscoeff,
9708  int flux)
9709 {
9710 
9711  const char *func = "mos_wavelength_calibration";
9712 
9713  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
9714  /* Max order is 5 */
9715 
9716  cpl_image *resampled;
9717  cpl_polynomial *ids;
9718  double pixel_per_lambda;
9719  double lambda;
9720  double c;
9721  float *sdata;
9722  float *rdata;
9723  float v0, v1, v2, v3, vi;
9724  float fpixel;
9725  int order;
9726  int pixstart, pixend;
9727  int nl, nx, ny, pixel;
9728  int missing;
9729  int null;
9730  int i, j;
9731  cpl_size k;
9732 
9733 
9734  if (dispersion <= 0.0) {
9735  cpl_msg_error(func, "The resampling step must be positive");
9736  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9737  return NULL;
9738  }
9739 
9740  if (lastLambda - firstLambda < dispersion) {
9741  cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f",
9742  firstLambda, lastLambda);
9743  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9744  return NULL;
9745  }
9746 
9747  if (idscoeff == NULL) {
9748  cpl_msg_error(func, "An IDS coeff table must be given");
9749  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9750  return NULL;
9751  }
9752 
9753  if (image == NULL) {
9754  cpl_msg_error(func, "A scientific spectral image must be given");
9755  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9756  return NULL;
9757  }
9758 
9759  nx = cpl_image_get_size_x(image);
9760  ny = cpl_image_get_size_y(image);
9761  sdata = cpl_image_get_data_float(image);
9762 
9763  nl = (lastLambda - firstLambda) / dispersion;
9764  resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
9765  rdata = cpl_image_get_data_float(resampled);
9766 
9767  order = 0;
9768  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
9769  ++order;
9770  --order;
9771 
9772  for (i = 0; i < ny; i++) {
9773 
9774  missing = 0;
9775  ids = cpl_polynomial_new(1);
9776  for (k = 0; k <= order; k++) {
9777  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
9778  if (null) {
9779  cpl_polynomial_delete(ids);
9780  missing = 1;
9781  break;
9782  }
9783  cpl_polynomial_set_coeff(ids, &k, c);
9784  }
9785  if (missing)
9786  continue;
9787 
9788  pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
9789  pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
9790  if (pixstart < 0)
9791  pixstart = 0;
9792  if (pixend > nx)
9793  pixend = nx;
9794 
9795  /*
9796  * Resampled image:
9797  */
9798 
9799  for (j = 0; j < nl; j++) {
9800  lambda = firstLambda + j * dispersion;
9801  fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave,
9802  &pixel_per_lambda);
9803 
9804  /*
9805  * The local dispersion is 1 / pixel_per_lambda
9806  * and this factor is used for applying the flux
9807  * conservation correction (if requested).
9808  */
9809 
9810  pixel = fpixel;
9811 
9812  // if (dispersion * pixel_per_lambda < 2.0) {
9813  if (1) { /* Old behaviour: this is safe. */
9814 
9815  /*
9816  * In this case we just sample interpolating the
9817  * signal of nearby pixels.
9818  */
9819 
9820  //the wave calibration should be a monotonically increasing
9821  //function. If the detivative is negative, we are outside of
9822  //the wavelength solution domain.
9823  if(pixel_per_lambda <= 0)
9824  vi = 0;
9825  else if (fpixel < 0)
9826  vi = 0;
9827  else if (pixel >= 1 && pixel < nx-2) {
9828  v0 = (sdata + i*nx)[pixel-1];
9829  v1 = (sdata + i*nx)[pixel];
9830  v2 = (sdata + i*nx)[pixel+1];
9831  v3 = (sdata + i*nx)[pixel+2];
9832  vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
9833  + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
9834  + 2*v1;
9835  vi /= 2;
9836  if (v1 > v2) {
9837  if (vi > v1) {
9838  vi = v1;
9839  }
9840  else if (vi < v2) {
9841  vi = v2;
9842  }
9843  }
9844  else {
9845  if (vi > v2) {
9846  vi = v2;
9847  }
9848  else if (vi < v1) {
9849  vi = v1;
9850  }
9851  }
9852  if (flux)
9853  vi *= dispersion * pixel_per_lambda;
9854  }
9855  else if (pixel >= 0 && pixel < nx-1) {
9856  v1 = (sdata + i*nx)[pixel];
9857  v2 = (sdata + i*nx)[pixel+1];
9858  vi = v1 + (v2-v1)*(fpixel-pixel);
9859  if (flux)
9860  vi *= dispersion * pixel_per_lambda;
9861  }
9862  else
9863  vi = 0;
9864  (rdata + i*nl)[j] = vi;
9865  }
9866  else {
9867 
9868  /*
9869  * Here instead we integrate the pixel values in
9870  * the interval centered at the interpolation point.
9871  * This interval is long dispersion * pixel_per_lambda
9872  * of the original pixels, and is centered at fpixel.
9873  * So it starts at fpixel - dispersion * pixel_per_lambda / 2,
9874  * and it ends at fpixel + dispersion * pixel_per_lambda / 2.
9875  */
9876 
9877  double spos = fpixel - dispersion * pixel_per_lambda / 2;
9878  double epos = fpixel + dispersion * pixel_per_lambda / 2;
9879 
9880  /*
9881  * Brutal sum over all involved pixels
9882  */
9883 
9884  int spix = spos;
9885  int epix = epos + 1;
9886 
9887  if (spix < 0)
9888  spix = 0;
9889 
9890  if (epix > nx)
9891  epix = nx;
9892 
9893  vi = 0.0;
9894  for (k = spix; k < epix; k++) {
9895  if (pixel >= 0 && pixel < nx) {
9896  vi += (sdata + i*nx)[k];
9897  }
9898  }
9899 
9900  /*
9901  * Correct integrated flux by true length
9902  * of interval. This is clearly an approximation,
9903  * but it's good enough if the PSF is much larger
9904  * than the original pix.
9905  */
9906 
9907  vi *= dispersion * pixel_per_lambda / (epix - spix);
9908 
9909  /*
9910  * Flux conservation is a geometric factor that is applied
9911  * always in the same way...
9912  */
9913 
9914  if (flux)
9915  vi *= dispersion * pixel_per_lambda;
9916 
9917  (rdata + i*nl)[j] = vi;
9918  }
9919  }
9920 
9921  cpl_polynomial_delete(ids);
9922  }
9923 
9924  return resampled;
9925 }
9926 
9927 
9994 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits,
9995  double refwave, double firstLambda,
9996  double lastLambda, cpl_table *idscoeff,
9997  cpl_vector *skylines, int highres, int order,
9998  cpl_image *calibration, int sradius)
9999 {
10000  const char *func = "mos_wavelength_align";
10001 
10002  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10003  /* Max order is 5 */
10004  double *line;
10005  double *data;
10006  double expPos, offset;
10007  double c;
10008  double lambda1, lambda2;
10009  double rms;
10010  float pos;
10011  float *sdata;
10012  float *cdata;
10013  int *idata;
10014  int startPos, endPos;
10015  int window = 2*sradius + 1;
10016  int nlines;
10017  int nslits;
10018  int npoints;
10019  int nrows;
10020  int nx, ny;
10021  int xlow, ylow, xhig, yhig;
10022  int idsorder, uorder;
10023  int *slit_id;
10024  int *position;
10025  int *length;
10026  int missing;
10027  int null;
10028  int i;
10029  cpl_size j, k;
10030 
10031  char offname[MAX_COLNAME];
10032  char name[MAX_COLNAME];
10033 
10034  cpl_polynomial *ids;
10035  cpl_polynomial *polycorr;
10036  cpl_image *exslit;
10037  cpl_image *sky;
10038  cpl_table *offsets;
10039  cpl_table *dummy;
10040  cpl_vector *wave;
10041  cpl_vector *offs;
10042 
10043 
10044  if (idscoeff == NULL) {
10045  cpl_msg_error(func, "An IDS coeff table must be given");
10046  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10047  return NULL;
10048  }
10049 
10050  if (image == NULL) {
10051  cpl_msg_error(func, "A scientific spectral image must be given");
10052  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10053  return NULL;
10054  }
10055 
10056  if (slits == NULL) {
10057  cpl_msg_error(func, "A slit position table must be given");
10058  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10059  return NULL;
10060  }
10061 
10062  if (skylines) {
10063  line = cpl_vector_get_data(skylines);
10064  nlines = cpl_vector_get_size(skylines);
10065  }
10066  else {
10067  cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10068  "given: using internal list of reference sky lines");
10069  if (highres) {
10070  line = default_lines_hi;
10071  nlines = sizeof(default_lines_hi) / sizeof(double);
10072  }
10073  else {
10074  line = default_lines_lo;
10075  nlines = sizeof(default_lines_lo) / sizeof(double);
10076  }
10077  }
10078 
10079  if (calibration)
10080  cdata = cpl_image_get_data(calibration);
10081 
10082  nx = cpl_image_get_size_x(image);
10083  ny = cpl_image_get_size_y(image);
10084 
10085  nslits = cpl_table_get_nrow(slits);
10086  slit_id = cpl_table_get_data_int(slits, "slit_id");
10087  position = cpl_table_get_data_int(slits, "position");
10088  length = cpl_table_get_data_int(slits, "length");
10089 
10090 
10091  /*
10092  * Define the output table of offsets
10093  */
10094 
10095  nrows = 0;
10096  for (i = 0; i < nlines; i++)
10097  if (line[i] > firstLambda && line[i] < lastLambda)
10098  nrows++;
10099 
10100  offsets = cpl_table_new(nrows);
10101  cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10102  cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10103 
10104  nrows = 0;
10105  for (i = 0; i < nlines; i++) {
10106  if (line[i] > firstLambda && line[i] < lastLambda) {
10107  cpl_table_set_double(offsets, "wave", nrows, line[i]);
10108  nrows++;
10109  }
10110  }
10111 
10112  /*
10113  * Here "line" is made to point to the new list of selected wavelengths
10114  */
10115 
10116  line = cpl_table_get_data_double(offsets, "wave");
10117  nlines = nrows;
10118 
10119  idsorder = 0;
10120  while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10121  ++idsorder;
10122  --idsorder;
10123 
10124  xlow = 1;
10125  xhig = nx;
10126  for (i = 0; i < nslits; i++) {
10127 
10128  if (length[i] == 0)
10129  continue;
10130 
10131  snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
10132  cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
10133 
10134  /*
10135  * Define the extraction boundaries. We DON'T write:
10136  *
10137  * ylow = position[i];
10138  * yhig = ylow + length[i];
10139  *
10140  * because the cpl_image pixels are counted from 1, and because in
10141  * cpl_image_extract() the coordinates of the last pixel are inclusive.
10142  */
10143 
10144  ylow = position[i] + 1;
10145  yhig = ylow + length[i] - 1;
10146 
10147  exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
10148  sky = cpl_image_collapse_median_create(exslit, 0, 0, 1);
10149  sdata = cpl_image_get_data(sky);
10150 
10151  cpl_image_delete(exslit);
10152 
10153  /*
10154  * Return here to a decent way of counting pixels (i.e., starting
10155  * from 0)
10156  */
10157 
10158  ylow--;
10159 
10160  /*
10161  * Allocate a dummy table for collecting all the offsets
10162  * for all the lines: this is only needed for the computation
10163  * of the median offset for each sky line
10164  */
10165 
10166  dummy = cpl_table_new(yhig - ylow);
10167  for (j = 0; j < nlines; j++) {
10168  snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
10169  cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10170  }
10171 
10172  for (j = ylow; j < yhig; j++) {
10173 
10174  /*
10175  * Get the IDS polynomial for the current slit row
10176  */
10177 
10178  missing = 0;
10179  ids = cpl_polynomial_new(1);
10180  for (k = 0; k <= idsorder; k++) {
10181  c = cpl_table_get_double(idscoeff, clab[k], j, &null);
10182  if (null) {
10183  cpl_polynomial_delete(ids);
10184  missing = 1;
10185  break;
10186  }
10187  cpl_polynomial_set_coeff(ids, &k, c);
10188  }
10189  if (missing)
10190  continue;
10191 
10192  for (k = 0; k < nlines; k++) {
10193  expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10194  startPos = expPos - sradius;
10195  endPos = startPos + window;
10196  if (startPos < 0 || endPos >= nx)
10197  continue;
10198 
10199  if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10200  pos += startPos;
10201  offset = pos - expPos;
10202  snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, k);
10203  cpl_table_set_double(dummy, name, j - ylow, offset);
10204  }
10205  }
10206 
10207  cpl_polynomial_delete(ids);
10208  }
10209 
10210  cpl_image_delete(sky);
10211 
10212  for (j = 0; j < nlines; j++) {
10213  snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
10214  if (cpl_table_has_valid(dummy, name)) {
10215  offset = cpl_table_get_column_median(dummy, name);
10216  cpl_table_set_double(offsets, offname, j, offset);
10217  }
10218  }
10219 
10220  cpl_table_delete(dummy);
10221 
10222  }
10223 
10224 
10225  /*
10226  * In the following the input idscoeff table is modified by simply
10227  * adding the coefficients of the polynomial used to fit the sky
10228  * line residuals to the coefficients of the IDS polynomials.
10229  */
10230 
10231  for (i = 0; i < nslits; i++) {
10232 
10233  if (length[i] == 0)
10234  continue;
10235 
10236  snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
10237 
10238  /*
10239  * In the following, the "dummy" table is just a tool for
10240  * eliminating invalid points from the vectors to be fitted.
10241  */
10242 
10243  dummy = cpl_table_new(nlines);
10244  cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
10245  cpl_table_duplicate_column(dummy, "offset", offsets, offname);
10246 
10247  npoints = nlines - cpl_table_count_invalid(dummy, "offset");
10248  if (npoints == 0) {
10249  cpl_msg_warning(func, "No sky lines alignment was possible "
10250  "for slit ID=%d: no sky line found", slit_id[i]);
10251  cpl_table_delete(dummy);
10252  continue;
10253  }
10254 
10255  uorder = order;
10256  if (npoints <= uorder) {
10257  uorder = npoints - 1;
10258  if (uorder) {
10259  cpl_msg_warning(func, "Just %d sky lines detected for slit "
10260  "ID=%d, while a polynomial order %d was "
10261  "requested. Using polynomial order %d for "
10262  "this slit!", npoints, slit_id[i], order,
10263  uorder);
10264  }
10265  else {
10266  cpl_msg_warning(func, "Just %d sky lines detected for slit "
10267  "ID=%d, while a polynomial order %d was "
10268  "requested. Computing a median offset for "
10269  "this slit!", npoints, slit_id[i], order);
10270  }
10271  }
10272 
10273  cpl_table_erase_invalid(dummy);
10274 
10275  if (uorder > 1) {
10276 
10277  /*
10278  * Model offsets with polynomial fitting
10279  */
10280 
10281  wave = cpl_vector_wrap(npoints,
10282  cpl_table_get_data_double(dummy, "wave"));
10283  offs = cpl_vector_wrap(npoints,
10284  cpl_table_get_data_double(dummy, "offset"));
10285 
10286  /*
10287  * Set reference wavelength as zero point
10288  */
10289 
10290  cpl_vector_subtract_scalar(wave, refwave);
10291 
10292  polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10293 
10294  rms = sqrt(rms * (uorder + 1) / npoints);
10295 
10296  cpl_vector_unwrap(wave);
10297  cpl_vector_unwrap(offs);
10298  cpl_table_delete(dummy);
10299 
10300  /*
10301  * Now correct the coefficients of the corresponding IDS
10302  * polynomials related to this slit:
10303  */
10304 
10305  ylow = position[i];
10306  yhig = ylow + length[i];
10307 
10308  for (j = 0; j <= uorder; j++) {
10309  data = cpl_table_get_data_double(idscoeff, clab[j]);
10310  c = cpl_polynomial_get_coeff(polycorr, &j);
10311  for (k = ylow; k < yhig; k++)
10312  data[k] += c;
10313  }
10314 
10315  data = cpl_table_get_data_double(idscoeff, "error");
10316  for (k = ylow; k < yhig; k++)
10317  data[k] = sqrt(data[k]*data[k] + rms*rms);
10318 
10319  idata = cpl_table_get_data_int(idscoeff, "nlines");
10320  for (k = ylow; k < yhig; k++)
10321  idata[k] = npoints;
10322 
10323  /*
10324  * If a wavelengths map was provided, correct it to keep
10325  * into account the alignment to skylines:
10326  */
10327 
10328  if (calibration) {
10329  for (j = ylow; j < yhig; j++) {
10330  for (k = 1; k < nx; k++) {
10331  lambda1 = cdata[k - 1 + j*nx];
10332  lambda2 = cdata[k + j*nx];
10333  if (lambda1 < 1.0 || lambda2 < 1.0)
10334  continue;
10335  offset = cpl_polynomial_eval_1d(polycorr,
10336  lambda1-refwave, NULL);
10337  cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10338  }
10339  }
10340  }
10341 
10342  cpl_polynomial_delete(polycorr);
10343  }
10344  else if (uorder == 1) {
10345 
10346  /*
10347  * Model offsets with robust linear fitting
10348  */
10349 
10350  double q, m;
10351  cpl_bivector *list;
10352 
10353 
10354  wave = cpl_vector_wrap(npoints,
10355  cpl_table_get_data_double(dummy, "wave"));
10356  offs = cpl_vector_wrap(npoints,
10357  cpl_table_get_data_double(dummy, "offset"));
10358 
10359  list = cpl_bivector_wrap_vectors(wave, offs);
10360 
10361  /*
10362  * Set reference wavelength as zero point
10363  */
10364 
10365  cpl_vector_subtract_scalar(wave, refwave);
10366 
10367  robustLinearFit(list, &q, &m, &rms);
10368 
10369  rms = sqrt(rms * (uorder + 1) / npoints);
10370 
10371  cpl_bivector_unwrap_vectors(list);
10372  cpl_vector_unwrap(wave);
10373  cpl_vector_unwrap(offs);
10374  cpl_table_delete(dummy);
10375 
10376  /*
10377  * Now correct the coefficients of the corresponding IDS
10378  * polynomials related to this slit:
10379  */
10380 
10381  ylow = position[i];
10382  yhig = ylow + length[i];
10383 
10384  for (j = 0; j <= uorder; j++) {
10385  data = cpl_table_get_data_double(idscoeff, clab[j]);
10386  if (j)
10387  c = m;
10388  else
10389  c = q;
10390  for (k = ylow; k < yhig; k++)
10391  data[k] += c;
10392  }
10393 
10394  data = cpl_table_get_data_double(idscoeff, "error");
10395  for (k = ylow; k < yhig; k++)
10396  data[k] = sqrt(data[k]*data[k] + rms*rms);
10397 
10398  idata = cpl_table_get_data_int(idscoeff, "nlines");
10399  for (k = ylow; k < yhig; k++)
10400  idata[k] = npoints;
10401 
10402  /*
10403  * If a wavelengths map was provided, correct it to keep
10404  * into account the alignment to skylines:
10405  */
10406 
10407  if (calibration) {
10408  for (j = ylow; j < yhig; j++) {
10409  for (k = 1; k < nx; k++) {
10410  lambda1 = cdata[k - 1 + j*nx];
10411  lambda2 = cdata[k + j*nx];
10412  if (lambda1 < 1.0 || lambda2 < 1.0)
10413  continue;
10414  offset = q + m*(lambda1-refwave);
10415  cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10416  }
10417  }
10418  }
10419  }
10420  else {
10421 
10422  /*
10423  * Just compute median offset
10424  */
10425 
10426  offs = cpl_vector_wrap(npoints,
10427  cpl_table_get_data_double(dummy, "offset"));
10428 
10429  offset = cpl_vector_get_median_const(offs);
10430 
10431  if (npoints > 1)
10432  rms = cpl_table_get_column_stdev(dummy, "offset");
10433  else
10434  rms = 0.0;
10435 
10436  rms /= sqrt(npoints);
10437 
10438  cpl_vector_unwrap(offs);
10439  cpl_table_delete(dummy);
10440 
10441  /*
10442  * Now correct the constant term of the corresponding IDS
10443  * polynomials related to this slit:
10444  */
10445 
10446  ylow = position[i];
10447  yhig = ylow + length[i];
10448 
10449  data = cpl_table_get_data_double(idscoeff, clab[0]);
10450  for (k = ylow; k < yhig; k++)
10451  data[k] += offset;
10452 
10453  data = cpl_table_get_data_double(idscoeff, "error");
10454  for (k = ylow; k < yhig; k++)
10455  data[k] = sqrt(data[k]*data[k] + rms*rms);
10456 
10457  idata = cpl_table_get_data_int(idscoeff, "nlines");
10458  for (k = ylow; k < yhig; k++)
10459  idata[k] = npoints;
10460 
10461  /*
10462  * If a wavelengths map was provided, correct it to keep
10463  * into account the alignment to skylines. Note that
10464  * the offset must be converted from pixels to wavelengths.
10465  */
10466 
10467  if (calibration) {
10468  for (j = ylow; j < yhig; j++) {
10469  for (k = 1; k < nx; k++) {
10470  lambda1 = cdata[k - 1 + j*nx];
10471  lambda2 = cdata[k + j*nx];
10472  if (lambda1 < 1.0 || lambda2 < 1.0)
10473  continue;
10474  cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10475  }
10476  }
10477  }
10478  }
10479  }
10480 
10481  return offsets;
10482 
10483 }
10484 
10485 
10547 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave,
10548  double firstLambda, double lastLambda,
10549  cpl_table *idscoeff, cpl_vector *skylines,
10550  int highres, int order,
10551  cpl_image *calibration, int sradius)
10552 {
10553  const char *func = "mos_wavelength_align_lss";
10554 
10555  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10556  /* Max order is 5 */
10557  double *line;
10558  double *data;
10559  double *wdata;
10560  double *odata;
10561  double expPos, offset;
10562  double c;
10563  double lambda1, lambda2;
10564  double rms;
10565  float pos;
10566  float *sdata;
10567  float *cdata;
10568  int *idata;
10569  int startPos, endPos;
10570  int window = 2*sradius + 1;
10571  int nlines;
10572  int npoints;
10573  int nrows;
10574  int nx, ny;
10575  int idsorder, uorder;
10576  int missing;
10577  int i;
10578  cpl_size j, k;
10579 
10580  char name[MAX_COLNAME];
10581  char fname[MAX_COLNAME];
10582 
10583  cpl_polynomial *ids;
10584  cpl_polynomial *polycorr;
10585  cpl_table *offsets;
10586  cpl_table *fittable;
10587  cpl_table *dummy;
10588  cpl_vector *wave;
10589  cpl_vector *offs;
10590  cpl_vector *row;
10591 
10592 
10593  if (idscoeff == NULL) {
10594  cpl_msg_error(func, "An IDS coeff table must be given");
10595  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10596  return NULL;
10597  }
10598 
10599  if (image == NULL) {
10600  cpl_msg_error(func, "A scientific spectral image must be given");
10601  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10602  return NULL;
10603  }
10604 
10605  if (skylines) {
10606  line = cpl_vector_get_data(skylines);
10607  nlines = cpl_vector_get_size(skylines);
10608  }
10609  else {
10610  cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10611  "given: using internal list of reference sky lines");
10612  if (highres) {
10613  line = default_lines_hi;
10614  nlines = sizeof(default_lines_hi) / sizeof(double);
10615  }
10616  else {
10617  line = default_lines_lo;
10618  nlines = sizeof(default_lines_lo) / sizeof(double);
10619  }
10620  }
10621 
10622  if (calibration)
10623  cdata = cpl_image_get_data(calibration);
10624 
10625  nx = cpl_image_get_size_x(image);
10626  ny = cpl_image_get_size_y(image);
10627 
10628  sdata = cpl_image_get_data(image);
10629 
10630  if (ny != cpl_table_get_nrow(idscoeff)) {
10631  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10632  return NULL;
10633  }
10634 
10635 
10636  /*FIXME: This is a remnant of the adaptation of the function
10637  * mos_wavelength_align(), where an offset table was created.
10638  * I leave it here because I am in a hurry, it is just used to
10639  * hold the list of selected sky lines.
10640  *
10641  * Define table of wavelengths
10642  */
10643 
10644  nrows = 0;
10645  for (i = 0; i < nlines; i++)
10646  if (line[i] > firstLambda && line[i] < lastLambda)
10647  nrows++;
10648 
10649  offsets = cpl_table_new(nrows);
10650  cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10651  cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10652 
10653  nrows = 0;
10654  for (i = 0; i < nlines; i++) {
10655  if (line[i] > firstLambda && line[i] < lastLambda) {
10656  cpl_table_set_double(offsets, "wave", nrows, line[i]);
10657  nrows++;
10658  }
10659  }
10660 
10661  /*
10662  * Here "line" is made to point to the new list of selected wavelengths
10663  */
10664 
10665  line = cpl_table_get_data_double(offsets, "wave");
10666  nlines = nrows;
10667 
10668  idsorder = 0;
10669  while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10670  ++idsorder;
10671  --idsorder;
10672 
10673 
10674  /*
10675  * Allocate a dummy table for collecting all the offsets
10676  * for all the lines
10677  */
10678 
10679  dummy = cpl_table_new(ny);
10680  for (j = 0; j < nlines; j++) {
10681  snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10682  snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10683  cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10684  cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
10685  }
10686 
10687  for (j = 0; j < ny; j++, sdata += nx) {
10688 
10689  /*
10690  * Get the IDS polynomial for the current slit row
10691  */
10692 
10693  missing = 0;
10694  ids = cpl_polynomial_new(1);
10695  for (k = 0; k <= idsorder; k++) {
10696  c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
10697  if (missing) {
10698  cpl_polynomial_delete(ids);
10699  break;
10700  }
10701  cpl_polynomial_set_coeff(ids, &k, c);
10702  }
10703  if (missing)
10704  continue;
10705 
10706  for (k = 0; k < nlines; k++) {
10707  expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10708  startPos = expPos - sradius;
10709  endPos = startPos + window;
10710  if (startPos < 0 || endPos >= nx)
10711  continue;
10712 
10713  if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10714  pos += startPos;
10715  offset = pos - expPos;
10716  snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
10717  cpl_table_set_double(dummy, name, j, offset);
10718  }
10719  }
10720 
10721  cpl_polynomial_delete(ids);
10722  }
10723 
10724 
10725  /*
10726  * At this point for each sky line we model its offset along
10727  * the image rows using a robust linear fitting
10728  */
10729 
10730  for (j = 0; j < nlines; j++) {
10731  snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10732  snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10733  if (cpl_table_has_valid(dummy, name)) {
10734 
10735  /*
10736  * In the following, the "fittable" is just a tool for
10737  * eliminating invalid points from the vectors to be fitted.
10738  */
10739 
10740  double q, m;
10741  cpl_bivector *list;
10742 
10743  fittable = cpl_table_new(ny);
10744  cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
10745  cpl_table_set_column_unit(fittable, "row", "pixel");
10746  for (k = 0; k < ny; k++)
10747  cpl_table_set_double(fittable, "row", k, k);
10748  cpl_table_duplicate_column(fittable, "offset", dummy, name);
10749  npoints = ny - cpl_table_count_invalid(fittable, "offset");
10750  cpl_table_erase_invalid(fittable);
10751  row = cpl_vector_wrap(npoints,
10752  cpl_table_get_data_double(fittable, "row"));
10753  offs = cpl_vector_wrap(npoints,
10754  cpl_table_get_data_double(fittable, "offset"));
10755  list = cpl_bivector_wrap_vectors(row, offs);
10756  robustLinearFit(list, &q, &m, &rms);
10757  cpl_bivector_unwrap_vectors(list);
10758  cpl_vector_unwrap(row);
10759  cpl_vector_unwrap(offs);
10760  cpl_table_delete(fittable);
10761  for (k = 0; k < ny; k++)
10762  cpl_table_set_double(dummy, fname, k, q + m*k);
10763  }
10764  }
10765 
10766 
10767  /*
10768  * Now each dummy table row consists of a sequence of offsets,
10769  * one for each wavelength. A table row corresponds to an image row.
10770  * We must fit a polynomial to each one of these rows, in order to
10771  * express the offsets as a function of wavelength. The obtained
10772  * polynomial coefficients are used to correct the IDS coefficients.
10773  */
10774 
10775  for (i = 0; i < ny; i++) {
10776 
10777  if (!cpl_table_is_valid(idscoeff, clab[0], i))
10778  continue;
10779 
10780  npoints = 0;
10781  for (j = 0; j < nlines; j++) {
10782  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10783  if (cpl_table_is_valid(dummy, name, i))
10784  npoints++;
10785  }
10786 
10787  if (npoints == 0)
10788  continue;
10789 
10790  uorder = order;
10791  if (npoints <= uorder)
10792  uorder = npoints - 1;
10793 
10794  if (uorder > 1) {
10795 
10796  /*
10797  * Model offsets with polynomial fitting
10798  */
10799 
10800  wave = cpl_vector_new(npoints);
10801  wdata = cpl_vector_get_data(wave);
10802  offs = cpl_vector_new(npoints);
10803  odata = cpl_vector_get_data(offs);
10804 
10805  npoints = 0;
10806  for (j = 0; j < nlines; j++) {
10807  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10808  if (cpl_table_is_valid(dummy, name, i)) {
10809  wdata[npoints] = line[j] - refwave;
10810  odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10811  npoints++;
10812  }
10813  }
10814 
10815  polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10816 
10817  rms = sqrt(rms * (uorder + 1) / npoints);
10818 
10819  cpl_vector_delete(wave);
10820  cpl_vector_delete(offs);
10821 
10822  /*
10823  * Now correct the coefficients of the corresponding IDS
10824  * polynomials related to this slit:
10825  */
10826 
10827  for (j = 0; j <= uorder; j++) {
10828  data = cpl_table_get_data_double(idscoeff, clab[j]);
10829  c = cpl_polynomial_get_coeff(polycorr, &j);
10830  data[i] += c;
10831  }
10832 
10833  data = cpl_table_get_data_double(idscoeff, "error");
10834  data[i] = sqrt(data[i]*data[i] + rms*rms);
10835 
10836  idata = cpl_table_get_data_int(idscoeff, "nlines");
10837  idata[i] = npoints;
10838 
10839  /*
10840  * If a wavelengths map was provided, correct it to keep
10841  * into account the alignment to skylines:
10842  */
10843 
10844  if (calibration) {
10845  for (k = 1; k < nx; k++) {
10846  lambda1 = cdata[k - 1 + i*nx];
10847  lambda2 = cdata[k + i*nx];
10848  if (lambda1 < 1.0 || lambda2 < 1.0)
10849  continue;
10850  offset = cpl_polynomial_eval_1d(polycorr,
10851  lambda1-refwave, NULL);
10852  cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10853  }
10854  }
10855 
10856  cpl_polynomial_delete(polycorr);
10857 
10858  }
10859  else if (uorder == 1) {
10860 
10861  /*
10862  * Model offsets with robust linear fitting
10863  */
10864 
10865  cpl_bivector *list;
10866  double q, m;
10867 
10868  wave = cpl_vector_new(npoints);
10869  wdata = cpl_vector_get_data(wave);
10870  offs = cpl_vector_new(npoints);
10871  odata = cpl_vector_get_data(offs);
10872 
10873  npoints = 0;
10874  for (j = 0; j < nlines; j++) {
10875  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10876  if (cpl_table_is_valid(dummy, name, i)) {
10877  wdata[npoints] = line[j] - refwave;
10878  odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10879  npoints++;
10880  }
10881  }
10882 
10883  list = cpl_bivector_wrap_vectors(wave, offs);
10884  robustLinearFit(list, &q, &m, &rms);
10885 
10886  rms = sqrt(rms * (uorder + 1) / npoints);
10887 
10888  cpl_bivector_unwrap_vectors(list);
10889  cpl_vector_delete(wave);
10890  cpl_vector_delete(offs);
10891 
10892  /*
10893  * Now correct the coefficients of the corresponding IDS
10894  * polynomials related to this row:
10895  */
10896 
10897  for (j = 0; j <= uorder; j++) {
10898  data = cpl_table_get_data_double(idscoeff, clab[j]);
10899  if (j)
10900  c = m;
10901  else
10902  c = q;
10903  data[i] += c;
10904  }
10905 
10906  data = cpl_table_get_data_double(idscoeff, "error");
10907  data[i] = sqrt(data[i]*data[i] + rms*rms);
10908 
10909  idata = cpl_table_get_data_int(idscoeff, "nlines");
10910  idata[i] = npoints;
10911 
10912  /*
10913  * If a wavelengths map was provided, correct it to keep
10914  * into account the alignment to skylines:
10915  */
10916 
10917  if (calibration) {
10918  for (k = 1; k < nx; k++) {
10919  lambda1 = cdata[k - 1 + i*nx];
10920  lambda2 = cdata[k + i*nx];
10921  if (lambda1 < 1.0 || lambda2 < 1.0)
10922  continue;
10923  offset = q + m*(lambda1-refwave);
10924  cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10925  }
10926  }
10927  }
10928  else {
10929 
10930  /*
10931  * Just compute median offset
10932  */
10933 
10934  offs = cpl_vector_new(npoints);
10935  odata = cpl_vector_get_data(offs);
10936 
10937  npoints = 0;
10938  for (j = 0; j < nlines; j++) {
10939  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10940  if (cpl_table_is_valid(dummy, name, i)) {
10941  odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10942  npoints++;
10943  }
10944  }
10945 
10946  offset = cpl_vector_get_median_const(offs);
10947 
10948  if (npoints > 1) {
10949  rms = cpl_vector_get_stdev(offs);
10950  }
10951  else if (npoints == 1) {
10952  snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
10953  if (cpl_table_has_valid(dummy, name)) {
10954  rms = cpl_table_get_column_stdev(dummy, name);
10955  rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
10956  }
10957  else {
10958  rms = 0.0;
10959  }
10960  }
10961  else {
10962  rms = 0.0;
10963  }
10964 
10965  rms /= sqrt(npoints);
10966 
10967  cpl_vector_delete(offs);
10968 
10969  /*
10970  * Now correct the constant term of the corresponding IDS
10971  * polynomials related to this slit:
10972  */
10973 
10974  data = cpl_table_get_data_double(idscoeff, clab[0]);
10975  data[i] += offset;
10976 
10977  data = cpl_table_get_data_double(idscoeff, "error");
10978  data[i] = sqrt(data[i]*data[i] + rms*rms);
10979 
10980  idata = cpl_table_get_data_int(idscoeff, "nlines");
10981  idata[i] = npoints;
10982 
10983  /*
10984  * If a wavelengths map was provided, correct it to keep
10985  * into account the alignment to skylines. Note that
10986  * the offset must be converted from pixels to wavelengths.
10987  */
10988 
10989  if (calibration) {
10990  for (k = 1; k < nx; k++) {
10991  lambda1 = cdata[k - 1 + i*nx];
10992  lambda2 = cdata[k + i*nx];
10993  if (lambda1 < 1.0 || lambda2 < 1.0)
10994  continue;
10995  cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10996  }
10997  }
10998  }
10999  }
11000 
11001  missing = 1;
11002  for (j = 0; j < nlines; j++) {
11003  snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
11004  if (cpl_table_has_valid(dummy, name)) {
11005  missing = 0;
11006  offset = cpl_table_get_column_median(dummy, name);
11007  cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
11008  line[j], offset);
11009  }
11010  else {
11011  cpl_msg_info(func,
11012  "Median offset for %.2f: not available", line[j]);
11013  }
11014  }
11015 
11016  cpl_table_delete(offsets);
11017 
11018  if (missing) {
11019  cpl_table_delete(dummy);
11020  dummy = NULL;
11021  }
11022 
11023  return dummy;
11024 
11025 }
11026 
11027 
11055 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines,
11056  double wavestart, double dispersion, int radius,
11057  int highres)
11058 {
11059 
11060  const char *func = "mos_distortions_rms";
11061 
11062  int xlen;
11063  int ylen;
11064  int numLines;
11065  int cpix, npix, nzero;
11066  int sp, ep;
11067  int i, j, k;
11068  int npeaks, allPeaks;
11069 
11070  float *profile;
11071  float peak, expectPeak, offset;
11072  double lambda;
11073 
11074  double average;
11075  double rms, oneRms;
11076 
11077  float *sdata;
11078  double *wdata;
11079 
11080 
11081  xlen = cpl_image_get_size_x(rectified);
11082  ylen = cpl_image_get_size_y(rectified);
11083  sdata = cpl_image_get_data(rectified);
11084 
11085  if (lines) {
11086  wdata = cpl_vector_get_data(lines);
11087  numLines = cpl_vector_get_size(lines);
11088  }
11089  else {
11090  cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
11091  "given: using internal list of reference sky lines");
11092  if (highres) {
11093  wdata = default_lines_hi;
11094  numLines = sizeof(default_lines_hi) / sizeof(double);
11095  }
11096  else {
11097  wdata = default_lines_lo;
11098  numLines = sizeof(default_lines_lo) / sizeof(double);
11099  }
11100  }
11101 
11102  npix = 2 * radius + 1;
11103  profile = cpl_calloc(npix, sizeof(float));
11104 
11105  rms = 0.0;
11106  allPeaks = 0;
11107 
11108  for (i = 0; i < numLines; i++) {
11109 
11110  /*
11111  * Expected peak and closest pixel to specified wavelength.
11112  */
11113 
11114  lambda = wdata[i];
11115  expectPeak = (lambda - wavestart) / dispersion;
11116  cpix = floor(expectPeak + 0.5);
11117 
11118  /*
11119  * Search interval for peak. Abort if too close to image border.
11120  */
11121 
11122  sp = cpix - radius;
11123  ep = cpix + radius;
11124 
11125  if (sp < 0 || ep > xlen)
11126  continue;
11127 
11128  average = 0.0;
11129  npeaks = 0;
11130  oneRms = 0.0;
11131 
11132  for (j = 0; j < ylen; j++) { /* For each row of each slit */
11133  nzero = 0;
11134  for (k = 0; k < npix; k++) {
11135  profile[k] = sdata[sp + k + j * xlen];
11136  if (fabs(profile[k]) < 0.0001)
11137  nzero++; /* Count number of 0 pixels (spectrum truncated) */
11138  }
11139  if (nzero > 0)
11140  continue;
11141 
11142  if (peakPosition(profile, npix, &peak, 1) == 0) {
11143  offset = (sp + peak) - expectPeak;
11144  average += offset;
11145  rms += fabs(offset);
11146  oneRms += fabs(offset);
11147  npeaks++;
11148  allPeaks++;
11149  }
11150  }
11151 
11152  if (npeaks)
11153  cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
11154  lambda, oneRms / npeaks * 1.25, npeaks);
11155  else
11156  cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
11157  }
11158 
11159  cpl_free(profile);
11160 
11161  if (allPeaks < 10)
11162  return 0.0;
11163 
11164  rms /= allPeaks;
11165  rms *= 1.25; /* Factor to convert average deviation to sigma */
11166 
11167  return rms;
11168 
11169 }
11170 
11171 
11192 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
11193  double blue, double red, double dispersion, int trend)
11194 {
11195  const char *func = "mos_map_pixel";
11196 
11197  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11198  /* Max order is 5 */
11199 
11200  cpl_polynomial *ids;
11201  cpl_image *map;
11202  float *mdata;
11203  double lambda;
11204  double c;
11205  int order;
11206  int xsize, ysize;
11207  int missing;
11208  int i, j;
11209  cpl_size k;
11210 
11211 
11212  if (idscoeff == NULL) {
11213  cpl_msg_error(func, "An IDS coeff table must be given");
11214  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11215  return NULL;
11216  }
11217 
11218  xsize = (red - blue) / dispersion;
11219  ysize = cpl_table_get_nrow(idscoeff);
11220  map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
11221  mdata = cpl_image_get_data(map);
11222 
11223  order = 0;
11224  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
11225  ++order;
11226  --order;
11227 
11228  for (i = 0; i < ysize; i++, mdata += xsize) {
11229 
11230  missing = 0;
11231  ids = cpl_polynomial_new(1);
11232  for (k = trend; k <= order; k++) {
11233  c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
11234  if (missing) {
11235  cpl_polynomial_delete(ids);
11236  break;
11237  }
11238  cpl_polynomial_set_coeff(ids, &k, c);
11239  }
11240  if (missing)
11241  continue;
11242 
11243  for (j = 0; j < xsize; j++) {
11244  lambda = blue + j*dispersion;
11245  mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
11246  }
11247 
11248  cpl_polynomial_delete(ids);
11249  }
11250 
11251  return map;
11252 
11253 }
11254 
11255 
11277 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
11278  double blue, double red)
11279 {
11280  const char *func = "mos_map_idscoeff";
11281 
11282  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11283  /* Max order is 5 */
11284 
11285  cpl_polynomial *ids;
11286  cpl_image *map;
11287  float *mdata;
11288  double lambda;
11289  double c;
11290  int order;
11291  int ysize;
11292  int missing;
11293  int i, j;
11294  cpl_size k;
11295 
11296 
11297  if (idscoeff == NULL) {
11298  cpl_msg_error(func, "An IDS coeff table must be given");
11299  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11300  return NULL;
11301  }
11302 
11303  if (xsize < 1) {
11304  cpl_msg_error(func, "Invalid image size");
11305  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11306  return NULL;
11307  }
11308 
11309  if (xsize < 20 || xsize > 5000) {
11310  cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
11311  xsize);
11312  }
11313 
11314  ysize = cpl_table_get_nrow(idscoeff);
11315  map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
11316  mdata = cpl_image_get_data(map);
11317 
11318  order = 0;
11319  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
11320  ++order;
11321  --order;
11322 
11323  for (i = 0; i < ysize; i++, mdata += xsize) {
11324 
11325  missing = 0;
11326  ids = cpl_polynomial_new(1);
11327  for (k = 0; k <= order; k++) {
11328  c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
11329  if (missing) {
11330  cpl_polynomial_delete(ids);
11331  break;
11332  }
11333  cpl_polynomial_set_coeff(ids, &k, c);
11334  }
11335  if (missing)
11336  continue;
11337 
11338  for (j = 0; j < xsize; j++) {
11339  lambda = mos_eval_dds(ids, blue, red, reference, j);
11340 
11341  if (lambda >= blue && lambda <= red) {
11342  mdata[j] = lambda;
11343  }
11344  }
11345 
11346  cpl_polynomial_delete(ids);
11347  }
11348 
11349  return map;
11350 
11351 }
11352 
11353 
11388 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
11389  cpl_table *slits, cpl_table *polytraces,
11390  double reference, double blue, double red,
11391  double dispersion)
11392 {
11393  const char *func = "mos_map_wavelengths";
11394 
11395  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11396  /* Max order is 5 */
11397  cpl_polynomial *polytop;
11398  cpl_polynomial *polybot;
11399  cpl_image *remapped;
11400  float *data;
11401  float *wdata;
11402  float *sdata;
11403  float *xdata;
11404  double vtop, vbot, value;
11405  double top, bot;
11406  double coeff;
11407  double ytop, ybot;
11408  double ypos;
11409  double fvalue;
11410  int ivalue;
11411  int yint, ysize, yprev;
11412  int nslits;
11413  int npseudo;
11414  int *slit_id;
11415  int *position;
11416  int *length;
11417  int nx, ny;
11418  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11419  int missing_top, missing_bot;
11420  int null;
11421  int order;
11422  int i, j;
11423  cpl_size k;
11424 
11425 
11426  if (spatial == NULL || calibration == NULL ||
11427  slits == NULL || polytraces == NULL) {
11428  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11429  return NULL;
11430  }
11431 
11432  if (dispersion <= 0.0) {
11433  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11434  return NULL;
11435  }
11436 
11437  if (red - blue < dispersion) {
11438  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11439  return NULL;
11440  }
11441 
11442  nx = cpl_image_get_size_x(spatial);
11443  ny = cpl_image_get_size_y(spatial);
11444  ysize = cpl_image_get_size_y(calibration);
11445  remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
11446  data = cpl_image_get_data(remapped);
11447  sdata = cpl_image_get_data(spatial);
11448  wdata = cpl_image_get_data(calibration);
11449 
11450  nslits = cpl_table_get_nrow(slits);
11451  slit_id = cpl_table_get_data_int(slits, "slit_id");
11452  order = cpl_table_get_ncol(polytraces) - 2;
11453  position = cpl_table_get_data_int(slits, "position");
11454  length = cpl_table_get_data_int(slits, "length");
11455 
11456  /*
11457  * The spatial resampling is performed for a certain number of
11458  * pixels above and below the position of the reference wavelength:
11459  */
11460 
11461  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11462  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11463 
11464  for (i = 0; i < nslits; i++) {
11465 
11466  if (length[i] == 0)
11467  continue;
11468 
11469  /*
11470  * Note that the x coordinate of the reference pixels on the CCD
11471  * is taken arbitrarily at the top end of each slit. This wouldn't
11472  * be entirely correct in case of curved slits, or in presence of
11473  * heavy distortions: in such cases the spatial resampling is
11474  * really performed across a wide range of wavelengths. But
11475  * the lag between top and bottom spectral curvature models
11476  * would introduce even in such cases negligible effects on
11477  * the spectral spatial resampling.
11478  */
11479 
11480  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11481 
11482  start_pixel = refpixel - pixel_below;
11483  if (start_pixel < 0)
11484  start_pixel = 0;
11485 
11486  end_pixel = refpixel + pixel_above;
11487  if (end_pixel > nx)
11488  end_pixel = nx;
11489 
11490  /*
11491  * Recover from the table of spectral curvature coefficients
11492  * the curvature polynomials.
11493  */
11494 
11495  missing_top = 0;
11496  polytop = cpl_polynomial_new(1);
11497  for (k = 0; k <= order; k++) {
11498  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11499  if (null) {
11500  cpl_polynomial_delete(polytop);
11501  missing_top = 1;
11502  break;
11503  }
11504  cpl_polynomial_set_coeff(polytop, &k, coeff);
11505  }
11506 
11507  missing_bot = 0;
11508  polybot = cpl_polynomial_new(1);
11509  for (k = 0; k <= order; k++) {
11510  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11511  if (null) {
11512  cpl_polynomial_delete(polybot);
11513  missing_bot = 1;
11514  break;
11515  }
11516  cpl_polynomial_set_coeff(polybot, &k, coeff);
11517  }
11518 
11519  if (missing_top && missing_bot) {
11520  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11521  slit_id[i]);
11522  continue;
11523  }
11524 
11525  /*
11526  * In case just one of the two edges was not traced, the other
11527  * edge curvature model is duplicated and shifted to the other
11528  * end of the slit: better than nothing!
11529  */
11530 
11531  if (missing_top) {
11532  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11533  "the spectral curvature of the lower edge "
11534  "is used instead.", slit_id[i]);
11535  polytop = cpl_polynomial_duplicate(polybot);
11536  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11537  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11538  k = 0;
11539  coeff = cpl_polynomial_get_coeff(polybot, &k);
11540  coeff += ytop - ybot;
11541  cpl_polynomial_set_coeff(polytop, &k, coeff);
11542  }
11543 
11544  if (missing_bot) {
11545  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11546  "the spectral curvature of the upper edge "
11547  "is used instead.", slit_id[i]);
11548  polybot = cpl_polynomial_duplicate(polytop);
11549  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11550  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11551  k = 0;
11552  coeff = cpl_polynomial_get_coeff(polytop, &k);
11553  coeff -= ytop - ybot;
11554  cpl_polynomial_set_coeff(polybot, &k, coeff);
11555  }
11556 
11557  /*
11558  * Point to current slit on wavelength calibration image.
11559  * Note that the npseudo value related to this slit is equal
11560  * to the number of spatial pseudo-pixels decreased by 1
11561  * (compare with function mos_spatial_calibration()).
11562  */
11563 
11564  xdata = wdata + nx*position[i];
11565  npseudo = length[i] - 1;
11566 
11567  /*
11568  * Write interpolated wavelengths to CCD image
11569  */
11570 
11571  for (j = start_pixel; j < end_pixel; j++) {
11572  top = cpl_polynomial_eval_1d(polytop, j, NULL);
11573  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
11574  for (k = 0; k <= npseudo; k++) {
11575  ypos = top - k*(top-bot)/npseudo;
11576  yint = ypos;
11577 
11578  /*
11579  * The line:
11580  * value = sdata[j + nx*yint];
11581  * should be equivalent to:
11582  * value = npseudo*(top-yint)/(top-bot);
11583  */
11584 
11585  if (yint < 0 || yint >= ny-1) {
11586  yprev = yint;
11587  continue;
11588  }
11589 
11590  value = sdata[j + nx*yint]; /* Spatial coordinate on CCD */
11591  ivalue = value; /* Nearest spatial pixels: */
11592  fvalue = value - ivalue; /* ivalue and ivalue+1 */
11593  if (ivalue < npseudo && ivalue >= 0) {
11594  vtop = xdata[j + nx*(npseudo-ivalue)];
11595  vbot = xdata[j + nx*(npseudo-ivalue-1)];
11596  if (vtop < 1.0) { /* Impossible wavelength */
11597  if (vbot < 1.0) {
11598  value = 0.0;
11599  }
11600  else {
11601  value = vbot;
11602  }
11603  }
11604  else if (vbot < 1.0) {
11605  if (k)
11606  value = vtop;
11607  else
11608  value = 0.0;
11609  }
11610  else if (fabs(vbot-vtop) > 10*dispersion) {
11611  value = 0.0;
11612  }
11613  else {
11614  value = vtop*(1-fvalue) + vbot*fvalue;
11615  }
11616  data[j + nx*yint] = value;
11617 
11618  if (k) {
11619 
11620  /*
11621  * This is added to recover lost pixels on
11622  * the CCD image (pixels are lost because
11623  * the CCD pixels are less than npseudo+1).
11624  */
11625 
11626  if (yprev - yint > 1) {
11627  value = sdata[j + nx*(yint+1)];
11628  ivalue = value;
11629  fvalue = value - ivalue;
11630  if (ivalue < npseudo && ivalue >= 0) {
11631  vtop = xdata[j + nx*(npseudo-ivalue)];
11632  vbot = xdata[j + nx*(npseudo-ivalue-1)];
11633  if (vtop < 1.0) {
11634  if (vbot < 1.0) {
11635  value = data[j + nx*(yint+1)];
11636  }
11637  else {
11638  value = vbot;
11639  }
11640  }
11641  else if (vbot < 1.0) {
11642  value = vtop;
11643  }
11644  else if (fabs(vbot-vtop) > 2*dispersion) {
11645  value = vtop;
11646  }
11647  else {
11648  value = vtop*(1-fvalue) + vbot*fvalue;
11649  }
11650  data[j + nx*(yint+1)] = value;
11651  }
11652  }
11653  }
11654  }
11655  yprev = yint;
11656  }
11657  }
11658  cpl_polynomial_delete(polytop);
11659  cpl_polynomial_delete(polybot);
11660  }
11661 
11662  return remapped;
11663 }
11664 
11738 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib,
11739  cpl_image *spatial, cpl_table *slits,
11740  cpl_table *polytraces, double reference,
11741  double blue, double red, double dispersion,
11742  int flux)
11743 {
11744  const char *func = "mos_map_spectrum";
11745 
11746  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11747  /* Max order is 5 */
11748  cpl_polynomial *polytop;
11749  cpl_polynomial *polybot;
11750  cpl_image *remapped;
11751  cpl_image **exslit;
11752  float *data;
11753  float *wdata;
11754  float *sdata;
11755  float *xdata;
11756  double lambda00, lambda01, lambda10, lambda11, lambda;
11757  double space00, space01, space10, space11, space;
11758  double value00, value01, value10, value11, value0, value1, value;
11759  double dL, dS;
11760  double top, bot;
11761  double coeff;
11762  double ytop, ybot;
11763  double xfrac, yfrac;
11764  int yint, ysize;
11765  int itop, ibot;
11766  int shift;
11767  int L, S;
11768  int nslits;
11769  int npseudo;
11770  int *slit_id;
11771  int *position;
11772  int *length;
11773  int nx, ny;
11774  int x, y;
11775  int nlambda;
11776  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11777  int missing_top, missing_bot;
11778  int null;
11779  int order;
11780  int i;
11781  cpl_size k;
11782 
11783 
11784  flux += flux;
11785 
11786  if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
11787  slits == NULL || polytraces == NULL) {
11788  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11789  return NULL;
11790  }
11791 
11792  if (dispersion <= 0.0) {
11793  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11794  return NULL;
11795  }
11796 
11797  if (red - blue < dispersion) {
11798  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11799  return NULL;
11800  }
11801 
11802  nx = cpl_image_get_size_x(spectra);
11803  ny = cpl_image_get_size_y(spectra);
11804 
11805  if (nx != cpl_image_get_size_x(spatial) ||
11806  ny != cpl_image_get_size_y(spatial) ||
11807  nx != cpl_image_get_size_x(wavecalib) ||
11808  ny != cpl_image_get_size_y(wavecalib)) {
11809  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11810  return NULL;
11811  }
11812 
11813  nlambda = STRETCH_FACTOR * (red - blue) / dispersion;
11814  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11815  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11816 
11817  data = cpl_image_get_data(spectra);
11818  sdata = cpl_image_get_data(spatial);
11819  wdata = cpl_image_get_data(wavecalib);
11820 
11821  nslits = cpl_table_get_nrow(slits);
11822  slit_id = cpl_table_get_data_int(slits, "slit_id");
11823  order = cpl_table_get_ncol(polytraces) - 2;
11824  position = cpl_table_get_data_int(slits, "position");
11825  length = cpl_table_get_data_int(slits, "length");
11826 
11827  exslit = cpl_calloc(nslits, sizeof(cpl_image *));
11828 
11829  for (i = 0; i < nslits; i++) {
11830 
11831  if (length == 0)
11832  continue;
11833 
11834  /*
11835  * Note that the x coordinate of the reference pixels on the CCD
11836  * is taken arbitrarily at the top end of each slit. This wouldn't
11837  * be entirely correct in case of curved slits, or in presence of
11838  * heavy distortions: in such cases the spatial resampling is
11839  * really performed across a wide range of wavelengths. But
11840  * the lag between top and bottom spectral curvature models
11841  * would introduce even in such cases negligible effects on
11842  * the spectral spatial resampling.
11843  */
11844 
11845  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11846 
11847  start_pixel = refpixel - pixel_below;
11848  if (start_pixel < 1)
11849  start_pixel = 1;
11850 
11851  end_pixel = refpixel + pixel_above;
11852  if (end_pixel > nx)
11853  end_pixel = nx;
11854 
11855  /*
11856  * Recover from the table of spectral curvature coefficients
11857  * the curvature polynomials.
11858  */
11859 
11860  missing_top = 0;
11861  polytop = cpl_polynomial_new(1);
11862  for (k = 0; k <= order; k++) {
11863  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11864  if (null) {
11865  cpl_polynomial_delete(polytop);
11866  missing_top = 1;
11867  break;
11868  }
11869  cpl_polynomial_set_coeff(polytop, &k, coeff);
11870  }
11871 
11872  missing_bot = 0;
11873  polybot = cpl_polynomial_new(1);
11874  for (k = 0; k <= order; k++) {
11875  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11876  if (null) {
11877  cpl_polynomial_delete(polybot);
11878  missing_bot = 1;
11879  break;
11880  }
11881  cpl_polynomial_set_coeff(polybot, &k, coeff);
11882  }
11883 
11884  if (missing_top && missing_bot) {
11885  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11886  slit_id[i]);
11887  continue;
11888  }
11889 
11890  /*
11891  * In case just one of the two edges was not traced, the other
11892  * edge curvature model is duplicated and shifted to the other
11893  * end of the slit: better than nothing!
11894  */
11895 
11896  if (missing_top) {
11897  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11898  "the spectral curvature of the lower edge "
11899  "is used instead.", slit_id[i]);
11900  polytop = cpl_polynomial_duplicate(polybot);
11901  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11902  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11903  k = 0;
11904  coeff = cpl_polynomial_get_coeff(polybot, &k);
11905  coeff += ytop - ybot;
11906  cpl_polynomial_set_coeff(polytop, &k, coeff);
11907  }
11908 
11909  if (missing_bot) {
11910  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11911  "the spectral curvature of the upper edge "
11912  "is used instead.", slit_id[i]);
11913  polybot = cpl_polynomial_duplicate(polytop);
11914  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11915  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11916  k = 0;
11917  coeff = cpl_polynomial_get_coeff(polytop, &k);
11918  coeff -= ytop - ybot;
11919  cpl_polynomial_set_coeff(polybot, &k, coeff);
11920  }
11921 
11922  /*
11923  * Allocate image for current extracted slit
11924  */
11925 
11926  top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
11927  bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
11928  npseudo = ceil(top-bot) + 1;
11929 
11930  if (npseudo < 1) {
11931  cpl_polynomial_delete(polytop);
11932  cpl_polynomial_delete(polybot);
11933  cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
11934  slit_id[i]);
11935  continue;
11936  }
11937 
11938  exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
11939  xdata = cpl_image_get_data(exslit[i]);
11940 
11941  /*
11942  * Write interpolated spectral values to remapped slit spectrum.
11943  */
11944 
11945  for (x = start_pixel; x < end_pixel; x++) {
11946  top = cpl_polynomial_eval_1d(polytop, x, NULL);
11947  bot = cpl_polynomial_eval_1d(polybot, x, NULL);
11948  itop = top + 1;
11949  ibot = bot;
11950  if (itop < 0)
11951  itop = 0;
11952  if (itop > ny - 1)
11953  itop = ny - 1;
11954  if (ibot < 0)
11955  ibot = 0;
11956  if (ibot > ny - 1)
11957  ibot = ny - 1;
11958  for (y = ibot; y < itop; y++) {
11959  lambda11 = wdata[x + y*nx];
11960  if (lambda11 < 1.0) /* Impossible wavelength */
11961  continue;
11962  space11 = sdata[x + y*nx];
11963  if (space11 < 0.0) /* Impossible spatial coordinate */
11964  continue;
11965  lambda01 = wdata[x - 1 + y*nx];
11966  if (lambda01 < 1.0) /* Impossible wavelength */
11967  continue;
11968  space01 = sdata[x - 1 + y*nx];
11969  if (space01 < 0.0) /* Impossible spatial coordinate */
11970  continue;
11971 
11972  shift = 0;
11973 
11974 /****+
11975  if (wdata[x + (y+1)*nx] > 1.0) {
11976  if (wdata[x + (y+1)*nx] - lambda11 > 0) {
11977  shift = -1;
11978  while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
11979  shift--;
11980  if (lambda11 - wdata[x + shift + (y+1)*nx] >
11981  wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
11982  shift++;
11983  }
11984  }
11985  else {
11986  shift = 1;
11987  while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
11988  shift++;
11989  if (wdata[x + shift + (y+1)*nx] - lambda11 >
11990  lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
11991  shift--;
11992  }
11993  }
11994  }
11995 ****/
11996 
11997 /****
11998 printf("y = %d, shift = %d\n", y, shift);
11999 ****/
12000 
12001  lambda10 = wdata[x + shift + (y+1)*nx];
12002  if (lambda10 < 1.0) /* Impossible wavelength */
12003  continue;
12004  space10 = sdata[x + shift + (y+1)*nx];
12005  if (space10 < 0.0) /* Impossible spatial coordinate */
12006  continue;
12007  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
12008  if (lambda00 < 1.0) /* Impossible wavelength */
12009  continue;
12010  space00 = sdata[x - 1 + shift + (y+1)*nx];
12011  if (space00 < 0.0) /* Impossible spatial coordinate */
12012  continue;
12013 
12014  /*
12015  * Find the variation in lambda and space in this
12016  * position for each CCD pixel (both quantities are
12017  * expected to be positive).
12018  */
12019 
12020  dL = lambda11 - lambda01;
12021  dS = space11 - space10;
12022 
12023  /*
12024  * Find the position (L,S) of the output pixel
12025  * (by integer truncation).
12026  */
12027 
12028  L = (lambda11 - blue)/dispersion + 0.5;
12029  S = space11 + 0.5; /* Counted from top! */
12030 
12031  if (L < 0 || L >= nlambda)
12032  continue;
12033  if (S < 0 || S > npseudo)
12034  continue;
12035 
12036  /*
12037  * Find the coordinate of pixel (L,S)
12038  */
12039 
12040  lambda = blue + L*dispersion;
12041  space = S;
12042 
12043  /*
12044  * Find the interpolation point on the CCD: it is
12045  * defined as the (positive) distance from current
12046  * CCD pixel (x,y) of the interpolation point (x',y'),
12047  * measured in CCD pixels. The interpolation point
12048  * is located between the four CCD pixels selected
12049  * above.
12050  */
12051 
12052  xfrac = (lambda11-lambda)/dL;
12053  yfrac = (space11-space)/dS;
12054 
12055 /*
12056 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
12057 printf("xyfrac = %f, %f\n", xfrac, yfrac);
12058 */
12059 
12060  /*
12061  * Get the four values to interpolate
12062  */
12063 
12064  value11 = data[x + y*nx];
12065  value01 = data[x - 1 + y*nx];
12066  value10 = data[x + shift + (y+1)*nx];
12067  value00 = data[x + shift - 1 + (y+1)*nx];
12068 
12069  /*
12070  * Interpolation
12071  */
12072 
12073  value1 = (1-xfrac)*value11 + xfrac*value01;
12074  value0 = (1-xfrac)*value10 + xfrac*value00;
12075  value = (1-yfrac)*value1 + yfrac*value0;
12076 
12077  /*
12078  * Write this value to the appropriate (L,S) coordinate
12079  * on output slit
12080  */
12081 
12082  xdata[L + nlambda*(npseudo-S)] = value;
12083 
12084  }
12085  }
12086  cpl_polynomial_delete(polytop);
12087  cpl_polynomial_delete(polybot);
12088  }
12089 
12090  /*
12091  * Now all the slits images are copied to a single image
12092  */
12093 
12094  ysize = 0;
12095  for (i = 0; i < nslits; i++)
12096  if (exslit[i])
12097  ysize += cpl_image_get_size_y(exslit[i]);
12098 
12099  remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
12100 
12101  yint = -1;
12102  for (i = 0; i < nslits; i++) {
12103  if (exslit[i]) {
12104  yint += cpl_image_get_size_y(exslit[i]);
12105  cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
12106  cpl_image_delete(exslit[i]);
12107  cpl_table_set_int(slits, "position", i, ysize - yint - 1);
12108  }
12109  }
12110 
12111  cpl_free(exslit);
12112 
12113  return remapped;
12114 
12115 }
12116 
12117 
12150 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
12151  double dispersion, double factor, int minpoints,
12152  cpl_image *skymap)
12153 {
12154  const char *func = "mos_sky_map_super";
12155 
12156  cpl_vector **vector;
12157  cpl_vector **wvector;
12158  double firstLambda, lastLambda;
12159  double lambda, lambda1, lambda2;
12160  double value, value1, value2;
12161  double frac;
12162  float min, max;
12163  int *count;
12164  int nbin, bin;
12165  int nx, ny, npix;
12166  int first_valid, valid_bins;
12167  int i, j;
12168 
12169  cpl_table *sky;
12170  double *sky_spectrum;
12171  double *sky_wave;
12172  float *data;
12173  float *sdata;
12174  float *kdata;
12175 
12176 
12177  if (spectra == NULL || wavemap == NULL || skymap == NULL) {
12178  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12179  return NULL;
12180  }
12181 
12182  if (dispersion <= 0.0) {
12183  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12184  cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
12185  return NULL;
12186  }
12187 
12188  nx = cpl_image_get_size_x(spectra);
12189  ny = cpl_image_get_size_y(spectra);
12190  npix = nx * ny;
12191 
12192  if (nx != cpl_image_get_size_x(wavemap) ||
12193  ny != cpl_image_get_size_y(wavemap) ||
12194  nx != cpl_image_get_size_x(skymap) ||
12195  ny != cpl_image_get_size_y(skymap)) {
12196  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
12197  cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
12198  return NULL;
12199  }
12200 
12201  if (factor < 1.0) {
12202  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12203  cpl_msg_error(func, "Undersampling (%f): %s", factor,
12204  cpl_error_get_message());
12205  return NULL;
12206  }
12207 
12208  if (minpoints < 0) {
12209  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12210  cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
12211  return NULL;
12212  }
12213 
12214  dispersion /= factor;
12215 
12216 
12217  /*
12218  * Find bluest and reddest wavelengths in the whole image
12219  */
12220 
12221  data = cpl_image_get_data(wavemap);
12222 
12223  for (i = 0; i < npix; i++) {
12224  if (data[i] > 1.0) {
12225  min = max = data[i];
12226  j = i+1;
12227  break;
12228  }
12229  }
12230 
12231  for (i = j; i < npix; i++) {
12232  if (data[i] < 1.0) /* Impossible wavelength */
12233  continue;
12234  if (min > data[i])
12235  min = data[i];
12236  if (max < data[i])
12237  max = data[i];
12238  }
12239 
12240  firstLambda = min;
12241  lastLambda = max;
12242 
12243 
12244  /*
12245  * Determine length of median spectrum
12246  */
12247 
12248  nbin = (lastLambda - firstLambda) / dispersion;
12249 
12250  /*
12251  * Count how many values will be found for each spectral bin.
12252  * The ith bin has a wavelength range from firstLambda + i*dispersion
12253  * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12254  * it is assigned to its central wavelength.
12255  */
12256 
12257  count = cpl_calloc(nbin, sizeof(int));
12258 
12259  data = cpl_image_get_data(wavemap);
12260 
12261  for (i = 0; i < npix; i++) {
12262  if (data[i] < 1.0)
12263  continue;
12264  bin = (data[i] - firstLambda) / dispersion;
12265  if (bin < nbin) /* Safer */
12266  count[bin]++;
12267  }
12268 
12269  valid_bins = 0;
12270  for (i = 0; i < nbin; i++)
12271  if (count[i] >= minpoints)
12272  valid_bins++;
12273 
12274  if (valid_bins < nbin/3) {
12275  cpl_msg_warning(func, "Cannot determine a good global sky "
12276  "spectrum from input data");
12277  return NULL;
12278  }
12279 
12280 
12281  /*
12282  * Allocate an array of vectors with the appropriate size, to
12283  * contain a list of all the spectral pixels values. At the same
12284  * time, reset the array of counters (because we will have to
12285  * count again...).
12286  */
12287 
12288  vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12289  wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
12290  for (i = 0; i < nbin; i++) {
12291  if (count[i] >= minpoints) {
12292  vector[i] = cpl_vector_new(count[i]);
12293  wvector[i] = cpl_vector_new(count[i]);
12294  }
12295  count[i] = 0;
12296  }
12297 
12298 
12299  /*
12300  * Read the wavemap and the spectral images, and add the data values
12301  * to the appropriate wavelength bins
12302  */
12303 
12304  data = cpl_image_get_data(wavemap);
12305  sdata = cpl_image_get_data(spectra);
12306 
12307  for (i = 0; i < npix; i++) {
12308  if (data[i] < 1.0)
12309  continue;
12310  bin = (data[i] - firstLambda) / dispersion;
12311  if (bin < nbin) { /* Safer */
12312  if (vector[bin]) {
12313  cpl_vector_set(vector[bin], count[bin], sdata[i]);
12314  cpl_vector_set(wvector[bin], count[bin], data[i]);
12315  }
12316  count[bin]++;
12317  }
12318  }
12319 
12320 
12321  /*
12322  * Compute the median flux for each wavelength bin, and destroy
12323  * at the same time the used vectors
12324  */
12325 
12326  sky_spectrum = cpl_calloc(nbin, sizeof(double));
12327  sky_wave = cpl_calloc(nbin, sizeof(double));
12328  for (i = 0; i < nbin; i++) {
12329  if (vector[i]) {
12330  sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12331  sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
12332  cpl_vector_delete(vector[i]);
12333  cpl_vector_delete(wvector[i]);
12334  }
12335  }
12336 
12337  cpl_free(vector);
12338  cpl_free(wvector);
12339 
12340 
12341  /*
12342  * Here possible gaps in the final spectrum are filled by interpolation
12343  */
12344 
12345  for (i = 0; i < nbin; i++) {
12346  if (count[i] >= minpoints) {
12347  first_valid = i;
12348  break;
12349  }
12350  }
12351 
12352  for (i = first_valid; i < nbin; i++) {
12353  if (count[i] < minpoints) {
12354  sky_wave[i] = firstLambda + (i+0.5)*dispersion;
12355  for (j = i+1; j < nbin; j++) {
12356  if (count[j] >= minpoints) {
12357  if (sky_wave[j] - sky_wave[i-1] < 0.1) {
12358  sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
12359  / 2;
12360  }
12361  else {
12362  frac = (sky_wave[i] - sky_wave[i-1])
12363  / (sky_wave[j] - sky_wave[i-1]);
12364  sky_spectrum[i] = frac * sky_spectrum[j]
12365  + (1 - frac) * sky_spectrum[i-1];
12366  }
12367  }
12368  }
12369  }
12370  }
12371 
12372 
12373  /*
12374  * Create the output table
12375  */
12376 
12377  sky = cpl_table_new(nbin);
12378  cpl_table_wrap_double(sky, sky_wave, "wavelength");
12379  cpl_table_wrap_double(sky, sky_spectrum, "sky");
12380  cpl_table_wrap_int(sky, count, "npoints");
12381 
12382 
12383  /*
12384  * Fill the sky map
12385  */
12386 
12387  data = cpl_image_get_data(wavemap);
12388  sdata = cpl_image_get_data(spectra);
12389  kdata = cpl_image_get_data(skymap);
12390 
12391  for (i = 0; i < npix; i++) {
12392 
12393  /*
12394  * Currently based on linear interpolation
12395  */
12396 
12397  lambda = data[i];
12398  if (lambda < 1.0)
12399  continue;
12400  bin = (lambda - firstLambda) / dispersion;
12401  lambda1 = sky_wave[bin];
12402  value1 = sky_spectrum[bin];
12403  if (lambda1 < lambda) {
12404  bin++;
12405  if (bin < nbin) {
12406  lambda2 = sky_wave[bin];
12407  value2 = sky_spectrum[bin];
12408  if (lambda2 - lambda1 < 0.1) {
12409  value = (value1 + value2) / 2;
12410  }
12411  else {
12412  frac = (lambda - lambda1) / (lambda2 - lambda1);
12413  value = frac * value2 + (1 - frac) * value1;
12414  }
12415  }
12416  else {
12417  value = value1;
12418  }
12419  }
12420  else {
12421  if (bin > 0) {
12422  bin--;
12423  lambda2 = lambda1;
12424  value2 = value1;
12425  lambda1 = sky_wave[bin];
12426  value1 = sky_spectrum[bin];
12427  if (lambda2 - lambda1 < 0.1) {
12428  value = (value1 + value2) / 2;
12429  }
12430  else {
12431  frac = (lambda - lambda1) / (lambda2 - lambda1);
12432  value = frac * value2 + (1 - frac) * value1;
12433  }
12434  }
12435  else {
12436  value = value1;
12437  }
12438  }
12439  kdata[i] = value;
12440  }
12441 
12442  if (first_valid)
12443  cpl_table_erase_window(sky, 0, first_valid);
12444 
12445  return sky;
12446 
12447 }
12448 
12449 
12483 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
12484  double dispersion, cpl_image *skymap)
12485 {
12486  const char *func = "mos_sky_map";
12487 
12488  cpl_vector **vector;
12489  double firstLambda, lastLambda;
12490  double lambda, lambda1, lambda2;
12491  double value, value1, value2;
12492  float min, max;
12493  int *count;
12494  int nbin, bin;
12495  int nx, ny, npix;
12496  int i, j;
12497 
12498  cpl_table *sky;
12499  double *sky_spectrum;
12500  float *data;
12501  float *sdata;
12502  float *kdata;
12503  double *wdata;
12504 
12505 
12506  if (spectra == NULL || wavemap == NULL || skymap == NULL) {
12507  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12508  return NULL;
12509  }
12510 
12511  if (dispersion <= 0.0) {
12512  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12513  return NULL;
12514  }
12515 
12516  nx = cpl_image_get_size_x(spectra);
12517  ny = cpl_image_get_size_y(spectra);
12518  npix = nx * ny;
12519 
12520  if (nx != cpl_image_get_size_x(wavemap) ||
12521  ny != cpl_image_get_size_y(wavemap) ||
12522  nx != cpl_image_get_size_x(skymap) ||
12523  ny != cpl_image_get_size_y(skymap)) {
12524  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
12525  return NULL;
12526  }
12527 
12528 
12529  /*
12530  * Find bluest and reddest wavelengths in the whole image
12531  */
12532 
12533  data = cpl_image_get_data(wavemap);
12534 
12535  for (i = 0; i < npix; i++) {
12536  if (data[i] > 1.0) {
12537  min = max = data[i];
12538  j = i+1;
12539  break;
12540  }
12541  }
12542 
12543  for (i = j; i < npix; i++) {
12544  if (data[i] < 1.0) /* Impossible wavelength */
12545  continue;
12546  if (min > data[i])
12547  min = data[i];
12548  if (max < data[i])
12549  max = data[i];
12550  }
12551 
12552  firstLambda = min;
12553  lastLambda = max;
12554 
12555 
12556  /*
12557  * Determine length of median spectrum
12558  */
12559 
12560  nbin = (lastLambda - firstLambda) / dispersion;
12561 
12562  /*
12563  * Count how many values will be found for each spectral bin.
12564  * The ith bin has a wavelength range from firstLambda + i*dispersion
12565  * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12566  * it is assigned to its central wavelength.
12567  */
12568 
12569  count = cpl_calloc(nbin, sizeof(int));
12570 
12571  data = cpl_image_get_data(wavemap);
12572 
12573  for (i = 0; i < npix; i++) {
12574  if (data[i] < 1.0)
12575  continue;
12576  bin = (data[i] - firstLambda) / dispersion;
12577  if (bin < nbin) /* Safer */
12578  count[bin]++;
12579  }
12580 
12581 
12582  /*
12583  * Allocate an array of vectors with the appropriate size, to
12584  * contain a list of all the spectral pixels values. At the same
12585  * time, reset the array of counters (because we will have to
12586  * count again...).
12587  */
12588 
12589  vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12590  for (i = 0; i < nbin; i++) {
12591  if (count[i])
12592  vector[i] = cpl_vector_new(count[i]);
12593  else
12594  vector[i] = NULL;
12595  count[i] = 0;
12596  }
12597 
12598 
12599  /*
12600  * Read the wavemap and the spectral images, and add the data values
12601  * to the appropriate wavelength bins
12602  */
12603 
12604  data = cpl_image_get_data(wavemap);
12605  sdata = cpl_image_get_data(spectra);
12606 
12607  for (i = 0; i < npix; i++) {
12608  if (data[i] < 1.0)
12609  continue;
12610  bin = (data[i] - firstLambda) / dispersion;
12611  if (bin < nbin) { /* Safer */
12612  cpl_vector_set(vector[bin], count[bin], sdata[i]);
12613  count[bin]++;
12614  }
12615  }
12616 
12617 
12618  /*
12619  * Compute the median flux for each wavelength bin, and destroy
12620  * at the same time the used vectors
12621  */
12622 
12623  sky_spectrum = cpl_calloc(nbin, sizeof(double));
12624  for (i = 0; i < nbin; i++) {
12625  if (vector[i]) {
12626  sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12627  cpl_vector_delete(vector[i]);
12628  }
12629  }
12630 
12631  cpl_free(vector);
12632 
12633 
12634  /*
12635  * Here possible gaps in the final spectrum should be filled
12636  * by interpolation
12637  */
12638 
12639  /* ... */
12640 
12641  /*
12642  * Create the output table
12643  */
12644 
12645  sky = cpl_table_new(nbin);
12646  cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
12647  cpl_table_set_column_unit(sky, "wavelength", "pixel");
12648  cpl_table_wrap_double(sky, sky_spectrum, "sky");
12649  cpl_table_wrap_int(sky, count, "npoints");
12650  for (i = 0; i < nbin; i++)
12651  cpl_table_set_double(sky, "wavelength", i,
12652  firstLambda + (i+0.5)*dispersion);
12653 
12654 
12655  /*
12656  * Fill the sky map
12657  */
12658 
12659  data = cpl_image_get_data(wavemap);
12660  sdata = cpl_image_get_data(spectra);
12661  kdata = cpl_image_get_data(skymap);
12662  wdata = cpl_table_get_data_double(sky, "wavelength");
12663 
12664  for (i = 0; i < npix; i++) {
12665 
12666  /*
12667  * Currently based on linear interpolation
12668  */
12669 
12670  lambda = data[i];
12671  if (lambda < 1.0)
12672  continue;
12673  bin = (lambda - firstLambda) / dispersion;
12674  lambda1 = wdata[bin];
12675  value1 = sky_spectrum[bin];
12676  if (lambda1 < lambda) {
12677  bin++;
12678  if (bin < nbin) {
12679  lambda2 = wdata[bin];
12680  value2 = sky_spectrum[bin];
12681  value = ((lambda2 - lambda)*value1
12682  + (lambda - lambda1)*value2) / dispersion;
12683  }
12684  else {
12685  value = value1;
12686  }
12687  }
12688  else {
12689  if (bin > 0) {
12690  bin--;
12691  lambda2 = lambda1;
12692  value2 = value1;
12693  lambda1 = wdata[bin];
12694  value1 = sky_spectrum[bin];
12695  value = ((lambda2 - lambda)*value1
12696  + (lambda - lambda1)*value2)/dispersion;
12697  }
12698  else {
12699  value = value1;
12700  }
12701  }
12702  kdata[i] = value;
12703  }
12704 
12705  return sky;
12706 
12707 }
12708 
12709 
12725 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
12726 {
12727  const char *func = "mos_sky_local_old";
12728 
12729  cpl_image *exslit;
12730  cpl_image *sky;
12731  cpl_image *skymap;
12732  float *data;
12733  float *sdata;
12734  int nx, ny;
12735  int xlow, ylow, xhig, yhig;
12736  int nslits;
12737  int *slit_id;
12738  int *position;
12739  int *length;
12740  int i, j, k;
12741 
12742 
12743  if (spectra == NULL) {
12744  cpl_msg_error(func,
12745  "A scientific rectified spectral image must be given");
12746  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12747  return NULL;
12748  }
12749 
12750  if (slits == NULL) {
12751  cpl_msg_error(func, "A slits position table must be given");
12752  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12753  return NULL;
12754  }
12755 
12756  nslits = cpl_table_get_nrow(slits);
12757  slit_id = cpl_table_get_data_int(slits, "slit_id");
12758  position = cpl_table_get_data_int(slits, "position");
12759  length = cpl_table_get_data_int(slits, "length");
12760 
12761  nx = cpl_image_get_size_x(spectra);
12762  ny = cpl_image_get_size_y(spectra);
12763 
12764  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12765 
12766  xlow = 1;
12767  xhig = nx;
12768  for (i = 0; i < nslits; i++) {
12769 
12770  if (length[i] == 0)
12771  continue;
12772 
12773  /*
12774  * Define the extraction boundaries. We DON'T write:
12775  *
12776  * ylow = position[i];
12777  * yhig = ylow + length[i];
12778  *
12779  * because the cpl_image pixels are counted from 1, and because in
12780  * cpl_image_extract() the coordinates of the last pixel are inclusive.
12781  */
12782 
12783  ylow = position[i] + 1;
12784  yhig = ylow + length[i] - 1;
12785 
12786  exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12787  sky = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12788  cpl_image_delete(exslit);
12789 
12790  data = cpl_image_get_data(skymap);
12791  data += nx * position[i];
12792 
12793  for (j = 0; j < length[i]; j++) {
12794  sdata = cpl_image_get_data(sky);
12795  for (k = 0; k < nx; k++) {
12796  *data++ = *sdata++;
12797  }
12798  }
12799 
12800  cpl_image_delete(sky);
12801  }
12802 
12803  return skymap;
12804 
12805 }
12806 
12807 
12827 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
12828 {
12829  const char *func = "mos_sky_local";
12830 
12831  char name[MAX_COLNAME];
12832 
12833  cpl_polynomial *fit;
12834  cpl_vector *points;
12835  cpl_vector *values;
12836  cpl_vector *keep_points;
12837  cpl_vector *keep_values;
12838  cpl_image *exslit;
12839  cpl_image *sky;
12840  cpl_image *subtracted;
12841  cpl_image *profile;
12842  cpl_image *skymap;
12843  cpl_table *objects;
12844  float *data;
12845  float *sdata;
12846  float *xdata;
12847  double *vdata;
12848  double *pdata;
12849  double median;
12850  int nx, ny;
12851  int xlow, ylow, xhig, yhig;
12852  int nslits;
12853  int *slit_id;
12854  int *position;
12855  int *length;
12856  int *is_sky;
12857  int nsky, nbad;
12858  int maxobjects;
12859  int margin = 3;
12860  int radius = 6;
12861  int i, j, k;
12862 
12863 
12864  if (spectra == NULL) {
12865  cpl_msg_error(func,
12866  "A scientific rectified spectral image must be given");
12867  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12868  return NULL;
12869  }
12870 
12871  if (slits == NULL) {
12872  cpl_msg_error(func, "A slits position table must be given");
12873  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12874  return NULL;
12875  }
12876 
12877  if (order < 0) {
12878  cpl_msg_error(func, "Invalid fit order");
12879  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12880  return NULL;
12881  }
12882 
12883  nslits = cpl_table_get_nrow(slits);
12884  slit_id = cpl_table_get_data_int(slits, "slit_id");
12885  position = cpl_table_get_data_int(slits, "position");
12886  length = cpl_table_get_data_int(slits, "length");
12887 
12888  nx = cpl_image_get_size_x(spectra);
12889  ny = cpl_image_get_size_y(spectra);
12890 
12891  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12892 
12893  xlow = 1;
12894  xhig = nx;
12895  for (i = 0; i < nslits; i++) {
12896 
12897  if (length[i] == 0)
12898  continue;
12899 
12900  /*
12901  * Define the extraction boundaries. We DON'T write:
12902  *
12903  * ylow = position[i];
12904  * yhig = ylow + length[i];
12905  *
12906  * because the cpl_image pixels are counted from 1, and because in
12907  * cpl_image_extract() the coordinates of the last pixel are inclusive.
12908  */
12909 
12910  ylow = position[i] + 1;
12911  yhig = ylow + length[i] - 1;
12912 
12913  exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12914  sky = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12915  cpl_image_delete(exslit);
12916 
12917  data = cpl_image_get_data(skymap);
12918  data += nx * position[i];
12919 
12920  for (j = 0; j < length[i]; j++) {
12921  sdata = cpl_image_get_data(sky);
12922  for (k = 0; k < nx; k++) {
12923  *data++ = *sdata++;
12924  }
12925  }
12926 
12927  cpl_image_delete(sky);
12928  }
12929 
12930 
12931  /*
12932  * Preliminary sky-subtracted image
12933  */
12934 
12935  subtracted = cpl_image_duplicate(spectra);
12936  cpl_image_subtract(subtracted, skymap);
12937  cpl_image_delete(skymap);
12938 
12939 
12940  /*
12941  * Detect objects positions in all slits
12942  */
12943 
12944  objects = cpl_table_duplicate(slits);
12945  profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
12946  cpl_image_delete(profile);
12947  cpl_image_delete(subtracted);
12948 
12949 
12950  /*
12951  * Flag the sky pixels. Note that maxobjects is intentionally
12952  * the max number of objects increased by one.
12953  */
12954 
12955  maxobjects = 1;
12956  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12957  while (cpl_table_has_column(objects, name)) {
12958  maxobjects++;
12959  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12960  }
12961 
12962  is_sky = cpl_calloc(ny, sizeof(int));
12963 
12964  for (i = 0; i < nslits; i++) {
12965 
12966  if (length[i] == 0)
12967  continue;
12968 
12969  ylow = position[i] + margin;
12970  yhig = position[i] + length[i] - margin;
12971 
12972  for (j = ylow; j < yhig; j++)
12973  is_sky[j] = 1;
12974 
12975  for (j = 1; j < maxobjects; j++) {
12976  snprintf(name, MAX_COLNAME, "object_%d", j);
12977  if (cpl_table_is_valid(objects, name, i)) {
12978  snprintf(name, MAX_COLNAME, "start_%d", j);
12979  ylow = cpl_table_get_int(objects, name, i, NULL);
12980  snprintf(name, MAX_COLNAME, "end_%d", j);
12981  yhig = cpl_table_get_int(objects, name, i, NULL);
12982  for (k = ylow; k <= yhig; k++)
12983  is_sky[k] = 0;
12984  }
12985  }
12986 
12987 
12988  /*
12989  * Eliminate isolated sky points
12990  */
12991 
12992  ylow = position[i] + margin + 1;
12993  yhig = position[i] + length[i] - margin - 1;
12994 
12995  for (j = ylow; j < yhig; j++)
12996  if (is_sky[j])
12997  if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
12998  is_sky[j] = 0;
12999 
13000  }
13001 
13002 
13003  /*
13004  * Determination of the sky map
13005  */
13006 
13007  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13008 
13009  for (i = 0; i < nslits; i++) {
13010 
13011  if (length[i] == 0)
13012  continue;
13013 
13014  ylow = position[i];
13015  yhig = ylow + length[i];
13016 
13017  nsky = 0;
13018  for (j = ylow; j < yhig; j++)
13019  if (is_sky[j])
13020  nsky++;
13021 
13022  if (nsky > order + 1) {
13023  if (order) {
13024  points = cpl_vector_new(nsky);
13025  nsky = 0;
13026  for (j = ylow; j < yhig; j++) {
13027  if (is_sky[j]) {
13028  cpl_vector_set(points, nsky, j);
13029  nsky++;
13030  }
13031  }
13032 
13033  exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
13034  xdata = cpl_image_get_data(exslit);
13035  values = cpl_vector_new(nsky);
13036 
13037  for (j = 0; j < nx; j++) {
13038  nsky = 0;
13039  for (k = ylow; k < yhig; k++) {
13040  if (is_sky[k]) {
13041  cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
13042  nsky++;
13043  }
13044  }
13045 
13046  /*
13047  * Eliminate obvious outliers
13048  */
13049 
13050  median = cpl_vector_get_median_const(values);
13051  vdata = cpl_vector_get_data(values);
13052  pdata = cpl_vector_get_data(points);
13053  nbad = 0;
13054  for (k = 0; k < nsky; k++) {
13055  if (fabs(vdata[k] - median) < 100) {
13056  if (nbad) {
13057  vdata[k-nbad] = vdata[k];
13058  pdata[k-nbad] = pdata[k];
13059  }
13060  }
13061  else
13062  nbad++;
13063  }
13064 
13065  if (nsky == nbad)
13066  continue;
13067 
13068  if (nbad && nsky - nbad > order + 1) {
13069  keep_values = values;
13070  keep_points = points;
13071  values = cpl_vector_wrap(nsky-nbad, vdata);
13072  points = cpl_vector_wrap(nsky-nbad, pdata);
13073  }
13074 
13075  if (nsky - nbad > order + 1) {
13076 
13077  fit = cpl_polynomial_fit_1d_create(points, values,
13078  order, NULL);
13079 
13080  if (fit) {
13081  for (k = ylow; k < yhig; k++) {
13082  xdata[j+(k-ylow)*nx] =
13083  cpl_polynomial_eval_1d(fit, k, NULL);
13084  }
13085 
13086  cpl_polynomial_delete(fit);
13087  }
13088  else
13089  cpl_error_reset();
13090  }
13091  else {
13092  for (k = 0; k < nsky; k++) {
13093  xdata[j+k*nx] = median;
13094  }
13095  }
13096 
13097  if (nbad && nsky - nbad > order + 1) {
13098  cpl_vector_unwrap(values);
13099  cpl_vector_unwrap(points);
13100  values = keep_values;
13101  points = keep_points;
13102  }
13103 
13104  if (nbad) {
13105  nsky = 0;
13106  for (k = ylow; k < yhig; k++) {
13107  if (is_sky[k]) {
13108  cpl_vector_set(points, nsky, k);
13109  nsky++;
13110  }
13111  }
13112  }
13113 
13114  }
13115 
13116  cpl_vector_delete(values);
13117  cpl_vector_delete(points);
13118 
13119  cpl_image_copy(skymap, exslit, 1, ylow+1);
13120  cpl_image_delete(exslit);
13121 
13122  }
13123  else {
13124  exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
13125  xdata = cpl_image_get_data(exslit);
13126  values = cpl_vector_new(nsky);
13127 
13128  for (j = 0; j < nx; j++) {
13129  nsky = 0;
13130  for (k = ylow; k < yhig; k++) {
13131  if (is_sky[k]) {
13132  cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
13133  nsky++;
13134  }
13135  }
13136 
13137  median = cpl_vector_get_median_const(values);
13138 
13139  for (k = ylow; k < yhig; k++)
13140  xdata[j+(k-ylow)*nx] = median;
13141 
13142  }
13143 
13144  cpl_vector_delete(values);
13145 
13146  cpl_image_copy(skymap, exslit, 1, ylow+1);
13147  cpl_image_delete(exslit);
13148  }
13149  }
13150  else
13151  cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
13152  }
13153 
13154  cpl_free(is_sky);
13155 
13156  return skymap;
13157 
13158 }
13159 
13160 
13182 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
13183  float threshold, float ratio)
13184 {
13185  const char *func = "mos_clean_cosmics";
13186 
13187  cpl_image *smoothImage;
13188  cpl_table *table;
13189  cpl_matrix *kernel;
13190  int *xdata;
13191  int *ydata;
13192  float *idata;
13193  float *sdata;
13194  float sigma, sum, value, smoothValue;
13195  double noise;
13196  int count;
13197  float fMax;
13198  int iMin, iMax, jMin, jMax, iPosMax, jPosMax;
13199  int xLen;
13200  int yLen;
13201  int nPix;
13202  int first = 1; /* position of first cosmic ray candidate
13203  encountered while scanning the image */
13204  int pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
13205  int numCosmic = 0;
13206  int found, foundContiguousCandidate;
13207  int *cosmic;
13208 
13209 
13210  if (image == NULL)
13211  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13212 
13213 
13214  /*
13215  * "cosmic" is a flags holder (initialized to zero):
13216  *
13217  * -1 = candidate for cosmic ray
13218  * 0 = not a cosmic
13219  * 1 = a cosmic ray
13220  * 2 = member of current group of contiguous candidates
13221  * 3 = examined member of current group
13222  */
13223 
13224  xLen = cpl_image_get_size_x(image);
13225  yLen = cpl_image_get_size_y(image);
13226 
13227  if (xLen < 4 || yLen < 4)
13228  return CPL_ERROR_NONE;
13229 
13230  nPix = xLen * yLen;
13231 
13232  /*
13233  * Noise estimation from negative offsets in image. Note that this
13234  * assumes that the background level (skyLevel) has already been
13235  * subtracted from the data. In this way we estimate the noise due
13236  * to detector readout and to the background signal level (before
13237  * it were removed). Theoretically this is given by
13238  *
13239  * noise = sqrt(ron^2 + skyLevel/gain)
13240  *
13241  * where ron is the read-out-noise. To this we will sum the noise
13242  * contribution due to any increase of the signal above the background
13243  * by an amount scienceLevel. Theoretically the total noise is given by
13244  *
13245  * totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
13246  *
13247  * that is
13248  *
13249  * totalNoise = sqrt(noise^2 + scienceLevel/gain)
13250  *
13251  */
13252 
13253  idata = cpl_image_get_data(image);
13254  noise = 0.0;
13255  count = 0;
13256 
13257  for (i = 0; i < nPix; i++) {
13258  if (idata[i] < -0.00001) {
13259  noise -= idata[i];
13260  count++;
13261  }
13262  }
13263 
13264  noise /= count;
13265  noise *= 1.25; /* Factor to convert average deviation to sigma */
13266 
13267  cosmic = cpl_calloc(nPix, sizeof(int));
13268 
13269  if (threshold < 0.)
13270  threshold = 4.0;
13271  if (ratio < 0.)
13272  ratio = 2.0;
13273 
13274  kernel = cpl_matrix_new(3, 3);
13275  cpl_matrix_fill(kernel, 1.0);
13276  cpl_matrix_set(kernel, 1, 1, 0.0);
13277  smoothImage = cpl_image_filter_median(image, kernel);
13278  cpl_matrix_delete(kernel);
13279 
13280  /*
13281  * Loop on images pixels, searching for cosmic rays candidates.
13282  * Border pixels are currently excluded (they cannot contain
13283  * candidates), to avoid that the search for groups of contiguous
13284  * pixels would ever go out of image boundaries. In future we may
13285  * overcome this limit, adding an appropriate check when contiguous
13286  * pixels are searched.
13287  */
13288 
13289  sdata = cpl_image_get_data(smoothImage);
13290 
13291  for (j = 1; j < yLen - 1; j++) {
13292  for (i = 1; i < xLen - 1; i++) {
13293  value = idata[i + j * xLen];
13294  smoothValue = sdata[i + j * xLen];
13295  if (smoothValue < 1.0)
13296  smoothValue = 1.0;
13297  sigma = sqrt(noise * noise + smoothValue / gain);
13298  if (value - smoothValue >= threshold * sigma)
13299  cosmic[i + j * xLen] = -1;
13300  }
13301  }
13302 
13303  cpl_image_delete(smoothImage);
13304 
13305 
13306  /*
13307  * Search for groups of contiguous cosmic rays candidates.
13308  */
13309 
13310  do {
13311  found = 0;
13312  for (pos = first; pos < nPix; pos++) {
13313  if (cosmic[pos] == -1) {
13314  cosmic[pos] = 2; /* Candidate found. */
13315  i = pos % xLen; /* Its coordinates. */
13316  j = pos / xLen;
13317  first = pos;
13318  first++; /* ??? really necessary? */
13319  found = 1;
13320  break;
13321  }
13322  }
13323 
13324  if (found) {
13325 
13326  /*
13327  * Determine new group of contiguous cosmic rays candidates.
13328  * Initialize the working box boundaries, iMin, iMax, jMin, jMax,
13329  * and the value of the max pixel and its position, fMax, iPosMax,
13330  * jPosMax.
13331  */
13332 
13333  iMin = iMax = iPosMax = i;
13334  jMin = jMax = jPosMax = j;
13335  fMax = idata[i + j * xLen];
13336 
13337  do {
13338  foundContiguousCandidate = 0;
13339  for (l = 0; l <= 1; l++) {
13340  for (k = 0; k <= 1; k++) {
13341 
13342  /*
13343  * Looping on 4 pixels to North, East, South and West
13344  */
13345 
13346  ii = i + k - l;
13347  jj = j + k + l - 1;
13348  if (cosmic[ii + jj * xLen] == -1) {
13349  foundContiguousCandidate = 1;
13350  cosmic[ii + jj * xLen] = 2;
13351  /* Candidate belongs to current group */
13352  iii = ii; /* Keep its position */
13353  jjj = jj;
13354 
13355  /*
13356  * Upgrade search box
13357  */
13358 
13359  if (ii < iMin)
13360  iMin = ii;
13361  if (ii > iMax)
13362  iMax = ii;
13363  if (jj < jMin)
13364  jMin = jj;
13365  if (jj > jMax)
13366  jMax = jj;
13367 
13368  if (idata[ii + jj * xLen] > fMax) {
13369  fMax = idata[ii + jj * xLen];
13370  iPosMax = ii;
13371  jPosMax = jj;
13372  }
13373  }
13374  }
13375  }
13376 
13377  /*
13378  * We are done exploring the "cross". Now mark as "examined"
13379  * the current candidate (at the center of the cross):
13380  */
13381 
13382  cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
13383 
13384  if (foundContiguousCandidate) {
13385 
13386  /*
13387  * Pass (arbitrarily) the coordinates of the LAST found
13388  * candidate
13389  */
13390 
13391  i = iii;
13392  j = jjj;
13393 
13394  /*
13395  * Skip the rest, continue loop on new candidate
13396  */
13397 
13398  continue;
13399  }
13400 
13401 
13402  /*
13403  * Look for leftovers in the (growing!) search box
13404  */
13405 
13406  for (l = jMin; l <= jMax; l++) {
13407  for (k = iMin; k <= iMax; k++) {
13408  if (cosmic[k + l * xLen] == 2) {
13409  i = k;
13410  j = l;
13411  foundContiguousCandidate = 1;
13412  break;
13413  }
13414  }
13415  if (foundContiguousCandidate)
13416  break;
13417  }
13418  } while (foundContiguousCandidate);
13419 
13420 
13421  /*
13422  * No more contiguous candidates are found. Decide now
13423  * whether the current group is a cosmic ray or not.
13424  */
13425 
13426  sum = 0.; /* Sum of 8 pixels around max position */
13427  for (l = -1; l <= 1; l++) {
13428  for (k = -1; k <= 1; k++) {
13429  if (l != 0 || k != 0) {
13430  sum += idata[iPosMax + k + (jPosMax + l) * xLen];
13431  }
13432  }
13433  }
13434 
13435  sum /= 8.;
13436  if (fMax > ratio * sum) {
13437  for (l = jMin - 1; l <= jMax + 1; l++) {
13438  for (k = iMin - 1; k <= iMax + 1; k++) {
13439  if (cosmic[k + l * xLen] == 3) {
13440  cosmic[k + l * xLen] = 1;
13441  numCosmic++;
13442  }
13443  }
13444  }
13445  }
13446  else {
13447  for (l = jMin - 1; l <= jMax + 1; l++) {
13448  for (k = iMin - 1; k <= iMax + 1; k++) {
13449  if (cosmic[k + l * xLen] != -1) {
13450  if (cosmic[k + l * xLen] == 1)
13451  numCosmic--;
13452  cosmic[k + l * xLen] = 0;
13453  }
13454  }
13455  }
13456  }
13457  }
13458  } while (found);
13459 
13460 
13461  /*
13462  * Prepare table containing cosmic rays coordinates.
13463  */
13464 
13465  table = cpl_table_new(numCosmic);
13466  cpl_table_new_column(table, "x", CPL_TYPE_INT);
13467  cpl_table_new_column(table, "y", CPL_TYPE_INT);
13468  cpl_table_set_column_unit(table, "x", "pixel");
13469  cpl_table_set_column_unit(table, "y", "pixel");
13470  xdata = cpl_table_get_data_int(table, "x");
13471  ydata = cpl_table_get_data_int(table, "y");
13472 
13473  for (pos = 0, i = 0; pos < nPix; pos++) {
13474  if (cosmic[pos] == 1) {
13475  xdata[i] = (pos % xLen);
13476  ydata[i] = (pos / xLen);
13477  i++;
13478  }
13479  }
13480 
13481  mos_clean_bad_pixels(image, table, 1);
13482 
13483  cpl_free(cosmic);
13484  cpl_table_delete(table);
13485 
13486  return CPL_ERROR_NONE;
13487 
13488 }
13489 
13490 
13491 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
13492  int spectral)
13493 {
13494  const char *func = "mos_clean_cosmics";
13495 
13496  float *idata;
13497  int *isBadPix;
13498  int i, j, k, d;
13499  int xlen, ylen, totPix;
13500  int nBadPixels = 0;
13501  int sign, foundFirst;
13502  int *xValue = NULL;
13503  int *yValue = NULL;
13504  float save = 0.;
13505  double sumd;
13506  int cx, cy;
13507  int nPairs;
13508  float estimate[4];
13509  int sx[] = {0, 1, 1, 1};
13510  int sy[] = {1,-1, 0, 1};
13511  int searchHorizon = 100;
13512  int percent = 15;
13513 
13514 
13515  if (image == NULL || table == NULL)
13516  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13517 
13518  if (1 != cpl_table_has_column(table, "x"))
13519  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13520 
13521  if (1 != cpl_table_has_column(table, "y"))
13522  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13523 
13524  if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
13525  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13526 
13527  if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
13528  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13529 
13530  nBadPixels = cpl_table_get_nrow(table);
13531 
13532  if (nBadPixels) {
13533  xlen = cpl_image_get_size_x(image);
13534  ylen = cpl_image_get_size_y(image);
13535  idata = cpl_image_get_data(image);
13536  totPix = xlen * ylen;
13537  if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
13538  isBadPix = cpl_calloc(totPix, sizeof(int));
13539  }
13540  else {
13541  cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
13542  "skip bad pixel correction", percent);
13543  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13544  }
13545  }
13546  else {
13547  cpl_msg_debug(func, "No pixel values to interpolate");
13548  return CPL_ERROR_NONE;
13549  }
13550 
13551  xValue = cpl_table_get_data_int(table, "x");
13552  yValue = cpl_table_get_data_int(table, "y");
13553 
13554  for (i = 0; i < nBadPixels; i++)
13555  isBadPix[xValue[i] + yValue[i] * xlen] = 1;
13556 
13557  for (i = 0; i < nBadPixels; i++) {
13558 
13559  /*
13560  * Search for the closest good pixel along the 4 fundamental
13561  * directions (in both senses):
13562  * \ | /
13563  * \|/
13564  * --- ---
13565  * /|\
13566  * / | \
13567  *
13568  * Then collect pairs of values to interpolate linearly.
13569  */
13570 
13571  nPairs = 0;
13572  for (j = 0; j < 4; j++) {
13573 
13574  if (spectral) /* Just horizontal interpolation for spectral data */
13575  if (j != 2)
13576  continue;
13577 
13578  estimate[nPairs] = 0.; /* Pairs interpolations are stored here */
13579  sumd = 0.;
13580  foundFirst = 0;
13581  for (k = 0; k < 2; k++) {
13582  sign = 2 * k - 1;
13583  d = 0;
13584  cx = xValue[i];
13585  cy = yValue[i];
13586  do {
13587  cx += sign * sx[j];
13588  cy += sign * sy[j];
13589  if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen)
13590  break;
13591  d++;
13592  } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
13593 
13594  if (cx >= 0 && cx < xlen &&
13595  cy >= 0 && cy < ylen && d < searchHorizon) {
13596 
13597  /*
13598  * In this block is cripted the linear interpolation...
13599  */
13600 
13601  save = idata[cx + cy * xlen];
13602  estimate[nPairs] += save / d;
13603  sumd += 1. / (double) d;
13604  if (k) {
13605  estimate[nPairs] /= sumd;
13606  nPairs++;
13607  }
13608  else {
13609  foundFirst = 1;
13610  }
13611  }
13612  else {
13613 
13614  /*
13615  * Image borders were crossed, incomplete pair of values
13616  */
13617 
13618  if (k) {
13619  if (foundFirst) {
13620  estimate[nPairs] = save;
13621  nPairs++;
13622  }
13623  }
13624  }
13625  }
13626  }
13627 
13628  /*
13629  * Replace pixel value of the input image, corresponding to
13630  * the current bad pixel, with the median of the estimates
13631  * resulted from the 4 linear interpolations.
13632  */
13633 
13634  if (nPairs > 2) {
13635  idata[xValue[i] + yValue[i] * xlen] =
13636  cpl_tools_get_median_float(estimate, nPairs);
13637  }
13638  else if (nPairs == 2) {
13639  idata[xValue[i] + yValue[i] * xlen] =
13640  (estimate[0] + estimate[1]) / 2.;
13641  }
13642  else if (nPairs == 1) {
13643  idata[xValue[i] + yValue[i] * xlen] = estimate[0];
13644  }
13645  else {
13646  cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
13647  xValue[i], yValue[i]);
13648  }
13649  }
13650 
13651  cpl_free(isBadPix);
13652 
13653  return CPL_ERROR_NONE;
13654 }
13655 
13656 
13686 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
13687  cpl_table *polytraces, double reference,
13688  double blue, double red, double dispersion)
13689 {
13690  const char *func = "mos_spatial_map";
13691 
13692  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
13693  /* Max order is 5 */
13694  cpl_polynomial *polytop;
13695  cpl_polynomial *polybot;
13696  cpl_image *calibration;
13697  float *data;
13698  double top, bot;
13699  double coeff;
13700  double ytop, ybot;
13701  double ypos, yfra;
13702  double factor;
13703  int yint, yprev;
13704  int nslits;
13705  int npseudo;
13706  int *slit_id;
13707  int *length;
13708  int nx, ny;
13709  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
13710  int missing_top, missing_bot;
13711  int null;
13712  int order;
13713  int i, j;
13714  cpl_size k;
13715 
13716 
13717  if (spectra == NULL || slits == NULL || polytraces == NULL) {
13718  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13719  return NULL;
13720  }
13721 
13722  if (dispersion <= 0.0) {
13723  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13724  return NULL;
13725  }
13726 
13727  if (red - blue < dispersion) {
13728  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13729  return NULL;
13730  }
13731 
13732  nx = cpl_image_get_size_x(spectra);
13733  ny = cpl_image_get_size_y(spectra);
13734 
13735  calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13736  data = cpl_image_get_data(calibration);
13737 
13738  length = cpl_table_get_data_int(slits, "length");
13739  nslits = cpl_table_get_nrow(slits);
13740  slit_id = cpl_table_get_data_int(slits, "slit_id");
13741  order = cpl_table_get_ncol(polytraces) - 2;
13742 
13743  /*
13744  * The spatial resampling is performed for a certain number of
13745  * pixels above and below the position of the reference wavelength:
13746  */
13747 
13748  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
13749  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
13750 
13751  for (i = 0; i < nslits; i++) {
13752 
13753  if (length[i] == 0)
13754  continue;
13755 
13756  /*
13757  * Note that the x coordinate of the reference pixels on the CCD
13758  * is taken arbitrarily at the top end of each slit. This wouldn't
13759  * be entirely correct in case of curved slits, or in presence of
13760  * heavy distortions: in such cases the spatial resampling is
13761  * really performed across a wide range of wavelengths. But
13762  * the lag between top and bottom spectral curvature models
13763  * would introduce even in such cases negligible effects on
13764  * the spectral spatial resampling.
13765  */
13766 
13767  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
13768 
13769  start_pixel = refpixel - pixel_below;
13770  if (start_pixel < 0)
13771  start_pixel = 0;
13772 
13773  end_pixel = refpixel + pixel_above;
13774  if (end_pixel > nx)
13775  end_pixel = nx;
13776 
13777  /*
13778  * Recover from the table of spectral curvature coefficients
13779  * the curvature polynomials.
13780  */
13781 
13782  missing_top = 0;
13783  polytop = cpl_polynomial_new(1);
13784  for (k = 0; k <= order; k++) {
13785  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
13786  if (null) {
13787  cpl_polynomial_delete(polytop);
13788  missing_top = 1;
13789  break;
13790  }
13791  cpl_polynomial_set_coeff(polytop, &k, coeff);
13792  }
13793 
13794  missing_bot = 0;
13795  polybot = cpl_polynomial_new(1);
13796  for (k = 0; k <= order; k++) {
13797  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
13798  if (null) {
13799  cpl_polynomial_delete(polybot);
13800  missing_bot = 1;
13801  break;
13802  }
13803  cpl_polynomial_set_coeff(polybot, &k, coeff);
13804  }
13805 
13806  if (missing_top && missing_bot) {
13807  cpl_msg_warning(func, "Spatial map, slit %d was not traced!",
13808  slit_id[i]);
13809  continue;
13810  }
13811 
13812  /*
13813  * In case just one of the two edges was not traced, the other
13814  * edge curvature model is duplicated and shifted to the other
13815  * end of the slit: better than nothing!
13816  */
13817 
13818  if (missing_top) {
13819  cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
13820  "the spectral curvature of the lower edge "
13821  "is used instead.", slit_id[i]);
13822  polytop = cpl_polynomial_duplicate(polybot);
13823  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13824  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13825  k = 0;
13826  coeff = cpl_polynomial_get_coeff(polybot, &k);
13827  coeff += ytop - ybot;
13828  cpl_polynomial_set_coeff(polytop, &k, coeff);
13829  }
13830 
13831  if (missing_bot) {
13832  cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
13833  "the spectral curvature of the upper edge "
13834  "is used instead.", slit_id[i]);
13835  polybot = cpl_polynomial_duplicate(polytop);
13836  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13837  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13838  k = 0;
13839  coeff = cpl_polynomial_get_coeff(polytop, &k);
13840  coeff -= ytop - ybot;
13841  cpl_polynomial_set_coeff(polybot, &k, coeff);
13842  }
13843 
13844  top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
13845  bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
13846  npseudo = ceil(top-bot) + 1;
13847 
13848  if (npseudo < 1) {
13849  cpl_polynomial_delete(polytop);
13850  cpl_polynomial_delete(polybot);
13851  cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
13852  slit_id[i]);
13853  continue;
13854  }
13855 
13856  for (j = start_pixel; j < end_pixel; j++) {
13857  top = cpl_polynomial_eval_1d(polytop, j, NULL);
13858  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
13859  factor = (top-bot)/npseudo;
13860  for (k = 0; k <= npseudo; k++) {
13861  ypos = top - k*factor;
13862  yint = ypos;
13863  yfra = ypos - yint;
13864  if (yint >= 0 && yint < ny-1) {
13865  data[j + nx*yint] = (top-yint)/factor;
13866  if (k) {
13867 
13868  /*
13869  * This is added to recover lost pixels on
13870  * the CCD image (pixels are lost because
13871  * the CCD pixels are less than npseudo+1).
13872  */
13873 
13874  if (yprev - yint > 1) {
13875  data[j + nx*(yint+1)] = (top-yint-1)/factor;
13876  }
13877  }
13878  }
13879  yprev = yint;
13880  }
13881  }
13882  cpl_polynomial_delete(polytop);
13883  cpl_polynomial_delete(polybot);
13884  }
13885 
13886  return calibration;
13887 }
13888 
13889 
13952 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
13953  int maxradius, int conradius)
13954 {
13955  const char *func = "mos_detect_objects";
13956 
13957  cpl_image *profile;
13958  float *pdata;
13959  float *p;
13960 
13961  char name[MAX_COLNAME];
13962 
13963  int nslits;
13964  int npeaks;
13965  int nobjects, objpos, totobj;
13966  int maxobjects;
13967  int *position;
13968  int *length;
13969  int *reject;
13970  double *place;
13971  double *bright;
13972  double mindistance;
13973  int pos, count;
13974  int up;
13975  int low, hig;
13976  int row;
13977  int i, j, k;
13978 
13979  const int min_pixels = 10;
13980 
13981 
13982  if (cpl_error_get_code() != CPL_ERROR_NONE)
13983  return NULL;
13984 
13985  if (image == NULL || slits == NULL) {
13986  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13987  return NULL;
13988  }
13989 
13990  if (margin < 0)
13991  margin = 0;
13992 
13993  if (maxradius < 0) {
13994  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13995  return NULL;
13996  }
13997 
13998  if (conradius < 0) {
13999  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14000  return NULL;
14001  }
14002 
14003  nslits = cpl_table_get_nrow(slits);
14004  position = cpl_table_get_data_int(slits, "position");
14005  length = cpl_table_get_data_int(slits, "length");
14006 
14007  profile = cpl_image_collapse_create(image, 1);
14008  cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
14009  pdata = cpl_image_get_data(profile);
14010 
14011  row = 1;
14012  maxobjects = 0;
14013  totobj = 0;
14014  for (i = 0; i < nslits; i++) {
14015 
14016  if (length[i] == 0)
14017  continue;
14018 
14019  pos = position[i] + margin;
14020  count = length[i] - 2*margin;
14021 
14022  if (count < min_pixels)
14023  continue;
14024 
14025  p = pdata + pos;
14026 
14027 
14028  /*
14029  * Count peaks candidates
14030  */
14031 
14032  npeaks = 0;
14033  if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
14034  npeaks++;
14035  }
14036 
14037  up = 0;
14038  for (j = 0; j < count - 3; j++) {
14039  if (p[j] > 0) {
14040  if (p[j+1] > p[j]) {
14041  up++;
14042  }
14043  else {
14044  if (up > 2) {
14045  if (p[j+1] > p[j+2] && p[j+2] > 0) {
14046  if (p[j] > 5)
14047  npeaks++;
14048  }
14049  }
14050  else if (up > 1) {
14051  if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
14052  if (p[j] > 5)
14053  npeaks++;
14054  }
14055  }
14056  up = 0;
14057  }
14058  }
14059  else {
14060  up = 0;
14061  }
14062  }
14063 
14064  if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
14065  && p[count-3] > p[count-4] && p[count-4] > 0) {
14066  npeaks++;
14067  }
14068 
14069  if (npeaks == 0)
14070  continue;
14071 
14072 
14073  /*
14074  * Get candidates parameters
14075  */
14076 
14077  reject = cpl_calloc(npeaks, sizeof(int));
14078  bright = cpl_calloc(npeaks, sizeof(double));
14079  place = cpl_calloc(npeaks, sizeof(double));
14080 
14081  npeaks = 0;
14082  if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
14083  bright[0] = p[0];
14084  place[0] = position[i] + margin;
14085  npeaks++;
14086  }
14087 
14088  up = 0;
14089  for (j = 0; j < count - 3; j++) {
14090  if (p[j] > 0) {
14091  if (p[j+1] > p[j]) {
14092  up++;
14093  }
14094  else {
14095  if (up > 2) {
14096  if (p[j+1] > p[j+2] && p[j+2] > 0) {
14097  if (p[j] > 5) {
14098  bright[npeaks] = p[j];
14099  place[npeaks] = position[i] + margin + j + 1
14100  + values_to_dx(p[j-1], p[j], p[j+1]);
14101  npeaks++;
14102  }
14103  }
14104  }
14105  else if (up > 1) {
14106  if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
14107  if (p[j] > 5) {
14108  bright[npeaks] = p[j];
14109  place[npeaks] = position[i] + margin + j + 1
14110  + values_to_dx(p[j-1], p[j], p[j+1]);
14111  npeaks++;
14112  }
14113  }
14114  }
14115  up = 0;
14116  }
14117  }
14118  else {
14119  up = 0;
14120  }
14121  }
14122 
14123  if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
14124  && p[count-3] > p[count-4] && p[count-4] > 0) {
14125  bright[npeaks] = p[count-1];
14126  place[npeaks] = position[i] + count;
14127  npeaks++;
14128  }
14129 
14130 
14131  /*
14132  * Now select the uncontaminated peaks
14133  */
14134 
14135  if (fabs(place[0] - pos) < 1.0)
14136  reject[0] = 1;
14137  if (fabs(place[npeaks-1] - pos - count) < 1.0)
14138  reject[npeaks-1] = 1;
14139  for (j = 0; j < npeaks; j++) {
14140  for (k = 0; k < npeaks; k++) {
14141  if (k == j)
14142  continue;
14143  mindistance = conradius * bright[k] / bright[j]
14144  * bright[k] / bright[j];
14145  if (fabs(place[j] - place[k]) < mindistance)
14146  reject[j] = 1;
14147  }
14148  }
14149 
14150 /* new part */
14151  for (j = 0; j < npeaks; j++) {
14152  if (reject[j])
14153  continue;
14154  if (j) {
14155  low = (place[j-1]*bright[j] + place[j]*bright[j-1])
14156  / (bright[j-1] + bright[j]) + 1;
14157  }
14158  else {
14159  low = pos;
14160  }
14161  if (j < npeaks - 1) {
14162  hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
14163  / (bright[j+1] + bright[j]) + 1;
14164  }
14165  else {
14166  hig = pos + count;
14167  }
14168 
14169  if (low < pos)
14170  low = pos;
14171  if (hig > pos + count)
14172  hig = pos + count;
14173  if (place[j] - low > maxradius)
14174  low = place[j] - maxradius;
14175  if (hig - place[j] > maxradius)
14176  hig = place[j] + maxradius;
14177  if (hig == low)
14178  reject[j] = 1;
14179  }
14180 /* end new part */
14181 
14182  nobjects = npeaks;
14183  for (j = 0; j < npeaks; j++)
14184  if (reject[j])
14185  nobjects--;
14186 
14187  for (j = 0; j < nobjects; j++) {
14188  snprintf(name, MAX_COLNAME, "object_%d", j+1);
14189  if (cpl_table_has_column(slits, name))
14190  continue;
14191  cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
14192  snprintf(name, MAX_COLNAME, "start_%d", j+1);
14193  cpl_table_new_column(slits, name, CPL_TYPE_INT);
14194  cpl_table_set_column_unit(slits, name, "pixel");
14195  snprintf(name, MAX_COLNAME, "end_%d", j+1);
14196  cpl_table_new_column(slits, name, CPL_TYPE_INT);
14197  cpl_table_set_column_unit(slits, name, "pixel");
14198  snprintf(name, MAX_COLNAME, "row_%d", j+1);
14199  cpl_table_new_column(slits, name, CPL_TYPE_INT);
14200  cpl_table_set_column_unit(slits, name, "pixel");
14201  }
14202 
14203  objpos = nobjects;
14204  for (j = 0; j < npeaks; j++) {
14205  if (reject[j])
14206  continue;
14207  if (j) {
14208  low = (place[j-1]*bright[j] + place[j]*bright[j-1])
14209  / (bright[j-1] + bright[j]) + 1;
14210  }
14211  else {
14212  low = pos;
14213  }
14214  if (j < npeaks - 1) {
14215  hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
14216  / (bright[j+1] + bright[j]) + 1;
14217  }
14218  else {
14219  hig = pos + count;
14220  }
14221 
14222  if (low < pos)
14223  low = pos;
14224  if (hig > pos + count)
14225  hig = pos + count;
14226  if (place[j] - low > maxradius)
14227  low = place[j] - maxradius;
14228  if (hig - place[j] > maxradius)
14229  hig = place[j] + maxradius;
14230 
14231  snprintf(name, MAX_COLNAME, "object_%d", objpos);
14232  cpl_table_set_double(slits, name, i, place[j]);
14233  snprintf(name, MAX_COLNAME, "start_%d", objpos);
14234  cpl_table_set_int(slits, name, i, low);
14235  snprintf(name, MAX_COLNAME, "end_%d", objpos);
14236  cpl_table_set_int(slits, name, i, hig);
14237  snprintf(name, MAX_COLNAME, "row_%d", objpos);
14238  cpl_table_set_int(slits, name, i, row + objpos - 1);
14239  totobj++;
14240  objpos--;
14241  }
14242 
14243  row += nobjects;
14244 
14245  if (maxobjects < nobjects)
14246  maxobjects = nobjects;
14247 
14248  cpl_free(reject);
14249  cpl_free(bright);
14250  cpl_free(place);
14251 
14252  }
14253 
14254 /* nobjects = row - nobjects; A bug, I think... */
14255  row = cpl_table_get_nrow(slits);
14256 
14257  for (i = 0; i < row; i++) {
14258  for (j = 0; j < maxobjects; j++) {
14259  snprintf(name, MAX_COLNAME, "row_%d", j+1);
14260  if (cpl_table_is_valid(slits, name, i))
14261  cpl_table_set_int(slits, name, i, totobj -
14262  cpl_table_get_int(slits, name, i, NULL));
14263  }
14264  }
14265 
14266  for (i = 0; i < maxobjects; i++) {
14267  snprintf(name, MAX_COLNAME, "start_%d", i+1);
14268  cpl_table_fill_invalid_int(slits, name, -1);
14269  snprintf(name, MAX_COLNAME, "end_%d", i+1);
14270  cpl_table_fill_invalid_int(slits, name, -1);
14271  snprintf(name, MAX_COLNAME, "row_%d", i+1);
14272  cpl_table_fill_invalid_int(slits, name, -1);
14273  }
14274 
14275  return profile;
14276 }
14277 
14278 
14303 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *science_var,
14304  cpl_image *sky,
14305  cpl_table *objects, int extraction, double ron,
14306  double gain, int ncombined)
14307 {
14308  const char *func = "mos_extract_objects";
14309 
14310  char name[MAX_COLNAME];
14311 
14312  cpl_image **output;
14313  cpl_image *extracted;
14314  cpl_image *extr_sky;
14315  cpl_image *error;
14316  cpl_image *sciwin;
14317  cpl_image *sci_var_win = NULL;
14318  cpl_image *skywin;
14319  int nslits;
14320  int nobjects;
14321  int maxobjects;
14322  int nx;
14323  int ylow, yhig;
14324  int i, j;
14325 
14326 
14327  if (science == NULL || sky == NULL) {
14328  cpl_msg_error(func, "Both scientific exposures are required in input");
14329  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14330  return NULL;
14331  }
14332 
14333  if (objects == NULL) {
14334  cpl_msg_error(func, "An object table is required in input");
14335  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14336  return NULL;
14337  }
14338 
14339  if (extraction < 0 || extraction > 1) {
14340  cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
14341  "either 0 or 1", extraction);
14342  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14343  return NULL;
14344  }
14345 
14346  if (ron < 0.0) {
14347  cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
14348  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14349  return NULL;
14350  }
14351 
14352  if (gain < 0.1) {
14353  cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
14354  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14355  return NULL;
14356  }
14357 
14358  if (ncombined < 1) {
14359  cpl_msg_error(func, "Invalid number of combined frames (%d): "
14360  "it should be at least 1", ncombined);
14361  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14362  return NULL;
14363  }
14364 
14365 
14366  /*
14367  * Count the max number of objects per slit. Note that maxobjects
14368  * is intentionally the max number of objects increased by one.
14369  */
14370 
14371  maxobjects = 1;
14372  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
14373  while (cpl_table_has_column(objects, name)) {
14374  maxobjects++;
14375  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
14376  }
14377 
14378 
14379  /*
14380  * Count objects to extract
14381  */
14382 
14383  nobjects = 0;
14384  nslits = cpl_table_get_nrow(objects);
14385 
14386  for (i = 0; i < nslits; i++) {
14387  for (j = 1; j < maxobjects; j++) {
14388  snprintf(name, MAX_COLNAME, "object_%d", j);
14389  if (cpl_table_is_valid(objects, name, i))
14390  nobjects++;
14391  }
14392  }
14393 
14394  if (nobjects == 0)
14395  return NULL;
14396 
14397  nx = cpl_image_get_size_x(science);
14398 
14399  output = cpl_calloc(3, sizeof(cpl_image *));
14400  extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14401  extr_sky = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14402  error = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14403 
14404 
14405  /*
14406  * Extract objects
14407  */
14408 
14409  nobjects = 0;
14410  for (i = 0; i < nslits; i++) {
14411  for (j = 1; j < maxobjects; j++) {
14412  snprintf(name, MAX_COLNAME, "object_%d", j);
14413  if (cpl_table_is_valid(objects, name, i)) {
14414  snprintf(name, MAX_COLNAME, "start_%d", j);
14415  ylow = cpl_table_get_int(objects, name, i, NULL);
14416  snprintf(name, MAX_COLNAME, "end_%d", j);
14417  yhig = cpl_table_get_int(objects, name, i, NULL);
14418  snprintf(name, MAX_COLNAME, "row_%d", j);
14419  nobjects = cpl_table_get_int(objects, name, i, NULL);
14420  sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
14421  if(science_var != NULL)
14422  sci_var_win = cpl_image_extract(science_var, 1, ylow+1, nx, yhig);
14423  skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
14424 /*
14425  * Cleaning the cosmics locally was really NOT a good idea...
14426  * I leave it here, commented out, to never forget this mistake!
14427 
14428  if (extraction) {
14429  mos_clean_cosmics(sciwin, gain, -1., -1.);
14430  }
14431  */
14432  mos_extraction(sciwin, sci_var_win, skywin, extracted, extr_sky, error,
14433  nobjects, extraction, ron, gain, ncombined);
14434 
14435  /*
14436  * Hidden check whether the spectrum was saturated or not
14437  */
14438 
14439  {
14440  cpl_image *total = cpl_image_add_create(sciwin, skywin);
14441  float *data = cpl_image_get_data_float(total);
14442  int size = cpl_image_get_size_x(total)
14443  * cpl_image_get_size_y(total);
14444  int k;
14445  char *saturation_level = getenv("SATURATION_LEVEL");
14446  float saturation = 62000.0;
14447  char *max_saturated = getenv("MAX_SATURATED");
14448  int max_satur = 10;
14449  int saturated;
14450 
14451  if (saturation_level)
14452  saturation = atof(saturation_level);
14453 
14454  if (max_saturated)
14455  max_satur = atoi(max_saturated);
14456 
14457  saturated = 0;
14458  for (k = 0; k < size; k++) {
14459  if (data[k] > saturation) {
14460  saturated++;
14461  if (saturated > max_satur) {
14462  break;
14463  }
14464  }
14465  }
14466 
14467  if (saturated > max_satur)
14468  saturated = 1;
14469  else
14470  saturated = 0;
14471 
14472  data = cpl_image_get_data(extracted);
14473  data[nobjects * nx] = saturated;
14474  }
14475 
14476  cpl_image_delete(sciwin);
14477  cpl_image_delete(skywin);
14478  nobjects++;
14479  }
14480  }
14481  }
14482 
14483  return output;
14484 
14485 }
14486 
14487 
14510 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave,
14511  double dispersion, int saturation,
14512  double *mfwhm, double *rmsfwhm,
14513  double *resolution, double *rmsres, int *nlines)
14514 {
14515  cpl_vector *vector;
14516 
14517  int i, j, n, m;
14518  int position, maxpos;
14519  int xlen, ylen;
14520  int sp, ep;
14521  int radius;
14522  int sradius = 40;
14523  int threshold = 250; /* Peak must be so many ADUs above min */
14524 
14525  int ifwhm;
14526  double fwhm;
14527  double *buffer;
14528  double min, max, halfmax;
14529  double cut = 1.5; /* To cut outliers from FWHM values (pixel) */
14530  double value, rms;
14531 
14532  float *data;
14533 
14534 
14535  *resolution = 0.0;
14536  *rmsres = 0.0;
14537  *nlines = 0;
14538 
14539  xlen = cpl_image_get_size_x(image);
14540  ylen = cpl_image_get_size_y(image);
14541  data = cpl_image_get_data(image);
14542 
14543  buffer = cpl_malloc(ylen * sizeof(double));
14544 
14545  /*
14546  * Closest pixel to specified wavelength.
14547  */
14548 
14549  position = floor((lambda - startwave) / dispersion + 0.5);
14550 
14551  sp = position - sradius;
14552  ep = position + sradius;
14553 
14554  if (sp < 0 || ep > xlen) {
14555  cpl_free(buffer);
14556  return 0;
14557  }
14558 
14559  for (i = 0, n = 0; i < ylen; i++) { /* For each row of each slit */
14560 
14561  /*
14562  * Search interval for peak. Abort if too close to image border.
14563  */
14564 
14565  radius = mos_lines_width(data + i*xlen + position - sradius,
14566  2*sradius + 1);
14567  if (radius < 5)
14568  radius = 5;
14569 
14570  sp = position - radius;
14571  ep = position + radius;
14572 
14573  if (sp < 0 || ep > xlen) {
14574  cpl_free(buffer);
14575  return 0;
14576  }
14577 
14578 
14579  /*
14580  * Determine min-max value and position.
14581  */
14582 
14583  maxpos = sp;
14584  min = max = data[sp + i * xlen];
14585  for (j = sp; j < ep; j++) {
14586  if (data[j + i * xlen] > max) {
14587  max = data[j + i * xlen];
14588  maxpos = j;
14589  }
14590  if (data[j + i * xlen] < min) {
14591  min = data[j + i * xlen];
14592  }
14593  }
14594 
14595  if (fabs(min) < 0.0000001) /* Truncated spectrum */
14596  continue;
14597 
14598  if (max - min < threshold) /* Low signal... */
14599  continue;
14600 
14601  if (max > saturation) /* Saturation */
14602  continue;
14603 
14604  /*
14605  * Determine FWHM counting pixels with value greater than
14606  * half of the max value, to the right and to the left of
14607  * the max. Linear interpolation between the pixels where
14608  * the transition happens.
14609  */
14610 
14611  halfmax = (max + min)/ 2.0;
14612 
14613  fwhm = 0.0;
14614  ifwhm = 0;
14615  for (j = maxpos; j < maxpos + radius; j++) {
14616  if (j < xlen) {
14617  if (data[j + i * xlen] < halfmax) {
14618  fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
14619  / (data[j - 1 + i * xlen] - data[j + i * xlen]);
14620  break;
14621  }
14622  ifwhm++;
14623  }
14624  }
14625 
14626  ifwhm = 0;
14627  for (j = maxpos; j > maxpos - radius; j--) {
14628  if (j >= 0) {
14629  if (data[j + i * xlen] < halfmax) {
14630  fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
14631  / (data[j + 1 + i * xlen] - data[j + i * xlen]);
14632  break;
14633  }
14634  ifwhm++;
14635  }
14636  }
14637 
14638  if (fwhm > 3.0) {
14639  buffer[n] = fwhm - 2.0;
14640  n++;
14641  }
14642 
14643  }
14644 
14645  if (n == 0) {
14646  cpl_free(buffer);
14647  return 0;
14648  }
14649 
14650  vector = cpl_vector_wrap(n, buffer);
14651  value = cpl_vector_get_median_const(vector);
14652  cpl_vector_unwrap(vector);
14653 
14654  rms = 0.0;
14655  for (i = 0, m = 0; i < n; i++) {
14656  if (fabs(buffer[i] - value) < cut) {
14657  rms += fabs(buffer[i] - value);
14658  m++;
14659  }
14660  }
14661 
14662  cpl_free(buffer);
14663 
14664  if (m < 3)
14665  return 0;
14666 
14667  rms /= m;
14668  rms *= 1.25; /* Factor to convert average deviation to sigma */
14669 
14670  value *= dispersion;
14671  rms *= dispersion;
14672 
14673  *mfwhm = value;
14674  *rmsfwhm = rms;
14675 
14676  *resolution = lambda / value;
14677  *rmsres = *resolution * rms / value;
14678 
14679  *nlines = m;
14680 
14681  return 1;
14682 }
14683 
14684 
14706 cpl_table *mos_resolution_table(cpl_image *image, double startwave,
14707  double dispersion, int saturation,
14708  cpl_vector *lines)
14709 {
14710 
14711  cpl_table *table;
14712  double *line;
14713  double fwhm;
14714  double rmsfwhm;
14715  double resolution;
14716  double rmsres;
14717  int nref;
14718  int nlines;
14719  int i;
14720 
14721 
14722  nref = cpl_vector_get_size(lines);
14723  line = cpl_vector_get_data(lines);
14724 
14725  table = cpl_table_new(nref);
14726  cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
14727  cpl_table_set_column_unit(table, "wavelength", "Angstrom");
14728  cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
14729  cpl_table_set_column_unit(table, "fwhm", "Angstrom");
14730  cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
14731  cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
14732  cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
14733  cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
14734  cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
14735 
14736  for (i = 0; i < nref; i++) {
14737  if (mos_spectral_resolution(image, line[i], startwave, dispersion,
14738  saturation, &fwhm, &rmsfwhm,
14739  &resolution, &rmsres, &nlines)) {
14740  cpl_table_set_double(table, "wavelength", i, line[i]);
14741  cpl_table_set_double(table, "fwhm", i, fwhm);
14742  cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
14743  cpl_table_set_double(table, "resolution", i, resolution);
14744  cpl_table_set_double(table, "resolution_rms", i, rmsres);
14745  cpl_table_set_int(table, "nlines", i, nlines);
14746  }
14747  else
14748  cpl_table_set_int(table, "nlines", i, 0);
14749  }
14750 
14751  if (cpl_table_has_valid(table, "wavelength"))
14752  return table;
14753 
14754  cpl_table_delete(table);
14755 
14756  return NULL;
14757 
14758 }
14759 
14760 
14778 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
14779  int ystart, int yend, double wstart, double wend)
14780 {
14781  const char *func = "mos_integrate_signal";
14782 
14783  double sum;
14784  float *sdata;
14785  float *wdata;
14786  int nx, ny;
14787  int x, y;
14788 
14789 
14790  if (image == NULL || wavemap == NULL) {
14791  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14792  return 0.0;
14793  }
14794 
14795  if (ystart > yend || wstart >= wend) {
14796  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14797  return 0.0;
14798  }
14799 
14800  nx = cpl_image_get_size_x(image);
14801  ny = cpl_image_get_size_y(image);
14802 
14803  if (!(nx == cpl_image_get_size_x(wavemap)
14804  && ny == cpl_image_get_size_y(wavemap))) {
14805  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
14806  return 0.0;
14807  }
14808 
14809  if (ystart < 0 || yend > ny) {
14810  cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
14811  return 0.0;
14812  }
14813 
14814  sdata = cpl_image_get_data(image);
14815  wdata = cpl_image_get_data(wavemap);
14816 
14817  sdata += ystart*nx;
14818  wdata += ystart*nx;
14819 
14820  sum = 0.0;
14821  for (y = ystart; y < yend; y++) {
14822  for (x = 0; x < nx; x++) {
14823  if (wdata[x] < wstart || wdata[x] > wend)
14824  continue;
14825  sum += sdata[x];
14826  }
14827  sdata += nx;
14828  wdata += nx;
14829  }
14830 
14831  return sum;
14832 
14833 }
14834 
14835 /****************************************************************************
14836  * From this point on, the instrument dependent functions are added:
14837  * they are functions that retrieve information that is stored in
14838  * the data headers in some instrument specific way, such as the
14839  * location of overscans, the slits positions on the telescope
14840  * focal plane, the gain factor, etc.
14841  */
14842 
14843 
14866 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
14867 {
14868  const char *func = "mos_load_slits_fors_mxu";
14869 
14870  cpl_table *slits;
14871  char keyname[MAX_COLNAME];
14872  const char *instrume;
14873  const char *target_name;
14874  float slit_x;
14875  float slit_y;
14876  float length;
14877 /* double arc2mm = 0.53316; */
14878  double arc2mm = 0.528;
14879  int nslits;
14880  int slit_id;
14881  int fors;
14882  int chip;
14883  int found;
14884 
14885  /*
14886  * The limits below are used to exclude from the loaded slit list
14887  * any slit that surely doesn't belong to the used chip. This is
14888  * a way to reduce the chance of ambiguous slit identification.
14889  */
14890 
14891  float low_limit1 = 10.0;
14892  float hig_limit2 = 30.0;
14893 
14894 
14895  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14896  return NULL;
14897  }
14898 
14899  if (header == NULL) {
14900  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14901  return NULL;
14902  }
14903 
14904 
14905  /*
14906  * See if this is FORS1 or FORS2;
14907  */
14908 
14909  instrume = cpl_propertylist_get_string(header, "INSTRUME");
14910 
14911  fors = 0;
14912  if (instrume[4] == '1')
14913  fors = 1;
14914  if (instrume[4] == '2')
14915  fors = 2;
14916 
14917  if (fors != 2) {
14918  cpl_msg_error(func, "Wrong instrument: %s\n"
14919  "FORS2 is expected for MXU data", instrume);
14920  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14921  return NULL;
14922  }
14923 
14924 
14925  /*
14926  * The master and slave chips can be identified by their positions
14927  * in the chip array in the case of FORS2 data (with fors1 the chip
14928  * is always 1). chip = 2 is the master, chip = 1 is the slave.
14929  */
14930 
14931  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14932 
14933  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14934  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14935  "in FITS header");
14936  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14937  return NULL;
14938  }
14939 
14940  if (chip != 1 && chip != 2) {
14941  cpl_msg_error(func, "Unexpected chip position in keyword "
14942  "ESO DET CHIP1 Y: %d", chip);
14943  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14944  return NULL;
14945  }
14946 
14947 
14948  /*
14949  * Count slits in header (excluding reference slits, and the slits
14950  * that _surely_ belong to the other chip)
14951  */
14952 
14953  nslits = 0;
14954  slit_id = 0;
14955  found = 1;
14956 
14957  while (found) {
14958  slit_id++;
14959  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14960  if (cpl_propertylist_has(header, keyname)) {
14961  slit_y = cpl_propertylist_get_double(header, keyname);
14962 
14963  if (chip == 1)
14964  if (slit_y < low_limit1)
14965  continue;
14966  if (chip == 2)
14967  if (slit_y > hig_limit2)
14968  continue;
14969 
14970  snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME",
14971  slit_id + 100);
14972  if (cpl_propertylist_has(header, keyname)) {
14973  target_name = cpl_propertylist_get_string(header, keyname);
14974  if (strncmp(target_name, "refslit", 7))
14975  nslits++;
14976  }
14977  else
14978  nslits++;
14979  }
14980  else
14981  found = 0;
14982  }
14983 
14984  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14985  cpl_msg_error(func, "%s while loading slits coordinates from "
14986  "FITS header", cpl_error_get_message());
14987  cpl_error_set_where(func);
14988  return NULL;
14989  }
14990 
14991  if (nslits == 0) {
14992  cpl_msg_error(func, "No slits coordinates found in header");
14993  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14994  return NULL;
14995  }
14996 
14997  slits = cpl_table_new(nslits);
14998  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14999  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15000  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15001  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15002  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15003  cpl_table_set_column_unit(slits, "xtop", "pixel");
15004  cpl_table_set_column_unit(slits, "ytop", "pixel");
15005  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15006  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15007 
15008  nslits = 0;
15009  slit_id = 0;
15010  found = 1;
15011  while (found) {
15012  slit_id++;
15013  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
15014  if (cpl_propertylist_has(header, keyname)) {
15015  slit_y = cpl_propertylist_get_double(header, keyname);
15016 
15017  if (chip == 1)
15018  if (slit_y < low_limit1)
15019  continue;
15020  if (chip == 2)
15021  if (slit_y > hig_limit2)
15022  continue;
15023 
15024  /*
15025  * Y-flip the slit position, to match CCD pixel coordinate
15026  * convention
15027  */
15028 
15029  slit_y = -slit_y;
15030 
15031  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
15032  slit_x = cpl_propertylist_get_double(header, keyname);
15033  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15034  cpl_table_delete(slits);
15035  cpl_msg_error(func, "Missing keyword %s in FITS header",
15036  keyname);
15037  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15038  return NULL;
15039  }
15040 
15041  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
15042  length = cpl_propertylist_get_double(header, keyname);
15043  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15044  cpl_table_delete(slits);
15045  cpl_msg_error(func, "Missing keyword %s in FITS header",
15046  keyname);
15047  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15048  return NULL;
15049  }
15050 
15051  length *= arc2mm;
15052 
15053  snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME",
15054  slit_id + 100);
15055  if (cpl_propertylist_has(header, keyname)) {
15056  target_name = cpl_propertylist_get_string(header, keyname);
15057  if (strncmp(target_name, "refslit", 7)) {
15058  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15059  cpl_table_set(slits, "xtop", nslits, slit_x);
15060  cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15061  cpl_table_set(slits, "xbottom", nslits, slit_x);
15062  cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15063  nslits++;
15064  }
15065  }
15066  else {
15067  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15068  cpl_table_set(slits, "xtop", nslits, slit_x);
15069  cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15070  cpl_table_set(slits, "xbottom", nslits, slit_x);
15071  cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15072  nslits++;
15073  }
15074  }
15075  else
15076  found = 0;
15077  }
15078 
15079  return slits;
15080 }
15081 
15082 
15106 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header,
15107  int * nslits_out_det)
15108 {
15109  const char *func = "mos_load_slits_fors_mos";
15110 
15111  cpl_table *slits;
15112  char keyname[MAX_COLNAME];
15113  const char *instrume;
15114  const char *chipname;
15115  float slit_x;
15116  int first_slit, last_slit;
15117  cpl_size nslits;
15118  int slit_id;
15119  int fors;
15120  int chip;
15121  int fors_is_old;
15122 
15123  /*
15124  * The Y coordinates of the slits are fixed
15125  */
15126 
15127  float ytop[19] = { 113.9, 101.3, 89.9, 77.3, 65.9, 53.3,
15128  41.9, 29.3, 17.9, 5.3, -6.1, -18.7,
15129  -30.1, -42.7, -54.1, -66.7, -78.1, -90.7,
15130  -102.1 };
15131  float ybottom[19] = { 102.1, 90.7, 78.1, 66.7, 54.1, 42.7,
15132  30.1, 18.7, 6.1, -5.3, -17.9, -29.3,
15133  -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
15134  -113.9 };
15135 
15136 
15137  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15138  return NULL;
15139  }
15140 
15141  if (header == NULL) {
15142  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15143  return NULL;
15144  }
15145 
15146 
15147  /*
15148  * See if this is FORS1 or FORS2;
15149  */
15150 
15151  instrume = cpl_propertylist_get_string(header, "INSTRUME");
15152 
15153  fors = 0;
15154  if (instrume[4] == '1')
15155  fors = 1;
15156  if (instrume[4] == '2')
15157  fors = 2;
15158 
15159  if (fors == 0) {
15160  cpl_msg_error(func, "Wrong instrument found in FITS header: %s",
15161  instrume);
15162  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15163  return NULL;
15164  }
15165 
15166  /* FIXME:
15167  * This is the way FORS1 data belong to the upgraded chips,
15168  * named "Marlene" and "Norma III". It's a quick solution,
15169  * there are hardcoded values here!!!
15170  */
15171 
15172  chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
15173 
15174  if (chipname[0] == 'M' || chipname[0] == 'N')
15175  fors_is_old = 0;
15176  else
15177  fors_is_old = 1;
15178 
15179  if (fors == 1 && fors_is_old) {
15180  first_slit = 1;
15181  last_slit = 19;
15182  }
15183  else {
15184 
15185  /*
15186  * The master and slave chips can be identified by their positions
15187  * in the chip array in the case of FORS2 data: chip = 2 is the
15188  * master, chip = 1 is the slave.
15189  */
15190 
15191  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15192 
15193  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15194  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15195  "in FITS header");
15196  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15197  return NULL;
15198  }
15199 
15200  if (chip != 1 && chip != 2) {
15201  cpl_msg_error(func, "Unexpected chip position in keyword "
15202  "ESO DET CHIP1 Y: %d", chip);
15203  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15204  return NULL;
15205  }
15206 
15207  if (chip == 1) {
15208  first_slit = 12;
15209  last_slit = 19;
15210  }
15211  else {
15212  first_slit = 1;
15213  last_slit = 11;
15214  }
15215  }
15216 
15217 
15218  /*
15219  * Count slits in header (excluding closed slits - i.e. those with
15220  * offsets greater than 115 mm - and the slits that do not belong
15221  * to this chip)
15222  */
15223 
15224  nslits = 0;
15225  slit_id = 0;
15226  for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15227  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15228  if (cpl_propertylist_has(header, keyname)) {
15229  slit_x = cpl_propertylist_get_double(header, keyname);
15230  if (fabs(slit_x) < 115.0)
15231  nslits++;
15232  else
15233  (*nslits_out_det)++;
15234  }
15235  else {
15236  cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
15237  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15238  return NULL;
15239  }
15240  }
15241 
15242  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15243  cpl_msg_error(func, "%s while loading slits coordinates from "
15244  "FITS header", cpl_error_get_message());
15245  cpl_error_set_where(func);
15246  return NULL;
15247  }
15248 
15249  if (nslits == 0) {
15250  cpl_msg_error(func, "No slits coordinates found in header");
15251  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15252  return NULL;
15253  }
15254 
15255  slits = cpl_table_new(nslits);
15256  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15257  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15258  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15259  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15260  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15261  cpl_table_set_column_unit(slits, "xtop", "pixel");
15262  cpl_table_set_column_unit(slits, "ytop", "pixel");
15263  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15264  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15265 
15266  nslits = 0;
15267  slit_id = 0;
15268  for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15269  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15270  slit_x = cpl_propertylist_get_double(header, keyname);
15271  if (fabs(slit_x) < 115.0) {
15272  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15273  cpl_table_set(slits, "xtop", nslits, slit_x);
15274  cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
15275  cpl_table_set(slits, "xbottom", nslits, slit_x);
15276  cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
15277  nslits++;
15278  }
15279  }
15280 
15281  return slits;
15282 }
15283 
15284 
15308 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
15309 {
15310  const char *func = "mos_load_slits_fors_lss";
15311 
15312  cpl_table *slits;
15313  char *slit_name;
15314  const char *instrume;
15315  int fors;
15316  int chip;
15317  float ytop;
15318  float ybottom;
15319 
15320  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15321  return NULL;
15322  }
15323 
15324  if (header == NULL) {
15325  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15326  return NULL;
15327  }
15328 
15329 
15330  /*
15331  * See if this is FORS1 or FORS2;
15332  */
15333 
15334  instrume = cpl_propertylist_get_string(header, "INSTRUME");
15335 
15336  fors = 0;
15337  if (instrume[4] == '1')
15338  fors = 1;
15339  if (instrume[4] == '2')
15340  fors = 2;
15341 
15342  if (fors == 0) {
15343  cpl_msg_error(func, "Wrong instrument found in FITS header: %s",
15344  instrume);
15345  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15346  return NULL;
15347  }
15348 
15349  if (fors == 1) {
15350  ytop = 109.94;
15351  ybottom = -109.94;
15352  }
15353  else {
15354 
15355  /*
15356  * The master and slave chips can be identified by their positions
15357  * in the chip array in the case of FORS2 data: chip = 2 is the
15358  * master, chip = 1 is the slave.
15359  */
15360 
15361  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15362 
15363  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15364  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15365  "in FITS header");
15366  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15367  return NULL;
15368  }
15369 
15370  if (chip != 1 && chip != 2) {
15371  cpl_msg_error(func, "Unexpected chip position in keyword "
15372  "ESO DET CHIP1 Y: %d", chip);
15373  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15374  return NULL;
15375  }
15376 
15377  if (chip == 1) {
15378  ytop = 30.0;
15379  ybottom = -109.94;
15380  }
15381  else {
15382  ytop = 109.94;
15383  ybottom = -20.0;
15384  }
15385  }
15386 
15387 
15388  slits = cpl_table_new(1);
15389  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15390  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15391  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15392  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15393  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15394  cpl_table_set_column_unit(slits, "xtop", "pixel");
15395  cpl_table_set_column_unit(slits, "ytop", "pixel");
15396  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15397  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15398 
15399  slit_name = (char *)cpl_propertylist_get_string(header,
15400  "ESO INS SLIT NAME");
15401 
15402  cpl_table_set(slits, "ytop", 0, ytop);
15403  cpl_table_set(slits, "ybottom", 0, ybottom);
15404 
15405  if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
15406  cpl_table_set_int(slits, "slit_id", 0, 1);
15407  cpl_table_set(slits, "xbottom", 0, -0.075);
15408  cpl_table_set(slits, "xtop", 0, 0.075);
15409  }
15410  else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
15411  cpl_table_set_int(slits, "slit_id", 0, 2);
15412  cpl_table_set(slits, "xbottom", 0, 5.895);
15413  cpl_table_set(slits, "xtop", 0, 6.105);
15414  }
15415  else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
15416  cpl_table_set_int(slits, "slit_id", 0, 3);
15417  cpl_table_set(slits, "xbottom", 0, -6.135);
15418  cpl_table_set(slits, "xtop", 0, -5.865);
15419  }
15420  else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
15421  cpl_table_set_int(slits, "slit_id", 0, 4);
15422  cpl_table_set(slits, "xbottom", 0, 11.815);
15423  cpl_table_set(slits, "xtop", 0, 12.185);
15424  }
15425  else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
15426  cpl_table_set_int(slits, "slit_id", 0, 5);
15427  cpl_table_set(slits, "xbottom", 0, -12.265);
15428  cpl_table_set(slits, "xtop", 0, -11.735);
15429  }
15430  else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
15431  cpl_table_set_int(slits, "slit_id", 0, 6);
15432  cpl_table_set(slits, "xbottom", 0, 17.655);
15433  cpl_table_set(slits, "xtop", 0, 18.345);
15434  }
15435  else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
15436  cpl_table_set_int(slits, "slit_id", 0, 7);
15437  cpl_table_set(slits, "xbottom", 0, -18.425);
15438  cpl_table_set(slits, "xtop", 0, -17.575);
15439  }
15440  else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
15441  cpl_table_set_int(slits, "slit_id", 0, 8);
15442  cpl_table_set(slits, "xbottom", 0, 23.475);
15443  cpl_table_set(slits, "xtop", 0, 24.525);
15444  }
15445  else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
15446  cpl_table_set_int(slits, "slit_id", 0, 9);
15447  cpl_table_set(slits, "xbottom", 0, -24.66);
15448  cpl_table_set(slits, "xtop", 0, -23.34);
15449  }
15450  else {
15451  cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
15452  slit_name);
15453  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15454  cpl_table_delete(slits);
15455  return NULL;
15456  }
15457 
15458  return slits;
15459 }
15460 
15461 
15476 double mos_get_gain_vimos(cpl_propertylist *header)
15477 {
15478  const char *func = "mos_get_gain_vimos";
15479 
15480  double gain = -1.0;
15481 
15482 
15483  if (cpl_error_get_code() != CPL_ERROR_NONE)
15484  return gain;
15485 
15486  if (header == NULL) {
15487  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15488  return gain;
15489  }
15490 
15491  gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
15492  if (cpl_error_get_code()) {
15493  cpl_error_set_where(func);
15494  gain = -1.0;
15495  }
15496 
15497  return gain;
15498 
15499 }
15500 
15501 
15521 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
15522 {
15523  const char *func = "mos_load_slits_vimos";
15524 
15525  cpl_table *slits;
15526  char keyname[MAX_COLNAME];
15527  float slit_x;
15528  float slit_y;
15529  float dim_x;
15530  float dim_y;
15531  int nslits;
15532  int slit_id;
15533  int curved;
15534  int i;
15535 
15536 
15537  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15538  return NULL;
15539  }
15540 
15541  if (header == NULL) {
15542  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15543  return NULL;
15544  }
15545 
15546  nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
15547 
15548  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15549  cpl_error_set_where(func);
15550  return NULL;
15551  }
15552 
15553  slits = cpl_table_new(nslits);
15554  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15555  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15556  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15557  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15558  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15559  cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15560  cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE);
15561  cpl_table_new_column(slits, "curved", CPL_TYPE_INT);
15562  cpl_table_set_column_unit(slits, "xtop", "pixel");
15563  cpl_table_set_column_unit(slits, "ytop", "pixel");
15564  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15565  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15566  cpl_table_set_column_unit(slits, "xwidth", "mm");
15567  cpl_table_set_column_unit(slits, "ywidth", "mm");
15568 
15569  for (i = 0; i < nslits; i++) {
15570  sprintf(keyname, "ESO INS SLIT%d ID", i+1);
15571  slit_id = cpl_propertylist_get_int(header, keyname);
15572  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15573  cpl_error_set_where(func);
15574  return NULL;
15575  }
15576  sprintf(keyname, "ESO INS SLIT%d X", i+1);
15577  slit_x = cpl_propertylist_get_double(header, keyname);
15578  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15579  cpl_error_set_where(func);
15580  return NULL;
15581  }
15582  sprintf(keyname, "ESO INS SLIT%d Y", i+1);
15583  slit_y = cpl_propertylist_get_double(header, keyname);
15584  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15585  cpl_error_set_where(func);
15586  return NULL;
15587  }
15588  sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
15589  dim_x = cpl_propertylist_get_double(header, keyname);
15590  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15591  cpl_error_set_where(func);
15592  return NULL;
15593  }
15594 
15595  sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1);
15596  if (cpl_propertylist_has(header, keyname)) {
15597  curved = 1;
15598  }
15599  else {
15600  sprintf(keyname, "ESO INS SLIT%d DIMY", i+1);
15601  curved = 0;
15602  }
15603  dim_y = cpl_propertylist_get_double(header, keyname);
15604  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15605  cpl_error_set_where(func);
15606  return NULL;
15607  }
15608 
15609  cpl_table_set_int(slits, "slit_id", i, slit_id);
15610  cpl_table_set(slits, "xtop", i, slit_x - dim_x/2);
15611  cpl_table_set(slits, "ytop", i, slit_y);
15612  cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2);
15613  cpl_table_set(slits, "ybottom", i, slit_y);
15614  cpl_table_set(slits, "xwidth", i, dim_x);
15615  cpl_table_set(slits, "ywidth", i, dim_y);
15616  cpl_table_set_int(slits, "curved", i, curved);
15617  }
15618 
15619  return slits;
15620 }
15621 
15622 
15632 int mos_check_multiplex(cpl_table *slits)
15633 {
15634  cpl_propertylist *sort;
15635  int nrow;
15636  int i, multiplex, xprev, xcur;
15637  double prev, cur;
15638  double tolerance = 1.0; // About spatially aligned slits (mm)
15639 
15640 
15641  /*
15642  * Create an auxiliary column containing a sort of integer
15643  * x coordinate of the slit, to guarantee that slits at the
15644  * same spatial offset are recognised immediately as in spectral
15645  * multiplexing.
15646  */
15647 
15648  sort = cpl_propertylist_new();
15649  cpl_propertylist_append_bool(sort, "xtop", 0);
15650  cpl_table_sort(slits, sort);
15651  cpl_propertylist_delete(sort);
15652 
15653  prev = cpl_table_get_double(slits, "xtop", 0, NULL);
15654  cpl_table_new_column(slits, "xind", CPL_TYPE_INT);
15655  cpl_table_set_int(slits, "xind", 0, prev); // cast to int is intentional
15656  nrow = cpl_table_get_nrow(slits);
15657  for (i = 1; i < nrow; i++) {
15658  cur = cpl_table_get_double(slits, "xtop", i, NULL);
15659  if (fabs(prev - cur) > tolerance)
15660  prev = cur;
15661  cpl_table_set_int(slits, "xind", i, prev);
15662  }
15663 
15664  /*
15665  * Now sort according to increasing (integer) x positions, and when
15666  * those are equal (multiplexed) according to the increasing y position.
15667  */
15668 
15669  sort = cpl_propertylist_new();
15670  cpl_propertylist_append_bool(sort, "xind", 0);
15671  cpl_propertylist_append_bool(sort, "ytop", 0);
15672  cpl_table_sort(slits, sort);
15673  cpl_propertylist_delete(sort);
15674 
15675  /*
15676  * Now assign to each slit its multiplex order.
15677  */
15678 
15679  multiplex = 0;
15680  cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT);
15681  xprev = cpl_table_get_int(slits, "xind", 0, NULL);
15682  cpl_table_set_int(slits, "multiplex", 0, multiplex);
15683  nrow = cpl_table_get_nrow(slits);
15684  for (i = 1; i < nrow; i++) {
15685  xcur = cpl_table_get_int(slits, "xind", i, NULL);
15686  if (xcur == xprev) {
15687  multiplex++;
15688  }
15689  else {
15690  xprev = xcur;
15691  multiplex = 0;
15692  }
15693  cpl_table_set_int(slits, "multiplex", i, multiplex);
15694  }
15695 
15696  cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT);
15697 
15698  cpl_table_erase_column(slits, "xind");
15699 
15700  return 1 + cpl_table_get_column_max(slits, "multiplex");
15701 
15702 }
15703 
15704 
15731 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header,
15732  int check_consistency)
15733 {
15734  const char *func = "mos_load_overscans_vimos";
15735 
15736  int nx = 0;
15737  int ny = 0;
15738  int px = 0;
15739  int py = 0;
15740  int ox = 0;
15741  int oy = 0;
15742  int vx = 0;
15743  int vy = 0;
15744  int nrows;
15745  cpl_table *overscans;
15746 
15747 
15748  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15749  cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15750  return NULL;
15751  }
15752 
15753  if (header == NULL) {
15754  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15755  return NULL;
15756  }
15757 
15758  if (cpl_propertylist_has(header, "NAXIS1"))
15759  nx = cpl_propertylist_get_int(header, "NAXIS1");
15760  if (cpl_propertylist_has(header, "NAXIS2"))
15761  ny = cpl_propertylist_get_int(header, "NAXIS2");
15762  if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
15763  px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
15764  if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
15765  py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
15766  if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
15767  ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
15768  if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
15769  oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
15770  if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
15771  vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
15772  if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
15773  vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
15774 
15775  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15776  cpl_msg_error(func, "Missing overscan keywords in header");
15777  cpl_error_set_where(func);
15778  return NULL;
15779  }
15780 
15781  if (px < 0 || py < 0 || ox < 0 || oy < 0) {
15782  cpl_msg_error(func, "Missing overscan keywords in header");
15783  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15784  return NULL;
15785  }
15786 
15787  if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
15788  if (check_consistency) {
15789  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15790  return NULL;
15791  }
15792  else {
15793  cpl_msg_debug(func, "Overscans description conflicts with "
15794  "reported image sizes, "
15795  "%d + %d + %d != %d or "
15796  "%d + %d + %d != %d",
15797  px, vx, ox, nx,
15798  py, vy, oy, ny);
15799  }
15800  }
15801 
15802  nrows = 0;
15803  if (px > 0)
15804  nrows++;
15805  if (ox > 0)
15806  nrows++;
15807  if (py > 0)
15808  nrows++;
15809  if (oy > 0)
15810  nrows++;
15811 
15812  if (nrows > 2) {
15813  cpl_msg_error(func, "Unexpected overscan regions "
15814  "(both in X and Y direction)");
15815  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15816  return NULL;
15817  }
15818 
15819 
15820  /*
15821  * A row is added for the description of the valid region of the
15822  * exposure the input header belongs to.
15823  */
15824 
15825  nrows++;
15826 
15827  overscans = cpl_table_new(nrows);
15828  cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15829  cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15830  cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15831  cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15832 
15833  nrows = 0;
15834 
15835  cpl_table_set_int(overscans, "xlow", nrows, px);
15836  cpl_table_set_int(overscans, "ylow", nrows, py);
15837  cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15838  cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15839  nrows++;
15840 
15841  if (px > 0) {
15842  cpl_table_set_int(overscans, "xlow", nrows, 0);
15843  cpl_table_set_int(overscans, "ylow", nrows, 0);
15844  cpl_table_set_int(overscans, "xhig", nrows, px);
15845  cpl_table_set_int(overscans, "yhig", nrows, ny);
15846  nrows++;
15847  }
15848 
15849  if (ox > 0) {
15850  cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15851  cpl_table_set_int(overscans, "ylow", nrows, 0);
15852  cpl_table_set_int(overscans, "xhig", nrows, nx);
15853  cpl_table_set_int(overscans, "yhig", nrows, ny);
15854  nrows++;
15855  }
15856 
15857  if (py > 0) {
15858  cpl_table_set_int(overscans, "xlow", nrows, 0);
15859  cpl_table_set_int(overscans, "ylow", nrows, 0);
15860  cpl_table_set_int(overscans, "xhig", nrows, nx);
15861  cpl_table_set_int(overscans, "yhig", nrows, py);
15862  nrows++;
15863  }
15864 
15865  if (oy > 0) {
15866  cpl_table_set_int(overscans, "xlow", nrows, 0);
15867  cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
15868  cpl_table_set_int(overscans, "xhig", nrows, nx);
15869  cpl_table_set_int(overscans, "yhig", nrows, ny);
15870  nrows++;
15871  }
15872 
15873  return overscans;
15874 
15875 }
15876 
15877 
15878 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
15879 {
15880  const char *func = "mos_load_overscans_fors";
15881 
15882  int nports;
15883  int nx = 0;
15884  int ny = 0;
15885  int px = 0;
15886  int py = 0;
15887  int ox = 0;
15888  int oy = 0;
15889  int rebin;
15890  int nrows;
15891  cpl_table *overscans;
15892 
15893 
15894  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15895  cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15896  return NULL;
15897  }
15898 
15899  if (header == NULL) {
15900  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15901  return NULL;
15902  }
15903 
15904  if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
15905  nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
15906 
15907  if (nports == 4 &&
15908  cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
15909  cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
15910 
15911  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
15912 
15913  overscans = cpl_table_new(3);
15914  cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15915  cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15916  cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15917  cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15918 
15919  px = 16 / rebin;
15920  ox = 16 / rebin;
15921  nx = 2080 / rebin;
15922  ny = 2048 / rebin;
15923  nrows = 0;
15924 
15925  cpl_table_set_int(overscans, "xlow", nrows, px);
15926  cpl_table_set_int(overscans, "ylow", nrows, py);
15927  cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15928  cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15929  nrows++;
15930 
15931  cpl_table_set_int(overscans, "xlow", nrows, 0);
15932  cpl_table_set_int(overscans, "ylow", nrows, 0);
15933  cpl_table_set_int(overscans, "xhig", nrows, px);
15934  cpl_table_set_int(overscans, "yhig", nrows, ny);
15935  nrows++;
15936 
15937  cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15938  cpl_table_set_int(overscans, "ylow", nrows, 0);
15939  cpl_table_set_int(overscans, "xhig", nrows, nx);
15940  cpl_table_set_int(overscans, "yhig", nrows, ny);
15941  nrows++;
15942  }
15943  else {
15944  overscans = mos_load_overscans_vimos(header, 0);
15945  }
15946 
15947  return overscans;
15948 
15949 }
15950 
15982 #define READY 1
15983 #ifdef READY
15984 
15985 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate,
15986  int samples, int order)
15987 {
15988 
15989  const char *func = "mos_montecarlo_polyfit";
15990 
15991  cpl_polynomial *p;
15992  cpl_polynomial *q;
15993  cpl_vector *listx;
15994  cpl_vector *listy;
15995  double err;
15996  double *x;
15997  double *px;
15998  double *x_eval;
15999  double *px_eval;
16000  double *sigma;
16001  double *vy;
16002  double *dy;
16003  int npoints, nevaluate;
16004  int i, j;
16005 
16006 
16007  if (points == NULL || evaluate == NULL) {
16008  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
16009  return NULL;
16010  }
16011 
16012  if (!cpl_table_has_column(points, "x")) {
16013  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16014  return NULL;
16015  }
16016 
16017  if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
16018  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16019  return NULL;
16020  }
16021 
16022  if (cpl_table_has_invalid(points, "x")) {
16023  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16024  return NULL;
16025  }
16026 
16027  if (!cpl_table_has_column(points, "y")) {
16028  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16029  return NULL;
16030  }
16031 
16032  if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
16033  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16034  return NULL;
16035  }
16036 
16037  if (cpl_table_has_invalid(points, "y")) {
16038  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16039  return NULL;
16040  }
16041 
16042  if (cpl_table_has_column(points, "y_err")) {
16043 
16044  if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
16045  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16046  return NULL;
16047  }
16048 
16049  if (cpl_table_has_invalid(points, "y_err")) {
16050  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16051  return NULL;
16052  }
16053  }
16054 
16055  if (!cpl_table_has_column(evaluate, "x")) {
16056  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16057  return NULL;
16058  }
16059 
16060  if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
16061  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16062  return NULL;
16063  }
16064 
16065  if (cpl_table_has_invalid(evaluate, "x")) {
16066  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16067  return NULL;
16068  }
16069 
16070  if (samples < 2 || order < 0) {
16071  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16072  return NULL;
16073  }
16074 
16075  npoints = cpl_table_get_nrow(points);
16076  listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
16077  listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
16078 
16079  p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
16080 
16081  if (!cpl_table_has_column(points, "y_err")) {
16082  err = sqrt(err);
16083  cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
16084  cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
16085  cpl_msg_info(func, "Error column not found - set to %f\n", err);
16086  }
16087 
16088  /*
16089  * Create columns containing modeled values at each x
16090  */
16091 
16092  if (cpl_table_has_column(points, "px"))
16093  cpl_table_erase_column(points, "px");
16094  cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
16095  cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
16096  x = cpl_table_get_data_double(points, "x");
16097  px = cpl_table_get_data_double(points, "px");
16098  for (i = 0; i < npoints; i++)
16099  px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
16100 
16101  nevaluate = cpl_table_get_nrow(evaluate);
16102 
16103  if (cpl_table_has_column(evaluate, "px"))
16104  cpl_table_erase_column(evaluate, "px");
16105  cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
16106  cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
16107  x_eval = cpl_table_get_data_double(evaluate, "x");
16108  px_eval = cpl_table_get_data_double(evaluate, "px");
16109  for (i = 0; i < nevaluate; i++)
16110  px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
16111 
16112  /*
16113  * Initialise column with sigma
16114  */
16115 
16116  if (cpl_table_has_column(evaluate, "sigma"))
16117  cpl_table_erase_column(evaluate, "sigma");
16118  cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
16119  cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
16120  sigma = cpl_table_get_data_double(evaluate, "sigma");
16121 
16122  /*
16123  * Compute varied y cordinates to fit
16124  */
16125 
16126  if (cpl_table_has_column(points, "vy"))
16127  cpl_table_erase_column(points, "vy");
16128  cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
16129  cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
16130  vy = cpl_table_get_data_double(points, "vy");
16131  dy = cpl_table_get_data_double(points, "y_err");
16132  cpl_vector_unwrap(listy);
16133  listy = cpl_vector_wrap(npoints, vy);
16134 
16135  for (i = 0; i < samples; i++) {
16136  for (j = 0; j < npoints; j++)
16137  vy[j] = px[j] + dy[j] * mos_randg(1);
16138  q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
16139  for (j = 0; j < nevaluate; j++)
16140  sigma[j] += fabs(px_eval[j]
16141  - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
16142  cpl_polynomial_delete(q);
16143  }
16144 
16145  /*
16146  * Factor 1.25 to convert average deviation to sigma
16147  */
16148 
16149  cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
16150  cpl_table_divide_scalar(evaluate, "sigma", samples);
16151 
16152  cpl_vector_unwrap(listx);
16153  cpl_vector_unwrap(listy);
16154 
16155  return p;
16156 }
16157 
16158 #endif
16159 
16182 cpl_error_code mos_randomise_image(cpl_image *image, double ron,
16183  double gain, double bias)
16184 {
16185  float *data;
16186  int npix, i;
16187 
16188 
16189  if (image == NULL)
16190  return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
16191 
16192  if (ron < 0.0 || gain <= FLT_EPSILON)
16193  return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
16194 
16195  data = cpl_image_get_data_float(image);
16196  npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image);
16197  ron *= ron;
16198 
16199  for (i = 0; i < npix; i++) {
16200  if (data[i] < bias) {
16201  data[i] += sqrt(ron) * mos_randg(1);
16202  }
16203  else {
16204  data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1);
16205  }
16206  }
16207 
16208  return CPL_ERROR_NONE;
16209 }
16210 
16211 
16226 cpl_error_code mos_refmask_find_gaps(cpl_mask *refmask,
16227  cpl_image *master_flat,
16228  double level)
16229 {
16230  int nx = cpl_mask_get_size_x(refmask);
16231  int ny = cpl_mask_get_size_y(refmask);
16232 
16233  int * xpos = cpl_calloc(sizeof(int), ny);
16234 
16235  cpl_image * filtered = cpl_image_duplicate(master_flat);
16236  cpl_mask * kernel = cpl_mask_new(9, 3);
16237  cpl_vector * v = cpl_vector_new(ny);
16238  cpl_vector * truev;
16239  int nvalid = 0;
16240  double * flats = cpl_vector_get_data(v);
16241 
16242  double median, stdev, delta;
16243 
16244  int i, kill;
16245 
16246  cpl_mask_not(kernel);
16247  cpl_image_filter_mask(filtered, master_flat, kernel,
16248  CPL_FILTER_MEDIAN, CPL_BORDER_COPY);
16249  cpl_mask_delete(kernel);
16250 
16251  for (i = 1; i <= ny; i++) {
16252  int j = 0;
16253 
16254  do j++;
16255  while (!cpl_mask_get(refmask, j, i) && j < nx);
16256 
16257  if (j < nx) {
16258  int rejected;
16259 
16260  xpos[i - 1] = j;
16261  flats[nvalid] = cpl_image_get(filtered, j, i, &rejected);
16262  nvalid++;
16263  }
16264  else {
16265  xpos[i - 1] = -1;
16266  }
16267  }
16268 
16269  if (nvalid == 0)
16270  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16271 
16272  truev = cpl_vector_wrap(nvalid, flats);
16273 
16274  median = cpl_vector_get_median(truev);
16275 
16276  if (level < 0.0)
16277  stdev = cpl_vector_get_stdev(truev);
16278 
16279  cpl_vector_unwrap(truev);
16280  cpl_vector_delete(v);
16281 
16282  for (i = 1; i <= ny; i++) {
16283  if (xpos[i - 1] > 0) {
16284  int rejected;
16285  double kappa = 1.5;
16286 
16287  delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median;
16288 
16289  if (level < 0.0)
16290  kill = fabs(delta) > stdev * kappa;
16291  else
16292  kill = delta < level;
16293 
16294  if (kill) {
16295  int j = 0;
16296 
16297  while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
16298  cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
16299  j++;
16300  }
16301  }
16302  }
16303  }
16304 
16305  cpl_image_delete(filtered);
16306  cpl_free(xpos);
16307 
16308  return cpl_error_get_code();
16309 }
16310 
16318 cpl_error_code mos_saturation_process(cpl_image * image)
16319 {
16320  int nx = cpl_image_get_size_x(image);
16321  int ny = cpl_image_get_size_y(image);
16322  int npix = nx * ny;
16323  float * sdata = cpl_image_get_data_float(image);
16324 
16325  int count, i, j, k;
16326 
16327  /*
16328  * This is used to avoid saturation level coded with pixel value zero
16329  * To make it more robust against random 0.0 values, check that also
16330  * next pixel along the spatial direction is 0.0.
16331  */
16332 
16333  //This could be applied only to raw images, but it is being applied
16334  //to already bias/overscan processed images, which doesn't make sense.
16335 // for (i = 0; i < npix - nx; i++)
16336 // if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
16337 // sdata[i] = 65535.0;
16338 
16339 // for (i = npix - nx; i < npix; i++)
16340 // if (sdata[i] == 0.0)
16341 // sdata[i] = 65535.0;
16342 
16343  /*
16344  * This is a dirty trick to overcome saturations (making up a false
16345  * tip on their flat tops). This should be useless with a better
16346  * peak detection algorithm.
16347  */
16348 
16349  for (i = 0; i < npix; i++) {
16350  if (sdata[i] >= 65535.0) {
16351  count = 0;
16352  for (j = i; j < npix; j++) {
16353  if (sdata[j] < 65535.0) {
16354  break;
16355  }
16356  else {
16357  count++;
16358  }
16359  }
16360  if (count < 30 && count > 2) {
16361  for (j = i; j < i + count/2; j++)
16362  sdata[j] = sdata[i] + 1000.0 * (j - i);
16363  if (count % 2 != 0) {
16364  sdata[j] = sdata[j-1] + 1000.0;
16365  j++;
16366  }
16367  for (k = j; k <= i + count; k++)
16368  sdata[k] = sdata[i] - 1000.0 * (k - i - count);
16369  i = k;
16370  }
16371  }
16372  }
16373 
16374  return cpl_error_get_code();
16375 }
16376 
16377 
16386 cpl_error_code mos_subtract_background(cpl_image * image)
16387 {
16388  /*
16389  * Create and subtract background
16390  */
16391 
16392  cpl_image * bimage = mos_arc_background(image, 15, 15);
16393  cpl_image_subtract(image, bimage);
16394  cpl_image_delete(bimage);
16395 
16396  return cpl_error_get_code();
16397 }
16398 
16399 
16416 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits,
16417  int nscience, float tolerance)
16418 {
16419  int i, j;
16420 
16421  cpl_table *summary;
16422  int summary_nobjs = 0;
16423 
16424  int nobjs;
16425 
16426  int nmatches;
16427  int nslits = cpl_table_get_nrow(slitss[0]);
16428 
16429  int maxobjs;
16430  int k, m;
16431  int nstokes, sstokes;
16432 
16433  cpl_table **work;
16434 
16435  work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
16436 
16437 
16438  /*
16439  * First we build a table listing the offset of each detected
16440  * object at each angle and each beam, from the bottom of each
16441  * slit spectrum, and the pair that slit spectrum belongs to.
16442  * This summary table will have as many rows as objects found
16443  * in total at all angles.
16444  */
16445 
16446  for (j = 0; j < nscience; j++) {
16447  int c_nobjs = mos_get_nobjects(slitss[j]);
16448  if (!c_nobjs)
16449  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16450  summary_nobjs += c_nobjs;
16451  }
16452 
16453  summary = cpl_table_new(summary_nobjs);
16454 
16455  cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
16456  cpl_table_new_column(summary, "pair", CPL_TYPE_INT);
16457  cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE);
16458  cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE);
16459 
16460  /*
16461  * Fill the summary table with data from all objects:
16462  */
16463 
16464  nobjs = 0;
16465 
16466  /* Loop on all object tables (one for each angle) */
16467  for (j = 0; j < nscience; j++) {
16468  int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
16469 
16470  /* Loop on all slits found on first - i.e., ALL - object table */
16471  for (k = 0; k < nslits; k++) {
16472 
16473  /* Loop on all objects found on each object table */
16474  for (m = 0; m < c_maxobjs; m++) {
16475  int null;
16476  char *name = cpl_sprintf("object_%d", m + 1);
16477  double obj = cpl_table_get_double(slitss[j], name, k, &null);
16478  int pos;
16479  int pair;
16480 
16481  cpl_free(name);
16482 
16483  if (null)
16484  break; /* No object #m+1 in this slit - go to next slit */
16485 
16486  /*
16487  * Copy necessary object data to summary table. Note
16488  * that the absolute object position (row) in the
16489  * rectified image is made relative to the bottom
16490  * position (row) of the current slit.
16491  */
16492 
16493  pos = cpl_table_get_int(slitss[j], "position", k, &null);
16494  pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
16495  cpl_table_set(summary, "absolute", nobjs, obj);
16496  cpl_table_set(summary, "pos", nobjs, pos);
16497  cpl_table_set(summary, "offset", nobjs, obj - pos);
16498  cpl_table_set(summary, "pair", nobjs, pair);
16499 
16500  nobjs++;
16501  }
16502  }
16503  }
16504 
16505 // cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
16506 
16507  /*
16508  * Perform the intersection: what are the objects belonging
16509  * to the same slit (same pair ordinary + extraordinary) which
16510  * are observed at the same offset at all angles? Those are
16511  * the polarimetric objects.
16512  */
16513 
16514  nmatches = 0;
16515  maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
16516 
16517  /*
16518  * We loop on the objects of the first-angle object table as
16519  * reference, and check whether those objects are present also
16520  * at *all* other angles. Note that the loop advances by pairs.
16521  * If the top (k = 0) slit spectrum is not an ordinary beam,
16522  * it is ignored. The loop advances by pairs, starting at the
16523  * first complete pair. It is implicitely assumed that the
16524  * slit spectrum on top is always from the ordinary beam, and
16525  * the spectrum below (k+1) its extraordinary match.
16526  */
16527 
16528  for (k = 0; k < nslits; k+=2) {
16529  int slitmatches = 0;
16530 
16531  if (k + 1 < nslits ) {
16532  if (cpl_table_get_int(slitss[0], "pair_id", k, NULL) !=
16533  cpl_table_get_int(slitss[0], "pair_id", k + 1, NULL)) {
16534 
16535  /*
16536  * This is not an ordinary beam - advance to next slit.
16537  */
16538 
16539  /* It will be incremented by two, so the effect is like k++ */
16540  k--;
16541 
16542  continue;
16543  }
16544  }
16545 
16546  for (m = 0; m < maxobjs; m++) {
16547  int null;
16548  char *name = cpl_sprintf("object_%d", m + 1);
16549  double obj = cpl_table_get_double(slitss[0], name, k, &null);
16550  double pos;
16551  int pair;
16552 
16553  char *name_obj = NULL;
16554  char *name_start = NULL;
16555  char *name_end = NULL;
16556  char *name_row = NULL;
16557  char *name_row_s = NULL;
16558 
16559  char *name_start_o = NULL;
16560  char *name_end_o = NULL;
16561  char *name_row_o = NULL;
16562  char *name_start_v = NULL;
16563  char *name_end_v = NULL;
16564  char *name_obj_v = NULL;
16565 
16566  int start, end;
16567  int length;
16568 
16569  int selected;
16570  int v, start_v, end_v;
16571  double min_v, obj_v;
16572 
16573 
16574  cpl_free(name);
16575 
16576  if (null)
16577  break;
16578 
16579  /*
16580  * Each object of the first object table belongs to a
16581  * slit spectrum (k). This slit spectrum has a position
16582  * in the rectified image, and it belongs to a given
16583  * ordinary + extraordinary pair.
16584  */
16585 
16586  pos = cpl_table_get_int(slitss[0], "position", k, &null);
16587  pair = cpl_table_get_int(slitss[0], "pair_id", k, &null);
16588 
16589  /*
16590  * Now from the summary table we can select all objects
16591  * which have the same offset (obj - pos) within all slit
16592  * spectra belonging to the same ordinary + extraordinary
16593  * pair (at all angles).
16594  */
16595 
16596  cpl_table_select_all(summary); /* Reset selection */
16597 
16598  cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
16599  cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
16600  obj - pos + tolerance);
16601  selected =
16602  cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
16603  obj - pos - tolerance);
16604 
16605 
16606  /*
16607  * If this object were observed at all angles (nscience) and
16608  * at all beams (2), we should have selected exactly 2*nscience
16609  * objects. If not, this is not a polarimetric object, and it
16610  * is discarded from the intersection.
16611  */
16612 
16613  if (selected != nscience * 2)
16614  continue;
16615 
16616  /*
16617  * If we reach this point we have found one valid polarimetric
16618  * object, that must be inserted in the intersection object
16619  * table.
16620  */
16621 
16622  slitmatches++;
16623 
16624  /*
16625  * Names of the columns of the output table where the
16626  * object information needs to be copied. Note that a
16627  * new column is created, the "row_stokes_#", where the
16628  * row number of the extracted polarimetric signal is
16629  * also computed. For the moment this column will be
16630  * left empty - it will be filled only when all matches
16631  * are collected.
16632  */
16633 
16634  name_obj = cpl_sprintf("object_%d", slitmatches);
16635  name_start = cpl_sprintf("start_%d", slitmatches);
16636  name_end = cpl_sprintf("end_%d", slitmatches);
16637  name_row = cpl_sprintf("row_%d", slitmatches);
16638  name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
16639 
16640  /*
16641  * Names of the columns of the input table where the
16642  * object information is available.
16643  */
16644 
16645  name_start_o = cpl_sprintf("start_%d", m + 1);
16646  name_end_o = cpl_sprintf("end_%d", m + 1);
16647  name_row_o = cpl_sprintf("row_%d", m + 1);
16648 
16649  /*
16650  * If the output columns do not exist yet, create them.
16651  */
16652 
16653  if (!cpl_table_has_column(origslits, name_obj)) {
16654  cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
16655  cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
16656  cpl_table_new_column(origslits, name_end, CPL_TYPE_INT);
16657  cpl_table_new_column(origslits, name_row, CPL_TYPE_INT);
16658  cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
16659  }
16660 
16661  /*
16662  * The current slit spectrum is k. The slit spectrum immediately
16663  * below (in the rectified image) is k+1. We need the length of
16664  * the spectrum below for computing the _absolute_ coordinates
16665  * of the objects in the rectified image in both beams.
16666  */
16667 
16668  length = cpl_table_get_int(origslits, "length", k + 1, &null);
16669 
16670  /* NEW:
16671  * Names of the columns of the input table where
16672  * the information of the corresponding object of
16673  * the next beam is available.
16674  */
16675 
16676  for (v = 0; v < maxobjs; v++) {
16677  char *name_v = cpl_sprintf("object_%d", v + 1);
16678  double obj_v = cpl_table_get_double(slitss[0], name_v,
16679  k + 1, &null);
16680 
16681  cpl_free(name_v);
16682 
16683  if (null)
16684  break;
16685 
16686  if (v) {
16687  if (fabs(obj - length - obj_v) < min_v) {
16688  min_v = fabs(obj - length - obj_v);
16689  cpl_free(name_start_v);
16690  cpl_free(name_end_v);
16691  cpl_free(name_obj_v);
16692  name_start_v = cpl_sprintf("start_%d", v + 1);
16693  name_end_v = cpl_sprintf("end_%d", v + 1);
16694  name_obj_v = cpl_sprintf("object_%d", v + 1);
16695  }
16696  }
16697  else {
16698  min_v = fabs(obj - length - obj_v);
16699  name_start_v = cpl_sprintf("start_%d", v + 1);
16700  name_end_v = cpl_sprintf("end_%d", v + 1);
16701  name_obj_v = cpl_sprintf("object_%d", v + 1);
16702  }
16703  }
16704 
16705  /*
16706  * Read from the first input object table (first angle)
16707  * the spatial window enclosing the object.
16708  */
16709 
16710  start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
16711  end = cpl_table_get_int(slitss[0], name_end_o, k, &null);
16712 
16713  /* NEW:
16714  * Spatial window of the matching object in the next beam.
16715  */
16716 
16717  start_v = cpl_table_get_int(slitss[0], name_start_v, k + 1, &null);
16718  end_v = cpl_table_get_int(slitss[0], name_end_v, k + 1, &null);
16719  obj_v = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null);
16720 
16721  /*
16722  * Write the object coordinates in the same slit, and in the
16723  * slit below. Note that here we assume that all slits were
16724  * traced perfectly, and we compute the theoretical coords
16725  * (obj - length) within the next slit spectrum (k + 1). In
16726  * principle we should read them as well from the input
16727  * table!
16728  */
16729 
16730  cpl_table_set_double(origslits, name_obj, k, obj);
16731  cpl_table_set_double(origslits, name_obj, k + 1, obj_v);
16732  // cpl_table_set_double(origslits, name_obj, k + 1, obj - length);
16733 
16734  cpl_table_set_int(origslits, name_start, k, start);
16735  cpl_table_set_int(origslits, name_start, k + 1, start_v);
16736  // cpl_table_set_int(origslits, name_start, k + 1, start - length);
16737 
16738  cpl_table_set_int(origslits, name_end, k, end);
16739  cpl_table_set_int(origslits, name_end, k + 1, end_v);
16740  // cpl_table_set_int(origslits, name_end, k + 1, end - length);
16741 
16742  /*
16743  * "nmatches" is counting at what "reduced" image row the
16744  * extracted spectra are. Note that this is s preliminary
16745  * numbering - which is wrong: other objects may be found
16746  * in the same slit, and then the indeces would not be in
16747  * sequence. What is important is that at the end of this
16748  * loop "nmatches" would be the total number of matching
16749  * objects. The two cpl_table_set_int() calls made here
16750  * cannot be removed - they "validate" those table elements
16751  * (see ahead).
16752  */
16753 
16754  cpl_table_set_int(origslits, name_row, k, nmatches);
16755  nmatches++;
16756  cpl_table_set_int(origslits, name_row, k + 1, nmatches);
16757  nmatches++;
16758 
16759  cpl_free(name_obj);
16760  cpl_free(name_start);
16761  cpl_free(name_end);
16762  cpl_free(name_row);
16763  cpl_free(name_row_s);
16764 
16765  cpl_free(name_start_o);
16766  cpl_free(name_end_o);
16767  cpl_free(name_row_o);
16768 
16769  cpl_free(name_start_v); name_start_v = NULL;
16770  cpl_free(name_end_v); name_end_v = NULL;
16771  cpl_free(name_obj_v); name_obj_v = NULL;
16772  }
16773  }
16774 
16775  /*
16776  * The summary table has fulfilled its function. If no matching
16777  * objects are found, the function returns with an error.
16778  */
16779 
16780  cpl_table_delete(summary);
16781 
16782  if (!nmatches)
16783  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16784 
16785  /*
16786  * Now we consider the resulting intersection object table,
16787  * listing all matches. As seen, the image row number reported
16788  * in the columns "row_#" was not really performed sequentially.
16789  * We need to renumber sequentially...
16790  * We need also to fill the "row_stokes_#" column the way the
16791  * extracted polarimetric signal will be stored in the
16792  * reduced_pol_images...
16793  */
16794 
16795  maxobjs = mos_get_maxobjs_per_slit(origslits);
16796  nstokes = nmatches / 2; /* nmatches is always an even number */
16797 
16798  for (k = 0; k < nslits; k++) {
16799  if (k % 2) { /* Extraordinary beam */
16800  nstokes = sstokes; /* Use same start value as for ordinary */
16801  }
16802  else { /* Ordinary beam */
16803  sstokes = nstokes; /* Memorise start value at ordinary beam */
16804  }
16805 
16806  for (m = 0; m < maxobjs; m++) {
16807  char *name = cpl_sprintf("row_%d", m + 1);
16808  char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
16809 
16810  if (!cpl_table_is_valid(origslits, name, k)) {
16811  cpl_free(name);
16812  cpl_free(namestokes);
16813  break;
16814  }
16815  else {
16816  nmatches--;
16817  nstokes--;
16818  cpl_table_set_int(origslits, name, k, nmatches);
16819  cpl_table_set_int(origslits, namestokes, k, nstokes);
16820  }
16821 
16822  cpl_free(name);
16823  cpl_free(namestokes);
16824  }
16825  }
16826 
16827 
16828  /*
16829  * This is done to avoid the NULL value is zero (it would invalidate
16830  * also the row_# = 0 or start_# = 0 for an object), and to enable
16831  * working directly with the column data buffers, when using this
16832  * table afterwards.
16833  */
16834 
16835  for (j = 0; j < maxobjs; j++) {
16836  char *name = cpl_sprintf("object_%d", j + 1);
16837  cpl_table_fill_invalid_double(origslits, name, -1);
16838  cpl_free(name);
16839 
16840  name = cpl_sprintf("start_%d", j + 1);
16841  cpl_table_fill_invalid_int(origslits, name, -1);
16842  cpl_free(name);
16843 
16844  name = cpl_sprintf("end_%d", j + 1);
16845  cpl_table_fill_invalid_int(origslits, name, -1);
16846  cpl_free(name);
16847 
16848  name = cpl_sprintf("row_%d", j + 1);
16849  cpl_table_fill_invalid_int(origslits, name, -1);
16850  cpl_free(name);
16851 
16852  name = cpl_sprintf("row_stokes_%d", j + 1);
16853  cpl_table_fill_invalid_int(origslits, name, -1);
16854  cpl_free(name);
16855  }
16856 
16857  /*********************************************************************
16858  * This tail has been added to propagate the selection of valid
16859  * objects also to the input slitss[] tables. Just eliminate all
16860  * this final part to suppress this behaviour.
16861  */
16862 
16863  /*
16864  * First of all, make a working copy and remove all columns related
16865  * to objects from the input object tables.
16866  */
16867 
16868  for (i = 0; i < nscience; i++) {
16869  int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
16870 
16871  work[i] = cpl_table_duplicate(slitss[i]);
16872 
16873  for (m = 0; m < c_maxobjs; m++) {
16874  char *object_o = cpl_sprintf("object_%d", m + 1);
16875  char *start_o = cpl_sprintf("start_%d", m + 1);
16876  char *end_o = cpl_sprintf("end_%d", m + 1);
16877  char *row_o = cpl_sprintf("row_%d", m + 1);
16878 
16879  cpl_table_erase_column(slitss[i], object_o);
16880  cpl_table_erase_column(slitss[i], start_o);
16881  cpl_table_erase_column(slitss[i], end_o);
16882  cpl_table_erase_column(slitss[i], row_o);
16883  }
16884  }
16885 
16886  /*
16887  * Now just consider all the objects in the intersection table.
16888  */
16889 
16890  for (k = 0; k < nslits; k++) {
16891  for (j = 0; j < maxobjs; j++) {
16892  double object_w, object_r;
16893  int row_w;
16894 
16895  char *object_i = cpl_sprintf("object_%d", j + 1);
16896  char *start_i = cpl_sprintf("start_%d", j + 1);
16897  char *end_i = cpl_sprintf("end_%d", j + 1);
16898  char *row_i = cpl_sprintf("row_%d", j + 1);
16899 
16900 
16901  if (!cpl_table_is_valid(origslits, object_i, k))
16902  break;
16903 
16904  /*
16905  * We have found a valid object (valid because it belongs
16906  * to the intersection). Now we look for this object in each
16907  * one of the original tables, we get its parameters, and
16908  * copy them at the right position (i.e., same position as
16909  * in intersection table). The object will be the one closest
16910  * to the object position (column object_i) in the intersection
16911  * table. Note that we examine the same row, k, in all tables.
16912  */
16913 
16914  object_w = cpl_table_get_double(origslits, object_i, k, NULL);
16915  row_w = cpl_table_get_int (origslits, row_i, k, NULL);
16916 
16917  for (i = 0; i < nscience; i++) {
16918  int c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
16919  int minpos;
16920  double mindiff, diff;
16921  char *object_o;
16922  char *start_o;
16923  char *end_o;
16924  char *row_o;
16925 
16926  for (m = 0; m < c_maxobjs; m++) {
16927  object_o = cpl_sprintf("object_%d", m + 1);
16928  start_o = cpl_sprintf("start_%d", m + 1);
16929  end_o = cpl_sprintf("end_%d", m + 1);
16930  row_o = cpl_sprintf("row_%d", m + 1);
16931 
16932  if (!cpl_table_is_valid(work[i], object_o, k))
16933  break;
16934 
16935  object_r = cpl_table_get_double(work[i], object_o, k, NULL);
16936  //row_r = cpl_table_get_int (work[i], row_o, k, NULL);
16937 
16938  diff = fabs(object_w - object_r);
16939  if (m) {
16940  if (mindiff > diff) {
16941  mindiff = diff;
16942  minpos = m;
16943  }
16944  }
16945  else {
16946  mindiff = diff;
16947  minpos = 0;
16948  }
16949 
16950  cpl_free(object_o);
16951  cpl_free(start_o);
16952  cpl_free(end_o);
16953  cpl_free(row_o);
16954  }
16955 
16956  object_o = cpl_sprintf("object_%d", minpos + 1);
16957  start_o = cpl_sprintf("start_%d", minpos + 1);
16958  end_o = cpl_sprintf("end_%d", minpos + 1);
16959  row_o = cpl_sprintf("row_%d", minpos + 1);
16960 
16961  if (!cpl_table_has_column(slitss[i], object_i)) {
16962  cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
16963  cpl_table_new_column(slitss[i], start_i, CPL_TYPE_INT);
16964  cpl_table_new_column(slitss[i], end_i, CPL_TYPE_INT);
16965  cpl_table_new_column(slitss[i], row_i, CPL_TYPE_INT);
16966  cpl_table_fill_invalid_double(slitss[i], object_i, -1);
16967  cpl_table_fill_invalid_int (slitss[i], start_i, -1);
16968  cpl_table_fill_invalid_int (slitss[i], end_i, -1);
16969  cpl_table_fill_invalid_int (slitss[i], row_i, -1);
16970  }
16971 
16972  cpl_table_set_double(slitss[i], object_i, k,
16973  cpl_table_get_double(work[i], object_o,
16974  k, NULL));
16975  cpl_table_set_int(slitss[i], start_i , k,
16976  cpl_table_get_int(work[i], start_o, k, NULL));
16977  cpl_table_set_int(slitss[i], end_i , k,
16978  cpl_table_get_int(work[i], end_o, k, NULL));
16979  cpl_table_set_int(slitss[i], row_i , k, row_w);
16980 
16981  cpl_free(object_o);
16982  cpl_free(start_o);
16983  cpl_free(end_o);
16984  cpl_free(row_o);
16985  }
16986 
16987  cpl_free(object_i);
16988  cpl_free(start_i);
16989  cpl_free(end_i);
16990  cpl_free(row_i);
16991  }
16992  }
16993 
16994  for (i = 0; i < nscience; i++)
16995  cpl_table_delete(work[i]);
16996 
16997  cpl_free(work);
16998 
16999 
17000  return cpl_error_get_code();
17001 }
17002 
17003 
17011 int mos_get_maxobjs_per_slit(cpl_table * slits)
17012 {
17013  int maxobjs = 1;
17014 
17015  char * colname = cpl_sprintf("object_%d", maxobjs);
17016 
17017  while (cpl_table_has_column(slits, colname)) {
17018  maxobjs++;
17019  cpl_free(colname);
17020  colname = cpl_sprintf("object_%d", maxobjs);
17021  }
17022 
17023  cpl_free(colname);
17024 
17025  maxobjs--;
17026 
17027  return maxobjs;
17028 }
17029 
17037 int mos_get_nobjects(cpl_table * slits)
17038 {
17039  int nobjs = 0;
17040 
17041  int nslits = cpl_table_get_nrow(slits);
17042  int maxobjs = mos_get_maxobjs_per_slit(slits);
17043 
17044  int k, m;
17045 
17046  for (k = 0; k < nslits; k++) {
17047  for (m = 0; m < maxobjs; m++) {
17048  char * name = cpl_sprintf("object_%d", m + 1);
17049  int null = !cpl_table_is_valid(slits, name, k);
17050 
17051  cpl_free(name);
17052 
17053  if (null) break;
17054  else nobjs++;
17055  }
17056  }
17057 
17058  return nobjs;
17059 }
17060 
17068 int mos_check_slits(cpl_table *slits, float rescale)
17069 {
17070 
17071  cpl_propertylist *sort;
17072 
17073  int nslits = cpl_table_get_nrow(slits);
17074 
17075  int k, null;
17076 
17077  const float interval = 90.0 * rescale;
17078  const float offset = (90.0 - 5) * rescale;
17079 
17080 
17081  for (k = 0; k < nslits; k++) {
17082  double ytop = cpl_table_get_double(slits, "ytop", k, &null);
17083  double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17084 
17085  double xtop = cpl_table_get_double(slits, "xtop", k, &null);
17086  double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
17087 
17088  int nmiss = (int)((ytop - ybottom) / interval + 0.5);
17089 
17090  if (nmiss > 1) {
17091  cpl_msg_warning(cpl_func,
17092  "Some slits could not be properly detected. "
17093  "There might be accountable inaccuracies.");
17094  while (nmiss > 1) {
17095  cpl_table_set_size(slits, nslits + 1);
17096 
17097  /* Fill in new slit 'cut' */
17098 
17099  /* x coordinates be the same (acceptable approximation) */
17100  cpl_table_set_double(slits, "xtop", nslits, xtop);
17101  cpl_table_set_double(slits, "xbottom", nslits, xbottom);
17102 
17103  /* Cut */
17104  if (k == 0) {
17105  cpl_table_set_double(slits, "ybottom", nslits, ybottom);
17106  cpl_table_set_double(slits, "ytop", nslits, ybottom
17107  + offset);
17108  ybottom += interval;
17109  cpl_table_set_double(slits, "ybottom", k, ybottom);
17110  } else {
17111  cpl_table_set_double(slits, "ytop", nslits, ytop);
17112  cpl_table_set_double(slits, "ybottom", nslits, ytop
17113  - offset);
17114  ytop -= interval;
17115  cpl_table_set_double(slits, "ytop", k, ytop);
17116  }
17117 
17118  nslits++; nmiss--;
17119  }
17120  }
17121  }
17122 
17123  sort = cpl_propertylist_new();
17124  cpl_propertylist_append_bool(sort, "ytop", 1);
17125  cpl_table_sort(slits, sort);
17126  cpl_propertylist_delete(sort);
17127 
17128  /*
17129  * Add here an ad hoc check on the last slit: is it too long
17130  * (by more than 10%)? Then shorten it...
17131  */
17132 
17133  k = cpl_table_get_nrow(slits) - 1;
17134 
17135  {
17136  double ytop = cpl_table_get_double(slits, "ytop", k, &null);
17137  double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17138  double length = (ytop - ybottom) / interval;
17139 
17140  if (length > 1.1) {
17141  cpl_table_set_double(slits, "ybottom", k, ytop - offset);
17142  }
17143 
17144  }
17145 
17146  return 0;
17147 }
17148 
17171 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header,
17172  int * nslits_out_det)
17173 {
17174  int m, null;
17175  int halfsize;
17176 
17177  cpl_propertylist * sort;
17178  cpl_table * slits;
17179 
17180  slits = mos_load_slits_fors_mos(header, nslits_out_det);
17181  halfsize = cpl_table_get_nrow(slits);
17182 
17183  cpl_table_set_size(slits, 2 * halfsize);
17184 
17185  for (m = 0; m < halfsize; m++) {
17186 
17187  double gap = 1.4;
17188 
17189  double length =
17190  cpl_table_get(slits, "ytop", m, &null) -
17191  cpl_table_get(slits, "ybottom", m, &null);
17192 
17193  if (m) {
17194  double interval =
17195  cpl_table_get(slits, "ybottom", m - 1, &null) -
17196  cpl_table_get(slits, "ytop", m, &null);
17197 
17198  gap = (interval - length) / 2;
17199  }
17200 
17201  cpl_table_set(slits, "slit_id", m + halfsize,
17202  cpl_table_get(slits, "slit_id", m, &null) - 1);
17203 
17204  cpl_table_set(slits, "xtop", m + halfsize,
17205  cpl_table_get(slits, "xtop", m, &null));
17206 
17207  cpl_table_set(slits, "xbottom", m + halfsize,
17208  cpl_table_get(slits, "xbottom", m, &null));
17209 
17210  cpl_table_set(slits, "ytop", m + halfsize,
17211  cpl_table_get(slits, "ytop", m, &null) + gap + length);
17212 
17213  cpl_table_set(slits, "ybottom", m + halfsize,
17214  cpl_table_get(slits, "ytop", m, &null) + gap);
17215  }
17216 
17217  for (m = 0; m < 2 * halfsize; m++) {
17218  cpl_table_set(slits, "ytop", m,
17219  cpl_table_get(slits, "ytop", m, &null) - 5.3);
17220 
17221  cpl_table_set(slits, "ybottom", m,
17222  cpl_table_get(slits, "ybottom", m, &null) - 5.3);
17223 
17224  }
17225 
17226  sort = cpl_propertylist_new();
17227  cpl_propertylist_append_bool(sort, "ytop", 1);
17228  cpl_table_sort(slits, sort);
17229 
17230  cpl_propertylist_delete(sort);
17231 
17232  return slits;
17233 }
17234 
17235 int * fors_get_nobjs_perslit(cpl_table * slits)
17236 {
17237  int nslits = cpl_table_get_nrow(slits);
17238  int maxobjs = mos_get_maxobjs_per_slit(slits);
17239 
17240  int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
17241 
17242  int k, m;
17243 
17244  for (k = 0; k < nslits; k++) {
17245  int nobjs = 0;
17246  for (m = 0; m < maxobjs; m++) {
17247  char * name = cpl_sprintf("object_%d", m + 1);
17248  int null = !cpl_table_is_valid(slits, name, k);
17249 
17250  cpl_free(name);
17251 
17252  if (null) break;
17253  else nobjs++;
17254  }
17255 
17256  nobjs_per_slit[k] = nobjs;
17257  }
17258 
17259  return nobjs_per_slit;
17260 }
17261 
17262 double fors_get_object_position(cpl_table *slits, int slit, int object)
17263 {
17264  char *name = cpl_sprintf("object_%d", object);
17265  double position;
17266 
17267  position = cpl_table_get_double(slits, name, slit, NULL)
17268  - cpl_table_get_int(slits, "position", slit, NULL);
17269 
17270  cpl_free(name);
17271 
17272  return position;
17273 }
17274 
17275 int mos_rebin_signal(cpl_image **image, int rebin)
17276 {
17277  cpl_image *rebinned;
17278 
17279 
17280  if (*image == NULL)
17281  return 1;
17282 
17283  if (rebin == 1)
17284  return 0;
17285 
17286  rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1);
17287 
17288  cpl_image_delete(*image);
17289 
17290  *image = rebinned;
17291 
17292  return 0;
17293 }
17294 
17295 int mos_rebin_error(cpl_image **image, int rebin)
17296 {
17297  if (*image == NULL)
17298  return 1;
17299 
17300  if (rebin == 1)
17301  return 0;
17302 
17303  cpl_image_power(*image, 2);
17304  mos_rebin_signal(image, rebin);
17305  cpl_image_power(*image, 0.5);
17306 
17307  return 0;
17308 }
17309 
17310 /*
17311  * @brief
17312  * Map table values into a 1D image
17313  *
17314  * @param image Target image
17315  * @param start Coordinate of first pixel in image
17316  * @param step Coordinate step for one pixel in image
17317  * @param table Source table
17318  * @param xname Name of coordinate column
17319  * @param yname Name of values column
17320  *
17321  * @return 0 on success
17322  *
17323  * The values in @em yname are linearly interpolated at the @em image
17324  * pixel coordinates. The @em image must have Nx1 size.
17325  */
17326 
17327 int map_table(cpl_image *image, double start, double step,
17328  cpl_table *table, const char *xname, const char *yname)
17329 {
17330  int length = cpl_image_get_size_x(image);
17331  int nrows = cpl_table_get_nrow(table);
17332  float *data = cpl_image_get_data_float(image);
17333  float *fdata = NULL;
17334  double *xdata = NULL;
17335  double *ydata = NULL;
17336  cpl_type xtype = cpl_table_get_column_type(table, xname);
17337  cpl_type ytype = cpl_table_get_column_type(table, yname);
17338  double xzero, pos;
17339  int i, j, n;
17340 
17341 
17342  /*
17343  * Initialization of output image at 0.0 - this value is left
17344  * on non-overlapping portions.
17345  */
17346 
17347  for (i = 0; i < length; i++)
17348  data[i] = 0.0;
17349 
17350 
17351  /*
17352  * Do everything in double precision
17353  */
17354 
17355  if (xtype == CPL_TYPE_FLOAT) {
17356  fdata = cpl_table_get_data_float(table, xname);
17357  xdata = cpl_malloc(nrows * sizeof(double));
17358  for (i = 0; i < nrows; i++) {
17359  xdata[i] = fdata[i];
17360  }
17361  }
17362  else {
17363  xdata = cpl_table_get_data_double(table, xname);
17364  }
17365 
17366  if (ytype == CPL_TYPE_FLOAT) {
17367  fdata = cpl_table_get_data_float(table, yname);
17368  ydata = cpl_malloc(nrows * sizeof(double));
17369  for (i = 0; i < nrows; i++) {
17370  ydata[i] = fdata[i];
17371  }
17372  }
17373  else {
17374  ydata = cpl_table_get_data_double(table, yname);
17375  }
17376 
17377  /*
17378  * Mapping
17379  */
17380 
17381  n = 0;
17382  xzero = xdata[n];
17383 
17384  for (i = 0; i < length; i++) {
17385  pos = start + step * i;
17386  if (pos < xzero)
17387  continue;
17388  for (j = n; j < nrows; j++) {
17389  if (xdata[j] > pos) {
17390  n = j;
17391  data[i] = ydata[j-1]
17392  + (ydata[j] - ydata[j-1])
17393  * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]);
17394  break;
17395  }
17396  }
17397  }
17398 
17399  if (xtype == CPL_TYPE_FLOAT)
17400  cpl_free(xdata);
17401 
17402  if (ytype == CPL_TYPE_FLOAT)
17403  cpl_free(ydata);
17404 
17405  return 0;
17406 }
17407 
17408 
17409 /*
17410  * @brief
17411  * Fit overall trend of a Nx1 image
17412  *
17413  * @param image Values to smooth
17414  * @param order Order of fitting polynomial
17415  * @param hw Half width of smoothing window
17416  *
17417  * @return Smoothed image, or NULL on failure.
17418  *
17419  * Heavily smooth and fit data in the input Nx1 size @em image.
17420  */
17421 
17422 static cpl_image *polysmooth(cpl_image *image, int order, int hw)
17423 {
17424  int npoints;
17425  cpl_vector *x;
17426  cpl_vector *y;
17427  double *xdata;
17428  double *ydata;
17429  cpl_polynomial *poly;
17430  cpl_vector *ysmooth;
17431  cpl_image *smoothed;
17432  float *sdata;
17433  int i;
17434 
17435 
17436  npoints = cpl_image_get_size_x(image);
17437 
17438  if (2 * hw + 1 > npoints)
17439  return NULL;
17440 
17441  x = cpl_vector_new(npoints);
17442  y = cpl_vector_new(npoints);
17443  xdata = cpl_vector_get_data(x);
17444  ydata = cpl_vector_get_data(y);
17445 
17446  smoothed = cpl_image_duplicate(image);
17447  sdata = cpl_image_get_data_float(smoothed);
17448 
17449  for (i = 0; i < npoints; i++) {
17450  xdata[i] = i;
17451  ydata[i] = sdata[i];
17452  }
17453 
17454  ysmooth = cpl_vector_filter_median_create(y, hw);
17455  cpl_vector_delete(y);
17456 
17457  poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL);
17458  cpl_vector_delete(x);
17459  cpl_vector_delete(ysmooth);
17460 
17461  if (poly) {
17462  for (i = 0; i < npoints; i++)
17463  sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL);
17464 
17465  cpl_polynomial_delete(poly);
17466  }
17467  else {
17468  cpl_image_delete(smoothed);
17469  return NULL;
17470  }
17471 
17472  return smoothed;
17473 }
17474 
17475 #undef cleanup
17476 #define cleanup \
17477 do { \
17478  cpl_image_delete(spectrum); \
17479  cpl_image_delete(flux); \
17480  cpl_image_delete(efficiency); \
17481  cpl_image_delete(smo_efficiency); \
17482  cpl_image_delete(extinction); \
17483  cpl_image_delete(response); \
17484  cpl_image_delete(smo_response); \
17485  cpl_image_delete(physical); \
17486 } while (0)
17487 
17511 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave,
17512  double dispersion, double gain,
17513  double exptime, cpl_table *ext_table,
17514  double airmass, cpl_table *flux_table,
17515  int order)
17516 {
17517 
17518  cpl_image *spectrum = NULL; // Extracted standard star spectrum
17519  float *data;
17520  cpl_image *extinction = NULL; // Extinction binned as "spectrum"
17521  float *ext_data;
17522  cpl_image *flux = NULL; // Standard star flux binned as "spectrum"
17523  float *flux_data;
17524  cpl_image *physical = NULL; // Physical units of above
17525  float *phys_data;
17526  cpl_image *efficiency = NULL; // Raw efficiency curve
17527  float *eff_data;
17528  cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve
17529  float *smo_eff_data;
17530  cpl_image *response = NULL; // Raw response curve
17531  float *res_data;
17532  cpl_image *smo_response = NULL; // Smoothed response curve
17533  float *smo_res_data;
17534  cpl_image *image;
17535  cpl_image *smo_image;
17536  cpl_table *table;
17537  float lambda;
17538  int nx, ny;
17539  int ext_count, ext_pos;
17540  int eff_count, eff_pos;
17541  int flux_count, flux_pos;
17542  int start, end;
17543  int i;
17544 
17545 
17546  if (spectra == NULL || ext_table == NULL || flux_table == NULL) {
17547  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17548  return NULL;
17549  }
17550 
17551  if (!cpl_table_has_column(ext_table, "WAVE")) {
17552  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17553  "Column WAVE in atmospheric extinction table");
17554  return NULL;
17555  }
17556 
17557  if (!cpl_table_has_column(ext_table, "EXTINCTION")) {
17558  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17559  "Column EXTINCTION in atmospheric extinction table");
17560  return NULL;
17561  }
17562 
17563  if (!cpl_table_has_column(flux_table, "WAVE")) {
17564  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17565  "Column WAVE in standard star flux table");
17566  return NULL;
17567  }
17568 
17569  if (!cpl_table_has_column(flux_table, "FLUX")) {
17570  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17571  "Column FLUX in standard star flux table");
17572  return NULL;
17573  }
17574 
17575  if (gain < 0.1) {
17576  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17577  "Invalid gain factor (%.2f)", gain);
17578  return NULL;
17579  }
17580 
17581  if (exptime < 0.001) {
17582  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17583  "Invalid exposure time (%.2f)", exptime);
17584  return NULL;
17585  }
17586 
17587  if (dispersion < 0.001) {
17588  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17589  "Invalid dispersion (%.2f)", dispersion);
17590  return NULL;
17591  }
17592 
17593  if (order < 2) {
17594  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17595  "Order of the polynomial fitting the "
17596  "instrument response must be at least 2");
17597  return NULL;
17598  }
17599 
17600  nx = cpl_image_get_size_x(spectra);
17601  ny = cpl_image_get_size_y(spectra);
17602 
17603 
17604  /*
17605  * Find brightest spectrum and duplicate it.
17606  */
17607 
17608  if (ny == 1) {
17609  spectrum = cpl_image_duplicate(spectra);
17610  }
17611  else {
17612  cpl_size x, y;
17613  cpl_image *brights = cpl_image_collapse_create(spectra, 1);
17614 
17615  cpl_image_get_maxpos(brights, &x, &y);
17616  cpl_image_delete(brights);
17617  spectrum = cpl_image_extract(spectra, 1, y, nx, y);
17618  }
17619 
17620 
17621  /*
17622  * Convert standard star spectrum in electrons per second per Angstrom.
17623  */
17624 
17625  cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion);
17626 
17627 
17628  /*
17629  * Map the atmospheric extinction factors to the same lambda sampling
17630  * of the extracted spectrum.
17631  */
17632 
17633  extinction = cpl_image_duplicate(spectrum);
17634  map_table(extinction, startwave + dispersion/2, dispersion,
17635  ext_table, "WAVE", "EXTINCTION");
17636 
17637 
17638  /*
17639  * Convert from magnitudes to actual flux loss.
17640  */
17641 
17642  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17643  cpl_image_exponential(extinction, 10.);
17644 
17645 
17646  /*
17647  * Correct the scientific spectrum to airmass 0
17648  */
17649 
17650  cpl_image_multiply(spectrum, extinction);
17651 
17652 
17653  /*
17654  * Find in what pixel interval (start at "ext_pos", for "ext_count"
17655  * pixels) the atmospheric extinction is available.
17656  */
17657 
17658  ext_data = cpl_image_get_data_float(extinction);
17659 
17660  ext_count = 0;
17661  ext_pos = 0;
17662  for (i = 0; i < nx; i++) {
17663  if (ext_data[i] > 0.0) {
17664  if (ext_count == 0) {
17665  ext_pos = i;
17666  }
17667  ext_count++;
17668  }
17669  else {
17670  if (ext_count) {
17671  break;
17672  }
17673  }
17674  }
17675 
17676  cpl_image_delete(extinction); extinction = NULL;
17677 
17678 
17679  /*
17680  * Map the standard star catalog flux to the same lambda sampling
17681  * of the extracted spectrum.
17682  */
17683 
17684  flux = cpl_image_duplicate(spectrum);
17685  map_table(flux, startwave + dispersion/2, dispersion,
17686  flux_table, "WAVE", "FLUX");
17687 
17688 
17689  /*
17690  * Find in what pixel interval (start at "flux_pos", for "flux_count"
17691  * pixels) the standard star flux is available.
17692  */
17693 
17694  flux_data = cpl_image_get_data_float(flux);
17695 
17696  flux_count = 0;
17697  flux_pos = 0;
17698  for (i = 0; i < nx; i++) {
17699  if (flux_data[i] > 0.0) {
17700  if (flux_count == 0) {
17701  flux_pos = i;
17702  }
17703  flux_count++;
17704  }
17705  else {
17706  if (flux_count) {
17707  break;
17708  }
17709  }
17710  }
17711 
17712 
17713  /*
17714  * Intersection with previous selection
17715  */
17716 
17717  start = ext_pos > flux_pos ? ext_pos : flux_pos;
17718  end = (ext_pos + ext_count) < (flux_pos + flux_count) ?
17719  (ext_pos + ext_count) : (flux_pos + flux_count);
17720  flux_pos = start;
17721  flux_count = end - start;
17722 
17723 
17724  /*
17725  * Convert the flux to photons (per second per Angstrom).
17726  * std_flux is in units of erg / cm^2 / s / Angstrom. This
17727  * must be multiplied by the efficient area of the telescope,
17728  * 5.18E+5 cm^2, and divided by hv (v = frequency). With
17729  * hc = 1.98E-8 erg*Angstrom one obtains the following:
17730  */
17731 
17732  physical = cpl_image_duplicate(spectrum);
17733  phys_data = cpl_image_get_data_float(physical);
17734 
17735  for (i = 0; i < nx; i++) {
17736  lambda = startwave + dispersion * (i + 0.5);
17737  phys_data[i] = 0.0026 * lambda * flux_data[i];
17738  }
17739 
17740  efficiency = cpl_image_duplicate(spectrum);
17741  eff_data = cpl_image_get_data_float(efficiency);
17742  data = cpl_image_get_data_float(spectrum);
17743 
17744  for (i = 0; i < nx; i++) {
17745  if (phys_data[i] > 0.0)
17746  eff_data[i] = data[i] / phys_data[i];
17747  else
17748  eff_data[i] = 0.0;
17749  }
17750 
17751  cpl_image_delete(physical); physical = NULL;
17752 
17753 
17754  /*
17755  * Find interval (longer than 300 pixels) where efficiency is
17756  * greater than 1%
17757  */
17758 
17759  eff_count = 0;
17760  eff_pos = 0;
17761  for (i = 0; i < nx; i++) {
17762  if (eff_data[i] > 0.01) {
17763  if (eff_count == 0) {
17764  eff_pos = i;
17765  }
17766  eff_count++;
17767  }
17768  else {
17769  if (eff_count > 300) {
17770  break;
17771  }
17772  }
17773  }
17774 
17775 
17776  /*
17777  * Intersection with previous selection
17778  */
17779 
17780  start = eff_pos > flux_pos ? eff_pos : flux_pos;
17781  end = (eff_pos + eff_count) < (flux_pos + flux_count) ?
17782  (eff_pos + eff_count) : (flux_pos + flux_count);
17783  eff_pos = start;
17784  eff_count = end - start;
17785 
17786  if (eff_count < 1) {
17787  cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
17788  "No overlap between catalog and spectrum");
17789  cleanup;
17790  return NULL;
17791  }
17792 
17793 
17794  /*
17795  * Extract only data to fit, i.e., where the efficiency is available.
17796  */
17797 
17798  image = cpl_image_extract(efficiency, eff_pos + 1, 1,
17799  eff_pos + eff_count, 1);
17800 
17801  smo_image = polysmooth(image, order, 50);
17802  cpl_image_delete(image);
17803 
17804  smo_efficiency = cpl_image_duplicate(efficiency);
17805  smo_eff_data = cpl_image_get_data_float(smo_efficiency);
17806  cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1);
17807 
17808  cpl_image_delete(smo_image);
17809 
17810 
17811  /*
17812  * Compute instrument response as the ratio between the catalog
17813  * spectrum and the observed spectrum (converted in physical units).
17814  * The polynomial smoothing, however, is performed on the inverse
17815  * of this ration, for obvious reasons (i.e., no divergence at zero
17816  * efficiency).
17817  */
17818 
17819  response = cpl_image_duplicate(spectrum);
17820  res_data = cpl_image_get_data_float(response);
17821 
17822  for (i = 0; i < nx; i++) {
17823  if (eff_data[i] > 0.01 && flux_data[i] > 0.0)
17824  res_data[i] = data[i] / flux_data[i];
17825  else
17826  res_data[i] = 0.0;
17827  }
17828 
17829 
17830  /*
17831  * Extract only data to fit, i.e., where the response is available.
17832  */
17833 
17834  image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1);
17835 
17836  smo_image = polysmooth(image, order, 50);
17837  cpl_image_delete(image);
17838 
17839  smo_response = cpl_image_duplicate(response);
17840  smo_res_data = cpl_image_get_data_float(smo_response);
17841  cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1);
17842 
17843  cpl_image_delete(smo_image);
17844 
17845  for (i = 0; i < nx; i++) {
17846  if (eff_data[i] > 0.01) {
17847  res_data[i] = 1 / res_data[i];
17848  smo_res_data[i] = 1 / smo_res_data[i];
17849  }
17850  else {
17851  res_data[i] = 0.0;
17852  smo_res_data[i] = 0.0;
17853  }
17854  }
17855 
17856 
17857  /*
17858  * Assemble the product spectrophotometric table.
17859  */
17860 
17861  table = cpl_table_new(nx);
17862 
17863  cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT);
17864  cpl_table_set_column_unit(table, "WAVE", "Angstrom");
17865 
17866  for (i = 0; i < nx; i++)
17867  cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5));
17868 
17869  cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT);
17870  cpl_table_set_column_unit(table, "STD_FLUX",
17871  "10^(-16) erg/(cm^2 s Angstrom)");
17872  cpl_table_copy_data_float(table, "STD_FLUX", flux_data);
17873  cpl_image_delete(flux); flux = NULL;
17874 
17875  cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT);
17876  cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)");
17877  cpl_table_copy_data_float(table, "OBS_FLUX", data);
17878  cpl_image_delete(spectrum); spectrum = NULL;
17879 
17880  cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT);
17881  cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon");
17882  cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data);
17883  cpl_image_delete(efficiency); efficiency = NULL;
17884 
17885  cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT);
17886  cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon");
17887  cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data);
17888  cpl_image_delete(smo_efficiency); smo_efficiency = NULL;
17889 
17890  cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT);
17891  cpl_table_set_column_unit(table, "RAW_RESPONSE",
17892  "10^(-16) erg/(cm^2 electron)");
17893  cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data);
17894  cpl_image_delete(response); response = NULL;
17895 
17896  cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT);
17897  cpl_table_set_column_unit(table,
17898  "RESPONSE", "10^(-16) erg/(cm^2 electron)");
17899  cpl_table_copy_data_float(table, "RESPONSE", smo_res_data);
17900  cpl_image_delete(smo_response); smo_response = NULL;
17901 
17902  cleanup;
17903 
17904  return table;
17905 }
17906 
17907 static double ksigma_vector(cpl_vector *values,
17908  double klow, double khigh, int kiter, int *good)
17909 {
17910  cpl_vector *accepted;
17911  double mean = 0.0;
17912  double sigma = 0.0;
17913  double *data = cpl_vector_get_data(values);
17914  int n = cpl_vector_get_size(values);
17915  int ngood = n;
17916  int count = 0;
17917  int i;
17918 
17919 
17920  /*
17921  * At first iteration the mean is taken as the median, and the
17922  * standard deviation relative to this value is computed.
17923  */
17924 
17925  mean = cpl_vector_get_median(values);
17926 
17927  for (i = 0; i < n; i++)
17928  sigma += (mean - data[i]) * (mean - data[i]);
17929 
17930  sigma = sqrt(sigma / (n - 1));
17931 
17932  while (kiter) {
17933  count = 0;
17934  for (i = 0; i < ngood; i++) {
17935  if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) {
17936  data[count] = data[i];
17937  ++count;
17938  }
17939  }
17940 
17941  if (count == 0) // This cannot happen at first iteration.
17942  break; // So we can break: we have already computed a mean.
17943 
17944  /*
17945  * The mean must be computed even if no element was rejected
17946  * (count == ngood), because at first iteration median instead
17947  * of mean was computed.
17948  */
17949 
17950  accepted = cpl_vector_wrap(count, data);
17951  mean = cpl_vector_get_mean(accepted);
17952  if (count > 1)
17953  sigma = cpl_vector_get_stdev(accepted);
17954  cpl_vector_unwrap(accepted);
17955 
17956  if (count == ngood || count == 1)
17957  break;
17958 
17959  ngood = count;
17960  --kiter;
17961  }
17962 
17963  if (good)
17964  *good = ngood;
17965 
17966  return mean;
17967 }
17968 
17969 
17988 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist,
17989  double klow, double khigh, int kiter,
17990  cpl_image **good)
17991 {
17992  int ni, nx, ny, npix;
17993  cpl_image *out_ima;
17994  float *pout_ima;
17995  float *good_ima;
17996  cpl_image *image;
17997  float **data;
17998  cpl_vector *time_line;
17999  double *ptime_line;
18000  int ngood;
18001  int i, j;
18002 
18003 
18004  ni = cpl_imagelist_get_size(imlist);
18005 
18006  image = cpl_imagelist_get(imlist, 0);
18007  nx = cpl_image_get_size_x(image);
18008  ny = cpl_image_get_size_y(image);
18009  npix = nx * ny;
18010 
18011  out_ima = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18012  pout_ima = cpl_image_get_data_float(out_ima);
18013 
18014  if (good) {
18015  *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18016  good_ima = cpl_image_get_data_float(*good);
18017  }
18018 
18019  time_line = cpl_vector_new(ni);
18020  ptime_line = cpl_vector_get_data(time_line);
18021 
18022  data = cpl_calloc(sizeof(float *), ni);
18023 
18024  for (i = 0; i < ni; i++) {
18025  image = cpl_imagelist_get(imlist, i);
18026  data[i] = cpl_image_get_data_float(image);
18027  }
18028 
18029  for (i = 0; i < npix; i++) {
18030  for (j = 0; j < ni; j++) {
18031  ptime_line[j] = data[j][i];
18032  }
18033  pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood);
18034  if (good) {
18035  good_ima[i] = ngood;
18036  }
18037  }
18038 
18039  cpl_free(data);
18040  cpl_vector_delete(time_line);
18041 
18042  return out_ima;
18043 
18044 }
18045 
18046 
18063 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response,
18064  cpl_table *ext_table, double startwave,
18065  double dispersion, double gain,
18066  double exptime, double airmass)
18067 {
18068  cpl_image *extinction;
18069  cpl_image *outspectra;
18070  cpl_image *mapresponse;
18071  float *res_data;
18072  float *out_data;
18073  float *ext_data;
18074  int tlength, xlength, ylength;
18075  int i, j, k;
18076  double resp_startwave;
18077  double resp_endwave;
18078  int null;
18079 
18080 
18081  if (spectra == NULL || ext_table == NULL || response == NULL) {
18082  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18083  return NULL;
18084  }
18085 
18086  /* Use the normal response if available. If not, use the flat corrected
18087  * response. Usually only one of the two is available in the interpolated
18088  * response.
18089  */
18090  if(cpl_table_has_column(response, "RESPONSE"))
18091  cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18092  else if(cpl_table_has_column(response, "RESPONSE_FFSED"))
18093  cpl_table_cast_column(response, "RESPONSE_FFSED", "RESPONSE_F", CPL_TYPE_FLOAT);
18094  else
18095  return NULL;
18096 
18097  res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18098 
18099  if (res_data == NULL) {
18100  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18101  return NULL;
18102  }
18103 
18104  tlength = cpl_table_get_nrow(response);
18105  xlength = cpl_image_get_size_x(spectra);
18106  ylength = cpl_image_get_size_y(spectra);
18107 
18108  /* Map the response */
18109  mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18110  map_table(mapresponse, startwave + dispersion/2, dispersion,
18111  response, "WAVE", "RESPONSE_F");
18112  res_data = cpl_image_get_data_float(mapresponse);
18113 
18114  /*
18115  * Map the atmospheric extinction factors to the same lambda sampling
18116  * of the extracted spectrum.
18117  */
18118 
18119  extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18120  map_table(extinction, startwave + dispersion/2, dispersion,
18121  ext_table, "WAVE", "EXTINCTION");
18122 
18123 
18124  /*
18125  * Convert from magnitudes to actual flux loss.
18126  */
18127 
18128  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18129  cpl_image_exponential(extinction, 10.);
18130 
18131  outspectra = cpl_image_duplicate(spectra);
18132 
18133  ext_data = cpl_image_get_data_float(extinction);
18134  out_data = cpl_image_get_data_float(outspectra);
18135 
18136  for (k = 0, i = 0; i < ylength; i++) {
18137  for (j = 0; j < xlength; j++, k++)
18138  out_data[k] *= ext_data[j] * res_data[j];
18139  }
18140 
18141  cpl_image_delete(extinction);
18142  cpl_image_delete(mapresponse);
18143 
18144  cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion);
18145 
18146  /*
18147  * Set to -1 the extrapolated values
18148  */
18149  resp_startwave = cpl_table_get(response, "WAVE", 0, &null);
18150  resp_endwave = cpl_table_get(response, "WAVE",
18151  cpl_table_get_nrow(response) -1, &null);
18152  for (j = 0; j < xlength; j++) {
18153  double this_wave = startwave + j * dispersion;
18154  if(this_wave < resp_startwave ||this_wave > resp_endwave)
18155  {
18156  for (i = 0; i < ylength; i++)
18157  out_data[j + xlength * i] = -1;
18158  }
18159  }
18160 
18161  cpl_table_erase_column(response, "RESPONSE_F");
18162 
18163  return outspectra;
18164 }
18165 
18166 
18183 cpl_image *mos_propagate_photometry_error(cpl_image *spectra,
18184  cpl_image *errors,
18185  cpl_table *response,
18186  cpl_table *ext_table,
18187  double startwave,
18188  double dispersion, double gain,
18189  double exptime, double airmass)
18190 {
18191  cpl_image *extinction;
18192  cpl_image *outerrors;
18193  cpl_image *mapresponse;
18194  cpl_image *maperror;
18195  float *err_data;
18196  float *out_data;
18197  float *ext_data;
18198  float *res_data;
18199  float *spe_data;
18200  int tlength, xlength, ylength;
18201  int i, j, k;
18202 
18203 
18204  if (errors == NULL || ext_table == NULL || response == NULL) {
18205  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18206  return NULL;
18207  }
18208 
18209  if (!cpl_table_has_column(response, "ERROR")) {
18210  return mos_apply_photometry(errors, response, ext_table, startwave,
18211  dispersion, gain, exptime, airmass);
18212  }
18213 
18214  cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18215  res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18216 
18217  if (res_data == NULL) {
18218  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18219  return NULL;
18220  }
18221 
18222  err_data = cpl_table_get_data_float(response, "ERROR");
18223 
18224  if (err_data == NULL) {
18225  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18226  return NULL;
18227  }
18228 
18229  tlength = cpl_table_get_nrow(response);
18230  xlength = cpl_image_get_size_x(errors);
18231  ylength = cpl_image_get_size_y(errors);
18232 
18233  if (xlength != tlength) {
18234  mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18235  map_table(mapresponse, startwave + dispersion/2, dispersion,
18236  response, "WAVE", "RESPONSE_F");
18237  res_data = cpl_image_get_data_float(mapresponse);
18238 
18239  maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18240  map_table(maperror, startwave + dispersion/2, dispersion,
18241  response, "WAVE", "ERROR");
18242  err_data = cpl_image_get_data_float(maperror);
18243  }
18244 
18245  /*
18246  * Map the atmospheric extinction factors to the same lambda sampling
18247  * of the extracted spectrum.
18248  */
18249 
18250  extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18251  map_table(extinction, startwave + dispersion/2, dispersion,
18252  ext_table, "WAVE", "EXTINCTION");
18253 
18254 
18255  /*
18256  * Convert from magnitudes to actual flux loss.
18257  */
18258 
18259  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18260  cpl_image_exponential(extinction, 10.);
18261 
18262  outerrors = cpl_image_duplicate(errors);
18263 
18264  ext_data = cpl_image_get_data_float(extinction);
18265  out_data = cpl_image_get_data_float(outerrors);
18266  spe_data = cpl_image_get_data_float(spectra);
18267 
18268  for (k = 0, i = 0; i < ylength; i++) {
18269  for (j = 0; j < xlength; j++, k++) {
18270  out_data[k] = ext_data[j] *
18271  sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] +
18272  res_data[j] * res_data[j] * out_data[k] * out_data[k]);
18273  }
18274  }
18275 
18276  cpl_image_delete(extinction);
18277  if (xlength != tlength) {
18278  cpl_image_delete(maperror);
18279  }
18280 
18281  cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion);
18282 
18283  cpl_table_erase_column(response, "RESPONSE_F");
18284  return outerrors;
18285 }
18286 
18287 
18363 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error,
18364  cpl_image *u_image, cpl_image *u_error,
18365  double startwave, double dispersion,
18366  double band, cpl_table *pol_sta,
18367  double ra, double dec, char *filter,
18368  int *polarisation,
18369  double *p_offset, double *p_error,
18370  double *a_offset, double *a_error)
18371 {
18372  cpl_table *standard;
18373  cpl_image *q_noise;
18374  cpl_image *q_signal;
18375  cpl_image *u_noise;
18376  cpl_image *u_signal;
18377  cpl_image *noise;
18378  double *q_ndata;
18379  double *q_sdata;
18380  double *u_ndata;
18381  double *u_sdata;
18382  double arctol = 0.5; /* Arc tolerance in degrees */
18383  double mindist;
18384  double cwave;
18385  double bwave[] = {3650., 4450., 5510., 6580., 8060};
18386  char *bands = "UBVRI";
18387  char p_label[] = {' ', 'p', '\0'};
18388  char dp_label[] = {' ', 'd', 'p', '\0'};
18389  char a_label[] = {' ', 'a', '\0'};
18390  char da_label[] = {' ', 'd', 'a', '\0'};
18391  int nbands = strlen(bands);
18392  int selected;
18393  int first, last, count, center;
18394  int nx;
18395  cpl_size col, row;
18396  int i, found, closest;
18397  int pband;
18398  int polarised;
18399  double q_obs;
18400  double q_err;
18401  double u_obs;
18402  double u_err;
18403  double p_obs;
18404  double p_err;
18405  double p_ref;
18406  double dp_ref;
18407  double a_obs;
18408  double a_err;
18409  double a_ref;
18410  double da_ref;
18411 
18412 
18413  *filter = '\0';
18414  *polarisation = 0;
18415  *p_offset = 0.0;
18416  *p_error = 0.0;
18417  *a_offset = 0.0;
18418  *a_error = 0.0;
18419 
18420  /*
18421  * Select reference standard star
18422  */
18423 
18424  cpl_table_select_all(pol_sta);
18425  cpl_table_and_selected_double(pol_sta, "RA", CPL_GREATER_THAN, ra-arctol);
18426  cpl_table_and_selected_double(pol_sta, "RA", CPL_LESS_THAN, ra+arctol);
18427  cpl_table_and_selected_double(pol_sta, "DEC", CPL_GREATER_THAN, dec-arctol);
18428  selected =
18429  cpl_table_and_selected_double(pol_sta, "DEC", CPL_LESS_THAN, dec+arctol);
18430 
18431  if (selected == 0) {
18432  cpl_msg_warning(cpl_func, "No standard star found in FOV");
18433  return 1;
18434  }
18435 
18436  if (selected > 1) {
18437  cpl_msg_warning(cpl_func,
18438  "Ambiguity: %d standard stars found in FOV", selected);
18439  return 1;
18440  }
18441 
18442  standard = cpl_table_extract_selected(pol_sta);
18443 
18444  cpl_msg_info(cpl_func, "Standard star: %s",
18445  cpl_table_get_string(standard, "name", 0));
18446 
18447  /*
18448  * Check whether the star is polarised or not
18449  */
18450 
18451  polarised = cpl_table_get_int(standard, "polarised", 0, NULL);
18452 
18453  cpl_msg_info(cpl_func, "This star is%sexpected to be polarised",
18454  polarised ? " " : " not ");
18455 
18456 
18457  /*
18458  * Determine the image row with the smallest median noise: this
18459  * row is assumed to refer to the standard star.
18460  * (note: the higher the S/N ratio of the original spectra, the
18461  * smaller the noise of the Stokes parameters Q and U).
18462  */
18463 
18464  nx = cpl_image_get_size_x(q_error);
18465 
18466  noise = cpl_image_collapse_median_create(q_error, 1, 0, 0);
18467  cpl_image_get_minpos(noise, &col, &row);
18468 
18469  cpl_image_delete(noise);
18470 
18471  if (col != 1) {
18472  cpl_table_delete(standard);
18473  cpl_msg_error(cpl_func,
18474  "Assertion failure!!! col = %"CPL_SIZE_FORMAT" (it should be 1)", col);
18475  return 1;
18476  }
18477 
18478  q_signal = cpl_image_extract(q_image, 1, row, nx, row);
18479  q_noise = cpl_image_extract(q_error, 1, row, nx, row);
18480  u_signal = cpl_image_extract(u_image, 1, row, nx, row);
18481  u_noise = cpl_image_extract(u_error, 1, row, nx, row);
18482 
18483  q_sdata = cpl_image_get_data_double(q_signal);
18484  q_ndata = cpl_image_get_data_double(q_noise);
18485  u_sdata = cpl_image_get_data_double(u_signal);
18486  u_ndata = cpl_image_get_data_double(u_noise);
18487 
18488 
18489  /*
18490  * Determine valid interval in input images (where error is positive).
18491  */
18492 
18493  first = -1;
18494  last = nx = cpl_image_get_size_x(q_signal);
18495  for (i = 0; i < nx; i++) {
18496  if (first < 0) {
18497  if (q_ndata[i] > 0.0) {
18498  first = i;
18499  }
18500  }
18501  else {
18502  if (q_ndata[i] <= 0.0) {
18503  last = i - 1;
18504  break;
18505  }
18506  }
18507  }
18508 
18509  count = last - first + 1;
18510 
18511  if (first < 0 || count < band) {
18512  cpl_table_delete(standard);
18513  cpl_image_delete(q_signal);
18514  cpl_image_delete(q_noise);
18515  cpl_image_delete(u_signal);
18516  cpl_image_delete(u_noise);
18517  cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count);
18518  return 1;
18519  }
18520 
18521  center = (first + last) / 2; // Center of valid spectrum
18522  cwave = startwave + dispersion * center; // Corresponding wavelength
18523 
18524 
18525  /*
18526  * Find the band UBVRI closest to the central wavelength.
18527  */
18528 
18529  found = 0;
18530  for (i = 0; i < nbands; i++) {
18531  p_label[0] = bands[i];
18532  if (cpl_table_is_valid(standard, p_label, 0)) {
18533  if (found == 0) {
18534  found = 1;
18535  mindist = fabs(bwave[i] - cwave);
18536  closest = i;
18537  }
18538  else if (mindist > fabs(bwave[i] - cwave)) {
18539  mindist = fabs(bwave[i] - cwave);
18540  closest = i;
18541  }
18542  }
18543  }
18544 
18545  if (!found) {
18546  cpl_table_delete(standard);
18547  cpl_image_delete(q_signal);
18548  cpl_image_delete(q_noise);
18549  cpl_image_delete(u_signal);
18550  cpl_image_delete(u_noise);
18551  cpl_msg_warning(cpl_func, "No reference value available");
18552  return 1;
18553  }
18554 
18555  center = (bwave[closest] - startwave) / dispersion; // Center of band (pix)
18556  cwave = bwave[closest]; // Wavelength of band
18557 
18558 
18559  /*
18560  * Check that the integration interval is entirely contained
18561  * in the valid interval, or give it up.
18562  */
18563 
18564  pband = floor(band / dispersion); // Band width in pixels
18565 
18566  if (center - pband/2 < first || center + pband/2 > last) {
18567  cpl_table_delete(standard);
18568  cpl_image_delete(q_signal);
18569  cpl_image_delete(q_noise);
18570  cpl_image_delete(u_signal);
18571  cpl_image_delete(u_noise);
18572  cpl_msg_warning(cpl_func, "No reference value available");
18573  return 1;
18574  }
18575 
18576  first = center - pband/2;
18577  last = center + pband/2;
18578 
18579  /*
18580  * Collect reference values. Note that if angle info is not available,
18581  * angle stuff is set automaticaly to zero.
18582  */
18583 
18584  p_label[0] = bands[closest];
18585  dp_label[0] = bands[closest];
18586  a_label[0] = bands[closest];
18587  da_label[0] = bands[closest];
18588 
18589  p_ref = cpl_table_get(standard, p_label, 0, NULL);
18590  dp_ref = cpl_table_get(standard, dp_label, 0, NULL);
18591  a_ref = cpl_table_get(standard, a_label, 0, NULL);
18592  da_ref = cpl_table_get(standard, da_label, 0, NULL);
18593 
18594  cpl_msg_info(cpl_func,
18595  "The expected polarisation is %.2f +- %.2f %%",
18596  p_ref, dp_ref);
18597 
18598  if (polarised) {
18599  cpl_msg_info(cpl_func,
18600  "The expected polarisation angle is %.2f +- %.2f degrees",
18601  a_ref, da_ref);
18602  }
18603 
18604  /*
18605  * Find median signal and median error.
18606  */
18607 
18608  q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1);
18609  q_err = cpl_image_get_median_window(q_error, first, 1, last, 1);
18610  u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1);
18611  u_err = cpl_image_get_median_window(u_error, first, 1, last, 1);
18612 
18613  /*
18614  * Measured linear polarisation and its error
18615  */
18616 
18617  p_obs = sqrt(q_obs * q_obs + u_obs * u_obs);
18618  p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err);
18619 
18620  /*
18621  * Measured polarisation angle
18622  */
18623 
18624  a_obs = 0.0;
18625  if (polarised) {
18626  if (fabs(q_obs) < 0.00001) {
18627  if (u_obs > 0.0) {
18628  a_obs = 45.0;
18629  }
18630  else {
18631  a_obs = 135.0;
18632  }
18633  }
18634  else {
18635  a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI;
18636  if (q_obs > 0.0) {
18637  if (u_obs < 0.0) {
18638  a_obs += 180.;
18639  }
18640  }
18641  else {
18642  a_obs += 90.;
18643  }
18644  }
18645  }
18646 
18647  /*
18648  * Error on polarisation angle
18649  */
18650 
18651  a_err = 0.0;
18652  if (polarised) {
18653  a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err)
18654  / (p_obs * p_obs)
18655  * 90 / CPL_MATH_PI;
18656  }
18657 
18658  p_obs *= 100;
18659  p_err *= 100;
18660  cpl_msg_info(cpl_func,
18661  "The measured polarisation is %.2f +- %.2f %%",
18662  p_obs, p_err);
18663 
18664  if (polarised) {
18665  cpl_msg_info(cpl_func,
18666  "The measured polarisation angle is %.2f +- %.2f degrees",
18667  a_obs, a_err);
18668  }
18669 
18670  *filter = bands[closest];
18671  *polarisation = polarised;
18672 
18673  if (polarised) {
18674  *p_offset = (p_obs - p_ref) / p_ref;
18675  *p_error = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref;
18676  }
18677  else {
18678  *p_offset = p_obs - p_ref;
18679  *p_error = sqrt(p_err * p_err + dp_ref * dp_ref);
18680  }
18681 
18682  *a_offset = a_obs - a_ref;
18683  *a_error = sqrt(a_err*a_err + da_ref*da_ref);
18684 
18685  return 0;
18686 
18687 }
18688 
18689 
18721 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
18722 {
18723  cpl_array *offsets;
18724  int noffset;
18725  int nslits = cpl_table_get_nrow(reference);
18726  int *nref;
18727  int *nobj;
18728  int corr, maxcorr;
18729  int best_shift;
18730  int i, j, k;
18731 
18732  cpl_error_code status = CPL_ERROR_NONE;
18733 
18734 
18735  *offset = 0.0;
18736 
18737  if (objects == NULL)
18738  return CPL_ERROR_NULL_INPUT;
18739 
18740  if (nslits != cpl_table_get_nrow(objects))
18741  return CPL_ERROR_INCOMPATIBLE_INPUT;
18742 
18743  nref = fors_get_nobjs_perslit(reference);
18744  nobj = fors_get_nobjs_perslit(objects);
18745 
18746  noffset = 0;
18747  for (i = 0; i < nslits; i++)
18748  noffset += nobj[i];
18749 
18750  if (noffset == 0) {
18751  cpl_free(nref);
18752  cpl_free(nobj);
18753  return CPL_ERROR_DATA_NOT_FOUND;
18754  }
18755 
18756  noffset = 0;
18757  for (i = 0; i < nslits; i++)
18758  noffset += nref[i];
18759 
18760  if (noffset == 0) {
18761  cpl_free(nref);
18762  cpl_free(nobj);
18763  return CPL_ERROR_DATA_NOT_FOUND;
18764  }
18765 
18766  offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE);
18767 
18768  noffset = 0; // The real number of offsets found will be counted.
18769 
18770  for (i = 0; i < nslits; i++) {
18771  if (nref[i] > 0 && nobj[i] > 0) {
18772  double shift;
18773  int length = cpl_table_get_int(objects, "length", i, NULL);
18774  double ytop = cpl_table_get_double(objects, "xtop", i, NULL);
18775  double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL);
18776  int *aref = cpl_calloc(length, sizeof(int));
18777  int *aobj = cpl_calloc(length, sizeof(int));
18778  float *pref = cpl_calloc(nref[i], sizeof(float));
18779  float *pobj = cpl_calloc(nobj[i], sizeof(float));
18780 
18781  for (j = 0; j < nref[i]; j++) {
18782  pref[j] = fors_get_object_position(reference, i, j + 1);
18783  aref[(int)pref[j]] = 1;
18784  }
18785 
18786  for (j = 0; j < nobj[i]; j++) {
18787  pobj[j] = fors_get_object_position(objects, i, j + 1);
18788  aobj[(int)pobj[j]] = 1;
18789  }
18790 
18791  /*
18792  * Do not consider objects at border
18793  */
18794 
18795  aref[0] = 0;
18796  aref[length - 1] = 0;
18797  aobj[0] = 0;
18798  aobj[length - 1] = 0;
18799 
18800 //for (j = 0; j < nref[i]; j++)
18801 //printf("references: %f, ", pref[j]);
18802 //printf("\n");
18803 //for (j = 0; j < nref[i]; j++)
18804 //printf("objects : %f, ", pobj[j]);
18805 //printf("\n");
18806 //for (j = 0; j < length; j++)
18807 //printf("%d", aref[j]);
18808 //printf("\n");
18809 //for (j = 0; j < length; j++)
18810 //printf("%d", aobj[j]);
18811 //printf("\n");
18812 
18813  /*
18814  * Object matching by correlation
18815  */
18816 
18817  maxcorr = 0;
18818  best_shift = length;
18819 
18820  for (shift = length/2, j = 0; j <= length; shift--, j++) {
18821  int rstart, ostart, count;
18822 
18823  if (shift > 0) {
18824  rstart = shift;
18825  ostart = 0;
18826  count = length - shift;
18827  }
18828  else {
18829  rstart = 0;
18830  ostart = -shift;
18831  count = length + shift;
18832  }
18833 
18834  corr = 0;
18835  for (k = 0; k < count; k++) {
18836  corr += aref[rstart + k] * aobj[ostart + k];
18837  }
18838 
18839  if (maxcorr < corr) {
18840  maxcorr = corr;
18841  best_shift = shift;
18842  }
18843  }
18844 
18845  if (best_shift == length) { // No shift found
18846 //printf("%d: No shift found\n", i);
18847  cpl_free(aref);
18848  cpl_free(aobj);
18849  cpl_free(pref);
18850  cpl_free(pobj);
18851  continue;
18852  }
18853 //printf("%d: Integer shift found = %d\n", i, best_shift);
18854 
18855  for (j = 0; j < nref[i]; j++) {
18856  for (k = 0; k < nobj[i]; k++) {
18857  if (fabs(pref[j] - pobj[k] - best_shift) < 2) {
18858  double ccd_offset = (pref[j] - pobj[k])
18859  * (ytop - ybottom)
18860  / length;
18861 
18862 //printf("%d: Match found: %f\n", i, ccd_offset);
18863  /*
18864  * The matching object is found, store the
18865  * corresponding offset
18866  */
18867 
18868  cpl_array_set(offsets, noffset, ccd_offset);
18869  noffset++;
18870  break;
18871  }
18872  }
18873  }
18874 
18875  cpl_free(aref);
18876  cpl_free(aobj);
18877  cpl_free(pref);
18878  cpl_free(pobj);
18879  }
18880 //else
18881 //printf("%d: No object found\n", i);
18882  }
18883 
18884  cpl_free(nref);
18885  cpl_free(nobj);
18886 
18887 //printf("%d offsets found in total\n", noffset);
18888  if (noffset > 0) {
18889  if (noffset % 2) {
18890  *offset = cpl_array_get_median(offsets);
18891  }
18892  else {
18893  double *a = cpl_malloc(sizeof(double) * noffset);
18894  for (i = 0; i < noffset; i++) {
18895  a[i] = cpl_array_get_double(offsets, i, NULL);
18896  }
18897  *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) +
18898  fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0;
18899  cpl_free(a);
18900  }
18901  }
18902  else
18903  status = CPL_ERROR_DATA_NOT_FOUND;
18904 //printf("Median offset: %f\n", *offset);
18905 
18906  cpl_array_delete(offsets);
18907 
18908  return status;
18909 
18910 }
18911 
18912 
18924 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
18925 {
18926  cpl_image *source;
18927  int nx = cpl_image_get_size_x(image);
18928  int ny = cpl_image_get_size_y(image);
18929  float *idata;
18930  float *sdata;
18931  int i, j, pos;
18932  double xpos, ypos, xfrac, yfrac;
18933  int xint, yint;
18934 
18935 
18936  if (fabs(dx) >= nx || fabs(dy) >= ny)
18937  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18938 
18939  source = cpl_image_duplicate(image);
18940  idata = cpl_image_get_data_float(image);
18941  sdata = cpl_image_get_data_float(source);
18942 
18943  /*
18944  * Shift in y
18945  */
18946 
18947  yfrac = - dy - floor(- dy);
18948  xfrac = - dx - floor(- dx);
18949 
18950  for (pos = 0, j = 0; j < ny; j++) {
18951  ypos = j - dy;
18952  yint = floor(ypos);
18953  for (i = 0; i < nx; i++) {
18954  xpos = i - dx;
18955  xint = floor(xpos);
18956  if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) {
18957  idata[pos] = 0.0;
18958  }
18959  else {
18960  idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac)
18961  + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac)
18962  + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac
18963  + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac;
18964  }
18965  pos++;
18966  }
18967  }
18968 
18969  cpl_image_delete(source);
18970 
18971  return CPL_ERROR_NONE;
18972 }
18973 
18985 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
18986 {
18987 #ifdef CPL_SIZE_FORMAT
18988  cpl_size row;
18989 #else
18990  int row;
18991 #endif
18992 
18993  cpl_table_duplicate_column(slits, "x", slits, "xtop");
18994  cpl_table_add_columns(slits, "x", "xbottom");
18995  cpl_table_divide_scalar(slits, "x", 2); // Mean x position
18996  cpl_table_subtract_scalar(slits, "x", nx/2); // Relative to CCD center
18997  cpl_table_multiply_columns(slits, "x", "x"); // Squared
18998 
18999  cpl_table_duplicate_column(slits, "y", slits, "ytop");
19000  cpl_table_add_columns(slits, "y", "ybottom");
19001  cpl_table_divide_scalar(slits, "y", 2); // Mean y position
19002  cpl_table_subtract_scalar(slits, "y", ny/2); // Relative to CCD center
19003  cpl_table_multiply_columns(slits, "y", "y"); // Squared
19004 
19005  cpl_table_add_columns(slits, "x", "y"); // Distance from center
19006  cpl_table_get_column_minpos(slits, "x", &row); // Min distance from center
19007 
19008  cpl_table_erase_column(slits, "x");
19009  cpl_table_erase_column(slits, "y");
19010 
19011  return row;
19012 }
19013 
19033 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits,
19034  double xwidth, double ywidth,
19035  int dx, double gain, double *o_flux, double *o_err)
19036 {
19037  int nx = cpl_image_get_size_x(image);
19038  int ny = cpl_image_get_size_y(image);
19039  int slit = mos_slit_closest_to_center(slits, nx, ny);
19040  int ytop = (int)cpl_table_get(slits, "ytop", slit, NULL);
19041  int ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL);
19042  int dy = ytop - ybottom;
19043  int xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) +
19044  cpl_table_get(slits, "xbottom", slit, NULL)) / 2);
19045  int xleft = xcenter - dx;
19046  int xright = xcenter + dx + 1;
19047  double area = xwidth * ywidth; // squared mm
19048  int npix = (2*dx + 1) * dy;
19049  int count = 0;
19050  float *data = cpl_image_get_data_float(image);
19051  double flux = 0.0;
19052  double error = 0.0;
19053  int satur = 60000;
19054  int x, y;
19055 
19056 
19057  if (cpl_table_has_column(slits, "ywidth")) {
19058  area = cpl_table_get(slits, "xwidth", slit, NULL)
19059  * cpl_table_get(slits, "ywidth", slit, NULL);
19060  }
19061 
19062  *o_flux = 0.0;
19063  *o_err = 0.0;
19064 
19065  if (xleft < 0)
19066  xleft = 0;
19067 
19068  if (xleft > nx)
19069  xleft = nx;
19070 
19071  if (xright < 0)
19072  xright = 0;
19073 
19074  if (xright > nx)
19075  xright = nx;
19076 
19077  if (ytop < 0)
19078  ytop = 0;
19079 
19080  if (ytop > ny)
19081  ytop = ny;
19082 
19083  if (ybottom < 0)
19084  ybottom = 0;
19085 
19086  if (ybottom > ny)
19087  ybottom = ny;
19088 
19089  count = (xright - xleft) * (ytop - ybottom);
19090 
19091  if (count == 0)
19092  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19093 
19094  count = 0;
19095 
19096  for (y = ybottom; y < ytop; y++) {
19097  for (x = xleft; x < xright; x++) {
19098  double value = data[x + y * nx];
19099  if (value < satur) {
19100  flux += value;
19101  count++;
19102  }
19103  }
19104  }
19105 
19106  if (count == 0)
19107  return CPL_ERROR_DIVISION_BY_ZERO;
19108 
19109  error = sqrt(flux/gain);
19110 
19111  /*
19112  * Flux correction for lost pixels
19113  */
19114 
19115  flux *= (float)npix / count;
19116  error *= (float)npix / count;
19117 
19118  flux /= area;
19119  error /= area;
19120 
19121  *o_flux = flux;
19122  *o_err = error;
19123 
19124  return CPL_ERROR_NONE;
19125 }
19126 
19127 
19150 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits,
19151  double xwidth, double ywidth,
19152  double lambda, double startwave,
19153  double dispersion, int dx, double gain,
19154  double *o_flux, double *o_err)
19155 {
19156  int nx = cpl_image_get_size_x(image);
19157  int ny = cpl_image_get_size_y(image);
19158  int slit = mos_slit_closest_to_center(slits, nx, ny);
19159  int dy = (int)cpl_table_get(slits, "length", slit, NULL);
19160  int ybottom = (int)cpl_table_get(slits, "position", slit, NULL);
19161  int ytop = ybottom + dy;
19162  int xcenter = (int)floor((lambda - startwave) / dispersion + 0.5);
19163  int xleft = xcenter - dx;
19164  int xright = xcenter + dx + 1;
19165  double area = xwidth * ywidth;
19166  int npix = (2*dx + 1) * dy;
19167  int count = 0;
19168  float *data = cpl_image_get_data_float(image);
19169  double flux = 0.0;
19170  double error = 0.0;
19171  int satur = 60000;
19172  int x, y;
19173 
19174 
19175  if (cpl_table_has_column(slits, "ywidth")) {
19176  area = cpl_table_get(slits, "xwidth", slit, NULL)
19177  * cpl_table_get(slits, "ywidth", slit, NULL);
19178  }
19179 
19180  *o_flux = 0.0;
19181  *o_err = 0.0;
19182 
19183  if (xleft < 0)
19184  xleft = 0;
19185 
19186  if (xleft > nx)
19187  xleft = nx;
19188 
19189  if (xright < 0)
19190  xright = 0;
19191 
19192  if (xright > nx)
19193  xright = nx;
19194 
19195  if (ytop < 0)
19196  ytop = 0;
19197 
19198  if (ytop > ny)
19199  ytop = ny;
19200 
19201  if (ybottom < 0)
19202  ybottom = 0;
19203 
19204  if (ybottom > ny)
19205  ybottom = ny;
19206 
19207  count = (xright - xleft) * (ytop - ybottom);
19208 
19209  if (count == 0)
19210  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19211 
19212  count = 0;
19213 
19214  for (y = ybottom; y < ytop; y++) {
19215  for (x = xleft; x < xright; x++) {
19216  double value = data[x + y * nx];
19217  if (value < satur) {
19218  flux += value;
19219  count++;
19220  }
19221  }
19222  }
19223 
19224  if (count == 0)
19225  return CPL_ERROR_DIVISION_BY_ZERO;
19226 
19227  error = sqrt(flux/gain);
19228 
19229  /*
19230  * Flux correction for lost pixels
19231  */
19232 
19233  flux *= (float)npix / count;
19234  error *= (float)npix / count;
19235 
19236  flux /= area;
19237  error /= area;
19238 
19239  *o_flux = flux;
19240  *o_err = error;
19241 
19242  return CPL_ERROR_NONE;
19243 
19244 }
19245 
19246 
19260 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit,
19261  char *label, double *mvalue)
19262 {
19263  int position = cpl_table_get_int(slits, "position", slit, NULL);
19264  int length = cpl_table_get_int(slits, "length", slit, NULL);
19265  cpl_table *tmp = cpl_table_extract(table, position, length);
19266 
19267  *mvalue = cpl_table_get_column_median(tmp, label);
19268  cpl_table_delete(tmp);
19269 
19270  if (cpl_error_get_code() != CPL_ERROR_NONE)
19271  return 1;
19272 
19273  return 0;
19274 }
19275 
19276 
19288 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny)
19289 {
19290  cpl_mask *kernel = cpl_mask_new(nx, ny);
19291  cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image),
19292  cpl_image_get_size_y(image),
19293  cpl_image_get_type(image));
19294 
19295  cpl_mask_not(kernel);
19296  cpl_image_filter_mask(filtered, image, kernel,
19297  CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
19298  cpl_mask_delete(kernel);
19299 
19300  return filtered;
19301 }
19302 
19303 int fors_mos_is_lss_like(cpl_table *maskslits, int nslits_out_det)
19304 {
19305  int treat_as_lss = 1;
19306  double mxpos = cpl_table_get_column_median(maskslits, "xtop");
19307  double * slit_xpos = cpl_table_get_data_double(maskslits, "xtop");
19308  cpl_size nslits = cpl_table_get_nrow(maskslits);
19309 
19310  //If not all the slits are illuminated, then we cannot say that
19311  //it is lss-like (PIPE-4380)
19312  if(nslits_out_det != 0)
19313  return 0;
19314 
19315  for (cpl_size i = 0; i < nslits; i++) {
19316  if (fabs(mxpos-slit_xpos[i]) > 0.01) {
19317  treat_as_lss = 0;
19318  break;
19319  }
19320  }
19321  return treat_as_lss;
19322 }
cpl_image * mos_spatial_calibration(cpl_image *spectra, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion, int flux, cpl_image *calibration)
Spatial remapping of CCD spectra eliminating the spectral curvature.
Definition: moses.c:8518
cpl_table * mos_photometric_calibration(cpl_image *spectra, double startwave, double dispersion, double gain, double exptime, cpl_table *ext_table, double airmass, cpl_table *flux_table, int order)
Produce instrument response curve, with some ancillary information.
Definition: moses.c:17511
cpl_table * mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
Build the IDS coefficients table from a global distortions table.
Definition: moses.c:1848
cpl_image * mos_map_pixel(cpl_table *idscoeff, double reference, double blue, double red, double dispersion, int trend)
Create a pixel map from an IDS coefficients table.
Definition: moses.c:11192
double mos_integrate_signal(cpl_image *image, cpl_image *wavemap, int ystart, int yend, double wstart, double wend)
Integrate signal from wavelength and spatial interval.
Definition: moses.c:14778
cpl_bivector * mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines, double min_disp, double max_disp, double tolerance)
Identify peak candidates.
Definition: moses.c:4316
cpl_vector * mos_refine_peaks(const float *spectrum, int length, cpl_vector *peaks, int sradius)
Improve (when possible) accuracy of peaks candidates positions.
Definition: moses.c:4207
cpl_table * mos_sky_map(cpl_image *spectra, cpl_image *wavemap, double dispersion, cpl_image *skymap)
Create a CCD median sky map.
Definition: moses.c:12483
cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, cpl_image *wavemap, int mode, int degree)
Interpolate LSS wavelength calibration.
Definition: moses.c:3064
cpl_image * mos_wavelength_calibration_raw(const cpl_image *image, cpl_vector *lines, double dispersion, float level, int sradius, int order, double reject, double refwave, double *wavestart, double *waveend, int *nlines, double *error, cpl_table *idscoeff, cpl_image *calibration, cpl_image *residuals, cpl_table *restable, cpl_mask *refmask, cpl_table *detected_lines)
Derive wavelength calibration from a raw arc lamp or sky exposure.
Definition: moses.c:5469
double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, double wavestart, double dispersion, int radius, int highres)
Estimate the spectral distortion modeling goodness.
Definition: moses.c:11055
cpl_image * mos_propagate_photometry_error(cpl_image *spectra, cpl_image *errors, cpl_table *response, cpl_table *ext_table, double startwave, double dispersion, double gain, double exptime, double airmass)
Propagate errors from response curve and extracted spectra.
Definition: moses.c:18183
cpl_table * mos_load_slits_fors_mxu(cpl_propertylist *header)
Create slit location table from FITS header of FORS2-MXU data.
Definition: moses.c:14866
cpl_image * mos_normalise_flat(cpl_image *flat, cpl_image *spatial, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion, int sradius, int polyorder)
Normalise a flat field exposure.
Definition: moses.c:2293
int mos_get_nobjects(cpl_table *slits)
Get the total number of objects detected in a slits table.
Definition: moses.c:17037
cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
Rotate a slit location table.
Definition: moses.c:6324
cpl_image * mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion)
Remapping of spatially rectified wavelengths to original CCD pixels.
Definition: moses.c:11388
cpl_image * mos_wavelength_calibration(cpl_image *image, double refwave, double firstLambda, double lastLambda, double dispersion, cpl_table *idscoeff, int flux)
Remap at constant wavelength step an image of rectified scientific spectra.
Definition: moses.c:9705
cpl_table * mos_identify_slits(cpl_table *slits, cpl_table *maskslits, cpl_table *global)
Identify slits listed in a slit location table.
Definition: moses.c:6438
cpl_table * mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap, double dispersion, double factor, int minpoints, cpl_image *skymap)
Create a CCD median sky map.
Definition: moses.c:12150
cpl_image * mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
Local determination of sky.
Definition: moses.c:12827
double fors_tools_get_kth_double(double *a, int n, int k)
Same as cpl_tools_get_kth_double.
Definition: fors_utils.c:211
cpl_table * mos_load_slits_fors_mos(cpl_propertylist *header, int *nslits_out_det)
Create slit location table from FITS header of FORS1/2 MOS data.
Definition: moses.c:15106
double mos_get_gain_vimos(cpl_propertylist *header)
Return gain factor for a VIMOS exposure.
Definition: moses.c:15476
cpl_table * mos_wavelength_align(cpl_image *image, cpl_table *slits, double refwave, double firstLambda, double lastLambda, cpl_table *idscoeff, cpl_vector *skylines, int highres, int order, cpl_image *calibration, int sradius)
Modify the input wavelength solution to match reference sky lines.
Definition: moses.c:9994
cpl_error_code mos_validate_slits(cpl_table *slits)
Check validity of a slit location table.
Definition: moses.c:6260
cpl_error_code mos_subtract_background(cpl_image *image)
Subtract the background.
Definition: moses.c:16386
int mos_get_maxobjs_per_slit(cpl_table *slits)
Get the maximum possible number of objects in a slit.
Definition: moses.c:17011
cpl_error_code mos_interpolate_wavecalib_slit(cpl_table *idscoeff, cpl_table *slits, int order, int global)
Interpolate MOS wavelength calibration.
Definition: moses.c:2935
cpl_image * mos_remove_bias(cpl_image *image, cpl_image *bias, cpl_table *overscans)
Subtract the bias from a CCD exposure.
Definition: moses.c:3654
cpl_table * mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
Fit spectral traces.
Definition: moses.c:8176
cpl_image * mos_detect_objects(cpl_image *image, cpl_table *slits, int margin, int maxradius, int conradius)
Detect objects in rectified scientific frame.
Definition: moses.c:13952
cpl_image * mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
Local determination of sky.
Definition: moses.c:12725
int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error, cpl_image *u_image, cpl_image *u_error, double startwave, double dispersion, double band, cpl_table *pol_sta, double ra, double dec, char *filter, int *polarisation, double *p_offset, double *p_error, double *a_offset, double *a_error)
Estimate linear polarisation parameters on spectral interval.
Definition: moses.c:18363
cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
Shift values in an image.
Definition: moses.c:18924
int mos_check_multiplex(cpl_table *slits)
Determining whether a VIMOS mask has spectral multplexing or not.
Definition: moses.c:15632
cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits, double xwidth, double ywidth, int dx, double gain, double *o_flux, double *o_err)
Measure flux from spectral interval on CCD.
Definition: moses.c:19033
int mos_lines_width(const float *spectrum, int length)
Estimate lines widths (in pixel) in arc lamp spectrum.
Definition: moses.c:4001
cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces, int mode)
Recompute tracing coefficients globally.
Definition: moses.c:8337
cpl_bivector * mos_find_peaks(const float *spectrum, int length, cpl_vector *lines, cpl_polynomial *ids, double refwave, int sradius)
Find the reference lines peaks using a polynomial first-guess.
Definition: moses.c:5289
cpl_vector * mos_peak_candidates(const float *spectrum, int length, float level, float exp_width)
Find positions of peaks candidates.
Definition: moses.c:4104
cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, int nscience, float tolerance)
Intersect a number of slit tables.
Definition: moses.c:16416
cpl_table * mos_wavelength_align_lss(cpl_image *image, double refwave, double firstLambda, double lastLambda, cpl_table *idscoeff, cpl_vector *skylines, int highres, int order, cpl_image *calibration, int sradius)
Modify the input wavelength solution to match reference sky lines (LSS).
Definition: moses.c:10547
cpl_image * mos_subtract_sky(cpl_image *science, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion)
Subtract the sky from the scientific CCD exposure.
Definition: moses.c:1972
cpl_image * mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference, double blue, double red)
Create a wavelengths map from an IDS coefficients table.
Definition: moses.c:11277
double mos_eval_dds(cpl_polynomial *ids, double blue, double red, double refwave, double pixel)
Evaluate the wavelength of a pixel position.
Definition: moses.c:5005
cpl_table * mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference, double blue, double red, double dispersion)
Trace flat field spectra.
Definition: moses.c:7742
cpl_image * mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits, cpl_vector *lines, double dispersion, float level, int sradius, int order, double reject, double refwave, double *wavestart, double *waveend, int *nlines, double *error, cpl_table *idscoeff, cpl_image *calibration, cpl_image *residuals, cpl_table *restable, cpl_table *detected_lines)
Derive wavelength calibration from a rectified arc lamp or sky exposure.
Definition: moses.c:8936
int mos_check_slits(cpl_table *slits, float rescale)
Check that all slit have been detected, insert them if not.
Definition: moses.c:17068
cpl_image * mos_apply_photometry(cpl_image *spectra, cpl_table *response, cpl_table *ext_table, double startwave, double dispersion, double gain, double exptime, double airmass)
Apply response curve to extracted spectra.
Definition: moses.c:18063
cpl_error_code mos_refmask_find_gaps(cpl_mask *refmask, cpl_image *master_flat, double level)
Reconstruct the gaps required for slit location.
Definition: moses.c:16226
Definition: list.c:74
cpl_image * mos_ksigma_stack(cpl_imagelist *imlist, double klow, double khigh, int kiter, cpl_image **good)
Stack images using k-sigma clipping.
Definition: moses.c:17988
cpl_image ** mos_extract_objects(cpl_image *science, cpl_image *science_var, cpl_image *sky, cpl_table *objects, int extraction, double ron, double gain, int ncombined)
Extract detected objects from rectified scientific frame.
Definition: moses.c:14303
cpl_error_code mos_clean_cosmics(cpl_image *image, float gain, float threshold, float ratio)
Remove cosmic rays from sky-subtracted CCD spectral exposure.
Definition: moses.c:13182
cpl_table * mos_global_distortion(cpl_table *slits, cpl_table *maskslits, cpl_table *ids, cpl_table *crv, double reference)
Determine all global distortions models.
Definition: moses.c:1200
int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, double dispersion, int saturation, double *mfwhm, double *rmsfwhm, double *resolution, double *rmsres, int *nlines)
Compute mean spectral resolution at a given arc lamp line.
Definition: moses.c:14510
cpl_error_code mos_arc_background_1D(float *spectrum, float *back, int length, int msize, int fsize)
Background determination on 1D emission line spectrum (arc)
Definition: moses.c:3827
cpl_table * mos_resolution_table(cpl_image *image, double startwave, double dispersion, int saturation, cpl_vector *lines)
Compute mean spectral resolution at a given arc lamp line.
Definition: moses.c:14706
cpl_table * mos_load_slits_fors_pmos(cpl_propertylist *header, int *nslits_out_det)
Create PMOS slit location table from FITS header of FORS1/2 MOS data.
Definition: moses.c:17171
cpl_table * mos_load_slits_vimos(cpl_propertylist *header)
Create slit location table from FITS header of VIMOS data.
Definition: moses.c:15521
cpl_error_code mos_randomise_image(cpl_image *image, double ron, double gain, double bias)
Randomise image.
Definition: moses.c:16182
cpl_table * mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits, cpl_table *slits)
Build the curvature coefficients table from a global distortions table.
Definition: moses.c:1693
cpl_table * mos_locate_spectra(cpl_mask *mask)
Find the location of detected spectra on the CCD.
Definition: moses.c:6172
cpl_table * mos_load_slits_fors_lss(cpl_propertylist *header)
Create slit location table from FITS header of FORS1/2 LSS data.
Definition: moses.c:15308
cpl_polynomial * mos_poly_pix2wav(cpl_bivector *pixwav, int order, double reject, int minlines, int *nlines, double *err)
Fit polynomial relation from pixels to wavelengths.
Definition: moses.c:5232
cpl_error_code mos_interpolate_wavecalib_mos(cpl_table *idscoeff, int mode, int degree)
Interpolate wavelength calibration for a single MOS slit.
Definition: moses.c:3416
cpl_image * mos_arc_background(cpl_image *image, int msize, int fsize)
Background determination on emission line spectrum (arc)
Definition: moses.c:3929
cpl_error_code mos_saturation_process(cpl_image *image)
Process saturation.
Definition: moses.c:16318
cpl_polynomial * mos_poly_wav2pix(cpl_bivector *pixwav, int order, double reject, int minlines, int *nlines, double *err, cpl_bivector **pixwav_used)
Fit polynomial relation from wavelengths to pixels.
Definition: moses.c:5060
cpl_image * mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, cpl_image *spatial, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion, int flux)
Remapping of slit spectra into a grid of lambda-space coordinates.
Definition: moses.c:11738
int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
Return slit closest to CCD center.
Definition: moses.c:18985
cpl_image * mos_image_filter_median(cpl_image *image, int nx, int ny)
Convenience function for standard median filtering.
Definition: moses.c:19288
cpl_image * mos_spatial_map(cpl_image *spectra, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion)
Create coordinate map from spectral curvature table.
Definition: moses.c:13686
cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits, double xwidth, double ywidth, double lambda, double startwave, double dispersion, int dx, double gain, double *o_flux, double *o_err)
Measure flux from spectral interval on remapped frame.
Definition: moses.c:19150
int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
Estimate offset between two object tables.
Definition: moses.c:18721
cpl_image * mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, int polyorder)
Normalise a long slit flat field exposure.
Definition: moses.c:2744
cpl_table * mos_load_overscans_vimos(const cpl_propertylist *header, int check_consistency)
Get the overscan positions from FITS header of VIMOS data.
Definition: moses.c:15731
int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit, char *label, double *mvalue)
Compute median from a table column section corresponding to a slit.
Definition: moses.c:19260
cpl_table * mos_build_slit_location(cpl_table *global, cpl_table *maskslits, int ysize)
Build the slit location table from a global distortions table.
Definition: moses.c:1546