FORS Pipeline Reference Manual  5.2.7
moses.c
1 /* $Id: moses.c,v 1.116 2013-10-15 09:27:38 cgarcia Exp $
2  *
3  * This file is part of the MOSES library
4  * Copyright (C) 2002-2010 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22  * $Author: cgarcia $
23  * $Date: 2013-10-15 09:27:38 $
24  * $Revision: 1.116 $
25  * $Name: not supported by cvs2svn $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <math.h>
37 #include <time.h>
38 
39 #include <fors_utils.h>
40 #include <moses.h>
41 
42 /* Prototypes */
43 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row);
44 
45 
46 /* Cheating: moving here the cpl_tools_get_median_float() prototype,
47  * even if cpl_tool.h is not public. It should be removed as soon as
48  * an image median filtering with generic kernel will be implemented
49  * in the CPL, or as soon as this module will be moved into the CPL. */
50 /* FIXME: This has to be removed!! */
51 
52 float cpl_tools_get_median_float(float *, cpl_size);
53 
54 #define MAX_COLNAME (80)
55 #define STRETCH_FACTOR (1.20)
56 
57 // Related to mos_identify_peaks(), used in multiplex mode
58 
59 static int mos_multiplex = -1;
60 static int mos_region_size = 800;
61 
62 static double default_lines_hi[] = { /* Default sky line catalog */
63  5577.338, /* for high res data */
64  5889.953,
65  5895.923,
66  5915.301,
67  5932.862,
68  5953.420,
69  6257.961,
70  6287.434,
71  6300.304,
72  6306.869,
73  6363.780,
74  6498.729,
75  6533.044,
76  6553.617,
77  6841.945,
78  6863.955,
79  6870.994,
80  6889.288,
81  6900.833,
82  6912.623,
83  6923.220,
84  6939.521,
85  6969.930,
86  7003.858,
87  7244.907,
88  7276.405,
89  7284.439,
90  7316.282,
91  7329.148,
92  7340.885,
93  7358.659,
94  7571.746,
95  7750.640,
96  7759.996,
97  7794.112,
98  7808.467,
99  7821.503,
100  7841.266,
101  7913.708,
102  7949.204,
103  7964.650,
104  7993.332,
105  8014.059,
106  8310.719,
107  8344.602,
108  8382.392,
109  8399.170,
110  8415.231,
111  8430.174,
112  8452.250,
113  8493.389,
114  8791.186,
115  8827.096,
116  8885.850,
117  8903.114,
118  8943.395,
119  8988.366
120  };
121 
122 static double default_lines_lo[] = { /* Default sky line catalog */
123  5577.338, /* for low res data */
124  6300.304,
125  6863.955,
126  7571.746,
127  7964.650,
128  7993.332
129  };
130 
131 
141 /*
142  * The following macros and function for finding the k-th smallest
143  * value on a float array will be accessible from cpl_tools once
144  * this module will be moved into the CPL.
145  */
146 
147 /****
148 
149 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
150 
151 static float kthSmallest(float a[], int n, int k)
152 {
153  register int i,j,l,m;
154  register float x;
155 
156  l = 0;
157  m = n-1;
158  while (l < m) {
159  x = a[k];
160  i = l;
161  j = m;
162  do {
163  while (a[i] < x) {
164  i++;
165  }
166  while (x < a[j]) {
167  j--;
168  }
169  if (i <= j) {
170  PIX_SWAP(a[i],a[j]);
171  i++;
172  j--;
173  }
174  } while (i <= j);
175 
176  if (j < k) {
177  l = i;
178  }
179  if (k < i) {
180  m = j;
181  }
182 
183  }
184  return(a[k]);
185 }
186 
187 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1)))
188 
189 ****/
190 
191 /*
192  * Return random number with gaussian distribution (mean = 0, variance = 1)
193  * (Box-Mueller method). The mos_randg() argument is either true or false,
194  * indicating whether to "seed" or not the sequence of generated random
195  * numbers. The "seeding" is performed just at the first mos_randg(1) call,
196  * and at further calls the input argument is ignored. This function
197  * generates two random numbers at each call, returning the first one
198  * at odd calls, and the second one at even calls.
199  */
200 
201 static void mos_seed(void)
202 {
203  srand((unsigned int)time((time_t *)0));
204 }
205 
206 static double mos_randg(int seme)
207 {
208  static int doit = 1;
209  static int gotit = 1;
210  double x1, x2, w, y1;
211  static double y2;
212 
213  if (gotit && seme) {
214  mos_seed();
215  gotit = 0;
216  }
217 
218  if (doit) {
219  doit = 0;
220  do {
221  x1 = 2.0 * (double)rand() / RAND_MAX - 1.0;
222  x2 = 2.0 * (double)rand() / RAND_MAX - 1.0;
223  w = x1 * x1 + x2 * x2;
224  } while (w >= 1.0 || w == 0.0);
225 
226  w = sqrt( (-2.0 * log(w)) / w);
227 
228  y1 = x1 * w;
229  y2 = x2 * w;
230  return y1;
231  }
232 
233  doit = 1;
234  return y2;
235 }
236 
237 /*
238  * This function contained a dependency on the VIMOS library
239  * (function medianPixelvalue()): it should be removed as soon as an
240  * image median filtering with generic kernel will be implemented
241  * in the CPL. Currently it has been solved by a direct call to
242  * a cpl_tool function.
243  */
244 
245 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in,
246  int filtsizey, int refrow,
247  int above, int below, int step)
248 {
249 
250  const char *func = "mos_image_general_median_filter";
251 
252  cpl_image *filt_img = NULL;
253  int col, row;
254  float *buf = NULL;
255  float *data;
256  float *fdata;
257  int upright_y, loleft_y;
258  int j;
259  int yIsEven = !(filtsizey - (filtsizey/2)*2);
260  int f2y;
261  int nx = cpl_image_get_size_x(ima_in);
262  int ny = cpl_image_get_size_y(ima_in);
263  int firstRow;
264 
265 
266  if (yIsEven) filtsizey++;
267 
268  if (ny <= filtsizey) {
269  cpl_msg_error(func,
270  "Median filter size: %d, image size: %d", filtsizey, ny);
271  return NULL;
272  }
273 
274  f2y = filtsizey / 2;
275 
276  filt_img = cpl_image_duplicate(ima_in);
277  buf = cpl_malloc(filtsizey * sizeof(float));
278  data = cpl_image_get_data(ima_in);
279  fdata = cpl_image_get_data(filt_img);
280 
281  firstRow = refrow - step * (below / step);
282  if (firstRow < f2y)
283  firstRow += step;
284 
285  for (col = 0; col < nx; col++) {
286  for (row = firstRow; row < refrow + above; row += step) {
287  if (row >= ny - f2y)
288  break;
289  loleft_y = row - f2y;
290  upright_y = row + f2y + 1;
291  for (j = loleft_y; j < upright_y; j++)
292  buf[j - loleft_y] = data[col + j * nx];
293 
294  fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey);
295  }
296  }
297 
298  cpl_free(buf);
299 
300  return filt_img;
301 
302 }
303 
304 
305 /*
306  * The following static function is used to find an accurate position
307  * of a peak within a short interval (however at least 5 pixels long).
308  * The basic idea is to find the baricenter of all the pixel values
309  * that pass a threshold level between the median value and the maximum
310  * value within the examined interval (in case such levels are equal,
311  * the input is considered flat and no position is returned). At least
312  * minPoints must pass this threshold, or no position is computed. To
313  * evaluate the significance of the computed baricenter, the variance
314  * of the contributing positions (relative to the found baricenter) is
315  * also evaluated, and compared with the expected variance for a uniform
316  * distribution of positions. If the observed variance is greater than
317  * 80% of the variance of the uniform distribution, the found position
318  * is rejected.
319  */
320 
321 static int peakPosition(const float *data, int size, float *position,
322  int minPoints)
323 {
324  int i;
325  int count = 0;
326  float *copy;
327  float max, median, level, pos, variance, uniformVariance;
328  double sum, weights;
329 
330 
331  if (data == NULL)
332  return 1;
333 
334  if (size < 5) /* Hardcoded, I know... */
335  return 1;
336 
337 
338  /*
339  * Find median level
340  */
341 
342  copy = (float *) cpl_malloc(size*sizeof(float));
343  for (i = 0; i < size; i++)
344  copy[i] = data[i];
345  median = cpl_tools_get_median_float(copy, size);
346  cpl_free(copy);
347 
348 
349  /*
350  * Find max
351  */
352 
353  max = data[0];
354  for (i = 1; i < size; i++)
355  if (data[i] > max)
356  max = data[i];
357 
358 
359  /*
360  * If the max equals the median we have a flat input, therefore
361  * no peak is found.
362  */
363 
364  if (max-median < 0.00001)
365  return 1;
366 
367 
368  /*
369  * Discrimination level: only pixels with values above this
370  * level are considered in baricenter calculation.
371  */
372 
373  level = (max + median) / 2;
374 
375 
376  /*
377  * Of the values above this level compute the baricenter and
378  * then the variance of the positions used. Note that the weights
379  * are taken as the difference between the pixels values and
380  * the median level (supposedly the background).
381  */
382 
383  count = 0;
384  for (i = 0, sum = 0., weights = 0.; i < size; i++) {
385  if (data[i] > level) {
386  count++;
387  weights += (data[i] - median);
388  sum += i * (data[i] - median);
389  }
390  }
391 
392 
393  /*
394  * If too few values are above threshold, refuse the position
395  * as insignificant
396  */
397 
398  if (count < minPoints)
399  return 1;
400 
401  pos = sum / weights;
402  for (i = 0, sum = 0., weights = 0.; i < size; i++) {
403  if (data[i] > level) {
404  weights++;
405  sum += (i - pos) * (i - pos);
406  }
407  }
408  variance = sqrt(sum / weights);
409 
410 
411  /*
412  * The "uniform variance" is the variance that should be obtained
413  * in the case of uniform distribution of the points positions in
414  * the selected interval. If the real variance is comparable with
415  * this value, the peak is considered not found.
416  */
417 
418  uniformVariance = sqrt(size*size/3 - pos*size + pos*pos);
419 
420  if (variance > 0.8 * uniformVariance)
421  return 1;
422 
423  *position = pos + 0.5;
424 
425  return 0;
426 }
427 
428 
429 /*
430  * The following static function determines the quantity dx to be
431  * added to the position of the highest pixel of a fiber profile,
432  * to get the true position of the profile maximum. All is needed
433  * is the maximum observed value v2 in the profile, and the observed
434  * values v1 and v3 of the previous and the next pixels in the profile.
435  *
436  * The following ratio is defined:
437  *
438  * R = 0.5 (v3 - v1) / (2*v2 - v3 - v1)
439  *
440  * This is a conventional ratio that wouldn't diverge for any set of
441  * pixel values, and that would not depend on the presence of background
442  * (with the assumption that the background level is the same for the
443  * three pixels). R has also been chosen in such a way that its value
444  * is already quite close to the real dx. It should be noted that the
445  * following condition should be fulfilled:
446  *
447  * v1 <= v2 and v3 < v2
448  * or
449  * v1 < v2 and v3 <= v2
450  *
451  * This implies that dx varies between -0.5 and 0.5 pixels. In such
452  * boundary cases, one has:
453  *
454  * v2 = v1 and R = dx = -0.5
455  * v2 = v3 and R = dx = 0.5
456  *
457  * Another special case is when the observed pixel values are perfectly
458  * symmetrical:
459  *
460  * v1 = v3 and R = dx = 0.0
461  *
462  * In all the intermediate cases the relation between R and dx depends
463  * on the shape of the fiber profile, that has been determined elsewhere.
464  * Using the accurate reconstruction of the fiber profile obtained by
465  * the * functions ifuProfile() and rebinProfile(), it can be shown
466  * that R differs from dx always less than 0.01 pixels. If the condition
467  *
468  * v1 <= v2 and v3 < v2
469  * or
470  * v1 < v2 and v3 <= v2
471  *
472  * is not fulfilled, then this function returns the value 2.0.
473  */
474 
475 static double values_to_dx(double v1, double v2, double v3)
476 {
477 
478  static double epsilon = 0.00000001;
479  double r = 2.0;
480 
481 
482  if (v1 > v2 || v3 > v2)
483  return r;
484 
485  if (2 * v2 - v1 - v3 < epsilon)
486  return r;
487 
488  r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1);
489 
490  return r;
491 
492 }
493 
494 
495 /*
496  * The following static function passes a min filter of given box
497  * size on the data buffer. The box size must be a positive odd integer.
498  */
499 
500 static float *min_filter(float *buffer, int length, int size)
501 {
502  float *minf = cpl_calloc(length, sizeof(float));
503  float min;
504  int start = size / 2;
505  int end = length - size / 2;
506  int i, j;
507 
508 
509  for (i = start; i < end; i++) {
510  min = buffer[i-start];
511  for (j = i - start + 1; j <= i + start; j++)
512  if (min > buffer[j])
513  min = buffer[j];
514  minf[i] = min;
515  }
516 
517  for (i = 0; i < start; i++)
518  minf[i] = minf[start];
519 
520  for (i = end; i < length; i++)
521  minf[i] = minf[end-1];
522 
523  return minf;
524 }
525 
526 
527 /*
528  * The following static function passes a max filter of given box
529  * size on the data buffer. The box size must be a positive odd integer.
530  */
531 
532 static float *max_filter(float *buffer, int length, int size)
533 {
534  float *maxf = cpl_calloc(length, sizeof(float));
535  float max;
536  int start = size / 2;
537  int end = length - size / 2;
538  int i, j;
539 
540 
541  for (i = start; i < end; i++) {
542  max = buffer[i-start];
543  for (j = i - start + 1; j <= i + start; j++)
544  if (max < buffer[j])
545  max = buffer[j];
546  maxf[i] = max;
547  }
548 
549  for (i = 0; i < start; i++)
550  maxf[i] = maxf[start];
551 
552  for (i = end; i < length; i++)
553  maxf[i] = maxf[end-1];
554 
555  return maxf;
556 }
557 
558 
559 /*
560  * The following static function passes a running average of given box
561  * size on the data buffer. The box size must be a positive odd integer.
562  */
563 
564 static float *smo_filter(float *buffer, int length, int size)
565 {
566  float *smof = cpl_calloc(length, sizeof(float));
567  double sum;
568  int start = size / 2;
569  int end = length - size / 2;
570  int i, j;
571 
572 
573  for (i = start; i < end; i++) {
574  sum = 0.0;
575  for (j = i - start; j <= i + start; j++)
576  sum += buffer[j];
577  smof[i] = sum / size;
578  }
579 
580  for (i = 0; i < start; i++)
581  smof[i] = smof[start];
582 
583  for (i = end; i < length; i++)
584  smof[i] = smof[end-1];
585 
586  return smof;
587 }
588 
589 /*
590  * The following two static functions are used to read and write from the
591  * global distortion table the different model components. Conventionally
592  * the table consists of 6 columns and 10 rows. Each row is just ordered
593  * storage for model coefficients, and these functions guarantee that the
594  * coefficients are read in and written out correctly, independent on their
595  * physical meaning. The first 6 table rows are a description of the IDS
596  * coefficients, followed by a row containing only the used reference
597  * wavelength. The remaining 3 are a description of the spectral curvature.
598  * The first row is a description of coefficient c0, the second of coefficient
599  * c1, etc., of the IDS. The 8th row is a description of coefficient c0,
600  * the 9th of coefficient c1, etc., of the spectral curvature. All are
601  * bivariate polynomialx on x,y mask coordinates. If the input table
602  * to the write routine is NULL, it is allocated and initialised. Also
603  * the input polynomial could be NULL, and nothing would be written to
604  * the table. If both pointers are NULL the function is basically a
605  * constructor of the global distortion table.
606  */
607 
608 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row)
609 {
610  cpl_polynomial *poly = NULL;
611  cpl_size p[2];
612  cpl_size degree = 2;
613  int null;
614  double coeff;
615 
616  char name[MAX_COLNAME];
617 
618 
619  for (p[0] = 0; p[0] <= degree; p[0]++) {
620  for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
621  snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
622  coeff = cpl_table_get_double(global, name, row, &null);
623  if (null)
624  continue;
625  if (poly == NULL)
626  poly = cpl_polynomial_new(2);
627  cpl_polynomial_set_coeff(poly, p, coeff);
628  }
629  }
630 
631  return poly;
632 }
633 
634 static cpl_table *write_global_distortion(cpl_table *global, int row,
635  cpl_polynomial *poly)
636 {
637  cpl_table *table;
638  cpl_size p[2];
639  cpl_size degree = 2;
640  int nrow = 13;
641 
642  char name[MAX_COLNAME];
643 
644 
645  if (global) {
646  table = global;
647  }
648  else {
649  table = cpl_table_new(nrow);
650  for (p[0] = 0; p[0] <= degree; p[0]++) {
651  for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
652  snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
653  cpl_table_new_column(table, name, CPL_TYPE_DOUBLE);
654  }
655  }
656  }
657 
658  if (poly) {
659  for (p[0] = 0; p[0] <= degree; p[0]++) {
660  for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
661  snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
662  cpl_table_set_double(table, name, row,
663  cpl_polynomial_get_coeff(poly, p));
664  }
665  }
666  }
667 
668  return table;
669 }
670 
671 
672 /*
673  * The following static function is performing a robust linear fit
674  * (drawn from the VIMOS library, and originally from ESO-Eclipse).
675  *
676  * ----> y = a + b * x
677  *
678  * This function return 0 on success.
679  */
680 
681 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
682 static int robustLinearFit(cpl_bivector *list, double *a, double *b,
683  double *abdev)
684 {
685  cpl_vector *vx;
686  cpl_vector *vy;
687  cpl_vector *va;
688 
689  double aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum;
690  double sx, sy, sxy, sxx, chisq;
691  double *arr;
692  double aa_ls, bb_ls;
693  double *x;
694  double *y;
695  int np;
696  int iter;
697  int max_iterate = 30;
698  int i;
699 
700 
701  np = cpl_bivector_get_size(list);
702  vx = cpl_bivector_get_x(list);
703  vy = cpl_bivector_get_y(list);
704  x = cpl_vector_get_data(vx);
705  y = cpl_vector_get_data(vy);
706 
707  sx = sy = sxx = sxy = 0.00;
708  for (i = 0; i < np; i++) {
709  sx += x[i];
710  sy += y[i];
711  sxy += x[i] * y[i];
712  sxx += x[i] * x[i];
713  }
714 
715  del = np * sxx - sx * sx;
716  aa_ls = aa = (sxx * sy - sx * sxy) / del;
717  bb_ls = bb = (np * sxy - sx * sy) / del;
718 
719  chisq = 0.00;
720  for (i = 0; i < np; i++) {
721  temp = y[i] - (aa+bb*x[i]);
722  temp *= temp;
723  chisq += temp;
724  }
725 
726  va = cpl_vector_new(np);
727  arr = cpl_vector_get_data(va);
728  sigb = sqrt(chisq/del);
729  b1 = bb;
730 
731  bcomp = b1;
732  sum = 0.00;
733  for (i = 0; i < np; i++) {
734  arr[i] = y[i] - bcomp * x[i];
735  }
736  aa = cpl_vector_get_median_const(va);
737  abdevt = 0.0;
738  for (i = 0; i < np; i++) {
739  d = y[i] - (bcomp * x[i] + aa);
740  abdevt += fabs(d);
741  if (y[i] != 0.0)
742  d /= fabs(y[i]);
743  if (fabs(d) > 1e-7)
744  sum += (d >= 0.0 ? x[i] : -x[i]);
745  }
746  f1 = sum;
747 
748  b2 = bb + SEGNO(3.0 * sigb, f1);
749 
750  bcomp = b2;
751  sum = 0.00;
752  for (i = 0; i < np; i++) {
753  arr[i] = y[i] - bcomp * x[i];
754  }
755  aa = cpl_vector_get_median_const(va);
756  abdevt = 0.0;
757  for (i = 0; i < np; i++) {
758  d = y[i] - (bcomp * x[i] + aa);
759  abdevt += fabs(d);
760  if (y[i] != 0.0)
761  d /= fabs(y[i]);
762  if (fabs(d) > 1e-7)
763  sum += (d >= 0.0 ? x[i] : -x[i]);
764  }
765  f2 = sum;
766 
767  if (fabs(b2-b1)<1e-7) {
768  *a = aa;
769  *b = bb;
770  *abdev = abdevt / (double)np;
771  cpl_vector_delete(va);
772  return 0;
773  }
774 
775  iter = 0;
776  while (f1*f2 > 0.0) {
777  bb = 2.0*b2-b1;
778  b1 = b2;
779  f1 = f2;
780  b2 = bb;
781 
782  bcomp = b2;
783  sum = 0.00;
784  for (i = 0; i < np; i++) {
785  arr[i] = y[i] - bcomp * x[i];
786  }
787  aa = cpl_vector_get_median_const(va);
788  abdevt = 0.0;
789  for (i = 0; i < np; i++) {
790  d = y[i] - (bcomp * x[i] + aa);
791  abdevt += fabs(d);
792  if (y[i] != 0.0)
793  d /= fabs(y[i]);
794  if (fabs(d) > 1e-7)
795  sum += (d >= 0.0 ? x[i] : -x[i]);
796  }
797  f2 = sum;
798  iter++;
799  if (iter >= max_iterate)
800  break;
801  }
802  if (iter >= max_iterate) {
803  *a = aa_ls;
804  *b = bb_ls;
805  *abdev = -1.0;
806  cpl_vector_delete(va);
807  return 1;
808  }
809 
810  sigb = 0.01 * sigb;
811  while (fabs(b2-b1) > sigb) {
812  bb = 0.5 * (b1 + b2);
813  if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7))
814  break;
815  bcomp = bb;
816  sum = 0.0;
817  for (i = 0; i < np; i++) {
818  arr[i] = y[i] - bcomp * x[i];
819  }
820  aa = cpl_vector_get_median_const(va);
821  abdevt = 0.0;
822  for (i = 0; i < np; i++) {
823  d = y[i] - (bcomp * x[i] + aa);
824  abdevt += fabs(d);
825  if (y[i] != 0.0)
826  d /= fabs(y[i]);
827  if (fabs(d) > 1e-7)
828  sum += (d >= 0.0 ? x[i] : -x[i]);
829  }
830  f = sum;
831 
832  if (f*f1 >= 0.0) {
833  f1=f;
834  b1=bb;
835  }
836  else {
837  f2=f;
838  b2=bb;
839  }
840  }
841  cpl_vector_delete(va);
842  *a = aa;
843  *b = bb;
844  *abdev = abdevt / np;
845  return 0;
846 }
847 #undef SEGNO
848 
849 /*
850  * The following static function applies the Hough transform from a table
851  * of points to another table of points. Given the points p_i = (x_i,y_i)
852  * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j)
853  * and Y = y_i - X*x_i is computed and added to the output table for each
854  * p_i, p_j pair. This means that if the input table has N points, the
855  * output table has N*(N-1)/2 points.
856  */
857 
858 /* static */
859 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y)
860 {
861  cpl_table *output;
862  double *xdata;
863  double *ydata;
864  double *xodata;
865  double *yodata;
866  int npoints;
867  int opoints;
868  int i, j, k;
869 
870 
871  npoints = cpl_table_get_nrow(table);
872  opoints = npoints*(npoints-1)/2;
873 
874  output = cpl_table_new(opoints);
875  cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE);
876  cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE);
877  cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0);
878  cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0);
879 
880  xodata = cpl_table_get_data_double(output, "m");
881  yodata = cpl_table_get_data_double(output, "q");
882 
883  cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE);
884  cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE);
885 
886  xdata = cpl_table_get_data_double(table, "x");
887  ydata = cpl_table_get_data_double(table, "y");
888 
889  k = 0;
890  for (i = 0; i < npoints; i++) {
891  for (j = i+1; j < npoints; j++) {
892  xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]);
893  yodata[k] = ydata[i] - xodata[k] * xdata[i];
894  k++;
895  }
896  }
897 
898  if (k != opoints)
899  printf("Assert k = %d, expected %d\n", k, opoints);
900 
901  cpl_table_erase_column(table, "x");
902  cpl_table_erase_column(table, "y");
903 
904  return output;
905 }
906 
907 
908 /*
909  * The following static function is performing the spectral
910  * extraction for the function mos_extract_objects()
911  */
912 
913 static void mos_extraction(cpl_image *sciwin, cpl_image *sci_var_win,
914  cpl_image *skywin,
915  cpl_image *extracted, cpl_image *sky,
916  cpl_image *error, int nobjects, int extraction,
917  double ron, double conad, int ncomb)
918 {
919 
920  cpl_vector *vprofile;
921  cpl_image *smowin;
922 
923  int i, j;
924  int specLen;
925  int numRows;
926  int index;
927  int iter;
928  int maxIter = 2; /* Not less than 2 !!! */
929  int smoothBox = 31; /* Not less than 5 !!! */
930  double nsigma = 5.0;
931 
932  double sumWeight, sum, sumSky, sumProf, sumVar, variance, weight;
933  double *profile;
934  double *buffer;
935  float *edata;
936  float *ekdata;
937  float *endata;
938  float *sdata;
939  float *kdata;
940  float *fdata;
941  float *vardata;
942 
943  double value;
944 
945 
946  specLen = cpl_image_get_size_x(sciwin);
947  numRows = cpl_image_get_size_y(sciwin);
948 
949  edata = cpl_image_get_data(extracted);
950  edata += nobjects * specLen;
951 
952  ekdata = cpl_image_get_data(sky);
953  ekdata += nobjects * specLen;
954 
955  endata = cpl_image_get_data(error);
956  endata += nobjects * specLen;
957 
958  sdata = cpl_image_get_data(sciwin);
959  kdata = cpl_image_get_data(skywin);
960  if(sci_var_win != NULL)
961  vardata = cpl_image_get_data(sci_var_win);
962 
963  /*
964  * Initial spectrum estimate
965  if (sdata[i + j * specLen] > 0.0)
966  */
967 
968  if (extraction && numRows > 5) {
969  smowin = mos_image_filter_median(sciwin, 3, 3);
970  fdata = cpl_image_get_data(smowin);
971  for (i = 0; i < specLen; i++)
972  for (j = 0, edata[i] = 0.0; j < numRows; j++)
973  edata[i] += fdata[i + j * specLen];
974  cpl_image_delete(smowin);
975  }
976  else {
977  for (i = 0; i < specLen; i++)
978  for (j = 0, edata[i] = 0.0; j < numRows; j++)
979  edata[i] += sdata[i + j * specLen];
980  }
981 
982  if (extraction) {
983 
984  profile = cpl_calloc(specLen * numRows, sizeof(double));
985  buffer = cpl_calloc(specLen, sizeof(double));
986 
987  for (iter = 0; iter < maxIter; iter++) {
988 
989  /*
990  * Normalised spatial profile
991  */
992 
993  for (i = 0; i < specLen; i++) {
994  for (j = 0; j < numRows; j++) {
995  index = i + j * specLen;
996 /* if (sdata[index] > 0.0 && edata[i] > 0.00001) */
997  if (fabs(edata[i]) > 0.00001)
998  profile[index] = sdata[index] / edata[i];
999  else
1000  profile[index] = 0.0;
1001  }
1002  }
1003 
1004  for (j = 0; j < numRows; j++) {
1005 
1006  /*
1007  * Smooth each row in the dispersion direction, and enforce positivity
1008  */
1009 
1010  for (i = 0; i < specLen - smoothBox; i++) {
1011  vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen);
1012  value = cpl_vector_get_median_const(vprofile);
1013  cpl_vector_unwrap(vprofile);
1014  if (value < 0)
1015  value = 0.0;
1016  buffer[i + smoothBox / 2] = value;
1017  }
1018 
1019  /*
1020  * Replace the end portions (i.e., not median filtered) with a mean
1021  */
1022 
1023  vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen);
1024  value = cpl_vector_get_mean(vprofile);
1025  cpl_vector_unwrap(vprofile);
1026 
1027  if (value < 0)
1028  value = 0.0;
1029 
1030  for (i = 0; i < smoothBox / 2; i++)
1031  buffer[i] = value;
1032 
1033  vprofile = cpl_vector_wrap(smoothBox / 2,
1034  profile + specLen - smoothBox/2 + j*specLen);
1035  value = cpl_vector_get_mean(vprofile);
1036  cpl_vector_unwrap(vprofile);
1037 
1038  if (value < 0)
1039  value = 0.0;
1040 
1041  for (i = 0; i < smoothBox / 2; i++)
1042  buffer[i + specLen - smoothBox / 2] = value;
1043 
1044  for (i = 0; i < specLen; i++)
1045  profile[i + j * specLen] = buffer[i];
1046 
1047  }
1048 
1049  /*
1050  * Enforce normalization of spatial profile after smoothing
1051  */
1052 
1053  for (i = 0; i < specLen; i++) {
1054  for (j = 0, value = 0.0; j < numRows; j++)
1055  value += profile[i + j * specLen];
1056  if (value > 0.00001)
1057  for (j = 0; j < numRows; j++)
1058  profile[i + j * specLen] /= value;
1059  else
1060  for (j = 0; j < numRows; j++)
1061  profile[i + j * specLen] = 0.0;
1062  }
1063 
1064 
1065  /*
1066  * Optimal extraction
1067  */
1068 
1069  for (i = 0; i < specLen; i++) {
1070  sum = 0.0;
1071  sumSky = 0.0;
1072  sumWeight = 0.0;
1073  sumProf = 0.0;
1074  sumVar = 0;
1075  for (j = 0; j < numRows; j++) {
1076  index = i + j * specLen;
1077  /*
1078  if (sdata[index] > 0.0) {
1079  */
1080  //This is the theoretical estimated variance. In principle, since we
1081  //have the propagated variance, we could use that one, but I leave
1082  //this as this is the original algorithm (cgarcia)
1083  variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index])
1084  / conad;
1085  variance /= ncomb; /* If input dataset is sum of ncomb images */
1086  value = sdata[index] - edata[i] * profile[index];
1087  if (fabs(value) / sqrt(variance) < nsigma) {
1088  weight = 1000000 * profile[index] / variance;
1089  sum += weight * sdata[index];
1090  sumSky += weight * kdata[index];
1091  sumWeight += weight * profile[index];
1092  sumProf += profile[index];
1093  //This is how we propagated the variance. We assume that the
1094  //weigth has no error, although in has been computed from the
1095  //profile and the theoretical variance (which also includes the data)
1096  if(sci_var_win != NULL)
1097  sumVar += weight * weight * vardata[index];
1098  }
1099  }
1100 
1101  if (sumWeight > 0.00001) {
1102  edata[i] = sum / sumWeight;
1103  ekdata[i] = sumSky / sumWeight;
1104  if(sci_var_win != NULL)
1105  endata[i] = sqrt(sumVar / sumWeight / sumWeight); //This is the error, not the variance.
1106  else
1107  endata[i] = 1000 * sqrt(sumProf / sumWeight); //This was the old formula, which is not a real error propagation
1108  }
1109  else {
1110 /*
1111  edata[i] = 0.0;
1112  ekdata[i] = 0.0;
1113  endata[i] = 0.0;
1114 */
1115  //endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
1116  }
1117  }
1118  }
1119  cpl_free(profile);
1120  cpl_free(buffer);
1121  }
1122  else {
1123 
1124  /*
1125  * Add sky estimation for the simple aperture extraction.
1126  if (kdata[i + j * specLen] > 0.0)
1127  */
1128 
1129  for (i = 0; i < specLen; i++)
1130  for (j = 0, ekdata[i] = 0.0; j < numRows; j++)
1131  ekdata[i] += kdata[i + j * specLen];
1132 
1133  /*
1134  * Add error estimation for the simple aperture extraction.
1135  */
1136  for (i = 0; i < specLen; i++)
1137  {
1138  if(sci_var_win != NULL)
1139  {
1140  //We propagate the variance of a simple addition
1141  for (j = 0, endata[i] = 0.0; j < numRows; j++)
1142  endata[i] += vardata[i + j * specLen];
1143  endata[i] = sqrt(endata[i]); //We return the error, not the variance
1144  }
1145  else
1146  endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
1147 
1148  }
1149  }
1150 
1151 }
1152 
1153 
1200 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits,
1201  cpl_table *ids, cpl_table *crv,
1202  double reference)
1203 {
1204  const char *func = "mos_global_distortion";
1205 
1206  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
1207 
1208  cpl_table *global = NULL;
1209  cpl_table *coeff;
1210  cpl_table *dummy;
1211  cpl_vector *ci;
1212  cpl_vector *xmask;
1213  cpl_vector *ymask;
1214  cpl_bivector *mask;
1215  cpl_vector *xccd;
1216  cpl_vector *yccd;
1217  cpl_bivector *ccd;
1218  cpl_polynomial *poly;
1219  double *xtop;
1220  double *ytop;
1221  double *xbottom;
1222  double *ybottom;
1223  double *mxtop;
1224  double *mytop;
1225  double *mxbottom;
1226  double *mybottom;
1227  int *position;
1228  int *length;
1229  int *slit_id;
1230  int *mslit_id;
1231  int nslits, nmaskslits, npoints;
1232  int order;
1233  int i, j;
1234  int minslit = 6; // 12;
1235 
1236 
1237  if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) {
1238  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
1239  return NULL;
1240  }
1241 
1242  nslits = cpl_table_get_nrow(slits);
1243 
1244  if (nslits < minslit) {
1245  cpl_msg_warning(func, "Too few slits (%d < %d) for global "
1246  "distortion model determination", nslits, minslit);
1247  return NULL;
1248  }
1249 
1250  nmaskslits = cpl_table_get_nrow(maskslits);
1251 
1252  length = cpl_table_get_data_int(slits, "length");
1253  position = cpl_table_get_data_int(slits, "position");
1254  slit_id = cpl_table_get_data_int(slits, "slit_id");
1255  mslit_id = cpl_table_get_data_int(maskslits, "slit_id");
1256  xtop = cpl_table_get_data_double(slits, "xtop");
1257  ytop = cpl_table_get_data_double(slits, "ytop");
1258  xbottom = cpl_table_get_data_double(slits, "xbottom");
1259  ybottom = cpl_table_get_data_double(slits, "ybottom");
1260  mxtop = cpl_table_get_data_double(maskslits, "xtop");
1261  mytop = cpl_table_get_data_double(maskslits, "ytop");
1262  mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
1263  mybottom = cpl_table_get_data_double(maskslits, "ybottom");
1264 
1265 
1266  /*
1267  * Global IDS
1268  */
1269 
1270  coeff = cpl_table_new(nslits);
1271  cpl_table_copy_structure(coeff, ids);
1272  cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
1273  cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
1274  cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
1275  cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
1276 
1277  for (i = 0; i < nslits; i++) {
1278  for (j = 0; j < nmaskslits; j++) {
1279  if (slit_id[i] == mslit_id[j]) {
1280  cpl_table_set_double(coeff, "xmask", i,
1281  (mxtop[j] + mxbottom[j]) / 2);
1282  cpl_table_set_double(coeff, "ymask", i,
1283  (mytop[j] + mybottom[j]) / 2);
1284  }
1285  }
1286  }
1287 
1288  if (cpl_table_has_invalid(coeff, "xmask")) {
1289  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
1290  cpl_table_delete(coeff);
1291  return NULL;
1292  }
1293 
1294  for (i = 0; i < nslits; i++) {
1295  cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2);
1296  cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2);
1297  }
1298 
1299  for (i = 0; i < nslits; i++) {
1300 
1301  if (length[i] == 0)
1302  continue;
1303 
1304  cpl_table_and_selected_window(ids, position[i], length[i]);
1305  dummy = cpl_table_extract_selected(ids);
1306  for (j = 0; j < 6; j++) {
1307  if (cpl_table_has_column(dummy, clab[j])) {
1308  if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) {
1309  cpl_table_set_double(coeff, clab[j], i,
1310  cpl_table_get_column_median(dummy, clab[j]));
1311  }
1312  }
1313  }
1314 
1315  cpl_table_delete(dummy);
1316  cpl_table_select_all(ids);
1317 
1318  }
1319 
1320  for (j = 0; j < 6; j++) {
1321  if (cpl_table_has_column(coeff, clab[j])) {
1322  cpl_table_and_selected_invalid(coeff, clab[j]);
1323 
1324  if (cpl_table_not_selected(coeff))
1325  dummy = cpl_table_extract_selected(coeff);
1326  else
1327  break;
1328 
1329  npoints = cpl_table_get_nrow(dummy);
1330 
1331  if (npoints >= 6) {
1332 
1333  if (npoints >= 12)
1334  order = 2;
1335  else
1336  order = 1;
1337 
1338  ci = cpl_vector_wrap(npoints,
1339  cpl_table_get_data_double(dummy, clab[j]));
1340  if (j) {
1341  xccd = cpl_vector_wrap(npoints,
1342  cpl_table_get_data_double(dummy, "xccd"));
1343  yccd = cpl_vector_wrap(npoints,
1344  cpl_table_get_data_double(dummy, "yccd"));
1345  ccd = cpl_bivector_wrap_vectors(xccd, yccd);
1346 
1347 /* %%% */
1348  poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
1349 
1350  cpl_bivector_unwrap_vectors(ccd);
1351  cpl_vector_unwrap(xccd);
1352  cpl_vector_unwrap(yccd);
1353  cpl_vector_unwrap(ci);
1354  }
1355  else {
1356  xmask = cpl_vector_wrap(npoints,
1357  cpl_table_get_data_double(dummy, "xmask"));
1358  ymask = cpl_vector_wrap(npoints,
1359  cpl_table_get_data_double(dummy, "ymask"));
1360  mask = cpl_bivector_wrap_vectors(xmask, ymask);
1361 
1362 /* %%% */
1363  poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
1364 
1365  cpl_bivector_unwrap_vectors(mask);
1366  cpl_vector_unwrap(xmask);
1367  cpl_vector_unwrap(ymask);
1368  cpl_vector_unwrap(ci);
1369  }
1370  }
1371  else {
1372  cpl_size p[2] = {0, 0};
1373  poly = cpl_polynomial_new(2);
1374  cpl_polynomial_set_coeff(poly, p,
1375  cpl_table_get_column_median(dummy, clab[j]));
1376  }
1377 
1378  cpl_table_delete(dummy);
1379 
1380  global = write_global_distortion(global, j, poly);
1381 
1382  cpl_polynomial_delete(poly);
1383 
1384  cpl_table_select_all(coeff);
1385  }
1386  }
1387 
1388  cpl_table_delete(coeff);
1389 
1390 
1391  /*
1392  * Add model's reference wavelength
1393  */
1394 
1395  cpl_table_set_double(global, "a00", 6, reference);
1396 
1397 
1398  /*
1399  * Global curvature model
1400  */
1401 
1402  coeff = cpl_table_duplicate(crv);
1403  cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
1404  cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
1405  cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
1406  cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
1407  slit_id = cpl_table_get_data_int(coeff, "slit_id");
1408  npoints = cpl_table_get_nrow(coeff);
1409 
1410  for (i = 0; i < npoints; i++) {
1411  for (j = 0; j < nmaskslits; j++) {
1412  if (slit_id[i] == mslit_id[j]) {
1413  if (i%2) {
1414  cpl_table_set_double(coeff, "xmask", i, mxbottom[j]);
1415  cpl_table_set_double(coeff, "ymask", i, mybottom[j]);
1416  }
1417  else {
1418  cpl_table_set_double(coeff, "xmask", i, mxtop[j]);
1419  cpl_table_set_double(coeff, "ymask", i, mytop[j]);
1420  }
1421  }
1422  }
1423  if (i%2)
1424  {
1425  cpl_table_set_double(coeff, "xccd", i, xtop[(i-1)/2]);
1426  cpl_table_set_double(coeff, "yccd", i, ytop[(i-1)/2]);
1427  }
1428  else
1429  {
1430  cpl_table_set_double(coeff, "xccd", i, xbottom[i/2]);
1431  cpl_table_set_double(coeff, "yccd", i, ybottom[i/2]);
1432  }
1433  }
1434 
1435  if (cpl_table_has_invalid(coeff, "xmask")) {
1436  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
1437  cpl_table_delete(coeff);
1438  return NULL;
1439  }
1440 
1441  for (j = 0; j < 3; j++) {
1442  cpl_polynomial * poly_ccd;
1443  if (cpl_table_has_column(coeff, clab[j])) {
1444  cpl_table_and_selected_invalid(coeff, clab[j]);
1445 
1446  if (cpl_table_not_selected(coeff))
1447  dummy = cpl_table_extract_selected(coeff);
1448  else
1449  break;
1450 
1451  npoints = cpl_table_get_nrow(dummy);
1452 
1453  if (npoints >= 6) {
1454 
1455  if (npoints >= 12)
1456  order = 2;
1457  else
1458  order = 1;
1459 
1460  ci = cpl_vector_wrap(npoints,
1461  cpl_table_get_data_double(dummy, clab[j]));
1462  xmask = cpl_vector_wrap(npoints,
1463  cpl_table_get_data_double(dummy, "xmask"));
1464  ymask = cpl_vector_wrap(npoints,
1465  cpl_table_get_data_double(dummy, "ymask"));
1466  mask = cpl_bivector_wrap_vectors(xmask, ymask);
1467 
1468  poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
1469 
1470  xccd = cpl_vector_wrap(npoints,
1471  cpl_table_get_data_double(dummy, "xccd"));
1472  yccd = cpl_vector_wrap(npoints,
1473  cpl_table_get_data_double(dummy, "yccd"));
1474  ccd = cpl_bivector_wrap_vectors(xccd, yccd);
1475 
1476  poly_ccd = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
1477 
1478 
1479  cpl_bivector_unwrap_vectors(mask);
1480  cpl_vector_unwrap(ci);
1481  cpl_vector_unwrap(xmask);
1482  cpl_vector_unwrap(ymask);
1483  }
1484  else {
1485  cpl_size p[2] = {0, 0};
1486  poly = cpl_polynomial_new(2);
1487  cpl_polynomial_set_coeff(poly, p,
1488  cpl_table_get_column_median(dummy, clab[j]));
1489  }
1490 
1491  cpl_table_delete(dummy);
1492 
1493  global = write_global_distortion(global, j + 7, poly);
1494  global = write_global_distortion(global, j + 10, poly_ccd);
1495 
1496  cpl_polynomial_delete(poly);
1497  cpl_polynomial_delete(poly_ccd);
1498  cpl_table_select_all(coeff);
1499  }
1500  }
1501 
1502  cpl_table_delete(coeff);
1503 
1504  return global;
1505 
1506 }
1507 
1508 
1546 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits,
1547  int ysize)
1548 {
1549  const char *func = "mos_build_slit_location";
1550 
1551  cpl_propertylist *sort_col;
1552  cpl_polynomial *ids0;
1553  cpl_polynomial *crv[3];
1554  cpl_polynomial *loc_crv;
1555  cpl_vector *point;
1556  cpl_table *slits;
1557  cpl_size nslits;
1558  int *slit_id;
1559  double *dpoint;
1560  double *xtop;
1561  double *ytop;
1562  double *xbottom;
1563  double *ybottom;
1564  double *mxtop;
1565  double *mytop;
1566  double *mxbottom;
1567  double *mybottom;
1568  cpl_size i;
1569  cpl_size j;
1570 
1571 
1572  if (global == NULL || maskslits == NULL) {
1573  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
1574  return NULL;
1575  }
1576 
1577  nslits = cpl_table_get_nrow(maskslits);
1578  slit_id = cpl_table_get_data_int(maskslits, "slit_id");
1579  mxtop = cpl_table_get_data_double(maskslits, "xtop");
1580  mytop = cpl_table_get_data_double(maskslits, "ytop");
1581  mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
1582  mybottom = cpl_table_get_data_double(maskslits, "ybottom");
1583 
1584  slits = cpl_table_duplicate(maskslits);
1585 
1586  xtop = cpl_table_get_data_double(slits, "xtop");
1587  ytop = cpl_table_get_data_double(slits, "ytop");
1588  xbottom = cpl_table_get_data_double(slits, "xbottom");
1589  ybottom = cpl_table_get_data_double(slits, "ybottom");
1590 
1591  ids0 = read_global_distortion(global, 0);
1592  crv[0] = read_global_distortion(global, 7);
1593  crv[1] = read_global_distortion(global, 8);
1594  crv[2] = read_global_distortion(global, 9);
1595 
1596  loc_crv = cpl_polynomial_new(1);
1597 
1598  point = cpl_vector_new(2);
1599  dpoint = cpl_vector_get_data(point);
1600 
1601  for (i = 0; i < nslits; i++) {
1602  dpoint[0] = mxtop[i];
1603  dpoint[1] = mytop[i];
1604 
1605  xtop[i] = cpl_polynomial_eval(ids0, point);
1606 
1607  for (j = 0; j < 3; j++)
1608  if (crv[j])
1609  cpl_polynomial_set_coeff(loc_crv, &j,
1610  cpl_polynomial_eval(crv[j], point));
1611 
1612  ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL);
1613 
1614  dpoint[0] = mxbottom[i];
1615  dpoint[1] = mybottom[i];
1616  xbottom[i] = cpl_polynomial_eval(ids0, point);
1617 
1618  for (j = 0; j < 3; j++)
1619  if (crv[j])
1620  cpl_polynomial_set_coeff(loc_crv, &j,
1621  cpl_polynomial_eval(crv[j], point));
1622 
1623  ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL);
1624  }
1625 
1626  cpl_vector_delete(point);
1627  cpl_polynomial_delete(ids0);
1628  cpl_polynomial_delete(loc_crv);
1629  for (j = 0; j < 3; j++)
1630  cpl_polynomial_delete(crv[j]);
1631 
1632  sort_col = cpl_propertylist_new();
1633  cpl_propertylist_append_bool(sort_col, "ytop", 1);
1634  cpl_table_sort(slits, sort_col);
1635  cpl_table_sort(maskslits, sort_col);
1636  cpl_propertylist_delete(sort_col);
1637 
1638  /*
1639  * Eliminate slits which are _entirely_ outside the CCD
1640  */
1641 
1642  cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1);
1643  cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0);
1644  cpl_table_erase_selected(slits);
1645 
1646  nslits = cpl_table_get_nrow(slits);
1647 
1648  if (nslits == 0) {
1649  cpl_msg_warning(func, "No slits found on the CCD");
1650  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
1651  cpl_table_delete(slits);
1652  return NULL;
1653  }
1654 
1655  if (nslits > 1)
1656  cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slits are entirely or partially "
1657  "contained in CCD", nslits);
1658  else
1659  cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slit is entirely or partially "
1660  "contained in CCD", nslits);
1661 
1662  return slits;
1663 
1664 }
1665 
1666 
1693 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits,
1694  cpl_table *slits)
1695 {
1696  const char *func = "mos_build_curv_coeff";
1697 
1698  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
1699  /* Max order is 5 */
1700 
1701  cpl_polynomial *crv[3];
1702  cpl_vector *point;
1703  cpl_table *polytraces;
1704  double *dpoint;
1705  double *xtop;
1706  double *ytop;
1707  double *xbottom;
1708  double *ybottom;
1709  int *slit_id;
1710  int *valid_id;
1711  int nslits, nvalid;
1712  int found;
1713  int i, j, k;
1714 
1715 
1716  if (global == NULL || slits == NULL || maskslits == NULL) {
1717  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
1718  return NULL;
1719  }
1720 
1721  nslits = cpl_table_get_nrow(maskslits);
1722  slit_id = cpl_table_get_data_int(maskslits, "slit_id");
1723  xtop = cpl_table_get_data_double(maskslits, "xtop");
1724  ytop = cpl_table_get_data_double(maskslits, "ytop");
1725  xbottom = cpl_table_get_data_double(maskslits, "xbottom");
1726  ybottom = cpl_table_get_data_double(maskslits, "ybottom");
1727 
1728  polytraces = cpl_table_new(2*nslits);
1729  cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
1730  for (i = 0; i < 3; i++)
1731  cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
1732 
1733  crv[0] = read_global_distortion(global, 7);
1734  crv[1] = read_global_distortion(global, 8);
1735  crv[2] = read_global_distortion(global, 9);
1736 
1737  point = cpl_vector_new(2);
1738  dpoint = cpl_vector_get_data(point);
1739 
1740  for (i = 0; i < nslits; i++) {
1741  for (j = 0; j < 2; j++) { /* For top and bottom trace of each slit */
1742 
1743  cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
1744 
1745  if (j) {
1746  dpoint[0] = xbottom[i];
1747  dpoint[1] = ybottom[i];
1748  }
1749  else {
1750  dpoint[0] = xtop[i];
1751  dpoint[1] = ytop[i];
1752  }
1753 
1754  for (k = 0; k < 3; k++)
1755  if (crv[j])
1756  cpl_table_set_double(polytraces, clab[k], 2*i+j,
1757  cpl_polynomial_eval(crv[k], point));
1758  }
1759  }
1760 
1761  cpl_vector_delete(point);
1762  for (j = 0; j < 3; j++)
1763  cpl_polynomial_delete(crv[j]);
1764 
1765  /*
1766  * Eliminate slits which are _entirely_ outside the CCD
1767  */
1768 
1769  nvalid = cpl_table_get_nrow(slits);
1770  valid_id = cpl_table_get_data_int(slits, "slit_id");
1771  cpl_table_unselect_all(polytraces);
1772  for (i = 0; i < nslits; i++) {
1773  found = 0;
1774  for (j = 0; j < nvalid; j++) {
1775  if (slit_id[i] == valid_id[j]) {
1776  found = 1;
1777  break;
1778  }
1779  }
1780  if (!found) {
1781  cpl_table_select_row(polytraces, 2*i);
1782  cpl_table_select_row(polytraces, 2*i + 1);
1783  }
1784  }
1785  cpl_table_erase_selected(polytraces);
1786 
1787  nslits = cpl_table_get_nrow(polytraces);
1788 
1789  if (nslits == 0) {
1790  cpl_msg_warning(func, "No slits found on the CCD");
1791  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
1792  cpl_table_delete(polytraces);
1793  return NULL;
1794  }
1795 
1796  if (nslits > 2)
1797  cpl_msg_info(func, "Curvature model: %d slits are entirely or "
1798  "partially contained in CCD", nslits / 2);
1799  else
1800  cpl_msg_info(func, "Curvature model: %d slit is entirely or "
1801  "partially contained in CCD", nslits / 2);
1802 
1803  return polytraces;
1804 }
1805 
1806 
1848 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
1849 {
1850  const char *func = "mos_build_disp_coeff";
1851 
1852  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
1853 
1854  cpl_polynomial *ids[6];
1855  cpl_vector *point;
1856  cpl_table *idscoeff;
1857  double *dpoint;
1858  double *xtop;
1859  double *ytop;
1860  double *xbottom;
1861  double *ybottom;
1862  int *position;
1863  int *length;
1864  int nslits;
1865  int nrows;
1866  int order;
1867  int ylow, yhig;
1868  int i, j, k;
1869 
1870 
1871  if (global == NULL || slits == NULL) {
1872  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
1873  return NULL;
1874  }
1875 
1876  nslits = cpl_table_get_nrow(slits);
1877  position = cpl_table_get_data_int(slits, "position");
1878  length = cpl_table_get_data_int(slits, "length");
1879  xtop = cpl_table_get_data_double(slits, "xtop");
1880  ytop = cpl_table_get_data_double(slits, "ytop");
1881  xbottom = cpl_table_get_data_double(slits, "xbottom");
1882  ybottom = cpl_table_get_data_double(slits, "ybottom");
1883 
1884  for (i = 0; i < 6; i++)
1885  ids[i] = read_global_distortion(global, i);
1886 
1887  for (i = 0; i < 6; i++)
1888  if (ids[i] == NULL)
1889  break;
1890 
1891  order = i - 1;
1892 
1893  if (order < 1) {
1894  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
1895  return NULL;
1896  }
1897 
1898  nrows = 0;
1899  for (i = 0; i < nslits; i++)
1900  nrows += length[i];
1901 
1902  idscoeff = cpl_table_new(nrows);
1903 
1904  for (j = 0; j <= order; j++)
1905  cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
1906 
1907  cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE);
1908  cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0);
1909  cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT);
1910  cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0);
1911 
1912  point = cpl_vector_new(2);
1913  dpoint = cpl_vector_get_data(point);
1914 
1915  for (i = 0; i < nslits; i++) {
1916 
1917  if (length[i] == 0)
1918  continue;
1919 
1920  ylow = position[i];
1921  yhig = ylow + length[i];
1922 
1923  for (j = 0; j <= order; j++) {
1924  if (j) {
1925  for (k = 0; k < length[i]; k++) {
1926  dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i];
1927  dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i];
1928  cpl_table_set_double(idscoeff, clab[j], ylow + k,
1929  cpl_polynomial_eval(ids[j], point));
1930  }
1931  }
1932  else {
1933  for (k = 0; k < length[i]; k++) {
1934  cpl_table_set_double(idscoeff, clab[0], ylow + k,
1935  xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]);
1936  }
1937  }
1938  }
1939  }
1940 
1941  cpl_vector_delete(point);
1942  for (j = 0; j < 6; j++)
1943  cpl_polynomial_delete(ids[j]);
1944 
1945  return idscoeff;
1946 
1947 }
1948 
1949 
1972 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits,
1973  cpl_table *polytraces, double reference,
1974  double blue, double red, double dispersion)
1975 {
1976  const char *func = "mos_subtract_sky";
1977 
1978  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
1979  /* Max order is 5 */
1980 
1981  cpl_image *sky;
1982  cpl_bivector *list;
1983  cpl_vector *listx;
1984  cpl_vector *listy;
1985  cpl_polynomial *polytop;
1986  cpl_polynomial *polybot;
1987  cpl_polynomial *trend;
1988 
1989  int *slit_id;
1990  double *dlistx;
1991  double *dlisty;
1992  float *sdata;
1993  float *kdata;
1994  double top, bot;
1995  int itop, ibot;
1996  double coeff;
1997  double ytop, ybot;
1998  double m, q, err;
1999  int npix;
2000 
2001  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
2002  int nx, ny;
2003  int nslits;
2004  int *length;
2005  int missing_top, missing_bot;
2006  int order;
2007  int null;
2008  int window = 50; /* Longer slits have polynomial sky model */
2009  int count;
2010  int i, j;
2011  cpl_size k;
2012 
2013 
2014  if (science == NULL || slits == NULL || polytraces == NULL) {
2015  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
2016  return NULL;
2017  }
2018 
2019  if (dispersion <= 0.0) {
2020  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2021  return NULL;
2022  }
2023 
2024  if (red - blue < dispersion) {
2025  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2026  return NULL;
2027  }
2028 
2029  nx = cpl_image_get_size_x(science);
2030  ny = cpl_image_get_size_y(science);
2031 
2032  sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
2033 
2034  sdata = cpl_image_get_data(science);
2035  kdata = cpl_image_get_data(sky);
2036 
2037  nslits = cpl_table_get_nrow(slits);
2038  order = cpl_table_get_ncol(polytraces) - 2;
2039  length = cpl_table_get_data_int(slits, "length");
2040  slit_id = cpl_table_get_data_int(slits, "slit_id");
2041 
2042  /*
2043  * The spatial resampling is performed for a certain number of
2044  * pixels above and below the position of the reference wavelength:
2045  */
2046 
2047  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
2048  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
2049 
2050  for (i = 0; i < nslits; i++) {
2051 
2052  if (length[i] == 0)
2053  continue;
2054 
2055 
2056  /*
2057  * Recover from the table of spectral curvature coefficients
2058  * the curvature polynomials.
2059  */
2060 
2061  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
2062 
2063  start_pixel = refpixel - pixel_below;
2064  if (start_pixel < 0)
2065  start_pixel = 0;
2066 
2067  end_pixel = refpixel + pixel_above;
2068  if (end_pixel > nx)
2069  end_pixel = nx;
2070 
2071  missing_top = 0;
2072  polytop = cpl_polynomial_new(1);
2073  for (k = 0; k <= order; k++) {
2074  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
2075  if (null) {
2076  cpl_polynomial_delete(polytop);
2077  missing_top = 1;
2078  break;
2079  }
2080  cpl_polynomial_set_coeff(polytop, &k, coeff);
2081  }
2082 
2083  missing_bot = 0;
2084  polybot = cpl_polynomial_new(1);
2085  for (k = 0; k <= order; k++) {
2086  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
2087  if (null) {
2088  cpl_polynomial_delete(polybot);
2089  missing_bot = 1;
2090  break;
2091  }
2092  cpl_polynomial_set_coeff(polybot, &k, coeff);
2093  }
2094 
2095  if (missing_top && missing_bot) {
2096  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
2097  slit_id[i]);
2098  continue;
2099  }
2100 
2101  /*
2102  * In case just one of the two edges was not traced, the other
2103  * edge curvature model is duplicated and shifted to the other
2104  * end of the slit: better than nothing!
2105  */
2106 
2107  if (missing_top) {
2108  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
2109  "the spectral curvature of the lower edge "
2110  "is used instead.", slit_id[i]);
2111  polytop = cpl_polynomial_duplicate(polybot);
2112  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2113  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2114  k = 0;
2115  coeff = cpl_polynomial_get_coeff(polybot, &k);
2116  coeff += ytop - ybot;
2117  cpl_polynomial_set_coeff(polytop, &k, coeff);
2118  }
2119 
2120  if (missing_bot) {
2121  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
2122  "the spectral curvature of the upper edge "
2123  "is used instead.", slit_id[i]);
2124  polybot = cpl_polynomial_duplicate(polytop);
2125  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2126  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2127  k = 0;
2128  coeff = cpl_polynomial_get_coeff(polytop, &k);
2129  coeff -= ytop - ybot;
2130  cpl_polynomial_set_coeff(polybot, &k, coeff);
2131  }
2132 
2133 
2134  /*
2135  * Now read pixel values along spatial direction, and fit them.
2136  */
2137 
2138  for (j = start_pixel; j < end_pixel; j++) {
2139  top = cpl_polynomial_eval_1d(polytop, j, NULL);
2140  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
2141  itop = floor(top + 0.5) + 1;
2142  ibot = floor(bot + 0.5);
2143  if (itop > ny)
2144  itop = ny;
2145  if (ibot < 0)
2146  ibot = 0;
2147  npix = itop - ibot;
2148  if (npix < 5)
2149  break;
2150 
2151  list = cpl_bivector_new(npix);
2152  listx = cpl_bivector_get_x(list);
2153  listy = cpl_bivector_get_y(list);
2154  dlistx = cpl_vector_get_data(listx);
2155  dlisty = cpl_vector_get_data(listy);
2156 
2157  for (k = 0; k < npix; k++) {
2158  dlistx[k] = k;
2159  dlisty[k] = sdata[j + (ibot + k)*nx];
2160  }
2161 
2162  if (robustLinearFit(list, &q, &m, &err)) {
2163  cpl_bivector_delete(list);
2164  continue;
2165  }
2166 
2167  cpl_bivector_delete(list);
2168 
2169  for (k = 0; k < npix; k++) {
2170  kdata[j + (ibot + k)*nx] = m*k + q;
2171  }
2172 
2173  if (npix > window) {
2174 
2175  /*
2176  * Polynomial iteration
2177  */
2178 
2179  err = 3*sqrt(err);
2180 
2181  count = 0;
2182  for (k = 0; k < npix; k++)
2183  if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err)
2184  count++;
2185 
2186  if (count < 10)
2187  continue;
2188 
2189  list = cpl_bivector_new(count);
2190  listx = cpl_bivector_get_x(list);
2191  listy = cpl_bivector_get_y(list);
2192  dlistx = cpl_vector_get_data(listx);
2193  dlisty = cpl_vector_get_data(listy);
2194 
2195  count = 0;
2196  for (k = 0; k < npix; k++) {
2197  if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) {
2198  dlistx[count] = k;
2199  dlisty[count] = sdata[j + (ibot + k)*nx];
2200  count++;
2201  }
2202  }
2203 
2204  trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err);
2205 
2206  cpl_bivector_delete(list);
2207 
2208  err = 3*sqrt(err);
2209 
2210  count = 0;
2211  for (k = 0; k < npix; k++)
2212  if (fabs(sdata[j + (ibot + k)*nx]
2213  - cpl_polynomial_eval_1d(trend, k, NULL)) < err)
2214  count++;
2215 
2216  if (count < 10) {
2217  cpl_polynomial_delete(trend);
2218  continue;
2219  }
2220 
2221  list = cpl_bivector_new(count);
2222  listx = cpl_bivector_get_x(list);
2223  listy = cpl_bivector_get_y(list);
2224  dlistx = cpl_vector_get_data(listx);
2225  dlisty = cpl_vector_get_data(listy);
2226 
2227  count = 0;
2228  for (k = 0; k < npix; k++) {
2229  if (fabs(sdata[j + (ibot + k)*nx]
2230  - cpl_polynomial_eval_1d(trend, k, NULL)) < err) {
2231  dlistx[count] = k;
2232  dlisty[count] = sdata[j + (ibot + k)*nx];
2233  count++;
2234  }
2235  }
2236 
2237  cpl_polynomial_delete(trend);
2238 
2239  trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err);
2240 
2241  cpl_bivector_delete(list);
2242 
2243  for (k = 0; k < npix; k++) {
2244  kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend,
2245  k, NULL);
2246  }
2247 
2248  cpl_polynomial_delete(trend);
2249  }
2250  }
2251  cpl_polynomial_delete(polytop);
2252  cpl_polynomial_delete(polybot);
2253  }
2254 
2255  cpl_image_subtract(science, sky);
2256 
2257  return sky;
2258 }
2259 
2260 
2293 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial,
2294  cpl_table *slits, cpl_table *polytraces,
2295  double reference, double blue, double red,
2296  double dispersion, int sradius, int polyorder)
2297 {
2298  const char *func = "mos_normalise_flat";
2299 
2300  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
2301  /* Max order is 5 */
2302 
2303  cpl_image *rectified;
2304  cpl_image *smo_flat;
2305  cpl_image *exslit;
2306  cpl_vector *positions;
2307  cpl_vector *flux;
2308  cpl_vector *smo_flux;
2309  cpl_polynomial *trend;
2310  cpl_polynomial *polytop;
2311  cpl_polynomial *polybot;
2312 
2313  int *slit_id;
2314  float *p;
2315  float *data;
2316  double *fdata;
2317  double *pdata;
2318  float *sdata;
2319  float *xdata;
2320  float *wdata;
2321  double vtop, vbot, value;
2322  double top, bot;
2323  double coeff;
2324  double ytop, ybot;
2325  double ypos;
2326  double fvalue;
2327  int ivalue;
2328  int yint, yprev;
2329  int npseudo;
2330 
2331  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
2332  int nx, ny, nsubx, nsuby;
2333  int xlow, ylow, xhig, yhig;
2334  int nslits;
2335  int *position;
2336  int *length;
2337  int missing_top, missing_bot;
2338  int order;
2339  int npoints;
2340  int uradius;
2341  int null;
2342  int i, j;
2343  cpl_size k;
2344 
2345 /* int exclude = 5; Number of excluded pixels at edges */
2346 
2347  /* For debug puposes only: cpl_image *smo_rectified; */
2348 
2349 
2350  if (flat == NULL || slits == NULL || polytraces == NULL) {
2351  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
2352  return NULL;
2353  }
2354 
2355  if (dispersion <= 0.0) {
2356  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2357  return NULL;
2358  }
2359 
2360  if (red - blue < dispersion) {
2361  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2362  return NULL;
2363  }
2364 
2365  rectified = mos_spatial_calibration(flat, slits, polytraces, reference,
2366  blue, red, dispersion, 0, NULL);
2367 
2368  nx = cpl_image_get_size_x(rectified);
2369  ny = cpl_image_get_size_y(rectified);
2370 
2371  smo_flat = cpl_image_new(cpl_image_get_size_x(spatial),
2372  cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT);
2373  wdata = cpl_image_get_data(smo_flat);
2374 
2375  nslits = cpl_table_get_nrow(slits);
2376  order = cpl_table_get_ncol(polytraces) - 2;
2377  position = cpl_table_get_data_int(slits, "position");
2378  length = cpl_table_get_data_int(slits, "length");
2379  slit_id = cpl_table_get_data_int(slits, "slit_id");
2380 
2381  /*
2382  * The spatial resampling is performed for a certain number of
2383  * pixels above and below the position of the reference wavelength:
2384  */
2385 
2386  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
2387  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
2388 
2389  xlow = 1;
2390  xhig = nx;
2391  for (i = 0; i < nslits; i++) {
2392 
2393  if (length[i] == 0)
2394  continue;
2395 
2396  /*
2397  * We DON'T write:
2398  *
2399  * ylow = position[i];
2400  * yhig = ylow + length[i];
2401  *
2402  * because the cpl_image pixels are counted from 1, and because in
2403  * cpl_image_extract() the coordinates of the last pixel are inclusive.
2404  */
2405 
2406  ylow = position[i] + 1;
2407  yhig = ylow + length[i] - 1;
2408 
2409  exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig);
2410 
2411  if (polyorder < 0) {
2412 
2413  cpl_image_turn(exslit, -1); /* For faster memory access */
2414 
2415  nsubx = cpl_image_get_size_x(exslit);
2416  nsuby = cpl_image_get_size_y(exslit);
2417  data = cpl_image_get_data(exslit);
2418  flux = cpl_vector_new(nsubx);
2419 
2420  uradius = nsubx / 2;
2421  if (uradius > sradius)
2422  uradius = sradius;
2423 
2424  for (j = 0; j < nsuby; j++) {
2425  fdata = cpl_vector_get_data(flux);
2426  p = data;
2427  for (k = 0; k < nsubx; k++)
2428  *fdata++ = *p++;
2429  smo_flux = cpl_vector_filter_median_create(flux, uradius);
2430  fdata = cpl_vector_get_data(smo_flux);
2431  p = data;
2432  for (k = 0; k < nsubx; k++)
2433  *p++ = *fdata++;
2434  cpl_vector_delete(smo_flux);
2435  data += nsubx;
2436  }
2437 
2438  cpl_vector_delete(flux);
2439 
2440 
2441  /*
2442  * First fit fluxes along the spatial direction with a low-degree
2443  * polynomial (excluding the first and the last pixels, close to
2444  * the edges)
2445  */
2446 /*
2447  if (nsubx-2*exclude > 10) {
2448  flux = cpl_vector_new(nsubx-2*exclude);
2449  fdata = cpl_vector_get_data(flux);
2450  positions = cpl_vector_new(nsubx-2*exclude);
2451  for (j = 0; j < nsubx-2*exclude; j++)
2452  cpl_vector_set(positions, j, j+exclude);
2453 
2454  for (k = 0; k < nsuby; k++) {
2455  for (j = 0; j < nsubx-2*exclude; j++)
2456  fdata[j] = data[j+exclude];
2457  trend = cpl_polynomial_fit_1d_create(positions, flux,
2458  1, NULL);
2459  for (j = 0; j < nsubx; j++)
2460  data[j] = cpl_polynomial_eval_1d(trend, j, NULL);
2461  cpl_polynomial_delete(trend);
2462  data += nsubx;
2463  }
2464 
2465  cpl_vector_delete(flux);
2466  cpl_vector_delete(positions);
2467  }
2468 */
2469 
2470  /*
2471  * Now smooth along the dispersion direction
2472  */
2473 
2474  cpl_image_turn(exslit, 1); /* For faster memory access */
2475  nsubx = cpl_image_get_size_x(exslit);
2476  nsuby = cpl_image_get_size_y(exslit);
2477  data = cpl_image_get_data(exslit);
2478 
2479  for (j = 0; j < nsuby; j++) {
2480  flux = cpl_vector_new(nsubx);
2481  fdata = cpl_vector_get_data(flux);
2482  p = data;
2483  for (k = 0; k < nsubx; k++)
2484  *fdata++ = *p++;
2485  smo_flux = cpl_vector_filter_median_create(flux, sradius);
2486  cpl_vector_delete(flux);
2487  fdata = cpl_vector_get_data(smo_flux);
2488  p = data;
2489  for (k = 0; k < nsubx; k++)
2490  *p++ = *fdata++;
2491  cpl_vector_delete(smo_flux);
2492  data += nsubx;
2493  }
2494  }
2495  else {
2496 
2497  /*
2498  * Fit with a polynomial the flat field trend row by row.
2499  */
2500 
2501  nsubx = cpl_image_get_size_x(exslit);
2502  nsuby = cpl_image_get_size_y(exslit);
2503  data = cpl_image_get_data(exslit);
2504 
2505  for (j = 0; j < nsuby; j++) {
2506 
2507  /*
2508  * First get the size of the vectors to allocate:
2509  */
2510 
2511  npoints = 0;
2512  p = data + j*nsubx;
2513  for (k = 0; k < nsubx; k++)
2514  if (p[k] > 1.0)
2515  npoints++;
2516 
2517  if (npoints > polyorder + 1) {
2518 
2519  /*
2520  * Fill the vectors for the fitting
2521  */
2522 
2523  flux = cpl_vector_new(npoints);
2524  fdata = cpl_vector_get_data(flux);
2525  positions = cpl_vector_new(npoints);
2526  pdata = cpl_vector_get_data(positions);
2527 
2528  npoints = 0;
2529  p = data + j*nsubx;
2530  for (k = 0; k < nsubx; k++) {
2531  if (p[k] > 1.0) {
2532  fdata[npoints] = p[k];
2533  pdata[npoints] = k;
2534  npoints++;
2535  }
2536  }
2537 
2538  trend = cpl_polynomial_fit_1d_create(positions, flux,
2539  polyorder, NULL);
2540 
2541  cpl_vector_delete(flux);
2542  cpl_vector_delete(positions);
2543 
2544  if (trend) {
2545  p = data + j*nsubx;
2546  for (k = 0; k < nsubx; k++)
2547  if (p[k] > 1.0)
2548  p[k] = cpl_polynomial_eval_1d(trend, k, NULL);
2549  cpl_polynomial_delete(trend);
2550  }
2551  else {
2552  cpl_msg_warning(func, "Invalid flat field flux fit "
2553  "(ignored)");
2554  }
2555  }
2556  }
2557  }
2558 
2559 
2560  /*
2561  * Recover from the table of spectral curvature coefficients
2562  * the curvature polynomials.
2563  */
2564 
2565  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
2566 
2567  start_pixel = refpixel - pixel_below;
2568  if (start_pixel < 0)
2569  start_pixel = 0;
2570 
2571  end_pixel = refpixel + pixel_above;
2572  if (end_pixel > nx)
2573  end_pixel = nx;
2574 
2575  missing_top = 0;
2576  polytop = cpl_polynomial_new(1);
2577  for (k = 0; k <= order; k++) {
2578  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
2579  if (null) {
2580  cpl_polynomial_delete(polytop);
2581  missing_top = 1;
2582  break;
2583  }
2584  cpl_polynomial_set_coeff(polytop, &k, coeff);
2585  }
2586 
2587  missing_bot = 0;
2588  polybot = cpl_polynomial_new(1);
2589  for (k = 0; k <= order; k++) {
2590  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
2591  if (null) {
2592  cpl_polynomial_delete(polybot);
2593  missing_bot = 1;
2594  break;
2595  }
2596  cpl_polynomial_set_coeff(polybot, &k, coeff);
2597  }
2598 
2599  if (missing_top && missing_bot) {
2600  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
2601  slit_id[i]);
2602  continue;
2603  }
2604 
2605  /*
2606  * In case just one of the two edges was not traced, the other
2607  * edge curvature model is duplicated and shifted to the other
2608  * end of the slit: better than nothing!
2609  */
2610 
2611  if (missing_top) {
2612  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
2613  "the spectral curvature of the lower edge "
2614  "is used instead.", slit_id[i]);
2615  polytop = cpl_polynomial_duplicate(polybot);
2616  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2617  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2618  k = 0;
2619  coeff = cpl_polynomial_get_coeff(polybot, &k);
2620  coeff += ytop - ybot;
2621  cpl_polynomial_set_coeff(polytop, &k, coeff);
2622  }
2623 
2624  if (missing_bot) {
2625  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
2626  "the spectral curvature of the upper edge "
2627  "is used instead.", slit_id[i]);
2628  polybot = cpl_polynomial_duplicate(polytop);
2629  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2630  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2631  k = 0;
2632  coeff = cpl_polynomial_get_coeff(polytop, &k);
2633  coeff -= ytop - ybot;
2634  cpl_polynomial_set_coeff(polybot, &k, coeff);
2635  }
2636 
2637 
2638  /*
2639  * Now map smoothed image to CCD.
2640  * Note that the npseudo value related to this slit is equal
2641  * to the number of spatial pseudo-pixels decreased by 1
2642  * (compare with function mos_spatial_calibration()).
2643  */
2644 
2645  nx = cpl_image_get_size_x(flat);
2646  ny = cpl_image_get_size_y(flat);
2647 
2648  sdata = cpl_image_get_data(spatial);
2649  xdata = cpl_image_get_data(exslit);
2650  npseudo = cpl_image_get_size_y(exslit) - 1;
2651 
2652  /*
2653  * Write interpolated smoothed values to CCD image
2654  */
2655 
2656  for (j = start_pixel; j < end_pixel; j++) {
2657  top = cpl_polynomial_eval_1d(polytop, j, NULL);
2658  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
2659  for (k = 0; k <= npseudo; k++) {
2660  ypos = top - k*(top-bot)/npseudo;
2661  yint = ypos;
2662 
2663  /*
2664  * The line:
2665  * value = sdata[j + nx*yint];
2666  * should be equivalent to:
2667  * value = npseudo*(top-yint)/(top-bot);
2668  */
2669 
2670  if (yint < 0 || yint >= ny-1) {
2671  yprev = yint;
2672  continue;
2673  }
2674 
2675  value = sdata[j + nx*yint]; /* Spatial coordinate on CCD */
2676  ivalue = value; /* Nearest spatial pixels: */
2677  fvalue = value - ivalue; /* ivalue and ivalue+1 */
2678  if (ivalue < npseudo && ivalue >= 0) {
2679  vtop = xdata[j + nx*(npseudo-ivalue)];
2680  vbot = xdata[j + nx*(npseudo-ivalue-1)];
2681  wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue;
2682 
2683  if (k) {
2684 
2685  /*
2686  * This is added to recover lost pixels on
2687  * the CCD image (pixels are lost because
2688  * the CCD pixels are less than npseudo+1).
2689  */
2690 
2691  if (yprev - yint > 1) {
2692  value = sdata[j + nx*(yint+1)];
2693  ivalue = value;
2694  fvalue = value - ivalue;
2695  if (ivalue < npseudo && ivalue >= 0) {
2696  vtop = xdata[j + nx*(npseudo-ivalue)];
2697  vbot = xdata[j + nx*(npseudo-ivalue-1)];
2698  wdata[j + nx*(yint+1)] = vtop*(1-fvalue)
2699  + vbot*fvalue;
2700  }
2701  }
2702  }
2703  }
2704  yprev = yint;
2705  }
2706  }
2707  cpl_polynomial_delete(polytop);
2708  cpl_polynomial_delete(polybot);
2709  cpl_image_delete(exslit);
2710  }
2711 
2712  cpl_image_delete(rectified);
2713 
2714  cpl_image_divide(flat, smo_flat);
2715 
2716  return smo_flat;
2717 }
2718 
2719 
2744 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius,
2745  int polyorder)
2746 {
2747  const char *func = "mos_normalise_longflat";
2748 
2749  cpl_image *smo_flat;
2750  cpl_image *profile;
2751  cpl_vector *flux;
2752  cpl_vector *smo_flux;
2753  cpl_vector *positions;
2754  cpl_polynomial *trend;
2755 
2756  float *level;
2757  float *p;
2758  float *data;
2759  double *fdata;
2760  double *pdata;
2761 
2762  int nx, ny;
2763  int npoints;
2764  int i, j;
2765 
2766 
2767  if (flat == NULL) {
2768  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
2769  return NULL;
2770  }
2771 
2772  if (sradius < 1 || dradius < 1) {
2773  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
2774  return NULL;
2775  }
2776 
2777  smo_flat = cpl_image_duplicate(flat);
2778 
2779  if (polyorder < 0) {
2780 
2781  /*
2782  * First smooth along the spatial direction
2783  */
2784 
2785  cpl_image_turn(smo_flat, -1); /* For faster memory access */
2786 
2787  nx = cpl_image_get_size_x(smo_flat);
2788  ny = cpl_image_get_size_y(smo_flat);
2789  data = cpl_image_get_data(smo_flat);
2790 
2791  for (i = 0; i < ny; i++) {
2792  flux = cpl_vector_new(nx);
2793  fdata = cpl_vector_get_data(flux);
2794  p = data;
2795  for (j = 0; j < nx; j++)
2796  *fdata++ = *p++;
2797  smo_flux = cpl_vector_filter_median_create(flux, sradius);
2798  cpl_vector_delete(flux);
2799  fdata = cpl_vector_get_data(smo_flux);
2800  p = data;
2801  for (j = 0; j < nx; j++)
2802  *p++ = *fdata++;
2803  cpl_vector_delete(smo_flux);
2804  data += nx;
2805  }
2806 
2807  /*
2808  * Second smooth along the dispersion direction
2809  */
2810 
2811  cpl_image_turn(smo_flat, 1); /* For faster memory access */
2812 
2813  nx = cpl_image_get_size_x(smo_flat);
2814  ny = cpl_image_get_size_y(smo_flat);
2815  data = cpl_image_get_data(smo_flat);
2816 
2817  for (i = 0; i < ny; i++) {
2818  flux = cpl_vector_new(nx);
2819  fdata = cpl_vector_get_data(flux);
2820  p = data;
2821  for (j = 0; j < nx; j++)
2822  *fdata++ = *p++;
2823  smo_flux = cpl_vector_filter_median_create(flux, sradius);
2824  cpl_vector_delete(flux);
2825  fdata = cpl_vector_get_data(smo_flux);
2826  p = data;
2827  for (j = 0; j < nx; j++)
2828  *p++ = *fdata++;
2829  cpl_vector_delete(smo_flux);
2830  data += nx;
2831  }
2832  }
2833  else {
2834 
2835  /*
2836  * Fit with a polynomial the flat field trend column by column.
2837  */
2838 
2839  cpl_image_turn(smo_flat, -1); /* For faster memory access */
2840 
2841  nx = cpl_image_get_size_x(smo_flat);
2842  ny = cpl_image_get_size_y(smo_flat);
2843  data = cpl_image_get_data(smo_flat);
2844 
2845  profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0);
2846  level = cpl_image_get_data(profile);
2847 
2848  for (i = 0; i < ny; i++) {
2849 
2850  /*
2851  * First get the size of the vectors to allocate:
2852  * eliminate from fit any value more than 20% away
2853  * from median level in current column.
2854  */
2855 
2856  npoints = 0;
2857  p = data + i*nx;
2858  for (j = 0; j < nx; j++)
2859  if (fabs(p[j]/level[i] - 1) < 0.20)
2860  npoints++;
2861 
2862  if (npoints > polyorder + 1) {
2863 
2864  /*
2865  * Fill the vectors for the fitting
2866  */
2867 
2868  flux = cpl_vector_new(npoints);
2869  fdata = cpl_vector_get_data(flux);
2870  positions = cpl_vector_new(npoints);
2871  pdata = cpl_vector_get_data(positions);
2872 
2873  npoints = 0;
2874  p = data + i*nx;
2875  for (j = 0; j < nx; j++) {
2876  if (fabs(p[j]/level[i] - 1) < 0.20) {
2877  fdata[npoints] = p[j];
2878  pdata[npoints] = j;
2879  npoints++;
2880  }
2881  }
2882 
2883  trend = cpl_polynomial_fit_1d_create(positions, flux,
2884  polyorder, NULL);
2885 
2886  cpl_vector_delete(flux);
2887  cpl_vector_delete(positions);
2888 
2889  if (trend) {
2890  p = data + i*nx;
2891  for (j = 0; j < nx; j++)
2892  p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
2893  cpl_polynomial_delete(trend);
2894  }
2895  else {
2896  cpl_msg_warning(func,
2897  "Invalid flat field flux fit (ignored)");
2898  }
2899  }
2900  }
2901 
2902  cpl_image_delete(profile);
2903  cpl_image_turn(smo_flat, 1);
2904 
2905  }
2906 
2907  cpl_image_divide(flat, smo_flat);
2908 
2909  return smo_flat;
2910 }
2911 
2912 
2935 cpl_error_code mos_interpolate_wavecalib_slit(cpl_table *idscoeff,
2936  cpl_table *slits,
2937  int order, int global)
2938 {
2939  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
2940  /* Max order is 5 */
2941  int nrow = cpl_table_get_nrow(slits);
2942  int i, j;
2943 
2944 
2945  if (order < 0)
2946  return CPL_ERROR_NONE;
2947 
2948  cpl_table_new_column(idscoeff, "x", CPL_TYPE_DOUBLE);
2949  cpl_table_new_column(idscoeff, "y", CPL_TYPE_DOUBLE);
2950 
2951  for (i = 0; i < nrow; i++) {
2952  int position = cpl_table_get_int (slits, "position", i, NULL);
2953  int length = cpl_table_get_int (slits, "length", i, NULL);
2954  double xtop = cpl_table_get_double(slits, "xtop", i, NULL);
2955  double xbot = cpl_table_get_double(slits, "xbottom", i, NULL);
2956  double ytop = cpl_table_get_double(slits, "ytop", i, NULL);
2957  double ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
2958  double dx = xtop - xbot;
2959  double dy = ytop - ybot;
2960  cpl_table *table = cpl_table_extract(idscoeff, position, length);
2961 
2962  if (mos_interpolate_wavecalib(table, NULL, 2, order))
2963  continue;
2964 
2965  cpl_table_erase_window(idscoeff, position, length);
2966  cpl_table_insert(idscoeff, table, position);
2967 
2968  cpl_table_delete(table);
2969 
2970  for (j = 0; j < length; j++) {
2971  cpl_table_set_double(idscoeff, "x", j + position,
2972  xbot + j*(dx/length));
2973  cpl_table_set_double(idscoeff, "y", j + position,
2974  ybot + j*(dy/length));
2975  }
2976  }
2977 
2978  if (global) {
2979 
2980  /*
2981  * Now fit a global solution
2982  */
2983 
2984  nrow = cpl_table_get_nrow(idscoeff);
2985 
2986  for (i = 0; i < 6; i++) {
2987  cpl_table *dummy;
2988  cpl_vector *x;
2989  cpl_vector *y;
2990  cpl_bivector *z;
2991  cpl_vector *c;
2992  cpl_polynomial *p;
2993  cpl_vector *point;
2994  double *dpoint;
2995  int npoints;
2996 
2997  if (!cpl_table_has_column(idscoeff, clab[i]))
2998  break;
2999 
3000  npoints = nrow - cpl_table_count_invalid(idscoeff, clab[i]);
3001  if (npoints < 18)
3002  break;
3003 
3004  dummy = cpl_table_new(nrow);
3005  cpl_table_duplicate_column(dummy, "x", idscoeff, "x");
3006  cpl_table_duplicate_column(dummy, "y", idscoeff, "y");
3007  cpl_table_duplicate_column(dummy, clab[i], idscoeff, clab[i]);
3008  cpl_table_erase_invalid(dummy);
3009 
3010  x = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "x"));
3011  y = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "y"));
3012  z = cpl_bivector_wrap_vectors(x, y);
3013  c = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy,
3014  clab[i]));
3015  p = cpl_polynomial_fit_2d_create(z, c, 2, NULL);
3016  cpl_bivector_unwrap_vectors(z);
3017  cpl_vector_unwrap(x);
3018  cpl_vector_unwrap(y);
3019  cpl_vector_unwrap(c);
3020  cpl_table_delete(dummy);
3021 
3022  point = cpl_vector_new(2);
3023  dpoint = cpl_vector_get_data(point);
3024  for (j = 0; j < nrow; j++) {
3025  dpoint[0] = cpl_table_get_double(idscoeff, "x", j, NULL);
3026  dpoint[1] = cpl_table_get_double(idscoeff, "y", j, NULL);
3027  cpl_table_set_double(idscoeff, clab[i], j,
3028  cpl_polynomial_eval(p, point));
3029  }
3030  cpl_vector_delete(point);
3031  cpl_polynomial_delete(p);
3032  }
3033  }
3034 
3035  return CPL_ERROR_NONE;
3036 }
3037 
3038 
3064 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff,
3065  cpl_image *wavemap, int mode,
3066  int degree)
3067 {
3068  const char *func = "mos_interpolate_wavecalib";
3069 
3070  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
3071  /* Max order is 5 */
3072 
3073  cpl_vector *wave;
3074  cpl_vector *positions;
3075  cpl_polynomial *trend;
3076 
3077  float *p;
3078  float *data;
3079  double *wdata;
3080  double *pdata;
3081 
3082  double c;
3083  double mse, ksigma;
3084 
3085  int order;
3086  int nrows, first_row, last_row;
3087  int nx, ny;
3088  int npoints, rpoints;
3089  int null;
3090  int i, j, k;
3091 
3092  int polyorder = 4; /* Candidate input argument */
3093 
3094 
3095  if (idscoeff == NULL)
3096  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3097 
3098  if (mode < 0 || mode > 2)
3099  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
3100 
3101  if (mode == 0 || degree < 0)
3102  return CPL_ERROR_NONE;
3103 
3104  if (wavemap) {
3105 
3106  /*
3107  * Fit with a polynomial the wavelength trend column by column.
3108  */
3109 
3110  cpl_image_turn(wavemap, -1); /* For faster memory access */
3111 
3112  nx = cpl_image_get_size_x(wavemap);
3113  ny = cpl_image_get_size_y(wavemap);
3114  data = cpl_image_get_data(wavemap);
3115 
3116  for (i = 0; i < ny; i++) {
3117 
3118  /*
3119  * First get the size of the vectors to allocate:
3120  * eliminate from fit any value with "impossible" wavelength.
3121  */
3122 
3123  npoints = 0;
3124  p = data + i*nx;
3125  for (j = 0; j < nx; j++)
3126  if (p[j] > 1.0)
3127  npoints++;
3128 
3129  if (npoints > polyorder + 1) {
3130 
3131  /*
3132  * Fill the vectors for the fitting
3133  */
3134 
3135  wave = cpl_vector_new(npoints);
3136  wdata = cpl_vector_get_data(wave);
3137  positions = cpl_vector_new(npoints);
3138  pdata = cpl_vector_get_data(positions);
3139 
3140  npoints = 0;
3141  p = data + i*nx;
3142  for (j = 0; j < nx; j++) {
3143  if (p[j] > 1.0) {
3144  wdata[npoints] = p[j];
3145  pdata[npoints] = j;
3146  npoints++;
3147  }
3148  }
3149 
3150  trend = cpl_polynomial_fit_1d_create(positions, wave,
3151  polyorder, &mse);
3152 
3153  ksigma = 3*sqrt(mse);
3154 
3155  cpl_vector_delete(wave);
3156  cpl_vector_delete(positions);
3157 
3158  if (trend) {
3159 
3160  /*
3161  * Apply 3-sigma rejection
3162  */
3163 
3164  rpoints = 0;
3165  p = data + i*nx;
3166  for (j = 0; j < nx; j++)
3167  if (p[j] > 1.0)
3168  if (fabs(cpl_polynomial_eval_1d(trend, j, NULL)
3169  - p[j]) < ksigma)
3170  rpoints++;
3171 
3172  if (rpoints < npoints && rpoints > polyorder + 1) {
3173 
3174  wave = cpl_vector_new(rpoints);
3175  wdata = cpl_vector_get_data(wave);
3176  positions = cpl_vector_new(rpoints);
3177  pdata = cpl_vector_get_data(positions);
3178 
3179  npoints = 0;
3180  p = data + i*nx;
3181  for (j = 0; j < nx; j++) {
3182  if (p[j] > 1.0) {
3183  if (fabs(cpl_polynomial_eval_1d(trend,
3184  j, NULL) - p[j])
3185  < ksigma) {
3186  wdata[npoints] = p[j];
3187  pdata[npoints] = j;
3188  npoints++;
3189  }
3190  }
3191  }
3192 
3193  cpl_polynomial_delete(trend);
3194  trend = cpl_polynomial_fit_1d_create(positions, wave,
3195  polyorder, NULL);
3196 
3197  cpl_vector_delete(wave);
3198  cpl_vector_delete(positions);
3199  }
3200  }
3201 
3202  if (trend) {
3203  p = data + i*nx;
3204  if (mode == 1) {
3205  for (j = 0; j < nx; j++)
3206  if (p[j] < 1.0)
3207  p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
3208  }
3209  else if (mode == 2) {
3210  for (j = 0; j < nx; j++)
3211  p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
3212  }
3213  cpl_polynomial_delete(trend);
3214  }
3215  else {
3216  cpl_msg_warning(func,
3217  "Invalid wavelength field fit (ignored)");
3218  }
3219  }
3220 
3221  }
3222 
3223  cpl_image_turn(wavemap, 1);
3224 
3225  }
3226 
3227 
3228  /*
3229  * Interpolating the IDS coefficients
3230  */
3231 
3232  nrows = cpl_table_get_nrow(idscoeff);
3233 
3234  order = 0;
3235  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
3236  ++order;
3237  --order;
3238 
3239  if (degree == 0) {
3240  for (k = 0; k <= order; k++) {
3241  double m;
3242  if (cpl_table_has_column(idscoeff, clab[k])) {
3243  m = cpl_table_get_column_median(idscoeff, clab[k]);
3244  cpl_table_fill_column_window_double(idscoeff, clab[k],
3245  0, nrows, m);
3246  }
3247  }
3248 
3249  return CPL_ERROR_NONE;
3250  }
3251 
3252  first_row = 0;
3253  while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
3254  first_row++;
3255 
3256  last_row = nrows - 1;
3257  while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
3258  last_row--;
3259 
3260  for (k = 0; k <= order; k++) {
3261 
3262  npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]);
3263  wave = cpl_vector_new(npoints);
3264  wdata = cpl_vector_get_data(wave);
3265  positions = cpl_vector_new(npoints);
3266  pdata = cpl_vector_get_data(positions);
3267 
3268  npoints = 0;
3269  for (i = first_row; i <= last_row; i++) {
3270  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
3271  if (null == 0) {
3272  wdata[npoints] = c;
3273  pdata[npoints] = i;
3274  npoints++;
3275  }
3276  }
3277 
3278 // This doesn't seem to provide good results, I have not understood why.
3279 // Restore for robust linear fitting.
3280 //
3281  if (degree == 1) {
3282  cpl_vector *p;
3283  cpl_vector *w;
3284  cpl_bivector *list;
3285  double q, m;
3286 
3287  if (npoints > 4) {
3288  p = cpl_vector_extract(positions, 2, npoints - 2, 1);
3289  w = cpl_vector_extract(wave, 2, npoints - 2, 1);
3290  }
3291  else {
3292  p = positions;
3293  w = wave;
3294  }
3295 
3296  list = cpl_bivector_wrap_vectors(p, w);
3297 
3298  robustLinearFit(list, &q, &m, &mse);
3299  cpl_bivector_unwrap_vectors(list);
3300  for (i = first_row; i <= last_row; i++)
3301  cpl_table_set_double(idscoeff, clab[k], i, q + m*i);
3302 
3303  if (npoints > 4) {
3304  cpl_vector_delete(p);
3305  cpl_vector_delete(w);
3306  }
3307 
3308  cpl_vector_delete(wave);
3309  cpl_vector_delete(positions);
3310 
3311  continue;
3312  }
3313 
3314 // End robust linear fitting
3315 
3316  trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse);
3317 
3318  ksigma = 3*sqrt(mse);
3319 
3320  cpl_vector_delete(wave);
3321  cpl_vector_delete(positions);
3322 
3323  /*
3324  * Iteration
3325  */
3326 
3327  if (trend) {
3328  rpoints = 0;
3329  for (i = first_row; i <= last_row; i++) {
3330  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
3331  if (null == 0) {
3332  if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
3333  < ksigma) {
3334  rpoints++;
3335  }
3336  }
3337  }
3338 
3339  if (rpoints > 0 && rpoints < npoints) {
3340  cpl_msg_debug(func, "%d points rejected from "
3341  "wavelength calibration fit",
3342  npoints - rpoints);
3343 
3344  wave = cpl_vector_new(rpoints);
3345  wdata = cpl_vector_get_data(wave);
3346  positions = cpl_vector_new(rpoints);
3347  pdata = cpl_vector_get_data(positions);
3348 
3349  npoints = 0;
3350  for (i = first_row; i <= last_row; i++) {
3351  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
3352  if (null == 0) {
3353  if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
3354  < ksigma) {
3355  wdata[npoints] = c;
3356  pdata[npoints] = i;
3357  npoints++;
3358  }
3359  }
3360  }
3361 
3362  if (npoints) {
3363  cpl_polynomial_delete(trend);
3364  trend = cpl_polynomial_fit_1d_create(positions,
3365  wave, degree, NULL);
3366  }
3367 
3368  cpl_vector_delete(wave);
3369  cpl_vector_delete(positions);
3370 
3371  }
3372  }
3373 
3374  if (trend) {
3375  for (i = first_row; i <= last_row; i++) {
3376  if (mode == 1) {
3377  if (!cpl_table_is_valid(idscoeff, clab[k], i)) {
3378  cpl_table_set_double(idscoeff, clab[k], i,
3379  cpl_polynomial_eval_1d(trend, i,
3380  NULL));
3381  }
3382  }
3383  else if (mode == 2) {
3384  cpl_table_set_double(idscoeff, clab[k], i,
3385  cpl_polynomial_eval_1d(trend, i, NULL));
3386  }
3387  }
3388  cpl_polynomial_delete(trend);
3389  }
3390  else {
3391  cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
3392  }
3393 
3394  }
3395 
3396  return CPL_ERROR_NONE;
3397 }
3398 
3416 cpl_error_code mos_interpolate_wavecalib_mos(cpl_table *idscoeff,
3417  int mode,
3418  int degree)
3419 {
3420  const char *func = "mos_interpolate_wavecalib";
3421 
3422  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
3423  /* Max order is 5 */
3424 
3425  cpl_vector *wave;
3426  cpl_vector *positions;
3427  cpl_polynomial *trend;
3428 
3429  double *wdata;
3430  double *pdata;
3431 
3432  double c;
3433  double mse, ksigma;
3434 
3435  int order;
3436  int nrows, first_row, last_row;
3437  int npoints, rpoints;
3438  int null;
3439  int i;
3440 
3441 
3442  if (idscoeff == NULL)
3443  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3444 
3445  if (mode < 0 || mode > 2)
3446  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
3447 
3448  if (mode == 0 || degree < 0)
3449  return CPL_ERROR_NONE;
3450 
3451  /*
3452  * Interpolating the IDS coefficients
3453  */
3454 
3455  nrows = cpl_table_get_nrow(idscoeff);
3456 
3457  order = 0;
3458  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
3459  ++order;
3460  --order;
3461 
3462  //For coefficients > 0 always do a median
3463 
3464  int kinit = 1;
3465  if (degree == 0)
3466  kinit = 0;
3467  for (int k = kinit; k <= order; k++) {
3468  double m;
3469  if (cpl_table_has_column(idscoeff, clab[k])) {
3470  m = cpl_table_get_column_median(idscoeff, clab[k]);
3471  cpl_table_fill_column_window_double(idscoeff, clab[k],
3472  0, nrows, m);
3473  }
3474  }
3475 
3476  if(degree > 0)
3477  {
3478  first_row = 0;
3479  while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
3480  first_row++;
3481 
3482  last_row = nrows - 1;
3483  while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
3484  last_row--;
3485 
3486  int korder = 0;
3487 
3488  npoints = nrows - cpl_table_count_invalid(idscoeff, clab[korder]);
3489  wave = cpl_vector_new(npoints);
3490  wdata = cpl_vector_get_data(wave);
3491  positions = cpl_vector_new(npoints);
3492  pdata = cpl_vector_get_data(positions);
3493 
3494  npoints = 0;
3495  for (i = first_row; i <= last_row; i++) {
3496  c = cpl_table_get_double(idscoeff, clab[korder], i, &null);
3497  if (null == 0) {
3498  wdata[npoints] = c;
3499  pdata[npoints] = i;
3500  npoints++;
3501  }
3502  }
3503 
3504  // This doesn't seem to provide good results, I have not understood why.
3505  // Restore for robust linear fitting.
3506  //
3507  if (degree == 1) {
3508  cpl_vector *p;
3509  cpl_vector *w;
3510  cpl_bivector *list;
3511  double q, m;
3512 
3513  if (npoints > 4) {
3514  p = cpl_vector_extract(positions, 2, npoints - 2, 1);
3515  w = cpl_vector_extract(wave, 2, npoints - 2, 1);
3516  }
3517  else {
3518  p = positions;
3519  w = wave;
3520  }
3521 
3522  list = cpl_bivector_wrap_vectors(p, w);
3523 
3524  robustLinearFit(list, &q, &m, &mse);
3525  cpl_bivector_unwrap_vectors(list);
3526  for (i = first_row; i <= last_row; i++)
3527  cpl_table_set_double(idscoeff, clab[korder], i, q + m*i);
3528 
3529  if (npoints > 4) {
3530  cpl_vector_delete(p);
3531  cpl_vector_delete(w);
3532  }
3533 
3534  cpl_vector_delete(wave);
3535  cpl_vector_delete(positions);
3536 
3537  }
3538  else
3539  {
3540 
3541  // End robust linear fitting
3542 
3543  trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse);
3544 
3545  ksigma = 3*sqrt(mse);
3546 
3547  cpl_vector_delete(wave);
3548  cpl_vector_delete(positions);
3549 
3550  /*
3551  * Iteration
3552  */
3553 
3554  if (trend) {
3555  rpoints = 0;
3556  for (i = first_row; i <= last_row; i++) {
3557  c = cpl_table_get_double(idscoeff, clab[korder], i, &null);
3558  if (null == 0) {
3559  if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
3560  < ksigma) {
3561  rpoints++;
3562  }
3563  }
3564  }
3565 
3566  if (rpoints > 0 && rpoints < npoints) {
3567  cpl_msg_debug(func, "%d points rejected from "
3568  "wavelength calibration fit",
3569  npoints - rpoints);
3570 
3571  wave = cpl_vector_new(rpoints);
3572  wdata = cpl_vector_get_data(wave);
3573  positions = cpl_vector_new(rpoints);
3574  pdata = cpl_vector_get_data(positions);
3575 
3576  npoints = 0;
3577  for (i = first_row; i <= last_row; i++) {
3578  c = cpl_table_get_double(idscoeff, clab[korder], i, &null);
3579  if (null == 0) {
3580  if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
3581  < ksigma) {
3582  wdata[npoints] = c;
3583  pdata[npoints] = i;
3584  npoints++;
3585  }
3586  }
3587  }
3588 
3589  if (npoints) {
3590  cpl_polynomial_delete(trend);
3591  trend = cpl_polynomial_fit_1d_create(positions,
3592  wave, degree, NULL);
3593  }
3594 
3595  cpl_vector_delete(wave);
3596  cpl_vector_delete(positions);
3597 
3598  }
3599  }
3600 
3601  if (trend) {
3602  for (i = first_row; i <= last_row; i++) {
3603  if (mode == 1) {
3604  if (!cpl_table_is_valid(idscoeff, clab[korder], i)) {
3605  cpl_table_set_double(idscoeff, clab[korder], i,
3606  cpl_polynomial_eval_1d(trend, i,
3607  NULL));
3608  }
3609  }
3610  else if (mode == 2) {
3611  cpl_table_set_double(idscoeff, clab[korder], i,
3612  cpl_polynomial_eval_1d(trend, i, NULL));
3613  }
3614  }
3615  cpl_polynomial_delete(trend);
3616  }
3617  else {
3618  cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
3619  }
3620  }
3621  }
3622 
3623  return CPL_ERROR_NONE;
3624 }
3625 
3626 
3651 //TODO:Deprecate this function. Change all the recipes which still use it.
3652 //Use the new functionality to remove overscan, trimm and bias.
3653 
3654 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias,
3655  cpl_table *overscans)
3656 {
3657  const char *func = "mos_remove_bias";
3658 
3659  cpl_image *unbiased;
3660  cpl_image *overscan;
3661  double mean_bias_level;
3662  double mean_overscans_level;
3663  int count;
3664  int nrows;
3665  int xlow, ylow, xhig, yhig;
3666  int i;
3667 
3668 
3669  if (image == NULL || overscans == NULL) {
3670  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3671  return NULL;
3672  }
3673 
3674  nrows = cpl_table_get_nrow(overscans);
3675 
3676  if (nrows == 0) {
3677  cpl_msg_error(func, "Empty overscan table");
3678  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
3679  return NULL;
3680  }
3681 
3682  if (bias) {
3683  if (nrows == 1) {
3684  unbiased = cpl_image_subtract_create(image, bias);
3685  if (unbiased == NULL) {
3686  cpl_msg_error(func, "Incompatible master bias");
3687  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
3688  }
3689  return unbiased;
3690  }
3691  mean_bias_level = cpl_image_get_mean(bias);
3692  }
3693  else {
3694  if (nrows == 1) {
3695  cpl_msg_error(func, "No master bias in input, and no overscan "
3696  "regions in input image: bias subtraction "
3697  "cannot be performed!");
3698  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
3699  return NULL;
3700  }
3701  mean_bias_level = 0.0;
3702  }
3703 
3704  mean_overscans_level = 0.0;
3705  count = 0;
3706  for (i = 0; i < nrows; i++) {
3707  xlow = cpl_table_get_int(overscans, "xlow", i, NULL);
3708  ylow = cpl_table_get_int(overscans, "ylow", i, NULL);
3709  xhig = cpl_table_get_int(overscans, "xhig", i, NULL);
3710  yhig = cpl_table_get_int(overscans, "yhig", i, NULL);
3711 
3712  if (i == 0) {
3713  unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
3714  if (unbiased == NULL) {
3715  cpl_msg_error(func, "Incompatible overscan table");
3716  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
3717  return NULL;
3718  }
3719  if (bias) {
3720  if (cpl_image_subtract(unbiased, bias)) {
3721  cpl_msg_error(func, "Incompatible master bias");
3722  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
3723  cpl_image_delete(unbiased);
3724  return NULL;
3725  }
3726  }
3727  }
3728  else {
3729  overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
3730  if (overscan == NULL) {
3731  cpl_msg_error(func, "Incompatible overscan table");
3732  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
3733  cpl_image_delete(unbiased);
3734  return NULL;
3735  }
3736 
3737  mean_overscans_level += cpl_image_get_median(overscan);
3738  count++;
3739 
3740 /***
3741  * Here the mean level was used: not very robust...
3742 
3743  mean_overscans_level += cpl_image_get_flux(overscan);
3744  count += cpl_image_get_size_x(overscan)
3745  * cpl_image_get_size_y(overscan);
3746 ***/
3747  cpl_image_delete(overscan);
3748  }
3749  }
3750 
3751  /*
3752  * Overscan correction
3753  */
3754 
3755  mean_overscans_level /= count;
3756 
3757  cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level);
3758 
3759  cpl_msg_info(cpl_func,
3760  "Difference between mean overscans level "
3761  "and mean bias level: %.2f",
3762  mean_overscans_level - mean_bias_level);
3763 
3764  return unbiased;
3765 
3766 }
3767 
3768 
3827 cpl_error_code mos_arc_background_1D(float *spectrum, float *back,
3828  int length, int msize, int fsize)
3829 {
3830  const char *func = "mos_arc_background_1D";
3831 
3832  float *minf;
3833  float *maxf;
3834  float *smof;
3835  int i;
3836 
3837 
3838  if (spectrum == NULL || back == NULL)
3839  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3840 
3841  if (msize % 2 == 0)
3842  msize++;
3843 
3844  if (fsize % 2 == 0)
3845  fsize++;
3846 
3847  if (msize < 3 || fsize < msize || length < 2*fsize)
3848  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
3849 
3850 
3851  minf = min_filter(spectrum, length, msize);
3852  smof = smo_filter(minf, length, fsize);
3853  cpl_free(minf);
3854  maxf = max_filter(smof, length, 2*msize+1);
3855  cpl_free(smof);
3856  smof = smo_filter(maxf, length, 2*fsize+1);
3857  cpl_free(maxf);
3858  minf = min_filter(smof, length, 2*msize+1);
3859  cpl_free(smof);
3860  smof = smo_filter(minf, length, 2*fsize+1);
3861  cpl_free(minf);
3862 
3863  for (i = 0; i < length; i++)
3864  back[i] = smof[i];
3865 
3866  cpl_free(smof);
3867 
3868  return CPL_ERROR_NONE;
3869 
3870 }
3871 
3872 
3929 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize)
3930 {
3931  const char *func = "mos_arc_background";
3932 
3933  cpl_image *fimage;
3934  cpl_image *bimage;
3935  float *data;
3936  float *bdata;
3937  float *row;
3938  float *brow;
3939  int nx, ny;
3940  int i;
3941 
3942 
3943  if (image == NULL) {
3944  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
3945  return NULL;
3946  }
3947 
3948  if (msize % 2 == 0)
3949  msize++;
3950 
3951  if (fsize % 2 == 0)
3952  fsize++;
3953 
3954  nx = cpl_image_get_size_x(image);
3955  ny = cpl_image_get_size_y(image);
3956 
3957  bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
3958 
3959  fimage = mos_image_filter_median(image, 3, 3);
3960 
3961  data = cpl_image_get_data_float(fimage);
3962  bdata = cpl_image_get_data_float(bimage);
3963 
3964  for (i = 0; i < ny; i++) {
3965  row = data + i * nx;
3966  brow = bdata + i * nx;
3967  if (mos_arc_background_1D(row, brow, nx, msize, fsize)) {
3968  cpl_error_set_where(func);
3969  cpl_image_delete(fimage);
3970  cpl_image_delete(bimage);
3971  return NULL;
3972  }
3973  }
3974 
3975  cpl_image_delete(fimage);
3976 
3977  return bimage;
3978 }
3979 
3980 
4001 int mos_lines_width(const float *spectrum, int length)
4002 {
4003 
4004  const char *func = "mos_lines_width";
4005 
4006  double *profile1 = cpl_calloc(length - 1, sizeof(double));
4007  double *profile2 = cpl_calloc(length - 1, sizeof(double));
4008 
4009  double norm, value, max;
4010  int radius = 20;
4011  int short_length = length - 2*radius - 1;
4012  int width;
4013  int i, j, k;
4014 
4015 
4016  /*
4017  * Derivative, and separation of positive and negative derivatives
4018  */
4019 
4020  for (j = 0, i = 1; i < length; j++, i++) {
4021  profile1[j] = profile2[j] = spectrum[i] - spectrum[j];
4022  if (profile1[j] < 0)
4023  profile1[j] = 0;
4024  if (profile2[j] > 0)
4025  profile2[j] = 0;
4026  else
4027  profile2[j] = -profile2[j];
4028  }
4029 
4030 
4031  /*
4032  * Profiles normalisation
4033  */
4034 
4035  length--;
4036 
4037  norm = 0;
4038  for (i = 0; i < length; i++)
4039  if (norm < profile1[i])
4040  norm = profile1[i];
4041 
4042  for (i = 0; i < length; i++) {
4043  profile1[i] /= norm;
4044  profile2[i] /= norm;
4045  }
4046 
4047 
4048  /*
4049  * Cross-correlation
4050  */
4051 
4052  max = -1;
4053  for (i = 0; i <= radius; i++) {
4054  value = 0;
4055  for (j = 0; j < short_length; j++) {
4056  k = radius+j;
4057  value += profile1[k] * profile2[k+i];
4058  }
4059  if (max < value) {
4060  max = value;
4061  width = i;
4062  }
4063  }
4064 
4065  cpl_free(profile1);
4066  cpl_free(profile2);
4067 
4068  if (max < 0.0) {
4069  cpl_msg_debug(func, "Cannot estimate line width");
4070  width = 1;
4071  }
4072 
4073  return width;
4074 
4075 }
4076 
4077 
4104 cpl_vector *mos_peak_candidates(const float *spectrum,
4105  int length, float level,
4106  float exp_width)
4107 {
4108 
4109  const char *func = "mos_peak_candidates";
4110 
4111  int i, j;
4112  int nint = length - 1;
4113  int n = 0;
4114  int width = 2 * ceil(exp_width / 2) + 1;
4115  int start = width / 2;
4116  int end = length - width / 2;
4117  int step;
4118  float *smo;
4119  double *data = cpl_calloc(length/2, sizeof(double));
4120 
4121 
4122  if (spectrum == NULL) {
4123  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
4124  return NULL;
4125  }
4126 
4127 
4128  /*
4129  * If lines have a flat top (as in the case of broad slit), smooth
4130  * before determining the max.
4131  */
4132 
4133  if (width > 7) {
4134  smo = cpl_calloc(length, sizeof(float));
4135  start = width / 2;
4136  end = length - width / 2;
4137  for (i = 0; i < start; i++)
4138  smo[i] = spectrum[i];
4139  for (i = start; i < end; i++) {
4140  for (j = i - start; j <= i + start; j++)
4141  smo[i] += spectrum[j];
4142  smo[i] /= width;
4143  }
4144  for (i = end; i < length; i++)
4145  smo[i] = spectrum[i];
4146  }
4147  else {
4148  smo = (float *)spectrum;
4149  }
4150 
4151  /*
4152  * Collect all relative maxima along spectrum, that are higher than the
4153  * specified level.
4154  */
4155 
4156  if (width > 20)
4157  step = width / 2;
4158  else
4159  step = 1;
4160 
4161  for (i = step; i < nint - step + 1; i += step) {
4162  if (smo[i] > level) {
4163  if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) {
4164  if (smo[i-step] != 0.0 && smo[i+step] != 0.0) {
4165  data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]);
4166  ++n;
4167  }
4168  }
4169  }
4170  }
4171 
4172  if (width > 7) {
4173  cpl_free(smo);
4174  }
4175 
4176  if (n == 0) {
4177  cpl_free(data);
4178  return NULL;
4179  }
4180 
4181  return cpl_vector_wrap(n, data);
4182 
4183 }
4184 
4185 
4207 cpl_vector *mos_refine_peaks(const float *spectrum, int length,
4208  cpl_vector *peaks, int sradius)
4209 {
4210 
4211  const char *func = "mos_refine_peaks";
4212 
4213  double *data;
4214  float pos;
4215  int npeaks;
4216  int startPos, endPos;
4217  int window = 2*sradius+1;
4218  int i, j;
4219 
4220 
4221  if (peaks == NULL || spectrum == NULL) {
4222  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
4223  return NULL;
4224  }
4225 
4226  npeaks = cpl_vector_get_size(peaks);
4227  data = cpl_vector_unwrap(peaks);
4228 
4229  for (i = 0; i < npeaks; i++) {
4230  startPos = data[i] - window/2;
4231  endPos = startPos + window;
4232  if (startPos < 0 || endPos >= length)
4233  continue;
4234 
4235  if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) {
4236  pos += startPos;
4237  data[i] = pos;
4238  }
4239  }
4240 
4241  for (i = 1; i < npeaks; i++)
4242  if (data[i] - data[i-1] < 0.5)
4243  data[i-1] = -1.0;
4244 
4245  for (i = 0, j = 0; i < npeaks; i++) {
4246  if (data[i] > 0.0) {
4247  if (i != j)
4248  data[j] = data[i];
4249  j++;
4250  }
4251  }
4252 
4253  return cpl_vector_wrap(j, data);
4254 
4255 }
4256 
4257 
4258 void mos_set_multiplex(int multiplex)
4259 {
4260  mos_multiplex = multiplex;
4261 }
4262 
4316 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines,
4317  double min_disp, double max_disp,
4318  double tolerance)
4319 {
4320 
4321  int i, j, k, l;
4322  int nlint, npint;
4323  int minpos;
4324  float min;
4325  double lratio, pratio;
4326  double lo_start, lo_end, hi_start, hi_end, denom;
4327  double disp, variation, prev_variation;
4328  int max, maxpos, minl, mink;
4329  int ambiguous;
4330  int npeaks_lo, npeaks_hi;
4331  int *peak_lo;
4332  int *peak_hi;
4333  int **ident;
4334  int *nident;
4335  int *lident;
4336 
4337  double *peak;
4338  double *line;
4339  int npeaks, nlines;
4340 
4341  double *xpos;
4342  double *lambda;
4343  int *ilambda;
4344  double *tmp_xpos;
4345  double *tmp_lambda;
4346  int *tmp_ilambda;
4347  int *flag;
4348  int n = 0;
4349  int nn;
4350  int nseq = 0;
4351  int gap;
4352  int *seq_length;
4353  int found;
4354 
4355  peak = cpl_vector_get_data(peaks);
4356  npeaks = cpl_vector_get_size(peaks);
4357  line = cpl_vector_get_data(lines);
4358  nlines = cpl_vector_get_size(lines);
4359 
4360  if (npeaks < 4)
4361  return NULL;
4362 
4363  peak_lo = cpl_malloc(npeaks * sizeof(int));
4364  peak_hi = cpl_malloc(npeaks * sizeof(int));
4365  nident = cpl_calloc(npeaks, sizeof(int));
4366  lident = cpl_calloc(nlines, sizeof(int));
4367  xpos = cpl_calloc(npeaks, sizeof(double));
4368  lambda = cpl_calloc(npeaks, sizeof(double));
4369  ilambda = cpl_calloc(npeaks, sizeof(int));
4370  tmp_xpos = cpl_calloc(npeaks, sizeof(double));
4371  tmp_lambda = cpl_calloc(npeaks, sizeof(double));
4372  tmp_ilambda = cpl_calloc(npeaks, sizeof(int));
4373  flag = cpl_calloc(npeaks, sizeof(int));
4374  seq_length = cpl_calloc(npeaks, sizeof(int));
4375  ident = cpl_malloc(npeaks * sizeof(int *));
4376  for (i = 0; i < npeaks; i++)
4377  ident[i] = cpl_malloc(3 * npeaks * sizeof(int));
4378 
4379  /*
4380  * This is just the number of intervals - one less than the number
4381  * of points (catalog wavelengths, or detected peaks).
4382  */
4383 
4384  nlint = nlines - 1;
4385  npint = npeaks - 1;
4386 
4387 
4388  /*
4389  * Here the big loops on catalog lines begins.
4390  */
4391 
4392  for (i = 1; i < nlint; i++) {
4393 
4394 
4395  /*
4396  * For each catalog wavelength I take the previous and the next one,
4397  * and compute the ratio of the corresponding wavelength intervals.
4398  * This ratio will be compared to all the ratios obtained doing the
4399  * same with all the detected peaks positions.
4400  */
4401 
4402  lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
4403 
4404 
4405  /*
4406  * Here the loop on detected peaks positions begins.
4407  */
4408 
4409  for (j = 1; j < npint; j++) {
4410 
4411  /*
4412  * Not all peaks are used for computing ratios: just the ones
4413  * that are compatible with the expected spectral dispersion
4414  * are taken into consideration. Therefore, I define the pixel
4415  * intervals before and after any peak that are compatible with
4416  * the specified dispersion interval, and select just the peaks
4417  * within such intervals. If either of the two intervals doesn't
4418  * contain any peak, then I skip the current peak and continue
4419  * with the next.
4420  */
4421 
4422  lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
4423  lo_end = peak[j] - (line[i] - line[i-1]) / max_disp;
4424  hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
4425  hi_end = peak[j] + (line[i+1] - line[i]) / min_disp;
4426 
4427  for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
4428  if (peak[k] > lo_end)
4429  break;
4430  if (peak[k] > lo_start) {
4431  peak_lo[npeaks_lo] = k;
4432  ++npeaks_lo;
4433  }
4434  }
4435 
4436  if (npeaks_lo == 0)
4437  continue;
4438 
4439  for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
4440  if (peak[k] > hi_end)
4441  break;
4442  if (peak[k] > hi_start) {
4443  peak_hi[npeaks_hi] = k;
4444  ++npeaks_hi;
4445  }
4446  }
4447 
4448  if (npeaks_hi == 0)
4449  continue;
4450 
4451 
4452  /*
4453  * Now I have all peaks that may help for a local identification.
4454  * peak_lo[k] is the sequence number of the k-th peak of the lower
4455  * interval; peak_hi[l] is the sequence number of the l-th peak of
4456  * the higher interval. j is, of course, the sequence number of the
4457  * current peak (second big loop).
4458  */
4459 
4460  prev_variation = 1000.0;
4461  minl = mink = 0;
4462 
4463  for (k = 0; k < npeaks_lo; k++) {
4464  denom = peak[j] - peak[peak_lo[k]];
4465  for (l = 0; l < npeaks_hi; l++) {
4466 
4467  /*
4468  * For any pair of peaks - one from the lower and the other
4469  * from the higher interval - I compute the same ratio that
4470  * was computed with the current line catalog wavelength.
4471  */
4472 
4473  pratio = (peak[peak_hi[l]] - peak[j]) / denom;
4474 
4475  /*
4476  * If the two ratios are compatible within the specified
4477  * tolerance, we have a preliminary identification. This
4478  * will be marked in the matrix ident[][], where the first
4479  * index corresponds to a peak sequence number, and the second
4480  * index is the counter of the identifications made during
4481  * this whole process. The array of counters is nident[].
4482  * If more than one interval pair fulfills the specified
4483  * tolerance, the closest to the expected ratio is selected.
4484  */
4485 
4486  variation = fabs(lratio-pratio) / pratio;
4487 
4488  if (variation < tolerance) {
4489  if (variation < prev_variation) {
4490  prev_variation = variation;
4491  minl = l;
4492  mink = k;
4493  }
4494  }
4495  }
4496  }
4497  if (prev_variation < tolerance) {
4498  ident[j][nident[j]] = i;
4499  ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
4500  ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
4501  ++nident[j];
4502  ++nident[peak_hi[minl]];
4503  ++nident[peak_lo[mink]];
4504  }
4505  } /* End loop on positions */
4506  } /* End loop on lines */
4507 
4508 
4509  /*
4510  * At this point I have filled the ident matrix with all my preliminary
4511  * identifications. Ambiguous identifications must be eliminated.
4512  */
4513 
4514 
4515  for (i = 0; i < npeaks; i++) {
4516 
4517 
4518  /*
4519  * I don't take into consideration peaks that were never identified.
4520  * They are likely contaminations, or emission lines that were not
4521  * listed in the input wavelength catalog.
4522  */
4523 
4524  if (nident[i] > 1) {
4525 
4526 
4527  /*
4528  * Initialise the histogram of wavelengths assigned to the i-th peak.
4529  */
4530 
4531  for (j = 0; j < nlines; j++)
4532  lident[j] = 0;
4533 
4534 
4535  /*
4536  * Count how many times each catalog wavelength was assigned
4537  * to the i-th peak.
4538  */
4539 
4540  for (j = 0; j < nident[i]; j++)
4541  ++lident[ident[i][j]];
4542 
4543 
4544  /*
4545  * What wavelength was most frequently assigned to the i-th peak?
4546  */
4547 
4548  max = 0;
4549  maxpos = 0;
4550  for (j = 0; j < nlines; j++) {
4551  if (max < lident[j]) {
4552  max = lident[j];
4553  maxpos = j;
4554  }
4555  }
4556 
4557 
4558  /*
4559  * Were there other wavelengths assigned with the same frequency?
4560  * This would be the case of an ambiguous identification. It is
4561  * safer to reject this peak...
4562  */
4563 
4564  ambiguous = 0;
4565 
4566  for (k = maxpos + 1; k < nlines; k++) {
4567  if (lident[k] == max) {
4568  ambiguous = 1;
4569  break;
4570  }
4571  }
4572 
4573  if (ambiguous)
4574  continue;
4575 
4576 
4577  /*
4578  * Otherwise, I assign to the i-th peak the wavelength that was
4579  * most often assigned to it.
4580  */
4581 
4582  tmp_xpos[n] = peak[i];
4583  tmp_lambda[n] = line[maxpos];
4584  tmp_ilambda[n] = maxpos;
4585 
4586  ++n;
4587 
4588  }
4589 
4590  }
4591 
4592 
4593  /*
4594  * Check on identified peaks. Contaminations from other spectra might
4595  * be present and should be excluded: this type of contamination
4596  * consists of peaks that have been _correctly_ identified! The non-
4597  * spectral type of light contamination should have been almost all
4598  * removed already in the previous steps, but it may still be present.
4599  * Here, the self-consistent sequences of identified peaks are
4600  * separated one from the other. At the moment, just the longest of
4601  * such sequences is selected (in other words, spectral multiplexing
4602  * is ignored).
4603  */
4604 
4605  if (n > 1) {
4606  nn = 0; /* Number of peaks in the list of sequences */
4607  nseq = 0; /* Current sequence */
4608  for (k = 0; k < n; k++) {
4609  if (flag[k] == 0) { /* Was peak k already assigned to a sequence? */
4610  flag[k] = 1;
4611  xpos[nn] = tmp_xpos[k]; /* Begin the nseq-th sequence */
4612  lambda[nn] = tmp_lambda[k];
4613  ilambda[nn] = tmp_ilambda[k];
4614  ++seq_length[nseq];
4615  ++nn;
4616 
4617  /*
4618  * Now look for all the following peaks that are compatible
4619  * with the expected spectral dispersion, and add them in
4620  * sequence to xpos. Note that missing peaks are not a problem...
4621  */
4622 
4623  i = k;
4624  while (i < n - 1) {
4625  found = 0;
4626  for (j = i + 1; j < n; j++) {
4627  if (flag[j] == 0) {
4628  disp = (tmp_lambda[j] - tmp_lambda[i])
4629  / (tmp_xpos[j] - tmp_xpos[i]);
4630  if (disp >= min_disp && disp <= max_disp) {
4631  flag[j] = 1;
4632  xpos[nn] = tmp_xpos[j];
4633  lambda[nn] = tmp_lambda[j];
4634  ilambda[nn] = tmp_ilambda[j];
4635  ++seq_length[nseq];
4636  ++nn;
4637  i = j;
4638  found = 1;
4639  break;
4640  }
4641  }
4642  }
4643  if (!found)
4644  break;
4645  }
4646 
4647  /*
4648  * Current sequence is completed: begin new sequence on the
4649  * excluded peaks, starting the loop on peaks again.
4650  */
4651 
4652  ++nseq;
4653  k = 0;
4654  }
4655  }
4656 
4657 
4658  /*
4659  * Find the longest sequence of self-consistent peaks.
4660  */
4661 
4662  maxpos = max = 0;
4663 
4664  if (mos_multiplex < 0) {
4665  for (i = 0; i < nseq; i++) {
4666  if (seq_length[i] > max) {
4667  max = seq_length[i];
4668  maxpos = i;
4669  }
4670  }
4671  }
4672  else {
4673 
4674  /*
4675  * Now consider the sequence which lays in the specified
4676  * CCD region (indicated by mos_multiplex): that is, _most_
4677  * of its lines (more than half) must be in that region...
4678  */
4679 
4680  nn = 0;
4681  found = 0;
4682 
4683  for (i = 0; i < nseq; i++) {
4684  n = seq_length[i];
4685  if (n > 5) {
4686  cpl_array *regions = cpl_array_new(n, CPL_TYPE_INT);
4687  int region;
4688 
4689  for (j = 0; j < n; j++)
4690  cpl_array_set_int(regions, j,
4691  ((int)floor(xpos[nn + j])) / mos_region_size);
4692 
4693  region = (int)cpl_array_get_median(regions);
4694  cpl_array_delete(regions);
4695 
4696  if (mos_multiplex == region) {
4697  if (found) {
4698  cpl_msg_debug(cpl_func, "More than one spectrum found in "
4699  "region %d (only the first one is extracted)",
4700  mos_multiplex);
4701  break;
4702  }
4703  found = 1;
4704  max = seq_length[i];
4705  maxpos = i;
4706  }
4707  }
4708  nn += seq_length[i];
4709  }
4710  }
4711 
4712  /*
4713  * Find where this sequence starts in the whole peak position
4714  * storage.
4715  */
4716 
4717  nn = 0;
4718  for (i = 0; i < maxpos; i++)
4719  nn += seq_length[i];
4720 
4721  /*
4722  * Move the longest sequence at the beginning of the returned lists
4723  */
4724 
4725  n = max;
4726  for (i = 0; i < n; i++, nn++) {
4727  xpos[i] = xpos[nn];
4728  lambda[i] = lambda[nn];
4729  ilambda[i] = ilambda[nn];
4730  }
4731 
4732 
4733  /*
4734  * Are some wavelengths missing? Recover them.
4735  */
4736 
4737  for (i = 1; i < n; i++) {
4738  gap = ilambda[i] - ilambda[i-1];
4739  for (j = 1; j < gap; j++) {
4740 
4741  if (j == 1) {
4742 
4743  /*
4744  * Determine the local dispersion from the current pair of peaks
4745  */
4746 
4747  disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
4748  }
4749 
4750  /*
4751  * With this, find the expected position of the missing
4752  * peak by linear interpolation.
4753  */
4754 
4755  hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
4756 
4757  /*
4758  * Is there a peak at that position? Here a peak from the
4759  * original list is searched, that is closer than 2 pixels
4760  * to the expected position. If it is found, insert it at
4761  * the current position on the list of identified peaks,
4762  * and leave immediately the loop (taking the new position
4763  * for the following linear interpolation, in case more
4764  * than one peak is missing in the current interval).
4765  * If it is not found, stay in the loop, looking for
4766  * the following missing peaks in this interval.
4767  */
4768 
4769  found = 0;
4770  for (k = 0; k < npeaks; k++) {
4771  if (fabs(peak[k] - hi_start) < 2) {
4772  for (l = n; l > i; l--) {
4773  xpos[l] = xpos[l-1];
4774  lambda[l] = lambda[l-1];
4775  ilambda[l] = ilambda[l-1];
4776  }
4777  xpos[i] = peak[k];
4778  lambda[i] = line[ilambda[i-1] + j];
4779  ilambda[i] = ilambda[i-1] + j;
4780  ++n;
4781  found = 1;
4782  break;
4783  }
4784  }
4785  if (found)
4786  break;
4787  }
4788  }
4789 
4790 
4791  /*
4792  * Try to extrapolate forward
4793  */
4794 
4795  found = 1;
4796  while (ilambda[n-1] < nlines - 1 && found) {
4797 
4798  /*
4799  * Determine the local dispersion from the last pair of
4800  * identified peaks
4801  */
4802 
4803  if (n > 1)
4804  disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
4805  else
4806  disp = 0.0;
4807 
4808  if (disp > max_disp || disp < min_disp)
4809  break;
4810 
4811 
4812  /*
4813  * With this, find the expected position of the missing
4814  * peak by linear interpolation.
4815  */
4816 
4817  hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
4818 
4819  /*
4820  * Is there a peak at that position? Here a peak from the
4821  * original list is searched, that is closer than 6 pixels
4822  * to the expected position. If it is found, insert it at
4823  * the end of the list of identified peaks. If it is not
4824  * found, leave the loop.
4825  */
4826 
4827  found = 0;
4828  min = fabs(peak[0] - hi_start);
4829  minpos = 0;
4830  for (k = 1; k < npeaks; k++) {
4831  if (min > fabs(peak[k] - hi_start)) {
4832  min = fabs(peak[k] - hi_start);
4833  minpos = k;
4834  }
4835  }
4836  if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
4837  xpos[n] = peak[minpos];
4838  lambda[n] = line[ilambda[n-1] + 1];
4839  ilambda[n] = ilambda[n-1] + 1;
4840  ++n;
4841  found = 1;
4842  }
4843  }
4844 
4845 
4846  /*
4847  * Try to extrapolate backward
4848  */
4849 
4850  found = 1;
4851  while (ilambda[0] > 0 && found) {
4852 
4853  /*
4854  * Determine the local dispersion from the first pair of
4855  * identified peaks
4856  */
4857 
4858  disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
4859 
4860  if (disp > max_disp || disp < min_disp)
4861  break;
4862 
4863 
4864  /*
4865  * With this, find the expected position of the missing
4866  * peak by linear interpolation.
4867  */
4868 
4869  hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
4870 
4871 
4872  /*
4873  * Is there a peak at that position? Here a peak from the
4874  * original list is searched, that is closer than 6 pixels
4875  * to the expected position. If it is found, insert it at
4876  * the beginning of the list of identified peaks. If it is not
4877  * found, leave the loop.
4878  */
4879 
4880  found = 0;
4881  min = fabs(peak[0] - hi_start);
4882  minpos = 0;
4883  for (k = 1; k < npeaks; k++) {
4884  if (min > fabs(peak[k] - hi_start)) {
4885  min = fabs(peak[k] - hi_start);
4886  minpos = k;
4887  }
4888  }
4889  if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
4890  for (j = n; j > 0; j--) {
4891  xpos[j] = xpos[j-1];
4892  lambda[j] = lambda[j-1];
4893  ilambda[j] = ilambda[j-1];
4894  }
4895  xpos[0] = peak[minpos];
4896  lambda[0] = line[ilambda[0] - 1];
4897  ilambda[0] = ilambda[0] - 1;
4898  ++n;
4899  found = 1;
4900  }
4901  }
4902  }
4903 
4904 
4905  /*
4906  * At this point all peaks are processed. Free memory, and return
4907  * the result.
4908  */
4909 
4910 /************************************************+
4911  for (i = 0; i < npeaks; i++) {
4912  printf("Peak %d:\n ", i);
4913  for (j = 0; j < nident[i]; j++)
4914  printf("%.2f, ", line[ident[i][j]]);
4915  printf("\n");
4916  }
4917 
4918  printf("\n");
4919 
4920  for (i = 0; i < n; i++)
4921  printf("%.2f, %.2f\n", xpos[i], lambda[i]);
4922 +************************************************/
4923  for (i = 0; i < npeaks; i++)
4924  cpl_free(ident[i]);
4925  cpl_free(ident);
4926  cpl_free(nident);
4927  cpl_free(lident);
4928  cpl_free(ilambda);
4929  cpl_free(tmp_xpos);
4930  cpl_free(tmp_lambda);
4931  cpl_free(tmp_ilambda);
4932  cpl_free(peak_lo);
4933  cpl_free(flag);
4934  cpl_free(seq_length);
4935  cpl_free(peak_hi);
4936 
4937  if (n == 0) {
4938  cpl_free(xpos);
4939  cpl_free(lambda);
4940  return NULL;
4941  }
4942 
4943  return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos),
4944  cpl_vector_wrap(n, lambda));
4945 }
4946 
4947 
4965 /*
4966 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
4967  double refwave, double pixel)
4968 {
4969  double yellow;
4970  double cpixel;
4971  double tolerance = 0.02;
4972  int max_iter = 20;
4973  int iter = 0;
4974 
4975 
4976  if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
4977  return 0.0;
4978 
4979  if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
4980  return 0.0;
4981 
4982  yellow = (blue + red) / 2;
4983 
4984  cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
4985 
4986  while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
4987 
4988  if (cpixel > pixel)
4989  red = yellow;
4990  else
4991  blue = yellow;
4992 
4993  yellow = (blue + red) / 2;
4994  cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
4995 
4996  iter++;
4997 
4998  }
4999 
5000  return yellow;
5001 
5002 }
5003 */
5004 
5005 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
5006  double refwave, double pixel)
5007 {
5008  double yellow;
5009  double coeff;
5010  cpl_size zero = 0;
5011 
5012  if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
5013  return 0.0;
5014 
5015  if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
5016  return 0.0;
5017 
5018  yellow = (blue + red) / 2 - refwave;
5019 
5020  coeff = cpl_polynomial_get_coeff(ids, &zero);
5021  cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
5022 
5023  cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
5024  if (cpl_error_get_code() != CPL_ERROR_NONE) {
5025  cpl_error_reset();
5026  return 0.0;
5027  }
5028 
5029  cpl_polynomial_set_coeff(ids, &zero, coeff);
5030 
5031  return yellow + refwave;
5032 
5033 }
5034 
5060 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order,
5061  double reject, int minlines,
5062  int *nlines, double *err,
5063  cpl_bivector **pixwav_used)
5064 {
5065  const char *func = "mos_poly_wav2pix";
5066 
5067  cpl_bivector *pixwav2;
5068  cpl_vector *wavel;
5069  cpl_vector *pixel;
5070  double *d_wavel;
5071  double *d_pixel;
5072  double pixpos;
5073  int fitlines;
5074  int rejection = 0;
5075  int i, j;
5076 
5077  cpl_polynomial *ids;
5078 
5079 
5080  *nlines = 0;
5081  *err = 0;
5082 
5083  if (pixwav == NULL) {
5084  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
5085  return NULL;
5086  }
5087 
5088  fitlines = cpl_bivector_get_size(pixwav);
5089 
5090  if (fitlines < minlines) {
5091  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5092  return NULL;
5093  }
5094 
5095 
5096  /*
5097  * If outliers rejection was requested, allocate a working
5098  * vector (that can be modified as soon as outliers are removed)
5099  */
5100 
5101  if (reject > 0.0)
5102  rejection = 1;
5103 
5104  if (rejection)
5105  pixwav2 = cpl_bivector_duplicate(pixwav);
5106  else
5107  pixwav2 = pixwav;
5108 
5109 
5110  /*
5111  * The single vectors are extracted just because the fitting routine
5112  * requires it
5113  */
5114 
5115  pixel = cpl_bivector_get_x(pixwav2);
5116  wavel = cpl_bivector_get_y(pixwav2);
5117 
5118 
5119  /*
5120  * Get rid of the wrapper, in case of duplication
5121  */
5122 
5123  if (rejection)
5124  cpl_bivector_unwrap_vectors(pixwav2);
5125 
5126 
5127  /*
5128  * Here begins the iterative fit of identified lines
5129  */
5130 
5131  while (fitlines >= minlines) {
5132 
5133  ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
5134  *err = sqrt(*err);
5135 
5136  if (ids == NULL) {
5137  cpl_msg_debug(cpl_error_get_where(), "%s", cpl_error_get_message());
5138  cpl_msg_debug(func, "Fitting IDS");
5139  cpl_error_set_where(func);
5140  if (rejection) {
5141  cpl_vector_delete(wavel);
5142  cpl_vector_delete(pixel);
5143  }
5144  return NULL;
5145  }
5146 
5147  if (rejection) {
5148  cpl_vector * wavel_used = cpl_vector_duplicate(wavel);
5149  cpl_vector * pixel_used = cpl_vector_duplicate(pixel);
5150 
5151 
5152  /*
5153  * Now work directly with the vector data buffers...
5154  */
5155 
5156  d_pixel = cpl_vector_unwrap(pixel);
5157  d_wavel = cpl_vector_unwrap(wavel);
5158 
5159  for (i = 0, j = 0; i < fitlines; i++) {
5160  pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
5161  if (fabs(pixpos - d_pixel[i]) < reject) {
5162  d_pixel[j] = d_pixel[i];
5163  d_wavel[j] = d_wavel[i];
5164  j++;
5165  }
5166  }
5167 
5168  if (j == fitlines) { /* No rejection in last iteration */
5169  cpl_bivector * pixwav_used_temp =
5170  cpl_bivector_wrap_vectors(pixel_used, wavel_used);
5171  *pixwav_used = cpl_bivector_duplicate(pixwav_used_temp);
5172  cpl_bivector_unwrap_vectors(pixwav_used_temp);
5173  cpl_vector_delete(wavel_used);
5174  cpl_vector_delete(pixel_used);
5175  cpl_free(d_wavel);
5176  cpl_free(d_pixel);
5177  *nlines = fitlines;
5178  return ids;
5179  }
5180  else { /* Some lines were rejected */
5181  fitlines = j;
5182  cpl_polynomial_delete(ids);
5183  if (fitlines >= minlines) {
5184  pixel = cpl_vector_wrap(fitlines, d_pixel);
5185  wavel = cpl_vector_wrap(fitlines, d_wavel);
5186  }
5187  else { /* Too few lines: failure */
5188  cpl_free(d_wavel);
5189  cpl_free(d_pixel);
5190  cpl_error_set(func, CPL_ERROR_CONTINUE);
5191  return NULL;
5192  }
5193  }
5194  cpl_vector_delete(wavel_used);
5195  cpl_vector_delete(pixel_used);
5196  }
5197  else {
5198  *nlines = fitlines;
5199  *pixwav_used = cpl_bivector_duplicate(pixwav2);
5200  return ids; /* Exit at first iteration if no rejection */
5201  }
5202  }
5203 
5204  return ids; /* To avoid compiler warnings */
5205 }
5206 
5207 
5232 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
5233  double reject, int minlines,
5234  int *nlines, double *err)
5235 {
5236 
5237  cpl_bivector *wavpix;
5238  cpl_vector *wavel;
5239  cpl_vector *pixel;
5240 
5241  cpl_polynomial *dds;
5242 
5243  cpl_bivector *wavepix_used;
5244 
5245 
5246  /*
5247  * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
5248  */
5249 
5250  pixel = cpl_bivector_get_x(pixwav);
5251  wavel = cpl_bivector_get_y(pixwav);
5252 
5253  wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
5254 
5255  dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err,
5256  &wavepix_used);
5257 
5258  cpl_bivector_unwrap_vectors(wavpix);
5259 
5260  cpl_bivector_delete(wavepix_used);
5261 
5262  return dds;
5263 
5264 }
5265 
5266 
5289 cpl_bivector *mos_find_peaks(const float *spectrum, int length,
5290  cpl_vector *lines, cpl_polynomial *ids,
5291  double refwave, int sradius)
5292 {
5293  const char *func = "mos_find_peaks";
5294 
5295  double *data;
5296  double *d_pixel;
5297  double *d_wavel;
5298  float pos;
5299  int nlines;
5300  int pixel;
5301  int i, j;
5302 
5303 
5304  if (spectrum == NULL || lines == NULL || ids == NULL) {
5305  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
5306  return NULL;
5307  }
5308 
5309  nlines = cpl_vector_get_size(lines);
5310 
5311  if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
5312  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5313  return NULL;
5314  }
5315 
5316  d_wavel = cpl_malloc(nlines * sizeof(double));
5317  d_pixel = cpl_malloc(nlines * sizeof(double));
5318 
5319  data = cpl_vector_get_data(lines);
5320 
5321  for (i = 0, j = 0; i < nlines; i++) {
5322  pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
5323  if (pixel < 0 || pixel - sradius < 0 || pixel + sradius >= length)
5324  continue;
5325  if (peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1) == 0) {
5326  pos += pixel - sradius;
5327  d_pixel[j] = pos;
5328  d_wavel[j] = data[i];
5329  j++;
5330  }
5331  }
5332 
5333  if (j > 0) {
5334  return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
5335  cpl_vector_wrap(j, d_wavel));
5336  }
5337  else {
5338  cpl_free(d_wavel);
5339  cpl_free(d_pixel);
5340  cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
5341  return NULL;
5342  }
5343 }
5344 
5345 
5469 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image,
5470  cpl_vector *lines,
5471  double dispersion, float level,
5472  int sradius, int order,
5473  double reject, double refwave,
5474  double *wavestart, double *waveend,
5475  int *nlines, double *error,
5476  cpl_table *idscoeff,
5477  cpl_image *calibration,
5478  cpl_image *residuals,
5479  cpl_table *restable,
5480  cpl_mask *refmask,
5481  cpl_table *detected_lines)
5482 {
5483 
5484  const char *func = "mos_wavelength_calibration_raw";
5485 
5486  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
5487  /* Max order is 5 */
5488 
5489  double tolerance = 20.0; /* Probably forever... */
5490  int step = 10; /* Compute restable every "step" rows */
5491 
5492  char name[MAX_COLNAME];
5493  cpl_image *resampled;
5494  cpl_bivector *output;
5495  cpl_bivector *new_output;
5496  cpl_vector *peaks;
5497  cpl_vector *wavel;
5498  cpl_polynomial *ids;
5499  cpl_polynomial *lin;
5500  cpl_matrix *kernel;
5501  double ids_err;
5502  double max_disp, min_disp;
5503  double *line;
5504  double firstLambda, lastLambda, lambda;
5505  double value, wave, pixe;
5506  cpl_binary *mdata;
5507  const float *sdata;
5508  float *rdata;
5509  float *idata;
5510  float *ddata;
5511  float v1, v2, vi;
5512  float fpixel;
5513  int *have_it;
5514  int pixstart, pixend;
5515  int extrapolation;
5516  int nref;
5517  int nl, nx, ny, pixel;
5518  int countLines, usedLines;
5519  int uorder;
5520  int in, first, last;
5521  int width, uradius;
5522  int i, j;
5523  int null;
5524  cpl_size k;
5525 
5526 
5527  if (dispersion == 0.0) {
5528  cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
5529  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5530  return NULL;
5531  }
5532 
5533  if (dispersion < 0.0) {
5534  cpl_msg_error(func, "The expected dispersion must be positive");
5535  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5536  return NULL;
5537  }
5538 
5539  max_disp = dispersion + dispersion * tolerance / 100;
5540  min_disp = dispersion - dispersion * tolerance / 100;
5541 
5542  if (order < 1) {
5543  cpl_msg_error(func, "The order of the fitting polynomial "
5544  "must be at least 1");
5545  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
5546  return NULL;
5547  }
5548 
5549  if (image == NULL || lines == NULL) {
5550  cpl_msg_error(func, "Both spectral exposure and reference line "
5551  "catalog are required in input");
5552  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
5553  return NULL;
5554  }
5555 
5556  nx = cpl_image_get_size_x(image);
5557  ny = cpl_image_get_size_y(image);
5558  sdata = cpl_image_get_data_float_const(image);
5559 
5560  nref = cpl_vector_get_size(lines);
5561  line = cpl_vector_get_data(lines);
5562 
5563  if (*wavestart < 1.0 && *waveend < 1.0) {
5564  firstLambda = line[0];
5565  lastLambda = line[nref-1];
5566  extrapolation = (lastLambda - firstLambda) / 10;
5567  firstLambda -= extrapolation;
5568  lastLambda += extrapolation;
5569  *wavestart = firstLambda;
5570  *waveend = lastLambda;
5571  }
5572  else {
5573  firstLambda = *wavestart;
5574  lastLambda = *waveend;
5575  }
5576 
5577  nl = (lastLambda - firstLambda) / dispersion;
5578  resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
5579  rdata = cpl_image_get_data_float(resampled);
5580 
5581  if (calibration)
5582  idata = cpl_image_get_data_float(calibration);
5583 
5584  if (residuals)
5585  ddata = cpl_image_get_data_float(residuals);
5586 
5587  if (idscoeff)
5588  for (j = 0; j <= order; j++)
5589  cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
5590 
5591  if (restable) {
5592  cpl_table_set_size(restable, nref);
5593  cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
5594  cpl_table_copy_data_double(restable, "wavelength", line);
5595  for (i = 0; i < ny; i += step) {
5596  snprintf(name, MAX_COLNAME, "r%d", i);
5597  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
5598  snprintf(name, MAX_COLNAME, "d%d", i);
5599  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
5600  snprintf(name, MAX_COLNAME, "p%d", i);
5601  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
5602  }
5603  }
5604 
5605  if (detected_lines) {
5606  cpl_table_set_size(detected_lines, 0);
5607  cpl_table_new_column(detected_lines, "xpos", CPL_TYPE_DOUBLE);
5608  cpl_table_new_column(detected_lines, "ypos", CPL_TYPE_DOUBLE);
5609  cpl_table_new_column(detected_lines, "xpos_iter", CPL_TYPE_DOUBLE);
5610  cpl_table_new_column(detected_lines, "ypos_iter", CPL_TYPE_DOUBLE);
5611  cpl_table_new_column(detected_lines, "peak_flux", CPL_TYPE_DOUBLE);
5612  cpl_table_new_column(detected_lines, "wave_ident", CPL_TYPE_DOUBLE);
5613  cpl_table_new_column(detected_lines, "wave_ident_iter", CPL_TYPE_DOUBLE);
5614  cpl_table_new_column(detected_lines, "xpos_fit_rect_wavecal", CPL_TYPE_DOUBLE);
5615  cpl_table_new_column(detected_lines, "res_xpos", CPL_TYPE_DOUBLE);
5616  cpl_table_new_column(detected_lines, "fit_used", CPL_TYPE_INT);
5617  }
5618 
5619  /*
5620  * Here is the real thing: detecting and identifying peaks,
5621  * and then fit the transformation from wavelength to pixel
5622  * and from pixel to wavelength.
5623  */
5624 
5625  for (i = 0; i < ny; i++) {
5626  width = mos_lines_width(sdata + i*nx, nx);
5627  if (sradius > 0) {
5628  if (width > sradius) {
5629  uradius = width;
5630  }
5631  else {
5632  uradius = sradius;
5633  }
5634  }
5635  if (width < 5)
5636  width = 5;
5637  peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
5638  if (peaks) {
5639  peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
5640  }
5641  if (peaks) {
5642  output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05);
5643  if (output) {
5644  cpl_bivector * peaks_ident_used_fit;
5645  countLines = cpl_bivector_get_size(output);
5646  if (countLines < 4) {
5647  cpl_bivector_delete(output);
5648  cpl_vector_delete(peaks);
5649  if (nlines)
5650  nlines[i] = 0;
5651  if (error)
5652  error[i] = 0.0;
5653  continue;
5654  }
5655 
5656  /*
5657  * Set reference wavelength as zero point
5658  */
5659 
5660  wavel = cpl_bivector_get_y(output);
5661  cpl_vector_subtract_scalar(wavel, refwave);
5662 
5663  uorder = countLines / 2 - 1;
5664  if (uorder > order)
5665  uorder = order;
5666 
5667 /* This part is now commented out. In case the first-guess iteration
5668  * was requested, the first fit was made with a lower polynomial degree:
5669  * more robust, and still accurate enough to be used as a first-guess.
5670 
5671  if (sradius > 0 && uorder > 2)
5672  --uorder;
5673 
5674  * End of commented part */
5675 
5676  ids = mos_poly_wav2pix(output, uorder, reject,
5677  2 * (uorder + 1), &usedLines,
5678  &ids_err, &peaks_ident_used_fit);
5679 
5680  if (ids == NULL) {
5681  cpl_bivector_delete(output);
5682  cpl_vector_delete(peaks);
5683  if (nlines)
5684  nlines[i] = 0;
5685  if (error)
5686  error[i] = 0.0;
5687  cpl_error_reset();
5688  continue;
5689  }
5690 
5691  if (idscoeff) {
5692 
5693  /*
5694  * Write it anyway, even in case a first-guess based
5695  * solution will be searched afterwards: in case of
5696  * failure, the "blind" solution is kept.
5697  */
5698 
5699  for (k = 0; k <= order; k++) {
5700  if (k > uorder) {
5701  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
5702  }
5703  else {
5704  cpl_table_set_double(idscoeff, clab[k], i,
5705  cpl_polynomial_get_coeff(ids, &k));
5706  }
5707  }
5708  }
5709 
5710  if(detected_lines)
5711  {
5712  cpl_size newlines = cpl_vector_get_size(peaks);
5713  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
5714  cpl_table_set_size(detected_lines, oldsize + newlines);
5715  for(cpl_size iline = 0; iline < newlines; ++iline)
5716  {
5717  cpl_table_set_double(detected_lines, "xpos",
5718  oldsize + iline, cpl_vector_get(peaks, iline) + 1);
5719  cpl_table_set_double(detected_lines, "ypos",
5720  oldsize + iline, (double)i + 1);
5721  cpl_table_set_double(detected_lines, "peak_flux",
5722  oldsize + iline,
5723  sdata[i*nx+(int)(cpl_vector_get(peaks, iline)+0.5)]);
5724  cpl_table_set_int(detected_lines,
5725  "fit_used",
5726  oldsize + iline, 0);
5727  }
5728  }
5729 
5730  //Fill the line identification information in
5731  //the detected_lines table
5732  if(detected_lines)
5733  {
5734  cpl_size nidentlines = cpl_bivector_get_size(output);
5735  cpl_size ndetectlines = cpl_vector_get_size(peaks);
5736  cpl_size totalsize = cpl_table_get_nrow(detected_lines);
5737  for(cpl_size idline = 0; idline < nidentlines; ++idline)
5738  {
5739  for(cpl_size detline = 0; detline < ndetectlines; ++detline)
5740  {
5741  if(cpl_vector_get(peaks, detline) ==
5742  cpl_bivector_get_x_data(output)[idline])
5743  {
5744  cpl_size table_pos = totalsize - ndetectlines + detline;
5745  double wave_ident = cpl_bivector_get_y_data(output)[idline] + refwave;
5746  double xpix_fit = cpl_polynomial_eval_1d(ids,
5747  wave_ident - refwave, NULL);
5748  double xpos_det = cpl_table_get_double(detected_lines,
5749  "xpos",
5750  table_pos, &null);
5751  cpl_table_set_double(detected_lines,
5752  "wave_ident",
5753  table_pos,
5754  wave_ident);
5755  cpl_table_set_double(detected_lines,
5756  "xpos_fit_rect_wavecal",
5757  table_pos,
5758  xpix_fit + 1);
5759  cpl_table_set_double(detected_lines,
5760  "res_xpos",
5761  table_pos,
5762  xpos_det - xpix_fit - 1);
5763  cpl_table_set_int(detected_lines,
5764  "fit_used",
5765  table_pos, 0);
5766  for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
5767  {
5768  if(cpl_bivector_get_x_data(output)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
5769  cpl_table_set_int(detected_lines,
5770  "fit_used",
5771  table_pos, 1);
5772  }
5773  }
5774  }
5775  }
5776  }
5777 
5778  if (sradius > 0) {
5779  cpl_bivector * peaks_ident_used_fit;
5780 
5781  /*
5782  * Use ids as a first-guess
5783  */
5784 
5785  new_output = mos_find_peaks(sdata + i*nx, nx, lines,
5786  ids, refwave, uradius);
5787 
5788  if (new_output) {
5789  cpl_bivector_delete(output);
5790  output = new_output;
5791  }
5792  else
5793  cpl_error_reset();
5794 
5795 
5796  cpl_polynomial_delete(ids);
5797 
5798  countLines = cpl_bivector_get_size(output);
5799 
5800  if (countLines < 4) {
5801  cpl_bivector_delete(output);
5802  cpl_vector_delete(peaks);
5803 
5804  /*
5805  * With the following code a decision is taken:
5806  * if using the first-guess gives no results,
5807  * then also the "blind" solution is rejected.
5808  */
5809 
5810  if (nlines)
5811  nlines[i] = 0;
5812  if (error)
5813  error[i] = 0.0;
5814  if (idscoeff)
5815  for (k = 0; k <= order; k++)
5816  cpl_table_set_invalid(idscoeff, clab[k], i);
5817  continue;
5818  }
5819 
5820  wavel = cpl_bivector_get_y(output);
5821  cpl_vector_subtract_scalar(wavel, refwave);
5822 
5823  uorder = countLines / 2 - 1;
5824  if (uorder > order)
5825  uorder = order;
5826 
5827  ids = mos_poly_wav2pix(output, uorder, reject,
5828  2 * (uorder + 1), &usedLines,
5829  &ids_err, &peaks_ident_used_fit);
5830 
5831  if (ids == NULL) {
5832  cpl_bivector_delete(output);
5833  cpl_vector_delete(peaks);
5834 
5835  /*
5836  * With the following code a decision is taken:
5837  * if using the first-guess gives no results,
5838  * then also the "blind" solution is rejected.
5839  */
5840 
5841  if (nlines)
5842  nlines[i] = 0;
5843  if (error)
5844  error[i] = 0.0;
5845  if (idscoeff)
5846  for (k = 0; k <= order; k++)
5847  cpl_table_set_invalid(idscoeff, clab[k], i);
5848  cpl_error_reset();
5849  continue;
5850  }
5851 
5852  if (idscoeff) {
5853  for (k = 0; k <= order; k++) {
5854  if (k > uorder) {
5855  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
5856  }
5857  else {
5858  cpl_table_set_double(idscoeff, clab[k], i,
5859  cpl_polynomial_get_coeff(ids, &k));
5860  }
5861  }
5862  }
5863 
5864 
5865  if(detected_lines)
5866  {
5867  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
5868  cpl_size nidentlines = cpl_bivector_get_size(output);
5869  cpl_table_set_size(detected_lines, oldsize + nidentlines);
5870  for(cpl_size idline = 0; idline < nidentlines ; ++idline)
5871  {
5872  double wave_ident = cpl_bivector_get_y_data(output)[idline] + refwave;
5873  double xpix_fit = cpl_polynomial_eval_1d(ids,
5874  wave_ident - refwave, NULL);
5875  cpl_table_set_double(detected_lines, "xpos_iter",
5876  oldsize + idline, cpl_bivector_get_x_data(output)[idline] + 1);
5877  cpl_table_set_double(detected_lines, "ypos_iter",
5878  oldsize + idline, (double)i + 1);
5879  cpl_table_set_double(detected_lines, "peak_flux",
5880  oldsize + idline,
5881  sdata[i*nx+(int)(cpl_bivector_get_x_data(output)[idline]+0.5)]);
5882  cpl_table_set_double(detected_lines, "wave_ident_iter",
5883  oldsize + idline, wave_ident);
5884  cpl_table_set_double(detected_lines, "xpos_fit_rect_wavecal",
5885  oldsize + idline, xpix_fit + 1);
5886  }
5887  }
5888 
5889  } /* End of "use ids as a first-guess" */
5890 
5891  if (nlines)
5892  nlines[i] = usedLines;
5893  if (error)
5894  error[i] = ids_err / sqrt(usedLines/(uorder + 1));
5895 
5896  pixstart = cpl_polynomial_eval_1d(ids,
5897  cpl_bivector_get_y_data(output)[0], NULL);
5898  pixend = cpl_polynomial_eval_1d(ids,
5899  cpl_bivector_get_y_data(output)[countLines-1], NULL);
5900  extrapolation = (pixend - pixstart) / 5;
5901  pixstart -= extrapolation;
5902  pixend += extrapolation;
5903  if (pixstart < 0)
5904  pixstart = 0;
5905  if (pixend > nx)
5906  pixend = nx;
5907 
5908  /*
5909  * Wavelength calibrated image (if requested):
5910  */
5911 
5912  if (calibration) {
5913  for (j = pixstart; j < pixend; j++) {
5914  (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda,
5915  lastLambda, refwave,
5916  j);
5917  }
5918  }
5919 
5920  /*
5921  * Resampled image:
5922  */
5923 
5924  for (j = 0; j < nl; j++) {
5925  lambda = firstLambda + j * dispersion;
5926  fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave,
5927  NULL);
5928  pixel = fpixel;
5929  if (pixel >= 0 && pixel < nx-1) {
5930  v1 = (sdata + i*nx)[pixel];
5931  v2 = (sdata + i*nx)[pixel+1];
5932  vi = v1 + (v2-v1)*(fpixel-pixel);
5933  (rdata + i*nl)[j] = vi;
5934  }
5935  }
5936 
5937  /*
5938  * Residuals image
5939  */
5940 
5941  if (residuals || (restable && !(i%step))) {
5942  if (restable && !(i%step)) {
5943  lin = cpl_polynomial_new(1);
5944  for (k = 0; k < 2; k++)
5945  cpl_polynomial_set_coeff(lin, &k,
5946  cpl_polynomial_get_coeff(ids, &k));
5947  }
5948  for (j = 0; j < countLines; j++) {
5949  pixe = cpl_bivector_get_x_data(output)[j];
5950  wave = cpl_bivector_get_y_data(output)[j];
5951  value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
5952  if (residuals) {
5953  pixel = pixe + 0.5;
5954  (ddata + i*nx)[pixel] = value;
5955  }
5956  if (restable && !(i%step)) {
5957  for (k = 0; k < nref; k++) {
5958  if (fabs(line[k] - refwave - wave) < 0.1) {
5959  snprintf(name, MAX_COLNAME, "r%d", i);
5960  cpl_table_set_double(restable, name,
5961  k, value);
5962  value = pixe
5963  - cpl_polynomial_eval_1d(lin, wave,
5964  NULL);
5965  snprintf(name, MAX_COLNAME, "d%d", i);
5966  cpl_table_set_double(restable, name,
5967  k, value);
5968  snprintf(name, MAX_COLNAME, "p%d", i);
5969  cpl_table_set_double(restable, name,
5970  k, pixe);
5971  break;
5972  }
5973  }
5974  }
5975  }
5976  if (restable && !(i%step)) {
5977  cpl_polynomial_delete(lin);
5978  }
5979  }
5980 
5981  /*
5982  * Mask at reference wavelength
5983  */
5984 
5985  if (refmask) {
5986  mdata = cpl_mask_get_data(refmask);
5987  pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
5988  if (pixel - 1 >= 0 && pixel + 1 < nx) {
5989  mdata[pixel-1 + i*nx] = CPL_BINARY_1;
5990  mdata[pixel + i*nx] = CPL_BINARY_1;
5991  mdata[pixel+1 + i*nx] = CPL_BINARY_1;
5992  }
5993  }
5994 
5995  cpl_polynomial_delete(ids);
5996  cpl_bivector_delete(output);
5997  }
5998  cpl_vector_delete(peaks);
5999  }
6000  }
6001 
6002  if (refmask) {
6003  kernel = cpl_matrix_new(3, 3);
6004  cpl_matrix_set(kernel, 0, 1, 1.0);
6005  cpl_matrix_set(kernel, 1, 1, 1.0);
6006  cpl_matrix_set(kernel, 2, 1, 1.0);
6007 
6008  cpl_mask_dilation(refmask, kernel);
6009  cpl_mask_erosion(refmask, kernel);
6010  cpl_mask_erosion(refmask, kernel);
6011  cpl_mask_dilation(refmask, kernel);
6012 
6013  cpl_matrix_delete(kernel);
6014 
6015  /*
6016  * Fill possible gaps
6017  */
6018 
6019  mdata = cpl_mask_get_data(refmask);
6020  have_it = cpl_calloc(ny, sizeof(int));
6021 
6022  for (i = 0; i < ny; i++, mdata += nx) {
6023  for (j = 0; j < nx; j++) {
6024  if (mdata[j] == CPL_BINARY_1) {
6025  have_it[i] = j;
6026  break;
6027  }
6028  }
6029  }
6030 
6031  mdata = cpl_mask_get_data(refmask);
6032  in = 0;
6033  first = last = 0;
6034 
6035  for (i = 0; i < ny; i++) {
6036  if (have_it[i]) {
6037  if (!in) {
6038  in = 1;
6039  if (first) {
6040  last = i;
6041  if (abs(have_it[first] - have_it[last]) < 3) {
6042  for (j = first; j < last; j++) {
6043  mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
6044  mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
6045  mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
6046  }
6047  }
6048  }
6049  }
6050  }
6051  else {
6052  if (in) {
6053  in = 0;
6054  first = i - 1;
6055  }
6056  }
6057  }
6058 
6059  cpl_free(have_it);
6060 
6061  }
6062 
6063 /*
6064  for (i = 0; i < ny; i++) {
6065  if (nlines[i] == 0) {
6066  for (k = 0; k <= order; k++) {
6067  cpl_table_set_invalid(idscoeff, clab[k], i);
6068  }
6069  }
6070  }
6071 */
6072 
6073  return resampled;
6074 }
6075 
6076 
6098 /*
6099 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
6100 {
6101  const char *func = "mos_locate_spectra_bis";
6102 
6103  cpl_apertures *slits;
6104  cpl_image *labimage;
6105  cpl_image *refimage;
6106  cpl_binary *mdata;
6107  cpl_table *slitpos;
6108  cpl_propertylist *sort_col;
6109  int nslits;
6110  int *have_it;
6111  int in, first, last;
6112  int i, j;
6113 
6114 
6115  if (mask == NULL) {
6116  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
6117  return NULL;
6118  }
6119 
6120  nx = cpl_mask_get_size_x(mask);
6121  ny = cpl_mask_get_size_y(mask);
6122 
6123  mdata = cpl_mask_get_data(refmask);
6124  have_it = cpl_calloc(ny, sizeof(int));
6125 
6126  for (i = 0; i < ny; i++, mdata += nx) {
6127  for (j = 0; j < nx; j++) {
6128  if (mdata[j] == CPL_BINARY_1) {
6129  have_it[i] = j + 1;
6130  break;
6131  }
6132  }
6133  }
6134 
6135  mdata = cpl_mask_get_data(refmask);
6136  in = 0;
6137  first = last = 0;
6138  nslits = 0;
6139 
6140  for (i = 0; i < ny; i++) {
6141  if (have_it[i]) {
6142  if (in) {
6143  if (i) {
6144  if (abs(have_it[i] - have_it[i-1]) > 3) {
6145  nslits++;
6146  }
6147  }
6148  }
6149  else {
6150  in = 1;
6151  nslits++;
6152  }
6153  }
6154  else {
6155  if (in) {
6156  in = 0;
6157  }
6158  }
6159  }
6160 }
6161 */
6162 
6163 
6185 cpl_table *mos_locate_spectra(cpl_mask *mask)
6186 {
6187  const char *func = "mos_locate_spectra";
6188 
6189  cpl_apertures *slits;
6190  cpl_image *labimage;
6191  cpl_image *refimage;
6192  cpl_table *slitpos;
6193  cpl_propertylist *sort_col;
6194  cpl_size nslits;
6195  int i;
6196 
6197 
6198  if (mask == NULL) {
6199  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
6200  return NULL;
6201  }
6202 
6203  labimage = cpl_image_labelise_mask_create(mask, &nslits);
6204 
6205  if (nslits < 1) {
6206  cpl_image_delete(labimage);
6207  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6208  return NULL;
6209  }
6210 
6211  refimage = cpl_image_new_from_mask(mask);
6212 
6213  slits = cpl_apertures_new_from_image(refimage, labimage);
6214 
6215  cpl_image_delete(labimage);
6216  cpl_image_delete(refimage);
6217 
6218  nslits = cpl_apertures_get_size(slits); /* Overwriting nslits - safer! */
6219  if (nslits < 1) {
6220  cpl_apertures_delete(slits);
6221  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6222  return NULL;
6223  }
6224 
6225  slitpos = cpl_table_new(nslits);
6226  cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
6227  cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
6228  cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
6229  cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
6230  cpl_table_set_column_unit(slitpos, "xtop", "pixel");
6231  cpl_table_set_column_unit(slitpos, "ytop", "pixel");
6232  cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
6233  cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
6234 
6235  for (i = 0; i < nslits; i++) {
6236  cpl_table_set_double(slitpos, "xtop", i,
6237  cpl_apertures_get_top_x(slits, i+1) - 1);
6238  cpl_table_set_double(slitpos, "ytop", i,
6239  cpl_apertures_get_top(slits, i+1));
6240  cpl_table_set_double(slitpos, "xbottom", i,
6241  cpl_apertures_get_bottom_x(slits, i+1) - 1);
6242  cpl_table_set_double(slitpos, "ybottom", i,
6243  cpl_apertures_get_bottom(slits, i+1));
6244  }
6245 
6246  cpl_apertures_delete(slits);
6247 
6248  sort_col = cpl_propertylist_new();
6249  cpl_propertylist_append_bool(sort_col, "ytop", 1);
6250  cpl_table_sort(slitpos, sort_col);
6251  cpl_propertylist_delete(sort_col);
6252 
6253  return slitpos;
6254 
6255 }
6256 
6257 
6273 cpl_error_code mos_validate_slits(cpl_table *slits)
6274 {
6275  const char *func = "mos_validate_slits";
6276 
6277 
6278  if (slits == NULL)
6279  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
6280 
6281  if (1 != cpl_table_has_column(slits, "xtop"))
6282  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6283 
6284  if (1 != cpl_table_has_column(slits, "ytop"))
6285  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6286 
6287  if (1 != cpl_table_has_column(slits, "xbottom"))
6288  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6289 
6290  if (1 != cpl_table_has_column(slits, "ybottom"))
6291  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
6292 
6293  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
6294  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6295 
6296  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
6297  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6298 
6299  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
6300  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6301 
6302  if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
6303  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
6304 
6305  return CPL_ERROR_NONE;
6306 }
6307 
6308 
6337 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
6338 {
6339  const char *func = "mos_rotate_slits";
6340 
6341  cpl_error_code error;
6342  char aux_name[] = "_0";
6343  int i;
6344 
6345 
6346  rotation %= 4;
6347  if (rotation < 0)
6348  rotation += 4;
6349 
6350  if (rotation == 0)
6351  return CPL_ERROR_NONE;
6352 
6353  error = mos_validate_slits(slits);
6354  if (error)
6355  return cpl_error_set(func, error);
6356 
6357  if (rotation == 1 || rotation == 3) {
6358 
6359  /*
6360  * Swap x and y column names
6361  */
6362 
6363  for (i = 0; i < 77; i++)
6364  if (1 == cpl_table_has_column(slits, aux_name))
6365  aux_name[1]++;
6366  if (1 == cpl_table_has_column(slits, aux_name))
6367  return cpl_error_set(func, CPL_ERROR_CONTINUE);
6368  cpl_table_name_column(slits, "xtop", aux_name);
6369  cpl_table_name_column(slits, "ytop", "xtop");
6370  cpl_table_name_column(slits, aux_name, "ytop");
6371  cpl_table_name_column(slits, "xbottom", aux_name);
6372  cpl_table_name_column(slits, "ybottom", "xbottom");
6373  cpl_table_name_column(slits, aux_name, "ybottom");
6374  }
6375 
6376  if (rotation == 1 || rotation == 2) {
6377  cpl_table_multiply_scalar(slits, "xtop", -1.0);
6378  cpl_table_multiply_scalar(slits, "xbottom", -1.0);
6379  cpl_table_add_scalar(slits, "xtop", nx);
6380  cpl_table_add_scalar(slits, "xbottom", nx);
6381  }
6382 
6383  if (rotation == 3 || rotation == 2) {
6384  cpl_table_multiply_scalar(slits, "ytop", -1.0);
6385  cpl_table_multiply_scalar(slits, "ybottom", -1.0);
6386  cpl_table_add_scalar(slits, "ytop", ny);
6387  cpl_table_add_scalar(slits, "ybottom", ny);
6388  }
6389 
6390  return CPL_ERROR_NONE;
6391 }
6392 
6393 
6451 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
6452  cpl_table *global)
6453 {
6454  cpl_array *top_ident = NULL;;
6455  cpl_array *bot_ident = NULL;;
6456  cpl_matrix *mdata;
6457  cpl_matrix *mpattern;
6458  cpl_matrix *top_data;
6459  cpl_matrix *top_pattern;
6460  cpl_matrix *top_mdata;
6461  cpl_matrix *top_mpattern;
6462  cpl_matrix *bot_data;
6463  cpl_matrix *bot_pattern;
6464  cpl_matrix *bot_mdata;
6465  cpl_matrix *bot_mpattern;
6466  cpl_propertylist *sort_col;
6467  double *xtop;
6468  double *ytop;
6469  double *xmtop;
6470  double *ymtop;
6471  double *xbot;
6472  double *ybot;
6473  double *xmbot;
6474  double *ymbot;
6475  double top_scale, bot_scale;
6476  double angle, top_angle, bot_angle;
6477  double xmse, ymse;
6478  double xrms, top_xrms, bot_xrms;
6479  double yrms, top_yrms, bot_yrms;
6480  int nslits;
6481  int nmaskslits, use_pattern;
6482  int found_slits, found_slits_top, found_slits_bot;
6483  int i;
6484  cpl_table *positions;
6485  cpl_error_code error;
6486 
6487  cpl_vector *point;
6488  double *dpoint;
6489  cpl_vector *xpos;
6490  cpl_vector *ypos;
6491  cpl_vector *xmpos;
6492  cpl_vector *ympos;
6493  cpl_bivector *mpos;
6494  cpl_polynomial *xpoly = NULL;
6495  cpl_polynomial *ypoly = NULL;
6496  cpl_polynomial *top_xpoly = NULL;
6497  cpl_polynomial *top_ypoly = NULL;
6498  cpl_polynomial *bot_xpoly = NULL;
6499  cpl_polynomial *bot_ypoly = NULL;
6500 
6501  char *msg_multiplex = " ";
6502 
6503 
6504  error = mos_validate_slits(slits);
6505  if (error) {
6506  cpl_msg_error(cpl_func, "CCD slits table validation: %s",
6507  cpl_error_get_message());
6508  cpl_error_set(cpl_func, error);
6509  return NULL;
6510  }
6511 
6512  error = mos_validate_slits(maskslits);
6513  if (error) {
6514  cpl_msg_error(cpl_func, "Mask slits table validation: %s",
6515  cpl_error_get_message());
6516  cpl_error_set(cpl_func, error);
6517  return NULL;
6518  }
6519 
6520  if (1 != cpl_table_has_column(maskslits, "slit_id")) {
6521  cpl_msg_error(cpl_func, "Missing slits identifiers");
6522  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
6523  return NULL;
6524  }
6525 
6526  if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
6527  cpl_msg_error(cpl_func, "Wrong type used for slits identifiers");
6528  cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE);
6529  return NULL;
6530  }
6531 
6532  nslits = cpl_table_get_nrow(slits);
6533  nmaskslits = cpl_table_get_nrow(maskslits);
6534 
6535  if (nslits == 0 || nmaskslits == 0) {
6536  cpl_msg_error(cpl_func, "Empty slits table");
6537  cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
6538  return NULL;
6539  }
6540 
6541  if (nslits > 100 && mos_multiplex < 0) {
6542  cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching...");
6543  positions = mos_identify_slits_fast(slits, maskslits, global);
6544  if (positions == NULL)
6545  cpl_error_set_where(cpl_func);
6546  return positions;
6547  }
6548 
6549  /*
6550  * Guarantee that both input tables are sorted in the same way
6551  */
6552 
6553  sort_col = cpl_propertylist_new();
6554  cpl_propertylist_append_bool(sort_col, "ytop", 1);
6555  cpl_table_sort(slits, sort_col);
6556  cpl_table_sort(maskslits, sort_col);
6557  cpl_propertylist_delete(sort_col);
6558 
6559  /*
6560  * First we handle all the special cases (too few slits...)
6561  */
6562 
6563  if (nslits < 3 && nmaskslits > nslits) {
6564 
6565  /*
6566  * If there are just 1 or 2 slits on the CCD, and more on the
6567  * mask, the ambiguity cannot be solved, and an error is returned.
6568  * This is a case that must be solved with a first-guess relation
6569  * between mask and CCD.
6570  */
6571 
6572  if (nslits > 1)
6573  cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits "
6574  "with the %d mask slits: process will continue "
6575  "using the detected CCD slits positions", nslits,
6576  nmaskslits);
6577  else
6578  cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with "
6579  "the %d mask slits: process will continue using "
6580  "the detected CCD slit position", nmaskslits);
6581  return NULL;
6582  }
6583 
6584  if (nmaskslits < 3 && nslits > nmaskslits) {
6585 
6586  /*
6587  * If there are less than 3 slits on the mask the ambiguity cannot
6588  * be solved, and an error is returned. This is a case that must
6589  * be solved with a first-guess relation between mask and CCD.
6590  */
6591 
6592  cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with "
6593  "the %d mask slits: process will continue using "
6594  "the detected CCD slits positions", nslits,
6595  nmaskslits);
6596  return NULL;
6597  }
6598 
6599  /*
6600  * Pattern matching related operations begin here. Two pattern
6601  * matching will be run, one based on the "top" and another one
6602  * based on the "bottom" slit coordinates. The one with the
6603  * smallest rms in the Y coordinate will be chosen.
6604  */
6605 
6606  xtop = cpl_table_get_data_double(slits, "xtop");
6607  ytop = cpl_table_get_data_double(slits, "ytop");
6608  xmtop = cpl_table_get_data_double(maskslits, "xtop");
6609  ymtop = cpl_table_get_data_double(maskslits, "ytop");
6610 
6611  xbot = cpl_table_get_data_double(slits, "xbottom");
6612  ybot = cpl_table_get_data_double(slits, "ybottom");
6613  xmbot = cpl_table_get_data_double(maskslits, "xbottom");
6614  ymbot = cpl_table_get_data_double(maskslits, "ybottom");
6615 
6616  top_data = cpl_matrix_new(2, nslits);
6617  top_pattern = cpl_matrix_new(2, nmaskslits);
6618  bot_data = cpl_matrix_new(2, nslits);
6619  bot_pattern = cpl_matrix_new(2, nmaskslits);
6620 
6621  for (i = 0; i < nslits; i++)
6622  cpl_matrix_set(top_data, 0, i, xtop[i]);
6623 
6624  for (i = 0; i < nslits; i++)
6625  cpl_matrix_set(top_data, 1, i, ytop[i]);
6626 
6627  for (i = 0; i < nmaskslits; i++)
6628  cpl_matrix_set(top_pattern, 0, i, xmtop[i]);
6629 
6630  for (i = 0; i < nmaskslits; i++)
6631  cpl_matrix_set(top_pattern, 1, i, ymtop[i]);
6632 
6633  for (i = 0; i < nslits; i++)
6634  cpl_matrix_set(bot_data, 0, i, xbot[i]);
6635 
6636  for (i = 0; i < nslits; i++)
6637  cpl_matrix_set(bot_data, 1, i, ybot[i]);
6638 
6639  for (i = 0; i < nmaskslits; i++)
6640  cpl_matrix_set(bot_pattern, 0, i, xmbot[i]);
6641 
6642  for (i = 0; i < nmaskslits; i++)
6643  cpl_matrix_set(bot_pattern, 1, i, ymbot[i]);
6644 
6645  if (nmaskslits > nslits)
6646  use_pattern = nslits;
6647  else
6648  use_pattern = nmaskslits;
6649 
6650  top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern,
6651  use_pattern, 0.0, 0.1, 5, &top_mdata,
6652  &top_mpattern, &top_scale, &top_angle);
6653 
6654  bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern,
6655  use_pattern, 0.0, 0.1, 5, &bot_mdata,
6656  &bot_mpattern, &bot_scale, &bot_angle);
6657  cpl_matrix_delete(top_data);
6658  cpl_matrix_delete(top_pattern);
6659  cpl_matrix_delete(bot_data);
6660  cpl_matrix_delete(bot_pattern);
6661 
6662  if (top_ident == NULL && bot_ident == NULL) {
6663  cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match "
6664  "the %d found CCD slits with the %d mask slits: "
6665  "process will continue using the detected CCD "
6666  "slits positions", nslits, nmaskslits);
6667  return NULL;
6668  }
6669 
6670  found_slits_top = 0;
6671  found_slits_bot = 0;
6672  if (top_ident && bot_ident) {
6673  cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm",
6674  (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale));
6675  cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees",
6676  (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle));
6677  if (fabs(top_angle) < fabs(bot_angle))
6678  angle = fabs(top_angle);
6679  else
6680  angle = fabs(bot_angle);
6681  found_slits_top = cpl_matrix_get_ncol(top_mdata);
6682  found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
6683  }
6684  else if (top_ident) {
6685  cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale);
6686  cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle);
6687  angle = fabs(top_angle);
6688  found_slits_top = cpl_matrix_get_ncol(top_mdata);
6689  }
6690  else {
6691  cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale);
6692  cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle);
6693  angle = fabs(bot_angle);
6694  found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
6695  }
6696 
6697  cpl_array_delete(top_ident);
6698  cpl_array_delete(bot_ident);
6699 
6700  if (angle > 4.0) {
6701  cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation "
6702  "angle is expected to be around zero. This match is "
6703  "rejected: the process will continue using the %d "
6704  "detected CCD slits positions", nslits);
6705  return NULL;
6706  }
6707 
6708  found_slits = found_slits_top;
6709  if (found_slits < found_slits_bot)
6710  found_slits = found_slits_bot; /* Max value */
6711 
6712  if (found_slits < 4) {
6713  cpl_msg_warning(cpl_func,
6714  "Too few safely identified slits: %d out of %d "
6715  "candidates (%d expected). Process will continue "
6716  "using the detected CCD slits positions", found_slits,
6717  nslits, nmaskslits);
6718  return NULL;
6719  }
6720 
6721  cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d "
6722  "candidates\n(%d expected)", found_slits, nslits,
6723  nmaskslits);
6724 
6725  if (found_slits_top < 4)
6726  found_slits_top = 0;
6727 
6728  if (found_slits_bot < 4)
6729  found_slits_bot = 0;
6730 
6731  /*
6732  * Now for each set select the points of the identified slits, and fit
6733  * two bivariate polynomials to determine a first approximate relation
6734  * between positions on the mask and positions on the CCD.
6735  */
6736 
6737  for (i = 0; i < 2; i++) {
6738  cpl_size mindeg2d[] = {0, 0};
6739  cpl_size maxdeg2d[2];
6740  cpl_vector * fitresidual;
6741  if (i) {
6742  found_slits = found_slits_top;
6743  mdata = top_mdata;
6744  mpattern = top_mpattern;
6745  }
6746  else {
6747  found_slits = found_slits_bot;
6748  mdata = bot_mdata;
6749  mpattern = bot_mpattern;
6750  }
6751 
6752  if (found_slits == 0)
6753  continue;
6754  else if (found_slits < 10)
6755  maxdeg2d[0] = maxdeg2d[1] = 1;
6756  else
6757  maxdeg2d[0] = maxdeg2d[1] = 2;
6758 
6759  xpos = cpl_vector_wrap(found_slits,
6760  cpl_matrix_get_data(mdata) );
6761  ypos = cpl_vector_wrap(found_slits,
6762  cpl_matrix_get_data(mdata) + found_slits);
6763  xmpos = cpl_vector_wrap(found_slits,
6764  cpl_matrix_get_data(mpattern) );
6765  ympos = cpl_vector_wrap(found_slits,
6766  cpl_matrix_get_data(mpattern) + found_slits);
6767  mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
6768  fitresidual = cpl_vector_new(cpl_vector_get_size(xpos));
6769  xpoly = cpl_polynomial_new(2);
6770  cpl_polynomial_fit(xpoly, mpattern, NULL, xpos, NULL, CPL_FALSE, mindeg2d, maxdeg2d);
6771  cpl_vector_fill_polynomial_fit_residual(fitresidual, xpos, NULL, xpoly, mpattern, NULL);
6772  xmse = cpl_vector_product(fitresidual, fitresidual)
6773  / cpl_vector_get_size(fitresidual);
6774  ypoly = cpl_polynomial_new(2);
6775  cpl_polynomial_fit(ypoly, mpattern, NULL, ypos, NULL, CPL_FALSE, mindeg2d, maxdeg2d);
6776  cpl_vector_fill_polynomial_fit_residual(fitresidual, ypos, NULL, ypoly, mpattern, NULL);
6777  ymse = cpl_vector_product(fitresidual, fitresidual)
6778  / cpl_vector_get_size(fitresidual);
6779 
6780  cpl_bivector_unwrap_vectors(mpos);
6781  cpl_vector_unwrap(xpos);
6782  cpl_vector_unwrap(ypos);
6783  cpl_vector_unwrap(xmpos);
6784  cpl_vector_unwrap(ympos);
6785  cpl_matrix_delete(mdata);
6786  cpl_matrix_delete(mpattern);
6787  cpl_vector_delete(fitresidual);
6788 
6789  if (i) {
6790  top_xpoly = xpoly;
6791  top_ypoly = ypoly;
6792  top_xrms = sqrt(xmse*2*maxdeg2d[0]/(found_slits - 1));
6793  top_yrms = sqrt(ymse*2*maxdeg2d[0]/(found_slits - 1));
6794  }
6795  else {
6796  bot_xpoly = xpoly;
6797  bot_ypoly = ypoly;
6798  bot_xrms = sqrt(xmse*2*maxdeg2d[0]/(found_slits - 1));
6799  bot_yrms = sqrt(ymse*2*maxdeg2d[0]/(found_slits - 1));
6800  }
6801  }
6802 
6803  if (top_xpoly && bot_xpoly) {
6804  if (top_xrms < bot_xrms) { /* top X solution wins... */
6805  xrms = top_xrms;
6806  xpoly = top_xpoly;
6807  cpl_polynomial_delete(bot_xpoly);
6808  }
6809  else { /* bottom X solution wins... */
6810  xrms = bot_xrms;
6811  xpoly = bot_xpoly;
6812  cpl_polynomial_delete(top_xpoly);
6813  }
6814  }
6815  else if (top_xpoly) {
6816  xrms = top_xrms;
6817  xpoly = top_xpoly;
6818  }
6819  else {
6820  xrms = bot_xrms;
6821  xpoly = bot_xpoly;
6822  }
6823 
6824  if (top_ypoly && bot_ypoly) {
6825  if (top_yrms < bot_yrms) { /* top Y solution wins... */
6826  yrms = top_yrms;
6827  ypoly = top_ypoly;
6828  cpl_polynomial_delete(bot_ypoly);
6829  }
6830  else { /* bottom Y solution wins... */
6831  yrms = bot_yrms;
6832  ypoly = bot_ypoly;
6833  cpl_polynomial_delete(top_ypoly);
6834  }
6835  }
6836  else if (top_ypoly) {
6837  yrms = top_yrms;
6838  ypoly = top_ypoly;
6839  }
6840  else {
6841  yrms = bot_yrms;
6842  ypoly = bot_ypoly;
6843  }
6844 
6845  if (xpoly == NULL || ypoly == NULL) {
6846  cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the "
6847  "identified slits positions cannot be improved.");
6848  cpl_polynomial_delete(xpoly);
6849  cpl_polynomial_delete(ypoly);
6850  cpl_error_reset();
6851  return NULL;
6852  }
6853 
6854  cpl_msg_info(cpl_func,
6855  "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
6856  xrms, yrms);
6857 
6858  if (global) {
6859  write_global_distortion(global, 0, xpoly);
6860  write_global_distortion(global, 7, ypoly);
6861  }
6862 
6863  /*
6864  * The fit was successful: use the polynomials to obtain a new
6865  * position table with the improved positions of the slits
6866  */
6867 
6868  positions = cpl_table_duplicate(maskslits);
6869  cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
6870  cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
6871  cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
6872  cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
6873 
6874  point = cpl_vector_new(2);
6875  dpoint = cpl_vector_get_data(point);
6876 
6877  for (i = 0; i < nmaskslits; i++) {
6878  double position_x;
6879  double position_y;
6880 
6881  dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
6882  dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
6883  position_x = cpl_polynomial_eval(xpoly, point);
6884 // if (mos_multiplex >= 0) {
6885 // if (mos_multiplex != ((int)floor(position)) / mos_region_size) {
6886 // cpl_table_unselect_row(positions, i);
6887 // continue;
6888 // }
6889 // }
6890  cpl_table_set_double(positions, "xtop", i, position_x);
6891  position_y = cpl_polynomial_eval(ypoly, point);
6892  cpl_table_set_double(positions, "ytop", i, position_y);
6893  dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
6894  dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
6895  position_x = cpl_polynomial_eval(xpoly, point);
6896  cpl_table_set_double(positions, "xbottom", i, position_x);
6897  position_y = cpl_polynomial_eval(ypoly, point);
6898  cpl_table_set_double(positions, "ybottom", i, position_y);
6899  }
6900 
6901 // if (mos_multiplex >= 0) {
6902 // cpl_table_not_selected(positions);
6903 // cpl_table_erase_selected(positions);
6904 // nmaskslits = cpl_table_get_nrow(positions);
6905 // }
6906 
6907  cpl_vector_delete(point);
6908  cpl_polynomial_delete(xpoly);
6909  cpl_polynomial_delete(ypoly);
6910 
6911  cpl_table_erase_column(positions, "xmtop");
6912  cpl_table_erase_column(positions, "ymtop");
6913  cpl_table_erase_column(positions, "xmbottom");
6914  cpl_table_erase_column(positions, "ymbottom");
6915 
6916  if (mos_multiplex >= 0) {
6917  msg_multiplex =
6918  cpl_sprintf("in the CCD section between %d and %d pixel",
6919  mos_multiplex * mos_region_size,
6920  (mos_multiplex + 1) * mos_region_size);
6921  }
6922 
6923  if (nmaskslits > nslits)
6924  cpl_msg_info(cpl_func,
6925  "Finally identified slits: %d out of %d expected %s\n"
6926  "(%d recovered)", nmaskslits, nmaskslits, msg_multiplex,
6927  nmaskslits - nslits);
6928  else if (nmaskslits < nslits)
6929  cpl_msg_info(cpl_func,
6930  "Finally identified slits: %d out of %d expected %s\n"
6931  "(%d rejected)", nmaskslits, nmaskslits, msg_multiplex,
6932  nslits - nmaskslits);
6933  else
6934  cpl_msg_info(cpl_func,
6935  "Finally identified slits: %d out of %d expected %s",
6936  nmaskslits, nmaskslits, msg_multiplex);
6937 
6938  if (mos_multiplex >= 0) {
6939  cpl_free(msg_multiplex);
6940  }
6941 
6942  return positions;
6943 
6944 }
6945 
6946 
6947 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits,
6948  cpl_table *global)
6949 {
6950  const char *func = "mos_identify_slits_fast";
6951 
6952  cpl_propertylist *sort_col;
6953  cpl_table *positions;
6954  cpl_vector *scales;
6955  cpl_vector *angles;
6956  cpl_vector *point;
6957  cpl_vector *xpos;
6958  cpl_vector *ypos;
6959  cpl_vector *xmpos;
6960  cpl_vector *ympos;
6961  cpl_bivector *mpos;
6962  cpl_polynomial *xpoly = NULL;
6963  cpl_polynomial *ypoly = NULL;
6964  cpl_error_code error;
6965  int nslits;
6966  int nmaskslits;
6967  int found_slits;
6968  int i, j, k;
6969 
6970  double dist1, dist2, dist3, dist, mindist;
6971  double scale, minscale, maxscale;
6972  double angle, minangle, maxangle;
6973  double *dscale;
6974  double *dangle;
6975  double *dpoint;
6976  double *xtop;
6977  double *ytop;
6978  double *xbottom;
6979  double *ybottom;
6980  double *xcenter;
6981  double *ycenter;
6982  double *xpseudo;
6983  double *ypseudo;
6984  int *slit_id;
6985  double *xmtop;
6986  double *ymtop;
6987  double *xmbottom;
6988  double *ymbottom;
6989  double *xmcenter;
6990  double *ymcenter;
6991  double *xmpseudo;
6992  double *ympseudo;
6993  double xmse, ymse;
6994  int *mslit_id;
6995  int *good;
6996  int minpos;
6997  int degree;
6998 
6999  double sradius = 0.01; /* Candidate input argument... */
7000  int in_sradius;
7001 
7002  double pi = 3.14159265358979323846;
7003 
7004 
7005  error = mos_validate_slits(slits);
7006  if (error) {
7007  cpl_msg_error(func, "CCD slits table validation: %s",
7008  cpl_error_get_message());
7009  cpl_error_set(func, error);
7010  return NULL;
7011  }
7012 
7013  error = mos_validate_slits(maskslits);
7014  if (error) {
7015  cpl_msg_error(func, "Mask slits table validation: %s",
7016  cpl_error_get_message());
7017  cpl_error_set(func, error);
7018  return NULL;
7019  }
7020 
7021  if (1 != cpl_table_has_column(maskslits, "slit_id")) {
7022  cpl_msg_error(func, "Missing slits identifiers");
7023  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
7024  return NULL;
7025  }
7026 
7027  if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
7028  cpl_msg_error(func, "Wrong type used for slits identifiers");
7029  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
7030  return NULL;
7031  }
7032 
7033  nslits = cpl_table_get_nrow(slits);
7034  nmaskslits = cpl_table_get_nrow(maskslits);
7035 
7036  if (nslits == 0 || nmaskslits == 0) {
7037  cpl_msg_error(func, "Empty slits table");
7038  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
7039  return NULL;
7040  }
7041 
7042 
7043  /*
7044  * Compute middle point coordinates for each slit listed in both
7045  * input tables.
7046  */
7047 
7048  if (cpl_table_has_column(slits, "xcenter"))
7049  cpl_table_erase_column(slits, "xcenter");
7050 
7051  if (cpl_table_has_column(slits, "ycenter"))
7052  cpl_table_erase_column(slits, "ycenter");
7053 
7054  if (cpl_table_has_column(maskslits, "xcenter"))
7055  cpl_table_erase_column(maskslits, "xcenter");
7056 
7057  if (cpl_table_has_column(maskslits, "ycenter"))
7058  cpl_table_erase_column(maskslits, "ycenter");
7059 
7060  cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
7061  cpl_table_add_columns(slits, "xcenter", "xbottom");
7062  cpl_table_divide_scalar(slits, "xcenter", 2.0);
7063  cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
7064  cpl_table_add_columns(slits, "ycenter", "ybottom");
7065  cpl_table_divide_scalar(slits, "ycenter", 2.0);
7066 
7067  cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
7068  cpl_table_add_columns(maskslits, "xcenter", "xbottom");
7069  cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
7070  cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
7071  cpl_table_add_columns(maskslits, "ycenter", "ybottom");
7072  cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
7073 
7074 
7075  /*
7076  * Guarantee that both input tables are sorted in the same way
7077  */
7078 
7079  sort_col = cpl_propertylist_new();
7080  cpl_propertylist_append_bool(sort_col, "ycenter", 1);
7081  cpl_table_sort(slits, sort_col);
7082  cpl_table_sort(maskslits, sort_col);
7083  cpl_propertylist_delete(sort_col);
7084 
7085 
7086  /*
7087  * First we handle all the special cases (too few slits...)
7088  */
7089 
7090  if (nslits < 3 && nmaskslits > nslits) {
7091 
7092  /*
7093  * If there are just 1 or 2 slits on the CCD, and more on the
7094  * mask, the ambiguity cannot be solved, and an error is returned.
7095  * This is a case that must be solved with a first-guess relation
7096  * between mask and CCD.
7097  */
7098 
7099  if (nslits > 1)
7100  cpl_msg_warning(func, "Cannot match the found CCD slit with the "
7101  "%d mask slits: process will continue using the "
7102  "detected CCD slit position", nmaskslits);
7103  else
7104  cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
7105  "the %d mask slits: process will continue using "
7106  "the detected CCD slits positions", nslits,
7107  nmaskslits);
7108  return NULL;
7109  }
7110 
7111  if (nslits <= 3 && nslits == nmaskslits) {
7112 
7113  cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
7114  cpl_msg_warning(func, "Their detected positions are left unchanged");
7115 
7116  /*
7117  * If there are just up to 3 slits, both on the mask and on the CCD,
7118  * we can reasonably hope that those slits were found, and accept
7119  * that their positions on the CCD cannot be improved. We prepare
7120  * therefore an output position table containing the slits with
7121  * their original positions. We can however give an estimate of
7122  * the platescale if there is more than one slit.
7123  */
7124 
7125  positions = cpl_table_duplicate(slits);
7126  cpl_table_erase_column(slits, "xcenter");
7127  cpl_table_erase_column(slits, "ycenter");
7128  cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
7129  cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
7130  cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
7131  cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
7132  cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
7133  cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
7134  cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
7135  cpl_table_erase_column(maskslits, "xcenter");
7136  cpl_table_erase_column(maskslits, "ycenter");
7137 
7138  if (nslits > 1) {
7139  xcenter = cpl_table_get_data_double(positions, "xcenter");
7140  ycenter = cpl_table_get_data_double(positions, "ycenter");
7141  xmcenter = cpl_table_get_data_double(positions, "xmcenter");
7142  ymcenter = cpl_table_get_data_double(positions, "ymcenter");
7143 
7144  dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
7145  + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
7146  dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
7147  + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
7148  scale = sqrt(dist1/dist2);
7149 
7150  if (nslits == 3) {
7151  dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
7152  + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
7153  dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
7154  + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
7155  scale += sqrt(dist1/dist2);
7156  scale /= 2;
7157  }
7158 
7159  cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
7160  }
7161 
7162  return positions;
7163  }
7164 
7165  if (nmaskslits < 3 && nslits > nmaskslits) {
7166 
7167  /*
7168  * If there are less than 3 slits on the mask the ambiguity cannot
7169  * be solved, and an error is returned. This is a case that must
7170  * be solved with a first-guess relation between mask and CCD.
7171  */
7172 
7173  cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
7174  "the %d mask slits: process will continue using "
7175  "the detected CCD slits positions", nslits,
7176  nmaskslits);
7177  return NULL;
7178  }
7179 
7180 
7181  /*
7182  * At this point of the program all the region of the plane
7183  * (nslits, nmaskslits) where either or both mask and CCD display
7184  * less than 3 slits are handled in some way. It would be better
7185  * to add in this place a special handling for identifying slits
7186  * in case of a very reduced number of slits (say, below 6).
7187  * It is also clear that if there are many more slits on the
7188  * mask than on the CCD, or many more on the CCD than on the
7189  * mask, something went deeply wrong with the preliminary
7190  * wavelength calibration. Such cases should be handled with
7191  * a _complete_ pattern-recognition algorithm based on the
7192  * construction of all possible triangles. For the moment,
7193  * we go directly to the limited pattern-recognition applied
7194  * below, based on triangles build only for consecutive slits.
7195  * This is reasonably safe, since the preliminary wavelength
7196  * calibration performed by mos_identify_peaks() is generally
7197  * robust.
7198  */
7199 
7200 
7201  /*
7202  * Compute (X, Y) coordinates on pseudo-plane describing the
7203  * different position ratios of successive slits, in both
7204  * input tables.
7205  */
7206 
7207  if (cpl_table_has_column(slits, "xpseudo"))
7208  cpl_table_erase_column(slits, "xpseudo");
7209 
7210  if (cpl_table_has_column(slits, "ypseudo"))
7211  cpl_table_erase_column(slits, "ypseudo");
7212 
7213  if (cpl_table_has_column(maskslits, "xpseudo"))
7214  cpl_table_erase_column(maskslits, "xpseudo");
7215 
7216  if (cpl_table_has_column(maskslits, "ypseudo"))
7217  cpl_table_erase_column(maskslits, "ypseudo");
7218 
7219  cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
7220  cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
7221 
7222  xcenter = cpl_table_get_data_double(slits, "xcenter");
7223  ycenter = cpl_table_get_data_double(slits, "ycenter");
7224  xpseudo = cpl_table_get_data_double(slits, "xpseudo");
7225  ypseudo = cpl_table_get_data_double(slits, "ypseudo");
7226 
7227  for (i = 1; i < nslits - 1; i++) {
7228  dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
7229  + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
7230  dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
7231  + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
7232  dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
7233  + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
7234  xpseudo[i] = sqrt(dist1/dist2);
7235  ypseudo[i] = sqrt(dist3/dist2);
7236  }
7237 
7238  cpl_table_set_invalid(slits, "xpseudo", 0);
7239  cpl_table_set_invalid(slits, "xpseudo", nslits-1);
7240  cpl_table_set_invalid(slits, "ypseudo", 0);
7241  cpl_table_set_invalid(slits, "ypseudo", nslits-1);
7242 
7243  cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
7244  cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
7245 
7246  xcenter = cpl_table_get_data_double(maskslits, "xcenter");
7247  ycenter = cpl_table_get_data_double(maskslits, "ycenter");
7248  xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
7249  ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
7250 
7251  for (i = 1; i < nmaskslits - 1; i++) {
7252  dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
7253  + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
7254  dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
7255  + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
7256  dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
7257  + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
7258  xmpseudo[i] = sqrt(dist1/dist2);
7259  ympseudo[i] = sqrt(dist3/dist2);
7260  }
7261 
7262  cpl_table_set_invalid(maskslits, "xpseudo", 0);
7263  cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
7264  cpl_table_set_invalid(maskslits, "ypseudo", 0);
7265  cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
7266 
7267 
7268  /*
7269  * For each (X, Y) on the pseudo-plane related to the mask positions,
7270  * find the closest (X, Y) on the pseudo-plane related to the CCD
7271  * positions. If the closest point is closer than a given search
7272  * radius, a triangle has been found, and 3 slits are identified.
7273  * However, if more than one point is found within the search
7274  * radius, we have an ambiguity and this is rejected.
7275  */
7276 
7277  if (cpl_table_has_column(slits, "slit_id"))
7278  cpl_table_erase_column(slits, "slit_id");
7279  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
7280  slit_id = cpl_table_get_data_int(maskslits, "slit_id");
7281 
7282  for (i = 1; i < nmaskslits - 1; i++) {
7283  in_sradius = 0;
7284  mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
7285  + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
7286  minpos = 1;
7287  if (mindist < sradius*sradius)
7288  in_sradius++;
7289  for (j = 2; j < nslits - 1; j++) {
7290  dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
7291  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
7292  if (dist < sradius*sradius)
7293  in_sradius++;
7294  if (in_sradius > 1) /* More than one triangle within radius */
7295  break;
7296  if (mindist > dist) {
7297  mindist = dist;
7298  minpos = j;
7299  }
7300  }
7301 
7302  mindist = sqrt(mindist);
7303 
7304  if (mindist < sradius && in_sradius == 1) {
7305  cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
7306  cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
7307  cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
7308  }
7309  }
7310 
7311 
7312  /*
7313  * At this point, the slit_id column contains invalid elements
7314  * corresponding to unidentified slits.
7315  */
7316 
7317  found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
7318 
7319  if (found_slits < 3) {
7320  cpl_msg_warning(func, "Too few preliminarily identified slits: "
7321  "%d out of %d", found_slits, nslits);
7322  if (nslits == nmaskslits) {
7323  cpl_msg_warning(func, "(this is not an error, it could be caused "
7324  "by a mask with regularly located slits)");
7325  cpl_msg_warning(func, "The detected slits positions are left "
7326  "unchanged");
7327 
7328  /*
7329  * If there are less than 3 identified slits, this is probably
7330  * a mask with regularly spaced slits (leading to an ambiguous
7331  * pattern). Only in the case all expected slits appear to have
7332  * been found on the CCD we can proceed...
7333  */
7334 
7335  cpl_table_erase_column(slits, "slit_id");
7336  cpl_table_erase_column(slits, "xpseudo");
7337  cpl_table_erase_column(slits, "ypseudo");
7338  positions = cpl_table_duplicate(slits);
7339  cpl_table_erase_column(slits, "xcenter");
7340  cpl_table_erase_column(slits, "ycenter");
7341 
7342  cpl_table_erase_column(maskslits, "xpseudo");
7343  cpl_table_erase_column(maskslits, "ypseudo");
7344  cpl_table_duplicate_column(positions, "xmtop",
7345  maskslits, "xtop");
7346  cpl_table_duplicate_column(positions, "ymtop",
7347  maskslits, "ytop");
7348  cpl_table_duplicate_column(positions, "xmbottom",
7349  maskslits, "xbottom");
7350  cpl_table_duplicate_column(positions, "ymbottom",
7351  maskslits, "ybottom");
7352  cpl_table_duplicate_column(positions, "xmcenter",
7353  maskslits, "xcenter");
7354  cpl_table_duplicate_column(positions, "ymcenter",
7355  maskslits, "ycenter");
7356  cpl_table_duplicate_column(positions, "slit_id",
7357  maskslits, "slit_id");
7358  cpl_table_erase_column(maskslits, "xcenter");
7359  cpl_table_erase_column(maskslits, "ycenter");
7360  return positions;
7361  }
7362  else {
7363  cpl_table_erase_column(slits, "slit_id");
7364  cpl_table_erase_column(slits, "xpseudo");
7365  cpl_table_erase_column(slits, "ypseudo");
7366  positions = cpl_table_duplicate(slits);
7367  cpl_table_erase_column(slits, "xcenter");
7368  cpl_table_erase_column(slits, "ycenter");
7369  cpl_msg_warning(func, "(the failure could be caused "
7370  "by a mask with regularly located slits)");
7371  return NULL;
7372  }
7373  }
7374  else {
7375  cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
7376  "candidates (%d expected)", found_slits, nslits,
7377  nmaskslits);
7378  }
7379 
7380 
7381  /*
7382  * Create a table with the coordinates of the preliminarily identified
7383  * slits, both on CCD and mask. The original order of the slits positions
7384  * is preserved.
7385  */
7386 
7387  positions = cpl_table_new(found_slits);
7388  cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
7389  cpl_table_new_column(positions, "xtop", CPL_TYPE_DOUBLE);
7390  cpl_table_new_column(positions, "ytop", CPL_TYPE_DOUBLE);
7391  cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
7392  cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
7393  cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
7394  cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
7395  cpl_table_new_column(positions, "xmtop", CPL_TYPE_DOUBLE);
7396  cpl_table_new_column(positions, "ymtop", CPL_TYPE_DOUBLE);
7397  cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
7398  cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
7399  cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
7400  cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
7401  cpl_table_new_column(positions, "good", CPL_TYPE_INT);
7402  cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
7403 
7404  slit_id = cpl_table_get_data_int (slits, "slit_id");
7405  xtop = cpl_table_get_data_double(slits, "xtop");
7406  ytop = cpl_table_get_data_double(slits, "ytop");
7407  xbottom = cpl_table_get_data_double(slits, "xbottom");
7408  ybottom = cpl_table_get_data_double(slits, "ybottom");
7409  xcenter = cpl_table_get_data_double(slits, "xcenter");
7410  ycenter = cpl_table_get_data_double(slits, "ycenter");
7411 
7412  mslit_id = cpl_table_get_data_int (maskslits, "slit_id");
7413  xmtop = cpl_table_get_data_double(maskslits, "xtop");
7414  ymtop = cpl_table_get_data_double(maskslits, "ytop");
7415  xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
7416  ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
7417  xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
7418  ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
7419 
7420 
7421  /*
7422  * Transferring the valid slits information to the new table.
7423  * Note that invalid elements are coded as 0 in the internal
7424  * buffer, and this is the way they are recognised and excluded.
7425  */
7426 
7427  k = 0;
7428  cpl_table_fill_invalid_int(slits, "slit_id", 0);
7429  for (i = 0; i < nmaskslits; i++) {
7430  for (j = 0; j < nslits; j++) {
7431  if (slit_id[j] == 0)
7432  continue; /* Skip invalid slit */
7433  if (mslit_id[i] == slit_id[j]) {
7434  cpl_table_set_int (positions, "slit_id", k, slit_id[j]);
7435 
7436  cpl_table_set_double(positions, "xtop", k, xtop[j]);
7437  cpl_table_set_double(positions, "ytop", k, ytop[j]);
7438  cpl_table_set_double(positions, "xbottom", k, xbottom[j]);
7439  cpl_table_set_double(positions, "ybottom", k, ybottom[j]);
7440  cpl_table_set_double(positions, "xcenter", k, xcenter[j]);
7441  cpl_table_set_double(positions, "ycenter", k, ycenter[j]);
7442 
7443  cpl_table_set_double(positions, "xmtop", k, xmtop[i]);
7444  cpl_table_set_double(positions, "ymtop", k, ymtop[i]);
7445  cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
7446  cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
7447  cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
7448  cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
7449 
7450  k++;
7451 
7452  break;
7453  }
7454  }
7455  }
7456 
7457  found_slits = k;
7458 
7459  cpl_table_erase_column(slits, "slit_id");
7460  cpl_table_erase_column(slits, "xpseudo");
7461  cpl_table_erase_column(slits, "ypseudo");
7462  cpl_table_erase_column(slits, "xcenter");
7463  cpl_table_erase_column(slits, "ycenter");
7464  cpl_table_erase_column(maskslits, "xpseudo");
7465  cpl_table_erase_column(maskslits, "ypseudo");
7466  cpl_table_erase_column(maskslits, "xcenter");
7467  cpl_table_erase_column(maskslits, "ycenter");
7468 
7469 
7470  /*
7471  * Find the median platescale and rotation angle from the identified
7472  * slits, and then exclude slits outlaying more than 10% from the
7473  * median platescale, and more than 2 degrees from the median
7474  * rotation angle.
7475  */
7476 
7477  ytop = cpl_table_get_data_double(positions, "ytop");
7478  ybottom = cpl_table_get_data_double(positions, "ybottom");
7479  xcenter = cpl_table_get_data_double(positions, "xcenter");
7480  ycenter = cpl_table_get_data_double(positions, "ycenter");
7481  xmcenter = cpl_table_get_data_double(positions, "xmcenter");
7482  ymcenter = cpl_table_get_data_double(positions, "ymcenter");
7483 
7484  scales = cpl_vector_new(found_slits - 1);
7485  dscale = cpl_vector_get_data(scales);
7486  angles = cpl_vector_new(found_slits - 1);
7487  dangle = cpl_vector_get_data(angles);
7488 
7489  for (i = 1; i < found_slits; i++) {
7490  dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
7491  + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
7492  dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
7493  + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
7494  dscale[i-1] = sqrt(dist1/dist2);
7495  dangle[i-1] = atan2(ycenter[i-1] - ycenter[i],
7496  xcenter[i-1] - xcenter[i])
7497  - atan2(ymcenter[i-1] - ymcenter[i],
7498  xmcenter[i-1] - xmcenter[i]);
7499  dangle[i-1] *= 180;
7500  dangle[i-1] /= pi;
7501  }
7502 
7503  minscale = cpl_vector_get_min(scales);
7504  scale = cpl_vector_get_median_const(scales);
7505  maxscale = cpl_vector_get_max(scales);
7506 
7507  minangle = cpl_vector_get_min(angles);
7508  angle = cpl_vector_get_median_const(angles);
7509  maxangle = cpl_vector_get_max(angles);
7510 
7511  cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
7512  cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm",
7513  minscale, maxscale);
7514 
7515  cpl_msg_info(func, "Median rotation: %f degrees", angle);
7516  cpl_msg_info(func, "Minmax rotation: %f, %f degrees",
7517  minangle, maxangle);
7518 
7519  good = cpl_table_get_data_int(positions, "good");
7520 
7521  good[0] = good[found_slits - 1] = 1;
7522  for (i = 1; i < found_slits; i++) {
7523  if (fabs((dscale[i-1] - scale)/scale) < 0.10
7524  && fabs(dangle[i-1] - angle) < 2) {
7525  good[i-1]++;
7526  good[i]++;
7527  }
7528  }
7529 
7530  for (i = 0; i < found_slits; i++) {
7531  if (good[i] < 2)
7532  good[i] = 0;
7533  else
7534  good[i] = 1;
7535  }
7536 
7537 /*
7538  for (i = 1; i < found_slits; i++)
7539  if (fabs((dscale[i-1] - scale)/scale) < 0.10)
7540  good[i-1] = good[i] = 1;
7541 */
7542 
7543 /* DEBUG ************+
7544  for (i = 0; i < found_slits; i++) {
7545  if (good[i]) {
7546  if (i == found_slits - 1)
7547  printf("include slit %d, prev = %f, %f\n",
7548  i, dscale[i-1], dangle[i-1]);
7549  else if (i == 0)
7550  printf("include slit %d, next %f, %f\n",
7551  i, dscale[i], dangle[i]);
7552  else
7553  printf("include slit %d, prev = %f, %f, next %f, %f\n", i,
7554  dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
7555  }
7556  else {
7557  if (i == found_slits - 1)
7558  printf("EXclude slit %d, prev = %f, %f\n",
7559  i, dscale[i-1], dangle[i-1]);
7560  else if (i == 0)
7561  printf("EXclude slit %d, next %f, %f\n",
7562  i, dscale[i], dangle[i]);
7563  else
7564  printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,
7565  dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
7566  }
7567  }
7568 +*********** DEBUG */
7569 
7570  cpl_vector_delete(scales);
7571  cpl_vector_delete(angles);
7572 
7573  cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
7574  cpl_table_erase_selected(positions);
7575  cpl_table_erase_column(positions, "good");
7576  found_slits = cpl_table_get_nrow(positions);
7577 
7578  if (found_slits < 4) {
7579 
7580  /*
7581  * If the self-consistency check gives such a poor result,
7582  * something must have gone really wrong in the preliminary
7583  * wavelength calibration... Nothing can be done.
7584  */
7585 
7586  cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
7587  "candidates (%d expected). Process will continue "
7588  "using the detected CCD slits positions", found_slits,
7589  nslits, nmaskslits);
7590  cpl_table_delete(positions);
7591  return NULL;
7592  }
7593  else {
7594  cpl_msg_info(func, "Safely identified slits: %d out of %d "
7595  "candidates\n(%d expected)", found_slits, nslits,
7596  nmaskslits);
7597  }
7598 
7599 
7600  /*
7601  * Now select the central points of the identified slits, and
7602  * fit two bivariate polynomials to determine a first approximate
7603  * relation between positions on the mask and positions on the CCD.
7604  */
7605 
7606  xpos = cpl_vector_wrap(found_slits,
7607  cpl_table_get_data_double(positions, "xcenter"));
7608  ypos = cpl_vector_wrap(found_slits,
7609  cpl_table_get_data_double(positions, "ycenter"));
7610  xmpos = cpl_vector_wrap(found_slits,
7611  cpl_table_get_data_double(positions, "xmcenter"));
7612  ympos = cpl_vector_wrap(found_slits,
7613  cpl_table_get_data_double(positions, "ymcenter"));
7614  mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
7615 
7616  if (found_slits < 10)
7617  degree = 1;
7618  else
7619  degree = 2;
7620 
7621  xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
7622  if (xpoly != NULL)
7623  ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
7624  cpl_bivector_unwrap_vectors(mpos);
7625  cpl_vector_unwrap(xpos);
7626  cpl_vector_unwrap(ypos);
7627  cpl_vector_unwrap(xmpos);
7628  cpl_vector_unwrap(ympos);
7629  if (ypoly == NULL) {
7630  if (found_slits == nmaskslits) {
7631  cpl_msg_warning(func, "Fit failure: the accuracy of the "
7632  "identified slits positions is not improved.");
7633 
7634  /*
7635  * The determination of the transformation from mask to CCD
7636  * failed, but since all slits have been already identified
7637  * this is not a problem: data can be reduced also without
7638  * such transformation. Simply the accuracy of the slits
7639  * positions cannot be improved.
7640  */
7641 
7642  } else {
7643  cpl_msg_info(func, "Fit failure: not all slits have been "
7644  "identified. Process will continue using "
7645  "the detected CCD slits positions");
7646  }
7647 
7648  cpl_polynomial_delete(xpoly);
7649  return positions;
7650  }
7651 
7652  cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
7653  sqrt(xmse), sqrt(ymse));
7654 
7655  if (global) {
7656  write_global_distortion(global, 0, xpoly);
7657  write_global_distortion(global, 7, ypoly);
7658  }
7659 
7660  /*
7661  * The fit was successful: use the polynomials to obtain a new
7662  * position table with the improved positions of the slits
7663  */
7664 
7665  cpl_table_delete(positions);
7666 
7667  positions = cpl_table_duplicate(maskslits);
7668  cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
7669  cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
7670  cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
7671  cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
7672 
7673  point = cpl_vector_new(2);
7674  dpoint = cpl_vector_get_data(point);
7675 
7676  for (i = 0; i < nmaskslits; i++) {
7677  dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
7678  dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
7679  cpl_table_set_double(positions, "xtop", i,
7680  cpl_polynomial_eval(xpoly, point));
7681  cpl_table_set_double(positions, "ytop", i,
7682  cpl_polynomial_eval(ypoly, point));
7683  dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
7684  dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
7685  cpl_table_set_double(positions, "xbottom", i,
7686  cpl_polynomial_eval(xpoly, point));
7687  cpl_table_set_double(positions, "ybottom", i,
7688  cpl_polynomial_eval(ypoly, point));
7689  }
7690 
7691  cpl_vector_delete(point);
7692  cpl_polynomial_delete(xpoly);
7693  cpl_polynomial_delete(ypoly);
7694 
7695  cpl_table_erase_column(positions, "xmtop");
7696  cpl_table_erase_column(positions, "ymtop");
7697  cpl_table_erase_column(positions, "xmbottom");
7698  cpl_table_erase_column(positions, "ymbottom");
7699 
7700  if (nmaskslits > nslits)
7701  cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
7702  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
7703  else if (nmaskslits < nslits)
7704  cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
7705  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
7706  else
7707  cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
7708  nmaskslits, nmaskslits);
7709 
7710  return positions;
7711 }
7712 
7713 
7755 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
7756  double blue, double red, double dispersion)
7757 {
7758 
7759  const char *func = "mos_trace_flat";
7760 
7761  cpl_image *gradient;
7762  cpl_image *sgradient;
7763  float *dgradient;
7764  float level = 500; /* It was 250... */
7765  cpl_vector *row;
7766  cpl_vector *srow;
7767  cpl_vector **peaks;
7768  double *peak;
7769  int *slit_id;
7770  float *g;
7771  double *r;
7772  double *xtop;
7773  double *ytop;
7774  double *xbottom;
7775  double *ybottom;
7776  double min, dist;
7777  double sradius;
7778  double tolerance;
7779  double start_y, prev_y;
7780  int minpos;
7781  int nslits;
7782  int nrows;
7783  int step = 10;
7784  int filtbox = 15;
7785  int nx, ny, npix;
7786  int pos, ypos;
7787  int npeaks;
7788  int pixel_above, pixel_below;
7789  int i, j, k, l;
7790  char trace_id[MAX_COLNAME];
7791 
7792  cpl_table *traces;
7793 
7794 
7795  if (flat == NULL || slits == NULL) {
7796  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
7797  return NULL;
7798  }
7799 
7800  if (dispersion <= 0.0) {
7801  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
7802  return NULL;
7803  }
7804 
7805  if (red - blue < dispersion) {
7806  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
7807  return NULL;
7808  }
7809 
7810  /*
7811  * Create a dummy slit_id column if it is missing in the
7812  * input slits table
7813  */
7814 
7815  nslits = cpl_table_get_nrow(slits);
7816  if (1 != cpl_table_has_column(slits, "slit_id")) {
7817  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
7818  for (i = 0; i < nslits; i++)
7819  cpl_table_set_int(slits, "slit_id", i, -(i+1)); /* it was (i+1) */
7820  }
7821 
7822  slit_id = cpl_table_get_data_int(slits, "slit_id");
7823 
7824  nx = cpl_image_get_size_x(flat);
7825  ny = cpl_image_get_size_y(flat);
7826  npix = nx * ny;
7827 
7828  gradient = cpl_image_duplicate(flat);
7829  dgradient = cpl_image_get_data_float(gradient);
7830 
7831  for (i = 0; i < ny - 1; i++) {
7832  k = i * nx;
7833  for (j = 0; j < nx; j++) {
7834  l = k + j;
7835  dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
7836  }
7837  }
7838 
7839  npix--;
7840  for (j = 0; j < nx; j++)
7841  dgradient[npix - j] = 0.0;
7842 
7843  cpl_image_turn(gradient, -1);
7844  nx = cpl_image_get_size_x(gradient);
7845  ny = cpl_image_get_size_y(gradient);
7846  sgradient = mos_image_vertical_median_filter(gradient,
7847  filtbox, 0, ny, 0, step);
7848  cpl_image_delete(gradient);
7849 
7850 
7851  /*
7852  * Remove background from processed image rows
7853  */
7854 
7855  dgradient = cpl_image_get_data_float(sgradient);
7856 
7857  for (i = 1; i <= ny; i += step) {
7858  row = cpl_vector_new_from_image_row(sgradient, i);
7859  srow = cpl_vector_filter_median_create(row, filtbox);
7860  cpl_vector_subtract(row, srow);
7861  cpl_vector_delete(srow);
7862  g = dgradient + (i-1)*nx;
7863  r = cpl_vector_get_data(row);
7864  for (j = 0; j < nx; j++)
7865  g[j] = r[j];
7866  cpl_vector_delete(row);
7867  }
7868 
7869 
7870  /*
7871  * Rotate (temporarily) the input slits table, to get coordinates
7872  * compatible with the rotated gradient image.
7873  */
7874 
7875  mos_rotate_slits(slits, 1, nx, ny);
7876  xtop = cpl_table_get_data_double(slits, "xtop");
7877  ytop = cpl_table_get_data_double(slits, "ytop");
7878  xbottom = cpl_table_get_data_double(slits, "xbottom");
7879  ybottom = cpl_table_get_data_double(slits, "ybottom");
7880 
7881 
7882  /*
7883  * Get positions of peaks candidates for each processed gradient
7884  * image row
7885  */
7886 
7887  peaks = cpl_calloc(ny, sizeof(cpl_vector *));
7888 
7889  for (i = 0; i < ny; i += step) {
7890  g = dgradient + i*nx;
7891  peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
7892 
7893  /* I thought this would be required, but apparently I was wrong.
7894  * Check twice... */
7895  if (peaks[i])
7896  cpl_vector_subtract_scalar(peaks[i], 0.5);
7897 
7898  }
7899 
7900  cpl_image_delete(sgradient);
7901 
7902 
7903  /*
7904  * Tracing the flat field spectra edges, starting from the
7905  * slits positions obtained at reference wavelength. The
7906  * gradient maximum closest to each slits ends is found and
7907  * accepted only within a given search radius:
7908  */
7909 
7910  sradius = 5.0; /* Pixel */
7911 
7912  /*
7913  * The tracing proceeds along the processed gradient image rows,
7914  * above and below the start position, finding the closest peak
7915  * to the previous obtained position, accepting the new position
7916  * only if it is closer than a given tolerance:
7917  */
7918 
7919  tolerance = 0.9; /* Pixel */
7920 
7921  /*
7922  * The trace is attempted for a certain number of pixels above
7923  * and below the position of the reference wavelength:
7924  */
7925 
7926  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
7927  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
7928 
7929 
7930  /*
7931  * Prepare the structure of the output table:
7932  */
7933 
7934  nrows = (ny-1)/step + 1;
7935  traces = cpl_table_new(nrows);
7936  cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
7937  cpl_table_set_column_unit(traces, "x", "pixel");
7938  for (i = 0, j = 0; i < ny; i += step, j++)
7939  cpl_table_set(traces, "x", j, i);
7940 
7941  for (i = 0; i < nslits; i++) {
7942 
7943  /*
7944  * Find the closest processed gradient image row
7945  */
7946 
7947  ypos = ytop[i];
7948 
7949  if (ypos < 0)
7950  ypos = 0;
7951  if (ypos >= ny)
7952  ypos = ny - 1;
7953 
7954  pos = ypos / step;
7955  pos *= step;
7956 
7957  /*
7958  * Find the peak in that row that is closest to xtop[i]
7959  */
7960 
7961  if (peaks[pos]) {
7962  peak = cpl_vector_get_data(peaks[pos]);
7963  npeaks = cpl_vector_get_size(peaks[pos]);
7964 
7965  min = fabs(peak[0] - xtop[i]);
7966  minpos = 0;
7967  for (j = 1; j < npeaks; j++) {
7968  dist = fabs(peak[j] - xtop[i]);
7969  if (min > dist) {
7970  min = dist;
7971  minpos = j;
7972  }
7973  }
7974  }
7975  else {
7976  npeaks = 0;
7977  }
7978 
7979  snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
7980  cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
7981 
7982  if (min > sradius || npeaks == 0) {
7983  cpl_msg_warning(func, "Cannot find spectrum edge for "
7984  "top (or left) end of slit %d", slit_id[i]);
7985  }
7986  else {
7987 
7988  /*
7989  * Add to output table the start y position. Note that
7990  * y positions are written in coordinates of the unrotated
7991  * image, i.e., with horizontal dispersion. Currently nx
7992  * is the x size of the temporarily rotated image (for
7993  * faster memory access), but it has the sense of a y size.
7994  */
7995 
7996  cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
7997  start_y = peak[minpos];
7998 
7999  /*
8000  * Perform the tracing of current edge. Above:
8001  */
8002 
8003  prev_y = start_y;
8004 
8005  for (j = pos + step; j < ny; j += step) {
8006  if (j - pos > pixel_above)
8007  break;
8008  if (peaks[j]) {
8009  peak = cpl_vector_get_data(peaks[j]);
8010  npeaks = cpl_vector_get_size(peaks[j]);
8011  min = fabs(peak[0] - prev_y);
8012  minpos = 0;
8013  for (k = 1; k < npeaks; k++) {
8014  dist = fabs(peak[k] - prev_y);
8015  if (min > dist) {
8016  min = dist;
8017  minpos = k;
8018  }
8019  }
8020  if (min < tolerance) {
8021  cpl_table_set(traces, trace_id, j/step,
8022  nx - peak[minpos]);
8023  prev_y = peak[minpos];
8024  }
8025  }
8026  }
8027 
8028  /*
8029  * Perform the tracing of current edge. Below:
8030  */
8031 
8032  prev_y = start_y;
8033 
8034  for (j = pos - step; j >= 0; j -= step) {
8035  if (pos - j > pixel_below)
8036  break;
8037  if (peaks[j]) {
8038  peak = cpl_vector_get_data(peaks[j]);
8039  npeaks = cpl_vector_get_size(peaks[j]);
8040  min = fabs(peak[0] - prev_y);
8041  minpos = 0;
8042  for (k = 1; k < npeaks; k++) {
8043  dist = fabs(peak[k] - prev_y);
8044  if (min > dist) {
8045  min = dist;
8046  minpos = k;
8047  }
8048  }
8049  if (min < tolerance) {
8050  cpl_table_set(traces, trace_id, j/step,
8051  nx - peak[minpos]);
8052  prev_y = peak[minpos];
8053  }
8054  }
8055  }
8056  }
8057 
8058 
8059  /*
8060  * Find the peak in that row that is closest to xbottom[i]
8061  */
8062 
8063  if (peaks[pos]) {
8064  peak = cpl_vector_get_data(peaks[pos]);
8065  npeaks = cpl_vector_get_size(peaks[pos]);
8066 
8067  min = fabs(peak[0] - xbottom[i]);
8068  minpos = 0;
8069  for (j = 1; j < npeaks; j++) {
8070  dist = fabs(peak[j] - xbottom[i]);
8071  if (min > dist) {
8072  min = dist;
8073  minpos = j;
8074  }
8075  }
8076  }
8077  else {
8078  npeaks = 0;
8079  }
8080 
8081  snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
8082  cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
8083 
8084  if (min > sradius || npeaks == 0) {
8085  cpl_msg_warning(func, "Cannot find spectrum edge for "
8086  "bottom (or right) end of slit %d", slit_id[i]);
8087  }
8088  else {
8089 
8090  cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
8091  start_y = peak[minpos];
8092 
8093  /*
8094  * Perform the tracing of current edge. Above:
8095  */
8096 
8097  prev_y = start_y;
8098 
8099  for (j = pos + step; j < ny; j += step) {
8100  if (j - pos > pixel_above)
8101  break;
8102  if (peaks[j]) {
8103  peak = cpl_vector_get_data(peaks[j]);
8104  npeaks = cpl_vector_get_size(peaks[j]);
8105  min = fabs(peak[0] - prev_y);
8106  minpos = 0;
8107  for (k = 1; k < npeaks; k++) {
8108  dist = fabs(peak[k] - prev_y);
8109  if (min > dist) {
8110  min = dist;
8111  minpos = k;
8112  }
8113  }
8114  if (min < tolerance) {
8115  cpl_table_set(traces, trace_id, j/step,
8116  nx - peak[minpos]);
8117  prev_y = peak[minpos];
8118  }
8119  }
8120  }
8121 
8122  /*
8123  * Perform the tracing of current edge. Below:
8124  */
8125 
8126  prev_y = start_y;
8127 
8128  for (j = pos - step; j >= 0; j -= step) {
8129  if (pos - j > pixel_below)
8130  break;
8131  if (peaks[j]) {
8132  peak = cpl_vector_get_data(peaks[j]);
8133  npeaks = cpl_vector_get_size(peaks[j]);
8134  min = fabs(peak[0] - prev_y);
8135  minpos = 0;
8136  for (k = 1; k < npeaks; k++) {
8137  dist = fabs(peak[k] - prev_y);
8138  if (min > dist) {
8139  min = dist;
8140  minpos = k;
8141  }
8142  }
8143  if (min < tolerance) {
8144  cpl_table_set(traces, trace_id, j/step,
8145  nx - peak[minpos]);
8146  prev_y = peak[minpos];
8147  }
8148  }
8149  }
8150  }
8151 
8152  } /* End of loop on slits */
8153 
8154  for (i = 0; i < ny; i += step)
8155  cpl_vector_delete(peaks[i]);
8156  cpl_free(peaks);
8157 
8158  /*
8159  * Restore original orientation of slits positions table
8160  */
8161 
8162  mos_rotate_slits(slits, -1, ny, nx);
8163 
8164  return traces;
8165 
8166 }
8167 
8168 
8189 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
8190 {
8191  const char *func = "mos_poly_trace";
8192 
8193  cpl_table *polytraces;
8194  cpl_table *dummy;
8195  cpl_vector *x;
8196  cpl_vector *trace;
8197  cpl_polynomial *polytrace;
8198  char trace_id[MAX_COLNAME];
8199  char trace_res[MAX_COLNAME];
8200  char trace_mod[MAX_COLNAME];
8201  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8202  /* Max order is 5 */
8203  double *xdata;
8204  int *slit_id;
8205  int nslits;
8206  int nrows;
8207  int npoints;
8208  int i, j;
8209  cpl_size k;
8210 
8211 
8212  if (traces == NULL || slits == NULL) {
8213  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8214  return NULL;
8215  }
8216 
8217  if (order > 5) {
8218  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8219  return NULL;
8220  }
8221 
8222  nrows = cpl_table_get_nrow(traces);
8223  xdata = cpl_table_get_data_double(traces, "x");
8224  nslits = cpl_table_get_nrow(slits);
8225  slit_id = cpl_table_get_data_int(slits, "slit_id");
8226 
8227  polytraces = cpl_table_new(2*nslits);
8228  cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
8229  for (i = 0; i <= order; i++)
8230  cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
8231 
8232  for (i = 0; i < nslits; i++) {
8233  for (j = 0; j < 2; j++) { /* For top and bottom trace of each slit */
8234 
8235  if (j) {
8236  snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
8237  snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
8238  snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
8239  }
8240  else {
8241  snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
8242  snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
8243  snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
8244  }
8245 
8246  cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
8247 
8248  /*
8249  * The "dummy" table is just a tool for eliminating invalid
8250  * points from the vectors to be fitted.
8251  */
8252 
8253  dummy = cpl_table_new(nrows);
8254  cpl_table_duplicate_column(dummy, "x", traces, "x");
8255  cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
8256  npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
8257  if (npoints < 2 * order) {
8258  cpl_table_delete(dummy);
8259  continue;
8260  }
8261  cpl_table_erase_invalid(dummy);
8262  x = cpl_vector_wrap(npoints,
8263  cpl_table_get_data_double(dummy, "x"));
8264  trace = cpl_vector_wrap(npoints,
8265  cpl_table_get_data_double(dummy, trace_id));
8266  polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
8267  cpl_vector_unwrap(x);
8268  cpl_vector_unwrap(trace);
8269  cpl_table_delete(dummy);
8270 
8271  /*
8272  * Screen bad solutions. At the moment, a primitive screening
8273  * consists in excluding solutions displaying excessive
8274  * curvature (larger than 1E-5 / pixel)
8275  */
8276 
8277  k = 2;
8278  if (fabs(cpl_polynomial_get_coeff(polytrace, &k)) > 1.E-4) {
8279  cpl_polynomial_delete(polytrace);
8280  cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
8281  cpl_table_duplicate_column(traces, trace_res, traces,
8282  trace_mod);
8283  if (j)
8284  cpl_msg_warning(func, "Exclude bad curvature solution "
8285  "for bottom (right) edge of slit %d", slit_id[i]);
8286  else
8287  cpl_msg_warning(func, "Exclude bad curvature solution "
8288  "for top (left) edge of slit %d", slit_id[i]);
8289  continue;
8290  }
8291 
8292  /*
8293  * Write polynomial coefficients to the output table,
8294  * tagged with the appropriate slit_id
8295  */
8296 
8297  for (k = 0; k <= order; k++)
8298  cpl_table_set_double(polytraces, clab[k], 2*i+j,
8299  cpl_polynomial_get_coeff(polytrace, &k));
8300 
8301  /*
8302  * Add column of residuals to input traces table
8303  */
8304 
8305  cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
8306  cpl_table_set_column_unit(traces, trace_mod, "pixel");
8307 
8308  for (k = 0; k < nrows; k++) {
8309  cpl_table_set_double(traces, trace_mod, k,
8310  cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
8311  }
8312 
8313  cpl_polynomial_delete(polytrace);
8314 
8315  cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
8316  cpl_table_subtract_columns(traces, trace_res, trace_id);
8317  cpl_table_multiply_scalar(traces, trace_res, -1.0);
8318 
8319  }
8320  }
8321 
8322  return polytraces;
8323 
8324 }
8325 
8326 
8350 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
8351  int mode)
8352 {
8353  const char *func = "mos_global_trace";
8354 
8355  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8356  /* Max order is 5 */
8357  cpl_table *table;
8358  cpl_vector *c0;
8359  cpl_vector *cn;
8360  cpl_bivector *list;
8361 /* alternative (not robust)
8362  cpl_polynomial *poly;
8363 */
8364 
8365  double *offset;
8366  double rms, q, m;
8367 
8368  int order, nrows, nslits;
8369  int i, j;
8370 
8371 
8372  if (polytraces == NULL) {
8373  cpl_msg_error(func, "Missing spectral curvature table");
8374  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8375  }
8376 
8377  if (slits == NULL) {
8378  cpl_msg_error(func, "Missing slits positions table");
8379  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8380  }
8381 
8382  nslits = cpl_table_get_nrow(slits);
8383 
8384  table = cpl_table_duplicate(polytraces);
8385  cpl_table_erase_invalid(table);
8386 
8387  nrows = cpl_table_get_nrow(table);
8388 
8389  if (nrows < 4) {
8390  cpl_msg_warning(func, "Too few successful spectral curvature tracings "
8391  "(%d): the determination of a global curvature model "
8392  "failed", nrows);
8393  return CPL_ERROR_NONE;
8394  }
8395 
8396  order = cpl_table_get_ncol(polytraces) - 2;
8397 
8398  for (i = 0; i <= order; i++) {
8399  if (!cpl_table_has_column(table, clab[i])) {
8400  cpl_msg_error(func, "Wrong spectral curvature table");
8401  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8402  }
8403  }
8404 
8405 
8406  /*
8407  * Fill in advance the missing offset terms
8408  */
8409 
8410  for (i = 0; i < nslits; i++) {
8411  if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
8412  cpl_table_set_double(polytraces, clab[0], 2*i,
8413  cpl_table_get_double(slits, "ytop", i, NULL));
8414  }
8415  if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
8416  cpl_table_set_double(polytraces, clab[0], 2*i+1,
8417  cpl_table_get_double(slits, "ybottom", i, NULL));
8418  }
8419  }
8420 
8421  offset = cpl_table_get_data_double(polytraces, clab[0]);
8422 
8423 
8424  /*
8425  * Fit the global model and modify polytraces table accordingly
8426  */
8427 
8428  c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
8429 
8430  for (i = 1; i <= order; i++) {
8431  cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
8432  list = cpl_bivector_wrap_vectors(c0, cn);
8433  robustLinearFit(list, &q, &m, &rms);
8434 /* alternative (not robust)
8435  poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
8436 */
8437  for (j = 0; j < 2*nslits; j++) {
8438  if (mode == 1)
8439  if (cpl_table_is_valid(polytraces, clab[i], j))
8440  continue;
8441  cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
8442 /* alternative (not robust)
8443  cpl_table_set_double(polytraces, clab[i], j,
8444  cpl_polynomial_eval_1d(poly, offset[j], NULL));
8445 */
8446  }
8447  cpl_bivector_unwrap_vectors(list);
8448 /* alternative (not robust)
8449  cpl_polynomial_delete(poly);
8450 */
8451  cpl_vector_unwrap(cn);
8452  }
8453 
8454  cpl_vector_unwrap(c0);
8455  cpl_table_delete(table);
8456 
8457  return CPL_ERROR_NONE;
8458 
8459 }
8460 
8461 
8531 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
8532  cpl_table *polytraces, double reference,
8533  double blue, double red, double dispersion,
8534  int flux, cpl_image *calibration)
8535 {
8536  const char *func = "mos_spatial_calibration";
8537 
8538  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8539  /* Max order is 5 */
8540  cpl_polynomial *polytop;
8541  cpl_polynomial *polybot;
8542  cpl_image **exslit;
8543  cpl_image *resampled;
8544  float *data;
8545  float *sdata;
8546  float *xdata;
8547  double vtop, vbot, value;
8548  double top, bot;
8549  double coeff;
8550  double ytop, ybot;
8551  double ypos, yfra;
8552  double factor;
8553  int yint, ysize, yprev;
8554  int nslits;
8555  int npseudo;
8556  int *slit_id;
8557  int *length;
8558  int nx, ny;
8559  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
8560  int missing_top, missing_bot;
8561  int null;
8562  int order;
8563  int i, j;
8564  cpl_size k;
8565 
8566  int create_position = 1;
8567 
8568 
8569  if (spectra == NULL || slits == NULL || polytraces == NULL) {
8570  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
8571  return NULL;
8572  }
8573 
8574  if (dispersion <= 0.0) {
8575  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8576  return NULL;
8577  }
8578 
8579  if (red - blue < dispersion) {
8580  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
8581  return NULL;
8582  }
8583 
8584  nx = cpl_image_get_size_x(spectra);
8585  ny = cpl_image_get_size_y(spectra);
8586  sdata = cpl_image_get_data(spectra);
8587  if (calibration)
8588  data = cpl_image_get_data(calibration);
8589 
8590  if (cpl_table_has_column(slits, "position"))
8591  create_position = 0;
8592 
8593  if (create_position) {
8594  cpl_table_new_column(slits, "position", CPL_TYPE_INT);
8595  cpl_table_new_column(slits, "length", CPL_TYPE_INT);
8596  cpl_table_set_column_unit(slits, "position", "pixel");
8597  cpl_table_set_column_unit(slits, "length", "pixel");
8598  }
8599  else
8600  length = cpl_table_get_data_int(slits, "length");
8601 
8602  nslits = cpl_table_get_nrow(slits);
8603  slit_id = cpl_table_get_data_int(slits, "slit_id");
8604  order = cpl_table_get_ncol(polytraces) - 2;
8605 
8606  /*
8607  * The spatial resampling is performed for a certain number of
8608  * pixels above and below the position of the reference wavelength:
8609  */
8610 
8611  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
8612  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
8613 
8614  exslit = cpl_calloc(nslits, sizeof(cpl_image *));
8615 
8616  for (i = 0; i < nslits; i++) {
8617 
8618  if (create_position == 0)
8619  if (length[i] == 0)
8620  continue;
8621 
8622  /*
8623  * Note that the x coordinate of the reference pixels on the CCD
8624  * is taken arbitrarily at the top end of each slit. This wouldn't
8625  * be entirely correct in case of curved slits, or in presence of
8626  * heavy distortions: in such cases the spatial resampling is
8627  * really performed across a wide range of wavelengths. But
8628  * the lag between top and bottom spectral curvature models
8629  * would introduce even in such cases negligible effects on
8630  * the spectral spatial resampling.
8631  */
8632 
8633  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
8634 
8635  start_pixel = refpixel - pixel_below;
8636  if (start_pixel < 0)
8637  start_pixel = 0;
8638 
8639  end_pixel = refpixel + pixel_above;
8640  if (end_pixel > nx)
8641  end_pixel = nx;
8642 
8643  /*
8644  * Recover from the table of spectral curvature coefficients
8645  * the curvature polynomials.
8646  */
8647 
8648  missing_top = 0;
8649  polytop = cpl_polynomial_new(1);
8650  for (k = 0; k <= order; k++) {
8651  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
8652  if (null) {
8653  cpl_polynomial_delete(polytop);
8654  missing_top = 1;
8655  break;
8656  }
8657  cpl_polynomial_set_coeff(polytop, &k, coeff);
8658  }
8659 
8660  missing_bot = 0;
8661  polybot = cpl_polynomial_new(1);
8662  for (k = 0; k <= order; k++) {
8663  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
8664  if (null) {
8665  cpl_polynomial_delete(polybot);
8666  missing_bot = 1;
8667  break;
8668  }
8669  cpl_polynomial_set_coeff(polybot, &k, coeff);
8670  }
8671 
8672  if (missing_top && missing_bot) {
8673  cpl_msg_warning(func, "Spatial calibration, slit %d was not "
8674  "traced: no extraction!",
8675  slit_id[i]);
8676  continue;
8677  }
8678 
8679  /*
8680  * In case just one of the two edges was not traced, the other
8681  * edge curvature model is duplicated and shifted to the other
8682  * end of the slit: better than nothing!
8683  */
8684 
8685  if (missing_top) {
8686  cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
8687  "the spectral curvature of the lower edge "
8688  "is used instead.", slit_id[i]);
8689  polytop = cpl_polynomial_duplicate(polybot);
8690  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
8691  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
8692  k = 0;
8693  coeff = cpl_polynomial_get_coeff(polybot, &k);
8694  coeff += ytop - ybot;
8695  cpl_polynomial_set_coeff(polytop, &k, coeff);
8696  }
8697 
8698  if (missing_bot) {
8699  cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
8700  "the spectral curvature of the upper edge "
8701  "is used instead.", slit_id[i]);
8702  polybot = cpl_polynomial_duplicate(polytop);
8703  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
8704  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
8705  k = 0;
8706  coeff = cpl_polynomial_get_coeff(polytop, &k);
8707  coeff -= ytop - ybot;
8708  cpl_polynomial_set_coeff(polybot, &k, coeff);
8709  }
8710 
8711  /*
8712  * Allocate image for current extracted slit
8713  */
8714 
8715  top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
8716  bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
8717  npseudo = ceil(top-bot) + 1;
8718 
8719  if (npseudo < 1) {
8720  cpl_polynomial_delete(polytop);
8721  cpl_polynomial_delete(polybot);
8722  cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
8723  slit_id[i]);
8724  continue;
8725  }
8726 
8727  exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
8728  xdata = cpl_image_get_data(exslit[i]);
8729 
8730  /*
8731  * Write interpolated values to slit image.
8732  */
8733 
8734  for (j = start_pixel; j < end_pixel; j++) {
8735  top = cpl_polynomial_eval_1d(polytop, j, NULL);
8736  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
8737  factor = (top-bot)/npseudo;
8738  for (k = 0; k <= npseudo; k++) {
8739  ypos = top - k*factor;
8740  yint = floor(ypos);
8741  yfra = ypos - yint;
8742  if (yint >= 0 && yint < ny-1) {
8743  vtop = sdata[j + nx*yint];
8744  vbot = sdata[j + nx*(yint+1)];
8745 
8746  //This means that the top and bottom traces are crossing,
8747  //which is physically impossible, so let's set it to 0.
8748  if(factor <= 0 )
8749  value = 0;
8750  else if(vtop == FLT_MAX || vbot == FLT_MAX)
8751  value = FLT_MAX;
8752  else
8753  {
8754  value = vtop*(1-yfra) + vbot*yfra;
8755  if (flux)
8756  value *= factor;
8757  }
8758  xdata[j + nx*(npseudo-k)] = value;
8759  if (calibration) {
8760  data[j + nx*yint] = (top-yint)/factor;
8761  if (k) {
8762 
8763  /*
8764  * This is added to recover lost pixels on
8765  * the CCD image (pixels are lost because
8766  * the CCD pixels are less than npseudo+1).
8767  */
8768 
8769  if (yprev - yint > 1) {
8770  data[j + nx*(yint+1)] = (top-yint-1)/factor;
8771  }
8772  }
8773  }
8774  }
8775  yprev = yint;
8776  }
8777  }
8778  cpl_polynomial_delete(polytop);
8779  cpl_polynomial_delete(polybot);
8780  }
8781 
8782  /*
8783  * Now all the slits images are copied to a single image
8784  */
8785 
8786  ysize = 0;
8787  for (i = 0; i < nslits; i++)
8788  if (exslit[i])
8789  ysize += cpl_image_get_size_y(exslit[i]);
8790 
8791  if(ysize == 0)
8792  return NULL;
8793 
8794  resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
8795 
8796  yint = -1;
8797  for (i = 0; i < nslits; i++) {
8798  if (exslit[i]) {
8799  yint += cpl_image_get_size_y(exslit[i]);
8800  cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
8801  if (create_position) {
8802  cpl_table_set_int(slits, "position", i, ysize - yint - 1);
8803  cpl_table_set_int(slits, "length", i,
8804  cpl_image_get_size_y(exslit[i]));
8805  }
8806  cpl_image_delete(exslit[i]);
8807  }
8808  else if (create_position) {
8809  cpl_table_set_int(slits, "position", i, -1);
8810  cpl_table_set_int(slits, "length", i, 0);
8811  }
8812  }
8813 
8814 
8815  /*
8816  * Elimination of non-traced slits from slit position table: we cannot do
8817  * it because we would lose sync with polytraces and other slit-oriented
8818  * tables. COMMENTED OUT.
8819  *
8820 
8821  if (create_position) {
8822 
8823  if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
8824  cpl_table_erase_selected(slits);
8825 
8826  }
8827 
8828  */
8829 
8830  cpl_free(exslit);
8831 
8832  return resampled;
8833 
8834 }
8835 
8836 
8949 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
8950  cpl_vector *lines,
8951  double dispersion, float level,
8952  int sradius, int order,
8953  double reject, double refwave,
8954  double *wavestart, double *waveend,
8955  int *nlines, double *error,
8956  cpl_table *idscoeff,
8957  cpl_image *calibration,
8958  cpl_image *residuals,
8959  cpl_table *restable,
8960  cpl_table *detected_lines)
8961 {
8962 
8963  const char *func = "mos_wavelength_calibration_final";
8964 
8965  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
8966  /* Max order is 5 */
8967 
8968  double tolerance = 20.0; /* Probably forever... */
8969  int step = 10; /* Compute restable every "step" rows */
8970 
8971  char name[MAX_COLNAME];
8972 
8973  cpl_image *resampled;
8974  cpl_bivector *peaks_ident;
8975  cpl_vector *wavel;
8976  cpl_vector *peaks;
8977  cpl_polynomial *ids;
8978  cpl_polynomial *lin;
8979  cpl_polynomial *fguess;
8980  cpl_table *coeff;
8981  double ids_err;
8982  double max_disp, min_disp;
8983  double *line;
8984  double firstLambda, lastLambda, lambda;
8985  double wave, pixe, value;
8986  double c;
8987  float *sdata;
8988  float *rdata;
8989  float *idata;
8990  float *ddata;
8991  float v1, v2, vi;
8992  float fpixel;
8993  int *length;
8994  int pixstart, pixend;
8995  int row_top, row_bot;
8996  int extrapolation;
8997  int nref;
8998  int nslits;
8999  int nfits;
9000  int nl, nx, ny, pixel;
9001  int countLines, usedLines;
9002  int uorder;
9003  int missing;
9004  int null;
9005  int width, uradius;
9006  int i, j, s;
9007  cpl_size k;
9008 
9009 
9010  if (dispersion == 0.0) {
9011  cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
9012  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9013  return NULL;
9014  }
9015 
9016  if (dispersion < 0.0) {
9017  cpl_msg_error(func, "The expected dispersion must be positive");
9018  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9019  return NULL;
9020  }
9021 
9022  if (idscoeff == NULL) {
9023  cpl_msg_error(func, "A preallocated IDS coeff table must be given");
9024  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9025  return NULL;
9026  }
9027 
9028  max_disp = dispersion + dispersion * tolerance / 100;
9029  min_disp = dispersion - dispersion * tolerance / 100;
9030 
9031  if (order < 1) {
9032  cpl_msg_error(func, "The order of the fitting polynomial "
9033  "must be at least 1");
9034  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9035  return NULL;
9036  }
9037 
9038  if (image == NULL || lines == NULL) {
9039  cpl_msg_error(func, "Both spectral exposure and reference line "
9040  "catalog are required in input");
9041  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9042  return NULL;
9043  }
9044 
9045  nx = cpl_image_get_size_x(image);
9046  ny = cpl_image_get_size_y(image);
9047  sdata = cpl_image_get_data_float(image);
9048 
9049  nref = cpl_vector_get_size(lines);
9050  line = cpl_vector_get_data(lines);
9051 
9052  if (*wavestart < 1.0 && *waveend < 1.0) {
9053  firstLambda = line[0];
9054  lastLambda = line[nref-1];
9055  extrapolation = (lastLambda - firstLambda) / 10;
9056  firstLambda -= extrapolation;
9057  lastLambda += extrapolation;
9058  *wavestart = firstLambda;
9059  *waveend = lastLambda;
9060  }
9061  else {
9062  firstLambda = *wavestart;
9063  lastLambda = *waveend;
9064  }
9065 
9066  nl = (lastLambda - firstLambda) / dispersion;
9067  resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
9068  rdata = cpl_image_get_data_float(resampled);
9069 
9070  /*
9071  * Allocate total output table of IDS coefficients
9072  */
9073 
9074  for (j = 0; j <= order; j++)
9075  cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
9076 
9077  if (calibration)
9078  idata = cpl_image_get_data_float(calibration);
9079 
9080  if (residuals)
9081  ddata = cpl_image_get_data_float(residuals);
9082 
9083  if (restable) {
9084  cpl_table_set_size(restable, nref);
9085  cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
9086  cpl_table_copy_data_double(restable, "wavelength", line);
9087  for (i = 0; i < ny; i += step) {
9088  snprintf(name, MAX_COLNAME, "r%d", i);
9089  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
9090  snprintf(name, MAX_COLNAME, "d%d", i);
9091  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
9092  snprintf(name, MAX_COLNAME, "p%d", i);
9093  cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
9094  }
9095  }
9096 
9097  if (detected_lines) {
9098  cpl_table_set_size(detected_lines, 0);
9099  cpl_table_new_column(detected_lines, "slit_id", CPL_TYPE_INT);
9100  cpl_table_new_column(detected_lines, "xpos_rectified", CPL_TYPE_DOUBLE);
9101  cpl_table_new_column(detected_lines, "ypos_rectified", CPL_TYPE_DOUBLE);
9102  cpl_table_new_column(detected_lines, "xpos_rectified_iter", CPL_TYPE_DOUBLE);
9103  cpl_table_new_column(detected_lines, "ypos_rectified_iter", CPL_TYPE_DOUBLE);
9104  cpl_table_new_column(detected_lines, "peak_flux", CPL_TYPE_DOUBLE);
9105  cpl_table_new_column(detected_lines, "wave_ident", CPL_TYPE_DOUBLE);
9106  cpl_table_new_column(detected_lines, "wave_ident_iter", CPL_TYPE_DOUBLE);
9107  cpl_table_new_column(detected_lines, "xpos_fit_rect_wavecal", CPL_TYPE_DOUBLE);
9108  cpl_table_new_column(detected_lines, "res_xpos", CPL_TYPE_DOUBLE);
9109  cpl_table_new_column(detected_lines, "fit_used", CPL_TYPE_INT);
9110  }
9111 
9112 
9113  /*
9114  * Process all slits separately.
9115  */
9116 
9117  nslits = cpl_table_get_nrow(slits);
9118  length = cpl_table_get_data_int(slits, "length");
9119 
9120  row_top = ny;
9121  for (s = 0; s < nslits; s++) {
9122 
9123  int slit_id;
9124  slit_id = cpl_table_get_int(slits, "slit_id", s, NULL);
9125 
9126  if (length[s] == 0)
9127  continue;
9128 
9129  /*
9130  * row_top and row_bot define the boundaries of the current slit.
9131  * Here we begin (arbitrarily...) from the top slit.
9132  */
9133 
9134  row_bot = cpl_table_get_int(slits, "position", s, NULL);
9135 
9136  if (sradius > 0) {
9137 
9138  /*
9139  * If a search radius was defined, allocate the table of
9140  * the fitting polynomials coefficients. This table is
9141  * just used to generate the first-guess polynomial made
9142  * of the median coefficients of all polynomials found
9143  * for this slit.
9144  */
9145 
9146  coeff = cpl_table_new(row_top - row_bot);
9147  for (j = 0; j <= order; j++)
9148  cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
9149  }
9150 
9151  /*
9152  * Here is the loop on all rows of the current slit. They are
9153  * wavelength calibrated one by one.
9154  */
9155 
9156  for (i = row_bot; i < row_top; i++) {
9157  width = mos_lines_width(sdata + i*nx, nx);
9158  if (width < 5)
9159  width = 5;
9160  peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
9161  if (peaks) {
9162  peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
9163  }
9164  if (peaks) {
9165  int keep_multiplex = mos_multiplex;
9166  mos_multiplex = -1;
9167  if(detected_lines)
9168  {
9169  cpl_size newlines = cpl_vector_get_size(peaks);
9170  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
9171  cpl_table_set_size(detected_lines, oldsize + newlines);
9172  for(cpl_size iline = 0; iline < newlines; ++iline)
9173  {
9174  cpl_table_set_int(detected_lines, "slit_id",
9175  oldsize + iline, slit_id);
9176  cpl_table_set_double(detected_lines, "xpos_rectified",
9177  oldsize + iline, cpl_vector_get(peaks, iline) + 1);
9178  cpl_table_set_double(detected_lines, "ypos_rectified",
9179  oldsize + iline, (double)i + 1);
9180  cpl_table_set_double(detected_lines, "peak_flux",
9181  oldsize + iline,
9182  sdata[i*nx+(int)(cpl_vector_get(peaks, iline)+0.5)]);
9183  cpl_table_set_int(detected_lines,
9184  "fit_used",
9185  oldsize + iline, 0);
9186  }
9187  }
9188  peaks_ident = mos_identify_peaks(peaks, lines,
9189  min_disp, max_disp, 0.05);
9190  mos_multiplex = keep_multiplex;
9191  if (peaks_ident) {
9192  cpl_bivector * peaks_ident_used_fit;
9193  countLines = cpl_bivector_get_size(peaks_ident);
9194  if (countLines < 4) {
9195  cpl_bivector_delete(peaks_ident);
9196  cpl_vector_delete(peaks);
9197  if (nlines)
9198  nlines[i] = 0;
9199  if (error)
9200  error[i] = 0.0;
9201  continue;
9202  }
9203 
9204  /*
9205  * Set reference wavelength as zero point
9206  */
9207 
9208  wavel = cpl_bivector_get_y(peaks_ident);
9209  cpl_vector_subtract_scalar(wavel, refwave);
9210 
9211  uorder = countLines / 2 - 1;
9212  if (uorder > order)
9213  uorder = order;
9214 
9215  ids = mos_poly_wav2pix(peaks_ident, uorder, reject,
9216  2 * (uorder + 1), &usedLines,
9217  &ids_err, &peaks_ident_used_fit);
9218 
9219  if (ids == NULL) {
9220  cpl_bivector_delete(peaks_ident);
9221  cpl_vector_delete(peaks);
9222  if (nlines)
9223  nlines[i] = 0;
9224  if (error)
9225  error[i] = 0.0;
9226  cpl_error_reset();
9227  continue;
9228  }
9229 
9230  if (sradius > 0) {
9231  for (k = 0; k <= order; k++) {
9232  if (k > uorder) {
9233  cpl_table_set_double(coeff, clab[k],
9234  i - row_bot, 0.0);
9235  }
9236  else {
9237  cpl_table_set_double(coeff, clab[k],
9238  i - row_bot, cpl_polynomial_get_coeff(ids, &k));
9239  }
9240  }
9241  }
9242  /* else { */
9243  if (calibration) {
9244  pixstart = cpl_polynomial_eval_1d(ids,
9245  cpl_bivector_get_y_data(peaks_ident)[0],
9246  NULL);
9247  pixend = cpl_polynomial_eval_1d(ids,
9248  cpl_bivector_get_y_data(peaks_ident)[countLines-1],
9249  NULL);
9250  extrapolation = (pixend - pixstart) / 5;
9251  pixstart -= extrapolation;
9252  pixend += extrapolation;
9253  if (pixstart < 0)
9254  pixstart = 0;
9255  if (pixend > nx)
9256  pixend = nx;
9257 
9258  for (j = pixstart; j < pixend; j++) {
9259  (idata + i*nx)[j] = mos_eval_dds(ids,
9260  firstLambda, lastLambda, refwave, j);
9261  }
9262  }
9263 
9264  /*
9265  * Residuals image
9266  */
9267 
9268  if (residuals || (restable && !(i%step))) {
9269  if (restable && !(i%step)) {
9270  lin = cpl_polynomial_new(1);
9271  for (k = 0; k < 2; k++)
9272  cpl_polynomial_set_coeff(lin, &k,
9273  cpl_polynomial_get_coeff(ids, &k));
9274  }
9275  for (j = 0; j < countLines; j++) {
9276  pixe = cpl_bivector_get_x_data(peaks_ident)[j];
9277  wave = cpl_bivector_get_y_data(peaks_ident)[j];
9278  value = pixe
9279  - cpl_polynomial_eval_1d(ids, wave, NULL);
9280  if (residuals) {
9281  pixel = pixe + 0.5;
9282  (ddata + i*nx)[pixel] = value;
9283  }
9284  if (restable && !(i%step)) {
9285  for (k = 0; k < nref; k++) {
9286  if (fabs(line[k]-refwave-wave) < 0.1) {
9287  snprintf(name, MAX_COLNAME,
9288  "r%d", i);
9289  cpl_table_set_double(restable, name,
9290  k, value);
9291  value = pixe
9292  - cpl_polynomial_eval_1d(lin,
9293  wave, NULL);
9294  snprintf(name, MAX_COLNAME,
9295  "d%d", i);
9296  cpl_table_set_double(restable, name,
9297  k, value);
9298  snprintf(name, MAX_COLNAME,
9299  "p%d", i);
9300  cpl_table_set_double(restable, name,
9301  k, pixe);
9302  break;
9303  }
9304  }
9305  }
9306  }
9307  if (restable && !(i%step)) {
9308  cpl_polynomial_delete(lin);
9309  }
9310 /***
9311  for (j = 0; j < countLines; j++) {
9312  pixel = cpl_bivector_get_x_data(output)[j]
9313  + 0.5;
9314  (ddata + i*nx)[pixel] =
9315  cpl_bivector_get_x_data(output)[j]
9316  - cpl_polynomial_eval_1d(ids,
9317  cpl_bivector_get_y_data(output)[j],
9318  NULL);
9319  }
9320 ***/
9321  //Fill the line identification information in
9322  //the detected_lines table
9323  if(detected_lines)
9324  {
9325  cpl_size nidentlines = cpl_bivector_get_size(peaks_ident);
9326  cpl_size ndetectlines = cpl_vector_get_size(peaks);
9327  cpl_size totalsize = cpl_table_get_nrow(detected_lines);
9328  for(cpl_size idline = 0; idline < nidentlines; ++idline)
9329  {
9330  for(cpl_size detline = 0; detline < ndetectlines; ++detline)
9331  {
9332  if(cpl_vector_get(peaks, detline) ==
9333  cpl_bivector_get_x_data(peaks_ident)[idline])
9334  {
9335  cpl_size table_pos = totalsize - ndetectlines + detline;
9336  double wave_ident = cpl_bivector_get_y_data(peaks_ident)[idline] + refwave;
9337  double xpix_fit = cpl_polynomial_eval_1d(ids,
9338  wave_ident - refwave, NULL);
9339  double xpos_det = cpl_table_get_double(detected_lines,
9340  "xpos_rectified",
9341  table_pos, &null);
9342  cpl_table_set_double(detected_lines,
9343  "wave_ident",
9344  table_pos,
9345  wave_ident);
9346  cpl_table_set_double(detected_lines,
9347  "xpos_fit_rect_wavecal",
9348  table_pos,
9349  xpix_fit + 1);
9350  cpl_table_set_double(detected_lines,
9351  "res_xpos",
9352  table_pos,
9353  xpos_det - xpix_fit - 1);
9354  cpl_table_set_int(detected_lines,
9355  "fit_used",
9356  table_pos, 0);
9357  for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
9358  {
9359  if(cpl_bivector_get_x_data(peaks_ident)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
9360  cpl_table_set_int(detected_lines,
9361  "fit_used",
9362  table_pos, 1);
9363  }
9364  }
9365  }
9366  }
9367  }
9368 
9369  }
9370  /* } */
9371 
9372  /*
9373  * Write it anyway, even in case a first-guess based
9374  * solution will be searched afterwards: in case of
9375  * failure, the "blind" solution is kept.
9376  */
9377 
9378  if (nlines)
9379  nlines[i] = usedLines;
9380  if (error)
9381  error[i] = ids_err / sqrt(usedLines/(uorder + 1));
9382 
9383  for (k = 0; k <= order; k++) {
9384  if (k > uorder) {
9385  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
9386  }
9387  else {
9388  cpl_table_set_double(idscoeff, clab[k], i,
9389  cpl_polynomial_get_coeff(ids, &k));
9390  }
9391  }
9392 
9393  cpl_polynomial_delete(ids);
9394  cpl_bivector_delete(peaks_ident);
9395  }
9396  cpl_vector_delete(peaks);
9397  }
9398  } /* End of loop on current slit's rows */
9399 
9400 
9401  if (sradius > 0) {
9402 
9403  /*
9404  * See whether there are valid fits at all...
9405  */
9406 
9407  nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
9408 
9409  if (nfits) {
9410  int slope = 0;
9411 
9412  fguess = cpl_polynomial_new(1);
9413 
9414  if (mos_interpolate_wavecalib_mos(coeff, 2, 1)) {
9415 
9416  slope = 0;
9417 
9418  /*
9419  * Compute a median IDS polynomial for the current slit
9420  */
9421 
9422  for (k = 0; k <= order; k++) {
9423  c = cpl_table_get_column_median(coeff, clab[k]);
9424  cpl_polynomial_set_coeff(fguess, &k, c);
9425  }
9426  }
9427  else {
9428  slope = 1;
9429  }
9430 
9431  for (i = row_bot; i < row_top; i++) {
9432  cpl_bivector * peaks_ident_used_fit;
9433 
9434  /*
9435  * Use first-guess to find the reference lines again
9436  */
9437 
9438  width = mos_lines_width(sdata + i*nx, nx);
9439  if (width > sradius) {
9440  uradius = width;
9441  }
9442  else {
9443  uradius = sradius;
9444  }
9445 
9446  if (slope) {
9447  for (k = 0; k <= order; k++) {
9448  c = cpl_table_get_double(coeff, clab[k],
9449  i - row_bot, NULL);
9450  cpl_polynomial_set_coeff(fguess, &k, c);
9451  }
9452  }
9453 
9454  peaks_ident = mos_find_peaks(sdata + i*nx, nx, lines,
9455  fguess, refwave, uradius);
9456 
9457  if (peaks_ident == NULL) {
9458  cpl_error_reset();
9459  continue;
9460  }
9461 
9462  countLines = cpl_bivector_get_size(peaks_ident);
9463 
9464  if (countLines < 4) {
9465  cpl_bivector_delete(peaks_ident);
9466  continue;
9467  }
9468 
9469  /*
9470  * Set reference wavelength as zero point
9471  */
9472 
9473  wavel = cpl_bivector_get_y(peaks_ident);
9474  cpl_vector_subtract_scalar(wavel, refwave);
9475 
9476  uorder = countLines / 2 - 1;
9477  if (uorder > order)
9478  uorder = order;
9479 
9480  ids = mos_poly_wav2pix(peaks_ident, uorder, reject,
9481  2 * (uorder + 1), &usedLines,
9482  &ids_err, &peaks_ident_used_fit);
9483 
9484  if (ids == NULL) {
9485  cpl_error_reset();
9486  cpl_bivector_delete(peaks_ident);
9487  continue;
9488  }
9489 
9490  if (nlines)
9491  nlines[i] = usedLines;
9492  if (error)
9493  error[i] = ids_err / sqrt(usedLines/(uorder + 1));
9494 
9495  if (calibration) {
9496  pixstart = cpl_polynomial_eval_1d(ids,
9497  cpl_bivector_get_y_data(peaks_ident)[0],
9498  NULL);
9499  pixend = cpl_polynomial_eval_1d(ids,
9500  cpl_bivector_get_y_data(peaks_ident)[countLines-1],
9501  NULL);
9502  extrapolation = (pixend - pixstart) / 5;
9503  pixstart -= extrapolation;
9504  pixend += extrapolation;
9505  if (pixstart < 0)
9506  pixstart = 0;
9507  if (pixend > nx)
9508  pixend = nx;
9509 
9510  for (j = pixstart; j < pixend; j++) {
9511  (idata + i*nx)[j] = mos_eval_dds(ids,
9512  firstLambda, lastLambda, refwave, j);
9513  }
9514  }
9515 
9516  /*
9517  * Residuals image
9518  */
9519 
9520  if (residuals || (restable && !(i%step))) {
9521  if (restable && !(i%step)) {
9522  lin = cpl_polynomial_new(1);
9523  for (k = 0; k < 2; k++)
9524  cpl_polynomial_set_coeff(lin, &k,
9525  cpl_polynomial_get_coeff(ids, &k));
9526  }
9527  for (j = 0; j < countLines; j++) {
9528  pixe = cpl_bivector_get_x_data(peaks_ident)[j];
9529  wave = cpl_bivector_get_y_data(peaks_ident)[j];
9530  value = pixe
9531  - cpl_polynomial_eval_1d(ids, wave, NULL);
9532  if (residuals) {
9533  pixel = pixe + 0.5;
9534  (ddata + i*nx)[pixel] = value;
9535  }
9536  if (restable && !(i%step)) {
9537  for (k = 0; k < nref; k++) {
9538  if (fabs(line[k]-refwave-wave) < 0.1) {
9539  snprintf(name, MAX_COLNAME,
9540  "r%d", i);
9541  cpl_table_set_double(restable, name,
9542  k, value);
9543  value = pixe
9544  - cpl_polynomial_eval_1d(lin,
9545  wave, NULL);
9546  snprintf(name, MAX_COLNAME,
9547  "d%d", i);
9548  cpl_table_set_double(restable, name,
9549  k, value);
9550  snprintf(name, MAX_COLNAME,
9551  "p%d", i);
9552  cpl_table_set_double(restable, name,
9553  k, pixe);
9554  break;
9555  }
9556  }
9557  }
9558  }
9559  if (restable && !(i%step)) {
9560  cpl_polynomial_delete(lin);
9561  }
9562 /***
9563  for (j = 0; j < countLines; j++) {
9564  pixel = cpl_bivector_get_x_data(output)[j]
9565  + 0.5;
9566  (ddata + i*nx)[pixel] =
9567  cpl_bivector_get_x_data(output)[j]
9568  - cpl_polynomial_eval_1d(ids,
9569  cpl_bivector_get_y_data(output)[j],
9570  NULL);
9571  }
9572 ***/
9573  }
9574 
9575  if(detected_lines)
9576  {
9577  cpl_size oldsize = cpl_table_get_nrow(detected_lines);
9578  cpl_size nidentlines = cpl_bivector_get_size(peaks_ident);
9579  cpl_table_set_size(detected_lines, oldsize + nidentlines);
9580  for(cpl_size idline = 0; idline < nidentlines ; ++idline)
9581  {
9582  double wave_ident = cpl_bivector_get_y_data(peaks_ident)[idline] + refwave;
9583  double xpix_fit = cpl_polynomial_eval_1d(ids,
9584  wave_ident - refwave, NULL);
9585  cpl_table_set_int(detected_lines, "slit_id",
9586  oldsize + idline, slit_id);
9587  cpl_table_set_double(detected_lines, "xpos_rectified_iter",
9588  oldsize + idline, cpl_bivector_get_x_data(peaks_ident)[idline] + 1);
9589  cpl_table_set_double(detected_lines, "ypos_rectified_iter",
9590  oldsize + idline, (double)i + 1);
9591  cpl_table_set_double(detected_lines, "peak_flux",
9592  oldsize + idline,
9593  sdata[i*nx+(int)(cpl_bivector_get_x_data(peaks_ident)[idline]+0.5)]);
9594  cpl_table_set_double(detected_lines, "wave_ident_iter",
9595  oldsize + idline, wave_ident);
9596  cpl_table_set_double(detected_lines, "xpos_fit_rect_wavecal",
9597  oldsize + idline, xpix_fit + 1);
9598  cpl_table_set_int(detected_lines,
9599  "fit_used",
9600  oldsize + idline, 0);
9601  for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
9602  {
9603  if(cpl_bivector_get_x_data(peaks_ident)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
9604  cpl_table_set_int(detected_lines,
9605  "fit_used",
9606  oldsize + idline, 1);
9607  }
9608  }
9609  }
9610 
9611 
9612  for (k = 0; k <= order; k++) {
9613  if (k > uorder) {
9614  cpl_table_set_double(idscoeff, clab[k], i, 0.0);
9615  }
9616  else {
9617  cpl_table_set_double(idscoeff, clab[k], i,
9618  cpl_polynomial_get_coeff(ids, &k));
9619  }
9620  }
9621 
9622  cpl_bivector_delete(peaks_ident);
9623  cpl_polynomial_delete(ids);
9624 
9625  } /* End of loop "use ids as a first-guess" */
9626 
9627  cpl_polynomial_delete(fguess);
9628  }
9629 
9630  cpl_table_delete(coeff);
9631 
9632  }
9633 
9634  row_top = row_bot;
9635 
9636  } /* End of loop on slits */
9637 
9638 
9639  /*
9640  * At this point the idscoeff table has been filled with all the
9641  * fits coefficients obtained for all the rows of the input image.
9642  * Now we apply these coefficients to resample the input image
9643  * at constant wavelength step.
9644  */
9645 
9646  for (i = 0; i < ny; i++) {
9647 
9648  missing = 0;
9649  ids = cpl_polynomial_new(1);
9650  for (k = 0; k <= order; k++) {
9651  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
9652  if (null) {
9653  cpl_polynomial_delete(ids);
9654  missing = 1;
9655  break;
9656  }
9657  cpl_polynomial_set_coeff(ids, &k, c);
9658  }
9659  if (missing)
9660  continue;
9661 
9662  pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
9663  pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
9664  if (pixstart < 0)
9665  pixstart = 0;
9666  if (pixend > nx)
9667  pixend = nx;
9668 
9669  /*
9670  * Resampled image:
9671  */
9672 
9673  for (j = 0; j < nl; j++) {
9674  lambda = firstLambda + j * dispersion;
9675  fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
9676  pixel = fpixel;
9677  if (pixel >= 0 && pixel < nx-1) {
9678  v1 = (sdata + i*nx)[pixel];
9679  v2 = (sdata + i*nx)[pixel+1];
9680  vi = v1 + (v2-v1)*(fpixel-pixel);
9681  (rdata + i*nl)[j] = vi;
9682  }
9683  }
9684 
9685  cpl_polynomial_delete(ids);
9686  }
9687 
9688  return resampled;
9689 }
9690 
9691 
9718 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave,
9719  double firstLambda, double lastLambda,
9720  double dispersion, cpl_table *idscoeff,
9721  int flux)
9722 {
9723 
9724  const char *func = "mos_wavelength_calibration";
9725 
9726  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
9727  /* Max order is 5 */
9728 
9729  cpl_image *resampled;
9730  cpl_polynomial *ids;
9731  double pixel_per_lambda;
9732  double lambda;
9733  double c;
9734  float *sdata;
9735  float *rdata;
9736  float v0, v1, v2, v3, vi;
9737  float fpixel;
9738  int order;
9739  int pixstart, pixend;
9740  int nl, nx, ny, pixel;
9741  int missing;
9742  int null;
9743  int i, j;
9744  cpl_size k;
9745 
9746 
9747  if (dispersion <= 0.0) {
9748  cpl_msg_error(func, "The resampling step must be positive");
9749  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9750  return NULL;
9751  }
9752 
9753  if (lastLambda - firstLambda < dispersion) {
9754  cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f",
9755  firstLambda, lastLambda);
9756  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
9757  return NULL;
9758  }
9759 
9760  if (idscoeff == NULL) {
9761  cpl_msg_error(func, "An IDS coeff table must be given");
9762  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9763  return NULL;
9764  }
9765 
9766  if (image == NULL) {
9767  cpl_msg_error(func, "A scientific spectral image must be given");
9768  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
9769  return NULL;
9770  }
9771 
9772  nx = cpl_image_get_size_x(image);
9773  ny = cpl_image_get_size_y(image);
9774  sdata = cpl_image_get_data_float(image);
9775 
9776  nl = (lastLambda - firstLambda) / dispersion;
9777  resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
9778  rdata = cpl_image_get_data_float(resampled);
9779 
9780  order = 0;
9781  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
9782  ++order;
9783  --order;
9784 
9785  for (i = 0; i < ny; i++) {
9786 
9787  missing = 0;
9788  ids = cpl_polynomial_new(1);
9789  for (k = 0; k <= order; k++) {
9790  c = cpl_table_get_double(idscoeff, clab[k], i, &null);
9791  if (null) {
9792  cpl_polynomial_delete(ids);
9793  missing = 1;
9794  break;
9795  }
9796  cpl_polynomial_set_coeff(ids, &k, c);
9797  }
9798  if (missing)
9799  continue;
9800 
9801  pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
9802  pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
9803  if (pixstart < 0)
9804  pixstart = 0;
9805  if (pixend > nx)
9806  pixend = nx;
9807 
9808  /*
9809  * Resampled image:
9810  */
9811 
9812  for (j = 0; j < nl; j++) {
9813  lambda = firstLambda + j * dispersion;
9814  fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave,
9815  &pixel_per_lambda);
9816 
9817  /*
9818  * The local dispersion is 1 / pixel_per_lambda
9819  * and this factor is used for applying the flux
9820  * conservation correction (if requested).
9821  */
9822 
9823  pixel = fpixel;
9824 
9825  // if (dispersion * pixel_per_lambda < 2.0) {
9826  if (1) { /* Old behaviour: this is safe. */
9827 
9828  /*
9829  * In this case we just sample interpolating the
9830  * signal of nearby pixels.
9831  */
9832 
9833  //the wave calibration should be a monotonically increasing
9834  //function. If the detivative is negative, we are outside of
9835  //the wavelength solution domain.
9836  if(pixel_per_lambda <= 0)
9837  vi = 0;
9838  else if (fpixel < 0)
9839  vi = 0;
9840  else if (pixel >= 1 && pixel < nx-2) {
9841  v0 = (sdata + i*nx)[pixel-1];
9842  v1 = (sdata + i*nx)[pixel];
9843  v2 = (sdata + i*nx)[pixel+1];
9844  v3 = (sdata + i*nx)[pixel+2];
9845  vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
9846  + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
9847  + 2*v1;
9848  vi /= 2;
9849  if (v1 > v2) {
9850  if (vi > v1) {
9851  vi = v1;
9852  }
9853  else if (vi < v2) {
9854  vi = v2;
9855  }
9856  }
9857  else {
9858  if (vi > v2) {
9859  vi = v2;
9860  }
9861  else if (vi < v1) {
9862  vi = v1;
9863  }
9864  }
9865  if (flux)
9866  vi *= dispersion * pixel_per_lambda;
9867  }
9868  else if (pixel >= 0 && pixel < nx-1) {
9869  v1 = (sdata + i*nx)[pixel];
9870  v2 = (sdata + i*nx)[pixel+1];
9871  vi = v1 + (v2-v1)*(fpixel-pixel);
9872  if (flux)
9873  vi *= dispersion * pixel_per_lambda;
9874  }
9875  else
9876  vi = 0;
9877  (rdata + i*nl)[j] = vi;
9878  }
9879  else {
9880 
9881  /*
9882  * Here instead we integrate the pixel values in
9883  * the interval centered at the interpolation point.
9884  * This interval is long dispersion * pixel_per_lambda
9885  * of the original pixels, and is centered at fpixel.
9886  * So it starts at fpixel - dispersion * pixel_per_lambda / 2,
9887  * and it ends at fpixel + dispersion * pixel_per_lambda / 2.
9888  */
9889 
9890  double spos = fpixel - dispersion * pixel_per_lambda / 2;
9891  double epos = fpixel + dispersion * pixel_per_lambda / 2;
9892 
9893  /*
9894  * Brutal sum over all involved pixels
9895  */
9896 
9897  int spix = spos;
9898  int epix = epos + 1;
9899 
9900  if (spix < 0)
9901  spix = 0;
9902 
9903  if (epix > nx)
9904  epix = nx;
9905 
9906  vi = 0.0;
9907  for (k = spix; k < epix; k++) {
9908  if (pixel >= 0 && pixel < nx) {
9909  vi += (sdata + i*nx)[k];
9910  }
9911  }
9912 
9913  /*
9914  * Correct integrated flux by true length
9915  * of interval. This is clearly an approximation,
9916  * but it's good enough if the PSF is much larger
9917  * than the original pix.
9918  */
9919 
9920  vi *= dispersion * pixel_per_lambda / (epix - spix);
9921 
9922  /*
9923  * Flux conservation is a geometric factor that is applied
9924  * always in the same way...
9925  */
9926 
9927  if (flux)
9928  vi *= dispersion * pixel_per_lambda;
9929 
9930  (rdata + i*nl)[j] = vi;
9931  }
9932  }
9933 
9934  cpl_polynomial_delete(ids);
9935  }
9936 
9937  return resampled;
9938 }
9939 
9940 
10007 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits,
10008  double refwave, double firstLambda,
10009  double lastLambda, cpl_table *idscoeff,
10010  cpl_vector *skylines, int highres, int order,
10011  cpl_image *calibration, int sradius)
10012 {
10013  const char *func = "mos_wavelength_align";
10014 
10015  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10016  /* Max order is 5 */
10017  double *line;
10018  double *data;
10019  double expPos, offset;
10020  double c;
10021  double lambda1, lambda2;
10022  double rms;
10023  float pos;
10024  float *sdata;
10025  float *cdata;
10026  int *idata;
10027  int startPos, endPos;
10028  int window = 2*sradius + 1;
10029  int nlines;
10030  int nslits;
10031  int npoints;
10032  int nrows;
10033  int nx, ny;
10034  int xlow, ylow, xhig, yhig;
10035  int idsorder, uorder;
10036  int *slit_id;
10037  int *position;
10038  int *length;
10039  int missing;
10040  int null;
10041  int i;
10042  cpl_size j, k;
10043 
10044  char offname[MAX_COLNAME];
10045  char name[MAX_COLNAME];
10046 
10047  cpl_polynomial *ids;
10048  cpl_polynomial *polycorr;
10049  cpl_image *exslit;
10050  cpl_image *sky;
10051  cpl_table *offsets;
10052  cpl_table *dummy;
10053  cpl_vector *wave;
10054  cpl_vector *offs;
10055 
10056 
10057  if (idscoeff == NULL) {
10058  cpl_msg_error(func, "An IDS coeff table must be given");
10059  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10060  return NULL;
10061  }
10062 
10063  if (image == NULL) {
10064  cpl_msg_error(func, "A scientific spectral image must be given");
10065  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10066  return NULL;
10067  }
10068 
10069  if (slits == NULL) {
10070  cpl_msg_error(func, "A slit position table must be given");
10071  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10072  return NULL;
10073  }
10074 
10075  if (skylines) {
10076  line = cpl_vector_get_data(skylines);
10077  nlines = cpl_vector_get_size(skylines);
10078  }
10079  else {
10080  cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10081  "given: using internal list of reference sky lines");
10082  if (highres) {
10083  line = default_lines_hi;
10084  nlines = sizeof(default_lines_hi) / sizeof(double);
10085  }
10086  else {
10087  line = default_lines_lo;
10088  nlines = sizeof(default_lines_lo) / sizeof(double);
10089  }
10090  }
10091 
10092  if (calibration)
10093  cdata = cpl_image_get_data(calibration);
10094 
10095  nx = cpl_image_get_size_x(image);
10096  ny = cpl_image_get_size_y(image);
10097 
10098  nslits = cpl_table_get_nrow(slits);
10099  slit_id = cpl_table_get_data_int(slits, "slit_id");
10100  position = cpl_table_get_data_int(slits, "position");
10101  length = cpl_table_get_data_int(slits, "length");
10102 
10103 
10104  /*
10105  * Define the output table of offsets
10106  */
10107 
10108  nrows = 0;
10109  for (i = 0; i < nlines; i++)
10110  if (line[i] > firstLambda && line[i] < lastLambda)
10111  nrows++;
10112 
10113  offsets = cpl_table_new(nrows);
10114  cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10115  cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10116 
10117  nrows = 0;
10118  for (i = 0; i < nlines; i++) {
10119  if (line[i] > firstLambda && line[i] < lastLambda) {
10120  cpl_table_set_double(offsets, "wave", nrows, line[i]);
10121  nrows++;
10122  }
10123  }
10124 
10125  /*
10126  * Here "line" is made to point to the new list of selected wavelengths
10127  */
10128 
10129  line = cpl_table_get_data_double(offsets, "wave");
10130  nlines = nrows;
10131 
10132  idsorder = 0;
10133  while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10134  ++idsorder;
10135  --idsorder;
10136 
10137  xlow = 1;
10138  xhig = nx;
10139  for (i = 0; i < nslits; i++) {
10140 
10141  if (length[i] == 0)
10142  continue;
10143 
10144  snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
10145  cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
10146 
10147  /*
10148  * Define the extraction boundaries. We DON'T write:
10149  *
10150  * ylow = position[i];
10151  * yhig = ylow + length[i];
10152  *
10153  * because the cpl_image pixels are counted from 1, and because in
10154  * cpl_image_extract() the coordinates of the last pixel are inclusive.
10155  */
10156 
10157  ylow = position[i] + 1;
10158  yhig = ylow + length[i] - 1;
10159 
10160  exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
10161  sky = cpl_image_collapse_median_create(exslit, 0, 0, 1);
10162  sdata = cpl_image_get_data(sky);
10163 
10164  cpl_image_delete(exslit);
10165 
10166  /*
10167  * Return here to a decent way of counting pixels (i.e., starting
10168  * from 0)
10169  */
10170 
10171  ylow--;
10172 
10173  /*
10174  * Allocate a dummy table for collecting all the offsets
10175  * for all the lines: this is only needed for the computation
10176  * of the median offset for each sky line
10177  */
10178 
10179  dummy = cpl_table_new(yhig - ylow);
10180  for (j = 0; j < nlines; j++) {
10181  snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
10182  cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10183  }
10184 
10185  for (j = ylow; j < yhig; j++) {
10186 
10187  /*
10188  * Get the IDS polynomial for the current slit row
10189  */
10190 
10191  missing = 0;
10192  ids = cpl_polynomial_new(1);
10193  for (k = 0; k <= idsorder; k++) {
10194  c = cpl_table_get_double(idscoeff, clab[k], j, &null);
10195  if (null) {
10196  cpl_polynomial_delete(ids);
10197  missing = 1;
10198  break;
10199  }
10200  cpl_polynomial_set_coeff(ids, &k, c);
10201  }
10202  if (missing)
10203  continue;
10204 
10205  for (k = 0; k < nlines; k++) {
10206  expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10207  startPos = expPos - sradius;
10208  endPos = startPos + window;
10209  if (startPos < 0 || endPos >= nx)
10210  continue;
10211 
10212  if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10213  pos += startPos;
10214  offset = pos - expPos;
10215  snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, k);
10216  cpl_table_set_double(dummy, name, j - ylow, offset);
10217  }
10218  }
10219 
10220  cpl_polynomial_delete(ids);
10221  }
10222 
10223  cpl_image_delete(sky);
10224 
10225  for (j = 0; j < nlines; j++) {
10226  snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
10227  if (cpl_table_has_valid(dummy, name)) {
10228  offset = cpl_table_get_column_median(dummy, name);
10229  cpl_table_set_double(offsets, offname, j, offset);
10230  }
10231  }
10232 
10233  cpl_table_delete(dummy);
10234 
10235  }
10236 
10237 
10238  /*
10239  * In the following the input idscoeff table is modified by simply
10240  * adding the coefficients of the polynomial used to fit the sky
10241  * line residuals to the coefficients of the IDS polynomials.
10242  */
10243 
10244  for (i = 0; i < nslits; i++) {
10245 
10246  if (length[i] == 0)
10247  continue;
10248 
10249  snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
10250 
10251  /*
10252  * In the following, the "dummy" table is just a tool for
10253  * eliminating invalid points from the vectors to be fitted.
10254  */
10255 
10256  dummy = cpl_table_new(nlines);
10257  cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
10258  cpl_table_duplicate_column(dummy, "offset", offsets, offname);
10259 
10260  npoints = nlines - cpl_table_count_invalid(dummy, "offset");
10261  if (npoints == 0) {
10262  cpl_msg_warning(func, "No sky lines alignment was possible "
10263  "for slit ID=%d: no sky line found", slit_id[i]);
10264  cpl_table_delete(dummy);
10265  continue;
10266  }
10267 
10268  uorder = order;
10269  if (npoints <= uorder) {
10270  uorder = npoints - 1;
10271  if (uorder) {
10272  cpl_msg_warning(func, "Just %d sky lines detected for slit "
10273  "ID=%d, while a polynomial order %d was "
10274  "requested. Using polynomial order %d for "
10275  "this slit!", npoints, slit_id[i], order,
10276  uorder);
10277  }
10278  else {
10279  cpl_msg_warning(func, "Just %d sky lines detected for slit "
10280  "ID=%d, while a polynomial order %d was "
10281  "requested. Computing a median offset for "
10282  "this slit!", npoints, slit_id[i], order);
10283  }
10284  }
10285 
10286  cpl_table_erase_invalid(dummy);
10287 
10288  if (uorder > 1) {
10289 
10290  /*
10291  * Model offsets with polynomial fitting
10292  */
10293 
10294  wave = cpl_vector_wrap(npoints,
10295  cpl_table_get_data_double(dummy, "wave"));
10296  offs = cpl_vector_wrap(npoints,
10297  cpl_table_get_data_double(dummy, "offset"));
10298 
10299  /*
10300  * Set reference wavelength as zero point
10301  */
10302 
10303  cpl_vector_subtract_scalar(wave, refwave);
10304 
10305  polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10306 
10307  rms = sqrt(rms * (uorder + 1) / npoints);
10308 
10309  cpl_vector_unwrap(wave);
10310  cpl_vector_unwrap(offs);
10311  cpl_table_delete(dummy);
10312 
10313  /*
10314  * Now correct the coefficients of the corresponding IDS
10315  * polynomials related to this slit:
10316  */
10317 
10318  ylow = position[i];
10319  yhig = ylow + length[i];
10320 
10321  for (j = 0; j <= uorder; j++) {
10322  data = cpl_table_get_data_double(idscoeff, clab[j]);
10323  c = cpl_polynomial_get_coeff(polycorr, &j);
10324  for (k = ylow; k < yhig; k++)
10325  data[k] += c;
10326  }
10327 
10328  data = cpl_table_get_data_double(idscoeff, "error");
10329  for (k = ylow; k < yhig; k++)
10330  data[k] = sqrt(data[k]*data[k] + rms*rms);
10331 
10332  idata = cpl_table_get_data_int(idscoeff, "nlines");
10333  for (k = ylow; k < yhig; k++)
10334  idata[k] = npoints;
10335 
10336  /*
10337  * If a wavelengths map was provided, correct it to keep
10338  * into account the alignment to skylines:
10339  */
10340 
10341  if (calibration) {
10342  for (j = ylow; j < yhig; j++) {
10343  for (k = 1; k < nx; k++) {
10344  lambda1 = cdata[k - 1 + j*nx];
10345  lambda2 = cdata[k + j*nx];
10346  if (lambda1 < 1.0 || lambda2 < 1.0)
10347  continue;
10348  offset = cpl_polynomial_eval_1d(polycorr,
10349  lambda1-refwave, NULL);
10350  cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10351  }
10352  }
10353  }
10354 
10355  cpl_polynomial_delete(polycorr);
10356  }
10357  else if (uorder == 1) {
10358 
10359  /*
10360  * Model offsets with robust linear fitting
10361  */
10362 
10363  double q, m;
10364  cpl_bivector *list;
10365 
10366 
10367  wave = cpl_vector_wrap(npoints,
10368  cpl_table_get_data_double(dummy, "wave"));
10369  offs = cpl_vector_wrap(npoints,
10370  cpl_table_get_data_double(dummy, "offset"));
10371 
10372  list = cpl_bivector_wrap_vectors(wave, offs);
10373 
10374  /*
10375  * Set reference wavelength as zero point
10376  */
10377 
10378  cpl_vector_subtract_scalar(wave, refwave);
10379 
10380  robustLinearFit(list, &q, &m, &rms);
10381 
10382  rms = sqrt(rms * (uorder + 1) / npoints);
10383 
10384  cpl_bivector_unwrap_vectors(list);
10385  cpl_vector_unwrap(wave);
10386  cpl_vector_unwrap(offs);
10387  cpl_table_delete(dummy);
10388 
10389  /*
10390  * Now correct the coefficients of the corresponding IDS
10391  * polynomials related to this slit:
10392  */
10393 
10394  ylow = position[i];
10395  yhig = ylow + length[i];
10396 
10397  for (j = 0; j <= uorder; j++) {
10398  data = cpl_table_get_data_double(idscoeff, clab[j]);
10399  if (j)
10400  c = m;
10401  else
10402  c = q;
10403  for (k = ylow; k < yhig; k++)
10404  data[k] += c;
10405  }
10406 
10407  data = cpl_table_get_data_double(idscoeff, "error");
10408  for (k = ylow; k < yhig; k++)
10409  data[k] = sqrt(data[k]*data[k] + rms*rms);
10410 
10411  idata = cpl_table_get_data_int(idscoeff, "nlines");
10412  for (k = ylow; k < yhig; k++)
10413  idata[k] = npoints;
10414 
10415  /*
10416  * If a wavelengths map was provided, correct it to keep
10417  * into account the alignment to skylines:
10418  */
10419 
10420  if (calibration) {
10421  for (j = ylow; j < yhig; j++) {
10422  for (k = 1; k < nx; k++) {
10423  lambda1 = cdata[k - 1 + j*nx];
10424  lambda2 = cdata[k + j*nx];
10425  if (lambda1 < 1.0 || lambda2 < 1.0)
10426  continue;
10427  offset = q + m*(lambda1-refwave);
10428  cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10429  }
10430  }
10431  }
10432  }
10433  else {
10434 
10435  /*
10436  * Just compute median offset
10437  */
10438 
10439  offs = cpl_vector_wrap(npoints,
10440  cpl_table_get_data_double(dummy, "offset"));
10441 
10442  offset = cpl_vector_get_median_const(offs);
10443 
10444  if (npoints > 1)
10445  rms = cpl_table_get_column_stdev(dummy, "offset");
10446  else
10447  rms = 0.0;
10448 
10449  rms /= sqrt(npoints);
10450 
10451  cpl_vector_unwrap(offs);
10452  cpl_table_delete(dummy);
10453 
10454  /*
10455  * Now correct the constant term of the corresponding IDS
10456  * polynomials related to this slit:
10457  */
10458 
10459  ylow = position[i];
10460  yhig = ylow + length[i];
10461 
10462  data = cpl_table_get_data_double(idscoeff, clab[0]);
10463  for (k = ylow; k < yhig; k++)
10464  data[k] += offset;
10465 
10466  data = cpl_table_get_data_double(idscoeff, "error");
10467  for (k = ylow; k < yhig; k++)
10468  data[k] = sqrt(data[k]*data[k] + rms*rms);
10469 
10470  idata = cpl_table_get_data_int(idscoeff, "nlines");
10471  for (k = ylow; k < yhig; k++)
10472  idata[k] = npoints;
10473 
10474  /*
10475  * If a wavelengths map was provided, correct it to keep
10476  * into account the alignment to skylines. Note that
10477  * the offset must be converted from pixels to wavelengths.
10478  */
10479 
10480  if (calibration) {
10481  for (j = ylow; j < yhig; j++) {
10482  for (k = 1; k < nx; k++) {
10483  lambda1 = cdata[k - 1 + j*nx];
10484  lambda2 = cdata[k + j*nx];
10485  if (lambda1 < 1.0 || lambda2 < 1.0)
10486  continue;
10487  cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10488  }
10489  }
10490  }
10491  }
10492  }
10493 
10494  return offsets;
10495 
10496 }
10497 
10498 
10560 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave,
10561  double firstLambda, double lastLambda,
10562  cpl_table *idscoeff, cpl_vector *skylines,
10563  int highres, int order,
10564  cpl_image *calibration, int sradius)
10565 {
10566  const char *func = "mos_wavelength_align_lss";
10567 
10568  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10569  /* Max order is 5 */
10570  double *line;
10571  double *data;
10572  double *wdata;
10573  double *odata;
10574  double expPos, offset;
10575  double c;
10576  double lambda1, lambda2;
10577  double rms;
10578  float pos;
10579  float *sdata;
10580  float *cdata;
10581  int *idata;
10582  int startPos, endPos;
10583  int window = 2*sradius + 1;
10584  int nlines;
10585  int npoints;
10586  int nrows;
10587  int nx, ny;
10588  int idsorder, uorder;
10589  int missing;
10590  int i;
10591  cpl_size j, k;
10592 
10593  char name[MAX_COLNAME];
10594  char fname[MAX_COLNAME];
10595 
10596  cpl_polynomial *ids;
10597  cpl_polynomial *polycorr;
10598  cpl_table *offsets;
10599  cpl_table *fittable;
10600  cpl_table *dummy;
10601  cpl_vector *wave;
10602  cpl_vector *offs;
10603  cpl_vector *row;
10604 
10605 
10606  if (idscoeff == NULL) {
10607  cpl_msg_error(func, "An IDS coeff table must be given");
10608  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10609  return NULL;
10610  }
10611 
10612  if (image == NULL) {
10613  cpl_msg_error(func, "A scientific spectral image must be given");
10614  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10615  return NULL;
10616  }
10617 
10618  if (skylines) {
10619  line = cpl_vector_get_data(skylines);
10620  nlines = cpl_vector_get_size(skylines);
10621  }
10622  else {
10623  cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10624  "given: using internal list of reference sky lines");
10625  if (highres) {
10626  line = default_lines_hi;
10627  nlines = sizeof(default_lines_hi) / sizeof(double);
10628  }
10629  else {
10630  line = default_lines_lo;
10631  nlines = sizeof(default_lines_lo) / sizeof(double);
10632  }
10633  }
10634 
10635  if (calibration)
10636  cdata = cpl_image_get_data(calibration);
10637 
10638  nx = cpl_image_get_size_x(image);
10639  ny = cpl_image_get_size_y(image);
10640 
10641  sdata = cpl_image_get_data(image);
10642 
10643  if (ny != cpl_table_get_nrow(idscoeff)) {
10644  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10645  return NULL;
10646  }
10647 
10648 
10649  /*FIXME: This is a remnant of the adaptation of the function
10650  * mos_wavelength_align(), where an offset table was created.
10651  * I leave it here because I am in a hurry, it is just used to
10652  * hold the list of selected sky lines.
10653  *
10654  * Define table of wavelengths
10655  */
10656 
10657  nrows = 0;
10658  for (i = 0; i < nlines; i++)
10659  if (line[i] > firstLambda && line[i] < lastLambda)
10660  nrows++;
10661 
10662  offsets = cpl_table_new(nrows);
10663  cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10664  cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10665 
10666  nrows = 0;
10667  for (i = 0; i < nlines; i++) {
10668  if (line[i] > firstLambda && line[i] < lastLambda) {
10669  cpl_table_set_double(offsets, "wave", nrows, line[i]);
10670  nrows++;
10671  }
10672  }
10673 
10674  /*
10675  * Here "line" is made to point to the new list of selected wavelengths
10676  */
10677 
10678  line = cpl_table_get_data_double(offsets, "wave");
10679  nlines = nrows;
10680 
10681  idsorder = 0;
10682  while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10683  ++idsorder;
10684  --idsorder;
10685 
10686 
10687  /*
10688  * Allocate a dummy table for collecting all the offsets
10689  * for all the lines
10690  */
10691 
10692  dummy = cpl_table_new(ny);
10693  for (j = 0; j < nlines; j++) {
10694  snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10695  snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10696  cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10697  cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
10698  }
10699 
10700  for (j = 0; j < ny; j++, sdata += nx) {
10701 
10702  /*
10703  * Get the IDS polynomial for the current slit row
10704  */
10705 
10706  missing = 0;
10707  ids = cpl_polynomial_new(1);
10708  for (k = 0; k <= idsorder; k++) {
10709  c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
10710  if (missing) {
10711  cpl_polynomial_delete(ids);
10712  break;
10713  }
10714  cpl_polynomial_set_coeff(ids, &k, c);
10715  }
10716  if (missing)
10717  continue;
10718 
10719  for (k = 0; k < nlines; k++) {
10720  expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10721  startPos = expPos - sradius;
10722  endPos = startPos + window;
10723  if (startPos < 0 || endPos >= nx)
10724  continue;
10725 
10726  if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10727  pos += startPos;
10728  offset = pos - expPos;
10729  snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
10730  cpl_table_set_double(dummy, name, j, offset);
10731  }
10732  }
10733 
10734  cpl_polynomial_delete(ids);
10735  }
10736 
10737 
10738  /*
10739  * At this point for each sky line we model its offset along
10740  * the image rows using a robust linear fitting
10741  */
10742 
10743  for (j = 0; j < nlines; j++) {
10744  snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10745  snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10746  if (cpl_table_has_valid(dummy, name)) {
10747 
10748  /*
10749  * In the following, the "fittable" is just a tool for
10750  * eliminating invalid points from the vectors to be fitted.
10751  */
10752 
10753  double q, m;
10754  cpl_bivector *list;
10755 
10756  fittable = cpl_table_new(ny);
10757  cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
10758  cpl_table_set_column_unit(fittable, "row", "pixel");
10759  for (k = 0; k < ny; k++)
10760  cpl_table_set_double(fittable, "row", k, k);
10761  cpl_table_duplicate_column(fittable, "offset", dummy, name);
10762  npoints = ny - cpl_table_count_invalid(fittable, "offset");
10763  cpl_table_erase_invalid(fittable);
10764  row = cpl_vector_wrap(npoints,
10765  cpl_table_get_data_double(fittable, "row"));
10766  offs = cpl_vector_wrap(npoints,
10767  cpl_table_get_data_double(fittable, "offset"));
10768  list = cpl_bivector_wrap_vectors(row, offs);
10769  robustLinearFit(list, &q, &m, &rms);
10770  cpl_bivector_unwrap_vectors(list);
10771  cpl_vector_unwrap(row);
10772  cpl_vector_unwrap(offs);
10773  cpl_table_delete(fittable);
10774  for (k = 0; k < ny; k++)
10775  cpl_table_set_double(dummy, fname, k, q + m*k);
10776  }
10777  }
10778 
10779 
10780  /*
10781  * Now each dummy table row consists of a sequence of offsets,
10782  * one for each wavelength. A table row corresponds to an image row.
10783  * We must fit a polynomial to each one of these rows, in order to
10784  * express the offsets as a function of wavelength. The obtained
10785  * polynomial coefficients are used to correct the IDS coefficients.
10786  */
10787 
10788  for (i = 0; i < ny; i++) {
10789 
10790  if (!cpl_table_is_valid(idscoeff, clab[0], i))
10791  continue;
10792 
10793  npoints = 0;
10794  for (j = 0; j < nlines; j++) {
10795  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10796  if (cpl_table_is_valid(dummy, name, i))
10797  npoints++;
10798  }
10799 
10800  if (npoints == 0)
10801  continue;
10802 
10803  uorder = order;
10804  if (npoints <= uorder)
10805  uorder = npoints - 1;
10806 
10807  if (uorder > 1) {
10808 
10809  /*
10810  * Model offsets with polynomial fitting
10811  */
10812 
10813  wave = cpl_vector_new(npoints);
10814  wdata = cpl_vector_get_data(wave);
10815  offs = cpl_vector_new(npoints);
10816  odata = cpl_vector_get_data(offs);
10817 
10818  npoints = 0;
10819  for (j = 0; j < nlines; j++) {
10820  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10821  if (cpl_table_is_valid(dummy, name, i)) {
10822  wdata[npoints] = line[j] - refwave;
10823  odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10824  npoints++;
10825  }
10826  }
10827 
10828  polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10829 
10830  rms = sqrt(rms * (uorder + 1) / npoints);
10831 
10832  cpl_vector_delete(wave);
10833  cpl_vector_delete(offs);
10834 
10835  /*
10836  * Now correct the coefficients of the corresponding IDS
10837  * polynomials related to this slit:
10838  */
10839 
10840  for (j = 0; j <= uorder; j++) {
10841  data = cpl_table_get_data_double(idscoeff, clab[j]);
10842  c = cpl_polynomial_get_coeff(polycorr, &j);
10843  data[i] += c;
10844  }
10845 
10846  data = cpl_table_get_data_double(idscoeff, "error");
10847  data[i] = sqrt(data[i]*data[i] + rms*rms);
10848 
10849  idata = cpl_table_get_data_int(idscoeff, "nlines");
10850  idata[i] = npoints;
10851 
10852  /*
10853  * If a wavelengths map was provided, correct it to keep
10854  * into account the alignment to skylines:
10855  */
10856 
10857  if (calibration) {
10858  for (k = 1; k < nx; k++) {
10859  lambda1 = cdata[k - 1 + i*nx];
10860  lambda2 = cdata[k + i*nx];
10861  if (lambda1 < 1.0 || lambda2 < 1.0)
10862  continue;
10863  offset = cpl_polynomial_eval_1d(polycorr,
10864  lambda1-refwave, NULL);
10865  cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10866  }
10867  }
10868 
10869  cpl_polynomial_delete(polycorr);
10870 
10871  }
10872  else if (uorder == 1) {
10873 
10874  /*
10875  * Model offsets with robust linear fitting
10876  */
10877 
10878  cpl_bivector *list;
10879  double q, m;
10880 
10881  wave = cpl_vector_new(npoints);
10882  wdata = cpl_vector_get_data(wave);
10883  offs = cpl_vector_new(npoints);
10884  odata = cpl_vector_get_data(offs);
10885 
10886  npoints = 0;
10887  for (j = 0; j < nlines; j++) {
10888  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10889  if (cpl_table_is_valid(dummy, name, i)) {
10890  wdata[npoints] = line[j] - refwave;
10891  odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10892  npoints++;
10893  }
10894  }
10895 
10896  list = cpl_bivector_wrap_vectors(wave, offs);
10897  robustLinearFit(list, &q, &m, &rms);
10898 
10899  rms = sqrt(rms * (uorder + 1) / npoints);
10900 
10901  cpl_bivector_unwrap_vectors(list);
10902  cpl_vector_delete(wave);
10903  cpl_vector_delete(offs);
10904 
10905  /*
10906  * Now correct the coefficients of the corresponding IDS
10907  * polynomials related to this row:
10908  */
10909 
10910  for (j = 0; j <= uorder; j++) {
10911  data = cpl_table_get_data_double(idscoeff, clab[j]);
10912  if (j)
10913  c = m;
10914  else
10915  c = q;
10916  data[i] += c;
10917  }
10918 
10919  data = cpl_table_get_data_double(idscoeff, "error");
10920  data[i] = sqrt(data[i]*data[i] + rms*rms);
10921 
10922  idata = cpl_table_get_data_int(idscoeff, "nlines");
10923  idata[i] = npoints;
10924 
10925  /*
10926  * If a wavelengths map was provided, correct it to keep
10927  * into account the alignment to skylines:
10928  */
10929 
10930  if (calibration) {
10931  for (k = 1; k < nx; k++) {
10932  lambda1 = cdata[k - 1 + i*nx];
10933  lambda2 = cdata[k + i*nx];
10934  if (lambda1 < 1.0 || lambda2 < 1.0)
10935  continue;
10936  offset = q + m*(lambda1-refwave);
10937  cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10938  }
10939  }
10940  }
10941  else {
10942 
10943  /*
10944  * Just compute median offset
10945  */
10946 
10947  offs = cpl_vector_new(npoints);
10948  odata = cpl_vector_get_data(offs);
10949 
10950  npoints = 0;
10951  for (j = 0; j < nlines; j++) {
10952  snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10953  if (cpl_table_is_valid(dummy, name, i)) {
10954  odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10955  npoints++;
10956  }
10957  }
10958 
10959  offset = cpl_vector_get_median_const(offs);
10960 
10961  if (npoints > 1) {
10962  rms = cpl_vector_get_stdev(offs);
10963  }
10964  else if (npoints == 1) {
10965  snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
10966  if (cpl_table_has_valid(dummy, name)) {
10967  rms = cpl_table_get_column_stdev(dummy, name);
10968  rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
10969  }
10970  else {
10971  rms = 0.0;
10972  }
10973  }
10974  else {
10975  rms = 0.0;
10976  }
10977 
10978  rms /= sqrt(npoints);
10979 
10980  cpl_vector_delete(offs);
10981 
10982  /*
10983  * Now correct the constant term of the corresponding IDS
10984  * polynomials related to this slit:
10985  */
10986 
10987  data = cpl_table_get_data_double(idscoeff, clab[0]);
10988  data[i] += offset;
10989 
10990  data = cpl_table_get_data_double(idscoeff, "error");
10991  data[i] = sqrt(data[i]*data[i] + rms*rms);
10992 
10993  idata = cpl_table_get_data_int(idscoeff, "nlines");
10994  idata[i] = npoints;
10995 
10996  /*
10997  * If a wavelengths map was provided, correct it to keep
10998  * into account the alignment to skylines. Note that
10999  * the offset must be converted from pixels to wavelengths.
11000  */
11001 
11002  if (calibration) {
11003  for (k = 1; k < nx; k++) {
11004  lambda1 = cdata[k - 1 + i*nx];
11005  lambda2 = cdata[k + i*nx];
11006  if (lambda1 < 1.0 || lambda2 < 1.0)
11007  continue;
11008  cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
11009  }
11010  }
11011  }
11012  }
11013 
11014  missing = 1;
11015  for (j = 0; j < nlines; j++) {
11016  snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
11017  if (cpl_table_has_valid(dummy, name)) {
11018  missing = 0;
11019  offset = cpl_table_get_column_median(dummy, name);
11020  cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
11021  line[j], offset);
11022  }
11023  else {
11024  cpl_msg_info(func,
11025  "Median offset for %.2f: not available", line[j]);
11026  }
11027  }
11028 
11029  cpl_table_delete(offsets);
11030 
11031  if (missing) {
11032  cpl_table_delete(dummy);
11033  dummy = NULL;
11034  }
11035 
11036  return dummy;
11037 
11038 }
11039 
11040 
11068 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines,
11069  double wavestart, double dispersion, int radius,
11070  int highres)
11071 {
11072 
11073  const char *func = "mos_distortions_rms";
11074 
11075  int xlen;
11076  int ylen;
11077  int numLines;
11078  int cpix, npix, nzero;
11079  int sp, ep;
11080  int i, j, k;
11081  int npeaks, allPeaks;
11082 
11083  float *profile;
11084  float peak, expectPeak, offset;
11085  double lambda;
11086 
11087  double average;
11088  double rms, oneRms;
11089 
11090  float *sdata;
11091  double *wdata;
11092 
11093 
11094  xlen = cpl_image_get_size_x(rectified);
11095  ylen = cpl_image_get_size_y(rectified);
11096  sdata = cpl_image_get_data(rectified);
11097 
11098  if (lines) {
11099  wdata = cpl_vector_get_data(lines);
11100  numLines = cpl_vector_get_size(lines);
11101  }
11102  else {
11103  cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
11104  "given: using internal list of reference sky lines");
11105  if (highres) {
11106  wdata = default_lines_hi;
11107  numLines = sizeof(default_lines_hi) / sizeof(double);
11108  }
11109  else {
11110  wdata = default_lines_lo;
11111  numLines = sizeof(default_lines_lo) / sizeof(double);
11112  }
11113  }
11114 
11115  npix = 2 * radius + 1;
11116  profile = cpl_calloc(npix, sizeof(float));
11117 
11118  rms = 0.0;
11119  allPeaks = 0;
11120 
11121  for (i = 0; i < numLines; i++) {
11122 
11123  /*
11124  * Expected peak and closest pixel to specified wavelength.
11125  */
11126 
11127  lambda = wdata[i];
11128  expectPeak = (lambda - wavestart) / dispersion;
11129  cpix = floor(expectPeak + 0.5);
11130 
11131  /*
11132  * Search interval for peak. Abort if too close to image border.
11133  */
11134 
11135  sp = cpix - radius;
11136  ep = cpix + radius;
11137 
11138  if (sp < 0 || ep > xlen)
11139  continue;
11140 
11141  average = 0.0;
11142  npeaks = 0;
11143  oneRms = 0.0;
11144 
11145  for (j = 0; j < ylen; j++) { /* For each row of each slit */
11146  nzero = 0;
11147  for (k = 0; k < npix; k++) {
11148  profile[k] = sdata[sp + k + j * xlen];
11149  if (fabs(profile[k]) < 0.0001)
11150  nzero++; /* Count number of 0 pixels (spectrum truncated) */
11151  }
11152  if (nzero > 0)
11153  continue;
11154 
11155  if (peakPosition(profile, npix, &peak, 1) == 0) {
11156  offset = (sp + peak) - expectPeak;
11157  average += offset;
11158  rms += fabs(offset);
11159  oneRms += fabs(offset);
11160  npeaks++;
11161  allPeaks++;
11162  }
11163  }
11164 
11165  if (npeaks)
11166  cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
11167  lambda, oneRms / npeaks * 1.25, npeaks);
11168  else
11169  cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
11170  }
11171 
11172  cpl_free(profile);
11173 
11174  if (allPeaks < 10)
11175  return 0.0;
11176 
11177  rms /= allPeaks;
11178  rms *= 1.25; /* Factor to convert average deviation to sigma */
11179 
11180  return rms;
11181 
11182 }
11183 
11184 
11205 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
11206  double blue, double red, double dispersion, int trend)
11207 {
11208  const char *func = "mos_map_pixel";
11209 
11210  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11211  /* Max order is 5 */
11212 
11213  cpl_polynomial *ids;
11214  cpl_image *map;
11215  float *mdata;
11216  double lambda;
11217  double c;
11218  int order;
11219  int xsize, ysize;
11220  int missing;
11221  int i, j;
11222  cpl_size k;
11223 
11224 
11225  if (idscoeff == NULL) {
11226  cpl_msg_error(func, "An IDS coeff table must be given");
11227  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11228  return NULL;
11229  }
11230 
11231  xsize = (red - blue) / dispersion;
11232  ysize = cpl_table_get_nrow(idscoeff);
11233  map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
11234  mdata = cpl_image_get_data(map);
11235 
11236  order = 0;
11237  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
11238  ++order;
11239  --order;
11240 
11241  for (i = 0; i < ysize; i++, mdata += xsize) {
11242 
11243  missing = 0;
11244  ids = cpl_polynomial_new(1);
11245  for (k = trend; k <= order; k++) {
11246  c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
11247  if (missing) {
11248  cpl_polynomial_delete(ids);
11249  break;
11250  }
11251  cpl_polynomial_set_coeff(ids, &k, c);
11252  }
11253  if (missing)
11254  continue;
11255 
11256  for (j = 0; j < xsize; j++) {
11257  lambda = blue + j*dispersion;
11258  mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
11259  }
11260 
11261  cpl_polynomial_delete(ids);
11262  }
11263 
11264  return map;
11265 
11266 }
11267 
11268 
11290 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
11291  double blue, double red)
11292 {
11293  const char *func = "mos_map_idscoeff";
11294 
11295  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11296  /* Max order is 5 */
11297 
11298  cpl_polynomial *ids;
11299  cpl_image *map;
11300  float *mdata;
11301  double lambda;
11302  double c;
11303  int order;
11304  int ysize;
11305  int missing;
11306  int i, j;
11307  cpl_size k;
11308 
11309 
11310  if (idscoeff == NULL) {
11311  cpl_msg_error(func, "An IDS coeff table must be given");
11312  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11313  return NULL;
11314  }
11315 
11316  if (xsize < 1) {
11317  cpl_msg_error(func, "Invalid image size");
11318  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11319  return NULL;
11320  }
11321 
11322  if (xsize < 20 || xsize > 5000) {
11323  cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
11324  xsize);
11325  }
11326 
11327  ysize = cpl_table_get_nrow(idscoeff);
11328  map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
11329  mdata = cpl_image_get_data(map);
11330 
11331  order = 0;
11332  while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
11333  ++order;
11334  --order;
11335 
11336  for (i = 0; i < ysize; i++, mdata += xsize) {
11337 
11338  missing = 0;
11339  ids = cpl_polynomial_new(1);
11340  for (k = 0; k <= order; k++) {
11341  c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
11342  if (missing) {
11343  cpl_polynomial_delete(ids);
11344  break;
11345  }
11346  cpl_polynomial_set_coeff(ids, &k, c);
11347  }
11348  if (missing)
11349  continue;
11350 
11351  for (j = 0; j < xsize; j++) {
11352  lambda = mos_eval_dds(ids, blue, red, reference, j);
11353 
11354  if (lambda >= blue && lambda <= red) {
11355  mdata[j] = lambda;
11356  }
11357  }
11358 
11359  cpl_polynomial_delete(ids);
11360  }
11361 
11362  return map;
11363 
11364 }
11365 
11366 
11401 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
11402  cpl_table *slits, cpl_table *polytraces,
11403  double reference, double blue, double red,
11404  double dispersion)
11405 {
11406  const char *func = "mos_map_wavelengths";
11407 
11408  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11409  /* Max order is 5 */
11410  cpl_polynomial *polytop;
11411  cpl_polynomial *polybot;
11412  cpl_image *remapped;
11413  float *data;
11414  float *wdata;
11415  float *sdata;
11416  float *xdata;
11417  double vtop, vbot, value;
11418  double top, bot;
11419  double coeff;
11420  double ytop, ybot;
11421  double ypos;
11422  double fvalue;
11423  int ivalue;
11424  int yint, ysize, yprev;
11425  int nslits;
11426  int npseudo;
11427  int *slit_id;
11428  int *position;
11429  int *length;
11430  int nx, ny;
11431  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11432  int missing_top, missing_bot;
11433  int null;
11434  int order;
11435  int i, j;
11436  cpl_size k;
11437 
11438 
11439  if (spatial == NULL || calibration == NULL ||
11440  slits == NULL || polytraces == NULL) {
11441  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11442  return NULL;
11443  }
11444 
11445  if (dispersion <= 0.0) {
11446  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11447  return NULL;
11448  }
11449 
11450  if (red - blue < dispersion) {
11451  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11452  return NULL;
11453  }
11454 
11455  nx = cpl_image_get_size_x(spatial);
11456  ny = cpl_image_get_size_y(spatial);
11457  ysize = cpl_image_get_size_y(calibration);
11458  remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
11459  data = cpl_image_get_data(remapped);
11460  sdata = cpl_image_get_data(spatial);
11461  wdata = cpl_image_get_data(calibration);
11462 
11463  nslits = cpl_table_get_nrow(slits);
11464  slit_id = cpl_table_get_data_int(slits, "slit_id");
11465  order = cpl_table_get_ncol(polytraces) - 2;
11466  position = cpl_table_get_data_int(slits, "position");
11467  length = cpl_table_get_data_int(slits, "length");
11468 
11469  /*
11470  * The spatial resampling is performed for a certain number of
11471  * pixels above and below the position of the reference wavelength:
11472  */
11473 
11474  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11475  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11476 
11477  for (i = 0; i < nslits; i++) {
11478 
11479  if (length[i] == 0)
11480  continue;
11481 
11482  /*
11483  * Note that the x coordinate of the reference pixels on the CCD
11484  * is taken arbitrarily at the top end of each slit. This wouldn't
11485  * be entirely correct in case of curved slits, or in presence of
11486  * heavy distortions: in such cases the spatial resampling is
11487  * really performed across a wide range of wavelengths. But
11488  * the lag between top and bottom spectral curvature models
11489  * would introduce even in such cases negligible effects on
11490  * the spectral spatial resampling.
11491  */
11492 
11493  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11494 
11495  start_pixel = refpixel - pixel_below;
11496  if (start_pixel < 0)
11497  start_pixel = 0;
11498 
11499  end_pixel = refpixel + pixel_above;
11500  if (end_pixel > nx)
11501  end_pixel = nx;
11502 
11503  /*
11504  * Recover from the table of spectral curvature coefficients
11505  * the curvature polynomials.
11506  */
11507 
11508  missing_top = 0;
11509  polytop = cpl_polynomial_new(1);
11510  for (k = 0; k <= order; k++) {
11511  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11512  if (null) {
11513  cpl_polynomial_delete(polytop);
11514  missing_top = 1;
11515  break;
11516  }
11517  cpl_polynomial_set_coeff(polytop, &k, coeff);
11518  }
11519 
11520  missing_bot = 0;
11521  polybot = cpl_polynomial_new(1);
11522  for (k = 0; k <= order; k++) {
11523  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11524  if (null) {
11525  cpl_polynomial_delete(polybot);
11526  missing_bot = 1;
11527  break;
11528  }
11529  cpl_polynomial_set_coeff(polybot, &k, coeff);
11530  }
11531 
11532  if (missing_top && missing_bot) {
11533  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11534  slit_id[i]);
11535  continue;
11536  }
11537 
11538  /*
11539  * In case just one of the two edges was not traced, the other
11540  * edge curvature model is duplicated and shifted to the other
11541  * end of the slit: better than nothing!
11542  */
11543 
11544  if (missing_top) {
11545  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11546  "the spectral curvature of the lower edge "
11547  "is used instead.", slit_id[i]);
11548  polytop = cpl_polynomial_duplicate(polybot);
11549  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11550  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11551  k = 0;
11552  coeff = cpl_polynomial_get_coeff(polybot, &k);
11553  coeff += ytop - ybot;
11554  cpl_polynomial_set_coeff(polytop, &k, coeff);
11555  }
11556 
11557  if (missing_bot) {
11558  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11559  "the spectral curvature of the upper edge "
11560  "is used instead.", slit_id[i]);
11561  polybot = cpl_polynomial_duplicate(polytop);
11562  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11563  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11564  k = 0;
11565  coeff = cpl_polynomial_get_coeff(polytop, &k);
11566  coeff -= ytop - ybot;
11567  cpl_polynomial_set_coeff(polybot, &k, coeff);
11568  }
11569 
11570  /*
11571  * Point to current slit on wavelength calibration image.
11572  * Note that the npseudo value related to this slit is equal
11573  * to the number of spatial pseudo-pixels decreased by 1
11574  * (compare with function mos_spatial_calibration()).
11575  */
11576 
11577  xdata = wdata + nx*position[i];
11578  npseudo = length[i] - 1;
11579 
11580  /*
11581  * Write interpolated wavelengths to CCD image
11582  */
11583 
11584  for (j = start_pixel; j < end_pixel; j++) {
11585  top = cpl_polynomial_eval_1d(polytop, j, NULL);
11586  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
11587  for (k = 0; k <= npseudo; k++) {
11588  ypos = top - k*(top-bot)/npseudo;
11589  yint = ypos;
11590 
11591  /*
11592  * The line:
11593  * value = sdata[j + nx*yint];
11594  * should be equivalent to:
11595  * value = npseudo*(top-yint)/(top-bot);
11596  */
11597 
11598  if (yint < 0 || yint >= ny-1) {
11599  yprev = yint;
11600  continue;
11601  }
11602 
11603  value = sdata[j + nx*yint]; /* Spatial coordinate on CCD */
11604  ivalue = value; /* Nearest spatial pixels: */
11605  fvalue = value - ivalue; /* ivalue and ivalue+1 */
11606  if (ivalue < npseudo && ivalue >= 0) {
11607  vtop = xdata[j + nx*(npseudo-ivalue)];
11608  vbot = xdata[j + nx*(npseudo-ivalue-1)];
11609  if (vtop < 1.0) { /* Impossible wavelength */
11610  if (vbot < 1.0) {
11611  value = 0.0;
11612  }
11613  else {
11614  value = vbot;
11615  }
11616  }
11617  else if (vbot < 1.0) {
11618  if (k)
11619  value = vtop;
11620  else
11621  value = 0.0;
11622  }
11623  else if (fabs(vbot-vtop) > 10*dispersion) {
11624  value = 0.0;
11625  }
11626  else {
11627  value = vtop*(1-fvalue) + vbot*fvalue;
11628  }
11629  data[j + nx*yint] = value;
11630 
11631  if (k) {
11632 
11633  /*
11634  * This is added to recover lost pixels on
11635  * the CCD image (pixels are lost because
11636  * the CCD pixels are less than npseudo+1).
11637  */
11638 
11639  if (yprev - yint > 1) {
11640  value = sdata[j + nx*(yint+1)];
11641  ivalue = value;
11642  fvalue = value - ivalue;
11643  if (ivalue < npseudo && ivalue >= 0) {
11644  vtop = xdata[j + nx*(npseudo-ivalue)];
11645  vbot = xdata[j + nx*(npseudo-ivalue-1)];
11646  if (vtop < 1.0) {
11647  if (vbot < 1.0) {
11648  value = data[j + nx*(yint+1)];
11649  }
11650  else {
11651  value = vbot;
11652  }
11653  }
11654  else if (vbot < 1.0) {
11655  value = vtop;
11656  }
11657  else if (fabs(vbot-vtop) > 2*dispersion) {
11658  value = vtop;
11659  }
11660  else {
11661  value = vtop*(1-fvalue) + vbot*fvalue;
11662  }
11663  data[j + nx*(yint+1)] = value;
11664  }
11665  }
11666  }
11667  }
11668  yprev = yint;
11669  }
11670  }
11671  cpl_polynomial_delete(polytop);
11672  cpl_polynomial_delete(polybot);
11673  }
11674 
11675  return remapped;
11676 }
11677 
11751 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib,
11752  cpl_image *spatial, cpl_table *slits,
11753  cpl_table *polytraces, double reference,
11754  double blue, double red, double dispersion,
11755  int flux)
11756 {
11757  const char *func = "mos_map_spectrum";
11758 
11759  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11760  /* Max order is 5 */
11761  cpl_polynomial *polytop;
11762  cpl_polynomial *polybot;
11763  cpl_image *remapped;
11764  cpl_image **exslit;
11765  float *data;
11766  float *wdata;
11767  float *sdata;
11768  float *xdata;
11769  double lambda00, lambda01, lambda10, lambda11, lambda;
11770  double space00, space01, space10, space11, space;
11771  double value00, value01, value10, value11, value0, value1, value;
11772  double dL, dS;
11773  double top, bot;
11774  double coeff;
11775  double ytop, ybot;
11776  double xfrac, yfrac;
11777  int yint, ysize;
11778  int itop, ibot;
11779  int shift;
11780  int L, S;
11781  int nslits;
11782  int npseudo;
11783  int *slit_id;
11784  int *position;
11785  int *length;
11786  int nx, ny;
11787  int x, y;
11788  int nlambda;
11789  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11790  int missing_top, missing_bot;
11791  int null;
11792  int order;
11793  int i;
11794  cpl_size k;
11795 
11796 
11797  flux += flux;
11798 
11799  if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
11800  slits == NULL || polytraces == NULL) {
11801  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11802  return NULL;
11803  }
11804 
11805  if (dispersion <= 0.0) {
11806  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11807  return NULL;
11808  }
11809 
11810  if (red - blue < dispersion) {
11811  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11812  return NULL;
11813  }
11814 
11815  nx = cpl_image_get_size_x(spectra);
11816  ny = cpl_image_get_size_y(spectra);
11817 
11818  if (nx != cpl_image_get_size_x(spatial) ||
11819  ny != cpl_image_get_size_y(spatial) ||
11820  nx != cpl_image_get_size_x(wavecalib) ||
11821  ny != cpl_image_get_size_y(wavecalib)) {
11822  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11823  return NULL;
11824  }
11825 
11826  nlambda = STRETCH_FACTOR * (red - blue) / dispersion;
11827  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11828  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11829 
11830  data = cpl_image_get_data(spectra);
11831  sdata = cpl_image_get_data(spatial);
11832  wdata = cpl_image_get_data(wavecalib);
11833 
11834  nslits = cpl_table_get_nrow(slits);
11835  slit_id = cpl_table_get_data_int(slits, "slit_id");
11836  order = cpl_table_get_ncol(polytraces) - 2;
11837  position = cpl_table_get_data_int(slits, "position");
11838  length = cpl_table_get_data_int(slits, "length");
11839 
11840  exslit = cpl_calloc(nslits, sizeof(cpl_image *));
11841 
11842  for (i = 0; i < nslits; i++) {
11843 
11844  if (length == 0)
11845  continue;
11846 
11847  /*
11848  * Note that the x coordinate of the reference pixels on the CCD
11849  * is taken arbitrarily at the top end of each slit. This wouldn't
11850  * be entirely correct in case of curved slits, or in presence of
11851  * heavy distortions: in such cases the spatial resampling is
11852  * really performed across a wide range of wavelengths. But
11853  * the lag between top and bottom spectral curvature models
11854  * would introduce even in such cases negligible effects on
11855  * the spectral spatial resampling.
11856  */
11857 
11858  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11859 
11860  start_pixel = refpixel - pixel_below;
11861  if (start_pixel < 1)
11862  start_pixel = 1;
11863 
11864  end_pixel = refpixel + pixel_above;
11865  if (end_pixel > nx)
11866  end_pixel = nx;
11867 
11868  /*
11869  * Recover from the table of spectral curvature coefficients
11870  * the curvature polynomials.
11871  */
11872 
11873  missing_top = 0;
11874  polytop = cpl_polynomial_new(1);
11875  for (k = 0; k <= order; k++) {
11876  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11877  if (null) {
11878  cpl_polynomial_delete(polytop);
11879  missing_top = 1;
11880  break;
11881  }
11882  cpl_polynomial_set_coeff(polytop, &k, coeff);
11883  }
11884 
11885  missing_bot = 0;
11886  polybot = cpl_polynomial_new(1);
11887  for (k = 0; k <= order; k++) {
11888  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11889  if (null) {
11890  cpl_polynomial_delete(polybot);
11891  missing_bot = 1;
11892  break;
11893  }
11894  cpl_polynomial_set_coeff(polybot, &k, coeff);
11895  }
11896 
11897  if (missing_top && missing_bot) {
11898  cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11899  slit_id[i]);
11900  continue;
11901  }
11902 
11903  /*
11904  * In case just one of the two edges was not traced, the other
11905  * edge curvature model is duplicated and shifted to the other
11906  * end of the slit: better than nothing!
11907  */
11908 
11909  if (missing_top) {
11910  cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11911  "the spectral curvature of the lower edge "
11912  "is used instead.", slit_id[i]);
11913  polytop = cpl_polynomial_duplicate(polybot);
11914  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11915  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11916  k = 0;
11917  coeff = cpl_polynomial_get_coeff(polybot, &k);
11918  coeff += ytop - ybot;
11919  cpl_polynomial_set_coeff(polytop, &k, coeff);
11920  }
11921 
11922  if (missing_bot) {
11923  cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11924  "the spectral curvature of the upper edge "
11925  "is used instead.", slit_id[i]);
11926  polybot = cpl_polynomial_duplicate(polytop);
11927  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11928  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11929  k = 0;
11930  coeff = cpl_polynomial_get_coeff(polytop, &k);
11931  coeff -= ytop - ybot;
11932  cpl_polynomial_set_coeff(polybot, &k, coeff);
11933  }
11934 
11935  /*
11936  * Allocate image for current extracted slit
11937  */
11938 
11939  top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
11940  bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
11941  npseudo = ceil(top-bot) + 1;
11942 
11943  if (npseudo < 1) {
11944  cpl_polynomial_delete(polytop);
11945  cpl_polynomial_delete(polybot);
11946  cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
11947  slit_id[i]);
11948  continue;
11949  }
11950 
11951  exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
11952  xdata = cpl_image_get_data(exslit[i]);
11953 
11954  /*
11955  * Write interpolated spectral values to remapped slit spectrum.
11956  */
11957 
11958  for (x = start_pixel; x < end_pixel; x++) {
11959  top = cpl_polynomial_eval_1d(polytop, x, NULL);
11960  bot = cpl_polynomial_eval_1d(polybot, x, NULL);
11961  itop = top + 1;
11962  ibot = bot;
11963  if (itop < 0)
11964  itop = 0;
11965  if (itop > ny - 1)
11966  itop = ny - 1;
11967  if (ibot < 0)
11968  ibot = 0;
11969  if (ibot > ny - 1)
11970  ibot = ny - 1;
11971  for (y = ibot; y < itop; y++) {
11972  lambda11 = wdata[x + y*nx];
11973  if (lambda11 < 1.0) /* Impossible wavelength */
11974  continue;
11975  space11 = sdata[x + y*nx];
11976  if (space11 < 0.0) /* Impossible spatial coordinate */
11977  continue;
11978  lambda01 = wdata[x - 1 + y*nx];
11979  if (lambda01 < 1.0) /* Impossible wavelength */
11980  continue;
11981  space01 = sdata[x - 1 + y*nx];
11982  if (space01 < 0.0) /* Impossible spatial coordinate */
11983  continue;
11984 
11985  shift = 0;
11986 
11987 /****+
11988  if (wdata[x + (y+1)*nx] > 1.0) {
11989  if (wdata[x + (y+1)*nx] - lambda11 > 0) {
11990  shift = -1;
11991  while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
11992  shift--;
11993  if (lambda11 - wdata[x + shift + (y+1)*nx] >
11994  wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
11995  shift++;
11996  }
11997  }
11998  else {
11999  shift = 1;
12000  while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
12001  shift++;
12002  if (wdata[x + shift + (y+1)*nx] - lambda11 >
12003  lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
12004  shift--;
12005  }
12006  }
12007  }
12008 ****/
12009 
12010 /****
12011 printf("y = %d, shift = %d\n", y, shift);
12012 ****/
12013 
12014  lambda10 = wdata[x + shift + (y+1)*nx];
12015  if (lambda10 < 1.0) /* Impossible wavelength */
12016  continue;
12017  space10 = sdata[x + shift + (y+1)*nx];
12018  if (space10 < 0.0) /* Impossible spatial coordinate */
12019  continue;
12020  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
12021  if (lambda00 < 1.0) /* Impossible wavelength */
12022  continue;
12023  space00 = sdata[x - 1 + shift + (y+1)*nx];
12024  if (space00 < 0.0) /* Impossible spatial coordinate */
12025  continue;
12026 
12027  /*
12028  * Find the variation in lambda and space in this
12029  * position for each CCD pixel (both quantities are
12030  * expected to be positive).
12031  */
12032 
12033  dL = lambda11 - lambda01;
12034  dS = space11 - space10;
12035 
12036  /*
12037  * Find the position (L,S) of the output pixel
12038  * (by integer truncation).
12039  */
12040 
12041  L = (lambda11 - blue)/dispersion + 0.5;
12042  S = space11 + 0.5; /* Counted from top! */
12043 
12044  if (L < 0 || L >= nlambda)
12045  continue;
12046  if (S < 0 || S > npseudo)
12047  continue;
12048 
12049  /*
12050  * Find the coordinate of pixel (L,S)
12051  */
12052 
12053  lambda = blue + L*dispersion;
12054  space = S;
12055 
12056  /*
12057  * Find the interpolation point on the CCD: it is
12058  * defined as the (positive) distance from current
12059  * CCD pixel (x,y) of the interpolation point (x',y'),
12060  * measured in CCD pixels. The interpolation point
12061  * is located between the four CCD pixels selected
12062  * above.
12063  */
12064 
12065  xfrac = (lambda11-lambda)/dL;
12066  yfrac = (space11-space)/dS;
12067 
12068 /*
12069 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
12070 printf("xyfrac = %f, %f\n", xfrac, yfrac);
12071 */
12072 
12073  /*
12074  * Get the four values to interpolate
12075  */
12076 
12077  value11 = data[x + y*nx];
12078  value01 = data[x - 1 + y*nx];
12079  value10 = data[x + shift + (y+1)*nx];
12080  value00 = data[x + shift - 1 + (y+1)*nx];
12081 
12082  /*
12083  * Interpolation
12084  */
12085 
12086  value1 = (1-xfrac)*value11 + xfrac*value01;
12087  value0 = (1-xfrac)*value10 + xfrac*value00;
12088  value = (1-yfrac)*value1 + yfrac*value0;
12089 
12090  /*
12091  * Write this value to the appropriate (L,S) coordinate
12092  * on output slit
12093  */
12094 
12095  xdata[L + nlambda*(npseudo-S)] = value;
12096 
12097  }
12098  }
12099  cpl_polynomial_delete(polytop);
12100  cpl_polynomial_delete(polybot);
12101  }
12102 
12103  /*
12104  * Now all the slits images are copied to a single image
12105  */
12106 
12107  ysize = 0;
12108  for (i = 0; i < nslits; i++)
12109  if (exslit[i])
12110  ysize += cpl_image_get_size_y(exslit[i]);
12111 
12112  remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
12113 
12114  yint = -1;
12115  for (i = 0; i < nslits; i++) {
12116  if (exslit[i]) {
12117  yint += cpl_image_get_size_y(exslit[i]);
12118  cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
12119  cpl_image_delete(exslit[i]);
12120  cpl_table_set_int(slits, "position", i, ysize - yint - 1);
12121  }
12122  }
12123 
12124  cpl_free(exslit);
12125 
12126  return remapped;
12127 
12128 }
12129 
12130 
12163 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
12164  double dispersion, double factor, int minpoints,
12165  cpl_image *skymap)
12166 {
12167  const char *func = "mos_sky_map_super";
12168 
12169  cpl_vector **vector;
12170  cpl_vector **wvector;
12171  double firstLambda, lastLambda;
12172  double lambda, lambda1, lambda2;
12173  double value, value1, value2;
12174  double frac;
12175  float min, max;
12176  int *count;
12177  int nbin, bin;
12178  int nx, ny, npix;
12179  int first_valid, valid_bins;
12180  int i, j;
12181 
12182  cpl_table *sky;
12183  double *sky_spectrum;
12184  double *sky_wave;
12185  float *data;
12186  float *sdata;
12187  float *kdata;
12188 
12189 
12190  if (spectra == NULL || wavemap == NULL || skymap == NULL) {
12191  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12192  return NULL;
12193  }
12194 
12195  if (dispersion <= 0.0) {
12196  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12197  cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
12198  return NULL;
12199  }
12200 
12201  nx = cpl_image_get_size_x(spectra);
12202  ny = cpl_image_get_size_y(spectra);
12203  npix = nx * ny;
12204 
12205  if (nx != cpl_image_get_size_x(wavemap) ||
12206  ny != cpl_image_get_size_y(wavemap) ||
12207  nx != cpl_image_get_size_x(skymap) ||
12208  ny != cpl_image_get_size_y(skymap)) {
12209  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
12210  cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
12211  return NULL;
12212  }
12213 
12214  if (factor < 1.0) {
12215  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12216  cpl_msg_error(func, "Undersampling (%f): %s", factor,
12217  cpl_error_get_message());
12218  return NULL;
12219  }
12220 
12221  if (minpoints < 0) {
12222  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12223  cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
12224  return NULL;
12225  }
12226 
12227  dispersion /= factor;
12228 
12229 
12230  /*
12231  * Find bluest and reddest wavelengths in the whole image
12232  */
12233 
12234  data = cpl_image_get_data(wavemap);
12235 
12236  for (i = 0; i < npix; i++) {
12237  if (data[i] > 1.0) {
12238  min = max = data[i];
12239  j = i+1;
12240  break;
12241  }
12242  }
12243 
12244  for (i = j; i < npix; i++) {
12245  if (data[i] < 1.0) /* Impossible wavelength */
12246  continue;
12247  if (min > data[i])
12248  min = data[i];
12249  if (max < data[i])
12250  max = data[i];
12251  }
12252 
12253  firstLambda = min;
12254  lastLambda = max;
12255 
12256 
12257  /*
12258  * Determine length of median spectrum
12259  */
12260 
12261  nbin = (lastLambda - firstLambda) / dispersion;
12262 
12263  /*
12264  * Count how many values will be found for each spectral bin.
12265  * The ith bin has a wavelength range from firstLambda + i*dispersion
12266  * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12267  * it is assigned to its central wavelength.
12268  */
12269 
12270  count = cpl_calloc(nbin, sizeof(int));
12271 
12272  data = cpl_image_get_data(wavemap);
12273 
12274  for (i = 0; i < npix; i++) {
12275  if (data[i] < 1.0)
12276  continue;
12277  bin = (data[i] - firstLambda) / dispersion;
12278  if (bin < nbin) /* Safer */
12279  count[bin]++;
12280  }
12281 
12282  valid_bins = 0;
12283  for (i = 0; i < nbin; i++)
12284  if (count[i] >= minpoints)
12285  valid_bins++;
12286 
12287  if (valid_bins < nbin/3) {
12288  cpl_msg_warning(func, "Cannot determine a good global sky "
12289  "spectrum from input data");
12290  return NULL;
12291  }
12292 
12293 
12294  /*
12295  * Allocate an array of vectors with the appropriate size, to
12296  * contain a list of all the spectral pixels values. At the same
12297  * time, reset the array of counters (because we will have to
12298  * count again...).
12299  */
12300 
12301  vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12302  wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
12303  for (i = 0; i < nbin; i++) {
12304  if (count[i] >= minpoints) {
12305  vector[i] = cpl_vector_new(count[i]);
12306  wvector[i] = cpl_vector_new(count[i]);
12307  }
12308  count[i] = 0;
12309  }
12310 
12311 
12312  /*
12313  * Read the wavemap and the spectral images, and add the data values
12314  * to the appropriate wavelength bins
12315  */
12316 
12317  data = cpl_image_get_data(wavemap);
12318  sdata = cpl_image_get_data(spectra);
12319 
12320  for (i = 0; i < npix; i++) {
12321  if (data[i] < 1.0)
12322  continue;
12323  bin = (data[i] - firstLambda) / dispersion;
12324  if (bin < nbin) { /* Safer */
12325  if (vector[bin]) {
12326  cpl_vector_set(vector[bin], count[bin], sdata[i]);
12327  cpl_vector_set(wvector[bin], count[bin], data[i]);
12328  }
12329  count[bin]++;
12330  }
12331  }
12332 
12333 
12334  /*
12335  * Compute the median flux for each wavelength bin, and destroy
12336  * at the same time the used vectors
12337  */
12338 
12339  sky_spectrum = cpl_calloc(nbin, sizeof(double));
12340  sky_wave = cpl_calloc(nbin, sizeof(double));
12341  for (i = 0; i < nbin; i++) {
12342  if (vector[i]) {
12343  sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12344  sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
12345  cpl_vector_delete(vector[i]);
12346  cpl_vector_delete(wvector[i]);
12347  }
12348  }
12349 
12350  cpl_free(vector);
12351  cpl_free(wvector);
12352 
12353 
12354  /*
12355  * Here possible gaps in the final spectrum are filled by interpolation
12356  */
12357 
12358  for (i = 0; i < nbin; i++) {
12359  if (count[i] >= minpoints) {
12360  first_valid = i;
12361  break;
12362  }
12363  }
12364 
12365  for (i = first_valid; i < nbin; i++) {
12366  if (count[i] < minpoints) {
12367  sky_wave[i] = firstLambda + (i+0.5)*dispersion;
12368  for (j = i+1; j < nbin; j++) {
12369  if (count[j] >= minpoints) {
12370  if (sky_wave[j] - sky_wave[i-1] < 0.1) {
12371  sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
12372  / 2;
12373  }
12374  else {
12375  frac = (sky_wave[i] - sky_wave[i-1])
12376  / (sky_wave[j] - sky_wave[i-1]);
12377  sky_spectrum[i] = frac * sky_spectrum[j]
12378  + (1 - frac) * sky_spectrum[i-1];
12379  }
12380  }
12381  }
12382  }
12383  }
12384 
12385 
12386  /*
12387  * Create the output table
12388  */
12389 
12390  sky = cpl_table_new(nbin);
12391  cpl_table_wrap_double(sky, sky_wave, "wavelength");
12392  cpl_table_wrap_double(sky, sky_spectrum, "sky");
12393  cpl_table_wrap_int(sky, count, "npoints");
12394 
12395 
12396  /*
12397  * Fill the sky map
12398  */
12399 
12400  data = cpl_image_get_data(wavemap);
12401  sdata = cpl_image_get_data(spectra);
12402  kdata = cpl_image_get_data(skymap);
12403 
12404  for (i = 0; i < npix; i++) {
12405 
12406  /*
12407  * Currently based on linear interpolation
12408  */
12409 
12410  lambda = data[i];
12411  if (lambda < 1.0)
12412  continue;
12413  bin = (lambda - firstLambda) / dispersion;
12414  lambda1 = sky_wave[bin];
12415  value1 = sky_spectrum[bin];
12416  if (lambda1 < lambda) {
12417  bin++;
12418  if (bin < nbin) {
12419  lambda2 = sky_wave[bin];
12420  value2 = sky_spectrum[bin];
12421  if (lambda2 - lambda1 < 0.1) {
12422  value = (value1 + value2) / 2;
12423  }
12424  else {
12425  frac = (lambda - lambda1) / (lambda2 - lambda1);
12426  value = frac * value2 + (1 - frac) * value1;
12427  }
12428  }
12429  else {
12430  value = value1;
12431  }
12432  }
12433  else {
12434  if (bin > 0) {
12435  bin--;
12436  lambda2 = lambda1;
12437  value2 = value1;
12438  lambda1 = sky_wave[bin];
12439  value1 = sky_spectrum[bin];
12440  if (lambda2 - lambda1 < 0.1) {
12441  value = (value1 + value2) / 2;
12442  }
12443  else {
12444  frac = (lambda - lambda1) / (lambda2 - lambda1);
12445  value = frac * value2 + (1 - frac) * value1;
12446  }
12447  }
12448  else {
12449  value = value1;
12450  }
12451  }
12452  kdata[i] = value;
12453  }
12454 
12455  if (first_valid)
12456  cpl_table_erase_window(sky, 0, first_valid);
12457 
12458  return sky;
12459 
12460 }
12461 
12462 
12496 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
12497  double dispersion, cpl_image *skymap)
12498 {
12499  const char *func = "mos_sky_map";
12500 
12501  cpl_vector **vector;
12502  double firstLambda, lastLambda;
12503  double lambda, lambda1, lambda2;
12504  double value, value1, value2;
12505  float min, max;
12506  int *count;
12507  int nbin, bin;
12508  int nx, ny, npix;
12509  int i, j;
12510 
12511  cpl_table *sky;
12512  double *sky_spectrum;
12513  float *data;
12514  float *sdata;
12515  float *kdata;
12516  double *wdata;
12517 
12518 
12519  if (spectra == NULL || wavemap == NULL || skymap == NULL) {
12520  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12521  return NULL;
12522  }
12523 
12524  if (dispersion <= 0.0) {
12525  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12526  return NULL;
12527  }
12528 
12529  nx = cpl_image_get_size_x(spectra);
12530  ny = cpl_image_get_size_y(spectra);
12531  npix = nx * ny;
12532 
12533  if (nx != cpl_image_get_size_x(wavemap) ||
12534  ny != cpl_image_get_size_y(wavemap) ||
12535  nx != cpl_image_get_size_x(skymap) ||
12536  ny != cpl_image_get_size_y(skymap)) {
12537  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
12538  return NULL;
12539  }
12540 
12541 
12542  /*
12543  * Find bluest and reddest wavelengths in the whole image
12544  */
12545 
12546  data = cpl_image_get_data(wavemap);
12547 
12548  for (i = 0; i < npix; i++) {
12549  if (data[i] > 1.0) {
12550  min = max = data[i];
12551  j = i+1;
12552  break;
12553  }
12554  }
12555 
12556  for (i = j; i < npix; i++) {
12557  if (data[i] < 1.0) /* Impossible wavelength */
12558  continue;
12559  if (min > data[i])
12560  min = data[i];
12561  if (max < data[i])
12562  max = data[i];
12563  }
12564 
12565  firstLambda = min;
12566  lastLambda = max;
12567 
12568 
12569  /*
12570  * Determine length of median spectrum
12571  */
12572 
12573  nbin = (lastLambda - firstLambda) / dispersion;
12574 
12575  /*
12576  * Count how many values will be found for each spectral bin.
12577  * The ith bin has a wavelength range from firstLambda + i*dispersion
12578  * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12579  * it is assigned to its central wavelength.
12580  */
12581 
12582  count = cpl_calloc(nbin, sizeof(int));
12583 
12584  data = cpl_image_get_data(wavemap);
12585 
12586  for (i = 0; i < npix; i++) {
12587  if (data[i] < 1.0)
12588  continue;
12589  bin = (data[i] - firstLambda) / dispersion;
12590  if (bin < nbin) /* Safer */
12591  count[bin]++;
12592  }
12593 
12594 
12595  /*
12596  * Allocate an array of vectors with the appropriate size, to
12597  * contain a list of all the spectral pixels values. At the same
12598  * time, reset the array of counters (because we will have to
12599  * count again...).
12600  */
12601 
12602  vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12603  for (i = 0; i < nbin; i++) {
12604  if (count[i])
12605  vector[i] = cpl_vector_new(count[i]);
12606  else
12607  vector[i] = NULL;
12608  count[i] = 0;
12609  }
12610 
12611 
12612  /*
12613  * Read the wavemap and the spectral images, and add the data values
12614  * to the appropriate wavelength bins
12615  */
12616 
12617  data = cpl_image_get_data(wavemap);
12618  sdata = cpl_image_get_data(spectra);
12619 
12620  for (i = 0; i < npix; i++) {
12621  if (data[i] < 1.0)
12622  continue;
12623  bin = (data[i] - firstLambda) / dispersion;
12624  if (bin < nbin) { /* Safer */
12625  cpl_vector_set(vector[bin], count[bin], sdata[i]);
12626  count[bin]++;
12627  }
12628  }
12629 
12630 
12631  /*
12632  * Compute the median flux for each wavelength bin, and destroy
12633  * at the same time the used vectors
12634  */
12635 
12636  sky_spectrum = cpl_calloc(nbin, sizeof(double));
12637  for (i = 0; i < nbin; i++) {
12638  if (vector[i]) {
12639  sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12640  cpl_vector_delete(vector[i]);
12641  }
12642  }
12643 
12644  cpl_free(vector);
12645 
12646 
12647  /*
12648  * Here possible gaps in the final spectrum should be filled
12649  * by interpolation
12650  */
12651 
12652  /* ... */
12653 
12654  /*
12655  * Create the output table
12656  */
12657 
12658  sky = cpl_table_new(nbin);
12659  cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
12660  cpl_table_set_column_unit(sky, "wavelength", "pixel");
12661  cpl_table_wrap_double(sky, sky_spectrum, "sky");
12662  cpl_table_wrap_int(sky, count, "npoints");
12663  for (i = 0; i < nbin; i++)
12664  cpl_table_set_double(sky, "wavelength", i,
12665  firstLambda + (i+0.5)*dispersion);
12666 
12667 
12668  /*
12669  * Fill the sky map
12670  */
12671 
12672  data = cpl_image_get_data(wavemap);
12673  sdata = cpl_image_get_data(spectra);
12674  kdata = cpl_image_get_data(skymap);
12675  wdata = cpl_table_get_data_double(sky, "wavelength");
12676 
12677  for (i = 0; i < npix; i++) {
12678 
12679  /*
12680  * Currently based on linear interpolation
12681  */
12682 
12683  lambda = data[i];
12684  if (lambda < 1.0)
12685  continue;
12686  bin = (lambda - firstLambda) / dispersion;
12687  lambda1 = wdata[bin];
12688  value1 = sky_spectrum[bin];
12689  if (lambda1 < lambda) {
12690  bin++;
12691  if (bin < nbin) {
12692  lambda2 = wdata[bin];
12693  value2 = sky_spectrum[bin];
12694  value = ((lambda2 - lambda)*value1
12695  + (lambda - lambda1)*value2) / dispersion;
12696  }
12697  else {
12698  value = value1;
12699  }
12700  }
12701  else {
12702  if (bin > 0) {
12703  bin--;
12704  lambda2 = lambda1;
12705  value2 = value1;
12706  lambda1 = wdata[bin];
12707  value1 = sky_spectrum[bin];
12708  value = ((lambda2 - lambda)*value1
12709  + (lambda - lambda1)*value2)/dispersion;
12710  }
12711  else {
12712  value = value1;
12713  }
12714  }
12715  kdata[i] = value;
12716  }
12717 
12718  return sky;
12719 
12720 }
12721 
12722 
12738 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
12739 {
12740  const char *func = "mos_sky_local_old";
12741 
12742  cpl_image *exslit;
12743  cpl_image *sky;
12744  cpl_image *skymap;
12745  float *data;
12746  float *sdata;
12747  int nx, ny;
12748  int xlow, ylow, xhig, yhig;
12749  int nslits;
12750  int *slit_id;
12751  int *position;
12752  int *length;
12753  int i, j, k;
12754 
12755 
12756  if (spectra == NULL) {
12757  cpl_msg_error(func,
12758  "A scientific rectified spectral image must be given");
12759  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12760  return NULL;
12761  }
12762 
12763  if (slits == NULL) {
12764  cpl_msg_error(func, "A slits position table must be given");
12765  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12766  return NULL;
12767  }
12768 
12769  nslits = cpl_table_get_nrow(slits);
12770  slit_id = cpl_table_get_data_int(slits, "slit_id");
12771  position = cpl_table_get_data_int(slits, "position");
12772  length = cpl_table_get_data_int(slits, "length");
12773 
12774  nx = cpl_image_get_size_x(spectra);
12775  ny = cpl_image_get_size_y(spectra);
12776 
12777  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12778 
12779  xlow = 1;
12780  xhig = nx;
12781  for (i = 0; i < nslits; i++) {
12782 
12783  if (length[i] == 0)
12784  continue;
12785 
12786  /*
12787  * Define the extraction boundaries. We DON'T write:
12788  *
12789  * ylow = position[i];
12790  * yhig = ylow + length[i];
12791  *
12792  * because the cpl_image pixels are counted from 1, and because in
12793  * cpl_image_extract() the coordinates of the last pixel are inclusive.
12794  */
12795 
12796  ylow = position[i] + 1;
12797  yhig = ylow + length[i] - 1;
12798 
12799  exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12800  sky = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12801  cpl_image_delete(exslit);
12802 
12803  data = cpl_image_get_data(skymap);
12804  data += nx * position[i];
12805 
12806  for (j = 0; j < length[i]; j++) {
12807  sdata = cpl_image_get_data(sky);
12808  for (k = 0; k < nx; k++) {
12809  *data++ = *sdata++;
12810  }
12811  }
12812 
12813  cpl_image_delete(sky);
12814  }
12815 
12816  return skymap;
12817 
12818 }
12819 
12820 
12840 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
12841 {
12842  const char *func = "mos_sky_local";
12843 
12844  char name[MAX_COLNAME];
12845 
12846  cpl_polynomial *fit;
12847  cpl_vector *points;
12848  cpl_vector *values;
12849  cpl_vector *keep_points;
12850  cpl_vector *keep_values;
12851  cpl_image *exslit;
12852  cpl_image *sky;
12853  cpl_image *subtracted;
12854  cpl_image *profile;
12855  cpl_image *skymap;
12856  cpl_table *objects;
12857  float *data;
12858  float *sdata;
12859  float *xdata;
12860  double *vdata;
12861  double *pdata;
12862  double median;
12863  int nx, ny;
12864  int xlow, ylow, xhig, yhig;
12865  int nslits;
12866  int *slit_id;
12867  int *position;
12868  int *length;
12869  int *is_sky;
12870  int nsky, nbad;
12871  int maxobjects;
12872  int margin = 3;
12873  int radius = 6;
12874  int i, j, k;
12875 
12876 
12877  if (spectra == NULL) {
12878  cpl_msg_error(func,
12879  "A scientific rectified spectral image must be given");
12880  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12881  return NULL;
12882  }
12883 
12884  if (slits == NULL) {
12885  cpl_msg_error(func, "A slits position table must be given");
12886  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12887  return NULL;
12888  }
12889 
12890  if (order < 0) {
12891  cpl_msg_error(func, "Invalid fit order");
12892  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12893  return NULL;
12894  }
12895 
12896  nslits = cpl_table_get_nrow(slits);
12897  slit_id = cpl_table_get_data_int(slits, "slit_id");
12898  position = cpl_table_get_data_int(slits, "position");
12899  length = cpl_table_get_data_int(slits, "length");
12900 
12901  nx = cpl_image_get_size_x(spectra);
12902  ny = cpl_image_get_size_y(spectra);
12903 
12904  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12905 
12906  xlow = 1;
12907  xhig = nx;
12908  for (i = 0; i < nslits; i++) {
12909 
12910  if (length[i] == 0)
12911  continue;
12912 
12913  /*
12914  * Define the extraction boundaries. We DON'T write:
12915  *
12916  * ylow = position[i];
12917  * yhig = ylow + length[i];
12918  *
12919  * because the cpl_image pixels are counted from 1, and because in
12920  * cpl_image_extract() the coordinates of the last pixel are inclusive.
12921  */
12922 
12923  ylow = position[i] + 1;
12924  yhig = ylow + length[i] - 1;
12925 
12926  exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12927  sky = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12928  cpl_image_delete(exslit);
12929 
12930  data = cpl_image_get_data(skymap);
12931  data += nx * position[i];
12932 
12933  for (j = 0; j < length[i]; j++) {
12934  sdata = cpl_image_get_data(sky);
12935  for (k = 0; k < nx; k++) {
12936  *data++ = *sdata++;
12937  }
12938  }
12939 
12940  cpl_image_delete(sky);
12941  }
12942 
12943 
12944  /*
12945  * Preliminary sky-subtracted image
12946  */
12947 
12948  subtracted = cpl_image_duplicate(spectra);
12949  cpl_image_subtract(subtracted, skymap);
12950  cpl_image_delete(skymap);
12951 
12952 
12953  /*
12954  * Detect objects positions in all slits
12955  */
12956 
12957  objects = cpl_table_duplicate(slits);
12958  profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
12959  cpl_image_delete(profile);
12960  cpl_image_delete(subtracted);
12961 
12962 
12963  /*
12964  * Flag the sky pixels. Note that maxobjects is intentionally
12965  * the max number of objects increased by one.
12966  */
12967 
12968  maxobjects = 1;
12969  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12970  while (cpl_table_has_column(objects, name)) {
12971  maxobjects++;
12972  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12973  }
12974 
12975  is_sky = cpl_calloc(ny, sizeof(int));
12976 
12977  for (i = 0; i < nslits; i++) {
12978 
12979  if (length[i] == 0)
12980  continue;
12981 
12982  ylow = position[i] + margin;
12983  yhig = position[i] + length[i] - margin;
12984 
12985  for (j = ylow; j < yhig; j++)
12986  is_sky[j] = 1;
12987 
12988  for (j = 1; j < maxobjects; j++) {
12989  snprintf(name, MAX_COLNAME, "object_%d", j);
12990  if (cpl_table_is_valid(objects, name, i)) {
12991  snprintf(name, MAX_COLNAME, "start_%d", j);
12992  ylow = cpl_table_get_int(objects, name, i, NULL);
12993  snprintf(name, MAX_COLNAME, "end_%d", j);
12994  yhig = cpl_table_get_int(objects, name, i, NULL);
12995  for (k = ylow; k <= yhig; k++)
12996  is_sky[k] = 0;
12997  }
12998  }
12999 
13000 
13001  /*
13002  * Eliminate isolated sky points
13003  */
13004 
13005  ylow = position[i] + margin + 1;
13006  yhig = position[i] + length[i] - margin - 1;
13007 
13008  for (j = ylow; j < yhig; j++)
13009  if (is_sky[j])
13010  if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
13011  is_sky[j] = 0;
13012 
13013  }
13014 
13015 
13016  /*
13017  * Determination of the sky map
13018  */
13019 
13020  skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13021 
13022  for (i = 0; i < nslits; i++) {
13023 
13024  if (length[i] == 0)
13025  continue;
13026 
13027  ylow = position[i];
13028  yhig = ylow + length[i];
13029 
13030  nsky = 0;
13031  for (j = ylow; j < yhig; j++)
13032  if (is_sky[j])
13033  nsky++;
13034 
13035  if (nsky > order + 1) {
13036  if (order) {
13037  points = cpl_vector_new(nsky);
13038  nsky = 0;
13039  for (j = ylow; j < yhig; j++) {
13040  if (is_sky[j]) {
13041  cpl_vector_set(points, nsky, j);
13042  nsky++;
13043  }
13044  }
13045 
13046  exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
13047  xdata = cpl_image_get_data(exslit);
13048  values = cpl_vector_new(nsky);
13049 
13050  for (j = 0; j < nx; j++) {
13051  nsky = 0;
13052  for (k = ylow; k < yhig; k++) {
13053  if (is_sky[k]) {
13054  cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
13055  nsky++;
13056  }
13057  }
13058 
13059  /*
13060  * Eliminate obvious outliers
13061  */
13062 
13063  median = cpl_vector_get_median_const(values);
13064  vdata = cpl_vector_get_data(values);
13065  pdata = cpl_vector_get_data(points);
13066  nbad = 0;
13067  for (k = 0; k < nsky; k++) {
13068  if (fabs(vdata[k] - median) < 100) {
13069  if (nbad) {
13070  vdata[k-nbad] = vdata[k];
13071  pdata[k-nbad] = pdata[k];
13072  }
13073  }
13074  else
13075  nbad++;
13076  }
13077 
13078  if (nsky == nbad)
13079  continue;
13080 
13081  if (nbad && nsky - nbad > order + 1) {
13082  keep_values = values;
13083  keep_points = points;
13084  values = cpl_vector_wrap(nsky-nbad, vdata);
13085  points = cpl_vector_wrap(nsky-nbad, pdata);
13086  }
13087 
13088  if (nsky - nbad > order + 1) {
13089 
13090  fit = cpl_polynomial_fit_1d_create(points, values,
13091  order, NULL);
13092 
13093  if (fit) {
13094  for (k = ylow; k < yhig; k++) {
13095  xdata[j+(k-ylow)*nx] =
13096  cpl_polynomial_eval_1d(fit, k, NULL);
13097  }
13098 
13099  cpl_polynomial_delete(fit);
13100  }
13101  else
13102  cpl_error_reset();
13103  }
13104  else {
13105  for (k = 0; k < nsky; k++) {
13106  xdata[j+k*nx] = median;
13107  }
13108  }
13109 
13110  if (nbad && nsky - nbad > order + 1) {
13111  cpl_vector_unwrap(values);
13112  cpl_vector_unwrap(points);
13113  values = keep_values;
13114  points = keep_points;
13115  }
13116 
13117  if (nbad) {
13118  nsky = 0;
13119  for (k = ylow; k < yhig; k++) {
13120  if (is_sky[k]) {
13121  cpl_vector_set(points, nsky, k);
13122  nsky++;
13123  }
13124  }
13125  }
13126 
13127  }
13128 
13129  cpl_vector_delete(values);
13130  cpl_vector_delete(points);
13131 
13132  cpl_image_copy(skymap, exslit, 1, ylow+1);
13133  cpl_image_delete(exslit);
13134 
13135  }
13136  else {
13137  exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
13138  xdata = cpl_image_get_data(exslit);
13139  values = cpl_vector_new(nsky);
13140 
13141  for (j = 0; j < nx; j++) {
13142  nsky = 0;
13143  for (k = ylow; k < yhig; k++) {
13144  if (is_sky[k]) {
13145  cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
13146  nsky++;
13147  }
13148  }
13149 
13150  median = cpl_vector_get_median_const(values);
13151 
13152  for (k = ylow; k < yhig; k++)
13153  xdata[j+(k-ylow)*nx] = median;
13154 
13155  }
13156 
13157  cpl_vector_delete(values);
13158 
13159  cpl_image_copy(skymap, exslit, 1, ylow+1);
13160  cpl_image_delete(exslit);
13161  }
13162  }
13163  else
13164  cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
13165  }
13166 
13167  cpl_free(is_sky);
13168 
13169  return skymap;
13170 
13171 }
13172 
13173 
13195 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
13196  float threshold, float ratio)
13197 {
13198  const char *func = "mos_clean_cosmics";
13199 
13200  cpl_image *smoothImage;
13201  cpl_table *table;
13202  cpl_matrix *kernel;
13203  int *xdata;
13204  int *ydata;
13205  float *idata;
13206  float *sdata;
13207  float sigma, sum, value, smoothValue;
13208  double noise;
13209  int count;
13210  float fMax;
13211  int iMin, iMax, jMin, jMax, iPosMax, jPosMax;
13212  int xLen;
13213  int yLen;
13214  int nPix;
13215  int first = 1; /* position of first cosmic ray candidate
13216  encountered while scanning the image */
13217  int pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
13218  int numCosmic = 0;
13219  int found, foundContiguousCandidate;
13220  int *cosmic;
13221 
13222 
13223  if (image == NULL)
13224  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13225 
13226 
13227  /*
13228  * "cosmic" is a flags holder (initialized to zero):
13229  *
13230  * -1 = candidate for cosmic ray
13231  * 0 = not a cosmic
13232  * 1 = a cosmic ray
13233  * 2 = member of current group of contiguous candidates
13234  * 3 = examined member of current group
13235  */
13236 
13237  xLen = cpl_image_get_size_x(image);
13238  yLen = cpl_image_get_size_y(image);
13239 
13240  if (xLen < 4 || yLen < 4)
13241  return CPL_ERROR_NONE;
13242 
13243  nPix = xLen * yLen;
13244 
13245  /*
13246  * Noise estimation from negative offsets in image. Note that this
13247  * assumes that the background level (skyLevel) has already been
13248  * subtracted from the data. In this way we estimate the noise due
13249  * to detector readout and to the background signal level (before
13250  * it were removed). Theoretically this is given by
13251  *
13252  * noise = sqrt(ron^2 + skyLevel/gain)
13253  *
13254  * where ron is the read-out-noise. To this we will sum the noise
13255  * contribution due to any increase of the signal above the background
13256  * by an amount scienceLevel. Theoretically the total noise is given by
13257  *
13258  * totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
13259  *
13260  * that is
13261  *
13262  * totalNoise = sqrt(noise^2 + scienceLevel/gain)
13263  *
13264  */
13265 
13266  idata = cpl_image_get_data(image);
13267  noise = 0.0;
13268  count = 0;
13269 
13270  for (i = 0; i < nPix; i++) {
13271  if (idata[i] < -0.00001) {
13272  noise -= idata[i];
13273  count++;
13274  }
13275  }
13276 
13277  noise /= count;
13278  noise *= 1.25; /* Factor to convert average deviation to sigma */
13279 
13280  cosmic = cpl_calloc(nPix, sizeof(int));
13281 
13282  if (threshold < 0.)
13283  threshold = 4.0;
13284  if (ratio < 0.)
13285  ratio = 2.0;
13286 
13287  kernel = cpl_matrix_new(3, 3);
13288  cpl_matrix_fill(kernel, 1.0);
13289  cpl_matrix_set(kernel, 1, 1, 0.0);
13290  smoothImage = cpl_image_filter_median(image, kernel);
13291  cpl_matrix_delete(kernel);
13292 
13293  /*
13294  * Loop on images pixels, searching for cosmic rays candidates.
13295  * Border pixels are currently excluded (they cannot contain
13296  * candidates), to avoid that the search for groups of contiguous
13297  * pixels would ever go out of image boundaries. In future we may
13298  * overcome this limit, adding an appropriate check when contiguous
13299  * pixels are searched.
13300  */
13301 
13302  sdata = cpl_image_get_data(smoothImage);
13303 
13304  for (j = 1; j < yLen - 1; j++) {
13305  for (i = 1; i < xLen - 1; i++) {
13306  value = idata[i + j * xLen];
13307  smoothValue = sdata[i + j * xLen];
13308  if (smoothValue < 1.0)
13309  smoothValue = 1.0;
13310  sigma = sqrt(noise * noise + smoothValue / gain);
13311  if (value - smoothValue >= threshold * sigma)
13312  cosmic[i + j * xLen] = -1;
13313  }
13314  }
13315 
13316  cpl_image_delete(smoothImage);
13317 
13318 
13319  /*
13320  * Search for groups of contiguous cosmic rays candidates.
13321  */
13322 
13323  do {
13324  found = 0;
13325  for (pos = first; pos < nPix; pos++) {
13326  if (cosmic[pos] == -1) {
13327  cosmic[pos] = 2; /* Candidate found. */
13328  i = pos % xLen; /* Its coordinates. */
13329  j = pos / xLen;
13330  first = pos;
13331  first++; /* ??? really necessary? */
13332  found = 1;
13333  break;
13334  }
13335  }
13336 
13337  if (found) {
13338 
13339  /*
13340  * Determine new group of contiguous cosmic rays candidates.
13341  * Initialize the working box boundaries, iMin, iMax, jMin, jMax,
13342  * and the value of the max pixel and its position, fMax, iPosMax,
13343  * jPosMax.
13344  */
13345 
13346  iMin = iMax = iPosMax = i;
13347  jMin = jMax = jPosMax = j;
13348  fMax = idata[i + j * xLen];
13349 
13350  do {
13351  foundContiguousCandidate = 0;
13352  for (l = 0; l <= 1; l++) {
13353  for (k = 0; k <= 1; k++) {
13354 
13355  /*
13356  * Looping on 4 pixels to North, East, South and West
13357  */
13358 
13359  ii = i + k - l;
13360  jj = j + k + l - 1;
13361  if (cosmic[ii + jj * xLen] == -1) {
13362  foundContiguousCandidate = 1;
13363  cosmic[ii + jj * xLen] = 2;
13364  /* Candidate belongs to current group */
13365  iii = ii; /* Keep its position */
13366  jjj = jj;
13367 
13368  /*
13369  * Upgrade search box
13370  */
13371 
13372  if (ii < iMin)
13373  iMin = ii;
13374  if (ii > iMax)
13375  iMax = ii;
13376  if (jj < jMin)
13377  jMin = jj;
13378  if (jj > jMax)
13379  jMax = jj;
13380 
13381  if (idata[ii + jj * xLen] > fMax) {
13382  fMax = idata[ii + jj * xLen];
13383  iPosMax = ii;
13384  jPosMax = jj;
13385  }
13386  }
13387  }
13388  }
13389 
13390  /*
13391  * We are done exploring the "cross". Now mark as "examined"
13392  * the current candidate (at the center of the cross):
13393  */
13394 
13395  cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
13396 
13397  if (foundContiguousCandidate) {
13398 
13399  /*
13400  * Pass (arbitrarily) the coordinates of the LAST found
13401  * candidate
13402  */
13403 
13404  i = iii;
13405  j = jjj;
13406 
13407  /*
13408  * Skip the rest, continue loop on new candidate
13409  */
13410 
13411  continue;
13412  }
13413 
13414 
13415  /*
13416  * Look for leftovers in the (growing!) search box
13417  */
13418 
13419  for (l = jMin; l <= jMax; l++) {
13420  for (k = iMin; k <= iMax; k++) {
13421  if (cosmic[k + l * xLen] == 2) {
13422  i = k;
13423  j = l;
13424  foundContiguousCandidate = 1;
13425  break;
13426  }
13427  }
13428  if (foundContiguousCandidate)
13429  break;
13430  }
13431  } while (foundContiguousCandidate);
13432 
13433 
13434  /*
13435  * No more contiguous candidates are found. Decide now
13436  * whether the current group is a cosmic ray or not.
13437  */
13438 
13439  sum = 0.; /* Sum of 8 pixels around max position */
13440  for (l = -1; l <= 1; l++) {
13441  for (k = -1; k <= 1; k++) {
13442  if (l != 0 || k != 0) {
13443  sum += idata[iPosMax + k + (jPosMax + l) * xLen];
13444  }
13445  }
13446  }
13447 
13448  sum /= 8.;
13449  if (fMax > ratio * sum) {
13450  for (l = jMin - 1; l <= jMax + 1; l++) {
13451  for (k = iMin - 1; k <= iMax + 1; k++) {
13452  if (cosmic[k + l * xLen] == 3) {
13453  cosmic[k + l * xLen] = 1;
13454  numCosmic++;
13455  }
13456  }
13457  }
13458  }
13459  else {
13460  for (l = jMin - 1; l <= jMax + 1; l++) {
13461  for (k = iMin - 1; k <= iMax + 1; k++) {
13462  if (cosmic[k + l * xLen] != -1) {
13463  if (cosmic[k + l * xLen] == 1)
13464  numCosmic--;
13465  cosmic[k + l * xLen] = 0;
13466  }
13467  }
13468  }
13469  }
13470  }
13471  } while (found);
13472 
13473 
13474  /*
13475  * Prepare table containing cosmic rays coordinates.
13476  */
13477 
13478  table = cpl_table_new(numCosmic);
13479  cpl_table_new_column(table, "x", CPL_TYPE_INT);
13480  cpl_table_new_column(table, "y", CPL_TYPE_INT);
13481  cpl_table_set_column_unit(table, "x", "pixel");
13482  cpl_table_set_column_unit(table, "y", "pixel");
13483  xdata = cpl_table_get_data_int(table, "x");
13484  ydata = cpl_table_get_data_int(table, "y");
13485 
13486  for (pos = 0, i = 0; pos < nPix; pos++) {
13487  if (cosmic[pos] == 1) {
13488  xdata[i] = (pos % xLen);
13489  ydata[i] = (pos / xLen);
13490  i++;
13491  }
13492  }
13493 
13494  mos_clean_bad_pixels(image, table, 1);
13495 
13496  cpl_free(cosmic);
13497  cpl_table_delete(table);
13498 
13499  return CPL_ERROR_NONE;
13500 
13501 }
13502 
13503 
13504 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
13505  int spectral)
13506 {
13507  const char *func = "mos_clean_cosmics";
13508 
13509  float *idata;
13510  int *isBadPix;
13511  int i, j, k, d;
13512  int xlen, ylen, totPix;
13513  int nBadPixels = 0;
13514  int sign, foundFirst;
13515  int *xValue = NULL;
13516  int *yValue = NULL;
13517  float save = 0.;
13518  double sumd;
13519  int cx, cy;
13520  int nPairs;
13521  float estimate[4];
13522  int sx[] = {0, 1, 1, 1};
13523  int sy[] = {1,-1, 0, 1};
13524  int searchHorizon = 100;
13525  int percent = 15;
13526 
13527 
13528  if (image == NULL || table == NULL)
13529  return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13530 
13531  if (1 != cpl_table_has_column(table, "x"))
13532  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13533 
13534  if (1 != cpl_table_has_column(table, "y"))
13535  return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13536 
13537  if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
13538  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13539 
13540  if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
13541  return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13542 
13543  nBadPixels = cpl_table_get_nrow(table);
13544 
13545  if (nBadPixels) {
13546  xlen = cpl_image_get_size_x(image);
13547  ylen = cpl_image_get_size_y(image);
13548  idata = cpl_image_get_data(image);
13549  totPix = xlen * ylen;
13550  if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
13551  isBadPix = cpl_calloc(totPix, sizeof(int));
13552  }
13553  else {
13554  cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
13555  "skip bad pixel correction", percent);
13556  return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13557  }
13558  }
13559  else {
13560  cpl_msg_debug(func, "No pixel values to interpolate");
13561  return CPL_ERROR_NONE;
13562  }
13563 
13564  xValue = cpl_table_get_data_int(table, "x");
13565  yValue = cpl_table_get_data_int(table, "y");
13566 
13567  for (i = 0; i < nBadPixels; i++)
13568  isBadPix[xValue[i] + yValue[i] * xlen] = 1;
13569 
13570  for (i = 0; i < nBadPixels; i++) {
13571 
13572  /*
13573  * Search for the closest good pixel along the 4 fundamental
13574  * directions (in both senses):
13575  * \ | /
13576  * \|/
13577  * --- ---
13578  * /|\
13579  * / | \
13580  *
13581  * Then collect pairs of values to interpolate linearly.
13582  */
13583 
13584  nPairs = 0;
13585  for (j = 0; j < 4; j++) {
13586 
13587  if (spectral) /* Just horizontal interpolation for spectral data */
13588  if (j != 2)
13589  continue;
13590 
13591  estimate[nPairs] = 0.; /* Pairs interpolations are stored here */
13592  sumd = 0.;
13593  foundFirst = 0;
13594  for (k = 0; k < 2; k++) {
13595  sign = 2 * k - 1;
13596  d = 0;
13597  cx = xValue[i];
13598  cy = yValue[i];
13599  do {
13600  cx += sign * sx[j];
13601  cy += sign * sy[j];
13602  if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen)
13603  break;
13604  d++;
13605  } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
13606 
13607  if (cx >= 0 && cx < xlen &&
13608  cy >= 0 && cy < ylen && d < searchHorizon) {
13609 
13610  /*
13611  * In this block is cripted the linear interpolation...
13612  */
13613 
13614  save = idata[cx + cy * xlen];
13615  estimate[nPairs] += save / d;
13616  sumd += 1. / (double) d;
13617  if (k) {
13618  estimate[nPairs] /= sumd;
13619  nPairs++;
13620  }
13621  else {
13622  foundFirst = 1;
13623  }
13624  }
13625  else {
13626 
13627  /*
13628  * Image borders were crossed, incomplete pair of values
13629  */
13630 
13631  if (k) {
13632  if (foundFirst) {
13633  estimate[nPairs] = save;
13634  nPairs++;
13635  }
13636  }
13637  }
13638  }
13639  }
13640 
13641  /*
13642  * Replace pixel value of the input image, corresponding to
13643  * the current bad pixel, with the median of the estimates
13644  * resulted from the 4 linear interpolations.
13645  */
13646 
13647  if (nPairs > 2) {
13648  idata[xValue[i] + yValue[i] * xlen] =
13649  cpl_tools_get_median_float(estimate, nPairs);
13650  }
13651  else if (nPairs == 2) {
13652  idata[xValue[i] + yValue[i] * xlen] =
13653  (estimate[0] + estimate[1]) / 2.;
13654  }
13655  else if (nPairs == 1) {
13656  idata[xValue[i] + yValue[i] * xlen] = estimate[0];
13657  }
13658  else {
13659  cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
13660  xValue[i], yValue[i]);
13661  }
13662  }
13663 
13664  cpl_free(isBadPix);
13665 
13666  return CPL_ERROR_NONE;
13667 }
13668 
13669 
13699 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
13700  cpl_table *polytraces, double reference,
13701  double blue, double red, double dispersion)
13702 {
13703  const char *func = "mos_spatial_map";
13704 
13705  const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
13706  /* Max order is 5 */
13707  cpl_polynomial *polytop;
13708  cpl_polynomial *polybot;
13709  cpl_image *calibration;
13710  float *data;
13711  double top, bot;
13712  double coeff;
13713  double ytop, ybot;
13714  double ypos, yfra;
13715  double factor;
13716  int yint, yprev;
13717  int nslits;
13718  int npseudo;
13719  int *slit_id;
13720  int *length;
13721  int nx, ny;
13722  int pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
13723  int missing_top, missing_bot;
13724  int null;
13725  int order;
13726  int i, j;
13727  cpl_size k;
13728 
13729 
13730  if (spectra == NULL || slits == NULL || polytraces == NULL) {
13731  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13732  return NULL;
13733  }
13734 
13735  if (dispersion <= 0.0) {
13736  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13737  return NULL;
13738  }
13739 
13740  if (red - blue < dispersion) {
13741  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13742  return NULL;
13743  }
13744 
13745  nx = cpl_image_get_size_x(spectra);
13746  ny = cpl_image_get_size_y(spectra);
13747 
13748  calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13749  data = cpl_image_get_data(calibration);
13750 
13751  length = cpl_table_get_data_int(slits, "length");
13752  nslits = cpl_table_get_nrow(slits);
13753  slit_id = cpl_table_get_data_int(slits, "slit_id");
13754  order = cpl_table_get_ncol(polytraces) - 2;
13755 
13756  /*
13757  * The spatial resampling is performed for a certain number of
13758  * pixels above and below the position of the reference wavelength:
13759  */
13760 
13761  pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
13762  pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
13763 
13764  for (i = 0; i < nslits; i++) {
13765 
13766  if (length[i] == 0)
13767  continue;
13768 
13769  /*
13770  * Note that the x coordinate of the reference pixels on the CCD
13771  * is taken arbitrarily at the top end of each slit. This wouldn't
13772  * be entirely correct in case of curved slits, or in presence of
13773  * heavy distortions: in such cases the spatial resampling is
13774  * really performed across a wide range of wavelengths. But
13775  * the lag between top and bottom spectral curvature models
13776  * would introduce even in such cases negligible effects on
13777  * the spectral spatial resampling.
13778  */
13779 
13780  refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
13781 
13782  start_pixel = refpixel - pixel_below;
13783  if (start_pixel < 0)
13784  start_pixel = 0;
13785 
13786  end_pixel = refpixel + pixel_above;
13787  if (end_pixel > nx)
13788  end_pixel = nx;
13789 
13790  /*
13791  * Recover from the table of spectral curvature coefficients
13792  * the curvature polynomials.
13793  */
13794 
13795  missing_top = 0;
13796  polytop = cpl_polynomial_new(1);
13797  for (k = 0; k <= order; k++) {
13798  coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
13799  if (null) {
13800  cpl_polynomial_delete(polytop);
13801  missing_top = 1;
13802  break;
13803  }
13804  cpl_polynomial_set_coeff(polytop, &k, coeff);
13805  }
13806 
13807  missing_bot = 0;
13808  polybot = cpl_polynomial_new(1);
13809  for (k = 0; k <= order; k++) {
13810  coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
13811  if (null) {
13812  cpl_polynomial_delete(polybot);
13813  missing_bot = 1;
13814  break;
13815  }
13816  cpl_polynomial_set_coeff(polybot, &k, coeff);
13817  }
13818 
13819  if (missing_top && missing_bot) {
13820  cpl_msg_warning(func, "Spatial map, slit %d was not traced!",
13821  slit_id[i]);
13822  continue;
13823  }
13824 
13825  /*
13826  * In case just one of the two edges was not traced, the other
13827  * edge curvature model is duplicated and shifted to the other
13828  * end of the slit: better than nothing!
13829  */
13830 
13831  if (missing_top) {
13832  cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
13833  "the spectral curvature of the lower edge "
13834  "is used instead.", slit_id[i]);
13835  polytop = cpl_polynomial_duplicate(polybot);
13836  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13837  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13838  k = 0;
13839  coeff = cpl_polynomial_get_coeff(polybot, &k);
13840  coeff += ytop - ybot;
13841  cpl_polynomial_set_coeff(polytop, &k, coeff);
13842  }
13843 
13844  if (missing_bot) {
13845  cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
13846  "the spectral curvature of the upper edge "
13847  "is used instead.", slit_id[i]);
13848  polybot = cpl_polynomial_duplicate(polytop);
13849  ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13850  ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13851  k = 0;
13852  coeff = cpl_polynomial_get_coeff(polytop, &k);
13853  coeff -= ytop - ybot;
13854  cpl_polynomial_set_coeff(polybot, &k, coeff);
13855  }
13856 
13857  top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
13858  bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
13859  npseudo = ceil(top-bot) + 1;
13860 
13861  if (npseudo < 1) {
13862  cpl_polynomial_delete(polytop);
13863  cpl_polynomial_delete(polybot);
13864  cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
13865  slit_id[i]);
13866  continue;
13867  }
13868 
13869  for (j = start_pixel; j < end_pixel; j++) {
13870  top = cpl_polynomial_eval_1d(polytop, j, NULL);
13871  bot = cpl_polynomial_eval_1d(polybot, j, NULL);
13872  factor = (top-bot)/npseudo;
13873  for (k = 0; k <= npseudo; k++) {
13874  ypos = top - k*factor;
13875  yint = ypos;
13876  yfra = ypos - yint;
13877  if (yint >= 0 && yint < ny-1) {
13878  data[j + nx*yint] = (top-yint)/factor;
13879  if (k) {
13880 
13881  /*
13882  * This is added to recover lost pixels on
13883  * the CCD image (pixels are lost because
13884  * the CCD pixels are less than npseudo+1).
13885  */
13886 
13887  if (yprev - yint > 1) {
13888  data[j + nx*(yint+1)] = (top-yint-1)/factor;
13889  }
13890  }
13891  }
13892  yprev = yint;
13893  }
13894  }
13895  cpl_polynomial_delete(polytop);
13896  cpl_polynomial_delete(polybot);
13897  }
13898 
13899  return calibration;
13900 }
13901 
13902 
13965 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
13966  int maxradius, int conradius)
13967 {
13968  const char *func = "mos_detect_objects";
13969 
13970  cpl_image *profile;
13971  float *pdata;
13972  float *p;
13973 
13974  char name[MAX_COLNAME];
13975 
13976  int nslits;
13977  int npeaks;
13978  int nobjects, objpos, totobj;
13979  int maxobjects;
13980  int *position;
13981  int *length;
13982  int *reject;
13983  double *place;
13984  double *bright;
13985  double mindistance;
13986  int pos, count;
13987  int up;
13988  int low, hig;
13989  int row;
13990  int i, j, k;
13991 
13992  const int min_pixels = 10;
13993 
13994 
13995  if (cpl_error_get_code() != CPL_ERROR_NONE)
13996  return NULL;
13997 
13998  if (image == NULL || slits == NULL) {
13999  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14000  return NULL;
14001  }
14002 
14003  if (margin < 0)
14004  margin = 0;
14005 
14006  if (maxradius < 0) {
14007  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14008  return NULL;
14009  }
14010 
14011  if (conradius < 0) {
14012  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14013  return NULL;
14014  }
14015 
14016  nslits = cpl_table_get_nrow(slits);
14017  position = cpl_table_get_data_int(slits, "position");
14018  length = cpl_table_get_data_int(slits, "length");
14019 
14020  profile = cpl_image_collapse_create(image, 1);
14021  cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
14022  pdata = cpl_image_get_data(profile);
14023 
14024  row = 1;
14025  maxobjects = 0;
14026  totobj = 0;
14027  for (i = 0; i < nslits; i++) {
14028 
14029  if (length[i] == 0)
14030  continue;
14031 
14032  pos = position[i] + margin;
14033  count = length[i] - 2*margin;
14034 
14035  if (count < min_pixels)
14036  continue;
14037 
14038  p = pdata + pos;
14039 
14040 
14041  /*
14042  * Count peaks candidates
14043  */
14044 
14045  npeaks = 0;
14046  if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
14047  npeaks++;
14048  }
14049 
14050  up = 0;
14051  for (j = 0; j < count - 3; j++) {
14052  if (p[j] > 0) {
14053  if (p[j+1] > p[j]) {
14054  up++;
14055  }
14056  else {
14057  if (up > 2) {
14058  if (p[j+1] > p[j+2] && p[j+2] > 0) {
14059  if (p[j] > 5)
14060  npeaks++;
14061  }
14062  }
14063  else if (up > 1) {
14064  if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
14065  if (p[j] > 5)
14066  npeaks++;
14067  }
14068  }
14069  up = 0;
14070  }
14071  }
14072  else {
14073  up = 0;
14074  }
14075  }
14076 
14077  if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
14078  && p[count-3] > p[count-4] && p[count-4] > 0) {
14079  npeaks++;
14080  }
14081 
14082  if (npeaks == 0)
14083  continue;
14084 
14085 
14086  /*
14087  * Get candidates parameters
14088  */
14089 
14090  reject = cpl_calloc(npeaks, sizeof(int));
14091  bright = cpl_calloc(npeaks, sizeof(double));
14092  place = cpl_calloc(npeaks, sizeof(double));
14093 
14094  npeaks = 0;
14095  if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
14096  bright[0] = p[0];
14097  place[0] = position[i] + margin;
14098  npeaks++;
14099  }
14100 
14101  up = 0;
14102  for (j = 0; j < count - 3; j++) {
14103  if (p[j] > 0) {
14104  if (p[j+1] > p[j]) {
14105  up++;
14106  }
14107  else {
14108  if (up > 2) {
14109  if (p[j+1] > p[j+2] && p[j+2] > 0) {
14110  if (p[j] > 5) {
14111  bright[npeaks] = p[j];
14112  place[npeaks] = position[i] + margin + j + 1
14113  + values_to_dx(p[j-1], p[j], p[j+1]);
14114  npeaks++;
14115  }
14116  }
14117  }
14118  else if (up > 1) {
14119  if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
14120  if (p[j] > 5) {
14121  bright[npeaks] = p[j];
14122  place[npeaks] = position[i] + margin + j + 1
14123  + values_to_dx(p[j-1], p[j], p[j+1]);
14124  npeaks++;
14125  }
14126  }
14127  }
14128  up = 0;
14129  }
14130  }
14131  else {
14132  up = 0;
14133  }
14134  }
14135 
14136  if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
14137  && p[count-3] > p[count-4] && p[count-4] > 0) {
14138  bright[npeaks] = p[count-1];
14139  place[npeaks] = position[i] + count;
14140  npeaks++;
14141  }
14142 
14143 
14144  /*
14145  * Now select the uncontaminated peaks
14146  */
14147 
14148  if (fabs(place[0] - pos) < 1.0)
14149  reject[0] = 1;
14150  if (fabs(place[npeaks-1] - pos - count) < 1.0)
14151  reject[npeaks-1] = 1;
14152  for (j = 0; j < npeaks; j++) {
14153  for (k = 0; k < npeaks; k++) {
14154  if (k == j)
14155  continue;
14156  mindistance = conradius * bright[k] / bright[j]
14157  * bright[k] / bright[j];
14158  if (fabs(place[j] - place[k]) < mindistance)
14159  reject[j] = 1;
14160  }
14161  }
14162 
14163 /* new part */
14164  for (j = 0; j < npeaks; j++) {
14165  if (reject[j])
14166  continue;
14167  if (j) {
14168  low = (place[j-1]*bright[j] + place[j]*bright[j-1])
14169  / (bright[j-1] + bright[j]) + 1;
14170  }
14171  else {
14172  low = pos;
14173  }
14174  if (j < npeaks - 1) {
14175  hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
14176  / (bright[j+1] + bright[j]) + 1;
14177  }
14178  else {
14179  hig = pos + count;
14180  }
14181 
14182  if (low < pos)
14183  low = pos;
14184  if (hig > pos + count)
14185  hig = pos + count;
14186  if (place[j] - low > maxradius)
14187  low = place[j] - maxradius;
14188  if (hig - place[j] > maxradius)
14189  hig = place[j] + maxradius;
14190  if (hig == low)
14191  reject[j] = 1;
14192  }
14193 /* end new part */
14194 
14195  nobjects = npeaks;
14196  for (j = 0; j < npeaks; j++)
14197  if (reject[j])
14198  nobjects--;
14199 
14200  for (j = 0; j < nobjects; j++) {
14201  snprintf(name, MAX_COLNAME, "object_%d", j+1);
14202  if (cpl_table_has_column(slits, name))
14203  continue;
14204  cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
14205  snprintf(name, MAX_COLNAME, "start_%d", j+1);
14206  cpl_table_new_column(slits, name, CPL_TYPE_INT);
14207  cpl_table_set_column_unit(slits, name, "pixel");
14208  snprintf(name, MAX_COLNAME, "end_%d", j+1);
14209  cpl_table_new_column(slits, name, CPL_TYPE_INT);
14210  cpl_table_set_column_unit(slits, name, "pixel");
14211  snprintf(name, MAX_COLNAME, "row_%d", j+1);
14212  cpl_table_new_column(slits, name, CPL_TYPE_INT);
14213  cpl_table_set_column_unit(slits, name, "pixel");
14214  }
14215 
14216  objpos = nobjects;
14217  for (j = 0; j < npeaks; j++) {
14218  if (reject[j])
14219  continue;
14220  if (j) {
14221  low = (place[j-1]*bright[j] + place[j]*bright[j-1])
14222  / (bright[j-1] + bright[j]) + 1;
14223  }
14224  else {
14225  low = pos;
14226  }
14227  if (j < npeaks - 1) {
14228  hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
14229  / (bright[j+1] + bright[j]) + 1;
14230  }
14231  else {
14232  hig = pos + count;
14233  }
14234 
14235  if (low < pos)
14236  low = pos;
14237  if (hig > pos + count)
14238  hig = pos + count;
14239  if (place[j] - low > maxradius)
14240  low = place[j] - maxradius;
14241  if (hig - place[j] > maxradius)
14242  hig = place[j] + maxradius;
14243 
14244  snprintf(name, MAX_COLNAME, "object_%d", objpos);
14245  cpl_table_set_double(slits, name, i, place[j]);
14246  snprintf(name, MAX_COLNAME, "start_%d", objpos);
14247  cpl_table_set_int(slits, name, i, low);
14248  snprintf(name, MAX_COLNAME, "end_%d", objpos);
14249  cpl_table_set_int(slits, name, i, hig);
14250  snprintf(name, MAX_COLNAME, "row_%d", objpos);
14251  cpl_table_set_int(slits, name, i, row + objpos - 1);
14252  totobj++;
14253  objpos--;
14254  }
14255 
14256  row += nobjects;
14257 
14258  if (maxobjects < nobjects)
14259  maxobjects = nobjects;
14260 
14261  cpl_free(reject);
14262  cpl_free(bright);
14263  cpl_free(place);
14264 
14265  }
14266 
14267 /* nobjects = row - nobjects; A bug, I think... */
14268  row = cpl_table_get_nrow(slits);
14269 
14270  for (i = 0; i < row; i++) {
14271  for (j = 0; j < maxobjects; j++) {
14272  snprintf(name, MAX_COLNAME, "row_%d", j+1);
14273  if (cpl_table_is_valid(slits, name, i))
14274  cpl_table_set_int(slits, name, i, totobj -
14275  cpl_table_get_int(slits, name, i, NULL));
14276  }
14277  }
14278 
14279  for (i = 0; i < maxobjects; i++) {
14280  snprintf(name, MAX_COLNAME, "start_%d", i+1);
14281  cpl_table_fill_invalid_int(slits, name, -1);
14282  snprintf(name, MAX_COLNAME, "end_%d", i+1);
14283  cpl_table_fill_invalid_int(slits, name, -1);
14284  snprintf(name, MAX_COLNAME, "row_%d", i+1);
14285  cpl_table_fill_invalid_int(slits, name, -1);
14286  }
14287 
14288  return profile;
14289 }
14290 
14291 
14316 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *science_var,
14317  cpl_image *sky,
14318  cpl_table *objects, int extraction, double ron,
14319  double gain, int ncombined)
14320 {
14321  const char *func = "mos_extract_objects";
14322 
14323  char name[MAX_COLNAME];
14324 
14325  cpl_image **output;
14326  cpl_image *extracted;
14327  cpl_image *extr_sky;
14328  cpl_image *error;
14329  cpl_image *sciwin;
14330  cpl_image *sci_var_win = NULL;
14331  cpl_image *skywin;
14332  int nslits;
14333  int nobjects;
14334  int maxobjects;
14335  int nx;
14336  int ylow, yhig;
14337  int i, j;
14338 
14339 
14340  if (science == NULL || sky == NULL) {
14341  cpl_msg_error(func, "Both scientific exposures are required in input");
14342  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14343  return NULL;
14344  }
14345 
14346  if (objects == NULL) {
14347  cpl_msg_error(func, "An object table is required in input");
14348  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14349  return NULL;
14350  }
14351 
14352  if (extraction < 0 || extraction > 1) {
14353  cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
14354  "either 0 or 1", extraction);
14355  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14356  return NULL;
14357  }
14358 
14359  if (ron < 0.0) {
14360  cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
14361  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14362  return NULL;
14363  }
14364 
14365  if (gain < 0.1) {
14366  cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
14367  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14368  return NULL;
14369  }
14370 
14371  if (ncombined < 1) {
14372  cpl_msg_error(func, "Invalid number of combined frames (%d): "
14373  "it should be at least 1", ncombined);
14374  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14375  return NULL;
14376  }
14377 
14378 
14379  /*
14380  * Count the max number of objects per slit. Note that maxobjects
14381  * is intentionally the max number of objects increased by one.
14382  */
14383 
14384  maxobjects = 1;
14385  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
14386  while (cpl_table_has_column(objects, name)) {
14387  maxobjects++;
14388  snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
14389  }
14390 
14391 
14392  /*
14393  * Count objects to extract
14394  */
14395 
14396  nobjects = 0;
14397  nslits = cpl_table_get_nrow(objects);
14398 
14399  for (i = 0; i < nslits; i++) {
14400  for (j = 1; j < maxobjects; j++) {
14401  snprintf(name, MAX_COLNAME, "object_%d", j);
14402  if (cpl_table_is_valid(objects, name, i))
14403  nobjects++;
14404  }
14405  }
14406 
14407  if (nobjects == 0)
14408  return NULL;
14409 
14410  nx = cpl_image_get_size_x(science);
14411 
14412  output = cpl_calloc(3, sizeof(cpl_image *));
14413  extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14414  extr_sky = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14415  error = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14416 
14417 
14418  /*
14419  * Extract objects
14420  */
14421 
14422  nobjects = 0;
14423  for (i = 0; i < nslits; i++) {
14424  for (j = 1; j < maxobjects; j++) {
14425  snprintf(name, MAX_COLNAME, "object_%d", j);
14426  if (cpl_table_is_valid(objects, name, i)) {
14427  snprintf(name, MAX_COLNAME, "start_%d", j);
14428  ylow = cpl_table_get_int(objects, name, i, NULL);
14429  snprintf(name, MAX_COLNAME, "end_%d", j);
14430  yhig = cpl_table_get_int(objects, name, i, NULL);
14431  snprintf(name, MAX_COLNAME, "row_%d", j);
14432  nobjects = cpl_table_get_int(objects, name, i, NULL);
14433  sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
14434  if(science_var != NULL)
14435  sci_var_win = cpl_image_extract(science_var, 1, ylow+1, nx, yhig);
14436  skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
14437 /*
14438  * Cleaning the cosmics locally was really NOT a good idea...
14439  * I leave it here, commented out, to never forget this mistake!
14440 
14441  if (extraction) {
14442  mos_clean_cosmics(sciwin, gain, -1., -1.);
14443  }
14444  */
14445  mos_extraction(sciwin, sci_var_win, skywin, extracted, extr_sky, error,
14446  nobjects, extraction, ron, gain, ncombined);
14447 
14448  /*
14449  * Hidden check whether the spectrum was saturated or not
14450  */
14451 
14452  {
14453  cpl_image *total = cpl_image_add_create(sciwin, skywin);
14454  float *data = cpl_image_get_data_float(total);
14455  int size = cpl_image_get_size_x(total)
14456  * cpl_image_get_size_y(total);
14457  int k;
14458  char *saturation_level = getenv("SATURATION_LEVEL");
14459  float saturation = 62000.0;
14460  char *max_saturated = getenv("MAX_SATURATED");
14461  int max_satur = 10;
14462  int saturated;
14463 
14464  if (saturation_level)
14465  saturation = atof(saturation_level);
14466 
14467  if (max_saturated)
14468  max_satur = atoi(max_saturated);
14469 
14470  saturated = 0;
14471  for (k = 0; k < size; k++) {
14472  if (data[k] > saturation) {
14473  saturated++;
14474  if (saturated > max_satur) {
14475  break;
14476  }
14477  }
14478  }
14479 
14480  if (saturated > max_satur)
14481  saturated = 1;
14482  else
14483  saturated = 0;
14484 
14485  data = cpl_image_get_data(extracted);
14486  data[nobjects * nx] = saturated;
14487  }
14488 
14489  cpl_image_delete(sciwin);
14490  cpl_image_delete(skywin);
14491  nobjects++;
14492  }
14493  }
14494  }
14495 
14496  return output;
14497 
14498 }
14499 
14500 
14523 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave,
14524  double dispersion, int saturation,
14525  double *mfwhm, double *rmsfwhm,
14526  double *resolution, double *rmsres, int *nlines)
14527 {
14528  cpl_vector *vector;
14529 
14530  int i, j, n, m;
14531  int position, maxpos;
14532  int xlen, ylen;
14533  int sp, ep;
14534  int radius;
14535  int sradius = 40;
14536  int threshold = 250; /* Peak must be so many ADUs above min */
14537 
14538  int ifwhm;
14539  double fwhm;
14540  double *buffer;
14541  double min, max, halfmax;
14542  double cut = 1.5; /* To cut outliers from FWHM values (pixel) */
14543  double value, rms;
14544 
14545  float *data;
14546 
14547 
14548  *resolution = 0.0;
14549  *rmsres = 0.0;
14550  *nlines = 0;
14551 
14552  xlen = cpl_image_get_size_x(image);
14553  ylen = cpl_image_get_size_y(image);
14554  data = cpl_image_get_data(image);
14555 
14556  buffer = cpl_malloc(ylen * sizeof(double));
14557 
14558  /*
14559  * Closest pixel to specified wavelength.
14560  */
14561 
14562  position = floor((lambda - startwave) / dispersion + 0.5);
14563 
14564  sp = position - sradius;
14565  ep = position + sradius;
14566 
14567  if (sp < 0 || ep > xlen) {
14568  cpl_free(buffer);
14569  return 0;
14570  }
14571 
14572  for (i = 0, n = 0; i < ylen; i++) { /* For each row of each slit */
14573 
14574  /*
14575  * Search interval for peak. Abort if too close to image border.
14576  */
14577 
14578  radius = mos_lines_width(data + i*xlen + position - sradius,
14579  2*sradius + 1);
14580  if (radius < 5)
14581  radius = 5;
14582 
14583  sp = position - radius;
14584  ep = position + radius;
14585 
14586  if (sp < 0 || ep > xlen) {
14587  cpl_free(buffer);
14588  return 0;
14589  }
14590 
14591 
14592  /*
14593  * Determine min-max value and position.
14594  */
14595 
14596  maxpos = sp;
14597  min = max = data[sp + i * xlen];
14598  for (j = sp; j < ep; j++) {
14599  if (data[j + i * xlen] > max) {
14600  max = data[j + i * xlen];
14601  maxpos = j;
14602  }
14603  if (data[j + i * xlen] < min) {
14604  min = data[j + i * xlen];
14605  }
14606  }
14607 
14608  if (fabs(min) < 0.0000001) /* Truncated spectrum */
14609  continue;
14610 
14611  if (max - min < threshold) /* Low signal... */
14612  continue;
14613 
14614  if (max > saturation) /* Saturation */
14615  continue;
14616 
14617  /*
14618  * Determine FWHM counting pixels with value greater than
14619  * half of the max value, to the right and to the left of
14620  * the max. Linear interpolation between the pixels where
14621  * the transition happens.
14622  */
14623 
14624  halfmax = (max + min)/ 2.0;
14625 
14626  fwhm = 0.0;
14627  ifwhm = 0;
14628  for (j = maxpos; j < maxpos + radius; j++) {
14629  if (j < xlen) {
14630  if (data[j + i * xlen] < halfmax) {
14631  fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
14632  / (data[j - 1 + i * xlen] - data[j + i * xlen]);
14633  break;
14634  }
14635  ifwhm++;
14636  }
14637  }
14638 
14639  ifwhm = 0;
14640  for (j = maxpos; j > maxpos - radius; j--) {
14641  if (j >= 0) {
14642  if (data[j + i * xlen] < halfmax) {
14643  fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
14644  / (data[j + 1 + i * xlen] - data[j + i * xlen]);
14645  break;
14646  }
14647  ifwhm++;
14648  }
14649  }
14650 
14651  if (fwhm > 3.0) {
14652  buffer[n] = fwhm - 2.0;
14653  n++;
14654  }
14655 
14656  }
14657 
14658  if (n == 0) {
14659  cpl_free(buffer);
14660  return 0;
14661  }
14662 
14663  vector = cpl_vector_wrap(n, buffer);
14664  value = cpl_vector_get_median_const(vector);
14665  cpl_vector_unwrap(vector);
14666 
14667  rms = 0.0;
14668  for (i = 0, m = 0; i < n; i++) {
14669  if (fabs(buffer[i] - value) < cut) {
14670  rms += fabs(buffer[i] - value);
14671  m++;
14672  }
14673  }
14674 
14675  cpl_free(buffer);
14676 
14677  if (m < 3)
14678  return 0;
14679 
14680  rms /= m;
14681  rms *= 1.25; /* Factor to convert average deviation to sigma */
14682 
14683  value *= dispersion;
14684  rms *= dispersion;
14685 
14686  *mfwhm = value;
14687  *rmsfwhm = rms;
14688 
14689  *resolution = lambda / value;
14690  *rmsres = *resolution * rms / value;
14691 
14692  *nlines = m;
14693 
14694  return 1;
14695 }
14696 
14697 
14719 cpl_table *mos_resolution_table(cpl_image *image, double startwave,
14720  double dispersion, int saturation,
14721  cpl_vector *lines)
14722 {
14723 
14724  cpl_table *table;
14725  double *line;
14726  double fwhm;
14727  double rmsfwhm;
14728  double resolution;
14729  double rmsres;
14730  int nref;
14731  int nlines;
14732  int i;
14733 
14734 
14735  nref = cpl_vector_get_size(lines);
14736  line = cpl_vector_get_data(lines);
14737 
14738  table = cpl_table_new(nref);
14739  cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
14740  cpl_table_set_column_unit(table, "wavelength", "Angstrom");
14741  cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
14742  cpl_table_set_column_unit(table, "fwhm", "Angstrom");
14743  cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
14744  cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
14745  cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
14746  cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
14747  cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
14748 
14749  for (i = 0; i < nref; i++) {
14750  if (mos_spectral_resolution(image, line[i], startwave, dispersion,
14751  saturation, &fwhm, &rmsfwhm,
14752  &resolution, &rmsres, &nlines)) {
14753  cpl_table_set_double(table, "wavelength", i, line[i]);
14754  cpl_table_set_double(table, "fwhm", i, fwhm);
14755  cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
14756  cpl_table_set_double(table, "resolution", i, resolution);
14757  cpl_table_set_double(table, "resolution_rms", i, rmsres);
14758  cpl_table_set_int(table, "nlines", i, nlines);
14759  }
14760  else
14761  cpl_table_set_int(table, "nlines", i, 0);
14762  }
14763 
14764  if (cpl_table_has_valid(table, "wavelength"))
14765  return table;
14766 
14767  cpl_table_delete(table);
14768 
14769  return NULL;
14770 
14771 }
14772 
14773 
14791 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
14792  int ystart, int yend, double wstart, double wend)
14793 {
14794  const char *func = "mos_integrate_signal";
14795 
14796  double sum;
14797  float *sdata;
14798  float *wdata;
14799  int nx, ny;
14800  int x, y;
14801 
14802 
14803  if (image == NULL || wavemap == NULL) {
14804  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14805  return 0.0;
14806  }
14807 
14808  if (ystart > yend || wstart >= wend) {
14809  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14810  return 0.0;
14811  }
14812 
14813  nx = cpl_image_get_size_x(image);
14814  ny = cpl_image_get_size_y(image);
14815 
14816  if (!(nx == cpl_image_get_size_x(wavemap)
14817  && ny == cpl_image_get_size_y(wavemap))) {
14818  cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
14819  return 0.0;
14820  }
14821 
14822  if (ystart < 0 || yend > ny) {
14823  cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
14824  return 0.0;
14825  }
14826 
14827  sdata = cpl_image_get_data(image);
14828  wdata = cpl_image_get_data(wavemap);
14829 
14830  sdata += ystart*nx;
14831  wdata += ystart*nx;
14832 
14833  sum = 0.0;
14834  for (y = ystart; y < yend; y++) {
14835  for (x = 0; x < nx; x++) {
14836  if (wdata[x] < wstart || wdata[x] > wend)
14837  continue;
14838  sum += sdata[x];
14839  }
14840  sdata += nx;
14841  wdata += nx;
14842  }
14843 
14844  return sum;
14845 
14846 }
14847 
14848 /****************************************************************************
14849  * From this point on, the instrument dependent functions are added:
14850  * they are functions that retrieve information that is stored in
14851  * the data headers in some instrument specific way, such as the
14852  * location of overscans, the slits positions on the telescope
14853  * focal plane, the gain factor, etc.
14854  */
14855 
14856 
14879 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
14880 {
14881  const char *func = "mos_load_slits_fors_mxu";
14882 
14883  cpl_table *slits;
14884  char keyname[MAX_COLNAME];
14885  const char *instrume;
14886  const char *target_name;
14887  float slit_x;
14888  float slit_y;
14889  float length;
14890  float slit_width;
14891 /* double arc2mm = 0.53316; */
14892  double arc2mm = 0.528;
14893  int nslits;
14894  int slit_id;
14895  int fors;
14896  int chip;
14897  int found;
14898 
14899  /*
14900  * The limits below are used to exclude from the loaded slit list
14901  * any slit that surely doesn't belong to the used chip. This is
14902  * a way to reduce the chance of ambiguous slit identification.
14903  */
14904 
14905  float low_limit1 = 10.0;
14906  float hig_limit2 = 30.0;
14907 
14908 
14909  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14910  return NULL;
14911  }
14912 
14913  if (header == NULL) {
14914  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14915  return NULL;
14916  }
14917 
14918 
14919  /*
14920  * See if this is FORS1 or FORS2;
14921  */
14922 
14923  instrume = cpl_propertylist_get_string(header, "INSTRUME");
14924 
14925  fors = 0;
14926  if (instrume[4] == '1')
14927  fors = 1;
14928  if (instrume[4] == '2')
14929  fors = 2;
14930 
14931  if (fors != 2) {
14932  cpl_msg_error(func, "Wrong instrument: %s\n"
14933  "FORS2 is expected for MXU data", instrume);
14934  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14935  return NULL;
14936  }
14937 
14938 
14939  /*
14940  * The master and slave chips can be identified by their positions
14941  * in the chip array in the case of FORS2 data (with fors1 the chip
14942  * is always 1). chip = 2 is the master, chip = 1 is the slave.
14943  */
14944 
14945  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14946 
14947  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14948  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14949  "in FITS header");
14950  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14951  return NULL;
14952  }
14953 
14954  if (chip != 1 && chip != 2) {
14955  cpl_msg_error(func, "Unexpected chip position in keyword "
14956  "ESO DET CHIP1 Y: %d", chip);
14957  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14958  return NULL;
14959  }
14960 
14961 
14962  /*
14963  * Count slits in header (excluding reference slits, and the slits
14964  * that _surely_ belong to the other chip)
14965  */
14966 
14967  nslits = 0;
14968  slit_id = 0;
14969  found = 1;
14970 
14971  while (found) {
14972  slit_id++;
14973  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14974  if (cpl_propertylist_has(header, keyname)) {
14975  slit_y = cpl_propertylist_get_double(header, keyname);
14976 
14977  if (chip == 1)
14978  if (slit_y < low_limit1)
14979  continue;
14980  if (chip == 2)
14981  if (slit_y > hig_limit2)
14982  continue;
14983 
14984  snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME",
14985  slit_id + 100);
14986  if (cpl_propertylist_has(header, keyname)) {
14987  target_name = cpl_propertylist_get_string(header, keyname);
14988  if (strncmp(target_name, "refslit", 7))
14989  nslits++;
14990  }
14991  else
14992  nslits++;
14993  }
14994  else
14995  found = 0;
14996  }
14997 
14998  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14999  cpl_msg_error(func, "%s while loading slits coordinates from "
15000  "FITS header", cpl_error_get_message());
15001  cpl_error_set_where(func);
15002  return NULL;
15003  }
15004 
15005  if (nslits == 0) {
15006  cpl_msg_error(func, "No slits coordinates found in header");
15007  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15008  return NULL;
15009  }
15010 
15011  slits = cpl_table_new(nslits);
15012  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15013  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15014  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15015  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15016  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15017  cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15018  cpl_table_set_column_unit(slits, "xtop", "pixel");
15019  cpl_table_set_column_unit(slits, "ytop", "pixel");
15020  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15021  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15022  cpl_table_set_column_unit(slits, "xwidth" , "mm");
15023 
15024  nslits = 0;
15025  slit_id = 0;
15026  found = 1;
15027  while (found) {
15028  slit_id++;
15029  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
15030  if (cpl_propertylist_has(header, keyname)) {
15031  slit_y = cpl_propertylist_get_double(header, keyname);
15032 
15033  if (chip == 1)
15034  if (slit_y < low_limit1)
15035  continue;
15036  if (chip == 2)
15037  if (slit_y > hig_limit2)
15038  continue;
15039 
15040  /*
15041  * Y-flip the slit position, to match CCD pixel coordinate
15042  * convention
15043  */
15044 
15045  slit_y = -slit_y;
15046 
15047  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
15048  slit_x = cpl_propertylist_get_double(header, keyname);
15049  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15050  cpl_table_delete(slits);
15051  cpl_msg_error(func, "Missing keyword %s in FITS header",
15052  keyname);
15053  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15054  return NULL;
15055  }
15056 
15057  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
15058  length = cpl_propertylist_get_double(header, keyname);
15059  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15060  cpl_table_delete(slits);
15061  cpl_msg_error(func, "Missing keyword %s in FITS header",
15062  keyname);
15063  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15064  return NULL;
15065  }
15066 
15067  length *= arc2mm;
15068 
15069  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d WIDTH", slit_id + 100);
15070  slit_width = cpl_propertylist_get_double(header, keyname);
15071  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15072  cpl_table_delete(slits);
15073  cpl_msg_error(func, "Missing keyword %s in FITS header",
15074  keyname);
15075  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15076  return NULL;
15077  }
15078 
15079  snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME",
15080  slit_id + 100);
15081  if (cpl_propertylist_has(header, keyname)) {
15082  target_name = cpl_propertylist_get_string(header, keyname);
15083  if (strncmp(target_name, "refslit", 7)) {
15084  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15085  cpl_table_set(slits, "xtop", nslits, slit_x);
15086  cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15087  cpl_table_set(slits, "xbottom", nslits, slit_x);
15088  cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15089  cpl_table_set(slits, "xwidth", nslits, slit_width);
15090  nslits++;
15091  }
15092  }
15093  else {
15094  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15095  cpl_table_set(slits, "xtop", nslits, slit_x);
15096  cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15097  cpl_table_set(slits, "xbottom", nslits, slit_x);
15098  cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15099  cpl_table_set(slits, "xwidth", nslits, slit_width);
15100  nslits++;
15101  }
15102  }
15103  else
15104  found = 0;
15105  }
15106 
15107  return slits;
15108 }
15109 
15110 
15134 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header,
15135  int * nslits_out_det)
15136 {
15137  const char *func = "mos_load_slits_fors_mos";
15138 
15139  cpl_table *slits;
15140  char keyname[MAX_COLNAME];
15141  const char *instrume;
15142  const char *chipname;
15143  float slit_x;
15144  int first_slit, last_slit;
15145  cpl_size nslits;
15146  int slit_id;
15147  int fors;
15148  int chip;
15149  int fors_is_old;
15150 
15151  /*
15152  * The Y coordinates of the slits are fixed
15153  */
15154 
15155  float ytop[19] = { 113.9, 101.3, 89.9, 77.3, 65.9, 53.3,
15156  41.9, 29.3, 17.9, 5.3, -6.1, -18.7,
15157  -30.1, -42.7, -54.1, -66.7, -78.1, -90.7,
15158  -102.1 };
15159  float ybottom[19] = { 102.1, 90.7, 78.1, 66.7, 54.1, 42.7,
15160  30.1, 18.7, 6.1, -5.3, -17.9, -29.3,
15161  -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
15162  -113.9 };
15163 
15164 
15165  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15166  return NULL;
15167  }
15168 
15169  if (header == NULL) {
15170  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15171  return NULL;
15172  }
15173 
15174 
15175  /*
15176  * See if this is FORS1 or FORS2;
15177  */
15178 
15179  instrume = cpl_propertylist_get_string(header, "INSTRUME");
15180 
15181  fors = 0;
15182  if (instrume[4] == '1')
15183  fors = 1;
15184  if (instrume[4] == '2')
15185  fors = 2;
15186 
15187  if (fors == 0) {
15188  cpl_msg_error(func, "Wrong instrument found in FITS header: %s",
15189  instrume);
15190  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15191  return NULL;
15192  }
15193 
15194  /* FIXME:
15195  * This is the way FORS1 data belong to the upgraded chips,
15196  * named "Marlene" and "Norma III". It's a quick solution,
15197  * there are hardcoded values here!!!
15198  */
15199 
15200  chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
15201 
15202  if (chipname[0] == 'M' || chipname[0] == 'N')
15203  fors_is_old = 0;
15204  else
15205  fors_is_old = 1;
15206 
15207  if (fors == 1 && fors_is_old) {
15208  first_slit = 1;
15209  last_slit = 19;
15210  }
15211  else {
15212 
15213  /*
15214  * The master and slave chips can be identified by their positions
15215  * in the chip array in the case of FORS2 data: chip = 2 is the
15216  * master, chip = 1 is the slave.
15217  */
15218 
15219  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15220 
15221  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15222  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15223  "in FITS header");
15224  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15225  return NULL;
15226  }
15227 
15228  if (chip != 1 && chip != 2) {
15229  cpl_msg_error(func, "Unexpected chip position in keyword "
15230  "ESO DET CHIP1 Y: %d", chip);
15231  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15232  return NULL;
15233  }
15234 
15235  if (chip == 1) {
15236  first_slit = 12;
15237  last_slit = 19;
15238  }
15239  else {
15240  first_slit = 1;
15241  last_slit = 11;
15242  }
15243  }
15244 
15245 
15246  /*
15247  * Count slits in header (excluding closed slits - i.e. those with
15248  * offsets greater than 115 mm - and the slits that do not belong
15249  * to this chip)
15250  */
15251 
15252  nslits = 0;
15253  slit_id = 0;
15254  for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15255  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15256  if (cpl_propertylist_has(header, keyname)) {
15257  slit_x = cpl_propertylist_get_double(header, keyname);
15258  if (fabs(slit_x) < 115.0)
15259  nslits++;
15260  else
15261  (*nslits_out_det)++;
15262  }
15263  else {
15264  cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
15265  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15266  return NULL;
15267  }
15268  }
15269 
15270  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15271  cpl_msg_error(func, "%s while loading slits coordinates from "
15272  "FITS header", cpl_error_get_message());
15273  cpl_error_set_where(func);
15274  return NULL;
15275  }
15276 
15277  if (nslits == 0) {
15278  cpl_msg_error(func, "No slits coordinates found in header");
15279  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15280  return NULL;
15281  }
15282 
15283  slits = cpl_table_new(nslits);
15284  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15285  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15286  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15287  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15288  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15289  cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15290  cpl_table_set_column_unit(slits, "xtop", "pixel");
15291  cpl_table_set_column_unit(slits, "ytop", "pixel");
15292  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15293  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15294  cpl_table_set_column_unit(slits, "xwidth" , "mm");
15295 
15296  nslits = 0;
15297  slit_id = 0;
15298  for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15299  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15300  slit_x = cpl_propertylist_get_double(header, keyname);
15301  if (fabs(slit_x) < 115.0) {
15302  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15303  cpl_table_set(slits, "xtop", nslits, slit_x);
15304  cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
15305  cpl_table_set(slits, "xbottom", nslits, slit_x);
15306  cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
15307  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d WIDTH", slit_id);
15308  double slit_width = cpl_propertylist_get_double(header, keyname);
15309  cpl_table_set(slits, "xwidth", nslits, slit_width);
15310  nslits++;
15311  }
15312  }
15313 
15314  return slits;
15315 }
15316 
15317 
15341 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
15342 {
15343  const char *func = "mos_load_slits_fors_lss";
15344 
15345  cpl_table *slits;
15346  char *slit_name;
15347  const char *instrume;
15348  int fors;
15349  int chip;
15350  float ytop;
15351  float ybottom;
15352 
15353  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15354  return NULL;
15355  }
15356 
15357  if (header == NULL) {
15358  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15359  return NULL;
15360  }
15361 
15362 
15363  /*
15364  * See if this is FORS1 or FORS2;
15365  */
15366 
15367  instrume = cpl_propertylist_get_string(header, "INSTRUME");
15368 
15369  fors = 0;
15370  if (instrume[4] == '1')
15371  fors = 1;
15372  if (instrume[4] == '2')
15373  fors = 2;
15374 
15375  if (fors == 0) {
15376  cpl_msg_error(func, "Wrong instrument found in FITS header: %s",
15377  instrume);
15378  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15379  return NULL;
15380  }
15381 
15382  if (fors == 1) {
15383  ytop = 109.94;
15384  ybottom = -109.94;
15385  }
15386  else {
15387 
15388  /*
15389  * The master and slave chips can be identified by their positions
15390  * in the chip array in the case of FORS2 data: chip = 2 is the
15391  * master, chip = 1 is the slave.
15392  */
15393 
15394  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15395 
15396  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15397  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15398  "in FITS header");
15399  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15400  return NULL;
15401  }
15402 
15403  if (chip != 1 && chip != 2) {
15404  cpl_msg_error(func, "Unexpected chip position in keyword "
15405  "ESO DET CHIP1 Y: %d", chip);
15406  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15407  return NULL;
15408  }
15409 
15410  if (chip == 1) {
15411  ytop = 30.0;
15412  ybottom = -109.94;
15413  }
15414  else {
15415  ytop = 109.94;
15416  ybottom = -20.0;
15417  }
15418  }
15419 
15420 
15421  slits = cpl_table_new(1);
15422  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15423  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15424  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15425  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15426  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15427  cpl_table_set_column_unit(slits, "xtop", "pixel");
15428  cpl_table_set_column_unit(slits, "ytop", "pixel");
15429  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15430  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15431 
15432  slit_name = (char *)cpl_propertylist_get_string(header,
15433  "ESO INS SLIT NAME");
15434 
15435  cpl_table_set(slits, "ytop", 0, ytop);
15436  cpl_table_set(slits, "ybottom", 0, ybottom);
15437 
15438  if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
15439  cpl_table_set_int(slits, "slit_id", 0, 1);
15440  cpl_table_set(slits, "xbottom", 0, -0.075);
15441  cpl_table_set(slits, "xtop", 0, 0.075);
15442  }
15443  else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
15444  cpl_table_set_int(slits, "slit_id", 0, 2);
15445  cpl_table_set(slits, "xbottom", 0, 5.895);
15446  cpl_table_set(slits, "xtop", 0, 6.105);
15447  }
15448  else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
15449  cpl_table_set_int(slits, "slit_id", 0, 3);
15450  cpl_table_set(slits, "xbottom", 0, -6.135);
15451  cpl_table_set(slits, "xtop", 0, -5.865);
15452  }
15453  else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
15454  cpl_table_set_int(slits, "slit_id", 0, 4);
15455  cpl_table_set(slits, "xbottom", 0, 11.815);
15456  cpl_table_set(slits, "xtop", 0, 12.185);
15457  }
15458  else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
15459  cpl_table_set_int(slits, "slit_id", 0, 5);
15460  cpl_table_set(slits, "xbottom", 0, -12.265);
15461  cpl_table_set(slits, "xtop", 0, -11.735);
15462  }
15463  else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
15464  cpl_table_set_int(slits, "slit_id", 0, 6);
15465  cpl_table_set(slits, "xbottom", 0, 17.655);
15466  cpl_table_set(slits, "xtop", 0, 18.345);
15467  }
15468  else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
15469  cpl_table_set_int(slits, "slit_id", 0, 7);
15470  cpl_table_set(slits, "xbottom", 0, -18.425);
15471  cpl_table_set(slits, "xtop", 0, -17.575);
15472  }
15473  else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
15474  cpl_table_set_int(slits, "slit_id", 0, 8);
15475  cpl_table_set(slits, "xbottom", 0, 23.475);
15476  cpl_table_set(slits, "xtop", 0, 24.525);
15477  }
15478  else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
15479  cpl_table_set_int(slits, "slit_id", 0, 9);
15480  cpl_table_set(slits, "xbottom", 0, -24.66);
15481  cpl_table_set(slits, "xtop", 0, -23.34);
15482  }
15483  else {
15484  cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
15485  slit_name);
15486  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15487  cpl_table_delete(slits);
15488  return NULL;
15489  }
15490 
15491  return slits;
15492 }
15493 
15494 
15509 double mos_get_gain_vimos(cpl_propertylist *header)
15510 {
15511  const char *func = "mos_get_gain_vimos";
15512 
15513  double gain = -1.0;
15514 
15515 
15516  if (cpl_error_get_code() != CPL_ERROR_NONE)
15517  return gain;
15518 
15519  if (header == NULL) {
15520  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15521  return gain;
15522  }
15523 
15524  gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
15525  if (cpl_error_get_code()) {
15526  cpl_error_set_where(func);
15527  gain = -1.0;
15528  }
15529 
15530  return gain;
15531 
15532 }
15533 
15534 
15554 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
15555 {
15556  const char *func = "mos_load_slits_vimos";
15557 
15558  cpl_table *slits;
15559  char keyname[MAX_COLNAME];
15560  float slit_x;
15561  float slit_y;
15562  float dim_x;
15563  float dim_y;
15564  int nslits;
15565  int slit_id;
15566  int curved;
15567  int i;
15568 
15569 
15570  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15571  return NULL;
15572  }
15573 
15574  if (header == NULL) {
15575  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15576  return NULL;
15577  }
15578 
15579  nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
15580 
15581  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15582  cpl_error_set_where(func);
15583  return NULL;
15584  }
15585 
15586  slits = cpl_table_new(nslits);
15587  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15588  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15589  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15590  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15591  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15592  cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15593  cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE);
15594  cpl_table_new_column(slits, "curved", CPL_TYPE_INT);
15595  cpl_table_set_column_unit(slits, "xtop", "pixel");
15596  cpl_table_set_column_unit(slits, "ytop", "pixel");
15597  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15598  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15599  cpl_table_set_column_unit(slits, "xwidth", "mm");
15600  cpl_table_set_column_unit(slits, "ywidth", "mm");
15601 
15602  for (i = 0; i < nslits; i++) {
15603  sprintf(keyname, "ESO INS SLIT%d ID", i+1);
15604  slit_id = cpl_propertylist_get_int(header, keyname);
15605  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15606  cpl_error_set_where(func);
15607  return NULL;
15608  }
15609  sprintf(keyname, "ESO INS SLIT%d X", i+1);
15610  slit_x = cpl_propertylist_get_double(header, keyname);
15611  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15612  cpl_error_set_where(func);
15613  return NULL;
15614  }
15615  sprintf(keyname, "ESO INS SLIT%d Y", i+1);
15616  slit_y = cpl_propertylist_get_double(header, keyname);
15617  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15618  cpl_error_set_where(func);
15619  return NULL;
15620  }
15621  sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
15622  dim_x = cpl_propertylist_get_double(header, keyname);
15623  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15624  cpl_error_set_where(func);
15625  return NULL;
15626  }
15627 
15628  sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1);
15629  if (cpl_propertylist_has(header, keyname)) {
15630  curved = 1;
15631  }
15632  else {
15633  sprintf(keyname, "ESO INS SLIT%d DIMY", i+1);
15634  curved = 0;
15635  }
15636  dim_y = cpl_propertylist_get_double(header, keyname);
15637  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15638  cpl_error_set_where(func);
15639  return NULL;
15640  }
15641 
15642  cpl_table_set_int(slits, "slit_id", i, slit_id);
15643  cpl_table_set(slits, "xtop", i, slit_x - dim_x/2);
15644  cpl_table_set(slits, "ytop", i, slit_y);
15645  cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2);
15646  cpl_table_set(slits, "ybottom", i, slit_y);
15647  cpl_table_set(slits, "xwidth", i, dim_x);
15648  cpl_table_set(slits, "ywidth", i, dim_y);
15649  cpl_table_set_int(slits, "curved", i, curved);
15650  }
15651 
15652  return slits;
15653 }
15654 
15655 
15665 int mos_check_multiplex(cpl_table *slits)
15666 {
15667  cpl_propertylist *sort;
15668  int nrow;
15669  int i, multiplex, xprev, xcur;
15670  double prev, cur;
15671  double tolerance = 1.0; // About spatially aligned slits (mm)
15672 
15673 
15674  /*
15675  * Create an auxiliary column containing a sort of integer
15676  * x coordinate of the slit, to guarantee that slits at the
15677  * same spatial offset are recognised immediately as in spectral
15678  * multiplexing.
15679  */
15680 
15681  sort = cpl_propertylist_new();
15682  cpl_propertylist_append_bool(sort, "xtop", 0);
15683  cpl_table_sort(slits, sort);
15684  cpl_propertylist_delete(sort);
15685 
15686  prev = cpl_table_get_double(slits, "xtop", 0, NULL);
15687  cpl_table_new_column(slits, "xind", CPL_TYPE_INT);
15688  cpl_table_set_int(slits, "xind", 0, prev); // cast to int is intentional
15689  nrow = cpl_table_get_nrow(slits);
15690  for (i = 1; i < nrow; i++) {
15691  cur = cpl_table_get_double(slits, "xtop", i, NULL);
15692  if (fabs(prev - cur) > tolerance)
15693  prev = cur;
15694  cpl_table_set_int(slits, "xind", i, prev);
15695  }
15696 
15697  /*
15698  * Now sort according to increasing (integer) x positions, and when
15699  * those are equal (multiplexed) according to the increasing y position.
15700  */
15701 
15702  sort = cpl_propertylist_new();
15703  cpl_propertylist_append_bool(sort, "xind", 0);
15704  cpl_propertylist_append_bool(sort, "ytop", 0);
15705  cpl_table_sort(slits, sort);
15706  cpl_propertylist_delete(sort);
15707 
15708  /*
15709  * Now assign to each slit its multiplex order.
15710  */
15711 
15712  multiplex = 0;
15713  cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT);
15714  xprev = cpl_table_get_int(slits, "xind", 0, NULL);
15715  cpl_table_set_int(slits, "multiplex", 0, multiplex);
15716  nrow = cpl_table_get_nrow(slits);
15717  for (i = 1; i < nrow; i++) {
15718  xcur = cpl_table_get_int(slits, "xind", i, NULL);
15719  if (xcur == xprev) {
15720  multiplex++;
15721  }
15722  else {
15723  xprev = xcur;
15724  multiplex = 0;
15725  }
15726  cpl_table_set_int(slits, "multiplex", i, multiplex);
15727  }
15728 
15729  cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT);
15730 
15731  cpl_table_erase_column(slits, "xind");
15732 
15733  return 1 + cpl_table_get_column_max(slits, "multiplex");
15734 
15735 }
15736 
15737 
15764 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header,
15765  int check_consistency)
15766 {
15767  const char *func = "mos_load_overscans_vimos";
15768 
15769  int nx = 0;
15770  int ny = 0;
15771  int px = 0;
15772  int py = 0;
15773  int ox = 0;
15774  int oy = 0;
15775  int vx = 0;
15776  int vy = 0;
15777  int nrows;
15778  cpl_table *overscans;
15779 
15780 
15781  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15782  cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15783  return NULL;
15784  }
15785 
15786  if (header == NULL) {
15787  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15788  return NULL;
15789  }
15790 
15791  if (cpl_propertylist_has(header, "NAXIS1"))
15792  nx = cpl_propertylist_get_int(header, "NAXIS1");
15793  if (cpl_propertylist_has(header, "NAXIS2"))
15794  ny = cpl_propertylist_get_int(header, "NAXIS2");
15795  if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
15796  px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
15797  if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
15798  py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
15799  if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
15800  ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
15801  if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
15802  oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
15803  if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
15804  vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
15805  if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
15806  vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
15807 
15808  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15809  cpl_msg_error(func, "Missing overscan keywords in header");
15810  cpl_error_set_where(func);
15811  return NULL;
15812  }
15813 
15814  if (px < 0 || py < 0 || ox < 0 || oy < 0) {
15815  cpl_msg_error(func, "Missing overscan keywords in header");
15816  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15817  return NULL;
15818  }
15819 
15820  if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
15821  if (check_consistency) {
15822  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15823  return NULL;
15824  }
15825  else {
15826  cpl_msg_debug(func, "Overscans description conflicts with "
15827  "reported image sizes, "
15828  "%d + %d + %d != %d or "
15829  "%d + %d + %d != %d",
15830  px, vx, ox, nx,
15831  py, vy, oy, ny);
15832  }
15833  }
15834 
15835  nrows = 0;
15836  if (px > 0)
15837  nrows++;
15838  if (ox > 0)
15839  nrows++;
15840  if (py > 0)
15841  nrows++;
15842  if (oy > 0)
15843  nrows++;
15844 
15845  if (nrows > 2) {
15846  cpl_msg_error(func, "Unexpected overscan regions "
15847  "(both in X and Y direction)");
15848  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15849  return NULL;
15850  }
15851 
15852 
15853  /*
15854  * A row is added for the description of the valid region of the
15855  * exposure the input header belongs to.
15856  */
15857 
15858  nrows++;
15859 
15860  overscans = cpl_table_new(nrows);
15861  cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15862  cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15863  cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15864  cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15865 
15866  nrows = 0;
15867 
15868  cpl_table_set_int(overscans, "xlow", nrows, px);
15869  cpl_table_set_int(overscans, "ylow", nrows, py);
15870  cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15871  cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15872  nrows++;
15873 
15874  if (px > 0) {
15875  cpl_table_set_int(overscans, "xlow", nrows, 0);
15876  cpl_table_set_int(overscans, "ylow", nrows, 0);
15877  cpl_table_set_int(overscans, "xhig", nrows, px);
15878  cpl_table_set_int(overscans, "yhig", nrows, ny);
15879  nrows++;
15880  }
15881 
15882  if (ox > 0) {
15883  cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15884  cpl_table_set_int(overscans, "ylow", nrows, 0);
15885  cpl_table_set_int(overscans, "xhig", nrows, nx);
15886  cpl_table_set_int(overscans, "yhig", nrows, ny);
15887  nrows++;
15888  }
15889 
15890  if (py > 0) {
15891  cpl_table_set_int(overscans, "xlow", nrows, 0);
15892  cpl_table_set_int(overscans, "ylow", nrows, 0);
15893  cpl_table_set_int(overscans, "xhig", nrows, nx);
15894  cpl_table_set_int(overscans, "yhig", nrows, py);
15895  nrows++;
15896  }
15897 
15898  if (oy > 0) {
15899  cpl_table_set_int(overscans, "xlow", nrows, 0);
15900  cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
15901  cpl_table_set_int(overscans, "xhig", nrows, nx);
15902  cpl_table_set_int(overscans, "yhig", nrows, ny);
15903  nrows++;
15904  }
15905 
15906  return overscans;
15907 
15908 }
15909 
15910 
15911 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
15912 {
15913  const char *func = "mos_load_overscans_fors";
15914 
15915  int nports;
15916  int nx = 0;
15917  int ny = 0;
15918  int px = 0;
15919  int py = 0;
15920  int ox = 0;
15921  int oy = 0;
15922  int rebin;
15923  int nrows;
15924  cpl_table *overscans;
15925 
15926 
15927  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15928  cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15929  return NULL;
15930  }
15931 
15932  if (header == NULL) {
15933  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15934  return NULL;
15935  }
15936 
15937  if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
15938  nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
15939 
15940  if (nports == 4 &&
15941  cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
15942  cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
15943 
15944  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
15945 
15946  overscans = cpl_table_new(3);
15947  cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15948  cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15949  cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15950  cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15951 
15952  px = 16 / rebin;
15953  ox = 16 / rebin;
15954  nx = 2080 / rebin;
15955  ny = 2048 / rebin;
15956  nrows = 0;
15957 
15958  cpl_table_set_int(overscans, "xlow", nrows, px);
15959  cpl_table_set_int(overscans, "ylow", nrows, py);
15960  cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15961  cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15962  nrows++;
15963 
15964  cpl_table_set_int(overscans, "xlow", nrows, 0);
15965  cpl_table_set_int(overscans, "ylow", nrows, 0);
15966  cpl_table_set_int(overscans, "xhig", nrows, px);
15967  cpl_table_set_int(overscans, "yhig", nrows, ny);
15968  nrows++;
15969 
15970  cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15971  cpl_table_set_int(overscans, "ylow", nrows, 0);
15972  cpl_table_set_int(overscans, "xhig", nrows, nx);
15973  cpl_table_set_int(overscans, "yhig", nrows, ny);
15974  nrows++;
15975  }
15976  else {
15977  overscans = mos_load_overscans_vimos(header, 0);
15978  }
15979 
15980  return overscans;
15981 
15982 }
15983 
16015 #define READY 1
16016 #ifdef READY
16017 
16018 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate,
16019  int samples, int order)
16020 {
16021 
16022  const char *func = "mos_montecarlo_polyfit";
16023 
16024  cpl_polynomial *p;
16025  cpl_polynomial *q;
16026  cpl_vector *listx;
16027  cpl_vector *listy;
16028  double err;
16029  double *x;
16030  double *px;
16031  double *x_eval;
16032  double *px_eval;
16033  double *sigma;
16034  double *vy;
16035  double *dy;
16036  int npoints, nevaluate;
16037  int i, j;
16038 
16039 
16040  if (points == NULL || evaluate == NULL) {
16041  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
16042  return NULL;
16043  }
16044 
16045  if (!cpl_table_has_column(points, "x")) {
16046  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16047  return NULL;
16048  }
16049 
16050  if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
16051  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16052  return NULL;
16053  }
16054 
16055  if (cpl_table_has_invalid(points, "x")) {
16056  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16057  return NULL;
16058  }
16059 
16060  if (!cpl_table_has_column(points, "y")) {
16061  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16062  return NULL;
16063  }
16064 
16065  if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
16066  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16067  return NULL;
16068  }
16069 
16070  if (cpl_table_has_invalid(points, "y")) {
16071  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16072  return NULL;
16073  }
16074 
16075  if (cpl_table_has_column(points, "y_err")) {
16076 
16077  if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
16078  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16079  return NULL;
16080  }
16081 
16082  if (cpl_table_has_invalid(points, "y_err")) {
16083  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16084  return NULL;
16085  }
16086  }
16087 
16088  if (!cpl_table_has_column(evaluate, "x")) {
16089  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16090  return NULL;
16091  }
16092 
16093  if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
16094  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16095  return NULL;
16096  }
16097 
16098  if (cpl_table_has_invalid(evaluate, "x")) {
16099  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16100  return NULL;
16101  }
16102 
16103  if (samples < 2 || order < 0) {
16104  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16105  return NULL;
16106  }
16107 
16108  npoints = cpl_table_get_nrow(points);
16109  listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
16110  listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
16111 
16112  p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
16113 
16114  if (!cpl_table_has_column(points, "y_err")) {
16115  err = sqrt(err);
16116  cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
16117  cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
16118  cpl_msg_info(func, "Error column not found - set to %f\n", err);
16119  }
16120 
16121  /*
16122  * Create columns containing modeled values at each x
16123  */
16124 
16125  if (cpl_table_has_column(points, "px"))
16126  cpl_table_erase_column(points, "px");
16127  cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
16128  cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
16129  x = cpl_table_get_data_double(points, "x");
16130  px = cpl_table_get_data_double(points, "px");
16131  for (i = 0; i < npoints; i++)
16132  px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
16133 
16134  nevaluate = cpl_table_get_nrow(evaluate);
16135 
16136  if (cpl_table_has_column(evaluate, "px"))
16137  cpl_table_erase_column(evaluate, "px");
16138  cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
16139  cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
16140  x_eval = cpl_table_get_data_double(evaluate, "x");
16141  px_eval = cpl_table_get_data_double(evaluate, "px");
16142  for (i = 0; i < nevaluate; i++)
16143  px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
16144 
16145  /*
16146  * Initialise column with sigma
16147  */
16148 
16149  if (cpl_table_has_column(evaluate, "sigma"))
16150  cpl_table_erase_column(evaluate, "sigma");
16151  cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
16152  cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
16153  sigma = cpl_table_get_data_double(evaluate, "sigma");
16154 
16155  /*
16156  * Compute varied y cordinates to fit
16157  */
16158 
16159  if (cpl_table_has_column(points, "vy"))
16160  cpl_table_erase_column(points, "vy");
16161  cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
16162  cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
16163  vy = cpl_table_get_data_double(points, "vy");
16164  dy = cpl_table_get_data_double(points, "y_err");
16165  cpl_vector_unwrap(listy);
16166  listy = cpl_vector_wrap(npoints, vy);
16167 
16168  for (i = 0; i < samples; i++) {
16169  for (j = 0; j < npoints; j++)
16170  vy[j] = px[j] + dy[j] * mos_randg(1);
16171  q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
16172  for (j = 0; j < nevaluate; j++)
16173  sigma[j] += fabs(px_eval[j]
16174  - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
16175  cpl_polynomial_delete(q);
16176  }
16177 
16178  /*
16179  * Factor 1.25 to convert average deviation to sigma
16180  */
16181 
16182  cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
16183  cpl_table_divide_scalar(evaluate, "sigma", samples);
16184 
16185  cpl_vector_unwrap(listx);
16186  cpl_vector_unwrap(listy);
16187 
16188  return p;
16189 }
16190 
16191 #endif
16192 
16215 cpl_error_code mos_randomise_image(cpl_image *image, double ron,
16216  double gain, double bias)
16217 {
16218  float *data;
16219  int npix, i;
16220 
16221 
16222  if (image == NULL)
16223  return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
16224 
16225  if (ron < 0.0 || gain <= FLT_EPSILON)
16226  return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
16227 
16228  data = cpl_image_get_data_float(image);
16229  npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image);
16230  ron *= ron;
16231 
16232  for (i = 0; i < npix; i++) {
16233  if (data[i] < bias) {
16234  data[i] += sqrt(ron) * mos_randg(1);
16235  }
16236  else {
16237  data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1);
16238  }
16239  }
16240 
16241  return CPL_ERROR_NONE;
16242 }
16243 
16244 
16259 cpl_error_code mos_refmask_find_gaps(cpl_mask *refmask,
16260  cpl_image *master_flat,
16261  double level)
16262 {
16263  int nx = cpl_mask_get_size_x(refmask);
16264  int ny = cpl_mask_get_size_y(refmask);
16265 
16266  int * xpos = cpl_calloc(sizeof(int), ny);
16267 
16268  cpl_image * filtered = cpl_image_duplicate(master_flat);
16269  cpl_mask * kernel = cpl_mask_new(9, 3);
16270  cpl_vector * v = cpl_vector_new(ny);
16271  cpl_vector * truev;
16272  int nvalid = 0;
16273  double * flats = cpl_vector_get_data(v);
16274 
16275  double median, stdev, delta;
16276 
16277  int i, kill;
16278 
16279  cpl_mask_not(kernel);
16280  cpl_image_filter_mask(filtered, master_flat, kernel,
16281  CPL_FILTER_MEDIAN, CPL_BORDER_COPY);
16282  cpl_mask_delete(kernel);
16283 
16284  for (i = 1; i <= ny; i++) {
16285  int j = 0;
16286 
16287  do j++;
16288  while (!cpl_mask_get(refmask, j, i) && j < nx);
16289 
16290  if (j < nx) {
16291  int rejected;
16292 
16293  xpos[i - 1] = j;
16294  flats[nvalid] = cpl_image_get(filtered, j, i, &rejected);
16295  nvalid++;
16296  }
16297  else {
16298  xpos[i - 1] = -1;
16299  }
16300  }
16301 
16302  if (nvalid == 0)
16303  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16304 
16305  truev = cpl_vector_wrap(nvalid, flats);
16306 
16307  median = cpl_vector_get_median(truev);
16308 
16309  if (level < 0.0)
16310  stdev = cpl_vector_get_stdev(truev);
16311 
16312  cpl_vector_unwrap(truev);
16313  cpl_vector_delete(v);
16314 
16315  for (i = 1; i <= ny; i++) {
16316  if (xpos[i - 1] > 0) {
16317  int rejected;
16318  double kappa = 1.5;
16319 
16320  delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median;
16321 
16322  if (level < 0.0)
16323  kill = fabs(delta) > stdev * kappa;
16324  else
16325  kill = delta < level;
16326 
16327  if (kill) {
16328  int j = 0;
16329 
16330  while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
16331  cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
16332  j++;
16333  }
16334  }
16335  }
16336  }
16337 
16338  cpl_image_delete(filtered);
16339  cpl_free(xpos);
16340 
16341  return cpl_error_get_code();
16342 }
16343 
16351 cpl_error_code mos_saturation_process(cpl_image * image)
16352 {
16353  int nx = cpl_image_get_size_x(image);
16354  int ny = cpl_image_get_size_y(image);
16355  int npix = nx * ny;
16356  float * sdata = cpl_image_get_data_float(image);
16357 
16358  int count, i, j, k;
16359 
16360  /*
16361  * This is used to avoid saturation level coded with pixel value zero
16362  * To make it more robust against random 0.0 values, check that also
16363  * next pixel along the spatial direction is 0.0.
16364  */
16365 
16366  //This could be applied only to raw images, but it is being applied
16367  //to already bias/overscan processed images, which doesn't make sense.
16368 // for (i = 0; i < npix - nx; i++)
16369 // if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
16370 // sdata[i] = 65535.0;
16371 
16372 // for (i = npix - nx; i < npix; i++)
16373 // if (sdata[i] == 0.0)
16374 // sdata[i] = 65535.0;
16375 
16376  /*
16377  * This is a dirty trick to overcome saturations (making up a false
16378  * tip on their flat tops). This should be useless with a better
16379  * peak detection algorithm.
16380  */
16381 
16382  for (i = 0; i < npix; i++) {
16383  if (sdata[i] >= 65535.0) {
16384  count = 0;
16385  for (j = i; j < npix; j++) {
16386  if (sdata[j] < 65535.0) {
16387  break;
16388  }
16389  else {
16390  count++;
16391  }
16392  }
16393  if (count < 30 && count > 2) {
16394  for (j = i; j < i + count/2; j++)
16395  sdata[j] = sdata[i] + 1000.0 * (j - i);
16396  if (count % 2 != 0) {
16397  sdata[j] = sdata[j-1] + 1000.0;
16398  j++;
16399  }
16400  for (k = j; k <= i + count; k++)
16401  sdata[k] = sdata[i] - 1000.0 * (k - i - count);
16402  i = k;
16403  }
16404  }
16405  }
16406 
16407  return cpl_error_get_code();
16408 }
16409 
16410 
16419 cpl_error_code mos_subtract_background(cpl_image * image)
16420 {
16421  /*
16422  * Create and subtract background
16423  */
16424 
16425  cpl_image * bimage = mos_arc_background(image, 15, 15);
16426  cpl_image_subtract(image, bimage);
16427  cpl_image_delete(bimage);
16428 
16429  return cpl_error_get_code();
16430 }
16431 
16432 
16449 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits,
16450  int nscience, float tolerance)
16451 {
16452  int i, j;
16453 
16454  cpl_table *summary;
16455  int summary_nobjs = 0;
16456 
16457  int nobjs;
16458 
16459  int nmatches;
16460  int nslits = cpl_table_get_nrow(slitss[0]);
16461 
16462  int maxobjs;
16463  int k, m;
16464  int nstokes, sstokes;
16465 
16466  cpl_table **work;
16467 
16468  work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
16469 
16470 
16471  /*
16472  * First we build a table listing the offset of each detected
16473  * object at each angle and each beam, from the bottom of each
16474  * slit spectrum, and the pair that slit spectrum belongs to.
16475  * This summary table will have as many rows as objects found
16476  * in total at all angles.
16477  */
16478 
16479  for (j = 0; j < nscience; j++) {
16480  int c_nobjs = mos_get_nobjects(slitss[j]);
16481  if (!c_nobjs)
16482  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16483  summary_nobjs += c_nobjs;
16484  }
16485 
16486  summary = cpl_table_new(summary_nobjs);
16487 
16488  cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
16489  cpl_table_new_column(summary, "pair", CPL_TYPE_INT);
16490  cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE);
16491  cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE);
16492 
16493  /*
16494  * Fill the summary table with data from all objects:
16495  */
16496 
16497  nobjs = 0;
16498 
16499  /* Loop on all object tables (one for each angle) */
16500  for (j = 0; j < nscience; j++) {
16501  int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
16502 
16503  /* Loop on all slits found on first - i.e., ALL - object table */
16504  for (k = 0; k < nslits; k++) {
16505 
16506  /* Loop on all objects found on each object table */
16507  for (m = 0; m < c_maxobjs; m++) {
16508  int null;
16509  char *name = cpl_sprintf("object_%d", m + 1);
16510  double obj = cpl_table_get_double(slitss[j], name, k, &null);
16511  int pos;
16512  int pair;
16513 
16514  cpl_free(name);
16515 
16516  if (null)
16517  break; /* No object #m+1 in this slit - go to next slit */
16518 
16519  /*
16520  * Copy necessary object data to summary table. Note
16521  * that the absolute object position (row) in the
16522  * rectified image is made relative to the bottom
16523  * position (row) of the current slit.
16524  */
16525 
16526  pos = cpl_table_get_int(slitss[j], "position", k, &null);
16527  pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
16528  cpl_table_set(summary, "absolute", nobjs, obj);
16529  cpl_table_set(summary, "pos", nobjs, pos);
16530  cpl_table_set(summary, "offset", nobjs, obj - pos);
16531  cpl_table_set(summary, "pair", nobjs, pair);
16532 
16533  nobjs++;
16534  }
16535  }
16536  }
16537 
16538 // cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
16539 
16540  /*
16541  * Perform the intersection: what are the objects belonging
16542  * to the same slit (same pair ordinary + extraordinary) which
16543  * are observed at the same offset at all angles? Those are
16544  * the polarimetric objects.
16545  */
16546 
16547  nmatches = 0;
16548  maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
16549 
16550  /*
16551  * We loop on the objects of the first-angle object table as
16552  * reference, and check whether those objects are present also
16553  * at *all* other angles. Note that the loop advances by pairs.
16554  * If the top (k = 0) slit spectrum is not an ordinary beam,
16555  * it is ignored. The loop advances by pairs, starting at the
16556  * first complete pair. It is implicitely assumed that the
16557  * slit spectrum on top is always from the ordinary beam, and
16558  * the spectrum below (k+1) its extraordinary match.
16559  */
16560 
16561  for (k = 0; k < nslits; k+=2) {
16562  int slitmatches = 0;
16563 
16564  if (k + 1 < nslits ) {
16565  if (cpl_table_get_int(slitss[0], "pair_id", k, NULL) !=
16566  cpl_table_get_int(slitss[0], "pair_id", k + 1, NULL)) {
16567 
16568  /*
16569  * This is not an ordinary beam - advance to next slit.
16570  */
16571 
16572  /* It will be incremented by two, so the effect is like k++ */
16573  k--;
16574 
16575  continue;
16576  }
16577  }
16578 
16579  for (m = 0; m < maxobjs; m++) {
16580  int null;
16581  char *name = cpl_sprintf("object_%d", m + 1);
16582  double obj = cpl_table_get_double(slitss[0], name, k, &null);
16583  double pos;
16584  int pair;
16585 
16586  char *name_obj = NULL;
16587  char *name_start = NULL;
16588  char *name_end = NULL;
16589  char *name_row = NULL;
16590  char *name_row_s = NULL;
16591 
16592  char *name_start_o = NULL;
16593  char *name_end_o = NULL;
16594  char *name_row_o = NULL;
16595  char *name_start_v = NULL;
16596  char *name_end_v = NULL;
16597  char *name_obj_v = NULL;
16598 
16599  int start, end;
16600  int length;
16601 
16602  int selected;
16603  int v, start_v, end_v;
16604  double min_v, obj_v;
16605 
16606 
16607  cpl_free(name);
16608 
16609  if (null)
16610  break;
16611 
16612  /*
16613  * Each object of the first object table belongs to a
16614  * slit spectrum (k). This slit spectrum has a position
16615  * in the rectified image, and it belongs to a given
16616  * ordinary + extraordinary pair.
16617  */
16618 
16619  pos = cpl_table_get_int(slitss[0], "position", k, &null);
16620  pair = cpl_table_get_int(slitss[0], "pair_id", k, &null);
16621 
16622  /*
16623  * Now from the summary table we can select all objects
16624  * which have the same offset (obj - pos) within all slit
16625  * spectra belonging to the same ordinary + extraordinary
16626  * pair (at all angles).
16627  */
16628 
16629  cpl_table_select_all(summary); /* Reset selection */
16630 
16631  cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
16632  cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
16633  obj - pos + tolerance);
16634  selected =
16635  cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
16636  obj - pos - tolerance);
16637 
16638 
16639  /*
16640  * If this object were observed at all angles (nscience) and
16641  * at all beams (2), we should have selected exactly 2*nscience
16642  * objects. If not, this is not a polarimetric object, and it
16643  * is discarded from the intersection.
16644  */
16645 
16646  if (selected != nscience * 2)
16647  continue;
16648 
16649  /*
16650  * If we reach this point we have found one valid polarimetric
16651  * object, that must be inserted in the intersection object
16652  * table.
16653  */
16654 
16655  slitmatches++;
16656 
16657  /*
16658  * Names of the columns of the output table where the
16659  * object information needs to be copied. Note that a
16660  * new column is created, the "row_stokes_#", where the
16661  * row number of the extracted polarimetric signal is
16662  * also computed. For the moment this column will be
16663  * left empty - it will be filled only when all matches
16664  * are collected.
16665  */
16666 
16667  name_obj = cpl_sprintf("object_%d", slitmatches);
16668  name_start = cpl_sprintf("start_%d", slitmatches);
16669  name_end = cpl_sprintf("end_%d", slitmatches);
16670  name_row = cpl_sprintf("row_%d", slitmatches);
16671  name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
16672 
16673  /*
16674  * Names of the columns of the input table where the
16675  * object information is available.
16676  */
16677 
16678  name_start_o = cpl_sprintf("start_%d", m + 1);
16679  name_end_o = cpl_sprintf("end_%d", m + 1);
16680  name_row_o = cpl_sprintf("row_%d", m + 1);
16681 
16682  /*
16683  * If the output columns do not exist yet, create them.
16684  */
16685 
16686  if (!cpl_table_has_column(origslits, name_obj)) {
16687  cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
16688  cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
16689  cpl_table_new_column(origslits, name_end, CPL_TYPE_INT);
16690  cpl_table_new_column(origslits, name_row, CPL_TYPE_INT);
16691  cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
16692  }
16693 
16694  /*
16695  * The current slit spectrum is k. The slit spectrum immediately
16696  * below (in the rectified image) is k+1. We need the length of
16697  * the spectrum below for computing the _absolute_ coordinates
16698  * of the objects in the rectified image in both beams.
16699  */
16700 
16701  length = cpl_table_get_int(origslits, "length", k + 1, &null);
16702 
16703  /* NEW:
16704  * Names of the columns of the input table where
16705  * the information of the corresponding object of
16706  * the next beam is available.
16707  */
16708 
16709  for (v = 0; v < maxobjs; v++) {
16710  char *name_v = cpl_sprintf("object_%d", v + 1);
16711  double obj_v = cpl_table_get_double(slitss[0], name_v,
16712  k + 1, &null);
16713 
16714  cpl_free(name_v);
16715 
16716  if (null)
16717  break;
16718 
16719  if (v) {
16720  if (fabs(obj - length - obj_v) < min_v) {
16721  min_v = fabs(obj - length - obj_v);
16722  cpl_free(name_start_v);
16723  cpl_free(name_end_v);
16724  cpl_free(name_obj_v);
16725  name_start_v = cpl_sprintf("start_%d", v + 1);
16726  name_end_v = cpl_sprintf("end_%d", v + 1);
16727  name_obj_v = cpl_sprintf("object_%d", v + 1);
16728  }
16729  }
16730  else {
16731  min_v = fabs(obj - length - obj_v);
16732  name_start_v = cpl_sprintf("start_%d", v + 1);
16733  name_end_v = cpl_sprintf("end_%d", v + 1);
16734  name_obj_v = cpl_sprintf("object_%d", v + 1);
16735  }
16736  }
16737 
16738  /*
16739  * Read from the first input object table (first angle)
16740  * the spatial window enclosing the object.
16741  */
16742 
16743  start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
16744  end = cpl_table_get_int(slitss[0], name_end_o, k, &null);
16745 
16746  /* NEW:
16747  * Spatial window of the matching object in the next beam.
16748  */
16749 
16750  start_v = cpl_table_get_int(slitss[0], name_start_v, k + 1, &null);
16751  end_v = cpl_table_get_int(slitss[0], name_end_v, k + 1, &null);
16752  obj_v = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null);
16753 
16754  /*
16755  * Write the object coordinates in the same slit, and in the
16756  * slit below. Note that here we assume that all slits were
16757  * traced perfectly, and we compute the theoretical coords
16758  * (obj - length) within the next slit spectrum (k + 1). In
16759  * principle we should read them as well from the input
16760  * table!
16761  */
16762 
16763  cpl_table_set_double(origslits, name_obj, k, obj);
16764  cpl_table_set_double(origslits, name_obj, k + 1, obj_v);
16765  // cpl_table_set_double(origslits, name_obj, k + 1, obj - length);
16766 
16767  cpl_table_set_int(origslits, name_start, k, start);
16768  cpl_table_set_int(origslits, name_start, k + 1, start_v);
16769  // cpl_table_set_int(origslits, name_start, k + 1, start - length);
16770 
16771  cpl_table_set_int(origslits, name_end, k, end);
16772  cpl_table_set_int(origslits, name_end, k + 1, end_v);
16773  // cpl_table_set_int(origslits, name_end, k + 1, end - length);
16774 
16775  /*
16776  * "nmatches" is counting at what "reduced" image row the
16777  * extracted spectra are. Note that this is s preliminary
16778  * numbering - which is wrong: other objects may be found
16779  * in the same slit, and then the indeces would not be in
16780  * sequence. What is important is that at the end of this
16781  * loop "nmatches" would be the total number of matching
16782  * objects. The two cpl_table_set_int() calls made here
16783  * cannot be removed - they "validate" those table elements
16784  * (see ahead).
16785  */
16786 
16787  cpl_table_set_int(origslits, name_row, k, nmatches);
16788  nmatches++;
16789  cpl_table_set_int(origslits, name_row, k + 1, nmatches);
16790  nmatches++;
16791 
16792  cpl_free(name_obj);
16793  cpl_free(name_start);
16794  cpl_free(name_end);
16795  cpl_free(name_row);
16796  cpl_free(name_row_s);
16797 
16798  cpl_free(name_start_o);
16799  cpl_free(name_end_o);
16800  cpl_free(name_row_o);
16801 
16802  cpl_free(name_start_v); name_start_v = NULL;
16803  cpl_free(name_end_v); name_end_v = NULL;
16804  cpl_free(name_obj_v); name_obj_v = NULL;
16805  }
16806  }
16807 
16808  /*
16809  * The summary table has fulfilled its function. If no matching
16810  * objects are found, the function returns with an error.
16811  */
16812 
16813  cpl_table_delete(summary);
16814 
16815  if (!nmatches)
16816  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16817 
16818  /*
16819  * Now we consider the resulting intersection object table,
16820  * listing all matches. As seen, the image row number reported
16821  * in the columns "row_#" was not really performed sequentially.
16822  * We need to renumber sequentially...
16823  * We need also to fill the "row_stokes_#" column the way the
16824  * extracted polarimetric signal will be stored in the
16825  * reduced_pol_images...
16826  */
16827 
16828  maxobjs = mos_get_maxobjs_per_slit(origslits);
16829  nstokes = nmatches / 2; /* nmatches is always an even number */
16830 
16831  for (k = 0; k < nslits; k++) {
16832  if (k % 2) { /* Extraordinary beam */
16833  nstokes = sstokes; /* Use same start value as for ordinary */
16834  }
16835  else { /* Ordinary beam */
16836  sstokes = nstokes; /* Memorise start value at ordinary beam */
16837  }
16838 
16839  for (m = 0; m < maxobjs; m++) {
16840  char *name = cpl_sprintf("row_%d", m + 1);
16841  char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
16842 
16843  if (!cpl_table_is_valid(origslits, name, k)) {
16844  cpl_free(name);
16845  cpl_free(namestokes);
16846  break;
16847  }
16848  else {
16849  nmatches--;
16850  nstokes--;
16851  cpl_table_set_int(origslits, name, k, nmatches);
16852  cpl_table_set_int(origslits, namestokes, k, nstokes);
16853  }
16854 
16855  cpl_free(name);
16856  cpl_free(namestokes);
16857  }
16858  }
16859 
16860 
16861  /*
16862  * This is done to avoid the NULL value is zero (it would invalidate
16863  * also the row_# = 0 or start_# = 0 for an object), and to enable
16864  * working directly with the column data buffers, when using this
16865  * table afterwards.
16866  */
16867 
16868  for (j = 0; j < maxobjs; j++) {
16869  char *name = cpl_sprintf("object_%d", j + 1);
16870  cpl_table_fill_invalid_double(origslits, name, -1);
16871  cpl_free(name);
16872 
16873  name = cpl_sprintf("start_%d", j + 1);
16874  cpl_table_fill_invalid_int(origslits, name, -1);
16875  cpl_free(name);
16876 
16877  name = cpl_sprintf("end_%d", j + 1);
16878  cpl_table_fill_invalid_int(origslits, name, -1);
16879  cpl_free(name);
16880 
16881  name = cpl_sprintf("row_%d", j + 1);
16882  cpl_table_fill_invalid_int(origslits, name, -1);
16883  cpl_free(name);
16884 
16885  name = cpl_sprintf("row_stokes_%d", j + 1);
16886  cpl_table_fill_invalid_int(origslits, name, -1);
16887  cpl_free(name);
16888  }
16889 
16890  /*********************************************************************
16891  * This tail has been added to propagate the selection of valid
16892  * objects also to the input slitss[] tables. Just eliminate all
16893  * this final part to suppress this behaviour.
16894  */
16895 
16896  /*
16897  * First of all, make a working copy and remove all columns related
16898  * to objects from the input object tables.
16899  */
16900 
16901  for (i = 0; i < nscience; i++) {
16902  int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
16903 
16904  work[i] = cpl_table_duplicate(slitss[i]);
16905 
16906  for (m = 0; m < c_maxobjs; m++) {
16907  char *object_o = cpl_sprintf("object_%d", m + 1);
16908  char *start_o = cpl_sprintf("start_%d", m + 1);
16909  char *end_o = cpl_sprintf("end_%d", m + 1);
16910  char *row_o = cpl_sprintf("row_%d", m + 1);
16911 
16912  cpl_table_erase_column(slitss[i], object_o);
16913  cpl_table_erase_column(slitss[i], start_o);
16914  cpl_table_erase_column(slitss[i], end_o);
16915  cpl_table_erase_column(slitss[i], row_o);
16916  }
16917  }
16918 
16919  /*
16920  * Now just consider all the objects in the intersection table.
16921  */
16922 
16923  for (k = 0; k < nslits; k++) {
16924  for (j = 0; j < maxobjs; j++) {
16925  double object_w, object_r;
16926  int row_w;
16927 
16928  char *object_i = cpl_sprintf("object_%d", j + 1);
16929  char *start_i = cpl_sprintf("start_%d", j + 1);
16930  char *end_i = cpl_sprintf("end_%d", j + 1);
16931  char *row_i = cpl_sprintf("row_%d", j + 1);
16932 
16933 
16934  if (!cpl_table_is_valid(origslits, object_i, k))
16935  break;
16936 
16937  /*
16938  * We have found a valid object (valid because it belongs
16939  * to the intersection). Now we look for this object in each
16940  * one of the original tables, we get its parameters, and
16941  * copy them at the right position (i.e., same position as
16942  * in intersection table). The object will be the one closest
16943  * to the object position (column object_i) in the intersection
16944  * table. Note that we examine the same row, k, in all tables.
16945  */
16946 
16947  object_w = cpl_table_get_double(origslits, object_i, k, NULL);
16948  row_w = cpl_table_get_int (origslits, row_i, k, NULL);
16949 
16950  for (i = 0; i < nscience; i++) {
16951  int c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
16952  int minpos;
16953  double mindiff, diff;
16954  char *object_o;
16955  char *start_o;
16956  char *end_o;
16957  char *row_o;
16958 
16959  for (m = 0; m < c_maxobjs; m++) {
16960  object_o = cpl_sprintf("object_%d", m + 1);
16961  start_o = cpl_sprintf("start_%d", m + 1);
16962  end_o = cpl_sprintf("end_%d", m + 1);
16963  row_o = cpl_sprintf("row_%d", m + 1);
16964 
16965  if (!cpl_table_is_valid(work[i], object_o, k))
16966  break;
16967 
16968  object_r = cpl_table_get_double(work[i], object_o, k, NULL);
16969  //row_r = cpl_table_get_int (work[i], row_o, k, NULL);
16970 
16971  diff = fabs(object_w - object_r);
16972  if (m) {
16973  if (mindiff > diff) {
16974  mindiff = diff;
16975  minpos = m;
16976  }
16977  }
16978  else {
16979  mindiff = diff;
16980  minpos = 0;
16981  }
16982 
16983  cpl_free(object_o);
16984  cpl_free(start_o);
16985  cpl_free(end_o);
16986  cpl_free(row_o);
16987  }
16988 
16989  object_o = cpl_sprintf("object_%d", minpos + 1);
16990  start_o = cpl_sprintf("start_%d", minpos + 1);
16991  end_o = cpl_sprintf("end_%d", minpos + 1);
16992  row_o = cpl_sprintf("row_%d", minpos + 1);
16993 
16994  if (!cpl_table_has_column(slitss[i], object_i)) {
16995  cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
16996  cpl_table_new_column(slitss[i], start_i, CPL_TYPE_INT);
16997  cpl_table_new_column(slitss[i], end_i, CPL_TYPE_INT);
16998  cpl_table_new_column(slitss[i], row_i, CPL_TYPE_INT);
16999  cpl_table_fill_invalid_double(slitss[i], object_i, -1);
17000  cpl_table_fill_invalid_int (slitss[i], start_i, -1);
17001  cpl_table_fill_invalid_int (slitss[i], end_i, -1);
17002  cpl_table_fill_invalid_int (slitss[i], row_i, -1);
17003  }
17004 
17005  cpl_table_set_double(slitss[i], object_i, k,
17006  cpl_table_get_double(work[i], object_o,
17007  k, NULL));
17008  cpl_table_set_int(slitss[i], start_i , k,
17009  cpl_table_get_int(work[i], start_o, k, NULL));
17010  cpl_table_set_int(slitss[i], end_i , k,
17011  cpl_table_get_int(work[i], end_o, k, NULL));
17012  cpl_table_set_int(slitss[i], row_i , k, row_w);
17013 
17014  cpl_free(object_o);
17015  cpl_free(start_o);
17016  cpl_free(end_o);
17017  cpl_free(row_o);
17018  }
17019 
17020  cpl_free(object_i);
17021  cpl_free(start_i);
17022  cpl_free(end_i);
17023  cpl_free(row_i);
17024  }
17025  }
17026 
17027  for (i = 0; i < nscience; i++)
17028  cpl_table_delete(work[i]);
17029 
17030  cpl_free(work);
17031 
17032 
17033  return cpl_error_get_code();
17034 }
17035 
17036 
17044 int mos_get_maxobjs_per_slit(cpl_table * slits)
17045 {
17046  int maxobjs = 1;
17047 
17048  char * colname = cpl_sprintf("object_%d", maxobjs);
17049 
17050  while (cpl_table_has_column(slits, colname)) {
17051  maxobjs++;
17052  cpl_free(colname);
17053  colname = cpl_sprintf("object_%d", maxobjs);
17054  }
17055 
17056  cpl_free(colname);
17057 
17058  maxobjs--;
17059 
17060  return maxobjs;
17061 }
17062 
17070 int mos_get_nobjects(cpl_table * slits)
17071 {
17072  int nobjs = 0;
17073 
17074  int nslits = cpl_table_get_nrow(slits);
17075  int maxobjs = mos_get_maxobjs_per_slit(slits);
17076 
17077  int k, m;
17078 
17079  for (k = 0; k < nslits; k++) {
17080  for (m = 0; m < maxobjs; m++) {
17081  char * name = cpl_sprintf("object_%d", m + 1);
17082  int null = !cpl_table_is_valid(slits, name, k);
17083 
17084  cpl_free(name);
17085 
17086  if (null) break;
17087  else nobjs++;
17088  }
17089  }
17090 
17091  return nobjs;
17092 }
17093 
17101 int mos_check_slits(cpl_table *slits, float rescale)
17102 {
17103 
17104  cpl_propertylist *sort;
17105 
17106  int nslits = cpl_table_get_nrow(slits);
17107 
17108  int k, null;
17109 
17110  const float interval = 90.0 * rescale;
17111  const float offset = (90.0 - 5) * rescale;
17112 
17113 
17114  for (k = 0; k < nslits; k++) {
17115  double ytop = cpl_table_get_double(slits, "ytop", k, &null);
17116  double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17117 
17118  double xtop = cpl_table_get_double(slits, "xtop", k, &null);
17119  double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
17120 
17121  int nmiss = (int)((ytop - ybottom) / interval + 0.5);
17122 
17123  if (nmiss > 1) {
17124  cpl_msg_warning(cpl_func,
17125  "Some slits could not be properly detected. "
17126  "There might be accountable inaccuracies.");
17127  while (nmiss > 1) {
17128  cpl_table_set_size(slits, nslits + 1);
17129 
17130  /* Fill in new slit 'cut' */
17131 
17132  /* x coordinates be the same (acceptable approximation) */
17133  cpl_table_set_double(slits, "xtop", nslits, xtop);
17134  cpl_table_set_double(slits, "xbottom", nslits, xbottom);
17135 
17136  /* Cut */
17137  if (k == 0) {
17138  cpl_table_set_double(slits, "ybottom", nslits, ybottom);
17139  cpl_table_set_double(slits, "ytop", nslits, ybottom
17140  + offset);
17141  ybottom += interval;
17142  cpl_table_set_double(slits, "ybottom", k, ybottom);
17143  } else {
17144  cpl_table_set_double(slits, "ytop", nslits, ytop);
17145  cpl_table_set_double(slits, "ybottom", nslits, ytop
17146  - offset);
17147  ytop -= interval;
17148  cpl_table_set_double(slits, "ytop", k, ytop);
17149  }
17150 
17151  nslits++; nmiss--;
17152  }
17153  }
17154  }
17155 
17156  sort = cpl_propertylist_new();
17157  cpl_propertylist_append_bool(sort, "ytop", 1);
17158  cpl_table_sort(slits, sort);
17159  cpl_propertylist_delete(sort);
17160 
17161  /*
17162  * Add here an ad hoc check on the last slit: is it too long
17163  * (by more than 10%)? Then shorten it...
17164  */
17165 
17166  k = cpl_table_get_nrow(slits) - 1;
17167 
17168  {
17169  double ytop = cpl_table_get_double(slits, "ytop", k, &null);
17170  double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17171  double length = (ytop - ybottom) / interval;
17172 
17173  if (length > 1.1) {
17174  cpl_table_set_double(slits, "ybottom", k, ytop - offset);
17175  }
17176 
17177  }
17178 
17179  return 0;
17180 }
17181 
17204 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header,
17205  int * nslits_out_det)
17206 {
17207  int m, null;
17208  int halfsize;
17209 
17210  cpl_propertylist * sort;
17211  cpl_table * slits;
17212 
17213  slits = mos_load_slits_fors_mos(header, nslits_out_det);
17214  halfsize = cpl_table_get_nrow(slits);
17215 
17216  cpl_table_set_size(slits, 2 * halfsize);
17217 
17218  for (m = 0; m < halfsize; m++) {
17219 
17220  double gap = 1.4;
17221 
17222  double length =
17223  cpl_table_get(slits, "ytop", m, &null) -
17224  cpl_table_get(slits, "ybottom", m, &null);
17225 
17226  if (m) {
17227  double interval =
17228  cpl_table_get(slits, "ybottom", m - 1, &null) -
17229  cpl_table_get(slits, "ytop", m, &null);
17230 
17231  gap = (interval - length) / 2;
17232  }
17233 
17234  cpl_table_set(slits, "slit_id", m + halfsize,
17235  cpl_table_get(slits, "slit_id", m, &null) - 1);
17236 
17237  cpl_table_set(slits, "xtop", m + halfsize,
17238  cpl_table_get(slits, "xtop", m, &null));
17239 
17240  cpl_table_set(slits, "xbottom", m + halfsize,
17241  cpl_table_get(slits, "xbottom", m, &null));
17242 
17243  cpl_table_set(slits, "ytop", m + halfsize,
17244  cpl_table_get(slits, "ytop", m, &null) + gap + length);
17245 
17246  cpl_table_set(slits, "ybottom", m + halfsize,
17247  cpl_table_get(slits, "ytop", m, &null) + gap);
17248  }
17249 
17250  for (m = 0; m < 2 * halfsize; m++) {
17251  cpl_table_set(slits, "ytop", m,
17252  cpl_table_get(slits, "ytop", m, &null) - 5.3);
17253 
17254  cpl_table_set(slits, "ybottom", m,
17255  cpl_table_get(slits, "ybottom", m, &null) - 5.3);
17256 
17257  }
17258 
17259  sort = cpl_propertylist_new();
17260  cpl_propertylist_append_bool(sort, "ytop", 1);
17261  cpl_table_sort(slits, sort);
17262 
17263  cpl_propertylist_delete(sort);
17264 
17265  return slits;
17266 }
17267 
17268 int * fors_get_nobjs_perslit(cpl_table * slits)
17269 {
17270  int nslits = cpl_table_get_nrow(slits);
17271  int maxobjs = mos_get_maxobjs_per_slit(slits);
17272 
17273  int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
17274 
17275  int k, m;
17276 
17277  for (k = 0; k < nslits; k++) {
17278  int nobjs = 0;
17279  for (m = 0; m < maxobjs; m++) {
17280  char * name = cpl_sprintf("object_%d", m + 1);
17281  int null = !cpl_table_is_valid(slits, name, k);
17282 
17283  cpl_free(name);
17284 
17285  if (null) break;
17286  else nobjs++;
17287  }
17288 
17289  nobjs_per_slit[k] = nobjs;
17290  }
17291 
17292  return nobjs_per_slit;
17293 }
17294 
17295 double fors_get_object_position(cpl_table *slits, int slit, int object)
17296 {
17297  char *name = cpl_sprintf("object_%d", object);
17298  double position;
17299 
17300  position = cpl_table_get_double(slits, name, slit, NULL)
17301  - cpl_table_get_int(slits, "position", slit, NULL);
17302 
17303  cpl_free(name);
17304 
17305  return position;
17306 }
17307 
17308 int mos_rebin_signal(cpl_image **image, int rebin)
17309 {
17310  cpl_image *rebinned;
17311 
17312 
17313  if (*image == NULL)
17314  return 1;
17315 
17316  if (rebin == 1)
17317  return 0;
17318 
17319  rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1);
17320 
17321  cpl_image_delete(*image);
17322 
17323  *image = rebinned;
17324 
17325  return 0;
17326 }
17327 
17328 int mos_rebin_error(cpl_image **image, int rebin)
17329 {
17330  if (*image == NULL)
17331  return 1;
17332 
17333  if (rebin == 1)
17334  return 0;
17335 
17336  cpl_image_power(*image, 2);
17337  mos_rebin_signal(image, rebin);
17338  cpl_image_power(*image, 0.5);
17339 
17340  return 0;
17341 }
17342 
17343 /*
17344  * @brief
17345  * Map table values into a 1D image
17346  *
17347  * @param image Target image
17348  * @param start Coordinate of first pixel in image
17349  * @param step Coordinate step for one pixel in image
17350  * @param table Source table
17351  * @param xname Name of coordinate column
17352  * @param yname Name of values column
17353  *
17354  * @return 0 on success
17355  *
17356  * The values in @em yname are linearly interpolated at the @em image
17357  * pixel coordinates. The @em image must have Nx1 size.
17358  */
17359 
17360 int map_table(cpl_image *image, double start, double step,
17361  cpl_table *table, const char *xname, const char *yname)
17362 {
17363  int length = cpl_image_get_size_x(image);
17364  int nrows = cpl_table_get_nrow(table);
17365  float *data = cpl_image_get_data_float(image);
17366  float *fdata = NULL;
17367  double *xdata = NULL;
17368  double *ydata = NULL;
17369  cpl_type xtype = cpl_table_get_column_type(table, xname);
17370  cpl_type ytype = cpl_table_get_column_type(table, yname);
17371  double xzero, pos;
17372  int i, j, n;
17373 
17374 
17375  /*
17376  * Initialization of output image at 0.0 - this value is left
17377  * on non-overlapping portions.
17378  */
17379 
17380  for (i = 0; i < length; i++)
17381  data[i] = 0.0;
17382 
17383 
17384  /*
17385  * Do everything in double precision
17386  */
17387 
17388  if (xtype == CPL_TYPE_FLOAT) {
17389  fdata = cpl_table_get_data_float(table, xname);
17390  xdata = cpl_malloc(nrows * sizeof(double));
17391  for (i = 0; i < nrows; i++) {
17392  xdata[i] = fdata[i];
17393  }
17394  }
17395  else {
17396  xdata = cpl_table_get_data_double(table, xname);
17397  }
17398 
17399  if (ytype == CPL_TYPE_FLOAT) {
17400  fdata = cpl_table_get_data_float(table, yname);
17401  ydata = cpl_malloc(nrows * sizeof(double));
17402  for (i = 0; i < nrows; i++) {
17403  ydata[i] = fdata[i];
17404  }
17405  }
17406  else {
17407  ydata = cpl_table_get_data_double(table, yname);
17408  }
17409 
17410  /*
17411  * Mapping
17412  */
17413 
17414  n = 0;
17415  xzero = xdata[n];
17416 
17417  for (i = 0; i < length; i++) {
17418  pos = start + step * i;
17419  if (pos < xzero)
17420  continue;
17421  for (j = n; j < nrows; j++) {
17422  if (xdata[j] > pos) {
17423  n = j;
17424  data[i] = ydata[j-1]
17425  + (ydata[j] - ydata[j-1])
17426  * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]);
17427  break;
17428  }
17429  }
17430  }
17431 
17432  if (xtype == CPL_TYPE_FLOAT)
17433  cpl_free(xdata);
17434 
17435  if (ytype == CPL_TYPE_FLOAT)
17436  cpl_free(ydata);
17437 
17438  return 0;
17439 }
17440 
17441 
17442 /*
17443  * @brief
17444  * Fit overall trend of a Nx1 image
17445  *
17446  * @param image Values to smooth
17447  * @param order Order of fitting polynomial
17448  * @param hw Half width of smoothing window
17449  *
17450  * @return Smoothed image, or NULL on failure.
17451  *
17452  * Heavily smooth and fit data in the input Nx1 size @em image.
17453  */
17454 
17455 static cpl_image *polysmooth(cpl_image *image, int order, int hw)
17456 {
17457  int npoints;
17458  cpl_vector *x;
17459  cpl_vector *y;
17460  double *xdata;
17461  double *ydata;
17462  cpl_polynomial *poly;
17463  cpl_vector *ysmooth;
17464  cpl_image *smoothed;
17465  float *sdata;
17466  int i;
17467 
17468 
17469  npoints = cpl_image_get_size_x(image);
17470 
17471  if (2 * hw + 1 > npoints)
17472  return NULL;
17473 
17474  x = cpl_vector_new(npoints);
17475  y = cpl_vector_new(npoints);
17476  xdata = cpl_vector_get_data(x);
17477  ydata = cpl_vector_get_data(y);
17478 
17479  smoothed = cpl_image_duplicate(image);
17480  sdata = cpl_image_get_data_float(smoothed);
17481 
17482  for (i = 0; i < npoints; i++) {
17483  xdata[i] = i;
17484  ydata[i] = sdata[i];
17485  }
17486 
17487  ysmooth = cpl_vector_filter_median_create(y, hw);
17488  cpl_vector_delete(y);
17489 
17490  poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL);
17491  cpl_vector_delete(x);
17492  cpl_vector_delete(ysmooth);
17493 
17494  if (poly) {
17495  for (i = 0; i < npoints; i++)
17496  sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL);
17497 
17498  cpl_polynomial_delete(poly);
17499  }
17500  else {
17501  cpl_image_delete(smoothed);
17502  return NULL;
17503  }
17504 
17505  return smoothed;
17506 }
17507 
17508 #undef cleanup
17509 #define cleanup \
17510 do { \
17511  cpl_image_delete(spectrum); \
17512  cpl_image_delete(flux); \
17513  cpl_image_delete(efficiency); \
17514  cpl_image_delete(smo_efficiency); \
17515  cpl_image_delete(extinction); \
17516  cpl_image_delete(response); \
17517  cpl_image_delete(smo_response); \
17518  cpl_image_delete(physical); \
17519 } while (0)
17520 
17544 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave,
17545  double dispersion, double gain,
17546  double exptime, cpl_table *ext_table,
17547  double airmass, cpl_table *flux_table,
17548  int order)
17549 {
17550 
17551  cpl_image *spectrum = NULL; // Extracted standard star spectrum
17552  float *data;
17553  cpl_image *extinction = NULL; // Extinction binned as "spectrum"
17554  float *ext_data;
17555  cpl_image *flux = NULL; // Standard star flux binned as "spectrum"
17556  float *flux_data;
17557  cpl_image *physical = NULL; // Physical units of above
17558  float *phys_data;
17559  cpl_image *efficiency = NULL; // Raw efficiency curve
17560  float *eff_data;
17561  cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve
17562  float *smo_eff_data;
17563  cpl_image *response = NULL; // Raw response curve
17564  float *res_data;
17565  cpl_image *smo_response = NULL; // Smoothed response curve
17566  float *smo_res_data;
17567  cpl_image *image;
17568  cpl_image *smo_image;
17569  cpl_table *table;
17570  float lambda;
17571  int nx, ny;
17572  int ext_count, ext_pos;
17573  int eff_count, eff_pos;
17574  int flux_count, flux_pos;
17575  int start, end;
17576  int i;
17577 
17578 
17579  if (spectra == NULL || ext_table == NULL || flux_table == NULL) {
17580  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17581  return NULL;
17582  }
17583 
17584  if (!cpl_table_has_column(ext_table, "WAVE")) {
17585  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17586  "Column WAVE in atmospheric extinction table");
17587  return NULL;
17588  }
17589 
17590  if (!cpl_table_has_column(ext_table, "EXTINCTION")) {
17591  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17592  "Column EXTINCTION in atmospheric extinction table");
17593  return NULL;
17594  }
17595 
17596  if (!cpl_table_has_column(flux_table, "WAVE")) {
17597  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17598  "Column WAVE in standard star flux table");
17599  return NULL;
17600  }
17601 
17602  if (!cpl_table_has_column(flux_table, "FLUX")) {
17603  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17604  "Column FLUX in standard star flux table");
17605  return NULL;
17606  }
17607 
17608  if (gain < 0.1) {
17609  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17610  "Invalid gain factor (%.2f)", gain);
17611  return NULL;
17612  }
17613 
17614  if (exptime < 0.001) {
17615  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17616  "Invalid exposure time (%.2f)", exptime);
17617  return NULL;
17618  }
17619 
17620  if (dispersion < 0.001) {
17621  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17622  "Invalid dispersion (%.2f)", dispersion);
17623  return NULL;
17624  }
17625 
17626  if (order < 2) {
17627  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17628  "Order of the polynomial fitting the "
17629  "instrument response must be at least 2");
17630  return NULL;
17631  }
17632 
17633  nx = cpl_image_get_size_x(spectra);
17634  ny = cpl_image_get_size_y(spectra);
17635 
17636 
17637  /*
17638  * Find brightest spectrum and duplicate it.
17639  */
17640 
17641  if (ny == 1) {
17642  spectrum = cpl_image_duplicate(spectra);
17643  }
17644  else {
17645  cpl_size x, y;
17646  cpl_image *brights = cpl_image_collapse_create(spectra, 1);
17647 
17648  cpl_image_get_maxpos(brights, &x, &y);
17649  cpl_image_delete(brights);
17650  spectrum = cpl_image_extract(spectra, 1, y, nx, y);
17651  }
17652 
17653 
17654  /*
17655  * Convert standard star spectrum in electrons per second per Angstrom.
17656  */
17657 
17658  cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion);
17659 
17660 
17661  /*
17662  * Map the atmospheric extinction factors to the same lambda sampling
17663  * of the extracted spectrum.
17664  */
17665 
17666  extinction = cpl_image_duplicate(spectrum);
17667  map_table(extinction, startwave + dispersion/2, dispersion,
17668  ext_table, "WAVE", "EXTINCTION");
17669 
17670 
17671  /*
17672  * Convert from magnitudes to actual flux loss.
17673  */
17674 
17675  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17676  cpl_image_exponential(extinction, 10.);
17677 
17678 
17679  /*
17680  * Correct the scientific spectrum to airmass 0
17681  */
17682 
17683  cpl_image_multiply(spectrum, extinction);
17684 
17685 
17686  /*
17687  * Find in what pixel interval (start at "ext_pos", for "ext_count"
17688  * pixels) the atmospheric extinction is available.
17689  */
17690 
17691  ext_data = cpl_image_get_data_float(extinction);
17692 
17693  ext_count = 0;
17694  ext_pos = 0;
17695  for (i = 0; i < nx; i++) {
17696  if (ext_data[i] > 0.0) {
17697  if (ext_count == 0) {
17698  ext_pos = i;
17699  }
17700  ext_count++;
17701  }
17702  else {
17703  if (ext_count) {
17704  break;
17705  }
17706  }
17707  }
17708 
17709  cpl_image_delete(extinction); extinction = NULL;
17710 
17711 
17712  /*
17713  * Map the standard star catalog flux to the same lambda sampling
17714  * of the extracted spectrum.
17715  */
17716 
17717  flux = cpl_image_duplicate(spectrum);
17718  map_table(flux, startwave + dispersion/2, dispersion,
17719  flux_table, "WAVE", "FLUX");
17720 
17721 
17722  /*
17723  * Find in what pixel interval (start at "flux_pos", for "flux_count"
17724  * pixels) the standard star flux is available.
17725  */
17726 
17727  flux_data = cpl_image_get_data_float(flux);
17728 
17729  flux_count = 0;
17730  flux_pos = 0;
17731  for (i = 0; i < nx; i++) {
17732  if (flux_data[i] > 0.0) {
17733  if (flux_count == 0) {
17734  flux_pos = i;
17735  }
17736  flux_count++;
17737  }
17738  else {
17739  if (flux_count) {
17740  break;
17741  }
17742  }
17743  }
17744 
17745 
17746  /*
17747  * Intersection with previous selection
17748  */
17749 
17750  start = ext_pos > flux_pos ? ext_pos : flux_pos;
17751  end = (ext_pos + ext_count) < (flux_pos + flux_count) ?
17752  (ext_pos + ext_count) : (flux_pos + flux_count);
17753  flux_pos = start;
17754  flux_count = end - start;
17755 
17756 
17757  /*
17758  * Convert the flux to photons (per second per Angstrom).
17759  * std_flux is in units of erg / cm^2 / s / Angstrom. This
17760  * must be multiplied by the efficient area of the telescope,
17761  * 5.18E+5 cm^2, and divided by hv (v = frequency). With
17762  * hc = 1.98E-8 erg*Angstrom one obtains the following:
17763  */
17764 
17765  physical = cpl_image_duplicate(spectrum);
17766  phys_data = cpl_image_get_data_float(physical);
17767 
17768  for (i = 0; i < nx; i++) {
17769  lambda = startwave + dispersion * (i + 0.5);
17770  phys_data[i] = 0.0026 * lambda * flux_data[i];
17771  }
17772 
17773  efficiency = cpl_image_duplicate(spectrum);
17774  eff_data = cpl_image_get_data_float(efficiency);
17775  data = cpl_image_get_data_float(spectrum);
17776 
17777  for (i = 0; i < nx; i++) {
17778  if (phys_data[i] > 0.0)
17779  eff_data[i] = data[i] / phys_data[i];
17780  else
17781  eff_data[i] = 0.0;
17782  }
17783 
17784  cpl_image_delete(physical); physical = NULL;
17785 
17786 
17787  /*
17788  * Find interval (longer than 300 pixels) where efficiency is
17789  * greater than 1%
17790  */
17791 
17792  eff_count = 0;
17793  eff_pos = 0;
17794  for (i = 0; i < nx; i++) {
17795  if (eff_data[i] > 0.01) {
17796  if (eff_count == 0) {
17797  eff_pos = i;
17798  }
17799  eff_count++;
17800  }
17801  else {
17802  if (eff_count > 300) {
17803  break;
17804  }
17805  }
17806  }
17807 
17808 
17809  /*
17810  * Intersection with previous selection
17811  */
17812 
17813  start = eff_pos > flux_pos ? eff_pos : flux_pos;
17814  end = (eff_pos + eff_count) < (flux_pos + flux_count) ?
17815  (eff_pos + eff_count) : (flux_pos + flux_count);
17816  eff_pos = start;
17817  eff_count = end - start;
17818 
17819  if (eff_count < 1) {
17820  cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
17821  "No overlap between catalog and spectrum");
17822  cleanup;
17823  return NULL;
17824  }
17825 
17826 
17827  /*
17828  * Extract only data to fit, i.e., where the efficiency is available.
17829  */
17830 
17831  image = cpl_image_extract(efficiency, eff_pos + 1, 1,
17832  eff_pos + eff_count, 1);
17833 
17834  smo_image = polysmooth(image, order, 50);
17835  cpl_image_delete(image);
17836 
17837  smo_efficiency = cpl_image_duplicate(efficiency);
17838  smo_eff_data = cpl_image_get_data_float(smo_efficiency);
17839  cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1);
17840 
17841  cpl_image_delete(smo_image);
17842 
17843 
17844  /*
17845  * Compute instrument response as the ratio between the catalog
17846  * spectrum and the observed spectrum (converted in physical units).
17847  * The polynomial smoothing, however, is performed on the inverse
17848  * of this ration, for obvious reasons (i.e., no divergence at zero
17849  * efficiency).
17850  */
17851 
17852  response = cpl_image_duplicate(spectrum);
17853  res_data = cpl_image_get_data_float(response);
17854 
17855  for (i = 0; i < nx; i++) {
17856  if (eff_data[i] > 0.01 && flux_data[i] > 0.0)
17857  res_data[i] = data[i] / flux_data[i];
17858  else
17859  res_data[i] = 0.0;
17860  }
17861 
17862 
17863  /*
17864  * Extract only data to fit, i.e., where the response is available.
17865  */
17866 
17867  image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1);
17868 
17869  smo_image = polysmooth(image, order, 50);
17870  cpl_image_delete(image);
17871 
17872  smo_response = cpl_image_duplicate(response);
17873  smo_res_data = cpl_image_get_data_float(smo_response);
17874  cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1);
17875 
17876  cpl_image_delete(smo_image);
17877 
17878  for (i = 0; i < nx; i++) {
17879  if (eff_data[i] > 0.01) {
17880  res_data[i] = 1 / res_data[i];
17881  smo_res_data[i] = 1 / smo_res_data[i];
17882  }
17883  else {
17884  res_data[i] = 0.0;
17885  smo_res_data[i] = 0.0;
17886  }
17887  }
17888 
17889 
17890  /*
17891  * Assemble the product spectrophotometric table.
17892  */
17893 
17894  table = cpl_table_new(nx);
17895 
17896  cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT);
17897  cpl_table_set_column_unit(table, "WAVE", "Angstrom");
17898 
17899  for (i = 0; i < nx; i++)
17900  cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5));
17901 
17902  cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT);
17903  cpl_table_set_column_unit(table, "STD_FLUX",
17904  "10^(-16) erg/(cm^2 s Angstrom)");
17905  cpl_table_copy_data_float(table, "STD_FLUX", flux_data);
17906  cpl_image_delete(flux); flux = NULL;
17907 
17908  cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT);
17909  cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)");
17910  cpl_table_copy_data_float(table, "OBS_FLUX", data);
17911  cpl_image_delete(spectrum); spectrum = NULL;
17912 
17913  cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT);
17914  cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon");
17915  cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data);
17916  cpl_image_delete(efficiency); efficiency = NULL;
17917 
17918  cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT);
17919  cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon");
17920  cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data);
17921  cpl_image_delete(smo_efficiency); smo_efficiency = NULL;
17922 
17923  cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT);
17924  cpl_table_set_column_unit(table, "RAW_RESPONSE",
17925  "10^(-16) erg/(cm^2 electron)");
17926  cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data);
17927  cpl_image_delete(response); response = NULL;
17928 
17929  cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT);
17930  cpl_table_set_column_unit(table,
17931  "RESPONSE", "10^(-16) erg/(cm^2 electron)");
17932  cpl_table_copy_data_float(table, "RESPONSE", smo_res_data);
17933  cpl_image_delete(smo_response); smo_response = NULL;
17934 
17935  cleanup;
17936 
17937  return table;
17938 }
17939 
17940 static double ksigma_vector(cpl_vector *values,
17941  double klow, double khigh, int kiter, int *good)
17942 {
17943  cpl_vector *accepted;
17944  double mean = 0.0;
17945  double sigma = 0.0;
17946  double *data = cpl_vector_get_data(values);
17947  int n = cpl_vector_get_size(values);
17948  int ngood = n;
17949  int count = 0;
17950  int i;
17951 
17952 
17953  /*
17954  * At first iteration the mean is taken as the median, and the
17955  * standard deviation relative to this value is computed.
17956  */
17957 
17958  mean = cpl_vector_get_median(values);
17959 
17960  for (i = 0; i < n; i++)
17961  sigma += (mean - data[i]) * (mean - data[i]);
17962 
17963  sigma = sqrt(sigma / (n - 1));
17964 
17965  while (kiter) {
17966  count = 0;
17967  for (i = 0; i < ngood; i++) {
17968  if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) {
17969  data[count] = data[i];
17970  ++count;
17971  }
17972  }
17973 
17974  if (count == 0) // This cannot happen at first iteration.
17975  break; // So we can break: we have already computed a mean.
17976 
17977  /*
17978  * The mean must be computed even if no element was rejected
17979  * (count == ngood), because at first iteration median instead
17980  * of mean was computed.
17981  */
17982 
17983  accepted = cpl_vector_wrap(count, data);
17984  mean = cpl_vector_get_mean(accepted);
17985  if (count > 1)
17986  sigma = cpl_vector_get_stdev(accepted);
17987  cpl_vector_unwrap(accepted);
17988 
17989  if (count == ngood || count == 1)
17990  break;
17991 
17992  ngood = count;
17993  --kiter;
17994  }
17995 
17996  if (good)
17997  *good = ngood;
17998 
17999  return mean;
18000 }
18001 
18002 
18021 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist,
18022  double klow, double khigh, int kiter,
18023  cpl_image **good)
18024 {
18025  int ni, nx, ny, npix;
18026  cpl_image *out_ima;
18027  float *pout_ima;
18028  float *good_ima;
18029  cpl_image *image;
18030  float **data;
18031  cpl_vector *time_line;
18032  double *ptime_line;
18033  int ngood;
18034  int i, j;
18035 
18036 
18037  ni = cpl_imagelist_get_size(imlist);
18038 
18039  image = cpl_imagelist_get(imlist, 0);
18040  nx = cpl_image_get_size_x(image);
18041  ny = cpl_image_get_size_y(image);
18042  npix = nx * ny;
18043 
18044  out_ima = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18045  pout_ima = cpl_image_get_data_float(out_ima);
18046 
18047  if (good) {
18048  *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18049  good_ima = cpl_image_get_data_float(*good);
18050  }
18051 
18052  time_line = cpl_vector_new(ni);
18053  ptime_line = cpl_vector_get_data(time_line);
18054 
18055  data = cpl_calloc(sizeof(float *), ni);
18056 
18057  for (i = 0; i < ni; i++) {
18058  image = cpl_imagelist_get(imlist, i);
18059  data[i] = cpl_image_get_data_float(image);
18060  }
18061 
18062  for (i = 0; i < npix; i++) {
18063  for (j = 0; j < ni; j++) {
18064  ptime_line[j] = data[j][i];
18065  }
18066  pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood);
18067  if (good) {
18068  good_ima[i] = ngood;
18069  }
18070  }
18071 
18072  cpl_free(data);
18073  cpl_vector_delete(time_line);
18074 
18075  return out_ima;
18076 
18077 }
18078 
18079 
18096 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response,
18097  cpl_table *ext_table, double startwave,
18098  double dispersion, double gain,
18099  double exptime, double airmass)
18100 {
18101  cpl_image *extinction;
18102  cpl_image *outspectra;
18103  cpl_image *mapresponse;
18104  float *res_data;
18105  float *out_data;
18106  float *ext_data;
18107  int tlength, xlength, ylength;
18108  int i, j, k;
18109  double resp_startwave;
18110  double resp_endwave;
18111  int null;
18112 
18113 
18114  if (spectra == NULL || ext_table == NULL || response == NULL) {
18115  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18116  return NULL;
18117  }
18118 
18119  /* Use the normal response if available. If not, use the flat corrected
18120  * response. Usually only one of the two is available in the interpolated
18121  * response.
18122  */
18123  if(cpl_table_has_column(response, "RESPONSE"))
18124  cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18125  else if(cpl_table_has_column(response, "RESPONSE_FFSED"))
18126  cpl_table_cast_column(response, "RESPONSE_FFSED", "RESPONSE_F", CPL_TYPE_FLOAT);
18127  else
18128  return NULL;
18129 
18130  res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18131 
18132  if (res_data == NULL) {
18133  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18134  return NULL;
18135  }
18136 
18137  tlength = cpl_table_get_nrow(response);
18138  xlength = cpl_image_get_size_x(spectra);
18139  ylength = cpl_image_get_size_y(spectra);
18140 
18141  /* Map the response */
18142  mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18143  map_table(mapresponse, startwave + dispersion/2, dispersion,
18144  response, "WAVE", "RESPONSE_F");
18145  res_data = cpl_image_get_data_float(mapresponse);
18146 
18147  /*
18148  * Map the atmospheric extinction factors to the same lambda sampling
18149  * of the extracted spectrum.
18150  */
18151 
18152  extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18153  map_table(extinction, startwave + dispersion/2, dispersion,
18154  ext_table, "WAVE", "EXTINCTION");
18155 
18156 
18157  /*
18158  * Convert from magnitudes to actual flux loss.
18159  */
18160 
18161  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18162  cpl_image_exponential(extinction, 10.);
18163 
18164  outspectra = cpl_image_duplicate(spectra);
18165 
18166  ext_data = cpl_image_get_data_float(extinction);
18167  out_data = cpl_image_get_data_float(outspectra);
18168 
18169  for (k = 0, i = 0; i < ylength; i++) {
18170  for (j = 0; j < xlength; j++, k++)
18171  out_data[k] *= ext_data[j] * res_data[j];
18172  }
18173 
18174  cpl_image_delete(extinction);
18175  cpl_image_delete(mapresponse);
18176 
18177  cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion);
18178 
18179  /*
18180  * Set to -1 the extrapolated values
18181  */
18182  resp_startwave = cpl_table_get(response, "WAVE", 0, &null);
18183  resp_endwave = cpl_table_get(response, "WAVE",
18184  cpl_table_get_nrow(response) -1, &null);
18185  for (j = 0; j < xlength; j++) {
18186  double this_wave = startwave + j * dispersion;
18187  if(this_wave < resp_startwave ||this_wave > resp_endwave)
18188  {
18189  for (i = 0; i < ylength; i++)
18190  out_data[j + xlength * i] = -1;
18191  }
18192  }
18193 
18194  cpl_table_erase_column(response, "RESPONSE_F");
18195 
18196  return outspectra;
18197 }
18198 
18199 
18216 cpl_image *mos_propagate_photometry_error(cpl_image *spectra,
18217  cpl_image *errors,
18218  cpl_table *response,
18219  cpl_table *ext_table,
18220  double startwave,
18221  double dispersion, double gain,
18222  double exptime, double airmass)
18223 {
18224  cpl_image *extinction;
18225  cpl_image *outerrors;
18226  cpl_image *mapresponse;
18227  cpl_image *maperror;
18228  float *err_data;
18229  float *out_data;
18230  float *ext_data;
18231  float *res_data;
18232  float *spe_data;
18233  int tlength, xlength, ylength;
18234  int i, j, k;
18235 
18236 
18237  if (errors == NULL || ext_table == NULL || response == NULL) {
18238  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18239  return NULL;
18240  }
18241 
18242  if (!cpl_table_has_column(response, "ERROR")) {
18243  return mos_apply_photometry(errors, response, ext_table, startwave,
18244  dispersion, gain, exptime, airmass);
18245  }
18246 
18247  cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18248  res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18249 
18250  if (res_data == NULL) {
18251  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18252  return NULL;
18253  }
18254 
18255  err_data = cpl_table_get_data_float(response, "ERROR");
18256 
18257  if (err_data == NULL) {
18258  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18259  return NULL;
18260  }
18261 
18262  tlength = cpl_table_get_nrow(response);
18263  xlength = cpl_image_get_size_x(errors);
18264  ylength = cpl_image_get_size_y(errors);
18265 
18266  if (xlength != tlength) {
18267  mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18268  map_table(mapresponse, startwave + dispersion/2, dispersion,
18269  response, "WAVE", "RESPONSE_F");
18270  res_data = cpl_image_get_data_float(mapresponse);
18271 
18272  maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18273  map_table(maperror, startwave + dispersion/2, dispersion,
18274  response, "WAVE", "ERROR");
18275  err_data = cpl_image_get_data_float(maperror);
18276  }
18277 
18278  /*
18279  * Map the atmospheric extinction factors to the same lambda sampling
18280  * of the extracted spectrum.
18281  */
18282 
18283  extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18284  map_table(extinction, startwave + dispersion/2, dispersion,
18285  ext_table, "WAVE", "EXTINCTION");
18286 
18287 
18288  /*
18289  * Convert from magnitudes to actual flux loss.
18290  */
18291 
18292  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18293  cpl_image_exponential(extinction, 10.);
18294 
18295  outerrors = cpl_image_duplicate(errors);
18296 
18297  ext_data = cpl_image_get_data_float(extinction);
18298  out_data = cpl_image_get_data_float(outerrors);
18299  spe_data = cpl_image_get_data_float(spectra);
18300 
18301  for (k = 0, i = 0; i < ylength; i++) {
18302  for (j = 0; j < xlength; j++, k++) {
18303  out_data[k] = ext_data[j] *
18304  sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] +
18305  res_data[j] * res_data[j] * out_data[k] * out_data[k]);
18306  }
18307  }
18308 
18309  cpl_image_delete(extinction);
18310  if (xlength != tlength) {
18311  cpl_image_delete(maperror);
18312  }
18313 
18314  cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion);
18315 
18316  cpl_table_erase_column(response, "RESPONSE_F");
18317  return outerrors;
18318 }
18319 
18320 
18396 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error,
18397  cpl_image *u_image, cpl_image *u_error,
18398  double startwave, double dispersion,
18399  double band, cpl_table *pol_sta,
18400  double ra, double dec, char *filter,
18401  int *polarisation,
18402  double *p_offset, double *p_error,
18403  double *a_offset, double *a_error)
18404 {
18405  cpl_table *standard;
18406  cpl_image *q_noise;
18407  cpl_image *q_signal;
18408  cpl_image *u_noise;
18409  cpl_image *u_signal;
18410  cpl_image *noise;
18411  double *q_ndata;
18412  double *q_sdata;
18413  double *u_ndata;
18414  double *u_sdata;
18415  double arctol = 0.5; /* Arc tolerance in degrees */
18416  double mindist;
18417  double cwave;
18418  double bwave[] = {3650., 4450., 5510., 6580., 8060};
18419  char *bands = "UBVRI";
18420  char p_label[] = {' ', 'p', '\0'};
18421  char dp_label[] = {' ', 'd', 'p', '\0'};
18422  char a_label[] = {' ', 'a', '\0'};
18423  char da_label[] = {' ', 'd', 'a', '\0'};
18424  int nbands = strlen(bands);
18425  int selected;
18426  int first, last, count, center;
18427  int nx;
18428  cpl_size col, row;
18429  int i, found, closest;
18430  int pband;
18431  int polarised;
18432  double q_obs;
18433  double q_err;
18434  double u_obs;
18435  double u_err;
18436  double p_obs;
18437  double p_err;
18438  double p_ref;
18439  double dp_ref;
18440  double a_obs;
18441  double a_err;
18442  double a_ref;
18443  double da_ref;
18444 
18445 
18446  *filter = '\0';
18447  *polarisation = 0;
18448  *p_offset = 0.0;
18449  *p_error = 0.0;
18450  *a_offset = 0.0;
18451  *a_error = 0.0;
18452 
18453  /*
18454  * Select reference standard star
18455  */
18456 
18457  cpl_table_select_all(pol_sta);
18458  cpl_table_and_selected_double(pol_sta, "COORD_RA", CPL_GREATER_THAN,
18459  ra-arctol);
18460  cpl_table_and_selected_double(pol_sta, "COORD_RA", CPL_LESS_THAN,
18461  ra+arctol);
18462  cpl_table_and_selected_double(pol_sta, "COORD_DEC", CPL_GREATER_THAN,
18463  dec-arctol);
18464  selected =
18465  cpl_table_and_selected_double(pol_sta, "COORD_DEC", CPL_LESS_THAN,
18466  dec+arctol);
18467 
18468  if (selected == 0) {
18469  cpl_msg_warning(cpl_func, "No standard star found in FOV");
18470  return 1;
18471  }
18472 
18473  if (selected > 1) {
18474  cpl_msg_warning(cpl_func,
18475  "Ambiguity: %d standard stars found in FOV", selected);
18476  return 1;
18477  }
18478 
18479  standard = cpl_table_extract_selected(pol_sta);
18480 
18481  cpl_msg_info(cpl_func, "Standard star: %s",
18482  cpl_table_get_string(standard, "name", 0));
18483 
18484  /*
18485  * Check whether the star is polarised or not
18486  */
18487 
18488  polarised = cpl_table_get_int(standard, "polarised", 0, NULL);
18489 
18490  cpl_msg_info(cpl_func, "This star is%sexpected to be polarised",
18491  polarised ? " " : " not ");
18492 
18493 
18494  /*
18495  * Determine the image row with the smallest median noise: this
18496  * row is assumed to refer to the standard star.
18497  * (note: the higher the S/N ratio of the original spectra, the
18498  * smaller the noise of the Stokes parameters Q and U).
18499  */
18500 
18501  nx = cpl_image_get_size_x(q_error);
18502 
18503  noise = cpl_image_collapse_median_create(q_error, 1, 0, 0);
18504  cpl_image_get_minpos(noise, &col, &row);
18505 
18506  cpl_image_delete(noise);
18507 
18508  if (col != 1) {
18509  cpl_table_delete(standard);
18510  cpl_msg_error(cpl_func,
18511  "Assertion failure!!! col = %"CPL_SIZE_FORMAT" (it should be 1)", col);
18512  return 1;
18513  }
18514 
18515  q_signal = cpl_image_extract(q_image, 1, row, nx, row);
18516  q_noise = cpl_image_extract(q_error, 1, row, nx, row);
18517  u_signal = cpl_image_extract(u_image, 1, row, nx, row);
18518  u_noise = cpl_image_extract(u_error, 1, row, nx, row);
18519 
18520  q_sdata = cpl_image_get_data_double(q_signal);
18521  q_ndata = cpl_image_get_data_double(q_noise);
18522  u_sdata = cpl_image_get_data_double(u_signal);
18523  u_ndata = cpl_image_get_data_double(u_noise);
18524 
18525 
18526  /*
18527  * Determine valid interval in input images (where error is positive).
18528  */
18529 
18530  first = -1;
18531  last = nx = cpl_image_get_size_x(q_signal);
18532  for (i = 0; i < nx; i++) {
18533  if (first < 0) {
18534  if (q_ndata[i] > 0.0) {
18535  first = i;
18536  }
18537  }
18538  else {
18539  if (q_ndata[i] <= 0.0) {
18540  last = i - 1;
18541  break;
18542  }
18543  }
18544  }
18545 
18546  count = last - first + 1;
18547 
18548  if (first < 0 || count < band) {
18549  cpl_table_delete(standard);
18550  cpl_image_delete(q_signal);
18551  cpl_image_delete(q_noise);
18552  cpl_image_delete(u_signal);
18553  cpl_image_delete(u_noise);
18554  cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count);
18555  return 1;
18556  }
18557 
18558  center = (first + last) / 2; // Center of valid spectrum
18559  cwave = startwave + dispersion * center; // Corresponding wavelength
18560 
18561 
18562  /*
18563  * Find the band UBVRI closest to the central wavelength.
18564  */
18565 
18566  found = 0;
18567  for (i = 0; i < nbands; i++) {
18568  p_label[0] = bands[i];
18569  if (cpl_table_is_valid(standard, p_label, 0)) {
18570  if (found == 0) {
18571  found = 1;
18572  mindist = fabs(bwave[i] - cwave);
18573  closest = i;
18574  }
18575  else if (mindist > fabs(bwave[i] - cwave)) {
18576  mindist = fabs(bwave[i] - cwave);
18577  closest = i;
18578  }
18579  }
18580  }
18581 
18582  if (!found) {
18583  cpl_table_delete(standard);
18584  cpl_image_delete(q_signal);
18585  cpl_image_delete(q_noise);
18586  cpl_image_delete(u_signal);
18587  cpl_image_delete(u_noise);
18588  cpl_msg_warning(cpl_func, "No reference value available");
18589  return 1;
18590  }
18591 
18592  center = (bwave[closest] - startwave) / dispersion; // Center of band (pix)
18593  cwave = bwave[closest]; // Wavelength of band
18594 
18595 
18596  /*
18597  * Check that the integration interval is entirely contained
18598  * in the valid interval, or give it up.
18599  */
18600 
18601  pband = floor(band / dispersion); // Band width in pixels
18602 
18603  if (center - pband/2 < first || center + pband/2 > last) {
18604  cpl_table_delete(standard);
18605  cpl_image_delete(q_signal);
18606  cpl_image_delete(q_noise);
18607  cpl_image_delete(u_signal);
18608  cpl_image_delete(u_noise);
18609  cpl_msg_warning(cpl_func, "No reference value available");
18610  return 1;
18611  }
18612 
18613  first = center - pband/2;
18614  last = center + pband/2;
18615 
18616  /*
18617  * Collect reference values. Note that if angle info is not available,
18618  * angle stuff is set automaticaly to zero.
18619  */
18620 
18621  p_label[0] = bands[closest];
18622  dp_label[0] = bands[closest];
18623  a_label[0] = bands[closest];
18624  da_label[0] = bands[closest];
18625 
18626  p_ref = cpl_table_get(standard, p_label, 0, NULL);
18627  dp_ref = cpl_table_get(standard, dp_label, 0, NULL);
18628  a_ref = cpl_table_get(standard, a_label, 0, NULL);
18629  da_ref = cpl_table_get(standard, da_label, 0, NULL);
18630 
18631  cpl_msg_info(cpl_func,
18632  "The expected polarisation is %.2f +- %.2f %%",
18633  p_ref, dp_ref);
18634 
18635  if (polarised) {
18636  cpl_msg_info(cpl_func,
18637  "The expected polarisation angle is %.2f +- %.2f degrees",
18638  a_ref, da_ref);
18639  }
18640 
18641  /*
18642  * Find median signal and median error.
18643  */
18644 
18645  q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1);
18646  q_err = cpl_image_get_median_window(q_error, first, 1, last, 1);
18647  u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1);
18648  u_err = cpl_image_get_median_window(u_error, first, 1, last, 1);
18649 
18650  /*
18651  * Measured linear polarisation and its error
18652  */
18653 
18654  p_obs = sqrt(q_obs * q_obs + u_obs * u_obs);
18655  p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err);
18656 
18657  /*
18658  * Measured polarisation angle
18659  */
18660 
18661  a_obs = 0.0;
18662  if (polarised) {
18663  if (fabs(q_obs) < 0.00001) {
18664  if (u_obs > 0.0) {
18665  a_obs = 45.0;
18666  }
18667  else {
18668  a_obs = 135.0;
18669  }
18670  }
18671  else {
18672  a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI;
18673  if (q_obs > 0.0) {
18674  if (u_obs < 0.0) {
18675  a_obs += 180.;
18676  }
18677  }
18678  else {
18679  a_obs += 90.;
18680  }
18681  }
18682  }
18683 
18684  /*
18685  * Error on polarisation angle
18686  */
18687 
18688  a_err = 0.0;
18689  if (polarised) {
18690  a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err)
18691  / (p_obs * p_obs)
18692  * 90 / CPL_MATH_PI;
18693  }
18694 
18695  p_obs *= 100;
18696  p_err *= 100;
18697  cpl_msg_info(cpl_func,
18698  "The measured polarisation is %.2f +- %.2f %%",
18699  p_obs, p_err);
18700 
18701  if (polarised) {
18702  cpl_msg_info(cpl_func,
18703  "The measured polarisation angle is %.2f +- %.2f degrees",
18704  a_obs, a_err);
18705  }
18706 
18707  *filter = bands[closest];
18708  *polarisation = polarised;
18709 
18710  if (polarised) {
18711  *p_offset = (p_obs - p_ref) / p_ref;
18712  *p_error = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref;
18713  }
18714  else {
18715  *p_offset = p_obs - p_ref;
18716  *p_error = sqrt(p_err * p_err + dp_ref * dp_ref);
18717  }
18718 
18719  *a_offset = a_obs - a_ref;
18720  *a_error = sqrt(a_err*a_err + da_ref*da_ref);
18721 
18722  return 0;
18723 
18724 }
18725 
18726 
18758 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
18759 {
18760  cpl_array *offsets;
18761  int noffset;
18762  int nslits = cpl_table_get_nrow(reference);
18763  int *nref;
18764  int *nobj;
18765  int corr, maxcorr;
18766  int best_shift;
18767  int i, j, k;
18768 
18769  cpl_error_code status = CPL_ERROR_NONE;
18770 
18771 
18772  *offset = 0.0;
18773 
18774  if (objects == NULL)
18775  return CPL_ERROR_NULL_INPUT;
18776 
18777  if (nslits != cpl_table_get_nrow(objects))
18778  return CPL_ERROR_INCOMPATIBLE_INPUT;
18779 
18780  nref = fors_get_nobjs_perslit(reference);
18781  nobj = fors_get_nobjs_perslit(objects);
18782 
18783  noffset = 0;
18784  for (i = 0; i < nslits; i++)
18785  noffset += nobj[i];
18786 
18787  if (noffset == 0) {
18788  cpl_free(nref);
18789  cpl_free(nobj);
18790  return CPL_ERROR_DATA_NOT_FOUND;
18791  }
18792 
18793  noffset = 0;
18794  for (i = 0; i < nslits; i++)
18795  noffset += nref[i];
18796 
18797  if (noffset == 0) {
18798  cpl_free(nref);
18799  cpl_free(nobj);
18800  return CPL_ERROR_DATA_NOT_FOUND;
18801  }
18802 
18803  offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE);
18804 
18805  noffset = 0; // The real number of offsets found will be counted.
18806 
18807  for (i = 0; i < nslits; i++) {
18808  if (nref[i] > 0 && nobj[i] > 0) {
18809  double shift;
18810  int length = cpl_table_get_int(objects, "length", i, NULL);
18811  double ytop = cpl_table_get_double(objects, "xtop", i, NULL);
18812  double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL);
18813  int *aref = cpl_calloc(length, sizeof(int));
18814  int *aobj = cpl_calloc(length, sizeof(int));
18815  float *pref = cpl_calloc(nref[i], sizeof(float));
18816  float *pobj = cpl_calloc(nobj[i], sizeof(float));
18817 
18818  for (j = 0; j < nref[i]; j++) {
18819  pref[j] = fors_get_object_position(reference, i, j + 1);
18820  aref[(int)pref[j]] = 1;
18821  }
18822 
18823  for (j = 0; j < nobj[i]; j++) {
18824  pobj[j] = fors_get_object_position(objects, i, j + 1);
18825  aobj[(int)pobj[j]] = 1;
18826  }
18827 
18828  /*
18829  * Do not consider objects at border
18830  */
18831 
18832  aref[0] = 0;
18833  aref[length - 1] = 0;
18834  aobj[0] = 0;
18835  aobj[length - 1] = 0;
18836 
18837 //for (j = 0; j < nref[i]; j++)
18838 //printf("references: %f, ", pref[j]);
18839 //printf("\n");
18840 //for (j = 0; j < nref[i]; j++)
18841 //printf("objects : %f, ", pobj[j]);
18842 //printf("\n");
18843 //for (j = 0; j < length; j++)
18844 //printf("%d", aref[j]);
18845 //printf("\n");
18846 //for (j = 0; j < length; j++)
18847 //printf("%d", aobj[j]);
18848 //printf("\n");
18849 
18850  /*
18851  * Object matching by correlation
18852  */
18853 
18854  maxcorr = 0;
18855  best_shift = length;
18856 
18857  for (shift = length/2, j = 0; j <= length; shift--, j++) {
18858  int rstart, ostart, count;
18859 
18860  if (shift > 0) {
18861  rstart = shift;
18862  ostart = 0;
18863  count = length - shift;
18864  }
18865  else {
18866  rstart = 0;
18867  ostart = -shift;
18868  count = length + shift;
18869  }
18870 
18871  corr = 0;
18872  for (k = 0; k < count; k++) {
18873  corr += aref[rstart + k] * aobj[ostart + k];
18874  }
18875 
18876  if (maxcorr < corr) {
18877  maxcorr = corr;
18878  best_shift = shift;
18879  }
18880  }
18881 
18882  if (best_shift == length) { // No shift found
18883 //printf("%d: No shift found\n", i);
18884  cpl_free(aref);
18885  cpl_free(aobj);
18886  cpl_free(pref);
18887  cpl_free(pobj);
18888  continue;
18889  }
18890 //printf("%d: Integer shift found = %d\n", i, best_shift);
18891 
18892  for (j = 0; j < nref[i]; j++) {
18893  for (k = 0; k < nobj[i]; k++) {
18894  if (fabs(pref[j] - pobj[k] - best_shift) < 2) {
18895  double ccd_offset = (pref[j] - pobj[k])
18896  * (ytop - ybottom)
18897  / length;
18898 
18899 //printf("%d: Match found: %f\n", i, ccd_offset);
18900  /*
18901  * The matching object is found, store the
18902  * corresponding offset
18903  */
18904 
18905  cpl_array_set(offsets, noffset, ccd_offset);
18906  noffset++;
18907  break;
18908  }
18909  }
18910  }
18911 
18912  cpl_free(aref);
18913  cpl_free(aobj);
18914  cpl_free(pref);
18915  cpl_free(pobj);
18916  }
18917 //else
18918 //printf("%d: No object found\n", i);
18919  }
18920 
18921  cpl_free(nref);
18922  cpl_free(nobj);
18923 
18924 //printf("%d offsets found in total\n", noffset);
18925  if (noffset > 0) {
18926  if (noffset % 2) {
18927  *offset = cpl_array_get_median(offsets);
18928  }
18929  else {
18930  double *a = cpl_malloc(sizeof(double) * noffset);
18931  for (i = 0; i < noffset; i++) {
18932  a[i] = cpl_array_get_double(offsets, i, NULL);
18933  }
18934  *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) +
18935  fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0;
18936  cpl_free(a);
18937  }
18938  }
18939  else
18940  status = CPL_ERROR_DATA_NOT_FOUND;
18941 //printf("Median offset: %f\n", *offset);
18942 
18943  cpl_array_delete(offsets);
18944 
18945  return status;
18946 
18947 }
18948 
18949 
18961 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
18962 {
18963  cpl_image *source;
18964  int nx = cpl_image_get_size_x(image);
18965  int ny = cpl_image_get_size_y(image);
18966  float *idata;
18967  float *sdata;
18968  int i, j, pos;
18969  double xpos, ypos, xfrac, yfrac;
18970  int xint, yint;
18971 
18972 
18973  if (fabs(dx) >= nx || fabs(dy) >= ny)
18974  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18975 
18976  source = cpl_image_duplicate(image);
18977  idata = cpl_image_get_data_float(image);
18978  sdata = cpl_image_get_data_float(source);
18979 
18980  /*
18981  * Shift in y
18982  */
18983 
18984  yfrac = - dy - floor(- dy);
18985  xfrac = - dx - floor(- dx);
18986 
18987  for (pos = 0, j = 0; j < ny; j++) {
18988  ypos = j - dy;
18989  yint = floor(ypos);
18990  for (i = 0; i < nx; i++) {
18991  xpos = i - dx;
18992  xint = floor(xpos);
18993  if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) {
18994  idata[pos] = 0.0;
18995  }
18996  else {
18997  idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac)
18998  + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac)
18999  + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac
19000  + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac;
19001  }
19002  pos++;
19003  }
19004  }
19005 
19006  cpl_image_delete(source);
19007 
19008  return CPL_ERROR_NONE;
19009 }
19010 
19022 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
19023 {
19024 #ifdef CPL_SIZE_FORMAT
19025  cpl_size row;
19026 #else
19027  int row;
19028 #endif
19029 
19030  cpl_table_duplicate_column(slits, "x", slits, "xtop");
19031  cpl_table_add_columns(slits, "x", "xbottom");
19032  cpl_table_divide_scalar(slits, "x", 2); // Mean x position
19033  cpl_table_subtract_scalar(slits, "x", nx/2); // Relative to CCD center
19034  cpl_table_multiply_columns(slits, "x", "x"); // Squared
19035 
19036  cpl_table_duplicate_column(slits, "y", slits, "ytop");
19037  cpl_table_add_columns(slits, "y", "ybottom");
19038  cpl_table_divide_scalar(slits, "y", 2); // Mean y position
19039  cpl_table_subtract_scalar(slits, "y", ny/2); // Relative to CCD center
19040  cpl_table_multiply_columns(slits, "y", "y"); // Squared
19041 
19042  cpl_table_add_columns(slits, "x", "y"); // Distance from center
19043  cpl_table_get_column_minpos(slits, "x", &row); // Min distance from center
19044 
19045  cpl_table_erase_column(slits, "x");
19046  cpl_table_erase_column(slits, "y");
19047 
19048  return row;
19049 }
19050 
19070 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits,
19071  double xwidth, double ywidth,
19072  int dx, double gain, double *o_flux, double *o_err)
19073 {
19074  int nx = cpl_image_get_size_x(image);
19075  int ny = cpl_image_get_size_y(image);
19076  int slit = mos_slit_closest_to_center(slits, nx, ny);
19077  int ytop = (int)cpl_table_get(slits, "ytop", slit, NULL);
19078  int ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL);
19079  int dy = ytop - ybottom;
19080  int xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) +
19081  cpl_table_get(slits, "xbottom", slit, NULL)) / 2);
19082  int xleft = xcenter - dx;
19083  int xright = xcenter + dx + 1;
19084  double area = xwidth * ywidth; // squared mm
19085  int npix = (2*dx + 1) * dy;
19086  int count = 0;
19087  float *data = cpl_image_get_data_float(image);
19088  double flux = 0.0;
19089  double error = 0.0;
19090  int satur = 60000;
19091  int x, y;
19092 
19093 
19094  if (cpl_table_has_column(slits, "ywidth")) {
19095  area = cpl_table_get(slits, "xwidth", slit, NULL)
19096  * cpl_table_get(slits, "ywidth", slit, NULL);
19097  }
19098 
19099  *o_flux = 0.0;
19100  *o_err = 0.0;
19101 
19102  if (xleft < 0)
19103  xleft = 0;
19104 
19105  if (xleft > nx)
19106  xleft = nx;
19107 
19108  if (xright < 0)
19109  xright = 0;
19110 
19111  if (xright > nx)
19112  xright = nx;
19113 
19114  if (ytop < 0)
19115  ytop = 0;
19116 
19117  if (ytop > ny)
19118  ytop = ny;
19119 
19120  if (ybottom < 0)
19121  ybottom = 0;
19122 
19123  if (ybottom > ny)
19124  ybottom = ny;
19125 
19126  count = (xright - xleft) * (ytop - ybottom);
19127 
19128  if (count == 0)
19129  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19130 
19131  count = 0;
19132 
19133  for (y = ybottom; y < ytop; y++) {
19134  for (x = xleft; x < xright; x++) {
19135  double value = data[x + y * nx];
19136  if (value < satur) {
19137  flux += value;
19138  count++;
19139  }
19140  }
19141  }
19142 
19143  if (count == 0)
19144  return CPL_ERROR_DIVISION_BY_ZERO;
19145 
19146  error = sqrt(flux/gain);
19147 
19148  /*
19149  * Flux correction for lost pixels
19150  */
19151 
19152  flux *= (float)npix / count;
19153  error *= (float)npix / count;
19154 
19155  flux /= area;
19156  error /= area;
19157 
19158  *o_flux = flux;
19159  *o_err = error;
19160 
19161  return CPL_ERROR_NONE;
19162 }
19163 
19164 
19187 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits,
19188  double xwidth, double ywidth,
19189  double lambda, double startwave,
19190  double dispersion, int dx, double gain,
19191  double *o_flux, double *o_err)
19192 {
19193  int nx = cpl_image_get_size_x(image);
19194  int ny = cpl_image_get_size_y(image);
19195  int slit = mos_slit_closest_to_center(slits, nx, ny);
19196  int dy = (int)cpl_table_get(slits, "length", slit, NULL);
19197  int ybottom = (int)cpl_table_get(slits, "position", slit, NULL);
19198  int ytop = ybottom + dy;
19199  int xcenter = (int)floor((lambda - startwave) / dispersion + 0.5);
19200  int xleft = xcenter - dx;
19201  int xright = xcenter + dx + 1;
19202  double area = xwidth * ywidth;
19203  int npix = (2*dx + 1) * dy;
19204  int count = 0;
19205  float *data = cpl_image_get_data_float(image);
19206  double flux = 0.0;
19207  double error = 0.0;
19208  int satur = 60000;
19209  int x, y;
19210 
19211 
19212  if (cpl_table_has_column(slits, "ywidth")) {
19213  area = cpl_table_get(slits, "xwidth", slit, NULL)
19214  * cpl_table_get(slits, "ywidth", slit, NULL);
19215  }
19216 
19217  *o_flux = 0.0;
19218  *o_err = 0.0;
19219 
19220  if (xleft < 0)
19221  xleft = 0;
19222 
19223  if (xleft > nx)
19224  xleft = nx;
19225 
19226  if (xright < 0)
19227  xright = 0;
19228 
19229  if (xright > nx)
19230  xright = nx;
19231 
19232  if (ytop < 0)
19233  ytop = 0;
19234 
19235  if (ytop > ny)
19236  ytop = ny;
19237 
19238  if (ybottom < 0)
19239  ybottom = 0;
19240 
19241  if (ybottom > ny)
19242  ybottom = ny;
19243 
19244  count = (xright - xleft) * (ytop - ybottom);
19245 
19246  if (count == 0)
19247  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19248 
19249  count = 0;
19250 
19251  for (y = ybottom; y < ytop; y++) {
19252  for (x = xleft; x < xright; x++) {
19253  double value = data[x + y * nx];
19254  if (value < satur) {
19255  flux += value;
19256  count++;
19257  }
19258  }
19259  }
19260 
19261  if (count == 0)
19262  return CPL_ERROR_DIVISION_BY_ZERO;
19263 
19264  error = sqrt(flux/gain);
19265 
19266  /*
19267  * Flux correction for lost pixels
19268  */
19269 
19270  flux *= (float)npix / count;
19271  error *= (float)npix / count;
19272 
19273  flux /= area;
19274  error /= area;
19275 
19276  *o_flux = flux;
19277  *o_err = error;
19278 
19279  return CPL_ERROR_NONE;
19280 
19281 }
19282 
19283 
19297 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit,
19298  char *label, double *mvalue)
19299 {
19300  int position = cpl_table_get_int(slits, "position", slit, NULL);
19301  int length = cpl_table_get_int(slits, "length", slit, NULL);
19302  cpl_table *tmp = cpl_table_extract(table, position, length);
19303 
19304  *mvalue = cpl_table_get_column_median(tmp, label);
19305  cpl_table_delete(tmp);
19306 
19307  if (cpl_error_get_code() != CPL_ERROR_NONE)
19308  return 1;
19309 
19310  return 0;
19311 }
19312 
19313 
19325 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny)
19326 {
19327  cpl_mask *kernel = cpl_mask_new(nx, ny);
19328  cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image),
19329  cpl_image_get_size_y(image),
19330  cpl_image_get_type(image));
19331 
19332  cpl_mask_not(kernel);
19333  cpl_image_filter_mask(filtered, image, kernel,
19334  CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
19335  cpl_mask_delete(kernel);
19336 
19337  return filtered;
19338 }
19339 
19340 int fors_mos_is_lss_like(cpl_table *maskslits, int nslits_out_det)
19341 {
19342  int treat_as_lss = 1;
19343  double mxpos = cpl_table_get_column_median(maskslits, "xtop");
19344  double * slit_xpos = cpl_table_get_data_double(maskslits, "xtop");
19345  cpl_size nslits = cpl_table_get_nrow(maskslits);
19346 
19347  //If not all the slits are illuminated, then we cannot say that
19348  //it is lss-like (PIPE-4380)
19349  if(nslits_out_det != 0)
19350  return 0;
19351 
19352  for (cpl_size i = 0; i < nslits; i++) {
19353  if (fabs(mxpos-slit_xpos[i]) > 0.01) {
19354  treat_as_lss = 0;
19355  break;
19356  }
19357  }
19358  return treat_as_lss;
19359 }
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:8531
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:17544
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:11205
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:14791
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:12496
cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, cpl_image *wavemap, int mode, int degree)
Interpolate LSS wavelength calibration.
Definition: moses.c:3064
cpl_image * mos_wavelength_calibration_raw(const cpl_image *image, cpl_vector *lines, double dispersion, float level, int sradius, int order, double reject, double refwave, double *wavestart, double *waveend, int *nlines, double *error, cpl_table *idscoeff, cpl_image *calibration, cpl_image *residuals, cpl_table *restable, cpl_mask *refmask, cpl_table *detected_lines)
Derive wavelength calibration from a raw arc lamp or sky exposure.
Definition: moses.c:5469
double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, double wavestart, double dispersion, int radius, int highres)
Estimate the spectral distortion modeling goodness.
Definition: moses.c:11068
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:18216
cpl_table * mos_load_slits_fors_mxu(cpl_propertylist *header)
Create slit location table from FITS header of FORS2-MXU data.
Definition: moses.c:14879
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:17070
cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
Rotate a slit location table.
Definition: moses.c:6337
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:11401
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:9718
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:6451
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:12163
cpl_image * mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
Local determination of sky.
Definition: moses.c:12840
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:15134
double mos_get_gain_vimos(cpl_propertylist *header)
Return gain factor for a VIMOS exposure.
Definition: moses.c:15509
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:10007
cpl_error_code mos_validate_slits(cpl_table *slits)
Check validity of a slit location table.
Definition: moses.c:6273
cpl_error_code mos_subtract_background(cpl_image *image)
Subtract the background.
Definition: moses.c:16419
int mos_get_maxobjs_per_slit(cpl_table *slits)
Get the maximum possible number of objects in a slit.
Definition: moses.c:17044
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:8189
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:13965
cpl_image * mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
Local determination of sky.
Definition: moses.c:12738
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:18396
cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
Shift values in an image.
Definition: moses.c:18961
int mos_check_multiplex(cpl_table *slits)
Determining whether a VIMOS mask has spectral multplexing or not.
Definition: moses.c:15665
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:19070
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:8350
cpl_bivector * mos_find_peaks(const float *spectrum, int length, cpl_vector *lines, cpl_polynomial *ids, double refwave, int sradius)
Find the reference lines peaks using a polynomial first-guess.
Definition: moses.c:5289
cpl_vector * mos_peak_candidates(const float *spectrum, int length, float level, float exp_width)
Find positions of peaks candidates.
Definition: moses.c:4104
cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, int nscience, float tolerance)
Intersect a number of slit tables.
Definition: moses.c:16449
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:10560
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:11290
double mos_eval_dds(cpl_polynomial *ids, double blue, double red, double refwave, double pixel)
Evaluate the wavelength of a pixel position.
Definition: moses.c:5005
cpl_table * mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference, double blue, double red, double dispersion)
Trace flat field spectra.
Definition: moses.c:7755
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:8949
int mos_check_slits(cpl_table *slits, float rescale)
Check that all slit have been detected, insert them if not.
Definition: moses.c:17101
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:18096
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:16259
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:18021
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:14316
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:13195
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:14523
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:14719
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:17204
cpl_table * mos_load_slits_vimos(cpl_propertylist *header)
Create slit location table from FITS header of VIMOS data.
Definition: moses.c:15554
cpl_error_code mos_randomise_image(cpl_image *image, double ron, double gain, double bias)
Randomise image.
Definition: moses.c:16215
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:6185
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:15341
cpl_polynomial * mos_poly_pix2wav(cpl_bivector *pixwav, int order, double reject, int minlines, int *nlines, double *err)
Fit polynomial relation from pixels to wavelengths.
Definition: moses.c:5232
cpl_error_code mos_interpolate_wavecalib_mos(cpl_table *idscoeff, int mode, int degree)
Interpolate wavelength calibration for a single MOS slit.
Definition: moses.c:3416
cpl_image * mos_arc_background(cpl_image *image, int msize, int fsize)
Background determination on emission line spectrum (arc)
Definition: moses.c:3929
cpl_error_code mos_saturation_process(cpl_image *image)
Process saturation.
Definition: moses.c:16351
cpl_polynomial * mos_poly_wav2pix(cpl_bivector *pixwav, int order, double reject, int minlines, int *nlines, double *err, cpl_bivector **pixwav_used)
Fit polynomial relation from wavelengths to pixels.
Definition: moses.c:5060
cpl_image * mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, cpl_image *spatial, cpl_table *slits, cpl_table *polytraces, double reference, double blue, double red, double dispersion, int flux)
Remapping of slit spectra into a grid of lambda-space coordinates.
Definition: moses.c:11751
int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
Return slit closest to CCD center.
Definition: moses.c:19022
cpl_image * mos_image_filter_median(cpl_image *image, int nx, int ny)
Convenience function for standard median filtering.
Definition: moses.c:19325
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:13699
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:19187
int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
Estimate offset between two object tables.
Definition: moses.c:18758
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:15764
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:19297
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