FORS Pipeline Reference Manual  5.3.9
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  //In the very worst case scenario, a given peak is always assigned
4377  //a possible identification (within the tolerance). This is actually
4378  //very unlikely but not impossible.
4379  for (i = 0; i < npeaks; i++)
4380  ident[i] = cpl_malloc(npeaks * npeaks * sizeof(int));
4381 
4382  /*
4383  * This is just the number of intervals - one less than the number
4384  * of points (catalog wavelengths, or detected peaks).
4385  */
4386 
4387  nlint = nlines - 1;
4388  npint = npeaks - 1;
4389 
4390 
4391  /*
4392  * Here the big loops on catalog lines begins.
4393  */
4394 
4395  for (i = 1; i < nlint; i++) {
4396 
4397 
4398  /*
4399  * For each catalog wavelength I take the previous and the next one,
4400  * and compute the ratio of the corresponding wavelength intervals.
4401  * This ratio will be compared to all the ratios obtained doing the
4402  * same with all the detected peaks positions.
4403  */
4404 
4405  lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
4406 
4407 
4408  /*
4409  * Here the loop on detected peaks positions begins.
4410  */
4411 
4412  for (j = 1; j < npint; j++) {
4413 
4414  /*
4415  * Not all peaks are used for computing ratios: just the ones
4416  * that are compatible with the expected spectral dispersion
4417  * are taken into consideration. Therefore, I define the pixel
4418  * intervals before and after any peak that are compatible with
4419  * the specified dispersion interval, and select just the peaks
4420  * within such intervals. If either of the two intervals doesn't
4421  * contain any peak, then I skip the current peak and continue
4422  * with the next.
4423  */
4424 
4425  lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
4426  lo_end = peak[j] - (line[i] - line[i-1]) / max_disp;
4427  hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
4428  hi_end = peak[j] + (line[i+1] - line[i]) / min_disp;
4429 
4430  for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
4431  if (peak[k] > lo_end)
4432  break;
4433  if (peak[k] > lo_start) {
4434  peak_lo[npeaks_lo] = k;
4435  ++npeaks_lo;
4436  }
4437  }
4438 
4439  if (npeaks_lo == 0)
4440  continue;
4441 
4442  for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
4443  if (peak[k] > hi_end)
4444  break;
4445  if (peak[k] > hi_start) {
4446  peak_hi[npeaks_hi] = k;
4447  ++npeaks_hi;
4448  }
4449  }
4450 
4451  if (npeaks_hi == 0)
4452  continue;
4453 
4454 
4455  /*
4456  * Now I have all peaks that may help for a local identification.
4457  * peak_lo[k] is the sequence number of the k-th peak of the lower
4458  * interval; peak_hi[l] is the sequence number of the l-th peak of
4459  * the higher interval. j is, of course, the sequence number of the
4460  * current peak (second big loop).
4461  */
4462 
4463  prev_variation = 1000.0;
4464  minl = mink = 0;
4465 
4466  for (k = 0; k < npeaks_lo; k++) {
4467  denom = peak[j] - peak[peak_lo[k]];
4468  for (l = 0; l < npeaks_hi; l++) {
4469 
4470  /*
4471  * For any pair of peaks - one from the lower and the other
4472  * from the higher interval - I compute the same ratio that
4473  * was computed with the current line catalog wavelength.
4474  */
4475 
4476  pratio = (peak[peak_hi[l]] - peak[j]) / denom;
4477 
4478  /*
4479  * If the two ratios are compatible within the specified
4480  * tolerance, we have a preliminary identification. This
4481  * will be marked in the matrix ident[][], where the first
4482  * index corresponds to a peak sequence number, and the second
4483  * index is the counter of the identifications made during
4484  * this whole process. The array of counters is nident[].
4485  * If more than one interval pair fulfills the specified
4486  * tolerance, the closest to the expected ratio is selected.
4487  */
4488 
4489  variation = fabs(lratio-pratio) / pratio;
4490 
4491  if (variation < tolerance) {
4492  if (variation < prev_variation) {
4493  prev_variation = variation;
4494  minl = l;
4495  mink = k;
4496  }
4497  }
4498  }
4499  }
4500  if (prev_variation < tolerance) {
4501  ident[j][nident[j]] = i;
4502  ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
4503  ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
4504  ++nident[j];
4505  ++nident[peak_hi[minl]];
4506  ++nident[peak_lo[mink]];
4507  }
4508  } /* End loop on positions */
4509  } /* End loop on lines */
4510 
4511 
4512  /*
4513  * At this point I have filled the ident matrix with all my preliminary
4514  * identifications. Ambiguous identifications must be eliminated.
4515  */
4516 
4517 
4518  for (i = 0; i < npeaks; i++) {
4519 
4520 
4521  /*
4522  * I don't take into consideration peaks that were never identified.
4523  * They are likely contaminations, or emission lines that were not
4524  * listed in the input wavelength catalog.
4525  */
4526 
4527  if (nident[i] > 1) {
4528 
4529 
4530  /*
4531  * Initialise the histogram of wavelengths assigned to the i-th peak.
4532  */
4533 
4534  for (j = 0; j < nlines; j++)
4535  lident[j] = 0;
4536 
4537 
4538  /*
4539  * Count how many times each catalog wavelength was assigned
4540  * to the i-th peak.
4541  */
4542 
4543  for (j = 0; j < nident[i]; j++)
4544  ++lident[ident[i][j]];
4545 
4546 
4547  /*
4548  * What wavelength was most frequently assigned to the i-th peak?
4549  */
4550 
4551  max = 0;
4552  maxpos = 0;
4553  for (j = 0; j < nlines; j++) {
4554  if (max < lident[j]) {
4555  max = lident[j];
4556  maxpos = j;
4557  }
4558  }
4559 
4560 
4561  /*
4562  * Were there other wavelengths assigned with the same frequency?
4563  * This would be the case of an ambiguous identification. It is
4564  * safer to reject this peak...
4565  */
4566 
4567  ambiguous = 0;
4568 
4569  for (k = maxpos + 1; k < nlines; k++) {
4570  if (lident[k] == max) {
4571  ambiguous = 1;
4572  break;
4573  }
4574  }
4575 
4576  if (ambiguous)
4577  continue;
4578 
4579 
4580  /*
4581  * Otherwise, I assign to the i-th peak the wavelength that was
4582  * most often assigned to it.
4583  */
4584 
4585  tmp_xpos[n] = peak[i];
4586  tmp_lambda[n] = line[maxpos];
4587  tmp_ilambda[n] = maxpos;
4588 
4589  ++n;
4590 
4591  }
4592 
4593  }
4594 
4595 
4596  /*
4597  * Check on identified peaks. Contaminations from other spectra might
4598  * be present and should be excluded: this type of contamination
4599  * consists of peaks that have been _correctly_ identified! The non-
4600  * spectral type of light contamination should have been almost all
4601  * removed already in the previous steps, but it may still be present.
4602  * Here, the self-consistent sequences of identified peaks are
4603  * separated one from the other. At the moment, just the longest of
4604  * such sequences is selected (in other words, spectral multiplexing
4605  * is ignored).
4606  */
4607 
4608  if (n > 1) {
4609  nn = 0; /* Number of peaks in the list of sequences */
4610  nseq = 0; /* Current sequence */
4611  for (k = 0; k < n; k++) {
4612  if (flag[k] == 0) { /* Was peak k already assigned to a sequence? */
4613  flag[k] = 1;
4614  xpos[nn] = tmp_xpos[k]; /* Begin the nseq-th sequence */
4615  lambda[nn] = tmp_lambda[k];
4616  ilambda[nn] = tmp_ilambda[k];
4617  ++seq_length[nseq];
4618  ++nn;
4619 
4620  /*
4621  * Now look for all the following peaks that are compatible
4622  * with the expected spectral dispersion, and add them in
4623  * sequence to xpos. Note that missing peaks are not a problem...
4624  */
4625 
4626  i = k;
4627  while (i < n - 1) {
4628  found = 0;
4629  for (j = i + 1; j < n; j++) {
4630  if (flag[j] == 0) {
4631  disp = (tmp_lambda[j] - tmp_lambda[i])
4632  / (tmp_xpos[j] - tmp_xpos[i]);
4633  if (disp >= min_disp && disp <= max_disp) {
4634  flag[j] = 1;
4635  xpos[nn] = tmp_xpos[j];
4636  lambda[nn] = tmp_lambda[j];
4637  ilambda[nn] = tmp_ilambda[j];
4638  ++seq_length[nseq];
4639  ++nn;
4640  i = j;
4641  found = 1;
4642  break;
4643  }
4644  }
4645  }
4646  if (!found)
4647  break;
4648  }
4649 
4650  /*
4651  * Current sequence is completed: begin new sequence on the
4652  * excluded peaks, starting the loop on peaks again.
4653  */
4654 
4655  ++nseq;
4656  k = 0;
4657  }
4658  }
4659 
4660 
4661  /*
4662  * Find the longest sequence of self-consistent peaks.
4663  */
4664 
4665  maxpos = max = 0;
4666 
4667  if (mos_multiplex < 0) {
4668  for (i = 0; i < nseq; i++) {
4669  if (seq_length[i] > max) {
4670  max = seq_length[i];
4671  maxpos = i;
4672  }
4673  }
4674  }
4675  else {
4676 
4677  /*
4678  * Now consider the sequence which lays in the specified
4679  * CCD region (indicated by mos_multiplex): that is, _most_
4680  * of its lines (more than half) must be in that region...
4681  */
4682 
4683  nn = 0;
4684  found = 0;
4685 
4686  for (i = 0; i < nseq; i++) {
4687  n = seq_length[i];
4688  if (n > 5) {
4689  cpl_array *regions = cpl_array_new(n, CPL_TYPE_INT);
4690  int region;
4691 
4692  for (j = 0; j < n; j++)
4693  cpl_array_set_int(regions, j,
4694  ((int)floor(xpos[nn + j])) / mos_region_size);
4695 
4696  region = (int)cpl_array_get_median(regions);
4697  cpl_array_delete(regions);
4698 
4699  if (mos_multiplex == region) {
4700  if (found) {
4701  cpl_msg_debug(cpl_func, "More than one spectrum found in "
4702  "region %d (only the first one is extracted)",
4703  mos_multiplex);
4704  break;
4705  }
4706  found = 1;
4707  max = seq_length[i];
4708  maxpos = i;
4709  }
4710  }
4711  nn += seq_length[i];
4712  }
4713  }
4714 
4715  /*
4716  * Find where this sequence starts in the whole peak position
4717  * storage.
4718  */
4719 
4720  nn = 0;
4721  for (i = 0; i < maxpos; i++)
4722  nn += seq_length[i];
4723 
4724  /*
4725  * Move the longest sequence at the beginning of the returned lists
4726  */
4727 
4728  n = max;
4729  for (i = 0; i < n; i++, nn++) {
4730  xpos[i] = xpos[nn];
4731  lambda[i] = lambda[nn];
4732  ilambda[i] = ilambda[nn];
4733  }
4734 
4735 
4736  /*
4737  * Are some wavelengths missing? Recover them.
4738  */
4739 
4740  for (i = 1; i < n; i++) {
4741  gap = ilambda[i] - ilambda[i-1];
4742  for (j = 1; j < gap; j++) {
4743 
4744  if (j == 1) {
4745 
4746  /*
4747  * Determine the local dispersion from the current pair of peaks
4748  */
4749 
4750  disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
4751  }
4752 
4753  /*
4754  * With this, find the expected position of the missing
4755  * peak by linear interpolation.
4756  */
4757 
4758  hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
4759 
4760  /*
4761  * Is there a peak at that position? Here a peak from the
4762  * original list is searched, that is closer than 2 pixels
4763  * to the expected position. If it is found, insert it at
4764  * the current position on the list of identified peaks,
4765  * and leave immediately the loop (taking the new position
4766  * for the following linear interpolation, in case more
4767  * than one peak is missing in the current interval).
4768  * If it is not found, stay in the loop, looking for
4769  * the following missing peaks in this interval.
4770  */
4771 
4772  found = 0;
4773  for (k = 0; k < npeaks; k++) {
4774  if (fabs(peak[k] - hi_start) < 2) {
4775  for (l = n; l > i; l--) {
4776  xpos[l] = xpos[l-1];
4777  lambda[l] = lambda[l-1];
4778  ilambda[l] = ilambda[l-1];
4779  }
4780  xpos[i] = peak[k];
4781  lambda[i] = line[ilambda[i-1] + j];
4782  ilambda[i] = ilambda[i-1] + j;
4783  ++n;
4784  found = 1;
4785  break;
4786  }
4787  }
4788  if (found)
4789  break;
4790  }
4791  }
4792 
4793 
4794  /*
4795  * Try to extrapolate forward
4796  */
4797 
4798  found = 1;
4799  while (ilambda[n-1] < nlines - 1 && found) {
4800 
4801  /*
4802  * Determine the local dispersion from the last pair of
4803  * identified peaks
4804  */
4805 
4806  if (n > 1)
4807  disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
4808  else
4809  disp = 0.0;
4810 
4811  if (disp > max_disp || disp < min_disp)
4812  break;
4813 
4814 
4815  /*
4816  * With this, find the expected position of the missing
4817  * peak by linear interpolation.
4818  */
4819 
4820  hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
4821 
4822  /*
4823  * Is there a peak at that position? Here a peak from the
4824  * original list is searched, that is closer than 6 pixels
4825  * to the expected position. If it is found, insert it at
4826  * the end of the list of identified peaks. If it is not
4827  * found, leave the loop.
4828  */
4829 
4830  found = 0;
4831  min = fabs(peak[0] - hi_start);
4832  minpos = 0;
4833  for (k = 1; k < npeaks; k++) {
4834  if (min > fabs(peak[k] - hi_start)) {
4835  min = fabs(peak[k] - hi_start);
4836  minpos = k;
4837  }
4838  }
4839  if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
4840  xpos[n] = peak[minpos];
4841  lambda[n] = line[ilambda[n-1] + 1];
4842  ilambda[n] = ilambda[n-1] + 1;
4843  ++n;
4844  found = 1;
4845  }
4846  }
4847 
4848 
4849  /*
4850  * Try to extrapolate backward
4851  */
4852 
4853  found = 1;
4854  while (ilambda[0] > 0 && found) {
4855 
4856  /*
4857  * Determine the local dispersion from the first pair of
4858  * identified peaks
4859  */
4860 
4861  disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
4862 
4863  if (disp > max_disp || disp < min_disp)
4864  break;
4865 
4866 
4867  /*
4868  * With this, find the expected position of the missing
4869  * peak by linear interpolation.
4870  */
4871 
4872  hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
4873 
4874 
4875  /*
4876  * Is there a peak at that position? Here a peak from the
4877  * original list is searched, that is closer than 6 pixels
4878  * to the expected position. If it is found, insert it at
4879  * the beginning of the list of identified peaks. If it is not
4880  * found, leave the loop.
4881  */
4882 
4883  found = 0;
4884  min = fabs(peak[0] - hi_start);
4885  minpos = 0;
4886  for (k = 1; k < npeaks; k++) {
4887  if (min > fabs(peak[k] - hi_start)) {
4888  min = fabs(peak[k] - hi_start);
4889  minpos = k;
4890  }
4891  }
4892  if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
4893  for (j = n; j > 0; j--) {
4894  xpos[j] = xpos[j-1];
4895  lambda[j] = lambda[j-1];
4896  ilambda[j] = ilambda[j-1];
4897  }
4898  xpos[0] = peak[minpos];
4899  lambda[0] = line[ilambda[0] - 1];
4900  ilambda[0] = ilambda[0] - 1;
4901  ++n;
4902  found = 1;
4903  }
4904  }
4905  }
4906 
4907 
4908  /*
4909  * At this point all peaks are processed. Free memory, and return
4910  * the result.
4911  */
4912 
4913 /************************************************+
4914  for (i = 0; i < npeaks; i++) {
4915  printf("Peak %d:\n ", i);
4916  for (j = 0; j < nident[i]; j++)
4917  printf("%.2f, ", line[ident[i][j]]);
4918  printf("\n");
4919  }
4920 
4921  printf("\n");
4922 
4923  for (i = 0; i < n; i++)
4924  printf("%.2f, %.2f\n", xpos[i], lambda[i]);
4925 +************************************************/
4926  for (i = 0; i < npeaks; i++)
4927  cpl_free(ident[i]);
4928  cpl_free(ident);
4929  cpl_free(nident);
4930  cpl_free(lident);
4931  cpl_free(ilambda);
4932  cpl_free(tmp_xpos);
4933  cpl_free(tmp_lambda);
4934  cpl_free(tmp_ilambda);
4935  cpl_free(peak_lo);
4936  cpl_free(flag);
4937  cpl_free(seq_length);
4938  cpl_free(peak_hi);
4939 
4940  if (n == 0) {
4941  cpl_free(xpos);
4942  cpl_free(lambda);
4943  return NULL;
4944  }
4945 
4946  return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos),
4947  cpl_vector_wrap(n, lambda));
4948 }
4949 
4950 
4968 /*
4969 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
4970  double refwave, double pixel)
4971 {
4972  double yellow;
4973  double cpixel;
4974  double tolerance = 0.02;
4975  int max_iter = 20;
4976  int iter = 0;
4977 
4978 
4979  if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
4980  return 0.0;
4981 
4982  if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
4983  return 0.0;
4984 
4985  yellow = (blue + red) / 2;
4986 
4987  cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
4988 
4989  while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
4990 
4991  if (cpixel > pixel)
4992  red = yellow;
4993  else
4994  blue = yellow;
4995 
4996  yellow = (blue + red) / 2;
4997  cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
4998 
4999  iter++;
5000 
5001  }
5002 
5003  return yellow;
5004 
5005 }
5006 */
5007 
5008 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
5009  double refwave, double pixel)
5010 {
5011  double yellow;
5012  double coeff;
5013  cpl_size zero = 0;
5014 
5015  if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
5016  return 0.0;
5017 
5018  if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
5019  return 0.0;
5020 
5021  yellow = (blue + red) / 2 - refwave;
5022 
5023  coeff = cpl_polynomial_get_coeff(ids, &zero);
5024  cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
5025 
5026  cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
5027  if (cpl_error_get_code() != CPL_ERROR_NONE) {
5028  cpl_error_reset();
5029  return 0.0;
5030  }
5031 
5032  cpl_polynomial_set_coeff(ids, &zero, coeff);
5033 
5034  return yellow + refwave;
5035 
5036 }
5037 
5063 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order,
5064  double reject, int minlines,
5065  int *nlines, double *err,
5066  cpl_bivector **pixwav_used)
5067 {
5068  const char *func = "mos_poly_wav2pix";
5069 
5070  cpl_bivector *pixwav2;
5071  cpl_vector *wavel;
5072  cpl_vector *pixel;
5073  double *d_wavel;
5074  double *d_pixel;
5075  double pixpos;
5076  int fitlines;
5077  int rejection = 0;
5078  int i, j;
5079 
5080  cpl_polynomial *ids;
5081 
5082 
5083  *nlines = 0;
5084  *err = 0;
5085 
5086  if (pixwav == NULL) {
5087  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
5088  return NULL;
5089  }
5090 
5091  fitlines = cpl_bivector_get_size(pixwav);
5092 
5093  if (fitlines < minlines) {
5094  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5095  return NULL;
5096  }
5097 
5098 
5099  /*
5100  * If outliers rejection was requested, allocate a working
5101  * vector (that can be modified as soon as outliers are removed)
5102  */
5103 
5104  if (reject > 0.0)
5105  rejection = 1;
5106 
5107  if (rejection)
5108  pixwav2 = cpl_bivector_duplicate(pixwav);
5109  else
5110  pixwav2 = pixwav;
5111 
5112 
5113  /*
5114  * The single vectors are extracted just because the fitting routine
5115  * requires it
5116  */
5117 
5118  pixel = cpl_bivector_get_x(pixwav2);
5119  wavel = cpl_bivector_get_y(pixwav2);
5120 
5121 
5122  /*
5123  * Get rid of the wrapper, in case of duplication
5124  */
5125 
5126  if (rejection)
5127  cpl_bivector_unwrap_vectors(pixwav2);
5128 
5129 
5130  /*
5131  * Here begins the iterative fit of identified lines
5132  */
5133 
5134  while (fitlines >= minlines) {
5135 
5136  ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
5137  *err = sqrt(*err);
5138 
5139  if (ids == NULL) {
5140  cpl_msg_debug(cpl_error_get_where(), "%s", cpl_error_get_message());
5141  cpl_msg_debug(func, "Fitting IDS");
5142  cpl_error_set_where(func);
5143  if (rejection) {
5144  cpl_vector_delete(wavel);
5145  cpl_vector_delete(pixel);
5146  }
5147  return NULL;
5148  }
5149 
5150  if (rejection) {
5151  cpl_vector * wavel_used = cpl_vector_duplicate(wavel);
5152  cpl_vector * pixel_used = cpl_vector_duplicate(pixel);
5153 
5154 
5155  /*
5156  * Now work directly with the vector data buffers...
5157  */
5158 
5159  d_pixel = cpl_vector_unwrap(pixel);
5160  d_wavel = cpl_vector_unwrap(wavel);
5161 
5162  for (i = 0, j = 0; i < fitlines; i++) {
5163  pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
5164  if (fabs(pixpos - d_pixel[i]) < reject) {
5165  d_pixel[j] = d_pixel[i];
5166  d_wavel[j] = d_wavel[i];
5167  j++;
5168  }
5169  }
5170 
5171  if (j == fitlines) { /* No rejection in last iteration */
5172  cpl_bivector * pixwav_used_temp =
5173  cpl_bivector_wrap_vectors(pixel_used, wavel_used);
5174  *pixwav_used = cpl_bivector_duplicate(pixwav_used_temp);
5175  cpl_bivector_unwrap_vectors(pixwav_used_temp);
5176  cpl_vector_delete(wavel_used);
5177  cpl_vector_delete(pixel_used);
5178  cpl_free(d_wavel);
5179  cpl_free(d_pixel);
5180  *nlines = fitlines;
5181  return ids;
5182  }
5183  else { /* Some lines were rejected */
5184  fitlines = j;
5185  cpl_polynomial_delete(ids);
5186  if (fitlines >= minlines) {
5187  pixel = cpl_vector_wrap(fitlines, d_pixel);
5188  wavel = cpl_vector_wrap(fitlines, d_wavel);
5189  }
5190  else { /* Too few lines: failure */
5191  cpl_free(d_wavel);
5192  cpl_free(d_pixel);
5193  cpl_error_set(func, CPL_ERROR_CONTINUE);
5194  return NULL;
5195  }
5196  }
5197  cpl_vector_delete(wavel_used);
5198  cpl_vector_delete(pixel_used);
5199  }
5200  else {
5201  *nlines = fitlines;
5202  *pixwav_used = cpl_bivector_duplicate(pixwav2);
5203  return ids; /* Exit at first iteration if no rejection */
5204  }
5205  }
5206 
5207  return ids; /* To avoid compiler warnings */
5208 }
5209 
5210 
5235 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
5236  double reject, int minlines,
5237  int *nlines, double *err)
5238 {
5239 
5240  cpl_bivector *wavpix;
5241  cpl_vector *wavel;
5242  cpl_vector *pixel;
5243 
5244  cpl_polynomial *dds;
5245 
5246  cpl_bivector *wavepix_used;
5247 
5248 
5249  /*
5250  * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
5251  */
5252 
5253  pixel = cpl_bivector_get_x(pixwav);
5254  wavel = cpl_bivector_get_y(pixwav);
5255 
5256  wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
5257 
5258  dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err,
5259  &wavepix_used);
5260 
5261  cpl_bivector_unwrap_vectors(wavpix);
5262 
5263  cpl_bivector_delete(wavepix_used);
5264 
5265  return dds;
5266 
5267 }
5268 
5269 
5292 cpl_bivector *mos_find_peaks(const float *spectrum, int length,
5293  cpl_vector *lines, cpl_polynomial *ids,
5294  double refwave, int sradius)
5295 {
5296  const char *func = "mos_find_peaks";
5297 
5298  double *data;
5299  double *d_pixel;
5300  double *d_wavel;
5301  float pos;
5302  int nlines;
5303  int pixel;
5304  int i, j;
5305 
5306 
5307  if (spectrum == NULL || lines == NULL || ids == NULL) {
5308  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
5309  return NULL;
5310  }
5311 
5312  nlines = cpl_vector_get_size(lines);
5313 
5314  if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
5315  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5316  return NULL;
5317  }
5318 
5319  d_wavel = cpl_malloc(nlines * sizeof(double));
5320  d_pixel = cpl_malloc(nlines * sizeof(double));
5321 
5322  data = cpl_vector_get_data(lines);
5323 
5324  for (i = 0, j = 0; i < nlines; i++) {
5325  pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
5326  if (pixel < 0 || pixel - sradius < 0 || pixel + sradius >= length)
5327  continue;
5328  if (peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1) == 0) {
5329  pos += pixel - sradius;
5330  d_pixel[j] = pos;
5331  d_wavel[j] = data[i];
5332  j++;
5333  }
5334  }
5335 
5336  if (j > 0) {
5337  return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
5338  cpl_vector_wrap(j, d_wavel));
5339  }
5340  else {
5341  cpl_free(d_wavel);
5342  cpl_free(d_pixel);
5343  cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
5344  return NULL;
5345  }
5346 }
5347 
5348 
5472 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image,
5473  cpl_vector *lines,
5474  double dispersion, float level,
5475  int sradius, int order,
5476  double reject, double refwave,
5477  double *wavestart, double *waveend,
5478  int *nlines, double *error,
5479  cpl_table *idscoeff,
5480  cpl_image *calibration,
5481  cpl_image *residuals,
5482  cpl_table *restable,
5483  cpl_mask *refmask,
5484  cpl_table *detected_lines)
5485 {
5486 
5487  const char *func = "mos_wavelength_calibration_raw";
5488 
5489  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
5490  /* Max order is 5 */
5491 
5492  double tolerance = 20.0; /* Probably forever... */
5493  int step = 10; /* Compute restable every "step" rows */
5494 
5495  char name[MAX_COLNAME];
5496  cpl_image *resampled;
5497  cpl_bivector *output;
5498  cpl_bivector *new_output;
5499  cpl_vector *peaks;
5500  cpl_vector *wavel;
5501  cpl_polynomial *ids;
5502  cpl_polynomial *lin;
5503  cpl_matrix *kernel;
5504  double ids_err;
5505  double max_disp, min_disp;
5506  double *line;
5507  double firstLambda, lastLambda, lambda;
5508  double value, wave, pixe;
5509  cpl_binary *mdata;
5510  const float *sdata;
5511  float *rdata;
5512  float *idata;
5513  float *ddata;
5514  float v1, v2, vi;
5515  float fpixel;
5516  int *have_it;
5517  int pixstart, pixend;
5518  int extrapolation;
5519  int nref;
5520  int nl, nx, ny, pixel;
5521  int countLines, usedLines;
5522  int uorder;
5523  int in, first, last;
5524  int width, uradius;
5525  int i, j;
5526  int null;
5527  cpl_size k;
5528 
5529 
5530  if (dispersion == 0.0) {
5531  cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
5532  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5533  return NULL;
5534  }
5535 
5536  if (dispersion < 0.0) {
5537  cpl_msg_error(func, "The expected dispersion must be positive");
5538  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5539  return NULL;
5540  }
5541 
5542  max_disp = dispersion + dispersion * tolerance / 100;
5543  min_disp = dispersion - dispersion * tolerance / 100;
5544 
5545  if (order < 1) {
5546  cpl_msg_error(func, "The order of the fitting polynomial "
5547  "must be at least 1");
5548  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5549  return NULL;
5550  }
5551 
5552  if (image == NULL || lines == NULL) {
5553  cpl_msg_error(func, "Both spectral exposure and reference line "
5554  "catalog are required in input");
5555  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
5556  return NULL;
5557  }
5558 
5559  nx = cpl_image_get_size_x(image);
5560  ny = cpl_image_get_size_y(image);
5561  sdata = cpl_image_get_data_float_const(image);
5562 
5563  nref = cpl_vector_get_size(lines);
5564  line = cpl_vector_get_data(lines);
5565 
5566  if (*wavestart < 1.0 && *waveend < 1.0) {
5567  firstLambda = line[0];
5568  lastLambda = line[nref-1];
5569  extrapolation = (lastLambda - firstLambda) / 10;
5570  firstLambda -= extrapolation;
5571  lastLambda += extrapolation;
5572  *wavestart = firstLambda;
5573  *waveend = lastLambda;
5574  }
5575  else {
5576  firstLambda = *wavestart;
5577  lastLambda = *waveend;
5578  }
5579 
5580  nl = (lastLambda - firstLambda) / dispersion;
5581  resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
5582  rdata = cpl_image_get_data_float(resampled);
5583 
5584  if (calibration)
5585  idata = cpl_image_get_data_float(calibration);
5586 
5587  if (residuals)
5588  ddata = cpl_image_get_data_float(residuals);
5589 
5590  if (idscoeff)
5591  for (j = 0; j <= order; j++)
5592  cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
5593 
5594  if (restable) {
5595  cpl_table_set_size(restable, nref);
5596  cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
5597  cpl_table_copy_data_double(restable, "wavelength", line);
5598  for (i = 0; i < ny; i += step) {
5599  snprintf(name, MAX_COLNAME, "r%d", i);
5600  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
5601  snprintf(name, MAX_COLNAME, "d%d", i);
5602  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
5603  snprintf(name, MAX_COLNAME, "p%d", i);
5604  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
5605  }
5606  }
5607 
5608  if (detected_lines) {
5609  cpl_table_set_size(detected_lines, 0);
5610  cpl_table_new_column(detected_lines, "xpos", CPL_TYPE_DOUBLE);
5611  cpl_table_new_column(detected_lines, "ypos", CPL_TYPE_DOUBLE);
5612  cpl_table_new_column(detected_lines, "xpos_iter", CPL_TYPE_DOUBLE);
5613  cpl_table_new_column(detected_lines, "ypos_iter", CPL_TYPE_DOUBLE);
5614  cpl_table_new_column(detected_lines, "peak_flux", CPL_TYPE_DOUBLE);
5615  cpl_table_new_column(detected_lines, "wave_ident", CPL_TYPE_DOUBLE);
5616  cpl_table_new_column(detected_lines, "wave_ident_iter", CPL_TYPE_DOUBLE);
5617  cpl_table_new_column(detected_lines, "xpos_fit_rect_wavecal", CPL_TYPE_DOUBLE);
5618  cpl_table_new_column(detected_lines, "res_xpos", CPL_TYPE_DOUBLE);
5619  cpl_table_new_column(detected_lines, "fit_used", CPL_TYPE_INT);
5620  }
5621 
5622  /*
5623  * Here is the real thing: detecting and identifying peaks,
5624  * and then fit the transformation from wavelength to pixel
5625  * and from pixel to wavelength.
5626  */
5627 
5628  for (i = 0; i < ny; i++) {
5629  width = mos_lines_width(sdata + i*nx, nx);
5630  if (sradius > 0) {
5631  if (width > sradius) {
5632  uradius = width;
5633  }
5634  else {
5635  uradius = sradius;
5636  }
5637  }
5638  if (width < 5)
5639  width = 5;
5640  peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
5641  if (peaks) {
5642  peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
5643  }
5644  if (peaks) {
5645  output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05);
5646  if (output) {
5647  cpl_bivector * peaks_ident_used_fit;
5648  countLines = cpl_bivector_get_size(output);
5649  if (countLines < 4) {
5650  cpl_bivector_delete(output);
5651  cpl_vector_delete(peaks);
5652  if (nlines)
5653  nlines[i] = 0;
5654  if (error)
5655  error[i] = 0.0;
5656  continue;
5657  }
5658 
5659  /*
5660  * Set reference wavelength as zero point
5661  */
5662 
5663  wavel = cpl_bivector_get_y(output);
5664  cpl_vector_subtract_scalar(wavel, refwave);
5665 
5666  uorder = countLines / 2 - 1;
5667  if (uorder > order)
5668  uorder = order;
5669 
5670 /* This part is now commented out. In case the first-guess iteration
5671  * was requested, the first fit was made with a lower polynomial degree:
5672  * more robust, and still accurate enough to be used as a first-guess.
5673 
5674  if (sradius > 0 && uorder > 2)
5675  --uorder;
5676 
5677  * End of commented part */
5678 
5679  ids = mos_poly_wav2pix(output, uorder, reject,
5680  2 * (uorder + 1), &usedLines,
5681  &ids_err, &peaks_ident_used_fit);
5682 
5683  if (ids == NULL) {
5684  cpl_bivector_delete(output);
5685  cpl_vector_delete(peaks);
5686  if (nlines)
5687  nlines[i] = 0;
5688  if (error)
5689  error[i] = 0.0;
5690  cpl_error_reset();
5691  continue;
5692  }
5693 
5694  if (idscoeff) {
5695 
5696  /*
5697  * Write it anyway, even in case a first-guess based
5698  * solution will be searched afterwards: in case of
5699  * failure, the "blind" solution is kept.
5700  */
5701 
5702  for (k = 0; k <= order; k++) {
5703  if (k > uorder) {
5704  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
5705  }
5706  else {
5707  cpl_table_set_double(idscoeff, clab[k], i,
5708  cpl_polynomial_get_coeff(ids, &k));
5709  }
5710  }
5711  }
5712 
5713  if(detected_lines)
5714  {
5715  cpl_size newlines = cpl_vector_get_size(peaks);
5716  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
5717  cpl_table_set_size(detected_lines, oldsize + newlines);
5718  for(cpl_size iline = 0; iline < newlines; ++iline)
5719  {
5720  cpl_table_set_double(detected_lines, "xpos",
5721  oldsize + iline, cpl_vector_get(peaks, iline) + 1);
5722  cpl_table_set_double(detected_lines, "ypos",
5723  oldsize + iline, (double)i + 1);
5724  cpl_table_set_double(detected_lines, "peak_flux",
5725  oldsize + iline,
5726  sdata[i*nx+(int)(cpl_vector_get(peaks, iline)+0.5)]);
5727  cpl_table_set_int(detected_lines,
5728  "fit_used",
5729  oldsize + iline, 0);
5730  }
5731  }
5732 
5733  //Fill the line identification information in
5734  //the detected_lines table
5735  if(detected_lines)
5736  {
5737  cpl_size nidentlines = cpl_bivector_get_size(output);
5738  cpl_size ndetectlines = cpl_vector_get_size(peaks);
5739  cpl_size totalsize = cpl_table_get_nrow(detected_lines);
5740  for(cpl_size idline = 0; idline < nidentlines; ++idline)
5741  {
5742  for(cpl_size detline = 0; detline < ndetectlines; ++detline)
5743  {
5744  if(cpl_vector_get(peaks, detline) ==
5745  cpl_bivector_get_x_data(output)[idline])
5746  {
5747  cpl_size table_pos = totalsize - ndetectlines + detline;
5748  double wave_ident = cpl_bivector_get_y_data(output)[idline] + refwave;
5749  double xpix_fit = cpl_polynomial_eval_1d(ids,
5750  wave_ident - refwave, NULL);
5751  double xpos_det = cpl_table_get_double(detected_lines,
5752  "xpos",
5753  table_pos, &null);
5754  cpl_table_set_double(detected_lines,
5755  "wave_ident",
5756  table_pos,
5757  wave_ident);
5758  cpl_table_set_double(detected_lines,
5759  "xpos_fit_rect_wavecal",
5760  table_pos,
5761  xpix_fit + 1);
5762  cpl_table_set_double(detected_lines,
5763  "res_xpos",
5764  table_pos,
5765  xpos_det - xpix_fit - 1);
5766  cpl_table_set_int(detected_lines,
5767  "fit_used",
5768  table_pos, 0);
5769  for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
5770  {
5771  if(cpl_bivector_get_x_data(output)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
5772  cpl_table_set_int(detected_lines,
5773  "fit_used",
5774  table_pos, 1);
5775  }
5776  }
5777  }
5778  }
5779  }
5780 
5781  if (sradius > 0) {
5782  cpl_bivector * peaks_ident_used_fit;
5783 
5784  /*
5785  * Use ids as a first-guess
5786  */
5787 
5788  new_output = mos_find_peaks(sdata + i*nx, nx, lines,
5789  ids, refwave, uradius);
5790 
5791  if (new_output) {
5792  cpl_bivector_delete(output);
5793  output = new_output;
5794  }
5795  else
5796  cpl_error_reset();
5797 
5798 
5799  cpl_polynomial_delete(ids);
5800 
5801  countLines = cpl_bivector_get_size(output);
5802 
5803  if (countLines < 4) {
5804  cpl_bivector_delete(output);
5805  cpl_vector_delete(peaks);
5806 
5807  /*
5808  * With the following code a decision is taken:
5809  * if using the first-guess gives no results,
5810  * then also the "blind" solution is rejected.
5811  */
5812 
5813  if (nlines)
5814  nlines[i] = 0;
5815  if (error)
5816  error[i] = 0.0;
5817  if (idscoeff)
5818  for (k = 0; k <= order; k++)
5819  cpl_table_set_invalid(idscoeff, clab[k], i);
5820  continue;
5821  }
5822 
5823  wavel = cpl_bivector_get_y(output);
5824  cpl_vector_subtract_scalar(wavel, refwave);
5825 
5826  uorder = countLines / 2 - 1;
5827  if (uorder > order)
5828  uorder = order;
5829 
5830  ids = mos_poly_wav2pix(output, uorder, reject,
5831  2 * (uorder + 1), &usedLines,
5832  &ids_err, &peaks_ident_used_fit);
5833 
5834  if (ids == NULL) {
5835  cpl_bivector_delete(output);
5836  cpl_vector_delete(peaks);
5837 
5838  /*
5839  * With the following code a decision is taken:
5840  * if using the first-guess gives no results,
5841  * then also the "blind" solution is rejected.
5842  */
5843 
5844  if (nlines)
5845  nlines[i] = 0;
5846  if (error)
5847  error[i] = 0.0;
5848  if (idscoeff)
5849  for (k = 0; k <= order; k++)
5850  cpl_table_set_invalid(idscoeff, clab[k], i);
5851  cpl_error_reset();
5852  continue;
5853  }
5854 
5855  if (idscoeff) {
5856  for (k = 0; k <= order; k++) {
5857  if (k > uorder) {
5858  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
5859  }
5860  else {
5861  cpl_table_set_double(idscoeff, clab[k], i,
5862  cpl_polynomial_get_coeff(ids, &k));
5863  }
5864  }
5865  }
5866 
5867 
5868  if(detected_lines)
5869  {
5870  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
5871  cpl_size nidentlines = cpl_bivector_get_size(output);
5872  cpl_table_set_size(detected_lines, oldsize + nidentlines);
5873  for(cpl_size idline = 0; idline < nidentlines ; ++idline)
5874  {
5875  double wave_ident = cpl_bivector_get_y_data(output)[idline] + refwave;
5876  double xpix_fit = cpl_polynomial_eval_1d(ids,
5877  wave_ident - refwave, NULL);
5878  cpl_table_set_double(detected_lines, "xpos_iter",
5879  oldsize + idline, cpl_bivector_get_x_data(output)[idline] + 1);
5880  cpl_table_set_double(detected_lines, "ypos_iter",
5881  oldsize + idline, (double)i + 1);
5882  cpl_table_set_double(detected_lines, "peak_flux",
5883  oldsize + idline,
5884  sdata[i*nx+(int)(cpl_bivector_get_x_data(output)[idline]+0.5)]);
5885  cpl_table_set_double(detected_lines, "wave_ident_iter",
5886  oldsize + idline, wave_ident);
5887  cpl_table_set_double(detected_lines, "xpos_fit_rect_wavecal",
5888  oldsize + idline, xpix_fit + 1);
5889  }
5890  }
5891 
5892  } /* End of "use ids as a first-guess" */
5893 
5894  if (nlines)
5895  nlines[i] = usedLines;
5896  if (error)
5897  error[i] = ids_err / sqrt(usedLines/(uorder + 1));
5898 
5899  pixstart = cpl_polynomial_eval_1d(ids,
5900  cpl_bivector_get_y_data(output)[0], NULL);
5901  pixend = cpl_polynomial_eval_1d(ids,
5902  cpl_bivector_get_y_data(output)[countLines-1], NULL);
5903  extrapolation = (pixend - pixstart) / 5;
5904  pixstart -= extrapolation;
5905  pixend += extrapolation;
5906  if (pixstart < 0)
5907  pixstart = 0;
5908  if (pixend > nx)
5909  pixend = nx;
5910 
5911  /*
5912  * Wavelength calibrated image (if requested):
5913  */
5914 
5915  if (calibration) {
5916  for (j = pixstart; j < pixend; j++) {
5917  (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda,
5918  lastLambda, refwave,
5919  j);
5920  }
5921  }
5922 
5923  /*
5924  * Resampled image:
5925  */
5926 
5927  for (j = 0; j < nl; j++) {
5928  lambda = firstLambda + j * dispersion;
5929  fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave,
5930  NULL);
5931  pixel = fpixel;
5932  if (pixel >= 0 && pixel < nx-1) {
5933  v1 = (sdata + i*nx)[pixel];
5934  v2 = (sdata + i*nx)[pixel+1];
5935  vi = v1 + (v2-v1)*(fpixel-pixel);
5936  (rdata + i*nl)[j] = vi;
5937  }
5938  }
5939 
5940  /*
5941  * Residuals image
5942  */
5943 
5944  if (residuals || (restable && !(i%step))) {
5945  if (restable && !(i%step)) {
5946  lin = cpl_polynomial_new(1);
5947  for (k = 0; k < 2; k++)
5948  cpl_polynomial_set_coeff(lin, &k,
5949  cpl_polynomial_get_coeff(ids, &k));
5950  }
5951  for (j = 0; j < countLines; j++) {
5952  pixe = cpl_bivector_get_x_data(output)[j];
5953  wave = cpl_bivector_get_y_data(output)[j];
5954  value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
5955  if (residuals) {
5956  pixel = pixe + 0.5;
5957  (ddata + i*nx)[pixel] = value;
5958  }
5959  if (restable && !(i%step)) {
5960  for (k = 0; k < nref; k++) {
5961  if (fabs(line[k] - refwave - wave) < 0.1) {
5962  snprintf(name, MAX_COLNAME, "r%d", i);
5963  cpl_table_set_double(restable, name,
5964  k, value);
5965  value = pixe
5966  - cpl_polynomial_eval_1d(lin, wave,
5967  NULL);
5968  snprintf(name, MAX_COLNAME, "d%d", i);
5969  cpl_table_set_double(restable, name,
5970  k, value);
5971  snprintf(name, MAX_COLNAME, "p%d", i);
5972  cpl_table_set_double(restable, name,
5973  k, pixe);
5974  break;
5975  }
5976  }
5977  }
5978  }
5979  if (restable && !(i%step)) {
5980  cpl_polynomial_delete(lin);
5981  }
5982  }
5983 
5984  /*
5985  * Mask at reference wavelength
5986  */
5987 
5988  if (refmask) {
5989  mdata = cpl_mask_get_data(refmask);
5990  pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
5991  if (pixel - 1 >= 0 && pixel + 1 < nx) {
5992  mdata[pixel-1 + i*nx] = CPL_BINARY_1;
5993  mdata[pixel + i*nx] = CPL_BINARY_1;
5994  mdata[pixel+1 + i*nx] = CPL_BINARY_1;
5995  }
5996  }
5997 
5998  cpl_polynomial_delete(ids);
5999  cpl_bivector_delete(output);
6000  }
6001  cpl_vector_delete(peaks);
6002  }
6003  }
6004 
6005  if (refmask) {
6006  kernel = cpl_matrix_new(3, 3);
6007  cpl_matrix_set(kernel, 0, 1, 1.0);
6008  cpl_matrix_set(kernel, 1, 1, 1.0);
6009  cpl_matrix_set(kernel, 2, 1, 1.0);
6010 
6011  cpl_mask_dilation(refmask, kernel);
6012  cpl_mask_erosion(refmask, kernel);
6013  cpl_mask_erosion(refmask, kernel);
6014  cpl_mask_dilation(refmask, kernel);
6015 
6016  cpl_matrix_delete(kernel);
6017 
6018  /*
6019  * Fill possible gaps
6020  */
6021 
6022  mdata = cpl_mask_get_data(refmask);
6023  have_it = cpl_calloc(ny, sizeof(int));
6024 
6025  for (i = 0; i < ny; i++, mdata += nx) {
6026  for (j = 0; j < nx; j++) {
6027  if (mdata[j] == CPL_BINARY_1) {
6028  have_it[i] = j;
6029  break;
6030  }
6031  }
6032  }
6033 
6034  mdata = cpl_mask_get_data(refmask);
6035  in = 0;
6036  first = last = 0;
6037 
6038  for (i = 0; i < ny; i++) {
6039  if (have_it[i]) {
6040  if (!in) {
6041  in = 1;
6042  if (first) {
6043  last = i;
6044  if (abs(have_it[first] - have_it[last]) < 3) {
6045  for (j = first; j < last; j++) {
6046  mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
6047  mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
6048  mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
6049  }
6050  }
6051  }
6052  }
6053  }
6054  else {
6055  if (in) {
6056  in = 0;
6057  first = i - 1;
6058  }
6059  }
6060  }
6061 
6062  cpl_free(have_it);
6063 
6064  }
6065 
6066  /* Set the invalid flag for the integer columns. Otherwise cpl_save_table()
6067  can crash (PIPE-6839) due to uninitialise values. For double columns this is
6068  just fine. See documentation for cpl_table_save() */
6069  if(detected_lines)
6070  cpl_table_fill_invalid_int(detected_lines, "fit_used", -1);
6071 
6072 /*
6073  for (i = 0; i < ny; i++) {
6074  if (nlines[i] == 0) {
6075  for (k = 0; k <= order; k++) {
6076  cpl_table_set_invalid(idscoeff, clab[k], i);
6077  }
6078  }
6079  }
6080 */
6081 
6082  return resampled;
6083 }
6084 
6085 
6107 /*
6108 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
6109 {
6110  const char *func = "mos_locate_spectra_bis";
6111 
6112  cpl_apertures *slits;
6113  cpl_image *labimage;
6114  cpl_image *refimage;
6115  cpl_binary *mdata;
6116  cpl_table *slitpos;
6117  cpl_propertylist *sort_col;
6118  int nslits;
6119  int *have_it;
6120  int in, first, last;
6121  int i, j;
6122 
6123 
6124  if (mask == NULL) {
6125  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
6126  return NULL;
6127  }
6128 
6129  nx = cpl_mask_get_size_x(mask);
6130  ny = cpl_mask_get_size_y(mask);
6131 
6132  mdata = cpl_mask_get_data(refmask);
6133  have_it = cpl_calloc(ny, sizeof(int));
6134 
6135  for (i = 0; i < ny; i++, mdata += nx) {
6136  for (j = 0; j < nx; j++) {
6137  if (mdata[j] == CPL_BINARY_1) {
6138  have_it[i] = j + 1;
6139  break;
6140  }
6141  }
6142  }
6143 
6144  mdata = cpl_mask_get_data(refmask);
6145  in = 0;
6146  first = last = 0;
6147  nslits = 0;
6148 
6149  for (i = 0; i < ny; i++) {
6150  if (have_it[i]) {
6151  if (in) {
6152  if (i) {
6153  if (abs(have_it[i] - have_it[i-1]) > 3) {
6154  nslits++;
6155  }
6156  }
6157  }
6158  else {
6159  in = 1;
6160  nslits++;
6161  }
6162  }
6163  else {
6164  if (in) {
6165  in = 0;
6166  }
6167  }
6168  }
6169 }
6170 */
6171 
6172 
6194 cpl_table *mos_locate_spectra(cpl_mask *mask)
6195 {
6196  const char *func = "mos_locate_spectra";
6197 
6198  cpl_apertures *slits;
6199  cpl_image *labimage;
6200  cpl_image *refimage;
6201  cpl_table *slitpos;
6202  cpl_propertylist *sort_col;
6203  cpl_size nslits;
6204  int i;
6205 
6206 
6207  if (mask == NULL) {
6208  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
6209  return NULL;
6210  }
6211 
6212  labimage = cpl_image_labelise_mask_create(mask, &nslits);
6213 
6214  if (nslits < 1) {
6215  cpl_image_delete(labimage);
6216  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6217  return NULL;
6218  }
6219 
6220  refimage = cpl_image_new_from_mask(mask);
6221 
6222  slits = cpl_apertures_new_from_image(refimage, labimage);
6223 
6224  cpl_image_delete(labimage);
6225  cpl_image_delete(refimage);
6226 
6227  nslits = cpl_apertures_get_size(slits); /* Overwriting nslits - safer! */
6228  if (nslits < 1) {
6229  cpl_apertures_delete(slits);
6230  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6231  return NULL;
6232  }
6233 
6234  slitpos = cpl_table_new(nslits);
6235  cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
6236  cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
6237  cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
6238  cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
6239  cpl_table_set_column_unit(slitpos, "xtop", "pixel");
6240  cpl_table_set_column_unit(slitpos, "ytop", "pixel");
6241  cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
6242  cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
6243 
6244  for (i = 0; i < nslits; i++) {
6245  cpl_table_set_double(slitpos, "xtop", i,
6246  cpl_apertures_get_top_x(slits, i+1) - 1);
6247  cpl_table_set_double(slitpos, "ytop", i,
6248  cpl_apertures_get_top(slits, i+1));
6249  cpl_table_set_double(slitpos, "xbottom", i,
6250  cpl_apertures_get_bottom_x(slits, i+1) - 1);
6251  cpl_table_set_double(slitpos, "ybottom", i,
6252  cpl_apertures_get_bottom(slits, i+1));
6253  }
6254 
6255  cpl_apertures_delete(slits);
6256 
6257  sort_col = cpl_propertylist_new();
6258  cpl_propertylist_append_bool(sort_col, "ytop", 1);
6259  cpl_table_sort(slitpos, sort_col);
6260  cpl_propertylist_delete(sort_col);
6261 
6262  return slitpos;
6263 
6264 }
6265 
6266 
6282 cpl_error_code mos_validate_slits(cpl_table *slits)
6283 {
6284  const char *func = "mos_validate_slits";
6285 
6286 
6287  if (slits == NULL)
6288  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
6289 
6290  if (1 != cpl_table_has_column(slits, "xtop"))
6291  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6292 
6293  if (1 != cpl_table_has_column(slits, "ytop"))
6294  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6295 
6296  if (1 != cpl_table_has_column(slits, "xbottom"))
6297  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6298 
6299  if (1 != cpl_table_has_column(slits, "ybottom"))
6300  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6301 
6302  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
6303  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6304 
6305  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
6306  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6307 
6308  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
6309  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6310 
6311  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
6312  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6313 
6314  return CPL_ERROR_NONE;
6315 }
6316 
6317 
6346 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
6347 {
6348  const char *func = "mos_rotate_slits";
6349 
6350  cpl_error_code error;
6351  char aux_name[] = "_0";
6352  int i;
6353 
6354 
6355  rotation %= 4;
6356  if (rotation < 0)
6357  rotation += 4;
6358 
6359  if (rotation == 0)
6360  return CPL_ERROR_NONE;
6361 
6362  error = mos_validate_slits(slits);
6363  if (error)
6364  return cpl_error_set(func, error);
6365 
6366  if (rotation == 1 || rotation == 3) {
6367 
6368  /*
6369  * Swap x and y column names
6370  */
6371 
6372  for (i = 0; i < 77; i++)
6373  if (1 == cpl_table_has_column(slits, aux_name))
6374  aux_name[1]++;
6375  if (1 == cpl_table_has_column(slits, aux_name))
6376  return cpl_error_set(func, CPL_ERROR_CONTINUE);
6377  cpl_table_name_column(slits, "xtop", aux_name);
6378  cpl_table_name_column(slits, "ytop", "xtop");
6379  cpl_table_name_column(slits, aux_name, "ytop");
6380  cpl_table_name_column(slits, "xbottom", aux_name);
6381  cpl_table_name_column(slits, "ybottom", "xbottom");
6382  cpl_table_name_column(slits, aux_name, "ybottom");
6383  }
6384 
6385  if (rotation == 1 || rotation == 2) {
6386  cpl_table_multiply_scalar(slits, "xtop", -1.0);
6387  cpl_table_multiply_scalar(slits, "xbottom", -1.0);
6388  cpl_table_add_scalar(slits, "xtop", nx);
6389  cpl_table_add_scalar(slits, "xbottom", nx);
6390  }
6391 
6392  if (rotation == 3 || rotation == 2) {
6393  cpl_table_multiply_scalar(slits, "ytop", -1.0);
6394  cpl_table_multiply_scalar(slits, "ybottom", -1.0);
6395  cpl_table_add_scalar(slits, "ytop", ny);
6396  cpl_table_add_scalar(slits, "ybottom", ny);
6397  }
6398 
6399  return CPL_ERROR_NONE;
6400 }
6401 
6402 
6460 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
6461  cpl_table *global)
6462 {
6463  cpl_array *top_ident = NULL;;
6464  cpl_array *bot_ident = NULL;;
6465  cpl_matrix *mdata;
6466  cpl_matrix *mpattern;
6467  cpl_matrix *top_data;
6468  cpl_matrix *top_pattern;
6469  cpl_matrix *top_mdata;
6470  cpl_matrix *top_mpattern;
6471  cpl_matrix *bot_data;
6472  cpl_matrix *bot_pattern;
6473  cpl_matrix *bot_mdata;
6474  cpl_matrix *bot_mpattern;
6475  cpl_propertylist *sort_col;
6476  double *xtop;
6477  double *ytop;
6478  double *xmtop;
6479  double *ymtop;
6480  double *xbot;
6481  double *ybot;
6482  double *xmbot;
6483  double *ymbot;
6484  double top_scale, bot_scale;
6485  double angle, top_angle, bot_angle;
6486  double xmse, ymse;
6487  double xrms, top_xrms, bot_xrms;
6488  double yrms, top_yrms, bot_yrms;
6489  int nslits;
6490  int nmaskslits, use_pattern;
6491  int found_slits, found_slits_top, found_slits_bot;
6492  int i;
6493  cpl_table *positions;
6494  cpl_error_code error;
6495 
6496  cpl_vector *point;
6497  double *dpoint;
6498  cpl_vector *xpos;
6499  cpl_vector *ypos;
6500  cpl_vector *xmpos;
6501  cpl_vector *ympos;
6502  cpl_bivector *mpos;
6503  cpl_polynomial *xpoly = NULL;
6504  cpl_polynomial *ypoly = NULL;
6505  cpl_polynomial *top_xpoly = NULL;
6506  cpl_polynomial *top_ypoly = NULL;
6507  cpl_polynomial *bot_xpoly = NULL;
6508  cpl_polynomial *bot_ypoly = NULL;
6509 
6510  char *msg_multiplex = " ";
6511 
6512 
6513  error = mos_validate_slits(slits);
6514  if (error) {
6515  cpl_msg_error(cpl_func, "CCD slits table validation: %s",
6516  cpl_error_get_message());
6517  cpl_error_set(cpl_func, error);
6518  return NULL;
6519  }
6520 
6521  error = mos_validate_slits(maskslits);
6522  if (error) {
6523  cpl_msg_error(cpl_func, "Mask slits table validation: %s",
6524  cpl_error_get_message());
6525  cpl_error_set(cpl_func, error);
6526  return NULL;
6527  }
6528 
6529  if (1 != cpl_table_has_column(maskslits, "slit_id")) {
6530  cpl_msg_error(cpl_func, "Missing slits identifiers");
6531  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
6532  return NULL;
6533  }
6534 
6535  if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
6536  cpl_msg_error(cpl_func, "Wrong type used for slits identifiers");
6537  cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE);
6538  return NULL;
6539  }
6540 
6541  nslits = cpl_table_get_nrow(slits);
6542  nmaskslits = cpl_table_get_nrow(maskslits);
6543 
6544  if (nslits == 0 || nmaskslits == 0) {
6545  cpl_msg_error(cpl_func, "Empty slits table");
6546  cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
6547  return NULL;
6548  }
6549 
6550  if (nslits > 100 && mos_multiplex < 0) {
6551  cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching...");
6552  positions = mos_identify_slits_fast(slits, maskslits, global);
6553  if (positions == NULL)
6554  cpl_error_set_where(cpl_func);
6555  return positions;
6556  }
6557 
6558  /*
6559  * Guarantee that both input tables are sorted in the same way
6560  */
6561 
6562  sort_col = cpl_propertylist_new();
6563  cpl_propertylist_append_bool(sort_col, "ytop", 1);
6564  cpl_table_sort(slits, sort_col);
6565  cpl_table_sort(maskslits, sort_col);
6566  cpl_propertylist_delete(sort_col);
6567 
6568  /*
6569  * First we handle all the special cases (too few slits...)
6570  */
6571 
6572  if (nslits < 3 && nmaskslits > nslits) {
6573 
6574  /*
6575  * If there are just 1 or 2 slits on the CCD, and more on the
6576  * mask, the ambiguity cannot be solved, and an error is returned.
6577  * This is a case that must be solved with a first-guess relation
6578  * between mask and CCD.
6579  */
6580 
6581  if (nslits > 1)
6582  cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits "
6583  "with the %d mask slits: process will continue "
6584  "using the detected CCD slits positions", nslits,
6585  nmaskslits);
6586  else
6587  cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with "
6588  "the %d mask slits: process will continue using "
6589  "the detected CCD slit position", nmaskslits);
6590  return NULL;
6591  }
6592 
6593  if (nmaskslits < 3 && nslits > nmaskslits) {
6594 
6595  /*
6596  * If there are less than 3 slits on the mask the ambiguity cannot
6597  * be solved, and an error is returned. This is a case that must
6598  * be solved with a first-guess relation between mask and CCD.
6599  */
6600 
6601  cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with "
6602  "the %d mask slits: process will continue using "
6603  "the detected CCD slits positions", nslits,
6604  nmaskslits);
6605  return NULL;
6606  }
6607 
6608  /*
6609  * Pattern matching related operations begin here. Two pattern
6610  * matching will be run, one based on the "top" and another one
6611  * based on the "bottom" slit coordinates. The one with the
6612  * smallest rms in the Y coordinate will be chosen.
6613  */
6614 
6615  xtop = cpl_table_get_data_double(slits, "xtop");
6616  ytop = cpl_table_get_data_double(slits, "ytop");
6617  xmtop = cpl_table_get_data_double(maskslits, "xtop");
6618  ymtop = cpl_table_get_data_double(maskslits, "ytop");
6619 
6620  xbot = cpl_table_get_data_double(slits, "xbottom");
6621  ybot = cpl_table_get_data_double(slits, "ybottom");
6622  xmbot = cpl_table_get_data_double(maskslits, "xbottom");
6623  ymbot = cpl_table_get_data_double(maskslits, "ybottom");
6624 
6625  top_data = cpl_matrix_new(2, nslits);
6626  top_pattern = cpl_matrix_new(2, nmaskslits);
6627  bot_data = cpl_matrix_new(2, nslits);
6628  bot_pattern = cpl_matrix_new(2, nmaskslits);
6629 
6630  for (i = 0; i < nslits; i++)
6631  cpl_matrix_set(top_data, 0, i, xtop[i]);
6632 
6633  for (i = 0; i < nslits; i++)
6634  cpl_matrix_set(top_data, 1, i, ytop[i]);
6635 
6636  for (i = 0; i < nmaskslits; i++)
6637  cpl_matrix_set(top_pattern, 0, i, xmtop[i]);
6638 
6639  for (i = 0; i < nmaskslits; i++)
6640  cpl_matrix_set(top_pattern, 1, i, ymtop[i]);
6641 
6642  for (i = 0; i < nslits; i++)
6643  cpl_matrix_set(bot_data, 0, i, xbot[i]);
6644 
6645  for (i = 0; i < nslits; i++)
6646  cpl_matrix_set(bot_data, 1, i, ybot[i]);
6647 
6648  for (i = 0; i < nmaskslits; i++)
6649  cpl_matrix_set(bot_pattern, 0, i, xmbot[i]);
6650 
6651  for (i = 0; i < nmaskslits; i++)
6652  cpl_matrix_set(bot_pattern, 1, i, ymbot[i]);
6653 
6654  if (nmaskslits > nslits)
6655  use_pattern = nslits;
6656  else
6657  use_pattern = nmaskslits;
6658 
6659  top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern,
6660  use_pattern, 0.0, 0.1, 5, &top_mdata,
6661  &top_mpattern, &top_scale, &top_angle);
6662 
6663  bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern,
6664  use_pattern, 0.0, 0.1, 5, &bot_mdata,
6665  &bot_mpattern, &bot_scale, &bot_angle);
6666  cpl_matrix_delete(top_data);
6667  cpl_matrix_delete(top_pattern);
6668  cpl_matrix_delete(bot_data);
6669  cpl_matrix_delete(bot_pattern);
6670 
6671  if (top_ident == NULL && bot_ident == NULL) {
6672  cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match "
6673  "the %d found CCD slits with the %d mask slits: "
6674  "process will continue using the detected CCD "
6675  "slits positions", nslits, nmaskslits);
6676  return NULL;
6677  }
6678 
6679  found_slits_top = 0;
6680  found_slits_bot = 0;
6681  if (top_ident && bot_ident) {
6682  cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm",
6683  (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale));
6684  cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees",
6685  (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle));
6686  if (fabs(top_angle) < fabs(bot_angle))
6687  angle = fabs(top_angle);
6688  else
6689  angle = fabs(bot_angle);
6690  found_slits_top = cpl_matrix_get_ncol(top_mdata);
6691  found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
6692  }
6693  else if (top_ident) {
6694  cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale);
6695  cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle);
6696  angle = fabs(top_angle);
6697  found_slits_top = cpl_matrix_get_ncol(top_mdata);
6698  }
6699  else {
6700  cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale);
6701  cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle);
6702  angle = fabs(bot_angle);
6703  found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
6704  }
6705 
6706  cpl_array_delete(top_ident);
6707  cpl_array_delete(bot_ident);
6708 
6709  if (angle > 4.0) {
6710  cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation "
6711  "angle is expected to be around zero. This match is "
6712  "rejected: the process will continue using the %d "
6713  "detected CCD slits positions", nslits);
6714  return NULL;
6715  }
6716 
6717  found_slits = found_slits_top;
6718  if (found_slits < found_slits_bot)
6719  found_slits = found_slits_bot; /* Max value */
6720 
6721  if (found_slits < 4) {
6722  cpl_msg_warning(cpl_func,
6723  "Too few safely identified slits: %d out of %d "
6724  "candidates (%d expected). Process will continue "
6725  "using the detected CCD slits positions", found_slits,
6726  nslits, nmaskslits);
6727  return NULL;
6728  }
6729 
6730  cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d "
6731  "candidates\n(%d expected)", found_slits, nslits,
6732  nmaskslits);
6733 
6734  if (found_slits_top < 4)
6735  found_slits_top = 0;
6736 
6737  if (found_slits_bot < 4)
6738  found_slits_bot = 0;
6739 
6740  /*
6741  * Now for each set select the points of the identified slits, and fit
6742  * two bivariate polynomials to determine a first approximate relation
6743  * between positions on the mask and positions on the CCD.
6744  */
6745 
6746  for (i = 0; i < 2; i++) {
6747  cpl_size mindeg2d[] = {0, 0};
6748  cpl_size maxdeg2d[2];
6749  cpl_vector * fitresidual;
6750  if (i) {
6751  found_slits = found_slits_top;
6752  mdata = top_mdata;
6753  mpattern = top_mpattern;
6754  }
6755  else {
6756  found_slits = found_slits_bot;
6757  mdata = bot_mdata;
6758  mpattern = bot_mpattern;
6759  }
6760 
6761  if (found_slits == 0)
6762  continue;
6763  else if (found_slits < 10)
6764  maxdeg2d[0] = maxdeg2d[1] = 1;
6765  else
6766  maxdeg2d[0] = maxdeg2d[1] = 2;
6767 
6768  xpos = cpl_vector_wrap(found_slits,
6769  cpl_matrix_get_data(mdata) );
6770  ypos = cpl_vector_wrap(found_slits,
6771  cpl_matrix_get_data(mdata) + found_slits);
6772  xmpos = cpl_vector_wrap(found_slits,
6773  cpl_matrix_get_data(mpattern) );
6774  ympos = cpl_vector_wrap(found_slits,
6775  cpl_matrix_get_data(mpattern) + found_slits);
6776  mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
6777  fitresidual = cpl_vector_new(cpl_vector_get_size(xpos));
6778  xpoly = cpl_polynomial_new(2);
6779  cpl_polynomial_fit(xpoly, mpattern, NULL, xpos, NULL, CPL_FALSE, mindeg2d, maxdeg2d);
6780  cpl_vector_fill_polynomial_fit_residual(fitresidual, xpos, NULL, xpoly, mpattern, NULL);
6781  xmse = cpl_vector_product(fitresidual, fitresidual)
6782  / cpl_vector_get_size(fitresidual);
6783  ypoly = cpl_polynomial_new(2);
6784  cpl_polynomial_fit(ypoly, mpattern, NULL, ypos, NULL, CPL_FALSE, mindeg2d, maxdeg2d);
6785  cpl_vector_fill_polynomial_fit_residual(fitresidual, ypos, NULL, ypoly, mpattern, NULL);
6786  ymse = cpl_vector_product(fitresidual, fitresidual)
6787  / cpl_vector_get_size(fitresidual);
6788 
6789  cpl_bivector_unwrap_vectors(mpos);
6790  cpl_vector_unwrap(xpos);
6791  cpl_vector_unwrap(ypos);
6792  cpl_vector_unwrap(xmpos);
6793  cpl_vector_unwrap(ympos);
6794  cpl_matrix_delete(mdata);
6795  cpl_matrix_delete(mpattern);
6796  cpl_vector_delete(fitresidual);
6797 
6798  if (i) {
6799  top_xpoly = xpoly;
6800  top_ypoly = ypoly;
6801  top_xrms = sqrt(xmse*2*maxdeg2d[0]/(found_slits - 1));
6802  top_yrms = sqrt(ymse*2*maxdeg2d[0]/(found_slits - 1));
6803  }
6804  else {
6805  bot_xpoly = xpoly;
6806  bot_ypoly = ypoly;
6807  bot_xrms = sqrt(xmse*2*maxdeg2d[0]/(found_slits - 1));
6808  bot_yrms = sqrt(ymse*2*maxdeg2d[0]/(found_slits - 1));
6809  }
6810  }
6811 
6812  if (top_xpoly && bot_xpoly) {
6813  if (top_xrms < bot_xrms) { /* top X solution wins... */
6814  xrms = top_xrms;
6815  xpoly = top_xpoly;
6816  cpl_polynomial_delete(bot_xpoly);
6817  }
6818  else { /* bottom X solution wins... */
6819  xrms = bot_xrms;
6820  xpoly = bot_xpoly;
6821  cpl_polynomial_delete(top_xpoly);
6822  }
6823  }
6824  else if (top_xpoly) {
6825  xrms = top_xrms;
6826  xpoly = top_xpoly;
6827  }
6828  else {
6829  xrms = bot_xrms;
6830  xpoly = bot_xpoly;
6831  }
6832 
6833  if (top_ypoly && bot_ypoly) {
6834  if (top_yrms < bot_yrms) { /* top Y solution wins... */
6835  yrms = top_yrms;
6836  ypoly = top_ypoly;
6837  cpl_polynomial_delete(bot_ypoly);
6838  }
6839  else { /* bottom Y solution wins... */
6840  yrms = bot_yrms;
6841  ypoly = bot_ypoly;
6842  cpl_polynomial_delete(top_ypoly);
6843  }
6844  }
6845  else if (top_ypoly) {
6846  yrms = top_yrms;
6847  ypoly = top_ypoly;
6848  }
6849  else {
6850  yrms = bot_yrms;
6851  ypoly = bot_ypoly;
6852  }
6853 
6854  if (xpoly == NULL || ypoly == NULL) {
6855  cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the "
6856  "identified slits positions cannot be improved.");
6857  cpl_polynomial_delete(xpoly);
6858  cpl_polynomial_delete(ypoly);
6859  cpl_error_reset();
6860  return NULL;
6861  }
6862 
6863  cpl_msg_info(cpl_func,
6864  "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
6865  xrms, yrms);
6866 
6867  if (global) {
6868  write_global_distortion(global, 0, xpoly);
6869  write_global_distortion(global, 7, ypoly);
6870  }
6871 
6872  /*
6873  * The fit was successful: use the polynomials to obtain a new
6874  * position table with the improved positions of the slits
6875  */
6876 
6877  positions = cpl_table_duplicate(maskslits);
6878  cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
6879  cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
6880  cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
6881  cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
6882 
6883  point = cpl_vector_new(2);
6884  dpoint = cpl_vector_get_data(point);
6885 
6886  for (i = 0; i < nmaskslits; i++) {
6887  double position_x;
6888  double position_y;
6889 
6890  dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
6891  dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
6892  position_x = cpl_polynomial_eval(xpoly, point);
6893 // if (mos_multiplex >= 0) {
6894 // if (mos_multiplex != ((int)floor(position)) / mos_region_size) {
6895 // cpl_table_unselect_row(positions, i);
6896 // continue;
6897 // }
6898 // }
6899  cpl_table_set_double(positions, "xtop", i, position_x);
6900  position_y = cpl_polynomial_eval(ypoly, point);
6901  cpl_table_set_double(positions, "ytop", i, position_y);
6902  dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
6903  dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
6904  position_x = cpl_polynomial_eval(xpoly, point);
6905  cpl_table_set_double(positions, "xbottom", i, position_x);
6906  position_y = cpl_polynomial_eval(ypoly, point);
6907  cpl_table_set_double(positions, "ybottom", i, position_y);
6908  }
6909 
6910 // if (mos_multiplex >= 0) {
6911 // cpl_table_not_selected(positions);
6912 // cpl_table_erase_selected(positions);
6913 // nmaskslits = cpl_table_get_nrow(positions);
6914 // }
6915 
6916  cpl_vector_delete(point);
6917  cpl_polynomial_delete(xpoly);
6918  cpl_polynomial_delete(ypoly);
6919 
6920  cpl_table_erase_column(positions, "xmtop");
6921  cpl_table_erase_column(positions, "ymtop");
6922  cpl_table_erase_column(positions, "xmbottom");
6923  cpl_table_erase_column(positions, "ymbottom");
6924 
6925  if (mos_multiplex >= 0) {
6926  msg_multiplex =
6927  cpl_sprintf("in the CCD section between %d and %d pixel",
6928  mos_multiplex * mos_region_size,
6929  (mos_multiplex + 1) * mos_region_size);
6930  }
6931 
6932  if (nmaskslits > nslits)
6933  cpl_msg_info(cpl_func,
6934  "Finally identified slits: %d out of %d expected %s\n"
6935  "(%d recovered)", nmaskslits, nmaskslits, msg_multiplex,
6936  nmaskslits - nslits);
6937  else if (nmaskslits < nslits)
6938  cpl_msg_info(cpl_func,
6939  "Finally identified slits: %d out of %d expected %s\n"
6940  "(%d rejected)", nmaskslits, nmaskslits, msg_multiplex,
6941  nslits - nmaskslits);
6942  else
6943  cpl_msg_info(cpl_func,
6944  "Finally identified slits: %d out of %d expected %s",
6945  nmaskslits, nmaskslits, msg_multiplex);
6946 
6947  if (mos_multiplex >= 0) {
6948  cpl_free(msg_multiplex);
6949  }
6950 
6951  return positions;
6952 
6953 }
6954 
6955 
6956 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits,
6957  cpl_table *global)
6958 {
6959  const char *func = "mos_identify_slits_fast";
6960 
6961  cpl_propertylist *sort_col;
6962  cpl_table *positions;
6963  cpl_vector *scales;
6964  cpl_vector *angles;
6965  cpl_vector *point;
6966  cpl_vector *xpos;
6967  cpl_vector *ypos;
6968  cpl_vector *xmpos;
6969  cpl_vector *ympos;
6970  cpl_bivector *mpos;
6971  cpl_polynomial *xpoly = NULL;
6972  cpl_polynomial *ypoly = NULL;
6973  cpl_error_code error;
6974  int nslits;
6975  int nmaskslits;
6976  int found_slits;
6977  int i, j, k;
6978 
6979  double dist1, dist2, dist3, dist, mindist;
6980  double scale, minscale, maxscale;
6981  double angle, minangle, maxangle;
6982  double *dscale;
6983  double *dangle;
6984  double *dpoint;
6985  double *xtop;
6986  double *ytop;
6987  double *xbottom;
6988  double *ybottom;
6989  double *xcenter;
6990  double *ycenter;
6991  double *xpseudo;
6992  double *ypseudo;
6993  int *slit_id;
6994  double *xmtop;
6995  double *ymtop;
6996  double *xmbottom;
6997  double *ymbottom;
6998  double *xmcenter;
6999  double *ymcenter;
7000  double *xmpseudo;
7001  double *ympseudo;
7002  double xmse, ymse;
7003  int *mslit_id;
7004  int *good;
7005  int minpos;
7006  int degree;
7007 
7008  double sradius = 0.01; /* Candidate input argument... */
7009  int in_sradius;
7010 
7011  double pi = 3.14159265358979323846;
7012 
7013 
7014  error = mos_validate_slits(slits);
7015  if (error) {
7016  cpl_msg_error(func, "CCD slits table validation: %s",
7017  cpl_error_get_message());
7018  cpl_error_set(func, error);
7019  return NULL;
7020  }
7021 
7022  error = mos_validate_slits(maskslits);
7023  if (error) {
7024  cpl_msg_error(func, "Mask slits table validation: %s",
7025  cpl_error_get_message());
7026  cpl_error_set(func, error);
7027  return NULL;
7028  }
7029 
7030  if (1 != cpl_table_has_column(maskslits, "slit_id")) {
7031  cpl_msg_error(func, "Missing slits identifiers");
7032  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
7033  return NULL;
7034  }
7035 
7036  if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
7037  cpl_msg_error(func, "Wrong type used for slits identifiers");
7038  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
7039  return NULL;
7040  }
7041 
7042  nslits = cpl_table_get_nrow(slits);
7043  nmaskslits = cpl_table_get_nrow(maskslits);
7044 
7045  if (nslits == 0 || nmaskslits == 0) {
7046  cpl_msg_error(func, "Empty slits table");
7047  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
7048  return NULL;
7049  }
7050 
7051 
7052  /*
7053  * Compute middle point coordinates for each slit listed in both
7054  * input tables.
7055  */
7056 
7057  if (cpl_table_has_column(slits, "xcenter"))
7058  cpl_table_erase_column(slits, "xcenter");
7059 
7060  if (cpl_table_has_column(slits, "ycenter"))
7061  cpl_table_erase_column(slits, "ycenter");
7062 
7063  if (cpl_table_has_column(maskslits, "xcenter"))
7064  cpl_table_erase_column(maskslits, "xcenter");
7065 
7066  if (cpl_table_has_column(maskslits, "ycenter"))
7067  cpl_table_erase_column(maskslits, "ycenter");
7068 
7069  cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
7070  cpl_table_add_columns(slits, "xcenter", "xbottom");
7071  cpl_table_divide_scalar(slits, "xcenter", 2.0);
7072  cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
7073  cpl_table_add_columns(slits, "ycenter", "ybottom");
7074  cpl_table_divide_scalar(slits, "ycenter", 2.0);
7075 
7076  cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
7077  cpl_table_add_columns(maskslits, "xcenter", "xbottom");
7078  cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
7079  cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
7080  cpl_table_add_columns(maskslits, "ycenter", "ybottom");
7081  cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
7082 
7083 
7084  /*
7085  * Guarantee that both input tables are sorted in the same way
7086  */
7087 
7088  sort_col = cpl_propertylist_new();
7089  cpl_propertylist_append_bool(sort_col, "ycenter", 1);
7090  cpl_table_sort(slits, sort_col);
7091  cpl_table_sort(maskslits, sort_col);
7092  cpl_propertylist_delete(sort_col);
7093 
7094 
7095  /*
7096  * First we handle all the special cases (too few slits...)
7097  */
7098 
7099  if (nslits < 3 && nmaskslits > nslits) {
7100 
7101  /*
7102  * If there are just 1 or 2 slits on the CCD, and more on the
7103  * mask, the ambiguity cannot be solved, and an error is returned.
7104  * This is a case that must be solved with a first-guess relation
7105  * between mask and CCD.
7106  */
7107 
7108  if (nslits > 1)
7109  cpl_msg_warning(func, "Cannot match the found CCD slit with the "
7110  "%d mask slits: process will continue using the "
7111  "detected CCD slit position", nmaskslits);
7112  else
7113  cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
7114  "the %d mask slits: process will continue using "
7115  "the detected CCD slits positions", nslits,
7116  nmaskslits);
7117  return NULL;
7118  }
7119 
7120  if (nslits <= 3 && nslits == nmaskslits) {
7121 
7122  cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
7123  cpl_msg_warning(func, "Their detected positions are left unchanged");
7124 
7125  /*
7126  * If there are just up to 3 slits, both on the mask and on the CCD,
7127  * we can reasonably hope that those slits were found, and accept
7128  * that their positions on the CCD cannot be improved. We prepare
7129  * therefore an output position table containing the slits with
7130  * their original positions. We can however give an estimate of
7131  * the platescale if there is more than one slit.
7132  */
7133 
7134  positions = cpl_table_duplicate(slits);
7135  cpl_table_erase_column(slits, "xcenter");
7136  cpl_table_erase_column(slits, "ycenter");
7137  cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
7138  cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
7139  cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
7140  cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
7141  cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
7142  cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
7143  cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
7144  cpl_table_erase_column(maskslits, "xcenter");
7145  cpl_table_erase_column(maskslits, "ycenter");
7146 
7147  if (nslits > 1) {
7148  xcenter = cpl_table_get_data_double(positions, "xcenter");
7149  ycenter = cpl_table_get_data_double(positions, "ycenter");
7150  xmcenter = cpl_table_get_data_double(positions, "xmcenter");
7151  ymcenter = cpl_table_get_data_double(positions, "ymcenter");
7152 
7153  dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
7154  + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
7155  dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
7156  + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
7157  scale = sqrt(dist1/dist2);
7158 
7159  if (nslits == 3) {
7160  dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
7161  + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
7162  dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
7163  + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
7164  scale += sqrt(dist1/dist2);
7165  scale /= 2;
7166  }
7167 
7168  cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
7169  }
7170 
7171  return positions;
7172  }
7173 
7174  if (nmaskslits < 3 && nslits > nmaskslits) {
7175 
7176  /*
7177  * If there are less than 3 slits on the mask the ambiguity cannot
7178  * be solved, and an error is returned. This is a case that must
7179  * be solved with a first-guess relation between mask and CCD.
7180  */
7181 
7182  cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
7183  "the %d mask slits: process will continue using "
7184  "the detected CCD slits positions", nslits,
7185  nmaskslits);
7186  return NULL;
7187  }
7188 
7189 
7190  /*
7191  * At this point of the program all the region of the plane
7192  * (nslits, nmaskslits) where either or both mask and CCD display
7193  * less than 3 slits are handled in some way. It would be better
7194  * to add in this place a special handling for identifying slits
7195  * in case of a very reduced number of slits (say, below 6).
7196  * It is also clear that if there are many more slits on the
7197  * mask than on the CCD, or many more on the CCD than on the
7198  * mask, something went deeply wrong with the preliminary
7199  * wavelength calibration. Such cases should be handled with
7200  * a _complete_ pattern-recognition algorithm based on the
7201  * construction of all possible triangles. For the moment,
7202  * we go directly to the limited pattern-recognition applied
7203  * below, based on triangles build only for consecutive slits.
7204  * This is reasonably safe, since the preliminary wavelength
7205  * calibration performed by mos_identify_peaks() is generally
7206  * robust.
7207  */
7208 
7209 
7210  /*
7211  * Compute (X, Y) coordinates on pseudo-plane describing the
7212  * different position ratios of successive slits, in both
7213  * input tables.
7214  */
7215 
7216  if (cpl_table_has_column(slits, "xpseudo"))
7217  cpl_table_erase_column(slits, "xpseudo");
7218 
7219  if (cpl_table_has_column(slits, "ypseudo"))
7220  cpl_table_erase_column(slits, "ypseudo");
7221 
7222  if (cpl_table_has_column(maskslits, "xpseudo"))
7223  cpl_table_erase_column(maskslits, "xpseudo");
7224 
7225  if (cpl_table_has_column(maskslits, "ypseudo"))
7226  cpl_table_erase_column(maskslits, "ypseudo");
7227 
7228  cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
7229  cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
7230 
7231  xcenter = cpl_table_get_data_double(slits, "xcenter");
7232  ycenter = cpl_table_get_data_double(slits, "ycenter");
7233  xpseudo = cpl_table_get_data_double(slits, "xpseudo");
7234  ypseudo = cpl_table_get_data_double(slits, "ypseudo");
7235 
7236  for (i = 1; i < nslits - 1; i++) {
7237  dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
7238  + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
7239  dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
7240  + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
7241  dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
7242  + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
7243  xpseudo[i] = sqrt(dist1/dist2);
7244  ypseudo[i] = sqrt(dist3/dist2);
7245  }
7246 
7247  cpl_table_set_invalid(slits, "xpseudo", 0);
7248  cpl_table_set_invalid(slits, "xpseudo", nslits-1);
7249  cpl_table_set_invalid(slits, "ypseudo", 0);
7250  cpl_table_set_invalid(slits, "ypseudo", nslits-1);
7251 
7252  cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
7253  cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
7254 
7255  xcenter = cpl_table_get_data_double(maskslits, "xcenter");
7256  ycenter = cpl_table_get_data_double(maskslits, "ycenter");
7257  xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
7258  ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
7259 
7260  for (i = 1; i < nmaskslits - 1; i++) {
7261  dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
7262  + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
7263  dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
7264  + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
7265  dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
7266  + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
7267  xmpseudo[i] = sqrt(dist1/dist2);
7268  ympseudo[i] = sqrt(dist3/dist2);
7269  }
7270 
7271  cpl_table_set_invalid(maskslits, "xpseudo", 0);
7272  cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
7273  cpl_table_set_invalid(maskslits, "ypseudo", 0);
7274  cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
7275 
7276 
7277  /*
7278  * For each (X, Y) on the pseudo-plane related to the mask positions,
7279  * find the closest (X, Y) on the pseudo-plane related to the CCD
7280  * positions. If the closest point is closer than a given search
7281  * radius, a triangle has been found, and 3 slits are identified.
7282  * However, if more than one point is found within the search
7283  * radius, we have an ambiguity and this is rejected.
7284  */
7285 
7286  if (cpl_table_has_column(slits, "slit_id"))
7287  cpl_table_erase_column(slits, "slit_id");
7288  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
7289  slit_id = cpl_table_get_data_int(maskslits, "slit_id");
7290 
7291  for (i = 1; i < nmaskslits - 1; i++) {
7292  in_sradius = 0;
7293  mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
7294  + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
7295  minpos = 1;
7296  if (mindist < sradius*sradius)
7297  in_sradius++;
7298  for (j = 2; j < nslits - 1; j++) {
7299  dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
7300  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
7301  if (dist < sradius*sradius)
7302  in_sradius++;
7303  if (in_sradius > 1) /* More than one triangle within radius */
7304  break;
7305  if (mindist > dist) {
7306  mindist = dist;
7307  minpos = j;
7308  }
7309  }
7310 
7311  mindist = sqrt(mindist);
7312 
7313  if (mindist < sradius && in_sradius == 1) {
7314  cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
7315  cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
7316  cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
7317  }
7318  }
7319 
7320 
7321  /*
7322  * At this point, the slit_id column contains invalid elements
7323  * corresponding to unidentified slits.
7324  */
7325 
7326  found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
7327 
7328  if (found_slits < 3) {
7329  cpl_msg_warning(func, "Too few preliminarily identified slits: "
7330  "%d out of %d", found_slits, nslits);
7331  if (nslits == nmaskslits) {
7332  cpl_msg_warning(func, "(this is not an error, it could be caused "
7333  "by a mask with regularly located slits)");
7334  cpl_msg_warning(func, "The detected slits positions are left "
7335  "unchanged");
7336 
7337  /*
7338  * If there are less than 3 identified slits, this is probably
7339  * a mask with regularly spaced slits (leading to an ambiguous
7340  * pattern). Only in the case all expected slits appear to have
7341  * been found on the CCD we can proceed...
7342  */
7343 
7344  cpl_table_erase_column(slits, "slit_id");
7345  cpl_table_erase_column(slits, "xpseudo");
7346  cpl_table_erase_column(slits, "ypseudo");
7347  positions = cpl_table_duplicate(slits);
7348  cpl_table_erase_column(slits, "xcenter");
7349  cpl_table_erase_column(slits, "ycenter");
7350 
7351  cpl_table_erase_column(maskslits, "xpseudo");
7352  cpl_table_erase_column(maskslits, "ypseudo");
7353  cpl_table_duplicate_column(positions, "xmtop",
7354  maskslits, "xtop");
7355  cpl_table_duplicate_column(positions, "ymtop",
7356  maskslits, "ytop");
7357  cpl_table_duplicate_column(positions, "xmbottom",
7358  maskslits, "xbottom");
7359  cpl_table_duplicate_column(positions, "ymbottom",
7360  maskslits, "ybottom");
7361  cpl_table_duplicate_column(positions, "xmcenter",
7362  maskslits, "xcenter");
7363  cpl_table_duplicate_column(positions, "ymcenter",
7364  maskslits, "ycenter");
7365  cpl_table_duplicate_column(positions, "slit_id",
7366  maskslits, "slit_id");
7367  cpl_table_erase_column(maskslits, "xcenter");
7368  cpl_table_erase_column(maskslits, "ycenter");
7369  return positions;
7370  }
7371  else {
7372  cpl_table_erase_column(slits, "slit_id");
7373  cpl_table_erase_column(slits, "xpseudo");
7374  cpl_table_erase_column(slits, "ypseudo");
7375  positions = cpl_table_duplicate(slits);
7376  cpl_table_erase_column(slits, "xcenter");
7377  cpl_table_erase_column(slits, "ycenter");
7378  cpl_msg_warning(func, "(the failure could be caused "
7379  "by a mask with regularly located slits)");
7380  return NULL;
7381  }
7382  }
7383  else {
7384  cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
7385  "candidates (%d expected)", found_slits, nslits,
7386  nmaskslits);
7387  }
7388 
7389 
7390  /*
7391  * Create a table with the coordinates of the preliminarily identified
7392  * slits, both on CCD and mask. The original order of the slits positions
7393  * is preserved.
7394  */
7395 
7396  positions = cpl_table_new(found_slits);
7397  cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
7398  cpl_table_new_column(positions, "xtop", CPL_TYPE_DOUBLE);
7399  cpl_table_new_column(positions, "ytop", CPL_TYPE_DOUBLE);
7400  cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
7401  cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
7402  cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
7403  cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
7404  cpl_table_new_column(positions, "xmtop", CPL_TYPE_DOUBLE);
7405  cpl_table_new_column(positions, "ymtop", CPL_TYPE_DOUBLE);
7406  cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
7407  cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
7408  cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
7409  cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
7410  cpl_table_new_column(positions, "good", CPL_TYPE_INT);
7411  cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
7412 
7413  slit_id = cpl_table_get_data_int (slits, "slit_id");
7414  xtop = cpl_table_get_data_double(slits, "xtop");
7415  ytop = cpl_table_get_data_double(slits, "ytop");
7416  xbottom = cpl_table_get_data_double(slits, "xbottom");
7417  ybottom = cpl_table_get_data_double(slits, "ybottom");
7418  xcenter = cpl_table_get_data_double(slits, "xcenter");
7419  ycenter = cpl_table_get_data_double(slits, "ycenter");
7420 
7421  mslit_id = cpl_table_get_data_int (maskslits, "slit_id");
7422  xmtop = cpl_table_get_data_double(maskslits, "xtop");
7423  ymtop = cpl_table_get_data_double(maskslits, "ytop");
7424  xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
7425  ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
7426  xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
7427  ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
7428 
7429 
7430  /*
7431  * Transferring the valid slits information to the new table.
7432  * Note that invalid elements are coded as 0 in the internal
7433  * buffer, and this is the way they are recognised and excluded.
7434  */
7435 
7436  k = 0;
7437  cpl_table_fill_invalid_int(slits, "slit_id", 0);
7438  for (i = 0; i < nmaskslits; i++) {
7439  for (j = 0; j < nslits; j++) {
7440  if (slit_id[j] == 0)
7441  continue; /* Skip invalid slit */
7442  if (mslit_id[i] == slit_id[j]) {
7443  cpl_table_set_int (positions, "slit_id", k, slit_id[j]);
7444 
7445  cpl_table_set_double(positions, "xtop", k, xtop[j]);
7446  cpl_table_set_double(positions, "ytop", k, ytop[j]);
7447  cpl_table_set_double(positions, "xbottom", k, xbottom[j]);
7448  cpl_table_set_double(positions, "ybottom", k, ybottom[j]);
7449  cpl_table_set_double(positions, "xcenter", k, xcenter[j]);
7450  cpl_table_set_double(positions, "ycenter", k, ycenter[j]);
7451 
7452  cpl_table_set_double(positions, "xmtop", k, xmtop[i]);
7453  cpl_table_set_double(positions, "ymtop", k, ymtop[i]);
7454  cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
7455  cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
7456  cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
7457  cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
7458 
7459  k++;
7460 
7461  break;
7462  }
7463  }
7464  }
7465 
7466  found_slits = k;
7467 
7468  cpl_table_erase_column(slits, "slit_id");
7469  cpl_table_erase_column(slits, "xpseudo");
7470  cpl_table_erase_column(slits, "ypseudo");
7471  cpl_table_erase_column(slits, "xcenter");
7472  cpl_table_erase_column(slits, "ycenter");
7473  cpl_table_erase_column(maskslits, "xpseudo");
7474  cpl_table_erase_column(maskslits, "ypseudo");
7475  cpl_table_erase_column(maskslits, "xcenter");
7476  cpl_table_erase_column(maskslits, "ycenter");
7477 
7478 
7479  /*
7480  * Find the median platescale and rotation angle from the identified
7481  * slits, and then exclude slits outlaying more than 10% from the
7482  * median platescale, and more than 2 degrees from the median
7483  * rotation angle.
7484  */
7485 
7486  ytop = cpl_table_get_data_double(positions, "ytop");
7487  ybottom = cpl_table_get_data_double(positions, "ybottom");
7488  xcenter = cpl_table_get_data_double(positions, "xcenter");
7489  ycenter = cpl_table_get_data_double(positions, "ycenter");
7490  xmcenter = cpl_table_get_data_double(positions, "xmcenter");
7491  ymcenter = cpl_table_get_data_double(positions, "ymcenter");
7492 
7493  scales = cpl_vector_new(found_slits - 1);
7494  dscale = cpl_vector_get_data(scales);
7495  angles = cpl_vector_new(found_slits - 1);
7496  dangle = cpl_vector_get_data(angles);
7497 
7498  for (i = 1; i < found_slits; i++) {
7499  dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
7500  + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
7501  dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
7502  + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
7503  dscale[i-1] = sqrt(dist1/dist2);
7504  dangle[i-1] = atan2(ycenter[i-1] - ycenter[i],
7505  xcenter[i-1] - xcenter[i])
7506  - atan2(ymcenter[i-1] - ymcenter[i],
7507  xmcenter[i-1] - xmcenter[i]);
7508  dangle[i-1] *= 180;
7509  dangle[i-1] /= pi;
7510  }
7511 
7512  minscale = cpl_vector_get_min(scales);
7513  scale = cpl_vector_get_median_const(scales);
7514  maxscale = cpl_vector_get_max(scales);
7515 
7516  minangle = cpl_vector_get_min(angles);
7517  angle = cpl_vector_get_median_const(angles);
7518  maxangle = cpl_vector_get_max(angles);
7519 
7520  cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
7521  cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm",
7522  minscale, maxscale);
7523 
7524  cpl_msg_info(func, "Median rotation: %f degrees", angle);
7525  cpl_msg_info(func, "Minmax rotation: %f, %f degrees",
7526  minangle, maxangle);
7527 
7528  good = cpl_table_get_data_int(positions, "good");
7529 
7530  good[0] = good[found_slits - 1] = 1;
7531  for (i = 1; i < found_slits; i++) {
7532  if (fabs((dscale[i-1] - scale)/scale) < 0.10
7533  && fabs(dangle[i-1] - angle) < 2) {
7534  good[i-1]++;
7535  good[i]++;
7536  }
7537  }
7538 
7539  for (i = 0; i < found_slits; i++) {
7540  if (good[i] < 2)
7541  good[i] = 0;
7542  else
7543  good[i] = 1;
7544  }
7545 
7546 /*
7547  for (i = 1; i < found_slits; i++)
7548  if (fabs((dscale[i-1] - scale)/scale) < 0.10)
7549  good[i-1] = good[i] = 1;
7550 */
7551 
7552 /* DEBUG ************+
7553  for (i = 0; i < found_slits; i++) {
7554  if (good[i]) {
7555  if (i == found_slits - 1)
7556  printf("include slit %d, prev = %f, %f\n",
7557  i, dscale[i-1], dangle[i-1]);
7558  else if (i == 0)
7559  printf("include slit %d, next %f, %f\n",
7560  i, dscale[i], dangle[i]);
7561  else
7562  printf("include slit %d, prev = %f, %f, next %f, %f\n", i,
7563  dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
7564  }
7565  else {
7566  if (i == found_slits - 1)
7567  printf("EXclude slit %d, prev = %f, %f\n",
7568  i, dscale[i-1], dangle[i-1]);
7569  else if (i == 0)
7570  printf("EXclude slit %d, next %f, %f\n",
7571  i, dscale[i], dangle[i]);
7572  else
7573  printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,
7574  dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
7575  }
7576  }
7577 +*********** DEBUG */
7578 
7579  cpl_vector_delete(scales);
7580  cpl_vector_delete(angles);
7581 
7582  cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
7583  cpl_table_erase_selected(positions);
7584  cpl_table_erase_column(positions, "good");
7585  found_slits = cpl_table_get_nrow(positions);
7586 
7587  if (found_slits < 4) {
7588 
7589  /*
7590  * If the self-consistency check gives such a poor result,
7591  * something must have gone really wrong in the preliminary
7592  * wavelength calibration... Nothing can be done.
7593  */
7594 
7595  cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
7596  "candidates (%d expected). Process will continue "
7597  "using the detected CCD slits positions", found_slits,
7598  nslits, nmaskslits);
7599  cpl_table_delete(positions);
7600  return NULL;
7601  }
7602  else {
7603  cpl_msg_info(func, "Safely identified slits: %d out of %d "
7604  "candidates\n(%d expected)", found_slits, nslits,
7605  nmaskslits);
7606  }
7607 
7608 
7609  /*
7610  * Now select the central points of the identified slits, and
7611  * fit two bivariate polynomials to determine a first approximate
7612  * relation between positions on the mask and positions on the CCD.
7613  */
7614 
7615  xpos = cpl_vector_wrap(found_slits,
7616  cpl_table_get_data_double(positions, "xcenter"));
7617  ypos = cpl_vector_wrap(found_slits,
7618  cpl_table_get_data_double(positions, "ycenter"));
7619  xmpos = cpl_vector_wrap(found_slits,
7620  cpl_table_get_data_double(positions, "xmcenter"));
7621  ympos = cpl_vector_wrap(found_slits,
7622  cpl_table_get_data_double(positions, "ymcenter"));
7623  mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
7624 
7625  if (found_slits < 10)
7626  degree = 1;
7627  else
7628  degree = 2;
7629 
7630  xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
7631  if (xpoly != NULL)
7632  ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
7633  cpl_bivector_unwrap_vectors(mpos);
7634  cpl_vector_unwrap(xpos);
7635  cpl_vector_unwrap(ypos);
7636  cpl_vector_unwrap(xmpos);
7637  cpl_vector_unwrap(ympos);
7638  if (ypoly == NULL) {
7639  if (found_slits == nmaskslits) {
7640  cpl_msg_warning(func, "Fit failure: the accuracy of the "
7641  "identified slits positions is not improved.");
7642 
7643  /*
7644  * The determination of the transformation from mask to CCD
7645  * failed, but since all slits have been already identified
7646  * this is not a problem: data can be reduced also without
7647  * such transformation. Simply the accuracy of the slits
7648  * positions cannot be improved.
7649  */
7650 
7651  } else {
7652  cpl_msg_info(func, "Fit failure: not all slits have been "
7653  "identified. Process will continue using "
7654  "the detected CCD slits positions");
7655  }
7656 
7657  cpl_polynomial_delete(xpoly);
7658  return positions;
7659  }
7660 
7661  cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
7662  sqrt(xmse), sqrt(ymse));
7663 
7664  if (global) {
7665  write_global_distortion(global, 0, xpoly);
7666  write_global_distortion(global, 7, ypoly);
7667  }
7668 
7669  /*
7670  * The fit was successful: use the polynomials to obtain a new
7671  * position table with the improved positions of the slits
7672  */
7673 
7674  cpl_table_delete(positions);
7675 
7676  positions = cpl_table_duplicate(maskslits);
7677  cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
7678  cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
7679  cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
7680  cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
7681 
7682  point = cpl_vector_new(2);
7683  dpoint = cpl_vector_get_data(point);
7684 
7685  for (i = 0; i < nmaskslits; i++) {
7686  dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
7687  dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
7688  cpl_table_set_double(positions, "xtop", i,
7689  cpl_polynomial_eval(xpoly, point));
7690  cpl_table_set_double(positions, "ytop", i,
7691  cpl_polynomial_eval(ypoly, point));
7692  dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
7693  dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
7694  cpl_table_set_double(positions, "xbottom", i,
7695  cpl_polynomial_eval(xpoly, point));
7696  cpl_table_set_double(positions, "ybottom", i,
7697  cpl_polynomial_eval(ypoly, point));
7698  }
7699 
7700  cpl_vector_delete(point);
7701  cpl_polynomial_delete(xpoly);
7702  cpl_polynomial_delete(ypoly);
7703 
7704  cpl_table_erase_column(positions, "xmtop");
7705  cpl_table_erase_column(positions, "ymtop");
7706  cpl_table_erase_column(positions, "xmbottom");
7707  cpl_table_erase_column(positions, "ymbottom");
7708 
7709  if (nmaskslits > nslits)
7710  cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
7711  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
7712  else if (nmaskslits < nslits)
7713  cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
7714  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
7715  else
7716  cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
7717  nmaskslits, nmaskslits);
7718 
7719  return positions;
7720 }
7721 
7722 
7764 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
7765  double blue, double red, double dispersion)
7766 {
7767 
7768  const char *func = "mos_trace_flat";
7769 
7770  cpl_image *gradient;
7771  cpl_image *sgradient;
7772  float *dgradient;
7773  float level = 500; /* It was 250... */
7774  cpl_vector *row;
7775  cpl_vector *srow;
7776  cpl_vector **peaks;
7777  double *peak;
7778  int *slit_id;
7779  float *g;
7780  double *r;
7781  double *xtop;
7782  double *ytop;
7783  double *xbottom;
7784  double *ybottom;
7785  double min, dist;
7786  double sradius;
7787  double tolerance;
7788  double start_y, prev_y;
7789  int minpos;
7790  int nslits;
7791  int nrows;
7792  int step = 10;
7793  int filtbox = 15;
7794  int nx, ny, npix;
7795  int pos, ypos;
7796  int npeaks;
7797  int pixel_above, pixel_below;
7798  int i, j, k, l;
7799  char trace_id[MAX_COLNAME];
7800 
7801  cpl_table *traces;
7802 
7803 
7804  if (flat == NULL || slits == NULL) {
7805  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
7806  return NULL;
7807  }
7808 
7809  if (dispersion <= 0.0) {
7810  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
7811  return NULL;
7812  }
7813 
7814  if (red - blue < dispersion) {
7815  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
7816  return NULL;
7817  }
7818 
7819  /*
7820  * Create a dummy slit_id column if it is missing in the
7821  * input slits table
7822  */
7823 
7824  nslits = cpl_table_get_nrow(slits);
7825  if (1 != cpl_table_has_column(slits, "slit_id")) {
7826  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
7827  for (i = 0; i < nslits; i++)
7828  cpl_table_set_int(slits, "slit_id", i, -(i+1)); /* it was (i+1) */
7829  }
7830 
7831  slit_id = cpl_table_get_data_int(slits, "slit_id");
7832 
7833  nx = cpl_image_get_size_x(flat);
7834  ny = cpl_image_get_size_y(flat);
7835  npix = nx * ny;
7836 
7837  gradient = cpl_image_duplicate(flat);
7838  dgradient = cpl_image_get_data_float(gradient);
7839 
7840  for (i = 0; i < ny - 1; i++) {
7841  k = i * nx;
7842  for (j = 0; j < nx; j++) {
7843  l = k + j;
7844  dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
7845  }
7846  }
7847 
7848  npix--;
7849  for (j = 0; j < nx; j++)
7850  dgradient[npix - j] = 0.0;
7851 
7852  cpl_image_turn(gradient, -1);
7853  nx = cpl_image_get_size_x(gradient);
7854  ny = cpl_image_get_size_y(gradient);
7855  sgradient = mos_image_vertical_median_filter(gradient,
7856  filtbox, 0, ny, 0, step);
7857  cpl_image_delete(gradient);
7858 
7859 
7860  /*
7861  * Remove background from processed image rows
7862  */
7863 
7864  dgradient = cpl_image_get_data_float(sgradient);
7865 
7866  for (i = 1; i <= ny; i += step) {
7867  row = cpl_vector_new_from_image_row(sgradient, i);
7868  srow = cpl_vector_filter_median_create(row, filtbox);
7869  cpl_vector_subtract(row, srow);
7870  cpl_vector_delete(srow);
7871  g = dgradient + (i-1)*nx;
7872  r = cpl_vector_get_data(row);
7873  for (j = 0; j < nx; j++)
7874  g[j] = r[j];
7875  cpl_vector_delete(row);
7876  }
7877 
7878 
7879  /*
7880  * Rotate (temporarily) the input slits table, to get coordinates
7881  * compatible with the rotated gradient image.
7882  */
7883 
7884  mos_rotate_slits(slits, 1, nx, ny);
7885  xtop = cpl_table_get_data_double(slits, "xtop");
7886  ytop = cpl_table_get_data_double(slits, "ytop");
7887  xbottom = cpl_table_get_data_double(slits, "xbottom");
7888  ybottom = cpl_table_get_data_double(slits, "ybottom");
7889 
7890 
7891  /*
7892  * Get positions of peaks candidates for each processed gradient
7893  * image row
7894  */
7895 
7896  peaks = cpl_calloc(ny, sizeof(cpl_vector *));
7897 
7898  for (i = 0; i < ny; i += step) {
7899  g = dgradient + i*nx;
7900  peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
7901 
7902  /* I thought this would be required, but apparently I was wrong.
7903  * Check twice... */
7904  if (peaks[i])
7905  cpl_vector_subtract_scalar(peaks[i], 0.5);
7906 
7907  }
7908 
7909  cpl_image_delete(sgradient);
7910 
7911 
7912  /*
7913  * Tracing the flat field spectra edges, starting from the
7914  * slits positions obtained at reference wavelength. The
7915  * gradient maximum closest to each slits ends is found and
7916  * accepted only within a given search radius:
7917  */
7918 
7919  sradius = 5.0; /* Pixel */
7920 
7921  /*
7922  * The tracing proceeds along the processed gradient image rows,
7923  * above and below the start position, finding the closest peak
7924  * to the previous obtained position, accepting the new position
7925  * only if it is closer than a given tolerance:
7926  */
7927 
7928  tolerance = 0.9; /* Pixel */
7929 
7930  /*
7931  * The trace is attempted for a certain number of pixels above
7932  * and below the position of the reference wavelength:
7933  */
7934 
7935  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
7936  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
7937 
7938 
7939  /*
7940  * Prepare the structure of the output table:
7941  */
7942 
7943  nrows = (ny-1)/step + 1;
7944  traces = cpl_table_new(nrows);
7945  cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
7946  cpl_table_set_column_unit(traces, "x", "pixel");
7947  for (i = 0, j = 0; i < ny; i += step, j++)
7948  cpl_table_set(traces, "x", j, i);
7949 
7950  for (i = 0; i < nslits; i++) {
7951 
7952  /*
7953  * Find the closest processed gradient image row
7954  */
7955 
7956  ypos = ytop[i];
7957 
7958  if (ypos < 0)
7959  ypos = 0;
7960  if (ypos >= ny)
7961  ypos = ny - 1;
7962 
7963  pos = ypos / step;
7964  pos *= step;
7965 
7966  /*
7967  * Find the peak in that row that is closest to xtop[i]
7968  */
7969 
7970  if (peaks[pos]) {
7971  peak = cpl_vector_get_data(peaks[pos]);
7972  npeaks = cpl_vector_get_size(peaks[pos]);
7973 
7974  min = fabs(peak[0] - xtop[i]);
7975  minpos = 0;
7976  for (j = 1; j < npeaks; j++) {
7977  dist = fabs(peak[j] - xtop[i]);
7978  if (min > dist) {
7979  min = dist;
7980  minpos = j;
7981  }
7982  }
7983  }
7984  else {
7985  npeaks = 0;
7986  }
7987 
7988  snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
7989  cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
7990 
7991  if (min > sradius || npeaks == 0) {
7992  cpl_msg_warning(func, "Cannot find spectrum edge for "
7993  "top (or left) end of slit %d", slit_id[i]);
7994  }
7995  else {
7996 
7997  /*
7998  * Add to output table the start y position. Note that
7999  * y positions are written in coordinates of the unrotated
8000  * image, i.e., with horizontal dispersion. Currently nx
8001  * is the x size of the temporarily rotated image (for
8002  * faster memory access), but it has the sense of a y size.
8003  */
8004 
8005  cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
8006  start_y = peak[minpos];
8007 
8008  /*
8009  * Perform the tracing of current edge. Above:
8010  */
8011 
8012  prev_y = start_y;
8013 
8014  for (j = pos + step; j < ny; j += step) {
8015  if (j - pos > pixel_above)
8016  break;
8017  if (peaks[j]) {
8018  peak = cpl_vector_get_data(peaks[j]);
8019  npeaks = cpl_vector_get_size(peaks[j]);
8020  min = fabs(peak[0] - prev_y);
8021  minpos = 0;
8022  for (k = 1; k < npeaks; k++) {
8023  dist = fabs(peak[k] - prev_y);
8024  if (min > dist) {
8025  min = dist;
8026  minpos = k;
8027  }
8028  }
8029  if (min < tolerance) {
8030  cpl_table_set(traces, trace_id, j/step,
8031  nx - peak[minpos]);
8032  prev_y = peak[minpos];
8033  }
8034  }
8035  }
8036 
8037  /*
8038  * Perform the tracing of current edge. Below:
8039  */
8040 
8041  prev_y = start_y;
8042 
8043  for (j = pos - step; j >= 0; j -= step) {
8044  if (pos - j > pixel_below)
8045  break;
8046  if (peaks[j]) {
8047  peak = cpl_vector_get_data(peaks[j]);
8048  npeaks = cpl_vector_get_size(peaks[j]);
8049  min = fabs(peak[0] - prev_y);
8050  minpos = 0;
8051  for (k = 1; k < npeaks; k++) {
8052  dist = fabs(peak[k] - prev_y);
8053  if (min > dist) {
8054  min = dist;
8055  minpos = k;
8056  }
8057  }
8058  if (min < tolerance) {
8059  cpl_table_set(traces, trace_id, j/step,
8060  nx - peak[minpos]);
8061  prev_y = peak[minpos];
8062  }
8063  }
8064  }
8065  }
8066 
8067 
8068  /*
8069  * Find the peak in that row that is closest to xbottom[i]
8070  */
8071 
8072  if (peaks[pos]) {
8073  peak = cpl_vector_get_data(peaks[pos]);
8074  npeaks = cpl_vector_get_size(peaks[pos]);
8075 
8076  min = fabs(peak[0] - xbottom[i]);
8077  minpos = 0;
8078  for (j = 1; j < npeaks; j++) {
8079  dist = fabs(peak[j] - xbottom[i]);
8080  if (min > dist) {
8081  min = dist;
8082  minpos = j;
8083  }
8084  }
8085  }
8086  else {
8087  npeaks = 0;
8088  }
8089 
8090  snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
8091  cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
8092 
8093  if (min > sradius || npeaks == 0) {
8094  cpl_msg_warning(func, "Cannot find spectrum edge for "
8095  "bottom (or right) end of slit %d", slit_id[i]);
8096  }
8097  else {
8098 
8099  cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
8100  start_y = peak[minpos];
8101 
8102  /*
8103  * Perform the tracing of current edge. Above:
8104  */
8105 
8106  prev_y = start_y;
8107 
8108  for (j = pos + step; j < ny; j += step) {
8109  if (j - pos > pixel_above)
8110  break;
8111  if (peaks[j]) {
8112  peak = cpl_vector_get_data(peaks[j]);
8113  npeaks = cpl_vector_get_size(peaks[j]);
8114  min = fabs(peak[0] - prev_y);
8115  minpos = 0;
8116  for (k = 1; k < npeaks; k++) {
8117  dist = fabs(peak[k] - prev_y);
8118  if (min > dist) {
8119  min = dist;
8120  minpos = k;
8121  }
8122  }
8123  if (min < tolerance) {
8124  cpl_table_set(traces, trace_id, j/step,
8125  nx - peak[minpos]);
8126  prev_y = peak[minpos];
8127  }
8128  }
8129  }
8130 
8131  /*
8132  * Perform the tracing of current edge. Below:
8133  */
8134 
8135  prev_y = start_y;
8136 
8137  for (j = pos - step; j >= 0; j -= step) {
8138  if (pos - j > pixel_below)
8139  break;
8140  if (peaks[j]) {
8141  peak = cpl_vector_get_data(peaks[j]);
8142  npeaks = cpl_vector_get_size(peaks[j]);
8143  min = fabs(peak[0] - prev_y);
8144  minpos = 0;
8145  for (k = 1; k < npeaks; k++) {
8146  dist = fabs(peak[k] - prev_y);
8147  if (min > dist) {
8148  min = dist;
8149  minpos = k;
8150  }
8151  }
8152  if (min < tolerance) {
8153  cpl_table_set(traces, trace_id, j/step,
8154  nx - peak[minpos]);
8155  prev_y = peak[minpos];
8156  }
8157  }
8158  }
8159  }
8160 
8161  } /* End of loop on slits */
8162 
8163  for (i = 0; i < ny; i += step)
8164  cpl_vector_delete(peaks[i]);
8165  cpl_free(peaks);
8166 
8167  /*
8168  * Restore original orientation of slits positions table
8169  */
8170 
8171  mos_rotate_slits(slits, -1, ny, nx);
8172 
8173  return traces;
8174 
8175 }
8176 
8177 
8198 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
8199 {
8200  const char *func = "mos_poly_trace";
8201 
8202  cpl_table *polytraces;
8203  cpl_table *dummy;
8204  cpl_vector *x;
8205  cpl_vector *trace;
8206  cpl_polynomial *polytrace;
8207  char trace_id[MAX_COLNAME];
8208  char trace_res[MAX_COLNAME];
8209  char trace_mod[MAX_COLNAME];
8210  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8211  /* Max order is 5 */
8212  double *xdata;
8213  int *slit_id;
8214  int nslits;
8215  int nrows;
8216  int npoints;
8217  int i, j;
8218  cpl_size k;
8219 
8220 
8221  if (traces == NULL || slits == NULL) {
8222  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8223  return NULL;
8224  }
8225 
8226  if (order > 5) {
8227  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8228  return NULL;
8229  }
8230 
8231  nrows = cpl_table_get_nrow(traces);
8232  xdata = cpl_table_get_data_double(traces, "x");
8233  nslits = cpl_table_get_nrow(slits);
8234  slit_id = cpl_table_get_data_int(slits, "slit_id");
8235 
8236  polytraces = cpl_table_new(2*nslits);
8237  cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
8238  for (i = 0; i <= order; i++)
8239  cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
8240 
8241  for (i = 0; i < nslits; i++) {
8242  for (j = 0; j < 2; j++) { /* For top and bottom trace of each slit */
8243 
8244  if (j) {
8245  snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
8246  snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
8247  snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
8248  }
8249  else {
8250  snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
8251  snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
8252  snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
8253  }
8254 
8255  cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
8256 
8257  /*
8258  * The "dummy" table is just a tool for eliminating invalid
8259  * points from the vectors to be fitted.
8260  */
8261 
8262  dummy = cpl_table_new(nrows);
8263  cpl_table_duplicate_column(dummy, "x", traces, "x");
8264  cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
8265  npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
8266  if (npoints < 2 * order) {
8267  cpl_table_delete(dummy);
8268  continue;
8269  }
8270  cpl_table_erase_invalid(dummy);
8271  x = cpl_vector_wrap(npoints,
8272  cpl_table_get_data_double(dummy, "x"));
8273  trace = cpl_vector_wrap(npoints,
8274  cpl_table_get_data_double(dummy, trace_id));
8275  polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
8276  cpl_vector_unwrap(x);
8277  cpl_vector_unwrap(trace);
8278  cpl_table_delete(dummy);
8279 
8280  /*
8281  * Screen bad solutions. At the moment, a primitive screening
8282  * consists in excluding solutions displaying excessive
8283  * curvature (larger than 1E-5 / pixel)
8284  */
8285 
8286  k = 2;
8287  if (fabs(cpl_polynomial_get_coeff(polytrace, &k)) > 1.E-4) {
8288  cpl_polynomial_delete(polytrace);
8289  cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
8290  cpl_table_duplicate_column(traces, trace_res, traces,
8291  trace_mod);
8292  if (j)
8293  cpl_msg_warning(func, "Exclude bad curvature solution "
8294  "for bottom (right) edge of slit %d", slit_id[i]);
8295  else
8296  cpl_msg_warning(func, "Exclude bad curvature solution "
8297  "for top (left) edge of slit %d", slit_id[i]);
8298  continue;
8299  }
8300 
8301  /*
8302  * Write polynomial coefficients to the output table,
8303  * tagged with the appropriate slit_id
8304  */
8305 
8306  for (k = 0; k <= order; k++)
8307  cpl_table_set_double(polytraces, clab[k], 2*i+j,
8308  cpl_polynomial_get_coeff(polytrace, &k));
8309 
8310  /*
8311  * Add column of residuals to input traces table
8312  */
8313 
8314  cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
8315  cpl_table_set_column_unit(traces, trace_mod, "pixel");
8316 
8317  for (k = 0; k < nrows; k++) {
8318  cpl_table_set_double(traces, trace_mod, k,
8319  cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
8320  }
8321 
8322  cpl_polynomial_delete(polytrace);
8323 
8324  cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
8325  cpl_table_subtract_columns(traces, trace_res, trace_id);
8326  cpl_table_multiply_scalar(traces, trace_res, -1.0);
8327 
8328  }
8329  }
8330 
8331  return polytraces;
8332 
8333 }
8334 
8335 
8359 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
8360  int mode)
8361 {
8362  const char *func = "mos_global_trace";
8363 
8364  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8365  /* Max order is 5 */
8366  cpl_table *table;
8367  cpl_vector *c0;
8368  cpl_vector *cn;
8369  cpl_bivector *list;
8370 /* alternative (not robust)
8371  cpl_polynomial *poly;
8372 */
8373 
8374  double *offset;
8375  double rms, q, m;
8376 
8377  int order, nrows, nslits;
8378  int i, j;
8379 
8380 
8381  if (polytraces == NULL) {
8382  cpl_msg_error(func, "Missing spectral curvature table");
8383  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8384  }
8385 
8386  if (slits == NULL) {
8387  cpl_msg_error(func, "Missing slits positions table");
8388  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8389  }
8390 
8391  nslits = cpl_table_get_nrow(slits);
8392 
8393  table = cpl_table_duplicate(polytraces);
8394  cpl_table_erase_invalid(table);
8395 
8396  nrows = cpl_table_get_nrow(table);
8397 
8398  if (nrows < 4) {
8399  cpl_msg_warning(func, "Too few successful spectral curvature tracings "
8400  "(%d): the determination of a global curvature model "
8401  "failed", nrows);
8402  return CPL_ERROR_NONE;
8403  }
8404 
8405  order = cpl_table_get_ncol(polytraces) - 2;
8406 
8407  for (i = 0; i <= order; i++) {
8408  if (!cpl_table_has_column(table, clab[i])) {
8409  cpl_msg_error(func, "Wrong spectral curvature table");
8410  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8411  }
8412  }
8413 
8414 
8415  /*
8416  * Fill in advance the missing offset terms
8417  */
8418 
8419  for (i = 0; i < nslits; i++) {
8420  if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
8421  cpl_table_set_double(polytraces, clab[0], 2*i,
8422  cpl_table_get_double(slits, "ytop", i, NULL));
8423  }
8424  if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
8425  cpl_table_set_double(polytraces, clab[0], 2*i+1,
8426  cpl_table_get_double(slits, "ybottom", i, NULL));
8427  }
8428  }
8429 
8430  offset = cpl_table_get_data_double(polytraces, clab[0]);
8431 
8432 
8433  /*
8434  * Fit the global model and modify polytraces table accordingly
8435  */
8436 
8437  c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
8438 
8439  for (i = 1; i <= order; i++) {
8440  cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
8441  list = cpl_bivector_wrap_vectors(c0, cn);
8442  robustLinearFit(list, &q, &m, &rms);
8443 /* alternative (not robust)
8444  poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
8445 */
8446  for (j = 0; j < 2*nslits; j++) {
8447  if (mode == 1)
8448  if (cpl_table_is_valid(polytraces, clab[i], j))
8449  continue;
8450  cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
8451 /* alternative (not robust)
8452  cpl_table_set_double(polytraces, clab[i], j,
8453  cpl_polynomial_eval_1d(poly, offset[j], NULL));
8454 */
8455  }
8456  cpl_bivector_unwrap_vectors(list);
8457 /* alternative (not robust)
8458  cpl_polynomial_delete(poly);
8459 */
8460  cpl_vector_unwrap(cn);
8461  }
8462 
8463  cpl_vector_unwrap(c0);
8464  cpl_table_delete(table);
8465 
8466  return CPL_ERROR_NONE;
8467 
8468 }
8469 
8470 
8540 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
8541  cpl_table *polytraces, double reference,
8542  double blue, double red, double dispersion,
8543  int flux, cpl_image *calibration)
8544 {
8545  const char *func = "mos_spatial_calibration";
8546 
8547  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8548  /* Max order is 5 */
8549  cpl_polynomial *polytop;
8550  cpl_polynomial *polybot;
8551  cpl_image **exslit;
8552  cpl_image *resampled;
8553  float *data;
8554  float *sdata;
8555  float *xdata;
8556  double vtop, vbot, value;
8557  double top, bot;
8558  double coeff;
8559  double ytop, ybot;
8560  double ypos, yfra;
8561  double factor;
8562  int yint, ysize, yprev;
8563  int nslits;
8564  int npseudo;
8565  int *slit_id;
8566  int *length;
8567  int nx, ny;
8568  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
8569  int missing_top, missing_bot;
8570  int null;
8571  int order;
8572  int i, j;
8573  cpl_size k;
8574 
8575  int create_position = 1;
8576 
8577 
8578  if (spectra == NULL || slits == NULL || polytraces == NULL) {
8579  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8580  return NULL;
8581  }
8582 
8583  if (dispersion <= 0.0) {
8584  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8585  return NULL;
8586  }
8587 
8588  if (red - blue < dispersion) {
8589  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8590  return NULL;
8591  }
8592 
8593  nx = cpl_image_get_size_x(spectra);
8594  ny = cpl_image_get_size_y(spectra);
8595  sdata = cpl_image_get_data(spectra);
8596  if (calibration)
8597  data = cpl_image_get_data(calibration);
8598 
8599  if (cpl_table_has_column(slits, "position"))
8600  create_position = 0;
8601 
8602  if (create_position) {
8603  cpl_table_new_column(slits, "position", CPL_TYPE_INT);
8604  cpl_table_new_column(slits, "length", CPL_TYPE_INT);
8605  cpl_table_set_column_unit(slits, "position", "pixel");
8606  cpl_table_set_column_unit(slits, "length", "pixel");
8607  }
8608  else
8609  length = cpl_table_get_data_int(slits, "length");
8610 
8611  nslits = cpl_table_get_nrow(slits);
8612  slit_id = cpl_table_get_data_int(slits, "slit_id");
8613  order = cpl_table_get_ncol(polytraces) - 2;
8614 
8615  /*
8616  * The spatial resampling is performed for a certain number of
8617  * pixels above and below the position of the reference wavelength:
8618  */
8619 
8620  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
8621  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
8622 
8623  exslit = cpl_calloc(nslits, sizeof(cpl_image *));
8624 
8625  for (i = 0; i < nslits; i++) {
8626 
8627  if (create_position == 0)
8628  if (length[i] == 0)
8629  continue;
8630 
8631  /*
8632  * Note that the x coordinate of the reference pixels on the CCD
8633  * is taken arbitrarily at the top end of each slit. This wouldn't
8634  * be entirely correct in case of curved slits, or in presence of
8635  * heavy distortions: in such cases the spatial resampling is
8636  * really performed across a wide range of wavelengths. But
8637  * the lag between top and bottom spectral curvature models
8638  * would introduce even in such cases negligible effects on
8639  * the spectral spatial resampling.
8640  */
8641 
8642  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
8643 
8644  start_pixel = refpixel - pixel_below;
8645  if (start_pixel < 0)
8646  start_pixel = 0;
8647 
8648  end_pixel = refpixel + pixel_above;
8649  if (end_pixel > nx)
8650  end_pixel = nx;
8651 
8652  /*
8653  * Recover from the table of spectral curvature coefficients
8654  * the curvature polynomials.
8655  */
8656 
8657  missing_top = 0;
8658  polytop = cpl_polynomial_new(1);
8659  for (k = 0; k <= order; k++) {
8660  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
8661  if (null) {
8662  cpl_polynomial_delete(polytop);
8663  missing_top = 1;
8664  break;
8665  }
8666  cpl_polynomial_set_coeff(polytop, &k, coeff);
8667  }
8668 
8669  missing_bot = 0;
8670  polybot = cpl_polynomial_new(1);
8671  for (k = 0; k <= order; k++) {
8672  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
8673  if (null) {
8674  cpl_polynomial_delete(polybot);
8675  missing_bot = 1;
8676  break;
8677  }
8678  cpl_polynomial_set_coeff(polybot, &k, coeff);
8679  }
8680 
8681  if (missing_top && missing_bot) {
8682  cpl_msg_warning(func, "Spatial calibration, slit %d was not "
8683  "traced: no extraction!",
8684  slit_id[i]);
8685  continue;
8686  }
8687 
8688  /*
8689  * In case just one of the two edges was not traced, the other
8690  * edge curvature model is duplicated and shifted to the other
8691  * end of the slit: better than nothing!
8692  */
8693 
8694  if (missing_top) {
8695  cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
8696  "the spectral curvature of the lower edge "
8697  "is used instead.", slit_id[i]);
8698  polytop = cpl_polynomial_duplicate(polybot);
8699  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
8700  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
8701  k = 0;
8702  coeff = cpl_polynomial_get_coeff(polybot, &k);
8703  coeff += ytop - ybot;
8704  cpl_polynomial_set_coeff(polytop, &k, coeff);
8705  }
8706 
8707  if (missing_bot) {
8708  cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
8709  "the spectral curvature of the upper edge "
8710  "is used instead.", slit_id[i]);
8711  polybot = cpl_polynomial_duplicate(polytop);
8712  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
8713  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
8714  k = 0;
8715  coeff = cpl_polynomial_get_coeff(polytop, &k);
8716  coeff -= ytop - ybot;
8717  cpl_polynomial_set_coeff(polybot, &k, coeff);
8718  }
8719 
8720  /*
8721  * Allocate image for current extracted slit
8722  */
8723 
8724  top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
8725  bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
8726  npseudo = ceil(top-bot) + 1;
8727 
8728  if (npseudo < 1) {
8729  cpl_polynomial_delete(polytop);
8730  cpl_polynomial_delete(polybot);
8731  cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
8732  slit_id[i]);
8733  continue;
8734  }
8735 
8736  exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
8737  xdata = cpl_image_get_data(exslit[i]);
8738 
8739  /*
8740  * Write interpolated values to slit image.
8741  */
8742 
8743  for (j = start_pixel; j < end_pixel; j++) {
8744  top = cpl_polynomial_eval_1d(polytop, j, NULL);
8745  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
8746  factor = (top-bot)/npseudo;
8747  for (k = 0; k <= npseudo; k++) {
8748  ypos = top - k*factor;
8749  yint = floor(ypos);
8750  yfra = ypos - yint;
8751  if (yint >= 0 && yint < ny-1) {
8752  vtop = sdata[j + nx*yint];
8753  vbot = sdata[j + nx*(yint+1)];
8754 
8755  //This means that the top and bottom traces are crossing,
8756  //which is physically impossible, so let's set it to 0.
8757  if(factor <= 0 )
8758  value = 0;
8759  else if(vtop == FLT_MAX || vbot == FLT_MAX)
8760  value = FLT_MAX;
8761  else
8762  {
8763  value = vtop*(1-yfra) + vbot*yfra;
8764  if (flux)
8765  value *= factor;
8766  }
8767  xdata[j + nx*(npseudo-k)] = value;
8768  if (calibration) {
8769  data[j + nx*yint] = (top-yint)/factor;
8770  if (k) {
8771 
8772  /*
8773  * This is added to recover lost pixels on
8774  * the CCD image (pixels are lost because
8775  * the CCD pixels are less than npseudo+1).
8776  */
8777 
8778  if (yprev - yint > 1) {
8779  data[j + nx*(yint+1)] = (top-yint-1)/factor;
8780  }
8781  }
8782  }
8783  }
8784  yprev = yint;
8785  }
8786  }
8787  cpl_polynomial_delete(polytop);
8788  cpl_polynomial_delete(polybot);
8789  }
8790 
8791  /*
8792  * Now all the slits images are copied to a single image
8793  */
8794 
8795  ysize = 0;
8796  for (i = 0; i < nslits; i++)
8797  if (exslit[i])
8798  ysize += cpl_image_get_size_y(exslit[i]);
8799 
8800  if(ysize == 0)
8801  return NULL;
8802 
8803  resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
8804 
8805  yint = -1;
8806  for (i = 0; i < nslits; i++) {
8807  if (exslit[i]) {
8808  yint += cpl_image_get_size_y(exslit[i]);
8809  cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
8810  if (create_position) {
8811  cpl_table_set_int(slits, "position", i, ysize - yint - 1);
8812  cpl_table_set_int(slits, "length", i,
8813  cpl_image_get_size_y(exslit[i]));
8814  }
8815  cpl_image_delete(exslit[i]);
8816  }
8817  else if (create_position) {
8818  cpl_table_set_int(slits, "position", i, -1);
8819  cpl_table_set_int(slits, "length", i, 0);
8820  }
8821  }
8822 
8823 
8824  /*
8825  * Elimination of non-traced slits from slit position table: we cannot do
8826  * it because we would lose sync with polytraces and other slit-oriented
8827  * tables. COMMENTED OUT.
8828  *
8829 
8830  if (create_position) {
8831 
8832  if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
8833  cpl_table_erase_selected(slits);
8834 
8835  }
8836 
8837  */
8838 
8839  cpl_free(exslit);
8840 
8841  return resampled;
8842 
8843 }
8844 
8845 
8958 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
8959  cpl_vector *lines,
8960  double dispersion, float level,
8961  int sradius, int order,
8962  double reject, double refwave,
8963  double *wavestart, double *waveend,
8964  int *nlines, double *error,
8965  cpl_table *idscoeff,
8966  cpl_image *calibration,
8967  cpl_image *residuals,
8968  cpl_table *restable,
8969  cpl_table *detected_lines)
8970 {
8971 
8972  const char *func = "mos_wavelength_calibration_final";
8973 
8974  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8975  /* Max order is 5 */
8976 
8977  double tolerance = 20.0; /* Probably forever... */
8978  int step = 10; /* Compute restable every "step" rows */
8979 
8980  char name[MAX_COLNAME];
8981 
8982  cpl_image *resampled;
8983  cpl_bivector *peaks_ident;
8984  cpl_vector *wavel;
8985  cpl_vector *peaks;
8986  cpl_polynomial *ids;
8987  cpl_polynomial *lin;
8988  cpl_polynomial *fguess;
8989  cpl_table *coeff;
8990  double ids_err;
8991  double max_disp, min_disp;
8992  double *line;
8993  double firstLambda, lastLambda, lambda;
8994  double wave, pixe, value;
8995  double c;
8996  float *sdata;
8997  float *rdata;
8998  float *idata;
8999  float *ddata;
9000  float v1, v2, vi;
9001  float fpixel;
9002  int *length;
9003  int pixstart, pixend;
9004  int row_top, row_bot;
9005  int extrapolation;
9006  int nref;
9007  int nslits;
9008  int nfits;
9009  int nl, nx, ny, pixel;
9010  int countLines, usedLines;
9011  int uorder;
9012  int missing;
9013  int null;
9014  int width, uradius;
9015  int i, j, s;
9016  cpl_size k;
9017 
9018 
9019  if (dispersion == 0.0) {
9020  cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
9021  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9022  return NULL;
9023  }
9024 
9025  if (dispersion < 0.0) {
9026  cpl_msg_error(func, "The expected dispersion must be positive");
9027  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9028  return NULL;
9029  }
9030 
9031  if (idscoeff == NULL) {
9032  cpl_msg_error(func, "A preallocated IDS coeff table must be given");
9033  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9034  return NULL;
9035  }
9036 
9037  max_disp = dispersion + dispersion * tolerance / 100;
9038  min_disp = dispersion - dispersion * tolerance / 100;
9039 
9040  if (order < 1) {
9041  cpl_msg_error(func, "The order of the fitting polynomial "
9042  "must be at least 1");
9043  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9044  return NULL;
9045  }
9046 
9047  if (image == NULL || lines == NULL) {
9048  cpl_msg_error(func, "Both spectral exposure and reference line "
9049  "catalog are required in input");
9050  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9051  return NULL;
9052  }
9053 
9054  nx = cpl_image_get_size_x(image);
9055  ny = cpl_image_get_size_y(image);
9056  sdata = cpl_image_get_data_float(image);
9057 
9058  nref = cpl_vector_get_size(lines);
9059  line = cpl_vector_get_data(lines);
9060 
9061  if (*wavestart < 1.0 && *waveend < 1.0) {
9062  firstLambda = line[0];
9063  lastLambda = line[nref-1];
9064  extrapolation = (lastLambda - firstLambda) / 10;
9065  firstLambda -= extrapolation;
9066  lastLambda += extrapolation;
9067  *wavestart = firstLambda;
9068  *waveend = lastLambda;
9069  }
9070  else {
9071  firstLambda = *wavestart;
9072  lastLambda = *waveend;
9073  }
9074 
9075  nl = (lastLambda - firstLambda) / dispersion;
9076  resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
9077  rdata = cpl_image_get_data_float(resampled);
9078 
9079  /*
9080  * Allocate total output table of IDS coefficients
9081  */
9082 
9083  for (j = 0; j <= order; j++)
9084  cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
9085 
9086  if (calibration)
9087  idata = cpl_image_get_data_float(calibration);
9088 
9089  if (residuals)
9090  ddata = cpl_image_get_data_float(residuals);
9091 
9092  if (restable) {
9093  cpl_table_set_size(restable, nref);
9094  cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
9095  cpl_table_copy_data_double(restable, "wavelength", line);
9096  for (i = 0; i < ny; i += step) {
9097  snprintf(name, MAX_COLNAME, "r%d", i);
9098  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
9099  snprintf(name, MAX_COLNAME, "d%d", i);
9100  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
9101  snprintf(name, MAX_COLNAME, "p%d", i);
9102  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
9103  }
9104  }
9105 
9106  if (detected_lines) {
9107  cpl_table_set_size(detected_lines, 0);
9108  cpl_table_new_column(detected_lines, "slit_id", CPL_TYPE_INT);
9109  cpl_table_new_column(detected_lines, "xpos_rectified", CPL_TYPE_DOUBLE);
9110  cpl_table_new_column(detected_lines, "ypos_rectified", CPL_TYPE_DOUBLE);
9111  cpl_table_new_column(detected_lines, "xpos_rectified_iter", CPL_TYPE_DOUBLE);
9112  cpl_table_new_column(detected_lines, "ypos_rectified_iter", CPL_TYPE_DOUBLE);
9113  cpl_table_new_column(detected_lines, "peak_flux", CPL_TYPE_DOUBLE);
9114  cpl_table_new_column(detected_lines, "wave_ident", CPL_TYPE_DOUBLE);
9115  cpl_table_new_column(detected_lines, "wave_ident_iter", CPL_TYPE_DOUBLE);
9116  cpl_table_new_column(detected_lines, "xpos_fit_rect_wavecal", CPL_TYPE_DOUBLE);
9117  cpl_table_new_column(detected_lines, "res_xpos", CPL_TYPE_DOUBLE);
9118  cpl_table_new_column(detected_lines, "fit_used", CPL_TYPE_INT);
9119  }
9120 
9121 
9122  /*
9123  * Process all slits separately.
9124  */
9125 
9126  nslits = cpl_table_get_nrow(slits);
9127  length = cpl_table_get_data_int(slits, "length");
9128 
9129  row_top = ny;
9130  for (s = 0; s < nslits; s++) {
9131 
9132  int slit_id;
9133  slit_id = cpl_table_get_int(slits, "slit_id", s, NULL);
9134 
9135  if (length[s] == 0)
9136  continue;
9137 
9138  /*
9139  * row_top and row_bot define the boundaries of the current slit.
9140  * Here we begin (arbitrarily...) from the top slit.
9141  */
9142 
9143  row_bot = cpl_table_get_int(slits, "position", s, NULL);
9144 
9145  if (sradius > 0) {
9146 
9147  /*
9148  * If a search radius was defined, allocate the table of
9149  * the fitting polynomials coefficients. This table is
9150  * just used to generate the first-guess polynomial made
9151  * of the median coefficients of all polynomials found
9152  * for this slit.
9153  */
9154 
9155  coeff = cpl_table_new(row_top - row_bot);
9156  for (j = 0; j <= order; j++)
9157  cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
9158  }
9159 
9160  /*
9161  * Here is the loop on all rows of the current slit. They are
9162  * wavelength calibrated one by one.
9163  */
9164 
9165  for (i = row_bot; i < row_top; i++) {
9166  width = mos_lines_width(sdata + i*nx, nx);
9167  if (width < 5)
9168  width = 5;
9169  peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
9170  if (peaks) {
9171  peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
9172  }
9173  if (peaks) {
9174  int keep_multiplex = mos_multiplex;
9175  mos_multiplex = -1;
9176  if(detected_lines)
9177  {
9178  cpl_size newlines = cpl_vector_get_size(peaks);
9179  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
9180  cpl_table_set_size(detected_lines, oldsize + newlines);
9181  for(cpl_size iline = 0; iline < newlines; ++iline)
9182  {
9183  cpl_table_set_int(detected_lines, "slit_id",
9184  oldsize + iline, slit_id);
9185  cpl_table_set_double(detected_lines, "xpos_rectified",
9186  oldsize + iline, cpl_vector_get(peaks, iline) + 1);
9187  cpl_table_set_double(detected_lines, "ypos_rectified",
9188  oldsize + iline, (double)i + 1);
9189  cpl_table_set_double(detected_lines, "peak_flux",
9190  oldsize + iline,
9191  sdata[i*nx+(int)(cpl_vector_get(peaks, iline)+0.5)]);
9192  cpl_table_set_int(detected_lines,
9193  "fit_used",
9194  oldsize + iline, 0);
9195  }
9196  }
9197  peaks_ident = mos_identify_peaks(peaks, lines,
9198  min_disp, max_disp, 0.05);
9199  mos_multiplex = keep_multiplex;
9200  if (peaks_ident) {
9201  cpl_bivector * peaks_ident_used_fit;
9202  countLines = cpl_bivector_get_size(peaks_ident);
9203  if (countLines < 4) {
9204  cpl_bivector_delete(peaks_ident);
9205  cpl_vector_delete(peaks);
9206  if (nlines)
9207  nlines[i] = 0;
9208  if (error)
9209  error[i] = 0.0;
9210  continue;
9211  }
9212 
9213  /*
9214  * Set reference wavelength as zero point
9215  */
9216 
9217  wavel = cpl_bivector_get_y(peaks_ident);
9218  cpl_vector_subtract_scalar(wavel, refwave);
9219 
9220  uorder = countLines / 2 - 1;
9221  if (uorder > order)
9222  uorder = order;
9223 
9224  ids = mos_poly_wav2pix(peaks_ident, uorder, reject,
9225  2 * (uorder + 1), &usedLines,
9226  &ids_err, &peaks_ident_used_fit);
9227 
9228  if (ids == NULL) {
9229  cpl_bivector_delete(peaks_ident);
9230  cpl_vector_delete(peaks);
9231  if (nlines)
9232  nlines[i] = 0;
9233  if (error)
9234  error[i] = 0.0;
9235  cpl_error_reset();
9236  continue;
9237  }
9238 
9239  if (sradius > 0) {
9240  for (k = 0; k <= order; k++) {
9241  if (k > uorder) {
9242  cpl_table_set_double(coeff, clab[k],
9243  i - row_bot, 0.0);
9244  }
9245  else {
9246  cpl_table_set_double(coeff, clab[k],
9247  i - row_bot, cpl_polynomial_get_coeff(ids, &k));
9248  }
9249  }
9250  }
9251  /* else { */
9252  if (calibration) {
9253  pixstart = cpl_polynomial_eval_1d(ids,
9254  cpl_bivector_get_y_data(peaks_ident)[0],
9255  NULL);
9256  pixend = cpl_polynomial_eval_1d(ids,
9257  cpl_bivector_get_y_data(peaks_ident)[countLines-1],
9258  NULL);
9259  extrapolation = (pixend - pixstart) / 5;
9260  pixstart -= extrapolation;
9261  pixend += extrapolation;
9262  if (pixstart < 0)
9263  pixstart = 0;
9264  if (pixend > nx)
9265  pixend = nx;
9266 
9267  for (j = pixstart; j < pixend; j++) {
9268  (idata + i*nx)[j] = mos_eval_dds(ids,
9269  firstLambda, lastLambda, refwave, j);
9270  }
9271  }
9272 
9273  /*
9274  * Residuals image
9275  */
9276 
9277  if (residuals || (restable && !(i%step))) {
9278  if (restable && !(i%step)) {
9279  lin = cpl_polynomial_new(1);
9280  for (k = 0; k < 2; k++)
9281  cpl_polynomial_set_coeff(lin, &k,
9282  cpl_polynomial_get_coeff(ids, &k));
9283  }
9284  for (j = 0; j < countLines; j++) {
9285  pixe = cpl_bivector_get_x_data(peaks_ident)[j];
9286  wave = cpl_bivector_get_y_data(peaks_ident)[j];
9287  value = pixe
9288  - cpl_polynomial_eval_1d(ids, wave, NULL);
9289  if (residuals) {
9290  pixel = pixe + 0.5;
9291  (ddata + i*nx)[pixel] = value;
9292  }
9293  if (restable && !(i%step)) {
9294  for (k = 0; k < nref; k++) {
9295  if (fabs(line[k]-refwave-wave) < 0.1) {
9296  snprintf(name, MAX_COLNAME,
9297  "r%d", i);
9298  cpl_table_set_double(restable, name,
9299  k, value);
9300  value = pixe
9301  - cpl_polynomial_eval_1d(lin,
9302  wave, NULL);
9303  snprintf(name, MAX_COLNAME,
9304  "d%d", i);
9305  cpl_table_set_double(restable, name,
9306  k, value);
9307  snprintf(name, MAX_COLNAME,
9308  "p%d", i);
9309  cpl_table_set_double(restable, name,
9310  k, pixe);
9311  break;
9312  }
9313  }
9314  }
9315  }
9316  if (restable && !(i%step)) {
9317  cpl_polynomial_delete(lin);
9318  }
9319 /***
9320  for (j = 0; j < countLines; j++) {
9321  pixel = cpl_bivector_get_x_data(output)[j]
9322  + 0.5;
9323  (ddata + i*nx)[pixel] =
9324  cpl_bivector_get_x_data(output)[j]
9325  - cpl_polynomial_eval_1d(ids,
9326  cpl_bivector_get_y_data(output)[j],
9327  NULL);
9328  }
9329 ***/
9330  //Fill the line identification information in
9331  //the detected_lines table
9332  if(detected_lines)
9333  {
9334  cpl_size nidentlines = cpl_bivector_get_size(peaks_ident);
9335  cpl_size ndetectlines = cpl_vector_get_size(peaks);
9336  cpl_size totalsize = cpl_table_get_nrow(detected_lines);
9337  for(cpl_size idline = 0; idline < nidentlines; ++idline)
9338  {
9339  for(cpl_size detline = 0; detline < ndetectlines; ++detline)
9340  {
9341  if(cpl_vector_get(peaks, detline) ==
9342  cpl_bivector_get_x_data(peaks_ident)[idline])
9343  {
9344  cpl_size table_pos = totalsize - ndetectlines + detline;
9345  double wave_ident = cpl_bivector_get_y_data(peaks_ident)[idline] + refwave;
9346  double xpix_fit = cpl_polynomial_eval_1d(ids,
9347  wave_ident - refwave, NULL);
9348  double xpos_det = cpl_table_get_double(detected_lines,
9349  "xpos_rectified",
9350  table_pos, &null);
9351  cpl_table_set_double(detected_lines,
9352  "wave_ident",
9353  table_pos,
9354  wave_ident);
9355  cpl_table_set_double(detected_lines,
9356  "xpos_fit_rect_wavecal",
9357  table_pos,
9358  xpix_fit + 1);
9359  cpl_table_set_double(detected_lines,
9360  "res_xpos",
9361  table_pos,
9362  xpos_det - xpix_fit - 1);
9363  cpl_table_set_int(detected_lines,
9364  "fit_used",
9365  table_pos, 0);
9366  for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
9367  {
9368  if(cpl_bivector_get_x_data(peaks_ident)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
9369  cpl_table_set_int(detected_lines,
9370  "fit_used",
9371  table_pos, 1);
9372  }
9373  }
9374  }
9375  }
9376  }
9377 
9378  }
9379  /* } */
9380 
9381  /*
9382  * Write it anyway, even in case a first-guess based
9383  * solution will be searched afterwards: in case of
9384  * failure, the "blind" solution is kept.
9385  */
9386 
9387  if (nlines)
9388  nlines[i] = usedLines;
9389  if (error)
9390  error[i] = ids_err / sqrt(usedLines/(uorder + 1));
9391 
9392  for (k = 0; k <= order; k++) {
9393  if (k > uorder) {
9394  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
9395  }
9396  else {
9397  cpl_table_set_double(idscoeff, clab[k], i,
9398  cpl_polynomial_get_coeff(ids, &k));
9399  }
9400  }
9401 
9402  cpl_polynomial_delete(ids);
9403  cpl_bivector_delete(peaks_ident);
9404  }
9405  cpl_vector_delete(peaks);
9406  }
9407  } /* End of loop on current slit's rows */
9408 
9409 
9410  if (sradius > 0) {
9411 
9412  /*
9413  * See whether there are valid fits at all...
9414  */
9415 
9416  nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
9417 
9418  if (nfits) {
9419  int slope = 0;
9420 
9421  fguess = cpl_polynomial_new(1);
9422 
9423  if (mos_interpolate_wavecalib_mos(coeff, 2, 1)) {
9424 
9425  slope = 0;
9426 
9427  /*
9428  * Compute a median IDS polynomial for the current slit
9429  */
9430 
9431  for (k = 0; k <= order; k++) {
9432  c = cpl_table_get_column_median(coeff, clab[k]);
9433  cpl_polynomial_set_coeff(fguess, &k, c);
9434  }
9435  }
9436  else {
9437  slope = 1;
9438  }
9439 
9440  for (i = row_bot; i < row_top; i++) {
9441  cpl_bivector * peaks_ident_used_fit;
9442 
9443  /*
9444  * Use first-guess to find the reference lines again
9445  */
9446 
9447  width = mos_lines_width(sdata + i*nx, nx);
9448  if (width > sradius) {
9449  uradius = width;
9450  }
9451  else {
9452  uradius = sradius;
9453  }
9454 
9455  if (slope) {
9456  for (k = 0; k <= order; k++) {
9457  c = cpl_table_get_double(coeff, clab[k],
9458  i - row_bot, NULL);
9459  cpl_polynomial_set_coeff(fguess, &k, c);
9460  }
9461  }
9462 
9463  peaks_ident = mos_find_peaks(sdata + i*nx, nx, lines,
9464  fguess, refwave, uradius);
9465 
9466  if (peaks_ident == NULL) {
9467  cpl_error_reset();
9468  continue;
9469  }
9470 
9471  countLines = cpl_bivector_get_size(peaks_ident);
9472 
9473  if (countLines < 4) {
9474  cpl_bivector_delete(peaks_ident);
9475  continue;
9476  }
9477 
9478  /*
9479  * Set reference wavelength as zero point
9480  */
9481 
9482  wavel = cpl_bivector_get_y(peaks_ident);
9483  cpl_vector_subtract_scalar(wavel, refwave);
9484 
9485  uorder = countLines / 2 - 1;
9486  if (uorder > order)
9487  uorder = order;
9488 
9489  ids = mos_poly_wav2pix(peaks_ident, uorder, reject,
9490  2 * (uorder + 1), &usedLines,
9491  &ids_err, &peaks_ident_used_fit);
9492 
9493  if (ids == NULL) {
9494  cpl_error_reset();
9495  cpl_bivector_delete(peaks_ident);
9496  continue;
9497  }
9498 
9499  if (nlines)
9500  nlines[i] = usedLines;
9501  if (error)
9502  error[i] = ids_err / sqrt(usedLines/(uorder + 1));
9503 
9504  if (calibration) {
9505  pixstart = cpl_polynomial_eval_1d(ids,
9506  cpl_bivector_get_y_data(peaks_ident)[0],
9507  NULL);
9508  pixend = cpl_polynomial_eval_1d(ids,
9509  cpl_bivector_get_y_data(peaks_ident)[countLines-1],
9510  NULL);
9511  extrapolation = (pixend - pixstart) / 5;
9512  pixstart -= extrapolation;
9513  pixend += extrapolation;
9514  if (pixstart < 0)
9515  pixstart = 0;
9516  if (pixend > nx)
9517  pixend = nx;
9518 
9519  for (j = pixstart; j < pixend; j++) {
9520  (idata + i*nx)[j] = mos_eval_dds(ids,
9521  firstLambda, lastLambda, refwave, j);
9522  }
9523  }
9524 
9525  /*
9526  * Residuals image
9527  */
9528 
9529  if (residuals || (restable && !(i%step))) {
9530  if (restable && !(i%step)) {
9531  lin = cpl_polynomial_new(1);
9532  for (k = 0; k < 2; k++)
9533  cpl_polynomial_set_coeff(lin, &k,
9534  cpl_polynomial_get_coeff(ids, &k));
9535  }
9536  for (j = 0; j < countLines; j++) {
9537  pixe = cpl_bivector_get_x_data(peaks_ident)[j];
9538  wave = cpl_bivector_get_y_data(peaks_ident)[j];
9539  value = pixe
9540  - cpl_polynomial_eval_1d(ids, wave, NULL);
9541  if (residuals) {
9542  pixel = pixe + 0.5;
9543  (ddata + i*nx)[pixel] = value;
9544  }
9545  if (restable && !(i%step)) {
9546  for (k = 0; k < nref; k++) {
9547  if (fabs(line[k]-refwave-wave) < 0.1) {
9548  snprintf(name, MAX_COLNAME,
9549  "r%d", i);
9550  cpl_table_set_double(restable, name,
9551  k, value);
9552  value = pixe
9553  - cpl_polynomial_eval_1d(lin,
9554  wave, NULL);
9555  snprintf(name, MAX_COLNAME,
9556  "d%d", i);
9557  cpl_table_set_double(restable, name,
9558  k, value);
9559  snprintf(name, MAX_COLNAME,
9560  "p%d", i);
9561  cpl_table_set_double(restable, name,
9562  k, pixe);
9563  break;
9564  }
9565  }
9566  }
9567  }
9568  if (restable && !(i%step)) {
9569  cpl_polynomial_delete(lin);
9570  }
9571 /***
9572  for (j = 0; j < countLines; j++) {
9573  pixel = cpl_bivector_get_x_data(output)[j]
9574  + 0.5;
9575  (ddata + i*nx)[pixel] =
9576  cpl_bivector_get_x_data(output)[j]
9577  - cpl_polynomial_eval_1d(ids,
9578  cpl_bivector_get_y_data(output)[j],
9579  NULL);
9580  }
9581 ***/
9582  }
9583 
9584  if(detected_lines)
9585  {
9586  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
9587  cpl_size nidentlines = cpl_bivector_get_size(peaks_ident);
9588  cpl_table_set_size(detected_lines, oldsize + nidentlines);
9589  for(cpl_size idline = 0; idline < nidentlines ; ++idline)
9590  {
9591  double wave_ident = cpl_bivector_get_y_data(peaks_ident)[idline] + refwave;
9592  double xpix_fit = cpl_polynomial_eval_1d(ids,
9593  wave_ident - refwave, NULL);
9594  cpl_table_set_int(detected_lines, "slit_id",
9595  oldsize + idline, slit_id);
9596  cpl_table_set_double(detected_lines, "xpos_rectified_iter",
9597  oldsize + idline, cpl_bivector_get_x_data(peaks_ident)[idline] + 1);
9598  cpl_table_set_double(detected_lines, "ypos_rectified_iter",
9599  oldsize + idline, (double)i + 1);
9600  cpl_table_set_double(detected_lines, "peak_flux",
9601  oldsize + idline,
9602  sdata[i*nx+(int)(cpl_bivector_get_x_data(peaks_ident)[idline]+0.5)]);
9603  cpl_table_set_double(detected_lines, "wave_ident_iter",
9604  oldsize + idline, wave_ident);
9605  cpl_table_set_double(detected_lines, "xpos_fit_rect_wavecal",
9606  oldsize + idline, xpix_fit + 1);
9607  cpl_table_set_int(detected_lines,
9608  "fit_used",
9609  oldsize + idline, 0);
9610  for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
9611  {
9612  if(cpl_bivector_get_x_data(peaks_ident)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
9613  cpl_table_set_int(detected_lines,
9614  "fit_used",
9615  oldsize + idline, 1);
9616  }
9617  }
9618  }
9619 
9620 
9621  for (k = 0; k <= order; k++) {
9622  if (k > uorder) {
9623  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
9624  }
9625  else {
9626  cpl_table_set_double(idscoeff, clab[k], i,
9627  cpl_polynomial_get_coeff(ids, &k));
9628  }
9629  }
9630 
9631  cpl_bivector_delete(peaks_ident);
9632  cpl_polynomial_delete(ids);
9633 
9634  } /* End of loop "use ids as a first-guess" */
9635 
9636  cpl_polynomial_delete(fguess);
9637  }
9638 
9639  cpl_table_delete(coeff);
9640 
9641  }
9642 
9643  row_top = row_bot;
9644 
9645  } /* End of loop on slits */
9646 
9647 
9648  /*
9649  * At this point the idscoeff table has been filled with all the
9650  * fits coefficients obtained for all the rows of the input image.
9651  * Now we apply these coefficients to resample the input image
9652  * at constant wavelength step.
9653  */
9654 
9655  for (i = 0; i < ny; i++) {
9656 
9657  missing = 0;
9658  ids = cpl_polynomial_new(1);
9659  for (k = 0; k <= order; k++) {
9660  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
9661  if (null) {
9662  cpl_polynomial_delete(ids);
9663  missing = 1;
9664  break;
9665  }
9666  cpl_polynomial_set_coeff(ids, &k, c);
9667  }
9668  if (missing)
9669  continue;
9670 
9671  pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
9672  pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
9673  if (pixstart < 0)
9674  pixstart = 0;
9675  if (pixend > nx)
9676  pixend = nx;
9677 
9678  /*
9679  * Resampled image:
9680  */
9681 
9682  for (j = 0; j < nl; j++) {
9683  lambda = firstLambda + j * dispersion;
9684  fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
9685  pixel = fpixel;
9686  if (pixel >= 0 && pixel < nx-1) {
9687  v1 = (sdata + i*nx)[pixel];
9688  v2 = (sdata + i*nx)[pixel+1];
9689  vi = v1 + (v2-v1)*(fpixel-pixel);
9690  (rdata + i*nl)[j] = vi;
9691  }
9692  }
9693 
9694  cpl_polynomial_delete(ids);
9695  }
9696 
9697  return resampled;
9698 }
9699 
9700 
9727 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave,
9728  double firstLambda, double lastLambda,
9729  double dispersion, cpl_table *idscoeff,
9730  int flux)
9731 {
9732 
9733  const char *func = "mos_wavelength_calibration";
9734 
9735  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
9736  /* Max order is 5 */
9737 
9738  cpl_image *resampled;
9739  cpl_polynomial *ids;
9740  double pixel_per_lambda;
9741  double lambda;
9742  double c;
9743  float *sdata;
9744  float *rdata;
9745  float v0, v1, v2, v3, vi;
9746  float fpixel;
9747  int order;
9748  int pixstart, pixend;
9749  int nl, nx, ny, pixel;
9750  int missing;
9751  int null;
9752  int i, j;
9753  cpl_size k;
9754 
9755 
9756  if (dispersion <= 0.0) {
9757  cpl_msg_error(func, "The resampling step must be positive");
9758  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9759  return NULL;
9760  }
9761 
9762  if (lastLambda - firstLambda < dispersion) {
9763  cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f",
9764  firstLambda, lastLambda);
9765  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9766  return NULL;
9767  }
9768 
9769  if (idscoeff == NULL) {
9770  cpl_msg_error(func, "An IDS coeff table must be given");
9771  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9772  return NULL;
9773  }
9774 
9775  if (image == NULL) {
9776  cpl_msg_error(func, "A scientific spectral image must be given");
9777  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9778  return NULL;
9779  }
9780 
9781  nx = cpl_image_get_size_x(image);
9782  ny = cpl_image_get_size_y(image);
9783  sdata = cpl_image_get_data_float(image);
9784 
9785  nl = (lastLambda - firstLambda) / dispersion;
9786  resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
9787  rdata = cpl_image_get_data_float(resampled);
9788 
9789  order = 0;
9790  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
9791  ++order;
9792  --order;
9793 
9794  for (i = 0; i < ny; i++) {
9795 
9796  missing = 0;
9797  ids = cpl_polynomial_new(1);
9798  for (k = 0; k <= order; k++) {
9799  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
9800  if (null) {
9801  cpl_polynomial_delete(ids);
9802  missing = 1;
9803  break;
9804  }
9805  cpl_polynomial_set_coeff(ids, &k, c);
9806  }
9807  if (missing)
9808  continue;
9809 
9810  pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
9811  pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
9812  if (pixstart < 0)
9813  pixstart = 0;
9814  if (pixend > nx)
9815  pixend = nx;
9816 
9817  /*
9818  * Resampled image:
9819  */
9820 
9821  for (j = 0; j < nl; j++) {
9822  lambda = firstLambda + j * dispersion;
9823  fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave,
9824  &pixel_per_lambda);
9825 
9826  /*
9827  * The local dispersion is 1 / pixel_per_lambda
9828  * and this factor is used for applying the flux
9829  * conservation correction (if requested).
9830  */
9831 
9832  pixel = fpixel;
9833 
9834  // if (dispersion * pixel_per_lambda < 2.0) {
9835  if (1) { /* Old behaviour: this is safe. */
9836 
9837  /*
9838  * In this case we just sample interpolating the
9839  * signal of nearby pixels.
9840  */
9841 
9842  //the wave calibration should be a monotonically increasing
9843  //function. If the detivative is negative, we are outside of
9844  //the wavelength solution domain.
9845  if(pixel_per_lambda <= 0)
9846  vi = 0;
9847  else if (fpixel < 0)
9848  vi = 0;
9849  else if (pixel >= 1 && pixel < nx-2) {
9850  v0 = (sdata + i*nx)[pixel-1];
9851  v1 = (sdata + i*nx)[pixel];
9852  v2 = (sdata + i*nx)[pixel+1];
9853  v3 = (sdata + i*nx)[pixel+2];
9854  vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
9855  + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
9856  + 2*v1;
9857  vi /= 2;
9858  if (v1 > v2) {
9859  if (vi > v1) {
9860  vi = v1;
9861  }
9862  else if (vi < v2) {
9863  vi = v2;
9864  }
9865  }
9866  else {
9867  if (vi > v2) {
9868  vi = v2;
9869  }
9870  else if (vi < v1) {
9871  vi = v1;
9872  }
9873  }
9874  if (flux)
9875  vi *= dispersion * pixel_per_lambda;
9876  }
9877  else if (pixel >= 0 && pixel < nx-1) {
9878  v1 = (sdata + i*nx)[pixel];
9879  v2 = (sdata + i*nx)[pixel+1];
9880  vi = v1 + (v2-v1)*(fpixel-pixel);
9881  if (flux)
9882  vi *= dispersion * pixel_per_lambda;
9883  }
9884  else
9885  vi = 0;
9886  (rdata + i*nl)[j] = vi;
9887  }
9888  else {
9889 
9890  /*
9891  * Here instead we integrate the pixel values in
9892  * the interval centered at the interpolation point.
9893  * This interval is long dispersion * pixel_per_lambda
9894  * of the original pixels, and is centered at fpixel.
9895  * So it starts at fpixel - dispersion * pixel_per_lambda / 2,
9896  * and it ends at fpixel + dispersion * pixel_per_lambda / 2.
9897  */
9898 
9899  double spos = fpixel - dispersion * pixel_per_lambda / 2;
9900  double epos = fpixel + dispersion * pixel_per_lambda / 2;
9901 
9902  /*
9903  * Brutal sum over all involved pixels
9904  */
9905 
9906  int spix = spos;
9907  int epix = epos + 1;
9908 
9909  if (spix < 0)
9910  spix = 0;
9911 
9912  if (epix > nx)
9913  epix = nx;
9914 
9915  vi = 0.0;
9916  for (k = spix; k < epix; k++) {
9917  if (pixel >= 0 && pixel < nx) {
9918  vi += (sdata + i*nx)[k];
9919  }
9920  }
9921 
9922  /*
9923  * Correct integrated flux by true length
9924  * of interval. This is clearly an approximation,
9925  * but it's good enough if the PSF is much larger
9926  * than the original pix.
9927  */
9928 
9929  vi *= dispersion * pixel_per_lambda / (epix - spix);
9930 
9931  /*
9932  * Flux conservation is a geometric factor that is applied
9933  * always in the same way...
9934  */
9935 
9936  if (flux)
9937  vi *= dispersion * pixel_per_lambda;
9938 
9939  (rdata + i*nl)[j] = vi;
9940  }
9941  }
9942 
9943  cpl_polynomial_delete(ids);
9944  }
9945 
9946  return resampled;
9947 }
9948 
9949 
10016 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits,
10017  double refwave, double firstLambda,
10018  double lastLambda, cpl_table *idscoeff,
10019  cpl_vector *skylines, int highres, int order,
10020  cpl_image *calibration, int sradius)
10021 {
10022  const char *func = "mos_wavelength_align";
10023 
10024  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10025  /* Max order is 5 */
10026  double *line;
10027  double *data;
10028  double expPos, offset;
10029  double c;
10030  double lambda1, lambda2;
10031  double rms;
10032  float pos;
10033  float *sdata;
10034  float *cdata;
10035  int *idata;
10036  int startPos, endPos;
10037  int window = 2*sradius + 1;
10038  int nlines;
10039  int nslits;
10040  int npoints;
10041  int nrows;
10042  int nx, ny;
10043  int xlow, ylow, xhig, yhig;
10044  int idsorder, uorder;
10045  int *slit_id;
10046  int *position;
10047  int *length;
10048  int missing;
10049  int null;
10050  int i;
10051  cpl_size j, k;
10052 
10053  char offname[MAX_COLNAME];
10054  char name[MAX_COLNAME];
10055 
10056  cpl_polynomial *ids;
10057  cpl_polynomial *polycorr;
10058  cpl_image *exslit;
10059  cpl_image *sky;
10060  cpl_table *offsets;
10061  cpl_table *dummy;
10062  cpl_vector *wave;
10063  cpl_vector *offs;
10064 
10065 
10066  if (idscoeff == NULL) {
10067  cpl_msg_error(func, "An IDS coeff table must be given");
10068  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10069  return NULL;
10070  }
10071 
10072  if (image == NULL) {
10073  cpl_msg_error(func, "A scientific spectral image must be given");
10074  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10075  return NULL;
10076  }
10077 
10078  if (slits == NULL) {
10079  cpl_msg_error(func, "A slit position table must be given");
10080  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10081  return NULL;
10082  }
10083 
10084  if (skylines) {
10085  line = cpl_vector_get_data(skylines);
10086  nlines = cpl_vector_get_size(skylines);
10087  }
10088  else {
10089  cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10090  "given: using internal list of reference sky lines");
10091  if (highres) {
10092  line = default_lines_hi;
10093  nlines = sizeof(default_lines_hi) / sizeof(double);
10094  }
10095  else {
10096  line = default_lines_lo;
10097  nlines = sizeof(default_lines_lo) / sizeof(double);
10098  }
10099  }
10100 
10101  if (calibration)
10102  cdata = cpl_image_get_data(calibration);
10103 
10104  nx = cpl_image_get_size_x(image);
10105  ny = cpl_image_get_size_y(image);
10106 
10107  nslits = cpl_table_get_nrow(slits);
10108  slit_id = cpl_table_get_data_int(slits, "slit_id");
10109  position = cpl_table_get_data_int(slits, "position");
10110  length = cpl_table_get_data_int(slits, "length");
10111 
10112 
10113  /*
10114  * Define the output table of offsets
10115  */
10116 
10117  nrows = 0;
10118  for (i = 0; i < nlines; i++)
10119  if (line[i] > firstLambda && line[i] < lastLambda)
10120  nrows++;
10121 
10122  offsets = cpl_table_new(nrows);
10123  cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10124  cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10125 
10126  nrows = 0;
10127  for (i = 0; i < nlines; i++) {
10128  if (line[i] > firstLambda && line[i] < lastLambda) {
10129  cpl_table_set_double(offsets, "wave", nrows, line[i]);
10130  nrows++;
10131  }
10132  }
10133 
10134  /*
10135  * Here "line" is made to point to the new list of selected wavelengths
10136  */
10137 
10138  line = cpl_table_get_data_double(offsets, "wave");
10139  nlines = nrows;
10140 
10141  idsorder = 0;
10142  while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10143  ++idsorder;
10144  --idsorder;
10145 
10146  xlow = 1;
10147  xhig = nx;
10148  for (i = 0; i < nslits; i++) {
10149 
10150  if (length[i] == 0)
10151  continue;
10152 
10153  snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
10154  cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
10155 
10156  /*
10157  * Define the extraction boundaries. We DON'T write:
10158  *
10159  * ylow = position[i];
10160  * yhig = ylow + length[i];
10161  *
10162  * because the cpl_image pixels are counted from 1, and because in
10163  * cpl_image_extract() the coordinates of the last pixel are inclusive.
10164  */
10165 
10166  ylow = position[i] + 1;
10167  yhig = ylow + length[i] - 1;
10168 
10169  exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
10170  sky = cpl_image_collapse_median_create(exslit, 0, 0, 1);
10171  sdata = cpl_image_get_data(sky);
10172 
10173  cpl_image_delete(exslit);
10174 
10175  /*
10176  * Return here to a decent way of counting pixels (i.e., starting
10177  * from 0)
10178  */
10179 
10180  ylow--;
10181 
10182  /*
10183  * Allocate a dummy table for collecting all the offsets
10184  * for all the lines: this is only needed for the computation
10185  * of the median offset for each sky line
10186  */
10187 
10188  dummy = cpl_table_new(yhig - ylow);
10189  for (j = 0; j < nlines; j++) {
10190  snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
10191  cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10192  }
10193 
10194  for (j = ylow; j < yhig; j++) {
10195 
10196  /*
10197  * Get the IDS polynomial for the current slit row
10198  */
10199 
10200  missing = 0;
10201  ids = cpl_polynomial_new(1);
10202  for (k = 0; k <= idsorder; k++) {
10203  c = cpl_table_get_double(idscoeff, clab[k], j, &null);
10204  if (null) {
10205  cpl_polynomial_delete(ids);
10206  missing = 1;
10207  break;
10208  }
10209  cpl_polynomial_set_coeff(ids, &k, c);
10210  }
10211  if (missing)
10212  continue;
10213 
10214  for (k = 0; k < nlines; k++) {
10215  expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10216  startPos = expPos - sradius;
10217  endPos = startPos + window;
10218  if (startPos < 0 || endPos >= nx)
10219  continue;
10220 
10221  if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10222  pos += startPos;
10223  offset = pos - expPos;
10224  snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, k);
10225  cpl_table_set_double(dummy, name, j - ylow, offset);
10226  }
10227  }
10228 
10229  cpl_polynomial_delete(ids);
10230  }
10231 
10232  cpl_image_delete(sky);
10233 
10234  for (j = 0; j < nlines; j++) {
10235  snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
10236  if (cpl_table_has_valid(dummy, name)) {
10237  offset = cpl_table_get_column_median(dummy, name);
10238  cpl_table_set_double(offsets, offname, j, offset);
10239  }
10240  }
10241 
10242  cpl_table_delete(dummy);
10243 
10244  }
10245 
10246 
10247  /*
10248  * In the following the input idscoeff table is modified by simply
10249  * adding the coefficients of the polynomial used to fit the sky
10250  * line residuals to the coefficients of the IDS polynomials.
10251  */
10252 
10253  for (i = 0; i < nslits; i++) {
10254 
10255  if (length[i] == 0)
10256  continue;
10257 
10258  snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
10259 
10260  /*
10261  * In the following, the "dummy" table is just a tool for
10262  * eliminating invalid points from the vectors to be fitted.
10263  */
10264 
10265  dummy = cpl_table_new(nlines);
10266  cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
10267  cpl_table_duplicate_column(dummy, "offset", offsets, offname);
10268 
10269  npoints = nlines - cpl_table_count_invalid(dummy, "offset");
10270  if (npoints == 0) {
10271  cpl_msg_warning(func, "No sky lines alignment was possible "
10272  "for slit ID=%d: no sky line found", slit_id[i]);
10273  cpl_table_delete(dummy);
10274  continue;
10275  }
10276 
10277  uorder = order;
10278  if (npoints <= uorder) {
10279  uorder = npoints - 1;
10280  if (uorder) {
10281  cpl_msg_warning(func, "Just %d sky lines detected for slit "
10282  "ID=%d, while a polynomial order %d was "
10283  "requested. Using polynomial order %d for "
10284  "this slit!", npoints, slit_id[i], order,
10285  uorder);
10286  }
10287  else {
10288  cpl_msg_warning(func, "Just %d sky lines detected for slit "
10289  "ID=%d, while a polynomial order %d was "
10290  "requested. Computing a median offset for "
10291  "this slit!", npoints, slit_id[i], order);
10292  }
10293  }
10294 
10295  cpl_table_erase_invalid(dummy);
10296 
10297  if (uorder > 1) {
10298 
10299  /*
10300  * Model offsets with polynomial fitting
10301  */
10302 
10303  wave = cpl_vector_wrap(npoints,
10304  cpl_table_get_data_double(dummy, "wave"));
10305  offs = cpl_vector_wrap(npoints,
10306  cpl_table_get_data_double(dummy, "offset"));
10307 
10308  /*
10309  * Set reference wavelength as zero point
10310  */
10311 
10312  cpl_vector_subtract_scalar(wave, refwave);
10313 
10314  polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10315 
10316  rms = sqrt(rms * (uorder + 1) / npoints);
10317 
10318  cpl_vector_unwrap(wave);
10319  cpl_vector_unwrap(offs);
10320  cpl_table_delete(dummy);
10321 
10322  /*
10323  * Now correct the coefficients of the corresponding IDS
10324  * polynomials related to this slit:
10325  */
10326 
10327  ylow = position[i];
10328  yhig = ylow + length[i];
10329 
10330  for (j = 0; j <= uorder; j++) {
10331  data = cpl_table_get_data_double(idscoeff, clab[j]);
10332  c = cpl_polynomial_get_coeff(polycorr, &j);
10333  for (k = ylow; k < yhig; k++)
10334  data[k] += c;
10335  }
10336 
10337  data = cpl_table_get_data_double(idscoeff, "error");
10338  for (k = ylow; k < yhig; k++)
10339  data[k] = sqrt(data[k]*data[k] + rms*rms);
10340 
10341  idata = cpl_table_get_data_int(idscoeff, "nlines");
10342  for (k = ylow; k < yhig; k++)
10343  idata[k] = npoints;
10344 
10345  /*
10346  * If a wavelengths map was provided, correct it to keep
10347  * into account the alignment to skylines:
10348  */
10349 
10350  if (calibration) {
10351  for (j = ylow; j < yhig; j++) {
10352  for (k = 1; k < nx; k++) {
10353  lambda1 = cdata[k - 1 + j*nx];
10354  lambda2 = cdata[k + j*nx];
10355  if (lambda1 < 1.0 || lambda2 < 1.0)
10356  continue;
10357  offset = cpl_polynomial_eval_1d(polycorr,
10358  lambda1-refwave, NULL);
10359  cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10360  }
10361  }
10362  }
10363 
10364  cpl_polynomial_delete(polycorr);
10365  }
10366  else if (uorder == 1) {
10367 
10368  /*
10369  * Model offsets with robust linear fitting
10370  */
10371 
10372  double q, m;
10373  cpl_bivector *list;
10374 
10375 
10376  wave = cpl_vector_wrap(npoints,
10377  cpl_table_get_data_double(dummy, "wave"));
10378  offs = cpl_vector_wrap(npoints,
10379  cpl_table_get_data_double(dummy, "offset"));
10380 
10381  list = cpl_bivector_wrap_vectors(wave, offs);
10382 
10383  /*
10384  * Set reference wavelength as zero point
10385  */
10386 
10387  cpl_vector_subtract_scalar(wave, refwave);
10388 
10389  robustLinearFit(list, &q, &m, &rms);
10390 
10391  rms = sqrt(rms * (uorder + 1) / npoints);
10392 
10393  cpl_bivector_unwrap_vectors(list);
10394  cpl_vector_unwrap(wave);
10395  cpl_vector_unwrap(offs);
10396  cpl_table_delete(dummy);
10397 
10398  /*
10399  * Now correct the coefficients of the corresponding IDS
10400  * polynomials related to this slit:
10401  */
10402 
10403  ylow = position[i];
10404  yhig = ylow + length[i];
10405 
10406  for (j = 0; j <= uorder; j++) {
10407  data = cpl_table_get_data_double(idscoeff, clab[j]);
10408  if (j)
10409  c = m;
10410  else
10411  c = q;
10412  for (k = ylow; k < yhig; k++)
10413  data[k] += c;
10414  }
10415 
10416  data = cpl_table_get_data_double(idscoeff, "error");
10417  for (k = ylow; k < yhig; k++)
10418  data[k] = sqrt(data[k]*data[k] + rms*rms);
10419 
10420  idata = cpl_table_get_data_int(idscoeff, "nlines");
10421  for (k = ylow; k < yhig; k++)
10422  idata[k] = npoints;
10423 
10424  /*
10425  * If a wavelengths map was provided, correct it to keep
10426  * into account the alignment to skylines:
10427  */
10428 
10429  if (calibration) {
10430  for (j = ylow; j < yhig; j++) {
10431  for (k = 1; k < nx; k++) {
10432  lambda1 = cdata[k - 1 + j*nx];
10433  lambda2 = cdata[k + j*nx];
10434  if (lambda1 < 1.0 || lambda2 < 1.0)
10435  continue;
10436  offset = q + m*(lambda1-refwave);
10437  cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10438  }
10439  }
10440  }
10441  }
10442  else {
10443 
10444  /*
10445  * Just compute median offset
10446  */
10447 
10448  offs = cpl_vector_wrap(npoints,
10449  cpl_table_get_data_double(dummy, "offset"));
10450 
10451  offset = cpl_vector_get_median_const(offs);
10452 
10453  if (npoints > 1)
10454  rms = cpl_table_get_column_stdev(dummy, "offset");
10455  else
10456  rms = 0.0;
10457 
10458  rms /= sqrt(npoints);
10459 
10460  cpl_vector_unwrap(offs);
10461  cpl_table_delete(dummy);
10462 
10463  /*
10464  * Now correct the constant term of the corresponding IDS
10465  * polynomials related to this slit:
10466  */
10467 
10468  ylow = position[i];
10469  yhig = ylow + length[i];
10470 
10471  data = cpl_table_get_data_double(idscoeff, clab[0]);
10472  for (k = ylow; k < yhig; k++)
10473  data[k] += offset;
10474 
10475  data = cpl_table_get_data_double(idscoeff, "error");
10476  for (k = ylow; k < yhig; k++)
10477  data[k] = sqrt(data[k]*data[k] + rms*rms);
10478 
10479  idata = cpl_table_get_data_int(idscoeff, "nlines");
10480  for (k = ylow; k < yhig; k++)
10481  idata[k] = npoints;
10482 
10483  /*
10484  * If a wavelengths map was provided, correct it to keep
10485  * into account the alignment to skylines. Note that
10486  * the offset must be converted from pixels to wavelengths.
10487  */
10488 
10489  if (calibration) {
10490  for (j = ylow; j < yhig; j++) {
10491  for (k = 1; k < nx; k++) {
10492  lambda1 = cdata[k - 1 + j*nx];
10493  lambda2 = cdata[k + j*nx];
10494  if (lambda1 < 1.0 || lambda2 < 1.0)
10495  continue;
10496  cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10497  }
10498  }
10499  }
10500  }
10501  }
10502 
10503  return offsets;
10504 
10505 }
10506 
10507 
10569 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave,
10570  double firstLambda, double lastLambda,
10571  cpl_table *idscoeff, cpl_vector *skylines,
10572  int highres, int order,
10573  cpl_image *calibration, int sradius)
10574 {
10575  const char *func = "mos_wavelength_align_lss";
10576 
10577  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10578  /* Max order is 5 */
10579  double *line;
10580  double *data;
10581  double *wdata;
10582  double *odata;
10583  double expPos, offset;
10584  double c;
10585  double lambda1, lambda2;
10586  double rms;
10587  float pos;
10588  float *sdata;
10589  float *cdata;
10590  int *idata;
10591  int startPos, endPos;
10592  int window = 2*sradius + 1;
10593  int nlines;
10594  int npoints;
10595  int nrows;
10596  int nx, ny;
10597  int idsorder, uorder;
10598  int missing;
10599  int i;
10600  cpl_size j, k;
10601 
10602  char name[MAX_COLNAME];
10603  char fname[MAX_COLNAME];
10604 
10605  cpl_polynomial *ids;
10606  cpl_polynomial *polycorr;
10607  cpl_table *offsets;
10608  cpl_table *fittable;
10609  cpl_table *dummy;
10610  cpl_vector *wave;
10611  cpl_vector *offs;
10612  cpl_vector *row;
10613 
10614 
10615  if (idscoeff == NULL) {
10616  cpl_msg_error(func, "An IDS coeff table must be given");
10617  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10618  return NULL;
10619  }
10620 
10621  if (image == NULL) {
10622  cpl_msg_error(func, "A scientific spectral image must be given");
10623  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10624  return NULL;
10625  }
10626 
10627  if (skylines) {
10628  line = cpl_vector_get_data(skylines);
10629  nlines = cpl_vector_get_size(skylines);
10630  }
10631  else {
10632  cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10633  "given: using internal list of reference sky lines");
10634  if (highres) {
10635  line = default_lines_hi;
10636  nlines = sizeof(default_lines_hi) / sizeof(double);
10637  }
10638  else {
10639  line = default_lines_lo;
10640  nlines = sizeof(default_lines_lo) / sizeof(double);
10641  }
10642  }
10643 
10644  if (calibration)
10645  cdata = cpl_image_get_data(calibration);
10646 
10647  nx = cpl_image_get_size_x(image);
10648  ny = cpl_image_get_size_y(image);
10649 
10650  sdata = cpl_image_get_data(image);
10651 
10652  if (ny != cpl_table_get_nrow(idscoeff)) {
10653  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10654  return NULL;
10655  }
10656 
10657 
10658  /*FIXME: This is a remnant of the adaptation of the function
10659  * mos_wavelength_align(), where an offset table was created.
10660  * I leave it here because I am in a hurry, it is just used to
10661  * hold the list of selected sky lines.
10662  *
10663  * Define table of wavelengths
10664  */
10665 
10666  nrows = 0;
10667  for (i = 0; i < nlines; i++)
10668  if (line[i] > firstLambda && line[i] < lastLambda)
10669  nrows++;
10670 
10671  offsets = cpl_table_new(nrows);
10672  cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10673  cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10674 
10675  nrows = 0;
10676  for (i = 0; i < nlines; i++) {
10677  if (line[i] > firstLambda && line[i] < lastLambda) {
10678  cpl_table_set_double(offsets, "wave", nrows, line[i]);
10679  nrows++;
10680  }
10681  }
10682 
10683  /*
10684  * Here "line" is made to point to the new list of selected wavelengths
10685  */
10686 
10687  line = cpl_table_get_data_double(offsets, "wave");
10688  nlines = nrows;
10689 
10690  idsorder = 0;
10691  while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10692  ++idsorder;
10693  --idsorder;
10694 
10695 
10696  /*
10697  * Allocate a dummy table for collecting all the offsets
10698  * for all the lines
10699  */
10700 
10701  dummy = cpl_table_new(ny);
10702  for (j = 0; j < nlines; j++) {
10703  snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10704  snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10705  cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10706  cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
10707  }
10708 
10709  for (j = 0; j < ny; j++, sdata += nx) {
10710 
10711  /*
10712  * Get the IDS polynomial for the current slit row
10713  */
10714 
10715  missing = 0;
10716  ids = cpl_polynomial_new(1);
10717  for (k = 0; k <= idsorder; k++) {
10718  c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
10719  if (missing) {
10720  cpl_polynomial_delete(ids);
10721  break;
10722  }
10723  cpl_polynomial_set_coeff(ids, &k, c);
10724  }
10725  if (missing)
10726  continue;
10727 
10728  for (k = 0; k < nlines; k++) {
10729  expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10730  startPos = expPos - sradius;
10731  endPos = startPos + window;
10732  if (startPos < 0 || endPos >= nx)
10733  continue;
10734 
10735  if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10736  pos += startPos;
10737  offset = pos - expPos;
10738  snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
10739  cpl_table_set_double(dummy, name, j, offset);
10740  }
10741  }
10742 
10743  cpl_polynomial_delete(ids);
10744  }
10745 
10746 
10747  /*
10748  * At this point for each sky line we model its offset along
10749  * the image rows using a robust linear fitting
10750  */
10751 
10752  for (j = 0; j < nlines; j++) {
10753  snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10754  snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10755  if (cpl_table_has_valid(dummy, name)) {
10756 
10757  /*
10758  * In the following, the "fittable" is just a tool for
10759  * eliminating invalid points from the vectors to be fitted.
10760  */
10761 
10762  double q, m;
10763  cpl_bivector *list;
10764 
10765  fittable = cpl_table_new(ny);
10766  cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
10767  cpl_table_set_column_unit(fittable, "row", "pixel");
10768  for (k = 0; k < ny; k++)
10769  cpl_table_set_double(fittable, "row", k, k);
10770  cpl_table_duplicate_column(fittable, "offset", dummy, name);
10771  npoints = ny - cpl_table_count_invalid(fittable, "offset");
10772  cpl_table_erase_invalid(fittable);
10773  row = cpl_vector_wrap(npoints,
10774  cpl_table_get_data_double(fittable, "row"));
10775  offs = cpl_vector_wrap(npoints,
10776  cpl_table_get_data_double(fittable, "offset"));
10777  list = cpl_bivector_wrap_vectors(row, offs);
10778  robustLinearFit(list, &q, &m, &rms);
10779  cpl_bivector_unwrap_vectors(list);
10780  cpl_vector_unwrap(row);
10781  cpl_vector_unwrap(offs);
10782  cpl_table_delete(fittable);
10783  for (k = 0; k < ny; k++)
10784  cpl_table_set_double(dummy, fname, k, q + m*k);
10785  }
10786  }
10787 
10788 
10789  /*
10790  * Now each dummy table row consists of a sequence of offsets,
10791  * one for each wavelength. A table row corresponds to an image row.
10792  * We must fit a polynomial to each one of these rows, in order to
10793  * express the offsets as a function of wavelength. The obtained
10794  * polynomial coefficients are used to correct the IDS coefficients.
10795  */
10796 
10797  for (i = 0; i < ny; i++) {
10798 
10799  if (!cpl_table_is_valid(idscoeff, clab[0], i))
10800  continue;
10801 
10802  npoints = 0;
10803  for (j = 0; j < nlines; j++) {
10804  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10805  if (cpl_table_is_valid(dummy, name, i))
10806  npoints++;
10807  }
10808 
10809  if (npoints == 0)
10810  continue;
10811 
10812  uorder = order;
10813  if (npoints <= uorder)
10814  uorder = npoints - 1;
10815 
10816  if (uorder > 1) {
10817 
10818  /*
10819  * Model offsets with polynomial fitting
10820  */
10821 
10822  wave = cpl_vector_new(npoints);
10823  wdata = cpl_vector_get_data(wave);
10824  offs = cpl_vector_new(npoints);
10825  odata = cpl_vector_get_data(offs);
10826 
10827  npoints = 0;
10828  for (j = 0; j < nlines; j++) {
10829  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10830  if (cpl_table_is_valid(dummy, name, i)) {
10831  wdata[npoints] = line[j] - refwave;
10832  odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10833  npoints++;
10834  }
10835  }
10836 
10837  polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10838 
10839  rms = sqrt(rms * (uorder + 1) / npoints);
10840 
10841  cpl_vector_delete(wave);
10842  cpl_vector_delete(offs);
10843 
10844  /*
10845  * Now correct the coefficients of the corresponding IDS
10846  * polynomials related to this slit:
10847  */
10848 
10849  for (j = 0; j <= uorder; j++) {
10850  data = cpl_table_get_data_double(idscoeff, clab[j]);
10851  c = cpl_polynomial_get_coeff(polycorr, &j);
10852  data[i] += c;
10853  }
10854 
10855  data = cpl_table_get_data_double(idscoeff, "error");
10856  data[i] = sqrt(data[i]*data[i] + rms*rms);
10857 
10858  idata = cpl_table_get_data_int(idscoeff, "nlines");
10859  idata[i] = npoints;
10860 
10861  /*
10862  * If a wavelengths map was provided, correct it to keep
10863  * into account the alignment to skylines:
10864  */
10865 
10866  if (calibration) {
10867  for (k = 1; k < nx; k++) {
10868  lambda1 = cdata[k - 1 + i*nx];
10869  lambda2 = cdata[k + i*nx];
10870  if (lambda1 < 1.0 || lambda2 < 1.0)
10871  continue;
10872  offset = cpl_polynomial_eval_1d(polycorr,
10873  lambda1-refwave, NULL);
10874  cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10875  }
10876  }
10877 
10878  cpl_polynomial_delete(polycorr);
10879 
10880  }
10881  else if (uorder == 1) {
10882 
10883  /*
10884  * Model offsets with robust linear fitting
10885  */
10886 
10887  cpl_bivector *list;
10888  double q, m;
10889 
10890  wave = cpl_vector_new(npoints);
10891  wdata = cpl_vector_get_data(wave);
10892  offs = cpl_vector_new(npoints);
10893  odata = cpl_vector_get_data(offs);
10894 
10895  npoints = 0;
10896  for (j = 0; j < nlines; j++) {
10897  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10898  if (cpl_table_is_valid(dummy, name, i)) {
10899  wdata[npoints] = line[j] - refwave;
10900  odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10901  npoints++;
10902  }
10903  }
10904 
10905  list = cpl_bivector_wrap_vectors(wave, offs);
10906  robustLinearFit(list, &q, &m, &rms);
10907 
10908  rms = sqrt(rms * (uorder + 1) / npoints);
10909 
10910  cpl_bivector_unwrap_vectors(list);
10911  cpl_vector_delete(wave);
10912  cpl_vector_delete(offs);
10913 
10914  /*
10915  * Now correct the coefficients of the corresponding IDS
10916  * polynomials related to this row:
10917  */
10918 
10919  for (j = 0; j <= uorder; j++) {
10920  data = cpl_table_get_data_double(idscoeff, clab[j]);
10921  if (j)
10922  c = m;
10923  else
10924  c = q;
10925  data[i] += c;
10926  }
10927 
10928  data = cpl_table_get_data_double(idscoeff, "error");
10929  data[i] = sqrt(data[i]*data[i] + rms*rms);
10930 
10931  idata = cpl_table_get_data_int(idscoeff, "nlines");
10932  idata[i] = npoints;
10933 
10934  /*
10935  * If a wavelengths map was provided, correct it to keep
10936  * into account the alignment to skylines:
10937  */
10938 
10939  if (calibration) {
10940  for (k = 1; k < nx; k++) {
10941  lambda1 = cdata[k - 1 + i*nx];
10942  lambda2 = cdata[k + i*nx];
10943  if (lambda1 < 1.0 || lambda2 < 1.0)
10944  continue;
10945  offset = q + m*(lambda1-refwave);
10946  cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10947  }
10948  }
10949  }
10950  else {
10951 
10952  /*
10953  * Just compute median offset
10954  */
10955 
10956  offs = cpl_vector_new(npoints);
10957  odata = cpl_vector_get_data(offs);
10958 
10959  npoints = 0;
10960  for (j = 0; j < nlines; j++) {
10961  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10962  if (cpl_table_is_valid(dummy, name, i)) {
10963  odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10964  npoints++;
10965  }
10966  }
10967 
10968  offset = cpl_vector_get_median_const(offs);
10969 
10970  if (npoints > 1) {
10971  rms = cpl_vector_get_stdev(offs);
10972  }
10973  else if (npoints == 1) {
10974  snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
10975  if (cpl_table_has_valid(dummy, name)) {
10976  rms = cpl_table_get_column_stdev(dummy, name);
10977  rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
10978  }
10979  else {
10980  rms = 0.0;
10981  }
10982  }
10983  else {
10984  rms = 0.0;
10985  }
10986 
10987  rms /= sqrt(npoints);
10988 
10989  cpl_vector_delete(offs);
10990 
10991  /*
10992  * Now correct the constant term of the corresponding IDS
10993  * polynomials related to this slit:
10994  */
10995 
10996  data = cpl_table_get_data_double(idscoeff, clab[0]);
10997  data[i] += offset;
10998 
10999  data = cpl_table_get_data_double(idscoeff, "error");
11000  data[i] = sqrt(data[i]*data[i] + rms*rms);
11001 
11002  idata = cpl_table_get_data_int(idscoeff, "nlines");
11003  idata[i] = npoints;
11004 
11005  /*
11006  * If a wavelengths map was provided, correct it to keep
11007  * into account the alignment to skylines. Note that
11008  * the offset must be converted from pixels to wavelengths.
11009  */
11010 
11011  if (calibration) {
11012  for (k = 1; k < nx; k++) {
11013  lambda1 = cdata[k - 1 + i*nx];
11014  lambda2 = cdata[k + i*nx];
11015  if (lambda1 < 1.0 || lambda2 < 1.0)
11016  continue;
11017  cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
11018  }
11019  }
11020  }
11021  }
11022 
11023  missing = 1;
11024  for (j = 0; j < nlines; j++) {
11025  snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
11026  if (cpl_table_has_valid(dummy, name)) {
11027  missing = 0;
11028  offset = cpl_table_get_column_median(dummy, name);
11029  cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
11030  line[j], offset);
11031  }
11032  else {
11033  cpl_msg_info(func,
11034  "Median offset for %.2f: not available", line[j]);
11035  }
11036  }
11037 
11038  cpl_table_delete(offsets);
11039 
11040  if (missing) {
11041  cpl_table_delete(dummy);
11042  dummy = NULL;
11043  }
11044 
11045  return dummy;
11046 
11047 }
11048 
11049 
11077 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines,
11078  double wavestart, double dispersion, int radius,
11079  int highres)
11080 {
11081 
11082  const char *func = "mos_distortions_rms";
11083 
11084  int xlen;
11085  int ylen;
11086  int numLines;
11087  int cpix, npix, nzero;
11088  int sp, ep;
11089  int i, j, k;
11090  int npeaks, allPeaks;
11091 
11092  float *profile;
11093  float peak, expectPeak, offset;
11094  double lambda;
11095 
11096  double average;
11097  double rms, oneRms;
11098 
11099  float *sdata;
11100  double *wdata;
11101 
11102 
11103  xlen = cpl_image_get_size_x(rectified);
11104  ylen = cpl_image_get_size_y(rectified);
11105  sdata = cpl_image_get_data(rectified);
11106 
11107  if (lines) {
11108  wdata = cpl_vector_get_data(lines);
11109  numLines = cpl_vector_get_size(lines);
11110  }
11111  else {
11112  cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
11113  "given: using internal list of reference sky lines");
11114  if (highres) {
11115  wdata = default_lines_hi;
11116  numLines = sizeof(default_lines_hi) / sizeof(double);
11117  }
11118  else {
11119  wdata = default_lines_lo;
11120  numLines = sizeof(default_lines_lo) / sizeof(double);
11121  }
11122  }
11123 
11124  npix = 2 * radius + 1;
11125  profile = cpl_calloc(npix, sizeof(float));
11126 
11127  rms = 0.0;
11128  allPeaks = 0;
11129 
11130  for (i = 0; i < numLines; i++) {
11131 
11132  /*
11133  * Expected peak and closest pixel to specified wavelength.
11134  */
11135 
11136  lambda = wdata[i];
11137  expectPeak = (lambda - wavestart) / dispersion;
11138  cpix = floor(expectPeak + 0.5);
11139 
11140  /*
11141  * Search interval for peak. Abort if too close to image border.
11142  */
11143 
11144  sp = cpix - radius;
11145  ep = cpix + radius;
11146 
11147  if (sp < 0 || ep > xlen)
11148  continue;
11149 
11150  average = 0.0;
11151  npeaks = 0;
11152  oneRms = 0.0;
11153 
11154  for (j = 0; j < ylen; j++) { /* For each row of each slit */
11155  nzero = 0;
11156  for (k = 0; k < npix; k++) {
11157  profile[k] = sdata[sp + k + j * xlen];
11158  if (fabs(profile[k]) < 0.0001)
11159  nzero++; /* Count number of 0 pixels (spectrum truncated) */
11160  }
11161  if (nzero > 0)
11162  continue;
11163 
11164  if (peakPosition(profile, npix, &peak, 1) == 0) {
11165  offset = (sp + peak) - expectPeak;
11166  average += offset;
11167  rms += fabs(offset);
11168  oneRms += fabs(offset);
11169  npeaks++;
11170  allPeaks++;
11171  }
11172  }
11173 
11174  if (npeaks)
11175  cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
11176  lambda, oneRms / npeaks * 1.25, npeaks);
11177  else
11178  cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
11179  }
11180 
11181  cpl_free(profile);
11182 
11183  if (allPeaks < 10)
11184  return 0.0;
11185 
11186  rms /= allPeaks;
11187  rms *= 1.25; /* Factor to convert average deviation to sigma */
11188 
11189  return rms;
11190 
11191 }
11192 
11193 
11214 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
11215  double blue, double red, double dispersion, int trend)
11216 {
11217  const char *func = "mos_map_pixel";
11218 
11219  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11220  /* Max order is 5 */
11221 
11222  cpl_polynomial *ids;
11223  cpl_image *map;
11224  float *mdata;
11225  double lambda;
11226  double c;
11227  int order;
11228  int xsize, ysize;
11229  int missing;
11230  int i, j;
11231  cpl_size k;
11232 
11233 
11234  if (idscoeff == NULL) {
11235  cpl_msg_error(func, "An IDS coeff table must be given");
11236  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11237  return NULL;
11238  }
11239 
11240  xsize = (red - blue) / dispersion;
11241  ysize = cpl_table_get_nrow(idscoeff);
11242  map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
11243  mdata = cpl_image_get_data(map);
11244 
11245  order = 0;
11246  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
11247  ++order;
11248  --order;
11249 
11250  for (i = 0; i < ysize; i++, mdata += xsize) {
11251 
11252  missing = 0;
11253  ids = cpl_polynomial_new(1);
11254  for (k = trend; k <= order; k++) {
11255  c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
11256  if (missing) {
11257  cpl_polynomial_delete(ids);
11258  break;
11259  }
11260  cpl_polynomial_set_coeff(ids, &k, c);
11261  }
11262  if (missing)
11263  continue;
11264 
11265  for (j = 0; j < xsize; j++) {
11266  lambda = blue + j*dispersion;
11267  mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
11268  }
11269 
11270  cpl_polynomial_delete(ids);
11271  }
11272 
11273  return map;
11274 
11275 }
11276 
11277 
11299 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
11300  double blue, double red)
11301 {
11302  const char *func = "mos_map_idscoeff";
11303 
11304  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11305  /* Max order is 5 */
11306 
11307  cpl_polynomial *ids;
11308  cpl_image *map;
11309  float *mdata;
11310  double lambda;
11311  double c;
11312  int order;
11313  int ysize;
11314  int missing;
11315  int i, j;
11316  cpl_size k;
11317 
11318 
11319  if (idscoeff == NULL) {
11320  cpl_msg_error(func, "An IDS coeff table must be given");
11321  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11322  return NULL;
11323  }
11324 
11325  if (xsize < 1) {
11326  cpl_msg_error(func, "Invalid image size");
11327  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11328  return NULL;
11329  }
11330 
11331  if (xsize < 20 || xsize > 5000) {
11332  cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
11333  xsize);
11334  }
11335 
11336  ysize = cpl_table_get_nrow(idscoeff);
11337  map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
11338  mdata = cpl_image_get_data(map);
11339 
11340  order = 0;
11341  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
11342  ++order;
11343  --order;
11344 
11345  for (i = 0; i < ysize; i++, mdata += xsize) {
11346 
11347  missing = 0;
11348  ids = cpl_polynomial_new(1);
11349  for (k = 0; k <= order; k++) {
11350  c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
11351  if (missing) {
11352  cpl_polynomial_delete(ids);
11353  break;
11354  }
11355  cpl_polynomial_set_coeff(ids, &k, c);
11356  }
11357  if (missing)
11358  continue;
11359 
11360  for (j = 0; j < xsize; j++) {
11361  lambda = mos_eval_dds(ids, blue, red, reference, j);
11362 
11363  if (lambda >= blue && lambda <= red) {
11364  mdata[j] = lambda;
11365  }
11366  }
11367 
11368  cpl_polynomial_delete(ids);
11369  }
11370 
11371  return map;
11372 
11373 }
11374 
11375 
11410 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
11411  cpl_table *slits, cpl_table *polytraces,
11412  double reference, double blue, double red,
11413  double dispersion)
11414 {
11415  const char *func = "mos_map_wavelengths";
11416 
11417  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11418  /* Max order is 5 */
11419  cpl_polynomial *polytop;
11420  cpl_polynomial *polybot;
11421  cpl_image *remapped;
11422  float *data;
11423  float *wdata;
11424  float *sdata;
11425  float *xdata;
11426  double vtop, vbot, value;
11427  double top, bot;
11428  double coeff;
11429  double ytop, ybot;
11430  double ypos;
11431  double fvalue;
11432  int ivalue;
11433  int yint, ysize, yprev;
11434  int nslits;
11435  int npseudo;
11436  int *slit_id;
11437  int *position;
11438  int *length;
11439  int nx, ny;
11440  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11441  int missing_top, missing_bot;
11442  int null;
11443  int order;
11444  int i, j;
11445  cpl_size k;
11446 
11447 
11448  if (spatial == NULL || calibration == NULL ||
11449  slits == NULL || polytraces == NULL) {
11450  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11451  return NULL;
11452  }
11453 
11454  if (dispersion <= 0.0) {
11455  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11456  return NULL;
11457  }
11458 
11459  if (red - blue < dispersion) {
11460  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11461  return NULL;
11462  }
11463 
11464  nx = cpl_image_get_size_x(spatial);
11465  ny = cpl_image_get_size_y(spatial);
11466  ysize = cpl_image_get_size_y(calibration);
11467  remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
11468  data = cpl_image_get_data(remapped);
11469  sdata = cpl_image_get_data(spatial);
11470  wdata = cpl_image_get_data(calibration);
11471 
11472  nslits = cpl_table_get_nrow(slits);
11473  slit_id = cpl_table_get_data_int(slits, "slit_id");
11474  order = cpl_table_get_ncol(polytraces) - 2;
11475  position = cpl_table_get_data_int(slits, "position");
11476  length = cpl_table_get_data_int(slits, "length");
11477 
11478  /*
11479  * The spatial resampling is performed for a certain number of
11480  * pixels above and below the position of the reference wavelength:
11481  */
11482 
11483  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11484  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11485 
11486  for (i = 0; i < nslits; i++) {
11487 
11488  if (length[i] == 0)
11489  continue;
11490 
11491  /*
11492  * Note that the x coordinate of the reference pixels on the CCD
11493  * is taken arbitrarily at the top end of each slit. This wouldn't
11494  * be entirely correct in case of curved slits, or in presence of
11495  * heavy distortions: in such cases the spatial resampling is
11496  * really performed across a wide range of wavelengths. But
11497  * the lag between top and bottom spectral curvature models
11498  * would introduce even in such cases negligible effects on
11499  * the spectral spatial resampling.
11500  */
11501 
11502  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11503 
11504  start_pixel = refpixel - pixel_below;
11505  if (start_pixel < 0)
11506  start_pixel = 0;
11507 
11508  end_pixel = refpixel + pixel_above;
11509  if (end_pixel > nx)
11510  end_pixel = nx;
11511 
11512  /*
11513  * Recover from the table of spectral curvature coefficients
11514  * the curvature polynomials.
11515  */
11516 
11517  missing_top = 0;
11518  polytop = cpl_polynomial_new(1);
11519  for (k = 0; k <= order; k++) {
11520  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11521  if (null) {
11522  cpl_polynomial_delete(polytop);
11523  missing_top = 1;
11524  break;
11525  }
11526  cpl_polynomial_set_coeff(polytop, &k, coeff);
11527  }
11528 
11529  missing_bot = 0;
11530  polybot = cpl_polynomial_new(1);
11531  for (k = 0; k <= order; k++) {
11532  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11533  if (null) {
11534  cpl_polynomial_delete(polybot);
11535  missing_bot = 1;
11536  break;
11537  }
11538  cpl_polynomial_set_coeff(polybot, &k, coeff);
11539  }
11540 
11541  if (missing_top && missing_bot) {
11542  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11543  slit_id[i]);
11544  continue;
11545  }
11546 
11547  /*
11548  * In case just one of the two edges was not traced, the other
11549  * edge curvature model is duplicated and shifted to the other
11550  * end of the slit: better than nothing!
11551  */
11552 
11553  if (missing_top) {
11554  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11555  "the spectral curvature of the lower edge "
11556  "is used instead.", slit_id[i]);
11557  polytop = cpl_polynomial_duplicate(polybot);
11558  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11559  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11560  k = 0;
11561  coeff = cpl_polynomial_get_coeff(polybot, &k);
11562  coeff += ytop - ybot;
11563  cpl_polynomial_set_coeff(polytop, &k, coeff);
11564  }
11565 
11566  if (missing_bot) {
11567  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11568  "the spectral curvature of the upper edge "
11569  "is used instead.", slit_id[i]);
11570  polybot = cpl_polynomial_duplicate(polytop);
11571  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11572  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11573  k = 0;
11574  coeff = cpl_polynomial_get_coeff(polytop, &k);
11575  coeff -= ytop - ybot;
11576  cpl_polynomial_set_coeff(polybot, &k, coeff);
11577  }
11578 
11579  /*
11580  * Point to current slit on wavelength calibration image.
11581  * Note that the npseudo value related to this slit is equal
11582  * to the number of spatial pseudo-pixels decreased by 1
11583  * (compare with function mos_spatial_calibration()).
11584  */
11585 
11586  xdata = wdata + nx*position[i];
11587  npseudo = length[i] - 1;
11588 
11589  /*
11590  * Write interpolated wavelengths to CCD image
11591  */
11592 
11593  for (j = start_pixel; j < end_pixel; j++) {
11594  top = cpl_polynomial_eval_1d(polytop, j, NULL);
11595  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
11596  for (k = 0; k <= npseudo; k++) {
11597  ypos = top - k*(top-bot)/npseudo;
11598  yint = ypos;
11599 
11600  /*
11601  * The line:
11602  * value = sdata[j + nx*yint];
11603  * should be equivalent to:
11604  * value = npseudo*(top-yint)/(top-bot);
11605  */
11606 
11607  if (yint < 0 || yint >= ny-1) {
11608  yprev = yint;
11609  continue;
11610  }
11611 
11612  value = sdata[j + nx*yint]; /* Spatial coordinate on CCD */
11613  ivalue = value; /* Nearest spatial pixels: */
11614  fvalue = value - ivalue; /* ivalue and ivalue+1 */
11615  if (ivalue < npseudo && ivalue >= 0) {
11616  vtop = xdata[j + nx*(npseudo-ivalue)];
11617  vbot = xdata[j + nx*(npseudo-ivalue-1)];
11618  if (vtop < 1.0) { /* Impossible wavelength */
11619  if (vbot < 1.0) {
11620  value = 0.0;
11621  }
11622  else {
11623  value = vbot;
11624  }
11625  }
11626  else if (vbot < 1.0) {
11627  if (k)
11628  value = vtop;
11629  else
11630  value = 0.0;
11631  }
11632  else if (fabs(vbot-vtop) > 10*dispersion) {
11633  value = 0.0;
11634  }
11635  else {
11636  value = vtop*(1-fvalue) + vbot*fvalue;
11637  }
11638  data[j + nx*yint] = value;
11639 
11640  if (k) {
11641 
11642  /*
11643  * This is added to recover lost pixels on
11644  * the CCD image (pixels are lost because
11645  * the CCD pixels are less than npseudo+1).
11646  */
11647 
11648  if (yprev - yint > 1) {
11649  value = sdata[j + nx*(yint+1)];
11650  ivalue = value;
11651  fvalue = value - ivalue;
11652  if (ivalue < npseudo && ivalue >= 0) {
11653  vtop = xdata[j + nx*(npseudo-ivalue)];
11654  vbot = xdata[j + nx*(npseudo-ivalue-1)];
11655  if (vtop < 1.0) {
11656  if (vbot < 1.0) {
11657  value = data[j + nx*(yint+1)];
11658  }
11659  else {
11660  value = vbot;
11661  }
11662  }
11663  else if (vbot < 1.0) {
11664  value = vtop;
11665  }
11666  else if (fabs(vbot-vtop) > 2*dispersion) {
11667  value = vtop;
11668  }
11669  else {
11670  value = vtop*(1-fvalue) + vbot*fvalue;
11671  }
11672  data[j + nx*(yint+1)] = value;
11673  }
11674  }
11675  }
11676  }
11677  yprev = yint;
11678  }
11679  }
11680  cpl_polynomial_delete(polytop);
11681  cpl_polynomial_delete(polybot);
11682  }
11683 
11684  return remapped;
11685 }
11686 
11760 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib,
11761  cpl_image *spatial, cpl_table *slits,
11762  cpl_table *polytraces, double reference,
11763  double blue, double red, double dispersion,
11764  int flux)
11765 {
11766  const char *func = "mos_map_spectrum";
11767 
11768  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11769  /* Max order is 5 */
11770  cpl_polynomial *polytop;
11771  cpl_polynomial *polybot;
11772  cpl_image *remapped;
11773  cpl_image **exslit;
11774  float *data;
11775  float *wdata;
11776  float *sdata;
11777  float *xdata;
11778  double lambda00, lambda01, lambda10, lambda11, lambda;
11779  double space00, space01, space10, space11, space;
11780  double value00, value01, value10, value11, value0, value1, value;
11781  double dL, dS;
11782  double top, bot;
11783  double coeff;
11784  double ytop, ybot;
11785  double xfrac, yfrac;
11786  int yint, ysize;
11787  int itop, ibot;
11788  int shift;
11789  int L, S;
11790  int nslits;
11791  int npseudo;
11792  int *slit_id;
11793  int *position;
11794  int *length;
11795  int nx, ny;
11796  int x, y;
11797  int nlambda;
11798  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11799  int missing_top, missing_bot;
11800  int null;
11801  int order;
11802  int i;
11803  cpl_size k;
11804 
11805 
11806  flux += flux;
11807 
11808  if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
11809  slits == NULL || polytraces == NULL) {
11810  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11811  return NULL;
11812  }
11813 
11814  if (dispersion <= 0.0) {
11815  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11816  return NULL;
11817  }
11818 
11819  if (red - blue < dispersion) {
11820  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11821  return NULL;
11822  }
11823 
11824  nx = cpl_image_get_size_x(spectra);
11825  ny = cpl_image_get_size_y(spectra);
11826 
11827  if (nx != cpl_image_get_size_x(spatial) ||
11828  ny != cpl_image_get_size_y(spatial) ||
11829  nx != cpl_image_get_size_x(wavecalib) ||
11830  ny != cpl_image_get_size_y(wavecalib)) {
11831  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11832  return NULL;
11833  }
11834 
11835  nlambda = STRETCH_FACTOR * (red - blue) / dispersion;
11836  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11837  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11838 
11839  data = cpl_image_get_data(spectra);
11840  sdata = cpl_image_get_data(spatial);
11841  wdata = cpl_image_get_data(wavecalib);
11842 
11843  nslits = cpl_table_get_nrow(slits);
11844  slit_id = cpl_table_get_data_int(slits, "slit_id");
11845  order = cpl_table_get_ncol(polytraces) - 2;
11846  position = cpl_table_get_data_int(slits, "position");
11847  length = cpl_table_get_data_int(slits, "length");
11848 
11849  exslit = cpl_calloc(nslits, sizeof(cpl_image *));
11850 
11851  for (i = 0; i < nslits; i++) {
11852 
11853  if (length == 0)
11854  continue;
11855 
11856  /*
11857  * Note that the x coordinate of the reference pixels on the CCD
11858  * is taken arbitrarily at the top end of each slit. This wouldn't
11859  * be entirely correct in case of curved slits, or in presence of
11860  * heavy distortions: in such cases the spatial resampling is
11861  * really performed across a wide range of wavelengths. But
11862  * the lag between top and bottom spectral curvature models
11863  * would introduce even in such cases negligible effects on
11864  * the spectral spatial resampling.
11865  */
11866 
11867  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11868 
11869  start_pixel = refpixel - pixel_below;
11870  if (start_pixel < 1)
11871  start_pixel = 1;
11872 
11873  end_pixel = refpixel + pixel_above;
11874  if (end_pixel > nx)
11875  end_pixel = nx;
11876 
11877  /*
11878  * Recover from the table of spectral curvature coefficients
11879  * the curvature polynomials.
11880  */
11881 
11882  missing_top = 0;
11883  polytop = cpl_polynomial_new(1);
11884  for (k = 0; k <= order; k++) {
11885  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11886  if (null) {
11887  cpl_polynomial_delete(polytop);
11888  missing_top = 1;
11889  break;
11890  }
11891  cpl_polynomial_set_coeff(polytop, &k, coeff);
11892  }
11893 
11894  missing_bot = 0;
11895  polybot = cpl_polynomial_new(1);
11896  for (k = 0; k <= order; k++) {
11897  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11898  if (null) {
11899  cpl_polynomial_delete(polybot);
11900  missing_bot = 1;
11901  break;
11902  }
11903  cpl_polynomial_set_coeff(polybot, &k, coeff);
11904  }
11905 
11906  if (missing_top && missing_bot) {
11907  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11908  slit_id[i]);
11909  continue;
11910  }
11911 
11912  /*
11913  * In case just one of the two edges was not traced, the other
11914  * edge curvature model is duplicated and shifted to the other
11915  * end of the slit: better than nothing!
11916  */
11917 
11918  if (missing_top) {
11919  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11920  "the spectral curvature of the lower edge "
11921  "is used instead.", slit_id[i]);
11922  polytop = cpl_polynomial_duplicate(polybot);
11923  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11924  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11925  k = 0;
11926  coeff = cpl_polynomial_get_coeff(polybot, &k);
11927  coeff += ytop - ybot;
11928  cpl_polynomial_set_coeff(polytop, &k, coeff);
11929  }
11930 
11931  if (missing_bot) {
11932  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11933  "the spectral curvature of the upper edge "
11934  "is used instead.", slit_id[i]);
11935  polybot = cpl_polynomial_duplicate(polytop);
11936  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11937  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11938  k = 0;
11939  coeff = cpl_polynomial_get_coeff(polytop, &k);
11940  coeff -= ytop - ybot;
11941  cpl_polynomial_set_coeff(polybot, &k, coeff);
11942  }
11943 
11944  /*
11945  * Allocate image for current extracted slit
11946  */
11947 
11948  top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
11949  bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
11950  npseudo = ceil(top-bot) + 1;
11951 
11952  if (npseudo < 1) {
11953  cpl_polynomial_delete(polytop);
11954  cpl_polynomial_delete(polybot);
11955  cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
11956  slit_id[i]);
11957  continue;
11958  }
11959 
11960  exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
11961  xdata = cpl_image_get_data(exslit[i]);
11962 
11963  /*
11964  * Write interpolated spectral values to remapped slit spectrum.
11965  */
11966 
11967  for (x = start_pixel; x < end_pixel; x++) {
11968  top = cpl_polynomial_eval_1d(polytop, x, NULL);
11969  bot = cpl_polynomial_eval_1d(polybot, x, NULL);
11970  itop = top + 1;
11971  ibot = bot;
11972  if (itop < 0)
11973  itop = 0;
11974  if (itop > ny - 1)
11975  itop = ny - 1;
11976  if (ibot < 0)
11977  ibot = 0;
11978  if (ibot > ny - 1)
11979  ibot = ny - 1;
11980  for (y = ibot; y < itop; y++) {
11981  lambda11 = wdata[x + y*nx];
11982  if (lambda11 < 1.0) /* Impossible wavelength */
11983  continue;
11984  space11 = sdata[x + y*nx];
11985  if (space11 < 0.0) /* Impossible spatial coordinate */
11986  continue;
11987  lambda01 = wdata[x - 1 + y*nx];
11988  if (lambda01 < 1.0) /* Impossible wavelength */
11989  continue;
11990  space01 = sdata[x - 1 + y*nx];
11991  if (space01 < 0.0) /* Impossible spatial coordinate */
11992  continue;
11993 
11994  shift = 0;
11995 
11996 /****+
11997  if (wdata[x + (y+1)*nx] > 1.0) {
11998  if (wdata[x + (y+1)*nx] - lambda11 > 0) {
11999  shift = -1;
12000  while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
12001  shift--;
12002  if (lambda11 - wdata[x + shift + (y+1)*nx] >
12003  wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
12004  shift++;
12005  }
12006  }
12007  else {
12008  shift = 1;
12009  while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
12010  shift++;
12011  if (wdata[x + shift + (y+1)*nx] - lambda11 >
12012  lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
12013  shift--;
12014  }
12015  }
12016  }
12017 ****/
12018 
12019 /****
12020 printf("y = %d, shift = %d\n", y, shift);
12021 ****/
12022 
12023  lambda10 = wdata[x + shift + (y+1)*nx];
12024  if (lambda10 < 1.0) /* Impossible wavelength */
12025  continue;
12026  space10 = sdata[x + shift + (y+1)*nx];
12027  if (space10 < 0.0) /* Impossible spatial coordinate */
12028  continue;
12029  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
12030  if (lambda00 < 1.0) /* Impossible wavelength */
12031  continue;
12032  space00 = sdata[x - 1 + shift + (y+1)*nx];
12033  if (space00 < 0.0) /* Impossible spatial coordinate */
12034  continue;
12035 
12036  /*
12037  * Find the variation in lambda and space in this
12038  * position for each CCD pixel (both quantities are
12039  * expected to be positive).
12040  */
12041 
12042  dL = lambda11 - lambda01;
12043  dS = space11 - space10;
12044 
12045  /*
12046  * Find the position (L,S) of the output pixel
12047  * (by integer truncation).
12048  */
12049 
12050  L = (lambda11 - blue)/dispersion + 0.5;
12051  S = space11 + 0.5; /* Counted from top! */
12052 
12053  if (L < 0 || L >= nlambda)
12054  continue;
12055  if (S < 0 || S > npseudo)
12056  continue;
12057 
12058  /*
12059  * Find the coordinate of pixel (L,S)
12060  */
12061 
12062  lambda = blue + L*dispersion;
12063  space = S;
12064 
12065  /*
12066  * Find the interpolation point on the CCD: it is
12067  * defined as the (positive) distance from current
12068  * CCD pixel (x,y) of the interpolation point (x',y'),
12069  * measured in CCD pixels. The interpolation point
12070  * is located between the four CCD pixels selected
12071  * above.
12072  */
12073 
12074  xfrac = (lambda11-lambda)/dL;
12075  yfrac = (space11-space)/dS;
12076 
12077 /*
12078 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
12079 printf("xyfrac = %f, %f\n", xfrac, yfrac);
12080 */
12081 
12082  /*
12083  * Get the four values to interpolate
12084  */
12085 
12086  value11 = data[x + y*nx];
12087  value01 = data[x - 1 + y*nx];
12088  value10 = data[x + shift + (y+1)*nx];
12089  value00 = data[x + shift - 1 + (y+1)*nx];
12090 
12091  /*
12092  * Interpolation
12093  */
12094 
12095  value1 = (1-xfrac)*value11 + xfrac*value01;
12096  value0 = (1-xfrac)*value10 + xfrac*value00;
12097  value = (1-yfrac)*value1 + yfrac*value0;
12098 
12099  /*
12100  * Write this value to the appropriate (L,S) coordinate
12101  * on output slit
12102  */
12103 
12104  xdata[L + nlambda*(npseudo-S)] = value;
12105 
12106  }
12107  }
12108  cpl_polynomial_delete(polytop);
12109  cpl_polynomial_delete(polybot);
12110  }
12111 
12112  /*
12113  * Now all the slits images are copied to a single image
12114  */
12115 
12116  ysize = 0;
12117  for (i = 0; i < nslits; i++)
12118  if (exslit[i])
12119  ysize += cpl_image_get_size_y(exslit[i]);
12120 
12121  remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
12122 
12123  yint = -1;
12124  for (i = 0; i < nslits; i++) {
12125  if (exslit[i]) {
12126  yint += cpl_image_get_size_y(exslit[i]);
12127  cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
12128  cpl_image_delete(exslit[i]);
12129  cpl_table_set_int(slits, "position", i, ysize - yint - 1);
12130  }
12131  }
12132 
12133  cpl_free(exslit);
12134 
12135  return remapped;
12136 
12137 }
12138 
12139 
12172 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
12173  double dispersion, double factor, int minpoints,
12174  cpl_image *skymap)
12175 {
12176  const char *func = "mos_sky_map_super";
12177 
12178  cpl_vector **vector;
12179  cpl_vector **wvector;
12180  double firstLambda, lastLambda;
12181  double lambda, lambda1, lambda2;
12182  double value, value1, value2;
12183  double frac;
12184  float min, max;
12185  int *count;
12186  int nbin, bin;
12187  int nx, ny, npix;
12188  int first_valid, valid_bins;
12189  int i, j;
12190 
12191  cpl_table *sky;
12192  double *sky_spectrum;
12193  double *sky_wave;
12194  float *data;
12195  float *sdata;
12196  float *kdata;
12197 
12198 
12199  if (spectra == NULL || wavemap == NULL || skymap == NULL) {
12200  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12201  return NULL;
12202  }
12203 
12204  if (dispersion <= 0.0) {
12205  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12206  cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
12207  return NULL;
12208  }
12209 
12210  nx = cpl_image_get_size_x(spectra);
12211  ny = cpl_image_get_size_y(spectra);
12212  npix = nx * ny;
12213 
12214  if (nx != cpl_image_get_size_x(wavemap) ||
12215  ny != cpl_image_get_size_y(wavemap) ||
12216  nx != cpl_image_get_size_x(skymap) ||
12217  ny != cpl_image_get_size_y(skymap)) {
12218  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
12219  cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
12220  return NULL;
12221  }
12222 
12223  if (factor < 1.0) {
12224  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12225  cpl_msg_error(func, "Undersampling (%f): %s", factor,
12226  cpl_error_get_message());
12227  return NULL;
12228  }
12229 
12230  if (minpoints < 0) {
12231  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12232  cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
12233  return NULL;
12234  }
12235 
12236  dispersion /= factor;
12237 
12238 
12239  /*
12240  * Find bluest and reddest wavelengths in the whole image
12241  */
12242 
12243  data = cpl_image_get_data(wavemap);
12244 
12245  for (i = 0; i < npix; i++) {
12246  if (data[i] > 1.0) {
12247  min = max = data[i];
12248  j = i+1;
12249  break;
12250  }
12251  }
12252 
12253  for (i = j; i < npix; i++) {
12254  if (data[i] < 1.0) /* Impossible wavelength */
12255  continue;
12256  if (min > data[i])
12257  min = data[i];
12258  if (max < data[i])
12259  max = data[i];
12260  }
12261 
12262  firstLambda = min;
12263  lastLambda = max;
12264 
12265 
12266  /*
12267  * Determine length of median spectrum
12268  */
12269 
12270  nbin = (lastLambda - firstLambda) / dispersion;
12271 
12272  /*
12273  * Count how many values will be found for each spectral bin.
12274  * The ith bin has a wavelength range from firstLambda + i*dispersion
12275  * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12276  * it is assigned to its central wavelength.
12277  */
12278 
12279  count = cpl_calloc(nbin, sizeof(int));
12280 
12281  data = cpl_image_get_data(wavemap);
12282 
12283  for (i = 0; i < npix; i++) {
12284  if (data[i] < 1.0)
12285  continue;
12286  bin = (data[i] - firstLambda) / dispersion;
12287  if (bin < nbin) /* Safer */
12288  count[bin]++;
12289  }
12290 
12291  valid_bins = 0;
12292  for (i = 0; i < nbin; i++)
12293  if (count[i] >= minpoints)
12294  valid_bins++;
12295 
12296  if (valid_bins < nbin/3) {
12297  cpl_msg_warning(func, "Cannot determine a good global sky "
12298  "spectrum from input data");
12299  return NULL;
12300  }
12301 
12302 
12303  /*
12304  * Allocate an array of vectors with the appropriate size, to
12305  * contain a list of all the spectral pixels values. At the same
12306  * time, reset the array of counters (because we will have to
12307  * count again...).
12308  */
12309 
12310  vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12311  wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
12312  for (i = 0; i < nbin; i++) {
12313  if (count[i] >= minpoints) {
12314  vector[i] = cpl_vector_new(count[i]);
12315  wvector[i] = cpl_vector_new(count[i]);
12316  }
12317  count[i] = 0;
12318  }
12319 
12320 
12321  /*
12322  * Read the wavemap and the spectral images, and add the data values
12323  * to the appropriate wavelength bins
12324  */
12325 
12326  data = cpl_image_get_data(wavemap);
12327  sdata = cpl_image_get_data(spectra);
12328 
12329  for (i = 0; i < npix; i++) {
12330  if (data[i] < 1.0)
12331  continue;
12332  bin = (data[i] - firstLambda) / dispersion;
12333  if (bin < nbin) { /* Safer */
12334  if (vector[bin]) {
12335  cpl_vector_set(vector[bin], count[bin], sdata[i]);
12336  cpl_vector_set(wvector[bin], count[bin], data[i]);
12337  }
12338  count[bin]++;
12339  }
12340  }
12341 
12342 
12343  /*
12344  * Compute the median flux for each wavelength bin, and destroy
12345  * at the same time the used vectors
12346  */
12347 
12348  sky_spectrum = cpl_calloc(nbin, sizeof(double));
12349  sky_wave = cpl_calloc(nbin, sizeof(double));
12350  for (i = 0; i < nbin; i++) {
12351  if (vector[i]) {
12352  sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12353  sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
12354  cpl_vector_delete(vector[i]);
12355  cpl_vector_delete(wvector[i]);
12356  }
12357  }
12358 
12359  cpl_free(vector);
12360  cpl_free(wvector);
12361 
12362 
12363  /*
12364  * Here possible gaps in the final spectrum are filled by interpolation
12365  */
12366 
12367  for (i = 0; i < nbin; i++) {
12368  if (count[i] >= minpoints) {
12369  first_valid = i;
12370  break;
12371  }
12372  }
12373 
12374  for (i = first_valid; i < nbin; i++) {
12375  if (count[i] < minpoints) {
12376  sky_wave[i] = firstLambda + (i+0.5)*dispersion;
12377  for (j = i+1; j < nbin; j++) {
12378  if (count[j] >= minpoints) {
12379  if (sky_wave[j] - sky_wave[i-1] < 0.1) {
12380  sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
12381  / 2;
12382  }
12383  else {
12384  frac = (sky_wave[i] - sky_wave[i-1])
12385  / (sky_wave[j] - sky_wave[i-1]);
12386  sky_spectrum[i] = frac * sky_spectrum[j]
12387  + (1 - frac) * sky_spectrum[i-1];
12388  }
12389  }
12390  }
12391  }
12392  }
12393 
12394 
12395  /*
12396  * Create the output table
12397  */
12398 
12399  sky = cpl_table_new(nbin);
12400  cpl_table_wrap_double(sky, sky_wave, "wavelength");
12401  cpl_table_wrap_double(sky, sky_spectrum, "sky");
12402  cpl_table_wrap_int(sky, count, "npoints");
12403 
12404 
12405  /*
12406  * Fill the sky map
12407  */
12408 
12409  data = cpl_image_get_data(wavemap);
12410  sdata = cpl_image_get_data(spectra);
12411  kdata = cpl_image_get_data(skymap);
12412 
12413  for (i = 0; i < npix; i++) {
12414 
12415  /*
12416  * Currently based on linear interpolation
12417  */
12418 
12419  lambda = data[i];
12420  if (lambda < 1.0)
12421  continue;
12422  bin = (lambda - firstLambda) / dispersion;
12423  lambda1 = sky_wave[bin];
12424  value1 = sky_spectrum[bin];
12425  if (lambda1 < lambda) {
12426  bin++;
12427  if (bin < nbin) {
12428  lambda2 = sky_wave[bin];
12429  value2 = sky_spectrum[bin];
12430  if (lambda2 - lambda1 < 0.1) {
12431  value = (value1 + value2) / 2;
12432  }
12433  else {
12434  frac = (lambda - lambda1) / (lambda2 - lambda1);
12435  value = frac * value2 + (1 - frac) * value1;
12436  }
12437  }
12438  else {
12439  value = value1;
12440  }
12441  }
12442  else {
12443  if (bin > 0) {
12444  bin--;
12445  lambda2 = lambda1;
12446  value2 = value1;
12447  lambda1 = sky_wave[bin];
12448  value1 = sky_spectrum[bin];
12449  if (lambda2 - lambda1 < 0.1) {
12450  value = (value1 + value2) / 2;
12451  }
12452  else {
12453  frac = (lambda - lambda1) / (lambda2 - lambda1);
12454  value = frac * value2 + (1 - frac) * value1;
12455  }
12456  }
12457  else {
12458  value = value1;
12459  }
12460  }
12461  kdata[i] = value;
12462  }
12463 
12464  if (first_valid)
12465  cpl_table_erase_window(sky, 0, first_valid);
12466 
12467  return sky;
12468 
12469 }
12470 
12471 
12505 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
12506  double dispersion, cpl_image *skymap)
12507 {
12508  const char *func = "mos_sky_map";
12509 
12510  cpl_vector **vector;
12511  double firstLambda, lastLambda;
12512  double lambda, lambda1, lambda2;
12513  double value, value1, value2;
12514  float min, max;
12515  int *count;
12516  int nbin, bin;
12517  int nx, ny, npix;
12518  int i, j;
12519 
12520  cpl_table *sky;
12521  double *sky_spectrum;
12522  float *data;
12523  float *sdata;
12524  float *kdata;
12525  double *wdata;
12526 
12527 
12528  if (spectra == NULL || wavemap == NULL || skymap == NULL) {
12529  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12530  return NULL;
12531  }
12532 
12533  if (dispersion <= 0.0) {
12534  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12535  return NULL;
12536  }
12537 
12538  nx = cpl_image_get_size_x(spectra);
12539  ny = cpl_image_get_size_y(spectra);
12540  npix = nx * ny;
12541 
12542  if (nx != cpl_image_get_size_x(wavemap) ||
12543  ny != cpl_image_get_size_y(wavemap) ||
12544  nx != cpl_image_get_size_x(skymap) ||
12545  ny != cpl_image_get_size_y(skymap)) {
12546  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
12547  return NULL;
12548  }
12549 
12550 
12551  /*
12552  * Find bluest and reddest wavelengths in the whole image
12553  */
12554 
12555  data = cpl_image_get_data(wavemap);
12556 
12557  for (i = 0; i < npix; i++) {
12558  if (data[i] > 1.0) {
12559  min = max = data[i];
12560  j = i+1;
12561  break;
12562  }
12563  }
12564 
12565  for (i = j; i < npix; i++) {
12566  if (data[i] < 1.0) /* Impossible wavelength */
12567  continue;
12568  if (min > data[i])
12569  min = data[i];
12570  if (max < data[i])
12571  max = data[i];
12572  }
12573 
12574  firstLambda = min;
12575  lastLambda = max;
12576 
12577 
12578  /*
12579  * Determine length of median spectrum
12580  */
12581 
12582  nbin = (lastLambda - firstLambda) / dispersion;
12583 
12584  /*
12585  * Count how many values will be found for each spectral bin.
12586  * The ith bin has a wavelength range from firstLambda + i*dispersion
12587  * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12588  * it is assigned to its central wavelength.
12589  */
12590 
12591  count = cpl_calloc(nbin, sizeof(int));
12592 
12593  data = cpl_image_get_data(wavemap);
12594 
12595  for (i = 0; i < npix; i++) {
12596  if (data[i] < 1.0)
12597  continue;
12598  bin = (data[i] - firstLambda) / dispersion;
12599  if (bin < nbin) /* Safer */
12600  count[bin]++;
12601  }
12602 
12603 
12604  /*
12605  * Allocate an array of vectors with the appropriate size, to
12606  * contain a list of all the spectral pixels values. At the same
12607  * time, reset the array of counters (because we will have to
12608  * count again...).
12609  */
12610 
12611  vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12612  for (i = 0; i < nbin; i++) {
12613  if (count[i])
12614  vector[i] = cpl_vector_new(count[i]);
12615  else
12616  vector[i] = NULL;
12617  count[i] = 0;
12618  }
12619 
12620 
12621  /*
12622  * Read the wavemap and the spectral images, and add the data values
12623  * to the appropriate wavelength bins
12624  */
12625 
12626  data = cpl_image_get_data(wavemap);
12627  sdata = cpl_image_get_data(spectra);
12628 
12629  for (i = 0; i < npix; i++) {
12630  if (data[i] < 1.0)
12631  continue;
12632  bin = (data[i] - firstLambda) / dispersion;
12633  if (bin < nbin) { /* Safer */
12634  cpl_vector_set(vector[bin], count[bin], sdata[i]);
12635  count[bin]++;
12636  }
12637  }
12638 
12639 
12640  /*
12641  * Compute the median flux for each wavelength bin, and destroy
12642  * at the same time the used vectors
12643  */
12644 
12645  sky_spectrum = cpl_calloc(nbin, sizeof(double));
12646  for (i = 0; i < nbin; i++) {
12647  if (vector[i]) {
12648  sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12649  cpl_vector_delete(vector[i]);
12650  }
12651  }
12652 
12653  cpl_free(vector);
12654 
12655 
12656  /*
12657  * Here possible gaps in the final spectrum should be filled
12658  * by interpolation
12659  */
12660 
12661  /* ... */
12662 
12663  /*
12664  * Create the output table
12665  */
12666 
12667  sky = cpl_table_new(nbin);
12668  cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
12669  cpl_table_set_column_unit(sky, "wavelength", "pixel");
12670  cpl_table_wrap_double(sky, sky_spectrum, "sky");
12671  cpl_table_wrap_int(sky, count, "npoints");
12672  for (i = 0; i < nbin; i++)
12673  cpl_table_set_double(sky, "wavelength", i,
12674  firstLambda + (i+0.5)*dispersion);
12675 
12676 
12677  /*
12678  * Fill the sky map
12679  */
12680 
12681  data = cpl_image_get_data(wavemap);
12682  sdata = cpl_image_get_data(spectra);
12683  kdata = cpl_image_get_data(skymap);
12684  wdata = cpl_table_get_data_double(sky, "wavelength");
12685 
12686  for (i = 0; i < npix; i++) {
12687 
12688  /*
12689  * Currently based on linear interpolation
12690  */
12691 
12692  lambda = data[i];
12693  if (lambda < 1.0)
12694  continue;
12695  bin = (lambda - firstLambda) / dispersion;
12696  lambda1 = wdata[bin];
12697  value1 = sky_spectrum[bin];
12698  if (lambda1 < lambda) {
12699  bin++;
12700  if (bin < nbin) {
12701  lambda2 = wdata[bin];
12702  value2 = sky_spectrum[bin];
12703  value = ((lambda2 - lambda)*value1
12704  + (lambda - lambda1)*value2) / dispersion;
12705  }
12706  else {
12707  value = value1;
12708  }
12709  }
12710  else {
12711  if (bin > 0) {
12712  bin--;
12713  lambda2 = lambda1;
12714  value2 = value1;
12715  lambda1 = wdata[bin];
12716  value1 = sky_spectrum[bin];
12717  value = ((lambda2 - lambda)*value1
12718  + (lambda - lambda1)*value2)/dispersion;
12719  }
12720  else {
12721  value = value1;
12722  }
12723  }
12724  kdata[i] = value;
12725  }
12726 
12727  return sky;
12728 
12729 }
12730 
12731 
12747 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
12748 {
12749  const char *func = "mos_sky_local_old";
12750 
12751  cpl_image *exslit;
12752  cpl_image *sky;
12753  cpl_image *skymap;
12754  float *data;
12755  float *sdata;
12756  int nx, ny;
12757  int xlow, ylow, xhig, yhig;
12758  int nslits;
12759  int *slit_id;
12760  int *position;
12761  int *length;
12762  int i, j, k;
12763 
12764 
12765  if (spectra == NULL) {
12766  cpl_msg_error(func,
12767  "A scientific rectified spectral image must be given");
12768  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12769  return NULL;
12770  }
12771 
12772  if (slits == NULL) {
12773  cpl_msg_error(func, "A slits position table must be given");
12774  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12775  return NULL;
12776  }
12777 
12778  nslits = cpl_table_get_nrow(slits);
12779  slit_id = cpl_table_get_data_int(slits, "slit_id");
12780  position = cpl_table_get_data_int(slits, "position");
12781  length = cpl_table_get_data_int(slits, "length");
12782 
12783  nx = cpl_image_get_size_x(spectra);
12784  ny = cpl_image_get_size_y(spectra);
12785 
12786  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12787 
12788  xlow = 1;
12789  xhig = nx;
12790  for (i = 0; i < nslits; i++) {
12791 
12792  if (length[i] == 0)
12793  continue;
12794 
12795  /*
12796  * Define the extraction boundaries. We DON'T write:
12797  *
12798  * ylow = position[i];
12799  * yhig = ylow + length[i];
12800  *
12801  * because the cpl_image pixels are counted from 1, and because in
12802  * cpl_image_extract() the coordinates of the last pixel are inclusive.
12803  */
12804 
12805  ylow = position[i] + 1;
12806  yhig = ylow + length[i] - 1;
12807 
12808  exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12809  sky = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12810  cpl_image_delete(exslit);
12811 
12812  data = cpl_image_get_data(skymap);
12813  data += nx * position[i];
12814 
12815  for (j = 0; j < length[i]; j++) {
12816  sdata = cpl_image_get_data(sky);
12817  for (k = 0; k < nx; k++) {
12818  *data++ = *sdata++;
12819  }
12820  }
12821 
12822  cpl_image_delete(sky);
12823  }
12824 
12825  return skymap;
12826 
12827 }
12828 
12829 
12849 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
12850 {
12851  const char *func = "mos_sky_local";
12852 
12853  char name[MAX_COLNAME];
12854 
12855  cpl_polynomial *fit;
12856  cpl_vector *points;
12857  cpl_vector *values;
12858  cpl_vector *keep_points;
12859  cpl_vector *keep_values;
12860  cpl_image *exslit;
12861  cpl_image *sky;
12862  cpl_image *subtracted;
12863  cpl_image *profile;
12864  cpl_image *skymap;
12865  cpl_table *objects;
12866  float *data;
12867  float *sdata;
12868  float *xdata;
12869  double *vdata;
12870  double *pdata;
12871  double median;
12872  int nx, ny;
12873  int xlow, ylow, xhig, yhig;
12874  int nslits;
12875  int *slit_id;
12876  int *position;
12877  int *length;
12878  int *is_sky;
12879  int nsky, nbad;
12880  int maxobjects;
12881  int margin = 3;
12882  int radius = 6;
12883  int i, j, k;
12884 
12885 
12886  if (spectra == NULL) {
12887  cpl_msg_error(func,
12888  "A scientific rectified spectral image must be given");
12889  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12890  return NULL;
12891  }
12892 
12893  if (slits == NULL) {
12894  cpl_msg_error(func, "A slits position table must be given");
12895  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12896  return NULL;
12897  }
12898 
12899  if (order < 0) {
12900  cpl_msg_error(func, "Invalid fit order");
12901  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12902  return NULL;
12903  }
12904 
12905  nslits = cpl_table_get_nrow(slits);
12906  slit_id = cpl_table_get_data_int(slits, "slit_id");
12907  position = cpl_table_get_data_int(slits, "position");
12908  length = cpl_table_get_data_int(slits, "length");
12909 
12910  nx = cpl_image_get_size_x(spectra);
12911  ny = cpl_image_get_size_y(spectra);
12912 
12913  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12914 
12915  xlow = 1;
12916  xhig = nx;
12917  for (i = 0; i < nslits; i++) {
12918 
12919  if (length[i] == 0)
12920  continue;
12921 
12922  /*
12923  * Define the extraction boundaries. We DON'T write:
12924  *
12925  * ylow = position[i];
12926  * yhig = ylow + length[i];
12927  *
12928  * because the cpl_image pixels are counted from 1, and because in
12929  * cpl_image_extract() the coordinates of the last pixel are inclusive.
12930  */
12931 
12932  ylow = position[i] + 1;
12933  yhig = ylow + length[i] - 1;
12934 
12935  exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12936  sky = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12937  cpl_image_delete(exslit);
12938 
12939  data = cpl_image_get_data(skymap);
12940  data += nx * position[i];
12941 
12942  for (j = 0; j < length[i]; j++) {
12943  sdata = cpl_image_get_data(sky);
12944  for (k = 0; k < nx; k++) {
12945  *data++ = *sdata++;
12946  }
12947  }
12948 
12949  cpl_image_delete(sky);
12950  }
12951 
12952 
12953  /*
12954  * Preliminary sky-subtracted image
12955  */
12956 
12957  subtracted = cpl_image_duplicate(spectra);
12958  cpl_image_subtract(subtracted, skymap);
12959  cpl_image_delete(skymap);
12960 
12961 
12962  /*
12963  * Detect objects positions in all slits
12964  */
12965 
12966  objects = cpl_table_duplicate(slits);
12967  profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
12968  cpl_image_delete(profile);
12969  cpl_image_delete(subtracted);
12970 
12971 
12972  /*
12973  * Flag the sky pixels. Note that maxobjects is intentionally
12974  * the max number of objects increased by one.
12975  */
12976 
12977  maxobjects = 1;
12978  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12979  while (cpl_table_has_column(objects, name)) {
12980  maxobjects++;
12981  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12982  }
12983 
12984  is_sky = cpl_calloc(ny, sizeof(int));
12985 
12986  for (i = 0; i < nslits; i++) {
12987 
12988  if (length[i] == 0)
12989  continue;
12990 
12991  ylow = position[i] + margin;
12992  yhig = position[i] + length[i] - margin;
12993 
12994  for (j = ylow; j < yhig; j++)
12995  is_sky[j] = 1;
12996 
12997  for (j = 1; j < maxobjects; j++) {
12998  snprintf(name, MAX_COLNAME, "object_%d", j);
12999  if (cpl_table_is_valid(objects, name, i)) {
13000  snprintf(name, MAX_COLNAME, "start_%d", j);
13001  ylow = cpl_table_get_int(objects, name, i, NULL);
13002  snprintf(name, MAX_COLNAME, "end_%d", j);
13003  yhig = cpl_table_get_int(objects, name, i, NULL);
13004  for (k = ylow; k <= yhig; k++)
13005  is_sky[k] = 0;
13006  }
13007  }
13008 
13009 
13010  /*
13011  * Eliminate isolated sky points
13012  */
13013 
13014  ylow = position[i] + margin + 1;
13015  yhig = position[i] + length[i] - margin - 1;
13016 
13017  for (j = ylow; j < yhig; j++)
13018  if (is_sky[j])
13019  if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
13020  is_sky[j] = 0;
13021 
13022  }
13023 
13024 
13025  /*
13026  * Determination of the sky map
13027  */
13028 
13029  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13030 
13031  for (i = 0; i < nslits; i++) {
13032 
13033  if (length[i] == 0)
13034  continue;
13035 
13036  ylow = position[i];
13037  yhig = ylow + length[i];
13038 
13039  nsky = 0;
13040  for (j = ylow; j < yhig; j++)
13041  if (is_sky[j])
13042  nsky++;
13043 
13044  if (nsky > order + 1) {
13045  if (order) {
13046  points = cpl_vector_new(nsky);
13047  nsky = 0;
13048  for (j = ylow; j < yhig; j++) {
13049  if (is_sky[j]) {
13050  cpl_vector_set(points, nsky, j);
13051  nsky++;
13052  }
13053  }
13054 
13055  exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
13056  xdata = cpl_image_get_data(exslit);
13057  values = cpl_vector_new(nsky);
13058 
13059  for (j = 0; j < nx; j++) {
13060  nsky = 0;
13061  for (k = ylow; k < yhig; k++) {
13062  if (is_sky[k]) {
13063  cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
13064  nsky++;
13065  }
13066  }
13067 
13068  /*
13069  * Eliminate obvious outliers
13070  */
13071 
13072  median = cpl_vector_get_median_const(values);
13073  vdata = cpl_vector_get_data(values);
13074  pdata = cpl_vector_get_data(points);
13075  nbad = 0;
13076  for (k = 0; k < nsky; k++) {
13077  if (fabs(vdata[k] - median) < 100) {
13078  if (nbad) {
13079  vdata[k-nbad] = vdata[k];
13080  pdata[k-nbad] = pdata[k];
13081  }
13082  }
13083  else
13084  nbad++;
13085  }
13086 
13087  if (nsky == nbad)
13088  continue;
13089 
13090  if (nbad && nsky - nbad > order + 1) {
13091  keep_values = values;
13092  keep_points = points;
13093  values = cpl_vector_wrap(nsky-nbad, vdata);
13094  points = cpl_vector_wrap(nsky-nbad, pdata);
13095  }
13096 
13097  if (nsky - nbad > order + 1) {
13098 
13099  fit = cpl_polynomial_fit_1d_create(points, values,
13100  order, NULL);
13101 
13102  if (fit) {
13103  for (k = ylow; k < yhig; k++) {
13104  xdata[j+(k-ylow)*nx] =
13105  cpl_polynomial_eval_1d(fit, k, NULL);
13106  }
13107 
13108  cpl_polynomial_delete(fit);
13109  }
13110  else
13111  cpl_error_reset();
13112  }
13113  else {
13114  for (k = 0; k < nsky; k++) {
13115  xdata[j+k*nx] = median;
13116  }
13117  }
13118 
13119  if (nbad && nsky - nbad > order + 1) {
13120  cpl_vector_unwrap(values);
13121  cpl_vector_unwrap(points);
13122  values = keep_values;
13123  points = keep_points;
13124  }
13125 
13126  if (nbad) {
13127  nsky = 0;
13128  for (k = ylow; k < yhig; k++) {
13129  if (is_sky[k]) {
13130  cpl_vector_set(points, nsky, k);
13131  nsky++;
13132  }
13133  }
13134  }
13135 
13136  }
13137 
13138  cpl_vector_delete(values);
13139  cpl_vector_delete(points);
13140 
13141  cpl_image_copy(skymap, exslit, 1, ylow+1);
13142  cpl_image_delete(exslit);
13143 
13144  }
13145  else {
13146  exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
13147  xdata = cpl_image_get_data(exslit);
13148  values = cpl_vector_new(nsky);
13149 
13150  for (j = 0; j < nx; j++) {
13151  nsky = 0;
13152  for (k = ylow; k < yhig; k++) {
13153  if (is_sky[k]) {
13154  cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
13155  nsky++;
13156  }
13157  }
13158 
13159  median = cpl_vector_get_median_const(values);
13160 
13161  for (k = ylow; k < yhig; k++)
13162  xdata[j+(k-ylow)*nx] = median;
13163 
13164  }
13165 
13166  cpl_vector_delete(values);
13167 
13168  cpl_image_copy(skymap, exslit, 1, ylow+1);
13169  cpl_image_delete(exslit);
13170  }
13171  }
13172  else
13173  cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
13174  }
13175 
13176  cpl_free(is_sky);
13177 
13178  return skymap;
13179 
13180 }
13181 
13182 
13204 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
13205  float threshold, float ratio)
13206 {
13207  const char *func = "mos_clean_cosmics";
13208 
13209  cpl_image *smoothImage;
13210  cpl_table *table;
13211  cpl_matrix *kernel;
13212  int *xdata;
13213  int *ydata;
13214  float *idata;
13215  float *sdata;
13216  float sigma, sum, value, smoothValue;
13217  double noise;
13218  int count;
13219  float fMax;
13220  int iMin, iMax, jMin, jMax, iPosMax, jPosMax;
13221  int xLen;
13222  int yLen;
13223  int nPix;
13224  int first = 1; /* position of first cosmic ray candidate
13225  encountered while scanning the image */
13226  int pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
13227  int numCosmic = 0;
13228  int found, foundContiguousCandidate;
13229  int *cosmic;
13230 
13231 
13232  if (image == NULL)
13233  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13234 
13235 
13236  /*
13237  * "cosmic" is a flags holder (initialized to zero):
13238  *
13239  * -1 = candidate for cosmic ray
13240  * 0 = not a cosmic
13241  * 1 = a cosmic ray
13242  * 2 = member of current group of contiguous candidates
13243  * 3 = examined member of current group
13244  */
13245 
13246  xLen = cpl_image_get_size_x(image);
13247  yLen = cpl_image_get_size_y(image);
13248 
13249  if (xLen < 4 || yLen < 4)
13250  return CPL_ERROR_NONE;
13251 
13252  nPix = xLen * yLen;
13253 
13254  /*
13255  * Noise estimation from negative offsets in image. Note that this
13256  * assumes that the background level (skyLevel) has already been
13257  * subtracted from the data. In this way we estimate the noise due
13258  * to detector readout and to the background signal level (before
13259  * it were removed). Theoretically this is given by
13260  *
13261  * noise = sqrt(ron^2 + skyLevel/gain)
13262  *
13263  * where ron is the read-out-noise. To this we will sum the noise
13264  * contribution due to any increase of the signal above the background
13265  * by an amount scienceLevel. Theoretically the total noise is given by
13266  *
13267  * totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
13268  *
13269  * that is
13270  *
13271  * totalNoise = sqrt(noise^2 + scienceLevel/gain)
13272  *
13273  */
13274 
13275  idata = cpl_image_get_data(image);
13276  noise = 0.0;
13277  count = 0;
13278 
13279  for (i = 0; i < nPix; i++) {
13280  if (idata[i] < -0.00001) {
13281  noise -= idata[i];
13282  count++;
13283  }
13284  }
13285 
13286  noise /= count;
13287  noise *= 1.25; /* Factor to convert average deviation to sigma */
13288 
13289  cosmic = cpl_calloc(nPix, sizeof(int));
13290 
13291  if (threshold < 0.)
13292  threshold = 4.0;
13293  if (ratio < 0.)
13294  ratio = 2.0;
13295 
13296  kernel = cpl_matrix_new(3, 3);
13297  cpl_matrix_fill(kernel, 1.0);
13298  cpl_matrix_set(kernel, 1, 1, 0.0);
13299  smoothImage = cpl_image_filter_median(image, kernel);
13300  cpl_matrix_delete(kernel);
13301 
13302  /*
13303  * Loop on images pixels, searching for cosmic rays candidates.
13304  * Border pixels are currently excluded (they cannot contain
13305  * candidates), to avoid that the search for groups of contiguous
13306  * pixels would ever go out of image boundaries. In future we may
13307  * overcome this limit, adding an appropriate check when contiguous
13308  * pixels are searched.
13309  */
13310 
13311  sdata = cpl_image_get_data(smoothImage);
13312 
13313  for (j = 1; j < yLen - 1; j++) {
13314  for (i = 1; i < xLen - 1; i++) {
13315  value = idata[i + j * xLen];
13316  smoothValue = sdata[i + j * xLen];
13317  if (smoothValue < 1.0)
13318  smoothValue = 1.0;
13319  sigma = sqrt(noise * noise + smoothValue / gain);
13320  if (value - smoothValue >= threshold * sigma)
13321  cosmic[i + j * xLen] = -1;
13322  }
13323  }
13324 
13325  cpl_image_delete(smoothImage);
13326 
13327 
13328  /*
13329  * Search for groups of contiguous cosmic rays candidates.
13330  */
13331 
13332  do {
13333  found = 0;
13334  for (pos = first; pos < nPix; pos++) {
13335  if (cosmic[pos] == -1) {
13336  cosmic[pos] = 2; /* Candidate found. */
13337  i = pos % xLen; /* Its coordinates. */
13338  j = pos / xLen;
13339  first = pos;
13340  first++; /* ??? really necessary? */
13341  found = 1;
13342  break;
13343  }
13344  }
13345 
13346  if (found) {
13347 
13348  /*
13349  * Determine new group of contiguous cosmic rays candidates.
13350  * Initialize the working box boundaries, iMin, iMax, jMin, jMax,
13351  * and the value of the max pixel and its position, fMax, iPosMax,
13352  * jPosMax.
13353  */
13354 
13355  iMin = iMax = iPosMax = i;
13356  jMin = jMax = jPosMax = j;
13357  fMax = idata[i + j * xLen];
13358 
13359  do {
13360  foundContiguousCandidate = 0;
13361  for (l = 0; l <= 1; l++) {
13362  for (k = 0; k <= 1; k++) {
13363 
13364  /*
13365  * Looping on 4 pixels to North, East, South and West
13366  */
13367 
13368  ii = i + k - l;
13369  jj = j + k + l - 1;
13370  if (cosmic[ii + jj * xLen] == -1) {
13371  foundContiguousCandidate = 1;
13372  cosmic[ii + jj * xLen] = 2;
13373  /* Candidate belongs to current group */
13374  iii = ii; /* Keep its position */
13375  jjj = jj;
13376 
13377  /*
13378  * Upgrade search box
13379  */
13380 
13381  if (ii < iMin)
13382  iMin = ii;
13383  if (ii > iMax)
13384  iMax = ii;
13385  if (jj < jMin)
13386  jMin = jj;
13387  if (jj > jMax)
13388  jMax = jj;
13389 
13390  if (idata[ii + jj * xLen] > fMax) {
13391  fMax = idata[ii + jj * xLen];
13392  iPosMax = ii;
13393  jPosMax = jj;
13394  }
13395  }
13396  }
13397  }
13398 
13399  /*
13400  * We are done exploring the "cross". Now mark as "examined"
13401  * the current candidate (at the center of the cross):
13402  */
13403 
13404  cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
13405 
13406  if (foundContiguousCandidate) {
13407 
13408  /*
13409  * Pass (arbitrarily) the coordinates of the LAST found
13410  * candidate
13411  */
13412 
13413  i = iii;
13414  j = jjj;
13415 
13416  /*
13417  * Skip the rest, continue loop on new candidate
13418  */
13419 
13420  continue;
13421  }
13422 
13423 
13424  /*
13425  * Look for leftovers in the (growing!) search box
13426  */
13427 
13428  for (l = jMin; l <= jMax; l++) {
13429  for (k = iMin; k <= iMax; k++) {
13430  if (cosmic[k + l * xLen] == 2) {
13431  i = k;
13432  j = l;
13433  foundContiguousCandidate = 1;
13434  break;
13435  }
13436  }
13437  if (foundContiguousCandidate)
13438  break;
13439  }
13440  } while (foundContiguousCandidate);
13441 
13442 
13443  /*
13444  * No more contiguous candidates are found. Decide now
13445  * whether the current group is a cosmic ray or not.
13446  */
13447 
13448  sum = 0.; /* Sum of 8 pixels around max position */
13449  for (l = -1; l <= 1; l++) {
13450  for (k = -1; k <= 1; k++) {
13451  if (l != 0 || k != 0) {
13452  sum += idata[iPosMax + k + (jPosMax + l) * xLen];
13453  }
13454  }
13455  }
13456 
13457  sum /= 8.;
13458  if (fMax > ratio * sum) {
13459  for (l = jMin - 1; l <= jMax + 1; l++) {
13460  for (k = iMin - 1; k <= iMax + 1; k++) {
13461  if (cosmic[k + l * xLen] == 3) {
13462  cosmic[k + l * xLen] = 1;
13463  numCosmic++;
13464  }
13465  }
13466  }
13467  }
13468  else {
13469  for (l = jMin - 1; l <= jMax + 1; l++) {
13470  for (k = iMin - 1; k <= iMax + 1; k++) {
13471  if (cosmic[k + l * xLen] != -1) {
13472  if (cosmic[k + l * xLen] == 1)
13473  numCosmic--;
13474  cosmic[k + l * xLen] = 0;
13475  }
13476  }
13477  }
13478  }
13479  }
13480  } while (found);
13481 
13482 
13483  /*
13484  * Prepare table containing cosmic rays coordinates.
13485  */
13486 
13487  table = cpl_table_new(numCosmic);
13488  cpl_table_new_column(table, "x", CPL_TYPE_INT);
13489  cpl_table_new_column(table, "y", CPL_TYPE_INT);
13490  cpl_table_set_column_unit(table, "x", "pixel");
13491  cpl_table_set_column_unit(table, "y", "pixel");
13492  xdata = cpl_table_get_data_int(table, "x");
13493  ydata = cpl_table_get_data_int(table, "y");
13494 
13495  for (pos = 0, i = 0; pos < nPix; pos++) {
13496  if (cosmic[pos] == 1) {
13497  xdata[i] = (pos % xLen);
13498  ydata[i] = (pos / xLen);
13499  i++;
13500  }
13501  }
13502 
13503  mos_clean_bad_pixels(image, table, 1);
13504 
13505  cpl_free(cosmic);
13506  cpl_table_delete(table);
13507 
13508  return CPL_ERROR_NONE;
13509 
13510 }
13511 
13512 
13513 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
13514  int spectral)
13515 {
13516  const char *func = "mos_clean_cosmics";
13517 
13518  float *idata;
13519  int *isBadPix;
13520  int i, j, k, d;
13521  int xlen, ylen, totPix;
13522  int nBadPixels = 0;
13523  int sign, foundFirst;
13524  int *xValue = NULL;
13525  int *yValue = NULL;
13526  float save = 0.;
13527  double sumd;
13528  int cx, cy;
13529  int nPairs;
13530  float estimate[4];
13531  int sx[] = {0, 1, 1, 1};
13532  int sy[] = {1,-1, 0, 1};
13533  int searchHorizon = 100;
13534  int percent = 15;
13535 
13536 
13537  if (image == NULL || table == NULL)
13538  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13539 
13540  if (1 != cpl_table_has_column(table, "x"))
13541  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13542 
13543  if (1 != cpl_table_has_column(table, "y"))
13544  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13545 
13546  if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
13547  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13548 
13549  if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
13550  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13551 
13552  nBadPixels = cpl_table_get_nrow(table);
13553 
13554  if (nBadPixels) {
13555  xlen = cpl_image_get_size_x(image);
13556  ylen = cpl_image_get_size_y(image);
13557  idata = cpl_image_get_data(image);
13558  totPix = xlen * ylen;
13559  if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
13560  isBadPix = cpl_calloc(totPix, sizeof(int));
13561  }
13562  else {
13563  cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
13564  "skip bad pixel correction", percent);
13565  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13566  }
13567  }
13568  else {
13569  cpl_msg_debug(func, "No pixel values to interpolate");
13570  return CPL_ERROR_NONE;
13571  }
13572 
13573  xValue = cpl_table_get_data_int(table, "x");
13574  yValue = cpl_table_get_data_int(table, "y");
13575 
13576  for (i = 0; i < nBadPixels; i++)
13577  isBadPix[xValue[i] + yValue[i] * xlen] = 1;
13578 
13579  for (i = 0; i < nBadPixels; i++) {
13580 
13581  /*
13582  * Search for the closest good pixel along the 4 fundamental
13583  * directions (in both senses):
13584  * \ | /
13585  * \|/
13586  * --- ---
13587  * /|\
13588  * / | \
13589  *
13590  * Then collect pairs of values to interpolate linearly.
13591  */
13592 
13593  nPairs = 0;
13594  for (j = 0; j < 4; j++) {
13595 
13596  if (spectral) /* Just horizontal interpolation for spectral data */
13597  if (j != 2)
13598  continue;
13599 
13600  estimate[nPairs] = 0.; /* Pairs interpolations are stored here */
13601  sumd = 0.;
13602  foundFirst = 0;
13603  for (k = 0; k < 2; k++) {
13604  sign = 2 * k - 1;
13605  d = 0;
13606  cx = xValue[i];
13607  cy = yValue[i];
13608  do {
13609  cx += sign * sx[j];
13610  cy += sign * sy[j];
13611  if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen)
13612  break;
13613  d++;
13614  } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
13615 
13616  if (cx >= 0 && cx < xlen &&
13617  cy >= 0 && cy < ylen && d < searchHorizon) {
13618 
13619  /*
13620  * In this block is cripted the linear interpolation...
13621  */
13622 
13623  save = idata[cx + cy * xlen];
13624  estimate[nPairs] += save / d;
13625  sumd += 1. / (double) d;
13626  if (k) {
13627  estimate[nPairs] /= sumd;
13628  nPairs++;
13629  }
13630  else {
13631  foundFirst = 1;
13632  }
13633  }
13634  else {
13635 
13636  /*
13637  * Image borders were crossed, incomplete pair of values
13638  */
13639 
13640  if (k) {
13641  if (foundFirst) {
13642  estimate[nPairs] = save;
13643  nPairs++;
13644  }
13645  }
13646  }
13647  }
13648  }
13649 
13650  /*
13651  * Replace pixel value of the input image, corresponding to
13652  * the current bad pixel, with the median of the estimates
13653  * resulted from the 4 linear interpolations.
13654  */
13655 
13656  if (nPairs > 2) {
13657  idata[xValue[i] + yValue[i] * xlen] =
13658  cpl_tools_get_median_float(estimate, nPairs);
13659  }
13660  else if (nPairs == 2) {
13661  idata[xValue[i] + yValue[i] * xlen] =
13662  (estimate[0] + estimate[1]) / 2.;
13663  }
13664  else if (nPairs == 1) {
13665  idata[xValue[i] + yValue[i] * xlen] = estimate[0];
13666  }
13667  else {
13668  cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
13669  xValue[i], yValue[i]);
13670  }
13671  }
13672 
13673  cpl_free(isBadPix);
13674 
13675  return CPL_ERROR_NONE;
13676 }
13677 
13678 
13708 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
13709  cpl_table *polytraces, double reference,
13710  double blue, double red, double dispersion)
13711 {
13712  const char *func = "mos_spatial_map";
13713 
13714  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
13715  /* Max order is 5 */
13716  cpl_polynomial *polytop;
13717  cpl_polynomial *polybot;
13718  cpl_image *calibration;
13719  float *data;
13720  double top, bot;
13721  double coeff;
13722  double ytop, ybot;
13723  double ypos, yfra;
13724  double factor;
13725  int yint, yprev;
13726  int nslits;
13727  int npseudo;
13728  int *slit_id;
13729  int *length;
13730  int nx, ny;
13731  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
13732  int missing_top, missing_bot;
13733  int null;
13734  int order;
13735  int i, j;
13736  cpl_size k;
13737 
13738 
13739  if (spectra == NULL || slits == NULL || polytraces == NULL) {
13740  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13741  return NULL;
13742  }
13743 
13744  if (dispersion <= 0.0) {
13745  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13746  return NULL;
13747  }
13748 
13749  if (red - blue < dispersion) {
13750  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13751  return NULL;
13752  }
13753 
13754  nx = cpl_image_get_size_x(spectra);
13755  ny = cpl_image_get_size_y(spectra);
13756 
13757  calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13758  data = cpl_image_get_data(calibration);
13759 
13760  length = cpl_table_get_data_int(slits, "length");
13761  nslits = cpl_table_get_nrow(slits);
13762  slit_id = cpl_table_get_data_int(slits, "slit_id");
13763  order = cpl_table_get_ncol(polytraces) - 2;
13764 
13765  /*
13766  * The spatial resampling is performed for a certain number of
13767  * pixels above and below the position of the reference wavelength:
13768  */
13769 
13770  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
13771  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
13772 
13773  for (i = 0; i < nslits; i++) {
13774 
13775  if (length[i] == 0)
13776  continue;
13777 
13778  /*
13779  * Note that the x coordinate of the reference pixels on the CCD
13780  * is taken arbitrarily at the top end of each slit. This wouldn't
13781  * be entirely correct in case of curved slits, or in presence of
13782  * heavy distortions: in such cases the spatial resampling is
13783  * really performed across a wide range of wavelengths. But
13784  * the lag between top and bottom spectral curvature models
13785  * would introduce even in such cases negligible effects on
13786  * the spectral spatial resampling.
13787  */
13788 
13789  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
13790 
13791  start_pixel = refpixel - pixel_below;
13792  if (start_pixel < 0)
13793  start_pixel = 0;
13794 
13795  end_pixel = refpixel + pixel_above;
13796  if (end_pixel > nx)
13797  end_pixel = nx;
13798 
13799  /*
13800  * Recover from the table of spectral curvature coefficients
13801  * the curvature polynomials.
13802  */
13803 
13804  missing_top = 0;
13805  polytop = cpl_polynomial_new(1);
13806  for (k = 0; k <= order; k++) {
13807  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
13808  if (null) {
13809  cpl_polynomial_delete(polytop);
13810  missing_top = 1;
13811  break;
13812  }
13813  cpl_polynomial_set_coeff(polytop, &k, coeff);
13814  }
13815 
13816  missing_bot = 0;
13817  polybot = cpl_polynomial_new(1);
13818  for (k = 0; k <= order; k++) {
13819  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
13820  if (null) {
13821  cpl_polynomial_delete(polybot);
13822  missing_bot = 1;
13823  break;
13824  }
13825  cpl_polynomial_set_coeff(polybot, &k, coeff);
13826  }
13827 
13828  if (missing_top && missing_bot) {
13829  cpl_msg_warning(func, "Spatial map, slit %d was not traced!",
13830  slit_id[i]);
13831  continue;
13832  }
13833 
13834  /*
13835  * In case just one of the two edges was not traced, the other
13836  * edge curvature model is duplicated and shifted to the other
13837  * end of the slit: better than nothing!
13838  */
13839 
13840  if (missing_top) {
13841  cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
13842  "the spectral curvature of the lower edge "
13843  "is used instead.", slit_id[i]);
13844  polytop = cpl_polynomial_duplicate(polybot);
13845  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13846  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13847  k = 0;
13848  coeff = cpl_polynomial_get_coeff(polybot, &k);
13849  coeff += ytop - ybot;
13850  cpl_polynomial_set_coeff(polytop, &k, coeff);
13851  }
13852 
13853  if (missing_bot) {
13854  cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
13855  "the spectral curvature of the upper edge "
13856  "is used instead.", slit_id[i]);
13857  polybot = cpl_polynomial_duplicate(polytop);
13858  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13859  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13860  k = 0;
13861  coeff = cpl_polynomial_get_coeff(polytop, &k);
13862  coeff -= ytop - ybot;
13863  cpl_polynomial_set_coeff(polybot, &k, coeff);
13864  }
13865 
13866  top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
13867  bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
13868  npseudo = ceil(top-bot) + 1;
13869 
13870  if (npseudo < 1) {
13871  cpl_polynomial_delete(polytop);
13872  cpl_polynomial_delete(polybot);
13873  cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
13874  slit_id[i]);
13875  continue;
13876  }
13877 
13878  for (j = start_pixel; j < end_pixel; j++) {
13879  top = cpl_polynomial_eval_1d(polytop, j, NULL);
13880  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
13881  factor = (top-bot)/npseudo;
13882  for (k = 0; k <= npseudo; k++) {
13883  ypos = top - k*factor;
13884  yint = ypos;
13885  yfra = ypos - yint;
13886  if (yint >= 0 && yint < ny-1) {
13887  data[j + nx*yint] = (top-yint)/factor;
13888  if (k) {
13889 
13890  /*
13891  * This is added to recover lost pixels on
13892  * the CCD image (pixels are lost because
13893  * the CCD pixels are less than npseudo+1).
13894  */
13895 
13896  if (yprev - yint > 1) {
13897  data[j + nx*(yint+1)] = (top-yint-1)/factor;
13898  }
13899  }
13900  }
13901  yprev = yint;
13902  }
13903  }
13904  cpl_polynomial_delete(polytop);
13905  cpl_polynomial_delete(polybot);
13906  }
13907 
13908  return calibration;
13909 }
13910 
13911 
13974 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
13975  int maxradius, int conradius)
13976 {
13977  const char *func = "mos_detect_objects";
13978 
13979  cpl_image *profile;
13980  float *pdata;
13981  float *p;
13982 
13983  char name[MAX_COLNAME];
13984 
13985  int nslits;
13986  int npeaks;
13987  int nobjects, objpos, totobj;
13988  int maxobjects;
13989  int *position;
13990  int *length;
13991  int *reject;
13992  double *place;
13993  double *bright;
13994  double mindistance;
13995  int pos, count;
13996  int up;
13997  int low, hig;
13998  int row;
13999  int i, j, k;
14000 
14001  const int min_pixels = 10;
14002 
14003 
14004  if (cpl_error_get_code() != CPL_ERROR_NONE)
14005  return NULL;
14006 
14007  if (image == NULL || slits == NULL) {
14008  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14009  return NULL;
14010  }
14011 
14012  if (margin < 0)
14013  margin = 0;
14014 
14015  if (maxradius < 0) {
14016  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14017  return NULL;
14018  }
14019 
14020  if (conradius < 0) {
14021  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14022  return NULL;
14023  }
14024 
14025  nslits = cpl_table_get_nrow(slits);
14026  position = cpl_table_get_data_int(slits, "position");
14027  length = cpl_table_get_data_int(slits, "length");
14028 
14029  profile = cpl_image_collapse_create(image, 1);
14030  cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
14031  pdata = cpl_image_get_data(profile);
14032 
14033  row = 1;
14034  maxobjects = 0;
14035  totobj = 0;
14036  for (i = 0; i < nslits; i++) {
14037 
14038  if (length[i] == 0)
14039  continue;
14040 
14041  pos = position[i] + margin;
14042  count = length[i] - 2*margin;
14043 
14044  if (count < min_pixels)
14045  continue;
14046 
14047  p = pdata + pos;
14048 
14049 
14050  /*
14051  * Count peaks candidates
14052  */
14053 
14054  npeaks = 0;
14055  if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
14056  npeaks++;
14057  }
14058 
14059  up = 0;
14060  for (j = 0; j < count - 3; j++) {
14061  if (p[j] > 0) {
14062  if (p[j+1] > p[j]) {
14063  up++;
14064  }
14065  else {
14066  if (up > 2) {
14067  if (p[j+1] > p[j+2] && p[j+2] > 0) {
14068  if (p[j] > 5)
14069  npeaks++;
14070  }
14071  }
14072  else if (up > 1) {
14073  if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
14074  if (p[j] > 5)
14075  npeaks++;
14076  }
14077  }
14078  up = 0;
14079  }
14080  }
14081  else {
14082  up = 0;
14083  }
14084  }
14085 
14086  if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
14087  && p[count-3] > p[count-4] && p[count-4] > 0) {
14088  npeaks++;
14089  }
14090 
14091  if (npeaks == 0)
14092  continue;
14093 
14094 
14095  /*
14096  * Get candidates parameters
14097  */
14098 
14099  reject = cpl_calloc(npeaks, sizeof(int));
14100  bright = cpl_calloc(npeaks, sizeof(double));
14101  place = cpl_calloc(npeaks, sizeof(double));
14102 
14103  npeaks = 0;
14104  if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
14105  bright[0] = p[0];
14106  place[0] = position[i] + margin;
14107  npeaks++;
14108  }
14109 
14110  up = 0;
14111  for (j = 0; j < count - 3; j++) {
14112  if (p[j] > 0) {
14113  if (p[j+1] > p[j]) {
14114  up++;
14115  }
14116  else {
14117  if (up > 2) {
14118  if (p[j+1] > p[j+2] && p[j+2] > 0) {
14119  if (p[j] > 5) {
14120  bright[npeaks] = p[j];
14121  place[npeaks] = position[i] + margin + j + 1
14122  + values_to_dx(p[j-1], p[j], p[j+1]);
14123  npeaks++;
14124  }
14125  }
14126  }
14127  else if (up > 1) {
14128  if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
14129  if (p[j] > 5) {
14130  bright[npeaks] = p[j];
14131  place[npeaks] = position[i] + margin + j + 1
14132  + values_to_dx(p[j-1], p[j], p[j+1]);
14133  npeaks++;
14134  }
14135  }
14136  }
14137  up = 0;
14138  }
14139  }
14140  else {
14141  up = 0;
14142  }
14143  }
14144 
14145  if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
14146  && p[count-3] > p[count-4] && p[count-4] > 0) {
14147  bright[npeaks] = p[count-1];
14148  place[npeaks] = position[i] + count;
14149  npeaks++;
14150  }
14151 
14152 
14153  /*
14154  * Now select the uncontaminated peaks
14155  */
14156 
14157  if (fabs(place[0] - pos) < 1.0)
14158  reject[0] = 1;
14159  if (fabs(place[npeaks-1] - pos - count) < 1.0)
14160  reject[npeaks-1] = 1;
14161  for (j = 0; j < npeaks; j++) {
14162  for (k = 0; k < npeaks; k++) {
14163  if (k == j)
14164  continue;
14165  mindistance = conradius * bright[k] / bright[j]
14166  * bright[k] / bright[j];
14167  if (fabs(place[j] - place[k]) < mindistance)
14168  reject[j] = 1;
14169  }
14170  }
14171 
14172 /* new part */
14173  for (j = 0; j < npeaks; j++) {
14174  if (reject[j])
14175  continue;
14176  if (j) {
14177  low = (place[j-1]*bright[j] + place[j]*bright[j-1])
14178  / (bright[j-1] + bright[j]) + 1;
14179  }
14180  else {
14181  low = pos;
14182  }
14183  if (j < npeaks - 1) {
14184  hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
14185  / (bright[j+1] + bright[j]) + 1;
14186  }
14187  else {
14188  hig = pos + count;
14189  }
14190 
14191  if (low < pos)
14192  low = pos;
14193  if (hig > pos + count)
14194  hig = pos + count;
14195  if (place[j] - low > maxradius)
14196  low = place[j] - maxradius;
14197  if (hig - place[j] > maxradius)
14198  hig = place[j] + maxradius;
14199  if (hig == low)
14200  reject[j] = 1;
14201  }
14202 /* end new part */
14203 
14204  nobjects = npeaks;
14205  for (j = 0; j < npeaks; j++)
14206  if (reject[j])
14207  nobjects--;
14208 
14209  for (j = 0; j < nobjects; j++) {
14210  snprintf(name, MAX_COLNAME, "object_%d", j+1);
14211  if (cpl_table_has_column(slits, name))
14212  continue;
14213  cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
14214  snprintf(name, MAX_COLNAME, "start_%d", j+1);
14215  cpl_table_new_column(slits, name, CPL_TYPE_INT);
14216  cpl_table_set_column_unit(slits, name, "pixel");
14217  snprintf(name, MAX_COLNAME, "end_%d", j+1);
14218  cpl_table_new_column(slits, name, CPL_TYPE_INT);
14219  cpl_table_set_column_unit(slits, name, "pixel");
14220  snprintf(name, MAX_COLNAME, "row_%d", j+1);
14221  cpl_table_new_column(slits, name, CPL_TYPE_INT);
14222  cpl_table_set_column_unit(slits, name, "pixel");
14223  }
14224 
14225  objpos = nobjects;
14226  for (j = 0; j < npeaks; j++) {
14227  if (reject[j])
14228  continue;
14229  if (j) {
14230  low = (place[j-1]*bright[j] + place[j]*bright[j-1])
14231  / (bright[j-1] + bright[j]) + 1;
14232  }
14233  else {
14234  low = pos;
14235  }
14236  if (j < npeaks - 1) {
14237  hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
14238  / (bright[j+1] + bright[j]) + 1;
14239  }
14240  else {
14241  hig = pos + count;
14242  }
14243 
14244  if (low < pos)
14245  low = pos;
14246  if (hig > pos + count)
14247  hig = pos + count;
14248  if (place[j] - low > maxradius)
14249  low = place[j] - maxradius;
14250  if (hig - place[j] > maxradius)
14251  hig = place[j] + maxradius;
14252 
14253  snprintf(name, MAX_COLNAME, "object_%d", objpos);
14254  cpl_table_set_double(slits, name, i, place[j]);
14255  snprintf(name, MAX_COLNAME, "start_%d", objpos);
14256  cpl_table_set_int(slits, name, i, low);
14257  snprintf(name, MAX_COLNAME, "end_%d", objpos);
14258  cpl_table_set_int(slits, name, i, hig);
14259  snprintf(name, MAX_COLNAME, "row_%d", objpos);
14260  cpl_table_set_int(slits, name, i, row + objpos - 1);
14261  totobj++;
14262  objpos--;
14263  }
14264 
14265  row += nobjects;
14266 
14267  if (maxobjects < nobjects)
14268  maxobjects = nobjects;
14269 
14270  cpl_free(reject);
14271  cpl_free(bright);
14272  cpl_free(place);
14273 
14274  }
14275 
14276 /* nobjects = row - nobjects; A bug, I think... */
14277  row = cpl_table_get_nrow(slits);
14278 
14279  for (i = 0; i < row; i++) {
14280  for (j = 0; j < maxobjects; j++) {
14281  snprintf(name, MAX_COLNAME, "row_%d", j+1);
14282  if (cpl_table_is_valid(slits, name, i))
14283  cpl_table_set_int(slits, name, i, totobj -
14284  cpl_table_get_int(slits, name, i, NULL));
14285  }
14286  }
14287 
14288  for (i = 0; i < maxobjects; i++) {
14289  snprintf(name, MAX_COLNAME, "start_%d", i+1);
14290  cpl_table_fill_invalid_int(slits, name, -1);
14291  snprintf(name, MAX_COLNAME, "end_%d", i+1);
14292  cpl_table_fill_invalid_int(slits, name, -1);
14293  snprintf(name, MAX_COLNAME, "row_%d", i+1);
14294  cpl_table_fill_invalid_int(slits, name, -1);
14295  }
14296 
14297  return profile;
14298 }
14299 
14300 
14325 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *science_var,
14326  cpl_image *sky,
14327  cpl_table *objects, int extraction, double ron,
14328  double gain, int ncombined)
14329 {
14330  const char *func = "mos_extract_objects";
14331 
14332  char name[MAX_COLNAME];
14333 
14334  cpl_image **output;
14335  cpl_image *extracted;
14336  cpl_image *extr_sky;
14337  cpl_image *error;
14338  cpl_image *sciwin;
14339  cpl_image *sci_var_win = NULL;
14340  cpl_image *skywin;
14341  int nslits;
14342  int nobjects;
14343  int maxobjects;
14344  int nx;
14345  int ylow, yhig;
14346  int i, j;
14347 
14348 
14349  if (science == NULL || sky == NULL) {
14350  cpl_msg_error(func, "Both scientific exposures are required in input");
14351  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14352  return NULL;
14353  }
14354 
14355  if (objects == NULL) {
14356  cpl_msg_error(func, "An object table is required in input");
14357  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14358  return NULL;
14359  }
14360 
14361  if (extraction < 0 || extraction > 1) {
14362  cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
14363  "either 0 or 1", extraction);
14364  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14365  return NULL;
14366  }
14367 
14368  if (ron < 0.0) {
14369  cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
14370  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14371  return NULL;
14372  }
14373 
14374  if (gain < 0.1) {
14375  cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
14376  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14377  return NULL;
14378  }
14379 
14380  if (ncombined < 1) {
14381  cpl_msg_error(func, "Invalid number of combined frames (%d): "
14382  "it should be at least 1", ncombined);
14383  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14384  return NULL;
14385  }
14386 
14387 
14388  /*
14389  * Count the max number of objects per slit. Note that maxobjects
14390  * is intentionally the max number of objects increased by one.
14391  */
14392 
14393  maxobjects = 1;
14394  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
14395  while (cpl_table_has_column(objects, name)) {
14396  maxobjects++;
14397  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
14398  }
14399 
14400 
14401  /*
14402  * Count objects to extract
14403  */
14404 
14405  nobjects = 0;
14406  nslits = cpl_table_get_nrow(objects);
14407 
14408  for (i = 0; i < nslits; i++) {
14409  for (j = 1; j < maxobjects; j++) {
14410  snprintf(name, MAX_COLNAME, "object_%d", j);
14411  if (cpl_table_is_valid(objects, name, i))
14412  nobjects++;
14413  }
14414  }
14415 
14416  if (nobjects == 0)
14417  return NULL;
14418 
14419  nx = cpl_image_get_size_x(science);
14420 
14421  output = cpl_calloc(3, sizeof(cpl_image *));
14422  extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14423  extr_sky = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14424  error = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14425 
14426 
14427  /*
14428  * Extract objects
14429  */
14430 
14431  nobjects = 0;
14432  for (i = 0; i < nslits; i++) {
14433  for (j = 1; j < maxobjects; j++) {
14434  snprintf(name, MAX_COLNAME, "object_%d", j);
14435  if (cpl_table_is_valid(objects, name, i)) {
14436  snprintf(name, MAX_COLNAME, "start_%d", j);
14437  ylow = cpl_table_get_int(objects, name, i, NULL);
14438  snprintf(name, MAX_COLNAME, "end_%d", j);
14439  yhig = cpl_table_get_int(objects, name, i, NULL);
14440  snprintf(name, MAX_COLNAME, "row_%d", j);
14441  nobjects = cpl_table_get_int(objects, name, i, NULL);
14442  sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
14443  if(science_var != NULL)
14444  sci_var_win = cpl_image_extract(science_var, 1, ylow+1, nx, yhig);
14445  skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
14446 /*
14447  * Cleaning the cosmics locally was really NOT a good idea...
14448  * I leave it here, commented out, to never forget this mistake!
14449 
14450  if (extraction) {
14451  mos_clean_cosmics(sciwin, gain, -1., -1.);
14452  }
14453  */
14454  mos_extraction(sciwin, sci_var_win, skywin, extracted, extr_sky, error,
14455  nobjects, extraction, ron, gain, ncombined);
14456 
14457  /*
14458  * Hidden check whether the spectrum was saturated or not
14459  */
14460 
14461  {
14462  cpl_image *total = cpl_image_add_create(sciwin, skywin);
14463  float *data = cpl_image_get_data_float(total);
14464  int size = cpl_image_get_size_x(total)
14465  * cpl_image_get_size_y(total);
14466  int k;
14467  char *saturation_level = getenv("SATURATION_LEVEL");
14468  float saturation = 62000.0;
14469  char *max_saturated = getenv("MAX_SATURATED");
14470  int max_satur = 10;
14471  int saturated;
14472 
14473  if (saturation_level)
14474  saturation = atof(saturation_level);
14475 
14476  if (max_saturated)
14477  max_satur = atoi(max_saturated);
14478 
14479  saturated = 0;
14480  for (k = 0; k < size; k++) {
14481  if (data[k] > saturation) {
14482  saturated++;
14483  if (saturated > max_satur) {
14484  break;
14485  }
14486  }
14487  }
14488 
14489  if (saturated > max_satur)
14490  saturated = 1;
14491  else
14492  saturated = 0;
14493 
14494  data = cpl_image_get_data(extracted);
14495  data[nobjects * nx] = saturated;
14496  }
14497 
14498  cpl_image_delete(sciwin);
14499  cpl_image_delete(skywin);
14500  nobjects++;
14501  }
14502  }
14503  }
14504 
14505  return output;
14506 
14507 }
14508 
14509 
14532 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave,
14533  double dispersion, int saturation,
14534  double *mfwhm, double *rmsfwhm,
14535  double *resolution, double *rmsres, int *nlines)
14536 {
14537  cpl_vector *vector;
14538 
14539  int i, j, n, m;
14540  int position, maxpos;
14541  int xlen, ylen;
14542  int sp, ep;
14543  int radius;
14544  int sradius = 40;
14545  int threshold = 250; /* Peak must be so many ADUs above min */
14546 
14547  int ifwhm;
14548  double fwhm;
14549  double *buffer;
14550  double min, max, halfmax;
14551  double cut = 1.5; /* To cut outliers from FWHM values (pixel) */
14552  double value, rms;
14553 
14554  float *data;
14555 
14556 
14557  *resolution = 0.0;
14558  *rmsres = 0.0;
14559  *nlines = 0;
14560 
14561  xlen = cpl_image_get_size_x(image);
14562  ylen = cpl_image_get_size_y(image);
14563  data = cpl_image_get_data(image);
14564 
14565  buffer = cpl_malloc(ylen * sizeof(double));
14566 
14567  /*
14568  * Closest pixel to specified wavelength.
14569  */
14570 
14571  position = floor((lambda - startwave) / dispersion + 0.5);
14572 
14573  sp = position - sradius;
14574  ep = position + sradius;
14575 
14576  if (sp < 0 || ep > xlen) {
14577  cpl_free(buffer);
14578  return 0;
14579  }
14580 
14581  for (i = 0, n = 0; i < ylen; i++) { /* For each row of each slit */
14582 
14583  /*
14584  * Search interval for peak. Abort if too close to image border.
14585  */
14586 
14587  radius = mos_lines_width(data + i*xlen + position - sradius,
14588  2*sradius + 1);
14589  if (radius < 5)
14590  radius = 5;
14591 
14592  sp = position - radius;
14593  ep = position + radius;
14594 
14595  if (sp < 0 || ep > xlen) {
14596  cpl_free(buffer);
14597  return 0;
14598  }
14599 
14600 
14601  /*
14602  * Determine min-max value and position.
14603  */
14604 
14605  maxpos = sp;
14606  min = max = data[sp + i * xlen];
14607  for (j = sp; j < ep; j++) {
14608  if (data[j + i * xlen] > max) {
14609  max = data[j + i * xlen];
14610  maxpos = j;
14611  }
14612  if (data[j + i * xlen] < min) {
14613  min = data[j + i * xlen];
14614  }
14615  }
14616 
14617  if (fabs(min) < 0.0000001) /* Truncated spectrum */
14618  continue;
14619 
14620  if (max - min < threshold) /* Low signal... */
14621  continue;
14622 
14623  if (max > saturation) /* Saturation */
14624  continue;
14625 
14626  /*
14627  * Determine FWHM counting pixels with value greater than
14628  * half of the max value, to the right and to the left of
14629  * the max. Linear interpolation between the pixels where
14630  * the transition happens.
14631  */
14632 
14633  halfmax = (max + min)/ 2.0;
14634 
14635  fwhm = 0.0;
14636  ifwhm = 0;
14637  for (j = maxpos; j < maxpos + radius; j++) {
14638  if (j < xlen) {
14639  if (data[j + i * xlen] < halfmax) {
14640  fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
14641  / (data[j - 1 + i * xlen] - data[j + i * xlen]);
14642  break;
14643  }
14644  ifwhm++;
14645  }
14646  }
14647 
14648  ifwhm = 0;
14649  for (j = maxpos; j > maxpos - radius; j--) {
14650  if (j >= 0) {
14651  if (data[j + i * xlen] < halfmax) {
14652  fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
14653  / (data[j + 1 + i * xlen] - data[j + i * xlen]);
14654  break;
14655  }
14656  ifwhm++;
14657  }
14658  }
14659 
14660  if (fwhm > 3.0) {
14661  buffer[n] = fwhm - 2.0;
14662  n++;
14663  }
14664 
14665  }
14666 
14667  if (n == 0) {
14668  cpl_free(buffer);
14669  return 0;
14670  }
14671 
14672  vector = cpl_vector_wrap(n, buffer);
14673  value = cpl_vector_get_median_const(vector);
14674  cpl_vector_unwrap(vector);
14675 
14676  rms = 0.0;
14677  for (i = 0, m = 0; i < n; i++) {
14678  if (fabs(buffer[i] - value) < cut) {
14679  rms += fabs(buffer[i] - value);
14680  m++;
14681  }
14682  }
14683 
14684  cpl_free(buffer);
14685 
14686  if (m < 3)
14687  return 0;
14688 
14689  rms /= m;
14690  rms *= 1.25; /* Factor to convert average deviation to sigma */
14691 
14692  value *= dispersion;
14693  rms *= dispersion;
14694 
14695  *mfwhm = value;
14696  *rmsfwhm = rms;
14697 
14698  *resolution = lambda / value;
14699  *rmsres = *resolution * rms / value;
14700 
14701  *nlines = m;
14702 
14703  return 1;
14704 }
14705 
14706 
14728 cpl_table *mos_resolution_table(cpl_image *image, double startwave,
14729  double dispersion, int saturation,
14730  cpl_vector *lines)
14731 {
14732 
14733  cpl_table *table;
14734  double *line;
14735  double fwhm;
14736  double rmsfwhm;
14737  double resolution;
14738  double rmsres;
14739  int nref;
14740  int nlines;
14741  int i;
14742 
14743 
14744  nref = cpl_vector_get_size(lines);
14745  line = cpl_vector_get_data(lines);
14746 
14747  table = cpl_table_new(nref);
14748  cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
14749  cpl_table_set_column_unit(table, "wavelength", "Angstrom");
14750  cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
14751  cpl_table_set_column_unit(table, "fwhm", "Angstrom");
14752  cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
14753  cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
14754  cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
14755  cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
14756  cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
14757 
14758  for (i = 0; i < nref; i++) {
14759  if (mos_spectral_resolution(image, line[i], startwave, dispersion,
14760  saturation, &fwhm, &rmsfwhm,
14761  &resolution, &rmsres, &nlines)) {
14762  cpl_table_set_double(table, "wavelength", i, line[i]);
14763  cpl_table_set_double(table, "fwhm", i, fwhm);
14764  cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
14765  cpl_table_set_double(table, "resolution", i, resolution);
14766  cpl_table_set_double(table, "resolution_rms", i, rmsres);
14767  cpl_table_set_int(table, "nlines", i, nlines);
14768  }
14769  else
14770  cpl_table_set_int(table, "nlines", i, 0);
14771  }
14772 
14773  if (cpl_table_has_valid(table, "wavelength"))
14774  return table;
14775 
14776  cpl_table_delete(table);
14777 
14778  return NULL;
14779 
14780 }
14781 
14782 
14800 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
14801  int ystart, int yend, double wstart, double wend)
14802 {
14803  const char *func = "mos_integrate_signal";
14804 
14805  double sum;
14806  float *sdata;
14807  float *wdata;
14808  int nx, ny;
14809  int x, y;
14810 
14811 
14812  if (image == NULL || wavemap == NULL) {
14813  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14814  return 0.0;
14815  }
14816 
14817  if (ystart > yend || wstart >= wend) {
14818  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14819  return 0.0;
14820  }
14821 
14822  nx = cpl_image_get_size_x(image);
14823  ny = cpl_image_get_size_y(image);
14824 
14825  if (!(nx == cpl_image_get_size_x(wavemap)
14826  && ny == cpl_image_get_size_y(wavemap))) {
14827  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
14828  return 0.0;
14829  }
14830 
14831  if (ystart < 0 || yend > ny) {
14832  cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
14833  return 0.0;
14834  }
14835 
14836  sdata = cpl_image_get_data(image);
14837  wdata = cpl_image_get_data(wavemap);
14838 
14839  sdata += ystart*nx;
14840  wdata += ystart*nx;
14841 
14842  sum = 0.0;
14843  for (y = ystart; y < yend; y++) {
14844  for (x = 0; x < nx; x++) {
14845  if (wdata[x] < wstart || wdata[x] > wend)
14846  continue;
14847  sum += sdata[x];
14848  }
14849  sdata += nx;
14850  wdata += nx;
14851  }
14852 
14853  return sum;
14854 
14855 }
14856 
14857 /****************************************************************************
14858  * From this point on, the instrument dependent functions are added:
14859  * they are functions that retrieve information that is stored in
14860  * the data headers in some instrument specific way, such as the
14861  * location of overscans, the slits positions on the telescope
14862  * focal plane, the gain factor, etc.
14863  */
14864 
14865 
14888 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
14889 {
14890  const char *func = "mos_load_slits_fors_mxu";
14891 
14892  cpl_table *slits;
14893  char keyname[MAX_COLNAME];
14894  const char *instrume;
14895  const char *target_name;
14896  float slit_x;
14897  float slit_y;
14898  float length;
14899  float slit_width;
14900 /* double arc2mm = 0.53316; */
14901  double arc2mm = 0.528;
14902  int nslits;
14903  int slit_id;
14904  int fors;
14905  int chip;
14906  int found;
14907 
14908  /*
14909  * The limits below are used to exclude from the loaded slit list
14910  * any slit that surely doesn't belong to the used chip. This is
14911  * a way to reduce the chance of ambiguous slit identification.
14912  */
14913 
14914  float low_limit1 = 10.0;
14915  float hig_limit2 = 30.0;
14916 
14917 
14918  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14919  return NULL;
14920  }
14921 
14922  if (header == NULL) {
14923  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14924  return NULL;
14925  }
14926 
14927 
14928  /*
14929  * See if this is FORS1 or FORS2;
14930  */
14931 
14932  instrume = cpl_propertylist_get_string(header, "INSTRUME");
14933 
14934  fors = 0;
14935  if (instrume[4] == '1')
14936  fors = 1;
14937  if (instrume[4] == '2')
14938  fors = 2;
14939 
14940  if (fors != 2) {
14941  cpl_msg_error(func, "Wrong instrument: %s\n"
14942  "FORS2 is expected for MXU data", instrume);
14943  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14944  return NULL;
14945  }
14946 
14947 
14948  /*
14949  * The master and slave chips can be identified by their positions
14950  * in the chip array in the case of FORS2 data (with fors1 the chip
14951  * is always 1). chip = 2 is the master, chip = 1 is the slave.
14952  */
14953 
14954  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14955 
14956  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14957  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14958  "in FITS header");
14959  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14960  return NULL;
14961  }
14962 
14963  if (chip != 1 && chip != 2) {
14964  cpl_msg_error(func, "Unexpected chip position in keyword "
14965  "ESO DET CHIP1 Y: %d", chip);
14966  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14967  return NULL;
14968  }
14969 
14970 
14971  /*
14972  * Count slits in header (excluding reference slits, and the slits
14973  * that _surely_ belong to the other chip)
14974  */
14975 
14976  nslits = 0;
14977  slit_id = 0;
14978  found = 1;
14979 
14980  while (found) {
14981  slit_id++;
14982  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14983  if (cpl_propertylist_has(header, keyname)) {
14984  slit_y = cpl_propertylist_get_double(header, keyname);
14985 
14986  if (chip == 1)
14987  if (slit_y < low_limit1)
14988  continue;
14989  if (chip == 2)
14990  if (slit_y > hig_limit2)
14991  continue;
14992 
14993  snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME",
14994  slit_id + 100);
14995  if (cpl_propertylist_has(header, keyname)) {
14996  target_name = cpl_propertylist_get_string(header, keyname);
14997  if (strncmp(target_name, "refslit", 7))
14998  nslits++;
14999  }
15000  else
15001  nslits++;
15002  }
15003  else
15004  found = 0;
15005  }
15006 
15007  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15008  cpl_msg_error(func, "%s while loading slits coordinates from "
15009  "FITS header", cpl_error_get_message());
15010  cpl_error_set_where(func);
15011  return NULL;
15012  }
15013 
15014  if (nslits == 0) {
15015  cpl_msg_error(func, "No slits coordinates found in header");
15016  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15017  return NULL;
15018  }
15019 
15020  slits = cpl_table_new(nslits);
15021  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15022  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15023  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15024  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15025  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15026  cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15027  cpl_table_set_column_unit(slits, "xtop", "pixel");
15028  cpl_table_set_column_unit(slits, "ytop", "pixel");
15029  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15030  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15031  cpl_table_set_column_unit(slits, "xwidth" , "mm");
15032 
15033  nslits = 0;
15034  slit_id = 0;
15035  found = 1;
15036  while (found) {
15037  slit_id++;
15038  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
15039  if (cpl_propertylist_has(header, keyname)) {
15040  slit_y = cpl_propertylist_get_double(header, keyname);
15041 
15042  if (chip == 1)
15043  if (slit_y < low_limit1)
15044  continue;
15045  if (chip == 2)
15046  if (slit_y > hig_limit2)
15047  continue;
15048 
15049  /*
15050  * Y-flip the slit position, to match CCD pixel coordinate
15051  * convention
15052  */
15053 
15054  slit_y = -slit_y;
15055 
15056  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
15057  slit_x = cpl_propertylist_get_double(header, keyname);
15058  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15059  cpl_table_delete(slits);
15060  cpl_msg_error(func, "Missing keyword %s in FITS header",
15061  keyname);
15062  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15063  return NULL;
15064  }
15065 
15066  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
15067  length = cpl_propertylist_get_double(header, keyname);
15068  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15069  cpl_table_delete(slits);
15070  cpl_msg_error(func, "Missing keyword %s in FITS header",
15071  keyname);
15072  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15073  return NULL;
15074  }
15075 
15076  length *= arc2mm;
15077 
15078  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d WIDTH", slit_id + 100);
15079  slit_width = cpl_propertylist_get_double(header, keyname);
15080  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15081  cpl_table_delete(slits);
15082  cpl_msg_error(func, "Missing keyword %s in FITS header",
15083  keyname);
15084  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15085  return NULL;
15086  }
15087 
15088  snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME",
15089  slit_id + 100);
15090  if (cpl_propertylist_has(header, keyname)) {
15091  target_name = cpl_propertylist_get_string(header, keyname);
15092  if (strncmp(target_name, "refslit", 7)) {
15093  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15094  cpl_table_set(slits, "xtop", nslits, slit_x);
15095  cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15096  cpl_table_set(slits, "xbottom", nslits, slit_x);
15097  cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15098  cpl_table_set(slits, "xwidth", nslits, slit_width);
15099  nslits++;
15100  }
15101  }
15102  else {
15103  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15104  cpl_table_set(slits, "xtop", nslits, slit_x);
15105  cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15106  cpl_table_set(slits, "xbottom", nslits, slit_x);
15107  cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15108  cpl_table_set(slits, "xwidth", nslits, slit_width);
15109  nslits++;
15110  }
15111  }
15112  else
15113  found = 0;
15114  }
15115 
15116  return slits;
15117 }
15118 
15119 
15143 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header,
15144  int * nslits_out_det)
15145 {
15146  const char *func = "mos_load_slits_fors_mos";
15147 
15148  cpl_table *slits;
15149  char keyname[MAX_COLNAME];
15150  const char *instrume;
15151  const char *chipname;
15152  float slit_x;
15153  int first_slit, last_slit;
15154  cpl_size nslits;
15155  int slit_id;
15156  int fors;
15157  int chip;
15158  int fors_is_old;
15159 
15160  /*
15161  * The Y coordinates of the slits are fixed
15162  */
15163 
15164  float ytop[19] = { 113.9, 101.3, 89.9, 77.3, 65.9, 53.3,
15165  41.9, 29.3, 17.9, 5.3, -6.1, -18.7,
15166  -30.1, -42.7, -54.1, -66.7, -78.1, -90.7,
15167  -102.1 };
15168  float ybottom[19] = { 102.1, 90.7, 78.1, 66.7, 54.1, 42.7,
15169  30.1, 18.7, 6.1, -5.3, -17.9, -29.3,
15170  -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
15171  -113.9 };
15172 
15173 
15174  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15175  return NULL;
15176  }
15177 
15178  if (header == NULL) {
15179  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15180  return NULL;
15181  }
15182 
15183 
15184  /*
15185  * See if this is FORS1 or FORS2;
15186  */
15187 
15188  instrume = cpl_propertylist_get_string(header, "INSTRUME");
15189 
15190  fors = 0;
15191  if (instrume[4] == '1')
15192  fors = 1;
15193  if (instrume[4] == '2')
15194  fors = 2;
15195 
15196  if (fors == 0) {
15197  cpl_msg_error(func, "Wrong instrument found in FITS header: %s",
15198  instrume);
15199  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15200  return NULL;
15201  }
15202 
15203  /* FIXME:
15204  * This is the way FORS1 data belong to the upgraded chips,
15205  * named "Marlene" and "Norma III". It's a quick solution,
15206  * there are hardcoded values here!!!
15207  */
15208 
15209  chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
15210 
15211  if (chipname[0] == 'M' || chipname[0] == 'N')
15212  fors_is_old = 0;
15213  else
15214  fors_is_old = 1;
15215 
15216  if (fors == 1 && fors_is_old) {
15217  first_slit = 1;
15218  last_slit = 19;
15219  }
15220  else {
15221 
15222  /*
15223  * The master and slave chips can be identified by their positions
15224  * in the chip array in the case of FORS2 data: chip = 2 is the
15225  * master, chip = 1 is the slave.
15226  */
15227 
15228  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15229 
15230  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15231  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15232  "in FITS header");
15233  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15234  return NULL;
15235  }
15236 
15237  if (chip != 1 && chip != 2) {
15238  cpl_msg_error(func, "Unexpected chip position in keyword "
15239  "ESO DET CHIP1 Y: %d", chip);
15240  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15241  return NULL;
15242  }
15243 
15244  if (chip == 1) {
15245  first_slit = 12;
15246  last_slit = 19;
15247  }
15248  else {
15249  first_slit = 1;
15250  last_slit = 11;
15251  }
15252  }
15253 
15254 
15255  /*
15256  * Count slits in header (excluding closed slits - i.e. those with
15257  * offsets greater than 115 mm - and the slits that do not belong
15258  * to this chip)
15259  */
15260 
15261  nslits = 0;
15262  slit_id = 0;
15263  for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15264  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15265  if (cpl_propertylist_has(header, keyname)) {
15266  slit_x = cpl_propertylist_get_double(header, keyname);
15267  if (fabs(slit_x) < 115.0)
15268  nslits++;
15269  else
15270  (*nslits_out_det)++;
15271  }
15272  else {
15273  cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
15274  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15275  return NULL;
15276  }
15277  }
15278 
15279  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15280  cpl_msg_error(func, "%s while loading slits coordinates from "
15281  "FITS header", cpl_error_get_message());
15282  cpl_error_set_where(func);
15283  return NULL;
15284  }
15285 
15286  if (nslits == 0) {
15287  cpl_msg_error(func, "No slits coordinates found in header");
15288  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15289  return NULL;
15290  }
15291 
15292  slits = cpl_table_new(nslits);
15293  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15294  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15295  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15296  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15297  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15298  cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15299  cpl_table_set_column_unit(slits, "xtop", "pixel");
15300  cpl_table_set_column_unit(slits, "ytop", "pixel");
15301  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15302  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15303  cpl_table_set_column_unit(slits, "xwidth" , "mm");
15304 
15305  nslits = 0;
15306  slit_id = 0;
15307  for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15308  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15309  slit_x = cpl_propertylist_get_double(header, keyname);
15310  if (fabs(slit_x) < 115.0) {
15311  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15312  cpl_table_set(slits, "xtop", nslits, slit_x);
15313  cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
15314  cpl_table_set(slits, "xbottom", nslits, slit_x);
15315  cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
15316  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d WIDTH", slit_id);
15317  double slit_width = cpl_propertylist_get_double(header, keyname);
15318  cpl_table_set(slits, "xwidth", nslits, slit_width);
15319  nslits++;
15320  }
15321  }
15322 
15323  return slits;
15324 }
15325 
15326 
15350 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
15351 {
15352  const char *func = "mos_load_slits_fors_lss";
15353 
15354  cpl_table *slits;
15355  char *slit_name;
15356  const char *instrume;
15357  int fors;
15358  int chip;
15359  float ytop;
15360  float ybottom;
15361 
15362  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15363  return NULL;
15364  }
15365 
15366  if (header == NULL) {
15367  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15368  return NULL;
15369  }
15370 
15371 
15372  /*
15373  * See if this is FORS1 or FORS2;
15374  */
15375 
15376  instrume = cpl_propertylist_get_string(header, "INSTRUME");
15377 
15378  fors = 0;
15379  if (instrume[4] == '1')
15380  fors = 1;
15381  if (instrume[4] == '2')
15382  fors = 2;
15383 
15384  if (fors == 0) {
15385  cpl_msg_error(func, "Wrong instrument found in FITS header: %s",
15386  instrume);
15387  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15388  return NULL;
15389  }
15390 
15391  if (fors == 1) {
15392  ytop = 109.94;
15393  ybottom = -109.94;
15394  }
15395  else {
15396 
15397  /*
15398  * The master and slave chips can be identified by their positions
15399  * in the chip array in the case of FORS2 data: chip = 2 is the
15400  * master, chip = 1 is the slave.
15401  */
15402 
15403  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15404 
15405  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15406  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15407  "in FITS header");
15408  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15409  return NULL;
15410  }
15411 
15412  if (chip != 1 && chip != 2) {
15413  cpl_msg_error(func, "Unexpected chip position in keyword "
15414  "ESO DET CHIP1 Y: %d", chip);
15415  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15416  return NULL;
15417  }
15418 
15419  if (chip == 1) {
15420  ytop = 30.0;
15421  ybottom = -109.94;
15422  }
15423  else {
15424  ytop = 109.94;
15425  ybottom = -20.0;
15426  }
15427  }
15428 
15429 
15430  slits = cpl_table_new(1);
15431  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15432  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15433  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15434  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15435  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15436  cpl_table_set_column_unit(slits, "xtop", "pixel");
15437  cpl_table_set_column_unit(slits, "ytop", "pixel");
15438  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15439  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15440 
15441  slit_name = (char *)cpl_propertylist_get_string(header,
15442  "ESO INS SLIT NAME");
15443 
15444  cpl_table_set(slits, "ytop", 0, ytop);
15445  cpl_table_set(slits, "ybottom", 0, ybottom);
15446 
15447  if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
15448  cpl_table_set_int(slits, "slit_id", 0, 1);
15449  cpl_table_set(slits, "xbottom", 0, -0.075);
15450  cpl_table_set(slits, "xtop", 0, 0.075);
15451  }
15452  else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
15453  cpl_table_set_int(slits, "slit_id", 0, 2);
15454  cpl_table_set(slits, "xbottom", 0, 5.895);
15455  cpl_table_set(slits, "xtop", 0, 6.105);
15456  }
15457  else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
15458  cpl_table_set_int(slits, "slit_id", 0, 3);
15459  cpl_table_set(slits, "xbottom", 0, -6.135);
15460  cpl_table_set(slits, "xtop", 0, -5.865);
15461  }
15462  else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
15463  cpl_table_set_int(slits, "slit_id", 0, 4);
15464  cpl_table_set(slits, "xbottom", 0, 11.815);
15465  cpl_table_set(slits, "xtop", 0, 12.185);
15466  }
15467  else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
15468  cpl_table_set_int(slits, "slit_id", 0, 5);
15469  cpl_table_set(slits, "xbottom", 0, -12.265);
15470  cpl_table_set(slits, "xtop", 0, -11.735);
15471  }
15472  else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
15473  cpl_table_set_int(slits, "slit_id", 0, 6);
15474  cpl_table_set(slits, "xbottom", 0, 17.655);
15475  cpl_table_set(slits, "xtop", 0, 18.345);
15476  }
15477  else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
15478  cpl_table_set_int(slits, "slit_id", 0, 7);
15479  cpl_table_set(slits, "xbottom", 0, -18.425);
15480  cpl_table_set(slits, "xtop", 0, -17.575);
15481  }
15482  else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
15483  cpl_table_set_int(slits, "slit_id", 0, 8);
15484  cpl_table_set(slits, "xbottom", 0, 23.475);
15485  cpl_table_set(slits, "xtop", 0, 24.525);
15486  }
15487  else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
15488  cpl_table_set_int(slits, "slit_id", 0, 9);
15489  cpl_table_set(slits, "xbottom", 0, -24.66);
15490  cpl_table_set(slits, "xtop", 0, -23.34);
15491  }
15492  else {
15493  cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
15494  slit_name);
15495  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15496  cpl_table_delete(slits);
15497  return NULL;
15498  }
15499 
15500  return slits;
15501 }
15502 
15503 
15518 double mos_get_gain_vimos(cpl_propertylist *header)
15519 {
15520  const char *func = "mos_get_gain_vimos";
15521 
15522  double gain = -1.0;
15523 
15524 
15525  if (cpl_error_get_code() != CPL_ERROR_NONE)
15526  return gain;
15527 
15528  if (header == NULL) {
15529  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15530  return gain;
15531  }
15532 
15533  gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
15534  if (cpl_error_get_code()) {
15535  cpl_error_set_where(func);
15536  gain = -1.0;
15537  }
15538 
15539  return gain;
15540 
15541 }
15542 
15543 
15563 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
15564 {
15565  const char *func = "mos_load_slits_vimos";
15566 
15567  cpl_table *slits;
15568  char keyname[MAX_COLNAME];
15569  float slit_x;
15570  float slit_y;
15571  float dim_x;
15572  float dim_y;
15573  int nslits;
15574  int slit_id;
15575  int curved;
15576  int i;
15577 
15578 
15579  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15580  return NULL;
15581  }
15582 
15583  if (header == NULL) {
15584  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15585  return NULL;
15586  }
15587 
15588  nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
15589 
15590  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15591  cpl_error_set_where(func);
15592  return NULL;
15593  }
15594 
15595  slits = cpl_table_new(nslits);
15596  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15597  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15598  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15599  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15600  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15601  cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15602  cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE);
15603  cpl_table_new_column(slits, "curved", CPL_TYPE_INT);
15604  cpl_table_set_column_unit(slits, "xtop", "pixel");
15605  cpl_table_set_column_unit(slits, "ytop", "pixel");
15606  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15607  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15608  cpl_table_set_column_unit(slits, "xwidth", "mm");
15609  cpl_table_set_column_unit(slits, "ywidth", "mm");
15610 
15611  for (i = 0; i < nslits; i++) {
15612  sprintf(keyname, "ESO INS SLIT%d ID", i+1);
15613  slit_id = cpl_propertylist_get_int(header, keyname);
15614  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15615  cpl_error_set_where(func);
15616  return NULL;
15617  }
15618  sprintf(keyname, "ESO INS SLIT%d X", i+1);
15619  slit_x = cpl_propertylist_get_double(header, keyname);
15620  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15621  cpl_error_set_where(func);
15622  return NULL;
15623  }
15624  sprintf(keyname, "ESO INS SLIT%d Y", i+1);
15625  slit_y = cpl_propertylist_get_double(header, keyname);
15626  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15627  cpl_error_set_where(func);
15628  return NULL;
15629  }
15630  sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
15631  dim_x = cpl_propertylist_get_double(header, keyname);
15632  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15633  cpl_error_set_where(func);
15634  return NULL;
15635  }
15636 
15637  sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1);
15638  if (cpl_propertylist_has(header, keyname)) {
15639  curved = 1;
15640  }
15641  else {
15642  sprintf(keyname, "ESO INS SLIT%d DIMY", i+1);
15643  curved = 0;
15644  }
15645  dim_y = cpl_propertylist_get_double(header, keyname);
15646  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15647  cpl_error_set_where(func);
15648  return NULL;
15649  }
15650 
15651  cpl_table_set_int(slits, "slit_id", i, slit_id);
15652  cpl_table_set(slits, "xtop", i, slit_x - dim_x/2);
15653  cpl_table_set(slits, "ytop", i, slit_y);
15654  cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2);
15655  cpl_table_set(slits, "ybottom", i, slit_y);
15656  cpl_table_set(slits, "xwidth", i, dim_x);
15657  cpl_table_set(slits, "ywidth", i, dim_y);
15658  cpl_table_set_int(slits, "curved", i, curved);
15659  }
15660 
15661  return slits;
15662 }
15663 
15664 
15674 int mos_check_multiplex(cpl_table *slits)
15675 {
15676  cpl_propertylist *sort;
15677  int nrow;
15678  int i, multiplex, xprev, xcur;
15679  double prev, cur;
15680  double tolerance = 1.0; // About spatially aligned slits (mm)
15681 
15682 
15683  /*
15684  * Create an auxiliary column containing a sort of integer
15685  * x coordinate of the slit, to guarantee that slits at the
15686  * same spatial offset are recognised immediately as in spectral
15687  * multiplexing.
15688  */
15689 
15690  sort = cpl_propertylist_new();
15691  cpl_propertylist_append_bool(sort, "xtop", 0);
15692  cpl_table_sort(slits, sort);
15693  cpl_propertylist_delete(sort);
15694 
15695  prev = cpl_table_get_double(slits, "xtop", 0, NULL);
15696  cpl_table_new_column(slits, "xind", CPL_TYPE_INT);
15697  cpl_table_set_int(slits, "xind", 0, prev); // cast to int is intentional
15698  nrow = cpl_table_get_nrow(slits);
15699  for (i = 1; i < nrow; i++) {
15700  cur = cpl_table_get_double(slits, "xtop", i, NULL);
15701  if (fabs(prev - cur) > tolerance)
15702  prev = cur;
15703  cpl_table_set_int(slits, "xind", i, prev);
15704  }
15705 
15706  /*
15707  * Now sort according to increasing (integer) x positions, and when
15708  * those are equal (multiplexed) according to the increasing y position.
15709  */
15710 
15711  sort = cpl_propertylist_new();
15712  cpl_propertylist_append_bool(sort, "xind", 0);
15713  cpl_propertylist_append_bool(sort, "ytop", 0);
15714  cpl_table_sort(slits, sort);
15715  cpl_propertylist_delete(sort);
15716 
15717  /*
15718  * Now assign to each slit its multiplex order.
15719  */
15720 
15721  multiplex = 0;
15722  cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT);
15723  xprev = cpl_table_get_int(slits, "xind", 0, NULL);
15724  cpl_table_set_int(slits, "multiplex", 0, multiplex);
15725  nrow = cpl_table_get_nrow(slits);
15726  for (i = 1; i < nrow; i++) {
15727  xcur = cpl_table_get_int(slits, "xind", i, NULL);
15728  if (xcur == xprev) {
15729  multiplex++;
15730  }
15731  else {
15732  xprev = xcur;
15733  multiplex = 0;
15734  }
15735  cpl_table_set_int(slits, "multiplex", i, multiplex);
15736  }
15737 
15738  cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT);
15739 
15740  cpl_table_erase_column(slits, "xind");
15741 
15742  return 1 + cpl_table_get_column_max(slits, "multiplex");
15743 
15744 }
15745 
15746 
15773 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header,
15774  int check_consistency)
15775 {
15776  const char *func = "mos_load_overscans_vimos";
15777 
15778  int nx = 0;
15779  int ny = 0;
15780  int px = 0;
15781  int py = 0;
15782  int ox = 0;
15783  int oy = 0;
15784  int vx = 0;
15785  int vy = 0;
15786  int nrows;
15787  cpl_table *overscans;
15788 
15789 
15790  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15791  cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15792  return NULL;
15793  }
15794 
15795  if (header == NULL) {
15796  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15797  return NULL;
15798  }
15799 
15800  if (cpl_propertylist_has(header, "NAXIS1"))
15801  nx = cpl_propertylist_get_int(header, "NAXIS1");
15802  if (cpl_propertylist_has(header, "NAXIS2"))
15803  ny = cpl_propertylist_get_int(header, "NAXIS2");
15804  if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
15805  px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
15806  if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
15807  py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
15808  if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
15809  ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
15810  if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
15811  oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
15812  if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
15813  vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
15814  if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
15815  vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
15816 
15817  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15818  cpl_msg_error(func, "Missing overscan keywords in header");
15819  cpl_error_set_where(func);
15820  return NULL;
15821  }
15822 
15823  if (px < 0 || py < 0 || ox < 0 || oy < 0) {
15824  cpl_msg_error(func, "Missing overscan keywords in header");
15825  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15826  return NULL;
15827  }
15828 
15829  if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
15830  if (check_consistency) {
15831  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15832  return NULL;
15833  }
15834  else {
15835  cpl_msg_debug(func, "Overscans description conflicts with "
15836  "reported image sizes, "
15837  "%d + %d + %d != %d or "
15838  "%d + %d + %d != %d",
15839  px, vx, ox, nx,
15840  py, vy, oy, ny);
15841  }
15842  }
15843 
15844  nrows = 0;
15845  if (px > 0)
15846  nrows++;
15847  if (ox > 0)
15848  nrows++;
15849  if (py > 0)
15850  nrows++;
15851  if (oy > 0)
15852  nrows++;
15853 
15854  if (nrows > 2) {
15855  cpl_msg_error(func, "Unexpected overscan regions "
15856  "(both in X and Y direction)");
15857  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15858  return NULL;
15859  }
15860 
15861 
15862  /*
15863  * A row is added for the description of the valid region of the
15864  * exposure the input header belongs to.
15865  */
15866 
15867  nrows++;
15868 
15869  overscans = cpl_table_new(nrows);
15870  cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15871  cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15872  cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15873  cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15874 
15875  nrows = 0;
15876 
15877  cpl_table_set_int(overscans, "xlow", nrows, px);
15878  cpl_table_set_int(overscans, "ylow", nrows, py);
15879  cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15880  cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15881  nrows++;
15882 
15883  if (px > 0) {
15884  cpl_table_set_int(overscans, "xlow", nrows, 0);
15885  cpl_table_set_int(overscans, "ylow", nrows, 0);
15886  cpl_table_set_int(overscans, "xhig", nrows, px);
15887  cpl_table_set_int(overscans, "yhig", nrows, ny);
15888  nrows++;
15889  }
15890 
15891  if (ox > 0) {
15892  cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15893  cpl_table_set_int(overscans, "ylow", nrows, 0);
15894  cpl_table_set_int(overscans, "xhig", nrows, nx);
15895  cpl_table_set_int(overscans, "yhig", nrows, ny);
15896  nrows++;
15897  }
15898 
15899  if (py > 0) {
15900  cpl_table_set_int(overscans, "xlow", nrows, 0);
15901  cpl_table_set_int(overscans, "ylow", nrows, 0);
15902  cpl_table_set_int(overscans, "xhig", nrows, nx);
15903  cpl_table_set_int(overscans, "yhig", nrows, py);
15904  nrows++;
15905  }
15906 
15907  if (oy > 0) {
15908  cpl_table_set_int(overscans, "xlow", nrows, 0);
15909  cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
15910  cpl_table_set_int(overscans, "xhig", nrows, nx);
15911  cpl_table_set_int(overscans, "yhig", nrows, ny);
15912  nrows++;
15913  }
15914 
15915  return overscans;
15916 
15917 }
15918 
15919 
15920 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
15921 {
15922  const char *func = "mos_load_overscans_fors";
15923 
15924  int nports;
15925  int nx = 0;
15926  int ny = 0;
15927  int px = 0;
15928  int py = 0;
15929  int ox = 0;
15930  int oy = 0;
15931  int rebin;
15932  int nrows;
15933  cpl_table *overscans;
15934 
15935 
15936  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15937  cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15938  return NULL;
15939  }
15940 
15941  if (header == NULL) {
15942  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15943  return NULL;
15944  }
15945 
15946  if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
15947  nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
15948 
15949  if (nports == 4 &&
15950  cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
15951  cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
15952 
15953  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
15954 
15955  overscans = cpl_table_new(3);
15956  cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15957  cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15958  cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15959  cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15960 
15961  px = 16 / rebin;
15962  ox = 16 / rebin;
15963  nx = 2080 / rebin;
15964  ny = 2048 / rebin;
15965  nrows = 0;
15966 
15967  cpl_table_set_int(overscans, "xlow", nrows, px);
15968  cpl_table_set_int(overscans, "ylow", nrows, py);
15969  cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15970  cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15971  nrows++;
15972 
15973  cpl_table_set_int(overscans, "xlow", nrows, 0);
15974  cpl_table_set_int(overscans, "ylow", nrows, 0);
15975  cpl_table_set_int(overscans, "xhig", nrows, px);
15976  cpl_table_set_int(overscans, "yhig", nrows, ny);
15977  nrows++;
15978 
15979  cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15980  cpl_table_set_int(overscans, "ylow", nrows, 0);
15981  cpl_table_set_int(overscans, "xhig", nrows, nx);
15982  cpl_table_set_int(overscans, "yhig", nrows, ny);
15983  nrows++;
15984  }
15985  else {
15986  overscans = mos_load_overscans_vimos(header, 0);
15987  }
15988 
15989  return overscans;
15990 
15991 }
15992 
16024 #define READY 1
16025 #ifdef READY
16026 
16027 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate,
16028  int samples, int order)
16029 {
16030 
16031  const char *func = "mos_montecarlo_polyfit";
16032 
16033  cpl_polynomial *p;
16034  cpl_polynomial *q;
16035  cpl_vector *listx;
16036  cpl_vector *listy;
16037  double err;
16038  double *x;
16039  double *px;
16040  double *x_eval;
16041  double *px_eval;
16042  double *sigma;
16043  double *vy;
16044  double *dy;
16045  int npoints, nevaluate;
16046  int i, j;
16047 
16048 
16049  if (points == NULL || evaluate == NULL) {
16050  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
16051  return NULL;
16052  }
16053 
16054  if (!cpl_table_has_column(points, "x")) {
16055  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16056  return NULL;
16057  }
16058 
16059  if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
16060  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16061  return NULL;
16062  }
16063 
16064  if (cpl_table_has_invalid(points, "x")) {
16065  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16066  return NULL;
16067  }
16068 
16069  if (!cpl_table_has_column(points, "y")) {
16070  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16071  return NULL;
16072  }
16073 
16074  if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
16075  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16076  return NULL;
16077  }
16078 
16079  if (cpl_table_has_invalid(points, "y")) {
16080  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16081  return NULL;
16082  }
16083 
16084  if (cpl_table_has_column(points, "y_err")) {
16085 
16086  if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
16087  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16088  return NULL;
16089  }
16090 
16091  if (cpl_table_has_invalid(points, "y_err")) {
16092  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16093  return NULL;
16094  }
16095  }
16096 
16097  if (!cpl_table_has_column(evaluate, "x")) {
16098  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16099  return NULL;
16100  }
16101 
16102  if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
16103  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16104  return NULL;
16105  }
16106 
16107  if (cpl_table_has_invalid(evaluate, "x")) {
16108  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16109  return NULL;
16110  }
16111 
16112  if (samples < 2 || order < 0) {
16113  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16114  return NULL;
16115  }
16116 
16117  npoints = cpl_table_get_nrow(points);
16118  listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
16119  listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
16120 
16121  p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
16122 
16123  if (!cpl_table_has_column(points, "y_err")) {
16124  err = sqrt(err);
16125  cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
16126  cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
16127  cpl_msg_info(func, "Error column not found - set to %f\n", err);
16128  }
16129 
16130  /*
16131  * Create columns containing modeled values at each x
16132  */
16133 
16134  if (cpl_table_has_column(points, "px"))
16135  cpl_table_erase_column(points, "px");
16136  cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
16137  cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
16138  x = cpl_table_get_data_double(points, "x");
16139  px = cpl_table_get_data_double(points, "px");
16140  for (i = 0; i < npoints; i++)
16141  px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
16142 
16143  nevaluate = cpl_table_get_nrow(evaluate);
16144 
16145  if (cpl_table_has_column(evaluate, "px"))
16146  cpl_table_erase_column(evaluate, "px");
16147  cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
16148  cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
16149  x_eval = cpl_table_get_data_double(evaluate, "x");
16150  px_eval = cpl_table_get_data_double(evaluate, "px");
16151  for (i = 0; i < nevaluate; i++)
16152  px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
16153 
16154  /*
16155  * Initialise column with sigma
16156  */
16157 
16158  if (cpl_table_has_column(evaluate, "sigma"))
16159  cpl_table_erase_column(evaluate, "sigma");
16160  cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
16161  cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
16162  sigma = cpl_table_get_data_double(evaluate, "sigma");
16163 
16164  /*
16165  * Compute varied y cordinates to fit
16166  */
16167 
16168  if (cpl_table_has_column(points, "vy"))
16169  cpl_table_erase_column(points, "vy");
16170  cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
16171  cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
16172  vy = cpl_table_get_data_double(points, "vy");
16173  dy = cpl_table_get_data_double(points, "y_err");
16174  cpl_vector_unwrap(listy);
16175  listy = cpl_vector_wrap(npoints, vy);
16176 
16177  for (i = 0; i < samples; i++) {
16178  for (j = 0; j < npoints; j++)
16179  vy[j] = px[j] + dy[j] * mos_randg(1);
16180  q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
16181  for (j = 0; j < nevaluate; j++)
16182  sigma[j] += fabs(px_eval[j]
16183  - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
16184  cpl_polynomial_delete(q);
16185  }
16186 
16187  /*
16188  * Factor 1.25 to convert average deviation to sigma
16189  */
16190 
16191  cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
16192  cpl_table_divide_scalar(evaluate, "sigma", samples);
16193 
16194  cpl_vector_unwrap(listx);
16195  cpl_vector_unwrap(listy);
16196 
16197  return p;
16198 }
16199 
16200 #endif
16201 
16224 cpl_error_code mos_randomise_image(cpl_image *image, double ron,
16225  double gain, double bias)
16226 {
16227  float *data;
16228  int npix, i;
16229 
16230 
16231  if (image == NULL)
16232  return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
16233 
16234  if (ron < 0.0 || gain <= FLT_EPSILON)
16235  return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
16236 
16237  data = cpl_image_get_data_float(image);
16238  npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image);
16239  ron *= ron;
16240 
16241  for (i = 0; i < npix; i++) {
16242  if (data[i] < bias) {
16243  data[i] += sqrt(ron) * mos_randg(1);
16244  }
16245  else {
16246  data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1);
16247  }
16248  }
16249 
16250  return CPL_ERROR_NONE;
16251 }
16252 
16253 
16268 cpl_error_code mos_refmask_find_gaps(cpl_mask *refmask,
16269  cpl_image *master_flat,
16270  double level)
16271 {
16272  int nx = cpl_mask_get_size_x(refmask);
16273  int ny = cpl_mask_get_size_y(refmask);
16274 
16275  int * xpos = cpl_calloc(sizeof(int), ny);
16276 
16277  cpl_image * filtered = cpl_image_duplicate(master_flat);
16278  cpl_mask * kernel = cpl_mask_new(9, 3);
16279  cpl_vector * v = cpl_vector_new(ny);
16280  cpl_vector * truev;
16281  int nvalid = 0;
16282  double * flats = cpl_vector_get_data(v);
16283 
16284  double median, stdev, delta;
16285 
16286  int i, kill;
16287 
16288  cpl_mask_not(kernel);
16289  cpl_image_filter_mask(filtered, master_flat, kernel,
16290  CPL_FILTER_MEDIAN, CPL_BORDER_COPY);
16291  cpl_mask_delete(kernel);
16292 
16293  for (i = 1; i <= ny; i++) {
16294  int j = 0;
16295 
16296  do j++;
16297  while (!cpl_mask_get(refmask, j, i) && j < nx);
16298 
16299  if (j < nx) {
16300  int rejected;
16301 
16302  xpos[i - 1] = j;
16303  flats[nvalid] = cpl_image_get(filtered, j, i, &rejected);
16304  nvalid++;
16305  }
16306  else {
16307  xpos[i - 1] = -1;
16308  }
16309  }
16310 
16311  if (nvalid == 0)
16312  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16313 
16314  truev = cpl_vector_wrap(nvalid, flats);
16315 
16316  median = cpl_vector_get_median(truev);
16317 
16318  if (level < 0.0)
16319  stdev = cpl_vector_get_stdev(truev);
16320 
16321  cpl_vector_unwrap(truev);
16322  cpl_vector_delete(v);
16323 
16324  for (i = 1; i <= ny; i++) {
16325  if (xpos[i - 1] > 0) {
16326  int rejected;
16327  double kappa = 1.5;
16328 
16329  delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median;
16330 
16331  if (level < 0.0)
16332  kill = fabs(delta) > stdev * kappa;
16333  else
16334  kill = delta < level;
16335 
16336  if (kill) {
16337  int j = 0;
16338 
16339  while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
16340  cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
16341  j++;
16342  }
16343  }
16344  }
16345  }
16346 
16347  cpl_image_delete(filtered);
16348  cpl_free(xpos);
16349 
16350  return cpl_error_get_code();
16351 }
16352 
16360 cpl_error_code mos_saturation_process(cpl_image * image)
16361 {
16362  int nx = cpl_image_get_size_x(image);
16363  int ny = cpl_image_get_size_y(image);
16364  int npix = nx * ny;
16365  float * sdata = cpl_image_get_data_float(image);
16366 
16367  int count, i, j, k;
16368 
16369  /*
16370  * This is used to avoid saturation level coded with pixel value zero
16371  * To make it more robust against random 0.0 values, check that also
16372  * next pixel along the spatial direction is 0.0.
16373  */
16374 
16375  //This could be applied only to raw images, but it is being applied
16376  //to already bias/overscan processed images, which doesn't make sense.
16377 // for (i = 0; i < npix - nx; i++)
16378 // if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
16379 // sdata[i] = 65535.0;
16380 
16381 // for (i = npix - nx; i < npix; i++)
16382 // if (sdata[i] == 0.0)
16383 // sdata[i] = 65535.0;
16384 
16385  /*
16386  * This is a dirty trick to overcome saturations (making up a false
16387  * tip on their flat tops). This should be useless with a better
16388  * peak detection algorithm.
16389  */
16390 
16391  for (i = 0; i < npix; i++) {
16392  if (sdata[i] >= 65535.0) {
16393  count = 0;
16394  for (j = i; j < npix; j++) {
16395  if (sdata[j] < 65535.0) {
16396  break;
16397  }
16398  else {
16399  count++;
16400  }
16401  }
16402  if (count < 30 && count > 2) {
16403  for (j = i; j < i + count/2; j++)
16404  sdata[j] = sdata[i] + 1000.0 * (j - i);
16405  if (count % 2 != 0) {
16406  sdata[j] = sdata[j-1] + 1000.0;
16407  j++;
16408  }
16409  for (k = j; k <= i + count; k++)
16410  sdata[k] = sdata[i] - 1000.0 * (k - i - count);
16411  i = k;
16412  }
16413  }
16414  }
16415 
16416  return cpl_error_get_code();
16417 }
16418 
16419 
16428 cpl_error_code mos_subtract_background(cpl_image * image)
16429 {
16430  /*
16431  * Create and subtract background
16432  */
16433 
16434  cpl_image * bimage = mos_arc_background(image, 15, 15);
16435  cpl_image_subtract(image, bimage);
16436  cpl_image_delete(bimage);
16437 
16438  return cpl_error_get_code();
16439 }
16440 
16441 
16458 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits,
16459  int nscience, float tolerance)
16460 {
16461  int i, j;
16462 
16463  cpl_table *summary;
16464  int summary_nobjs = 0;
16465 
16466  int nobjs;
16467 
16468  int nmatches;
16469  int nslits = cpl_table_get_nrow(slitss[0]);
16470 
16471  int maxobjs;
16472  int k, m;
16473  int nstokes, sstokes;
16474 
16475  cpl_table **work;
16476 
16477  work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
16478 
16479 
16480  /*
16481  * First we build a table listing the offset of each detected
16482  * object at each angle and each beam, from the bottom of each
16483  * slit spectrum, and the pair that slit spectrum belongs to.
16484  * This summary table will have as many rows as objects found
16485  * in total at all angles.
16486  */
16487 
16488  for (j = 0; j < nscience; j++) {
16489  int c_nobjs = mos_get_nobjects(slitss[j]);
16490  if (!c_nobjs)
16491  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16492  summary_nobjs += c_nobjs;
16493  }
16494 
16495  summary = cpl_table_new(summary_nobjs);
16496 
16497  cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
16498  cpl_table_new_column(summary, "pair", CPL_TYPE_INT);
16499  cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE);
16500  cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE);
16501 
16502  /*
16503  * Fill the summary table with data from all objects:
16504  */
16505 
16506  nobjs = 0;
16507 
16508  /* Loop on all object tables (one for each angle) */
16509  for (j = 0; j < nscience; j++) {
16510  int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
16511 
16512  /* Loop on all slits found on first - i.e., ALL - object table */
16513  for (k = 0; k < nslits; k++) {
16514 
16515  /* Loop on all objects found on each object table */
16516  for (m = 0; m < c_maxobjs; m++) {
16517  int null;
16518  char *name = cpl_sprintf("object_%d", m + 1);
16519  double obj = cpl_table_get_double(slitss[j], name, k, &null);
16520  int pos;
16521  int pair;
16522 
16523  cpl_free(name);
16524 
16525  if (null)
16526  break; /* No object #m+1 in this slit - go to next slit */
16527 
16528  /*
16529  * Copy necessary object data to summary table. Note
16530  * that the absolute object position (row) in the
16531  * rectified image is made relative to the bottom
16532  * position (row) of the current slit.
16533  */
16534 
16535  pos = cpl_table_get_int(slitss[j], "position", k, &null);
16536  pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
16537  cpl_table_set(summary, "absolute", nobjs, obj);
16538  cpl_table_set(summary, "pos", nobjs, pos);
16539  cpl_table_set(summary, "offset", nobjs, obj - pos);
16540  cpl_table_set(summary, "pair", nobjs, pair);
16541 
16542  nobjs++;
16543  }
16544  }
16545  }
16546 
16547 // cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
16548 
16549  /*
16550  * Perform the intersection: what are the objects belonging
16551  * to the same slit (same pair ordinary + extraordinary) which
16552  * are observed at the same offset at all angles? Those are
16553  * the polarimetric objects.
16554  */
16555 
16556  nmatches = 0;
16557  maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
16558 
16559  /*
16560  * We loop on the objects of the first-angle object table as
16561  * reference, and check whether those objects are present also
16562  * at *all* other angles. Note that the loop advances by pairs.
16563  * If the top (k = 0) slit spectrum is not an ordinary beam,
16564  * it is ignored. The loop advances by pairs, starting at the
16565  * first complete pair. It is implicitely assumed that the
16566  * slit spectrum on top is always from the ordinary beam, and
16567  * the spectrum below (k+1) its extraordinary match.
16568  */
16569 
16570  for (k = 0; k < nslits; k+=2) {
16571  int slitmatches = 0;
16572 
16573  if (k + 1 < nslits ) {
16574  if (cpl_table_get_int(slitss[0], "pair_id", k, NULL) !=
16575  cpl_table_get_int(slitss[0], "pair_id", k + 1, NULL)) {
16576 
16577  /*
16578  * This is not an ordinary beam - advance to next slit.
16579  */
16580 
16581  /* It will be incremented by two, so the effect is like k++ */
16582  k--;
16583 
16584  continue;
16585  }
16586  }
16587 
16588  for (m = 0; m < maxobjs; m++) {
16589  int null;
16590  char *name = cpl_sprintf("object_%d", m + 1);
16591  double obj = cpl_table_get_double(slitss[0], name, k, &null);
16592  double pos;
16593  int pair;
16594 
16595  char *name_obj = NULL;
16596  char *name_start = NULL;
16597  char *name_end = NULL;
16598  char *name_row = NULL;
16599  char *name_row_s = NULL;
16600 
16601  char *name_start_o = NULL;
16602  char *name_end_o = NULL;
16603  char *name_row_o = NULL;
16604  char *name_start_v = NULL;
16605  char *name_end_v = NULL;
16606  char *name_obj_v = NULL;
16607 
16608  int start, end;
16609  int length;
16610 
16611  int selected;
16612  int v, start_v, end_v;
16613  double min_v, obj_v;
16614 
16615 
16616  cpl_free(name);
16617 
16618  if (null)
16619  break;
16620 
16621  /*
16622  * Each object of the first object table belongs to a
16623  * slit spectrum (k). This slit spectrum has a position
16624  * in the rectified image, and it belongs to a given
16625  * ordinary + extraordinary pair.
16626  */
16627 
16628  pos = cpl_table_get_int(slitss[0], "position", k, &null);
16629  pair = cpl_table_get_int(slitss[0], "pair_id", k, &null);
16630 
16631  /*
16632  * Now from the summary table we can select all objects
16633  * which have the same offset (obj - pos) within all slit
16634  * spectra belonging to the same ordinary + extraordinary
16635  * pair (at all angles).
16636  */
16637 
16638  cpl_table_select_all(summary); /* Reset selection */
16639 
16640  cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
16641  cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
16642  obj - pos + tolerance);
16643  selected =
16644  cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
16645  obj - pos - tolerance);
16646 
16647 
16648  /*
16649  * If this object were observed at all angles (nscience) and
16650  * at all beams (2), we should have selected exactly 2*nscience
16651  * objects. If not, this is not a polarimetric object, and it
16652  * is discarded from the intersection.
16653  */
16654 
16655  if (selected != nscience * 2)
16656  continue;
16657 
16658  /*
16659  * If we reach this point we have found one valid polarimetric
16660  * object, that must be inserted in the intersection object
16661  * table.
16662  */
16663 
16664  slitmatches++;
16665 
16666  /*
16667  * Names of the columns of the output table where the
16668  * object information needs to be copied. Note that a
16669  * new column is created, the "row_stokes_#", where the
16670  * row number of the extracted polarimetric signal is
16671  * also computed. For the moment this column will be
16672  * left empty - it will be filled only when all matches
16673  * are collected.
16674  */
16675 
16676  name_obj = cpl_sprintf("object_%d", slitmatches);
16677  name_start = cpl_sprintf("start_%d", slitmatches);
16678  name_end = cpl_sprintf("end_%d", slitmatches);
16679  name_row = cpl_sprintf("row_%d", slitmatches);
16680  name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
16681 
16682  /*
16683  * Names of the columns of the input table where the
16684  * object information is available.
16685  */
16686 
16687  name_start_o = cpl_sprintf("start_%d", m + 1);
16688  name_end_o = cpl_sprintf("end_%d", m + 1);
16689  name_row_o = cpl_sprintf("row_%d", m + 1);
16690 
16691  /*
16692  * If the output columns do not exist yet, create them.
16693  */
16694 
16695  if (!cpl_table_has_column(origslits, name_obj)) {
16696  cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
16697  cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
16698  cpl_table_new_column(origslits, name_end, CPL_TYPE_INT);
16699  cpl_table_new_column(origslits, name_row, CPL_TYPE_INT);
16700  cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
16701  }
16702 
16703  /*
16704  * The current slit spectrum is k. The slit spectrum immediately
16705  * below (in the rectified image) is k+1. We need the length of
16706  * the spectrum below for computing the _absolute_ coordinates
16707  * of the objects in the rectified image in both beams.
16708  */
16709 
16710  length = cpl_table_get_int(origslits, "length", k + 1, &null);
16711 
16712  /* NEW:
16713  * Names of the columns of the input table where
16714  * the information of the corresponding object of
16715  * the next beam is available.
16716  */
16717 
16718  for (v = 0; v < maxobjs; v++) {
16719  char *name_v = cpl_sprintf("object_%d", v + 1);
16720  double obj_v = cpl_table_get_double(slitss[0], name_v,
16721  k + 1, &null);
16722 
16723  cpl_free(name_v);
16724 
16725  if (null)
16726  break;
16727 
16728  if (v) {
16729  if (fabs(obj - length - obj_v) < min_v) {
16730  min_v = fabs(obj - length - obj_v);
16731  cpl_free(name_start_v);
16732  cpl_free(name_end_v);
16733  cpl_free(name_obj_v);
16734  name_start_v = cpl_sprintf("start_%d", v + 1);
16735  name_end_v = cpl_sprintf("end_%d", v + 1);
16736  name_obj_v = cpl_sprintf("object_%d", v + 1);
16737  }
16738  }
16739  else {
16740  min_v = fabs(obj - length - obj_v);
16741  name_start_v = cpl_sprintf("start_%d", v + 1);
16742  name_end_v = cpl_sprintf("end_%d", v + 1);
16743  name_obj_v = cpl_sprintf("object_%d", v + 1);
16744  }
16745  }
16746 
16747  /*
16748  * Read from the first input object table (first angle)
16749  * the spatial window enclosing the object.
16750  */
16751 
16752  start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
16753  end = cpl_table_get_int(slitss[0], name_end_o, k, &null);
16754 
16755  /* NEW:
16756  * Spatial window of the matching object in the next beam.
16757  */
16758 
16759  start_v = cpl_table_get_int(slitss[0], name_start_v, k + 1, &null);
16760  end_v = cpl_table_get_int(slitss[0], name_end_v, k + 1, &null);
16761  obj_v = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null);
16762 
16763  /*
16764  * Write the object coordinates in the same slit, and in the
16765  * slit below. Note that here we assume that all slits were
16766  * traced perfectly, and we compute the theoretical coords
16767  * (obj - length) within the next slit spectrum (k + 1). In
16768  * principle we should read them as well from the input
16769  * table!
16770  */
16771 
16772  cpl_table_set_double(origslits, name_obj, k, obj);
16773  cpl_table_set_double(origslits, name_obj, k + 1, obj_v);
16774  // cpl_table_set_double(origslits, name_obj, k + 1, obj - length);
16775 
16776  cpl_table_set_int(origslits, name_start, k, start);
16777  cpl_table_set_int(origslits, name_start, k + 1, start_v);
16778  // cpl_table_set_int(origslits, name_start, k + 1, start - length);
16779 
16780  cpl_table_set_int(origslits, name_end, k, end);
16781  cpl_table_set_int(origslits, name_end, k + 1, end_v);
16782  // cpl_table_set_int(origslits, name_end, k + 1, end - length);
16783 
16784  /*
16785  * "nmatches" is counting at what "reduced" image row the
16786  * extracted spectra are. Note that this is s preliminary
16787  * numbering - which is wrong: other objects may be found
16788  * in the same slit, and then the indeces would not be in
16789  * sequence. What is important is that at the end of this
16790  * loop "nmatches" would be the total number of matching
16791  * objects. The two cpl_table_set_int() calls made here
16792  * cannot be removed - they "validate" those table elements
16793  * (see ahead).
16794  */
16795 
16796  cpl_table_set_int(origslits, name_row, k, nmatches);
16797  nmatches++;
16798  cpl_table_set_int(origslits, name_row, k + 1, nmatches);
16799  nmatches++;
16800 
16801  cpl_free(name_obj);
16802  cpl_free(name_start);
16803  cpl_free(name_end);
16804  cpl_free(name_row);
16805  cpl_free(name_row_s);
16806 
16807  cpl_free(name_start_o);
16808  cpl_free(name_end_o);
16809  cpl_free(name_row_o);
16810 
16811  cpl_free(name_start_v); name_start_v = NULL;
16812  cpl_free(name_end_v); name_end_v = NULL;
16813  cpl_free(name_obj_v); name_obj_v = NULL;
16814  }
16815  }
16816 
16817  /*
16818  * The summary table has fulfilled its function. If no matching
16819  * objects are found, the function returns with an error.
16820  */
16821 
16822  cpl_table_delete(summary);
16823 
16824  if (!nmatches)
16825  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16826 
16827  /*
16828  * Now we consider the resulting intersection object table,
16829  * listing all matches. As seen, the image row number reported
16830  * in the columns "row_#" was not really performed sequentially.
16831  * We need to renumber sequentially...
16832  * We need also to fill the "row_stokes_#" column the way the
16833  * extracted polarimetric signal will be stored in the
16834  * reduced_pol_images...
16835  */
16836 
16837  maxobjs = mos_get_maxobjs_per_slit(origslits);
16838  nstokes = nmatches / 2; /* nmatches is always an even number */
16839 
16840  for (k = 0; k < nslits; k++) {
16841  if (k % 2) { /* Extraordinary beam */
16842  nstokes = sstokes; /* Use same start value as for ordinary */
16843  }
16844  else { /* Ordinary beam */
16845  sstokes = nstokes; /* Memorise start value at ordinary beam */
16846  }
16847 
16848  for (m = 0; m < maxobjs; m++) {
16849  char *name = cpl_sprintf("row_%d", m + 1);
16850  char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
16851 
16852  if (!cpl_table_is_valid(origslits, name, k)) {
16853  cpl_free(name);
16854  cpl_free(namestokes);
16855  break;
16856  }
16857  else {
16858  nmatches--;
16859  nstokes--;
16860  cpl_table_set_int(origslits, name, k, nmatches);
16861  cpl_table_set_int(origslits, namestokes, k, nstokes);
16862  }
16863 
16864  cpl_free(name);
16865  cpl_free(namestokes);
16866  }
16867  }
16868 
16869 
16870  /*
16871  * This is done to avoid the NULL value is zero (it would invalidate
16872  * also the row_# = 0 or start_# = 0 for an object), and to enable
16873  * working directly with the column data buffers, when using this
16874  * table afterwards.
16875  */
16876 
16877  for (j = 0; j < maxobjs; j++) {
16878  char *name = cpl_sprintf("object_%d", j + 1);
16879  cpl_table_fill_invalid_double(origslits, name, -1);
16880  cpl_free(name);
16881 
16882  name = cpl_sprintf("start_%d", j + 1);
16883  cpl_table_fill_invalid_int(origslits, name, -1);
16884  cpl_free(name);
16885 
16886  name = cpl_sprintf("end_%d", j + 1);
16887  cpl_table_fill_invalid_int(origslits, name, -1);
16888  cpl_free(name);
16889 
16890  name = cpl_sprintf("row_%d", j + 1);
16891  cpl_table_fill_invalid_int(origslits, name, -1);
16892  cpl_free(name);
16893 
16894  name = cpl_sprintf("row_stokes_%d", j + 1);
16895  cpl_table_fill_invalid_int(origslits, name, -1);
16896  cpl_free(name);
16897  }
16898 
16899  /*********************************************************************
16900  * This tail has been added to propagate the selection of valid
16901  * objects also to the input slitss[] tables. Just eliminate all
16902  * this final part to suppress this behaviour.
16903  */
16904 
16905  /*
16906  * First of all, make a working copy and remove all columns related
16907  * to objects from the input object tables.
16908  */
16909 
16910  for (i = 0; i < nscience; i++) {
16911  int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
16912 
16913  work[i] = cpl_table_duplicate(slitss[i]);
16914 
16915  for (m = 0; m < c_maxobjs; m++) {
16916  char *object_o = cpl_sprintf("object_%d", m + 1);
16917  char *start_o = cpl_sprintf("start_%d", m + 1);
16918  char *end_o = cpl_sprintf("end_%d", m + 1);
16919  char *row_o = cpl_sprintf("row_%d", m + 1);
16920 
16921  cpl_table_erase_column(slitss[i], object_o);
16922  cpl_table_erase_column(slitss[i], start_o);
16923  cpl_table_erase_column(slitss[i], end_o);
16924  cpl_table_erase_column(slitss[i], row_o);
16925  }
16926  }
16927 
16928  /*
16929  * Now just consider all the objects in the intersection table.
16930  */
16931 
16932  for (k = 0; k < nslits; k++) {
16933  for (j = 0; j < maxobjs; j++) {
16934  double object_w, object_r;
16935  int row_w;
16936 
16937  char *object_i = cpl_sprintf("object_%d", j + 1);
16938  char *start_i = cpl_sprintf("start_%d", j + 1);
16939  char *end_i = cpl_sprintf("end_%d", j + 1);
16940  char *row_i = cpl_sprintf("row_%d", j + 1);
16941 
16942 
16943  if (!cpl_table_is_valid(origslits, object_i, k))
16944  break;
16945 
16946  /*
16947  * We have found a valid object (valid because it belongs
16948  * to the intersection). Now we look for this object in each
16949  * one of the original tables, we get its parameters, and
16950  * copy them at the right position (i.e., same position as
16951  * in intersection table). The object will be the one closest
16952  * to the object position (column object_i) in the intersection
16953  * table. Note that we examine the same row, k, in all tables.
16954  */
16955 
16956  object_w = cpl_table_get_double(origslits, object_i, k, NULL);
16957  row_w = cpl_table_get_int (origslits, row_i, k, NULL);
16958 
16959  for (i = 0; i < nscience; i++) {
16960  int c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
16961  int minpos;
16962  double mindiff, diff;
16963  char *object_o;
16964  char *start_o;
16965  char *end_o;
16966  char *row_o;
16967 
16968  for (m = 0; m < c_maxobjs; m++) {
16969  object_o = cpl_sprintf("object_%d", m + 1);
16970  start_o = cpl_sprintf("start_%d", m + 1);
16971  end_o = cpl_sprintf("end_%d", m + 1);
16972  row_o = cpl_sprintf("row_%d", m + 1);
16973 
16974  if (!cpl_table_is_valid(work[i], object_o, k))
16975  break;
16976 
16977  object_r = cpl_table_get_double(work[i], object_o, k, NULL);
16978  //row_r = cpl_table_get_int (work[i], row_o, k, NULL);
16979 
16980  diff = fabs(object_w - object_r);
16981  if (m) {
16982  if (mindiff > diff) {
16983  mindiff = diff;
16984  minpos = m;
16985  }
16986  }
16987  else {
16988  mindiff = diff;
16989  minpos = 0;
16990  }
16991 
16992  cpl_free(object_o);
16993  cpl_free(start_o);
16994  cpl_free(end_o);
16995  cpl_free(row_o);
16996  }
16997 
16998  object_o = cpl_sprintf("object_%d", minpos + 1);
16999  start_o = cpl_sprintf("start_%d", minpos + 1);
17000  end_o = cpl_sprintf("end_%d", minpos + 1);
17001  row_o = cpl_sprintf("row_%d", minpos + 1);
17002 
17003  if (!cpl_table_has_column(slitss[i], object_i)) {
17004  cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
17005  cpl_table_new_column(slitss[i], start_i, CPL_TYPE_INT);
17006  cpl_table_new_column(slitss[i], end_i, CPL_TYPE_INT);
17007  cpl_table_new_column(slitss[i], row_i, CPL_TYPE_INT);
17008  cpl_table_fill_invalid_double(slitss[i], object_i, -1);
17009  cpl_table_fill_invalid_int (slitss[i], start_i, -1);
17010  cpl_table_fill_invalid_int (slitss[i], end_i, -1);
17011  cpl_table_fill_invalid_int (slitss[i], row_i, -1);
17012  }
17013 
17014  cpl_table_set_double(slitss[i], object_i, k,
17015  cpl_table_get_double(work[i], object_o,
17016  k, NULL));
17017  cpl_table_set_int(slitss[i], start_i , k,
17018  cpl_table_get_int(work[i], start_o, k, NULL));
17019  cpl_table_set_int(slitss[i], end_i , k,
17020  cpl_table_get_int(work[i], end_o, k, NULL));
17021  cpl_table_set_int(slitss[i], row_i , k, row_w);
17022 
17023  cpl_free(object_o);
17024  cpl_free(start_o);
17025  cpl_free(end_o);
17026  cpl_free(row_o);
17027  }
17028 
17029  cpl_free(object_i);
17030  cpl_free(start_i);
17031  cpl_free(end_i);
17032  cpl_free(row_i);
17033  }
17034  }
17035 
17036  for (i = 0; i < nscience; i++)
17037  cpl_table_delete(work[i]);
17038 
17039  cpl_free(work);
17040 
17041 
17042  return cpl_error_get_code();
17043 }
17044 
17045 
17053 int mos_get_maxobjs_per_slit(cpl_table * slits)
17054 {
17055  int maxobjs = 1;
17056 
17057  char * colname = cpl_sprintf("object_%d", maxobjs);
17058 
17059  while (cpl_table_has_column(slits, colname)) {
17060  maxobjs++;
17061  cpl_free(colname);
17062  colname = cpl_sprintf("object_%d", maxobjs);
17063  }
17064 
17065  cpl_free(colname);
17066 
17067  maxobjs--;
17068 
17069  return maxobjs;
17070 }
17071 
17079 int mos_get_nobjects(cpl_table * slits)
17080 {
17081  int nobjs = 0;
17082 
17083  int nslits = cpl_table_get_nrow(slits);
17084  int maxobjs = mos_get_maxobjs_per_slit(slits);
17085 
17086  int k, m;
17087 
17088  for (k = 0; k < nslits; k++) {
17089  for (m = 0; m < maxobjs; m++) {
17090  char * name = cpl_sprintf("object_%d", m + 1);
17091  int null = !cpl_table_is_valid(slits, name, k);
17092 
17093  cpl_free(name);
17094 
17095  if (null) break;
17096  else nobjs++;
17097  }
17098  }
17099 
17100  return nobjs;
17101 }
17102 
17110 int mos_check_slits(cpl_table *slits, float rescale)
17111 {
17112 
17113  cpl_propertylist *sort;
17114 
17115  int nslits = cpl_table_get_nrow(slits);
17116 
17117  int k, null;
17118 
17119  const float interval = 90.0 * rescale;
17120  const float offset = (90.0 - 5) * rescale;
17121 
17122 
17123  for (k = 0; k < nslits; k++) {
17124  double ytop = cpl_table_get_double(slits, "ytop", k, &null);
17125  double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17126 
17127  double xtop = cpl_table_get_double(slits, "xtop", k, &null);
17128  double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
17129 
17130  int nmiss = (int)((ytop - ybottom) / interval + 0.5);
17131 
17132  if (nmiss > 1) {
17133  cpl_msg_warning(cpl_func,
17134  "Some slits could not be properly detected. "
17135  "There might be accountable inaccuracies.");
17136  while (nmiss > 1) {
17137  cpl_table_set_size(slits, nslits + 1);
17138 
17139  /* Fill in new slit 'cut' */
17140 
17141  /* x coordinates be the same (acceptable approximation) */
17142  cpl_table_set_double(slits, "xtop", nslits, xtop);
17143  cpl_table_set_double(slits, "xbottom", nslits, xbottom);
17144 
17145  /* Cut */
17146  if (k == 0) {
17147  cpl_table_set_double(slits, "ybottom", nslits, ybottom);
17148  cpl_table_set_double(slits, "ytop", nslits, ybottom
17149  + offset);
17150  ybottom += interval;
17151  cpl_table_set_double(slits, "ybottom", k, ybottom);
17152  } else {
17153  cpl_table_set_double(slits, "ytop", nslits, ytop);
17154  cpl_table_set_double(slits, "ybottom", nslits, ytop
17155  - offset);
17156  ytop -= interval;
17157  cpl_table_set_double(slits, "ytop", k, ytop);
17158  }
17159 
17160  nslits++; nmiss--;
17161  }
17162  }
17163  }
17164 
17165  sort = cpl_propertylist_new();
17166  cpl_propertylist_append_bool(sort, "ytop", 1);
17167  cpl_table_sort(slits, sort);
17168  cpl_propertylist_delete(sort);
17169 
17170  /*
17171  * Add here an ad hoc check on the last slit: is it too long
17172  * (by more than 10%)? Then shorten it...
17173  */
17174 
17175  k = cpl_table_get_nrow(slits) - 1;
17176 
17177  {
17178  double ytop = cpl_table_get_double(slits, "ytop", k, &null);
17179  double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17180  double length = (ytop - ybottom) / interval;
17181 
17182  if (length > 1.1) {
17183  cpl_table_set_double(slits, "ybottom", k, ytop - offset);
17184  }
17185 
17186  }
17187 
17188  return 0;
17189 }
17190 
17213 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header,
17214  int * nslits_out_det)
17215 {
17216  int m, null;
17217  int halfsize;
17218 
17219  cpl_propertylist * sort;
17220  cpl_table * slits;
17221 
17222  slits = mos_load_slits_fors_mos(header, nslits_out_det);
17223  halfsize = cpl_table_get_nrow(slits);
17224 
17225  cpl_table_set_size(slits, 2 * halfsize);
17226 
17227  for (m = 0; m < halfsize; m++) {
17228 
17229  double gap = 1.4;
17230 
17231  double length =
17232  cpl_table_get(slits, "ytop", m, &null) -
17233  cpl_table_get(slits, "ybottom", m, &null);
17234 
17235  if (m) {
17236  double interval =
17237  cpl_table_get(slits, "ybottom", m - 1, &null) -
17238  cpl_table_get(slits, "ytop", m, &null);
17239 
17240  gap = (interval - length) / 2;
17241  }
17242 
17243  cpl_table_set(slits, "slit_id", m + halfsize,
17244  cpl_table_get(slits, "slit_id", m, &null) - 1);
17245 
17246  cpl_table_set(slits, "xtop", m + halfsize,
17247  cpl_table_get(slits, "xtop", m, &null));
17248 
17249  cpl_table_set(slits, "xbottom", m + halfsize,
17250  cpl_table_get(slits, "xbottom", m, &null));
17251 
17252  cpl_table_set(slits, "ytop", m + halfsize,
17253  cpl_table_get(slits, "ytop", m, &null) + gap + length);
17254 
17255  cpl_table_set(slits, "ybottom", m + halfsize,
17256  cpl_table_get(slits, "ytop", m, &null) + gap);
17257  }
17258 
17259  for (m = 0; m < 2 * halfsize; m++) {
17260  cpl_table_set(slits, "ytop", m,
17261  cpl_table_get(slits, "ytop", m, &null) - 5.3);
17262 
17263  cpl_table_set(slits, "ybottom", m,
17264  cpl_table_get(slits, "ybottom", m, &null) - 5.3);
17265 
17266  }
17267 
17268  sort = cpl_propertylist_new();
17269  cpl_propertylist_append_bool(sort, "ytop", 1);
17270  cpl_table_sort(slits, sort);
17271 
17272  cpl_propertylist_delete(sort);
17273 
17274  return slits;
17275 }
17276 
17277 int * fors_get_nobjs_perslit(cpl_table * slits)
17278 {
17279  int nslits = cpl_table_get_nrow(slits);
17280  int maxobjs = mos_get_maxobjs_per_slit(slits);
17281 
17282  int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
17283 
17284  int k, m;
17285 
17286  for (k = 0; k < nslits; k++) {
17287  int nobjs = 0;
17288  for (m = 0; m < maxobjs; m++) {
17289  char * name = cpl_sprintf("object_%d", m + 1);
17290  int null = !cpl_table_is_valid(slits, name, k);
17291 
17292  cpl_free(name);
17293 
17294  if (null) break;
17295  else nobjs++;
17296  }
17297 
17298  nobjs_per_slit[k] = nobjs;
17299  }
17300 
17301  return nobjs_per_slit;
17302 }
17303 
17304 double fors_get_object_position(cpl_table *slits, int slit, int object)
17305 {
17306  char *name = cpl_sprintf("object_%d", object);
17307  double position;
17308 
17309  position = cpl_table_get_double(slits, name, slit, NULL)
17310  - cpl_table_get_int(slits, "position", slit, NULL);
17311 
17312  cpl_free(name);
17313 
17314  return position;
17315 }
17316 
17317 int mos_rebin_signal(cpl_image **image, int rebin)
17318 {
17319  cpl_image *rebinned;
17320 
17321 
17322  if (*image == NULL)
17323  return 1;
17324 
17325  if (rebin == 1)
17326  return 0;
17327 
17328  rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1);
17329 
17330  cpl_image_delete(*image);
17331 
17332  *image = rebinned;
17333 
17334  return 0;
17335 }
17336 
17337 int mos_rebin_error(cpl_image **image, int rebin)
17338 {
17339  if (*image == NULL)
17340  return 1;
17341 
17342  if (rebin == 1)
17343  return 0;
17344 
17345  cpl_image_power(*image, 2);
17346  mos_rebin_signal(image, rebin);
17347  cpl_image_power(*image, 0.5);
17348 
17349  return 0;
17350 }
17351 
17352 /*
17353  * @brief
17354  * Map table values into a 1D image
17355  *
17356  * @param image Target image
17357  * @param start Coordinate of first pixel in image
17358  * @param step Coordinate step for one pixel in image
17359  * @param table Source table
17360  * @param xname Name of coordinate column
17361  * @param yname Name of values column
17362  *
17363  * @return 0 on success
17364  *
17365  * The values in @em yname are linearly interpolated at the @em image
17366  * pixel coordinates. The @em image must have Nx1 size.
17367  */
17368 
17369 int map_table(cpl_image *image, double start, double step,
17370  cpl_table *table, const char *xname, const char *yname)
17371 {
17372  int length = cpl_image_get_size_x(image);
17373  int nrows = cpl_table_get_nrow(table);
17374  float *data = cpl_image_get_data_float(image);
17375  float *fdata = NULL;
17376  double *xdata = NULL;
17377  double *ydata = NULL;
17378  cpl_type xtype = cpl_table_get_column_type(table, xname);
17379  cpl_type ytype = cpl_table_get_column_type(table, yname);
17380  double xzero, pos;
17381  int i, j, n;
17382 
17383 
17384  /*
17385  * Initialization of output image at 0.0 - this value is left
17386  * on non-overlapping portions.
17387  */
17388 
17389  for (i = 0; i < length; i++)
17390  data[i] = 0.0;
17391 
17392 
17393  /*
17394  * Do everything in double precision
17395  */
17396 
17397  if (xtype == CPL_TYPE_FLOAT) {
17398  fdata = cpl_table_get_data_float(table, xname);
17399  xdata = cpl_malloc(nrows * sizeof(double));
17400  for (i = 0; i < nrows; i++) {
17401  xdata[i] = fdata[i];
17402  }
17403  }
17404  else {
17405  xdata = cpl_table_get_data_double(table, xname);
17406  }
17407 
17408  if (ytype == CPL_TYPE_FLOAT) {
17409  fdata = cpl_table_get_data_float(table, yname);
17410  ydata = cpl_malloc(nrows * sizeof(double));
17411  for (i = 0; i < nrows; i++) {
17412  ydata[i] = fdata[i];
17413  }
17414  }
17415  else {
17416  ydata = cpl_table_get_data_double(table, yname);
17417  }
17418 
17419  /*
17420  * Mapping
17421  */
17422 
17423  n = 0;
17424  xzero = xdata[n];
17425 
17426  for (i = 0; i < length; i++) {
17427  pos = start + step * i;
17428  if (pos < xzero)
17429  continue;
17430  for (j = n; j < nrows; j++) {
17431  if (xdata[j] > pos) {
17432  n = j;
17433  data[i] = ydata[j-1]
17434  + (ydata[j] - ydata[j-1])
17435  * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]);
17436  break;
17437  }
17438  }
17439  }
17440 
17441  if (xtype == CPL_TYPE_FLOAT)
17442  cpl_free(xdata);
17443 
17444  if (ytype == CPL_TYPE_FLOAT)
17445  cpl_free(ydata);
17446 
17447  return 0;
17448 }
17449 
17450 
17451 /*
17452  * @brief
17453  * Fit overall trend of a Nx1 image
17454  *
17455  * @param image Values to smooth
17456  * @param order Order of fitting polynomial
17457  * @param hw Half width of smoothing window
17458  *
17459  * @return Smoothed image, or NULL on failure.
17460  *
17461  * Heavily smooth and fit data in the input Nx1 size @em image.
17462  */
17463 
17464 static cpl_image *polysmooth(cpl_image *image, int order, int hw)
17465 {
17466  int npoints;
17467  cpl_vector *x;
17468  cpl_vector *y;
17469  double *xdata;
17470  double *ydata;
17471  cpl_polynomial *poly;
17472  cpl_vector *ysmooth;
17473  cpl_image *smoothed;
17474  float *sdata;
17475  int i;
17476 
17477 
17478  npoints = cpl_image_get_size_x(image);
17479 
17480  if (2 * hw + 1 > npoints)
17481  return NULL;
17482 
17483  x = cpl_vector_new(npoints);
17484  y = cpl_vector_new(npoints);
17485  xdata = cpl_vector_get_data(x);
17486  ydata = cpl_vector_get_data(y);
17487 
17488  smoothed = cpl_image_duplicate(image);
17489  sdata = cpl_image_get_data_float(smoothed);
17490 
17491  for (i = 0; i < npoints; i++) {
17492  xdata[i] = i;
17493  ydata[i] = sdata[i];
17494  }
17495 
17496  ysmooth = cpl_vector_filter_median_create(y, hw);
17497  cpl_vector_delete(y);
17498 
17499  poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL);
17500  cpl_vector_delete(x);
17501  cpl_vector_delete(ysmooth);
17502 
17503  if (poly) {
17504  for (i = 0; i < npoints; i++)
17505  sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL);
17506 
17507  cpl_polynomial_delete(poly);
17508  }
17509  else {
17510  cpl_image_delete(smoothed);
17511  return NULL;
17512  }
17513 
17514  return smoothed;
17515 }
17516 
17517 #undef cleanup
17518 #define cleanup \
17519 do { \
17520  cpl_image_delete(spectrum); \
17521  cpl_image_delete(flux); \
17522  cpl_image_delete(efficiency); \
17523  cpl_image_delete(smo_efficiency); \
17524  cpl_image_delete(extinction); \
17525  cpl_image_delete(response); \
17526  cpl_image_delete(smo_response); \
17527  cpl_image_delete(physical); \
17528 } while (0)
17529 
17553 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave,
17554  double dispersion, double gain,
17555  double exptime, cpl_table *ext_table,
17556  double airmass, cpl_table *flux_table,
17557  int order)
17558 {
17559 
17560  cpl_image *spectrum = NULL; // Extracted standard star spectrum
17561  float *data;
17562  cpl_image *extinction = NULL; // Extinction binned as "spectrum"
17563  float *ext_data;
17564  cpl_image *flux = NULL; // Standard star flux binned as "spectrum"
17565  float *flux_data;
17566  cpl_image *physical = NULL; // Physical units of above
17567  float *phys_data;
17568  cpl_image *efficiency = NULL; // Raw efficiency curve
17569  float *eff_data;
17570  cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve
17571  float *smo_eff_data;
17572  cpl_image *response = NULL; // Raw response curve
17573  float *res_data;
17574  cpl_image *smo_response = NULL; // Smoothed response curve
17575  float *smo_res_data;
17576  cpl_image *image;
17577  cpl_image *smo_image;
17578  cpl_table *table;
17579  float lambda;
17580  int nx, ny;
17581  int ext_count, ext_pos;
17582  int eff_count, eff_pos;
17583  int flux_count, flux_pos;
17584  int start, end;
17585  int i;
17586 
17587 
17588  if (spectra == NULL || ext_table == NULL || flux_table == NULL) {
17589  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17590  return NULL;
17591  }
17592 
17593  if (!cpl_table_has_column(ext_table, "WAVE")) {
17594  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17595  "Column WAVE in atmospheric extinction table");
17596  return NULL;
17597  }
17598 
17599  if (!cpl_table_has_column(ext_table, "EXTINCTION")) {
17600  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17601  "Column EXTINCTION in atmospheric extinction table");
17602  return NULL;
17603  }
17604 
17605  if (!cpl_table_has_column(flux_table, "WAVE")) {
17606  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17607  "Column WAVE in standard star flux table");
17608  return NULL;
17609  }
17610 
17611  if (!cpl_table_has_column(flux_table, "FLUX")) {
17612  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17613  "Column FLUX in standard star flux table");
17614  return NULL;
17615  }
17616 
17617  if (gain < 0.1) {
17618  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17619  "Invalid gain factor (%.2f)", gain);
17620  return NULL;
17621  }
17622 
17623  if (exptime < 0.001) {
17624  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17625  "Invalid exposure time (%.2f)", exptime);
17626  return NULL;
17627  }
17628 
17629  if (dispersion < 0.001) {
17630  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17631  "Invalid dispersion (%.2f)", dispersion);
17632  return NULL;
17633  }
17634 
17635  if (order < 2) {
17636  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17637  "Order of the polynomial fitting the "
17638  "instrument response must be at least 2");
17639  return NULL;
17640  }
17641 
17642  nx = cpl_image_get_size_x(spectra);
17643  ny = cpl_image_get_size_y(spectra);
17644 
17645 
17646  /*
17647  * Find brightest spectrum and duplicate it.
17648  */
17649 
17650  if (ny == 1) {
17651  spectrum = cpl_image_duplicate(spectra);
17652  }
17653  else {
17654  cpl_size x, y;
17655  cpl_image *brights = cpl_image_collapse_create(spectra, 1);
17656 
17657  cpl_image_get_maxpos(brights, &x, &y);
17658  cpl_image_delete(brights);
17659  spectrum = cpl_image_extract(spectra, 1, y, nx, y);
17660  }
17661 
17662 
17663  /*
17664  * Convert standard star spectrum in electrons per second per Angstrom.
17665  */
17666 
17667  cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion);
17668 
17669 
17670  /*
17671  * Map the atmospheric extinction factors to the same lambda sampling
17672  * of the extracted spectrum.
17673  */
17674 
17675  extinction = cpl_image_duplicate(spectrum);
17676  map_table(extinction, startwave + dispersion/2, dispersion,
17677  ext_table, "WAVE", "EXTINCTION");
17678 
17679 
17680  /*
17681  * Convert from magnitudes to actual flux loss.
17682  */
17683 
17684  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17685  cpl_image_exponential(extinction, 10.);
17686 
17687 
17688  /*
17689  * Correct the scientific spectrum to airmass 0
17690  */
17691 
17692  cpl_image_multiply(spectrum, extinction);
17693 
17694 
17695  /*
17696  * Find in what pixel interval (start at "ext_pos", for "ext_count"
17697  * pixels) the atmospheric extinction is available.
17698  */
17699 
17700  ext_data = cpl_image_get_data_float(extinction);
17701 
17702  ext_count = 0;
17703  ext_pos = 0;
17704  for (i = 0; i < nx; i++) {
17705  if (ext_data[i] > 0.0) {
17706  if (ext_count == 0) {
17707  ext_pos = i;
17708  }
17709  ext_count++;
17710  }
17711  else {
17712  if (ext_count) {
17713  break;
17714  }
17715  }
17716  }
17717 
17718  cpl_image_delete(extinction); extinction = NULL;
17719 
17720 
17721  /*
17722  * Map the standard star catalog flux to the same lambda sampling
17723  * of the extracted spectrum.
17724  */
17725 
17726  flux = cpl_image_duplicate(spectrum);
17727  map_table(flux, startwave + dispersion/2, dispersion,
17728  flux_table, "WAVE", "FLUX");
17729 
17730 
17731  /*
17732  * Find in what pixel interval (start at "flux_pos", for "flux_count"
17733  * pixels) the standard star flux is available.
17734  */
17735 
17736  flux_data = cpl_image_get_data_float(flux);
17737 
17738  flux_count = 0;
17739  flux_pos = 0;
17740  for (i = 0; i < nx; i++) {
17741  if (flux_data[i] > 0.0) {
17742  if (flux_count == 0) {
17743  flux_pos = i;
17744  }
17745  flux_count++;
17746  }
17747  else {
17748  if (flux_count) {
17749  break;
17750  }
17751  }
17752  }
17753 
17754 
17755  /*
17756  * Intersection with previous selection
17757  */
17758 
17759  start = ext_pos > flux_pos ? ext_pos : flux_pos;
17760  end = (ext_pos + ext_count) < (flux_pos + flux_count) ?
17761  (ext_pos + ext_count) : (flux_pos + flux_count);
17762  flux_pos = start;
17763  flux_count = end - start;
17764 
17765 
17766  /*
17767  * Convert the flux to photons (per second per Angstrom).
17768  * std_flux is in units of erg / cm^2 / s / Angstrom. This
17769  * must be multiplied by the efficient area of the telescope,
17770  * 5.18E+5 cm^2, and divided by hv (v = frequency). With
17771  * hc = 1.98E-8 erg*Angstrom one obtains the following:
17772  */
17773 
17774  physical = cpl_image_duplicate(spectrum);
17775  phys_data = cpl_image_get_data_float(physical);
17776 
17777  for (i = 0; i < nx; i++) {
17778  lambda = startwave + dispersion * (i + 0.5);
17779  phys_data[i] = 0.0026 * lambda * flux_data[i];
17780  }
17781 
17782  efficiency = cpl_image_duplicate(spectrum);
17783  eff_data = cpl_image_get_data_float(efficiency);
17784  data = cpl_image_get_data_float(spectrum);
17785 
17786  for (i = 0; i < nx; i++) {
17787  if (phys_data[i] > 0.0)
17788  eff_data[i] = data[i] / phys_data[i];
17789  else
17790  eff_data[i] = 0.0;
17791  }
17792 
17793  cpl_image_delete(physical); physical = NULL;
17794 
17795 
17796  /*
17797  * Find interval (longer than 300 pixels) where efficiency is
17798  * greater than 1%
17799  */
17800 
17801  eff_count = 0;
17802  eff_pos = 0;
17803  for (i = 0; i < nx; i++) {
17804  if (eff_data[i] > 0.01) {
17805  if (eff_count == 0) {
17806  eff_pos = i;
17807  }
17808  eff_count++;
17809  }
17810  else {
17811  if (eff_count > 300) {
17812  break;
17813  }
17814  }
17815  }
17816 
17817 
17818  /*
17819  * Intersection with previous selection
17820  */
17821 
17822  start = eff_pos > flux_pos ? eff_pos : flux_pos;
17823  end = (eff_pos + eff_count) < (flux_pos + flux_count) ?
17824  (eff_pos + eff_count) : (flux_pos + flux_count);
17825  eff_pos = start;
17826  eff_count = end - start;
17827 
17828  if (eff_count < 1) {
17829  cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
17830  "No overlap between catalog and spectrum");
17831  cleanup;
17832  return NULL;
17833  }
17834 
17835 
17836  /*
17837  * Extract only data to fit, i.e., where the efficiency is available.
17838  */
17839 
17840  image = cpl_image_extract(efficiency, eff_pos + 1, 1,
17841  eff_pos + eff_count, 1);
17842 
17843  smo_image = polysmooth(image, order, 50);
17844  cpl_image_delete(image);
17845 
17846  smo_efficiency = cpl_image_duplicate(efficiency);
17847  smo_eff_data = cpl_image_get_data_float(smo_efficiency);
17848  cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1);
17849 
17850  cpl_image_delete(smo_image);
17851 
17852 
17853  /*
17854  * Compute instrument response as the ratio between the catalog
17855  * spectrum and the observed spectrum (converted in physical units).
17856  * The polynomial smoothing, however, is performed on the inverse
17857  * of this ration, for obvious reasons (i.e., no divergence at zero
17858  * efficiency).
17859  */
17860 
17861  response = cpl_image_duplicate(spectrum);
17862  res_data = cpl_image_get_data_float(response);
17863 
17864  for (i = 0; i < nx; i++) {
17865  if (eff_data[i] > 0.01 && flux_data[i] > 0.0)
17866  res_data[i] = data[i] / flux_data[i];
17867  else
17868  res_data[i] = 0.0;
17869  }
17870 
17871 
17872  /*
17873  * Extract only data to fit, i.e., where the response is available.
17874  */
17875 
17876  image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1);
17877 
17878  smo_image = polysmooth(image, order, 50);
17879  cpl_image_delete(image);
17880 
17881  smo_response = cpl_image_duplicate(response);
17882  smo_res_data = cpl_image_get_data_float(smo_response);
17883  cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1);
17884 
17885  cpl_image_delete(smo_image);
17886 
17887  for (i = 0; i < nx; i++) {
17888  if (eff_data[i] > 0.01) {
17889  res_data[i] = 1 / res_data[i];
17890  smo_res_data[i] = 1 / smo_res_data[i];
17891  }
17892  else {
17893  res_data[i] = 0.0;
17894  smo_res_data[i] = 0.0;
17895  }
17896  }
17897 
17898 
17899  /*
17900  * Assemble the product spectrophotometric table.
17901  */
17902 
17903  table = cpl_table_new(nx);
17904 
17905  cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT);
17906  cpl_table_set_column_unit(table, "WAVE", "Angstrom");
17907 
17908  for (i = 0; i < nx; i++)
17909  cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5));
17910 
17911  cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT);
17912  cpl_table_set_column_unit(table, "STD_FLUX",
17913  "10^(-16) erg/(cm^2 s Angstrom)");
17914  cpl_table_copy_data_float(table, "STD_FLUX", flux_data);
17915  cpl_image_delete(flux); flux = NULL;
17916 
17917  cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT);
17918  cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)");
17919  cpl_table_copy_data_float(table, "OBS_FLUX", data);
17920  cpl_image_delete(spectrum); spectrum = NULL;
17921 
17922  cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT);
17923  cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon");
17924  cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data);
17925  cpl_image_delete(efficiency); efficiency = NULL;
17926 
17927  cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT);
17928  cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon");
17929  cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data);
17930  cpl_image_delete(smo_efficiency); smo_efficiency = NULL;
17931 
17932  cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT);
17933  cpl_table_set_column_unit(table, "RAW_RESPONSE",
17934  "10^(-16) erg/(cm^2 electron)");
17935  cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data);
17936  cpl_image_delete(response); response = NULL;
17937 
17938  cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT);
17939  cpl_table_set_column_unit(table,
17940  "RESPONSE", "10^(-16) erg/(cm^2 electron)");
17941  cpl_table_copy_data_float(table, "RESPONSE", smo_res_data);
17942  cpl_image_delete(smo_response); smo_response = NULL;
17943 
17944  cleanup;
17945 
17946  return table;
17947 }
17948 
17949 static double ksigma_vector(cpl_vector *values,
17950  double klow, double khigh, int kiter, int *good)
17951 {
17952  cpl_vector *accepted;
17953  double mean = 0.0;
17954  double sigma = 0.0;
17955  double *data = cpl_vector_get_data(values);
17956  int n = cpl_vector_get_size(values);
17957  int ngood = n;
17958  int count = 0;
17959  int i;
17960 
17961 
17962  /*
17963  * At first iteration the mean is taken as the median, and the
17964  * standard deviation relative to this value is computed.
17965  */
17966 
17967  mean = cpl_vector_get_median(values);
17968 
17969  for (i = 0; i < n; i++)
17970  sigma += (mean - data[i]) * (mean - data[i]);
17971 
17972  sigma = sqrt(sigma / (n - 1));
17973 
17974  while (kiter) {
17975  count = 0;
17976  for (i = 0; i < ngood; i++) {
17977  if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) {
17978  data[count] = data[i];
17979  ++count;
17980  }
17981  }
17982 
17983  if (count == 0) // This cannot happen at first iteration.
17984  break; // So we can break: we have already computed a mean.
17985 
17986  /*
17987  * The mean must be computed even if no element was rejected
17988  * (count == ngood), because at first iteration median instead
17989  * of mean was computed.
17990  */
17991 
17992  accepted = cpl_vector_wrap(count, data);
17993  mean = cpl_vector_get_mean(accepted);
17994  if (count > 1)
17995  sigma = cpl_vector_get_stdev(accepted);
17996  cpl_vector_unwrap(accepted);
17997 
17998  if (count == ngood || count == 1)
17999  break;
18000 
18001  ngood = count;
18002  --kiter;
18003  }
18004 
18005  if (good)
18006  *good = ngood;
18007 
18008  return mean;
18009 }
18010 
18011 
18030 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist,
18031  double klow, double khigh, int kiter,
18032  cpl_image **good)
18033 {
18034  int ni, nx, ny, npix;
18035  cpl_image *out_ima;
18036  float *pout_ima;
18037  float *good_ima;
18038  cpl_image *image;
18039  float **data;
18040  cpl_vector *time_line;
18041  double *ptime_line;
18042  int ngood;
18043  int i, j;
18044 
18045 
18046  ni = cpl_imagelist_get_size(imlist);
18047 
18048  image = cpl_imagelist_get(imlist, 0);
18049  nx = cpl_image_get_size_x(image);
18050  ny = cpl_image_get_size_y(image);
18051  npix = nx * ny;
18052 
18053  out_ima = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18054  pout_ima = cpl_image_get_data_float(out_ima);
18055 
18056  if (good) {
18057  *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18058  good_ima = cpl_image_get_data_float(*good);
18059  }
18060 
18061  time_line = cpl_vector_new(ni);
18062  ptime_line = cpl_vector_get_data(time_line);
18063 
18064  data = cpl_calloc(sizeof(float *), ni);
18065 
18066  for (i = 0; i < ni; i++) {
18067  image = cpl_imagelist_get(imlist, i);
18068  data[i] = cpl_image_get_data_float(image);
18069  }
18070 
18071  for (i = 0; i < npix; i++) {
18072  for (j = 0; j < ni; j++) {
18073  ptime_line[j] = data[j][i];
18074  }
18075  pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood);
18076  if (good) {
18077  good_ima[i] = ngood;
18078  }
18079  }
18080 
18081  cpl_free(data);
18082  cpl_vector_delete(time_line);
18083 
18084  return out_ima;
18085 
18086 }
18087 
18088 
18105 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response,
18106  cpl_table *ext_table, double startwave,
18107  double dispersion, double gain,
18108  double exptime, double airmass)
18109 {
18110  cpl_image *extinction;
18111  cpl_image *outspectra;
18112  cpl_image *mapresponse;
18113  float *res_data;
18114  float *out_data;
18115  float *ext_data;
18116  int tlength, xlength, ylength;
18117  int i, j, k;
18118  double resp_startwave;
18119  double resp_endwave;
18120  int null;
18121 
18122 
18123  if (spectra == NULL || ext_table == NULL || response == NULL) {
18124  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18125  return NULL;
18126  }
18127 
18128  /* Use the normal response if available. If not, use the flat corrected
18129  * response. Usually only one of the two is available in the interpolated
18130  * response.
18131  */
18132  if(cpl_table_has_column(response, "RESPONSE"))
18133  cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18134  else if(cpl_table_has_column(response, "RESPONSE_FFSED"))
18135  cpl_table_cast_column(response, "RESPONSE_FFSED", "RESPONSE_F", CPL_TYPE_FLOAT);
18136  else
18137  return NULL;
18138 
18139  res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18140 
18141  if (res_data == NULL) {
18142  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18143  return NULL;
18144  }
18145 
18146  tlength = cpl_table_get_nrow(response);
18147  xlength = cpl_image_get_size_x(spectra);
18148  ylength = cpl_image_get_size_y(spectra);
18149 
18150  /* Map the response */
18151  mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18152  map_table(mapresponse, startwave + dispersion/2, dispersion,
18153  response, "WAVE", "RESPONSE_F");
18154  res_data = cpl_image_get_data_float(mapresponse);
18155 
18156  /*
18157  * Map the atmospheric extinction factors to the same lambda sampling
18158  * of the extracted spectrum.
18159  */
18160 
18161  extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18162  map_table(extinction, startwave + dispersion/2, dispersion,
18163  ext_table, "WAVE", "EXTINCTION");
18164 
18165 
18166  /*
18167  * Convert from magnitudes to actual flux loss.
18168  */
18169 
18170  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18171  cpl_image_exponential(extinction, 10.);
18172 
18173  outspectra = cpl_image_duplicate(spectra);
18174 
18175  ext_data = cpl_image_get_data_float(extinction);
18176  out_data = cpl_image_get_data_float(outspectra);
18177 
18178  for (k = 0, i = 0; i < ylength; i++) {
18179  for (j = 0; j < xlength; j++, k++)
18180  out_data[k] *= ext_data[j] * res_data[j];
18181  }
18182 
18183  cpl_image_delete(extinction);
18184  cpl_image_delete(mapresponse);
18185 
18186  cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion);
18187 
18188  /*
18189  * Set to -1 the extrapolated values
18190  */
18191  resp_startwave = cpl_table_get(response, "WAVE", 0, &null);
18192  resp_endwave = cpl_table_get(response, "WAVE",
18193  cpl_table_get_nrow(response) -1, &null);
18194  for (j = 0; j < xlength; j++) {
18195  double this_wave = startwave + j * dispersion;
18196  if(this_wave < resp_startwave ||this_wave > resp_endwave)
18197  {
18198  for (i = 0; i < ylength; i++)
18199  out_data[j + xlength * i] = -1;
18200  }
18201  }
18202 
18203  cpl_table_erase_column(response, "RESPONSE_F");
18204 
18205  return outspectra;
18206 }
18207 
18208 
18225 cpl_image *mos_propagate_photometry_error(cpl_image *spectra,
18226  cpl_image *errors,
18227  cpl_table *response,
18228  cpl_table *ext_table,
18229  double startwave,
18230  double dispersion, double gain,
18231  double exptime, double airmass)
18232 {
18233  cpl_image *extinction;
18234  cpl_image *outerrors;
18235  cpl_image *mapresponse;
18236  cpl_image *maperror;
18237  float *err_data;
18238  float *out_data;
18239  float *ext_data;
18240  float *res_data;
18241  float *spe_data;
18242  int tlength, xlength, ylength;
18243  int i, j, k;
18244 
18245 
18246  if (errors == NULL || ext_table == NULL || response == NULL) {
18247  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18248  return NULL;
18249  }
18250 
18251  if (!cpl_table_has_column(response, "ERROR")) {
18252  return mos_apply_photometry(errors, response, ext_table, startwave,
18253  dispersion, gain, exptime, airmass);
18254  }
18255 
18256  cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18257  res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18258 
18259  if (res_data == NULL) {
18260  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18261  return NULL;
18262  }
18263 
18264  err_data = cpl_table_get_data_float(response, "ERROR");
18265 
18266  if (err_data == NULL) {
18267  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18268  return NULL;
18269  }
18270 
18271  tlength = cpl_table_get_nrow(response);
18272  xlength = cpl_image_get_size_x(errors);
18273  ylength = cpl_image_get_size_y(errors);
18274 
18275  if (xlength != tlength) {
18276  mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18277  map_table(mapresponse, startwave + dispersion/2, dispersion,
18278  response, "WAVE", "RESPONSE_F");
18279  res_data = cpl_image_get_data_float(mapresponse);
18280 
18281  maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18282  map_table(maperror, startwave + dispersion/2, dispersion,
18283  response, "WAVE", "ERROR");
18284  err_data = cpl_image_get_data_float(maperror);
18285  }
18286 
18287  /*
18288  * Map the atmospheric extinction factors to the same lambda sampling
18289  * of the extracted spectrum.
18290  */
18291 
18292  extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18293  map_table(extinction, startwave + dispersion/2, dispersion,
18294  ext_table, "WAVE", "EXTINCTION");
18295 
18296 
18297  /*
18298  * Convert from magnitudes to actual flux loss.
18299  */
18300 
18301  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18302  cpl_image_exponential(extinction, 10.);
18303 
18304  outerrors = cpl_image_duplicate(errors);
18305 
18306  ext_data = cpl_image_get_data_float(extinction);
18307  out_data = cpl_image_get_data_float(outerrors);
18308  spe_data = cpl_image_get_data_float(spectra);
18309 
18310  for (k = 0, i = 0; i < ylength; i++) {
18311  for (j = 0; j < xlength; j++, k++) {
18312  out_data[k] = ext_data[j] *
18313  sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] +
18314  res_data[j] * res_data[j] * out_data[k] * out_data[k]);
18315  }
18316  }
18317 
18318  cpl_image_delete(extinction);
18319  if (xlength != tlength) {
18320  cpl_image_delete(maperror);
18321  }
18322 
18323  cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion);
18324 
18325  cpl_table_erase_column(response, "RESPONSE_F");
18326  return outerrors;
18327 }
18328 
18329 
18405 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error,
18406  cpl_image *u_image, cpl_image *u_error,
18407  double startwave, double dispersion,
18408  double band, cpl_table *pol_sta,
18409  double ra, double dec, char *filter,
18410  int *polarisation,
18411  double *p_offset, double *p_error,
18412  double *a_offset, double *a_error)
18413 {
18414  cpl_table *standard;
18415  cpl_image *q_noise;
18416  cpl_image *q_signal;
18417  cpl_image *u_noise;
18418  cpl_image *u_signal;
18419  cpl_image *noise;
18420  double *q_ndata;
18421  double *q_sdata;
18422  double *u_ndata;
18423  double *u_sdata;
18424  double arctol = 0.5; /* Arc tolerance in degrees */
18425  double mindist;
18426  double cwave;
18427  double bwave[] = {3650., 4450., 5510., 6580., 8060};
18428  char *bands = "UBVRI";
18429  char p_label[] = {' ', 'p', '\0'};
18430  char dp_label[] = {' ', 'd', 'p', '\0'};
18431  char a_label[] = {' ', 'a', '\0'};
18432  char da_label[] = {' ', 'd', 'a', '\0'};
18433  int nbands = strlen(bands);
18434  int selected;
18435  int first, last, count, center;
18436  int nx;
18437  cpl_size col, row;
18438  int i, found, closest;
18439  int pband;
18440  int polarised;
18441  double q_obs;
18442  double q_err;
18443  double u_obs;
18444  double u_err;
18445  double p_obs;
18446  double p_err;
18447  double p_ref;
18448  double dp_ref;
18449  double a_obs;
18450  double a_err;
18451  double a_ref;
18452  double da_ref;
18453 
18454 
18455  *filter = '\0';
18456  *polarisation = 0;
18457  *p_offset = 0.0;
18458  *p_error = 0.0;
18459  *a_offset = 0.0;
18460  *a_error = 0.0;
18461 
18462  /*
18463  * Select reference standard star
18464  */
18465 
18466  cpl_table_select_all(pol_sta);
18467  cpl_table_and_selected_double(pol_sta, "COORD_RA", CPL_GREATER_THAN,
18468  ra-arctol);
18469  cpl_table_and_selected_double(pol_sta, "COORD_RA", CPL_LESS_THAN,
18470  ra+arctol);
18471  cpl_table_and_selected_double(pol_sta, "COORD_DEC", CPL_GREATER_THAN,
18472  dec-arctol);
18473  selected =
18474  cpl_table_and_selected_double(pol_sta, "COORD_DEC", CPL_LESS_THAN,
18475  dec+arctol);
18476 
18477  if (selected == 0) {
18478  cpl_msg_warning(cpl_func, "No standard star found in FOV");
18479  return 1;
18480  }
18481 
18482  if (selected > 1) {
18483  cpl_msg_warning(cpl_func,
18484  "Ambiguity: %d standard stars found in FOV", selected);
18485  return 1;
18486  }
18487 
18488  standard = cpl_table_extract_selected(pol_sta);
18489 
18490  cpl_msg_info(cpl_func, "Standard star: %s",
18491  cpl_table_get_string(standard, "name", 0));
18492 
18493  /*
18494  * Check whether the star is polarised or not
18495  */
18496 
18497  polarised = cpl_table_get_int(standard, "polarised", 0, NULL);
18498 
18499  cpl_msg_info(cpl_func, "This star is%sexpected to be polarised",
18500  polarised ? " " : " not ");
18501 
18502 
18503  /*
18504  * Determine the image row with the smallest median noise: this
18505  * row is assumed to refer to the standard star.
18506  * (note: the higher the S/N ratio of the original spectra, the
18507  * smaller the noise of the Stokes parameters Q and U).
18508  */
18509 
18510  nx = cpl_image_get_size_x(q_error);
18511 
18512  noise = cpl_image_collapse_median_create(q_error, 1, 0, 0);
18513  cpl_image_get_minpos(noise, &col, &row);
18514 
18515  cpl_image_delete(noise);
18516 
18517  if (col != 1) {
18518  cpl_table_delete(standard);
18519  cpl_msg_error(cpl_func,
18520  "Assertion failure!!! col = %"CPL_SIZE_FORMAT" (it should be 1)", col);
18521  return 1;
18522  }
18523 
18524  q_signal = cpl_image_extract(q_image, 1, row, nx, row);
18525  q_noise = cpl_image_extract(q_error, 1, row, nx, row);
18526  u_signal = cpl_image_extract(u_image, 1, row, nx, row);
18527  u_noise = cpl_image_extract(u_error, 1, row, nx, row);
18528 
18529  q_sdata = cpl_image_get_data_double(q_signal);
18530  q_ndata = cpl_image_get_data_double(q_noise);
18531  u_sdata = cpl_image_get_data_double(u_signal);
18532  u_ndata = cpl_image_get_data_double(u_noise);
18533 
18534 
18535  /*
18536  * Determine valid interval in input images (where error is positive).
18537  */
18538 
18539  first = -1;
18540  last = nx = cpl_image_get_size_x(q_signal);
18541  for (i = 0; i < nx; i++) {
18542  if (first < 0) {
18543  if (q_ndata[i] > 0.0) {
18544  first = i;
18545  }
18546  }
18547  else {
18548  if (q_ndata[i] <= 0.0) {
18549  last = i - 1;
18550  break;
18551  }
18552  }
18553  }
18554 
18555  count = last - first + 1;
18556 
18557  if (first < 0 || count < band) {
18558  cpl_table_delete(standard);
18559  cpl_image_delete(q_signal);
18560  cpl_image_delete(q_noise);
18561  cpl_image_delete(u_signal);
18562  cpl_image_delete(u_noise);
18563  cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count);
18564  return 1;
18565  }
18566 
18567  center = (first + last) / 2; // Center of valid spectrum
18568  cwave = startwave + dispersion * center; // Corresponding wavelength
18569 
18570 
18571  /*
18572  * Find the band UBVRI closest to the central wavelength.
18573  */
18574 
18575  found = 0;
18576  for (i = 0; i < nbands; i++) {
18577  p_label[0] = bands[i];
18578  if (cpl_table_is_valid(standard, p_label, 0)) {
18579  if (found == 0) {
18580  found = 1;
18581  mindist = fabs(bwave[i] - cwave);
18582  closest = i;
18583  }
18584  else if (mindist > fabs(bwave[i] - cwave)) {
18585  mindist = fabs(bwave[i] - cwave);
18586  closest = i;
18587  }
18588  }
18589  }
18590 
18591  if (!found) {
18592  cpl_table_delete(standard);
18593  cpl_image_delete(q_signal);
18594  cpl_image_delete(q_noise);
18595  cpl_image_delete(u_signal);
18596  cpl_image_delete(u_noise);
18597  cpl_msg_warning(cpl_func, "No reference value available");
18598  return 1;
18599  }
18600 
18601  center = (bwave[closest] - startwave) / dispersion; // Center of band (pix)
18602  cwave = bwave[closest]; // Wavelength of band
18603 
18604 
18605  /*
18606  * Check that the integration interval is entirely contained
18607  * in the valid interval, or give it up.
18608  */
18609 
18610  pband = floor(band / dispersion); // Band width in pixels
18611 
18612  if (center - pband/2 < first || center + pband/2 > last) {
18613  cpl_table_delete(standard);
18614  cpl_image_delete(q_signal);
18615  cpl_image_delete(q_noise);
18616  cpl_image_delete(u_signal);
18617  cpl_image_delete(u_noise);
18618  cpl_msg_warning(cpl_func, "No reference value available");
18619  return 1;
18620  }
18621 
18622  first = center - pband/2;
18623  last = center + pband/2;
18624 
18625  /*
18626  * Collect reference values. Note that if angle info is not available,
18627  * angle stuff is set automaticaly to zero.
18628  */
18629 
18630  p_label[0] = bands[closest];
18631  dp_label[0] = bands[closest];
18632  a_label[0] = bands[closest];
18633  da_label[0] = bands[closest];
18634 
18635  p_ref = cpl_table_get(standard, p_label, 0, NULL);
18636  dp_ref = cpl_table_get(standard, dp_label, 0, NULL);
18637  a_ref = cpl_table_get(standard, a_label, 0, NULL);
18638  da_ref = cpl_table_get(standard, da_label, 0, NULL);
18639 
18640  cpl_msg_info(cpl_func,
18641  "The expected polarisation is %.2f +- %.2f %%",
18642  p_ref, dp_ref);
18643 
18644  if (polarised) {
18645  cpl_msg_info(cpl_func,
18646  "The expected polarisation angle is %.2f +- %.2f degrees",
18647  a_ref, da_ref);
18648  }
18649 
18650  /*
18651  * Find median signal and median error.
18652  */
18653 
18654  q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1);
18655  q_err = cpl_image_get_median_window(q_error, first, 1, last, 1);
18656  u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1);
18657  u_err = cpl_image_get_median_window(u_error, first, 1, last, 1);
18658 
18659  /*
18660  * Measured linear polarisation and its error
18661  */
18662 
18663  p_obs = sqrt(q_obs * q_obs + u_obs * u_obs);
18664  p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err);
18665 
18666  /*
18667  * Measured polarisation angle
18668  */
18669 
18670  a_obs = 0.0;
18671  if (polarised) {
18672  if (fabs(q_obs) < 0.00001) {
18673  if (u_obs > 0.0) {
18674  a_obs = 45.0;
18675  }
18676  else {
18677  a_obs = 135.0;
18678  }
18679  }
18680  else {
18681  a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI;
18682  if (q_obs > 0.0) {
18683  if (u_obs < 0.0) {
18684  a_obs += 180.;
18685  }
18686  }
18687  else {
18688  a_obs += 90.;
18689  }
18690  }
18691  }
18692 
18693  /*
18694  * Error on polarisation angle
18695  */
18696 
18697  a_err = 0.0;
18698  if (polarised) {
18699  a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err)
18700  / (p_obs * p_obs)
18701  * 90 / CPL_MATH_PI;
18702  }
18703 
18704  p_obs *= 100;
18705  p_err *= 100;
18706  cpl_msg_info(cpl_func,
18707  "The measured polarisation is %.2f +- %.2f %%",
18708  p_obs, p_err);
18709 
18710  if (polarised) {
18711  cpl_msg_info(cpl_func,
18712  "The measured polarisation angle is %.2f +- %.2f degrees",
18713  a_obs, a_err);
18714  }
18715 
18716  *filter = bands[closest];
18717  *polarisation = polarised;
18718 
18719  if (polarised) {
18720  *p_offset = (p_obs - p_ref) / p_ref;
18721  *p_error = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref;
18722  }
18723  else {
18724  *p_offset = p_obs - p_ref;
18725  *p_error = sqrt(p_err * p_err + dp_ref * dp_ref);
18726  }
18727 
18728  *a_offset = a_obs - a_ref;
18729  *a_error = sqrt(a_err*a_err + da_ref*da_ref);
18730 
18731  return 0;
18732 
18733 }
18734 
18735 
18767 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
18768 {
18769  cpl_array *offsets;
18770  int noffset;
18771  int nslits = cpl_table_get_nrow(reference);
18772  int *nref;
18773  int *nobj;
18774  int corr, maxcorr;
18775  int best_shift;
18776  int i, j, k;
18777 
18778  cpl_error_code status = CPL_ERROR_NONE;
18779 
18780 
18781  *offset = 0.0;
18782 
18783  if (objects == NULL)
18784  return CPL_ERROR_NULL_INPUT;
18785 
18786  if (nslits != cpl_table_get_nrow(objects))
18787  return CPL_ERROR_INCOMPATIBLE_INPUT;
18788 
18789  nref = fors_get_nobjs_perslit(reference);
18790  nobj = fors_get_nobjs_perslit(objects);
18791 
18792  noffset = 0;
18793  for (i = 0; i < nslits; i++)
18794  noffset += nobj[i];
18795 
18796  if (noffset == 0) {
18797  cpl_free(nref);
18798  cpl_free(nobj);
18799  return CPL_ERROR_DATA_NOT_FOUND;
18800  }
18801 
18802  noffset = 0;
18803  for (i = 0; i < nslits; i++)
18804  noffset += nref[i];
18805 
18806  if (noffset == 0) {
18807  cpl_free(nref);
18808  cpl_free(nobj);
18809  return CPL_ERROR_DATA_NOT_FOUND;
18810  }
18811 
18812  offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE);
18813 
18814  noffset = 0; // The real number of offsets found will be counted.
18815 
18816  for (i = 0; i < nslits; i++) {
18817  if (nref[i] > 0 && nobj[i] > 0) {
18818  double shift;
18819  int length = cpl_table_get_int(objects, "length", i, NULL);
18820  double ytop = cpl_table_get_double(objects, "xtop", i, NULL);
18821  double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL);
18822  int *aref = cpl_calloc(length, sizeof(int));
18823  int *aobj = cpl_calloc(length, sizeof(int));
18824  float *pref = cpl_calloc(nref[i], sizeof(float));
18825  float *pobj = cpl_calloc(nobj[i], sizeof(float));
18826 
18827  for (j = 0; j < nref[i]; j++) {
18828  pref[j] = fors_get_object_position(reference, i, j + 1);
18829  aref[(int)pref[j]] = 1;
18830  }
18831 
18832  for (j = 0; j < nobj[i]; j++) {
18833  pobj[j] = fors_get_object_position(objects, i, j + 1);
18834  aobj[(int)pobj[j]] = 1;
18835  }
18836 
18837  /*
18838  * Do not consider objects at border
18839  */
18840 
18841  aref[0] = 0;
18842  aref[length - 1] = 0;
18843  aobj[0] = 0;
18844  aobj[length - 1] = 0;
18845 
18846 //for (j = 0; j < nref[i]; j++)
18847 //printf("references: %f, ", pref[j]);
18848 //printf("\n");
18849 //for (j = 0; j < nref[i]; j++)
18850 //printf("objects : %f, ", pobj[j]);
18851 //printf("\n");
18852 //for (j = 0; j < length; j++)
18853 //printf("%d", aref[j]);
18854 //printf("\n");
18855 //for (j = 0; j < length; j++)
18856 //printf("%d", aobj[j]);
18857 //printf("\n");
18858 
18859  /*
18860  * Object matching by correlation
18861  */
18862 
18863  maxcorr = 0;
18864  best_shift = length;
18865 
18866  for (shift = length/2, j = 0; j <= length; shift--, j++) {
18867  int rstart, ostart, count;
18868 
18869  if (shift > 0) {
18870  rstart = shift;
18871  ostart = 0;
18872  count = length - shift;
18873  }
18874  else {
18875  rstart = 0;
18876  ostart = -shift;
18877  count = length + shift;
18878  }
18879 
18880  corr = 0;
18881  for (k = 0; k < count; k++) {
18882  corr += aref[rstart + k] * aobj[ostart + k];
18883  }
18884 
18885  if (maxcorr < corr) {
18886  maxcorr = corr;
18887  best_shift = shift;
18888  }
18889  }
18890 
18891  if (best_shift == length) { // No shift found
18892 //printf("%d: No shift found\n", i);
18893  cpl_free(aref);
18894  cpl_free(aobj);
18895  cpl_free(pref);
18896  cpl_free(pobj);
18897  continue;
18898  }
18899 //printf("%d: Integer shift found = %d\n", i, best_shift);
18900 
18901  for (j = 0; j < nref[i]; j++) {
18902  for (k = 0; k < nobj[i]; k++) {
18903  if (fabs(pref[j] - pobj[k] - best_shift) < 2) {
18904  double ccd_offset = (pref[j] - pobj[k])
18905  * (ytop - ybottom)
18906  / length;
18907 
18908 //printf("%d: Match found: %f\n", i, ccd_offset);
18909  /*
18910  * The matching object is found, store the
18911  * corresponding offset
18912  */
18913 
18914  cpl_array_set(offsets, noffset, ccd_offset);
18915  noffset++;
18916  break;
18917  }
18918  }
18919  }
18920 
18921  cpl_free(aref);
18922  cpl_free(aobj);
18923  cpl_free(pref);
18924  cpl_free(pobj);
18925  }
18926 //else
18927 //printf("%d: No object found\n", i);
18928  }
18929 
18930  cpl_free(nref);
18931  cpl_free(nobj);
18932 
18933 //printf("%d offsets found in total\n", noffset);
18934  if (noffset > 0) {
18935  if (noffset % 2) {
18936  *offset = cpl_array_get_median(offsets);
18937  }
18938  else {
18939  double *a = cpl_malloc(sizeof(double) * noffset);
18940  for (i = 0; i < noffset; i++) {
18941  a[i] = cpl_array_get_double(offsets, i, NULL);
18942  }
18943  *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) +
18944  fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0;
18945  cpl_free(a);
18946  }
18947  }
18948  else
18949  status = CPL_ERROR_DATA_NOT_FOUND;
18950 //printf("Median offset: %f\n", *offset);
18951 
18952  cpl_array_delete(offsets);
18953 
18954  return status;
18955 
18956 }
18957 
18958 
18970 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
18971 {
18972  cpl_image *source;
18973  int nx = cpl_image_get_size_x(image);
18974  int ny = cpl_image_get_size_y(image);
18975  float *idata;
18976  float *sdata;
18977  int i, j, pos;
18978  double xpos, ypos, xfrac, yfrac;
18979  int xint, yint;
18980 
18981 
18982  if (fabs(dx) >= nx || fabs(dy) >= ny)
18983  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18984 
18985  source = cpl_image_duplicate(image);
18986  idata = cpl_image_get_data_float(image);
18987  sdata = cpl_image_get_data_float(source);
18988 
18989  /*
18990  * Shift in y
18991  */
18992 
18993  yfrac = - dy - floor(- dy);
18994  xfrac = - dx - floor(- dx);
18995 
18996  for (pos = 0, j = 0; j < ny; j++) {
18997  ypos = j - dy;
18998  yint = floor(ypos);
18999  for (i = 0; i < nx; i++) {
19000  xpos = i - dx;
19001  xint = floor(xpos);
19002  if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) {
19003  idata[pos] = 0.0;
19004  }
19005  else {
19006  idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac)
19007  + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac)
19008  + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac
19009  + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac;
19010  }
19011  pos++;
19012  }
19013  }
19014 
19015  cpl_image_delete(source);
19016 
19017  return CPL_ERROR_NONE;
19018 }
19019 
19031 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
19032 {
19033 #ifdef CPL_SIZE_FORMAT
19034  cpl_size row;
19035 #else
19036  int row;
19037 #endif
19038 
19039  cpl_table_duplicate_column(slits, "x", slits, "xtop");
19040  cpl_table_add_columns(slits, "x", "xbottom");
19041  cpl_table_divide_scalar(slits, "x", 2); // Mean x position
19042  cpl_table_subtract_scalar(slits, "x", nx/2); // Relative to CCD center
19043  cpl_table_multiply_columns(slits, "x", "x"); // Squared
19044 
19045  cpl_table_duplicate_column(slits, "y", slits, "ytop");
19046  cpl_table_add_columns(slits, "y", "ybottom");
19047  cpl_table_divide_scalar(slits, "y", 2); // Mean y position
19048  cpl_table_subtract_scalar(slits, "y", ny/2); // Relative to CCD center
19049  cpl_table_multiply_columns(slits, "y", "y"); // Squared
19050 
19051  cpl_table_add_columns(slits, "x", "y"); // Distance from center
19052  cpl_table_get_column_minpos(slits, "x", &row); // Min distance from center
19053 
19054  cpl_table_erase_column(slits, "x");
19055  cpl_table_erase_column(slits, "y");
19056 
19057  return row;
19058 }
19059 
19079 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits,
19080  double xwidth, double ywidth,
19081  int dx, double gain, double *o_flux, double *o_err)
19082 {
19083  int nx = cpl_image_get_size_x(image);
19084  int ny = cpl_image_get_size_y(image);
19085  int slit = mos_slit_closest_to_center(slits, nx, ny);
19086  int ytop = (int)cpl_table_get(slits, "ytop", slit, NULL);
19087  int ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL);
19088  int dy = ytop - ybottom;
19089  int xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) +
19090  cpl_table_get(slits, "xbottom", slit, NULL)) / 2);
19091  int xleft = xcenter - dx;
19092  int xright = xcenter + dx + 1;
19093  double area = xwidth * ywidth; // squared mm
19094  int npix = (2*dx + 1) * dy;
19095  int count = 0;
19096  float *data = cpl_image_get_data_float(image);
19097  double flux = 0.0;
19098  double error = 0.0;
19099  int satur = 60000;
19100  int x, y;
19101 
19102 
19103  if (cpl_table_has_column(slits, "ywidth")) {
19104  area = cpl_table_get(slits, "xwidth", slit, NULL)
19105  * cpl_table_get(slits, "ywidth", slit, NULL);
19106  }
19107 
19108  *o_flux = 0.0;
19109  *o_err = 0.0;
19110 
19111  if (xleft < 0)
19112  xleft = 0;
19113 
19114  if (xleft > nx)
19115  xleft = nx;
19116 
19117  if (xright < 0)
19118  xright = 0;
19119 
19120  if (xright > nx)
19121  xright = nx;
19122 
19123  if (ytop < 0)
19124  ytop = 0;
19125 
19126  if (ytop > ny)
19127  ytop = ny;
19128 
19129  if (ybottom < 0)
19130  ybottom = 0;
19131 
19132  if (ybottom > ny)
19133  ybottom = ny;
19134 
19135  count = (xright - xleft) * (ytop - ybottom);
19136 
19137  if (count == 0)
19138  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19139 
19140  count = 0;
19141 
19142  for (y = ybottom; y < ytop; y++) {
19143  for (x = xleft; x < xright; x++) {
19144  double value = data[x + y * nx];
19145  if (value < satur) {
19146  flux += value;
19147  count++;
19148  }
19149  }
19150  }
19151 
19152  if (count == 0)
19153  return CPL_ERROR_DIVISION_BY_ZERO;
19154 
19155  error = sqrt(flux/gain);
19156 
19157  /*
19158  * Flux correction for lost pixels
19159  */
19160 
19161  flux *= (float)npix / count;
19162  error *= (float)npix / count;
19163 
19164  flux /= area;
19165  error /= area;
19166 
19167  *o_flux = flux;
19168  *o_err = error;
19169 
19170  return CPL_ERROR_NONE;
19171 }
19172 
19173 
19196 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits,
19197  double xwidth, double ywidth,
19198  double lambda, double startwave,
19199  double dispersion, int dx, double gain,
19200  double *o_flux, double *o_err)
19201 {
19202  int nx = cpl_image_get_size_x(image);
19203  int ny = cpl_image_get_size_y(image);
19204  int slit = mos_slit_closest_to_center(slits, nx, ny);
19205  int dy = (int)cpl_table_get(slits, "length", slit, NULL);
19206  int ybottom = (int)cpl_table_get(slits, "position", slit, NULL);
19207  int ytop = ybottom + dy;
19208  int xcenter = (int)floor((lambda - startwave) / dispersion + 0.5);
19209  int xleft = xcenter - dx;
19210  int xright = xcenter + dx + 1;
19211  double area = xwidth * ywidth;
19212  int npix = (2*dx + 1) * dy;
19213  int count = 0;
19214  float *data = cpl_image_get_data_float(image);
19215  double flux = 0.0;
19216  double error = 0.0;
19217  int satur = 60000;
19218  int x, y;
19219 
19220 
19221  if (cpl_table_has_column(slits, "ywidth")) {
19222  area = cpl_table_get(slits, "xwidth", slit, NULL)
19223  * cpl_table_get(slits, "ywidth", slit, NULL);
19224  }
19225 
19226  *o_flux = 0.0;
19227  *o_err = 0.0;
19228 
19229  if (xleft < 0)
19230  xleft = 0;
19231 
19232  if (xleft > nx)
19233  xleft = nx;
19234 
19235  if (xright < 0)
19236  xright = 0;
19237 
19238  if (xright > nx)
19239  xright = nx;
19240 
19241  if (ytop < 0)
19242  ytop = 0;
19243 
19244  if (ytop > ny)
19245  ytop = ny;
19246 
19247  if (ybottom < 0)
19248  ybottom = 0;
19249 
19250  if (ybottom > ny)
19251  ybottom = ny;
19252 
19253  count = (xright - xleft) * (ytop - ybottom);
19254 
19255  if (count == 0)
19256  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19257 
19258  count = 0;
19259 
19260  for (y = ybottom; y < ytop; y++) {
19261  for (x = xleft; x < xright; x++) {
19262  double value = data[x + y * nx];
19263  if (value < satur) {
19264  flux += value;
19265  count++;
19266  }
19267  }
19268  }
19269 
19270  if (count == 0)
19271  return CPL_ERROR_DIVISION_BY_ZERO;
19272 
19273  error = sqrt(flux/gain);
19274 
19275  /*
19276  * Flux correction for lost pixels
19277  */
19278 
19279  flux *= (float)npix / count;
19280  error *= (float)npix / count;
19281 
19282  flux /= area;
19283  error /= area;
19284 
19285  *o_flux = flux;
19286  *o_err = error;
19287 
19288  return CPL_ERROR_NONE;
19289 
19290 }
19291 
19292 
19306 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit,
19307  char *label, double *mvalue)
19308 {
19309  int position = cpl_table_get_int(slits, "position", slit, NULL);
19310  int length = cpl_table_get_int(slits, "length", slit, NULL);
19311  cpl_table *tmp = cpl_table_extract(table, position, length);
19312 
19313  *mvalue = cpl_table_get_column_median(tmp, label);
19314  cpl_table_delete(tmp);
19315 
19316  if (cpl_error_get_code() != CPL_ERROR_NONE)
19317  return 1;
19318 
19319  return 0;
19320 }
19321 
19322 
19334 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny)
19335 {
19336  cpl_mask *kernel = cpl_mask_new(nx, ny);
19337  cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image),
19338  cpl_image_get_size_y(image),
19339  cpl_image_get_type(image));
19340 
19341  cpl_mask_not(kernel);
19342  cpl_image_filter_mask(filtered, image, kernel,
19343  CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
19344  cpl_mask_delete(kernel);
19345 
19346  return filtered;
19347 }
19348 
19349 int fors_mos_is_lss_like(cpl_table *maskslits, int nslits_out_det)
19350 {
19351  int treat_as_lss = 1;
19352  double mxpos = cpl_table_get_column_median(maskslits, "xtop");
19353  double * slit_xpos = cpl_table_get_data_double(maskslits, "xtop");
19354  cpl_size nslits = cpl_table_get_nrow(maskslits);
19355 
19356  //If not all the slits are illuminated, then we cannot say that
19357  //it is lss-like (PIPE-4380)
19358  if(nslits_out_det != 0)
19359  return 0;
19360 
19361  for (cpl_size i = 0; i < nslits; i++) {
19362  if (fabs(mxpos-slit_xpos[i]) > 0.01) {
19363  treat_as_lss = 0;
19364  break;
19365  }
19366  }
19367  return treat_as_lss;
19368 }
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:8540
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:17553
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:11214
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:14800
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:12505
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:5472
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:11077
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:18225
cpl_table * mos_load_slits_fors_mxu(cpl_propertylist *header)
Create slit location table from FITS header of FORS2-MXU data.
Definition: moses.c:14888
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:17079
cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
Rotate a slit location table.
Definition: moses.c:6346
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:11410
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:9727
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:6460
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:12172
cpl_image * mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
Local determination of sky.
Definition: moses.c:12849
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:15143
double mos_get_gain_vimos(cpl_propertylist *header)
Return gain factor for a VIMOS exposure.
Definition: moses.c:15518
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:10016
cpl_error_code mos_validate_slits(cpl_table *slits)
Check validity of a slit location table.
Definition: moses.c:6282
cpl_error_code mos_subtract_background(cpl_image *image)
Subtract the background.
Definition: moses.c:16428
int mos_get_maxobjs_per_slit(cpl_table *slits)
Get the maximum possible number of objects in a slit.
Definition: moses.c:17053
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:8198
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:13974
cpl_image * mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
Local determination of sky.
Definition: moses.c:12747
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:18405
cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
Shift values in an image.
Definition: moses.c:18970
int mos_check_multiplex(cpl_table *slits)
Determining whether a VIMOS mask has spectral multplexing or not.
Definition: moses.c:15674
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:19079
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:8359
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:5292
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:16458
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:10569
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:11299
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:5008
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:7764
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:8958
int mos_check_slits(cpl_table *slits, float rescale)
Check that all slit have been detected, insert them if not.
Definition: moses.c:17110
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:18105
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:16268
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:18030
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:14325
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:13204
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:14532
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:14728
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:17213
cpl_table * mos_load_slits_vimos(cpl_propertylist *header)
Create slit location table from FITS header of VIMOS data.
Definition: moses.c:15563
cpl_error_code mos_randomise_image(cpl_image *image, double ron, double gain, double bias)
Randomise image.
Definition: moses.c:16224
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:6194
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:15350
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:5235
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:16360
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:5063
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:11760
int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
Return slit closest to CCD center.
Definition: moses.c:19031
cpl_image * mos_image_filter_median(cpl_image *image, int nx, int ny)
Convenience function for standard median filtering.
Definition: moses.c:19334
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:13708
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:19196
int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
Estimate offset between two object tables.
Definition: moses.c:18767
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:15773
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:19306
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