FORS Pipeline Reference Manual  5.0.19
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 /* double arc2mm = 0.53316; */
14891  double arc2mm = 0.528;
14892  int nslits;
14893  int slit_id;
14894  int fors;
14895  int chip;
14896  int found;
14897 
14898  /*
14899  * The limits below are used to exclude from the loaded slit list
14900  * any slit that surely doesn't belong to the used chip. This is
14901  * a way to reduce the chance of ambiguous slit identification.
14902  */
14903 
14904  float low_limit1 = 10.0;
14905  float hig_limit2 = 30.0;
14906 
14907 
14908  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14909  return NULL;
14910  }
14911 
14912  if (header == NULL) {
14913  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14914  return NULL;
14915  }
14916 
14917 
14918  /*
14919  * See if this is FORS1 or FORS2;
14920  */
14921 
14922  instrume = cpl_propertylist_get_string(header, "INSTRUME");
14923 
14924  fors = 0;
14925  if (instrume[4] == '1')
14926  fors = 1;
14927  if (instrume[4] == '2')
14928  fors = 2;
14929 
14930  if (fors != 2) {
14931  cpl_msg_error(func, "Wrong instrument: %s\n"
14932  "FORS2 is expected for MXU data", instrume);
14933  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14934  return NULL;
14935  }
14936 
14937 
14938  /*
14939  * The master and slave chips can be identified by their positions
14940  * in the chip array in the case of FORS2 data (with fors1 the chip
14941  * is always 1). chip = 2 is the master, chip = 1 is the slave.
14942  */
14943 
14944  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14945 
14946  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14947  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14948  "in FITS header");
14949  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14950  return NULL;
14951  }
14952 
14953  if (chip != 1 && chip != 2) {
14954  cpl_msg_error(func, "Unexpected chip position in keyword "
14955  "ESO DET CHIP1 Y: %d", chip);
14956  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14957  return NULL;
14958  }
14959 
14960 
14961  /*
14962  * Count slits in header (excluding reference slits, and the slits
14963  * that _surely_ belong to the other chip)
14964  */
14965 
14966  nslits = 0;
14967  slit_id = 0;
14968  found = 1;
14969 
14970  while (found) {
14971  slit_id++;
14972  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14973  if (cpl_propertylist_has(header, keyname)) {
14974  slit_y = cpl_propertylist_get_double(header, keyname);
14975 
14976  if (chip == 1)
14977  if (slit_y < low_limit1)
14978  continue;
14979  if (chip == 2)
14980  if (slit_y > hig_limit2)
14981  continue;
14982 
14983  snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME",
14984  slit_id + 100);
14985  if (cpl_propertylist_has(header, keyname)) {
14986  target_name = cpl_propertylist_get_string(header, keyname);
14987  if (strncmp(target_name, "refslit", 7))
14988  nslits++;
14989  }
14990  else
14991  nslits++;
14992  }
14993  else
14994  found = 0;
14995  }
14996 
14997  if (cpl_error_get_code() != CPL_ERROR_NONE) {
14998  cpl_msg_error(func, "%s while loading slits coordinates from "
14999  "FITS header", cpl_error_get_message());
15000  cpl_error_set_where(func);
15001  return NULL;
15002  }
15003 
15004  if (nslits == 0) {
15005  cpl_msg_error(func, "No slits coordinates found in header");
15006  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15007  return NULL;
15008  }
15009 
15010  slits = cpl_table_new(nslits);
15011  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15012  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15013  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15014  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15015  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15016  cpl_table_set_column_unit(slits, "xtop", "pixel");
15017  cpl_table_set_column_unit(slits, "ytop", "pixel");
15018  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15019  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15020 
15021  nslits = 0;
15022  slit_id = 0;
15023  found = 1;
15024  while (found) {
15025  slit_id++;
15026  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
15027  if (cpl_propertylist_has(header, keyname)) {
15028  slit_y = cpl_propertylist_get_double(header, keyname);
15029 
15030  if (chip == 1)
15031  if (slit_y < low_limit1)
15032  continue;
15033  if (chip == 2)
15034  if (slit_y > hig_limit2)
15035  continue;
15036 
15037  /*
15038  * Y-flip the slit position, to match CCD pixel coordinate
15039  * convention
15040  */
15041 
15042  slit_y = -slit_y;
15043 
15044  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
15045  slit_x = cpl_propertylist_get_double(header, keyname);
15046  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15047  cpl_table_delete(slits);
15048  cpl_msg_error(func, "Missing keyword %s in FITS header",
15049  keyname);
15050  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15051  return NULL;
15052  }
15053 
15054  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
15055  length = cpl_propertylist_get_double(header, keyname);
15056  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15057  cpl_table_delete(slits);
15058  cpl_msg_error(func, "Missing keyword %s in FITS header",
15059  keyname);
15060  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15061  return NULL;
15062  }
15063 
15064  length *= arc2mm;
15065 
15066  snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME",
15067  slit_id + 100);
15068  if (cpl_propertylist_has(header, keyname)) {
15069  target_name = cpl_propertylist_get_string(header, keyname);
15070  if (strncmp(target_name, "refslit", 7)) {
15071  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15072  cpl_table_set(slits, "xtop", nslits, slit_x);
15073  cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15074  cpl_table_set(slits, "xbottom", nslits, slit_x);
15075  cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15076  nslits++;
15077  }
15078  }
15079  else {
15080  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15081  cpl_table_set(slits, "xtop", nslits, slit_x);
15082  cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15083  cpl_table_set(slits, "xbottom", nslits, slit_x);
15084  cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15085  nslits++;
15086  }
15087  }
15088  else
15089  found = 0;
15090  }
15091 
15092  return slits;
15093 }
15094 
15095 
15119 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header,
15120  int * nslits_out_det)
15121 {
15122  const char *func = "mos_load_slits_fors_mos";
15123 
15124  cpl_table *slits;
15125  char keyname[MAX_COLNAME];
15126  const char *instrume;
15127  const char *chipname;
15128  float slit_x;
15129  int first_slit, last_slit;
15130  cpl_size nslits;
15131  int slit_id;
15132  int fors;
15133  int chip;
15134  int fors_is_old;
15135 
15136  /*
15137  * The Y coordinates of the slits are fixed
15138  */
15139 
15140  float ytop[19] = { 113.9, 101.3, 89.9, 77.3, 65.9, 53.3,
15141  41.9, 29.3, 17.9, 5.3, -6.1, -18.7,
15142  -30.1, -42.7, -54.1, -66.7, -78.1, -90.7,
15143  -102.1 };
15144  float ybottom[19] = { 102.1, 90.7, 78.1, 66.7, 54.1, 42.7,
15145  30.1, 18.7, 6.1, -5.3, -17.9, -29.3,
15146  -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
15147  -113.9 };
15148 
15149 
15150  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15151  return NULL;
15152  }
15153 
15154  if (header == NULL) {
15155  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15156  return NULL;
15157  }
15158 
15159 
15160  /*
15161  * See if this is FORS1 or FORS2;
15162  */
15163 
15164  instrume = cpl_propertylist_get_string(header, "INSTRUME");
15165 
15166  fors = 0;
15167  if (instrume[4] == '1')
15168  fors = 1;
15169  if (instrume[4] == '2')
15170  fors = 2;
15171 
15172  if (fors == 0) {
15173  cpl_msg_error(func, "Wrong instrument found in FITS header: %s",
15174  instrume);
15175  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15176  return NULL;
15177  }
15178 
15179  /* FIXME:
15180  * This is the way FORS1 data belong to the upgraded chips,
15181  * named "Marlene" and "Norma III". It's a quick solution,
15182  * there are hardcoded values here!!!
15183  */
15184 
15185  chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
15186 
15187  if (chipname[0] == 'M' || chipname[0] == 'N')
15188  fors_is_old = 0;
15189  else
15190  fors_is_old = 1;
15191 
15192  if (fors == 1 && fors_is_old) {
15193  first_slit = 1;
15194  last_slit = 19;
15195  }
15196  else {
15197 
15198  /*
15199  * The master and slave chips can be identified by their positions
15200  * in the chip array in the case of FORS2 data: chip = 2 is the
15201  * master, chip = 1 is the slave.
15202  */
15203 
15204  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15205 
15206  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15207  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15208  "in FITS header");
15209  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15210  return NULL;
15211  }
15212 
15213  if (chip != 1 && chip != 2) {
15214  cpl_msg_error(func, "Unexpected chip position in keyword "
15215  "ESO DET CHIP1 Y: %d", chip);
15216  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15217  return NULL;
15218  }
15219 
15220  if (chip == 1) {
15221  first_slit = 12;
15222  last_slit = 19;
15223  }
15224  else {
15225  first_slit = 1;
15226  last_slit = 11;
15227  }
15228  }
15229 
15230 
15231  /*
15232  * Count slits in header (excluding closed slits - i.e. those with
15233  * offsets greater than 115 mm - and the slits that do not belong
15234  * to this chip)
15235  */
15236 
15237  nslits = 0;
15238  slit_id = 0;
15239  for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15240  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15241  if (cpl_propertylist_has(header, keyname)) {
15242  slit_x = cpl_propertylist_get_double(header, keyname);
15243  if (fabs(slit_x) < 115.0)
15244  nslits++;
15245  else
15246  (*nslits_out_det)++;
15247  }
15248  else {
15249  cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
15250  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15251  return NULL;
15252  }
15253  }
15254 
15255  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15256  cpl_msg_error(func, "%s while loading slits coordinates from "
15257  "FITS header", cpl_error_get_message());
15258  cpl_error_set_where(func);
15259  return NULL;
15260  }
15261 
15262  if (nslits == 0) {
15263  cpl_msg_error(func, "No slits coordinates found in header");
15264  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15265  return NULL;
15266  }
15267 
15268  slits = cpl_table_new(nslits);
15269  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15270  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15271  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15272  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15273  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15274  cpl_table_set_column_unit(slits, "xtop", "pixel");
15275  cpl_table_set_column_unit(slits, "ytop", "pixel");
15276  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15277  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15278 
15279  nslits = 0;
15280  slit_id = 0;
15281  for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15282  snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15283  slit_x = cpl_propertylist_get_double(header, keyname);
15284  if (fabs(slit_x) < 115.0) {
15285  cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15286  cpl_table_set(slits, "xtop", nslits, slit_x);
15287  cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
15288  cpl_table_set(slits, "xbottom", nslits, slit_x);
15289  cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
15290  nslits++;
15291  }
15292  }
15293 
15294  return slits;
15295 }
15296 
15297 
15321 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
15322 {
15323  const char *func = "mos_load_slits_fors_lss";
15324 
15325  cpl_table *slits;
15326  char *slit_name;
15327  const char *instrume;
15328  int fors;
15329  int chip;
15330  float ytop;
15331  float ybottom;
15332 
15333  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15334  return NULL;
15335  }
15336 
15337  if (header == NULL) {
15338  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15339  return NULL;
15340  }
15341 
15342 
15343  /*
15344  * See if this is FORS1 or FORS2;
15345  */
15346 
15347  instrume = cpl_propertylist_get_string(header, "INSTRUME");
15348 
15349  fors = 0;
15350  if (instrume[4] == '1')
15351  fors = 1;
15352  if (instrume[4] == '2')
15353  fors = 2;
15354 
15355  if (fors == 0) {
15356  cpl_msg_error(func, "Wrong instrument found in FITS header: %s",
15357  instrume);
15358  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15359  return NULL;
15360  }
15361 
15362  if (fors == 1) {
15363  ytop = 109.94;
15364  ybottom = -109.94;
15365  }
15366  else {
15367 
15368  /*
15369  * The master and slave chips can be identified by their positions
15370  * in the chip array in the case of FORS2 data: chip = 2 is the
15371  * master, chip = 1 is the slave.
15372  */
15373 
15374  chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15375 
15376  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15377  cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15378  "in FITS header");
15379  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15380  return NULL;
15381  }
15382 
15383  if (chip != 1 && chip != 2) {
15384  cpl_msg_error(func, "Unexpected chip position in keyword "
15385  "ESO DET CHIP1 Y: %d", chip);
15386  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15387  return NULL;
15388  }
15389 
15390  if (chip == 1) {
15391  ytop = 30.0;
15392  ybottom = -109.94;
15393  }
15394  else {
15395  ytop = 109.94;
15396  ybottom = -20.0;
15397  }
15398  }
15399 
15400 
15401  slits = cpl_table_new(1);
15402  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15403  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15404  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15405  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15406  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15407  cpl_table_set_column_unit(slits, "xtop", "pixel");
15408  cpl_table_set_column_unit(slits, "ytop", "pixel");
15409  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15410  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15411 
15412  slit_name = (char *)cpl_propertylist_get_string(header,
15413  "ESO INS SLIT NAME");
15414 
15415  cpl_table_set(slits, "ytop", 0, ytop);
15416  cpl_table_set(slits, "ybottom", 0, ybottom);
15417 
15418  if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
15419  cpl_table_set_int(slits, "slit_id", 0, 1);
15420  cpl_table_set(slits, "xbottom", 0, -0.075);
15421  cpl_table_set(slits, "xtop", 0, 0.075);
15422  }
15423  else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
15424  cpl_table_set_int(slits, "slit_id", 0, 2);
15425  cpl_table_set(slits, "xbottom", 0, 5.895);
15426  cpl_table_set(slits, "xtop", 0, 6.105);
15427  }
15428  else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
15429  cpl_table_set_int(slits, "slit_id", 0, 3);
15430  cpl_table_set(slits, "xbottom", 0, -6.135);
15431  cpl_table_set(slits, "xtop", 0, -5.865);
15432  }
15433  else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
15434  cpl_table_set_int(slits, "slit_id", 0, 4);
15435  cpl_table_set(slits, "xbottom", 0, 11.815);
15436  cpl_table_set(slits, "xtop", 0, 12.185);
15437  }
15438  else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
15439  cpl_table_set_int(slits, "slit_id", 0, 5);
15440  cpl_table_set(slits, "xbottom", 0, -12.265);
15441  cpl_table_set(slits, "xtop", 0, -11.735);
15442  }
15443  else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
15444  cpl_table_set_int(slits, "slit_id", 0, 6);
15445  cpl_table_set(slits, "xbottom", 0, 17.655);
15446  cpl_table_set(slits, "xtop", 0, 18.345);
15447  }
15448  else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
15449  cpl_table_set_int(slits, "slit_id", 0, 7);
15450  cpl_table_set(slits, "xbottom", 0, -18.425);
15451  cpl_table_set(slits, "xtop", 0, -17.575);
15452  }
15453  else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
15454  cpl_table_set_int(slits, "slit_id", 0, 8);
15455  cpl_table_set(slits, "xbottom", 0, 23.475);
15456  cpl_table_set(slits, "xtop", 0, 24.525);
15457  }
15458  else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
15459  cpl_table_set_int(slits, "slit_id", 0, 9);
15460  cpl_table_set(slits, "xbottom", 0, -24.66);
15461  cpl_table_set(slits, "xtop", 0, -23.34);
15462  }
15463  else {
15464  cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
15465  slit_name);
15466  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15467  cpl_table_delete(slits);
15468  return NULL;
15469  }
15470 
15471  return slits;
15472 }
15473 
15474 
15489 double mos_get_gain_vimos(cpl_propertylist *header)
15490 {
15491  const char *func = "mos_get_gain_vimos";
15492 
15493  double gain = -1.0;
15494 
15495 
15496  if (cpl_error_get_code() != CPL_ERROR_NONE)
15497  return gain;
15498 
15499  if (header == NULL) {
15500  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15501  return gain;
15502  }
15503 
15504  gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
15505  if (cpl_error_get_code()) {
15506  cpl_error_set_where(func);
15507  gain = -1.0;
15508  }
15509 
15510  return gain;
15511 
15512 }
15513 
15514 
15534 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
15535 {
15536  const char *func = "mos_load_slits_vimos";
15537 
15538  cpl_table *slits;
15539  char keyname[MAX_COLNAME];
15540  float slit_x;
15541  float slit_y;
15542  float dim_x;
15543  float dim_y;
15544  int nslits;
15545  int slit_id;
15546  int curved;
15547  int i;
15548 
15549 
15550  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15551  return NULL;
15552  }
15553 
15554  if (header == NULL) {
15555  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15556  return NULL;
15557  }
15558 
15559  nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
15560 
15561  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15562  cpl_error_set_where(func);
15563  return NULL;
15564  }
15565 
15566  slits = cpl_table_new(nslits);
15567  cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15568  cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
15569  cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
15570  cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15571  cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15572  cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15573  cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE);
15574  cpl_table_new_column(slits, "curved", CPL_TYPE_INT);
15575  cpl_table_set_column_unit(slits, "xtop", "pixel");
15576  cpl_table_set_column_unit(slits, "ytop", "pixel");
15577  cpl_table_set_column_unit(slits, "xbottom", "pixel");
15578  cpl_table_set_column_unit(slits, "ybottom", "pixel");
15579  cpl_table_set_column_unit(slits, "xwidth", "mm");
15580  cpl_table_set_column_unit(slits, "ywidth", "mm");
15581 
15582  for (i = 0; i < nslits; i++) {
15583  sprintf(keyname, "ESO INS SLIT%d ID", i+1);
15584  slit_id = cpl_propertylist_get_int(header, keyname);
15585  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15586  cpl_error_set_where(func);
15587  return NULL;
15588  }
15589  sprintf(keyname, "ESO INS SLIT%d X", i+1);
15590  slit_x = cpl_propertylist_get_double(header, keyname);
15591  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15592  cpl_error_set_where(func);
15593  return NULL;
15594  }
15595  sprintf(keyname, "ESO INS SLIT%d Y", i+1);
15596  slit_y = cpl_propertylist_get_double(header, keyname);
15597  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15598  cpl_error_set_where(func);
15599  return NULL;
15600  }
15601  sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
15602  dim_x = cpl_propertylist_get_double(header, keyname);
15603  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15604  cpl_error_set_where(func);
15605  return NULL;
15606  }
15607 
15608  sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1);
15609  if (cpl_propertylist_has(header, keyname)) {
15610  curved = 1;
15611  }
15612  else {
15613  sprintf(keyname, "ESO INS SLIT%d DIMY", i+1);
15614  curved = 0;
15615  }
15616  dim_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 
15622  cpl_table_set_int(slits, "slit_id", i, slit_id);
15623  cpl_table_set(slits, "xtop", i, slit_x - dim_x/2);
15624  cpl_table_set(slits, "ytop", i, slit_y);
15625  cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2);
15626  cpl_table_set(slits, "ybottom", i, slit_y);
15627  cpl_table_set(slits, "xwidth", i, dim_x);
15628  cpl_table_set(slits, "ywidth", i, dim_y);
15629  cpl_table_set_int(slits, "curved", i, curved);
15630  }
15631 
15632  return slits;
15633 }
15634 
15635 
15645 int mos_check_multiplex(cpl_table *slits)
15646 {
15647  cpl_propertylist *sort;
15648  int nrow;
15649  int i, multiplex, xprev, xcur;
15650  double prev, cur;
15651  double tolerance = 1.0; // About spatially aligned slits (mm)
15652 
15653 
15654  /*
15655  * Create an auxiliary column containing a sort of integer
15656  * x coordinate of the slit, to guarantee that slits at the
15657  * same spatial offset are recognised immediately as in spectral
15658  * multiplexing.
15659  */
15660 
15661  sort = cpl_propertylist_new();
15662  cpl_propertylist_append_bool(sort, "xtop", 0);
15663  cpl_table_sort(slits, sort);
15664  cpl_propertylist_delete(sort);
15665 
15666  prev = cpl_table_get_double(slits, "xtop", 0, NULL);
15667  cpl_table_new_column(slits, "xind", CPL_TYPE_INT);
15668  cpl_table_set_int(slits, "xind", 0, prev); // cast to int is intentional
15669  nrow = cpl_table_get_nrow(slits);
15670  for (i = 1; i < nrow; i++) {
15671  cur = cpl_table_get_double(slits, "xtop", i, NULL);
15672  if (fabs(prev - cur) > tolerance)
15673  prev = cur;
15674  cpl_table_set_int(slits, "xind", i, prev);
15675  }
15676 
15677  /*
15678  * Now sort according to increasing (integer) x positions, and when
15679  * those are equal (multiplexed) according to the increasing y position.
15680  */
15681 
15682  sort = cpl_propertylist_new();
15683  cpl_propertylist_append_bool(sort, "xind", 0);
15684  cpl_propertylist_append_bool(sort, "ytop", 0);
15685  cpl_table_sort(slits, sort);
15686  cpl_propertylist_delete(sort);
15687 
15688  /*
15689  * Now assign to each slit its multiplex order.
15690  */
15691 
15692  multiplex = 0;
15693  cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT);
15694  xprev = cpl_table_get_int(slits, "xind", 0, NULL);
15695  cpl_table_set_int(slits, "multiplex", 0, multiplex);
15696  nrow = cpl_table_get_nrow(slits);
15697  for (i = 1; i < nrow; i++) {
15698  xcur = cpl_table_get_int(slits, "xind", i, NULL);
15699  if (xcur == xprev) {
15700  multiplex++;
15701  }
15702  else {
15703  xprev = xcur;
15704  multiplex = 0;
15705  }
15706  cpl_table_set_int(slits, "multiplex", i, multiplex);
15707  }
15708 
15709  cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT);
15710 
15711  cpl_table_erase_column(slits, "xind");
15712 
15713  return 1 + cpl_table_get_column_max(slits, "multiplex");
15714 
15715 }
15716 
15717 
15744 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header,
15745  int check_consistency)
15746 {
15747  const char *func = "mos_load_overscans_vimos";
15748 
15749  int nx = 0;
15750  int ny = 0;
15751  int px = 0;
15752  int py = 0;
15753  int ox = 0;
15754  int oy = 0;
15755  int vx = 0;
15756  int vy = 0;
15757  int nrows;
15758  cpl_table *overscans;
15759 
15760 
15761  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15762  cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15763  return NULL;
15764  }
15765 
15766  if (header == NULL) {
15767  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15768  return NULL;
15769  }
15770 
15771  if (cpl_propertylist_has(header, "NAXIS1"))
15772  nx = cpl_propertylist_get_int(header, "NAXIS1");
15773  if (cpl_propertylist_has(header, "NAXIS2"))
15774  ny = cpl_propertylist_get_int(header, "NAXIS2");
15775  if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
15776  px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
15777  if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
15778  py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
15779  if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
15780  ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
15781  if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
15782  oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
15783  if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
15784  vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
15785  if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
15786  vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
15787 
15788  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15789  cpl_msg_error(func, "Missing overscan keywords in header");
15790  cpl_error_set_where(func);
15791  return NULL;
15792  }
15793 
15794  if (px < 0 || py < 0 || ox < 0 || oy < 0) {
15795  cpl_msg_error(func, "Missing overscan keywords in header");
15796  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15797  return NULL;
15798  }
15799 
15800  if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
15801  if (check_consistency) {
15802  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15803  return NULL;
15804  }
15805  else {
15806  cpl_msg_debug(func, "Overscans description conflicts with "
15807  "reported image sizes, "
15808  "%d + %d + %d != %d or "
15809  "%d + %d + %d != %d",
15810  px, vx, ox, nx,
15811  py, vy, oy, ny);
15812  }
15813  }
15814 
15815  nrows = 0;
15816  if (px > 0)
15817  nrows++;
15818  if (ox > 0)
15819  nrows++;
15820  if (py > 0)
15821  nrows++;
15822  if (oy > 0)
15823  nrows++;
15824 
15825  if (nrows > 2) {
15826  cpl_msg_error(func, "Unexpected overscan regions "
15827  "(both in X and Y direction)");
15828  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15829  return NULL;
15830  }
15831 
15832 
15833  /*
15834  * A row is added for the description of the valid region of the
15835  * exposure the input header belongs to.
15836  */
15837 
15838  nrows++;
15839 
15840  overscans = cpl_table_new(nrows);
15841  cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15842  cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15843  cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15844  cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15845 
15846  nrows = 0;
15847 
15848  cpl_table_set_int(overscans, "xlow", nrows, px);
15849  cpl_table_set_int(overscans, "ylow", nrows, py);
15850  cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15851  cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15852  nrows++;
15853 
15854  if (px > 0) {
15855  cpl_table_set_int(overscans, "xlow", nrows, 0);
15856  cpl_table_set_int(overscans, "ylow", nrows, 0);
15857  cpl_table_set_int(overscans, "xhig", nrows, px);
15858  cpl_table_set_int(overscans, "yhig", nrows, ny);
15859  nrows++;
15860  }
15861 
15862  if (ox > 0) {
15863  cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15864  cpl_table_set_int(overscans, "ylow", nrows, 0);
15865  cpl_table_set_int(overscans, "xhig", nrows, nx);
15866  cpl_table_set_int(overscans, "yhig", nrows, ny);
15867  nrows++;
15868  }
15869 
15870  if (py > 0) {
15871  cpl_table_set_int(overscans, "xlow", nrows, 0);
15872  cpl_table_set_int(overscans, "ylow", nrows, 0);
15873  cpl_table_set_int(overscans, "xhig", nrows, nx);
15874  cpl_table_set_int(overscans, "yhig", nrows, py);
15875  nrows++;
15876  }
15877 
15878  if (oy > 0) {
15879  cpl_table_set_int(overscans, "xlow", nrows, 0);
15880  cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
15881  cpl_table_set_int(overscans, "xhig", nrows, nx);
15882  cpl_table_set_int(overscans, "yhig", nrows, ny);
15883  nrows++;
15884  }
15885 
15886  return overscans;
15887 
15888 }
15889 
15890 
15891 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
15892 {
15893  const char *func = "mos_load_overscans_fors";
15894 
15895  int nports;
15896  int nx = 0;
15897  int ny = 0;
15898  int px = 0;
15899  int py = 0;
15900  int ox = 0;
15901  int oy = 0;
15902  int rebin;
15903  int nrows;
15904  cpl_table *overscans;
15905 
15906 
15907  if (cpl_error_get_code() != CPL_ERROR_NONE) {
15908  cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15909  return NULL;
15910  }
15911 
15912  if (header == NULL) {
15913  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15914  return NULL;
15915  }
15916 
15917  if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
15918  nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
15919 
15920  if (nports == 4 &&
15921  cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
15922  cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
15923 
15924  rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
15925 
15926  overscans = cpl_table_new(3);
15927  cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15928  cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15929  cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15930  cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15931 
15932  px = 16 / rebin;
15933  ox = 16 / rebin;
15934  nx = 2080 / rebin;
15935  ny = 2048 / rebin;
15936  nrows = 0;
15937 
15938  cpl_table_set_int(overscans, "xlow", nrows, px);
15939  cpl_table_set_int(overscans, "ylow", nrows, py);
15940  cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15941  cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15942  nrows++;
15943 
15944  cpl_table_set_int(overscans, "xlow", nrows, 0);
15945  cpl_table_set_int(overscans, "ylow", nrows, 0);
15946  cpl_table_set_int(overscans, "xhig", nrows, px);
15947  cpl_table_set_int(overscans, "yhig", nrows, ny);
15948  nrows++;
15949 
15950  cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15951  cpl_table_set_int(overscans, "ylow", nrows, 0);
15952  cpl_table_set_int(overscans, "xhig", nrows, nx);
15953  cpl_table_set_int(overscans, "yhig", nrows, ny);
15954  nrows++;
15955  }
15956  else {
15957  overscans = mos_load_overscans_vimos(header, 0);
15958  }
15959 
15960  return overscans;
15961 
15962 }
15963 
15995 #define READY 1
15996 #ifdef READY
15997 
15998 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate,
15999  int samples, int order)
16000 {
16001 
16002  const char *func = "mos_montecarlo_polyfit";
16003 
16004  cpl_polynomial *p;
16005  cpl_polynomial *q;
16006  cpl_vector *listx;
16007  cpl_vector *listy;
16008  double err;
16009  double *x;
16010  double *px;
16011  double *x_eval;
16012  double *px_eval;
16013  double *sigma;
16014  double *vy;
16015  double *dy;
16016  int npoints, nevaluate;
16017  int i, j;
16018 
16019 
16020  if (points == NULL || evaluate == NULL) {
16021  cpl_error_set(func, CPL_ERROR_NULL_INPUT);
16022  return NULL;
16023  }
16024 
16025  if (!cpl_table_has_column(points, "x")) {
16026  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16027  return NULL;
16028  }
16029 
16030  if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
16031  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16032  return NULL;
16033  }
16034 
16035  if (cpl_table_has_invalid(points, "x")) {
16036  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16037  return NULL;
16038  }
16039 
16040  if (!cpl_table_has_column(points, "y")) {
16041  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16042  return NULL;
16043  }
16044 
16045  if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
16046  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16047  return NULL;
16048  }
16049 
16050  if (cpl_table_has_invalid(points, "y")) {
16051  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16052  return NULL;
16053  }
16054 
16055  if (cpl_table_has_column(points, "y_err")) {
16056 
16057  if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
16058  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16059  return NULL;
16060  }
16061 
16062  if (cpl_table_has_invalid(points, "y_err")) {
16063  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16064  return NULL;
16065  }
16066  }
16067 
16068  if (!cpl_table_has_column(evaluate, "x")) {
16069  cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16070  return NULL;
16071  }
16072 
16073  if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
16074  cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16075  return NULL;
16076  }
16077 
16078  if (cpl_table_has_invalid(evaluate, "x")) {
16079  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16080  return NULL;
16081  }
16082 
16083  if (samples < 2 || order < 0) {
16084  cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16085  return NULL;
16086  }
16087 
16088  npoints = cpl_table_get_nrow(points);
16089  listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
16090  listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
16091 
16092  p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
16093 
16094  if (!cpl_table_has_column(points, "y_err")) {
16095  err = sqrt(err);
16096  cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
16097  cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
16098  cpl_msg_info(func, "Error column not found - set to %f\n", err);
16099  }
16100 
16101  /*
16102  * Create columns containing modeled values at each x
16103  */
16104 
16105  if (cpl_table_has_column(points, "px"))
16106  cpl_table_erase_column(points, "px");
16107  cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
16108  cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
16109  x = cpl_table_get_data_double(points, "x");
16110  px = cpl_table_get_data_double(points, "px");
16111  for (i = 0; i < npoints; i++)
16112  px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
16113 
16114  nevaluate = cpl_table_get_nrow(evaluate);
16115 
16116  if (cpl_table_has_column(evaluate, "px"))
16117  cpl_table_erase_column(evaluate, "px");
16118  cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
16119  cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
16120  x_eval = cpl_table_get_data_double(evaluate, "x");
16121  px_eval = cpl_table_get_data_double(evaluate, "px");
16122  for (i = 0; i < nevaluate; i++)
16123  px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
16124 
16125  /*
16126  * Initialise column with sigma
16127  */
16128 
16129  if (cpl_table_has_column(evaluate, "sigma"))
16130  cpl_table_erase_column(evaluate, "sigma");
16131  cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
16132  cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
16133  sigma = cpl_table_get_data_double(evaluate, "sigma");
16134 
16135  /*
16136  * Compute varied y cordinates to fit
16137  */
16138 
16139  if (cpl_table_has_column(points, "vy"))
16140  cpl_table_erase_column(points, "vy");
16141  cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
16142  cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
16143  vy = cpl_table_get_data_double(points, "vy");
16144  dy = cpl_table_get_data_double(points, "y_err");
16145  cpl_vector_unwrap(listy);
16146  listy = cpl_vector_wrap(npoints, vy);
16147 
16148  for (i = 0; i < samples; i++) {
16149  for (j = 0; j < npoints; j++)
16150  vy[j] = px[j] + dy[j] * mos_randg(1);
16151  q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
16152  for (j = 0; j < nevaluate; j++)
16153  sigma[j] += fabs(px_eval[j]
16154  - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
16155  cpl_polynomial_delete(q);
16156  }
16157 
16158  /*
16159  * Factor 1.25 to convert average deviation to sigma
16160  */
16161 
16162  cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
16163  cpl_table_divide_scalar(evaluate, "sigma", samples);
16164 
16165  cpl_vector_unwrap(listx);
16166  cpl_vector_unwrap(listy);
16167 
16168  return p;
16169 }
16170 
16171 #endif
16172 
16195 cpl_error_code mos_randomise_image(cpl_image *image, double ron,
16196  double gain, double bias)
16197 {
16198  float *data;
16199  int npix, i;
16200 
16201 
16202  if (image == NULL)
16203  return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
16204 
16205  if (ron < 0.0 || gain <= FLT_EPSILON)
16206  return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
16207 
16208  data = cpl_image_get_data_float(image);
16209  npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image);
16210  ron *= ron;
16211 
16212  for (i = 0; i < npix; i++) {
16213  if (data[i] < bias) {
16214  data[i] += sqrt(ron) * mos_randg(1);
16215  }
16216  else {
16217  data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1);
16218  }
16219  }
16220 
16221  return CPL_ERROR_NONE;
16222 }
16223 
16224 
16239 cpl_error_code mos_refmask_find_gaps(cpl_mask *refmask,
16240  cpl_image *master_flat,
16241  double level)
16242 {
16243  int nx = cpl_mask_get_size_x(refmask);
16244  int ny = cpl_mask_get_size_y(refmask);
16245 
16246  int * xpos = cpl_calloc(sizeof(int), ny);
16247 
16248  cpl_image * filtered = cpl_image_duplicate(master_flat);
16249  cpl_mask * kernel = cpl_mask_new(9, 3);
16250  cpl_vector * v = cpl_vector_new(ny);
16251  cpl_vector * truev;
16252  int nvalid = 0;
16253  double * flats = cpl_vector_get_data(v);
16254 
16255  double median, stdev, delta;
16256 
16257  int i, kill;
16258 
16259  cpl_mask_not(kernel);
16260  cpl_image_filter_mask(filtered, master_flat, kernel,
16261  CPL_FILTER_MEDIAN, CPL_BORDER_COPY);
16262  cpl_mask_delete(kernel);
16263 
16264  for (i = 1; i <= ny; i++) {
16265  int j = 0;
16266 
16267  do j++;
16268  while (!cpl_mask_get(refmask, j, i) && j < nx);
16269 
16270  if (j < nx) {
16271  int rejected;
16272 
16273  xpos[i - 1] = j;
16274  flats[nvalid] = cpl_image_get(filtered, j, i, &rejected);
16275  nvalid++;
16276  }
16277  else {
16278  xpos[i - 1] = -1;
16279  }
16280  }
16281 
16282  if (nvalid == 0)
16283  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16284 
16285  truev = cpl_vector_wrap(nvalid, flats);
16286 
16287  median = cpl_vector_get_median(truev);
16288 
16289  if (level < 0.0)
16290  stdev = cpl_vector_get_stdev(truev);
16291 
16292  cpl_vector_unwrap(truev);
16293  cpl_vector_delete(v);
16294 
16295  for (i = 1; i <= ny; i++) {
16296  if (xpos[i - 1] > 0) {
16297  int rejected;
16298  double kappa = 1.5;
16299 
16300  delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median;
16301 
16302  if (level < 0.0)
16303  kill = fabs(delta) > stdev * kappa;
16304  else
16305  kill = delta < level;
16306 
16307  if (kill) {
16308  int j = 0;
16309 
16310  while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
16311  cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
16312  j++;
16313  }
16314  }
16315  }
16316  }
16317 
16318  cpl_image_delete(filtered);
16319  cpl_free(xpos);
16320 
16321  return cpl_error_get_code();
16322 }
16323 
16331 cpl_error_code mos_saturation_process(cpl_image * image)
16332 {
16333  int nx = cpl_image_get_size_x(image);
16334  int ny = cpl_image_get_size_y(image);
16335  int npix = nx * ny;
16336  float * sdata = cpl_image_get_data_float(image);
16337 
16338  int count, i, j, k;
16339 
16340  /*
16341  * This is used to avoid saturation level coded with pixel value zero
16342  * To make it more robust against random 0.0 values, check that also
16343  * next pixel along the spatial direction is 0.0.
16344  */
16345 
16346  //This could be applied only to raw images, but it is being applied
16347  //to already bias/overscan processed images, which doesn't make sense.
16348 // for (i = 0; i < npix - nx; i++)
16349 // if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
16350 // sdata[i] = 65535.0;
16351 
16352 // for (i = npix - nx; i < npix; i++)
16353 // if (sdata[i] == 0.0)
16354 // sdata[i] = 65535.0;
16355 
16356  /*
16357  * This is a dirty trick to overcome saturations (making up a false
16358  * tip on their flat tops). This should be useless with a better
16359  * peak detection algorithm.
16360  */
16361 
16362  for (i = 0; i < npix; i++) {
16363  if (sdata[i] >= 65535.0) {
16364  count = 0;
16365  for (j = i; j < npix; j++) {
16366  if (sdata[j] < 65535.0) {
16367  break;
16368  }
16369  else {
16370  count++;
16371  }
16372  }
16373  if (count < 30 && count > 2) {
16374  for (j = i; j < i + count/2; j++)
16375  sdata[j] = sdata[i] + 1000.0 * (j - i);
16376  if (count % 2 != 0) {
16377  sdata[j] = sdata[j-1] + 1000.0;
16378  j++;
16379  }
16380  for (k = j; k <= i + count; k++)
16381  sdata[k] = sdata[i] - 1000.0 * (k - i - count);
16382  i = k;
16383  }
16384  }
16385  }
16386 
16387  return cpl_error_get_code();
16388 }
16389 
16390 
16399 cpl_error_code mos_subtract_background(cpl_image * image)
16400 {
16401  /*
16402  * Create and subtract background
16403  */
16404 
16405  cpl_image * bimage = mos_arc_background(image, 15, 15);
16406  cpl_image_subtract(image, bimage);
16407  cpl_image_delete(bimage);
16408 
16409  return cpl_error_get_code();
16410 }
16411 
16412 
16429 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits,
16430  int nscience, float tolerance)
16431 {
16432  int i, j;
16433 
16434  cpl_table *summary;
16435  int summary_nobjs = 0;
16436 
16437  int nobjs;
16438 
16439  int nmatches;
16440  int nslits = cpl_table_get_nrow(slitss[0]);
16441 
16442  int maxobjs;
16443  int k, m;
16444  int nstokes, sstokes;
16445 
16446  cpl_table **work;
16447 
16448  work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
16449 
16450 
16451  /*
16452  * First we build a table listing the offset of each detected
16453  * object at each angle and each beam, from the bottom of each
16454  * slit spectrum, and the pair that slit spectrum belongs to.
16455  * This summary table will have as many rows as objects found
16456  * in total at all angles.
16457  */
16458 
16459  for (j = 0; j < nscience; j++) {
16460  int c_nobjs = mos_get_nobjects(slitss[j]);
16461  if (!c_nobjs)
16462  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16463  summary_nobjs += c_nobjs;
16464  }
16465 
16466  summary = cpl_table_new(summary_nobjs);
16467 
16468  cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
16469  cpl_table_new_column(summary, "pair", CPL_TYPE_INT);
16470  cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE);
16471  cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE);
16472 
16473  /*
16474  * Fill the summary table with data from all objects:
16475  */
16476 
16477  nobjs = 0;
16478 
16479  /* Loop on all object tables (one for each angle) */
16480  for (j = 0; j < nscience; j++) {
16481  int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
16482 
16483  /* Loop on all slits found on first - i.e., ALL - object table */
16484  for (k = 0; k < nslits; k++) {
16485 
16486  /* Loop on all objects found on each object table */
16487  for (m = 0; m < c_maxobjs; m++) {
16488  int null;
16489  char *name = cpl_sprintf("object_%d", m + 1);
16490  double obj = cpl_table_get_double(slitss[j], name, k, &null);
16491  int pos;
16492  int pair;
16493 
16494  cpl_free(name);
16495 
16496  if (null)
16497  break; /* No object #m+1 in this slit - go to next slit */
16498 
16499  /*
16500  * Copy necessary object data to summary table. Note
16501  * that the absolute object position (row) in the
16502  * rectified image is made relative to the bottom
16503  * position (row) of the current slit.
16504  */
16505 
16506  pos = cpl_table_get_int(slitss[j], "position", k, &null);
16507  pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
16508  cpl_table_set(summary, "absolute", nobjs, obj);
16509  cpl_table_set(summary, "pos", nobjs, pos);
16510  cpl_table_set(summary, "offset", nobjs, obj - pos);
16511  cpl_table_set(summary, "pair", nobjs, pair);
16512 
16513  nobjs++;
16514  }
16515  }
16516  }
16517 
16518 // cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
16519 
16520  /*
16521  * Perform the intersection: what are the objects belonging
16522  * to the same slit (same pair ordinary + extraordinary) which
16523  * are observed at the same offset at all angles? Those are
16524  * the polarimetric objects.
16525  */
16526 
16527  nmatches = 0;
16528  maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
16529 
16530  /*
16531  * We loop on the objects of the first-angle object table as
16532  * reference, and check whether those objects are present also
16533  * at *all* other angles. Note that the loop advances by pairs.
16534  * If the top (k = 0) slit spectrum is not an ordinary beam,
16535  * it is ignored. The loop advances by pairs, starting at the
16536  * first complete pair. It is implicitely assumed that the
16537  * slit spectrum on top is always from the ordinary beam, and
16538  * the spectrum below (k+1) its extraordinary match.
16539  */
16540 
16541  for (k = 0; k < nslits; k+=2) {
16542  int slitmatches = 0;
16543 
16544  if (k + 1 < nslits ) {
16545  if (cpl_table_get_int(slitss[0], "pair_id", k, NULL) !=
16546  cpl_table_get_int(slitss[0], "pair_id", k + 1, NULL)) {
16547 
16548  /*
16549  * This is not an ordinary beam - advance to next slit.
16550  */
16551 
16552  /* It will be incremented by two, so the effect is like k++ */
16553  k--;
16554 
16555  continue;
16556  }
16557  }
16558 
16559  for (m = 0; m < maxobjs; m++) {
16560  int null;
16561  char *name = cpl_sprintf("object_%d", m + 1);
16562  double obj = cpl_table_get_double(slitss[0], name, k, &null);
16563  double pos;
16564  int pair;
16565 
16566  char *name_obj = NULL;
16567  char *name_start = NULL;
16568  char *name_end = NULL;
16569  char *name_row = NULL;
16570  char *name_row_s = NULL;
16571 
16572  char *name_start_o = NULL;
16573  char *name_end_o = NULL;
16574  char *name_row_o = NULL;
16575  char *name_start_v = NULL;
16576  char *name_end_v = NULL;
16577  char *name_obj_v = NULL;
16578 
16579  int start, end;
16580  int length;
16581 
16582  int selected;
16583  int v, start_v, end_v;
16584  double min_v, obj_v;
16585 
16586 
16587  cpl_free(name);
16588 
16589  if (null)
16590  break;
16591 
16592  /*
16593  * Each object of the first object table belongs to a
16594  * slit spectrum (k). This slit spectrum has a position
16595  * in the rectified image, and it belongs to a given
16596  * ordinary + extraordinary pair.
16597  */
16598 
16599  pos = cpl_table_get_int(slitss[0], "position", k, &null);
16600  pair = cpl_table_get_int(slitss[0], "pair_id", k, &null);
16601 
16602  /*
16603  * Now from the summary table we can select all objects
16604  * which have the same offset (obj - pos) within all slit
16605  * spectra belonging to the same ordinary + extraordinary
16606  * pair (at all angles).
16607  */
16608 
16609  cpl_table_select_all(summary); /* Reset selection */
16610 
16611  cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
16612  cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
16613  obj - pos + tolerance);
16614  selected =
16615  cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
16616  obj - pos - tolerance);
16617 
16618 
16619  /*
16620  * If this object were observed at all angles (nscience) and
16621  * at all beams (2), we should have selected exactly 2*nscience
16622  * objects. If not, this is not a polarimetric object, and it
16623  * is discarded from the intersection.
16624  */
16625 
16626  if (selected != nscience * 2)
16627  continue;
16628 
16629  /*
16630  * If we reach this point we have found one valid polarimetric
16631  * object, that must be inserted in the intersection object
16632  * table.
16633  */
16634 
16635  slitmatches++;
16636 
16637  /*
16638  * Names of the columns of the output table where the
16639  * object information needs to be copied. Note that a
16640  * new column is created, the "row_stokes_#", where the
16641  * row number of the extracted polarimetric signal is
16642  * also computed. For the moment this column will be
16643  * left empty - it will be filled only when all matches
16644  * are collected.
16645  */
16646 
16647  name_obj = cpl_sprintf("object_%d", slitmatches);
16648  name_start = cpl_sprintf("start_%d", slitmatches);
16649  name_end = cpl_sprintf("end_%d", slitmatches);
16650  name_row = cpl_sprintf("row_%d", slitmatches);
16651  name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
16652 
16653  /*
16654  * Names of the columns of the input table where the
16655  * object information is available.
16656  */
16657 
16658  name_start_o = cpl_sprintf("start_%d", m + 1);
16659  name_end_o = cpl_sprintf("end_%d", m + 1);
16660  name_row_o = cpl_sprintf("row_%d", m + 1);
16661 
16662  /*
16663  * If the output columns do not exist yet, create them.
16664  */
16665 
16666  if (!cpl_table_has_column(origslits, name_obj)) {
16667  cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
16668  cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
16669  cpl_table_new_column(origslits, name_end, CPL_TYPE_INT);
16670  cpl_table_new_column(origslits, name_row, CPL_TYPE_INT);
16671  cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
16672  }
16673 
16674  /*
16675  * The current slit spectrum is k. The slit spectrum immediately
16676  * below (in the rectified image) is k+1. We need the length of
16677  * the spectrum below for computing the _absolute_ coordinates
16678  * of the objects in the rectified image in both beams.
16679  */
16680 
16681  length = cpl_table_get_int(origslits, "length", k + 1, &null);
16682 
16683  /* NEW:
16684  * Names of the columns of the input table where
16685  * the information of the corresponding object of
16686  * the next beam is available.
16687  */
16688 
16689  for (v = 0; v < maxobjs; v++) {
16690  char *name_v = cpl_sprintf("object_%d", v + 1);
16691  double obj_v = cpl_table_get_double(slitss[0], name_v,
16692  k + 1, &null);
16693 
16694  cpl_free(name_v);
16695 
16696  if (null)
16697  break;
16698 
16699  if (v) {
16700  if (fabs(obj - length - obj_v) < min_v) {
16701  min_v = fabs(obj - length - obj_v);
16702  cpl_free(name_start_v);
16703  cpl_free(name_end_v);
16704  cpl_free(name_obj_v);
16705  name_start_v = cpl_sprintf("start_%d", v + 1);
16706  name_end_v = cpl_sprintf("end_%d", v + 1);
16707  name_obj_v = cpl_sprintf("object_%d", v + 1);
16708  }
16709  }
16710  else {
16711  min_v = fabs(obj - length - obj_v);
16712  name_start_v = cpl_sprintf("start_%d", v + 1);
16713  name_end_v = cpl_sprintf("end_%d", v + 1);
16714  name_obj_v = cpl_sprintf("object_%d", v + 1);
16715  }
16716  }
16717 
16718  /*
16719  * Read from the first input object table (first angle)
16720  * the spatial window enclosing the object.
16721  */
16722 
16723  start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
16724  end = cpl_table_get_int(slitss[0], name_end_o, k, &null);
16725 
16726  /* NEW:
16727  * Spatial window of the matching object in the next beam.
16728  */
16729 
16730  start_v = cpl_table_get_int(slitss[0], name_start_v, k + 1, &null);
16731  end_v = cpl_table_get_int(slitss[0], name_end_v, k + 1, &null);
16732  obj_v = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null);
16733 
16734  /*
16735  * Write the object coordinates in the same slit, and in the
16736  * slit below. Note that here we assume that all slits were
16737  * traced perfectly, and we compute the theoretical coords
16738  * (obj - length) within the next slit spectrum (k + 1). In
16739  * principle we should read them as well from the input
16740  * table!
16741  */
16742 
16743  cpl_table_set_double(origslits, name_obj, k, obj);
16744  cpl_table_set_double(origslits, name_obj, k + 1, obj_v);
16745  // cpl_table_set_double(origslits, name_obj, k + 1, obj - length);
16746 
16747  cpl_table_set_int(origslits, name_start, k, start);
16748  cpl_table_set_int(origslits, name_start, k + 1, start_v);
16749  // cpl_table_set_int(origslits, name_start, k + 1, start - length);
16750 
16751  cpl_table_set_int(origslits, name_end, k, end);
16752  cpl_table_set_int(origslits, name_end, k + 1, end_v);
16753  // cpl_table_set_int(origslits, name_end, k + 1, end - length);
16754 
16755  /*
16756  * "nmatches" is counting at what "reduced" image row the
16757  * extracted spectra are. Note that this is s preliminary
16758  * numbering - which is wrong: other objects may be found
16759  * in the same slit, and then the indeces would not be in
16760  * sequence. What is important is that at the end of this
16761  * loop "nmatches" would be the total number of matching
16762  * objects. The two cpl_table_set_int() calls made here
16763  * cannot be removed - they "validate" those table elements
16764  * (see ahead).
16765  */
16766 
16767  cpl_table_set_int(origslits, name_row, k, nmatches);
16768  nmatches++;
16769  cpl_table_set_int(origslits, name_row, k + 1, nmatches);
16770  nmatches++;
16771 
16772  cpl_free(name_obj);
16773  cpl_free(name_start);
16774  cpl_free(name_end);
16775  cpl_free(name_row);
16776  cpl_free(name_row_s);
16777 
16778  cpl_free(name_start_o);
16779  cpl_free(name_end_o);
16780  cpl_free(name_row_o);
16781 
16782  cpl_free(name_start_v); name_start_v = NULL;
16783  cpl_free(name_end_v); name_end_v = NULL;
16784  cpl_free(name_obj_v); name_obj_v = NULL;
16785  }
16786  }
16787 
16788  /*
16789  * The summary table has fulfilled its function. If no matching
16790  * objects are found, the function returns with an error.
16791  */
16792 
16793  cpl_table_delete(summary);
16794 
16795  if (!nmatches)
16796  return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16797 
16798  /*
16799  * Now we consider the resulting intersection object table,
16800  * listing all matches. As seen, the image row number reported
16801  * in the columns "row_#" was not really performed sequentially.
16802  * We need to renumber sequentially...
16803  * We need also to fill the "row_stokes_#" column the way the
16804  * extracted polarimetric signal will be stored in the
16805  * reduced_pol_images...
16806  */
16807 
16808  maxobjs = mos_get_maxobjs_per_slit(origslits);
16809  nstokes = nmatches / 2; /* nmatches is always an even number */
16810 
16811  for (k = 0; k < nslits; k++) {
16812  if (k % 2) { /* Extraordinary beam */
16813  nstokes = sstokes; /* Use same start value as for ordinary */
16814  }
16815  else { /* Ordinary beam */
16816  sstokes = nstokes; /* Memorise start value at ordinary beam */
16817  }
16818 
16819  for (m = 0; m < maxobjs; m++) {
16820  char *name = cpl_sprintf("row_%d", m + 1);
16821  char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
16822 
16823  if (!cpl_table_is_valid(origslits, name, k)) {
16824  cpl_free(name);
16825  cpl_free(namestokes);
16826  break;
16827  }
16828  else {
16829  nmatches--;
16830  nstokes--;
16831  cpl_table_set_int(origslits, name, k, nmatches);
16832  cpl_table_set_int(origslits, namestokes, k, nstokes);
16833  }
16834 
16835  cpl_free(name);
16836  cpl_free(namestokes);
16837  }
16838  }
16839 
16840 
16841  /*
16842  * This is done to avoid the NULL value is zero (it would invalidate
16843  * also the row_# = 0 or start_# = 0 for an object), and to enable
16844  * working directly with the column data buffers, when using this
16845  * table afterwards.
16846  */
16847 
16848  for (j = 0; j < maxobjs; j++) {
16849  char *name = cpl_sprintf("object_%d", j + 1);
16850  cpl_table_fill_invalid_double(origslits, name, -1);
16851  cpl_free(name);
16852 
16853  name = cpl_sprintf("start_%d", j + 1);
16854  cpl_table_fill_invalid_int(origslits, name, -1);
16855  cpl_free(name);
16856 
16857  name = cpl_sprintf("end_%d", j + 1);
16858  cpl_table_fill_invalid_int(origslits, name, -1);
16859  cpl_free(name);
16860 
16861  name = cpl_sprintf("row_%d", j + 1);
16862  cpl_table_fill_invalid_int(origslits, name, -1);
16863  cpl_free(name);
16864 
16865  name = cpl_sprintf("row_stokes_%d", j + 1);
16866  cpl_table_fill_invalid_int(origslits, name, -1);
16867  cpl_free(name);
16868  }
16869 
16870  /*********************************************************************
16871  * This tail has been added to propagate the selection of valid
16872  * objects also to the input slitss[] tables. Just eliminate all
16873  * this final part to suppress this behaviour.
16874  */
16875 
16876  /*
16877  * First of all, make a working copy and remove all columns related
16878  * to objects from the input object tables.
16879  */
16880 
16881  for (i = 0; i < nscience; i++) {
16882  int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
16883 
16884  work[i] = cpl_table_duplicate(slitss[i]);
16885 
16886  for (m = 0; m < c_maxobjs; m++) {
16887  char *object_o = cpl_sprintf("object_%d", m + 1);
16888  char *start_o = cpl_sprintf("start_%d", m + 1);
16889  char *end_o = cpl_sprintf("end_%d", m + 1);
16890  char *row_o = cpl_sprintf("row_%d", m + 1);
16891 
16892  cpl_table_erase_column(slitss[i], object_o);
16893  cpl_table_erase_column(slitss[i], start_o);
16894  cpl_table_erase_column(slitss[i], end_o);
16895  cpl_table_erase_column(slitss[i], row_o);
16896  }
16897  }
16898 
16899  /*
16900  * Now just consider all the objects in the intersection table.
16901  */
16902 
16903  for (k = 0; k < nslits; k++) {
16904  for (j = 0; j < maxobjs; j++) {
16905  double object_w, object_r;
16906  int row_w;
16907 
16908  char *object_i = cpl_sprintf("object_%d", j + 1);
16909  char *start_i = cpl_sprintf("start_%d", j + 1);
16910  char *end_i = cpl_sprintf("end_%d", j + 1);
16911  char *row_i = cpl_sprintf("row_%d", j + 1);
16912 
16913 
16914  if (!cpl_table_is_valid(origslits, object_i, k))
16915  break;
16916 
16917  /*
16918  * We have found a valid object (valid because it belongs
16919  * to the intersection). Now we look for this object in each
16920  * one of the original tables, we get its parameters, and
16921  * copy them at the right position (i.e., same position as
16922  * in intersection table). The object will be the one closest
16923  * to the object position (column object_i) in the intersection
16924  * table. Note that we examine the same row, k, in all tables.
16925  */
16926 
16927  object_w = cpl_table_get_double(origslits, object_i, k, NULL);
16928  row_w = cpl_table_get_int (origslits, row_i, k, NULL);
16929 
16930  for (i = 0; i < nscience; i++) {
16931  int c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
16932  int minpos;
16933  double mindiff, diff;
16934  char *object_o;
16935  char *start_o;
16936  char *end_o;
16937  char *row_o;
16938 
16939  for (m = 0; m < c_maxobjs; m++) {
16940  object_o = cpl_sprintf("object_%d", m + 1);
16941  start_o = cpl_sprintf("start_%d", m + 1);
16942  end_o = cpl_sprintf("end_%d", m + 1);
16943  row_o = cpl_sprintf("row_%d", m + 1);
16944 
16945  if (!cpl_table_is_valid(work[i], object_o, k))
16946  break;
16947 
16948  object_r = cpl_table_get_double(work[i], object_o, k, NULL);
16949  //row_r = cpl_table_get_int (work[i], row_o, k, NULL);
16950 
16951  diff = fabs(object_w - object_r);
16952  if (m) {
16953  if (mindiff > diff) {
16954  mindiff = diff;
16955  minpos = m;
16956  }
16957  }
16958  else {
16959  mindiff = diff;
16960  minpos = 0;
16961  }
16962 
16963  cpl_free(object_o);
16964  cpl_free(start_o);
16965  cpl_free(end_o);
16966  cpl_free(row_o);
16967  }
16968 
16969  object_o = cpl_sprintf("object_%d", minpos + 1);
16970  start_o = cpl_sprintf("start_%d", minpos + 1);
16971  end_o = cpl_sprintf("end_%d", minpos + 1);
16972  row_o = cpl_sprintf("row_%d", minpos + 1);
16973 
16974  if (!cpl_table_has_column(slitss[i], object_i)) {
16975  cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
16976  cpl_table_new_column(slitss[i], start_i, CPL_TYPE_INT);
16977  cpl_table_new_column(slitss[i], end_i, CPL_TYPE_INT);
16978  cpl_table_new_column(slitss[i], row_i, CPL_TYPE_INT);
16979  cpl_table_fill_invalid_double(slitss[i], object_i, -1);
16980  cpl_table_fill_invalid_int (slitss[i], start_i, -1);
16981  cpl_table_fill_invalid_int (slitss[i], end_i, -1);
16982  cpl_table_fill_invalid_int (slitss[i], row_i, -1);
16983  }
16984 
16985  cpl_table_set_double(slitss[i], object_i, k,
16986  cpl_table_get_double(work[i], object_o,
16987  k, NULL));
16988  cpl_table_set_int(slitss[i], start_i , k,
16989  cpl_table_get_int(work[i], start_o, k, NULL));
16990  cpl_table_set_int(slitss[i], end_i , k,
16991  cpl_table_get_int(work[i], end_o, k, NULL));
16992  cpl_table_set_int(slitss[i], row_i , k, row_w);
16993 
16994  cpl_free(object_o);
16995  cpl_free(start_o);
16996  cpl_free(end_o);
16997  cpl_free(row_o);
16998  }
16999 
17000  cpl_free(object_i);
17001  cpl_free(start_i);
17002  cpl_free(end_i);
17003  cpl_free(row_i);
17004  }
17005  }
17006 
17007  for (i = 0; i < nscience; i++)
17008  cpl_table_delete(work[i]);
17009 
17010  cpl_free(work);
17011 
17012 
17013  return cpl_error_get_code();
17014 }
17015 
17016 
17024 int mos_get_maxobjs_per_slit(cpl_table * slits)
17025 {
17026  int maxobjs = 1;
17027 
17028  char * colname = cpl_sprintf("object_%d", maxobjs);
17029 
17030  while (cpl_table_has_column(slits, colname)) {
17031  maxobjs++;
17032  cpl_free(colname);
17033  colname = cpl_sprintf("object_%d", maxobjs);
17034  }
17035 
17036  cpl_free(colname);
17037 
17038  maxobjs--;
17039 
17040  return maxobjs;
17041 }
17042 
17050 int mos_get_nobjects(cpl_table * slits)
17051 {
17052  int nobjs = 0;
17053 
17054  int nslits = cpl_table_get_nrow(slits);
17055  int maxobjs = mos_get_maxobjs_per_slit(slits);
17056 
17057  int k, m;
17058 
17059  for (k = 0; k < nslits; k++) {
17060  for (m = 0; m < maxobjs; m++) {
17061  char * name = cpl_sprintf("object_%d", m + 1);
17062  int null = !cpl_table_is_valid(slits, name, k);
17063 
17064  cpl_free(name);
17065 
17066  if (null) break;
17067  else nobjs++;
17068  }
17069  }
17070 
17071  return nobjs;
17072 }
17073 
17081 int mos_check_slits(cpl_table *slits, float rescale)
17082 {
17083 
17084  cpl_propertylist *sort;
17085 
17086  int nslits = cpl_table_get_nrow(slits);
17087 
17088  int k, null;
17089 
17090  const float interval = 90.0 * rescale;
17091  const float offset = (90.0 - 5) * rescale;
17092 
17093 
17094  for (k = 0; k < nslits; k++) {
17095  double ytop = cpl_table_get_double(slits, "ytop", k, &null);
17096  double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17097 
17098  double xtop = cpl_table_get_double(slits, "xtop", k, &null);
17099  double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
17100 
17101  int nmiss = (int)((ytop - ybottom) / interval + 0.5);
17102 
17103  if (nmiss > 1) {
17104  cpl_msg_warning(cpl_func,
17105  "Some slits could not be properly detected. "
17106  "There might be accountable inaccuracies.");
17107  while (nmiss > 1) {
17108  cpl_table_set_size(slits, nslits + 1);
17109 
17110  /* Fill in new slit 'cut' */
17111 
17112  /* x coordinates be the same (acceptable approximation) */
17113  cpl_table_set_double(slits, "xtop", nslits, xtop);
17114  cpl_table_set_double(slits, "xbottom", nslits, xbottom);
17115 
17116  /* Cut */
17117  if (k == 0) {
17118  cpl_table_set_double(slits, "ybottom", nslits, ybottom);
17119  cpl_table_set_double(slits, "ytop", nslits, ybottom
17120  + offset);
17121  ybottom += interval;
17122  cpl_table_set_double(slits, "ybottom", k, ybottom);
17123  } else {
17124  cpl_table_set_double(slits, "ytop", nslits, ytop);
17125  cpl_table_set_double(slits, "ybottom", nslits, ytop
17126  - offset);
17127  ytop -= interval;
17128  cpl_table_set_double(slits, "ytop", k, ytop);
17129  }
17130 
17131  nslits++; nmiss--;
17132  }
17133  }
17134  }
17135 
17136  sort = cpl_propertylist_new();
17137  cpl_propertylist_append_bool(sort, "ytop", 1);
17138  cpl_table_sort(slits, sort);
17139  cpl_propertylist_delete(sort);
17140 
17141  /*
17142  * Add here an ad hoc check on the last slit: is it too long
17143  * (by more than 10%)? Then shorten it...
17144  */
17145 
17146  k = cpl_table_get_nrow(slits) - 1;
17147 
17148  {
17149  double ytop = cpl_table_get_double(slits, "ytop", k, &null);
17150  double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17151  double length = (ytop - ybottom) / interval;
17152 
17153  if (length > 1.1) {
17154  cpl_table_set_double(slits, "ybottom", k, ytop - offset);
17155  }
17156 
17157  }
17158 
17159  return 0;
17160 }
17161 
17184 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header,
17185  int * nslits_out_det)
17186 {
17187  int m, null;
17188  int halfsize;
17189 
17190  cpl_propertylist * sort;
17191  cpl_table * slits;
17192 
17193  slits = mos_load_slits_fors_mos(header, nslits_out_det);
17194  halfsize = cpl_table_get_nrow(slits);
17195 
17196  cpl_table_set_size(slits, 2 * halfsize);
17197 
17198  for (m = 0; m < halfsize; m++) {
17199 
17200  double gap = 1.4;
17201 
17202  double length =
17203  cpl_table_get(slits, "ytop", m, &null) -
17204  cpl_table_get(slits, "ybottom", m, &null);
17205 
17206  if (m) {
17207  double interval =
17208  cpl_table_get(slits, "ybottom", m - 1, &null) -
17209  cpl_table_get(slits, "ytop", m, &null);
17210 
17211  gap = (interval - length) / 2;
17212  }
17213 
17214  cpl_table_set(slits, "slit_id", m + halfsize,
17215  cpl_table_get(slits, "slit_id", m, &null) - 1);
17216 
17217  cpl_table_set(slits, "xtop", m + halfsize,
17218  cpl_table_get(slits, "xtop", m, &null));
17219 
17220  cpl_table_set(slits, "xbottom", m + halfsize,
17221  cpl_table_get(slits, "xbottom", m, &null));
17222 
17223  cpl_table_set(slits, "ytop", m + halfsize,
17224  cpl_table_get(slits, "ytop", m, &null) + gap + length);
17225 
17226  cpl_table_set(slits, "ybottom", m + halfsize,
17227  cpl_table_get(slits, "ytop", m, &null) + gap);
17228  }
17229 
17230  for (m = 0; m < 2 * halfsize; m++) {
17231  cpl_table_set(slits, "ytop", m,
17232  cpl_table_get(slits, "ytop", m, &null) - 5.3);
17233 
17234  cpl_table_set(slits, "ybottom", m,
17235  cpl_table_get(slits, "ybottom", m, &null) - 5.3);
17236 
17237  }
17238 
17239  sort = cpl_propertylist_new();
17240  cpl_propertylist_append_bool(sort, "ytop", 1);
17241  cpl_table_sort(slits, sort);
17242 
17243  cpl_propertylist_delete(sort);
17244 
17245  return slits;
17246 }
17247 
17248 int * fors_get_nobjs_perslit(cpl_table * slits)
17249 {
17250  int nslits = cpl_table_get_nrow(slits);
17251  int maxobjs = mos_get_maxobjs_per_slit(slits);
17252 
17253  int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
17254 
17255  int k, m;
17256 
17257  for (k = 0; k < nslits; k++) {
17258  int nobjs = 0;
17259  for (m = 0; m < maxobjs; m++) {
17260  char * name = cpl_sprintf("object_%d", m + 1);
17261  int null = !cpl_table_is_valid(slits, name, k);
17262 
17263  cpl_free(name);
17264 
17265  if (null) break;
17266  else nobjs++;
17267  }
17268 
17269  nobjs_per_slit[k] = nobjs;
17270  }
17271 
17272  return nobjs_per_slit;
17273 }
17274 
17275 double fors_get_object_position(cpl_table *slits, int slit, int object)
17276 {
17277  char *name = cpl_sprintf("object_%d", object);
17278  double position;
17279 
17280  position = cpl_table_get_double(slits, name, slit, NULL)
17281  - cpl_table_get_int(slits, "position", slit, NULL);
17282 
17283  cpl_free(name);
17284 
17285  return position;
17286 }
17287 
17288 int mos_rebin_signal(cpl_image **image, int rebin)
17289 {
17290  cpl_image *rebinned;
17291 
17292 
17293  if (*image == NULL)
17294  return 1;
17295 
17296  if (rebin == 1)
17297  return 0;
17298 
17299  rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1);
17300 
17301  cpl_image_delete(*image);
17302 
17303  *image = rebinned;
17304 
17305  return 0;
17306 }
17307 
17308 int mos_rebin_error(cpl_image **image, int rebin)
17309 {
17310  if (*image == NULL)
17311  return 1;
17312 
17313  if (rebin == 1)
17314  return 0;
17315 
17316  cpl_image_power(*image, 2);
17317  mos_rebin_signal(image, rebin);
17318  cpl_image_power(*image, 0.5);
17319 
17320  return 0;
17321 }
17322 
17323 /*
17324  * @brief
17325  * Map table values into a 1D image
17326  *
17327  * @param image Target image
17328  * @param start Coordinate of first pixel in image
17329  * @param step Coordinate step for one pixel in image
17330  * @param table Source table
17331  * @param xname Name of coordinate column
17332  * @param yname Name of values column
17333  *
17334  * @return 0 on success
17335  *
17336  * The values in @em yname are linearly interpolated at the @em image
17337  * pixel coordinates. The @em image must have Nx1 size.
17338  */
17339 
17340 int map_table(cpl_image *image, double start, double step,
17341  cpl_table *table, const char *xname, const char *yname)
17342 {
17343  int length = cpl_image_get_size_x(image);
17344  int nrows = cpl_table_get_nrow(table);
17345  float *data = cpl_image_get_data_float(image);
17346  float *fdata = NULL;
17347  double *xdata = NULL;
17348  double *ydata = NULL;
17349  cpl_type xtype = cpl_table_get_column_type(table, xname);
17350  cpl_type ytype = cpl_table_get_column_type(table, yname);
17351  double xzero, pos;
17352  int i, j, n;
17353 
17354 
17355  /*
17356  * Initialization of output image at 0.0 - this value is left
17357  * on non-overlapping portions.
17358  */
17359 
17360  for (i = 0; i < length; i++)
17361  data[i] = 0.0;
17362 
17363 
17364  /*
17365  * Do everything in double precision
17366  */
17367 
17368  if (xtype == CPL_TYPE_FLOAT) {
17369  fdata = cpl_table_get_data_float(table, xname);
17370  xdata = cpl_malloc(nrows * sizeof(double));
17371  for (i = 0; i < nrows; i++) {
17372  xdata[i] = fdata[i];
17373  }
17374  }
17375  else {
17376  xdata = cpl_table_get_data_double(table, xname);
17377  }
17378 
17379  if (ytype == CPL_TYPE_FLOAT) {
17380  fdata = cpl_table_get_data_float(table, yname);
17381  ydata = cpl_malloc(nrows * sizeof(double));
17382  for (i = 0; i < nrows; i++) {
17383  ydata[i] = fdata[i];
17384  }
17385  }
17386  else {
17387  ydata = cpl_table_get_data_double(table, yname);
17388  }
17389 
17390  /*
17391  * Mapping
17392  */
17393 
17394  n = 0;
17395  xzero = xdata[n];
17396 
17397  for (i = 0; i < length; i++) {
17398  pos = start + step * i;
17399  if (pos < xzero)
17400  continue;
17401  for (j = n; j < nrows; j++) {
17402  if (xdata[j] > pos) {
17403  n = j;
17404  data[i] = ydata[j-1]
17405  + (ydata[j] - ydata[j-1])
17406  * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]);
17407  break;
17408  }
17409  }
17410  }
17411 
17412  if (xtype == CPL_TYPE_FLOAT)
17413  cpl_free(xdata);
17414 
17415  if (ytype == CPL_TYPE_FLOAT)
17416  cpl_free(ydata);
17417 
17418  return 0;
17419 }
17420 
17421 
17422 /*
17423  * @brief
17424  * Fit overall trend of a Nx1 image
17425  *
17426  * @param image Values to smooth
17427  * @param order Order of fitting polynomial
17428  * @param hw Half width of smoothing window
17429  *
17430  * @return Smoothed image, or NULL on failure.
17431  *
17432  * Heavily smooth and fit data in the input Nx1 size @em image.
17433  */
17434 
17435 static cpl_image *polysmooth(cpl_image *image, int order, int hw)
17436 {
17437  int npoints;
17438  cpl_vector *x;
17439  cpl_vector *y;
17440  double *xdata;
17441  double *ydata;
17442  cpl_polynomial *poly;
17443  cpl_vector *ysmooth;
17444  cpl_image *smoothed;
17445  float *sdata;
17446  int i;
17447 
17448 
17449  npoints = cpl_image_get_size_x(image);
17450 
17451  if (2 * hw + 1 > npoints)
17452  return NULL;
17453 
17454  x = cpl_vector_new(npoints);
17455  y = cpl_vector_new(npoints);
17456  xdata = cpl_vector_get_data(x);
17457  ydata = cpl_vector_get_data(y);
17458 
17459  smoothed = cpl_image_duplicate(image);
17460  sdata = cpl_image_get_data_float(smoothed);
17461 
17462  for (i = 0; i < npoints; i++) {
17463  xdata[i] = i;
17464  ydata[i] = sdata[i];
17465  }
17466 
17467  ysmooth = cpl_vector_filter_median_create(y, hw);
17468  cpl_vector_delete(y);
17469 
17470  poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL);
17471  cpl_vector_delete(x);
17472  cpl_vector_delete(ysmooth);
17473 
17474  if (poly) {
17475  for (i = 0; i < npoints; i++)
17476  sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL);
17477 
17478  cpl_polynomial_delete(poly);
17479  }
17480  else {
17481  cpl_image_delete(smoothed);
17482  return NULL;
17483  }
17484 
17485  return smoothed;
17486 }
17487 
17488 #undef cleanup
17489 #define cleanup \
17490 do { \
17491  cpl_image_delete(spectrum); \
17492  cpl_image_delete(flux); \
17493  cpl_image_delete(efficiency); \
17494  cpl_image_delete(smo_efficiency); \
17495  cpl_image_delete(extinction); \
17496  cpl_image_delete(response); \
17497  cpl_image_delete(smo_response); \
17498  cpl_image_delete(physical); \
17499 } while (0)
17500 
17524 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave,
17525  double dispersion, double gain,
17526  double exptime, cpl_table *ext_table,
17527  double airmass, cpl_table *flux_table,
17528  int order)
17529 {
17530 
17531  cpl_image *spectrum = NULL; // Extracted standard star spectrum
17532  float *data;
17533  cpl_image *extinction = NULL; // Extinction binned as "spectrum"
17534  float *ext_data;
17535  cpl_image *flux = NULL; // Standard star flux binned as "spectrum"
17536  float *flux_data;
17537  cpl_image *physical = NULL; // Physical units of above
17538  float *phys_data;
17539  cpl_image *efficiency = NULL; // Raw efficiency curve
17540  float *eff_data;
17541  cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve
17542  float *smo_eff_data;
17543  cpl_image *response = NULL; // Raw response curve
17544  float *res_data;
17545  cpl_image *smo_response = NULL; // Smoothed response curve
17546  float *smo_res_data;
17547  cpl_image *image;
17548  cpl_image *smo_image;
17549  cpl_table *table;
17550  float lambda;
17551  int nx, ny;
17552  int ext_count, ext_pos;
17553  int eff_count, eff_pos;
17554  int flux_count, flux_pos;
17555  int start, end;
17556  int i;
17557 
17558 
17559  if (spectra == NULL || ext_table == NULL || flux_table == NULL) {
17560  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17561  return NULL;
17562  }
17563 
17564  if (!cpl_table_has_column(ext_table, "WAVE")) {
17565  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17566  "Column WAVE in atmospheric extinction table");
17567  return NULL;
17568  }
17569 
17570  if (!cpl_table_has_column(ext_table, "EXTINCTION")) {
17571  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17572  "Column EXTINCTION in atmospheric extinction table");
17573  return NULL;
17574  }
17575 
17576  if (!cpl_table_has_column(flux_table, "WAVE")) {
17577  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17578  "Column WAVE in standard star flux table");
17579  return NULL;
17580  }
17581 
17582  if (!cpl_table_has_column(flux_table, "FLUX")) {
17583  cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17584  "Column FLUX in standard star flux table");
17585  return NULL;
17586  }
17587 
17588  if (gain < 0.1) {
17589  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17590  "Invalid gain factor (%.2f)", gain);
17591  return NULL;
17592  }
17593 
17594  if (exptime < 0.001) {
17595  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17596  "Invalid exposure time (%.2f)", exptime);
17597  return NULL;
17598  }
17599 
17600  if (dispersion < 0.001) {
17601  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17602  "Invalid dispersion (%.2f)", dispersion);
17603  return NULL;
17604  }
17605 
17606  if (order < 2) {
17607  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17608  "Order of the polynomial fitting the "
17609  "instrument response must be at least 2");
17610  return NULL;
17611  }
17612 
17613  nx = cpl_image_get_size_x(spectra);
17614  ny = cpl_image_get_size_y(spectra);
17615 
17616 
17617  /*
17618  * Find brightest spectrum and duplicate it.
17619  */
17620 
17621  if (ny == 1) {
17622  spectrum = cpl_image_duplicate(spectra);
17623  }
17624  else {
17625  cpl_size x, y;
17626  cpl_image *brights = cpl_image_collapse_create(spectra, 1);
17627 
17628  cpl_image_get_maxpos(brights, &x, &y);
17629  cpl_image_delete(brights);
17630  spectrum = cpl_image_extract(spectra, 1, y, nx, y);
17631  }
17632 
17633 
17634  /*
17635  * Convert standard star spectrum in electrons per second per Angstrom.
17636  */
17637 
17638  cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion);
17639 
17640 
17641  /*
17642  * Map the atmospheric extinction factors to the same lambda sampling
17643  * of the extracted spectrum.
17644  */
17645 
17646  extinction = cpl_image_duplicate(spectrum);
17647  map_table(extinction, startwave + dispersion/2, dispersion,
17648  ext_table, "WAVE", "EXTINCTION");
17649 
17650 
17651  /*
17652  * Convert from magnitudes to actual flux loss.
17653  */
17654 
17655  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17656  cpl_image_exponential(extinction, 10.);
17657 
17658 
17659  /*
17660  * Correct the scientific spectrum to airmass 0
17661  */
17662 
17663  cpl_image_multiply(spectrum, extinction);
17664 
17665 
17666  /*
17667  * Find in what pixel interval (start at "ext_pos", for "ext_count"
17668  * pixels) the atmospheric extinction is available.
17669  */
17670 
17671  ext_data = cpl_image_get_data_float(extinction);
17672 
17673  ext_count = 0;
17674  ext_pos = 0;
17675  for (i = 0; i < nx; i++) {
17676  if (ext_data[i] > 0.0) {
17677  if (ext_count == 0) {
17678  ext_pos = i;
17679  }
17680  ext_count++;
17681  }
17682  else {
17683  if (ext_count) {
17684  break;
17685  }
17686  }
17687  }
17688 
17689  cpl_image_delete(extinction); extinction = NULL;
17690 
17691 
17692  /*
17693  * Map the standard star catalog flux to the same lambda sampling
17694  * of the extracted spectrum.
17695  */
17696 
17697  flux = cpl_image_duplicate(spectrum);
17698  map_table(flux, startwave + dispersion/2, dispersion,
17699  flux_table, "WAVE", "FLUX");
17700 
17701 
17702  /*
17703  * Find in what pixel interval (start at "flux_pos", for "flux_count"
17704  * pixels) the standard star flux is available.
17705  */
17706 
17707  flux_data = cpl_image_get_data_float(flux);
17708 
17709  flux_count = 0;
17710  flux_pos = 0;
17711  for (i = 0; i < nx; i++) {
17712  if (flux_data[i] > 0.0) {
17713  if (flux_count == 0) {
17714  flux_pos = i;
17715  }
17716  flux_count++;
17717  }
17718  else {
17719  if (flux_count) {
17720  break;
17721  }
17722  }
17723  }
17724 
17725 
17726  /*
17727  * Intersection with previous selection
17728  */
17729 
17730  start = ext_pos > flux_pos ? ext_pos : flux_pos;
17731  end = (ext_pos + ext_count) < (flux_pos + flux_count) ?
17732  (ext_pos + ext_count) : (flux_pos + flux_count);
17733  flux_pos = start;
17734  flux_count = end - start;
17735 
17736 
17737  /*
17738  * Convert the flux to photons (per second per Angstrom).
17739  * std_flux is in units of erg / cm^2 / s / Angstrom. This
17740  * must be multiplied by the efficient area of the telescope,
17741  * 5.18E+5 cm^2, and divided by hv (v = frequency). With
17742  * hc = 1.98E-8 erg*Angstrom one obtains the following:
17743  */
17744 
17745  physical = cpl_image_duplicate(spectrum);
17746  phys_data = cpl_image_get_data_float(physical);
17747 
17748  for (i = 0; i < nx; i++) {
17749  lambda = startwave + dispersion * (i + 0.5);
17750  phys_data[i] = 0.0026 * lambda * flux_data[i];
17751  }
17752 
17753  efficiency = cpl_image_duplicate(spectrum);
17754  eff_data = cpl_image_get_data_float(efficiency);
17755  data = cpl_image_get_data_float(spectrum);
17756 
17757  for (i = 0; i < nx; i++) {
17758  if (phys_data[i] > 0.0)
17759  eff_data[i] = data[i] / phys_data[i];
17760  else
17761  eff_data[i] = 0.0;
17762  }
17763 
17764  cpl_image_delete(physical); physical = NULL;
17765 
17766 
17767  /*
17768  * Find interval (longer than 300 pixels) where efficiency is
17769  * greater than 1%
17770  */
17771 
17772  eff_count = 0;
17773  eff_pos = 0;
17774  for (i = 0; i < nx; i++) {
17775  if (eff_data[i] > 0.01) {
17776  if (eff_count == 0) {
17777  eff_pos = i;
17778  }
17779  eff_count++;
17780  }
17781  else {
17782  if (eff_count > 300) {
17783  break;
17784  }
17785  }
17786  }
17787 
17788 
17789  /*
17790  * Intersection with previous selection
17791  */
17792 
17793  start = eff_pos > flux_pos ? eff_pos : flux_pos;
17794  end = (eff_pos + eff_count) < (flux_pos + flux_count) ?
17795  (eff_pos + eff_count) : (flux_pos + flux_count);
17796  eff_pos = start;
17797  eff_count = end - start;
17798 
17799  if (eff_count < 1) {
17800  cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
17801  "No overlap between catalog and spectrum");
17802  cleanup;
17803  return NULL;
17804  }
17805 
17806 
17807  /*
17808  * Extract only data to fit, i.e., where the efficiency is available.
17809  */
17810 
17811  image = cpl_image_extract(efficiency, eff_pos + 1, 1,
17812  eff_pos + eff_count, 1);
17813 
17814  smo_image = polysmooth(image, order, 50);
17815  cpl_image_delete(image);
17816 
17817  smo_efficiency = cpl_image_duplicate(efficiency);
17818  smo_eff_data = cpl_image_get_data_float(smo_efficiency);
17819  cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1);
17820 
17821  cpl_image_delete(smo_image);
17822 
17823 
17824  /*
17825  * Compute instrument response as the ratio between the catalog
17826  * spectrum and the observed spectrum (converted in physical units).
17827  * The polynomial smoothing, however, is performed on the inverse
17828  * of this ration, for obvious reasons (i.e., no divergence at zero
17829  * efficiency).
17830  */
17831 
17832  response = cpl_image_duplicate(spectrum);
17833  res_data = cpl_image_get_data_float(response);
17834 
17835  for (i = 0; i < nx; i++) {
17836  if (eff_data[i] > 0.01 && flux_data[i] > 0.0)
17837  res_data[i] = data[i] / flux_data[i];
17838  else
17839  res_data[i] = 0.0;
17840  }
17841 
17842 
17843  /*
17844  * Extract only data to fit, i.e., where the response is available.
17845  */
17846 
17847  image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1);
17848 
17849  smo_image = polysmooth(image, order, 50);
17850  cpl_image_delete(image);
17851 
17852  smo_response = cpl_image_duplicate(response);
17853  smo_res_data = cpl_image_get_data_float(smo_response);
17854  cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1);
17855 
17856  cpl_image_delete(smo_image);
17857 
17858  for (i = 0; i < nx; i++) {
17859  if (eff_data[i] > 0.01) {
17860  res_data[i] = 1 / res_data[i];
17861  smo_res_data[i] = 1 / smo_res_data[i];
17862  }
17863  else {
17864  res_data[i] = 0.0;
17865  smo_res_data[i] = 0.0;
17866  }
17867  }
17868 
17869 
17870  /*
17871  * Assemble the product spectrophotometric table.
17872  */
17873 
17874  table = cpl_table_new(nx);
17875 
17876  cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT);
17877  cpl_table_set_column_unit(table, "WAVE", "Angstrom");
17878 
17879  for (i = 0; i < nx; i++)
17880  cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5));
17881 
17882  cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT);
17883  cpl_table_set_column_unit(table, "STD_FLUX",
17884  "10^(-16) erg/(cm^2 s Angstrom)");
17885  cpl_table_copy_data_float(table, "STD_FLUX", flux_data);
17886  cpl_image_delete(flux); flux = NULL;
17887 
17888  cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT);
17889  cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)");
17890  cpl_table_copy_data_float(table, "OBS_FLUX", data);
17891  cpl_image_delete(spectrum); spectrum = NULL;
17892 
17893  cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT);
17894  cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon");
17895  cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data);
17896  cpl_image_delete(efficiency); efficiency = NULL;
17897 
17898  cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT);
17899  cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon");
17900  cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data);
17901  cpl_image_delete(smo_efficiency); smo_efficiency = NULL;
17902 
17903  cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT);
17904  cpl_table_set_column_unit(table, "RAW_RESPONSE",
17905  "10^(-16) erg/(cm^2 electron)");
17906  cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data);
17907  cpl_image_delete(response); response = NULL;
17908 
17909  cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT);
17910  cpl_table_set_column_unit(table,
17911  "RESPONSE", "10^(-16) erg/(cm^2 electron)");
17912  cpl_table_copy_data_float(table, "RESPONSE", smo_res_data);
17913  cpl_image_delete(smo_response); smo_response = NULL;
17914 
17915  cleanup;
17916 
17917  return table;
17918 }
17919 
17920 static double ksigma_vector(cpl_vector *values,
17921  double klow, double khigh, int kiter, int *good)
17922 {
17923  cpl_vector *accepted;
17924  double mean = 0.0;
17925  double sigma = 0.0;
17926  double *data = cpl_vector_get_data(values);
17927  int n = cpl_vector_get_size(values);
17928  int ngood = n;
17929  int count = 0;
17930  int i;
17931 
17932 
17933  /*
17934  * At first iteration the mean is taken as the median, and the
17935  * standard deviation relative to this value is computed.
17936  */
17937 
17938  mean = cpl_vector_get_median(values);
17939 
17940  for (i = 0; i < n; i++)
17941  sigma += (mean - data[i]) * (mean - data[i]);
17942 
17943  sigma = sqrt(sigma / (n - 1));
17944 
17945  while (kiter) {
17946  count = 0;
17947  for (i = 0; i < ngood; i++) {
17948  if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) {
17949  data[count] = data[i];
17950  ++count;
17951  }
17952  }
17953 
17954  if (count == 0) // This cannot happen at first iteration.
17955  break; // So we can break: we have already computed a mean.
17956 
17957  /*
17958  * The mean must be computed even if no element was rejected
17959  * (count == ngood), because at first iteration median instead
17960  * of mean was computed.
17961  */
17962 
17963  accepted = cpl_vector_wrap(count, data);
17964  mean = cpl_vector_get_mean(accepted);
17965  if (count > 1)
17966  sigma = cpl_vector_get_stdev(accepted);
17967  cpl_vector_unwrap(accepted);
17968 
17969  if (count == ngood || count == 1)
17970  break;
17971 
17972  ngood = count;
17973  --kiter;
17974  }
17975 
17976  if (good)
17977  *good = ngood;
17978 
17979  return mean;
17980 }
17981 
17982 
18001 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist,
18002  double klow, double khigh, int kiter,
18003  cpl_image **good)
18004 {
18005  int ni, nx, ny, npix;
18006  cpl_image *out_ima;
18007  float *pout_ima;
18008  float *good_ima;
18009  cpl_image *image;
18010  float **data;
18011  cpl_vector *time_line;
18012  double *ptime_line;
18013  int ngood;
18014  int i, j;
18015 
18016 
18017  ni = cpl_imagelist_get_size(imlist);
18018 
18019  image = cpl_imagelist_get(imlist, 0);
18020  nx = cpl_image_get_size_x(image);
18021  ny = cpl_image_get_size_y(image);
18022  npix = nx * ny;
18023 
18024  out_ima = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18025  pout_ima = cpl_image_get_data_float(out_ima);
18026 
18027  if (good) {
18028  *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18029  good_ima = cpl_image_get_data_float(*good);
18030  }
18031 
18032  time_line = cpl_vector_new(ni);
18033  ptime_line = cpl_vector_get_data(time_line);
18034 
18035  data = cpl_calloc(sizeof(float *), ni);
18036 
18037  for (i = 0; i < ni; i++) {
18038  image = cpl_imagelist_get(imlist, i);
18039  data[i] = cpl_image_get_data_float(image);
18040  }
18041 
18042  for (i = 0; i < npix; i++) {
18043  for (j = 0; j < ni; j++) {
18044  ptime_line[j] = data[j][i];
18045  }
18046  pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood);
18047  if (good) {
18048  good_ima[i] = ngood;
18049  }
18050  }
18051 
18052  cpl_free(data);
18053  cpl_vector_delete(time_line);
18054 
18055  return out_ima;
18056 
18057 }
18058 
18059 
18076 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response,
18077  cpl_table *ext_table, double startwave,
18078  double dispersion, double gain,
18079  double exptime, double airmass)
18080 {
18081  cpl_image *extinction;
18082  cpl_image *outspectra;
18083  cpl_image *mapresponse;
18084  float *res_data;
18085  float *out_data;
18086  float *ext_data;
18087  int tlength, xlength, ylength;
18088  int i, j, k;
18089  double resp_startwave;
18090  double resp_endwave;
18091  int null;
18092 
18093 
18094  if (spectra == NULL || ext_table == NULL || response == NULL) {
18095  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18096  return NULL;
18097  }
18098 
18099  /* Use the normal response if available. If not, use the flat corrected
18100  * response. Usually only one of the two is available in the interpolated
18101  * response.
18102  */
18103  if(cpl_table_has_column(response, "RESPONSE"))
18104  cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18105  else if(cpl_table_has_column(response, "RESPONSE_FFSED"))
18106  cpl_table_cast_column(response, "RESPONSE_FFSED", "RESPONSE_F", CPL_TYPE_FLOAT);
18107  else
18108  return NULL;
18109 
18110  res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18111 
18112  if (res_data == NULL) {
18113  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18114  return NULL;
18115  }
18116 
18117  tlength = cpl_table_get_nrow(response);
18118  xlength = cpl_image_get_size_x(spectra);
18119  ylength = cpl_image_get_size_y(spectra);
18120 
18121  /* Map the response */
18122  mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18123  map_table(mapresponse, startwave + dispersion/2, dispersion,
18124  response, "WAVE", "RESPONSE_F");
18125  res_data = cpl_image_get_data_float(mapresponse);
18126 
18127  /*
18128  * Map the atmospheric extinction factors to the same lambda sampling
18129  * of the extracted spectrum.
18130  */
18131 
18132  extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18133  map_table(extinction, startwave + dispersion/2, dispersion,
18134  ext_table, "WAVE", "EXTINCTION");
18135 
18136 
18137  /*
18138  * Convert from magnitudes to actual flux loss.
18139  */
18140 
18141  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18142  cpl_image_exponential(extinction, 10.);
18143 
18144  outspectra = cpl_image_duplicate(spectra);
18145 
18146  ext_data = cpl_image_get_data_float(extinction);
18147  out_data = cpl_image_get_data_float(outspectra);
18148 
18149  for (k = 0, i = 0; i < ylength; i++) {
18150  for (j = 0; j < xlength; j++, k++)
18151  out_data[k] *= ext_data[j] * res_data[j];
18152  }
18153 
18154  cpl_image_delete(extinction);
18155  cpl_image_delete(mapresponse);
18156 
18157  cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion);
18158 
18159  /*
18160  * Set to -1 the extrapolated values
18161  */
18162  resp_startwave = cpl_table_get(response, "WAVE", 0, &null);
18163  resp_endwave = cpl_table_get(response, "WAVE",
18164  cpl_table_get_nrow(response) -1, &null);
18165  for (j = 0; j < xlength; j++) {
18166  double this_wave = startwave + j * dispersion;
18167  if(this_wave < resp_startwave ||this_wave > resp_endwave)
18168  {
18169  for (i = 0; i < ylength; i++)
18170  out_data[j + xlength * i] = -1;
18171  }
18172  }
18173 
18174  cpl_table_erase_column(response, "RESPONSE_F");
18175 
18176  return outspectra;
18177 }
18178 
18179 
18196 cpl_image *mos_propagate_photometry_error(cpl_image *spectra,
18197  cpl_image *errors,
18198  cpl_table *response,
18199  cpl_table *ext_table,
18200  double startwave,
18201  double dispersion, double gain,
18202  double exptime, double airmass)
18203 {
18204  cpl_image *extinction;
18205  cpl_image *outerrors;
18206  cpl_image *mapresponse;
18207  cpl_image *maperror;
18208  float *err_data;
18209  float *out_data;
18210  float *ext_data;
18211  float *res_data;
18212  float *spe_data;
18213  int tlength, xlength, ylength;
18214  int i, j, k;
18215 
18216 
18217  if (errors == NULL || ext_table == NULL || response == NULL) {
18218  cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18219  return NULL;
18220  }
18221 
18222  if (!cpl_table_has_column(response, "ERROR")) {
18223  return mos_apply_photometry(errors, response, ext_table, startwave,
18224  dispersion, gain, exptime, airmass);
18225  }
18226 
18227  cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18228  res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18229 
18230  if (res_data == NULL) {
18231  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18232  return NULL;
18233  }
18234 
18235  err_data = cpl_table_get_data_float(response, "ERROR");
18236 
18237  if (err_data == NULL) {
18238  cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18239  return NULL;
18240  }
18241 
18242  tlength = cpl_table_get_nrow(response);
18243  xlength = cpl_image_get_size_x(errors);
18244  ylength = cpl_image_get_size_y(errors);
18245 
18246  if (xlength != tlength) {
18247  mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18248  map_table(mapresponse, startwave + dispersion/2, dispersion,
18249  response, "WAVE", "RESPONSE_F");
18250  res_data = cpl_image_get_data_float(mapresponse);
18251 
18252  maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18253  map_table(maperror, startwave + dispersion/2, dispersion,
18254  response, "WAVE", "ERROR");
18255  err_data = cpl_image_get_data_float(maperror);
18256  }
18257 
18258  /*
18259  * Map the atmospheric extinction factors to the same lambda sampling
18260  * of the extracted spectrum.
18261  */
18262 
18263  extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18264  map_table(extinction, startwave + dispersion/2, dispersion,
18265  ext_table, "WAVE", "EXTINCTION");
18266 
18267 
18268  /*
18269  * Convert from magnitudes to actual flux loss.
18270  */
18271 
18272  cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18273  cpl_image_exponential(extinction, 10.);
18274 
18275  outerrors = cpl_image_duplicate(errors);
18276 
18277  ext_data = cpl_image_get_data_float(extinction);
18278  out_data = cpl_image_get_data_float(outerrors);
18279  spe_data = cpl_image_get_data_float(spectra);
18280 
18281  for (k = 0, i = 0; i < ylength; i++) {
18282  for (j = 0; j < xlength; j++, k++) {
18283  out_data[k] = ext_data[j] *
18284  sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] +
18285  res_data[j] * res_data[j] * out_data[k] * out_data[k]);
18286  }
18287  }
18288 
18289  cpl_image_delete(extinction);
18290  if (xlength != tlength) {
18291  cpl_image_delete(maperror);
18292  }
18293 
18294  cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion);
18295 
18296  cpl_table_erase_column(response, "RESPONSE_F");
18297  return outerrors;
18298 }
18299 
18300 
18376 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error,
18377  cpl_image *u_image, cpl_image *u_error,
18378  double startwave, double dispersion,
18379  double band, cpl_table *pol_sta,
18380  double ra, double dec, char *filter,
18381  int *polarisation,
18382  double *p_offset, double *p_error,
18383  double *a_offset, double *a_error)
18384 {
18385  cpl_table *standard;
18386  cpl_image *q_noise;
18387  cpl_image *q_signal;
18388  cpl_image *u_noise;
18389  cpl_image *u_signal;
18390  cpl_image *noise;
18391  double *q_ndata;
18392  double *q_sdata;
18393  double *u_ndata;
18394  double *u_sdata;
18395  double arctol = 0.5; /* Arc tolerance in degrees */
18396  double mindist;
18397  double cwave;
18398  double bwave[] = {3650., 4450., 5510., 6580., 8060};
18399  char *bands = "UBVRI";
18400  char p_label[] = {' ', 'p', '\0'};
18401  char dp_label[] = {' ', 'd', 'p', '\0'};
18402  char a_label[] = {' ', 'a', '\0'};
18403  char da_label[] = {' ', 'd', 'a', '\0'};
18404  int nbands = strlen(bands);
18405  int selected;
18406  int first, last, count, center;
18407  int nx;
18408  cpl_size col, row;
18409  int i, found, closest;
18410  int pband;
18411  int polarised;
18412  double q_obs;
18413  double q_err;
18414  double u_obs;
18415  double u_err;
18416  double p_obs;
18417  double p_err;
18418  double p_ref;
18419  double dp_ref;
18420  double a_obs;
18421  double a_err;
18422  double a_ref;
18423  double da_ref;
18424 
18425 
18426  *filter = '\0';
18427  *polarisation = 0;
18428  *p_offset = 0.0;
18429  *p_error = 0.0;
18430  *a_offset = 0.0;
18431  *a_error = 0.0;
18432 
18433  /*
18434  * Select reference standard star
18435  */
18436 
18437  cpl_table_select_all(pol_sta);
18438  cpl_table_and_selected_double(pol_sta, "RA", CPL_GREATER_THAN, ra-arctol);
18439  cpl_table_and_selected_double(pol_sta, "RA", CPL_LESS_THAN, ra+arctol);
18440  cpl_table_and_selected_double(pol_sta, "DEC", CPL_GREATER_THAN, dec-arctol);
18441  selected =
18442  cpl_table_and_selected_double(pol_sta, "DEC", CPL_LESS_THAN, dec+arctol);
18443 
18444  if (selected == 0) {
18445  cpl_msg_warning(cpl_func, "No standard star found in FOV");
18446  return 1;
18447  }
18448 
18449  if (selected > 1) {
18450  cpl_msg_warning(cpl_func,
18451  "Ambiguity: %d standard stars found in FOV", selected);
18452  return 1;
18453  }
18454 
18455  standard = cpl_table_extract_selected(pol_sta);
18456 
18457  cpl_msg_info(cpl_func, "Standard star: %s",
18458  cpl_table_get_string(standard, "name", 0));
18459 
18460  /*
18461  * Check whether the star is polarised or not
18462  */
18463 
18464  polarised = cpl_table_get_int(standard, "polarised", 0, NULL);
18465 
18466  cpl_msg_info(cpl_func, "This star is%sexpected to be polarised",
18467  polarised ? " " : " not ");
18468 
18469 
18470  /*
18471  * Determine the image row with the smallest median noise: this
18472  * row is assumed to refer to the standard star.
18473  * (note: the higher the S/N ratio of the original spectra, the
18474  * smaller the noise of the Stokes parameters Q and U).
18475  */
18476 
18477  nx = cpl_image_get_size_x(q_error);
18478 
18479  noise = cpl_image_collapse_median_create(q_error, 1, 0, 0);
18480  cpl_image_get_minpos(noise, &col, &row);
18481 
18482  cpl_image_delete(noise);
18483 
18484  if (col != 1) {
18485  cpl_table_delete(standard);
18486  cpl_msg_error(cpl_func,
18487  "Assertion failure!!! col = %"CPL_SIZE_FORMAT" (it should be 1)", col);
18488  return 1;
18489  }
18490 
18491  q_signal = cpl_image_extract(q_image, 1, row, nx, row);
18492  q_noise = cpl_image_extract(q_error, 1, row, nx, row);
18493  u_signal = cpl_image_extract(u_image, 1, row, nx, row);
18494  u_noise = cpl_image_extract(u_error, 1, row, nx, row);
18495 
18496  q_sdata = cpl_image_get_data_double(q_signal);
18497  q_ndata = cpl_image_get_data_double(q_noise);
18498  u_sdata = cpl_image_get_data_double(u_signal);
18499  u_ndata = cpl_image_get_data_double(u_noise);
18500 
18501 
18502  /*
18503  * Determine valid interval in input images (where error is positive).
18504  */
18505 
18506  first = -1;
18507  last = nx = cpl_image_get_size_x(q_signal);
18508  for (i = 0; i < nx; i++) {
18509  if (first < 0) {
18510  if (q_ndata[i] > 0.0) {
18511  first = i;
18512  }
18513  }
18514  else {
18515  if (q_ndata[i] <= 0.0) {
18516  last = i - 1;
18517  break;
18518  }
18519  }
18520  }
18521 
18522  count = last - first + 1;
18523 
18524  if (first < 0 || count < band) {
18525  cpl_table_delete(standard);
18526  cpl_image_delete(q_signal);
18527  cpl_image_delete(q_noise);
18528  cpl_image_delete(u_signal);
18529  cpl_image_delete(u_noise);
18530  cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count);
18531  return 1;
18532  }
18533 
18534  center = (first + last) / 2; // Center of valid spectrum
18535  cwave = startwave + dispersion * center; // Corresponding wavelength
18536 
18537 
18538  /*
18539  * Find the band UBVRI closest to the central wavelength.
18540  */
18541 
18542  found = 0;
18543  for (i = 0; i < nbands; i++) {
18544  p_label[0] = bands[i];
18545  if (cpl_table_is_valid(standard, p_label, 0)) {
18546  if (found == 0) {
18547  found = 1;
18548  mindist = fabs(bwave[i] - cwave);
18549  closest = i;
18550  }
18551  else if (mindist > fabs(bwave[i] - cwave)) {
18552  mindist = fabs(bwave[i] - cwave);
18553  closest = i;
18554  }
18555  }
18556  }
18557 
18558  if (!found) {
18559  cpl_table_delete(standard);
18560  cpl_image_delete(q_signal);
18561  cpl_image_delete(q_noise);
18562  cpl_image_delete(u_signal);
18563  cpl_image_delete(u_noise);
18564  cpl_msg_warning(cpl_func, "No reference value available");
18565  return 1;
18566  }
18567 
18568  center = (bwave[closest] - startwave) / dispersion; // Center of band (pix)
18569  cwave = bwave[closest]; // Wavelength of band
18570 
18571 
18572  /*
18573  * Check that the integration interval is entirely contained
18574  * in the valid interval, or give it up.
18575  */
18576 
18577  pband = floor(band / dispersion); // Band width in pixels
18578 
18579  if (center - pband/2 < first || center + pband/2 > last) {
18580  cpl_table_delete(standard);
18581  cpl_image_delete(q_signal);
18582  cpl_image_delete(q_noise);
18583  cpl_image_delete(u_signal);
18584  cpl_image_delete(u_noise);
18585  cpl_msg_warning(cpl_func, "No reference value available");
18586  return 1;
18587  }
18588 
18589  first = center - pband/2;
18590  last = center + pband/2;
18591 
18592  /*
18593  * Collect reference values. Note that if angle info is not available,
18594  * angle stuff is set automaticaly to zero.
18595  */
18596 
18597  p_label[0] = bands[closest];
18598  dp_label[0] = bands[closest];
18599  a_label[0] = bands[closest];
18600  da_label[0] = bands[closest];
18601 
18602  p_ref = cpl_table_get(standard, p_label, 0, NULL);
18603  dp_ref = cpl_table_get(standard, dp_label, 0, NULL);
18604  a_ref = cpl_table_get(standard, a_label, 0, NULL);
18605  da_ref = cpl_table_get(standard, da_label, 0, NULL);
18606 
18607  cpl_msg_info(cpl_func,
18608  "The expected polarisation is %.2f +- %.2f %%",
18609  p_ref, dp_ref);
18610 
18611  if (polarised) {
18612  cpl_msg_info(cpl_func,
18613  "The expected polarisation angle is %.2f +- %.2f degrees",
18614  a_ref, da_ref);
18615  }
18616 
18617  /*
18618  * Find median signal and median error.
18619  */
18620 
18621  q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1);
18622  q_err = cpl_image_get_median_window(q_error, first, 1, last, 1);
18623  u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1);
18624  u_err = cpl_image_get_median_window(u_error, first, 1, last, 1);
18625 
18626  /*
18627  * Measured linear polarisation and its error
18628  */
18629 
18630  p_obs = sqrt(q_obs * q_obs + u_obs * u_obs);
18631  p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err);
18632 
18633  /*
18634  * Measured polarisation angle
18635  */
18636 
18637  a_obs = 0.0;
18638  if (polarised) {
18639  if (fabs(q_obs) < 0.00001) {
18640  if (u_obs > 0.0) {
18641  a_obs = 45.0;
18642  }
18643  else {
18644  a_obs = 135.0;
18645  }
18646  }
18647  else {
18648  a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI;
18649  if (q_obs > 0.0) {
18650  if (u_obs < 0.0) {
18651  a_obs += 180.;
18652  }
18653  }
18654  else {
18655  a_obs += 90.;
18656  }
18657  }
18658  }
18659 
18660  /*
18661  * Error on polarisation angle
18662  */
18663 
18664  a_err = 0.0;
18665  if (polarised) {
18666  a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err)
18667  / (p_obs * p_obs)
18668  * 90 / CPL_MATH_PI;
18669  }
18670 
18671  p_obs *= 100;
18672  p_err *= 100;
18673  cpl_msg_info(cpl_func,
18674  "The measured polarisation is %.2f +- %.2f %%",
18675  p_obs, p_err);
18676 
18677  if (polarised) {
18678  cpl_msg_info(cpl_func,
18679  "The measured polarisation angle is %.2f +- %.2f degrees",
18680  a_obs, a_err);
18681  }
18682 
18683  *filter = bands[closest];
18684  *polarisation = polarised;
18685 
18686  if (polarised) {
18687  *p_offset = (p_obs - p_ref) / p_ref;
18688  *p_error = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref;
18689  }
18690  else {
18691  *p_offset = p_obs - p_ref;
18692  *p_error = sqrt(p_err * p_err + dp_ref * dp_ref);
18693  }
18694 
18695  *a_offset = a_obs - a_ref;
18696  *a_error = sqrt(a_err*a_err + da_ref*da_ref);
18697 
18698  return 0;
18699 
18700 }
18701 
18702 
18734 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
18735 {
18736  cpl_array *offsets;
18737  int noffset;
18738  int nslits = cpl_table_get_nrow(reference);
18739  int *nref;
18740  int *nobj;
18741  int corr, maxcorr;
18742  int best_shift;
18743  int i, j, k;
18744 
18745  cpl_error_code status = CPL_ERROR_NONE;
18746 
18747 
18748  *offset = 0.0;
18749 
18750  if (objects == NULL)
18751  return CPL_ERROR_NULL_INPUT;
18752 
18753  if (nslits != cpl_table_get_nrow(objects))
18754  return CPL_ERROR_INCOMPATIBLE_INPUT;
18755 
18756  nref = fors_get_nobjs_perslit(reference);
18757  nobj = fors_get_nobjs_perslit(objects);
18758 
18759  noffset = 0;
18760  for (i = 0; i < nslits; i++)
18761  noffset += nobj[i];
18762 
18763  if (noffset == 0) {
18764  cpl_free(nref);
18765  cpl_free(nobj);
18766  return CPL_ERROR_DATA_NOT_FOUND;
18767  }
18768 
18769  noffset = 0;
18770  for (i = 0; i < nslits; i++)
18771  noffset += nref[i];
18772 
18773  if (noffset == 0) {
18774  cpl_free(nref);
18775  cpl_free(nobj);
18776  return CPL_ERROR_DATA_NOT_FOUND;
18777  }
18778 
18779  offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE);
18780 
18781  noffset = 0; // The real number of offsets found will be counted.
18782 
18783  for (i = 0; i < nslits; i++) {
18784  if (nref[i] > 0 && nobj[i] > 0) {
18785  double shift;
18786  int length = cpl_table_get_int(objects, "length", i, NULL);
18787  double ytop = cpl_table_get_double(objects, "xtop", i, NULL);
18788  double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL);
18789  int *aref = cpl_calloc(length, sizeof(int));
18790  int *aobj = cpl_calloc(length, sizeof(int));
18791  float *pref = cpl_calloc(nref[i], sizeof(float));
18792  float *pobj = cpl_calloc(nobj[i], sizeof(float));
18793 
18794  for (j = 0; j < nref[i]; j++) {
18795  pref[j] = fors_get_object_position(reference, i, j + 1);
18796  aref[(int)pref[j]] = 1;
18797  }
18798 
18799  for (j = 0; j < nobj[i]; j++) {
18800  pobj[j] = fors_get_object_position(objects, i, j + 1);
18801  aobj[(int)pobj[j]] = 1;
18802  }
18803 
18804  /*
18805  * Do not consider objects at border
18806  */
18807 
18808  aref[0] = 0;
18809  aref[length - 1] = 0;
18810  aobj[0] = 0;
18811  aobj[length - 1] = 0;
18812 
18813 //for (j = 0; j < nref[i]; j++)
18814 //printf("references: %f, ", pref[j]);
18815 //printf("\n");
18816 //for (j = 0; j < nref[i]; j++)
18817 //printf("objects : %f, ", pobj[j]);
18818 //printf("\n");
18819 //for (j = 0; j < length; j++)
18820 //printf("%d", aref[j]);
18821 //printf("\n");
18822 //for (j = 0; j < length; j++)
18823 //printf("%d", aobj[j]);
18824 //printf("\n");
18825 
18826  /*
18827  * Object matching by correlation
18828  */
18829 
18830  maxcorr = 0;
18831  best_shift = length;
18832 
18833  for (shift = length/2, j = 0; j <= length; shift--, j++) {
18834  int rstart, ostart, count;
18835 
18836  if (shift > 0) {
18837  rstart = shift;
18838  ostart = 0;
18839  count = length - shift;
18840  }
18841  else {
18842  rstart = 0;
18843  ostart = -shift;
18844  count = length + shift;
18845  }
18846 
18847  corr = 0;
18848  for (k = 0; k < count; k++) {
18849  corr += aref[rstart + k] * aobj[ostart + k];
18850  }
18851 
18852  if (maxcorr < corr) {
18853  maxcorr = corr;
18854  best_shift = shift;
18855  }
18856  }
18857 
18858  if (best_shift == length) { // No shift found
18859 //printf("%d: No shift found\n", i);
18860  cpl_free(aref);
18861  cpl_free(aobj);
18862  cpl_free(pref);
18863  cpl_free(pobj);
18864  continue;
18865  }
18866 //printf("%d: Integer shift found = %d\n", i, best_shift);
18867 
18868  for (j = 0; j < nref[i]; j++) {
18869  for (k = 0; k < nobj[i]; k++) {
18870  if (fabs(pref[j] - pobj[k] - best_shift) < 2) {
18871  double ccd_offset = (pref[j] - pobj[k])
18872  * (ytop - ybottom)
18873  / length;
18874 
18875 //printf("%d: Match found: %f\n", i, ccd_offset);
18876  /*
18877  * The matching object is found, store the
18878  * corresponding offset
18879  */
18880 
18881  cpl_array_set(offsets, noffset, ccd_offset);
18882  noffset++;
18883  break;
18884  }
18885  }
18886  }
18887 
18888  cpl_free(aref);
18889  cpl_free(aobj);
18890  cpl_free(pref);
18891  cpl_free(pobj);
18892  }
18893 //else
18894 //printf("%d: No object found\n", i);
18895  }
18896 
18897  cpl_free(nref);
18898  cpl_free(nobj);
18899 
18900 //printf("%d offsets found in total\n", noffset);
18901  if (noffset > 0) {
18902  if (noffset % 2) {
18903  *offset = cpl_array_get_median(offsets);
18904  }
18905  else {
18906  double *a = cpl_malloc(sizeof(double) * noffset);
18907  for (i = 0; i < noffset; i++) {
18908  a[i] = cpl_array_get_double(offsets, i, NULL);
18909  }
18910  *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) +
18911  fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0;
18912  cpl_free(a);
18913  }
18914  }
18915  else
18916  status = CPL_ERROR_DATA_NOT_FOUND;
18917 //printf("Median offset: %f\n", *offset);
18918 
18919  cpl_array_delete(offsets);
18920 
18921  return status;
18922 
18923 }
18924 
18925 
18937 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
18938 {
18939  cpl_image *source;
18940  int nx = cpl_image_get_size_x(image);
18941  int ny = cpl_image_get_size_y(image);
18942  float *idata;
18943  float *sdata;
18944  int i, j, pos;
18945  double xpos, ypos, xfrac, yfrac;
18946  int xint, yint;
18947 
18948 
18949  if (fabs(dx) >= nx || fabs(dy) >= ny)
18950  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18951 
18952  source = cpl_image_duplicate(image);
18953  idata = cpl_image_get_data_float(image);
18954  sdata = cpl_image_get_data_float(source);
18955 
18956  /*
18957  * Shift in y
18958  */
18959 
18960  yfrac = - dy - floor(- dy);
18961  xfrac = - dx - floor(- dx);
18962 
18963  for (pos = 0, j = 0; j < ny; j++) {
18964  ypos = j - dy;
18965  yint = floor(ypos);
18966  for (i = 0; i < nx; i++) {
18967  xpos = i - dx;
18968  xint = floor(xpos);
18969  if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) {
18970  idata[pos] = 0.0;
18971  }
18972  else {
18973  idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac)
18974  + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac)
18975  + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac
18976  + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac;
18977  }
18978  pos++;
18979  }
18980  }
18981 
18982  cpl_image_delete(source);
18983 
18984  return CPL_ERROR_NONE;
18985 }
18986 
18998 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
18999 {
19000 #ifdef CPL_SIZE_FORMAT
19001  cpl_size row;
19002 #else
19003  int row;
19004 #endif
19005 
19006  cpl_table_duplicate_column(slits, "x", slits, "xtop");
19007  cpl_table_add_columns(slits, "x", "xbottom");
19008  cpl_table_divide_scalar(slits, "x", 2); // Mean x position
19009  cpl_table_subtract_scalar(slits, "x", nx/2); // Relative to CCD center
19010  cpl_table_multiply_columns(slits, "x", "x"); // Squared
19011 
19012  cpl_table_duplicate_column(slits, "y", slits, "ytop");
19013  cpl_table_add_columns(slits, "y", "ybottom");
19014  cpl_table_divide_scalar(slits, "y", 2); // Mean y position
19015  cpl_table_subtract_scalar(slits, "y", ny/2); // Relative to CCD center
19016  cpl_table_multiply_columns(slits, "y", "y"); // Squared
19017 
19018  cpl_table_add_columns(slits, "x", "y"); // Distance from center
19019  cpl_table_get_column_minpos(slits, "x", &row); // Min distance from center
19020 
19021  cpl_table_erase_column(slits, "x");
19022  cpl_table_erase_column(slits, "y");
19023 
19024  return row;
19025 }
19026 
19046 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits,
19047  double xwidth, double ywidth,
19048  int dx, double gain, double *o_flux, double *o_err)
19049 {
19050  int nx = cpl_image_get_size_x(image);
19051  int ny = cpl_image_get_size_y(image);
19052  int slit = mos_slit_closest_to_center(slits, nx, ny);
19053  int ytop = (int)cpl_table_get(slits, "ytop", slit, NULL);
19054  int ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL);
19055  int dy = ytop - ybottom;
19056  int xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) +
19057  cpl_table_get(slits, "xbottom", slit, NULL)) / 2);
19058  int xleft = xcenter - dx;
19059  int xright = xcenter + dx + 1;
19060  double area = xwidth * ywidth; // squared mm
19061  int npix = (2*dx + 1) * dy;
19062  int count = 0;
19063  float *data = cpl_image_get_data_float(image);
19064  double flux = 0.0;
19065  double error = 0.0;
19066  int satur = 60000;
19067  int x, y;
19068 
19069 
19070  if (cpl_table_has_column(slits, "ywidth")) {
19071  area = cpl_table_get(slits, "xwidth", slit, NULL)
19072  * cpl_table_get(slits, "ywidth", slit, NULL);
19073  }
19074 
19075  *o_flux = 0.0;
19076  *o_err = 0.0;
19077 
19078  if (xleft < 0)
19079  xleft = 0;
19080 
19081  if (xleft > nx)
19082  xleft = nx;
19083 
19084  if (xright < 0)
19085  xright = 0;
19086 
19087  if (xright > nx)
19088  xright = nx;
19089 
19090  if (ytop < 0)
19091  ytop = 0;
19092 
19093  if (ytop > ny)
19094  ytop = ny;
19095 
19096  if (ybottom < 0)
19097  ybottom = 0;
19098 
19099  if (ybottom > ny)
19100  ybottom = ny;
19101 
19102  count = (xright - xleft) * (ytop - ybottom);
19103 
19104  if (count == 0)
19105  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19106 
19107  count = 0;
19108 
19109  for (y = ybottom; y < ytop; y++) {
19110  for (x = xleft; x < xright; x++) {
19111  double value = data[x + y * nx];
19112  if (value < satur) {
19113  flux += value;
19114  count++;
19115  }
19116  }
19117  }
19118 
19119  if (count == 0)
19120  return CPL_ERROR_DIVISION_BY_ZERO;
19121 
19122  error = sqrt(flux/gain);
19123 
19124  /*
19125  * Flux correction for lost pixels
19126  */
19127 
19128  flux *= (float)npix / count;
19129  error *= (float)npix / count;
19130 
19131  flux /= area;
19132  error /= area;
19133 
19134  *o_flux = flux;
19135  *o_err = error;
19136 
19137  return CPL_ERROR_NONE;
19138 }
19139 
19140 
19163 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits,
19164  double xwidth, double ywidth,
19165  double lambda, double startwave,
19166  double dispersion, int dx, double gain,
19167  double *o_flux, double *o_err)
19168 {
19169  int nx = cpl_image_get_size_x(image);
19170  int ny = cpl_image_get_size_y(image);
19171  int slit = mos_slit_closest_to_center(slits, nx, ny);
19172  int dy = (int)cpl_table_get(slits, "length", slit, NULL);
19173  int ybottom = (int)cpl_table_get(slits, "position", slit, NULL);
19174  int ytop = ybottom + dy;
19175  int xcenter = (int)floor((lambda - startwave) / dispersion + 0.5);
19176  int xleft = xcenter - dx;
19177  int xright = xcenter + dx + 1;
19178  double area = xwidth * ywidth;
19179  int npix = (2*dx + 1) * dy;
19180  int count = 0;
19181  float *data = cpl_image_get_data_float(image);
19182  double flux = 0.0;
19183  double error = 0.0;
19184  int satur = 60000;
19185  int x, y;
19186 
19187 
19188  if (cpl_table_has_column(slits, "ywidth")) {
19189  area = cpl_table_get(slits, "xwidth", slit, NULL)
19190  * cpl_table_get(slits, "ywidth", slit, NULL);
19191  }
19192 
19193  *o_flux = 0.0;
19194  *o_err = 0.0;
19195 
19196  if (xleft < 0)
19197  xleft = 0;
19198 
19199  if (xleft > nx)
19200  xleft = nx;
19201 
19202  if (xright < 0)
19203  xright = 0;
19204 
19205  if (xright > nx)
19206  xright = nx;
19207 
19208  if (ytop < 0)
19209  ytop = 0;
19210 
19211  if (ytop > ny)
19212  ytop = ny;
19213 
19214  if (ybottom < 0)
19215  ybottom = 0;
19216 
19217  if (ybottom > ny)
19218  ybottom = ny;
19219 
19220  count = (xright - xleft) * (ytop - ybottom);
19221 
19222  if (count == 0)
19223  return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19224 
19225  count = 0;
19226 
19227  for (y = ybottom; y < ytop; y++) {
19228  for (x = xleft; x < xright; x++) {
19229  double value = data[x + y * nx];
19230  if (value < satur) {
19231  flux += value;
19232  count++;
19233  }
19234  }
19235  }
19236 
19237  if (count == 0)
19238  return CPL_ERROR_DIVISION_BY_ZERO;
19239 
19240  error = sqrt(flux/gain);
19241 
19242  /*
19243  * Flux correction for lost pixels
19244  */
19245 
19246  flux *= (float)npix / count;
19247  error *= (float)npix / count;
19248 
19249  flux /= area;
19250  error /= area;
19251 
19252  *o_flux = flux;
19253  *o_err = error;
19254 
19255  return CPL_ERROR_NONE;
19256 
19257 }
19258 
19259 
19273 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit,
19274  char *label, double *mvalue)
19275 {
19276  int position = cpl_table_get_int(slits, "position", slit, NULL);
19277  int length = cpl_table_get_int(slits, "length", slit, NULL);
19278  cpl_table *tmp = cpl_table_extract(table, position, length);
19279 
19280  *mvalue = cpl_table_get_column_median(tmp, label);
19281  cpl_table_delete(tmp);
19282 
19283  if (cpl_error_get_code() != CPL_ERROR_NONE)
19284  return 1;
19285 
19286  return 0;
19287 }
19288 
19289 
19301 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny)
19302 {
19303  cpl_mask *kernel = cpl_mask_new(nx, ny);
19304  cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image),
19305  cpl_image_get_size_y(image),
19306  cpl_image_get_type(image));
19307 
19308  cpl_mask_not(kernel);
19309  cpl_image_filter_mask(filtered, image, kernel,
19310  CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
19311  cpl_mask_delete(kernel);
19312 
19313  return filtered;
19314 }
19315 
19316 int fors_mos_is_lss_like(cpl_table *maskslits, int nslits_out_det)
19317 {
19318  int treat_as_lss = 1;
19319  double mxpos = cpl_table_get_column_median(maskslits, "xtop");
19320  double * slit_xpos = cpl_table_get_data_double(maskslits, "xtop");
19321  cpl_size nslits = cpl_table_get_nrow(maskslits);
19322 
19323  //If not all the slits are illuminated, then we cannot say that
19324  //it is lss-like (PIPE-4380)
19325  if(nslits_out_det != 0)
19326  return 0;
19327 
19328  for (cpl_size i = 0; i < nslits; i++) {
19329  if (fabs(mxpos-slit_xpos[i]) > 0.01) {
19330  treat_as_lss = 0;
19331  break;
19332  }
19333  }
19334  return treat_as_lss;
19335 }
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:17524
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:18196
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:17050
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:15119
double mos_get_gain_vimos(cpl_propertylist *header)
Return gain factor for a VIMOS exposure.
Definition: moses.c:15489
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:16399
int mos_get_maxobjs_per_slit(cpl_table *slits)
Get the maximum possible number of objects in a slit.
Definition: moses.c:17024
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:18376
cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
Shift values in an image.
Definition: moses.c:18937
int mos_check_multiplex(cpl_table *slits)
Determining whether a VIMOS mask has spectral multplexing or not.
Definition: moses.c:15645
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:19046
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:16429
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:17081
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:18076
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:16239
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:18001
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:17184
cpl_table * mos_load_slits_vimos(cpl_propertylist *header)
Create slit location table from FITS header of VIMOS data.
Definition: moses.c:15534
cpl_error_code mos_randomise_image(cpl_image *image, double ron, double gain, double bias)
Randomise image.
Definition: moses.c:16195
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:15321
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:16331
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:18998
cpl_image * mos_image_filter_median(cpl_image *image, int nx, int ny)
Convenience function for standard median filtering.
Definition: moses.c:19301
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:19163
int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
Estimate offset between two object tables.
Definition: moses.c:18734
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:15744
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:19273
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